@woosh/meep-engine 3.26.0 → 3.28.0
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/build/bundle-worker-terrain.js +1 -1
- package/package.json +1 -1
- package/src/core/binary/lz4/LZ4_HIGH_COMPRESSION_PLAN_2026_09_14.md +542 -0
- package/src/core/binary/lz4/LZ4_LEVEL.d.ts +33 -0
- package/src/core/binary/lz4/LZ4_LEVEL.d.ts.map +1 -0
- package/src/core/binary/lz4/LZ4_LEVEL.js +32 -0
- package/src/core/binary/lz4/lz4_compress_block_at_level.d.ts +33 -0
- package/src/core/binary/lz4/lz4_compress_block_at_level.d.ts.map +1 -0
- package/src/core/binary/lz4/lz4_compress_block_at_level.js +47 -0
- package/src/core/binary/lz4/lz4_compress_block_hc.d.ts +40 -0
- package/src/core/binary/lz4/lz4_compress_block_hc.d.ts.map +1 -0
- package/src/core/binary/lz4/lz4_compress_block_hc.js +1382 -0
- package/src/core/binary/lz4/lz4_decompress_block.d.ts +2 -1
- package/src/core/binary/lz4/lz4_decompress_block.d.ts.map +1 -1
- package/src/core/binary/lz4/lz4_decompress_block.js +31 -6
- package/src/core/geom/3d/mat4/m4_linear_determinant.d.ts +15 -0
- package/src/core/geom/3d/mat4/m4_linear_determinant.d.ts.map +1 -0
- package/src/core/geom/3d/mat4/m4_linear_determinant.js +24 -0
- package/src/core/geom/3d/mat4/m4_normal_matrix3.d.ts +4 -2
- package/src/core/geom/3d/mat4/m4_normal_matrix3.d.ts.map +1 -1
- package/src/core/geom/3d/mat4/m4_normal_matrix3.js +21 -11
- package/src/core/geom/3d/shape/HeightMapShape3D.d.ts +14 -7
- package/src/core/geom/3d/shape/HeightMapShape3D.d.ts.map +1 -1
- package/src/core/geom/3d/shape/HeightMapShape3D.js +87 -78
- package/src/engine/Engine.d.ts.map +1 -1
- package/src/engine/Engine.js +38 -1
- package/src/engine/graphics3/GraphicsEngine.d.ts +5 -1
- package/src/engine/graphics3/GraphicsEngine.d.ts.map +1 -1
- package/src/engine/graphics3/GraphicsEngine.js +834 -830
- package/src/engine/physics/fluid/ecs/FluidObstacleSystem.d.ts +4 -4
- package/src/shade/RENDERER_CONTRACT.md +19 -10
- package/src/shade/device/timing/GPU_PROFILER_PROPOSAL_2026_08_28.md +1 -1
- package/src/shade/playground/vgeo_runtime/README.md +186 -27
- package/src/shade/playground/vgeo_runtime/index.html +112 -62
- package/src/shade/playground/vgeo_runtime/main.js +678 -88
- package/src/shade/playground/vgeo_runtime/sample_asset.d.ts +18 -0
- package/src/shade/playground/vgeo_runtime/sample_asset.d.ts.map +1 -0
- package/src/shade/playground/{vgeo_viewer → vgeo_runtime}/sample_asset.js +23 -5
- package/src/shade/playground/vgeo_scene/README.md +408 -0
- package/src/shade/playground/vgeo_scene/index.html +144 -0
- package/src/shade/playground/vgeo_scene/main.d.ts +2 -0
- package/src/shade/playground/vgeo_scene/main.d.ts.map +1 -0
- package/src/shade/playground/vgeo_scene/main.js +1716 -0
- package/src/shade/playground/vgeo_viewer/sample_asset.d.ts.map +1 -1
- package/src/shade/renderer/Renderer.d.ts +4 -1
- package/src/shade/renderer/Renderer.d.ts.map +1 -1
- package/src/shade/renderer/Renderer.js +5 -2
- package/src/shade/renderer/extension/RENDER_EXTENSION_DESIGN.md +1 -2
- package/src/shade/renderer/geometry/bvh/GPUGeometryBVHManager.d.ts +11 -0
- package/src/shade/renderer/geometry/bvh/GPUGeometryBVHManager.d.ts.map +1 -1
- package/src/shade/renderer/geometry/bvh/GPUGeometryBVHManager.js +16 -8
- package/src/shade/renderer/geometry/meshlet/GPUMeshletManager.d.ts.map +1 -1
- package/src/shade/renderer/geometry/meshlet/GPUMeshletManager.js +16 -11
- package/src/shade/renderer/geometry/meshlet/MeshletBatch.d.ts.map +1 -1
- package/src/shade/renderer/geometry/meshlet/MeshletBatch.js +3 -29
- package/src/shade/renderer/geometry/meshlet/encoding/ENCODED_ATTRIBUTE_DEFAULT_VALUES.d.ts +22 -0
- package/src/shade/renderer/geometry/meshlet/encoding/ENCODED_ATTRIBUTE_DEFAULT_VALUES.d.ts.map +1 -0
- package/src/shade/renderer/geometry/meshlet/encoding/ENCODED_ATTRIBUTE_DEFAULT_VALUES.js +49 -0
- package/src/shade/renderer/geometry/virtual/VGEO_FORMAT.md +77 -7
- package/src/shade/renderer/geometry/virtual/VIRTUAL_GEOMETRY_DESIGN.md +36 -23
- package/src/shade/renderer/geometry/virtual/VIRTUAL_GEOMETRY_PLAN.md +17 -14
- package/src/shade/renderer/geometry/virtual/build/VGeoBuildOptions.d.ts +19 -1
- package/src/shade/renderer/geometry/virtual/build/VGeoBuildOptions.d.ts.map +1 -1
- package/src/shade/renderer/geometry/virtual/build/VGeoBuildOptions.js +21 -1
- package/src/shade/renderer/geometry/virtual/build/gltf/gltf_open_document.d.ts.map +1 -1
- package/src/shade/renderer/geometry/virtual/build/gltf/gltf_open_document.js +5 -9
- package/src/shade/renderer/geometry/virtual/build/level/VGeoLevel.d.ts +32 -0
- package/src/shade/renderer/geometry/virtual/build/level/VGeoLevel.d.ts.map +1 -0
- package/src/shade/renderer/geometry/virtual/build/level/VGeoLevelOptions.d.ts +59 -0
- package/src/shade/renderer/geometry/virtual/build/level/VGeoLevelOptions.d.ts.map +1 -0
- package/src/shade/renderer/geometry/virtual/build/level/VGeoLevelSummary.d.ts +44 -0
- package/src/shade/renderer/geometry/virtual/build/level/VGeoLevelSummary.d.ts.map +1 -0
- package/src/shade/renderer/geometry/virtual/build/level/vgeo_build_levels.d.ts +36 -0
- package/src/shade/renderer/geometry/virtual/build/level/vgeo_build_levels.d.ts.map +1 -0
- package/src/shade/renderer/geometry/virtual/build/{vgeo_build_levels.js → level/vgeo_build_levels.js} +61 -95
- package/src/shade/renderer/geometry/virtual/build/level/vgeo_build_root_group.d.ts +19 -0
- package/src/shade/renderer/geometry/virtual/build/level/vgeo_build_root_group.d.ts.map +1 -0
- package/src/shade/renderer/geometry/virtual/build/{vgeo_build_root_group.js → level/vgeo_build_root_group.js} +1 -1
- package/src/shade/renderer/geometry/virtual/build/mesh/VirtualGeometryMesh.d.ts +73 -0
- package/src/shade/renderer/geometry/virtual/build/mesh/VirtualGeometryMesh.d.ts.map +1 -0
- package/src/shade/renderer/geometry/virtual/build/{VirtualGeometryMesh.js → mesh/VirtualGeometryMesh.js} +7 -7
- package/src/shade/renderer/geometry/virtual/build/mesh/vgeo_compute_vertex_normals.d.ts +29 -0
- package/src/shade/renderer/geometry/virtual/build/mesh/vgeo_compute_vertex_normals.d.ts.map +1 -0
- package/src/shade/renderer/geometry/virtual/build/{vgeo_compute_vertex_normals.js → mesh/vgeo_compute_vertex_normals.js} +3 -3
- package/src/shade/renderer/geometry/virtual/build/mesh/vgeo_compute_vertex_tangents.d.ts +31 -0
- package/src/shade/renderer/geometry/virtual/build/mesh/vgeo_compute_vertex_tangents.d.ts.map +1 -0
- package/src/shade/renderer/geometry/virtual/build/{vgeo_compute_vertex_tangents.js → mesh/vgeo_compute_vertex_tangents.js} +5 -5
- package/src/shade/renderer/geometry/virtual/build/mesh/vgeo_normalize_source.d.ts +46 -0
- package/src/shade/renderer/geometry/virtual/build/mesh/vgeo_normalize_source.d.ts.map +1 -0
- package/src/shade/renderer/geometry/virtual/build/{vgeo_normalize_source.js → mesh/vgeo_normalize_source.js} +9 -9
- package/src/shade/renderer/geometry/virtual/build/page/vgeo_assemble_pages.d.ts +13 -0
- package/src/shade/renderer/geometry/virtual/build/page/vgeo_assemble_pages.d.ts.map +1 -0
- package/src/shade/renderer/geometry/virtual/build/{vgeo_assemble_pages.js → page/vgeo_assemble_pages.js} +63 -16
- package/src/shade/renderer/geometry/virtual/build/page/vgeo_build_page_nodes.d.ts +13 -0
- package/src/shade/renderer/geometry/virtual/build/page/vgeo_build_page_nodes.d.ts.map +1 -0
- package/src/shade/renderer/geometry/virtual/build/{vgeo_build_page_nodes.js → page/vgeo_build_page_nodes.js} +4 -4
- package/src/shade/renderer/geometry/virtual/build/page/vgeo_page_node_count.d.ts +22 -0
- package/src/shade/renderer/geometry/virtual/build/page/vgeo_page_node_count.d.ts.map +1 -0
- package/src/shade/renderer/geometry/virtual/build/{vgeo_page_node_count.js → page/vgeo_page_node_count.js} +2 -2
- package/src/shade/renderer/geometry/virtual/build/partition/VGeoFaceGraph.d.ts +52 -0
- package/src/shade/renderer/geometry/virtual/build/partition/VGeoFaceGraph.d.ts.map +1 -0
- package/src/shade/renderer/geometry/virtual/build/{VGeoFaceGraph.js → partition/VGeoFaceGraph.js} +1 -1
- package/src/shade/renderer/geometry/virtual/build/partition/vgeo_graph_components.d.ts +14 -0
- package/src/shade/renderer/geometry/virtual/build/partition/vgeo_graph_components.d.ts.map +1 -0
- package/src/shade/renderer/geometry/virtual/build/partition/vgeo_partition_graph.d.ts +29 -0
- package/src/shade/renderer/geometry/virtual/build/partition/vgeo_partition_graph.d.ts.map +1 -0
- package/src/shade/renderer/geometry/virtual/build/{vgeo_partition_graph.js → partition/vgeo_partition_graph.js} +2 -2
- package/src/shade/renderer/geometry/virtual/build/partition/vgeo_partition_subgraph.d.ts +41 -0
- package/src/shade/renderer/geometry/virtual/build/partition/vgeo_partition_subgraph.d.ts.map +1 -0
- package/src/shade/renderer/geometry/virtual/build/partition/vgeo_partition_subgraph.js +92 -0
- package/src/shade/renderer/geometry/virtual/build/partition/vgeo_split_face_set.d.ts +10 -0
- package/src/shade/renderer/geometry/virtual/build/partition/vgeo_split_face_set.d.ts.map +1 -0
- package/src/shade/renderer/geometry/virtual/build/{vgeo_split_face_set.js → partition/vgeo_split_face_set.js} +12 -55
- package/src/shade/renderer/geometry/virtual/build/partition/vgeo_split_face_set_packed.d.ts +15 -0
- package/src/shade/renderer/geometry/virtual/build/partition/vgeo_split_face_set_packed.d.ts.map +1 -0
- package/src/shade/renderer/geometry/virtual/build/{vgeo_split_face_set_packed.js → partition/vgeo_split_face_set_packed.js} +3 -3
- package/src/shade/renderer/geometry/virtual/build/sphere/vgeo_bounding_sphere.d.ts +12 -0
- package/src/shade/renderer/geometry/virtual/build/sphere/vgeo_bounding_sphere.d.ts.map +1 -0
- package/src/shade/renderer/geometry/virtual/build/{vgeo_bounding_sphere.js → sphere/vgeo_bounding_sphere.js} +3 -3
- package/src/shade/renderer/geometry/virtual/build/sphere/vgeo_containing_sphere.d.ts +18 -0
- package/src/shade/renderer/geometry/virtual/build/sphere/vgeo_containing_sphere.d.ts.map +1 -0
- package/src/shade/renderer/geometry/virtual/build/{vgeo_containing_sphere.js → sphere/vgeo_containing_sphere.js} +3 -3
- package/src/shade/renderer/geometry/virtual/build/sphere/vgeo_sphere_inflate_to_contain.d.ts +9 -0
- package/src/shade/renderer/geometry/virtual/build/sphere/vgeo_sphere_inflate_to_contain.d.ts.map +1 -0
- package/src/shade/renderer/geometry/virtual/build/{vgeo_sphere_inflate_to_contain.js → sphere/vgeo_sphere_inflate_to_contain.js} +1 -1
- package/src/shade/renderer/geometry/virtual/build/submesh/VGeoGatheredFaces.d.ts +24 -0
- package/src/shade/renderer/geometry/virtual/build/submesh/VGeoGatheredFaces.d.ts.map +1 -0
- package/src/shade/renderer/geometry/virtual/build/submesh/VGeoSubmesh.d.ts +20 -0
- package/src/shade/renderer/geometry/virtual/build/submesh/VGeoSubmesh.d.ts.map +1 -0
- package/src/shade/renderer/geometry/virtual/build/submesh/vgeo_extract_submesh.d.ts +14 -0
- package/src/shade/renderer/geometry/virtual/build/submesh/vgeo_extract_submesh.d.ts.map +1 -0
- package/src/shade/renderer/geometry/virtual/build/{vgeo_extract_submesh.js → submesh/vgeo_extract_submesh.js} +5 -5
- package/src/shade/renderer/geometry/virtual/build/submesh/vgeo_gather_faces.d.ts +12 -0
- package/src/shade/renderer/geometry/virtual/build/submesh/vgeo_gather_faces.d.ts.map +1 -0
- package/src/shade/renderer/geometry/virtual/build/{vgeo_gather_faces.js → submesh/vgeo_gather_faces.js} +2 -2
- package/src/shade/renderer/geometry/virtual/build/submesh/vgeo_narrow_indices.d.ts +11 -0
- package/src/shade/renderer/geometry/virtual/build/submesh/vgeo_narrow_indices.d.ts.map +1 -0
- package/src/shade/renderer/geometry/virtual/build/{vgeo_narrow_indices.js → submesh/vgeo_narrow_indices.js} +3 -3
- package/src/shade/renderer/geometry/virtual/build/vgeo_assemble_pages.d.ts.map +1 -1
- package/src/shade/renderer/geometry/virtual/build/vgeo_build.d.ts.map +1 -1
- package/src/shade/renderer/geometry/virtual/build/vgeo_build.js +10 -11
- package/src/shade/renderer/geometry/virtual/format/VGeoWriteOptions.d.ts +22 -0
- package/src/shade/renderer/geometry/virtual/format/VGeoWriteOptions.d.ts.map +1 -1
- package/src/shade/renderer/geometry/virtual/format/VGeoWriteOptions.js +24 -0
- package/src/shade/renderer/geometry/virtual/format/attribute/vgeo_attributes_read.d.ts +8 -2
- package/src/shade/renderer/geometry/virtual/format/attribute/vgeo_attributes_read.d.ts.map +1 -1
- package/src/shade/renderer/geometry/virtual/format/attribute/vgeo_attributes_read.js +8 -2
- package/src/shade/renderer/geometry/virtual/format/dag/VirtualGeometryCluster.d.ts +12 -0
- package/src/shade/renderer/geometry/virtual/format/dag/VirtualGeometryCluster.d.ts.map +1 -1
- package/src/shade/renderer/geometry/virtual/format/dag/VirtualGeometryCluster.js +13 -0
- package/src/shade/renderer/geometry/virtual/format/frame/vgeo_encode_frame_blob.d.ts +8 -1
- package/src/shade/renderer/geometry/virtual/format/frame/vgeo_encode_frame_blob.d.ts.map +1 -1
- package/src/shade/renderer/geometry/virtual/format/frame/vgeo_encode_frame_blob.js +11 -3
- package/src/shade/renderer/geometry/virtual/format/header/VGEO_FORMAT_VERSION.d.ts +9 -0
- package/src/shade/renderer/geometry/virtual/format/header/VGEO_FORMAT_VERSION.d.ts.map +1 -1
- package/src/shade/renderer/geometry/virtual/format/header/VGEO_FORMAT_VERSION.js +10 -1
- package/src/shade/renderer/geometry/virtual/format/page/VGEO_GROUP_OFFSET.d.ts +15 -0
- package/src/shade/renderer/geometry/virtual/format/page/VGEO_GROUP_OFFSET.d.ts.map +1 -0
- package/src/shade/renderer/geometry/virtual/format/page/VGEO_GROUP_OFFSET.js +15 -0
- package/src/shade/renderer/geometry/virtual/format/page/VGEO_MAX_PARENT_ENTRY_COUNT.d.ts +18 -0
- package/src/shade/renderer/geometry/virtual/format/page/VGEO_MAX_PARENT_ENTRY_COUNT.d.ts.map +1 -0
- package/src/shade/renderer/geometry/virtual/format/page/VGEO_MAX_PARENT_ENTRY_COUNT.js +17 -0
- package/src/shade/renderer/geometry/virtual/format/page/VGEO_MAX_PARENT_REF.d.ts +8 -0
- package/src/shade/renderer/geometry/virtual/format/page/VGEO_MAX_PARENT_REF.d.ts.map +1 -0
- package/src/shade/renderer/geometry/virtual/format/page/VGEO_MAX_PARENT_REF.js +7 -0
- package/src/shade/renderer/geometry/virtual/format/page/VGEO_PAGE_OFFSET.d.ts +3 -0
- package/src/shade/renderer/geometry/virtual/format/page/VGEO_PAGE_OFFSET.js +16 -0
- package/src/shade/renderer/geometry/virtual/format/page/VGEO_PARENT_ENTRY_SIZE.d.ts +14 -0
- package/src/shade/renderer/geometry/virtual/format/page/VGEO_PARENT_ENTRY_SIZE.d.ts.map +1 -0
- package/src/shade/renderer/geometry/virtual/format/page/VGEO_PARENT_ENTRY_SIZE.js +13 -0
- package/src/shade/renderer/geometry/virtual/format/page/VGEO_PARENT_PAGE_ENTRY_SIZE.d.ts +11 -0
- package/src/shade/renderer/geometry/virtual/format/page/VGEO_PARENT_PAGE_ENTRY_SIZE.d.ts.map +1 -0
- package/src/shade/renderer/geometry/virtual/format/page/VGEO_PARENT_PAGE_ENTRY_SIZE.js +10 -0
- package/src/shade/renderer/geometry/virtual/format/payload/vgeo_encode_cluster_payload.d.ts +28 -0
- package/src/shade/renderer/geometry/virtual/format/payload/vgeo_encode_cluster_payload.d.ts.map +1 -1
- package/src/shade/renderer/geometry/virtual/format/payload/vgeo_encode_cluster_payload.js +7 -3
- package/src/shade/renderer/geometry/virtual/format/read/VGeoByteSource.d.ts +20 -0
- package/src/shade/renderer/geometry/virtual/format/read/VGeoByteSource.d.ts.map +1 -1
- package/src/shade/renderer/geometry/virtual/format/read/VGeoByteSource.js +20 -2
- package/src/shade/renderer/geometry/virtual/format/read/VGeoContainerHeader.d.ts +4 -0
- package/src/shade/renderer/geometry/virtual/format/read/VGeoContainerHeader.d.ts.map +1 -1
- package/src/shade/renderer/geometry/virtual/format/read/VGeoContainerHeader.js +4 -0
- package/src/shade/renderer/geometry/virtual/format/read/VGeoContainerReader.d.ts +29 -3
- package/src/shade/renderer/geometry/virtual/format/read/VGeoContainerReader.d.ts.map +1 -1
- package/src/shade/renderer/geometry/virtual/format/read/VGeoContainerReader.js +81 -122
- package/src/shade/renderer/geometry/virtual/format/read/VGeoPage.d.ts +40 -0
- package/src/shade/renderer/geometry/virtual/format/read/VGeoPage.d.ts.map +1 -1
- package/src/shade/renderer/geometry/virtual/format/read/VGeoPage.js +44 -0
- package/src/shade/renderer/geometry/virtual/format/read/VGeoPageFetcherCore.d.ts +69 -0
- package/src/shade/renderer/geometry/virtual/format/read/VGeoPageFetcherCore.d.ts.map +1 -0
- package/src/shade/renderer/geometry/virtual/format/read/VGeoPageFetcherCore.js +236 -0
- package/src/shade/renderer/geometry/virtual/format/read/VGeoReadOptions.d.ts +14 -0
- package/src/shade/renderer/geometry/virtual/format/read/VGeoReadOptions.d.ts.map +1 -1
- package/src/shade/renderer/geometry/virtual/format/read/VGeoReadOptions.js +15 -0
- package/src/shade/renderer/geometry/virtual/format/read/vgeo_byte_source_from_descriptor.d.ts +31 -0
- package/src/shade/renderer/geometry/virtual/format/read/vgeo_byte_source_from_descriptor.d.ts.map +1 -0
- package/src/shade/renderer/geometry/virtual/format/read/vgeo_byte_source_from_descriptor.js +32 -0
- package/src/shade/renderer/geometry/virtual/format/read/vgeo_fetch_byte_source.d.ts +10 -0
- package/src/shade/renderer/geometry/virtual/format/read/vgeo_fetch_byte_source.d.ts.map +1 -1
- package/src/shade/renderer/geometry/virtual/format/read/vgeo_fetch_byte_source.js +21 -1
- package/src/shade/renderer/geometry/virtual/format/read/vgeo_page_transfer.d.ts +15 -0
- package/src/shade/renderer/geometry/virtual/format/read/vgeo_page_transfer.d.ts.map +1 -0
- package/src/shade/renderer/geometry/virtual/format/read/vgeo_page_transfer.js +84 -0
- package/src/shade/renderer/geometry/virtual/format/read/vgeo_page_worker_serve.d.ts +22 -0
- package/src/shade/renderer/geometry/virtual/format/read/vgeo_page_worker_serve.d.ts.map +1 -0
- package/src/shade/renderer/geometry/virtual/format/read/vgeo_page_worker_serve.js +96 -0
- package/src/shade/renderer/geometry/virtual/format/read/vgeo_read_page.d.ts.map +1 -1
- package/src/shade/renderer/geometry/virtual/format/read/vgeo_read_page.js +63 -3
- package/src/shade/renderer/geometry/virtual/format/vgeo_validate_container.d.ts.map +1 -1
- package/src/shade/renderer/geometry/virtual/format/vgeo_validate_container.js +219 -17
- package/src/shade/renderer/geometry/virtual/format/vgeo_write_container.d.ts.map +1 -1
- package/src/shade/renderer/geometry/virtual/format/vgeo_write_container.js +156 -11
- package/src/shade/renderer/geometry/virtual/runtime/CUT_PLAN_2026_09_14.md +656 -0
- package/src/shade/renderer/geometry/virtual/runtime/GPUVirtualGeometryPool.d.ts +33 -36
- package/src/shade/renderer/geometry/virtual/runtime/GPUVirtualGeometryPool.d.ts.map +1 -1
- package/src/shade/renderer/geometry/virtual/runtime/GPUVirtualGeometryPool.js +109 -193
- package/src/shade/renderer/geometry/virtual/runtime/GPUVirtualGeometryRuntime.d.ts +109 -29
- package/src/shade/renderer/geometry/virtual/runtime/GPUVirtualGeometryRuntime.d.ts.map +1 -1
- package/src/shade/renderer/geometry/virtual/runtime/GPUVirtualGeometryRuntime.js +173 -105
- package/src/shade/renderer/geometry/virtual/runtime/REVIEW_LEDGER.md +37 -0
- package/src/shade/renderer/geometry/virtual/runtime/RUNTIME_PLAN.md +109 -26
- package/src/shade/renderer/geometry/virtual/runtime/RUNTIME_PLAN_2026_09_14.md +548 -0
- package/src/shade/renderer/geometry/virtual/runtime/VGeoFeedbackLoop.d.ts +29 -50
- package/src/shade/renderer/geometry/virtual/runtime/VGeoFeedbackLoop.d.ts.map +1 -1
- package/src/shade/renderer/geometry/virtual/runtime/VGeoFeedbackLoop.js +55 -487
- package/src/shade/renderer/geometry/virtual/runtime/VGeoPageFetcher.d.ts +84 -0
- package/src/shade/renderer/geometry/virtual/runtime/VGeoPageFetcher.d.ts.map +1 -0
- package/src/shade/renderer/geometry/virtual/runtime/VGeoPageFetcher.js +393 -0
- package/src/shade/renderer/geometry/virtual/runtime/VGeoResidency.d.ts +234 -0
- package/src/shade/renderer/geometry/virtual/runtime/VGeoResidency.d.ts.map +1 -0
- package/src/shade/renderer/geometry/virtual/runtime/VGeoResidency.js +1388 -0
- package/src/shade/renderer/geometry/virtual/runtime/VGeoResidencyOptions.d.ts +81 -0
- package/src/shade/renderer/geometry/virtual/runtime/VGeoResidencyOptions.d.ts.map +1 -0
- package/src/shade/renderer/geometry/virtual/runtime/VGeoResidencyOptions.js +89 -0
- package/src/shade/renderer/geometry/virtual/runtime/VGeoRuntimeTables.d.ts +53 -34
- package/src/shade/renderer/geometry/virtual/runtime/VGeoRuntimeTables.d.ts.map +1 -1
- package/src/shade/renderer/geometry/virtual/runtime/VGeoRuntimeTables.js +407 -124
- package/src/shade/renderer/geometry/virtual/runtime/VGeoViewCut.d.ts +100 -0
- package/src/shade/renderer/geometry/virtual/runtime/VGeoViewCut.d.ts.map +1 -0
- package/src/shade/renderer/geometry/virtual/runtime/VGeoViewCut.js +219 -0
- package/src/shade/renderer/geometry/virtual/runtime/cut/VGEO_CUT_EXPAND.d.ts +17 -0
- package/src/shade/renderer/geometry/virtual/runtime/cut/VGEO_CUT_EXPAND.d.ts.map +1 -0
- package/src/shade/renderer/geometry/virtual/runtime/cut/VGEO_CUT_EXPAND.js +47 -0
- package/src/shade/renderer/geometry/virtual/runtime/cut/VGEO_CUT_FLAG_COARSEN.d.ts +20 -0
- package/src/shade/renderer/geometry/virtual/runtime/cut/VGEO_CUT_FLAG_COARSEN.d.ts.map +1 -0
- package/src/shade/renderer/geometry/virtual/runtime/cut/VGEO_CUT_FLAG_COARSEN.js +19 -0
- package/src/shade/renderer/geometry/virtual/runtime/cut/VGEO_CUT_PARAMS_STRUCT.d.ts.map +1 -1
- package/src/shade/renderer/geometry/virtual/runtime/cut/VGEO_CUT_PARAMS_STRUCT.js +30 -12
- package/src/shade/renderer/geometry/virtual/runtime/cut/chunk_vgeo_cut_access.d.ts +8 -8
- package/src/shade/renderer/geometry/virtual/runtime/cut/chunk_vgeo_cut_access.d.ts.map +1 -1
- package/src/shade/renderer/geometry/virtual/runtime/cut/chunk_vgeo_cut_access.js +11 -73
- package/src/shade/renderer/geometry/virtual/runtime/cut/chunk_vgeo_cut_arena.d.ts +19 -0
- package/src/shade/renderer/geometry/virtual/runtime/cut/chunk_vgeo_cut_arena.d.ts.map +1 -0
- package/src/shade/renderer/geometry/virtual/runtime/cut/chunk_vgeo_cut_arena.js +98 -0
- package/src/shade/renderer/geometry/virtual/runtime/cut/chunk_vgeo_cut_claim.d.ts +31 -0
- package/src/shade/renderer/geometry/virtual/runtime/cut/chunk_vgeo_cut_claim.d.ts.map +1 -0
- package/src/shade/renderer/geometry/virtual/runtime/cut/chunk_vgeo_cut_claim.js +99 -0
- package/src/shade/renderer/geometry/virtual/runtime/cut/chunk_vgeo_cut_queue.d.ts +17 -0
- package/src/shade/renderer/geometry/virtual/runtime/cut/chunk_vgeo_cut_queue.d.ts.map +1 -0
- package/src/shade/renderer/geometry/virtual/runtime/cut/chunk_vgeo_cut_queue.js +39 -0
- package/src/shade/renderer/geometry/virtual/runtime/cut/shader_vgeo_cut_collect.d.ts.map +1 -1
- package/src/shade/renderer/geometry/virtual/runtime/cut/shader_vgeo_cut_collect.js +18 -1
- package/src/shade/renderer/geometry/virtual/runtime/cut/shader_vgeo_cut_queue_prepare.d.ts.map +1 -1
- package/src/shade/renderer/geometry/virtual/runtime/cut/shader_vgeo_cut_queue_prepare.js +5 -15
- package/src/shade/renderer/geometry/virtual/runtime/cut/shader_vgeo_cut_queue_remainder.d.ts +3 -0
- package/src/shade/renderer/geometry/virtual/runtime/cut/shader_vgeo_cut_queue_remainder.d.ts.map +1 -0
- package/src/shade/renderer/geometry/virtual/runtime/cut/shader_vgeo_cut_queue_remainder.js +77 -0
- package/src/shade/renderer/geometry/virtual/runtime/cut/shader_vgeo_cut_reseed.d.ts +3 -0
- package/src/shade/renderer/geometry/virtual/runtime/cut/shader_vgeo_cut_reseed.d.ts.map +1 -0
- package/src/shade/renderer/geometry/virtual/runtime/cut/shader_vgeo_cut_reseed.js +130 -0
- package/src/shade/renderer/geometry/virtual/runtime/cut/shader_vgeo_cut_seed.d.ts.map +1 -1
- package/src/shade/renderer/geometry/virtual/runtime/cut/shader_vgeo_cut_seed.js +32 -37
- package/src/shade/renderer/geometry/virtual/runtime/cut/shader_vgeo_cut_traverse.d.ts.map +1 -1
- package/src/shade/renderer/geometry/virtual/runtime/cut/shader_vgeo_cut_traverse.js +576 -230
- package/src/shade/renderer/geometry/virtual/runtime/cut/vgeo_cut_feedback_layout.d.ts +15 -18
- package/src/shade/renderer/geometry/virtual/runtime/cut/vgeo_cut_feedback_layout.d.ts.map +1 -1
- package/src/shade/renderer/geometry/virtual/runtime/cut/vgeo_cut_feedback_layout.js +19 -28
- package/src/shade/renderer/geometry/virtual/runtime/graph_vgeo_expand_instances_to_meshlets.d.ts.map +1 -1
- package/src/shade/renderer/geometry/virtual/runtime/graph_vgeo_expand_instances_to_meshlets.js +45 -25
- package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_CUT_STAT.d.ts +1 -0
- package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_CUT_STAT.js +11 -0
- package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_CUT_STAT_COUNT.js +1 -1
- package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_FEEDBACK_OFFSET.d.ts +0 -1
- package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_FEEDBACK_OFFSET.js +12 -10
- package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_GROUP_ROW_OFFSET.d.ts +1 -0
- package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_GROUP_ROW_OFFSET.js +8 -0
- package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_GROUP_ROW_WORDS.d.ts +7 -2
- package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_GROUP_ROW_WORDS.d.ts.map +1 -1
- package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_GROUP_ROW_WORDS.js +8 -3
- package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_PAGE_ROW_OFFSET.d.ts +2 -0
- package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_PAGE_ROW_OFFSET.js +19 -0
- package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_PAGE_ROW_WORDS.d.ts.map +1 -1
- package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_PAGE_ROW_WORDS.js +1 -1
- package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_PARENT_ROW_OFFSET.d.ts +5 -0
- package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_PARENT_ROW_OFFSET.d.ts.map +1 -0
- package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_PARENT_ROW_OFFSET.js +22 -0
- package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_PARENT_ROW_WORDS.d.ts +14 -0
- package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_PARENT_ROW_WORDS.d.ts.map +1 -0
- package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_PARENT_ROW_WORDS.js +13 -0
- package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_QUEUE_TARGET_PARENT_BIT.d.ts +17 -0
- package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_QUEUE_TARGET_PARENT_BIT.d.ts.map +1 -0
- package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_QUEUE_TARGET_PARENT_BIT.js +16 -0
- package/src/shade/renderer/geometry/virtual/runtime/make_vgeo_page_worker.d.ts +15 -0
- package/src/shade/renderer/geometry/virtual/runtime/make_vgeo_page_worker.d.ts.map +1 -0
- package/src/shade/renderer/geometry/virtual/runtime/make_vgeo_page_worker.js +16 -0
- package/src/shade/renderer/geometry/virtual/runtime/vgeo_frontier_words.d.ts +23 -0
- package/src/shade/renderer/geometry/virtual/runtime/vgeo_frontier_words.d.ts.map +1 -0
- package/src/shade/renderer/geometry/virtual/runtime/vgeo_frontier_words.js +28 -0
- package/src/shade/renderer/geometry/virtual/runtime/vgeo_page.worker.d.ts +2 -0
- package/src/shade/renderer/geometry/virtual/runtime/vgeo_page.worker.d.ts.map +1 -0
- package/src/shade/renderer/geometry/virtual/runtime/vgeo_page.worker.js +13 -0
- package/src/shade/renderer/geometry/virtual/runtime/vgeo_runtime_page_block_words.d.ts +10 -5
- package/src/shade/renderer/geometry/virtual/runtime/vgeo_runtime_page_block_words.d.ts.map +1 -1
- package/src/shade/renderer/geometry/virtual/runtime/vgeo_runtime_page_block_words.js +15 -6
- package/src/shade/renderer/geometry/virtual/runtime/vgeo_runtime_write_page_block.d.ts +6 -1
- package/src/shade/renderer/geometry/virtual/runtime/vgeo_runtime_write_page_block.d.ts.map +1 -1
- package/src/shade/renderer/geometry/virtual/runtime/vgeo_runtime_write_page_block.js +38 -1
- package/src/shade/renderer/global_illumination/sharc/shader/shader_sharc_evict.d.ts.map +1 -1
- package/src/shade/renderer/global_illumination/sharc/shader/shader_sharc_evict.js +4 -3
- package/src/shade/renderer/loader/gltf/load_gltf.d.ts.map +1 -1
- package/src/shade/renderer/loader/gltf/load_gltf.js +0 -19
- package/src/shade/renderer/loader/gltf/tiny-gltf.d.ts +1 -1
- package/src/shade/renderer/material/chunk_material_draw_side.d.ts +14 -0
- package/src/shade/renderer/material/chunk_material_draw_side.d.ts.map +1 -0
- package/src/shade/renderer/material/chunk_material_draw_side.js +34 -0
- package/src/shade/renderer/material/standard/fragment_gbuffer.d.ts.map +1 -1
- package/src/shade/renderer/material/standard/fragment_gbuffer.js +8 -1
- package/src/shade/renderer/particles/shaders/shader_particle_emit.d.ts.map +1 -1
- package/src/shade/renderer/particles/shaders/shader_particle_emit.js +6 -3
- package/src/shade/renderer/particles/shaders/shader_particle_simulate.d.ts.map +1 -1
- package/src/shade/renderer/particles/shaders/shader_particle_simulate.js +6 -3
- package/src/shade/renderer/path_tracer/shader/chunk_sample_material_data.d.ts.map +1 -1
- package/src/shade/renderer/path_tracer/shader/chunk_sample_material_data.js +13 -1
- package/src/shade/renderer/postprocess/nss/NSS.d.ts +1 -1
- package/src/shade/renderer/rasterize/bucket/construct_primitive_state.d.ts +19 -5
- package/src/shade/renderer/rasterize/bucket/construct_primitive_state.d.ts.map +1 -1
- package/src/shade/renderer/rasterize/bucket/construct_primitive_state.js +23 -5
- package/src/shade/renderer/rasterize/bucket/filter_materials_by_bucket.d.ts.map +1 -1
- package/src/shade/renderer/rasterize/bucket/filter_materials_by_bucket.js +12 -5
- package/src/shade/renderer/rasterize/bucket/prepare_meshlet_draw_commands_by_material.d.ts.map +1 -1
- package/src/shade/renderer/rasterize/bucket/prepare_meshlet_draw_commands_by_material.js +12 -4
- package/src/shade/renderer/rasterize/bucket/rasterization_opaque_buckets.d.ts +22 -0
- package/src/shade/renderer/rasterize/bucket/rasterization_opaque_buckets.d.ts.map +1 -0
- package/src/shade/renderer/rasterize/bucket/rasterization_opaque_buckets.js +36 -0
- package/src/shade/renderer/rasterize/bucket/shader_read_out_bucket.d.ts +2 -0
- package/src/shade/renderer/rasterize/bucket/shader_read_out_bucket.d.ts.map +1 -1
- package/src/shade/renderer/rasterize/bucket/shader_read_out_bucket.js +58 -57
- package/src/shade/renderer/rasterize/chunk_triangle_winding_is_reversed.d.ts +15 -0
- package/src/shade/renderer/rasterize/chunk_triangle_winding_is_reversed.d.ts.map +1 -0
- package/src/shade/renderer/rasterize/chunk_triangle_winding_is_reversed.js +35 -0
- package/src/shade/renderer/rasterize/cull/hzb/shader_hzb_mesh_filter_2way.d.ts.map +1 -1
- package/src/shade/renderer/rasterize/cull/hzb/shader_hzb_mesh_filter_2way.js +3 -5
- package/src/shade/renderer/rasterize/cull/hzb/shader_hzb_meshlet_filter_1way_full.d.ts.map +1 -1
- package/src/shade/renderer/rasterize/cull/hzb/shader_hzb_meshlet_filter_1way_full.js +3 -4
- package/src/shade/renderer/rasterize/expand/bucket/RASTERIZATION_BUCKET_LIMIT.d.ts +10 -1
- package/src/shade/renderer/rasterize/expand/bucket/RASTERIZATION_BUCKET_LIMIT.d.ts.map +1 -1
- package/src/shade/renderer/rasterize/expand/bucket/RASTERIZATION_BUCKET_LIMIT.js +38 -29
- package/src/shade/renderer/rasterize/expand/bucket/chunk_rasterization_material_bucket.d.ts +5 -0
- package/src/shade/renderer/rasterize/expand/bucket/chunk_rasterization_material_bucket.d.ts.map +1 -1
- package/src/shade/renderer/rasterize/expand/bucket/chunk_rasterization_material_bucket.js +63 -55
- package/src/shade/renderer/rasterize/expand/bucket/meshlet/shader_meshlets_extract_bucket.d.ts.map +1 -1
- package/src/shade/renderer/rasterize/expand/bucket/meshlet/shader_meshlets_extract_bucket.js +3 -4
- package/src/shade/renderer/rasterize/expand/graph_meshlets_rasterize.d.ts.map +1 -1
- package/src/shade/renderer/rasterize/expand/graph_meshlets_rasterize.js +1 -0
- package/src/shade/renderer/rasterize/expand/pass0/pass_mesh_cull.d.ts.map +1 -1
- package/src/shade/renderer/rasterize/expand/pass0/pass_mesh_cull.js +7 -7
- package/src/shade/renderer/rasterize/fast/graph_extract_meshes_from_bucket.d.ts.map +1 -1
- package/src/shade/renderer/rasterize/fast/graph_extract_meshes_from_bucket.js +83 -78
- package/src/shade/renderer/rasterize/fast/graph_rasterize_scene_fast.d.ts.map +1 -1
- package/src/shade/renderer/rasterize/fast/graph_rasterize_scene_fast.js +44 -53
- package/src/shade/renderer/rasterize/forward/chunk_forward_shade_standard_fragment_brick4.d.ts.map +1 -1
- package/src/shade/renderer/rasterize/forward/chunk_forward_shade_standard_fragment_brick4.js +6 -2
- package/src/shade/renderer/rasterize/forward/chunk_forward_shade_standard_fragment_ibl.d.ts.map +1 -1
- package/src/shade/renderer/rasterize/forward/chunk_forward_shade_standard_fragment_ibl.js +6 -2
- package/src/shade/renderer/rasterize/forward/vertex_shader_chunk.d.ts.map +1 -1
- package/src/shade/renderer/rasterize/forward/vertex_shader_chunk.js +19 -2
- package/src/shade/renderer/rasterize/native/avboit/AVBOIT_PLAN_2026_09_03.md +7 -4
- package/src/shade/renderer/rasterize/native/avboit/graph_rasterize_meshes_transparent_avboit.d.ts.map +1 -1
- package/src/shade/renderer/rasterize/native/avboit/graph_rasterize_meshes_transparent_avboit.js +3 -2
- package/src/shade/renderer/rasterize/native/chunk_rasterize_triangle.d.ts.map +1 -1
- package/src/shade/renderer/rasterize/native/chunk_rasterize_triangle.js +15 -4
- package/src/shade/renderer/rasterize/native/depth/shadow_rasterization_alpha_tested_pass_descriptor.d.ts.map +1 -1
- package/src/shade/renderer/rasterize/native/depth/shadow_rasterization_alpha_tested_pass_descriptor.js +16 -5
- package/src/shade/renderer/rasterize/native/depth/shadow_rasterization_pass_descriptor.d.ts.map +1 -1
- package/src/shade/renderer/rasterize/native/depth/shadow_rasterization_pass_descriptor.js +16 -5
- package/src/shade/renderer/rasterize/native/oit/graph_rasterize_meshes_transparent_oit.d.ts.map +1 -1
- package/src/shade/renderer/rasterize/native/oit/graph_rasterize_meshes_transparent_oit.js +2 -1
- package/src/shade/renderer/rasterize/native/rasterization_shader_resources.d.ts.map +1 -1
- package/src/shade/renderer/rasterize/native/rasterization_shader_resources.js +4 -0
- package/src/shade/renderer/rasterize/native/viz/viz_rasterization_alpha_tested_pass_descriptor.d.ts.map +1 -1
- package/src/shade/renderer/rasterize/native/viz/viz_rasterization_alpha_tested_pass_descriptor.js +16 -7
- package/src/shade/renderer/rasterize/standard/graph_rasterize_partial_opaque.d.ts.map +1 -1
- package/src/shade/renderer/rasterize/standard/graph_rasterize_partial_opaque.js +85 -89
- package/src/shade/renderer/rasterize/standard/graph_rasterize_remaining_opaque.d.ts.map +1 -1
- package/src/shade/renderer/rasterize/standard/graph_rasterize_remaining_opaque.js +28 -32
- package/src/shade/renderer/scene/GPUSceneContext.d.ts +22 -2
- package/src/shade/renderer/scene/GPUSceneContext.d.ts.map +1 -1
- package/src/shade/renderer/scene/GPUSceneContext.js +61 -4
- package/src/shade/renderer/scene/NODE3D_STRUCT.d.ts +19 -0
- package/src/shade/renderer/scene/NODE3D_STRUCT.d.ts.map +1 -1
- package/src/shade/renderer/scene/NODE3D_STRUCT.js +34 -0
- package/src/shade/renderer/scene/chunk_node3d_flags.d.ts +9 -0
- package/src/shade/renderer/scene/chunk_node3d_flags.d.ts.map +1 -0
- package/src/shade/renderer/scene/chunk_node3d_flags.js +23 -0
- package/src/shade/renderer/scene/chunk_scene_database_access.d.ts.map +1 -1
- package/src/shade/renderer/scene/chunk_scene_database_access.js +15 -1
- package/src/shade/renderer/scene/rows/GPUSceneRows.d.ts.map +1 -1
- package/src/shade/renderer/scene/rows/GPUSceneRows.js +9 -1
- package/src/shade/renderer/shader/chunk/geometry/mat4/chunk_compute_normal_matrix_from_m4.d.ts +7 -0
- package/src/shade/renderer/shader/chunk/geometry/mat4/chunk_compute_normal_matrix_from_m4.d.ts.map +1 -1
- package/src/shade/renderer/shader/chunk/geometry/mat4/chunk_compute_normal_matrix_from_m4.js +16 -4
- package/src/shade/renderer/shader/chunk/geometry/mat4/chunk_mat4_linear_determinant.d.ts +12 -0
- package/src/shade/renderer/shader/chunk/geometry/mat4/chunk_mat4_linear_determinant.d.ts.map +1 -0
- package/src/shade/renderer/shader/chunk/geometry/mat4/chunk_mat4_linear_determinant.js +18 -0
- package/src/shade/renderer/shader/chunk/geometry/triangle/chunk_triangle_list_reverse_winding.d.ts +24 -0
- package/src/shade/renderer/shader/chunk/geometry/triangle/chunk_triangle_list_reverse_winding.d.ts.map +1 -0
- package/src/shade/renderer/shader/chunk/geometry/triangle/chunk_triangle_list_reverse_winding.js +33 -0
- package/src/shade/renderer/shader/resource/ShaderResourceSetDescriptor.d.ts.map +1 -1
- package/src/shade/renderer/shader/resource/ShaderResourceSetDescriptor.js +92 -16
- package/src/shade/renderer/view/GPUViewContext.d.ts +5 -0
- package/src/shade/renderer/view/GPUViewContext.d.ts.map +1 -1
- package/src/shade/renderer/view/GPUViewContext.js +21 -0
- package/src/shade/wgsl/emulator/WGSLLanguageCore.d.ts.map +1 -1
- package/src/shade/wgsl/emulator/WGSLLanguageCore.js +74 -3
- package/src/shade/playground/vgeo_viewer/README.md +0 -150
- package/src/shade/playground/vgeo_viewer/cut_geometry.js +0 -460
- package/src/shade/playground/vgeo_viewer/index.html +0 -77
- package/src/shade/playground/vgeo_viewer/main.js +0 -1343
- package/src/shade/playground/vgeo_viewer/select_cut.js +0 -481
- package/src/shade/renderer/geometry/virtual/residency/VGeoResidencyManager.d.ts +0 -258
- package/src/shade/renderer/geometry/virtual/residency/VGeoResidencyManager.d.ts.map +0 -1
- package/src/shade/renderer/geometry/virtual/residency/VGeoResidencyManager.js +0 -723
- package/src/shade/renderer/geometry/virtual/residency/VGeoResidencyOptions.d.ts +0 -49
- package/src/shade/renderer/geometry/virtual/residency/VGeoResidencyOptions.d.ts.map +0 -1
- package/src/shade/renderer/geometry/virtual/residency/VGeoResidencyOptions.js +0 -50
- package/src/shade/renderer/loader/gltf/fix_up_material_sides.js +0 -109
- package/src/shade/renderer/rasterize/expand/graph_triangles_filter_by_material_side.js +0 -78
- package/src/shade/renderer/rasterize/expand/shader_filter_triangle_by_material_side.js +0 -100
- package/src/shade/renderer/rasterize/forward/chunk_material_side_should_draw.js +0 -26
- package/src/shade/renderer/shader/chunk/jobs/NOTES.md +0 -47
- package/src/shade/renderer/shader/chunk/jobs/chunk_job_system.d.ts +0 -3
- package/src/shade/renderer/shader/chunk/jobs/chunk_job_system.d.ts.map +0 -1
- package/src/shade/renderer/shader/chunk/jobs/chunk_job_system.js +0 -102
- /package/src/shade/renderer/geometry/virtual/build/{VGeoLevel.js → level/VGeoLevel.js} +0 -0
- /package/src/shade/renderer/geometry/virtual/build/{VGeoLevelOptions.js → level/VGeoLevelOptions.js} +0 -0
- /package/src/shade/renderer/geometry/virtual/build/{VGeoLevelSummary.js → level/VGeoLevelSummary.js} +0 -0
- /package/src/shade/renderer/geometry/virtual/build/{vgeo_graph_components.js → partition/vgeo_graph_components.js} +0 -0
- /package/src/shade/renderer/geometry/virtual/build/{VGeoGatheredFaces.js → submesh/VGeoGatheredFaces.js} +0 -0
- /package/src/shade/renderer/geometry/virtual/build/{VGeoSubmesh.js → submesh/VGeoSubmesh.js} +0 -0
|
@@ -0,0 +1,1382 @@
|
|
|
1
|
+
import { assert } from "../../assert.js";
|
|
2
|
+
import { lsb_32 } from "../lsb_32.js";
|
|
3
|
+
import { LZ4_LEVEL } from "./LZ4_LEVEL.js";
|
|
4
|
+
import { LZ4_MAX_INPUT_SIZE } from "./LZ4_MAX_INPUT_SIZE.js";
|
|
5
|
+
|
|
6
|
+
/** the shortest match the format can code — the match length field is biased by this */
|
|
7
|
+
const MIN_MATCH = 4;
|
|
8
|
+
|
|
9
|
+
/** no match starts within this many bytes of the end of a block */
|
|
10
|
+
const MATCH_FIND_LIMIT = 12;
|
|
11
|
+
|
|
12
|
+
/** the last bytes of a block are literals, and no match may cover them */
|
|
13
|
+
const LAST_LITERALS = 5;
|
|
14
|
+
|
|
15
|
+
/** below this there is no room for a match at all, so the block is one literal run */
|
|
16
|
+
const MIN_LENGTH = MATCH_FIND_LIMIT + 1;
|
|
17
|
+
|
|
18
|
+
/** the offset field is sixteen bits, so a match reaches back no further than this */
|
|
19
|
+
const MAX_DISTANCE = 0xFFFF;
|
|
20
|
+
|
|
21
|
+
/** both length nibbles saturate here, and continue in whole bytes */
|
|
22
|
+
const NIBBLE_MAX = 15;
|
|
23
|
+
|
|
24
|
+
/** a length continuation codes this much per byte, and a byte below it ends the run */
|
|
25
|
+
const CONTINUATION_MAX = 255;
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Literal runs shorter than this are cheaper to copy a byte at a time than to hand to `set`, whose
|
|
29
|
+
* call overhead dominates until there is a memmove's worth of work behind it. The high-compression
|
|
30
|
+
* encoder finds more matches than the fast one and so writes *shorter* literal runs on geometry —
|
|
31
|
+
* about 38 bytes a sequence against 62 — but on text it is the other way round, and the runs there
|
|
32
|
+
* are long enough that the call pays.
|
|
33
|
+
*/
|
|
34
|
+
const BULK_COPY_LENGTH = 32;
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* The match length past which the lazy parse stops trying to squeeze a second match in front of
|
|
38
|
+
* the one it has: the longest match whose length still fits the token's nibble.
|
|
39
|
+
*/
|
|
40
|
+
const OPTIMAL_ML = (NIBBLE_MAX - 1) + MIN_MATCH;
|
|
41
|
+
|
|
42
|
+
/** Knuth's multiplicative constant, which is what the reference encoder hashes with */
|
|
43
|
+
const HASH_MULTIPLIER = 2654435761;
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* The high-compression encoder hashes into a table twice the width of the fast one's, because it
|
|
47
|
+
* keeps a chain per slot rather than a single position and a collision costs a walk rather than a
|
|
48
|
+
* miss.
|
|
49
|
+
*/
|
|
50
|
+
const HASH_LOG = 15;
|
|
51
|
+
const HASH_SHIFT = 32 - HASH_LOG;
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Positions are held in an index space biased by one window, so that a zeroed hash slot reads as a
|
|
55
|
+
* position a whole window before the block — which is to say, as one no match may reach. That is
|
|
56
|
+
* what keeps blocks self-contained without a special case in the search: the "is this candidate
|
|
57
|
+
* inside the block" test the reference already needs is the same test that rejects an empty slot.
|
|
58
|
+
*/
|
|
59
|
+
const IDX_BASE = 0x10000;
|
|
60
|
+
|
|
61
|
+
/** how many attempts a search makes before pattern analysis is worth its own cost */
|
|
62
|
+
const PATTERN_ANALYSIS_THRESHOLD = 128;
|
|
63
|
+
|
|
64
|
+
/** positions the shortest path is solved over at once */
|
|
65
|
+
const OPT_NUM = 1 << 12;
|
|
66
|
+
|
|
67
|
+
/** positions past the last match a solved window also prices, as plain literals */
|
|
68
|
+
const TRAILING_LITERALS = 3;
|
|
69
|
+
|
|
70
|
+
/** the parse tables carry this many entries, which is the furthest either index above reaches */
|
|
71
|
+
const OPT_TABLE_SIZE = OPT_NUM + TRAILING_LITERALS;
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Whether the four bytes under the search are a repeat of a single byte, which is what makes a
|
|
75
|
+
* chain of consecutive positions worth jumping over rather than walking.
|
|
76
|
+
*/
|
|
77
|
+
const REPEAT_UNTESTED = 0;
|
|
78
|
+
const REPEAT_NOT = 1;
|
|
79
|
+
const REPEAT_CONFIRMED = 2;
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* What each level searches with, indexed by the level itself. The boundaries that matter are not
|
|
83
|
+
* the level numbers: the strategy changes at 10, and pattern analysis turns on above
|
|
84
|
+
* {@link PATTERN_ANALYSIS_THRESHOLD} attempts, which is what makes 9 the first level that has it.
|
|
85
|
+
*
|
|
86
|
+
* Entries for 0 and 2 are `null` — the reference gives level 2 different algorithms in different
|
|
87
|
+
* releases, so this encoder does not offer it. See {@link LZ4_LEVEL}.
|
|
88
|
+
*
|
|
89
|
+
* `attempts` is how many chain links a single search walks. `sufficient_length` is the match length
|
|
90
|
+
* at which a search stops looking for better and the parse takes what it has; it is also what keeps
|
|
91
|
+
* the optimal parse out of quadratic time on a run of one byte.
|
|
92
|
+
*
|
|
93
|
+
* @see https://github.com/lz4/lz4/blob/dev/lib/lz4hc.c — `k_clTable`
|
|
94
|
+
* @type {({attempts: number, sufficient_length: number, optimal: boolean, full_update: boolean}|null)[]}
|
|
95
|
+
*/
|
|
96
|
+
const LEVEL_TABLE = [
|
|
97
|
+
null,
|
|
98
|
+
null,
|
|
99
|
+
null,
|
|
100
|
+
{ attempts: 4, sufficient_length: 16, optimal: false, full_update: false },
|
|
101
|
+
{ attempts: 8, sufficient_length: 16, optimal: false, full_update: false },
|
|
102
|
+
{ attempts: 16, sufficient_length: 16, optimal: false, full_update: false },
|
|
103
|
+
{ attempts: 32, sufficient_length: 16, optimal: false, full_update: false },
|
|
104
|
+
{ attempts: 64, sufficient_length: 16, optimal: false, full_update: false },
|
|
105
|
+
{ attempts: 128, sufficient_length: 16, optimal: false, full_update: false },
|
|
106
|
+
{ attempts: 256, sufficient_length: 16, optimal: false, full_update: false },
|
|
107
|
+
{ attempts: 96, sufficient_length: 64, optimal: true, full_update: false },
|
|
108
|
+
{ attempts: 512, sufficient_length: 128, optimal: true, full_update: false },
|
|
109
|
+
{ attempts: 16384, sufficient_length: OPT_NUM, optimal: true, full_update: true }
|
|
110
|
+
];
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* The newest position carrying each four-byte hash, and, per position, the distance back to the
|
|
114
|
+
* previous position carrying the same one. Together they are the chain a search walks.
|
|
115
|
+
*
|
|
116
|
+
* Both are allocated on the first high-compression call rather than at module scope, and the parse
|
|
117
|
+
* tables below on the first call above level 9. meep ships raw source: a consumer that only ever
|
|
118
|
+
* *decodes* — which is what the runtime does — would otherwise hold a third of a megabyte of
|
|
119
|
+
* encoder tables for nothing.
|
|
120
|
+
*
|
|
121
|
+
* Module-static once taken, on the same terms as the fast encoder's table: this is straight-line
|
|
122
|
+
* synchronous code that clears what it needs on entry, so no second call can be part-way through.
|
|
123
|
+
*
|
|
124
|
+
* @type {Int32Array|null}
|
|
125
|
+
*/
|
|
126
|
+
let hash_table = null;
|
|
127
|
+
|
|
128
|
+
/** @type {Uint16Array|null} */
|
|
129
|
+
let chain_table = null;
|
|
130
|
+
|
|
131
|
+
/** the cheapest coding of the first `n` bytes found so far, and what the last step of it was */
|
|
132
|
+
/** @type {Int32Array|null} */
|
|
133
|
+
let opt_price = null;
|
|
134
|
+
/** @type {Int32Array|null} */
|
|
135
|
+
let opt_offset = null;
|
|
136
|
+
/** @type {Int32Array|null} */
|
|
137
|
+
let opt_match_length = null;
|
|
138
|
+
/** @type {Int32Array|null} */
|
|
139
|
+
let opt_literal_length = null;
|
|
140
|
+
|
|
141
|
+
/*
|
|
142
|
+
The call in progress. The reference carries these as a context struct and a pile of pointers; here
|
|
143
|
+
they are module bindings for the same reason the tables are, and with the same argument for why it
|
|
144
|
+
is safe. Positions below are *block* positions — zero is the first byte of the input — and the
|
|
145
|
+
index space the hash and chain tables are keyed in is that plus IDX_BASE.
|
|
146
|
+
*/
|
|
147
|
+
|
|
148
|
+
/** @type {Uint8Array} */
|
|
149
|
+
let source = null;
|
|
150
|
+
/** @type {DataView} */
|
|
151
|
+
let view = null;
|
|
152
|
+
/** @type {Uint8Array} */
|
|
153
|
+
let destination = null;
|
|
154
|
+
|
|
155
|
+
/** where block position zero sits in {@link source} */
|
|
156
|
+
let base = 0;
|
|
157
|
+
|
|
158
|
+
/** bytes of input */
|
|
159
|
+
let input_size = 0;
|
|
160
|
+
|
|
161
|
+
/** one past the last position a search may start at */
|
|
162
|
+
let match_find_limit = 0;
|
|
163
|
+
|
|
164
|
+
/** one past the last byte a match may cover */
|
|
165
|
+
let match_limit = 0;
|
|
166
|
+
|
|
167
|
+
/** positions below this are already on their chain */
|
|
168
|
+
let next_to_update = 0;
|
|
169
|
+
|
|
170
|
+
/** where the next output byte goes, and one past the last the block may write */
|
|
171
|
+
let out = 0;
|
|
172
|
+
let out_end = 0;
|
|
173
|
+
|
|
174
|
+
/*
|
|
175
|
+
What the last search found. The searches run thousands of times over a page and the reference
|
|
176
|
+
returns a three-field struct from each one; returning an object here would be that many allocations
|
|
177
|
+
for a value read immediately and never kept.
|
|
178
|
+
*/
|
|
179
|
+
|
|
180
|
+
/** the length of the match found, less than {@link MIN_MATCH} when there is none */
|
|
181
|
+
let found_length = 0;
|
|
182
|
+
|
|
183
|
+
/** how far back it reaches */
|
|
184
|
+
let found_offset = 0;
|
|
185
|
+
|
|
186
|
+
/** how far *before* the position searched from it starts, as a negative number */
|
|
187
|
+
let found_back = 0;
|
|
188
|
+
|
|
189
|
+
/** where {@link encode_window} left the parse, for the same reason */
|
|
190
|
+
let window_ip = 0;
|
|
191
|
+
let window_anchor = 0;
|
|
192
|
+
|
|
193
|
+
/**
|
|
194
|
+
* @param {number} sequence four bytes of input, read little-endian
|
|
195
|
+
* @returns {number} a slot in {@link hash_table}
|
|
196
|
+
*/
|
|
197
|
+
function hash_of(sequence) {
|
|
198
|
+
return Math.imul(sequence, HASH_MULTIPLIER) >>> HASH_SHIFT;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
/**
|
|
202
|
+
* Bytes a length continuation takes to carry `remainder`.
|
|
203
|
+
*
|
|
204
|
+
* @param {number} remainder what is left of a length once its nibble saturated
|
|
205
|
+
* @returns {number}
|
|
206
|
+
*/
|
|
207
|
+
function continuation_size(remainder) {
|
|
208
|
+
return 1 + ((remainder / CONTINUATION_MAX) | 0);
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
/**
|
|
212
|
+
* Write a length continuation: whole 255s, then a byte below 255 to end it.
|
|
213
|
+
*
|
|
214
|
+
* @param {number} d
|
|
215
|
+
* @param {number} remainder
|
|
216
|
+
* @returns {number} the offset just past the last byte written
|
|
217
|
+
*/
|
|
218
|
+
function write_continuation(d, remainder) {
|
|
219
|
+
let r = remainder;
|
|
220
|
+
let p = d;
|
|
221
|
+
|
|
222
|
+
while (r >= CONTINUATION_MAX) {
|
|
223
|
+
destination[p++] = CONTINUATION_MAX;
|
|
224
|
+
r -= CONTINUATION_MAX;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
destination[p++] = r;
|
|
228
|
+
|
|
229
|
+
return p;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
/**
|
|
233
|
+
* Two bytes of the block, with anything outside it reading as zero.
|
|
234
|
+
*
|
|
235
|
+
* This exists for one read: the candidate's half of the cheap test the search makes before a full
|
|
236
|
+
* compare, which looks at the two bytes where the *current best* match would end on either side.
|
|
237
|
+
* When a search is allowed to start behind the position it was called at — which is what the lazy
|
|
238
|
+
* parse's second and third searches do — the candidate's half is at `match - look_back + longest - 1`,
|
|
239
|
+
* and nothing in the arithmetic there stops it landing before the block. The reference reads
|
|
240
|
+
* whatever the buffer holds; a block that is a window into a larger buffer would then compress
|
|
241
|
+
* differently depending on what its caller happened to put in front of it, which is not a property
|
|
242
|
+
* an encoder is allowed to have. Reachable or not, it is cheap to make impossible, and the specs
|
|
243
|
+
* pin it by compressing the same payload at five offsets inside five different paddings.
|
|
244
|
+
*
|
|
245
|
+
* The *searched* side of that test needs no such guard: it sits at `low_limit + longest - 1`, and
|
|
246
|
+
* `longest` is never more than what a match starting at `low_limit` could cover, so it is inside
|
|
247
|
+
* the block by construction.
|
|
248
|
+
*
|
|
249
|
+
* @param {number} position a block position, which may be outside the block
|
|
250
|
+
* @returns {number}
|
|
251
|
+
*/
|
|
252
|
+
function read16_in_block(position) {
|
|
253
|
+
const low = (position >= 0 && position < input_size) ? source[base + position] : 0;
|
|
254
|
+
const high = (position + 1 >= 0 && position + 1 < input_size) ? source[base + position + 1] : 0;
|
|
255
|
+
|
|
256
|
+
return low | (high << 8);
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
/**
|
|
260
|
+
* How many bytes beyond the four already known to agree two positions agree on.
|
|
261
|
+
*
|
|
262
|
+
* @param {number} a the later position, past its first four bytes
|
|
263
|
+
* @param {number} b the earlier position, past its first four bytes
|
|
264
|
+
* @param {number} limit one past the last byte a match may cover
|
|
265
|
+
* @returns {number}
|
|
266
|
+
*/
|
|
267
|
+
function count_match(a, b, limit) {
|
|
268
|
+
let p = a;
|
|
269
|
+
let q = b;
|
|
270
|
+
|
|
271
|
+
while (p + 4 <= limit && view.getUint32(base + p, true) === view.getUint32(base + q, true)) {
|
|
272
|
+
p += 4;
|
|
273
|
+
q += 4;
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
while (p < limit && source[base + p] === source[base + q]) {
|
|
277
|
+
p++;
|
|
278
|
+
q++;
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
return p - a;
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
/**
|
|
285
|
+
* How many bytes *before* two positions agree, as a negative number, bounded on both sides.
|
|
286
|
+
*
|
|
287
|
+
* @param {number} ip the later position
|
|
288
|
+
* @param {number} match the earlier position
|
|
289
|
+
* @param {number} i_min the furthest back `ip` may walk
|
|
290
|
+
* @param {number} m_min the furthest back `match` may walk
|
|
291
|
+
* @returns {number} zero or less
|
|
292
|
+
*/
|
|
293
|
+
function count_back(ip, match, i_min, m_min) {
|
|
294
|
+
const a = i_min - ip;
|
|
295
|
+
const b = m_min - match;
|
|
296
|
+
const min = a > b ? a : b;
|
|
297
|
+
|
|
298
|
+
let back = 0;
|
|
299
|
+
|
|
300
|
+
while ((back - min) > 3) {
|
|
301
|
+
const v = view.getUint32(base + ip + back - 4, true) ^ view.getUint32(base + match + back - 4, true);
|
|
302
|
+
|
|
303
|
+
if (v !== 0) {
|
|
304
|
+
// the bytes nearest `back` are the high ones of a little-endian word, so the leading
|
|
305
|
+
// zero bytes of the difference are the ones that agreed
|
|
306
|
+
return back - (Math.clz32(v) >> 3);
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
back -= 4;
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
while (back > min && source[base + ip + back - 1] === source[base + match + back - 1]) {
|
|
313
|
+
back--;
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
return back;
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
/**
|
|
320
|
+
* How far a repeat of `pattern` runs forward from `position`.
|
|
321
|
+
*
|
|
322
|
+
* `pattern` is four bytes of a run whose period divides four — one byte, two bytes, or four — which
|
|
323
|
+
* is the only shape the caller ever offers it, and what makes reading it four bytes at a time
|
|
324
|
+
* rather than eight, as the reference does on a 64-bit word, give the same answer.
|
|
325
|
+
*
|
|
326
|
+
* @param {number} position
|
|
327
|
+
* @param {number} end one past the last byte it may look at
|
|
328
|
+
* @param {number} pattern
|
|
329
|
+
* @returns {number}
|
|
330
|
+
*/
|
|
331
|
+
function count_pattern(position, end, pattern) {
|
|
332
|
+
let p = position;
|
|
333
|
+
|
|
334
|
+
while (p < end - 3) {
|
|
335
|
+
const diff = view.getUint32(base + p, true) ^ pattern;
|
|
336
|
+
|
|
337
|
+
if (diff === 0) {
|
|
338
|
+
p += 4;
|
|
339
|
+
continue;
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
// the first byte that differs is the lowest one set, and the bytes below it agreed
|
|
343
|
+
return (p - position) + (lsb_32(diff) >> 3);
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
let shift = 0;
|
|
347
|
+
|
|
348
|
+
while (p < end && source[base + p] === ((pattern >>> shift) & 0xFF)) {
|
|
349
|
+
p++;
|
|
350
|
+
shift = (shift + 8) & 31;
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
return p - position;
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
/**
|
|
357
|
+
* How far a repeat of `pattern` runs *backwards* from `position`.
|
|
358
|
+
*
|
|
359
|
+
* @param {number} position
|
|
360
|
+
* @param {number} low the furthest back it may look
|
|
361
|
+
* @param {number} pattern
|
|
362
|
+
* @returns {number} zero or more
|
|
363
|
+
*/
|
|
364
|
+
function reverse_count_pattern(position, low, pattern) {
|
|
365
|
+
let p = position;
|
|
366
|
+
|
|
367
|
+
while (p >= low + 4) {
|
|
368
|
+
if (view.getUint32(base + p - 4, true) !== pattern) {
|
|
369
|
+
break;
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
p -= 4;
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
// the byte just before `p` is the pattern's last one, and walking back walks the pattern back
|
|
376
|
+
let shift = 24;
|
|
377
|
+
|
|
378
|
+
while (p > low && source[base + p - 1] === ((pattern >>> shift) & 0xFF)) {
|
|
379
|
+
p--;
|
|
380
|
+
shift -= 8;
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
return position - p;
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
/**
|
|
387
|
+
* Put every position below `position` on its chain.
|
|
388
|
+
*
|
|
389
|
+
* Lazily, from wherever the last search left off: a position is only ever wanted by a search that
|
|
390
|
+
* starts after it, so nothing is built that is never walked. The chain holds, per position, the
|
|
391
|
+
* *distance* back to the previous position with the same hash, which is what lets it be sixteen
|
|
392
|
+
* bits wide — a distance further than the window is one no match may use anyway, and saturating it
|
|
393
|
+
* there ends the walk exactly where the window does.
|
|
394
|
+
*
|
|
395
|
+
* @param {number} position a block position
|
|
396
|
+
*/
|
|
397
|
+
function insert(position) {
|
|
398
|
+
const target = position + IDX_BASE;
|
|
399
|
+
|
|
400
|
+
let idx = next_to_update;
|
|
401
|
+
|
|
402
|
+
while (idx < target) {
|
|
403
|
+
const h = hash_of(view.getUint32(base + idx - IDX_BASE, true));
|
|
404
|
+
|
|
405
|
+
let delta = idx - hash_table[h];
|
|
406
|
+
|
|
407
|
+
if (delta > MAX_DISTANCE) {
|
|
408
|
+
delta = MAX_DISTANCE;
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
chain_table[idx & 0xFFFF] = delta;
|
|
412
|
+
hash_table[h] = idx;
|
|
413
|
+
|
|
414
|
+
idx++;
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
next_to_update = target;
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
/**
|
|
421
|
+
* Walk the chain from `position` and leave the best match it holds in {@link found_length},
|
|
422
|
+
* {@link found_offset} and {@link found_back}.
|
|
423
|
+
*
|
|
424
|
+
* `low_limit` is where the match is allowed to *start*, which the caller sets behind `position`
|
|
425
|
+
* when it wants a match that reaches back over bytes an earlier one already covers; `found_back`
|
|
426
|
+
* comes back as how far behind `position` the match actually starts, and is zero or less. `longest`
|
|
427
|
+
* is what the caller already has, so a search that cannot beat it reports nothing.
|
|
428
|
+
*
|
|
429
|
+
* @param {number} position where the four bytes being matched are
|
|
430
|
+
* @param {number} low_limit the earliest position the match may start at
|
|
431
|
+
* @param {number} high_limit one past the last byte the match may cover
|
|
432
|
+
* @param {number} longest the length to beat
|
|
433
|
+
* @param {number} max_attempts how many links of the chain to walk
|
|
434
|
+
* @param {boolean} pattern_analysis whether to jump over runs rather than walk them
|
|
435
|
+
* @param {boolean} chain_swap whether to change chains on a tie
|
|
436
|
+
*/
|
|
437
|
+
function insert_and_get_wider_match(position, low_limit, high_limit, longest, max_attempts, pattern_analysis, chain_swap) {
|
|
438
|
+
// the call's context, read once: the loop below runs hundreds of millions of times over a
|
|
439
|
+
// conversion, and a module binding is not a local
|
|
440
|
+
const bytes = source;
|
|
441
|
+
const data = view;
|
|
442
|
+
const origin = base;
|
|
443
|
+
const length = input_size;
|
|
444
|
+
const chain = chain_table;
|
|
445
|
+
|
|
446
|
+
const ip_index = position + IDX_BASE;
|
|
447
|
+
|
|
448
|
+
// A match reaches back a window, and never before the block. Below one window in, those are the
|
|
449
|
+
// same bound, and the block's own start is what holds — which is also what makes an empty hash
|
|
450
|
+
// slot, reading as a position a window before the block, fail the test for free.
|
|
451
|
+
const lowest_match_index = (IDX_BASE + MAX_DISTANCE + 1) > ip_index ? IDX_BASE : ip_index - MAX_DISTANCE;
|
|
452
|
+
|
|
453
|
+
const look_back_length = position - low_limit;
|
|
454
|
+
const pattern = data.getUint32(origin + position, true);
|
|
455
|
+
|
|
456
|
+
let attempts = max_attempts;
|
|
457
|
+
let match_chain_pos = 0;
|
|
458
|
+
let repeat = REPEAT_UNTESTED;
|
|
459
|
+
let source_pattern_length = 0;
|
|
460
|
+
let offset = 0;
|
|
461
|
+
let s_back = 0;
|
|
462
|
+
|
|
463
|
+
insert(position);
|
|
464
|
+
|
|
465
|
+
let match_index = hash_table[hash_of(pattern)];
|
|
466
|
+
|
|
467
|
+
while (match_index >= lowest_match_index && attempts > 0) {
|
|
468
|
+
const match_position = match_index - IDX_BASE;
|
|
469
|
+
|
|
470
|
+
let match_length = 0;
|
|
471
|
+
|
|
472
|
+
attempts--;
|
|
473
|
+
|
|
474
|
+
// The cheap rejection: unless the two bytes where the current best match *ends* agree, no
|
|
475
|
+
// longer match can start here, and the full compare is not worth making. This is the
|
|
476
|
+
// hottest read in the encoder — once per link of every chain walked — so the candidate's
|
|
477
|
+
// side takes the guard only on the addresses that need it.
|
|
478
|
+
const gate = origin + low_limit + longest - 1;
|
|
479
|
+
const candidate_gate = match_position - look_back_length + longest - 1;
|
|
480
|
+
|
|
481
|
+
const candidate_bytes = (candidate_gate >= 0 && candidate_gate + 1 < length)
|
|
482
|
+
? (bytes[origin + candidate_gate] | (bytes[origin + candidate_gate + 1] << 8))
|
|
483
|
+
: read16_in_block(candidate_gate);
|
|
484
|
+
|
|
485
|
+
if ((bytes[gate] | (bytes[gate + 1] << 8)) === candidate_bytes) {
|
|
486
|
+
if (data.getUint32(origin + match_position, true) === pattern) {
|
|
487
|
+
const back = look_back_length !== 0 ? count_back(position, match_position, low_limit, 0) : 0;
|
|
488
|
+
|
|
489
|
+
match_length = MIN_MATCH + count_match(position + MIN_MATCH, match_position + MIN_MATCH, high_limit);
|
|
490
|
+
match_length -= back;
|
|
491
|
+
|
|
492
|
+
if (match_length > longest) {
|
|
493
|
+
longest = match_length;
|
|
494
|
+
offset = ip_index - match_index;
|
|
495
|
+
s_back = back;
|
|
496
|
+
}
|
|
497
|
+
}
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
// A tie means this chain has nothing more to offer. The positions inside the match have
|
|
501
|
+
// chains of their own, and the one that steps furthest back is the one worth following.
|
|
502
|
+
if (chain_swap && match_length === longest && match_index + longest <= ip_index) {
|
|
503
|
+
const end = longest - MIN_MATCH + 1;
|
|
504
|
+
|
|
505
|
+
let distance_to_next_match = 1;
|
|
506
|
+
let step = 1;
|
|
507
|
+
let accel = 1 << 4;
|
|
508
|
+
|
|
509
|
+
for (let p = 0; p < end; p += step) {
|
|
510
|
+
const candidate_distance = chain[(match_index + p) & 0xFFFF];
|
|
511
|
+
|
|
512
|
+
step = accel >> 4;
|
|
513
|
+
accel++;
|
|
514
|
+
|
|
515
|
+
if (candidate_distance > distance_to_next_match) {
|
|
516
|
+
distance_to_next_match = candidate_distance;
|
|
517
|
+
match_chain_pos = p;
|
|
518
|
+
accel = 1 << 4;
|
|
519
|
+
}
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
if (distance_to_next_match > 1) {
|
|
523
|
+
if (distance_to_next_match > match_index) {
|
|
524
|
+
break;
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
match_index -= distance_to_next_match;
|
|
528
|
+
continue;
|
|
529
|
+
}
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
// Consecutive positions on one chain are a run of the same byte. Walking it link by link
|
|
533
|
+
// is what takes the wider levels quadratic on a page of zeros; measuring the run instead
|
|
534
|
+
// and jumping to the best place in it is what makes them finish at all. It does not make
|
|
535
|
+
// them *fast*: the reference measures pattern analysis as paying for its own cost by level
|
|
536
|
+
// 9, on an eight-byte scan this port does at four, and here level 9 still comes out about
|
|
537
|
+
// 5% slower than level 3 on real pages.
|
|
538
|
+
if (pattern_analysis && chain[match_index & 0xFFFF] === 1 && match_chain_pos === 0) {
|
|
539
|
+
const candidate_index = match_index - 1;
|
|
540
|
+
|
|
541
|
+
if (repeat === REPEAT_UNTESTED) {
|
|
542
|
+
if (((pattern & 0xFFFF) === (pattern >>> 16)) && ((pattern & 0xFF) === (pattern >>> 24))) {
|
|
543
|
+
repeat = REPEAT_CONFIRMED;
|
|
544
|
+
source_pattern_length = count_pattern(position + 4, high_limit, pattern) + 4;
|
|
545
|
+
} else {
|
|
546
|
+
repeat = REPEAT_NOT;
|
|
547
|
+
}
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
if (repeat === REPEAT_CONFIRMED && candidate_index >= lowest_match_index) {
|
|
551
|
+
const candidate_position = candidate_index - IDX_BASE;
|
|
552
|
+
|
|
553
|
+
if (data.getUint32(origin + candidate_position, true) === pattern) {
|
|
554
|
+
const forward_length = count_pattern(candidate_position + 4, high_limit, pattern) + 4;
|
|
555
|
+
|
|
556
|
+
const raw_back = reverse_count_pattern(candidate_position, 0, pattern);
|
|
557
|
+
const floor = candidate_index - raw_back > lowest_match_index
|
|
558
|
+
? candidate_index - raw_back
|
|
559
|
+
: lowest_match_index;
|
|
560
|
+
const back_length = candidate_index - floor;
|
|
561
|
+
|
|
562
|
+
const segment_length = back_length + forward_length;
|
|
563
|
+
|
|
564
|
+
if (segment_length >= source_pattern_length && forward_length <= source_pattern_length) {
|
|
565
|
+
// the run here is long enough to hold the whole of the run at `position`,
|
|
566
|
+
// and this is where the two line up
|
|
567
|
+
match_index = candidate_index + forward_length - source_pattern_length;
|
|
568
|
+
} else {
|
|
569
|
+
// otherwise take the far end of the run, which is the longest match in it
|
|
570
|
+
match_index = floor;
|
|
571
|
+
|
|
572
|
+
if (look_back_length === 0) {
|
|
573
|
+
const max_length = segment_length < source_pattern_length
|
|
574
|
+
? segment_length
|
|
575
|
+
: source_pattern_length;
|
|
576
|
+
|
|
577
|
+
if (longest < max_length) {
|
|
578
|
+
if (ip_index - match_index > MAX_DISTANCE) {
|
|
579
|
+
break;
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
longest = max_length;
|
|
583
|
+
offset = ip_index - match_index;
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
const distance_to_next_pattern = chain[match_index & 0xFFFF];
|
|
587
|
+
|
|
588
|
+
if (distance_to_next_pattern > match_index) {
|
|
589
|
+
break;
|
|
590
|
+
}
|
|
591
|
+
|
|
592
|
+
match_index -= distance_to_next_pattern;
|
|
593
|
+
}
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
continue;
|
|
597
|
+
}
|
|
598
|
+
}
|
|
599
|
+
}
|
|
600
|
+
|
|
601
|
+
match_index -= chain[(match_index + match_chain_pos) & 0xFFFF];
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
found_length = longest;
|
|
605
|
+
found_offset = offset;
|
|
606
|
+
found_back = s_back;
|
|
607
|
+
}
|
|
608
|
+
|
|
609
|
+
/**
|
|
610
|
+
* The same search, made from the position the match must start at — so it cannot reach behind, and
|
|
611
|
+
* {@link found_back} comes back zero.
|
|
612
|
+
*
|
|
613
|
+
* @param {number} position
|
|
614
|
+
* @param {number} high_limit
|
|
615
|
+
* @param {number} max_attempts
|
|
616
|
+
* @param {boolean} pattern_analysis
|
|
617
|
+
*/
|
|
618
|
+
function insert_and_find_best_match(position, high_limit, max_attempts, pattern_analysis) {
|
|
619
|
+
insert_and_get_wider_match(position, position, high_limit, MIN_MATCH - 1, max_attempts, pattern_analysis, false);
|
|
620
|
+
}
|
|
621
|
+
|
|
622
|
+
/**
|
|
623
|
+
* Write one sequence: the token, the literal run in front of the match, the match's offset, and
|
|
624
|
+
* whatever its length spilled into.
|
|
625
|
+
*
|
|
626
|
+
* Where the fast encoder splits this — it knows a match's length only after it has written the
|
|
627
|
+
* token the length shares with the literal count — the high-compression parses hand a whole
|
|
628
|
+
* sequence over at once, so it is written as one unit and bounds-checked at the two points the
|
|
629
|
+
* output can actually run out.
|
|
630
|
+
*
|
|
631
|
+
* @param {number} anchor the first literal
|
|
632
|
+
* @param {number} position one past the last literal, and the first byte of the match
|
|
633
|
+
* @param {number} match_length
|
|
634
|
+
* @param {number} offset how far back the match reaches
|
|
635
|
+
* @throws {Error} if the sequence does not fit the space given
|
|
636
|
+
*/
|
|
637
|
+
function encode_sequence(anchor, position, match_length, offset) {
|
|
638
|
+
const literal_length = position - anchor;
|
|
639
|
+
const literal_saturated = literal_length >= NIBBLE_MAX;
|
|
640
|
+
const coded_match_length = match_length - MIN_MATCH;
|
|
641
|
+
const match_saturated = coded_match_length >= NIBBLE_MAX;
|
|
642
|
+
|
|
643
|
+
let required = 1 + literal_length;
|
|
644
|
+
|
|
645
|
+
if (literal_saturated) {
|
|
646
|
+
required += continuation_size(literal_length - NIBBLE_MAX);
|
|
647
|
+
}
|
|
648
|
+
|
|
649
|
+
if (out + required > out_end) {
|
|
650
|
+
throw new Error(
|
|
651
|
+
`lz4 sequence of ${literal_length} literals needs ${required} bytes, and only ${out_end - out} are left`
|
|
652
|
+
);
|
|
653
|
+
}
|
|
654
|
+
|
|
655
|
+
const token = out++;
|
|
656
|
+
|
|
657
|
+
destination[token] = ((literal_saturated ? NIBBLE_MAX : literal_length) << 4)
|
|
658
|
+
| (match_saturated ? NIBBLE_MAX : coded_match_length);
|
|
659
|
+
|
|
660
|
+
if (literal_saturated) {
|
|
661
|
+
out = write_continuation(out, literal_length - NIBBLE_MAX);
|
|
662
|
+
}
|
|
663
|
+
|
|
664
|
+
if (literal_length >= BULK_COPY_LENGTH) {
|
|
665
|
+
destination.set(source.subarray(base + anchor, base + anchor + literal_length), out);
|
|
666
|
+
} else {
|
|
667
|
+
for (let i = 0; i < literal_length; i++) {
|
|
668
|
+
destination[out + i] = source[base + anchor + i];
|
|
669
|
+
}
|
|
670
|
+
}
|
|
671
|
+
|
|
672
|
+
out += literal_length;
|
|
673
|
+
|
|
674
|
+
let match_required = 2;
|
|
675
|
+
|
|
676
|
+
if (match_saturated) {
|
|
677
|
+
match_required += continuation_size(coded_match_length - NIBBLE_MAX);
|
|
678
|
+
}
|
|
679
|
+
|
|
680
|
+
if (out + match_required > out_end) {
|
|
681
|
+
throw new Error(
|
|
682
|
+
`lz4 match of ${match_length} needs ${match_required} bytes, and only ${out_end - out} are left`
|
|
683
|
+
);
|
|
684
|
+
}
|
|
685
|
+
|
|
686
|
+
destination[out++] = offset & 0xFF;
|
|
687
|
+
destination[out++] = (offset >> 8) & 0xFF;
|
|
688
|
+
|
|
689
|
+
if (match_saturated) {
|
|
690
|
+
out = write_continuation(out, coded_match_length - NIBBLE_MAX);
|
|
691
|
+
}
|
|
692
|
+
}
|
|
693
|
+
|
|
694
|
+
/**
|
|
695
|
+
* Close the block with its trailing literals, and no match behind them.
|
|
696
|
+
*
|
|
697
|
+
* @param {number} anchor
|
|
698
|
+
* @param {number} end
|
|
699
|
+
* @throws {Error} if they do not fit the space given
|
|
700
|
+
*/
|
|
701
|
+
function write_last_literals(anchor, end) {
|
|
702
|
+
const literal_length = end - anchor;
|
|
703
|
+
const saturated = literal_length >= NIBBLE_MAX;
|
|
704
|
+
|
|
705
|
+
let required = 1 + literal_length;
|
|
706
|
+
|
|
707
|
+
if (saturated) {
|
|
708
|
+
required += continuation_size(literal_length - NIBBLE_MAX);
|
|
709
|
+
}
|
|
710
|
+
|
|
711
|
+
if (out + required > out_end) {
|
|
712
|
+
throw new Error(
|
|
713
|
+
`lz4 sequence of ${literal_length} literals needs ${required} bytes, and only ${out_end - out} are left`
|
|
714
|
+
);
|
|
715
|
+
}
|
|
716
|
+
|
|
717
|
+
destination[out++] = (saturated ? NIBBLE_MAX : literal_length) << 4;
|
|
718
|
+
|
|
719
|
+
if (saturated) {
|
|
720
|
+
out = write_continuation(out, literal_length - NIBBLE_MAX);
|
|
721
|
+
}
|
|
722
|
+
|
|
723
|
+
if (literal_length >= BULK_COPY_LENGTH) {
|
|
724
|
+
destination.set(source.subarray(base + anchor, base + anchor + literal_length), out);
|
|
725
|
+
} else {
|
|
726
|
+
for (let i = 0; i < literal_length; i++) {
|
|
727
|
+
destination[out + i] = source[base + anchor + i];
|
|
728
|
+
}
|
|
729
|
+
}
|
|
730
|
+
|
|
731
|
+
out += literal_length;
|
|
732
|
+
}
|
|
733
|
+
|
|
734
|
+
/**
|
|
735
|
+
* Levels 3 to 9: walk the chain, and parse lazily.
|
|
736
|
+
*
|
|
737
|
+
* The parse holds up to three matches at once — the one in hand, one starting two bytes before it
|
|
738
|
+
* ends, and one starting three bytes before *that* ends — and trims the earlier ones against the
|
|
739
|
+
* later so that a long match is never cut short by a short one that happened to be found first.
|
|
740
|
+
* Where they overlap it is the *bytes* that decide, not the order: a match is shortened only as far
|
|
741
|
+
* as the one behind it can pay for.
|
|
742
|
+
*
|
|
743
|
+
* @param {number} max_attempts
|
|
744
|
+
* @param {boolean} pattern_analysis
|
|
745
|
+
*/
|
|
746
|
+
function compress_hash_chain(max_attempts, pattern_analysis) {
|
|
747
|
+
let ip = 0;
|
|
748
|
+
let anchor = 0;
|
|
749
|
+
|
|
750
|
+
let start0 = 0;
|
|
751
|
+
let start2 = 0;
|
|
752
|
+
let start3 = 0;
|
|
753
|
+
|
|
754
|
+
let m0_length = 0;
|
|
755
|
+
let m0_offset = 0;
|
|
756
|
+
let m1_length = 0;
|
|
757
|
+
let m1_offset = 0;
|
|
758
|
+
let m2_length = 0;
|
|
759
|
+
let m2_offset = 0;
|
|
760
|
+
let m3_length = 0;
|
|
761
|
+
let m3_offset = 0;
|
|
762
|
+
|
|
763
|
+
main: while (ip <= match_find_limit) {
|
|
764
|
+
insert_and_find_best_match(ip, match_limit, max_attempts, pattern_analysis);
|
|
765
|
+
|
|
766
|
+
m1_length = found_length;
|
|
767
|
+
m1_offset = found_offset;
|
|
768
|
+
|
|
769
|
+
if (m1_length < MIN_MATCH) {
|
|
770
|
+
ip++;
|
|
771
|
+
continue;
|
|
772
|
+
}
|
|
773
|
+
|
|
774
|
+
// kept in case the search below walks so far forward that the first match is worth going
|
|
775
|
+
// back for
|
|
776
|
+
start0 = ip;
|
|
777
|
+
m0_length = m1_length;
|
|
778
|
+
m0_offset = m1_offset;
|
|
779
|
+
|
|
780
|
+
search2: for (;;) {
|
|
781
|
+
if (ip + m1_length <= match_find_limit) {
|
|
782
|
+
start2 = ip + m1_length - 2;
|
|
783
|
+
|
|
784
|
+
insert_and_get_wider_match(start2, ip, match_limit, m1_length, max_attempts, pattern_analysis, false);
|
|
785
|
+
|
|
786
|
+
m2_length = found_length;
|
|
787
|
+
m2_offset = found_offset;
|
|
788
|
+
start2 += found_back;
|
|
789
|
+
} else {
|
|
790
|
+
m2_length = 0;
|
|
791
|
+
m2_offset = 0;
|
|
792
|
+
}
|
|
793
|
+
|
|
794
|
+
if (m2_length <= m1_length) {
|
|
795
|
+
encode_sequence(anchor, ip, m1_length, m1_offset);
|
|
796
|
+
|
|
797
|
+
ip += m1_length;
|
|
798
|
+
anchor = ip;
|
|
799
|
+
|
|
800
|
+
continue main;
|
|
801
|
+
}
|
|
802
|
+
|
|
803
|
+
if (start0 < ip && start2 < ip + m0_length) {
|
|
804
|
+
// squeezing this match between the one it replaced and the better one found past
|
|
805
|
+
// it costs more than going back and taking the first one
|
|
806
|
+
ip = start0;
|
|
807
|
+
m1_length = m0_length;
|
|
808
|
+
m1_offset = m0_offset;
|
|
809
|
+
}
|
|
810
|
+
|
|
811
|
+
if ((start2 - ip) < 3) {
|
|
812
|
+
// nothing would be left of the first match, so it goes
|
|
813
|
+
ip = start2;
|
|
814
|
+
m1_length = m2_length;
|
|
815
|
+
m1_offset = m2_offset;
|
|
816
|
+
|
|
817
|
+
continue search2;
|
|
818
|
+
}
|
|
819
|
+
|
|
820
|
+
search3: for (;;) {
|
|
821
|
+
if ((start2 - ip) < OPTIMAL_ML) {
|
|
822
|
+
let new_length = m1_length;
|
|
823
|
+
|
|
824
|
+
if (new_length > OPTIMAL_ML) {
|
|
825
|
+
new_length = OPTIMAL_ML;
|
|
826
|
+
}
|
|
827
|
+
|
|
828
|
+
if (ip + new_length > start2 + m2_length - MIN_MATCH) {
|
|
829
|
+
new_length = (start2 - ip) + m2_length - MIN_MATCH;
|
|
830
|
+
}
|
|
831
|
+
|
|
832
|
+
const correction = new_length - (start2 - ip);
|
|
833
|
+
|
|
834
|
+
if (correction > 0) {
|
|
835
|
+
start2 += correction;
|
|
836
|
+
m2_length -= correction;
|
|
837
|
+
}
|
|
838
|
+
}
|
|
839
|
+
|
|
840
|
+
if (start2 + m2_length <= match_find_limit) {
|
|
841
|
+
start3 = start2 + m2_length - 3;
|
|
842
|
+
|
|
843
|
+
insert_and_get_wider_match(start3, start2, match_limit, m2_length, max_attempts, pattern_analysis, false);
|
|
844
|
+
|
|
845
|
+
m3_length = found_length;
|
|
846
|
+
m3_offset = found_offset;
|
|
847
|
+
start3 += found_back;
|
|
848
|
+
} else {
|
|
849
|
+
m3_length = 0;
|
|
850
|
+
m3_offset = 0;
|
|
851
|
+
}
|
|
852
|
+
|
|
853
|
+
if (m3_length <= m2_length) {
|
|
854
|
+
// two matches, and nothing better behind them
|
|
855
|
+
if (start2 < ip + m1_length) {
|
|
856
|
+
m1_length = start2 - ip;
|
|
857
|
+
}
|
|
858
|
+
|
|
859
|
+
encode_sequence(anchor, ip, m1_length, m1_offset);
|
|
860
|
+
|
|
861
|
+
ip += m1_length;
|
|
862
|
+
anchor = ip;
|
|
863
|
+
|
|
864
|
+
ip = start2;
|
|
865
|
+
|
|
866
|
+
encode_sequence(anchor, ip, m2_length, m2_offset);
|
|
867
|
+
|
|
868
|
+
ip += m2_length;
|
|
869
|
+
anchor = ip;
|
|
870
|
+
|
|
871
|
+
continue main;
|
|
872
|
+
}
|
|
873
|
+
|
|
874
|
+
if (start3 < ip + m1_length + 3) {
|
|
875
|
+
// no room for the middle match
|
|
876
|
+
if (start3 >= ip + m1_length) {
|
|
877
|
+
if (start2 < ip + m1_length) {
|
|
878
|
+
const correction = (ip + m1_length) - start2;
|
|
879
|
+
|
|
880
|
+
start2 += correction;
|
|
881
|
+
m2_length -= correction;
|
|
882
|
+
|
|
883
|
+
if (m2_length < MIN_MATCH) {
|
|
884
|
+
start2 = start3;
|
|
885
|
+
m2_length = m3_length;
|
|
886
|
+
m2_offset = m3_offset;
|
|
887
|
+
}
|
|
888
|
+
}
|
|
889
|
+
|
|
890
|
+
encode_sequence(anchor, ip, m1_length, m1_offset);
|
|
891
|
+
|
|
892
|
+
ip += m1_length;
|
|
893
|
+
anchor = ip;
|
|
894
|
+
|
|
895
|
+
ip = start3;
|
|
896
|
+
m1_length = m3_length;
|
|
897
|
+
m1_offset = m3_offset;
|
|
898
|
+
|
|
899
|
+
start0 = start2;
|
|
900
|
+
m0_length = m2_length;
|
|
901
|
+
m0_offset = m2_offset;
|
|
902
|
+
|
|
903
|
+
continue search2;
|
|
904
|
+
}
|
|
905
|
+
|
|
906
|
+
start2 = start3;
|
|
907
|
+
m2_length = m3_length;
|
|
908
|
+
m2_offset = m3_offset;
|
|
909
|
+
|
|
910
|
+
continue search3;
|
|
911
|
+
}
|
|
912
|
+
|
|
913
|
+
// three matches in a row: write the first, and slide the other two down
|
|
914
|
+
if (start2 < ip + m1_length) {
|
|
915
|
+
if ((start2 - ip) < OPTIMAL_ML) {
|
|
916
|
+
if (m1_length > OPTIMAL_ML) {
|
|
917
|
+
m1_length = OPTIMAL_ML;
|
|
918
|
+
}
|
|
919
|
+
|
|
920
|
+
if (ip + m1_length > start2 + m2_length - MIN_MATCH) {
|
|
921
|
+
m1_length = (start2 - ip) + m2_length - MIN_MATCH;
|
|
922
|
+
}
|
|
923
|
+
|
|
924
|
+
const correction = m1_length - (start2 - ip);
|
|
925
|
+
|
|
926
|
+
if (correction > 0) {
|
|
927
|
+
start2 += correction;
|
|
928
|
+
m2_length -= correction;
|
|
929
|
+
}
|
|
930
|
+
} else {
|
|
931
|
+
m1_length = start2 - ip;
|
|
932
|
+
}
|
|
933
|
+
}
|
|
934
|
+
|
|
935
|
+
encode_sequence(anchor, ip, m1_length, m1_offset);
|
|
936
|
+
|
|
937
|
+
ip += m1_length;
|
|
938
|
+
anchor = ip;
|
|
939
|
+
|
|
940
|
+
ip = start2;
|
|
941
|
+
m1_length = m2_length;
|
|
942
|
+
m1_offset = m2_offset;
|
|
943
|
+
|
|
944
|
+
start2 = start3;
|
|
945
|
+
m2_length = m3_length;
|
|
946
|
+
m2_offset = m3_offset;
|
|
947
|
+
|
|
948
|
+
continue search3;
|
|
949
|
+
}
|
|
950
|
+
}
|
|
951
|
+
}
|
|
952
|
+
|
|
953
|
+
write_last_literals(anchor, input_size);
|
|
954
|
+
}
|
|
955
|
+
|
|
956
|
+
/**
|
|
957
|
+
* What a literal run costs in bytes: the literals, and the continuation its count spills into.
|
|
958
|
+
*
|
|
959
|
+
* @param {number} length
|
|
960
|
+
* @returns {number}
|
|
961
|
+
*/
|
|
962
|
+
function literals_price(length) {
|
|
963
|
+
let price = length;
|
|
964
|
+
|
|
965
|
+
if (length >= NIBBLE_MAX) {
|
|
966
|
+
price += 1 + (((length - NIBBLE_MAX) / CONTINUATION_MAX) | 0);
|
|
967
|
+
}
|
|
968
|
+
|
|
969
|
+
return price;
|
|
970
|
+
}
|
|
971
|
+
|
|
972
|
+
/**
|
|
973
|
+
* What a whole sequence costs in bytes: a token, a sixteen-bit offset, the literals in front of it,
|
|
974
|
+
* and the continuation the match length spills into.
|
|
975
|
+
*
|
|
976
|
+
* @param {number} literal_length
|
|
977
|
+
* @param {number} match_length at least {@link MIN_MATCH}
|
|
978
|
+
* @returns {number}
|
|
979
|
+
*/
|
|
980
|
+
function sequence_price(literal_length, match_length) {
|
|
981
|
+
let price = 1 + 2 + literals_price(literal_length);
|
|
982
|
+
|
|
983
|
+
if (match_length >= NIBBLE_MAX + MIN_MATCH) {
|
|
984
|
+
price += 1 + (((match_length - (NIBBLE_MAX + MIN_MATCH)) / CONTINUATION_MAX) | 0);
|
|
985
|
+
}
|
|
986
|
+
|
|
987
|
+
return price;
|
|
988
|
+
}
|
|
989
|
+
|
|
990
|
+
/**
|
|
991
|
+
* The search the optimal parse makes: from the position itself, so the match cannot reach behind,
|
|
992
|
+
* and reporting nothing at all unless it beats `min_length` outright.
|
|
993
|
+
*
|
|
994
|
+
* @param {number} position
|
|
995
|
+
* @param {number} high_limit
|
|
996
|
+
* @param {number} min_length the length to beat, which a caller that has priced everything up to
|
|
997
|
+
* some point sets to what is already covered rather than to the format's
|
|
998
|
+
* minimum — a shorter match there cannot change any price
|
|
999
|
+
* @param {number} max_attempts
|
|
1000
|
+
*/
|
|
1001
|
+
function find_longer_match(position, high_limit, min_length, max_attempts) {
|
|
1002
|
+
insert_and_get_wider_match(position, position, high_limit, min_length, max_attempts, true, true);
|
|
1003
|
+
|
|
1004
|
+
if (found_length <= min_length) {
|
|
1005
|
+
found_length = 0;
|
|
1006
|
+
found_offset = 0;
|
|
1007
|
+
}
|
|
1008
|
+
}
|
|
1009
|
+
|
|
1010
|
+
/**
|
|
1011
|
+
* Recover the path through a solved window, write the sequences on it, and leave the parse just
|
|
1012
|
+
* past the last of them in {@link window_ip} and {@link window_anchor}.
|
|
1013
|
+
*
|
|
1014
|
+
* The table holds, at each position, the step that arrives there most cheaply — which is a step
|
|
1015
|
+
* read *backwards*. Walking those steps from the end of the path rewrites each entry as the step
|
|
1016
|
+
* that *leaves* it, and the same table is then the path read forwards.
|
|
1017
|
+
*
|
|
1018
|
+
* @param {number} ip where the window starts
|
|
1019
|
+
* @param {number} anchor the first literal not yet written
|
|
1020
|
+
* @param {number} from the position the last sequence on the path starts at
|
|
1021
|
+
* @param {number} last_match_pos one past the last position the path covers
|
|
1022
|
+
* @param {number} best_length the last sequence's match length
|
|
1023
|
+
* @param {number} best_offset the last sequence's offset
|
|
1024
|
+
*/
|
|
1025
|
+
function encode_window(ip, anchor, from, last_match_pos, best_length, best_offset) {
|
|
1026
|
+
let candidate = from;
|
|
1027
|
+
let length = best_length;
|
|
1028
|
+
let offset = best_offset;
|
|
1029
|
+
|
|
1030
|
+
for (;;) {
|
|
1031
|
+
const next_length = opt_match_length[candidate];
|
|
1032
|
+
const next_offset = opt_offset[candidate];
|
|
1033
|
+
|
|
1034
|
+
opt_match_length[candidate] = length;
|
|
1035
|
+
opt_offset[candidate] = offset;
|
|
1036
|
+
|
|
1037
|
+
length = next_length;
|
|
1038
|
+
offset = next_offset;
|
|
1039
|
+
|
|
1040
|
+
if (next_length > candidate) {
|
|
1041
|
+
// the step that arrives here reaches back past the start of the window, so it is the
|
|
1042
|
+
// first sequence of the path and there is nothing behind it
|
|
1043
|
+
break;
|
|
1044
|
+
}
|
|
1045
|
+
|
|
1046
|
+
candidate -= next_length;
|
|
1047
|
+
}
|
|
1048
|
+
|
|
1049
|
+
let p = ip;
|
|
1050
|
+
let a = anchor;
|
|
1051
|
+
let r = 0;
|
|
1052
|
+
|
|
1053
|
+
while (r < last_match_pos) {
|
|
1054
|
+
const match_length = opt_match_length[r];
|
|
1055
|
+
|
|
1056
|
+
// a literal, and possibly several: the path steps over them one at a time
|
|
1057
|
+
if (match_length === 1) {
|
|
1058
|
+
p++;
|
|
1059
|
+
r++;
|
|
1060
|
+
continue;
|
|
1061
|
+
}
|
|
1062
|
+
|
|
1063
|
+
encode_sequence(a, p, match_length, opt_offset[r]);
|
|
1064
|
+
|
|
1065
|
+
p += match_length;
|
|
1066
|
+
a = p;
|
|
1067
|
+
r += match_length;
|
|
1068
|
+
}
|
|
1069
|
+
|
|
1070
|
+
window_ip = p;
|
|
1071
|
+
window_anchor = a;
|
|
1072
|
+
}
|
|
1073
|
+
|
|
1074
|
+
/**
|
|
1075
|
+
* Levels 10 to 12: solve the parse rather than guess it.
|
|
1076
|
+
*
|
|
1077
|
+
* Over a window of the block, the cheapest coding of the first `n` bytes is built from the cheapest
|
|
1078
|
+
* coding of every shorter prefix, taking a match found at each position as one edge and a literal
|
|
1079
|
+
* as another. The price is in *bytes*, exactly — a token, a two-byte offset, and the continuation
|
|
1080
|
+
* bytes each saturated length field spills into — so this is a genuine shortest path and not a
|
|
1081
|
+
* heuristic with a cost model bolted on. The window is walked back from its end to recover the
|
|
1082
|
+
* path, and the sequences on it are written in order.
|
|
1083
|
+
*
|
|
1084
|
+
* What stops it going quadratic is `sufficient_length`: a match at least that long is taken and
|
|
1085
|
+
* encoded on the spot, with no path solved through it at all. Level 12 raises that to the window
|
|
1086
|
+
* itself and searches every position rather than only those whose price rises, which is what the
|
|
1087
|
+
* last two ten-thousandths of a percent cost — and why an encoder that drops the early exit
|
|
1088
|
+
* entirely, which was measured, spends a hundred times level 12's budget to find nothing.
|
|
1089
|
+
*
|
|
1090
|
+
* @param {number} max_attempts
|
|
1091
|
+
* @param {number} sufficient_length
|
|
1092
|
+
* @param {boolean} full_update
|
|
1093
|
+
*/
|
|
1094
|
+
function compress_optimal(max_attempts, sufficient_length, full_update) {
|
|
1095
|
+
// the window is what the tables hold, so nothing may be priced past it
|
|
1096
|
+
const enough = sufficient_length >= OPT_NUM ? OPT_NUM - 1 : sufficient_length;
|
|
1097
|
+
|
|
1098
|
+
let ip = 0;
|
|
1099
|
+
let anchor = 0;
|
|
1100
|
+
|
|
1101
|
+
while (ip <= match_find_limit) {
|
|
1102
|
+
const literal_run = ip - anchor;
|
|
1103
|
+
|
|
1104
|
+
find_longer_match(ip, match_limit, MIN_MATCH - 1, max_attempts);
|
|
1105
|
+
|
|
1106
|
+
if (found_length === 0) {
|
|
1107
|
+
ip++;
|
|
1108
|
+
continue;
|
|
1109
|
+
}
|
|
1110
|
+
|
|
1111
|
+
const first_length = found_length;
|
|
1112
|
+
const first_offset = found_offset;
|
|
1113
|
+
|
|
1114
|
+
if (first_length > enough) {
|
|
1115
|
+
// good enough to take on sight
|
|
1116
|
+
encode_sequence(anchor, ip, first_length, first_offset);
|
|
1117
|
+
|
|
1118
|
+
ip += first_length;
|
|
1119
|
+
anchor = ip;
|
|
1120
|
+
|
|
1121
|
+
continue;
|
|
1122
|
+
}
|
|
1123
|
+
|
|
1124
|
+
// the first few positions, as literals carrying on from the run already open
|
|
1125
|
+
for (let r = 0; r < MIN_MATCH; r++) {
|
|
1126
|
+
opt_match_length[r] = 1;
|
|
1127
|
+
opt_offset[r] = 0;
|
|
1128
|
+
opt_literal_length[r] = literal_run + r;
|
|
1129
|
+
opt_price[r] = literals_price(literal_run + r);
|
|
1130
|
+
}
|
|
1131
|
+
|
|
1132
|
+
// and every length the first match can be cut to
|
|
1133
|
+
for (let length = MIN_MATCH; length <= first_length; length++) {
|
|
1134
|
+
opt_match_length[length] = length;
|
|
1135
|
+
opt_offset[length] = first_offset;
|
|
1136
|
+
opt_literal_length[length] = literal_run;
|
|
1137
|
+
opt_price[length] = sequence_price(literal_run, length);
|
|
1138
|
+
}
|
|
1139
|
+
|
|
1140
|
+
let last_match_pos = first_length;
|
|
1141
|
+
let best_length = 0;
|
|
1142
|
+
let best_offset = 0;
|
|
1143
|
+
let from = 0;
|
|
1144
|
+
let solved = false;
|
|
1145
|
+
|
|
1146
|
+
for (let add = 1; add <= TRAILING_LITERALS; add++) {
|
|
1147
|
+
opt_match_length[last_match_pos + add] = 1;
|
|
1148
|
+
opt_offset[last_match_pos + add] = 0;
|
|
1149
|
+
opt_literal_length[last_match_pos + add] = add;
|
|
1150
|
+
opt_price[last_match_pos + add] = opt_price[last_match_pos] + literals_price(add);
|
|
1151
|
+
}
|
|
1152
|
+
|
|
1153
|
+
for (let cur = 1; cur < last_match_pos; cur++) {
|
|
1154
|
+
const position = ip + cur;
|
|
1155
|
+
|
|
1156
|
+
if (position > match_find_limit) {
|
|
1157
|
+
break;
|
|
1158
|
+
}
|
|
1159
|
+
|
|
1160
|
+
if (full_update) {
|
|
1161
|
+
// A position whose successor already codes at least as cheaply is one nothing can
|
|
1162
|
+
// be gained at — unless the price climbs sharply just past it, where even the
|
|
1163
|
+
// shortest sequence there is would pay for itself.
|
|
1164
|
+
if (
|
|
1165
|
+
opt_price[cur + 1] <= opt_price[cur]
|
|
1166
|
+
&& opt_price[cur + MIN_MATCH] < opt_price[cur] + 3
|
|
1167
|
+
) {
|
|
1168
|
+
continue;
|
|
1169
|
+
}
|
|
1170
|
+
} else if (opt_price[cur + 1] <= opt_price[cur]) {
|
|
1171
|
+
continue;
|
|
1172
|
+
}
|
|
1173
|
+
|
|
1174
|
+
// Below the top level, only a match reaching past everything already priced is compared
|
|
1175
|
+
// at all. It misses a few bytes, and it is most of what levels 10 and 11 cost less than
|
|
1176
|
+
// 12 for.
|
|
1177
|
+
find_longer_match(
|
|
1178
|
+
position,
|
|
1179
|
+
match_limit,
|
|
1180
|
+
full_update ? MIN_MATCH - 1 : last_match_pos - cur,
|
|
1181
|
+
max_attempts
|
|
1182
|
+
);
|
|
1183
|
+
|
|
1184
|
+
if (found_length === 0) {
|
|
1185
|
+
continue;
|
|
1186
|
+
}
|
|
1187
|
+
|
|
1188
|
+
const new_length = found_length;
|
|
1189
|
+
const new_offset = found_offset;
|
|
1190
|
+
|
|
1191
|
+
if (new_length > enough || new_length + cur >= OPT_NUM) {
|
|
1192
|
+
// good enough, or past what the tables hold: stop solving and take it
|
|
1193
|
+
best_length = new_length;
|
|
1194
|
+
best_offset = new_offset;
|
|
1195
|
+
from = cur;
|
|
1196
|
+
last_match_pos = cur + 1;
|
|
1197
|
+
solved = true;
|
|
1198
|
+
|
|
1199
|
+
break;
|
|
1200
|
+
}
|
|
1201
|
+
|
|
1202
|
+
// literals in front of a match here, which the prices past it may not have counted
|
|
1203
|
+
const base_literals = opt_literal_length[cur];
|
|
1204
|
+
|
|
1205
|
+
for (let extra = 1; extra < MIN_MATCH; extra++) {
|
|
1206
|
+
const price = opt_price[cur] - literals_price(base_literals) + literals_price(base_literals + extra);
|
|
1207
|
+
const p = cur + extra;
|
|
1208
|
+
|
|
1209
|
+
if (price < opt_price[p]) {
|
|
1210
|
+
opt_match_length[p] = 1;
|
|
1211
|
+
opt_offset[p] = 0;
|
|
1212
|
+
opt_literal_length[p] = base_literals + extra;
|
|
1213
|
+
opt_price[p] = price;
|
|
1214
|
+
}
|
|
1215
|
+
}
|
|
1216
|
+
|
|
1217
|
+
// and the match itself, at every length it can be cut to
|
|
1218
|
+
for (let length = MIN_MATCH; length <= new_length; length++) {
|
|
1219
|
+
const p = cur + length;
|
|
1220
|
+
|
|
1221
|
+
let literals;
|
|
1222
|
+
let price;
|
|
1223
|
+
|
|
1224
|
+
if (opt_match_length[cur] === 1) {
|
|
1225
|
+
// arriving here on literals, so they join the run in front of this match rather
|
|
1226
|
+
// than being paid for twice
|
|
1227
|
+
literals = opt_literal_length[cur];
|
|
1228
|
+
price = (cur > literals ? opt_price[cur - literals] : 0) + sequence_price(literals, length);
|
|
1229
|
+
} else {
|
|
1230
|
+
literals = 0;
|
|
1231
|
+
price = opt_price[cur] + sequence_price(0, length);
|
|
1232
|
+
}
|
|
1233
|
+
|
|
1234
|
+
if (p > last_match_pos + TRAILING_LITERALS || price <= opt_price[p]) {
|
|
1235
|
+
if (length === new_length && last_match_pos < p) {
|
|
1236
|
+
last_match_pos = p;
|
|
1237
|
+
}
|
|
1238
|
+
|
|
1239
|
+
opt_match_length[p] = length;
|
|
1240
|
+
opt_offset[p] = new_offset;
|
|
1241
|
+
opt_literal_length[p] = literals;
|
|
1242
|
+
opt_price[p] = price;
|
|
1243
|
+
}
|
|
1244
|
+
}
|
|
1245
|
+
|
|
1246
|
+
for (let add = 1; add <= TRAILING_LITERALS; add++) {
|
|
1247
|
+
opt_match_length[last_match_pos + add] = 1;
|
|
1248
|
+
opt_offset[last_match_pos + add] = 0;
|
|
1249
|
+
opt_literal_length[last_match_pos + add] = add;
|
|
1250
|
+
opt_price[last_match_pos + add] = opt_price[last_match_pos] + literals_price(add);
|
|
1251
|
+
}
|
|
1252
|
+
}
|
|
1253
|
+
|
|
1254
|
+
if (solved === false) {
|
|
1255
|
+
best_length = opt_match_length[last_match_pos];
|
|
1256
|
+
best_offset = opt_offset[last_match_pos];
|
|
1257
|
+
from = last_match_pos - best_length;
|
|
1258
|
+
}
|
|
1259
|
+
|
|
1260
|
+
encode_window(ip, anchor, from, last_match_pos, best_length, best_offset);
|
|
1261
|
+
|
|
1262
|
+
ip = window_ip;
|
|
1263
|
+
anchor = window_anchor;
|
|
1264
|
+
}
|
|
1265
|
+
|
|
1266
|
+
write_last_literals(anchor, input_size);
|
|
1267
|
+
}
|
|
1268
|
+
|
|
1269
|
+
/**
|
|
1270
|
+
* Encode one LZ4 block at a compression level.
|
|
1271
|
+
*
|
|
1272
|
+
* A port of the reference implementation's `lz4hc`, without its dictionary and streaming support:
|
|
1273
|
+
* meep compresses one self-contained block with no history, so the external dictionary, the
|
|
1274
|
+
* dictionary context, the pattern rotation across a dictionary boundary, `favorDecSpeed` and
|
|
1275
|
+
* `fillOutput` all drop, and with them about half the reference file. What is left is byte for byte
|
|
1276
|
+
* what `LZ4_compress_HC` writes at the same level.
|
|
1277
|
+
*
|
|
1278
|
+
* **The format does not change.** This emits ordinary LZ4 blocks — the same tokens, the same
|
|
1279
|
+
* sixteen-bit offsets, the same restrictions on where a match may start — so a block written here
|
|
1280
|
+
* at any level decodes under {@link lz4_decompress_block} and under every other reader that exists.
|
|
1281
|
+
* What changes is only how hard the encoder looked: where {@link lz4_compress_block} takes the
|
|
1282
|
+
* first match a hash slot offers, this walks a chain of every position carrying the same hash and
|
|
1283
|
+
* parses the matches it finds against each other.
|
|
1284
|
+
*
|
|
1285
|
+
* What that is worth depends entirely on the payload. On text it is 20% or more; on quantised,
|
|
1286
|
+
* densely packed bytes — geometry pages, say — it is about 2.5%, and almost all of that is already
|
|
1287
|
+
* there at level 3. What it costs is an order of magnitude of throughput: 400 MB/s becomes 50.
|
|
1288
|
+
*
|
|
1289
|
+
* `destination` and `source` must not overlap.
|
|
1290
|
+
*
|
|
1291
|
+
* @see https://github.com/lz4/lz4/blob/dev/lib/lz4hc.c — `LZ4HC_compress_hashChain` and `LZ4HC_compress_optimal`
|
|
1292
|
+
*
|
|
1293
|
+
* @param {Uint8Array} p_destination
|
|
1294
|
+
* @param {number} destination_offset where this block's output starts
|
|
1295
|
+
* @param {number} destination_end one past the last byte the block may write; give it
|
|
1296
|
+
* {@link lz4_compress_bound} bytes and it cannot run out
|
|
1297
|
+
* @param {Uint8Array} p_source
|
|
1298
|
+
* @param {number} source_offset the first byte of input, and the floor a match may reach back to
|
|
1299
|
+
* @param {number} source_end one past the last byte of input
|
|
1300
|
+
* @param {number} level one of {@link LZ4_LEVEL}'s high-compression levels
|
|
1301
|
+
* @returns {number} the offset just past the last byte written
|
|
1302
|
+
* @throws {Error} if the block does not fit the space given for it
|
|
1303
|
+
*
|
|
1304
|
+
* @author Alex Goldring
|
|
1305
|
+
* @copyright Company Named Limited (c) 2026
|
|
1306
|
+
*/
|
|
1307
|
+
export function lz4_compress_block_hc(p_destination, destination_offset, destination_end, p_source, source_offset, source_end, level) {
|
|
1308
|
+
assert.isNonNegativeInteger(destination_offset, 'destination_offset');
|
|
1309
|
+
assert.lessThanOrEqual(destination_end, p_destination.length, 'destination_end');
|
|
1310
|
+
assert.lessThanOrEqual(destination_offset, destination_end, 'destination_offset');
|
|
1311
|
+
assert.isNonNegativeInteger(source_offset, 'source_offset');
|
|
1312
|
+
assert.lessThanOrEqual(source_end, p_source.length, 'source_end');
|
|
1313
|
+
assert.lessThanOrEqual(source_offset, source_end, 'source_offset');
|
|
1314
|
+
// positions are held in an Int32Array, and a conforming block carries no more than this anyway
|
|
1315
|
+
assert.lessThanOrEqual(source_end, LZ4_MAX_INPUT_SIZE, 'source_end');
|
|
1316
|
+
assert.isInteger(level, 'level');
|
|
1317
|
+
// a level outside the ladder is a caller asking for something this encoder does not have, and
|
|
1318
|
+
// clamping it would silently give them a different encoder than the one they named
|
|
1319
|
+
assert.ok(
|
|
1320
|
+
level >= LZ4_LEVEL.HC_MIN && level <= LZ4_LEVEL.HC_MAX,
|
|
1321
|
+
`level must be between ${LZ4_LEVEL.HC_MIN} and ${LZ4_LEVEL.HC_MAX}, was ${level}`
|
|
1322
|
+
);
|
|
1323
|
+
|
|
1324
|
+
// Read *here*, not inside the length check below, so that a level this encoder does not have
|
|
1325
|
+
// fails the same way whatever the input size. The assert above is what refuses one, and a
|
|
1326
|
+
// consumer that strips asserts — which is how meep ships — has only this: without it, level 2
|
|
1327
|
+
// would throw on a page and quietly produce a valid block on anything under thirteen bytes.
|
|
1328
|
+
const parameters = LEVEL_TABLE[level];
|
|
1329
|
+
const attempts = parameters.attempts;
|
|
1330
|
+
const optimal = parameters.optimal;
|
|
1331
|
+
|
|
1332
|
+
destination = p_destination;
|
|
1333
|
+
source = p_source;
|
|
1334
|
+
base = source_offset;
|
|
1335
|
+
input_size = source_end - source_offset;
|
|
1336
|
+
|
|
1337
|
+
out = destination_offset;
|
|
1338
|
+
out_end = destination_end;
|
|
1339
|
+
|
|
1340
|
+
try {
|
|
1341
|
+
if (input_size >= MIN_LENGTH) {
|
|
1342
|
+
if (hash_table === null) {
|
|
1343
|
+
hash_table = new Int32Array(1 << HASH_LOG);
|
|
1344
|
+
chain_table = new Uint16Array(1 << 16);
|
|
1345
|
+
}
|
|
1346
|
+
|
|
1347
|
+
view = new DataView(source.buffer, source.byteOffset, source.byteLength);
|
|
1348
|
+
|
|
1349
|
+
hash_table.fill(0);
|
|
1350
|
+
chain_table.fill(0xFFFF);
|
|
1351
|
+
|
|
1352
|
+
next_to_update = IDX_BASE;
|
|
1353
|
+
match_find_limit = input_size - MATCH_FIND_LIMIT;
|
|
1354
|
+
match_limit = input_size - LAST_LITERALS;
|
|
1355
|
+
|
|
1356
|
+
if (optimal) {
|
|
1357
|
+
if (opt_price === null) {
|
|
1358
|
+
opt_price = new Int32Array(OPT_TABLE_SIZE);
|
|
1359
|
+
opt_offset = new Int32Array(OPT_TABLE_SIZE);
|
|
1360
|
+
opt_match_length = new Int32Array(OPT_TABLE_SIZE);
|
|
1361
|
+
opt_literal_length = new Int32Array(OPT_TABLE_SIZE);
|
|
1362
|
+
}
|
|
1363
|
+
|
|
1364
|
+
compress_optimal(attempts, parameters.sufficient_length, parameters.full_update);
|
|
1365
|
+
} else {
|
|
1366
|
+
compress_hash_chain(attempts, attempts > PATTERN_ANALYSIS_THRESHOLD);
|
|
1367
|
+
}
|
|
1368
|
+
} else {
|
|
1369
|
+
write_last_literals(0, input_size);
|
|
1370
|
+
}
|
|
1371
|
+
} finally {
|
|
1372
|
+
// However the call ends. A destination too small throws, and without this the encoder would
|
|
1373
|
+
// go on holding that call's buffers — the caller's whole input — until the next successful
|
|
1374
|
+
// one. A caller that catches the throw and carries on is exactly the caller that would
|
|
1375
|
+
// notice: it is the one still running.
|
|
1376
|
+
source = null;
|
|
1377
|
+
view = null;
|
|
1378
|
+
destination = null;
|
|
1379
|
+
}
|
|
1380
|
+
|
|
1381
|
+
return out;
|
|
1382
|
+
}
|