From 4c73cefab794d8a326b7db24db81ec39fe437414 Mon Sep 17 00:00:00 2001 From: Julien Pinsonneau Date: Mon, 20 Jul 2026 14:29:42 +0200 Subject: [PATCH 1/2] chore: update vendor for parquet/s3 dependencies Add parquet-go and related transitive deps needed by Hive Parquet encode and the flowBuffer write stage. Co-authored-by: Cursor --- go.mod | 14 +- go.sum | 26 +- vendor/github.com/andybalholm/brotli/LICENSE | 19 + .../github.com/andybalholm/brotli/README.md | 14 + .../andybalholm/brotli/backward_references.go | 185 + .../brotli/backward_references_hq.go | 796 + .../github.com/andybalholm/brotli/bit_cost.go | 436 + .../andybalholm/brotli/bit_reader.go | 266 + .../andybalholm/brotli/bitwriter.go | 56 + .../andybalholm/brotli/block_splitter.go | 144 + .../brotli/block_splitter_command.go | 434 + .../brotli/block_splitter_distance.go | 433 + .../brotli/block_splitter_literal.go | 433 + .../andybalholm/brotli/brotli_bit_stream.go | 1539 + .../github.com/andybalholm/brotli/cluster.go | 30 + .../andybalholm/brotli/cluster_command.go | 164 + .../andybalholm/brotli/cluster_distance.go | 326 + .../andybalholm/brotli/cluster_literal.go | 326 + .../github.com/andybalholm/brotli/command.go | 254 + .../andybalholm/brotli/compress_fragment.go | 834 + .../brotli/compress_fragment_two_pass.go | 773 + .../andybalholm/brotli/constants.go | 77 + .../github.com/andybalholm/brotli/context.go | 2176 + .../github.com/andybalholm/brotli/decode.go | 2581 + .../andybalholm/brotli/dictionary.go | 122890 +++++++++++++++ .../andybalholm/brotli/dictionary_hash.go | 32779 ++++ .../github.com/andybalholm/brotli/encode.go | 1220 + .../github.com/andybalholm/brotli/encoder.go | 177 + .../andybalholm/brotli/encoder_dict.go | 22 + .../andybalholm/brotli/entropy_encode.go | 592 + .../brotli/entropy_encode_static.go | 4399 + .../github.com/andybalholm/brotli/fast_log.go | 290 + .../andybalholm/brotli/find_match_length.go | 45 + vendor/github.com/andybalholm/brotli/h10.go | 287 + vendor/github.com/andybalholm/brotli/h5.go | 214 + vendor/github.com/andybalholm/brotli/h6.go | 216 + vendor/github.com/andybalholm/brotli/hash.go | 342 + .../andybalholm/brotli/hash_composite.go | 93 + .../brotli/hash_forgetful_chain.go | 252 + .../brotli/hash_longest_match_quickly.go | 214 + .../andybalholm/brotli/hash_rolling.go | 168 + .../andybalholm/brotli/histogram.go | 226 + vendor/github.com/andybalholm/brotli/http.go | 184 + .../github.com/andybalholm/brotli/huffman.go | 653 + .../andybalholm/brotli/literal_cost.go | 182 + .../andybalholm/brotli/matchfinder/emitter.go | 34 + .../andybalholm/brotli/matchfinder/m0.go | 169 + .../andybalholm/brotli/matchfinder/m4.go | 308 + .../brotli/matchfinder/matchfinder.go | 103 + .../brotli/matchfinder/textencoder.go | 53 + .../github.com/andybalholm/brotli/memory.go | 66 + .../andybalholm/brotli/metablock.go | 574 + .../andybalholm/brotli/metablock_command.go | 165 + .../andybalholm/brotli/metablock_distance.go | 165 + .../andybalholm/brotli/metablock_literal.go | 165 + .../github.com/andybalholm/brotli/params.go | 37 + .../github.com/andybalholm/brotli/platform.go | 103 + .../github.com/andybalholm/brotli/prefix.go | 30 + .../andybalholm/brotli/prefix_dec.go | 723 + .../github.com/andybalholm/brotli/quality.go | 196 + .../github.com/andybalholm/brotli/reader.go | 108 + .../andybalholm/brotli/ringbuffer.go | 134 + vendor/github.com/andybalholm/brotli/state.go | 294 + .../andybalholm/brotli/static_dict.go | 662 + .../andybalholm/brotli/static_dict_lut.go | 75094 +++++++++ .../andybalholm/brotli/symbol_list.go | 22 + .../andybalholm/brotli/transform.go | 641 + .../andybalholm/brotli/utf8_util.go | 70 + vendor/github.com/andybalholm/brotli/util.go | 7 + .../andybalholm/brotli/write_bits.go | 52 + .../github.com/andybalholm/brotli/writer.go | 162 + .../klauspost/compress/.gitattributes | 3 + .../github.com/klauspost/compress/.gitignore | 32 + .../klauspost/compress/.goreleaser.yml | 132 + vendor/github.com/klauspost/compress/LICENSE | 304 + .../github.com/klauspost/compress/README.md | 700 + .../github.com/klauspost/compress/SECURITY.md | 25 + .../klauspost/compress/compressible.go | 85 + .../klauspost/compress/flate/deflate.go | 996 + .../klauspost/compress/flate/dict_decoder.go | 181 + .../klauspost/compress/flate/fast_encoder.go | 189 + .../compress/flate/huffman_bit_writer.go | 1174 + .../klauspost/compress/flate/huffman_code.go | 417 + .../compress/flate/huffman_sortByFreq.go | 159 + .../compress/flate/huffman_sortByLiteral.go | 201 + .../klauspost/compress/flate/inflate.go | 865 + .../klauspost/compress/flate/inflate_gen.go | 1283 + .../klauspost/compress/flate/level1.go | 215 + .../klauspost/compress/flate/level2.go | 214 + .../klauspost/compress/flate/level3.go | 241 + .../klauspost/compress/flate/level4.go | 221 + .../klauspost/compress/flate/level5.go | 705 + .../klauspost/compress/flate/level6.go | 325 + .../compress/flate/matchlen_generic.go | 34 + .../klauspost/compress/flate/regmask_amd64.go | 37 + .../klauspost/compress/flate/regmask_other.go | 39 + .../klauspost/compress/flate/stateless.go | 325 + .../klauspost/compress/flate/token.go | 379 + .../klauspost/compress/fse/README.md | 79 + .../klauspost/compress/fse/bitreader.go | 122 + .../klauspost/compress/fse/bitwriter.go | 167 + .../klauspost/compress/fse/bytereader.go | 47 + .../klauspost/compress/fse/compress.go | 683 + .../klauspost/compress/fse/decompress.go | 376 + .../github.com/klauspost/compress/fse/fse.go | 144 + vendor/github.com/klauspost/compress/gen.sh | 4 + .../klauspost/compress/gzip/gunzip.go | 380 + .../klauspost/compress/gzip/gzip.go | 290 + .../klauspost/compress/huff0/.gitignore | 1 + .../klauspost/compress/huff0/README.md | 89 + .../klauspost/compress/huff0/bitreader.go | 224 + .../klauspost/compress/huff0/bitwriter.go | 102 + .../klauspost/compress/huff0/compress.go | 742 + .../klauspost/compress/huff0/decompress.go | 1161 + .../compress/huff0/decompress_amd64.go | 222 + .../compress/huff0/decompress_amd64.s | 830 + .../compress/huff0/decompress_generic.go | 298 + .../klauspost/compress/huff0/huff0.go | 337 + .../compress/internal/cpuinfo/cpuinfo.go | 34 + .../internal/cpuinfo/cpuinfo_amd64.go | 10 + .../compress/internal/cpuinfo/cpuinfo_amd64.s | 36 + .../klauspost/compress/internal/le/le.go | 5 + .../compress/internal/le/unsafe_disabled.go | 42 + .../compress/internal/le/unsafe_enabled.go | 52 + .../compress/internal/race/norace.go | 13 + .../klauspost/compress/internal/race/race.go | 26 + .../compress/internal/snapref/LICENSE | 27 + .../compress/internal/snapref/decode.go | 264 + .../compress/internal/snapref/decode_other.go | 113 + .../compress/internal/snapref/encode.go | 291 + .../compress/internal/snapref/encode_other.go | 250 + .../compress/internal/snapref/snappy.go | 98 + .../klauspost/compress/s2/.gitignore | 15 + .../github.com/klauspost/compress/s2/LICENSE | 28 + .../klauspost/compress/s2/README.md | 1134 + .../klauspost/compress/s2/decode.go | 443 + .../klauspost/compress/s2/decode_amd64.s | 568 + .../klauspost/compress/s2/decode_arm64.s | 574 + .../klauspost/compress/s2/decode_asm.go | 17 + .../klauspost/compress/s2/decode_other.go | 287 + .../github.com/klauspost/compress/s2/dict.go | 350 + .../klauspost/compress/s2/encode.go | 418 + .../klauspost/compress/s2/encode_all.go | 1477 + .../klauspost/compress/s2/encode_amd64.go | 316 + .../klauspost/compress/s2/encode_best.go | 797 + .../klauspost/compress/s2/encode_better.go | 1513 + .../klauspost/compress/s2/encode_go.go | 740 + .../compress/s2/encodeblock_amd64.go | 228 + .../klauspost/compress/s2/encodeblock_amd64.s | 21303 +++ .../klauspost/compress/s2/hashtable_pool.go | 65 + .../github.com/klauspost/compress/s2/index.go | 602 + .../klauspost/compress/s2/lz4convert.go | 585 + .../klauspost/compress/s2/lz4sconvert.go | 467 + .../klauspost/compress/s2/reader.go | 1075 + vendor/github.com/klauspost/compress/s2/s2.go | 151 + .../klauspost/compress/s2/writer.go | 1064 + vendor/github.com/klauspost/compress/s2sx.mod | 3 + vendor/github.com/klauspost/compress/s2sx.sum | 0 .../klauspost/compress/snappy/.gitignore | 16 + .../klauspost/compress/snappy/AUTHORS | 18 + .../klauspost/compress/snappy/CONTRIBUTORS | 41 + .../klauspost/compress/snappy/LICENSE | 27 + .../klauspost/compress/snappy/README.md | 17 + .../klauspost/compress/snappy/decode.go | 60 + .../klauspost/compress/snappy/encode.go | 59 + .../klauspost/compress/snappy/snappy.go | 46 + .../klauspost/compress/zstd/README.md | 441 + .../klauspost/compress/zstd/bitreader.go | 135 + .../klauspost/compress/zstd/bitwriter.go | 112 + .../klauspost/compress/zstd/blockdec.go | 712 + .../klauspost/compress/zstd/blockenc.go | 893 + .../compress/zstd/blocktype_string.go | 85 + .../klauspost/compress/zstd/bytebuf.go | 131 + .../klauspost/compress/zstd/bytereader.go | 82 + .../klauspost/compress/zstd/decodeheader.go | 261 + .../klauspost/compress/zstd/decoder.go | 957 + .../compress/zstd/decoder_options.go | 213 + .../klauspost/compress/zstd/dict.go | 559 + .../klauspost/compress/zstd/enc_base.go | 171 + .../klauspost/compress/zstd/enc_best.go | 553 + .../klauspost/compress/zstd/enc_better.go | 1238 + .../klauspost/compress/zstd/enc_dfast.go | 1107 + .../klauspost/compress/zstd/enc_fast.go | 875 + .../klauspost/compress/zstd/encoder.go | 671 + .../compress/zstd/encoder_options.go | 378 + .../klauspost/compress/zstd/framedec.go | 412 + .../klauspost/compress/zstd/frameenc.go | 137 + .../klauspost/compress/zstd/fse_decoder.go | 307 + .../compress/zstd/fse_decoder_amd64.go | 64 + .../compress/zstd/fse_decoder_amd64.s | 126 + .../compress/zstd/fse_decoder_generic.go | 72 + .../klauspost/compress/zstd/fse_encoder.go | 701 + .../klauspost/compress/zstd/fse_predefined.go | 158 + .../klauspost/compress/zstd/hash.go | 35 + .../klauspost/compress/zstd/history.go | 116 + .../compress/zstd/internal/xxhash/LICENSE.txt | 22 + .../compress/zstd/internal/xxhash/README.md | 71 + .../compress/zstd/internal/xxhash/xxhash.go | 230 + .../zstd/internal/xxhash/xxhash_amd64.s | 210 + .../zstd/internal/xxhash/xxhash_arm64.s | 184 + .../zstd/internal/xxhash/xxhash_asm.go | 16 + .../zstd/internal/xxhash/xxhash_other.go | 75 + .../zstd/internal/xxhash/xxhash_safe.go | 11 + .../klauspost/compress/zstd/matchlen_amd64.go | 15 + .../klauspost/compress/zstd/matchlen_amd64.s | 66 + .../compress/zstd/matchlen_generic.go | 37 + .../klauspost/compress/zstd/seqdec.go | 500 + .../klauspost/compress/zstd/seqdec_amd64.go | 387 + .../klauspost/compress/zstd/seqdec_amd64.s | 4151 + .../klauspost/compress/zstd/seqdec_generic.go | 236 + .../klauspost/compress/zstd/seqenc.go | 112 + .../klauspost/compress/zstd/simple_go124.go | 56 + .../klauspost/compress/zstd/snappy.go | 434 + .../github.com/klauspost/compress/zstd/zip.go | 141 + .../klauspost/compress/zstd/zstd.go | 126 + .../flowlogs-pipeline/pkg/api/api.go | 2 + .../flowlogs-pipeline/pkg/api/encode_s3.go | 22 +- .../flowlogs-pipeline/pkg/api/redacted.go | 16 + .../flowlogs-pipeline/pkg/api/tls.go | 1 + .../pkg/api/write_flowbuffer.go | 40 + .../flowlogs-pipeline/pkg/config/config.go | 27 +- .../pkg/config/pipeline_builder.go | 5 + .../pkg/config/stage_params.go | 4 + .../flowlogs-pipeline/pkg/schema/flow_v1.go | 197 + .../pkg/schema/parquet_encode.go | 44 + .../github.com/parquet-go/bitpack/.gitignore | 21 + vendor/github.com/parquet-go/bitpack/LICENSE | 201 + .../github.com/parquet-go/bitpack/README.md | 31 + .../github.com/parquet-go/bitpack/bitpack.go | 14 + .../parquet-go/bitpack/masks_int32_amd64.s | 1288 + .../parquet-go/bitpack/masks_int64_amd64.s | 427 + vendor/github.com/parquet-go/bitpack/pack.go | 19 + .../parquet-go/bitpack/pack_arm64.go | 31 + .../parquet-go/bitpack/pack_int32_arm64.s | 462 + .../parquet-go/bitpack/pack_int64_arm64.s | 514 + .../parquet-go/bitpack/pack_purego.go | 94 + .../github.com/parquet-go/bitpack/unpack.go | 29 + .../bitpack/unpack_int32_1bit_arm64.s | 184 + .../bitpack/unpack_int32_2bit_arm64.s | 136 + .../bitpack/unpack_int32_4bit_arm64.s | 106 + .../bitpack/unpack_int32_8bit_arm64.s | 65 + .../parquet-go/bitpack/unpack_int32_amd64.go | 36 + .../parquet-go/bitpack/unpack_int32_amd64.s | 352 + .../parquet-go/bitpack/unpack_int32_arm64.go | 48 + .../parquet-go/bitpack/unpack_int32_arm64.s | 732 + .../parquet-go/bitpack/unpack_int32_be.go | 15 + .../parquet-go/bitpack/unpack_int32_le.go | 9 + .../parquet-go/bitpack/unpack_int32_purego.go | 21 + .../bitpack/unpack_int64_1bit_amd64.s | 123 + .../bitpack/unpack_int64_1bit_arm64.s | 239 + .../bitpack/unpack_int64_2bit_amd64.s | 124 + .../bitpack/unpack_int64_2bit_arm64.s | 161 + .../bitpack/unpack_int64_4bit_amd64.s | 124 + .../bitpack/unpack_int64_4bit_arm64.s | 118 + .../bitpack/unpack_int64_8bit_amd64.s | 105 + .../bitpack/unpack_int64_8bit_arm64.s | 71 + .../parquet-go/bitpack/unpack_int64_amd64.go | 38 + .../parquet-go/bitpack/unpack_int64_amd64.s | 824 + .../parquet-go/bitpack/unpack_int64_arm64.go | 50 + .../parquet-go/bitpack/unpack_int64_arm64.s | 943 + .../parquet-go/bitpack/unpack_int64_purego.go | 25 + .../bitpack/unpack_neon_macros_arm64.h | 18 + .../bitpack/unsafecast/unsafecast.go | 54 + .../github.com/parquet-go/jsonlite/.gitignore | 21 + vendor/github.com/parquet-go/jsonlite/LICENSE | 21 + .../github.com/parquet-go/jsonlite/README.md | 54 + .../github.com/parquet-go/jsonlite/convert.go | 352 + vendor/github.com/parquet-go/jsonlite/doc.go | 8 + .../parquet-go/jsonlite/iterator.go | 562 + .../github.com/parquet-go/jsonlite/parse.go | 305 + .../github.com/parquet-go/jsonlite/quote.go | 121 + .../github.com/parquet-go/jsonlite/unquote.go | 218 + .../github.com/parquet-go/jsonlite/valid.go | 244 + .../github.com/parquet-go/jsonlite/value.go | 415 + .../parquet-go/parquet-go/.CLAUDE.md | 160 + .../parquet-go/parquet-go/.gitattributes | 2 + .../parquet-go/parquet-go/.gitignore | 21 + .../github.com/parquet-go/parquet-go/.mailmap | 2 + .../github.com/parquet-go/parquet-go/.words | 27 + .../parquet-go/parquet-go/AUTHORS.txt | 5 + .../parquet-go/parquet-go/CHANGELOG.md | 16 + .../parquet-go/parquet-go/CODEOWNERS | 1 + .../parquet-go/parquet-go/CODE_OF_CONDUCT.md | 73 + .../parquet-go/parquet-go/CONTRIBUTING.md | 52 + .../github.com/parquet-go/parquet-go/LICENSE | 213 + .../github.com/parquet-go/parquet-go/Makefile | 15 + .../parquet-go/parquet-go/README.md | 688 + .../parquet-go/parquet-go/allocator.go | 64 + .../github.com/parquet-go/parquet-go/array.go | 27 + .../parquet-go/parquet-go/bitmap.go | 38 + .../github.com/parquet-go/parquet-go/bloom.go | 356 + .../parquet-go/parquet-go/bloom/block.go | 28 + .../parquet-go/bloom/block_amd64.go | 39 + .../parquet-go/parquet-go/bloom/block_amd64.s | 129 + .../parquet-go/bloom/block_default.go | 65 + .../parquet-go/bloom/block_optimized.go | 53 + .../parquet-go/parquet-go/bloom/bloom.go | 13 + .../parquet-go/parquet-go/bloom/filter.go | 94 + .../parquet-go/bloom/filter_amd64.go | 33 + .../parquet-go/bloom/filter_amd64.s | 214 + .../parquet-go/bloom/filter_default.go | 17 + .../parquet-go/parquet-go/bloom/hash.go | 77 + .../parquet-go/bloom/xxhash/LICENSE | 27 + .../parquet-go/bloom/xxhash/sum64uint.go | 37 + .../bloom/xxhash/sum64uint_amd64.go | 49 + .../parquet-go/bloom/xxhash/sum64uint_amd64.s | 755 + .../bloom/xxhash/sum64uint_purego.go | 53 + .../parquet-go/bloom/xxhash/xxhash.go | 55 + .../parquet-go/bloom/xxhash/xxhash_amd64.go | 6 + .../parquet-go/bloom/xxhash/xxhash_amd64.s | 180 + .../parquet-go/bloom/xxhash/xxhash_purego.go | 50 + .../parquet-go/parquet-go/bloom_be.go | 19 + .../parquet-go/parquet-go/bloom_le.go | 12 + .../parquet-go/parquet-go/buf.gen.yaml | 6 + .../github.com/parquet-go/parquet-go/buf.yaml | 9 + .../parquet-go/parquet-go/buffer.go | 708 + .../parquet-go/parquet-go/buffer_pool.go | 146 + .../parquet-go/parquet-go/column.go | 871 + .../parquet-go/parquet-go/column_buffer.go | 151 + .../parquet-go/column_buffer_amd64.go | 30 + .../parquet-go/column_buffer_amd64.s | 67 + .../parquet-go/column_buffer_be128.go | 157 + .../parquet-go/column_buffer_boolean.go | 236 + .../parquet-go/column_buffer_byte_array.go | 271 + .../parquet-go/column_buffer_double.go | 145 + .../column_buffer_fixed_len_byte_array.go | 218 + .../parquet-go/column_buffer_float.go | 145 + .../parquet-go/column_buffer_geometry.go | 33 + .../parquet-go/column_buffer_int32.go | 145 + .../parquet-go/column_buffer_int64.go | 146 + .../parquet-go/column_buffer_int96.go | 149 + .../parquet-go/column_buffer_json.go | 226 + .../parquet-go/column_buffer_null.go | 114 + .../parquet-go/column_buffer_optional.go | 367 + .../parquet-go/column_buffer_proto.go | 206 + .../parquet-go/column_buffer_proto_any.go | 60 + .../parquet-go/column_buffer_proto_purego.go | 84 + .../parquet-go/column_buffer_purego.go | 30 + .../parquet-go/column_buffer_reflect.go | 1055 + .../parquet-go/column_buffer_repeated.go | 451 + .../parquet-go/column_buffer_uint32.go | 146 + .../parquet-go/column_buffer_uint64.go | 146 + .../parquet-go/column_buffer_variant.go | 155 + .../parquet-go/column_buffer_write.go | 1019 + .../parquet-go/parquet-go/column_chunk.go | 347 + .../parquet-go/parquet-go/column_index.go | 774 + .../parquet-go/parquet-go/column_index_be.go | 84 + .../parquet-go/parquet-go/column_index_le.go | 38 + .../parquet-go/parquet-go/column_mapping.go | 90 + .../parquet-go/parquet-go/column_path.go | 108 + .../parquet-go/parquet-go/compare.go | 356 + .../parquet-go/parquet-go/compress.go | 96 + .../parquet-go/compress/brotli/brotli.go | 54 + .../parquet-go/compress/compress.go | 148 + .../parquet-go/compress/gzip/gzip.go | 67 + .../parquet-go/parquet-go/compress/lz4/lz4.go | 87 + .../parquet-go/compress/snappy/snappy.go | 31 + .../compress/uncompressed/uncompressed.go | 27 + .../parquet-go/compress/zstd/zstd.go | 105 + .../parquet-go/parquet-go/config.go | 1052 + .../parquet-go/parquet-go/convert.go | 1590 + .../parquet-go/parquet-go/dedupe.go | 107 + .../parquet-go/parquet-go/deprecated/int96.go | 179 + .../parquet-go/deprecated/parquet.go | 112 + .../parquet-go/parquet-go/dictionary.go | 427 + .../parquet-go/parquet-go/dictionary_amd64.go | 168 + .../parquet-go/parquet-go/dictionary_amd64.s | 941 + .../parquet-go/parquet-go/dictionary_be128.go | 184 + .../parquet-go/dictionary_boolean.go | 181 + .../parquet-go/dictionary_byte_array.go | 173 + .../parquet-go/dictionary_double.go | 137 + .../dictionary_fixed_len_byte_array.go | 198 + .../parquet-go/parquet-go/dictionary_float.go | 137 + .../parquet-go/parquet-go/dictionary_int32.go | 150 + .../parquet-go/parquet-go/dictionary_int64.go | 134 + .../parquet-go/parquet-go/dictionary_int96.go | 143 + .../parquet-go/parquet-go/dictionary_null.go | 75 + .../parquet-go/dictionary_purego.go | 210 + .../parquet-go/dictionary_uint32.go | 141 + .../parquet-go/dictionary_uint64.go | 141 + .../parquet-go/parquet-go/encoding.go | 160 + .../encoding/bitpacked/bitpacked.go | 119 + .../bytestreamsplit/bytestreamsplit.go | 114 + .../bytestreamsplit/bytestreamsplit_amd64.go | 43 + .../bytestreamsplit/bytestreamsplit_amd64.s | 426 + .../bytestreamsplit_fixedlen.go | 21 + .../bytestreamsplit/bytestreamsplit_purego.go | 99 + .../encoding/delta/binary_packed.go | 488 + .../encoding/delta/binary_packed_amd64.go | 256 + .../encoding/delta/binary_packed_amd64.s | 920 + .../encoding/delta/binary_packed_purego.go | 105 + .../parquet-go/encoding/delta/byte_array.go | 212 + .../encoding/delta/byte_array_amd64.go | 164 + .../encoding/delta/byte_array_amd64.s | 243 + .../encoding/delta/byte_array_purego.go | 63 + .../parquet-go/encoding/delta/delta.go | 99 + .../parquet-go/encoding/delta/delta_amd64.go | 16 + .../parquet-go/encoding/delta/delta_amd64.s | 13 + .../encoding/delta/length_byte_array.go | 81 + .../encoding/delta/length_byte_array_amd64.go | 9 + .../encoding/delta/length_byte_array_amd64.s | 122 + .../delta/length_byte_array_purego.go | 24 + .../parquet-go/encoding/encoding.go | 72 + .../parquet-go/encoding/notsupported.go | 213 + .../parquet-go/encoding/plain/dictionary.go | 27 + .../parquet-go/encoding/plain/plain.go | 246 + .../parquet-go/encoding/plain/plain_be.go | 113 + .../parquet-go/encoding/plain/plain_le.go | 52 + .../parquet-go/encoding/rle/dictionary.go | 59 + .../parquet-go/parquet-go/encoding/rle/rle.go | 567 + .../parquet-go/encoding/rle/rle_amd64.go | 60 + .../parquet-go/encoding/rle/rle_amd64.s | 174 + .../parquet-go/encoding/rle/rle_purego.go | 22 + .../parquet-go/encoding/thrift/LICENSE | 21 + .../parquet-go/encoding/thrift/binary.go | 589 + .../parquet-go/encoding/thrift/compact.go | 576 + .../parquet-go/encoding/thrift/debug.go | 234 + .../parquet-go/encoding/thrift/decode.go | 737 + .../parquet-go/encoding/thrift/encode.go | 449 + .../parquet-go/encoding/thrift/error.go | 111 + .../parquet-go/encoding/thrift/list.go | 96 + .../parquet-go/encoding/thrift/null.go | 83 + .../parquet-go/encoding/thrift/protocol.go | 75 + .../parquet-go/encoding/thrift/struct.go | 154 + .../parquet-go/encoding/thrift/thrift.go | 173 + .../parquet-go/encoding/thrift/unsafe.go | 20 + .../parquet-go/encoding/thrift/value.go | 45 + .../parquet-go/parquet-go/encoding/values.go | 276 + .../parquet-go/parquet-go/encrypt.go | 301 + .../parquet-go/parquet-go/errors.go | 87 + .../github.com/parquet-go/parquet-go/file.go | 1658 + .../parquet-go/parquet-go/filter.go | 82 + .../parquet-go/parquet-go/format/parquet.go | 1324 + .../parquet-go/geospatial_statistics.go | 213 + .../parquet-go/parquet-go/go.tools.mod | 25 + .../parquet-go/parquet-go/go.tools.sum | 38 + .../parquet-go/hashprobe/aeshash/aeshash.go | 21 + .../hashprobe/aeshash/aeshash_amd64.go | 60 + .../hashprobe/aeshash/aeshash_amd64.s | 155 + .../hashprobe/aeshash/aeshash_purego.go | 29 + .../parquet-go/hashprobe/hashprobe.go | 783 + .../parquet-go/hashprobe/hashprobe_amd64.go | 38 + .../parquet-go/hashprobe/hashprobe_amd64.s | 197 + .../parquet-go/hashprobe/hashprobe_purego.go | 19 + .../parquet-go/hashprobe/wyhash/wyhash.go | 49 + .../hashprobe/wyhash/wyhash_amd64.go | 14 + .../hashprobe/wyhash/wyhash_amd64.s | 118 + .../hashprobe/wyhash/wyhash_purego.go | 23 + .../internal/bytealg/broadcast_amd64.go | 17 + .../internal/bytealg/broadcast_amd64.s | 51 + .../internal/bytealg/broadcast_purego.go | 9 + .../parquet-go/internal/bytealg/bytealg.go | 2 + .../internal/bytealg/bytealg_amd64.go | 17 + .../internal/bytealg/count_amd64.go | 26 + .../parquet-go/internal/bytealg/count_amd64.s | 100 + .../internal/bytealg/count_purego.go | 9 + .../parquet-go/internal/debug/debug.go | 95 + .../internal/debug/finalizer_off.go | 6 + .../parquet-go/internal/debug/finalizer_on.go | 7 + .../parquet-go/internal/memory/buffer.go | 128 + .../internal/memory/chunk_buffer.go | 116 + .../parquet-go/internal/memory/memory.go | 7 + .../parquet-go/internal/memory/pool.go | 25 + .../internal/memory/slice_buffer.go | 352 + .../internal/unsafecast/unsafecast.go | 54 + .../github.com/parquet-go/parquet-go/level.go | 33 + .../parquet-go/parquet-go/limits.go | 64 + .../github.com/parquet-go/parquet-go/merge.go | 958 + .../parquet-go/parquet-go/multi_row_group.go | 565 + .../github.com/parquet-go/parquet-go/node.go | 705 + .../github.com/parquet-go/parquet-go/null.go | 139 + .../parquet-go/parquet-go/null_amd64.go | 90 + .../parquet-go/parquet-go/null_amd64.s | 227 + .../parquet-go/parquet-go/null_purego.go | 80 + .../parquet-go/parquet-go/offset_index.go | 128 + .../github.com/parquet-go/parquet-go/order.go | 102 + .../parquet-go/parquet-go/order_amd64.go | 21 + .../parquet-go/parquet-go/order_amd64.s | 547 + .../parquet-go/parquet-go/order_purego.go | 42 + .../github.com/parquet-go/parquet-go/page.go | 414 + .../parquet-go/parquet-go/page_be128.go | 99 + .../parquet-go/parquet-go/page_boolean.go | 157 + .../parquet-go/parquet-go/page_bounds.go | 25 + .../parquet-go/page_bounds_amd64.go | 129 + .../parquet-go/parquet-go/page_bounds_amd64.s | 551 + .../parquet-go/page_bounds_purego.go | 143 + .../parquet-go/parquet-go/page_byte_array.go | 210 + .../parquet-go/parquet-go/page_double.go | 136 + .../parquet-go/page_fixed_len_byte_array.go | 133 + .../parquet-go/parquet-go/page_float.go | 137 + .../parquet-go/parquet-go/page_header.go | 221 + .../parquet-go/parquet-go/page_int32.go | 108 + .../parquet-go/parquet-go/page_int64.go | 108 + .../parquet-go/parquet-go/page_int96.go | 107 + .../parquet-go/parquet-go/page_max.go | 23 + .../parquet-go/parquet-go/page_max_amd64.go | 24 + .../parquet-go/parquet-go/page_max_amd64.s | 598 + .../parquet-go/parquet-go/page_max_purego.go | 72 + .../parquet-go/parquet-go/page_min.go | 23 + .../parquet-go/parquet-go/page_min_amd64.go | 24 + .../parquet-go/parquet-go/page_min_amd64.s | 592 + .../parquet-go/parquet-go/page_min_purego.go | 72 + .../parquet-go/parquet-go/page_null.go | 57 + .../parquet-go/parquet-go/page_optional.go | 117 + .../parquet-go/parquet-go/page_repeated.go | 177 + .../parquet-go/parquet-go/page_uint32.go | 108 + .../parquet-go/parquet-go/page_uint64.go | 108 + .../parquet-go/parquet-go/parquet.go | 122 + .../parquet-go/parquet-go/parquet_amd64.go | 18 + .../github.com/parquet-go/parquet-go/print.go | 363 + .../parquet-go/parquet-go/reader.go | 714 + .../github.com/parquet-go/parquet-go/row.go | 935 + .../parquet-go/parquet-go/row_buffer.go | 491 + .../parquet-go/parquet-go/row_builder.go | 204 + .../parquet-go/parquet-go/row_group.go | 493 + .../parquet-go/parquet-go/row_variant.go | 979 + .../github.com/parquet-go/parquet-go/scan.go | 33 + .../parquet-go/parquet-go/schema.go | 1396 + .../parquet-go/parquet-go/schema_int.go | 25 + .../parquet-go/parquet-go/search.go | 95 + .../parquet-go/parquet-go/sorting.go | 274 + .../parquet-go/parquet-go/sparse/array.go | 320 + .../parquet-go/parquet-go/sparse/gather.go | 37 + .../parquet-go/sparse/gather_amd64.go | 85 + .../parquet-go/sparse/gather_amd64.s | 193 + .../parquet-go/sparse/gather_purego.go | 72 + .../parquet-go/parquet-go/sparse/sparse.go | 20 + .../github.com/parquet-go/parquet-go/tags.go | 73 + .../parquet-go/parquet-go/transform.go | 140 + .../github.com/parquet-go/parquet-go/type.go | 285 + .../parquet-go/parquet-go/type_boolean.go | 90 + .../parquet-go/parquet-go/type_bson.go | 87 + .../parquet-go/parquet-go/type_byte_array.go | 102 + .../parquet-go/parquet-go/type_date.go | 117 + .../parquet-go/parquet-go/type_decimal.go | 104 + .../parquet-go/parquet-go/type_double.go | 90 + .../parquet-go/parquet-go/type_enum.go | 87 + .../parquet-go/type_fixed_len_byte_array.go | 234 + .../parquet-go/parquet-go/type_float.go | 90 + .../parquet-go/parquet-go/type_geography.go | 137 + .../parquet-go/parquet-go/type_geometry.go | 128 + .../parquet-go/parquet-go/type_group.go | 75 + .../parquet-go/parquet-go/type_int32.go | 132 + .../parquet-go/parquet-go/type_int64.go | 119 + .../parquet-go/parquet-go/type_int96.go | 86 + .../parquet-go/parquet-go/type_int_logical.go | 202 + .../parquet-go/parquet-go/type_interval.go | 115 + .../parquet-go/parquet-go/type_json.go | 106 + .../parquet-go/parquet-go/type_list.go | 86 + .../parquet-go/parquet-go/type_map.go | 91 + .../parquet-go/parquet-go/type_null.go | 77 + .../parquet-go/parquet-go/type_string.go | 121 + .../parquet-go/parquet-go/type_time.go | 279 + .../parquet-go/parquet-go/type_timestamp.go | 257 + .../parquet-go/parquet-go/type_uuid.go | 80 + .../parquet-go/parquet-go/type_variant.go | 227 + .../github.com/parquet-go/parquet-go/value.go | 1095 + .../parquet-go/parquet-go/value_amd64.go | 18 + .../parquet-go/parquet-go/value_amd64.s | 59 + .../parquet-go/parquet-go/value_be.go | 19 + .../parquet-go/parquet-go/value_le.go | 8 + .../parquet-go/parquet-go/values_purego.go | 9 + .../parquet-go/parquet-go/variant/decoding.go | 293 + .../parquet-go/parquet-go/variant/encoding.go | 290 + .../parquet-go/parquet-go/variant/marshal.go | 187 + .../parquet-go/parquet-go/variant/metadata.go | 198 + .../parquet-go/parquet-go/variant/types.go | 87 + .../parquet-go/parquet-go/variant/value.go | 357 + .../parquet-go/parquet-go/writer.go | 2727 + .../parquet-go/writer_statistics.go | 61 + vendor/github.com/pierrec/lz4/v4/.gitignore | 36 + vendor/github.com/pierrec/lz4/v4/LICENSE | 28 + vendor/github.com/pierrec/lz4/v4/README.md | 92 + .../pierrec/lz4/v4/compressing_reader.go | 222 + .../pierrec/lz4/v4/internal/lz4block/block.go | 481 + .../lz4/v4/internal/lz4block/blocks.go | 87 + .../lz4/v4/internal/lz4block/decode_amd64.s | 448 + .../lz4/v4/internal/lz4block/decode_arm.s | 231 + .../lz4/v4/internal/lz4block/decode_arm64.s | 241 + .../lz4/v4/internal/lz4block/decode_asm.go | 10 + .../lz4/v4/internal/lz4block/decode_other.go | 139 + .../lz4/v4/internal/lz4errors/errors.go | 19 + .../lz4/v4/internal/lz4stream/block.go | 348 + .../lz4/v4/internal/lz4stream/frame.go | 204 + .../lz4/v4/internal/lz4stream/frame_gen.go | 103 + .../lz4/v4/internal/xxh32/xxh32zero.go | 212 + .../lz4/v4/internal/xxh32/xxh32zero_arm.go | 11 + .../lz4/v4/internal/xxh32/xxh32zero_arm.s | 251 + .../lz4/v4/internal/xxh32/xxh32zero_other.go | 10 + vendor/github.com/pierrec/lz4/v4/lz4.go | 157 + vendor/github.com/pierrec/lz4/v4/options.go | 242 + .../github.com/pierrec/lz4/v4/options_gen.go | 92 + vendor/github.com/pierrec/lz4/v4/reader.go | 275 + vendor/github.com/pierrec/lz4/v4/state.go | 75 + vendor/github.com/pierrec/lz4/v4/state_gen.go | 28 + vendor/github.com/pierrec/lz4/v4/writer.go | 242 + .../stretchr/testify/require/doc.go | 31 + .../testify/require/forward_requirements.go | 16 + .../stretchr/testify/require/require.go | 2180 + .../stretchr/testify/require/require.go.tmpl | 6 + .../testify/require/require_forward.go | 1724 + .../testify/require/require_forward.go.tmpl | 5 + .../stretchr/testify/require/requirements.go | 29 + vendor/github.com/twpayne/go-geom/.gitignore | 2 + .../github.com/twpayne/go-geom/.golangci.yml | 174 + .../github.com/twpayne/go-geom/INTERNALS.md | 119 + vendor/github.com/twpayne/go-geom/LICENSE | 23 + vendor/github.com/twpayne/go-geom/Makefile | 56 + vendor/github.com/twpayne/go-geom/README.md | 75 + vendor/github.com/twpayne/go-geom/bounds.go | 178 + .../github.com/twpayne/go-geom/derived.gen.go | 314 + .../twpayne/go-geom/encoding/wkb/.gitignore | 1 + .../twpayne/go-geom/encoding/wkb/Makefile | 3 + .../twpayne/go-geom/encoding/wkb/fuzz.go | 11 + .../twpayne/go-geom/encoding/wkb/sql.go | 270 + .../twpayne/go-geom/encoding/wkb/wkb.go | 335 + .../go-geom/encoding/wkbcommon/binary.go | 86 + .../go-geom/encoding/wkbcommon/options.go | 36 + .../go-geom/encoding/wkbcommon/wkbcommon.go | 179 + vendor/github.com/twpayne/go-geom/flat.go | 423 + vendor/github.com/twpayne/go-geom/geom.go | 251 + .../twpayne/go-geom/geometrycollection.go | 169 + .../github.com/twpayne/go-geom/linearring.go | 60 + .../github.com/twpayne/go-geom/linestring.go | 99 + .../twpayne/go-geom/multilinestring.go | 88 + .../github.com/twpayne/go-geom/multipoint.go | 160 + .../twpayne/go-geom/multipolygon.go | 115 + vendor/github.com/twpayne/go-geom/point.go | 120 + vendor/github.com/twpayne/go-geom/polygon.go | 87 + vendor/golang.org/x/sys/cpu/asm_aix_ppc64.s | 17 + .../x/sys/cpu/asm_darwin_arm64_gc.s | 12 + .../golang.org/x/sys/cpu/asm_darwin_x86_gc.s | 17 + vendor/golang.org/x/sys/cpu/byteorder.go | 66 + vendor/golang.org/x/sys/cpu/cpu.go | 343 + vendor/golang.org/x/sys/cpu/cpu_aix.go | 33 + vendor/golang.org/x/sys/cpu/cpu_arm.go | 73 + vendor/golang.org/x/sys/cpu/cpu_arm64.go | 191 + vendor/golang.org/x/sys/cpu/cpu_arm64.s | 35 + .../golang.org/x/sys/cpu/cpu_darwin_arm64.go | 67 + .../x/sys/cpu/cpu_darwin_arm64_other.go | 31 + vendor/golang.org/x/sys/cpu/cpu_darwin_x86.go | 61 + vendor/golang.org/x/sys/cpu/cpu_gc_arm64.go | 12 + vendor/golang.org/x/sys/cpu/cpu_gc_s390x.go | 21 + vendor/golang.org/x/sys/cpu/cpu_gc_x86.go | 15 + vendor/golang.org/x/sys/cpu/cpu_gc_x86.s | 26 + .../golang.org/x/sys/cpu/cpu_gccgo_arm64.go | 12 + .../golang.org/x/sys/cpu/cpu_gccgo_s390x.go | 22 + vendor/golang.org/x/sys/cpu/cpu_gccgo_x86.c | 37 + vendor/golang.org/x/sys/cpu/cpu_gccgo_x86.go | 25 + vendor/golang.org/x/sys/cpu/cpu_linux.go | 15 + vendor/golang.org/x/sys/cpu/cpu_linux_arm.go | 39 + .../golang.org/x/sys/cpu/cpu_linux_arm64.go | 120 + .../golang.org/x/sys/cpu/cpu_linux_loong64.go | 22 + .../golang.org/x/sys/cpu/cpu_linux_mips64x.go | 22 + .../golang.org/x/sys/cpu/cpu_linux_noinit.go | 9 + .../golang.org/x/sys/cpu/cpu_linux_ppc64x.go | 30 + .../golang.org/x/sys/cpu/cpu_linux_riscv64.go | 162 + .../golang.org/x/sys/cpu/cpu_linux_s390x.go | 40 + vendor/golang.org/x/sys/cpu/cpu_loong64.go | 62 + vendor/golang.org/x/sys/cpu/cpu_loong64.s | 13 + vendor/golang.org/x/sys/cpu/cpu_mips64x.go | 15 + vendor/golang.org/x/sys/cpu/cpu_mipsx.go | 11 + .../golang.org/x/sys/cpu/cpu_netbsd_arm64.go | 173 + .../golang.org/x/sys/cpu/cpu_openbsd_arm64.go | 65 + .../golang.org/x/sys/cpu/cpu_openbsd_arm64.s | 11 + vendor/golang.org/x/sys/cpu/cpu_other_arm.go | 9 + .../golang.org/x/sys/cpu/cpu_other_arm64.go | 11 + .../golang.org/x/sys/cpu/cpu_other_mips64x.go | 11 + .../golang.org/x/sys/cpu/cpu_other_ppc64x.go | 12 + .../golang.org/x/sys/cpu/cpu_other_riscv64.go | 11 + vendor/golang.org/x/sys/cpu/cpu_other_x86.go | 11 + vendor/golang.org/x/sys/cpu/cpu_ppc64x.go | 16 + vendor/golang.org/x/sys/cpu/cpu_riscv64.go | 33 + vendor/golang.org/x/sys/cpu/cpu_s390x.go | 172 + vendor/golang.org/x/sys/cpu/cpu_s390x.s | 57 + vendor/golang.org/x/sys/cpu/cpu_wasm.go | 17 + vendor/golang.org/x/sys/cpu/cpu_windows.go | 26 + .../golang.org/x/sys/cpu/cpu_windows_arm64.go | 38 + vendor/golang.org/x/sys/cpu/cpu_x86.go | 236 + vendor/golang.org/x/sys/cpu/cpu_zos.go | 10 + vendor/golang.org/x/sys/cpu/cpu_zos_s390x.go | 25 + vendor/golang.org/x/sys/cpu/endian_big.go | 10 + vendor/golang.org/x/sys/cpu/endian_little.go | 10 + vendor/golang.org/x/sys/cpu/hwcap_linux.go | 71 + vendor/golang.org/x/sys/cpu/parse.go | 43 + .../x/sys/cpu/proc_cpuinfo_linux.go | 53 + vendor/golang.org/x/sys/cpu/runtime_auxv.go | 16 + .../x/sys/cpu/runtime_auxv_go121.go | 18 + .../golang.org/x/sys/cpu/syscall_aix_gccgo.go | 26 + .../x/sys/cpu/syscall_aix_ppc64_gc.go | 35 + .../x/sys/cpu/syscall_darwin_arm64_gc.go | 54 + .../x/sys/cpu/syscall_darwin_x86_gc.go | 98 + vendor/golang.org/x/sys/cpu/zcpu_windows.go | 48 + .../grpc/balancer/balancer.go | 4 +- .../grpc/balancer/pickfirst/pickfirst.go | 2 +- vendor/google.golang.org/grpc/dialoptions.go | 22 +- .../grpc/encoding/encoding.go | 3 + .../balancer/weight/weight.go | 36 +- .../grpc/internal/envconfig/envconfig.go | 94 +- .../grpc/internal/envconfig/xds.go | 10 + .../grpc/internal/grpcutil/encode_duration.go | 1 - .../grpc/internal/resolver/config_selector.go | 24 +- .../grpc/internal/stats/labels.go | 60 +- .../grpc/internal/transport/client_stream.go | 36 +- .../grpc/internal/transport/controlbuf.go | 45 +- .../grpc/internal/transport/flowcontrol.go | 10 +- .../grpc/internal/transport/handler_server.go | 4 +- .../grpc/internal/transport/http2_client.go | 46 +- .../grpc/internal/transport/http2_server.go | 16 +- .../internal/internal.go} | 20 +- .../grpc/internal/transport/transport.go | 5 + .../grpc_reflection_v1/reflection_grpc.pb.go | 2 +- .../reflection_grpc.pb.go | 2 +- vendor/google.golang.org/grpc/rpc_util.go | 42 +- vendor/google.golang.org/grpc/server.go | 52 +- vendor/google.golang.org/grpc/version.go | 2 +- .../types/known/structpb/struct.pb.go | 767 + .../types/known/wrapperspb/wrappers.pb.go | 648 + vendor/modules.txt | 81 +- 719 files changed, 415641 insertions(+), 192 deletions(-) create mode 100644 vendor/github.com/andybalholm/brotli/LICENSE create mode 100644 vendor/github.com/andybalholm/brotli/README.md create mode 100644 vendor/github.com/andybalholm/brotli/backward_references.go create mode 100644 vendor/github.com/andybalholm/brotli/backward_references_hq.go create mode 100644 vendor/github.com/andybalholm/brotli/bit_cost.go create mode 100644 vendor/github.com/andybalholm/brotli/bit_reader.go create mode 100644 vendor/github.com/andybalholm/brotli/bitwriter.go create mode 100644 vendor/github.com/andybalholm/brotli/block_splitter.go create mode 100644 vendor/github.com/andybalholm/brotli/block_splitter_command.go create mode 100644 vendor/github.com/andybalholm/brotli/block_splitter_distance.go create mode 100644 vendor/github.com/andybalholm/brotli/block_splitter_literal.go create mode 100644 vendor/github.com/andybalholm/brotli/brotli_bit_stream.go create mode 100644 vendor/github.com/andybalholm/brotli/cluster.go create mode 100644 vendor/github.com/andybalholm/brotli/cluster_command.go create mode 100644 vendor/github.com/andybalholm/brotli/cluster_distance.go create mode 100644 vendor/github.com/andybalholm/brotli/cluster_literal.go create mode 100644 vendor/github.com/andybalholm/brotli/command.go create mode 100644 vendor/github.com/andybalholm/brotli/compress_fragment.go create mode 100644 vendor/github.com/andybalholm/brotli/compress_fragment_two_pass.go create mode 100644 vendor/github.com/andybalholm/brotli/constants.go create mode 100644 vendor/github.com/andybalholm/brotli/context.go create mode 100644 vendor/github.com/andybalholm/brotli/decode.go create mode 100644 vendor/github.com/andybalholm/brotli/dictionary.go create mode 100644 vendor/github.com/andybalholm/brotli/dictionary_hash.go create mode 100644 vendor/github.com/andybalholm/brotli/encode.go create mode 100644 vendor/github.com/andybalholm/brotli/encoder.go create mode 100644 vendor/github.com/andybalholm/brotli/encoder_dict.go create mode 100644 vendor/github.com/andybalholm/brotli/entropy_encode.go create mode 100644 vendor/github.com/andybalholm/brotli/entropy_encode_static.go create mode 100644 vendor/github.com/andybalholm/brotli/fast_log.go create mode 100644 vendor/github.com/andybalholm/brotli/find_match_length.go create mode 100644 vendor/github.com/andybalholm/brotli/h10.go create mode 100644 vendor/github.com/andybalholm/brotli/h5.go create mode 100644 vendor/github.com/andybalholm/brotli/h6.go create mode 100644 vendor/github.com/andybalholm/brotli/hash.go create mode 100644 vendor/github.com/andybalholm/brotli/hash_composite.go create mode 100644 vendor/github.com/andybalholm/brotli/hash_forgetful_chain.go create mode 100644 vendor/github.com/andybalholm/brotli/hash_longest_match_quickly.go create mode 100644 vendor/github.com/andybalholm/brotli/hash_rolling.go create mode 100644 vendor/github.com/andybalholm/brotli/histogram.go create mode 100644 vendor/github.com/andybalholm/brotli/http.go create mode 100644 vendor/github.com/andybalholm/brotli/huffman.go create mode 100644 vendor/github.com/andybalholm/brotli/literal_cost.go create mode 100644 vendor/github.com/andybalholm/brotli/matchfinder/emitter.go create mode 100644 vendor/github.com/andybalholm/brotli/matchfinder/m0.go create mode 100644 vendor/github.com/andybalholm/brotli/matchfinder/m4.go create mode 100644 vendor/github.com/andybalholm/brotli/matchfinder/matchfinder.go create mode 100644 vendor/github.com/andybalholm/brotli/matchfinder/textencoder.go create mode 100644 vendor/github.com/andybalholm/brotli/memory.go create mode 100644 vendor/github.com/andybalholm/brotli/metablock.go create mode 100644 vendor/github.com/andybalholm/brotli/metablock_command.go create mode 100644 vendor/github.com/andybalholm/brotli/metablock_distance.go create mode 100644 vendor/github.com/andybalholm/brotli/metablock_literal.go create mode 100644 vendor/github.com/andybalholm/brotli/params.go create mode 100644 vendor/github.com/andybalholm/brotli/platform.go create mode 100644 vendor/github.com/andybalholm/brotli/prefix.go create mode 100644 vendor/github.com/andybalholm/brotli/prefix_dec.go create mode 100644 vendor/github.com/andybalholm/brotli/quality.go create mode 100644 vendor/github.com/andybalholm/brotli/reader.go create mode 100644 vendor/github.com/andybalholm/brotli/ringbuffer.go create mode 100644 vendor/github.com/andybalholm/brotli/state.go create mode 100644 vendor/github.com/andybalholm/brotli/static_dict.go create mode 100644 vendor/github.com/andybalholm/brotli/static_dict_lut.go create mode 100644 vendor/github.com/andybalholm/brotli/symbol_list.go create mode 100644 vendor/github.com/andybalholm/brotli/transform.go create mode 100644 vendor/github.com/andybalholm/brotli/utf8_util.go create mode 100644 vendor/github.com/andybalholm/brotli/util.go create mode 100644 vendor/github.com/andybalholm/brotli/write_bits.go create mode 100644 vendor/github.com/andybalholm/brotli/writer.go create mode 100644 vendor/github.com/klauspost/compress/.gitattributes create mode 100644 vendor/github.com/klauspost/compress/.gitignore create mode 100644 vendor/github.com/klauspost/compress/.goreleaser.yml create mode 100644 vendor/github.com/klauspost/compress/LICENSE create mode 100644 vendor/github.com/klauspost/compress/README.md create mode 100644 vendor/github.com/klauspost/compress/SECURITY.md create mode 100644 vendor/github.com/klauspost/compress/compressible.go create mode 100644 vendor/github.com/klauspost/compress/flate/deflate.go create mode 100644 vendor/github.com/klauspost/compress/flate/dict_decoder.go create mode 100644 vendor/github.com/klauspost/compress/flate/fast_encoder.go create mode 100644 vendor/github.com/klauspost/compress/flate/huffman_bit_writer.go create mode 100644 vendor/github.com/klauspost/compress/flate/huffman_code.go create mode 100644 vendor/github.com/klauspost/compress/flate/huffman_sortByFreq.go create mode 100644 vendor/github.com/klauspost/compress/flate/huffman_sortByLiteral.go create mode 100644 vendor/github.com/klauspost/compress/flate/inflate.go create mode 100644 vendor/github.com/klauspost/compress/flate/inflate_gen.go create mode 100644 vendor/github.com/klauspost/compress/flate/level1.go create mode 100644 vendor/github.com/klauspost/compress/flate/level2.go create mode 100644 vendor/github.com/klauspost/compress/flate/level3.go create mode 100644 vendor/github.com/klauspost/compress/flate/level4.go create mode 100644 vendor/github.com/klauspost/compress/flate/level5.go create mode 100644 vendor/github.com/klauspost/compress/flate/level6.go create mode 100644 vendor/github.com/klauspost/compress/flate/matchlen_generic.go create mode 100644 vendor/github.com/klauspost/compress/flate/regmask_amd64.go create mode 100644 vendor/github.com/klauspost/compress/flate/regmask_other.go create mode 100644 vendor/github.com/klauspost/compress/flate/stateless.go create mode 100644 vendor/github.com/klauspost/compress/flate/token.go create mode 100644 vendor/github.com/klauspost/compress/fse/README.md create mode 100644 vendor/github.com/klauspost/compress/fse/bitreader.go create mode 100644 vendor/github.com/klauspost/compress/fse/bitwriter.go create mode 100644 vendor/github.com/klauspost/compress/fse/bytereader.go create mode 100644 vendor/github.com/klauspost/compress/fse/compress.go create mode 100644 vendor/github.com/klauspost/compress/fse/decompress.go create mode 100644 vendor/github.com/klauspost/compress/fse/fse.go create mode 100644 vendor/github.com/klauspost/compress/gen.sh create mode 100644 vendor/github.com/klauspost/compress/gzip/gunzip.go create mode 100644 vendor/github.com/klauspost/compress/gzip/gzip.go create mode 100644 vendor/github.com/klauspost/compress/huff0/.gitignore create mode 100644 vendor/github.com/klauspost/compress/huff0/README.md create mode 100644 vendor/github.com/klauspost/compress/huff0/bitreader.go create mode 100644 vendor/github.com/klauspost/compress/huff0/bitwriter.go create mode 100644 vendor/github.com/klauspost/compress/huff0/compress.go create mode 100644 vendor/github.com/klauspost/compress/huff0/decompress.go create mode 100644 vendor/github.com/klauspost/compress/huff0/decompress_amd64.go create mode 100644 vendor/github.com/klauspost/compress/huff0/decompress_amd64.s create mode 100644 vendor/github.com/klauspost/compress/huff0/decompress_generic.go create mode 100644 vendor/github.com/klauspost/compress/huff0/huff0.go create mode 100644 vendor/github.com/klauspost/compress/internal/cpuinfo/cpuinfo.go create mode 100644 vendor/github.com/klauspost/compress/internal/cpuinfo/cpuinfo_amd64.go create mode 100644 vendor/github.com/klauspost/compress/internal/cpuinfo/cpuinfo_amd64.s create mode 100644 vendor/github.com/klauspost/compress/internal/le/le.go create mode 100644 vendor/github.com/klauspost/compress/internal/le/unsafe_disabled.go create mode 100644 vendor/github.com/klauspost/compress/internal/le/unsafe_enabled.go create mode 100644 vendor/github.com/klauspost/compress/internal/race/norace.go create mode 100644 vendor/github.com/klauspost/compress/internal/race/race.go create mode 100644 vendor/github.com/klauspost/compress/internal/snapref/LICENSE create mode 100644 vendor/github.com/klauspost/compress/internal/snapref/decode.go create mode 100644 vendor/github.com/klauspost/compress/internal/snapref/decode_other.go create mode 100644 vendor/github.com/klauspost/compress/internal/snapref/encode.go create mode 100644 vendor/github.com/klauspost/compress/internal/snapref/encode_other.go create mode 100644 vendor/github.com/klauspost/compress/internal/snapref/snappy.go create mode 100644 vendor/github.com/klauspost/compress/s2/.gitignore create mode 100644 vendor/github.com/klauspost/compress/s2/LICENSE create mode 100644 vendor/github.com/klauspost/compress/s2/README.md create mode 100644 vendor/github.com/klauspost/compress/s2/decode.go create mode 100644 vendor/github.com/klauspost/compress/s2/decode_amd64.s create mode 100644 vendor/github.com/klauspost/compress/s2/decode_arm64.s create mode 100644 vendor/github.com/klauspost/compress/s2/decode_asm.go create mode 100644 vendor/github.com/klauspost/compress/s2/decode_other.go create mode 100644 vendor/github.com/klauspost/compress/s2/dict.go create mode 100644 vendor/github.com/klauspost/compress/s2/encode.go create mode 100644 vendor/github.com/klauspost/compress/s2/encode_all.go create mode 100644 vendor/github.com/klauspost/compress/s2/encode_amd64.go create mode 100644 vendor/github.com/klauspost/compress/s2/encode_best.go create mode 100644 vendor/github.com/klauspost/compress/s2/encode_better.go create mode 100644 vendor/github.com/klauspost/compress/s2/encode_go.go create mode 100644 vendor/github.com/klauspost/compress/s2/encodeblock_amd64.go create mode 100644 vendor/github.com/klauspost/compress/s2/encodeblock_amd64.s create mode 100644 vendor/github.com/klauspost/compress/s2/hashtable_pool.go create mode 100644 vendor/github.com/klauspost/compress/s2/index.go create mode 100644 vendor/github.com/klauspost/compress/s2/lz4convert.go create mode 100644 vendor/github.com/klauspost/compress/s2/lz4sconvert.go create mode 100644 vendor/github.com/klauspost/compress/s2/reader.go create mode 100644 vendor/github.com/klauspost/compress/s2/s2.go create mode 100644 vendor/github.com/klauspost/compress/s2/writer.go create mode 100644 vendor/github.com/klauspost/compress/s2sx.mod create mode 100644 vendor/github.com/klauspost/compress/s2sx.sum create mode 100644 vendor/github.com/klauspost/compress/snappy/.gitignore create mode 100644 vendor/github.com/klauspost/compress/snappy/AUTHORS create mode 100644 vendor/github.com/klauspost/compress/snappy/CONTRIBUTORS create mode 100644 vendor/github.com/klauspost/compress/snappy/LICENSE create mode 100644 vendor/github.com/klauspost/compress/snappy/README.md create mode 100644 vendor/github.com/klauspost/compress/snappy/decode.go create mode 100644 vendor/github.com/klauspost/compress/snappy/encode.go create mode 100644 vendor/github.com/klauspost/compress/snappy/snappy.go create mode 100644 vendor/github.com/klauspost/compress/zstd/README.md create mode 100644 vendor/github.com/klauspost/compress/zstd/bitreader.go create mode 100644 vendor/github.com/klauspost/compress/zstd/bitwriter.go create mode 100644 vendor/github.com/klauspost/compress/zstd/blockdec.go create mode 100644 vendor/github.com/klauspost/compress/zstd/blockenc.go create mode 100644 vendor/github.com/klauspost/compress/zstd/blocktype_string.go create mode 100644 vendor/github.com/klauspost/compress/zstd/bytebuf.go create mode 100644 vendor/github.com/klauspost/compress/zstd/bytereader.go create mode 100644 vendor/github.com/klauspost/compress/zstd/decodeheader.go create mode 100644 vendor/github.com/klauspost/compress/zstd/decoder.go create mode 100644 vendor/github.com/klauspost/compress/zstd/decoder_options.go create mode 100644 vendor/github.com/klauspost/compress/zstd/dict.go create mode 100644 vendor/github.com/klauspost/compress/zstd/enc_base.go create mode 100644 vendor/github.com/klauspost/compress/zstd/enc_best.go create mode 100644 vendor/github.com/klauspost/compress/zstd/enc_better.go create mode 100644 vendor/github.com/klauspost/compress/zstd/enc_dfast.go create mode 100644 vendor/github.com/klauspost/compress/zstd/enc_fast.go create mode 100644 vendor/github.com/klauspost/compress/zstd/encoder.go create mode 100644 vendor/github.com/klauspost/compress/zstd/encoder_options.go create mode 100644 vendor/github.com/klauspost/compress/zstd/framedec.go create mode 100644 vendor/github.com/klauspost/compress/zstd/frameenc.go create mode 100644 vendor/github.com/klauspost/compress/zstd/fse_decoder.go create mode 100644 vendor/github.com/klauspost/compress/zstd/fse_decoder_amd64.go create mode 100644 vendor/github.com/klauspost/compress/zstd/fse_decoder_amd64.s create mode 100644 vendor/github.com/klauspost/compress/zstd/fse_decoder_generic.go create mode 100644 vendor/github.com/klauspost/compress/zstd/fse_encoder.go create mode 100644 vendor/github.com/klauspost/compress/zstd/fse_predefined.go create mode 100644 vendor/github.com/klauspost/compress/zstd/hash.go create mode 100644 vendor/github.com/klauspost/compress/zstd/history.go create mode 100644 vendor/github.com/klauspost/compress/zstd/internal/xxhash/LICENSE.txt create mode 100644 vendor/github.com/klauspost/compress/zstd/internal/xxhash/README.md create mode 100644 vendor/github.com/klauspost/compress/zstd/internal/xxhash/xxhash.go create mode 100644 vendor/github.com/klauspost/compress/zstd/internal/xxhash/xxhash_amd64.s create mode 100644 vendor/github.com/klauspost/compress/zstd/internal/xxhash/xxhash_arm64.s create mode 100644 vendor/github.com/klauspost/compress/zstd/internal/xxhash/xxhash_asm.go create mode 100644 vendor/github.com/klauspost/compress/zstd/internal/xxhash/xxhash_other.go create mode 100644 vendor/github.com/klauspost/compress/zstd/internal/xxhash/xxhash_safe.go create mode 100644 vendor/github.com/klauspost/compress/zstd/matchlen_amd64.go create mode 100644 vendor/github.com/klauspost/compress/zstd/matchlen_amd64.s create mode 100644 vendor/github.com/klauspost/compress/zstd/matchlen_generic.go create mode 100644 vendor/github.com/klauspost/compress/zstd/seqdec.go create mode 100644 vendor/github.com/klauspost/compress/zstd/seqdec_amd64.go create mode 100644 vendor/github.com/klauspost/compress/zstd/seqdec_amd64.s create mode 100644 vendor/github.com/klauspost/compress/zstd/seqdec_generic.go create mode 100644 vendor/github.com/klauspost/compress/zstd/seqenc.go create mode 100644 vendor/github.com/klauspost/compress/zstd/simple_go124.go create mode 100644 vendor/github.com/klauspost/compress/zstd/snappy.go create mode 100644 vendor/github.com/klauspost/compress/zstd/zip.go create mode 100644 vendor/github.com/klauspost/compress/zstd/zstd.go create mode 100644 vendor/github.com/netobserv/flowlogs-pipeline/pkg/api/redacted.go create mode 100644 vendor/github.com/netobserv/flowlogs-pipeline/pkg/api/write_flowbuffer.go create mode 100644 vendor/github.com/netobserv/flowlogs-pipeline/pkg/schema/flow_v1.go create mode 100644 vendor/github.com/netobserv/flowlogs-pipeline/pkg/schema/parquet_encode.go create mode 100644 vendor/github.com/parquet-go/bitpack/.gitignore create mode 100644 vendor/github.com/parquet-go/bitpack/LICENSE create mode 100644 vendor/github.com/parquet-go/bitpack/README.md create mode 100644 vendor/github.com/parquet-go/bitpack/bitpack.go create mode 100644 vendor/github.com/parquet-go/bitpack/masks_int32_amd64.s create mode 100644 vendor/github.com/parquet-go/bitpack/masks_int64_amd64.s create mode 100644 vendor/github.com/parquet-go/bitpack/pack.go create mode 100644 vendor/github.com/parquet-go/bitpack/pack_arm64.go create mode 100644 vendor/github.com/parquet-go/bitpack/pack_int32_arm64.s create mode 100644 vendor/github.com/parquet-go/bitpack/pack_int64_arm64.s create mode 100644 vendor/github.com/parquet-go/bitpack/pack_purego.go create mode 100644 vendor/github.com/parquet-go/bitpack/unpack.go create mode 100644 vendor/github.com/parquet-go/bitpack/unpack_int32_1bit_arm64.s create mode 100644 vendor/github.com/parquet-go/bitpack/unpack_int32_2bit_arm64.s create mode 100644 vendor/github.com/parquet-go/bitpack/unpack_int32_4bit_arm64.s create mode 100644 vendor/github.com/parquet-go/bitpack/unpack_int32_8bit_arm64.s create mode 100644 vendor/github.com/parquet-go/bitpack/unpack_int32_amd64.go create mode 100644 vendor/github.com/parquet-go/bitpack/unpack_int32_amd64.s create mode 100644 vendor/github.com/parquet-go/bitpack/unpack_int32_arm64.go create mode 100644 vendor/github.com/parquet-go/bitpack/unpack_int32_arm64.s create mode 100644 vendor/github.com/parquet-go/bitpack/unpack_int32_be.go create mode 100644 vendor/github.com/parquet-go/bitpack/unpack_int32_le.go create mode 100644 vendor/github.com/parquet-go/bitpack/unpack_int32_purego.go create mode 100644 vendor/github.com/parquet-go/bitpack/unpack_int64_1bit_amd64.s create mode 100644 vendor/github.com/parquet-go/bitpack/unpack_int64_1bit_arm64.s create mode 100644 vendor/github.com/parquet-go/bitpack/unpack_int64_2bit_amd64.s create mode 100644 vendor/github.com/parquet-go/bitpack/unpack_int64_2bit_arm64.s create mode 100644 vendor/github.com/parquet-go/bitpack/unpack_int64_4bit_amd64.s create mode 100644 vendor/github.com/parquet-go/bitpack/unpack_int64_4bit_arm64.s create mode 100644 vendor/github.com/parquet-go/bitpack/unpack_int64_8bit_amd64.s create mode 100644 vendor/github.com/parquet-go/bitpack/unpack_int64_8bit_arm64.s create mode 100644 vendor/github.com/parquet-go/bitpack/unpack_int64_amd64.go create mode 100644 vendor/github.com/parquet-go/bitpack/unpack_int64_amd64.s create mode 100644 vendor/github.com/parquet-go/bitpack/unpack_int64_arm64.go create mode 100644 vendor/github.com/parquet-go/bitpack/unpack_int64_arm64.s create mode 100644 vendor/github.com/parquet-go/bitpack/unpack_int64_purego.go create mode 100644 vendor/github.com/parquet-go/bitpack/unpack_neon_macros_arm64.h create mode 100644 vendor/github.com/parquet-go/bitpack/unsafecast/unsafecast.go create mode 100644 vendor/github.com/parquet-go/jsonlite/.gitignore create mode 100644 vendor/github.com/parquet-go/jsonlite/LICENSE create mode 100644 vendor/github.com/parquet-go/jsonlite/README.md create mode 100644 vendor/github.com/parquet-go/jsonlite/convert.go create mode 100644 vendor/github.com/parquet-go/jsonlite/doc.go create mode 100644 vendor/github.com/parquet-go/jsonlite/iterator.go create mode 100644 vendor/github.com/parquet-go/jsonlite/parse.go create mode 100644 vendor/github.com/parquet-go/jsonlite/quote.go create mode 100644 vendor/github.com/parquet-go/jsonlite/unquote.go create mode 100644 vendor/github.com/parquet-go/jsonlite/valid.go create mode 100644 vendor/github.com/parquet-go/jsonlite/value.go create mode 100644 vendor/github.com/parquet-go/parquet-go/.CLAUDE.md create mode 100644 vendor/github.com/parquet-go/parquet-go/.gitattributes create mode 100644 vendor/github.com/parquet-go/parquet-go/.gitignore create mode 100644 vendor/github.com/parquet-go/parquet-go/.mailmap create mode 100644 vendor/github.com/parquet-go/parquet-go/.words create mode 100644 vendor/github.com/parquet-go/parquet-go/AUTHORS.txt create mode 100644 vendor/github.com/parquet-go/parquet-go/CHANGELOG.md create mode 100644 vendor/github.com/parquet-go/parquet-go/CODEOWNERS create mode 100644 vendor/github.com/parquet-go/parquet-go/CODE_OF_CONDUCT.md create mode 100644 vendor/github.com/parquet-go/parquet-go/CONTRIBUTING.md create mode 100644 vendor/github.com/parquet-go/parquet-go/LICENSE create mode 100644 vendor/github.com/parquet-go/parquet-go/Makefile create mode 100644 vendor/github.com/parquet-go/parquet-go/README.md create mode 100644 vendor/github.com/parquet-go/parquet-go/allocator.go create mode 100644 vendor/github.com/parquet-go/parquet-go/array.go create mode 100644 vendor/github.com/parquet-go/parquet-go/bitmap.go create mode 100644 vendor/github.com/parquet-go/parquet-go/bloom.go create mode 100644 vendor/github.com/parquet-go/parquet-go/bloom/block.go create mode 100644 vendor/github.com/parquet-go/parquet-go/bloom/block_amd64.go create mode 100644 vendor/github.com/parquet-go/parquet-go/bloom/block_amd64.s create mode 100644 vendor/github.com/parquet-go/parquet-go/bloom/block_default.go create mode 100644 vendor/github.com/parquet-go/parquet-go/bloom/block_optimized.go create mode 100644 vendor/github.com/parquet-go/parquet-go/bloom/bloom.go create mode 100644 vendor/github.com/parquet-go/parquet-go/bloom/filter.go create mode 100644 vendor/github.com/parquet-go/parquet-go/bloom/filter_amd64.go create mode 100644 vendor/github.com/parquet-go/parquet-go/bloom/filter_amd64.s create mode 100644 vendor/github.com/parquet-go/parquet-go/bloom/filter_default.go create mode 100644 vendor/github.com/parquet-go/parquet-go/bloom/hash.go create mode 100644 vendor/github.com/parquet-go/parquet-go/bloom/xxhash/LICENSE create mode 100644 vendor/github.com/parquet-go/parquet-go/bloom/xxhash/sum64uint.go create mode 100644 vendor/github.com/parquet-go/parquet-go/bloom/xxhash/sum64uint_amd64.go create mode 100644 vendor/github.com/parquet-go/parquet-go/bloom/xxhash/sum64uint_amd64.s create mode 100644 vendor/github.com/parquet-go/parquet-go/bloom/xxhash/sum64uint_purego.go create mode 100644 vendor/github.com/parquet-go/parquet-go/bloom/xxhash/xxhash.go create mode 100644 vendor/github.com/parquet-go/parquet-go/bloom/xxhash/xxhash_amd64.go create mode 100644 vendor/github.com/parquet-go/parquet-go/bloom/xxhash/xxhash_amd64.s create mode 100644 vendor/github.com/parquet-go/parquet-go/bloom/xxhash/xxhash_purego.go create mode 100644 vendor/github.com/parquet-go/parquet-go/bloom_be.go create mode 100644 vendor/github.com/parquet-go/parquet-go/bloom_le.go create mode 100644 vendor/github.com/parquet-go/parquet-go/buf.gen.yaml create mode 100644 vendor/github.com/parquet-go/parquet-go/buf.yaml create mode 100644 vendor/github.com/parquet-go/parquet-go/buffer.go create mode 100644 vendor/github.com/parquet-go/parquet-go/buffer_pool.go create mode 100644 vendor/github.com/parquet-go/parquet-go/column.go create mode 100644 vendor/github.com/parquet-go/parquet-go/column_buffer.go create mode 100644 vendor/github.com/parquet-go/parquet-go/column_buffer_amd64.go create mode 100644 vendor/github.com/parquet-go/parquet-go/column_buffer_amd64.s create mode 100644 vendor/github.com/parquet-go/parquet-go/column_buffer_be128.go create mode 100644 vendor/github.com/parquet-go/parquet-go/column_buffer_boolean.go create mode 100644 vendor/github.com/parquet-go/parquet-go/column_buffer_byte_array.go create mode 100644 vendor/github.com/parquet-go/parquet-go/column_buffer_double.go create mode 100644 vendor/github.com/parquet-go/parquet-go/column_buffer_fixed_len_byte_array.go create mode 100644 vendor/github.com/parquet-go/parquet-go/column_buffer_float.go create mode 100644 vendor/github.com/parquet-go/parquet-go/column_buffer_geometry.go create mode 100644 vendor/github.com/parquet-go/parquet-go/column_buffer_int32.go create mode 100644 vendor/github.com/parquet-go/parquet-go/column_buffer_int64.go create mode 100644 vendor/github.com/parquet-go/parquet-go/column_buffer_int96.go create mode 100644 vendor/github.com/parquet-go/parquet-go/column_buffer_json.go create mode 100644 vendor/github.com/parquet-go/parquet-go/column_buffer_null.go create mode 100644 vendor/github.com/parquet-go/parquet-go/column_buffer_optional.go create mode 100644 vendor/github.com/parquet-go/parquet-go/column_buffer_proto.go create mode 100644 vendor/github.com/parquet-go/parquet-go/column_buffer_proto_any.go create mode 100644 vendor/github.com/parquet-go/parquet-go/column_buffer_proto_purego.go create mode 100644 vendor/github.com/parquet-go/parquet-go/column_buffer_purego.go create mode 100644 vendor/github.com/parquet-go/parquet-go/column_buffer_reflect.go create mode 100644 vendor/github.com/parquet-go/parquet-go/column_buffer_repeated.go create mode 100644 vendor/github.com/parquet-go/parquet-go/column_buffer_uint32.go create mode 100644 vendor/github.com/parquet-go/parquet-go/column_buffer_uint64.go create mode 100644 vendor/github.com/parquet-go/parquet-go/column_buffer_variant.go create mode 100644 vendor/github.com/parquet-go/parquet-go/column_buffer_write.go create mode 100644 vendor/github.com/parquet-go/parquet-go/column_chunk.go create mode 100644 vendor/github.com/parquet-go/parquet-go/column_index.go create mode 100644 vendor/github.com/parquet-go/parquet-go/column_index_be.go create mode 100644 vendor/github.com/parquet-go/parquet-go/column_index_le.go create mode 100644 vendor/github.com/parquet-go/parquet-go/column_mapping.go create mode 100644 vendor/github.com/parquet-go/parquet-go/column_path.go create mode 100644 vendor/github.com/parquet-go/parquet-go/compare.go create mode 100644 vendor/github.com/parquet-go/parquet-go/compress.go create mode 100644 vendor/github.com/parquet-go/parquet-go/compress/brotli/brotli.go create mode 100644 vendor/github.com/parquet-go/parquet-go/compress/compress.go create mode 100644 vendor/github.com/parquet-go/parquet-go/compress/gzip/gzip.go create mode 100644 vendor/github.com/parquet-go/parquet-go/compress/lz4/lz4.go create mode 100644 vendor/github.com/parquet-go/parquet-go/compress/snappy/snappy.go create mode 100644 vendor/github.com/parquet-go/parquet-go/compress/uncompressed/uncompressed.go create mode 100644 vendor/github.com/parquet-go/parquet-go/compress/zstd/zstd.go create mode 100644 vendor/github.com/parquet-go/parquet-go/config.go create mode 100644 vendor/github.com/parquet-go/parquet-go/convert.go create mode 100644 vendor/github.com/parquet-go/parquet-go/dedupe.go create mode 100644 vendor/github.com/parquet-go/parquet-go/deprecated/int96.go create mode 100644 vendor/github.com/parquet-go/parquet-go/deprecated/parquet.go create mode 100644 vendor/github.com/parquet-go/parquet-go/dictionary.go create mode 100644 vendor/github.com/parquet-go/parquet-go/dictionary_amd64.go create mode 100644 vendor/github.com/parquet-go/parquet-go/dictionary_amd64.s create mode 100644 vendor/github.com/parquet-go/parquet-go/dictionary_be128.go create mode 100644 vendor/github.com/parquet-go/parquet-go/dictionary_boolean.go create mode 100644 vendor/github.com/parquet-go/parquet-go/dictionary_byte_array.go create mode 100644 vendor/github.com/parquet-go/parquet-go/dictionary_double.go create mode 100644 vendor/github.com/parquet-go/parquet-go/dictionary_fixed_len_byte_array.go create mode 100644 vendor/github.com/parquet-go/parquet-go/dictionary_float.go create mode 100644 vendor/github.com/parquet-go/parquet-go/dictionary_int32.go create mode 100644 vendor/github.com/parquet-go/parquet-go/dictionary_int64.go create mode 100644 vendor/github.com/parquet-go/parquet-go/dictionary_int96.go create mode 100644 vendor/github.com/parquet-go/parquet-go/dictionary_null.go create mode 100644 vendor/github.com/parquet-go/parquet-go/dictionary_purego.go create mode 100644 vendor/github.com/parquet-go/parquet-go/dictionary_uint32.go create mode 100644 vendor/github.com/parquet-go/parquet-go/dictionary_uint64.go create mode 100644 vendor/github.com/parquet-go/parquet-go/encoding.go create mode 100644 vendor/github.com/parquet-go/parquet-go/encoding/bitpacked/bitpacked.go create mode 100644 vendor/github.com/parquet-go/parquet-go/encoding/bytestreamsplit/bytestreamsplit.go create mode 100644 vendor/github.com/parquet-go/parquet-go/encoding/bytestreamsplit/bytestreamsplit_amd64.go create mode 100644 vendor/github.com/parquet-go/parquet-go/encoding/bytestreamsplit/bytestreamsplit_amd64.s create mode 100644 vendor/github.com/parquet-go/parquet-go/encoding/bytestreamsplit/bytestreamsplit_fixedlen.go create mode 100644 vendor/github.com/parquet-go/parquet-go/encoding/bytestreamsplit/bytestreamsplit_purego.go create mode 100644 vendor/github.com/parquet-go/parquet-go/encoding/delta/binary_packed.go create mode 100644 vendor/github.com/parquet-go/parquet-go/encoding/delta/binary_packed_amd64.go create mode 100644 vendor/github.com/parquet-go/parquet-go/encoding/delta/binary_packed_amd64.s create mode 100644 vendor/github.com/parquet-go/parquet-go/encoding/delta/binary_packed_purego.go create mode 100644 vendor/github.com/parquet-go/parquet-go/encoding/delta/byte_array.go create mode 100644 vendor/github.com/parquet-go/parquet-go/encoding/delta/byte_array_amd64.go create mode 100644 vendor/github.com/parquet-go/parquet-go/encoding/delta/byte_array_amd64.s create mode 100644 vendor/github.com/parquet-go/parquet-go/encoding/delta/byte_array_purego.go create mode 100644 vendor/github.com/parquet-go/parquet-go/encoding/delta/delta.go create mode 100644 vendor/github.com/parquet-go/parquet-go/encoding/delta/delta_amd64.go create mode 100644 vendor/github.com/parquet-go/parquet-go/encoding/delta/delta_amd64.s create mode 100644 vendor/github.com/parquet-go/parquet-go/encoding/delta/length_byte_array.go create mode 100644 vendor/github.com/parquet-go/parquet-go/encoding/delta/length_byte_array_amd64.go create mode 100644 vendor/github.com/parquet-go/parquet-go/encoding/delta/length_byte_array_amd64.s create mode 100644 vendor/github.com/parquet-go/parquet-go/encoding/delta/length_byte_array_purego.go create mode 100644 vendor/github.com/parquet-go/parquet-go/encoding/encoding.go create mode 100644 vendor/github.com/parquet-go/parquet-go/encoding/notsupported.go create mode 100644 vendor/github.com/parquet-go/parquet-go/encoding/plain/dictionary.go create mode 100644 vendor/github.com/parquet-go/parquet-go/encoding/plain/plain.go create mode 100644 vendor/github.com/parquet-go/parquet-go/encoding/plain/plain_be.go create mode 100644 vendor/github.com/parquet-go/parquet-go/encoding/plain/plain_le.go create mode 100644 vendor/github.com/parquet-go/parquet-go/encoding/rle/dictionary.go create mode 100644 vendor/github.com/parquet-go/parquet-go/encoding/rle/rle.go create mode 100644 vendor/github.com/parquet-go/parquet-go/encoding/rle/rle_amd64.go create mode 100644 vendor/github.com/parquet-go/parquet-go/encoding/rle/rle_amd64.s create mode 100644 vendor/github.com/parquet-go/parquet-go/encoding/rle/rle_purego.go create mode 100644 vendor/github.com/parquet-go/parquet-go/encoding/thrift/LICENSE create mode 100644 vendor/github.com/parquet-go/parquet-go/encoding/thrift/binary.go create mode 100644 vendor/github.com/parquet-go/parquet-go/encoding/thrift/compact.go create mode 100644 vendor/github.com/parquet-go/parquet-go/encoding/thrift/debug.go create mode 100644 vendor/github.com/parquet-go/parquet-go/encoding/thrift/decode.go create mode 100644 vendor/github.com/parquet-go/parquet-go/encoding/thrift/encode.go create mode 100644 vendor/github.com/parquet-go/parquet-go/encoding/thrift/error.go create mode 100644 vendor/github.com/parquet-go/parquet-go/encoding/thrift/list.go create mode 100644 vendor/github.com/parquet-go/parquet-go/encoding/thrift/null.go create mode 100644 vendor/github.com/parquet-go/parquet-go/encoding/thrift/protocol.go create mode 100644 vendor/github.com/parquet-go/parquet-go/encoding/thrift/struct.go create mode 100644 vendor/github.com/parquet-go/parquet-go/encoding/thrift/thrift.go create mode 100644 vendor/github.com/parquet-go/parquet-go/encoding/thrift/unsafe.go create mode 100644 vendor/github.com/parquet-go/parquet-go/encoding/thrift/value.go create mode 100644 vendor/github.com/parquet-go/parquet-go/encoding/values.go create mode 100644 vendor/github.com/parquet-go/parquet-go/encrypt.go create mode 100644 vendor/github.com/parquet-go/parquet-go/errors.go create mode 100644 vendor/github.com/parquet-go/parquet-go/file.go create mode 100644 vendor/github.com/parquet-go/parquet-go/filter.go create mode 100644 vendor/github.com/parquet-go/parquet-go/format/parquet.go create mode 100644 vendor/github.com/parquet-go/parquet-go/geospatial_statistics.go create mode 100644 vendor/github.com/parquet-go/parquet-go/go.tools.mod create mode 100644 vendor/github.com/parquet-go/parquet-go/go.tools.sum create mode 100644 vendor/github.com/parquet-go/parquet-go/hashprobe/aeshash/aeshash.go create mode 100644 vendor/github.com/parquet-go/parquet-go/hashprobe/aeshash/aeshash_amd64.go create mode 100644 vendor/github.com/parquet-go/parquet-go/hashprobe/aeshash/aeshash_amd64.s create mode 100644 vendor/github.com/parquet-go/parquet-go/hashprobe/aeshash/aeshash_purego.go create mode 100644 vendor/github.com/parquet-go/parquet-go/hashprobe/hashprobe.go create mode 100644 vendor/github.com/parquet-go/parquet-go/hashprobe/hashprobe_amd64.go create mode 100644 vendor/github.com/parquet-go/parquet-go/hashprobe/hashprobe_amd64.s create mode 100644 vendor/github.com/parquet-go/parquet-go/hashprobe/hashprobe_purego.go create mode 100644 vendor/github.com/parquet-go/parquet-go/hashprobe/wyhash/wyhash.go create mode 100644 vendor/github.com/parquet-go/parquet-go/hashprobe/wyhash/wyhash_amd64.go create mode 100644 vendor/github.com/parquet-go/parquet-go/hashprobe/wyhash/wyhash_amd64.s create mode 100644 vendor/github.com/parquet-go/parquet-go/hashprobe/wyhash/wyhash_purego.go create mode 100644 vendor/github.com/parquet-go/parquet-go/internal/bytealg/broadcast_amd64.go create mode 100644 vendor/github.com/parquet-go/parquet-go/internal/bytealg/broadcast_amd64.s create mode 100644 vendor/github.com/parquet-go/parquet-go/internal/bytealg/broadcast_purego.go create mode 100644 vendor/github.com/parquet-go/parquet-go/internal/bytealg/bytealg.go create mode 100644 vendor/github.com/parquet-go/parquet-go/internal/bytealg/bytealg_amd64.go create mode 100644 vendor/github.com/parquet-go/parquet-go/internal/bytealg/count_amd64.go create mode 100644 vendor/github.com/parquet-go/parquet-go/internal/bytealg/count_amd64.s create mode 100644 vendor/github.com/parquet-go/parquet-go/internal/bytealg/count_purego.go create mode 100644 vendor/github.com/parquet-go/parquet-go/internal/debug/debug.go create mode 100644 vendor/github.com/parquet-go/parquet-go/internal/debug/finalizer_off.go create mode 100644 vendor/github.com/parquet-go/parquet-go/internal/debug/finalizer_on.go create mode 100644 vendor/github.com/parquet-go/parquet-go/internal/memory/buffer.go create mode 100644 vendor/github.com/parquet-go/parquet-go/internal/memory/chunk_buffer.go create mode 100644 vendor/github.com/parquet-go/parquet-go/internal/memory/memory.go create mode 100644 vendor/github.com/parquet-go/parquet-go/internal/memory/pool.go create mode 100644 vendor/github.com/parquet-go/parquet-go/internal/memory/slice_buffer.go create mode 100644 vendor/github.com/parquet-go/parquet-go/internal/unsafecast/unsafecast.go create mode 100644 vendor/github.com/parquet-go/parquet-go/level.go create mode 100644 vendor/github.com/parquet-go/parquet-go/limits.go create mode 100644 vendor/github.com/parquet-go/parquet-go/merge.go create mode 100644 vendor/github.com/parquet-go/parquet-go/multi_row_group.go create mode 100644 vendor/github.com/parquet-go/parquet-go/node.go create mode 100644 vendor/github.com/parquet-go/parquet-go/null.go create mode 100644 vendor/github.com/parquet-go/parquet-go/null_amd64.go create mode 100644 vendor/github.com/parquet-go/parquet-go/null_amd64.s create mode 100644 vendor/github.com/parquet-go/parquet-go/null_purego.go create mode 100644 vendor/github.com/parquet-go/parquet-go/offset_index.go create mode 100644 vendor/github.com/parquet-go/parquet-go/order.go create mode 100644 vendor/github.com/parquet-go/parquet-go/order_amd64.go create mode 100644 vendor/github.com/parquet-go/parquet-go/order_amd64.s create mode 100644 vendor/github.com/parquet-go/parquet-go/order_purego.go create mode 100644 vendor/github.com/parquet-go/parquet-go/page.go create mode 100644 vendor/github.com/parquet-go/parquet-go/page_be128.go create mode 100644 vendor/github.com/parquet-go/parquet-go/page_boolean.go create mode 100644 vendor/github.com/parquet-go/parquet-go/page_bounds.go create mode 100644 vendor/github.com/parquet-go/parquet-go/page_bounds_amd64.go create mode 100644 vendor/github.com/parquet-go/parquet-go/page_bounds_amd64.s create mode 100644 vendor/github.com/parquet-go/parquet-go/page_bounds_purego.go create mode 100644 vendor/github.com/parquet-go/parquet-go/page_byte_array.go create mode 100644 vendor/github.com/parquet-go/parquet-go/page_double.go create mode 100644 vendor/github.com/parquet-go/parquet-go/page_fixed_len_byte_array.go create mode 100644 vendor/github.com/parquet-go/parquet-go/page_float.go create mode 100644 vendor/github.com/parquet-go/parquet-go/page_header.go create mode 100644 vendor/github.com/parquet-go/parquet-go/page_int32.go create mode 100644 vendor/github.com/parquet-go/parquet-go/page_int64.go create mode 100644 vendor/github.com/parquet-go/parquet-go/page_int96.go create mode 100644 vendor/github.com/parquet-go/parquet-go/page_max.go create mode 100644 vendor/github.com/parquet-go/parquet-go/page_max_amd64.go create mode 100644 vendor/github.com/parquet-go/parquet-go/page_max_amd64.s create mode 100644 vendor/github.com/parquet-go/parquet-go/page_max_purego.go create mode 100644 vendor/github.com/parquet-go/parquet-go/page_min.go create mode 100644 vendor/github.com/parquet-go/parquet-go/page_min_amd64.go create mode 100644 vendor/github.com/parquet-go/parquet-go/page_min_amd64.s create mode 100644 vendor/github.com/parquet-go/parquet-go/page_min_purego.go create mode 100644 vendor/github.com/parquet-go/parquet-go/page_null.go create mode 100644 vendor/github.com/parquet-go/parquet-go/page_optional.go create mode 100644 vendor/github.com/parquet-go/parquet-go/page_repeated.go create mode 100644 vendor/github.com/parquet-go/parquet-go/page_uint32.go create mode 100644 vendor/github.com/parquet-go/parquet-go/page_uint64.go create mode 100644 vendor/github.com/parquet-go/parquet-go/parquet.go create mode 100644 vendor/github.com/parquet-go/parquet-go/parquet_amd64.go create mode 100644 vendor/github.com/parquet-go/parquet-go/print.go create mode 100644 vendor/github.com/parquet-go/parquet-go/reader.go create mode 100644 vendor/github.com/parquet-go/parquet-go/row.go create mode 100644 vendor/github.com/parquet-go/parquet-go/row_buffer.go create mode 100644 vendor/github.com/parquet-go/parquet-go/row_builder.go create mode 100644 vendor/github.com/parquet-go/parquet-go/row_group.go create mode 100644 vendor/github.com/parquet-go/parquet-go/row_variant.go create mode 100644 vendor/github.com/parquet-go/parquet-go/scan.go create mode 100644 vendor/github.com/parquet-go/parquet-go/schema.go create mode 100644 vendor/github.com/parquet-go/parquet-go/schema_int.go create mode 100644 vendor/github.com/parquet-go/parquet-go/search.go create mode 100644 vendor/github.com/parquet-go/parquet-go/sorting.go create mode 100644 vendor/github.com/parquet-go/parquet-go/sparse/array.go create mode 100644 vendor/github.com/parquet-go/parquet-go/sparse/gather.go create mode 100644 vendor/github.com/parquet-go/parquet-go/sparse/gather_amd64.go create mode 100644 vendor/github.com/parquet-go/parquet-go/sparse/gather_amd64.s create mode 100644 vendor/github.com/parquet-go/parquet-go/sparse/gather_purego.go create mode 100644 vendor/github.com/parquet-go/parquet-go/sparse/sparse.go create mode 100644 vendor/github.com/parquet-go/parquet-go/tags.go create mode 100644 vendor/github.com/parquet-go/parquet-go/transform.go create mode 100644 vendor/github.com/parquet-go/parquet-go/type.go create mode 100644 vendor/github.com/parquet-go/parquet-go/type_boolean.go create mode 100644 vendor/github.com/parquet-go/parquet-go/type_bson.go create mode 100644 vendor/github.com/parquet-go/parquet-go/type_byte_array.go create mode 100644 vendor/github.com/parquet-go/parquet-go/type_date.go create mode 100644 vendor/github.com/parquet-go/parquet-go/type_decimal.go create mode 100644 vendor/github.com/parquet-go/parquet-go/type_double.go create mode 100644 vendor/github.com/parquet-go/parquet-go/type_enum.go create mode 100644 vendor/github.com/parquet-go/parquet-go/type_fixed_len_byte_array.go create mode 100644 vendor/github.com/parquet-go/parquet-go/type_float.go create mode 100644 vendor/github.com/parquet-go/parquet-go/type_geography.go create mode 100644 vendor/github.com/parquet-go/parquet-go/type_geometry.go create mode 100644 vendor/github.com/parquet-go/parquet-go/type_group.go create mode 100644 vendor/github.com/parquet-go/parquet-go/type_int32.go create mode 100644 vendor/github.com/parquet-go/parquet-go/type_int64.go create mode 100644 vendor/github.com/parquet-go/parquet-go/type_int96.go create mode 100644 vendor/github.com/parquet-go/parquet-go/type_int_logical.go create mode 100644 vendor/github.com/parquet-go/parquet-go/type_interval.go create mode 100644 vendor/github.com/parquet-go/parquet-go/type_json.go create mode 100644 vendor/github.com/parquet-go/parquet-go/type_list.go create mode 100644 vendor/github.com/parquet-go/parquet-go/type_map.go create mode 100644 vendor/github.com/parquet-go/parquet-go/type_null.go create mode 100644 vendor/github.com/parquet-go/parquet-go/type_string.go create mode 100644 vendor/github.com/parquet-go/parquet-go/type_time.go create mode 100644 vendor/github.com/parquet-go/parquet-go/type_timestamp.go create mode 100644 vendor/github.com/parquet-go/parquet-go/type_uuid.go create mode 100644 vendor/github.com/parquet-go/parquet-go/type_variant.go create mode 100644 vendor/github.com/parquet-go/parquet-go/value.go create mode 100644 vendor/github.com/parquet-go/parquet-go/value_amd64.go create mode 100644 vendor/github.com/parquet-go/parquet-go/value_amd64.s create mode 100644 vendor/github.com/parquet-go/parquet-go/value_be.go create mode 100644 vendor/github.com/parquet-go/parquet-go/value_le.go create mode 100644 vendor/github.com/parquet-go/parquet-go/values_purego.go create mode 100644 vendor/github.com/parquet-go/parquet-go/variant/decoding.go create mode 100644 vendor/github.com/parquet-go/parquet-go/variant/encoding.go create mode 100644 vendor/github.com/parquet-go/parquet-go/variant/marshal.go create mode 100644 vendor/github.com/parquet-go/parquet-go/variant/metadata.go create mode 100644 vendor/github.com/parquet-go/parquet-go/variant/types.go create mode 100644 vendor/github.com/parquet-go/parquet-go/variant/value.go create mode 100644 vendor/github.com/parquet-go/parquet-go/writer.go create mode 100644 vendor/github.com/parquet-go/parquet-go/writer_statistics.go create mode 100644 vendor/github.com/pierrec/lz4/v4/.gitignore create mode 100644 vendor/github.com/pierrec/lz4/v4/LICENSE create mode 100644 vendor/github.com/pierrec/lz4/v4/README.md create mode 100644 vendor/github.com/pierrec/lz4/v4/compressing_reader.go create mode 100644 vendor/github.com/pierrec/lz4/v4/internal/lz4block/block.go create mode 100644 vendor/github.com/pierrec/lz4/v4/internal/lz4block/blocks.go create mode 100644 vendor/github.com/pierrec/lz4/v4/internal/lz4block/decode_amd64.s create mode 100644 vendor/github.com/pierrec/lz4/v4/internal/lz4block/decode_arm.s create mode 100644 vendor/github.com/pierrec/lz4/v4/internal/lz4block/decode_arm64.s create mode 100644 vendor/github.com/pierrec/lz4/v4/internal/lz4block/decode_asm.go create mode 100644 vendor/github.com/pierrec/lz4/v4/internal/lz4block/decode_other.go create mode 100644 vendor/github.com/pierrec/lz4/v4/internal/lz4errors/errors.go create mode 100644 vendor/github.com/pierrec/lz4/v4/internal/lz4stream/block.go create mode 100644 vendor/github.com/pierrec/lz4/v4/internal/lz4stream/frame.go create mode 100644 vendor/github.com/pierrec/lz4/v4/internal/lz4stream/frame_gen.go create mode 100644 vendor/github.com/pierrec/lz4/v4/internal/xxh32/xxh32zero.go create mode 100644 vendor/github.com/pierrec/lz4/v4/internal/xxh32/xxh32zero_arm.go create mode 100644 vendor/github.com/pierrec/lz4/v4/internal/xxh32/xxh32zero_arm.s create mode 100644 vendor/github.com/pierrec/lz4/v4/internal/xxh32/xxh32zero_other.go create mode 100644 vendor/github.com/pierrec/lz4/v4/lz4.go create mode 100644 vendor/github.com/pierrec/lz4/v4/options.go create mode 100644 vendor/github.com/pierrec/lz4/v4/options_gen.go create mode 100644 vendor/github.com/pierrec/lz4/v4/reader.go create mode 100644 vendor/github.com/pierrec/lz4/v4/state.go create mode 100644 vendor/github.com/pierrec/lz4/v4/state_gen.go create mode 100644 vendor/github.com/pierrec/lz4/v4/writer.go create mode 100644 vendor/github.com/stretchr/testify/require/doc.go create mode 100644 vendor/github.com/stretchr/testify/require/forward_requirements.go create mode 100644 vendor/github.com/stretchr/testify/require/require.go create mode 100644 vendor/github.com/stretchr/testify/require/require.go.tmpl create mode 100644 vendor/github.com/stretchr/testify/require/require_forward.go create mode 100644 vendor/github.com/stretchr/testify/require/require_forward.go.tmpl create mode 100644 vendor/github.com/stretchr/testify/require/requirements.go create mode 100644 vendor/github.com/twpayne/go-geom/.gitignore create mode 100644 vendor/github.com/twpayne/go-geom/.golangci.yml create mode 100644 vendor/github.com/twpayne/go-geom/INTERNALS.md create mode 100644 vendor/github.com/twpayne/go-geom/LICENSE create mode 100644 vendor/github.com/twpayne/go-geom/Makefile create mode 100644 vendor/github.com/twpayne/go-geom/README.md create mode 100644 vendor/github.com/twpayne/go-geom/bounds.go create mode 100644 vendor/github.com/twpayne/go-geom/derived.gen.go create mode 100644 vendor/github.com/twpayne/go-geom/encoding/wkb/.gitignore create mode 100644 vendor/github.com/twpayne/go-geom/encoding/wkb/Makefile create mode 100644 vendor/github.com/twpayne/go-geom/encoding/wkb/fuzz.go create mode 100644 vendor/github.com/twpayne/go-geom/encoding/wkb/sql.go create mode 100644 vendor/github.com/twpayne/go-geom/encoding/wkb/wkb.go create mode 100644 vendor/github.com/twpayne/go-geom/encoding/wkbcommon/binary.go create mode 100644 vendor/github.com/twpayne/go-geom/encoding/wkbcommon/options.go create mode 100644 vendor/github.com/twpayne/go-geom/encoding/wkbcommon/wkbcommon.go create mode 100644 vendor/github.com/twpayne/go-geom/flat.go create mode 100644 vendor/github.com/twpayne/go-geom/geom.go create mode 100644 vendor/github.com/twpayne/go-geom/geometrycollection.go create mode 100644 vendor/github.com/twpayne/go-geom/linearring.go create mode 100644 vendor/github.com/twpayne/go-geom/linestring.go create mode 100644 vendor/github.com/twpayne/go-geom/multilinestring.go create mode 100644 vendor/github.com/twpayne/go-geom/multipoint.go create mode 100644 vendor/github.com/twpayne/go-geom/multipolygon.go create mode 100644 vendor/github.com/twpayne/go-geom/point.go create mode 100644 vendor/github.com/twpayne/go-geom/polygon.go create mode 100644 vendor/golang.org/x/sys/cpu/asm_aix_ppc64.s create mode 100644 vendor/golang.org/x/sys/cpu/asm_darwin_arm64_gc.s create mode 100644 vendor/golang.org/x/sys/cpu/asm_darwin_x86_gc.s create mode 100644 vendor/golang.org/x/sys/cpu/byteorder.go create mode 100644 vendor/golang.org/x/sys/cpu/cpu.go create mode 100644 vendor/golang.org/x/sys/cpu/cpu_aix.go create mode 100644 vendor/golang.org/x/sys/cpu/cpu_arm.go create mode 100644 vendor/golang.org/x/sys/cpu/cpu_arm64.go create mode 100644 vendor/golang.org/x/sys/cpu/cpu_arm64.s create mode 100644 vendor/golang.org/x/sys/cpu/cpu_darwin_arm64.go create mode 100644 vendor/golang.org/x/sys/cpu/cpu_darwin_arm64_other.go create mode 100644 vendor/golang.org/x/sys/cpu/cpu_darwin_x86.go create mode 100644 vendor/golang.org/x/sys/cpu/cpu_gc_arm64.go create mode 100644 vendor/golang.org/x/sys/cpu/cpu_gc_s390x.go create mode 100644 vendor/golang.org/x/sys/cpu/cpu_gc_x86.go create mode 100644 vendor/golang.org/x/sys/cpu/cpu_gc_x86.s create mode 100644 vendor/golang.org/x/sys/cpu/cpu_gccgo_arm64.go create mode 100644 vendor/golang.org/x/sys/cpu/cpu_gccgo_s390x.go create mode 100644 vendor/golang.org/x/sys/cpu/cpu_gccgo_x86.c create mode 100644 vendor/golang.org/x/sys/cpu/cpu_gccgo_x86.go create mode 100644 vendor/golang.org/x/sys/cpu/cpu_linux.go create mode 100644 vendor/golang.org/x/sys/cpu/cpu_linux_arm.go create mode 100644 vendor/golang.org/x/sys/cpu/cpu_linux_arm64.go create mode 100644 vendor/golang.org/x/sys/cpu/cpu_linux_loong64.go create mode 100644 vendor/golang.org/x/sys/cpu/cpu_linux_mips64x.go create mode 100644 vendor/golang.org/x/sys/cpu/cpu_linux_noinit.go create mode 100644 vendor/golang.org/x/sys/cpu/cpu_linux_ppc64x.go create mode 100644 vendor/golang.org/x/sys/cpu/cpu_linux_riscv64.go create mode 100644 vendor/golang.org/x/sys/cpu/cpu_linux_s390x.go create mode 100644 vendor/golang.org/x/sys/cpu/cpu_loong64.go create mode 100644 vendor/golang.org/x/sys/cpu/cpu_loong64.s create mode 100644 vendor/golang.org/x/sys/cpu/cpu_mips64x.go create mode 100644 vendor/golang.org/x/sys/cpu/cpu_mipsx.go create mode 100644 vendor/golang.org/x/sys/cpu/cpu_netbsd_arm64.go create mode 100644 vendor/golang.org/x/sys/cpu/cpu_openbsd_arm64.go create mode 100644 vendor/golang.org/x/sys/cpu/cpu_openbsd_arm64.s create mode 100644 vendor/golang.org/x/sys/cpu/cpu_other_arm.go create mode 100644 vendor/golang.org/x/sys/cpu/cpu_other_arm64.go create mode 100644 vendor/golang.org/x/sys/cpu/cpu_other_mips64x.go create mode 100644 vendor/golang.org/x/sys/cpu/cpu_other_ppc64x.go create mode 100644 vendor/golang.org/x/sys/cpu/cpu_other_riscv64.go create mode 100644 vendor/golang.org/x/sys/cpu/cpu_other_x86.go create mode 100644 vendor/golang.org/x/sys/cpu/cpu_ppc64x.go create mode 100644 vendor/golang.org/x/sys/cpu/cpu_riscv64.go create mode 100644 vendor/golang.org/x/sys/cpu/cpu_s390x.go create mode 100644 vendor/golang.org/x/sys/cpu/cpu_s390x.s create mode 100644 vendor/golang.org/x/sys/cpu/cpu_wasm.go create mode 100644 vendor/golang.org/x/sys/cpu/cpu_windows.go create mode 100644 vendor/golang.org/x/sys/cpu/cpu_windows_arm64.go create mode 100644 vendor/golang.org/x/sys/cpu/cpu_x86.go create mode 100644 vendor/golang.org/x/sys/cpu/cpu_zos.go create mode 100644 vendor/golang.org/x/sys/cpu/cpu_zos_s390x.go create mode 100644 vendor/golang.org/x/sys/cpu/endian_big.go create mode 100644 vendor/golang.org/x/sys/cpu/endian_little.go create mode 100644 vendor/golang.org/x/sys/cpu/hwcap_linux.go create mode 100644 vendor/golang.org/x/sys/cpu/parse.go create mode 100644 vendor/golang.org/x/sys/cpu/proc_cpuinfo_linux.go create mode 100644 vendor/golang.org/x/sys/cpu/runtime_auxv.go create mode 100644 vendor/golang.org/x/sys/cpu/runtime_auxv_go121.go create mode 100644 vendor/golang.org/x/sys/cpu/syscall_aix_gccgo.go create mode 100644 vendor/golang.org/x/sys/cpu/syscall_aix_ppc64_gc.go create mode 100644 vendor/golang.org/x/sys/cpu/syscall_darwin_arm64_gc.go create mode 100644 vendor/golang.org/x/sys/cpu/syscall_darwin_x86_gc.go create mode 100644 vendor/golang.org/x/sys/cpu/zcpu_windows.go rename vendor/google.golang.org/grpc/{internal => experimental}/balancer/weight/weight.go (71%) rename vendor/google.golang.org/grpc/internal/{grpcutil/regex.go => transport/internal/internal.go} (59%) create mode 100644 vendor/google.golang.org/protobuf/types/known/structpb/struct.pb.go create mode 100644 vendor/google.golang.org/protobuf/types/known/wrapperspb/wrappers.pb.go diff --git a/go.mod b/go.mod index b56defe84..7f11c186b 100644 --- a/go.mod +++ b/go.mod @@ -12,6 +12,7 @@ require ( github.com/netobserv/netobserv-ebpf-agent v1.11.5-community github.com/onsi/ginkgo/v2 v2.31.0 github.com/onsi/gomega v1.42.0 + github.com/parquet-go/parquet-go v0.30.1 github.com/sirupsen/logrus v1.9.4 github.com/spf13/cobra v1.10.2 github.com/stretchr/testify v1.11.1 @@ -19,6 +20,17 @@ require ( sigs.k8s.io/e2e-framework v0.7.0 ) +require ( + github.com/andybalholm/brotli v1.1.1 // indirect + github.com/klauspost/compress v1.18.6 // indirect + github.com/parquet-go/bitpack v1.0.0 // indirect + github.com/parquet-go/jsonlite v1.0.0 // indirect + github.com/pierrec/lz4/v4 v4.1.22 // indirect + github.com/twpayne/go-geom v1.6.1 // indirect +) + +replace github.com/netobserv/flowlogs-pipeline => ../flowlogs-pipeline + require ( github.com/Masterminds/semver/v3 v3.4.0 // indirect github.com/beorn7/perks v1.0.1 // indirect @@ -92,7 +104,7 @@ require ( golang.org/x/time v0.15.0 // indirect golang.org/x/tools v0.45.0 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20260526163538-3dc84a4a5aaa // indirect - google.golang.org/grpc v1.81.1 // indirect + google.golang.org/grpc v1.82.0 // indirect google.golang.org/protobuf v1.36.11 gopkg.in/evanphx/json-patch.v4 v4.13.0 // indirect gopkg.in/inf.v0 v0.9.1 // indirect diff --git a/go.sum b/go.sum index 3f9cbb4a8..abd7e603a 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,13 @@ +github.com/DATA-DOG/go-sqlmock v1.5.2 h1:OcvFkGmslmlZibjAjaHm3L//6LiuBgolP7OputlJIzU= +github.com/DATA-DOG/go-sqlmock v1.5.2/go.mod h1:88MAG/4G7SMwSE3CeA0ZKzrT5CiOU3OJ+JlNzwDqpNU= github.com/Masterminds/semver/v3 v3.4.0 h1:Zog+i5UMtVoCU8oKka5P7i9q9HgrJeGzI9SA1Xbatp0= github.com/Masterminds/semver/v3 v3.4.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM= +github.com/alecthomas/assert/v2 v2.10.0 h1:jjRCHsj6hBJhkmhznrCzoNpbA3zqy0fYiUcYZP/GkPY= +github.com/alecthomas/assert/v2 v2.10.0/go.mod h1:Bze95FyfUr7x34QZrjL+XP+0qgp/zg8yS+TtBj1WA3k= +github.com/alecthomas/repr v0.4.0 h1:GhI2A8MACjfegCPVq9f1FLvIBS+DrQ2KQBFZP1iFzXc= +github.com/alecthomas/repr v0.4.0/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4= +github.com/andybalholm/brotli v1.1.1 h1:PR2pgnyFznKEugtsUo0xLdDop5SKXd5Qf5ysW+7XdTA= +github.com/andybalholm/brotli v1.1.1/go.mod h1:05ib4cKhjx3OQYUY22hTVd34Bc8upXjOLL2rKwwZBoA= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= @@ -110,6 +118,8 @@ github.com/gopacket/gopacket v1.6.1 h1:S19Ok/KVGDFNHVW2uCva5U0vZ+uHqiZQdxteL50v6 github.com/gopacket/gopacket v1.6.1/go.mod h1:i3NaGaqfoWKAr1+g7qxEdWsmfT+MXuWkAe9+THv8LME= github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674 h1:JeSE6pjso5THxAzdVpqr6/geYxZytqFMBCOtn/ujyeo= github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674/go.mod h1:r4w70xmWCQKmi1ONH4KIaBptdivuRPyosB9RmPlGEwA= +github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM= +github.com/hexops/gotextdiff v1.0.3/go.mod h1:pSWU5MAI3yDq+fZBTazCSJysOMbxWL1BSow5/V2vxeg= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/jmhobbs/tview-hexview v0.0.0-20230122051941-38d407ce0567 h1:DPQvtn2XG0wz2lgLc5kgRqIqnmyyTZDAkTBLyRlyx6s= @@ -156,8 +166,6 @@ github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f h1:y5//uYreIhSUg3J github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw= github.com/navidys/tvxwidgets v0.14.0 h1:8UDKTDbbQeWwmOBcsTzKTrUYv7BpGYphpib7zWt6i6s= github.com/navidys/tvxwidgets v0.14.0/go.mod h1:yILrJRJqf399gw7JyUM7UdT+e9PMMDXpk+CuIoqvEj8= -github.com/netobserv/flowlogs-pipeline v1.11.5-community h1:g056UVio6qFNzOzxD98k3YxBHZWUEr+tn3MxTog/f0A= -github.com/netobserv/flowlogs-pipeline v1.11.5-community/go.mod h1:RNGkTvF3w+epkBmtbKqj+M4h6f9VNwVkTB2Smy2OvCc= github.com/netobserv/netobserv-ebpf-agent v1.11.5-community h1:0wefOgcoDjaDhP8WJG4tKIQ2TjxkgVn1j2DFVBfkxcI= github.com/netobserv/netobserv-ebpf-agent v1.11.5-community/go.mod h1:NXh7mtzpv7mLu6H7q+hKCfM8ETVjYu8EgxFX2RLoFQg= github.com/onsi/ginkgo/v2 v2.31.0 h1:GtuJos5DFUV9EerYJo8RhYxosYNGvOdDE5haKq6Grfs= @@ -168,6 +176,12 @@ github.com/ovn-org/libovsdb v0.7.1-0.20240820095311-ce1951614a20 h1:OoDvzyaK7F/Z github.com/ovn-org/libovsdb v0.7.1-0.20240820095311-ce1951614a20/go.mod h1:dJbxEaalQl83nn904K32FaMjlH/qOObZ0bj4ejQ78AI= github.com/ovn-org/ovn-kubernetes/go-controller v0.0.0-20250227173154-57a2590a1d16 h1:t4NphP6IIFRe5/2NGc1MD0e72pLYIzaG9YizrYyk84Y= github.com/ovn-org/ovn-kubernetes/go-controller v0.0.0-20250227173154-57a2590a1d16/go.mod h1:MzFM3OEsLM2w/4MBMOCsxGR6ZBUvJfOxvQHB8LIKSv4= +github.com/parquet-go/bitpack v1.0.0 h1:AUqzlKzPPXf2bCdjfj4sTeacrUwsT7NlcYDMUQxPcQA= +github.com/parquet-go/bitpack v1.0.0/go.mod h1:XnVk9TH+O40eOOmvpAVZ7K2ocQFrQwysLMnc6M/8lgs= +github.com/parquet-go/jsonlite v1.0.0 h1:87QNdi56wOfsE5bdgas0vRzHPxfJgzrXGml1zZdd7VU= +github.com/parquet-go/jsonlite v1.0.0/go.mod h1:nDjpkpL4EOtqs6NQugUsi0Rleq9sW/OtC1NnZEnxzF0= +github.com/parquet-go/parquet-go v0.30.1 h1:Oy6ganNrAdFiVwy7wNmWagfPTWA2X9Z3tVHBc7JtuX8= +github.com/parquet-go/parquet-go v0.30.1/go.mod h1:navtkAYr2LGoJVp141oXPlO/sxLvaOe3la2JEoD8+rg= github.com/pelletier/go-toml/v2 v2.3.1 h1:MYEvvGnQjeNkRF1qUuGolNtNExTDwct51yp7olPtrEc= github.com/pelletier/go-toml/v2 v2.3.1/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY= github.com/pierrec/lz4/v4 v4.1.22 h1:cKFw6uJDK+/gfw5BcDL0JL5aBsAFdsIT18eRtLj7VIU= @@ -228,6 +242,8 @@ github.com/tidwall/pretty v1.2.1 h1:qjsOFOWWQl+N3RsoF5/ssm1pHmJJwhjlSbZ51I6wMl4= github.com/tidwall/pretty v1.2.1/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= github.com/tidwall/sjson v1.2.5 h1:kLy8mja+1c9jlljvWTlSazM7cKDRfJuR/bOJhcY5NcY= github.com/tidwall/sjson v1.2.5/go.mod h1:Fvgq9kS/6ociJEDnK0Fk1cpYF4FIW6ZF7LAe+6jwd28= +github.com/twpayne/go-geom v1.6.1 h1:iLE+Opv0Ihm/ABIcvQFGIiFBXd76oBIar9drAwHFhR4= +github.com/twpayne/go-geom v1.6.1/go.mod h1:Kr+Nly6BswFsKM5sd31YaoWS5PeDDH2NftJTK7Gd028= github.com/urfave/cli/v2 v2.27.6 h1:VdRdS98FNhKZ8/Az8B7MTyGQmpIr36O1EHybx/LaZ4g= github.com/urfave/cli/v2 v2.27.6/go.mod h1:3Sevf16NykTbInEnD0yKkjDAeZDS0A6bzhBH5hrMvTQ= github.com/vishvananda/netlink v1.3.1-0.20250425193846-9d88d8385bf9 h1:ZEjCI2kamoTYIx348/Nfco4c4NPvpq972DM2HMgnBgI= @@ -246,6 +262,8 @@ github.com/xdg-go/stringprep v1.0.4 h1:XLI/Ng3O1Atzq0oBs3TWm+5ZVgkq2aqdlvP9JtoZ6 github.com/xdg-go/stringprep v1.0.4/go.mod h1:mPGuuIYwz7CmR2bT9j4GbQqutWS1zV24gijq1dTyGkM= github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 h1:gEOO8jv9F4OT7lGCjxCBTO/36wtF6j2nSip77qHd4x4= github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1/go.mod h1:Ohn+xnUBiLI6FVj/9LpzZWtj1/D6lUovWYBkxHVV3aM= +github.com/xyproto/randomstring v1.0.5 h1:YtlWPoRdgMu3NZtP45drfy1GKoojuR7hmRcnhZqKjWU= +github.com/xyproto/randomstring v1.0.5/go.mod h1:rgmS5DeNXLivK7YprL0pY+lTuhNQW3iGxZ18UQApw/E= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64= go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y= @@ -322,8 +340,8 @@ gonum.org/v1/gonum v0.17.0 h1:VbpOemQlsSMrYmn7T2OUvQ4dqxQXU+ouZFQsZOx50z4= gonum.org/v1/gonum v0.17.0/go.mod h1:El3tOrEuMpv2UdMrbNlKEh9vd86bmQ6vqIcDwxEOc1E= google.golang.org/genproto/googleapis/rpc v0.0.0-20260526163538-3dc84a4a5aaa h1:mZHHdPZl0dbGHCflZgAq/Q468DWVFcU2whhB2KAo8fk= google.golang.org/genproto/googleapis/rpc v0.0.0-20260526163538-3dc84a4a5aaa/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8= -google.golang.org/grpc v1.81.1 h1:VnnIIZ88UzOOKLukQi+ImGz8O1Wdp8nAGGnvOfEIWQQ= -google.golang.org/grpc v1.81.1/go.mod h1:xGH9GfzOyMTGIOXBJmXt+BX/V0kcdQbdcuwQ/zNw42I= +google.golang.org/grpc v1.82.0 h1:vguDnZUPjE26w09A63VoxZPnvPjB5Riyc0mkXPFmAIU= +google.golang.org/grpc v1.82.0/go.mod h1:yzTZ1TB1Z3SG+LIYaI+WiE8D5+PZ3ArnrSp8zF3+/ZA= google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE= google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= diff --git a/vendor/github.com/andybalholm/brotli/LICENSE b/vendor/github.com/andybalholm/brotli/LICENSE new file mode 100644 index 000000000..33b7cdd2d --- /dev/null +++ b/vendor/github.com/andybalholm/brotli/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/vendor/github.com/andybalholm/brotli/README.md b/vendor/github.com/andybalholm/brotli/README.md new file mode 100644 index 000000000..00625211d --- /dev/null +++ b/vendor/github.com/andybalholm/brotli/README.md @@ -0,0 +1,14 @@ +This package is a brotli compressor and decompressor implemented in Go. +It was translated from the reference implementation (https://github.com/google/brotli) +with the `c2go` tool at https://github.com/andybalholm/c2go. + +I have been working on new compression algorithms (not translated from C) +in the matchfinder package. +You can use them with the NewWriterV2 function. +Currently they give better results than the old implementation +(at least for compressing my test file, Newton’s *Opticks*) +on levels 2 to 6. + +I am using it in production with https://github.com/andybalholm/redwood. + +API documentation is found at https://pkg.go.dev/github.com/andybalholm/brotli?tab=doc. diff --git a/vendor/github.com/andybalholm/brotli/backward_references.go b/vendor/github.com/andybalholm/brotli/backward_references.go new file mode 100644 index 000000000..008c054d1 --- /dev/null +++ b/vendor/github.com/andybalholm/brotli/backward_references.go @@ -0,0 +1,185 @@ +package brotli + +import ( + "sync" +) + +/* Copyright 2013 Google Inc. All Rights Reserved. + + Distributed under MIT license. + See file LICENSE for detail or copy at https://opensource.org/licenses/MIT +*/ + +/* Function to find backward reference copies. */ + +func computeDistanceCode(distance uint, max_distance uint, dist_cache []int) uint { + if distance <= max_distance { + var distance_plus_3 uint = distance + 3 + var offset0 uint = distance_plus_3 - uint(dist_cache[0]) + var offset1 uint = distance_plus_3 - uint(dist_cache[1]) + if distance == uint(dist_cache[0]) { + return 0 + } else if distance == uint(dist_cache[1]) { + return 1 + } else if offset0 < 7 { + return (0x9750468 >> (4 * offset0)) & 0xF + } else if offset1 < 7 { + return (0xFDB1ACE >> (4 * offset1)) & 0xF + } else if distance == uint(dist_cache[2]) { + return 2 + } else if distance == uint(dist_cache[3]) { + return 3 + } + } + + return distance + numDistanceShortCodes - 1 +} + +var hasherSearchResultPool sync.Pool + +func createBackwardReferences(num_bytes uint, position uint, ringbuffer []byte, ringbuffer_mask uint, params *encoderParams, hasher hasherHandle, dist_cache []int, last_insert_len *uint, commands *[]command, num_literals *uint) { + var max_backward_limit uint = maxBackwardLimit(params.lgwin) + var insert_length uint = *last_insert_len + var pos_end uint = position + num_bytes + var store_end uint + if num_bytes >= hasher.StoreLookahead() { + store_end = position + num_bytes - hasher.StoreLookahead() + 1 + } else { + store_end = position + } + var random_heuristics_window_size uint = literalSpreeLengthForSparseSearch(params) + var apply_random_heuristics uint = position + random_heuristics_window_size + var gap uint = 0 + /* Set maximum distance, see section 9.1. of the spec. */ + + const kMinScore uint = scoreBase + 100 + + /* For speed up heuristics for random data. */ + + /* Minimum score to accept a backward reference. */ + hasher.PrepareDistanceCache(dist_cache) + sr2, _ := hasherSearchResultPool.Get().(*hasherSearchResult) + if sr2 == nil { + sr2 = &hasherSearchResult{} + } + sr, _ := hasherSearchResultPool.Get().(*hasherSearchResult) + if sr == nil { + sr = &hasherSearchResult{} + } + + for position+hasher.HashTypeLength() < pos_end { + var max_length uint = pos_end - position + var max_distance uint = brotli_min_size_t(position, max_backward_limit) + sr.len = 0 + sr.len_code_delta = 0 + sr.distance = 0 + sr.score = kMinScore + hasher.FindLongestMatch(¶ms.dictionary, ringbuffer, ringbuffer_mask, dist_cache, position, max_length, max_distance, gap, params.dist.max_distance, sr) + if sr.score > kMinScore { + /* Found a match. Let's look for something even better ahead. */ + var delayed_backward_references_in_row int = 0 + max_length-- + for ; ; max_length-- { + var cost_diff_lazy uint = 175 + if params.quality < minQualityForExtensiveReferenceSearch { + sr2.len = brotli_min_size_t(sr.len-1, max_length) + } else { + sr2.len = 0 + } + sr2.len_code_delta = 0 + sr2.distance = 0 + sr2.score = kMinScore + max_distance = brotli_min_size_t(position+1, max_backward_limit) + hasher.FindLongestMatch(¶ms.dictionary, ringbuffer, ringbuffer_mask, dist_cache, position+1, max_length, max_distance, gap, params.dist.max_distance, sr2) + if sr2.score >= sr.score+cost_diff_lazy { + /* Ok, let's just write one byte for now and start a match from the + next byte. */ + position++ + + insert_length++ + *sr = *sr2 + delayed_backward_references_in_row++ + if delayed_backward_references_in_row < 4 && position+hasher.HashTypeLength() < pos_end { + continue + } + } + + break + } + + apply_random_heuristics = position + 2*sr.len + random_heuristics_window_size + max_distance = brotli_min_size_t(position, max_backward_limit) + { + /* The first 16 codes are special short-codes, + and the minimum offset is 1. */ + var distance_code uint = computeDistanceCode(sr.distance, max_distance+gap, dist_cache) + if (sr.distance <= (max_distance + gap)) && distance_code > 0 { + dist_cache[3] = dist_cache[2] + dist_cache[2] = dist_cache[1] + dist_cache[1] = dist_cache[0] + dist_cache[0] = int(sr.distance) + hasher.PrepareDistanceCache(dist_cache) + } + + *commands = append(*commands, makeCommand(¶ms.dist, insert_length, sr.len, sr.len_code_delta, distance_code)) + } + + *num_literals += insert_length + insert_length = 0 + /* Put the hash keys into the table, if there are enough bytes left. + Depending on the hasher implementation, it can push all positions + in the given range or only a subset of them. + Avoid hash poisoning with RLE data. */ + { + var range_start uint = position + 2 + var range_end uint = brotli_min_size_t(position+sr.len, store_end) + if sr.distance < sr.len>>2 { + range_start = brotli_min_size_t(range_end, brotli_max_size_t(range_start, position+sr.len-(sr.distance<<2))) + } + + hasher.StoreRange(ringbuffer, ringbuffer_mask, range_start, range_end) + } + + position += sr.len + } else { + insert_length++ + position++ + + /* If we have not seen matches for a long time, we can skip some + match lookups. Unsuccessful match lookups are very very expensive + and this kind of a heuristic speeds up compression quite + a lot. */ + if position > apply_random_heuristics { + /* Going through uncompressible data, jump. */ + if position > apply_random_heuristics+4*random_heuristics_window_size { + var kMargin uint = brotli_max_size_t(hasher.StoreLookahead()-1, 4) + /* It is quite a long time since we saw a copy, so we assume + that this data is not compressible, and store hashes less + often. Hashes of non compressible data are less likely to + turn out to be useful in the future, too, so we store less of + them to not to flood out the hash table of good compressible + data. */ + + var pos_jump uint = brotli_min_size_t(position+16, pos_end-kMargin) + for ; position < pos_jump; position += 4 { + hasher.Store(ringbuffer, ringbuffer_mask, position) + insert_length += 4 + } + } else { + var kMargin uint = brotli_max_size_t(hasher.StoreLookahead()-1, 2) + var pos_jump uint = brotli_min_size_t(position+8, pos_end-kMargin) + for ; position < pos_jump; position += 2 { + hasher.Store(ringbuffer, ringbuffer_mask, position) + insert_length += 2 + } + } + } + } + } + + insert_length += pos_end - position + *last_insert_len = insert_length + + hasherSearchResultPool.Put(sr) + hasherSearchResultPool.Put(sr2) +} diff --git a/vendor/github.com/andybalholm/brotli/backward_references_hq.go b/vendor/github.com/andybalholm/brotli/backward_references_hq.go new file mode 100644 index 000000000..21629c1cd --- /dev/null +++ b/vendor/github.com/andybalholm/brotli/backward_references_hq.go @@ -0,0 +1,796 @@ +package brotli + +import "math" + +type zopfliNode struct { + length uint32 + distance uint32 + dcode_insert_length uint32 + u struct { + cost float32 + next uint32 + shortcut uint32 + } +} + +const maxEffectiveDistanceAlphabetSize = 544 + +const kInfinity float32 = 1.7e38 /* ~= 2 ^ 127 */ + +var kDistanceCacheIndex = []uint32{0, 1, 2, 3, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1} + +var kDistanceCacheOffset = []int{0, 0, 0, 0, -1, 1, -2, 2, -3, 3, -1, 1, -2, 2, -3, 3} + +func initZopfliNodes(array []zopfliNode, length uint) { + var stub zopfliNode + var i uint + stub.length = 1 + stub.distance = 0 + stub.dcode_insert_length = 0 + stub.u.cost = kInfinity + for i = 0; i < length; i++ { + array[i] = stub + } +} + +func zopfliNodeCopyLength(self *zopfliNode) uint32 { + return self.length & 0x1FFFFFF +} + +func zopfliNodeLengthCode(self *zopfliNode) uint32 { + var modifier uint32 = self.length >> 25 + return zopfliNodeCopyLength(self) + 9 - modifier +} + +func zopfliNodeCopyDistance(self *zopfliNode) uint32 { + return self.distance +} + +func zopfliNodeDistanceCode(self *zopfliNode) uint32 { + var short_code uint32 = self.dcode_insert_length >> 27 + if short_code == 0 { + return zopfliNodeCopyDistance(self) + numDistanceShortCodes - 1 + } else { + return short_code - 1 + } +} + +func zopfliNodeCommandLength(self *zopfliNode) uint32 { + return zopfliNodeCopyLength(self) + (self.dcode_insert_length & 0x7FFFFFF) +} + +/* Histogram based cost model for zopflification. */ +type zopfliCostModel struct { + cost_cmd_ [numCommandSymbols]float32 + cost_dist_ []float32 + distance_histogram_size uint32 + literal_costs_ []float32 + min_cost_cmd_ float32 + num_bytes_ uint +} + +func initZopfliCostModel(self *zopfliCostModel, dist *distanceParams, num_bytes uint) { + var distance_histogram_size uint32 = dist.alphabet_size + if distance_histogram_size > maxEffectiveDistanceAlphabetSize { + distance_histogram_size = maxEffectiveDistanceAlphabetSize + } + + self.num_bytes_ = num_bytes + self.literal_costs_ = make([]float32, (num_bytes + 2)) + self.cost_dist_ = make([]float32, (dist.alphabet_size)) + self.distance_histogram_size = distance_histogram_size +} + +func cleanupZopfliCostModel(self *zopfliCostModel) { + self.literal_costs_ = nil + self.cost_dist_ = nil +} + +func setCost(histogram []uint32, histogram_size uint, literal_histogram bool, cost []float32) { + var sum uint = 0 + var missing_symbol_sum uint + var log2sum float32 + var missing_symbol_cost float32 + var i uint + for i = 0; i < histogram_size; i++ { + sum += uint(histogram[i]) + } + + log2sum = float32(fastLog2(sum)) + missing_symbol_sum = sum + if !literal_histogram { + for i = 0; i < histogram_size; i++ { + if histogram[i] == 0 { + missing_symbol_sum++ + } + } + } + + missing_symbol_cost = float32(fastLog2(missing_symbol_sum)) + 2 + for i = 0; i < histogram_size; i++ { + if histogram[i] == 0 { + cost[i] = missing_symbol_cost + continue + } + + /* Shannon bits for this symbol. */ + cost[i] = log2sum - float32(fastLog2(uint(histogram[i]))) + + /* Cannot be coded with less than 1 bit */ + if cost[i] < 1 { + cost[i] = 1 + } + } +} + +func zopfliCostModelSetFromCommands(self *zopfliCostModel, position uint, ringbuffer []byte, ringbuffer_mask uint, commands []command, last_insert_len uint) { + var histogram_literal [numLiteralSymbols]uint32 + var histogram_cmd [numCommandSymbols]uint32 + var histogram_dist [maxEffectiveDistanceAlphabetSize]uint32 + var cost_literal [numLiteralSymbols]float32 + var pos uint = position - last_insert_len + var min_cost_cmd float32 = kInfinity + var cost_cmd []float32 = self.cost_cmd_[:] + var literal_costs []float32 + + histogram_literal = [numLiteralSymbols]uint32{} + histogram_cmd = [numCommandSymbols]uint32{} + histogram_dist = [maxEffectiveDistanceAlphabetSize]uint32{} + + for i := range commands { + var inslength uint = uint(commands[i].insert_len_) + var copylength uint = uint(commandCopyLen(&commands[i])) + var distcode uint = uint(commands[i].dist_prefix_) & 0x3FF + var cmdcode uint = uint(commands[i].cmd_prefix_) + var j uint + + histogram_cmd[cmdcode]++ + if cmdcode >= 128 { + histogram_dist[distcode]++ + } + + for j = 0; j < inslength; j++ { + histogram_literal[ringbuffer[(pos+j)&ringbuffer_mask]]++ + } + + pos += inslength + copylength + } + + setCost(histogram_literal[:], numLiteralSymbols, true, cost_literal[:]) + setCost(histogram_cmd[:], numCommandSymbols, false, cost_cmd) + setCost(histogram_dist[:], uint(self.distance_histogram_size), false, self.cost_dist_) + + for i := 0; i < numCommandSymbols; i++ { + min_cost_cmd = brotli_min_float(min_cost_cmd, cost_cmd[i]) + } + + self.min_cost_cmd_ = min_cost_cmd + { + literal_costs = self.literal_costs_ + var literal_carry float32 = 0.0 + num_bytes := int(self.num_bytes_) + literal_costs[0] = 0.0 + for i := 0; i < num_bytes; i++ { + literal_carry += cost_literal[ringbuffer[(position+uint(i))&ringbuffer_mask]] + literal_costs[i+1] = literal_costs[i] + literal_carry + literal_carry -= literal_costs[i+1] - literal_costs[i] + } + } +} + +func zopfliCostModelSetFromLiteralCosts(self *zopfliCostModel, position uint, ringbuffer []byte, ringbuffer_mask uint) { + var literal_costs []float32 = self.literal_costs_ + var literal_carry float32 = 0.0 + var cost_dist []float32 = self.cost_dist_ + var cost_cmd []float32 = self.cost_cmd_[:] + var num_bytes uint = self.num_bytes_ + var i uint + estimateBitCostsForLiterals(position, num_bytes, ringbuffer_mask, ringbuffer, literal_costs[1:]) + literal_costs[0] = 0.0 + for i = 0; i < num_bytes; i++ { + literal_carry += literal_costs[i+1] + literal_costs[i+1] = literal_costs[i] + literal_carry + literal_carry -= literal_costs[i+1] - literal_costs[i] + } + + for i = 0; i < numCommandSymbols; i++ { + cost_cmd[i] = float32(fastLog2(uint(11 + uint32(i)))) + } + + for i = 0; uint32(i) < self.distance_histogram_size; i++ { + cost_dist[i] = float32(fastLog2(uint(20 + uint32(i)))) + } + + self.min_cost_cmd_ = float32(fastLog2(11)) +} + +func zopfliCostModelGetCommandCost(self *zopfliCostModel, cmdcode uint16) float32 { + return self.cost_cmd_[cmdcode] +} + +func zopfliCostModelGetDistanceCost(self *zopfliCostModel, distcode uint) float32 { + return self.cost_dist_[distcode] +} + +func zopfliCostModelGetLiteralCosts(self *zopfliCostModel, from uint, to uint) float32 { + return self.literal_costs_[to] - self.literal_costs_[from] +} + +func zopfliCostModelGetMinCostCmd(self *zopfliCostModel) float32 { + return self.min_cost_cmd_ +} + +/* REQUIRES: len >= 2, start_pos <= pos */ +/* REQUIRES: cost < kInfinity, nodes[start_pos].cost < kInfinity */ +/* Maintains the "ZopfliNode array invariant". */ +func updateZopfliNode(nodes []zopfliNode, pos uint, start_pos uint, len uint, len_code uint, dist uint, short_code uint, cost float32) { + var next *zopfliNode = &nodes[pos+len] + next.length = uint32(len | (len+9-len_code)<<25) + next.distance = uint32(dist) + next.dcode_insert_length = uint32(short_code<<27 | (pos - start_pos)) + next.u.cost = cost +} + +type posData struct { + pos uint + distance_cache [4]int + costdiff float32 + cost float32 +} + +/* Maintains the smallest 8 cost difference together with their positions */ +type startPosQueue struct { + q_ [8]posData + idx_ uint +} + +func initStartPosQueue(self *startPosQueue) { + self.idx_ = 0 +} + +func startPosQueueSize(self *startPosQueue) uint { + return brotli_min_size_t(self.idx_, 8) +} + +func startPosQueuePush(self *startPosQueue, posdata *posData) { + var offset uint = ^(self.idx_) & 7 + self.idx_++ + var len uint = startPosQueueSize(self) + var i uint + var q []posData = self.q_[:] + q[offset] = *posdata + + /* Restore the sorted order. In the list of |len| items at most |len - 1| + adjacent element comparisons / swaps are required. */ + for i = 1; i < len; i++ { + if q[offset&7].costdiff > q[(offset+1)&7].costdiff { + var tmp posData = q[offset&7] + q[offset&7] = q[(offset+1)&7] + q[(offset+1)&7] = tmp + } + + offset++ + } +} + +func startPosQueueAt(self *startPosQueue, k uint) *posData { + return &self.q_[(k-self.idx_)&7] +} + +/* Returns the minimum possible copy length that can improve the cost of any */ +/* future position. */ +func computeMinimumCopyLength(start_cost float32, nodes []zopfliNode, num_bytes uint, pos uint) uint { + var min_cost float32 = start_cost + var len uint = 2 + var next_len_bucket uint = 4 + /* Compute the minimum possible cost of reaching any future position. */ + + var next_len_offset uint = 10 + for pos+len <= num_bytes && nodes[pos+len].u.cost <= min_cost { + /* We already reached (pos + len) with no more cost than the minimum + possible cost of reaching anything from this pos, so there is no point in + looking for lengths <= len. */ + len++ + + if len == next_len_offset { + /* We reached the next copy length code bucket, so we add one more + extra bit to the minimum cost. */ + min_cost += 1.0 + + next_len_offset += next_len_bucket + next_len_bucket *= 2 + } + } + + return uint(len) +} + +/* REQUIRES: nodes[pos].cost < kInfinity + REQUIRES: nodes[0..pos] satisfies that "ZopfliNode array invariant". */ +func computeDistanceShortcut(block_start uint, pos uint, max_backward_limit uint, gap uint, nodes []zopfliNode) uint32 { + var clen uint = uint(zopfliNodeCopyLength(&nodes[pos])) + var ilen uint = uint(nodes[pos].dcode_insert_length & 0x7FFFFFF) + var dist uint = uint(zopfliNodeCopyDistance(&nodes[pos])) + + /* Since |block_start + pos| is the end position of the command, the copy part + starts from |block_start + pos - clen|. Distances that are greater than + this or greater than |max_backward_limit| + |gap| are static dictionary + references, and do not update the last distances. + Also distance code 0 (last distance) does not update the last distances. */ + if pos == 0 { + return 0 + } else if dist+clen <= block_start+pos+gap && dist <= max_backward_limit+gap && zopfliNodeDistanceCode(&nodes[pos]) > 0 { + return uint32(pos) + } else { + return nodes[pos-clen-ilen].u.shortcut + } +} + +/* Fills in dist_cache[0..3] with the last four distances (as defined by + Section 4. of the Spec) that would be used at (block_start + pos) if we + used the shortest path of commands from block_start, computed from + nodes[0..pos]. The last four distances at block_start are in + starting_dist_cache[0..3]. + REQUIRES: nodes[pos].cost < kInfinity + REQUIRES: nodes[0..pos] satisfies that "ZopfliNode array invariant". */ +func computeDistanceCache(pos uint, starting_dist_cache []int, nodes []zopfliNode, dist_cache []int) { + var idx int = 0 + var p uint = uint(nodes[pos].u.shortcut) + for idx < 4 && p > 0 { + var ilen uint = uint(nodes[p].dcode_insert_length & 0x7FFFFFF) + var clen uint = uint(zopfliNodeCopyLength(&nodes[p])) + var dist uint = uint(zopfliNodeCopyDistance(&nodes[p])) + dist_cache[idx] = int(dist) + idx++ + + /* Because of prerequisite, p >= clen + ilen >= 2. */ + p = uint(nodes[p-clen-ilen].u.shortcut) + } + + for ; idx < 4; idx++ { + dist_cache[idx] = starting_dist_cache[0] + starting_dist_cache = starting_dist_cache[1:] + } +} + +/* Maintains "ZopfliNode array invariant" and pushes node to the queue, if it + is eligible. */ +func evaluateNode(block_start uint, pos uint, max_backward_limit uint, gap uint, starting_dist_cache []int, model *zopfliCostModel, queue *startPosQueue, nodes []zopfliNode) { + /* Save cost, because ComputeDistanceCache invalidates it. */ + var node_cost float32 = nodes[pos].u.cost + nodes[pos].u.shortcut = computeDistanceShortcut(block_start, pos, max_backward_limit, gap, nodes) + if node_cost <= zopfliCostModelGetLiteralCosts(model, 0, pos) { + var posdata posData + posdata.pos = pos + posdata.cost = node_cost + posdata.costdiff = node_cost - zopfliCostModelGetLiteralCosts(model, 0, pos) + computeDistanceCache(pos, starting_dist_cache, nodes, posdata.distance_cache[:]) + startPosQueuePush(queue, &posdata) + } +} + +/* Returns longest copy length. */ +func updateNodes(num_bytes uint, block_start uint, pos uint, ringbuffer []byte, ringbuffer_mask uint, params *encoderParams, max_backward_limit uint, starting_dist_cache []int, num_matches uint, matches []backwardMatch, model *zopfliCostModel, queue *startPosQueue, nodes []zopfliNode) uint { + var cur_ix uint = block_start + pos + var cur_ix_masked uint = cur_ix & ringbuffer_mask + var max_distance uint = brotli_min_size_t(cur_ix, max_backward_limit) + var max_len uint = num_bytes - pos + var max_zopfli_len uint = maxZopfliLen(params) + var max_iters uint = maxZopfliCandidates(params) + var min_len uint + var result uint = 0 + var k uint + var gap uint = 0 + + evaluateNode(block_start, pos, max_backward_limit, gap, starting_dist_cache, model, queue, nodes) + { + var posdata *posData = startPosQueueAt(queue, 0) + var min_cost float32 = (posdata.cost + zopfliCostModelGetMinCostCmd(model) + zopfliCostModelGetLiteralCosts(model, posdata.pos, pos)) + min_len = computeMinimumCopyLength(min_cost, nodes, num_bytes, pos) + } + + /* Go over the command starting positions in order of increasing cost + difference. */ + for k = 0; k < max_iters && k < startPosQueueSize(queue); k++ { + var posdata *posData = startPosQueueAt(queue, k) + var start uint = posdata.pos + var inscode uint16 = getInsertLengthCode(pos - start) + var start_costdiff float32 = posdata.costdiff + var base_cost float32 = start_costdiff + float32(getInsertExtra(inscode)) + zopfliCostModelGetLiteralCosts(model, 0, pos) + var best_len uint = min_len - 1 + var j uint = 0 + /* Look for last distance matches using the distance cache from this + starting position. */ + for ; j < numDistanceShortCodes && best_len < max_len; j++ { + var idx uint = uint(kDistanceCacheIndex[j]) + var backward uint = uint(posdata.distance_cache[idx] + kDistanceCacheOffset[j]) + var prev_ix uint = cur_ix - backward + var len uint = 0 + var continuation byte = ringbuffer[cur_ix_masked+best_len] + if cur_ix_masked+best_len > ringbuffer_mask { + break + } + + if backward > max_distance+gap { + /* Word dictionary -> ignore. */ + continue + } + + if backward <= max_distance { + /* Regular backward reference. */ + if prev_ix >= cur_ix { + continue + } + + prev_ix &= ringbuffer_mask + if prev_ix+best_len > ringbuffer_mask || continuation != ringbuffer[prev_ix+best_len] { + continue + } + + len = findMatchLengthWithLimit(ringbuffer[prev_ix:], ringbuffer[cur_ix_masked:], max_len) + } else { + continue + } + { + var dist_cost float32 = base_cost + zopfliCostModelGetDistanceCost(model, j) + var l uint + for l = best_len + 1; l <= len; l++ { + var copycode uint16 = getCopyLengthCode(l) + var cmdcode uint16 = combineLengthCodes(inscode, copycode, j == 0) + var tmp float32 + if cmdcode < 128 { + tmp = base_cost + } else { + tmp = dist_cost + } + var cost float32 = tmp + float32(getCopyExtra(copycode)) + zopfliCostModelGetCommandCost(model, cmdcode) + if cost < nodes[pos+l].u.cost { + updateZopfliNode(nodes, pos, start, l, l, backward, j+1, cost) + result = brotli_max_size_t(result, l) + } + + best_len = l + } + } + } + + /* At higher iterations look only for new last distance matches, since + looking only for new command start positions with the same distances + does not help much. */ + if k >= 2 { + continue + } + { + /* Loop through all possible copy lengths at this position. */ + var len uint = min_len + for j = 0; j < num_matches; j++ { + var match backwardMatch = matches[j] + var dist uint = uint(match.distance) + var is_dictionary_match bool = (dist > max_distance+gap) + var dist_code uint = dist + numDistanceShortCodes - 1 + var dist_symbol uint16 + var distextra uint32 + var distnumextra uint32 + var dist_cost float32 + var max_match_len uint + /* We already tried all possible last distance matches, so we can use + normal distance code here. */ + prefixEncodeCopyDistance(dist_code, uint(params.dist.num_direct_distance_codes), uint(params.dist.distance_postfix_bits), &dist_symbol, &distextra) + + distnumextra = uint32(dist_symbol) >> 10 + dist_cost = base_cost + float32(distnumextra) + zopfliCostModelGetDistanceCost(model, uint(dist_symbol)&0x3FF) + + /* Try all copy lengths up until the maximum copy length corresponding + to this distance. If the distance refers to the static dictionary, or + the maximum length is long enough, try only one maximum length. */ + max_match_len = backwardMatchLength(&match) + + if len < max_match_len && (is_dictionary_match || max_match_len > max_zopfli_len) { + len = max_match_len + } + + for ; len <= max_match_len; len++ { + var len_code uint + if is_dictionary_match { + len_code = backwardMatchLengthCode(&match) + } else { + len_code = len + } + var copycode uint16 = getCopyLengthCode(len_code) + var cmdcode uint16 = combineLengthCodes(inscode, copycode, false) + var cost float32 = dist_cost + float32(getCopyExtra(copycode)) + zopfliCostModelGetCommandCost(model, cmdcode) + if cost < nodes[pos+len].u.cost { + updateZopfliNode(nodes, pos, start, uint(len), len_code, dist, 0, cost) + if len > result { + result = len + } + } + } + } + } + } + + return result +} + +func computeShortestPathFromNodes(num_bytes uint, nodes []zopfliNode) uint { + var index uint = num_bytes + var num_commands uint = 0 + for nodes[index].dcode_insert_length&0x7FFFFFF == 0 && nodes[index].length == 1 { + index-- + } + nodes[index].u.next = math.MaxUint32 + for index != 0 { + var len uint = uint(zopfliNodeCommandLength(&nodes[index])) + index -= uint(len) + nodes[index].u.next = uint32(len) + num_commands++ + } + + return num_commands +} + +/* REQUIRES: nodes != NULL and len(nodes) >= num_bytes + 1 */ +func zopfliCreateCommands(num_bytes uint, block_start uint, nodes []zopfliNode, dist_cache []int, last_insert_len *uint, params *encoderParams, commands *[]command, num_literals *uint) { + var max_backward_limit uint = maxBackwardLimit(params.lgwin) + var pos uint = 0 + var offset uint32 = nodes[0].u.next + var i uint + var gap uint = 0 + for i = 0; offset != math.MaxUint32; i++ { + var next *zopfliNode = &nodes[uint32(pos)+offset] + var copy_length uint = uint(zopfliNodeCopyLength(next)) + var insert_length uint = uint(next.dcode_insert_length & 0x7FFFFFF) + pos += insert_length + offset = next.u.next + if i == 0 { + insert_length += *last_insert_len + *last_insert_len = 0 + } + { + var distance uint = uint(zopfliNodeCopyDistance(next)) + var len_code uint = uint(zopfliNodeLengthCode(next)) + var max_distance uint = brotli_min_size_t(block_start+pos, max_backward_limit) + var is_dictionary bool = (distance > max_distance+gap) + var dist_code uint = uint(zopfliNodeDistanceCode(next)) + *commands = append(*commands, makeCommand(¶ms.dist, insert_length, copy_length, int(len_code)-int(copy_length), dist_code)) + + if !is_dictionary && dist_code > 0 { + dist_cache[3] = dist_cache[2] + dist_cache[2] = dist_cache[1] + dist_cache[1] = dist_cache[0] + dist_cache[0] = int(distance) + } + } + + *num_literals += insert_length + pos += copy_length + } + + *last_insert_len += num_bytes - pos +} + +func zopfliIterate(num_bytes uint, position uint, ringbuffer []byte, ringbuffer_mask uint, params *encoderParams, gap uint, dist_cache []int, model *zopfliCostModel, num_matches []uint32, matches []backwardMatch, nodes []zopfliNode) uint { + var max_backward_limit uint = maxBackwardLimit(params.lgwin) + var max_zopfli_len uint = maxZopfliLen(params) + var queue startPosQueue + var cur_match_pos uint = 0 + var i uint + nodes[0].length = 0 + nodes[0].u.cost = 0 + initStartPosQueue(&queue) + for i = 0; i+3 < num_bytes; i++ { + var skip uint = updateNodes(num_bytes, position, i, ringbuffer, ringbuffer_mask, params, max_backward_limit, dist_cache, uint(num_matches[i]), matches[cur_match_pos:], model, &queue, nodes) + if skip < longCopyQuickStep { + skip = 0 + } + cur_match_pos += uint(num_matches[i]) + if num_matches[i] == 1 && backwardMatchLength(&matches[cur_match_pos-1]) > max_zopfli_len { + skip = brotli_max_size_t(backwardMatchLength(&matches[cur_match_pos-1]), skip) + } + + if skip > 1 { + skip-- + for skip != 0 { + i++ + if i+3 >= num_bytes { + break + } + evaluateNode(position, i, max_backward_limit, gap, dist_cache, model, &queue, nodes) + cur_match_pos += uint(num_matches[i]) + skip-- + } + } + } + + return computeShortestPathFromNodes(num_bytes, nodes) +} + +/* Computes the shortest path of commands from position to at most + position + num_bytes. + + On return, path->size() is the number of commands found and path[i] is the + length of the i-th command (copy length plus insert length). + Note that the sum of the lengths of all commands can be less than num_bytes. + + On return, the nodes[0..num_bytes] array will have the following + "ZopfliNode array invariant": + For each i in [1..num_bytes], if nodes[i].cost < kInfinity, then + (1) nodes[i].copy_length() >= 2 + (2) nodes[i].command_length() <= i and + (3) nodes[i - nodes[i].command_length()].cost < kInfinity + + REQUIRES: nodes != nil and len(nodes) >= num_bytes + 1 */ +func zopfliComputeShortestPath(num_bytes uint, position uint, ringbuffer []byte, ringbuffer_mask uint, params *encoderParams, dist_cache []int, hasher *h10, nodes []zopfliNode) uint { + var max_backward_limit uint = maxBackwardLimit(params.lgwin) + var max_zopfli_len uint = maxZopfliLen(params) + var model zopfliCostModel + var queue startPosQueue + var matches [2 * (maxNumMatchesH10 + 64)]backwardMatch + var store_end uint + if num_bytes >= hasher.StoreLookahead() { + store_end = position + num_bytes - hasher.StoreLookahead() + 1 + } else { + store_end = position + } + var i uint + var gap uint = 0 + var lz_matches_offset uint = 0 + nodes[0].length = 0 + nodes[0].u.cost = 0 + initZopfliCostModel(&model, ¶ms.dist, num_bytes) + zopfliCostModelSetFromLiteralCosts(&model, position, ringbuffer, ringbuffer_mask) + initStartPosQueue(&queue) + for i = 0; i+hasher.HashTypeLength()-1 < num_bytes; i++ { + var pos uint = position + i + var max_distance uint = brotli_min_size_t(pos, max_backward_limit) + var skip uint + var num_matches uint + num_matches = findAllMatchesH10(hasher, ¶ms.dictionary, ringbuffer, ringbuffer_mask, pos, num_bytes-i, max_distance, gap, params, matches[lz_matches_offset:]) + if num_matches > 0 && backwardMatchLength(&matches[num_matches-1]) > max_zopfli_len { + matches[0] = matches[num_matches-1] + num_matches = 1 + } + + skip = updateNodes(num_bytes, position, i, ringbuffer, ringbuffer_mask, params, max_backward_limit, dist_cache, num_matches, matches[:], &model, &queue, nodes) + if skip < longCopyQuickStep { + skip = 0 + } + if num_matches == 1 && backwardMatchLength(&matches[0]) > max_zopfli_len { + skip = brotli_max_size_t(backwardMatchLength(&matches[0]), skip) + } + + if skip > 1 { + /* Add the tail of the copy to the hasher. */ + hasher.StoreRange(ringbuffer, ringbuffer_mask, pos+1, brotli_min_size_t(pos+skip, store_end)) + + skip-- + for skip != 0 { + i++ + if i+hasher.HashTypeLength()-1 >= num_bytes { + break + } + evaluateNode(position, i, max_backward_limit, gap, dist_cache, &model, &queue, nodes) + skip-- + } + } + } + + cleanupZopfliCostModel(&model) + return computeShortestPathFromNodes(num_bytes, nodes) +} + +func createZopfliBackwardReferences(num_bytes uint, position uint, ringbuffer []byte, ringbuffer_mask uint, params *encoderParams, hasher *h10, dist_cache []int, last_insert_len *uint, commands *[]command, num_literals *uint) { + var nodes []zopfliNode + nodes = make([]zopfliNode, (num_bytes + 1)) + initZopfliNodes(nodes, num_bytes+1) + zopfliComputeShortestPath(num_bytes, position, ringbuffer, ringbuffer_mask, params, dist_cache, hasher, nodes) + zopfliCreateCommands(num_bytes, position, nodes, dist_cache, last_insert_len, params, commands, num_literals) + nodes = nil +} + +func createHqZopfliBackwardReferences(num_bytes uint, position uint, ringbuffer []byte, ringbuffer_mask uint, params *encoderParams, hasher hasherHandle, dist_cache []int, last_insert_len *uint, commands *[]command, num_literals *uint) { + var max_backward_limit uint = maxBackwardLimit(params.lgwin) + var num_matches []uint32 = make([]uint32, num_bytes) + var matches_size uint = 4 * num_bytes + var store_end uint + if num_bytes >= hasher.StoreLookahead() { + store_end = position + num_bytes - hasher.StoreLookahead() + 1 + } else { + store_end = position + } + var cur_match_pos uint = 0 + var i uint + var orig_num_literals uint + var orig_last_insert_len uint + var orig_dist_cache [4]int + var orig_num_commands int + var model zopfliCostModel + var nodes []zopfliNode + var matches []backwardMatch = make([]backwardMatch, matches_size) + var gap uint = 0 + var shadow_matches uint = 0 + var new_array []backwardMatch + for i = 0; i+hasher.HashTypeLength()-1 < num_bytes; i++ { + var pos uint = position + i + var max_distance uint = brotli_min_size_t(pos, max_backward_limit) + var max_length uint = num_bytes - i + var num_found_matches uint + var cur_match_end uint + var j uint + + /* Ensure that we have enough free slots. */ + if matches_size < cur_match_pos+maxNumMatchesH10+shadow_matches { + var new_size uint = matches_size + if new_size == 0 { + new_size = cur_match_pos + maxNumMatchesH10 + shadow_matches + } + + for new_size < cur_match_pos+maxNumMatchesH10+shadow_matches { + new_size *= 2 + } + + new_array = make([]backwardMatch, new_size) + if matches_size != 0 { + copy(new_array, matches[:matches_size]) + } + + matches = new_array + matches_size = new_size + } + + num_found_matches = findAllMatchesH10(hasher.(*h10), ¶ms.dictionary, ringbuffer, ringbuffer_mask, pos, max_length, max_distance, gap, params, matches[cur_match_pos+shadow_matches:]) + cur_match_end = cur_match_pos + num_found_matches + for j = cur_match_pos; j+1 < cur_match_end; j++ { + assert(backwardMatchLength(&matches[j]) <= backwardMatchLength(&matches[j+1])) + } + + num_matches[i] = uint32(num_found_matches) + if num_found_matches > 0 { + var match_len uint = backwardMatchLength(&matches[cur_match_end-1]) + if match_len > maxZopfliLenQuality11 { + var skip uint = match_len - 1 + matches[cur_match_pos] = matches[cur_match_end-1] + cur_match_pos++ + num_matches[i] = 1 + + /* Add the tail of the copy to the hasher. */ + hasher.StoreRange(ringbuffer, ringbuffer_mask, pos+1, brotli_min_size_t(pos+match_len, store_end)) + var pos uint = i + for i := 0; i < int(skip); i++ { + num_matches[pos+1:][i] = 0 + } + i += skip + } else { + cur_match_pos = cur_match_end + } + } + } + + orig_num_literals = *num_literals + orig_last_insert_len = *last_insert_len + copy(orig_dist_cache[:], dist_cache[:4]) + orig_num_commands = len(*commands) + nodes = make([]zopfliNode, (num_bytes + 1)) + initZopfliCostModel(&model, ¶ms.dist, num_bytes) + for i = 0; i < 2; i++ { + initZopfliNodes(nodes, num_bytes+1) + if i == 0 { + zopfliCostModelSetFromLiteralCosts(&model, position, ringbuffer, ringbuffer_mask) + } else { + zopfliCostModelSetFromCommands(&model, position, ringbuffer, ringbuffer_mask, (*commands)[orig_num_commands:], orig_last_insert_len) + } + + *commands = (*commands)[:orig_num_commands] + *num_literals = orig_num_literals + *last_insert_len = orig_last_insert_len + copy(dist_cache, orig_dist_cache[:4]) + zopfliIterate(num_bytes, position, ringbuffer, ringbuffer_mask, params, gap, dist_cache, &model, num_matches, matches, nodes) + zopfliCreateCommands(num_bytes, position, nodes, dist_cache, last_insert_len, params, commands, num_literals) + } + + cleanupZopfliCostModel(&model) + nodes = nil + matches = nil + num_matches = nil +} diff --git a/vendor/github.com/andybalholm/brotli/bit_cost.go b/vendor/github.com/andybalholm/brotli/bit_cost.go new file mode 100644 index 000000000..0005fc15e --- /dev/null +++ b/vendor/github.com/andybalholm/brotli/bit_cost.go @@ -0,0 +1,436 @@ +package brotli + +/* Copyright 2013 Google Inc. All Rights Reserved. + + Distributed under MIT license. + See file LICENSE for detail or copy at https://opensource.org/licenses/MIT +*/ + +/* Functions to estimate the bit cost of Huffman trees. */ +func shannonEntropy(population []uint32, size uint, total *uint) float64 { + var sum uint = 0 + var retval float64 = 0 + var population_end []uint32 = population[size:] + var p uint + for -cap(population) < -cap(population_end) { + p = uint(population[0]) + population = population[1:] + sum += p + retval -= float64(p) * fastLog2(p) + } + + if sum != 0 { + retval += float64(sum) * fastLog2(sum) + } + *total = sum + return retval +} + +func bitsEntropy(population []uint32, size uint) float64 { + var sum uint + var retval float64 = shannonEntropy(population, size, &sum) + if retval < float64(sum) { + /* At least one bit per literal is needed. */ + retval = float64(sum) + } + + return retval +} + +const kOneSymbolHistogramCost float64 = 12 +const kTwoSymbolHistogramCost float64 = 20 +const kThreeSymbolHistogramCost float64 = 28 +const kFourSymbolHistogramCost float64 = 37 + +func populationCostLiteral(histogram *histogramLiteral) float64 { + var data_size uint = histogramDataSizeLiteral() + var count int = 0 + var s [5]uint + var bits float64 = 0.0 + var i uint + if histogram.total_count_ == 0 { + return kOneSymbolHistogramCost + } + + for i = 0; i < data_size; i++ { + if histogram.data_[i] > 0 { + s[count] = i + count++ + if count > 4 { + break + } + } + } + + if count == 1 { + return kOneSymbolHistogramCost + } + + if count == 2 { + return kTwoSymbolHistogramCost + float64(histogram.total_count_) + } + + if count == 3 { + var histo0 uint32 = histogram.data_[s[0]] + var histo1 uint32 = histogram.data_[s[1]] + var histo2 uint32 = histogram.data_[s[2]] + var histomax uint32 = brotli_max_uint32_t(histo0, brotli_max_uint32_t(histo1, histo2)) + return kThreeSymbolHistogramCost + 2*(float64(histo0)+float64(histo1)+float64(histo2)) - float64(histomax) + } + + if count == 4 { + var histo [4]uint32 + var h23 uint32 + var histomax uint32 + for i = 0; i < 4; i++ { + histo[i] = histogram.data_[s[i]] + } + + /* Sort */ + for i = 0; i < 4; i++ { + var j uint + for j = i + 1; j < 4; j++ { + if histo[j] > histo[i] { + var tmp uint32 = histo[j] + histo[j] = histo[i] + histo[i] = tmp + } + } + } + + h23 = histo[2] + histo[3] + histomax = brotli_max_uint32_t(h23, histo[0]) + return kFourSymbolHistogramCost + 3*float64(h23) + 2*(float64(histo[0])+float64(histo[1])) - float64(histomax) + } + { + var max_depth uint = 1 + var depth_histo = [codeLengthCodes]uint32{0} + /* In this loop we compute the entropy of the histogram and simultaneously + build a simplified histogram of the code length codes where we use the + zero repeat code 17, but we don't use the non-zero repeat code 16. */ + + var log2total float64 = fastLog2(histogram.total_count_) + for i = 0; i < data_size; { + if histogram.data_[i] > 0 { + var log2p float64 = log2total - fastLog2(uint(histogram.data_[i])) + /* Compute -log2(P(symbol)) = -log2(count(symbol)/total_count) = + = log2(total_count) - log2(count(symbol)) */ + + var depth uint = uint(log2p + 0.5) + /* Approximate the bit depth by round(-log2(P(symbol))) */ + bits += float64(histogram.data_[i]) * log2p + + if depth > 15 { + depth = 15 + } + + if depth > max_depth { + max_depth = depth + } + + depth_histo[depth]++ + i++ + } else { + var reps uint32 = 1 + /* Compute the run length of zeros and add the appropriate number of 0 + and 17 code length codes to the code length code histogram. */ + + var k uint + for k = i + 1; k < data_size && histogram.data_[k] == 0; k++ { + reps++ + } + + i += uint(reps) + if i == data_size { + /* Don't add any cost for the last zero run, since these are encoded + only implicitly. */ + break + } + + if reps < 3 { + depth_histo[0] += reps + } else { + reps -= 2 + for reps > 0 { + depth_histo[repeatZeroCodeLength]++ + + /* Add the 3 extra bits for the 17 code length code. */ + bits += 3 + + reps >>= 3 + } + } + } + } + + /* Add the estimated encoding cost of the code length code histogram. */ + bits += float64(18 + 2*max_depth) + + /* Add the entropy of the code length code histogram. */ + bits += bitsEntropy(depth_histo[:], codeLengthCodes) + } + + return bits +} + +func populationCostCommand(histogram *histogramCommand) float64 { + var data_size uint = histogramDataSizeCommand() + var count int = 0 + var s [5]uint + var bits float64 = 0.0 + var i uint + if histogram.total_count_ == 0 { + return kOneSymbolHistogramCost + } + + for i = 0; i < data_size; i++ { + if histogram.data_[i] > 0 { + s[count] = i + count++ + if count > 4 { + break + } + } + } + + if count == 1 { + return kOneSymbolHistogramCost + } + + if count == 2 { + return kTwoSymbolHistogramCost + float64(histogram.total_count_) + } + + if count == 3 { + var histo0 uint32 = histogram.data_[s[0]] + var histo1 uint32 = histogram.data_[s[1]] + var histo2 uint32 = histogram.data_[s[2]] + var histomax uint32 = brotli_max_uint32_t(histo0, brotli_max_uint32_t(histo1, histo2)) + return kThreeSymbolHistogramCost + 2*(float64(histo0)+float64(histo1)+float64(histo2)) - float64(histomax) + } + + if count == 4 { + var histo [4]uint32 + var h23 uint32 + var histomax uint32 + for i = 0; i < 4; i++ { + histo[i] = histogram.data_[s[i]] + } + + /* Sort */ + for i = 0; i < 4; i++ { + var j uint + for j = i + 1; j < 4; j++ { + if histo[j] > histo[i] { + var tmp uint32 = histo[j] + histo[j] = histo[i] + histo[i] = tmp + } + } + } + + h23 = histo[2] + histo[3] + histomax = brotli_max_uint32_t(h23, histo[0]) + return kFourSymbolHistogramCost + 3*float64(h23) + 2*(float64(histo[0])+float64(histo[1])) - float64(histomax) + } + { + var max_depth uint = 1 + var depth_histo = [codeLengthCodes]uint32{0} + /* In this loop we compute the entropy of the histogram and simultaneously + build a simplified histogram of the code length codes where we use the + zero repeat code 17, but we don't use the non-zero repeat code 16. */ + + var log2total float64 = fastLog2(histogram.total_count_) + for i = 0; i < data_size; { + if histogram.data_[i] > 0 { + var log2p float64 = log2total - fastLog2(uint(histogram.data_[i])) + /* Compute -log2(P(symbol)) = -log2(count(symbol)/total_count) = + = log2(total_count) - log2(count(symbol)) */ + + var depth uint = uint(log2p + 0.5) + /* Approximate the bit depth by round(-log2(P(symbol))) */ + bits += float64(histogram.data_[i]) * log2p + + if depth > 15 { + depth = 15 + } + + if depth > max_depth { + max_depth = depth + } + + depth_histo[depth]++ + i++ + } else { + var reps uint32 = 1 + /* Compute the run length of zeros and add the appropriate number of 0 + and 17 code length codes to the code length code histogram. */ + + var k uint + for k = i + 1; k < data_size && histogram.data_[k] == 0; k++ { + reps++ + } + + i += uint(reps) + if i == data_size { + /* Don't add any cost for the last zero run, since these are encoded + only implicitly. */ + break + } + + if reps < 3 { + depth_histo[0] += reps + } else { + reps -= 2 + for reps > 0 { + depth_histo[repeatZeroCodeLength]++ + + /* Add the 3 extra bits for the 17 code length code. */ + bits += 3 + + reps >>= 3 + } + } + } + } + + /* Add the estimated encoding cost of the code length code histogram. */ + bits += float64(18 + 2*max_depth) + + /* Add the entropy of the code length code histogram. */ + bits += bitsEntropy(depth_histo[:], codeLengthCodes) + } + + return bits +} + +func populationCostDistance(histogram *histogramDistance) float64 { + var data_size uint = histogramDataSizeDistance() + var count int = 0 + var s [5]uint + var bits float64 = 0.0 + var i uint + if histogram.total_count_ == 0 { + return kOneSymbolHistogramCost + } + + for i = 0; i < data_size; i++ { + if histogram.data_[i] > 0 { + s[count] = i + count++ + if count > 4 { + break + } + } + } + + if count == 1 { + return kOneSymbolHistogramCost + } + + if count == 2 { + return kTwoSymbolHistogramCost + float64(histogram.total_count_) + } + + if count == 3 { + var histo0 uint32 = histogram.data_[s[0]] + var histo1 uint32 = histogram.data_[s[1]] + var histo2 uint32 = histogram.data_[s[2]] + var histomax uint32 = brotli_max_uint32_t(histo0, brotli_max_uint32_t(histo1, histo2)) + return kThreeSymbolHistogramCost + 2*(float64(histo0)+float64(histo1)+float64(histo2)) - float64(histomax) + } + + if count == 4 { + var histo [4]uint32 + var h23 uint32 + var histomax uint32 + for i = 0; i < 4; i++ { + histo[i] = histogram.data_[s[i]] + } + + /* Sort */ + for i = 0; i < 4; i++ { + var j uint + for j = i + 1; j < 4; j++ { + if histo[j] > histo[i] { + var tmp uint32 = histo[j] + histo[j] = histo[i] + histo[i] = tmp + } + } + } + + h23 = histo[2] + histo[3] + histomax = brotli_max_uint32_t(h23, histo[0]) + return kFourSymbolHistogramCost + 3*float64(h23) + 2*(float64(histo[0])+float64(histo[1])) - float64(histomax) + } + { + var max_depth uint = 1 + var depth_histo = [codeLengthCodes]uint32{0} + /* In this loop we compute the entropy of the histogram and simultaneously + build a simplified histogram of the code length codes where we use the + zero repeat code 17, but we don't use the non-zero repeat code 16. */ + + var log2total float64 = fastLog2(histogram.total_count_) + for i = 0; i < data_size; { + if histogram.data_[i] > 0 { + var log2p float64 = log2total - fastLog2(uint(histogram.data_[i])) + /* Compute -log2(P(symbol)) = -log2(count(symbol)/total_count) = + = log2(total_count) - log2(count(symbol)) */ + + var depth uint = uint(log2p + 0.5) + /* Approximate the bit depth by round(-log2(P(symbol))) */ + bits += float64(histogram.data_[i]) * log2p + + if depth > 15 { + depth = 15 + } + + if depth > max_depth { + max_depth = depth + } + + depth_histo[depth]++ + i++ + } else { + var reps uint32 = 1 + /* Compute the run length of zeros and add the appropriate number of 0 + and 17 code length codes to the code length code histogram. */ + + var k uint + for k = i + 1; k < data_size && histogram.data_[k] == 0; k++ { + reps++ + } + + i += uint(reps) + if i == data_size { + /* Don't add any cost for the last zero run, since these are encoded + only implicitly. */ + break + } + + if reps < 3 { + depth_histo[0] += reps + } else { + reps -= 2 + for reps > 0 { + depth_histo[repeatZeroCodeLength]++ + + /* Add the 3 extra bits for the 17 code length code. */ + bits += 3 + + reps >>= 3 + } + } + } + } + + /* Add the estimated encoding cost of the code length code histogram. */ + bits += float64(18 + 2*max_depth) + + /* Add the entropy of the code length code histogram. */ + bits += bitsEntropy(depth_histo[:], codeLengthCodes) + } + + return bits +} diff --git a/vendor/github.com/andybalholm/brotli/bit_reader.go b/vendor/github.com/andybalholm/brotli/bit_reader.go new file mode 100644 index 000000000..fba8687c6 --- /dev/null +++ b/vendor/github.com/andybalholm/brotli/bit_reader.go @@ -0,0 +1,266 @@ +package brotli + +import "encoding/binary" + +/* Copyright 2013 Google Inc. All Rights Reserved. + + Distributed under MIT license. + See file LICENSE for detail or copy at https://opensource.org/licenses/MIT +*/ + +/* Bit reading helpers */ + +const shortFillBitWindowRead = (8 >> 1) + +var kBitMask = [33]uint32{ + 0x00000000, + 0x00000001, + 0x00000003, + 0x00000007, + 0x0000000F, + 0x0000001F, + 0x0000003F, + 0x0000007F, + 0x000000FF, + 0x000001FF, + 0x000003FF, + 0x000007FF, + 0x00000FFF, + 0x00001FFF, + 0x00003FFF, + 0x00007FFF, + 0x0000FFFF, + 0x0001FFFF, + 0x0003FFFF, + 0x0007FFFF, + 0x000FFFFF, + 0x001FFFFF, + 0x003FFFFF, + 0x007FFFFF, + 0x00FFFFFF, + 0x01FFFFFF, + 0x03FFFFFF, + 0x07FFFFFF, + 0x0FFFFFFF, + 0x1FFFFFFF, + 0x3FFFFFFF, + 0x7FFFFFFF, + 0xFFFFFFFF, +} + +func bitMask(n uint32) uint32 { + return kBitMask[n] +} + +type bitReader struct { + val_ uint64 + bit_pos_ uint32 + input []byte + input_len uint + byte_pos uint +} + +type bitReaderState struct { + val_ uint64 + bit_pos_ uint32 + input []byte + input_len uint + byte_pos uint +} + +/* Initializes the BrotliBitReader fields. */ + +/* Ensures that accumulator is not empty. + May consume up to sizeof(brotli_reg_t) - 1 bytes of input. + Returns false if data is required but there is no input available. + For BROTLI_ALIGNED_READ this function also prepares bit reader for aligned + reading. */ +func bitReaderSaveState(from *bitReader, to *bitReaderState) { + to.val_ = from.val_ + to.bit_pos_ = from.bit_pos_ + to.input = from.input + to.input_len = from.input_len + to.byte_pos = from.byte_pos +} + +func bitReaderRestoreState(to *bitReader, from *bitReaderState) { + to.val_ = from.val_ + to.bit_pos_ = from.bit_pos_ + to.input = from.input + to.input_len = from.input_len + to.byte_pos = from.byte_pos +} + +func getAvailableBits(br *bitReader) uint32 { + return 64 - br.bit_pos_ +} + +/* Returns amount of unread bytes the bit reader still has buffered from the + BrotliInput, including whole bytes in br->val_. */ +func getRemainingBytes(br *bitReader) uint { + return uint(uint32(br.input_len-br.byte_pos) + (getAvailableBits(br) >> 3)) +} + +/* Checks if there is at least |num| bytes left in the input ring-buffer + (excluding the bits remaining in br->val_). */ +func checkInputAmount(br *bitReader, num uint) bool { + return br.input_len-br.byte_pos >= num +} + +/* Guarantees that there are at least |n_bits| + 1 bits in accumulator. + Precondition: accumulator contains at least 1 bit. + |n_bits| should be in the range [1..24] for regular build. For portable + non-64-bit little-endian build only 16 bits are safe to request. */ +func fillBitWindow(br *bitReader, n_bits uint32) { + if br.bit_pos_ >= 32 { + br.val_ >>= 32 + br.bit_pos_ ^= 32 /* here same as -= 32 because of the if condition */ + br.val_ |= (uint64(binary.LittleEndian.Uint32(br.input[br.byte_pos:]))) << 32 + br.byte_pos += 4 + } +} + +/* Mostly like BrotliFillBitWindow, but guarantees only 16 bits and reads no + more than BROTLI_SHORT_FILL_BIT_WINDOW_READ bytes of input. */ +func fillBitWindow16(br *bitReader) { + fillBitWindow(br, 17) +} + +/* Tries to pull one byte of input to accumulator. + Returns false if there is no input available. */ +func pullByte(br *bitReader) bool { + if br.byte_pos == br.input_len { + return false + } + + br.val_ >>= 8 + br.val_ |= (uint64(br.input[br.byte_pos])) << 56 + br.bit_pos_ -= 8 + br.byte_pos++ + return true +} + +/* Returns currently available bits. + The number of valid bits could be calculated by BrotliGetAvailableBits. */ +func getBitsUnmasked(br *bitReader) uint64 { + return br.val_ >> br.bit_pos_ +} + +/* Like BrotliGetBits, but does not mask the result. + The result contains at least 16 valid bits. */ +func get16BitsUnmasked(br *bitReader) uint32 { + fillBitWindow(br, 16) + return uint32(getBitsUnmasked(br)) +} + +/* Returns the specified number of bits from |br| without advancing bit + position. */ +func getBits(br *bitReader, n_bits uint32) uint32 { + fillBitWindow(br, n_bits) + return uint32(getBitsUnmasked(br)) & bitMask(n_bits) +} + +/* Tries to peek the specified amount of bits. Returns false, if there + is not enough input. */ +func safeGetBits(br *bitReader, n_bits uint32, val *uint32) bool { + for getAvailableBits(br) < n_bits { + if !pullByte(br) { + return false + } + } + + *val = uint32(getBitsUnmasked(br)) & bitMask(n_bits) + return true +} + +/* Advances the bit pos by |n_bits|. */ +func dropBits(br *bitReader, n_bits uint32) { + br.bit_pos_ += n_bits +} + +func bitReaderUnload(br *bitReader) { + var unused_bytes uint32 = getAvailableBits(br) >> 3 + var unused_bits uint32 = unused_bytes << 3 + br.byte_pos -= uint(unused_bytes) + if unused_bits == 64 { + br.val_ = 0 + } else { + br.val_ <<= unused_bits + } + + br.bit_pos_ += unused_bits +} + +/* Reads the specified number of bits from |br| and advances the bit pos. + Precondition: accumulator MUST contain at least |n_bits|. */ +func takeBits(br *bitReader, n_bits uint32, val *uint32) { + *val = uint32(getBitsUnmasked(br)) & bitMask(n_bits) + dropBits(br, n_bits) +} + +/* Reads the specified number of bits from |br| and advances the bit pos. + Assumes that there is enough input to perform BrotliFillBitWindow. */ +func readBits(br *bitReader, n_bits uint32) uint32 { + var val uint32 + fillBitWindow(br, n_bits) + takeBits(br, n_bits, &val) + return val +} + +/* Tries to read the specified amount of bits. Returns false, if there + is not enough input. |n_bits| MUST be positive. */ +func safeReadBits(br *bitReader, n_bits uint32, val *uint32) bool { + for getAvailableBits(br) < n_bits { + if !pullByte(br) { + return false + } + } + + takeBits(br, n_bits, val) + return true +} + +/* Advances the bit reader position to the next byte boundary and verifies + that any skipped bits are set to zero. */ +func bitReaderJumpToByteBoundary(br *bitReader) bool { + var pad_bits_count uint32 = getAvailableBits(br) & 0x7 + var pad_bits uint32 = 0 + if pad_bits_count != 0 { + takeBits(br, pad_bits_count, &pad_bits) + } + + return pad_bits == 0 +} + +/* Copies remaining input bytes stored in the bit reader to the output. Value + |num| may not be larger than BrotliGetRemainingBytes. The bit reader must be + warmed up again after this. */ +func copyBytes(dest []byte, br *bitReader, num uint) { + for getAvailableBits(br) >= 8 && num > 0 { + dest[0] = byte(getBitsUnmasked(br)) + dropBits(br, 8) + dest = dest[1:] + num-- + } + + copy(dest, br.input[br.byte_pos:][:num]) + br.byte_pos += num +} + +func initBitReader(br *bitReader) { + br.val_ = 0 + br.bit_pos_ = 64 +} + +func warmupBitReader(br *bitReader) bool { + /* Fixing alignment after unaligned BrotliFillWindow would result accumulator + overflow. If unalignment is caused by BrotliSafeReadBits, then there is + enough space in accumulator to fix alignment. */ + if getAvailableBits(br) == 0 { + if !pullByte(br) { + return false + } + } + + return true +} diff --git a/vendor/github.com/andybalholm/brotli/bitwriter.go b/vendor/github.com/andybalholm/brotli/bitwriter.go new file mode 100644 index 000000000..dfc60360f --- /dev/null +++ b/vendor/github.com/andybalholm/brotli/bitwriter.go @@ -0,0 +1,56 @@ +package brotli + +/* Copyright 2010 Google Inc. All Rights Reserved. + + Distributed under MIT license. + See file LICENSE for detail or copy at https://opensource.org/licenses/MIT +*/ + +/* Write bits into a byte array. */ + +type bitWriter struct { + dst []byte + + // Data waiting to be written is the low nbits of bits. + bits uint64 + nbits uint +} + +func (w *bitWriter) writeBits(nb uint, b uint64) { + w.bits |= b << w.nbits + w.nbits += nb + if w.nbits >= 32 { + bits := w.bits + w.bits >>= 32 + w.nbits -= 32 + w.dst = append(w.dst, + byte(bits), + byte(bits>>8), + byte(bits>>16), + byte(bits>>24), + ) + } +} + +func (w *bitWriter) writeSingleBit(bit bool) { + if bit { + w.writeBits(1, 1) + } else { + w.writeBits(1, 0) + } +} + +func (w *bitWriter) jumpToByteBoundary() { + dst := w.dst + for w.nbits != 0 { + dst = append(dst, byte(w.bits)) + w.bits >>= 8 + if w.nbits > 8 { // Avoid underflow + w.nbits -= 8 + } else { + w.nbits = 0 + } + } + w.bits = 0 + w.dst = dst +} diff --git a/vendor/github.com/andybalholm/brotli/block_splitter.go b/vendor/github.com/andybalholm/brotli/block_splitter.go new file mode 100644 index 000000000..978a13147 --- /dev/null +++ b/vendor/github.com/andybalholm/brotli/block_splitter.go @@ -0,0 +1,144 @@ +package brotli + +/* Copyright 2013 Google Inc. All Rights Reserved. + + Distributed under MIT license. + See file LICENSE for detail or copy at https://opensource.org/licenses/MIT +*/ + +/* Block split point selection utilities. */ + +type blockSplit struct { + num_types uint + num_blocks uint + types []byte + lengths []uint32 + types_alloc_size uint + lengths_alloc_size uint +} + +const ( + kMaxLiteralHistograms uint = 100 + kMaxCommandHistograms uint = 50 + kLiteralBlockSwitchCost float64 = 28.1 + kCommandBlockSwitchCost float64 = 13.5 + kDistanceBlockSwitchCost float64 = 14.6 + kLiteralStrideLength uint = 70 + kCommandStrideLength uint = 40 + kSymbolsPerLiteralHistogram uint = 544 + kSymbolsPerCommandHistogram uint = 530 + kSymbolsPerDistanceHistogram uint = 544 + kMinLengthForBlockSplitting uint = 128 + kIterMulForRefining uint = 2 + kMinItersForRefining uint = 100 +) + +func countLiterals(cmds []command) uint { + var total_length uint = 0 + /* Count how many we have. */ + + for i := range cmds { + total_length += uint(cmds[i].insert_len_) + } + + return total_length +} + +func copyLiteralsToByteArray(cmds []command, data []byte, offset uint, mask uint, literals []byte) { + var pos uint = 0 + var from_pos uint = offset & mask + for i := range cmds { + var insert_len uint = uint(cmds[i].insert_len_) + if from_pos+insert_len > mask { + var head_size uint = mask + 1 - from_pos + copy(literals[pos:], data[from_pos:][:head_size]) + from_pos = 0 + pos += head_size + insert_len -= head_size + } + + if insert_len > 0 { + copy(literals[pos:], data[from_pos:][:insert_len]) + pos += insert_len + } + + from_pos = uint((uint32(from_pos+insert_len) + commandCopyLen(&cmds[i])) & uint32(mask)) + } +} + +func myRand(seed *uint32) uint32 { + /* Initial seed should be 7. In this case, loop length is (1 << 29). */ + *seed *= 16807 + + return *seed +} + +func bitCost(count uint) float64 { + if count == 0 { + return -2.0 + } else { + return fastLog2(count) + } +} + +const histogramsPerBatch = 64 + +const clustersPerBatch = 16 + +func initBlockSplit(self *blockSplit) { + self.num_types = 0 + self.num_blocks = 0 + self.types = self.types[:0] + self.lengths = self.lengths[:0] + self.types_alloc_size = 0 + self.lengths_alloc_size = 0 +} + +func splitBlock(cmds []command, data []byte, pos uint, mask uint, params *encoderParams, literal_split *blockSplit, insert_and_copy_split *blockSplit, dist_split *blockSplit) { + { + var literals_count uint = countLiterals(cmds) + var literals []byte = make([]byte, literals_count) + + /* Create a continuous array of literals. */ + copyLiteralsToByteArray(cmds, data, pos, mask, literals) + + /* Create the block split on the array of literals. + Literal histograms have alphabet size 256. */ + splitByteVectorLiteral(literals, literals_count, kSymbolsPerLiteralHistogram, kMaxLiteralHistograms, kLiteralStrideLength, kLiteralBlockSwitchCost, params, literal_split) + + literals = nil + } + { + var insert_and_copy_codes []uint16 = make([]uint16, len(cmds)) + /* Compute prefix codes for commands. */ + + for i := range cmds { + insert_and_copy_codes[i] = cmds[i].cmd_prefix_ + } + + /* Create the block split on the array of command prefixes. */ + splitByteVectorCommand(insert_and_copy_codes, kSymbolsPerCommandHistogram, kMaxCommandHistograms, kCommandStrideLength, kCommandBlockSwitchCost, params, insert_and_copy_split) + + /* TODO: reuse for distances? */ + + insert_and_copy_codes = nil + } + { + var distance_prefixes []uint16 = make([]uint16, len(cmds)) + var j uint = 0 + /* Create a continuous array of distance prefixes. */ + + for i := range cmds { + var cmd *command = &cmds[i] + if commandCopyLen(cmd) != 0 && cmd.cmd_prefix_ >= 128 { + distance_prefixes[j] = cmd.dist_prefix_ & 0x3FF + j++ + } + } + + /* Create the block split on the array of distance prefixes. */ + splitByteVectorDistance(distance_prefixes, j, kSymbolsPerDistanceHistogram, kMaxCommandHistograms, kCommandStrideLength, kDistanceBlockSwitchCost, params, dist_split) + + distance_prefixes = nil + } +} diff --git a/vendor/github.com/andybalholm/brotli/block_splitter_command.go b/vendor/github.com/andybalholm/brotli/block_splitter_command.go new file mode 100644 index 000000000..9dec13e4d --- /dev/null +++ b/vendor/github.com/andybalholm/brotli/block_splitter_command.go @@ -0,0 +1,434 @@ +package brotli + +import "math" + +/* Copyright 2013 Google Inc. All Rights Reserved. + + Distributed under MIT license. + See file LICENSE for detail or copy at https://opensource.org/licenses/MIT +*/ + +func initialEntropyCodesCommand(data []uint16, length uint, stride uint, num_histograms uint, histograms []histogramCommand) { + var seed uint32 = 7 + var block_length uint = length / num_histograms + var i uint + clearHistogramsCommand(histograms, num_histograms) + for i = 0; i < num_histograms; i++ { + var pos uint = length * i / num_histograms + if i != 0 { + pos += uint(myRand(&seed) % uint32(block_length)) + } + + if pos+stride >= length { + pos = length - stride - 1 + } + + histogramAddVectorCommand(&histograms[i], data[pos:], stride) + } +} + +func randomSampleCommand(seed *uint32, data []uint16, length uint, stride uint, sample *histogramCommand) { + var pos uint = 0 + if stride >= length { + stride = length + } else { + pos = uint(myRand(seed) % uint32(length-stride+1)) + } + + histogramAddVectorCommand(sample, data[pos:], stride) +} + +func refineEntropyCodesCommand(data []uint16, length uint, stride uint, num_histograms uint, histograms []histogramCommand) { + var iters uint = kIterMulForRefining*length/stride + kMinItersForRefining + var seed uint32 = 7 + var iter uint + iters = ((iters + num_histograms - 1) / num_histograms) * num_histograms + for iter = 0; iter < iters; iter++ { + var sample histogramCommand + histogramClearCommand(&sample) + randomSampleCommand(&seed, data, length, stride, &sample) + histogramAddHistogramCommand(&histograms[iter%num_histograms], &sample) + } +} + +/* Assigns a block id from the range [0, num_histograms) to each data element + in data[0..length) and fills in block_id[0..length) with the assigned values. + Returns the number of blocks, i.e. one plus the number of block switches. */ +func findBlocksCommand(data []uint16, length uint, block_switch_bitcost float64, num_histograms uint, histograms []histogramCommand, insert_cost []float64, cost []float64, switch_signal []byte, block_id []byte) uint { + var data_size uint = histogramDataSizeCommand() + var bitmaplen uint = (num_histograms + 7) >> 3 + var num_blocks uint = 1 + var i uint + var j uint + assert(num_histograms <= 256) + if num_histograms <= 1 { + for i = 0; i < length; i++ { + block_id[i] = 0 + } + + return 1 + } + + for i := 0; i < int(data_size*num_histograms); i++ { + insert_cost[i] = 0 + } + for i = 0; i < num_histograms; i++ { + insert_cost[i] = fastLog2(uint(uint32(histograms[i].total_count_))) + } + + for i = data_size; i != 0; { + i-- + for j = 0; j < num_histograms; j++ { + insert_cost[i*num_histograms+j] = insert_cost[j] - bitCost(uint(histograms[j].data_[i])) + } + } + + for i := 0; i < int(num_histograms); i++ { + cost[i] = 0 + } + for i := 0; i < int(length*bitmaplen); i++ { + switch_signal[i] = 0 + } + + /* After each iteration of this loop, cost[k] will contain the difference + between the minimum cost of arriving at the current byte position using + entropy code k, and the minimum cost of arriving at the current byte + position. This difference is capped at the block switch cost, and if it + reaches block switch cost, it means that when we trace back from the last + position, we need to switch here. */ + for i = 0; i < length; i++ { + var byte_ix uint = i + var ix uint = byte_ix * bitmaplen + var insert_cost_ix uint = uint(data[byte_ix]) * num_histograms + var min_cost float64 = 1e99 + var block_switch_cost float64 = block_switch_bitcost + var k uint + for k = 0; k < num_histograms; k++ { + /* We are coding the symbol in data[byte_ix] with entropy code k. */ + cost[k] += insert_cost[insert_cost_ix+k] + + if cost[k] < min_cost { + min_cost = cost[k] + block_id[byte_ix] = byte(k) + } + } + + /* More blocks for the beginning. */ + if byte_ix < 2000 { + block_switch_cost *= 0.77 + 0.07*float64(byte_ix)/2000 + } + + for k = 0; k < num_histograms; k++ { + cost[k] -= min_cost + if cost[k] >= block_switch_cost { + var mask byte = byte(1 << (k & 7)) + cost[k] = block_switch_cost + assert(k>>3 < bitmaplen) + switch_signal[ix+(k>>3)] |= mask + /* Trace back from the last position and switch at the marked places. */ + } + } + } + { + var byte_ix uint = length - 1 + var ix uint = byte_ix * bitmaplen + var cur_id byte = block_id[byte_ix] + for byte_ix > 0 { + var mask byte = byte(1 << (cur_id & 7)) + assert(uint(cur_id)>>3 < bitmaplen) + byte_ix-- + ix -= bitmaplen + if switch_signal[ix+uint(cur_id>>3)]&mask != 0 { + if cur_id != block_id[byte_ix] { + cur_id = block_id[byte_ix] + num_blocks++ + } + } + + block_id[byte_ix] = cur_id + } + } + + return num_blocks +} + +var remapBlockIdsCommand_kInvalidId uint16 = 256 + +func remapBlockIdsCommand(block_ids []byte, length uint, new_id []uint16, num_histograms uint) uint { + var next_id uint16 = 0 + var i uint + for i = 0; i < num_histograms; i++ { + new_id[i] = remapBlockIdsCommand_kInvalidId + } + + for i = 0; i < length; i++ { + assert(uint(block_ids[i]) < num_histograms) + if new_id[block_ids[i]] == remapBlockIdsCommand_kInvalidId { + new_id[block_ids[i]] = next_id + next_id++ + } + } + + for i = 0; i < length; i++ { + block_ids[i] = byte(new_id[block_ids[i]]) + assert(uint(block_ids[i]) < num_histograms) + } + + assert(uint(next_id) <= num_histograms) + return uint(next_id) +} + +func buildBlockHistogramsCommand(data []uint16, length uint, block_ids []byte, num_histograms uint, histograms []histogramCommand) { + var i uint + clearHistogramsCommand(histograms, num_histograms) + for i = 0; i < length; i++ { + histogramAddCommand(&histograms[block_ids[i]], uint(data[i])) + } +} + +var clusterBlocksCommand_kInvalidIndex uint32 = math.MaxUint32 + +func clusterBlocksCommand(data []uint16, length uint, num_blocks uint, block_ids []byte, split *blockSplit) { + var histogram_symbols []uint32 = make([]uint32, num_blocks) + var block_lengths []uint32 = make([]uint32, num_blocks) + var expected_num_clusters uint = clustersPerBatch * (num_blocks + histogramsPerBatch - 1) / histogramsPerBatch + var all_histograms_size uint = 0 + var all_histograms_capacity uint = expected_num_clusters + var all_histograms []histogramCommand = make([]histogramCommand, all_histograms_capacity) + var cluster_size_size uint = 0 + var cluster_size_capacity uint = expected_num_clusters + var cluster_size []uint32 = make([]uint32, cluster_size_capacity) + var num_clusters uint = 0 + var histograms []histogramCommand = make([]histogramCommand, brotli_min_size_t(num_blocks, histogramsPerBatch)) + var max_num_pairs uint = histogramsPerBatch * histogramsPerBatch / 2 + var pairs_capacity uint = max_num_pairs + 1 + var pairs []histogramPair = make([]histogramPair, pairs_capacity) + var pos uint = 0 + var clusters []uint32 + var num_final_clusters uint + var new_index []uint32 + var i uint + var sizes = [histogramsPerBatch]uint32{0} + var new_clusters = [histogramsPerBatch]uint32{0} + var symbols = [histogramsPerBatch]uint32{0} + var remap = [histogramsPerBatch]uint32{0} + + for i := 0; i < int(num_blocks); i++ { + block_lengths[i] = 0 + } + { + var block_idx uint = 0 + for i = 0; i < length; i++ { + assert(block_idx < num_blocks) + block_lengths[block_idx]++ + if i+1 == length || block_ids[i] != block_ids[i+1] { + block_idx++ + } + } + + assert(block_idx == num_blocks) + } + + for i = 0; i < num_blocks; i += histogramsPerBatch { + var num_to_combine uint = brotli_min_size_t(num_blocks-i, histogramsPerBatch) + var num_new_clusters uint + var j uint + for j = 0; j < num_to_combine; j++ { + var k uint + histogramClearCommand(&histograms[j]) + for k = 0; uint32(k) < block_lengths[i+j]; k++ { + histogramAddCommand(&histograms[j], uint(data[pos])) + pos++ + } + + histograms[j].bit_cost_ = populationCostCommand(&histograms[j]) + new_clusters[j] = uint32(j) + symbols[j] = uint32(j) + sizes[j] = 1 + } + + num_new_clusters = histogramCombineCommand(histograms, sizes[:], symbols[:], new_clusters[:], []histogramPair(pairs), num_to_combine, num_to_combine, histogramsPerBatch, max_num_pairs) + if all_histograms_capacity < (all_histograms_size + num_new_clusters) { + var _new_size uint + if all_histograms_capacity == 0 { + _new_size = all_histograms_size + num_new_clusters + } else { + _new_size = all_histograms_capacity + } + var new_array []histogramCommand + for _new_size < (all_histograms_size + num_new_clusters) { + _new_size *= 2 + } + new_array = make([]histogramCommand, _new_size) + if all_histograms_capacity != 0 { + copy(new_array, all_histograms[:all_histograms_capacity]) + } + + all_histograms = new_array + all_histograms_capacity = _new_size + } + + brotli_ensure_capacity_uint32_t(&cluster_size, &cluster_size_capacity, cluster_size_size+num_new_clusters) + for j = 0; j < num_new_clusters; j++ { + all_histograms[all_histograms_size] = histograms[new_clusters[j]] + all_histograms_size++ + cluster_size[cluster_size_size] = sizes[new_clusters[j]] + cluster_size_size++ + remap[new_clusters[j]] = uint32(j) + } + + for j = 0; j < num_to_combine; j++ { + histogram_symbols[i+j] = uint32(num_clusters) + remap[symbols[j]] + } + + num_clusters += num_new_clusters + assert(num_clusters == cluster_size_size) + assert(num_clusters == all_histograms_size) + } + + histograms = nil + + max_num_pairs = brotli_min_size_t(64*num_clusters, (num_clusters/2)*num_clusters) + if pairs_capacity < max_num_pairs+1 { + pairs = nil + pairs = make([]histogramPair, (max_num_pairs + 1)) + } + + clusters = make([]uint32, num_clusters) + for i = 0; i < num_clusters; i++ { + clusters[i] = uint32(i) + } + + num_final_clusters = histogramCombineCommand(all_histograms, cluster_size, histogram_symbols, clusters, pairs, num_clusters, num_blocks, maxNumberOfBlockTypes, max_num_pairs) + pairs = nil + cluster_size = nil + + new_index = make([]uint32, num_clusters) + for i = 0; i < num_clusters; i++ { + new_index[i] = clusterBlocksCommand_kInvalidIndex + } + pos = 0 + { + var next_index uint32 = 0 + for i = 0; i < num_blocks; i++ { + var histo histogramCommand + var j uint + var best_out uint32 + var best_bits float64 + histogramClearCommand(&histo) + for j = 0; uint32(j) < block_lengths[i]; j++ { + histogramAddCommand(&histo, uint(data[pos])) + pos++ + } + + if i == 0 { + best_out = histogram_symbols[0] + } else { + best_out = histogram_symbols[i-1] + } + best_bits = histogramBitCostDistanceCommand(&histo, &all_histograms[best_out]) + for j = 0; j < num_final_clusters; j++ { + var cur_bits float64 = histogramBitCostDistanceCommand(&histo, &all_histograms[clusters[j]]) + if cur_bits < best_bits { + best_bits = cur_bits + best_out = clusters[j] + } + } + + histogram_symbols[i] = best_out + if new_index[best_out] == clusterBlocksCommand_kInvalidIndex { + new_index[best_out] = next_index + next_index++ + } + } + } + + clusters = nil + all_histograms = nil + brotli_ensure_capacity_uint8_t(&split.types, &split.types_alloc_size, num_blocks) + brotli_ensure_capacity_uint32_t(&split.lengths, &split.lengths_alloc_size, num_blocks) + { + var cur_length uint32 = 0 + var block_idx uint = 0 + var max_type byte = 0 + for i = 0; i < num_blocks; i++ { + cur_length += block_lengths[i] + if i+1 == num_blocks || histogram_symbols[i] != histogram_symbols[i+1] { + var id byte = byte(new_index[histogram_symbols[i]]) + split.types[block_idx] = id + split.lengths[block_idx] = cur_length + max_type = brotli_max_uint8_t(max_type, id) + cur_length = 0 + block_idx++ + } + } + + split.num_blocks = block_idx + split.num_types = uint(max_type) + 1 + } + + new_index = nil + block_lengths = nil + histogram_symbols = nil +} + +func splitByteVectorCommand(data []uint16, literals_per_histogram uint, max_histograms uint, sampling_stride_length uint, block_switch_cost float64, params *encoderParams, split *blockSplit) { + length := uint(len(data)) + var data_size uint = histogramDataSizeCommand() + var num_histograms uint = length/literals_per_histogram + 1 + var histograms []histogramCommand + if num_histograms > max_histograms { + num_histograms = max_histograms + } + + if length == 0 { + split.num_types = 1 + return + } else if length < kMinLengthForBlockSplitting { + brotli_ensure_capacity_uint8_t(&split.types, &split.types_alloc_size, split.num_blocks+1) + brotli_ensure_capacity_uint32_t(&split.lengths, &split.lengths_alloc_size, split.num_blocks+1) + split.num_types = 1 + split.types[split.num_blocks] = 0 + split.lengths[split.num_blocks] = uint32(length) + split.num_blocks++ + return + } + + histograms = make([]histogramCommand, num_histograms) + + /* Find good entropy codes. */ + initialEntropyCodesCommand(data, length, sampling_stride_length, num_histograms, histograms) + + refineEntropyCodesCommand(data, length, sampling_stride_length, num_histograms, histograms) + { + var block_ids []byte = make([]byte, length) + var num_blocks uint = 0 + var bitmaplen uint = (num_histograms + 7) >> 3 + var insert_cost []float64 = make([]float64, (data_size * num_histograms)) + var cost []float64 = make([]float64, num_histograms) + var switch_signal []byte = make([]byte, (length * bitmaplen)) + var new_id []uint16 = make([]uint16, num_histograms) + var iters uint + if params.quality < hqZopflificationQuality { + iters = 3 + } else { + iters = 10 + } + /* Find a good path through literals with the good entropy codes. */ + + var i uint + for i = 0; i < iters; i++ { + num_blocks = findBlocksCommand(data, length, block_switch_cost, num_histograms, histograms, insert_cost, cost, switch_signal, block_ids) + num_histograms = remapBlockIdsCommand(block_ids, length, new_id, num_histograms) + buildBlockHistogramsCommand(data, length, block_ids, num_histograms, histograms) + } + + insert_cost = nil + cost = nil + switch_signal = nil + new_id = nil + histograms = nil + clusterBlocksCommand(data, length, num_blocks, block_ids, split) + block_ids = nil + } +} diff --git a/vendor/github.com/andybalholm/brotli/block_splitter_distance.go b/vendor/github.com/andybalholm/brotli/block_splitter_distance.go new file mode 100644 index 000000000..953530d51 --- /dev/null +++ b/vendor/github.com/andybalholm/brotli/block_splitter_distance.go @@ -0,0 +1,433 @@ +package brotli + +import "math" + +/* Copyright 2013 Google Inc. All Rights Reserved. + + Distributed under MIT license. + See file LICENSE for detail or copy at https://opensource.org/licenses/MIT +*/ + +func initialEntropyCodesDistance(data []uint16, length uint, stride uint, num_histograms uint, histograms []histogramDistance) { + var seed uint32 = 7 + var block_length uint = length / num_histograms + var i uint + clearHistogramsDistance(histograms, num_histograms) + for i = 0; i < num_histograms; i++ { + var pos uint = length * i / num_histograms + if i != 0 { + pos += uint(myRand(&seed) % uint32(block_length)) + } + + if pos+stride >= length { + pos = length - stride - 1 + } + + histogramAddVectorDistance(&histograms[i], data[pos:], stride) + } +} + +func randomSampleDistance(seed *uint32, data []uint16, length uint, stride uint, sample *histogramDistance) { + var pos uint = 0 + if stride >= length { + stride = length + } else { + pos = uint(myRand(seed) % uint32(length-stride+1)) + } + + histogramAddVectorDistance(sample, data[pos:], stride) +} + +func refineEntropyCodesDistance(data []uint16, length uint, stride uint, num_histograms uint, histograms []histogramDistance) { + var iters uint = kIterMulForRefining*length/stride + kMinItersForRefining + var seed uint32 = 7 + var iter uint + iters = ((iters + num_histograms - 1) / num_histograms) * num_histograms + for iter = 0; iter < iters; iter++ { + var sample histogramDistance + histogramClearDistance(&sample) + randomSampleDistance(&seed, data, length, stride, &sample) + histogramAddHistogramDistance(&histograms[iter%num_histograms], &sample) + } +} + +/* Assigns a block id from the range [0, num_histograms) to each data element + in data[0..length) and fills in block_id[0..length) with the assigned values. + Returns the number of blocks, i.e. one plus the number of block switches. */ +func findBlocksDistance(data []uint16, length uint, block_switch_bitcost float64, num_histograms uint, histograms []histogramDistance, insert_cost []float64, cost []float64, switch_signal []byte, block_id []byte) uint { + var data_size uint = histogramDataSizeDistance() + var bitmaplen uint = (num_histograms + 7) >> 3 + var num_blocks uint = 1 + var i uint + var j uint + assert(num_histograms <= 256) + if num_histograms <= 1 { + for i = 0; i < length; i++ { + block_id[i] = 0 + } + + return 1 + } + + for i := 0; i < int(data_size*num_histograms); i++ { + insert_cost[i] = 0 + } + for i = 0; i < num_histograms; i++ { + insert_cost[i] = fastLog2(uint(uint32(histograms[i].total_count_))) + } + + for i = data_size; i != 0; { + i-- + for j = 0; j < num_histograms; j++ { + insert_cost[i*num_histograms+j] = insert_cost[j] - bitCost(uint(histograms[j].data_[i])) + } + } + + for i := 0; i < int(num_histograms); i++ { + cost[i] = 0 + } + for i := 0; i < int(length*bitmaplen); i++ { + switch_signal[i] = 0 + } + + /* After each iteration of this loop, cost[k] will contain the difference + between the minimum cost of arriving at the current byte position using + entropy code k, and the minimum cost of arriving at the current byte + position. This difference is capped at the block switch cost, and if it + reaches block switch cost, it means that when we trace back from the last + position, we need to switch here. */ + for i = 0; i < length; i++ { + var byte_ix uint = i + var ix uint = byte_ix * bitmaplen + var insert_cost_ix uint = uint(data[byte_ix]) * num_histograms + var min_cost float64 = 1e99 + var block_switch_cost float64 = block_switch_bitcost + var k uint + for k = 0; k < num_histograms; k++ { + /* We are coding the symbol in data[byte_ix] with entropy code k. */ + cost[k] += insert_cost[insert_cost_ix+k] + + if cost[k] < min_cost { + min_cost = cost[k] + block_id[byte_ix] = byte(k) + } + } + + /* More blocks for the beginning. */ + if byte_ix < 2000 { + block_switch_cost *= 0.77 + 0.07*float64(byte_ix)/2000 + } + + for k = 0; k < num_histograms; k++ { + cost[k] -= min_cost + if cost[k] >= block_switch_cost { + var mask byte = byte(1 << (k & 7)) + cost[k] = block_switch_cost + assert(k>>3 < bitmaplen) + switch_signal[ix+(k>>3)] |= mask + /* Trace back from the last position and switch at the marked places. */ + } + } + } + { + var byte_ix uint = length - 1 + var ix uint = byte_ix * bitmaplen + var cur_id byte = block_id[byte_ix] + for byte_ix > 0 { + var mask byte = byte(1 << (cur_id & 7)) + assert(uint(cur_id)>>3 < bitmaplen) + byte_ix-- + ix -= bitmaplen + if switch_signal[ix+uint(cur_id>>3)]&mask != 0 { + if cur_id != block_id[byte_ix] { + cur_id = block_id[byte_ix] + num_blocks++ + } + } + + block_id[byte_ix] = cur_id + } + } + + return num_blocks +} + +var remapBlockIdsDistance_kInvalidId uint16 = 256 + +func remapBlockIdsDistance(block_ids []byte, length uint, new_id []uint16, num_histograms uint) uint { + var next_id uint16 = 0 + var i uint + for i = 0; i < num_histograms; i++ { + new_id[i] = remapBlockIdsDistance_kInvalidId + } + + for i = 0; i < length; i++ { + assert(uint(block_ids[i]) < num_histograms) + if new_id[block_ids[i]] == remapBlockIdsDistance_kInvalidId { + new_id[block_ids[i]] = next_id + next_id++ + } + } + + for i = 0; i < length; i++ { + block_ids[i] = byte(new_id[block_ids[i]]) + assert(uint(block_ids[i]) < num_histograms) + } + + assert(uint(next_id) <= num_histograms) + return uint(next_id) +} + +func buildBlockHistogramsDistance(data []uint16, length uint, block_ids []byte, num_histograms uint, histograms []histogramDistance) { + var i uint + clearHistogramsDistance(histograms, num_histograms) + for i = 0; i < length; i++ { + histogramAddDistance(&histograms[block_ids[i]], uint(data[i])) + } +} + +var clusterBlocksDistance_kInvalidIndex uint32 = math.MaxUint32 + +func clusterBlocksDistance(data []uint16, length uint, num_blocks uint, block_ids []byte, split *blockSplit) { + var histogram_symbols []uint32 = make([]uint32, num_blocks) + var block_lengths []uint32 = make([]uint32, num_blocks) + var expected_num_clusters uint = clustersPerBatch * (num_blocks + histogramsPerBatch - 1) / histogramsPerBatch + var all_histograms_size uint = 0 + var all_histograms_capacity uint = expected_num_clusters + var all_histograms []histogramDistance = make([]histogramDistance, all_histograms_capacity) + var cluster_size_size uint = 0 + var cluster_size_capacity uint = expected_num_clusters + var cluster_size []uint32 = make([]uint32, cluster_size_capacity) + var num_clusters uint = 0 + var histograms []histogramDistance = make([]histogramDistance, brotli_min_size_t(num_blocks, histogramsPerBatch)) + var max_num_pairs uint = histogramsPerBatch * histogramsPerBatch / 2 + var pairs_capacity uint = max_num_pairs + 1 + var pairs []histogramPair = make([]histogramPair, pairs_capacity) + var pos uint = 0 + var clusters []uint32 + var num_final_clusters uint + var new_index []uint32 + var i uint + var sizes = [histogramsPerBatch]uint32{0} + var new_clusters = [histogramsPerBatch]uint32{0} + var symbols = [histogramsPerBatch]uint32{0} + var remap = [histogramsPerBatch]uint32{0} + + for i := 0; i < int(num_blocks); i++ { + block_lengths[i] = 0 + } + { + var block_idx uint = 0 + for i = 0; i < length; i++ { + assert(block_idx < num_blocks) + block_lengths[block_idx]++ + if i+1 == length || block_ids[i] != block_ids[i+1] { + block_idx++ + } + } + + assert(block_idx == num_blocks) + } + + for i = 0; i < num_blocks; i += histogramsPerBatch { + var num_to_combine uint = brotli_min_size_t(num_blocks-i, histogramsPerBatch) + var num_new_clusters uint + var j uint + for j = 0; j < num_to_combine; j++ { + var k uint + histogramClearDistance(&histograms[j]) + for k = 0; uint32(k) < block_lengths[i+j]; k++ { + histogramAddDistance(&histograms[j], uint(data[pos])) + pos++ + } + + histograms[j].bit_cost_ = populationCostDistance(&histograms[j]) + new_clusters[j] = uint32(j) + symbols[j] = uint32(j) + sizes[j] = 1 + } + + num_new_clusters = histogramCombineDistance(histograms, sizes[:], symbols[:], new_clusters[:], []histogramPair(pairs), num_to_combine, num_to_combine, histogramsPerBatch, max_num_pairs) + if all_histograms_capacity < (all_histograms_size + num_new_clusters) { + var _new_size uint + if all_histograms_capacity == 0 { + _new_size = all_histograms_size + num_new_clusters + } else { + _new_size = all_histograms_capacity + } + var new_array []histogramDistance + for _new_size < (all_histograms_size + num_new_clusters) { + _new_size *= 2 + } + new_array = make([]histogramDistance, _new_size) + if all_histograms_capacity != 0 { + copy(new_array, all_histograms[:all_histograms_capacity]) + } + + all_histograms = new_array + all_histograms_capacity = _new_size + } + + brotli_ensure_capacity_uint32_t(&cluster_size, &cluster_size_capacity, cluster_size_size+num_new_clusters) + for j = 0; j < num_new_clusters; j++ { + all_histograms[all_histograms_size] = histograms[new_clusters[j]] + all_histograms_size++ + cluster_size[cluster_size_size] = sizes[new_clusters[j]] + cluster_size_size++ + remap[new_clusters[j]] = uint32(j) + } + + for j = 0; j < num_to_combine; j++ { + histogram_symbols[i+j] = uint32(num_clusters) + remap[symbols[j]] + } + + num_clusters += num_new_clusters + assert(num_clusters == cluster_size_size) + assert(num_clusters == all_histograms_size) + } + + histograms = nil + + max_num_pairs = brotli_min_size_t(64*num_clusters, (num_clusters/2)*num_clusters) + if pairs_capacity < max_num_pairs+1 { + pairs = nil + pairs = make([]histogramPair, (max_num_pairs + 1)) + } + + clusters = make([]uint32, num_clusters) + for i = 0; i < num_clusters; i++ { + clusters[i] = uint32(i) + } + + num_final_clusters = histogramCombineDistance(all_histograms, cluster_size, histogram_symbols, clusters, pairs, num_clusters, num_blocks, maxNumberOfBlockTypes, max_num_pairs) + pairs = nil + cluster_size = nil + + new_index = make([]uint32, num_clusters) + for i = 0; i < num_clusters; i++ { + new_index[i] = clusterBlocksDistance_kInvalidIndex + } + pos = 0 + { + var next_index uint32 = 0 + for i = 0; i < num_blocks; i++ { + var histo histogramDistance + var j uint + var best_out uint32 + var best_bits float64 + histogramClearDistance(&histo) + for j = 0; uint32(j) < block_lengths[i]; j++ { + histogramAddDistance(&histo, uint(data[pos])) + pos++ + } + + if i == 0 { + best_out = histogram_symbols[0] + } else { + best_out = histogram_symbols[i-1] + } + best_bits = histogramBitCostDistanceDistance(&histo, &all_histograms[best_out]) + for j = 0; j < num_final_clusters; j++ { + var cur_bits float64 = histogramBitCostDistanceDistance(&histo, &all_histograms[clusters[j]]) + if cur_bits < best_bits { + best_bits = cur_bits + best_out = clusters[j] + } + } + + histogram_symbols[i] = best_out + if new_index[best_out] == clusterBlocksDistance_kInvalidIndex { + new_index[best_out] = next_index + next_index++ + } + } + } + + clusters = nil + all_histograms = nil + brotli_ensure_capacity_uint8_t(&split.types, &split.types_alloc_size, num_blocks) + brotli_ensure_capacity_uint32_t(&split.lengths, &split.lengths_alloc_size, num_blocks) + { + var cur_length uint32 = 0 + var block_idx uint = 0 + var max_type byte = 0 + for i = 0; i < num_blocks; i++ { + cur_length += block_lengths[i] + if i+1 == num_blocks || histogram_symbols[i] != histogram_symbols[i+1] { + var id byte = byte(new_index[histogram_symbols[i]]) + split.types[block_idx] = id + split.lengths[block_idx] = cur_length + max_type = brotli_max_uint8_t(max_type, id) + cur_length = 0 + block_idx++ + } + } + + split.num_blocks = block_idx + split.num_types = uint(max_type) + 1 + } + + new_index = nil + block_lengths = nil + histogram_symbols = nil +} + +func splitByteVectorDistance(data []uint16, length uint, literals_per_histogram uint, max_histograms uint, sampling_stride_length uint, block_switch_cost float64, params *encoderParams, split *blockSplit) { + var data_size uint = histogramDataSizeDistance() + var num_histograms uint = length/literals_per_histogram + 1 + var histograms []histogramDistance + if num_histograms > max_histograms { + num_histograms = max_histograms + } + + if length == 0 { + split.num_types = 1 + return + } else if length < kMinLengthForBlockSplitting { + brotli_ensure_capacity_uint8_t(&split.types, &split.types_alloc_size, split.num_blocks+1) + brotli_ensure_capacity_uint32_t(&split.lengths, &split.lengths_alloc_size, split.num_blocks+1) + split.num_types = 1 + split.types[split.num_blocks] = 0 + split.lengths[split.num_blocks] = uint32(length) + split.num_blocks++ + return + } + + histograms = make([]histogramDistance, num_histograms) + + /* Find good entropy codes. */ + initialEntropyCodesDistance(data, length, sampling_stride_length, num_histograms, histograms) + + refineEntropyCodesDistance(data, length, sampling_stride_length, num_histograms, histograms) + { + var block_ids []byte = make([]byte, length) + var num_blocks uint = 0 + var bitmaplen uint = (num_histograms + 7) >> 3 + var insert_cost []float64 = make([]float64, (data_size * num_histograms)) + var cost []float64 = make([]float64, num_histograms) + var switch_signal []byte = make([]byte, (length * bitmaplen)) + var new_id []uint16 = make([]uint16, num_histograms) + var iters uint + if params.quality < hqZopflificationQuality { + iters = 3 + } else { + iters = 10 + } + /* Find a good path through literals with the good entropy codes. */ + + var i uint + for i = 0; i < iters; i++ { + num_blocks = findBlocksDistance(data, length, block_switch_cost, num_histograms, histograms, insert_cost, cost, switch_signal, block_ids) + num_histograms = remapBlockIdsDistance(block_ids, length, new_id, num_histograms) + buildBlockHistogramsDistance(data, length, block_ids, num_histograms, histograms) + } + + insert_cost = nil + cost = nil + switch_signal = nil + new_id = nil + histograms = nil + clusterBlocksDistance(data, length, num_blocks, block_ids, split) + block_ids = nil + } +} diff --git a/vendor/github.com/andybalholm/brotli/block_splitter_literal.go b/vendor/github.com/andybalholm/brotli/block_splitter_literal.go new file mode 100644 index 000000000..1c895cf38 --- /dev/null +++ b/vendor/github.com/andybalholm/brotli/block_splitter_literal.go @@ -0,0 +1,433 @@ +package brotli + +import "math" + +/* Copyright 2013 Google Inc. All Rights Reserved. + + Distributed under MIT license. + See file LICENSE for detail or copy at https://opensource.org/licenses/MIT +*/ + +func initialEntropyCodesLiteral(data []byte, length uint, stride uint, num_histograms uint, histograms []histogramLiteral) { + var seed uint32 = 7 + var block_length uint = length / num_histograms + var i uint + clearHistogramsLiteral(histograms, num_histograms) + for i = 0; i < num_histograms; i++ { + var pos uint = length * i / num_histograms + if i != 0 { + pos += uint(myRand(&seed) % uint32(block_length)) + } + + if pos+stride >= length { + pos = length - stride - 1 + } + + histogramAddVectorLiteral(&histograms[i], data[pos:], stride) + } +} + +func randomSampleLiteral(seed *uint32, data []byte, length uint, stride uint, sample *histogramLiteral) { + var pos uint = 0 + if stride >= length { + stride = length + } else { + pos = uint(myRand(seed) % uint32(length-stride+1)) + } + + histogramAddVectorLiteral(sample, data[pos:], stride) +} + +func refineEntropyCodesLiteral(data []byte, length uint, stride uint, num_histograms uint, histograms []histogramLiteral) { + var iters uint = kIterMulForRefining*length/stride + kMinItersForRefining + var seed uint32 = 7 + var iter uint + iters = ((iters + num_histograms - 1) / num_histograms) * num_histograms + for iter = 0; iter < iters; iter++ { + var sample histogramLiteral + histogramClearLiteral(&sample) + randomSampleLiteral(&seed, data, length, stride, &sample) + histogramAddHistogramLiteral(&histograms[iter%num_histograms], &sample) + } +} + +/* Assigns a block id from the range [0, num_histograms) to each data element + in data[0..length) and fills in block_id[0..length) with the assigned values. + Returns the number of blocks, i.e. one plus the number of block switches. */ +func findBlocksLiteral(data []byte, length uint, block_switch_bitcost float64, num_histograms uint, histograms []histogramLiteral, insert_cost []float64, cost []float64, switch_signal []byte, block_id []byte) uint { + var data_size uint = histogramDataSizeLiteral() + var bitmaplen uint = (num_histograms + 7) >> 3 + var num_blocks uint = 1 + var i uint + var j uint + assert(num_histograms <= 256) + if num_histograms <= 1 { + for i = 0; i < length; i++ { + block_id[i] = 0 + } + + return 1 + } + + for i := 0; i < int(data_size*num_histograms); i++ { + insert_cost[i] = 0 + } + for i = 0; i < num_histograms; i++ { + insert_cost[i] = fastLog2(uint(uint32(histograms[i].total_count_))) + } + + for i = data_size; i != 0; { + i-- + for j = 0; j < num_histograms; j++ { + insert_cost[i*num_histograms+j] = insert_cost[j] - bitCost(uint(histograms[j].data_[i])) + } + } + + for i := 0; i < int(num_histograms); i++ { + cost[i] = 0 + } + for i := 0; i < int(length*bitmaplen); i++ { + switch_signal[i] = 0 + } + + /* After each iteration of this loop, cost[k] will contain the difference + between the minimum cost of arriving at the current byte position using + entropy code k, and the minimum cost of arriving at the current byte + position. This difference is capped at the block switch cost, and if it + reaches block switch cost, it means that when we trace back from the last + position, we need to switch here. */ + for i = 0; i < length; i++ { + var byte_ix uint = i + var ix uint = byte_ix * bitmaplen + var insert_cost_ix uint = uint(data[byte_ix]) * num_histograms + var min_cost float64 = 1e99 + var block_switch_cost float64 = block_switch_bitcost + var k uint + for k = 0; k < num_histograms; k++ { + /* We are coding the symbol in data[byte_ix] with entropy code k. */ + cost[k] += insert_cost[insert_cost_ix+k] + + if cost[k] < min_cost { + min_cost = cost[k] + block_id[byte_ix] = byte(k) + } + } + + /* More blocks for the beginning. */ + if byte_ix < 2000 { + block_switch_cost *= 0.77 + 0.07*float64(byte_ix)/2000 + } + + for k = 0; k < num_histograms; k++ { + cost[k] -= min_cost + if cost[k] >= block_switch_cost { + var mask byte = byte(1 << (k & 7)) + cost[k] = block_switch_cost + assert(k>>3 < bitmaplen) + switch_signal[ix+(k>>3)] |= mask + /* Trace back from the last position and switch at the marked places. */ + } + } + } + { + var byte_ix uint = length - 1 + var ix uint = byte_ix * bitmaplen + var cur_id byte = block_id[byte_ix] + for byte_ix > 0 { + var mask byte = byte(1 << (cur_id & 7)) + assert(uint(cur_id)>>3 < bitmaplen) + byte_ix-- + ix -= bitmaplen + if switch_signal[ix+uint(cur_id>>3)]&mask != 0 { + if cur_id != block_id[byte_ix] { + cur_id = block_id[byte_ix] + num_blocks++ + } + } + + block_id[byte_ix] = cur_id + } + } + + return num_blocks +} + +var remapBlockIdsLiteral_kInvalidId uint16 = 256 + +func remapBlockIdsLiteral(block_ids []byte, length uint, new_id []uint16, num_histograms uint) uint { + var next_id uint16 = 0 + var i uint + for i = 0; i < num_histograms; i++ { + new_id[i] = remapBlockIdsLiteral_kInvalidId + } + + for i = 0; i < length; i++ { + assert(uint(block_ids[i]) < num_histograms) + if new_id[block_ids[i]] == remapBlockIdsLiteral_kInvalidId { + new_id[block_ids[i]] = next_id + next_id++ + } + } + + for i = 0; i < length; i++ { + block_ids[i] = byte(new_id[block_ids[i]]) + assert(uint(block_ids[i]) < num_histograms) + } + + assert(uint(next_id) <= num_histograms) + return uint(next_id) +} + +func buildBlockHistogramsLiteral(data []byte, length uint, block_ids []byte, num_histograms uint, histograms []histogramLiteral) { + var i uint + clearHistogramsLiteral(histograms, num_histograms) + for i = 0; i < length; i++ { + histogramAddLiteral(&histograms[block_ids[i]], uint(data[i])) + } +} + +var clusterBlocksLiteral_kInvalidIndex uint32 = math.MaxUint32 + +func clusterBlocksLiteral(data []byte, length uint, num_blocks uint, block_ids []byte, split *blockSplit) { + var histogram_symbols []uint32 = make([]uint32, num_blocks) + var block_lengths []uint32 = make([]uint32, num_blocks) + var expected_num_clusters uint = clustersPerBatch * (num_blocks + histogramsPerBatch - 1) / histogramsPerBatch + var all_histograms_size uint = 0 + var all_histograms_capacity uint = expected_num_clusters + var all_histograms []histogramLiteral = make([]histogramLiteral, all_histograms_capacity) + var cluster_size_size uint = 0 + var cluster_size_capacity uint = expected_num_clusters + var cluster_size []uint32 = make([]uint32, cluster_size_capacity) + var num_clusters uint = 0 + var histograms []histogramLiteral = make([]histogramLiteral, brotli_min_size_t(num_blocks, histogramsPerBatch)) + var max_num_pairs uint = histogramsPerBatch * histogramsPerBatch / 2 + var pairs_capacity uint = max_num_pairs + 1 + var pairs []histogramPair = make([]histogramPair, pairs_capacity) + var pos uint = 0 + var clusters []uint32 + var num_final_clusters uint + var new_index []uint32 + var i uint + var sizes = [histogramsPerBatch]uint32{0} + var new_clusters = [histogramsPerBatch]uint32{0} + var symbols = [histogramsPerBatch]uint32{0} + var remap = [histogramsPerBatch]uint32{0} + + for i := 0; i < int(num_blocks); i++ { + block_lengths[i] = 0 + } + { + var block_idx uint = 0 + for i = 0; i < length; i++ { + assert(block_idx < num_blocks) + block_lengths[block_idx]++ + if i+1 == length || block_ids[i] != block_ids[i+1] { + block_idx++ + } + } + + assert(block_idx == num_blocks) + } + + for i = 0; i < num_blocks; i += histogramsPerBatch { + var num_to_combine uint = brotli_min_size_t(num_blocks-i, histogramsPerBatch) + var num_new_clusters uint + var j uint + for j = 0; j < num_to_combine; j++ { + var k uint + histogramClearLiteral(&histograms[j]) + for k = 0; uint32(k) < block_lengths[i+j]; k++ { + histogramAddLiteral(&histograms[j], uint(data[pos])) + pos++ + } + + histograms[j].bit_cost_ = populationCostLiteral(&histograms[j]) + new_clusters[j] = uint32(j) + symbols[j] = uint32(j) + sizes[j] = 1 + } + + num_new_clusters = histogramCombineLiteral(histograms, sizes[:], symbols[:], new_clusters[:], []histogramPair(pairs), num_to_combine, num_to_combine, histogramsPerBatch, max_num_pairs) + if all_histograms_capacity < (all_histograms_size + num_new_clusters) { + var _new_size uint + if all_histograms_capacity == 0 { + _new_size = all_histograms_size + num_new_clusters + } else { + _new_size = all_histograms_capacity + } + var new_array []histogramLiteral + for _new_size < (all_histograms_size + num_new_clusters) { + _new_size *= 2 + } + new_array = make([]histogramLiteral, _new_size) + if all_histograms_capacity != 0 { + copy(new_array, all_histograms[:all_histograms_capacity]) + } + + all_histograms = new_array + all_histograms_capacity = _new_size + } + + brotli_ensure_capacity_uint32_t(&cluster_size, &cluster_size_capacity, cluster_size_size+num_new_clusters) + for j = 0; j < num_new_clusters; j++ { + all_histograms[all_histograms_size] = histograms[new_clusters[j]] + all_histograms_size++ + cluster_size[cluster_size_size] = sizes[new_clusters[j]] + cluster_size_size++ + remap[new_clusters[j]] = uint32(j) + } + + for j = 0; j < num_to_combine; j++ { + histogram_symbols[i+j] = uint32(num_clusters) + remap[symbols[j]] + } + + num_clusters += num_new_clusters + assert(num_clusters == cluster_size_size) + assert(num_clusters == all_histograms_size) + } + + histograms = nil + + max_num_pairs = brotli_min_size_t(64*num_clusters, (num_clusters/2)*num_clusters) + if pairs_capacity < max_num_pairs+1 { + pairs = nil + pairs = make([]histogramPair, (max_num_pairs + 1)) + } + + clusters = make([]uint32, num_clusters) + for i = 0; i < num_clusters; i++ { + clusters[i] = uint32(i) + } + + num_final_clusters = histogramCombineLiteral(all_histograms, cluster_size, histogram_symbols, clusters, pairs, num_clusters, num_blocks, maxNumberOfBlockTypes, max_num_pairs) + pairs = nil + cluster_size = nil + + new_index = make([]uint32, num_clusters) + for i = 0; i < num_clusters; i++ { + new_index[i] = clusterBlocksLiteral_kInvalidIndex + } + pos = 0 + { + var next_index uint32 = 0 + for i = 0; i < num_blocks; i++ { + var histo histogramLiteral + var j uint + var best_out uint32 + var best_bits float64 + histogramClearLiteral(&histo) + for j = 0; uint32(j) < block_lengths[i]; j++ { + histogramAddLiteral(&histo, uint(data[pos])) + pos++ + } + + if i == 0 { + best_out = histogram_symbols[0] + } else { + best_out = histogram_symbols[i-1] + } + best_bits = histogramBitCostDistanceLiteral(&histo, &all_histograms[best_out]) + for j = 0; j < num_final_clusters; j++ { + var cur_bits float64 = histogramBitCostDistanceLiteral(&histo, &all_histograms[clusters[j]]) + if cur_bits < best_bits { + best_bits = cur_bits + best_out = clusters[j] + } + } + + histogram_symbols[i] = best_out + if new_index[best_out] == clusterBlocksLiteral_kInvalidIndex { + new_index[best_out] = next_index + next_index++ + } + } + } + + clusters = nil + all_histograms = nil + brotli_ensure_capacity_uint8_t(&split.types, &split.types_alloc_size, num_blocks) + brotli_ensure_capacity_uint32_t(&split.lengths, &split.lengths_alloc_size, num_blocks) + { + var cur_length uint32 = 0 + var block_idx uint = 0 + var max_type byte = 0 + for i = 0; i < num_blocks; i++ { + cur_length += block_lengths[i] + if i+1 == num_blocks || histogram_symbols[i] != histogram_symbols[i+1] { + var id byte = byte(new_index[histogram_symbols[i]]) + split.types[block_idx] = id + split.lengths[block_idx] = cur_length + max_type = brotli_max_uint8_t(max_type, id) + cur_length = 0 + block_idx++ + } + } + + split.num_blocks = block_idx + split.num_types = uint(max_type) + 1 + } + + new_index = nil + block_lengths = nil + histogram_symbols = nil +} + +func splitByteVectorLiteral(data []byte, length uint, literals_per_histogram uint, max_histograms uint, sampling_stride_length uint, block_switch_cost float64, params *encoderParams, split *blockSplit) { + var data_size uint = histogramDataSizeLiteral() + var num_histograms uint = length/literals_per_histogram + 1 + var histograms []histogramLiteral + if num_histograms > max_histograms { + num_histograms = max_histograms + } + + if length == 0 { + split.num_types = 1 + return + } else if length < kMinLengthForBlockSplitting { + brotli_ensure_capacity_uint8_t(&split.types, &split.types_alloc_size, split.num_blocks+1) + brotli_ensure_capacity_uint32_t(&split.lengths, &split.lengths_alloc_size, split.num_blocks+1) + split.num_types = 1 + split.types[split.num_blocks] = 0 + split.lengths[split.num_blocks] = uint32(length) + split.num_blocks++ + return + } + + histograms = make([]histogramLiteral, num_histograms) + + /* Find good entropy codes. */ + initialEntropyCodesLiteral(data, length, sampling_stride_length, num_histograms, histograms) + + refineEntropyCodesLiteral(data, length, sampling_stride_length, num_histograms, histograms) + { + var block_ids []byte = make([]byte, length) + var num_blocks uint = 0 + var bitmaplen uint = (num_histograms + 7) >> 3 + var insert_cost []float64 = make([]float64, (data_size * num_histograms)) + var cost []float64 = make([]float64, num_histograms) + var switch_signal []byte = make([]byte, (length * bitmaplen)) + var new_id []uint16 = make([]uint16, num_histograms) + var iters uint + if params.quality < hqZopflificationQuality { + iters = 3 + } else { + iters = 10 + } + /* Find a good path through literals with the good entropy codes. */ + + var i uint + for i = 0; i < iters; i++ { + num_blocks = findBlocksLiteral(data, length, block_switch_cost, num_histograms, histograms, insert_cost, cost, switch_signal, block_ids) + num_histograms = remapBlockIdsLiteral(block_ids, length, new_id, num_histograms) + buildBlockHistogramsLiteral(data, length, block_ids, num_histograms, histograms) + } + + insert_cost = nil + cost = nil + switch_signal = nil + new_id = nil + histograms = nil + clusterBlocksLiteral(data, length, num_blocks, block_ids, split) + block_ids = nil + } +} diff --git a/vendor/github.com/andybalholm/brotli/brotli_bit_stream.go b/vendor/github.com/andybalholm/brotli/brotli_bit_stream.go new file mode 100644 index 000000000..ee6552982 --- /dev/null +++ b/vendor/github.com/andybalholm/brotli/brotli_bit_stream.go @@ -0,0 +1,1539 @@ +package brotli + +import ( + "math" + "sync" +) + +const maxHuffmanTreeSize = (2*numCommandSymbols + 1) + +/* +The maximum size of Huffman dictionary for distances assuming that + + NPOSTFIX = 0 and NDIRECT = 0. +*/ +const maxSimpleDistanceAlphabetSize = 140 + +/* +Represents the range of values belonging to a prefix code: + + [offset, offset + 2^nbits) +*/ +type prefixCodeRange struct { + offset uint32 + nbits uint32 +} + +var kBlockLengthPrefixCode = [numBlockLenSymbols]prefixCodeRange{ + prefixCodeRange{1, 2}, + prefixCodeRange{5, 2}, + prefixCodeRange{9, 2}, + prefixCodeRange{13, 2}, + prefixCodeRange{17, 3}, + prefixCodeRange{25, 3}, + prefixCodeRange{33, 3}, + prefixCodeRange{41, 3}, + prefixCodeRange{49, 4}, + prefixCodeRange{65, 4}, + prefixCodeRange{81, 4}, + prefixCodeRange{97, 4}, + prefixCodeRange{113, 5}, + prefixCodeRange{145, 5}, + prefixCodeRange{177, 5}, + prefixCodeRange{209, 5}, + prefixCodeRange{241, 6}, + prefixCodeRange{305, 6}, + prefixCodeRange{369, 7}, + prefixCodeRange{497, 8}, + prefixCodeRange{753, 9}, + prefixCodeRange{1265, 10}, + prefixCodeRange{2289, 11}, + prefixCodeRange{4337, 12}, + prefixCodeRange{8433, 13}, + prefixCodeRange{16625, 24}, +} + +func blockLengthPrefixCode(len uint32) uint32 { + var code uint32 + if len >= 177 { + if len >= 753 { + code = 20 + } else { + code = 14 + } + } else if len >= 41 { + code = 7 + } else { + code = 0 + } + for code < (numBlockLenSymbols-1) && len >= kBlockLengthPrefixCode[code+1].offset { + code++ + } + return code +} + +func getBlockLengthPrefixCode(len uint32, code *uint, n_extra *uint32, extra *uint32) { + *code = uint(blockLengthPrefixCode(uint32(len))) + *n_extra = kBlockLengthPrefixCode[*code].nbits + *extra = len - kBlockLengthPrefixCode[*code].offset +} + +type blockTypeCodeCalculator struct { + last_type uint + second_last_type uint +} + +func initBlockTypeCodeCalculator(self *blockTypeCodeCalculator) { + self.last_type = 1 + self.second_last_type = 0 +} + +func nextBlockTypeCode(calculator *blockTypeCodeCalculator, type_ byte) uint { + var type_code uint + if uint(type_) == calculator.last_type+1 { + type_code = 1 + } else if uint(type_) == calculator.second_last_type { + type_code = 0 + } else { + type_code = uint(type_) + 2 + } + calculator.second_last_type = calculator.last_type + calculator.last_type = uint(type_) + return type_code +} + +/* +|nibblesbits| represents the 2 bits to encode MNIBBLES (0-3) + + REQUIRES: length > 0 + REQUIRES: length <= (1 << 24) +*/ +func encodeMlen(length uint, bits *uint64, numbits *uint, nibblesbits *uint64) { + var lg uint + if length == 1 { + lg = 1 + } else { + lg = uint(log2FloorNonZero(uint(uint32(length-1)))) + 1 + } + var tmp uint + if lg < 16 { + tmp = 16 + } else { + tmp = (lg + 3) + } + var mnibbles uint = tmp / 4 + assert(length > 0) + assert(length <= 1<<24) + assert(lg <= 24) + *nibblesbits = uint64(mnibbles) - 4 + *numbits = mnibbles * 4 + *bits = uint64(length) - 1 +} + +func storeCommandExtra(cmd *command, storage_ix *uint, storage []byte) { + var copylen_code uint32 = commandCopyLenCode(cmd) + var inscode uint16 = getInsertLengthCode(uint(cmd.insert_len_)) + var copycode uint16 = getCopyLengthCode(uint(copylen_code)) + var insnumextra uint32 = getInsertExtra(inscode) + var insextraval uint64 = uint64(cmd.insert_len_) - uint64(getInsertBase(inscode)) + var copyextraval uint64 = uint64(copylen_code) - uint64(getCopyBase(copycode)) + var bits uint64 = copyextraval< 0 + REQUIRES: length <= (1 << 24) +*/ +func storeCompressedMetaBlockHeader(is_final_block bool, length uint, storage_ix *uint, storage []byte) { + var lenbits uint64 + var nlenbits uint + var nibblesbits uint64 + var is_final uint64 + if is_final_block { + is_final = 1 + } else { + is_final = 0 + } + + /* Write ISLAST bit. */ + writeBits(1, is_final, storage_ix, storage) + + /* Write ISEMPTY bit. */ + if is_final_block { + writeBits(1, 0, storage_ix, storage) + } + + encodeMlen(length, &lenbits, &nlenbits, &nibblesbits) + writeBits(2, nibblesbits, storage_ix, storage) + writeBits(nlenbits, lenbits, storage_ix, storage) + + if !is_final_block { + /* Write ISUNCOMPRESSED bit. */ + writeBits(1, 0, storage_ix, storage) + } +} + +/* +Stores the uncompressed meta-block header. + + REQUIRES: length > 0 + REQUIRES: length <= (1 << 24) +*/ +func storeUncompressedMetaBlockHeader(length uint, storage_ix *uint, storage []byte) { + var lenbits uint64 + var nlenbits uint + var nibblesbits uint64 + + /* Write ISLAST bit. + Uncompressed block cannot be the last one, so set to 0. */ + writeBits(1, 0, storage_ix, storage) + + encodeMlen(length, &lenbits, &nlenbits, &nibblesbits) + writeBits(2, nibblesbits, storage_ix, storage) + writeBits(nlenbits, lenbits, storage_ix, storage) + + /* Write ISUNCOMPRESSED bit. */ + writeBits(1, 1, storage_ix, storage) +} + +var storeHuffmanTreeOfHuffmanTreeToBitMask_kStorageOrder = [codeLengthCodes]byte{1, 2, 3, 4, 0, 5, 17, 6, 16, 7, 8, 9, 10, 11, 12, 13, 14, 15} + +var storeHuffmanTreeOfHuffmanTreeToBitMask_kHuffmanBitLengthHuffmanCodeSymbols = [6]byte{0, 7, 3, 2, 1, 15} +var storeHuffmanTreeOfHuffmanTreeToBitMask_kHuffmanBitLengthHuffmanCodeBitLengths = [6]byte{2, 4, 3, 2, 2, 4} + +func storeHuffmanTreeOfHuffmanTreeToBitMask(num_codes int, code_length_bitdepth []byte, storage_ix *uint, storage []byte) { + var skip_some uint = 0 + var codes_to_store uint = codeLengthCodes + /* The bit lengths of the Huffman code over the code length alphabet + are compressed with the following static Huffman code: + Symbol Code + ------ ---- + 0 00 + 1 1110 + 2 110 + 3 01 + 4 10 + 5 1111 */ + + /* Throw away trailing zeros: */ + if num_codes > 1 { + for ; codes_to_store > 0; codes_to_store-- { + if code_length_bitdepth[storeHuffmanTreeOfHuffmanTreeToBitMask_kStorageOrder[codes_to_store-1]] != 0 { + break + } + } + } + + if code_length_bitdepth[storeHuffmanTreeOfHuffmanTreeToBitMask_kStorageOrder[0]] == 0 && code_length_bitdepth[storeHuffmanTreeOfHuffmanTreeToBitMask_kStorageOrder[1]] == 0 { + skip_some = 2 /* skips two. */ + if code_length_bitdepth[storeHuffmanTreeOfHuffmanTreeToBitMask_kStorageOrder[2]] == 0 { + skip_some = 3 /* skips three. */ + } + } + + writeBits(2, uint64(skip_some), storage_ix, storage) + { + var i uint + for i = skip_some; i < codes_to_store; i++ { + var l uint = uint(code_length_bitdepth[storeHuffmanTreeOfHuffmanTreeToBitMask_kStorageOrder[i]]) + writeBits(uint(storeHuffmanTreeOfHuffmanTreeToBitMask_kHuffmanBitLengthHuffmanCodeBitLengths[l]), uint64(storeHuffmanTreeOfHuffmanTreeToBitMask_kHuffmanBitLengthHuffmanCodeSymbols[l]), storage_ix, storage) + } + } +} + +func storeHuffmanTreeToBitMask(huffman_tree_size uint, huffman_tree []byte, huffman_tree_extra_bits []byte, code_length_bitdepth []byte, code_length_bitdepth_symbols []uint16, storage_ix *uint, storage []byte) { + var i uint + for i = 0; i < huffman_tree_size; i++ { + var ix uint = uint(huffman_tree[i]) + writeBits(uint(code_length_bitdepth[ix]), uint64(code_length_bitdepth_symbols[ix]), storage_ix, storage) + + /* Extra bits */ + switch ix { + case repeatPreviousCodeLength: + writeBits(2, uint64(huffman_tree_extra_bits[i]), storage_ix, storage) + + case repeatZeroCodeLength: + writeBits(3, uint64(huffman_tree_extra_bits[i]), storage_ix, storage) + } + } +} + +func storeSimpleHuffmanTree(depths []byte, symbols []uint, num_symbols uint, max_bits uint, storage_ix *uint, storage []byte) { + /* value of 1 indicates a simple Huffman code */ + writeBits(2, 1, storage_ix, storage) + + writeBits(2, uint64(num_symbols)-1, storage_ix, storage) /* NSYM - 1 */ + { + /* Sort */ + var i uint + for i = 0; i < num_symbols; i++ { + var j uint + for j = i + 1; j < num_symbols; j++ { + if depths[symbols[j]] < depths[symbols[i]] { + var tmp uint = symbols[j] + symbols[j] = symbols[i] + symbols[i] = tmp + } + } + } + } + + if num_symbols == 2 { + writeBits(max_bits, uint64(symbols[0]), storage_ix, storage) + writeBits(max_bits, uint64(symbols[1]), storage_ix, storage) + } else if num_symbols == 3 { + writeBits(max_bits, uint64(symbols[0]), storage_ix, storage) + writeBits(max_bits, uint64(symbols[1]), storage_ix, storage) + writeBits(max_bits, uint64(symbols[2]), storage_ix, storage) + } else { + writeBits(max_bits, uint64(symbols[0]), storage_ix, storage) + writeBits(max_bits, uint64(symbols[1]), storage_ix, storage) + writeBits(max_bits, uint64(symbols[2]), storage_ix, storage) + writeBits(max_bits, uint64(symbols[3]), storage_ix, storage) + + /* tree-select */ + var tmp int + if depths[symbols[0]] == 1 { + tmp = 1 + } else { + tmp = 0 + } + writeBits(1, uint64(tmp), storage_ix, storage) + } +} + +/* +num = alphabet size + + depths = symbol depths +*/ +func storeHuffmanTree(depths []byte, num uint, tree []huffmanTree, storage_ix *uint, storage []byte) { + var huffman_tree [numCommandSymbols]byte + var huffman_tree_extra_bits [numCommandSymbols]byte + var huffman_tree_size uint = 0 + var code_length_bitdepth = [codeLengthCodes]byte{0} + var code_length_bitdepth_symbols [codeLengthCodes]uint16 + var huffman_tree_histogram = [codeLengthCodes]uint32{0} + var i uint + var num_codes int = 0 + /* Write the Huffman tree into the brotli-representation. + The command alphabet is the largest, so this allocation will fit all + alphabets. */ + + var code uint = 0 + + assert(num <= numCommandSymbols) + + writeHuffmanTree(depths, num, &huffman_tree_size, huffman_tree[:], huffman_tree_extra_bits[:]) + + /* Calculate the statistics of the Huffman tree in brotli-representation. */ + for i = 0; i < huffman_tree_size; i++ { + huffman_tree_histogram[huffman_tree[i]]++ + } + + for i = 0; i < codeLengthCodes; i++ { + if huffman_tree_histogram[i] != 0 { + if num_codes == 0 { + code = i + num_codes = 1 + } else if num_codes == 1 { + num_codes = 2 + break + } + } + } + + /* Calculate another Huffman tree to use for compressing both the + earlier Huffman tree with. */ + createHuffmanTree(huffman_tree_histogram[:], codeLengthCodes, 5, tree, code_length_bitdepth[:]) + + convertBitDepthsToSymbols(code_length_bitdepth[:], codeLengthCodes, code_length_bitdepth_symbols[:]) + + /* Now, we have all the data, let's start storing it */ + storeHuffmanTreeOfHuffmanTreeToBitMask(num_codes, code_length_bitdepth[:], storage_ix, storage) + + if num_codes == 1 { + code_length_bitdepth[code] = 0 + } + + /* Store the real Huffman tree now. */ + storeHuffmanTreeToBitMask(huffman_tree_size, huffman_tree[:], huffman_tree_extra_bits[:], code_length_bitdepth[:], code_length_bitdepth_symbols[:], storage_ix, storage) +} + +/* +Builds a Huffman tree from histogram[0:length] into depth[0:length] and + + bits[0:length] and stores the encoded tree to the bit stream. +*/ +func buildAndStoreHuffmanTree(histogram []uint32, histogram_length uint, alphabet_size uint, tree []huffmanTree, depth []byte, bits []uint16, storage_ix *uint, storage []byte) { + var count uint = 0 + var s4 = [4]uint{0} + var i uint + var max_bits uint = 0 + for i = 0; i < histogram_length; i++ { + if histogram[i] != 0 { + if count < 4 { + s4[count] = i + } else if count > 4 { + break + } + + count++ + } + } + { + var max_bits_counter uint = alphabet_size - 1 + for max_bits_counter != 0 { + max_bits_counter >>= 1 + max_bits++ + } + } + + if count <= 1 { + writeBits(4, 1, storage_ix, storage) + writeBits(max_bits, uint64(s4[0]), storage_ix, storage) + depth[s4[0]] = 0 + bits[s4[0]] = 0 + return + } + + for i := 0; i < int(histogram_length); i++ { + depth[i] = 0 + } + createHuffmanTree(histogram, histogram_length, 15, tree, depth) + convertBitDepthsToSymbols(depth, histogram_length, bits) + + if count <= 4 { + storeSimpleHuffmanTree(depth, s4[:], count, max_bits, storage_ix, storage) + } else { + storeHuffmanTree(depth, histogram_length, tree, storage_ix, storage) + } +} + +func sortHuffmanTree1(v0 huffmanTree, v1 huffmanTree) bool { + return v0.total_count_ < v1.total_count_ +} + +var huffmanTreePool sync.Pool + +func buildAndStoreHuffmanTreeFast(histogram []uint32, histogram_total uint, max_bits uint, depth []byte, bits []uint16, storage_ix *uint, storage []byte) { + var count uint = 0 + var symbols = [4]uint{0} + var length uint = 0 + var total uint = histogram_total + for total != 0 { + if histogram[length] != 0 { + if count < 4 { + symbols[count] = length + } + + count++ + total -= uint(histogram[length]) + } + + length++ + } + + if count <= 1 { + writeBits(4, 1, storage_ix, storage) + writeBits(max_bits, uint64(symbols[0]), storage_ix, storage) + depth[symbols[0]] = 0 + bits[symbols[0]] = 0 + return + } + + for i := 0; i < int(length); i++ { + depth[i] = 0 + } + { + var max_tree_size uint = 2*length + 1 + tree, _ := huffmanTreePool.Get().(*[]huffmanTree) + if tree == nil || cap(*tree) < int(max_tree_size) { + tmp := make([]huffmanTree, max_tree_size) + tree = &tmp + } else { + *tree = (*tree)[:max_tree_size] + } + var count_limit uint32 + for count_limit = 1; ; count_limit *= 2 { + var node int = 0 + var l uint + for l = length; l != 0; { + l-- + if histogram[l] != 0 { + if histogram[l] >= count_limit { + initHuffmanTree(&(*tree)[node:][0], histogram[l], -1, int16(l)) + } else { + initHuffmanTree(&(*tree)[node:][0], count_limit, -1, int16(l)) + } + + node++ + } + } + { + var n int = node + /* Points to the next leaf node. */ /* Points to the next non-leaf node. */ + var sentinel huffmanTree + var i int = 0 + var j int = n + 1 + var k int + + sortHuffmanTreeItems(*tree, uint(n), huffmanTreeComparator(sortHuffmanTree1)) + + /* The nodes are: + [0, n): the sorted leaf nodes that we start with. + [n]: we add a sentinel here. + [n + 1, 2n): new parent nodes are added here, starting from + (n+1). These are naturally in ascending order. + [2n]: we add a sentinel at the end as well. + There will be (2n+1) elements at the end. */ + initHuffmanTree(&sentinel, math.MaxUint32, -1, -1) + + (*tree)[node] = sentinel + node++ + (*tree)[node] = sentinel + node++ + + for k = n - 1; k > 0; k-- { + var left int + var right int + if (*tree)[i].total_count_ <= (*tree)[j].total_count_ { + left = i + i++ + } else { + left = j + j++ + } + + if (*tree)[i].total_count_ <= (*tree)[j].total_count_ { + right = i + i++ + } else { + right = j + j++ + } + + /* The sentinel node becomes the parent node. */ + (*tree)[node-1].total_count_ = (*tree)[left].total_count_ + (*tree)[right].total_count_ + + (*tree)[node-1].index_left_ = int16(left) + (*tree)[node-1].index_right_or_value_ = int16(right) + + /* Add back the last sentinel node. */ + (*tree)[node] = sentinel + node++ + } + + if setDepth(2*n-1, *tree, depth, 14) { + /* We need to pack the Huffman tree in 14 bits. If this was not + successful, add fake entities to the lowest values and retry. */ + break + } + } + } + + huffmanTreePool.Put(tree) + } + + convertBitDepthsToSymbols(depth, length, bits) + if count <= 4 { + var i uint + + /* value of 1 indicates a simple Huffman code */ + writeBits(2, 1, storage_ix, storage) + + writeBits(2, uint64(count)-1, storage_ix, storage) /* NSYM - 1 */ + + /* Sort */ + for i = 0; i < count; i++ { + var j uint + for j = i + 1; j < count; j++ { + if depth[symbols[j]] < depth[symbols[i]] { + var tmp uint = symbols[j] + symbols[j] = symbols[i] + symbols[i] = tmp + } + } + } + + if count == 2 { + writeBits(max_bits, uint64(symbols[0]), storage_ix, storage) + writeBits(max_bits, uint64(symbols[1]), storage_ix, storage) + } else if count == 3 { + writeBits(max_bits, uint64(symbols[0]), storage_ix, storage) + writeBits(max_bits, uint64(symbols[1]), storage_ix, storage) + writeBits(max_bits, uint64(symbols[2]), storage_ix, storage) + } else { + writeBits(max_bits, uint64(symbols[0]), storage_ix, storage) + writeBits(max_bits, uint64(symbols[1]), storage_ix, storage) + writeBits(max_bits, uint64(symbols[2]), storage_ix, storage) + writeBits(max_bits, uint64(symbols[3]), storage_ix, storage) + + /* tree-select */ + var tmp int + if depth[symbols[0]] == 1 { + tmp = 1 + } else { + tmp = 0 + } + writeBits(1, uint64(tmp), storage_ix, storage) + } + } else { + var previous_value byte = 8 + var i uint + + /* Complex Huffman Tree */ + storeStaticCodeLengthCode(storage_ix, storage) + + /* Actual RLE coding. */ + for i = 0; i < length; { + var value byte = depth[i] + var reps uint = 1 + var k uint + for k = i + 1; k < length && depth[k] == value; k++ { + reps++ + } + + i += reps + if value == 0 { + writeBits(uint(kZeroRepsDepth[reps]), kZeroRepsBits[reps], storage_ix, storage) + } else { + if previous_value != value { + writeBits(uint(kCodeLengthDepth[value]), uint64(kCodeLengthBits[value]), storage_ix, storage) + reps-- + } + + if reps < 3 { + for reps != 0 { + reps-- + writeBits(uint(kCodeLengthDepth[value]), uint64(kCodeLengthBits[value]), storage_ix, storage) + } + } else { + reps -= 3 + writeBits(uint(kNonZeroRepsDepth[reps]), kNonZeroRepsBits[reps], storage_ix, storage) + } + + previous_value = value + } + } + } +} + +func buildAndStoreHuffmanTreeFastBW(histogram []uint32, histogram_total uint, max_bits uint, depth []byte, bits []uint16, bw *bitWriter) { + var count uint = 0 + var symbols = [4]uint{0} + var length uint = 0 + var total uint = histogram_total + for total != 0 { + if histogram[length] != 0 { + if count < 4 { + symbols[count] = length + } + + count++ + total -= uint(histogram[length]) + } + + length++ + } + + if count <= 1 { + bw.writeBits(4, 1) + bw.writeBits(max_bits, uint64(symbols[0])) + depth[symbols[0]] = 0 + bits[symbols[0]] = 0 + return + } + + for i := 0; i < int(length); i++ { + depth[i] = 0 + } + { + var max_tree_size uint = 2*length + 1 + tree, _ := huffmanTreePool.Get().(*[]huffmanTree) + if tree == nil || cap(*tree) < int(max_tree_size) { + tmp := make([]huffmanTree, max_tree_size) + tree = &tmp + } else { + *tree = (*tree)[:max_tree_size] + } + var count_limit uint32 + for count_limit = 1; ; count_limit *= 2 { + var node int = 0 + var l uint + for l = length; l != 0; { + l-- + if histogram[l] != 0 { + if histogram[l] >= count_limit { + initHuffmanTree(&(*tree)[node:][0], histogram[l], -1, int16(l)) + } else { + initHuffmanTree(&(*tree)[node:][0], count_limit, -1, int16(l)) + } + + node++ + } + } + { + var n int = node + /* Points to the next leaf node. */ /* Points to the next non-leaf node. */ + var sentinel huffmanTree + var i int = 0 + var j int = n + 1 + var k int + + sortHuffmanTreeItems(*tree, uint(n), huffmanTreeComparator(sortHuffmanTree1)) + + /* The nodes are: + [0, n): the sorted leaf nodes that we start with. + [n]: we add a sentinel here. + [n + 1, 2n): new parent nodes are added here, starting from + (n+1). These are naturally in ascending order. + [2n]: we add a sentinel at the end as well. + There will be (2n+1) elements at the end. */ + initHuffmanTree(&sentinel, math.MaxUint32, -1, -1) + + (*tree)[node] = sentinel + node++ + (*tree)[node] = sentinel + node++ + + for k = n - 1; k > 0; k-- { + var left int + var right int + if (*tree)[i].total_count_ <= (*tree)[j].total_count_ { + left = i + i++ + } else { + left = j + j++ + } + + if (*tree)[i].total_count_ <= (*tree)[j].total_count_ { + right = i + i++ + } else { + right = j + j++ + } + + /* The sentinel node becomes the parent node. */ + (*tree)[node-1].total_count_ = (*tree)[left].total_count_ + (*tree)[right].total_count_ + + (*tree)[node-1].index_left_ = int16(left) + (*tree)[node-1].index_right_or_value_ = int16(right) + + /* Add back the last sentinel node. */ + (*tree)[node] = sentinel + node++ + } + + if setDepth(2*n-1, *tree, depth, 14) { + /* We need to pack the Huffman tree in 14 bits. If this was not + successful, add fake entities to the lowest values and retry. */ + break + } + } + } + + huffmanTreePool.Put(tree) + } + + convertBitDepthsToSymbols(depth, length, bits) + if count <= 4 { + var i uint + + /* value of 1 indicates a simple Huffman code */ + bw.writeBits(2, 1) + + bw.writeBits(2, uint64(count)-1) /* NSYM - 1 */ + + /* Sort */ + for i = 0; i < count; i++ { + var j uint + for j = i + 1; j < count; j++ { + if depth[symbols[j]] < depth[symbols[i]] { + var tmp uint = symbols[j] + symbols[j] = symbols[i] + symbols[i] = tmp + } + } + } + + if count == 2 { + bw.writeBits(max_bits, uint64(symbols[0])) + bw.writeBits(max_bits, uint64(symbols[1])) + } else if count == 3 { + bw.writeBits(max_bits, uint64(symbols[0])) + bw.writeBits(max_bits, uint64(symbols[1])) + bw.writeBits(max_bits, uint64(symbols[2])) + } else { + bw.writeBits(max_bits, uint64(symbols[0])) + bw.writeBits(max_bits, uint64(symbols[1])) + bw.writeBits(max_bits, uint64(symbols[2])) + bw.writeBits(max_bits, uint64(symbols[3])) + + /* tree-select */ + bw.writeSingleBit(depth[symbols[0]] == 1) + } + } else { + var previous_value byte = 8 + var i uint + + /* Complex Huffman Tree */ + storeStaticCodeLengthCodeBW(bw) + + /* Actual RLE coding. */ + for i = 0; i < length; { + var value byte = depth[i] + var reps uint = 1 + var k uint + for k = i + 1; k < length && depth[k] == value; k++ { + reps++ + } + + i += reps + if value == 0 { + bw.writeBits(uint(kZeroRepsDepth[reps]), kZeroRepsBits[reps]) + } else { + if previous_value != value { + bw.writeBits(uint(kCodeLengthDepth[value]), uint64(kCodeLengthBits[value])) + reps-- + } + + if reps < 3 { + for reps != 0 { + reps-- + bw.writeBits(uint(kCodeLengthDepth[value]), uint64(kCodeLengthBits[value])) + } + } else { + reps -= 3 + bw.writeBits(uint(kNonZeroRepsDepth[reps]), kNonZeroRepsBits[reps]) + } + + previous_value = value + } + } + } +} + +func indexOf(v []byte, v_size uint, value byte) uint { + var i uint = 0 + for ; i < v_size; i++ { + if v[i] == value { + return i + } + } + + return i +} + +func moveToFront(v []byte, index uint) { + var value byte = v[index] + var i uint + for i = index; i != 0; i-- { + v[i] = v[i-1] + } + + v[0] = value +} + +func moveToFrontTransform(v_in []uint32, v_size uint, v_out []uint32) { + var i uint + var mtf [256]byte + var max_value uint32 + if v_size == 0 { + return + } + + max_value = v_in[0] + for i = 1; i < v_size; i++ { + if v_in[i] > max_value { + max_value = v_in[i] + } + } + + assert(max_value < 256) + for i = 0; uint32(i) <= max_value; i++ { + mtf[i] = byte(i) + } + { + var mtf_size uint = uint(max_value + 1) + for i = 0; i < v_size; i++ { + var index uint = indexOf(mtf[:], mtf_size, byte(v_in[i])) + assert(index < mtf_size) + v_out[i] = uint32(index) + moveToFront(mtf[:], index) + } + } +} + +/* +Finds runs of zeros in v[0..in_size) and replaces them with a prefix code of + + the run length plus extra bits (lower 9 bits is the prefix code and the rest + are the extra bits). Non-zero values in v[] are shifted by + *max_length_prefix. Will not create prefix codes bigger than the initial + value of *max_run_length_prefix. The prefix code of run length L is simply + Log2Floor(L) and the number of extra bits is the same as the prefix code. +*/ +func runLengthCodeZeros(in_size uint, v []uint32, out_size *uint, max_run_length_prefix *uint32) { + var max_reps uint32 = 0 + var i uint + var max_prefix uint32 + for i = 0; i < in_size; { + var reps uint32 = 0 + for ; i < in_size && v[i] != 0; i++ { + } + for ; i < in_size && v[i] == 0; i++ { + reps++ + } + + max_reps = brotli_max_uint32_t(reps, max_reps) + } + + if max_reps > 0 { + max_prefix = log2FloorNonZero(uint(max_reps)) + } else { + max_prefix = 0 + } + max_prefix = brotli_min_uint32_t(max_prefix, *max_run_length_prefix) + *max_run_length_prefix = max_prefix + *out_size = 0 + for i = 0; i < in_size; { + assert(*out_size <= i) + if v[i] != 0 { + v[*out_size] = v[i] + *max_run_length_prefix + i++ + (*out_size)++ + } else { + var reps uint32 = 1 + var k uint + for k = i + 1; k < in_size && v[k] == 0; k++ { + reps++ + } + + i += uint(reps) + for reps != 0 { + if reps < 2< 0) + writeSingleBit(use_rle, storage_ix, storage) + if use_rle { + writeBits(4, uint64(max_run_length_prefix)-1, storage_ix, storage) + } + } + + buildAndStoreHuffmanTree(histogram[:], uint(uint32(num_clusters)+max_run_length_prefix), uint(uint32(num_clusters)+max_run_length_prefix), tree, depths[:], bits[:], storage_ix, storage) + for i = 0; i < num_rle_symbols; i++ { + var rle_symbol uint32 = rle_symbols[i] & encodeContextMap_kSymbolMask + var extra_bits_val uint32 = rle_symbols[i] >> symbolBits + writeBits(uint(depths[rle_symbol]), uint64(bits[rle_symbol]), storage_ix, storage) + if rle_symbol > 0 && rle_symbol <= max_run_length_prefix { + writeBits(uint(rle_symbol), uint64(extra_bits_val), storage_ix, storage) + } + } + + writeBits(1, 1, storage_ix, storage) /* use move-to-front */ + rle_symbols = nil +} + +/* Stores the block switch command with index block_ix to the bit stream. */ +func storeBlockSwitch(code *blockSplitCode, block_len uint32, block_type byte, is_first_block bool, storage_ix *uint, storage []byte) { + var typecode uint = nextBlockTypeCode(&code.type_code_calculator, block_type) + var lencode uint + var len_nextra uint32 + var len_extra uint32 + if !is_first_block { + writeBits(uint(code.type_depths[typecode]), uint64(code.type_bits[typecode]), storage_ix, storage) + } + + getBlockLengthPrefixCode(block_len, &lencode, &len_nextra, &len_extra) + + writeBits(uint(code.length_depths[lencode]), uint64(code.length_bits[lencode]), storage_ix, storage) + writeBits(uint(len_nextra), uint64(len_extra), storage_ix, storage) +} + +/* +Builds a BlockSplitCode data structure from the block split given by the + + vector of block types and block lengths and stores it to the bit stream. +*/ +func buildAndStoreBlockSplitCode(types []byte, lengths []uint32, num_blocks uint, num_types uint, tree []huffmanTree, code *blockSplitCode, storage_ix *uint, storage []byte) { + var type_histo [maxBlockTypeSymbols]uint32 + var length_histo [numBlockLenSymbols]uint32 + var i uint + var type_code_calculator blockTypeCodeCalculator + for i := 0; i < int(num_types+2); i++ { + type_histo[i] = 0 + } + length_histo = [numBlockLenSymbols]uint32{} + initBlockTypeCodeCalculator(&type_code_calculator) + for i = 0; i < num_blocks; i++ { + var type_code uint = nextBlockTypeCode(&type_code_calculator, types[i]) + if i != 0 { + type_histo[type_code]++ + } + length_histo[blockLengthPrefixCode(lengths[i])]++ + } + + storeVarLenUint8(num_types-1, storage_ix, storage) + if num_types > 1 { /* TODO: else? could StoreBlockSwitch occur? */ + buildAndStoreHuffmanTree(type_histo[0:], num_types+2, num_types+2, tree, code.type_depths[0:], code.type_bits[0:], storage_ix, storage) + buildAndStoreHuffmanTree(length_histo[0:], numBlockLenSymbols, numBlockLenSymbols, tree, code.length_depths[0:], code.length_bits[0:], storage_ix, storage) + storeBlockSwitch(code, lengths[0], types[0], true, storage_ix, storage) + } +} + +/* Stores a context map where the histogram type is always the block type. */ +func storeTrivialContextMap(num_types uint, context_bits uint, tree []huffmanTree, storage_ix *uint, storage []byte) { + storeVarLenUint8(num_types-1, storage_ix, storage) + if num_types > 1 { + var repeat_code uint = context_bits - 1 + var repeat_bits uint = (1 << repeat_code) - 1 + var alphabet_size uint = num_types + repeat_code + var histogram [maxContextMapSymbols]uint32 + var depths [maxContextMapSymbols]byte + var bits [maxContextMapSymbols]uint16 + var i uint + for i := 0; i < int(alphabet_size); i++ { + histogram[i] = 0 + } + + /* Write RLEMAX. */ + writeBits(1, 1, storage_ix, storage) + + writeBits(4, uint64(repeat_code)-1, storage_ix, storage) + histogram[repeat_code] = uint32(num_types) + histogram[0] = 1 + for i = context_bits; i < alphabet_size; i++ { + histogram[i] = 1 + } + + buildAndStoreHuffmanTree(histogram[:], alphabet_size, alphabet_size, tree, depths[:], bits[:], storage_ix, storage) + for i = 0; i < num_types; i++ { + var tmp uint + if i == 0 { + tmp = 0 + } else { + tmp = i + context_bits - 1 + } + var code uint = tmp + writeBits(uint(depths[code]), uint64(bits[code]), storage_ix, storage) + writeBits(uint(depths[repeat_code]), uint64(bits[repeat_code]), storage_ix, storage) + writeBits(repeat_code, uint64(repeat_bits), storage_ix, storage) + } + + /* Write IMTF (inverse-move-to-front) bit. */ + writeBits(1, 1, storage_ix, storage) + } +} + +/* Manages the encoding of one block category (literal, command or distance). */ +type blockEncoder struct { + histogram_length_ uint + num_block_types_ uint + block_types_ []byte + block_lengths_ []uint32 + num_blocks_ uint + block_split_code_ blockSplitCode + block_ix_ uint + block_len_ uint + entropy_ix_ uint + depths_ []byte + bits_ []uint16 +} + +var blockEncoderPool sync.Pool + +func getBlockEncoder(histogram_length uint, num_block_types uint, block_types []byte, block_lengths []uint32, num_blocks uint) *blockEncoder { + self, _ := blockEncoderPool.Get().(*blockEncoder) + + if self != nil { + self.block_ix_ = 0 + self.entropy_ix_ = 0 + self.depths_ = self.depths_[:0] + self.bits_ = self.bits_[:0] + } else { + self = &blockEncoder{} + } + + self.histogram_length_ = histogram_length + self.num_block_types_ = num_block_types + self.block_types_ = block_types + self.block_lengths_ = block_lengths + self.num_blocks_ = num_blocks + initBlockTypeCodeCalculator(&self.block_split_code_.type_code_calculator) + if num_blocks == 0 { + self.block_len_ = 0 + } else { + self.block_len_ = uint(block_lengths[0]) + } + + return self +} + +func cleanupBlockEncoder(self *blockEncoder) { + blockEncoderPool.Put(self) +} + +/* +Creates entropy codes of block lengths and block types and stores them + + to the bit stream. +*/ +func buildAndStoreBlockSwitchEntropyCodes(self *blockEncoder, tree []huffmanTree, storage_ix *uint, storage []byte) { + buildAndStoreBlockSplitCode(self.block_types_, self.block_lengths_, self.num_blocks_, self.num_block_types_, tree, &self.block_split_code_, storage_ix, storage) +} + +/* +Stores the next symbol with the entropy code of the current block type. + + Updates the block type and block length at block boundaries. +*/ +func storeSymbol(self *blockEncoder, symbol uint, storage_ix *uint, storage []byte) { + if self.block_len_ == 0 { + self.block_ix_++ + var block_ix uint = self.block_ix_ + var block_len uint32 = self.block_lengths_[block_ix] + var block_type byte = self.block_types_[block_ix] + self.block_len_ = uint(block_len) + self.entropy_ix_ = uint(block_type) * self.histogram_length_ + storeBlockSwitch(&self.block_split_code_, block_len, block_type, false, storage_ix, storage) + } + + self.block_len_-- + { + var ix uint = self.entropy_ix_ + symbol + writeBits(uint(self.depths_[ix]), uint64(self.bits_[ix]), storage_ix, storage) + } +} + +/* +Stores the next symbol with the entropy code of the current block type and + + context value. + Updates the block type and block length at block boundaries. +*/ +func storeSymbolWithContext(self *blockEncoder, symbol uint, context uint, context_map []uint32, storage_ix *uint, storage []byte, context_bits uint) { + if self.block_len_ == 0 { + self.block_ix_++ + var block_ix uint = self.block_ix_ + var block_len uint32 = self.block_lengths_[block_ix] + var block_type byte = self.block_types_[block_ix] + self.block_len_ = uint(block_len) + self.entropy_ix_ = uint(block_type) << context_bits + storeBlockSwitch(&self.block_split_code_, block_len, block_type, false, storage_ix, storage) + } + + self.block_len_-- + { + var histo_ix uint = uint(context_map[self.entropy_ix_+context]) + var ix uint = histo_ix*self.histogram_length_ + symbol + writeBits(uint(self.depths_[ix]), uint64(self.bits_[ix]), storage_ix, storage) + } +} + +func buildAndStoreEntropyCodesLiteral(self *blockEncoder, histograms []histogramLiteral, histograms_size uint, alphabet_size uint, tree []huffmanTree, storage_ix *uint, storage []byte) { + var table_size uint = histograms_size * self.histogram_length_ + if cap(self.depths_) < int(table_size) { + self.depths_ = make([]byte, table_size) + } else { + self.depths_ = self.depths_[:table_size] + } + if cap(self.bits_) < int(table_size) { + self.bits_ = make([]uint16, table_size) + } else { + self.bits_ = self.bits_[:table_size] + } + { + var i uint + for i = 0; i < histograms_size; i++ { + var ix uint = i * self.histogram_length_ + buildAndStoreHuffmanTree(histograms[i].data_[0:], self.histogram_length_, alphabet_size, tree, self.depths_[ix:], self.bits_[ix:], storage_ix, storage) + } + } +} + +func buildAndStoreEntropyCodesCommand(self *blockEncoder, histograms []histogramCommand, histograms_size uint, alphabet_size uint, tree []huffmanTree, storage_ix *uint, storage []byte) { + var table_size uint = histograms_size * self.histogram_length_ + if cap(self.depths_) < int(table_size) { + self.depths_ = make([]byte, table_size) + } else { + self.depths_ = self.depths_[:table_size] + } + if cap(self.bits_) < int(table_size) { + self.bits_ = make([]uint16, table_size) + } else { + self.bits_ = self.bits_[:table_size] + } + { + var i uint + for i = 0; i < histograms_size; i++ { + var ix uint = i * self.histogram_length_ + buildAndStoreHuffmanTree(histograms[i].data_[0:], self.histogram_length_, alphabet_size, tree, self.depths_[ix:], self.bits_[ix:], storage_ix, storage) + } + } +} + +func buildAndStoreEntropyCodesDistance(self *blockEncoder, histograms []histogramDistance, histograms_size uint, alphabet_size uint, tree []huffmanTree, storage_ix *uint, storage []byte) { + var table_size uint = histograms_size * self.histogram_length_ + if cap(self.depths_) < int(table_size) { + self.depths_ = make([]byte, table_size) + } else { + self.depths_ = self.depths_[:table_size] + } + if cap(self.bits_) < int(table_size) { + self.bits_ = make([]uint16, table_size) + } else { + self.bits_ = self.bits_[:table_size] + } + { + var i uint + for i = 0; i < histograms_size; i++ { + var ix uint = i * self.histogram_length_ + buildAndStoreHuffmanTree(histograms[i].data_[0:], self.histogram_length_, alphabet_size, tree, self.depths_[ix:], self.bits_[ix:], storage_ix, storage) + } + } +} + +func jumpToByteBoundary(storage_ix *uint, storage []byte) { + *storage_ix = (*storage_ix + 7) &^ 7 + storage[*storage_ix>>3] = 0 +} + +func storeMetaBlock(input []byte, start_pos uint, length uint, mask uint, prev_byte byte, prev_byte2 byte, is_last bool, params *encoderParams, literal_context_mode int, commands []command, mb *metaBlockSplit, storage_ix *uint, storage []byte) { + var pos uint = start_pos + var i uint + var num_distance_symbols uint32 = params.dist.alphabet_size + var num_effective_distance_symbols uint32 = num_distance_symbols + var tree []huffmanTree + var literal_context_lut contextLUT = getContextLUT(literal_context_mode) + var dist *distanceParams = ¶ms.dist + if params.large_window && num_effective_distance_symbols > numHistogramDistanceSymbols { + num_effective_distance_symbols = numHistogramDistanceSymbols + } + + storeCompressedMetaBlockHeader(is_last, length, storage_ix, storage) + + tree = make([]huffmanTree, maxHuffmanTreeSize) + literal_enc := getBlockEncoder(numLiteralSymbols, mb.literal_split.num_types, mb.literal_split.types, mb.literal_split.lengths, mb.literal_split.num_blocks) + command_enc := getBlockEncoder(numCommandSymbols, mb.command_split.num_types, mb.command_split.types, mb.command_split.lengths, mb.command_split.num_blocks) + distance_enc := getBlockEncoder(uint(num_effective_distance_symbols), mb.distance_split.num_types, mb.distance_split.types, mb.distance_split.lengths, mb.distance_split.num_blocks) + + buildAndStoreBlockSwitchEntropyCodes(literal_enc, tree, storage_ix, storage) + buildAndStoreBlockSwitchEntropyCodes(command_enc, tree, storage_ix, storage) + buildAndStoreBlockSwitchEntropyCodes(distance_enc, tree, storage_ix, storage) + + writeBits(2, uint64(dist.distance_postfix_bits), storage_ix, storage) + writeBits(4, uint64(dist.num_direct_distance_codes)>>dist.distance_postfix_bits, storage_ix, storage) + for i = 0; i < mb.literal_split.num_types; i++ { + writeBits(2, uint64(literal_context_mode), storage_ix, storage) + } + + if mb.literal_context_map_size == 0 { + storeTrivialContextMap(mb.literal_histograms_size, literalContextBits, tree, storage_ix, storage) + } else { + encodeContextMap(mb.literal_context_map, mb.literal_context_map_size, mb.literal_histograms_size, tree, storage_ix, storage) + } + + if mb.distance_context_map_size == 0 { + storeTrivialContextMap(mb.distance_histograms_size, distanceContextBits, tree, storage_ix, storage) + } else { + encodeContextMap(mb.distance_context_map, mb.distance_context_map_size, mb.distance_histograms_size, tree, storage_ix, storage) + } + + buildAndStoreEntropyCodesLiteral(literal_enc, mb.literal_histograms, mb.literal_histograms_size, numLiteralSymbols, tree, storage_ix, storage) + buildAndStoreEntropyCodesCommand(command_enc, mb.command_histograms, mb.command_histograms_size, numCommandSymbols, tree, storage_ix, storage) + buildAndStoreEntropyCodesDistance(distance_enc, mb.distance_histograms, mb.distance_histograms_size, uint(num_distance_symbols), tree, storage_ix, storage) + tree = nil + + for _, cmd := range commands { + var cmd_code uint = uint(cmd.cmd_prefix_) + storeSymbol(command_enc, cmd_code, storage_ix, storage) + storeCommandExtra(&cmd, storage_ix, storage) + if mb.literal_context_map_size == 0 { + var j uint + for j = uint(cmd.insert_len_); j != 0; j-- { + storeSymbol(literal_enc, uint(input[pos&mask]), storage_ix, storage) + pos++ + } + } else { + var j uint + for j = uint(cmd.insert_len_); j != 0; j-- { + var context uint = uint(getContext(prev_byte, prev_byte2, literal_context_lut)) + var literal byte = input[pos&mask] + storeSymbolWithContext(literal_enc, uint(literal), context, mb.literal_context_map, storage_ix, storage, literalContextBits) + prev_byte2 = prev_byte + prev_byte = literal + pos++ + } + } + + pos += uint(commandCopyLen(&cmd)) + if commandCopyLen(&cmd) != 0 { + prev_byte2 = input[(pos-2)&mask] + prev_byte = input[(pos-1)&mask] + if cmd.cmd_prefix_ >= 128 { + var dist_code uint = uint(cmd.dist_prefix_) & 0x3FF + var distnumextra uint32 = uint32(cmd.dist_prefix_) >> 10 + var distextra uint64 = uint64(cmd.dist_extra_) + if mb.distance_context_map_size == 0 { + storeSymbol(distance_enc, dist_code, storage_ix, storage) + } else { + var context uint = uint(commandDistanceContext(&cmd)) + storeSymbolWithContext(distance_enc, dist_code, context, mb.distance_context_map, storage_ix, storage, distanceContextBits) + } + + writeBits(uint(distnumextra), distextra, storage_ix, storage) + } + } + } + + cleanupBlockEncoder(distance_enc) + cleanupBlockEncoder(command_enc) + cleanupBlockEncoder(literal_enc) + if is_last { + jumpToByteBoundary(storage_ix, storage) + } +} + +func buildHistograms(input []byte, start_pos uint, mask uint, commands []command, lit_histo *histogramLiteral, cmd_histo *histogramCommand, dist_histo *histogramDistance) { + var pos uint = start_pos + for _, cmd := range commands { + var j uint + histogramAddCommand(cmd_histo, uint(cmd.cmd_prefix_)) + for j = uint(cmd.insert_len_); j != 0; j-- { + histogramAddLiteral(lit_histo, uint(input[pos&mask])) + pos++ + } + + pos += uint(commandCopyLen(&cmd)) + if commandCopyLen(&cmd) != 0 && cmd.cmd_prefix_ >= 128 { + histogramAddDistance(dist_histo, uint(cmd.dist_prefix_)&0x3FF) + } + } +} + +func storeDataWithHuffmanCodes(input []byte, start_pos uint, mask uint, commands []command, lit_depth []byte, lit_bits []uint16, cmd_depth []byte, cmd_bits []uint16, dist_depth []byte, dist_bits []uint16, storage_ix *uint, storage []byte) { + var pos uint = start_pos + for _, cmd := range commands { + var cmd_code uint = uint(cmd.cmd_prefix_) + var j uint + writeBits(uint(cmd_depth[cmd_code]), uint64(cmd_bits[cmd_code]), storage_ix, storage) + storeCommandExtra(&cmd, storage_ix, storage) + for j = uint(cmd.insert_len_); j != 0; j-- { + var literal byte = input[pos&mask] + writeBits(uint(lit_depth[literal]), uint64(lit_bits[literal]), storage_ix, storage) + pos++ + } + + pos += uint(commandCopyLen(&cmd)) + if commandCopyLen(&cmd) != 0 && cmd.cmd_prefix_ >= 128 { + var dist_code uint = uint(cmd.dist_prefix_) & 0x3FF + var distnumextra uint32 = uint32(cmd.dist_prefix_) >> 10 + var distextra uint32 = cmd.dist_extra_ + writeBits(uint(dist_depth[dist_code]), uint64(dist_bits[dist_code]), storage_ix, storage) + writeBits(uint(distnumextra), uint64(distextra), storage_ix, storage) + } + } +} + +func storeMetaBlockTrivial(input []byte, start_pos uint, length uint, mask uint, is_last bool, params *encoderParams, commands []command, storage_ix *uint, storage []byte) { + var lit_histo histogramLiteral + var cmd_histo histogramCommand + var dist_histo histogramDistance + var lit_depth [numLiteralSymbols]byte + var lit_bits [numLiteralSymbols]uint16 + var cmd_depth [numCommandSymbols]byte + var cmd_bits [numCommandSymbols]uint16 + var dist_depth [maxSimpleDistanceAlphabetSize]byte + var dist_bits [maxSimpleDistanceAlphabetSize]uint16 + var tree []huffmanTree + var num_distance_symbols uint32 = params.dist.alphabet_size + + storeCompressedMetaBlockHeader(is_last, length, storage_ix, storage) + + histogramClearLiteral(&lit_histo) + histogramClearCommand(&cmd_histo) + histogramClearDistance(&dist_histo) + + buildHistograms(input, start_pos, mask, commands, &lit_histo, &cmd_histo, &dist_histo) + + writeBits(13, 0, storage_ix, storage) + + tree = make([]huffmanTree, maxHuffmanTreeSize) + buildAndStoreHuffmanTree(lit_histo.data_[:], numLiteralSymbols, numLiteralSymbols, tree, lit_depth[:], lit_bits[:], storage_ix, storage) + buildAndStoreHuffmanTree(cmd_histo.data_[:], numCommandSymbols, numCommandSymbols, tree, cmd_depth[:], cmd_bits[:], storage_ix, storage) + buildAndStoreHuffmanTree(dist_histo.data_[:], maxSimpleDistanceAlphabetSize, uint(num_distance_symbols), tree, dist_depth[:], dist_bits[:], storage_ix, storage) + tree = nil + storeDataWithHuffmanCodes(input, start_pos, mask, commands, lit_depth[:], lit_bits[:], cmd_depth[:], cmd_bits[:], dist_depth[:], dist_bits[:], storage_ix, storage) + if is_last { + jumpToByteBoundary(storage_ix, storage) + } +} + +func storeMetaBlockFast(input []byte, start_pos uint, length uint, mask uint, is_last bool, params *encoderParams, commands []command, storage_ix *uint, storage []byte) { + var num_distance_symbols uint32 = params.dist.alphabet_size + var distance_alphabet_bits uint32 = log2FloorNonZero(uint(num_distance_symbols-1)) + 1 + + storeCompressedMetaBlockHeader(is_last, length, storage_ix, storage) + + writeBits(13, 0, storage_ix, storage) + + if len(commands) <= 128 { + var histogram = [numLiteralSymbols]uint32{0} + var pos uint = start_pos + var num_literals uint = 0 + var lit_depth [numLiteralSymbols]byte + var lit_bits [numLiteralSymbols]uint16 + for _, cmd := range commands { + var j uint + for j = uint(cmd.insert_len_); j != 0; j-- { + histogram[input[pos&mask]]++ + pos++ + } + + num_literals += uint(cmd.insert_len_) + pos += uint(commandCopyLen(&cmd)) + } + + buildAndStoreHuffmanTreeFast(histogram[:], num_literals, /* max_bits = */ + 8, lit_depth[:], lit_bits[:], storage_ix, storage) + + storeStaticCommandHuffmanTree(storage_ix, storage) + storeStaticDistanceHuffmanTree(storage_ix, storage) + storeDataWithHuffmanCodes(input, start_pos, mask, commands, lit_depth[:], lit_bits[:], kStaticCommandCodeDepth[:], kStaticCommandCodeBits[:], kStaticDistanceCodeDepth[:], kStaticDistanceCodeBits[:], storage_ix, storage) + } else { + var lit_histo histogramLiteral + var cmd_histo histogramCommand + var dist_histo histogramDistance + var lit_depth [numLiteralSymbols]byte + var lit_bits [numLiteralSymbols]uint16 + var cmd_depth [numCommandSymbols]byte + var cmd_bits [numCommandSymbols]uint16 + var dist_depth [maxSimpleDistanceAlphabetSize]byte + var dist_bits [maxSimpleDistanceAlphabetSize]uint16 + histogramClearLiteral(&lit_histo) + histogramClearCommand(&cmd_histo) + histogramClearDistance(&dist_histo) + buildHistograms(input, start_pos, mask, commands, &lit_histo, &cmd_histo, &dist_histo) + buildAndStoreHuffmanTreeFast(lit_histo.data_[:], lit_histo.total_count_, /* max_bits = */ + 8, lit_depth[:], lit_bits[:], storage_ix, storage) + + buildAndStoreHuffmanTreeFast(cmd_histo.data_[:], cmd_histo.total_count_, /* max_bits = */ + 10, cmd_depth[:], cmd_bits[:], storage_ix, storage) + + buildAndStoreHuffmanTreeFast(dist_histo.data_[:], dist_histo.total_count_, /* max_bits = */ + uint(distance_alphabet_bits), dist_depth[:], dist_bits[:], storage_ix, storage) + + storeDataWithHuffmanCodes(input, start_pos, mask, commands, lit_depth[:], lit_bits[:], cmd_depth[:], cmd_bits[:], dist_depth[:], dist_bits[:], storage_ix, storage) + } + + if is_last { + jumpToByteBoundary(storage_ix, storage) + } +} + +/* +This is for storing uncompressed blocks (simple raw storage of + + bytes-as-bytes). +*/ +func storeUncompressedMetaBlock(is_final_block bool, input []byte, position uint, mask uint, len uint, storage_ix *uint, storage []byte) { + var masked_pos uint = position & mask + storeUncompressedMetaBlockHeader(uint(len), storage_ix, storage) + jumpToByteBoundary(storage_ix, storage) + + if masked_pos+len > mask+1 { + var len1 uint = mask + 1 - masked_pos + copy(storage[*storage_ix>>3:], input[masked_pos:][:len1]) + *storage_ix += len1 << 3 + len -= len1 + masked_pos = 0 + } + + copy(storage[*storage_ix>>3:], input[masked_pos:][:len]) + *storage_ix += uint(len << 3) + + /* We need to clear the next 4 bytes to continue to be + compatible with BrotliWriteBits. */ + writeBitsPrepareStorage(*storage_ix, storage) + + /* Since the uncompressed block itself may not be the final block, add an + empty one after this. */ + if is_final_block { + writeBits(1, 1, storage_ix, storage) /* islast */ + writeBits(1, 1, storage_ix, storage) /* isempty */ + jumpToByteBoundary(storage_ix, storage) + } +} diff --git a/vendor/github.com/andybalholm/brotli/cluster.go b/vendor/github.com/andybalholm/brotli/cluster.go new file mode 100644 index 000000000..df8a32822 --- /dev/null +++ b/vendor/github.com/andybalholm/brotli/cluster.go @@ -0,0 +1,30 @@ +package brotli + +/* Copyright 2013 Google Inc. All Rights Reserved. + + Distributed under MIT license. + See file LICENSE for detail or copy at https://opensource.org/licenses/MIT +*/ + +/* Functions for clustering similar histograms together. */ + +type histogramPair struct { + idx1 uint32 + idx2 uint32 + cost_combo float64 + cost_diff float64 +} + +func histogramPairIsLess(p1 *histogramPair, p2 *histogramPair) bool { + if p1.cost_diff != p2.cost_diff { + return p1.cost_diff > p2.cost_diff + } + + return (p1.idx2 - p1.idx1) > (p2.idx2 - p2.idx1) +} + +/* Returns entropy reduction of the context map when we combine two clusters. */ +func clusterCostDiff(size_a uint, size_b uint) float64 { + var size_c uint = size_a + size_b + return float64(size_a)*fastLog2(size_a) + float64(size_b)*fastLog2(size_b) - float64(size_c)*fastLog2(size_c) +} diff --git a/vendor/github.com/andybalholm/brotli/cluster_command.go b/vendor/github.com/andybalholm/brotli/cluster_command.go new file mode 100644 index 000000000..45b569bb2 --- /dev/null +++ b/vendor/github.com/andybalholm/brotli/cluster_command.go @@ -0,0 +1,164 @@ +package brotli + +/* Copyright 2013 Google Inc. All Rights Reserved. + + Distributed under MIT license. + See file LICENSE for detail or copy at https://opensource.org/licenses/MIT +*/ + +/* Computes the bit cost reduction by combining out[idx1] and out[idx2] and if + it is below a threshold, stores the pair (idx1, idx2) in the *pairs queue. */ +func compareAndPushToQueueCommand(out []histogramCommand, cluster_size []uint32, idx1 uint32, idx2 uint32, max_num_pairs uint, pairs []histogramPair, num_pairs *uint) { + var is_good_pair bool = false + var p histogramPair + p.idx2 = 0 + p.idx1 = p.idx2 + p.cost_combo = 0 + p.cost_diff = p.cost_combo + if idx1 == idx2 { + return + } + + if idx2 < idx1 { + var t uint32 = idx2 + idx2 = idx1 + idx1 = t + } + + p.idx1 = idx1 + p.idx2 = idx2 + p.cost_diff = 0.5 * clusterCostDiff(uint(cluster_size[idx1]), uint(cluster_size[idx2])) + p.cost_diff -= out[idx1].bit_cost_ + p.cost_diff -= out[idx2].bit_cost_ + + if out[idx1].total_count_ == 0 { + p.cost_combo = out[idx2].bit_cost_ + is_good_pair = true + } else if out[idx2].total_count_ == 0 { + p.cost_combo = out[idx1].bit_cost_ + is_good_pair = true + } else { + var threshold float64 + if *num_pairs == 0 { + threshold = 1e99 + } else { + threshold = brotli_max_double(0.0, pairs[0].cost_diff) + } + var combo histogramCommand = out[idx1] + var cost_combo float64 + histogramAddHistogramCommand(&combo, &out[idx2]) + cost_combo = populationCostCommand(&combo) + if cost_combo < threshold-p.cost_diff { + p.cost_combo = cost_combo + is_good_pair = true + } + } + + if is_good_pair { + p.cost_diff += p.cost_combo + if *num_pairs > 0 && histogramPairIsLess(&pairs[0], &p) { + /* Replace the top of the queue if needed. */ + if *num_pairs < max_num_pairs { + pairs[*num_pairs] = pairs[0] + (*num_pairs)++ + } + + pairs[0] = p + } else if *num_pairs < max_num_pairs { + pairs[*num_pairs] = p + (*num_pairs)++ + } + } +} + +func histogramCombineCommand(out []histogramCommand, cluster_size []uint32, symbols []uint32, clusters []uint32, pairs []histogramPair, num_clusters uint, symbols_size uint, max_clusters uint, max_num_pairs uint) uint { + var cost_diff_threshold float64 = 0.0 + var min_cluster_size uint = 1 + var num_pairs uint = 0 + { + /* We maintain a vector of histogram pairs, with the property that the pair + with the maximum bit cost reduction is the first. */ + var idx1 uint + for idx1 = 0; idx1 < num_clusters; idx1++ { + var idx2 uint + for idx2 = idx1 + 1; idx2 < num_clusters; idx2++ { + compareAndPushToQueueCommand(out, cluster_size, clusters[idx1], clusters[idx2], max_num_pairs, pairs[0:], &num_pairs) + } + } + } + + for num_clusters > min_cluster_size { + var best_idx1 uint32 + var best_idx2 uint32 + var i uint + if pairs[0].cost_diff >= cost_diff_threshold { + cost_diff_threshold = 1e99 + min_cluster_size = max_clusters + continue + } + + /* Take the best pair from the top of heap. */ + best_idx1 = pairs[0].idx1 + + best_idx2 = pairs[0].idx2 + histogramAddHistogramCommand(&out[best_idx1], &out[best_idx2]) + out[best_idx1].bit_cost_ = pairs[0].cost_combo + cluster_size[best_idx1] += cluster_size[best_idx2] + for i = 0; i < symbols_size; i++ { + if symbols[i] == best_idx2 { + symbols[i] = best_idx1 + } + } + + for i = 0; i < num_clusters; i++ { + if clusters[i] == best_idx2 { + copy(clusters[i:], clusters[i+1:][:num_clusters-i-1]) + break + } + } + + num_clusters-- + { + /* Remove pairs intersecting the just combined best pair. */ + var copy_to_idx uint = 0 + for i = 0; i < num_pairs; i++ { + var p *histogramPair = &pairs[i] + if p.idx1 == best_idx1 || p.idx2 == best_idx1 || p.idx1 == best_idx2 || p.idx2 == best_idx2 { + /* Remove invalid pair from the queue. */ + continue + } + + if histogramPairIsLess(&pairs[0], p) { + /* Replace the top of the queue if needed. */ + var front histogramPair = pairs[0] + pairs[0] = *p + pairs[copy_to_idx] = front + } else { + pairs[copy_to_idx] = *p + } + + copy_to_idx++ + } + + num_pairs = copy_to_idx + } + + /* Push new pairs formed with the combined histogram to the heap. */ + for i = 0; i < num_clusters; i++ { + compareAndPushToQueueCommand(out, cluster_size, best_idx1, clusters[i], max_num_pairs, pairs[0:], &num_pairs) + } + } + + return num_clusters +} + +/* What is the bit cost of moving histogram from cur_symbol to candidate. */ +func histogramBitCostDistanceCommand(histogram *histogramCommand, candidate *histogramCommand) float64 { + if histogram.total_count_ == 0 { + return 0.0 + } else { + var tmp histogramCommand = *histogram + histogramAddHistogramCommand(&tmp, candidate) + return populationCostCommand(&tmp) - candidate.bit_cost_ + } +} diff --git a/vendor/github.com/andybalholm/brotli/cluster_distance.go b/vendor/github.com/andybalholm/brotli/cluster_distance.go new file mode 100644 index 000000000..1aaa86e6e --- /dev/null +++ b/vendor/github.com/andybalholm/brotli/cluster_distance.go @@ -0,0 +1,326 @@ +package brotli + +import "math" + +/* Copyright 2013 Google Inc. All Rights Reserved. + + Distributed under MIT license. + See file LICENSE for detail or copy at https://opensource.org/licenses/MIT +*/ + +/* Computes the bit cost reduction by combining out[idx1] and out[idx2] and if + it is below a threshold, stores the pair (idx1, idx2) in the *pairs queue. */ +func compareAndPushToQueueDistance(out []histogramDistance, cluster_size []uint32, idx1 uint32, idx2 uint32, max_num_pairs uint, pairs []histogramPair, num_pairs *uint) { + var is_good_pair bool = false + var p histogramPair + p.idx2 = 0 + p.idx1 = p.idx2 + p.cost_combo = 0 + p.cost_diff = p.cost_combo + if idx1 == idx2 { + return + } + + if idx2 < idx1 { + var t uint32 = idx2 + idx2 = idx1 + idx1 = t + } + + p.idx1 = idx1 + p.idx2 = idx2 + p.cost_diff = 0.5 * clusterCostDiff(uint(cluster_size[idx1]), uint(cluster_size[idx2])) + p.cost_diff -= out[idx1].bit_cost_ + p.cost_diff -= out[idx2].bit_cost_ + + if out[idx1].total_count_ == 0 { + p.cost_combo = out[idx2].bit_cost_ + is_good_pair = true + } else if out[idx2].total_count_ == 0 { + p.cost_combo = out[idx1].bit_cost_ + is_good_pair = true + } else { + var threshold float64 + if *num_pairs == 0 { + threshold = 1e99 + } else { + threshold = brotli_max_double(0.0, pairs[0].cost_diff) + } + var combo histogramDistance = out[idx1] + var cost_combo float64 + histogramAddHistogramDistance(&combo, &out[idx2]) + cost_combo = populationCostDistance(&combo) + if cost_combo < threshold-p.cost_diff { + p.cost_combo = cost_combo + is_good_pair = true + } + } + + if is_good_pair { + p.cost_diff += p.cost_combo + if *num_pairs > 0 && histogramPairIsLess(&pairs[0], &p) { + /* Replace the top of the queue if needed. */ + if *num_pairs < max_num_pairs { + pairs[*num_pairs] = pairs[0] + (*num_pairs)++ + } + + pairs[0] = p + } else if *num_pairs < max_num_pairs { + pairs[*num_pairs] = p + (*num_pairs)++ + } + } +} + +func histogramCombineDistance(out []histogramDistance, cluster_size []uint32, symbols []uint32, clusters []uint32, pairs []histogramPair, num_clusters uint, symbols_size uint, max_clusters uint, max_num_pairs uint) uint { + var cost_diff_threshold float64 = 0.0 + var min_cluster_size uint = 1 + var num_pairs uint = 0 + { + /* We maintain a vector of histogram pairs, with the property that the pair + with the maximum bit cost reduction is the first. */ + var idx1 uint + for idx1 = 0; idx1 < num_clusters; idx1++ { + var idx2 uint + for idx2 = idx1 + 1; idx2 < num_clusters; idx2++ { + compareAndPushToQueueDistance(out, cluster_size, clusters[idx1], clusters[idx2], max_num_pairs, pairs[0:], &num_pairs) + } + } + } + + for num_clusters > min_cluster_size { + var best_idx1 uint32 + var best_idx2 uint32 + var i uint + if pairs[0].cost_diff >= cost_diff_threshold { + cost_diff_threshold = 1e99 + min_cluster_size = max_clusters + continue + } + + /* Take the best pair from the top of heap. */ + best_idx1 = pairs[0].idx1 + + best_idx2 = pairs[0].idx2 + histogramAddHistogramDistance(&out[best_idx1], &out[best_idx2]) + out[best_idx1].bit_cost_ = pairs[0].cost_combo + cluster_size[best_idx1] += cluster_size[best_idx2] + for i = 0; i < symbols_size; i++ { + if symbols[i] == best_idx2 { + symbols[i] = best_idx1 + } + } + + for i = 0; i < num_clusters; i++ { + if clusters[i] == best_idx2 { + copy(clusters[i:], clusters[i+1:][:num_clusters-i-1]) + break + } + } + + num_clusters-- + { + /* Remove pairs intersecting the just combined best pair. */ + var copy_to_idx uint = 0 + for i = 0; i < num_pairs; i++ { + var p *histogramPair = &pairs[i] + if p.idx1 == best_idx1 || p.idx2 == best_idx1 || p.idx1 == best_idx2 || p.idx2 == best_idx2 { + /* Remove invalid pair from the queue. */ + continue + } + + if histogramPairIsLess(&pairs[0], p) { + /* Replace the top of the queue if needed. */ + var front histogramPair = pairs[0] + pairs[0] = *p + pairs[copy_to_idx] = front + } else { + pairs[copy_to_idx] = *p + } + + copy_to_idx++ + } + + num_pairs = copy_to_idx + } + + /* Push new pairs formed with the combined histogram to the heap. */ + for i = 0; i < num_clusters; i++ { + compareAndPushToQueueDistance(out, cluster_size, best_idx1, clusters[i], max_num_pairs, pairs[0:], &num_pairs) + } + } + + return num_clusters +} + +/* What is the bit cost of moving histogram from cur_symbol to candidate. */ +func histogramBitCostDistanceDistance(histogram *histogramDistance, candidate *histogramDistance) float64 { + if histogram.total_count_ == 0 { + return 0.0 + } else { + var tmp histogramDistance = *histogram + histogramAddHistogramDistance(&tmp, candidate) + return populationCostDistance(&tmp) - candidate.bit_cost_ + } +} + +/* Find the best 'out' histogram for each of the 'in' histograms. + When called, clusters[0..num_clusters) contains the unique values from + symbols[0..in_size), but this property is not preserved in this function. + Note: we assume that out[]->bit_cost_ is already up-to-date. */ +func histogramRemapDistance(in []histogramDistance, in_size uint, clusters []uint32, num_clusters uint, out []histogramDistance, symbols []uint32) { + var i uint + for i = 0; i < in_size; i++ { + var best_out uint32 + if i == 0 { + best_out = symbols[0] + } else { + best_out = symbols[i-1] + } + var best_bits float64 = histogramBitCostDistanceDistance(&in[i], &out[best_out]) + var j uint + for j = 0; j < num_clusters; j++ { + var cur_bits float64 = histogramBitCostDistanceDistance(&in[i], &out[clusters[j]]) + if cur_bits < best_bits { + best_bits = cur_bits + best_out = clusters[j] + } + } + + symbols[i] = best_out + } + + /* Recompute each out based on raw and symbols. */ + for i = 0; i < num_clusters; i++ { + histogramClearDistance(&out[clusters[i]]) + } + + for i = 0; i < in_size; i++ { + histogramAddHistogramDistance(&out[symbols[i]], &in[i]) + } +} + +/* Reorders elements of the out[0..length) array and changes values in + symbols[0..length) array in the following way: + * when called, symbols[] contains indexes into out[], and has N unique + values (possibly N < length) + * on return, symbols'[i] = f(symbols[i]) and + out'[symbols'[i]] = out[symbols[i]], for each 0 <= i < length, + where f is a bijection between the range of symbols[] and [0..N), and + the first occurrences of values in symbols'[i] come in consecutive + increasing order. + Returns N, the number of unique values in symbols[]. */ + +var histogramReindexDistance_kInvalidIndex uint32 = math.MaxUint32 + +func histogramReindexDistance(out []histogramDistance, symbols []uint32, length uint) uint { + var new_index []uint32 = make([]uint32, length) + var next_index uint32 + var tmp []histogramDistance + var i uint + for i = 0; i < length; i++ { + new_index[i] = histogramReindexDistance_kInvalidIndex + } + + next_index = 0 + for i = 0; i < length; i++ { + if new_index[symbols[i]] == histogramReindexDistance_kInvalidIndex { + new_index[symbols[i]] = next_index + next_index++ + } + } + + /* TODO: by using idea of "cycle-sort" we can avoid allocation of + tmp and reduce the number of copying by the factor of 2. */ + tmp = make([]histogramDistance, next_index) + + next_index = 0 + for i = 0; i < length; i++ { + if new_index[symbols[i]] == next_index { + tmp[next_index] = out[symbols[i]] + next_index++ + } + + symbols[i] = new_index[symbols[i]] + } + + new_index = nil + for i = 0; uint32(i) < next_index; i++ { + out[i] = tmp[i] + } + + tmp = nil + return uint(next_index) +} + +func clusterHistogramsDistance(in []histogramDistance, in_size uint, max_histograms uint, out []histogramDistance, out_size *uint, histogram_symbols []uint32) { + var cluster_size []uint32 = make([]uint32, in_size) + var clusters []uint32 = make([]uint32, in_size) + var num_clusters uint = 0 + var max_input_histograms uint = 64 + var pairs_capacity uint = max_input_histograms * max_input_histograms / 2 + var pairs []histogramPair = make([]histogramPair, (pairs_capacity + 1)) + var i uint + + /* For the first pass of clustering, we allow all pairs. */ + for i = 0; i < in_size; i++ { + cluster_size[i] = 1 + } + + for i = 0; i < in_size; i++ { + out[i] = in[i] + out[i].bit_cost_ = populationCostDistance(&in[i]) + histogram_symbols[i] = uint32(i) + } + + for i = 0; i < in_size; i += max_input_histograms { + var num_to_combine uint = brotli_min_size_t(in_size-i, max_input_histograms) + var num_new_clusters uint + var j uint + for j = 0; j < num_to_combine; j++ { + clusters[num_clusters+j] = uint32(i + j) + } + + num_new_clusters = histogramCombineDistance(out, cluster_size, histogram_symbols[i:], clusters[num_clusters:], pairs, num_to_combine, num_to_combine, max_histograms, pairs_capacity) + num_clusters += num_new_clusters + } + { + /* For the second pass, we limit the total number of histogram pairs. + After this limit is reached, we only keep searching for the best pair. */ + var max_num_pairs uint = brotli_min_size_t(64*num_clusters, (num_clusters/2)*num_clusters) + if pairs_capacity < (max_num_pairs + 1) { + var _new_size uint + if pairs_capacity == 0 { + _new_size = max_num_pairs + 1 + } else { + _new_size = pairs_capacity + } + var new_array []histogramPair + for _new_size < (max_num_pairs + 1) { + _new_size *= 2 + } + new_array = make([]histogramPair, _new_size) + if pairs_capacity != 0 { + copy(new_array, pairs[:pairs_capacity]) + } + + pairs = new_array + pairs_capacity = _new_size + } + + /* Collapse similar histograms. */ + num_clusters = histogramCombineDistance(out, cluster_size, histogram_symbols, clusters, pairs, num_clusters, in_size, max_histograms, max_num_pairs) + } + + pairs = nil + cluster_size = nil + + /* Find the optimal map from original histograms to the final ones. */ + histogramRemapDistance(in, in_size, clusters, num_clusters, out, histogram_symbols) + + clusters = nil + + /* Convert the context map to a canonical form. */ + *out_size = histogramReindexDistance(out, histogram_symbols, in_size) +} diff --git a/vendor/github.com/andybalholm/brotli/cluster_literal.go b/vendor/github.com/andybalholm/brotli/cluster_literal.go new file mode 100644 index 000000000..6ba66f31b --- /dev/null +++ b/vendor/github.com/andybalholm/brotli/cluster_literal.go @@ -0,0 +1,326 @@ +package brotli + +import "math" + +/* Copyright 2013 Google Inc. All Rights Reserved. + + Distributed under MIT license. + See file LICENSE for detail or copy at https://opensource.org/licenses/MIT +*/ + +/* Computes the bit cost reduction by combining out[idx1] and out[idx2] and if + it is below a threshold, stores the pair (idx1, idx2) in the *pairs queue. */ +func compareAndPushToQueueLiteral(out []histogramLiteral, cluster_size []uint32, idx1 uint32, idx2 uint32, max_num_pairs uint, pairs []histogramPair, num_pairs *uint) { + var is_good_pair bool = false + var p histogramPair + p.idx2 = 0 + p.idx1 = p.idx2 + p.cost_combo = 0 + p.cost_diff = p.cost_combo + if idx1 == idx2 { + return + } + + if idx2 < idx1 { + var t uint32 = idx2 + idx2 = idx1 + idx1 = t + } + + p.idx1 = idx1 + p.idx2 = idx2 + p.cost_diff = 0.5 * clusterCostDiff(uint(cluster_size[idx1]), uint(cluster_size[idx2])) + p.cost_diff -= out[idx1].bit_cost_ + p.cost_diff -= out[idx2].bit_cost_ + + if out[idx1].total_count_ == 0 { + p.cost_combo = out[idx2].bit_cost_ + is_good_pair = true + } else if out[idx2].total_count_ == 0 { + p.cost_combo = out[idx1].bit_cost_ + is_good_pair = true + } else { + var threshold float64 + if *num_pairs == 0 { + threshold = 1e99 + } else { + threshold = brotli_max_double(0.0, pairs[0].cost_diff) + } + var combo histogramLiteral = out[idx1] + var cost_combo float64 + histogramAddHistogramLiteral(&combo, &out[idx2]) + cost_combo = populationCostLiteral(&combo) + if cost_combo < threshold-p.cost_diff { + p.cost_combo = cost_combo + is_good_pair = true + } + } + + if is_good_pair { + p.cost_diff += p.cost_combo + if *num_pairs > 0 && histogramPairIsLess(&pairs[0], &p) { + /* Replace the top of the queue if needed. */ + if *num_pairs < max_num_pairs { + pairs[*num_pairs] = pairs[0] + (*num_pairs)++ + } + + pairs[0] = p + } else if *num_pairs < max_num_pairs { + pairs[*num_pairs] = p + (*num_pairs)++ + } + } +} + +func histogramCombineLiteral(out []histogramLiteral, cluster_size []uint32, symbols []uint32, clusters []uint32, pairs []histogramPair, num_clusters uint, symbols_size uint, max_clusters uint, max_num_pairs uint) uint { + var cost_diff_threshold float64 = 0.0 + var min_cluster_size uint = 1 + var num_pairs uint = 0 + { + /* We maintain a vector of histogram pairs, with the property that the pair + with the maximum bit cost reduction is the first. */ + var idx1 uint + for idx1 = 0; idx1 < num_clusters; idx1++ { + var idx2 uint + for idx2 = idx1 + 1; idx2 < num_clusters; idx2++ { + compareAndPushToQueueLiteral(out, cluster_size, clusters[idx1], clusters[idx2], max_num_pairs, pairs[0:], &num_pairs) + } + } + } + + for num_clusters > min_cluster_size { + var best_idx1 uint32 + var best_idx2 uint32 + var i uint + if pairs[0].cost_diff >= cost_diff_threshold { + cost_diff_threshold = 1e99 + min_cluster_size = max_clusters + continue + } + + /* Take the best pair from the top of heap. */ + best_idx1 = pairs[0].idx1 + + best_idx2 = pairs[0].idx2 + histogramAddHistogramLiteral(&out[best_idx1], &out[best_idx2]) + out[best_idx1].bit_cost_ = pairs[0].cost_combo + cluster_size[best_idx1] += cluster_size[best_idx2] + for i = 0; i < symbols_size; i++ { + if symbols[i] == best_idx2 { + symbols[i] = best_idx1 + } + } + + for i = 0; i < num_clusters; i++ { + if clusters[i] == best_idx2 { + copy(clusters[i:], clusters[i+1:][:num_clusters-i-1]) + break + } + } + + num_clusters-- + { + /* Remove pairs intersecting the just combined best pair. */ + var copy_to_idx uint = 0 + for i = 0; i < num_pairs; i++ { + var p *histogramPair = &pairs[i] + if p.idx1 == best_idx1 || p.idx2 == best_idx1 || p.idx1 == best_idx2 || p.idx2 == best_idx2 { + /* Remove invalid pair from the queue. */ + continue + } + + if histogramPairIsLess(&pairs[0], p) { + /* Replace the top of the queue if needed. */ + var front histogramPair = pairs[0] + pairs[0] = *p + pairs[copy_to_idx] = front + } else { + pairs[copy_to_idx] = *p + } + + copy_to_idx++ + } + + num_pairs = copy_to_idx + } + + /* Push new pairs formed with the combined histogram to the heap. */ + for i = 0; i < num_clusters; i++ { + compareAndPushToQueueLiteral(out, cluster_size, best_idx1, clusters[i], max_num_pairs, pairs[0:], &num_pairs) + } + } + + return num_clusters +} + +/* What is the bit cost of moving histogram from cur_symbol to candidate. */ +func histogramBitCostDistanceLiteral(histogram *histogramLiteral, candidate *histogramLiteral) float64 { + if histogram.total_count_ == 0 { + return 0.0 + } else { + var tmp histogramLiteral = *histogram + histogramAddHistogramLiteral(&tmp, candidate) + return populationCostLiteral(&tmp) - candidate.bit_cost_ + } +} + +/* Find the best 'out' histogram for each of the 'in' histograms. + When called, clusters[0..num_clusters) contains the unique values from + symbols[0..in_size), but this property is not preserved in this function. + Note: we assume that out[]->bit_cost_ is already up-to-date. */ +func histogramRemapLiteral(in []histogramLiteral, in_size uint, clusters []uint32, num_clusters uint, out []histogramLiteral, symbols []uint32) { + var i uint + for i = 0; i < in_size; i++ { + var best_out uint32 + if i == 0 { + best_out = symbols[0] + } else { + best_out = symbols[i-1] + } + var best_bits float64 = histogramBitCostDistanceLiteral(&in[i], &out[best_out]) + var j uint + for j = 0; j < num_clusters; j++ { + var cur_bits float64 = histogramBitCostDistanceLiteral(&in[i], &out[clusters[j]]) + if cur_bits < best_bits { + best_bits = cur_bits + best_out = clusters[j] + } + } + + symbols[i] = best_out + } + + /* Recompute each out based on raw and symbols. */ + for i = 0; i < num_clusters; i++ { + histogramClearLiteral(&out[clusters[i]]) + } + + for i = 0; i < in_size; i++ { + histogramAddHistogramLiteral(&out[symbols[i]], &in[i]) + } +} + +/* Reorders elements of the out[0..length) array and changes values in + symbols[0..length) array in the following way: + * when called, symbols[] contains indexes into out[], and has N unique + values (possibly N < length) + * on return, symbols'[i] = f(symbols[i]) and + out'[symbols'[i]] = out[symbols[i]], for each 0 <= i < length, + where f is a bijection between the range of symbols[] and [0..N), and + the first occurrences of values in symbols'[i] come in consecutive + increasing order. + Returns N, the number of unique values in symbols[]. */ + +var histogramReindexLiteral_kInvalidIndex uint32 = math.MaxUint32 + +func histogramReindexLiteral(out []histogramLiteral, symbols []uint32, length uint) uint { + var new_index []uint32 = make([]uint32, length) + var next_index uint32 + var tmp []histogramLiteral + var i uint + for i = 0; i < length; i++ { + new_index[i] = histogramReindexLiteral_kInvalidIndex + } + + next_index = 0 + for i = 0; i < length; i++ { + if new_index[symbols[i]] == histogramReindexLiteral_kInvalidIndex { + new_index[symbols[i]] = next_index + next_index++ + } + } + + /* TODO: by using idea of "cycle-sort" we can avoid allocation of + tmp and reduce the number of copying by the factor of 2. */ + tmp = make([]histogramLiteral, next_index) + + next_index = 0 + for i = 0; i < length; i++ { + if new_index[symbols[i]] == next_index { + tmp[next_index] = out[symbols[i]] + next_index++ + } + + symbols[i] = new_index[symbols[i]] + } + + new_index = nil + for i = 0; uint32(i) < next_index; i++ { + out[i] = tmp[i] + } + + tmp = nil + return uint(next_index) +} + +func clusterHistogramsLiteral(in []histogramLiteral, in_size uint, max_histograms uint, out []histogramLiteral, out_size *uint, histogram_symbols []uint32) { + var cluster_size []uint32 = make([]uint32, in_size) + var clusters []uint32 = make([]uint32, in_size) + var num_clusters uint = 0 + var max_input_histograms uint = 64 + var pairs_capacity uint = max_input_histograms * max_input_histograms / 2 + var pairs []histogramPair = make([]histogramPair, (pairs_capacity + 1)) + var i uint + + /* For the first pass of clustering, we allow all pairs. */ + for i = 0; i < in_size; i++ { + cluster_size[i] = 1 + } + + for i = 0; i < in_size; i++ { + out[i] = in[i] + out[i].bit_cost_ = populationCostLiteral(&in[i]) + histogram_symbols[i] = uint32(i) + } + + for i = 0; i < in_size; i += max_input_histograms { + var num_to_combine uint = brotli_min_size_t(in_size-i, max_input_histograms) + var num_new_clusters uint + var j uint + for j = 0; j < num_to_combine; j++ { + clusters[num_clusters+j] = uint32(i + j) + } + + num_new_clusters = histogramCombineLiteral(out, cluster_size, histogram_symbols[i:], clusters[num_clusters:], pairs, num_to_combine, num_to_combine, max_histograms, pairs_capacity) + num_clusters += num_new_clusters + } + { + /* For the second pass, we limit the total number of histogram pairs. + After this limit is reached, we only keep searching for the best pair. */ + var max_num_pairs uint = brotli_min_size_t(64*num_clusters, (num_clusters/2)*num_clusters) + if pairs_capacity < (max_num_pairs + 1) { + var _new_size uint + if pairs_capacity == 0 { + _new_size = max_num_pairs + 1 + } else { + _new_size = pairs_capacity + } + var new_array []histogramPair + for _new_size < (max_num_pairs + 1) { + _new_size *= 2 + } + new_array = make([]histogramPair, _new_size) + if pairs_capacity != 0 { + copy(new_array, pairs[:pairs_capacity]) + } + + pairs = new_array + pairs_capacity = _new_size + } + + /* Collapse similar histograms. */ + num_clusters = histogramCombineLiteral(out, cluster_size, histogram_symbols, clusters, pairs, num_clusters, in_size, max_histograms, max_num_pairs) + } + + pairs = nil + cluster_size = nil + + /* Find the optimal map from original histograms to the final ones. */ + histogramRemapLiteral(in, in_size, clusters, num_clusters, out, histogram_symbols) + + clusters = nil + + /* Convert the context map to a canonical form. */ + *out_size = histogramReindexLiteral(out, histogram_symbols, in_size) +} diff --git a/vendor/github.com/andybalholm/brotli/command.go b/vendor/github.com/andybalholm/brotli/command.go new file mode 100644 index 000000000..b1662a555 --- /dev/null +++ b/vendor/github.com/andybalholm/brotli/command.go @@ -0,0 +1,254 @@ +package brotli + +var kInsBase = []uint32{ + 0, + 1, + 2, + 3, + 4, + 5, + 6, + 8, + 10, + 14, + 18, + 26, + 34, + 50, + 66, + 98, + 130, + 194, + 322, + 578, + 1090, + 2114, + 6210, + 22594, +} + +var kInsExtra = []uint32{ + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 2, + 2, + 3, + 3, + 4, + 4, + 5, + 5, + 6, + 7, + 8, + 9, + 10, + 12, + 14, + 24, +} + +var kCopyBase = []uint32{ + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 12, + 14, + 18, + 22, + 30, + 38, + 54, + 70, + 102, + 134, + 198, + 326, + 582, + 1094, + 2118, +} + +var kCopyExtra = []uint32{ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 2, + 2, + 3, + 3, + 4, + 4, + 5, + 5, + 6, + 7, + 8, + 9, + 10, + 24, +} + +func getInsertLengthCode(insertlen uint) uint16 { + if insertlen < 6 { + return uint16(insertlen) + } else if insertlen < 130 { + var nbits uint32 = log2FloorNonZero(insertlen-2) - 1 + return uint16((nbits << 1) + uint32((insertlen-2)>>nbits) + 2) + } else if insertlen < 2114 { + return uint16(log2FloorNonZero(insertlen-66) + 10) + } else if insertlen < 6210 { + return 21 + } else if insertlen < 22594 { + return 22 + } else { + return 23 + } +} + +func getCopyLengthCode(copylen uint) uint16 { + if copylen < 10 { + return uint16(copylen - 2) + } else if copylen < 134 { + var nbits uint32 = log2FloorNonZero(copylen-6) - 1 + return uint16((nbits << 1) + uint32((copylen-6)>>nbits) + 4) + } else if copylen < 2118 { + return uint16(log2FloorNonZero(copylen-70) + 12) + } else { + return 23 + } +} + +func combineLengthCodes(inscode uint16, copycode uint16, use_last_distance bool) uint16 { + var bits64 uint16 = uint16(copycode&0x7 | (inscode&0x7)<<3) + if use_last_distance && inscode < 8 && copycode < 16 { + if copycode < 8 { + return bits64 + } else { + return bits64 | 64 + } + } else { + /* Specification: 5 Encoding of ... (last table) */ + /* offset = 2 * index, where index is in range [0..8] */ + var offset uint32 = 2 * ((uint32(copycode) >> 3) + 3*(uint32(inscode)>>3)) + + /* All values in specification are K * 64, + where K = [2, 3, 6, 4, 5, 8, 7, 9, 10], + i + 1 = [1, 2, 3, 4, 5, 6, 7, 8, 9], + K - i - 1 = [1, 1, 3, 0, 0, 2, 0, 1, 2] = D. + All values in D require only 2 bits to encode. + Magic constant is shifted 6 bits left, to avoid final multiplication. */ + offset = (offset << 5) + 0x40 + ((0x520D40 >> offset) & 0xC0) + + return uint16(offset | uint32(bits64)) + } +} + +func getLengthCode(insertlen uint, copylen uint, use_last_distance bool, code *uint16) { + var inscode uint16 = getInsertLengthCode(insertlen) + var copycode uint16 = getCopyLengthCode(copylen) + *code = combineLengthCodes(inscode, copycode, use_last_distance) +} + +func getInsertBase(inscode uint16) uint32 { + return kInsBase[inscode] +} + +func getInsertExtra(inscode uint16) uint32 { + return kInsExtra[inscode] +} + +func getCopyBase(copycode uint16) uint32 { + return kCopyBase[copycode] +} + +func getCopyExtra(copycode uint16) uint32 { + return kCopyExtra[copycode] +} + +type command struct { + insert_len_ uint32 + copy_len_ uint32 + dist_extra_ uint32 + cmd_prefix_ uint16 + dist_prefix_ uint16 +} + +/* distance_code is e.g. 0 for same-as-last short code, or 16 for offset 1. */ +func makeCommand(dist *distanceParams, insertlen uint, copylen uint, copylen_code_delta int, distance_code uint) (cmd command) { + /* Don't rely on signed int representation, use honest casts. */ + var delta uint32 = uint32(byte(int8(copylen_code_delta))) + cmd.insert_len_ = uint32(insertlen) + cmd.copy_len_ = uint32(uint32(copylen) | delta<<25) + + /* The distance prefix and extra bits are stored in this Command as if + npostfix and ndirect were 0, they are only recomputed later after the + clustering if needed. */ + prefixEncodeCopyDistance(distance_code, uint(dist.num_direct_distance_codes), uint(dist.distance_postfix_bits), &cmd.dist_prefix_, &cmd.dist_extra_) + getLengthCode(insertlen, uint(int(copylen)+copylen_code_delta), (cmd.dist_prefix_&0x3FF == 0), &cmd.cmd_prefix_) + + return cmd +} + +func makeInsertCommand(insertlen uint) (cmd command) { + cmd.insert_len_ = uint32(insertlen) + cmd.copy_len_ = 4 << 25 + cmd.dist_extra_ = 0 + cmd.dist_prefix_ = numDistanceShortCodes + getLengthCode(insertlen, 4, false, &cmd.cmd_prefix_) + return cmd +} + +func commandRestoreDistanceCode(self *command, dist *distanceParams) uint32 { + if uint32(self.dist_prefix_&0x3FF) < numDistanceShortCodes+dist.num_direct_distance_codes { + return uint32(self.dist_prefix_) & 0x3FF + } else { + var dcode uint32 = uint32(self.dist_prefix_) & 0x3FF + var nbits uint32 = uint32(self.dist_prefix_) >> 10 + var extra uint32 = self.dist_extra_ + var postfix_mask uint32 = (1 << dist.distance_postfix_bits) - 1 + var hcode uint32 = (dcode - dist.num_direct_distance_codes - numDistanceShortCodes) >> dist.distance_postfix_bits + var lcode uint32 = (dcode - dist.num_direct_distance_codes - numDistanceShortCodes) & postfix_mask + var offset uint32 = ((2 + (hcode & 1)) << nbits) - 4 + return ((offset + extra) << dist.distance_postfix_bits) + lcode + dist.num_direct_distance_codes + numDistanceShortCodes + } +} + +func commandDistanceContext(self *command) uint32 { + var r uint32 = uint32(self.cmd_prefix_) >> 6 + var c uint32 = uint32(self.cmd_prefix_) & 7 + if (r == 0 || r == 2 || r == 4 || r == 7) && (c <= 2) { + return c + } + + return 3 +} + +func commandCopyLen(self *command) uint32 { + return self.copy_len_ & 0x1FFFFFF +} + +func commandCopyLenCode(self *command) uint32 { + var modifier uint32 = self.copy_len_ >> 25 + var delta int32 = int32(int8(byte(modifier | (modifier&0x40)<<1))) + return uint32(int32(self.copy_len_&0x1FFFFFF) + delta) +} diff --git a/vendor/github.com/andybalholm/brotli/compress_fragment.go b/vendor/github.com/andybalholm/brotli/compress_fragment.go new file mode 100644 index 000000000..c9bd05770 --- /dev/null +++ b/vendor/github.com/andybalholm/brotli/compress_fragment.go @@ -0,0 +1,834 @@ +package brotli + +import "encoding/binary" + +/* Copyright 2015 Google Inc. All Rights Reserved. + + Distributed under MIT license. + See file LICENSE for detail or copy at https://opensource.org/licenses/MIT +*/ + +/* Function for fast encoding of an input fragment, independently from the input + history. This function uses one-pass processing: when we find a backward + match, we immediately emit the corresponding command and literal codes to + the bit stream. + + Adapted from the CompressFragment() function in + https://github.com/google/snappy/blob/master/snappy.cc */ + +const maxDistance_compress_fragment = 262128 + +func hash5(p []byte, shift uint) uint32 { + var h uint64 = (binary.LittleEndian.Uint64(p) << 24) * uint64(kHashMul32) + return uint32(h >> shift) +} + +func hashBytesAtOffset5(v uint64, offset int, shift uint) uint32 { + assert(offset >= 0) + assert(offset <= 3) + { + var h uint64 = ((v >> uint(8*offset)) << 24) * uint64(kHashMul32) + return uint32(h >> shift) + } +} + +func isMatch5(p1 []byte, p2 []byte) bool { + return binary.LittleEndian.Uint32(p1) == binary.LittleEndian.Uint32(p2) && + p1[4] == p2[4] +} + +/* Builds a literal prefix code into "depths" and "bits" based on the statistics + of the "input" string and stores it into the bit stream. + Note that the prefix code here is built from the pre-LZ77 input, therefore + we can only approximate the statistics of the actual literal stream. + Moreover, for long inputs we build a histogram from a sample of the input + and thus have to assign a non-zero depth for each literal. + Returns estimated compression ratio millibytes/char for encoding given input + with generated code. */ +func buildAndStoreLiteralPrefixCode(input []byte, input_size uint, depths []byte, bits []uint16, storage_ix *uint, storage []byte) uint { + var histogram = [256]uint32{0} + var histogram_total uint + var i uint + if input_size < 1<<15 { + for i = 0; i < input_size; i++ { + histogram[input[i]]++ + } + + histogram_total = input_size + for i = 0; i < 256; i++ { + /* We weigh the first 11 samples with weight 3 to account for the + balancing effect of the LZ77 phase on the histogram. */ + var adjust uint32 = 2 * brotli_min_uint32_t(histogram[i], 11) + histogram[i] += adjust + histogram_total += uint(adjust) + } + } else { + const kSampleRate uint = 29 + for i = 0; i < input_size; i += kSampleRate { + histogram[input[i]]++ + } + + histogram_total = (input_size + kSampleRate - 1) / kSampleRate + for i = 0; i < 256; i++ { + /* We add 1 to each population count to avoid 0 bit depths (since this is + only a sample and we don't know if the symbol appears or not), and we + weigh the first 11 samples with weight 3 to account for the balancing + effect of the LZ77 phase on the histogram (more frequent symbols are + more likely to be in backward references instead as literals). */ + var adjust uint32 = 1 + 2*brotli_min_uint32_t(histogram[i], 11) + histogram[i] += adjust + histogram_total += uint(adjust) + } + } + + buildAndStoreHuffmanTreeFast(histogram[:], histogram_total, /* max_bits = */ + 8, depths, bits, storage_ix, storage) + { + var literal_ratio uint = 0 + for i = 0; i < 256; i++ { + if histogram[i] != 0 { + literal_ratio += uint(histogram[i] * uint32(depths[i])) + } + } + + /* Estimated encoding ratio, millibytes per symbol. */ + return (literal_ratio * 125) / histogram_total + } +} + +/* Builds a command and distance prefix code (each 64 symbols) into "depth" and + "bits" based on "histogram" and stores it into the bit stream. */ +func buildAndStoreCommandPrefixCode1(histogram []uint32, depth []byte, bits []uint16, storage_ix *uint, storage []byte) { + var tree [129]huffmanTree + var cmd_depth = [numCommandSymbols]byte{0} + /* Tree size for building a tree over 64 symbols is 2 * 64 + 1. */ + + var cmd_bits [64]uint16 + + createHuffmanTree(histogram, 64, 15, tree[:], depth) + createHuffmanTree(histogram[64:], 64, 14, tree[:], depth[64:]) + + /* We have to jump through a few hoops here in order to compute + the command bits because the symbols are in a different order than in + the full alphabet. This looks complicated, but having the symbols + in this order in the command bits saves a few branches in the Emit* + functions. */ + copy(cmd_depth[:], depth[:24]) + + copy(cmd_depth[24:][:], depth[40:][:8]) + copy(cmd_depth[32:][:], depth[24:][:8]) + copy(cmd_depth[40:][:], depth[48:][:8]) + copy(cmd_depth[48:][:], depth[32:][:8]) + copy(cmd_depth[56:][:], depth[56:][:8]) + convertBitDepthsToSymbols(cmd_depth[:], 64, cmd_bits[:]) + copy(bits, cmd_bits[:24]) + copy(bits[24:], cmd_bits[32:][:8]) + copy(bits[32:], cmd_bits[48:][:8]) + copy(bits[40:], cmd_bits[24:][:8]) + copy(bits[48:], cmd_bits[40:][:8]) + copy(bits[56:], cmd_bits[56:][:8]) + convertBitDepthsToSymbols(depth[64:], 64, bits[64:]) + { + /* Create the bit length array for the full command alphabet. */ + var i uint + for i := 0; i < int(64); i++ { + cmd_depth[i] = 0 + } /* only 64 first values were used */ + copy(cmd_depth[:], depth[:8]) + copy(cmd_depth[64:][:], depth[8:][:8]) + copy(cmd_depth[128:][:], depth[16:][:8]) + copy(cmd_depth[192:][:], depth[24:][:8]) + copy(cmd_depth[384:][:], depth[32:][:8]) + for i = 0; i < 8; i++ { + cmd_depth[128+8*i] = depth[40+i] + cmd_depth[256+8*i] = depth[48+i] + cmd_depth[448+8*i] = depth[56+i] + } + + storeHuffmanTree(cmd_depth[:], numCommandSymbols, tree[:], storage_ix, storage) + } + + storeHuffmanTree(depth[64:], 64, tree[:], storage_ix, storage) +} + +/* REQUIRES: insertlen < 6210 */ +func emitInsertLen1(insertlen uint, depth []byte, bits []uint16, histo []uint32, storage_ix *uint, storage []byte) { + if insertlen < 6 { + var code uint = insertlen + 40 + writeBits(uint(depth[code]), uint64(bits[code]), storage_ix, storage) + histo[code]++ + } else if insertlen < 130 { + var tail uint = insertlen - 2 + var nbits uint32 = log2FloorNonZero(tail) - 1 + var prefix uint = tail >> nbits + var inscode uint = uint((nbits << 1) + uint32(prefix) + 42) + writeBits(uint(depth[inscode]), uint64(bits[inscode]), storage_ix, storage) + writeBits(uint(nbits), uint64(tail)-(uint64(prefix)<> nbits + var code uint = uint((nbits << 1) + uint32(prefix) + 20) + writeBits(uint(depth[code]), uint64(bits[code]), storage_ix, storage) + writeBits(uint(nbits), uint64(tail)-(uint64(prefix)<> nbits + var code uint = uint((nbits << 1) + uint32(prefix) + 4) + writeBits(uint(depth[code]), uint64(bits[code]), storage_ix, storage) + writeBits(uint(nbits), uint64(tail)-(uint64(prefix)<> 5) + 30 + writeBits(uint(depth[code]), uint64(bits[code]), storage_ix, storage) + writeBits(5, uint64(tail)&31, storage_ix, storage) + writeBits(uint(depth[64]), uint64(bits[64]), storage_ix, storage) + histo[code]++ + histo[64]++ + } else if copylen < 2120 { + var tail uint = copylen - 72 + var nbits uint32 = log2FloorNonZero(tail) + var code uint = uint(nbits + 28) + writeBits(uint(depth[code]), uint64(bits[code]), storage_ix, storage) + writeBits(uint(nbits), uint64(tail)-(uint64(uint(1))<> nbits) & 1 + var offset uint = (2 + prefix) << nbits + var distcode uint = uint(2*(nbits-1) + uint32(prefix) + 80) + writeBits(uint(depth[distcode]), uint64(bits[distcode]), storage_ix, storage) + writeBits(uint(nbits), uint64(d)-uint64(offset), storage_ix, storage) + histo[distcode]++ +} + +func emitLiterals(input []byte, len uint, depth []byte, bits []uint16, storage_ix *uint, storage []byte) { + var j uint + for j = 0; j < len; j++ { + var lit byte = input[j] + writeBits(uint(depth[lit]), uint64(bits[lit]), storage_ix, storage) + } +} + +/* REQUIRES: len <= 1 << 24. */ +func storeMetaBlockHeader1(len uint, is_uncompressed bool, storage_ix *uint, storage []byte) { + var nibbles uint = 6 + + /* ISLAST */ + writeBits(1, 0, storage_ix, storage) + + if len <= 1<<16 { + nibbles = 4 + } else if len <= 1<<20 { + nibbles = 5 + } + + writeBits(2, uint64(nibbles)-4, storage_ix, storage) + writeBits(nibbles*4, uint64(len)-1, storage_ix, storage) + + /* ISUNCOMPRESSED */ + writeSingleBit(is_uncompressed, storage_ix, storage) +} + +func updateBits(n_bits uint, bits uint32, pos uint, array []byte) { + for n_bits > 0 { + var byte_pos uint = pos >> 3 + var n_unchanged_bits uint = pos & 7 + var n_changed_bits uint = brotli_min_size_t(n_bits, 8-n_unchanged_bits) + var total_bits uint = n_unchanged_bits + n_changed_bits + var mask uint32 = (^((1 << total_bits) - 1)) | ((1 << n_unchanged_bits) - 1) + var unchanged_bits uint32 = uint32(array[byte_pos]) & mask + var changed_bits uint32 = bits & ((1 << n_changed_bits) - 1) + array[byte_pos] = byte(changed_bits<>= n_changed_bits + pos += n_changed_bits + } +} + +func rewindBitPosition1(new_storage_ix uint, storage_ix *uint, storage []byte) { + var bitpos uint = new_storage_ix & 7 + var mask uint = (1 << bitpos) - 1 + storage[new_storage_ix>>3] &= byte(mask) + *storage_ix = new_storage_ix +} + +var shouldMergeBlock_kSampleRate uint = 43 + +func shouldMergeBlock(data []byte, len uint, depths []byte) bool { + var histo = [256]uint{0} + var i uint + for i = 0; i < len; i += shouldMergeBlock_kSampleRate { + histo[data[i]]++ + } + { + var total uint = (len + shouldMergeBlock_kSampleRate - 1) / shouldMergeBlock_kSampleRate + var r float64 = (fastLog2(total)+0.5)*float64(total) + 200 + for i = 0; i < 256; i++ { + r -= float64(histo[i]) * (float64(depths[i]) + fastLog2(histo[i])) + } + + return r >= 0.0 + } +} + +func shouldUseUncompressedMode(metablock_start []byte, next_emit []byte, insertlen uint, literal_ratio uint) bool { + var compressed uint = uint(-cap(next_emit) + cap(metablock_start)) + if compressed*50 > insertlen { + return false + } else { + return literal_ratio > 980 + } +} + +func emitUncompressedMetaBlock1(begin []byte, end []byte, storage_ix_start uint, storage_ix *uint, storage []byte) { + var len uint = uint(-cap(end) + cap(begin)) + rewindBitPosition1(storage_ix_start, storage_ix, storage) + storeMetaBlockHeader1(uint(len), true, storage_ix, storage) + *storage_ix = (*storage_ix + 7) &^ 7 + copy(storage[*storage_ix>>3:], begin[:len]) + *storage_ix += uint(len << 3) + storage[*storage_ix>>3] = 0 +} + +var kCmdHistoSeed = [128]uint32{ + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, +} + +var compressFragmentFastImpl_kFirstBlockSize uint = 3 << 15 +var compressFragmentFastImpl_kMergeBlockSize uint = 1 << 16 + +func compressFragmentFastImpl(in []byte, input_size uint, is_last bool, table []int, table_bits uint, cmd_depth []byte, cmd_bits []uint16, cmd_code_numbits *uint, cmd_code []byte, storage_ix *uint, storage []byte) { + var cmd_histo [128]uint32 + var ip_end int + var next_emit int = 0 + var base_ip int = 0 + var input int = 0 + const kInputMarginBytes uint = windowGap + const kMinMatchLen uint = 5 + var metablock_start int = input + var block_size uint = brotli_min_size_t(input_size, compressFragmentFastImpl_kFirstBlockSize) + var total_block_size uint = block_size + var mlen_storage_ix uint = *storage_ix + 3 + var lit_depth [256]byte + var lit_bits [256]uint16 + var literal_ratio uint + var ip int + var last_distance int + var shift uint = 64 - table_bits + + /* "next_emit" is a pointer to the first byte that is not covered by a + previous copy. Bytes between "next_emit" and the start of the next copy or + the end of the input will be emitted as literal bytes. */ + + /* Save the start of the first block for position and distance computations. + */ + + /* Save the bit position of the MLEN field of the meta-block header, so that + we can update it later if we decide to extend this meta-block. */ + storeMetaBlockHeader1(block_size, false, storage_ix, storage) + + /* No block splits, no contexts. */ + writeBits(13, 0, storage_ix, storage) + + literal_ratio = buildAndStoreLiteralPrefixCode(in[input:], block_size, lit_depth[:], lit_bits[:], storage_ix, storage) + { + /* Store the pre-compressed command and distance prefix codes. */ + var i uint + for i = 0; i+7 < *cmd_code_numbits; i += 8 { + writeBits(8, uint64(cmd_code[i>>3]), storage_ix, storage) + } + } + + writeBits(*cmd_code_numbits&7, uint64(cmd_code[*cmd_code_numbits>>3]), storage_ix, storage) + + /* Initialize the command and distance histograms. We will gather + statistics of command and distance codes during the processing + of this block and use it to update the command and distance + prefix codes for the next block. */ +emit_commands: + copy(cmd_histo[:], kCmdHistoSeed[:]) + + /* "ip" is the input pointer. */ + ip = input + + last_distance = -1 + ip_end = int(uint(input) + block_size) + + if block_size >= kInputMarginBytes { + var len_limit uint = brotli_min_size_t(block_size-kMinMatchLen, input_size-kInputMarginBytes) + var ip_limit int = int(uint(input) + len_limit) + /* For the last block, we need to keep a 16 bytes margin so that we can be + sure that all distances are at most window size - 16. + For all other blocks, we only need to keep a margin of 5 bytes so that + we don't go over the block size with a copy. */ + + var next_hash uint32 + ip++ + for next_hash = hash5(in[ip:], shift); ; { + var skip uint32 = 32 + var next_ip int = ip + /* Step 1: Scan forward in the input looking for a 5-byte-long match. + If we get close to exhausting the input then goto emit_remainder. + + Heuristic match skipping: If 32 bytes are scanned with no matches + found, start looking only at every other byte. If 32 more bytes are + scanned, look at every third byte, etc.. When a match is found, + immediately go back to looking at every byte. This is a small loss + (~5% performance, ~0.1% density) for compressible data due to more + bookkeeping, but for non-compressible data (such as JPEG) it's a huge + win since the compressor quickly "realizes" the data is incompressible + and doesn't bother looking for matches everywhere. + + The "skip" variable keeps track of how many bytes there are since the + last match; dividing it by 32 (i.e. right-shifting by five) gives the + number of bytes to move ahead for each iteration. */ + + var candidate int + assert(next_emit < ip) + + trawl: + for { + var hash uint32 = next_hash + var bytes_between_hash_lookups uint32 = skip >> 5 + skip++ + assert(hash == hash5(in[next_ip:], shift)) + ip = next_ip + next_ip = int(uint32(ip) + bytes_between_hash_lookups) + if next_ip > ip_limit { + goto emit_remainder + } + + next_hash = hash5(in[next_ip:], shift) + candidate = ip - last_distance + if isMatch5(in[ip:], in[candidate:]) { + if candidate < ip { + table[hash] = int(ip - base_ip) + break + } + } + + candidate = base_ip + table[hash] + assert(candidate >= base_ip) + assert(candidate < ip) + + table[hash] = int(ip - base_ip) + if isMatch5(in[ip:], in[candidate:]) { + break + } + } + + /* Check copy distance. If candidate is not feasible, continue search. + Checking is done outside of hot loop to reduce overhead. */ + if ip-candidate > maxDistance_compress_fragment { + goto trawl + } + + /* Step 2: Emit the found match together with the literal bytes from + "next_emit" to the bit stream, and then see if we can find a next match + immediately afterwards. Repeat until we find no match for the input + without emitting some literal bytes. */ + { + var base int = ip + /* > 0 */ + var matched uint = 5 + findMatchLengthWithLimit(in[candidate+5:], in[ip+5:], uint(ip_end-ip)-5) + var distance int = int(base - candidate) + /* We have a 5-byte match at ip, and we need to emit bytes in + [next_emit, ip). */ + + var insert uint = uint(base - next_emit) + ip += int(matched) + if insert < 6210 { + emitInsertLen1(insert, cmd_depth, cmd_bits, cmd_histo[:], storage_ix, storage) + } else if shouldUseUncompressedMode(in[metablock_start:], in[next_emit:], insert, literal_ratio) { + emitUncompressedMetaBlock1(in[metablock_start:], in[base:], mlen_storage_ix-3, storage_ix, storage) + input_size -= uint(base - input) + input = base + next_emit = input + goto next_block + } else { + emitLongInsertLen(insert, cmd_depth, cmd_bits, cmd_histo[:], storage_ix, storage) + } + + emitLiterals(in[next_emit:], insert, lit_depth[:], lit_bits[:], storage_ix, storage) + if distance == last_distance { + writeBits(uint(cmd_depth[64]), uint64(cmd_bits[64]), storage_ix, storage) + cmd_histo[64]++ + } else { + emitDistance1(uint(distance), cmd_depth, cmd_bits, cmd_histo[:], storage_ix, storage) + last_distance = distance + } + + emitCopyLenLastDistance1(matched, cmd_depth, cmd_bits, cmd_histo[:], storage_ix, storage) + + next_emit = ip + if ip >= ip_limit { + goto emit_remainder + } + + /* We could immediately start working at ip now, but to improve + compression we first update "table" with the hashes of some positions + within the last copy. */ + { + var input_bytes uint64 = binary.LittleEndian.Uint64(in[ip-3:]) + var prev_hash uint32 = hashBytesAtOffset5(input_bytes, 0, shift) + var cur_hash uint32 = hashBytesAtOffset5(input_bytes, 3, shift) + table[prev_hash] = int(ip - base_ip - 3) + prev_hash = hashBytesAtOffset5(input_bytes, 1, shift) + table[prev_hash] = int(ip - base_ip - 2) + prev_hash = hashBytesAtOffset5(input_bytes, 2, shift) + table[prev_hash] = int(ip - base_ip - 1) + + candidate = base_ip + table[cur_hash] + table[cur_hash] = int(ip - base_ip) + } + } + + for isMatch5(in[ip:], in[candidate:]) { + var base int = ip + /* We have a 5-byte match at ip, and no need to emit any literal bytes + prior to ip. */ + + var matched uint = 5 + findMatchLengthWithLimit(in[candidate+5:], in[ip+5:], uint(ip_end-ip)-5) + if ip-candidate > maxDistance_compress_fragment { + break + } + ip += int(matched) + last_distance = int(base - candidate) /* > 0 */ + emitCopyLen1(matched, cmd_depth, cmd_bits, cmd_histo[:], storage_ix, storage) + emitDistance1(uint(last_distance), cmd_depth, cmd_bits, cmd_histo[:], storage_ix, storage) + + next_emit = ip + if ip >= ip_limit { + goto emit_remainder + } + + /* We could immediately start working at ip now, but to improve + compression we first update "table" with the hashes of some positions + within the last copy. */ + { + var input_bytes uint64 = binary.LittleEndian.Uint64(in[ip-3:]) + var prev_hash uint32 = hashBytesAtOffset5(input_bytes, 0, shift) + var cur_hash uint32 = hashBytesAtOffset5(input_bytes, 3, shift) + table[prev_hash] = int(ip - base_ip - 3) + prev_hash = hashBytesAtOffset5(input_bytes, 1, shift) + table[prev_hash] = int(ip - base_ip - 2) + prev_hash = hashBytesAtOffset5(input_bytes, 2, shift) + table[prev_hash] = int(ip - base_ip - 1) + + candidate = base_ip + table[cur_hash] + table[cur_hash] = int(ip - base_ip) + } + } + + ip++ + next_hash = hash5(in[ip:], shift) + } + } + +emit_remainder: + assert(next_emit <= ip_end) + input += int(block_size) + input_size -= block_size + block_size = brotli_min_size_t(input_size, compressFragmentFastImpl_kMergeBlockSize) + + /* Decide if we want to continue this meta-block instead of emitting the + last insert-only command. */ + if input_size > 0 && total_block_size+block_size <= 1<<20 && shouldMergeBlock(in[input:], block_size, lit_depth[:]) { + assert(total_block_size > 1<<16) + + /* Update the size of the current meta-block and continue emitting commands. + We can do this because the current size and the new size both have 5 + nibbles. */ + total_block_size += block_size + + updateBits(20, uint32(total_block_size-1), mlen_storage_ix, storage) + goto emit_commands + } + + /* Emit the remaining bytes as literals. */ + if next_emit < ip_end { + var insert uint = uint(ip_end - next_emit) + if insert < 6210 { + emitInsertLen1(insert, cmd_depth, cmd_bits, cmd_histo[:], storage_ix, storage) + emitLiterals(in[next_emit:], insert, lit_depth[:], lit_bits[:], storage_ix, storage) + } else if shouldUseUncompressedMode(in[metablock_start:], in[next_emit:], insert, literal_ratio) { + emitUncompressedMetaBlock1(in[metablock_start:], in[ip_end:], mlen_storage_ix-3, storage_ix, storage) + } else { + emitLongInsertLen(insert, cmd_depth, cmd_bits, cmd_histo[:], storage_ix, storage) + emitLiterals(in[next_emit:], insert, lit_depth[:], lit_bits[:], storage_ix, storage) + } + } + + next_emit = ip_end + + /* If we have more data, write a new meta-block header and prefix codes and + then continue emitting commands. */ +next_block: + if input_size > 0 { + metablock_start = input + block_size = brotli_min_size_t(input_size, compressFragmentFastImpl_kFirstBlockSize) + total_block_size = block_size + + /* Save the bit position of the MLEN field of the meta-block header, so that + we can update it later if we decide to extend this meta-block. */ + mlen_storage_ix = *storage_ix + 3 + + storeMetaBlockHeader1(block_size, false, storage_ix, storage) + + /* No block splits, no contexts. */ + writeBits(13, 0, storage_ix, storage) + + literal_ratio = buildAndStoreLiteralPrefixCode(in[input:], block_size, lit_depth[:], lit_bits[:], storage_ix, storage) + buildAndStoreCommandPrefixCode1(cmd_histo[:], cmd_depth, cmd_bits, storage_ix, storage) + goto emit_commands + } + + if !is_last { + /* If this is not the last block, update the command and distance prefix + codes for the next block and store the compressed forms. */ + cmd_code[0] = 0 + + *cmd_code_numbits = 0 + buildAndStoreCommandPrefixCode1(cmd_histo[:], cmd_depth, cmd_bits, cmd_code_numbits, cmd_code) + } +} + +/* Compresses "input" string to the "*storage" buffer as one or more complete + meta-blocks, and updates the "*storage_ix" bit position. + + If "is_last" is 1, emits an additional empty last meta-block. + + "cmd_depth" and "cmd_bits" contain the command and distance prefix codes + (see comment in encode.h) used for the encoding of this input fragment. + If "is_last" is 0, they are updated to reflect the statistics + of this input fragment, to be used for the encoding of the next fragment. + + "*cmd_code_numbits" is the number of bits of the compressed representation + of the command and distance prefix codes, and "cmd_code" is an array of + at least "(*cmd_code_numbits + 7) >> 3" size that contains the compressed + command and distance prefix codes. If "is_last" is 0, these are also + updated to represent the updated "cmd_depth" and "cmd_bits". + + REQUIRES: "input_size" is greater than zero, or "is_last" is 1. + REQUIRES: "input_size" is less or equal to maximal metablock size (1 << 24). + REQUIRES: All elements in "table[0..table_size-1]" are initialized to zero. + REQUIRES: "table_size" is an odd (9, 11, 13, 15) power of two + OUTPUT: maximal copy distance <= |input_size| + OUTPUT: maximal copy distance <= BROTLI_MAX_BACKWARD_LIMIT(18) */ +func compressFragmentFast(input []byte, input_size uint, is_last bool, table []int, table_size uint, cmd_depth []byte, cmd_bits []uint16, cmd_code_numbits *uint, cmd_code []byte, storage_ix *uint, storage []byte) { + var initial_storage_ix uint = *storage_ix + var table_bits uint = uint(log2FloorNonZero(table_size)) + + if input_size == 0 { + assert(is_last) + writeBits(1, 1, storage_ix, storage) /* islast */ + writeBits(1, 1, storage_ix, storage) /* isempty */ + *storage_ix = (*storage_ix + 7) &^ 7 + return + } + + compressFragmentFastImpl(input, input_size, is_last, table, table_bits, cmd_depth, cmd_bits, cmd_code_numbits, cmd_code, storage_ix, storage) + + /* If output is larger than single uncompressed block, rewrite it. */ + if *storage_ix-initial_storage_ix > 31+(input_size<<3) { + emitUncompressedMetaBlock1(input, input[input_size:], initial_storage_ix, storage_ix, storage) + } + + if is_last { + writeBits(1, 1, storage_ix, storage) /* islast */ + writeBits(1, 1, storage_ix, storage) /* isempty */ + *storage_ix = (*storage_ix + 7) &^ 7 + } +} diff --git a/vendor/github.com/andybalholm/brotli/compress_fragment_two_pass.go b/vendor/github.com/andybalholm/brotli/compress_fragment_two_pass.go new file mode 100644 index 000000000..79f9c7fdf --- /dev/null +++ b/vendor/github.com/andybalholm/brotli/compress_fragment_two_pass.go @@ -0,0 +1,773 @@ +package brotli + +import "encoding/binary" + +/* Copyright 2015 Google Inc. All Rights Reserved. + + Distributed under MIT license. + See file LICENSE for detail or copy at https://opensource.org/licenses/MIT +*/ + +/* Function for fast encoding of an input fragment, independently from the input + history. This function uses two-pass processing: in the first pass we save + the found backward matches and literal bytes into a buffer, and in the + second pass we emit them into the bit stream using prefix codes built based + on the actual command and literal byte histograms. */ + +const kCompressFragmentTwoPassBlockSize uint = 1 << 17 + +func hash1(p []byte, shift uint, length uint) uint32 { + var h uint64 = (binary.LittleEndian.Uint64(p) << ((8 - length) * 8)) * uint64(kHashMul32) + return uint32(h >> shift) +} + +func hashBytesAtOffset(v uint64, offset uint, shift uint, length uint) uint32 { + assert(offset <= 8-length) + { + var h uint64 = ((v >> (8 * offset)) << ((8 - length) * 8)) * uint64(kHashMul32) + return uint32(h >> shift) + } +} + +func isMatch1(p1 []byte, p2 []byte, length uint) bool { + if binary.LittleEndian.Uint32(p1) != binary.LittleEndian.Uint32(p2) { + return false + } + if length == 4 { + return true + } + return p1[4] == p2[4] && p1[5] == p2[5] +} + +/* +Builds a command and distance prefix code (each 64 symbols) into "depth" and + + "bits" based on "histogram" and stores it into the bit stream. +*/ +func buildAndStoreCommandPrefixCode(histogram []uint32, depth []byte, bits []uint16, storage_ix *uint, storage []byte) { + var tree [129]huffmanTree + var cmd_depth = [numCommandSymbols]byte{0} + /* Tree size for building a tree over 64 symbols is 2 * 64 + 1. */ + + var cmd_bits [64]uint16 + createHuffmanTree(histogram, 64, 15, tree[:], depth) + createHuffmanTree(histogram[64:], 64, 14, tree[:], depth[64:]) + + /* We have to jump through a few hoops here in order to compute + the command bits because the symbols are in a different order than in + the full alphabet. This looks complicated, but having the symbols + in this order in the command bits saves a few branches in the Emit* + functions. */ + copy(cmd_depth[:], depth[24:][:24]) + + copy(cmd_depth[24:][:], depth[:8]) + copy(cmd_depth[32:][:], depth[48:][:8]) + copy(cmd_depth[40:][:], depth[8:][:8]) + copy(cmd_depth[48:][:], depth[56:][:8]) + copy(cmd_depth[56:][:], depth[16:][:8]) + convertBitDepthsToSymbols(cmd_depth[:], 64, cmd_bits[:]) + copy(bits, cmd_bits[24:][:8]) + copy(bits[8:], cmd_bits[40:][:8]) + copy(bits[16:], cmd_bits[56:][:8]) + copy(bits[24:], cmd_bits[:24]) + copy(bits[48:], cmd_bits[32:][:8]) + copy(bits[56:], cmd_bits[48:][:8]) + convertBitDepthsToSymbols(depth[64:], 64, bits[64:]) + { + /* Create the bit length array for the full command alphabet. */ + var i uint + for i := 0; i < int(64); i++ { + cmd_depth[i] = 0 + } /* only 64 first values were used */ + copy(cmd_depth[:], depth[24:][:8]) + copy(cmd_depth[64:][:], depth[32:][:8]) + copy(cmd_depth[128:][:], depth[40:][:8]) + copy(cmd_depth[192:][:], depth[48:][:8]) + copy(cmd_depth[384:][:], depth[56:][:8]) + for i = 0; i < 8; i++ { + cmd_depth[128+8*i] = depth[i] + cmd_depth[256+8*i] = depth[8+i] + cmd_depth[448+8*i] = depth[16+i] + } + + storeHuffmanTree(cmd_depth[:], numCommandSymbols, tree[:], storage_ix, storage) + } + + storeHuffmanTree(depth[64:], 64, tree[:], storage_ix, storage) +} + +func emitInsertLen(insertlen uint32, commands *[]uint32) { + if insertlen < 6 { + (*commands)[0] = insertlen + } else if insertlen < 130 { + var tail uint32 = insertlen - 2 + var nbits uint32 = log2FloorNonZero(uint(tail)) - 1 + var prefix uint32 = tail >> nbits + var inscode uint32 = (nbits << 1) + prefix + 2 + var extra uint32 = tail - (prefix << nbits) + (*commands)[0] = inscode | extra<<8 + } else if insertlen < 2114 { + var tail uint32 = insertlen - 66 + var nbits uint32 = log2FloorNonZero(uint(tail)) + var code uint32 = nbits + 10 + var extra uint32 = tail - (1 << nbits) + (*commands)[0] = code | extra<<8 + } else if insertlen < 6210 { + var extra uint32 = insertlen - 2114 + (*commands)[0] = 21 | extra<<8 + } else if insertlen < 22594 { + var extra uint32 = insertlen - 6210 + (*commands)[0] = 22 | extra<<8 + } else { + var extra uint32 = insertlen - 22594 + (*commands)[0] = 23 | extra<<8 + } + + *commands = (*commands)[1:] +} + +func emitCopyLen(copylen uint, commands *[]uint32) { + if copylen < 10 { + (*commands)[0] = uint32(copylen + 38) + } else if copylen < 134 { + var tail uint = copylen - 6 + var nbits uint = uint(log2FloorNonZero(tail) - 1) + var prefix uint = tail >> nbits + var code uint = (nbits << 1) + prefix + 44 + var extra uint = tail - (prefix << nbits) + (*commands)[0] = uint32(code | extra<<8) + } else if copylen < 2118 { + var tail uint = copylen - 70 + var nbits uint = uint(log2FloorNonZero(tail)) + var code uint = nbits + 52 + var extra uint = tail - (uint(1) << nbits) + (*commands)[0] = uint32(code | extra<<8) + } else { + var extra uint = copylen - 2118 + (*commands)[0] = uint32(63 | extra<<8) + } + + *commands = (*commands)[1:] +} + +func emitCopyLenLastDistance(copylen uint, commands *[]uint32) { + if copylen < 12 { + (*commands)[0] = uint32(copylen + 20) + *commands = (*commands)[1:] + } else if copylen < 72 { + var tail uint = copylen - 8 + var nbits uint = uint(log2FloorNonZero(tail) - 1) + var prefix uint = tail >> nbits + var code uint = (nbits << 1) + prefix + 28 + var extra uint = tail - (prefix << nbits) + (*commands)[0] = uint32(code | extra<<8) + *commands = (*commands)[1:] + } else if copylen < 136 { + var tail uint = copylen - 8 + var code uint = (tail >> 5) + 54 + var extra uint = tail & 31 + (*commands)[0] = uint32(code | extra<<8) + *commands = (*commands)[1:] + (*commands)[0] = 64 + *commands = (*commands)[1:] + } else if copylen < 2120 { + var tail uint = copylen - 72 + var nbits uint = uint(log2FloorNonZero(tail)) + var code uint = nbits + 52 + var extra uint = tail - (uint(1) << nbits) + (*commands)[0] = uint32(code | extra<<8) + *commands = (*commands)[1:] + (*commands)[0] = 64 + *commands = (*commands)[1:] + } else { + var extra uint = copylen - 2120 + (*commands)[0] = uint32(63 | extra<<8) + *commands = (*commands)[1:] + (*commands)[0] = 64 + *commands = (*commands)[1:] + } +} + +func emitDistance(distance uint32, commands *[]uint32) { + var d uint32 = distance + 3 + var nbits uint32 = log2FloorNonZero(uint(d)) - 1 + var prefix uint32 = (d >> nbits) & 1 + var offset uint32 = (2 + prefix) << nbits + var distcode uint32 = 2*(nbits-1) + prefix + 80 + var extra uint32 = d - offset + (*commands)[0] = distcode | extra<<8 + *commands = (*commands)[1:] +} + +/* REQUIRES: len <= 1 << 24. */ +func storeMetaBlockHeader(len uint, is_uncompressed bool, storage_ix *uint, storage []byte) { + var nibbles uint = 6 + + /* ISLAST */ + writeBits(1, 0, storage_ix, storage) + + if len <= 1<<16 { + nibbles = 4 + } else if len <= 1<<20 { + nibbles = 5 + } + + writeBits(2, uint64(nibbles)-4, storage_ix, storage) + writeBits(nibbles*4, uint64(len)-1, storage_ix, storage) + + /* ISUNCOMPRESSED */ + writeSingleBit(is_uncompressed, storage_ix, storage) +} + +func storeMetaBlockHeaderBW(len uint, is_uncompressed bool, bw *bitWriter) { + var nibbles uint = 6 + + /* ISLAST */ + bw.writeBits(1, 0) + + if len <= 1<<16 { + nibbles = 4 + } else if len <= 1<<20 { + nibbles = 5 + } + + bw.writeBits(2, uint64(nibbles)-4) + bw.writeBits(nibbles*4, uint64(len)-1) + + /* ISUNCOMPRESSED */ + bw.writeSingleBit(is_uncompressed) +} + +func createCommands(input []byte, block_size uint, input_size uint, base_ip_ptr []byte, table []int, table_bits uint, min_match uint, literals *[]byte, commands *[]uint32) { + var ip int = 0 + var shift uint = 64 - table_bits + var ip_end int = int(block_size) + var base_ip int = -cap(base_ip_ptr) + cap(input) + var next_emit int = 0 + var last_distance int = -1 + /* "ip" is the input pointer. */ + + const kInputMarginBytes uint = windowGap + + /* "next_emit" is a pointer to the first byte that is not covered by a + previous copy. Bytes between "next_emit" and the start of the next copy or + the end of the input will be emitted as literal bytes. */ + if block_size >= kInputMarginBytes { + var len_limit uint = brotli_min_size_t(block_size-min_match, input_size-kInputMarginBytes) + var ip_limit int = int(len_limit) + /* For the last block, we need to keep a 16 bytes margin so that we can be + sure that all distances are at most window size - 16. + For all other blocks, we only need to keep a margin of 5 bytes so that + we don't go over the block size with a copy. */ + + var next_hash uint32 + ip++ + for next_hash = hash1(input[ip:], shift, min_match); ; { + var skip uint32 = 32 + var next_ip int = ip + /* Step 1: Scan forward in the input looking for a 6-byte-long match. + If we get close to exhausting the input then goto emit_remainder. + + Heuristic match skipping: If 32 bytes are scanned with no matches + found, start looking only at every other byte. If 32 more bytes are + scanned, look at every third byte, etc.. When a match is found, + immediately go back to looking at every byte. This is a small loss + (~5% performance, ~0.1% density) for compressible data due to more + bookkeeping, but for non-compressible data (such as JPEG) it's a huge + win since the compressor quickly "realizes" the data is incompressible + and doesn't bother looking for matches everywhere. + + The "skip" variable keeps track of how many bytes there are since the + last match; dividing it by 32 (ie. right-shifting by five) gives the + number of bytes to move ahead for each iteration. */ + + var candidate int + + assert(next_emit < ip) + + trawl: + for { + var hash uint32 = next_hash + var bytes_between_hash_lookups uint32 = skip >> 5 + skip++ + ip = next_ip + assert(hash == hash1(input[ip:], shift, min_match)) + next_ip = int(uint32(ip) + bytes_between_hash_lookups) + if next_ip > ip_limit { + goto emit_remainder + } + + next_hash = hash1(input[next_ip:], shift, min_match) + candidate = ip - last_distance + if isMatch1(input[ip:], base_ip_ptr[candidate-base_ip:], min_match) { + if candidate < ip { + table[hash] = int(ip - base_ip) + break + } + } + + candidate = base_ip + table[hash] + assert(candidate >= base_ip) + assert(candidate < ip) + + table[hash] = int(ip - base_ip) + if isMatch1(input[ip:], base_ip_ptr[candidate-base_ip:], min_match) { + break + } + } + + /* Check copy distance. If candidate is not feasible, continue search. + Checking is done outside of hot loop to reduce overhead. */ + if ip-candidate > maxDistance_compress_fragment { + goto trawl + } + + /* Step 2: Emit the found match together with the literal bytes from + "next_emit", and then see if we can find a next match immediately + afterwards. Repeat until we find no match for the input + without emitting some literal bytes. */ + { + var base int = ip + /* > 0 */ + var matched uint = min_match + findMatchLengthWithLimit(base_ip_ptr[uint(candidate-base_ip)+min_match:], input[uint(ip)+min_match:], uint(ip_end-ip)-min_match) + var distance int = int(base - candidate) + /* We have a 6-byte match at ip, and we need to emit bytes in + [next_emit, ip). */ + + var insert int = int(base - next_emit) + ip += int(matched) + emitInsertLen(uint32(insert), commands) + copy(*literals, input[next_emit:][:uint(insert)]) + *literals = (*literals)[insert:] + if distance == last_distance { + (*commands)[0] = 64 + *commands = (*commands)[1:] + } else { + emitDistance(uint32(distance), commands) + last_distance = distance + } + + emitCopyLenLastDistance(matched, commands) + + next_emit = ip + if ip >= ip_limit { + goto emit_remainder + } + { + var input_bytes uint64 + var cur_hash uint32 + /* We could immediately start working at ip now, but to improve + compression we first update "table" with the hashes of some + positions within the last copy. */ + + var prev_hash uint32 + if min_match == 4 { + input_bytes = binary.LittleEndian.Uint64(input[ip-3:]) + cur_hash = hashBytesAtOffset(input_bytes, 3, shift, min_match) + prev_hash = hashBytesAtOffset(input_bytes, 0, shift, min_match) + table[prev_hash] = int(ip - base_ip - 3) + prev_hash = hashBytesAtOffset(input_bytes, 1, shift, min_match) + table[prev_hash] = int(ip - base_ip - 2) + prev_hash = hashBytesAtOffset(input_bytes, 0, shift, min_match) + table[prev_hash] = int(ip - base_ip - 1) + } else { + input_bytes = binary.LittleEndian.Uint64(input[ip-5:]) + prev_hash = hashBytesAtOffset(input_bytes, 0, shift, min_match) + table[prev_hash] = int(ip - base_ip - 5) + prev_hash = hashBytesAtOffset(input_bytes, 1, shift, min_match) + table[prev_hash] = int(ip - base_ip - 4) + prev_hash = hashBytesAtOffset(input_bytes, 2, shift, min_match) + table[prev_hash] = int(ip - base_ip - 3) + input_bytes = binary.LittleEndian.Uint64(input[ip-2:]) + cur_hash = hashBytesAtOffset(input_bytes, 2, shift, min_match) + prev_hash = hashBytesAtOffset(input_bytes, 0, shift, min_match) + table[prev_hash] = int(ip - base_ip - 2) + prev_hash = hashBytesAtOffset(input_bytes, 1, shift, min_match) + table[prev_hash] = int(ip - base_ip - 1) + } + + candidate = base_ip + table[cur_hash] + table[cur_hash] = int(ip - base_ip) + } + } + + for ip-candidate <= maxDistance_compress_fragment && isMatch1(input[ip:], base_ip_ptr[candidate-base_ip:], min_match) { + var base int = ip + /* We have a 6-byte match at ip, and no need to emit any + literal bytes prior to ip. */ + + var matched uint = min_match + findMatchLengthWithLimit(base_ip_ptr[uint(candidate-base_ip)+min_match:], input[uint(ip)+min_match:], uint(ip_end-ip)-min_match) + ip += int(matched) + last_distance = int(base - candidate) /* > 0 */ + emitCopyLen(matched, commands) + emitDistance(uint32(last_distance), commands) + + next_emit = ip + if ip >= ip_limit { + goto emit_remainder + } + { + var input_bytes uint64 + var cur_hash uint32 + /* We could immediately start working at ip now, but to improve + compression we first update "table" with the hashes of some + positions within the last copy. */ + + var prev_hash uint32 + if min_match == 4 { + input_bytes = binary.LittleEndian.Uint64(input[ip-3:]) + cur_hash = hashBytesAtOffset(input_bytes, 3, shift, min_match) + prev_hash = hashBytesAtOffset(input_bytes, 0, shift, min_match) + table[prev_hash] = int(ip - base_ip - 3) + prev_hash = hashBytesAtOffset(input_bytes, 1, shift, min_match) + table[prev_hash] = int(ip - base_ip - 2) + prev_hash = hashBytesAtOffset(input_bytes, 2, shift, min_match) + table[prev_hash] = int(ip - base_ip - 1) + } else { + input_bytes = binary.LittleEndian.Uint64(input[ip-5:]) + prev_hash = hashBytesAtOffset(input_bytes, 0, shift, min_match) + table[prev_hash] = int(ip - base_ip - 5) + prev_hash = hashBytesAtOffset(input_bytes, 1, shift, min_match) + table[prev_hash] = int(ip - base_ip - 4) + prev_hash = hashBytesAtOffset(input_bytes, 2, shift, min_match) + table[prev_hash] = int(ip - base_ip - 3) + input_bytes = binary.LittleEndian.Uint64(input[ip-2:]) + cur_hash = hashBytesAtOffset(input_bytes, 2, shift, min_match) + prev_hash = hashBytesAtOffset(input_bytes, 0, shift, min_match) + table[prev_hash] = int(ip - base_ip - 2) + prev_hash = hashBytesAtOffset(input_bytes, 1, shift, min_match) + table[prev_hash] = int(ip - base_ip - 1) + } + + candidate = base_ip + table[cur_hash] + table[cur_hash] = int(ip - base_ip) + } + } + + ip++ + next_hash = hash1(input[ip:], shift, min_match) + } + } + +emit_remainder: + assert(next_emit <= ip_end) + + /* Emit the remaining bytes as literals. */ + if next_emit < ip_end { + var insert uint32 = uint32(ip_end - next_emit) + emitInsertLen(insert, commands) + copy(*literals, input[next_emit:][:insert]) + *literals = (*literals)[insert:] + } +} + +var storeCommands_kNumExtraBits = [128]uint32{ + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 2, + 2, + 3, + 3, + 4, + 4, + 5, + 5, + 6, + 7, + 8, + 9, + 10, + 12, + 14, + 24, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 2, + 2, + 3, + 3, + 4, + 4, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 2, + 2, + 3, + 3, + 4, + 4, + 5, + 5, + 6, + 7, + 8, + 9, + 10, + 24, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 2, + 2, + 3, + 3, + 4, + 4, + 5, + 5, + 6, + 6, + 7, + 7, + 8, + 8, + 9, + 9, + 10, + 10, + 11, + 11, + 12, + 12, + 13, + 13, + 14, + 14, + 15, + 15, + 16, + 16, + 17, + 17, + 18, + 18, + 19, + 19, + 20, + 20, + 21, + 21, + 22, + 22, + 23, + 23, + 24, + 24, +} +var storeCommands_kInsertOffset = [24]uint32{ + 0, + 1, + 2, + 3, + 4, + 5, + 6, + 8, + 10, + 14, + 18, + 26, + 34, + 50, + 66, + 98, + 130, + 194, + 322, + 578, + 1090, + 2114, + 6210, + 22594, +} + +func storeCommands(literals []byte, num_literals uint, commands []uint32, num_commands uint, storage_ix *uint, storage []byte) { + var lit_depths [256]byte + var lit_bits [256]uint16 + var lit_histo = [256]uint32{0} + var cmd_depths = [128]byte{0} + var cmd_bits = [128]uint16{0} + var cmd_histo = [128]uint32{0} + var i uint + for i = 0; i < num_literals; i++ { + lit_histo[literals[i]]++ + } + + buildAndStoreHuffmanTreeFast(lit_histo[:], num_literals, /* max_bits = */ + 8, lit_depths[:], lit_bits[:], storage_ix, storage) + + for i = 0; i < num_commands; i++ { + var code uint32 = commands[i] & 0xFF + assert(code < 128) + cmd_histo[code]++ + } + + cmd_histo[1] += 1 + cmd_histo[2] += 1 + cmd_histo[64] += 1 + cmd_histo[84] += 1 + buildAndStoreCommandPrefixCode(cmd_histo[:], cmd_depths[:], cmd_bits[:], storage_ix, storage) + + for i = 0; i < num_commands; i++ { + var cmd uint32 = commands[i] + var code uint32 = cmd & 0xFF + var extra uint32 = cmd >> 8 + assert(code < 128) + writeBits(uint(cmd_depths[code]), uint64(cmd_bits[code]), storage_ix, storage) + writeBits(uint(storeCommands_kNumExtraBits[code]), uint64(extra), storage_ix, storage) + if code < 24 { + var insert uint32 = storeCommands_kInsertOffset[code] + extra + var j uint32 + for j = 0; j < insert; j++ { + var lit byte = literals[0] + writeBits(uint(lit_depths[lit]), uint64(lit_bits[lit]), storage_ix, storage) + literals = literals[1:] + } + } + } +} + +/* Acceptable loss for uncompressible speedup is 2% */ +const minRatio = 0.98 + +const sampleRate = 43 + +func shouldCompress(input []byte, input_size uint, num_literals uint) bool { + var corpus_size float64 = float64(input_size) + if float64(num_literals) < minRatio*corpus_size { + return true + } else { + var literal_histo = [256]uint32{0} + var max_total_bit_cost float64 = corpus_size * 8 * minRatio / sampleRate + var i uint + for i = 0; i < input_size; i += sampleRate { + literal_histo[input[i]]++ + } + + return bitsEntropy(literal_histo[:], 256) < max_total_bit_cost + } +} + +func rewindBitPosition(new_storage_ix uint, storage_ix *uint, storage []byte) { + var bitpos uint = new_storage_ix & 7 + var mask uint = (1 << bitpos) - 1 + storage[new_storage_ix>>3] &= byte(mask) + *storage_ix = new_storage_ix +} + +func emitUncompressedMetaBlock(input []byte, input_size uint, storage_ix *uint, storage []byte) { + storeMetaBlockHeader(input_size, true, storage_ix, storage) + *storage_ix = (*storage_ix + 7) &^ 7 + copy(storage[*storage_ix>>3:], input[:input_size]) + *storage_ix += input_size << 3 + storage[*storage_ix>>3] = 0 +} + +func compressFragmentTwoPassImpl(input []byte, input_size uint, is_last bool, command_buf []uint32, literal_buf []byte, table []int, table_bits uint, min_match uint, storage_ix *uint, storage []byte) { + /* Save the start of the first block for position and distance computations. + */ + var base_ip []byte = input + + for input_size > 0 { + var block_size uint = brotli_min_size_t(input_size, kCompressFragmentTwoPassBlockSize) + var commands []uint32 = command_buf + var literals []byte = literal_buf + var num_literals uint + createCommands(input, block_size, input_size, base_ip, table, table_bits, min_match, &literals, &commands) + num_literals = uint(-cap(literals) + cap(literal_buf)) + if shouldCompress(input, block_size, num_literals) { + var num_commands uint = uint(-cap(commands) + cap(command_buf)) + storeMetaBlockHeader(block_size, false, storage_ix, storage) + + /* No block splits, no contexts. */ + writeBits(13, 0, storage_ix, storage) + + storeCommands(literal_buf, num_literals, command_buf, num_commands, storage_ix, storage) + } else { + /* Since we did not find many backward references and the entropy of + the data is close to 8 bits, we can simply emit an uncompressed block. + This makes compression speed of uncompressible data about 3x faster. */ + emitUncompressedMetaBlock(input, block_size, storage_ix, storage) + } + + input = input[block_size:] + input_size -= block_size + } +} + +/* +Compresses "input" string to the "*storage" buffer as one or more complete + + meta-blocks, and updates the "*storage_ix" bit position. + + If "is_last" is 1, emits an additional empty last meta-block. + + REQUIRES: "input_size" is greater than zero, or "is_last" is 1. + REQUIRES: "input_size" is less or equal to maximal metablock size (1 << 24). + REQUIRES: "command_buf" and "literal_buf" point to at least + kCompressFragmentTwoPassBlockSize long arrays. + REQUIRES: All elements in "table[0..table_size-1]" are initialized to zero. + REQUIRES: "table_size" is a power of two + OUTPUT: maximal copy distance <= |input_size| + OUTPUT: maximal copy distance <= BROTLI_MAX_BACKWARD_LIMIT(18) +*/ +func compressFragmentTwoPass(input []byte, input_size uint, is_last bool, command_buf []uint32, literal_buf []byte, table []int, table_size uint, storage_ix *uint, storage []byte) { + var initial_storage_ix uint = *storage_ix + var table_bits uint = uint(log2FloorNonZero(table_size)) + var min_match uint + if table_bits <= 15 { + min_match = 4 + } else { + min_match = 6 + } + compressFragmentTwoPassImpl(input, input_size, is_last, command_buf, literal_buf, table, table_bits, min_match, storage_ix, storage) + + /* If output is larger than single uncompressed block, rewrite it. */ + if *storage_ix-initial_storage_ix > 31+(input_size<<3) { + rewindBitPosition(initial_storage_ix, storage_ix, storage) + emitUncompressedMetaBlock(input, input_size, storage_ix, storage) + } + + if is_last { + writeBits(1, 1, storage_ix, storage) /* islast */ + writeBits(1, 1, storage_ix, storage) /* isempty */ + *storage_ix = (*storage_ix + 7) &^ 7 + } +} diff --git a/vendor/github.com/andybalholm/brotli/constants.go b/vendor/github.com/andybalholm/brotli/constants.go new file mode 100644 index 000000000..a880dff78 --- /dev/null +++ b/vendor/github.com/andybalholm/brotli/constants.go @@ -0,0 +1,77 @@ +package brotli + +/* Copyright 2016 Google Inc. All Rights Reserved. + + Distributed under MIT license. + See file LICENSE for detail or copy at https://opensource.org/licenses/MIT +*/ + +/* Specification: 7.3. Encoding of the context map */ +const contextMapMaxRle = 16 + +/* Specification: 2. Compressed representation overview */ +const maxNumberOfBlockTypes = 256 + +/* Specification: 3.3. Alphabet sizes: insert-and-copy length */ +const numLiteralSymbols = 256 + +const numCommandSymbols = 704 + +const numBlockLenSymbols = 26 + +const maxContextMapSymbols = (maxNumberOfBlockTypes + contextMapMaxRle) + +const maxBlockTypeSymbols = (maxNumberOfBlockTypes + 2) + +/* Specification: 3.5. Complex prefix codes */ +const repeatPreviousCodeLength = 16 + +const repeatZeroCodeLength = 17 + +const codeLengthCodes = (repeatZeroCodeLength + 1) + +/* "code length of 8 is repeated" */ +const initialRepeatedCodeLength = 8 + +/* "Large Window Brotli" */ +const largeMaxDistanceBits = 62 + +const largeMinWbits = 10 + +const largeMaxWbits = 30 + +/* Specification: 4. Encoding of distances */ +const numDistanceShortCodes = 16 + +const maxNpostfix = 3 + +const maxNdirect = 120 + +const maxDistanceBits = 24 + +func distanceAlphabetSize(NPOSTFIX uint, NDIRECT uint, MAXNBITS uint) uint { + return numDistanceShortCodes + NDIRECT + uint(MAXNBITS<<(NPOSTFIX+1)) +} + +/* numDistanceSymbols == 1128 */ +const numDistanceSymbols = 1128 + +const maxDistance = 0x3FFFFFC + +const maxAllowedDistance = 0x7FFFFFFC + +/* 7.1. Context modes and context ID lookup for literals */ +/* "context IDs for literals are in the range of 0..63" */ +const literalContextBits = 6 + +/* 7.2. Context ID for distances */ +const distanceContextBits = 2 + +/* 9.1. Format of the Stream Header */ +/* Number of slack bytes for window size. Don't confuse + with BROTLI_NUM_DISTANCE_SHORT_CODES. */ +const windowGap = 16 + +func maxBackwardLimit(W uint) uint { + return (uint(1) << W) - windowGap +} diff --git a/vendor/github.com/andybalholm/brotli/context.go b/vendor/github.com/andybalholm/brotli/context.go new file mode 100644 index 000000000..884ff8a2d --- /dev/null +++ b/vendor/github.com/andybalholm/brotli/context.go @@ -0,0 +1,2176 @@ +package brotli + +/* Lookup table to map the previous two bytes to a context id. + +There are four different context modeling modes defined here: + contextLSB6: context id is the least significant 6 bits of the last byte, + contextMSB6: context id is the most significant 6 bits of the last byte, + contextUTF8: second-order context model tuned for UTF8-encoded text, + contextSigned: second-order context model tuned for signed integers. + +If |p1| and |p2| are the previous two bytes, and |mode| is current context +mode, we calculate the context as: + + context = ContextLut(mode)[p1] | ContextLut(mode)[p2 + 256]. + +For contextUTF8 mode, if the previous two bytes are ASCII characters +(i.e. < 128), this will be equivalent to + + context = 4 * context1(p1) + context2(p2), + +where context1 is based on the previous byte in the following way: + + 0 : non-ASCII control + 1 : \t, \n, \r + 2 : space + 3 : other punctuation + 4 : " ' + 5 : % + 6 : ( < [ { + 7 : ) > ] } + 8 : , ; : + 9 : . + 10 : = + 11 : number + 12 : upper-case vowel + 13 : upper-case consonant + 14 : lower-case vowel + 15 : lower-case consonant + +and context2 is based on the second last byte: + + 0 : control, space + 1 : punctuation + 2 : upper-case letter, number + 3 : lower-case letter + +If the last byte is ASCII, and the second last byte is not (in a valid UTF8 +stream it will be a continuation byte, value between 128 and 191), the +context is the same as if the second last byte was an ASCII control or space. + +If the last byte is a UTF8 lead byte (value >= 192), then the next byte will +be a continuation byte and the context id is 2 or 3 depending on the LSB of +the last byte and to a lesser extent on the second last byte if it is ASCII. + +If the last byte is a UTF8 continuation byte, the second last byte can be: + - continuation byte: the next byte is probably ASCII or lead byte (assuming + 4-byte UTF8 characters are rare) and the context id is 0 or 1. + - lead byte (192 - 207): next byte is ASCII or lead byte, context is 0 or 1 + - lead byte (208 - 255): next byte is continuation byte, context is 2 or 3 + +The possible value combinations of the previous two bytes, the range of +context ids and the type of the next byte is summarized in the table below: + +|--------\-----------------------------------------------------------------| +| \ Last byte | +| Second \---------------------------------------------------------------| +| last byte \ ASCII | cont. byte | lead byte | +| \ (0-127) | (128-191) | (192-) | +|=============|===================|=====================|==================| +| ASCII | next: ASCII/lead | not valid | next: cont. | +| (0-127) | context: 4 - 63 | | context: 2 - 3 | +|-------------|-------------------|---------------------|------------------| +| cont. byte | next: ASCII/lead | next: ASCII/lead | next: cont. | +| (128-191) | context: 4 - 63 | context: 0 - 1 | context: 2 - 3 | +|-------------|-------------------|---------------------|------------------| +| lead byte | not valid | next: ASCII/lead | not valid | +| (192-207) | | context: 0 - 1 | | +|-------------|-------------------|---------------------|------------------| +| lead byte | not valid | next: cont. | not valid | +| (208-) | | context: 2 - 3 | | +|-------------|-------------------|---------------------|------------------| +*/ + +const ( + contextLSB6 = 0 + contextMSB6 = 1 + contextUTF8 = 2 + contextSigned = 3 +) + +/* Common context lookup table for all context modes. */ +var kContextLookup = [2048]byte{ + /* CONTEXT_LSB6, last byte. */ + 0, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 28, + 29, + 30, + 31, + 32, + 33, + 34, + 35, + 36, + 37, + 38, + 39, + 40, + 41, + 42, + 43, + 44, + 45, + 46, + 47, + 48, + 49, + 50, + 51, + 52, + 53, + 54, + 55, + 56, + 57, + 58, + 59, + 60, + 61, + 62, + 63, + 0, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 28, + 29, + 30, + 31, + 32, + 33, + 34, + 35, + 36, + 37, + 38, + 39, + 40, + 41, + 42, + 43, + 44, + 45, + 46, + 47, + 48, + 49, + 50, + 51, + 52, + 53, + 54, + 55, + 56, + 57, + 58, + 59, + 60, + 61, + 62, + 63, + 0, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 28, + 29, + 30, + 31, + 32, + 33, + 34, + 35, + 36, + 37, + 38, + 39, + 40, + 41, + 42, + 43, + 44, + 45, + 46, + 47, + 48, + 49, + 50, + 51, + 52, + 53, + 54, + 55, + 56, + 57, + 58, + 59, + 60, + 61, + 62, + 63, + 0, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 28, + 29, + 30, + 31, + 32, + 33, + 34, + 35, + 36, + 37, + 38, + 39, + 40, + 41, + 42, + 43, + 44, + 45, + 46, + 47, + 48, + 49, + 50, + 51, + 52, + 53, + 54, + 55, + 56, + 57, + 58, + 59, + 60, + 61, + 62, + 63, + + /* CONTEXT_LSB6, second last byte, */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* CONTEXT_MSB6, last byte. */ + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 6, + 6, + 6, + 6, + 7, + 7, + 7, + 7, + 8, + 8, + 8, + 8, + 9, + 9, + 9, + 9, + 10, + 10, + 10, + 10, + 11, + 11, + 11, + 11, + 12, + 12, + 12, + 12, + 13, + 13, + 13, + 13, + 14, + 14, + 14, + 14, + 15, + 15, + 15, + 15, + 16, + 16, + 16, + 16, + 17, + 17, + 17, + 17, + 18, + 18, + 18, + 18, + 19, + 19, + 19, + 19, + 20, + 20, + 20, + 20, + 21, + 21, + 21, + 21, + 22, + 22, + 22, + 22, + 23, + 23, + 23, + 23, + 24, + 24, + 24, + 24, + 25, + 25, + 25, + 25, + 26, + 26, + 26, + 26, + 27, + 27, + 27, + 27, + 28, + 28, + 28, + 28, + 29, + 29, + 29, + 29, + 30, + 30, + 30, + 30, + 31, + 31, + 31, + 31, + 32, + 32, + 32, + 32, + 33, + 33, + 33, + 33, + 34, + 34, + 34, + 34, + 35, + 35, + 35, + 35, + 36, + 36, + 36, + 36, + 37, + 37, + 37, + 37, + 38, + 38, + 38, + 38, + 39, + 39, + 39, + 39, + 40, + 40, + 40, + 40, + 41, + 41, + 41, + 41, + 42, + 42, + 42, + 42, + 43, + 43, + 43, + 43, + 44, + 44, + 44, + 44, + 45, + 45, + 45, + 45, + 46, + 46, + 46, + 46, + 47, + 47, + 47, + 47, + 48, + 48, + 48, + 48, + 49, + 49, + 49, + 49, + 50, + 50, + 50, + 50, + 51, + 51, + 51, + 51, + 52, + 52, + 52, + 52, + 53, + 53, + 53, + 53, + 54, + 54, + 54, + 54, + 55, + 55, + 55, + 55, + 56, + 56, + 56, + 56, + 57, + 57, + 57, + 57, + 58, + 58, + 58, + 58, + 59, + 59, + 59, + 59, + 60, + 60, + 60, + 60, + 61, + 61, + 61, + 61, + 62, + 62, + 62, + 62, + 63, + 63, + 63, + 63, + + /* CONTEXT_MSB6, second last byte, */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* CONTEXT_UTF8, last byte. */ + /* ASCII range. */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 4, + 4, + 0, + 0, + 4, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 8, + 12, + 16, + 12, + 12, + 20, + 12, + 16, + 24, + 28, + 12, + 12, + 32, + 12, + 36, + 12, + 44, + 44, + 44, + 44, + 44, + 44, + 44, + 44, + 44, + 44, + 32, + 32, + 24, + 40, + 28, + 12, + 12, + 48, + 52, + 52, + 52, + 48, + 52, + 52, + 52, + 48, + 52, + 52, + 52, + 52, + 52, + 48, + 52, + 52, + 52, + 52, + 52, + 48, + 52, + 52, + 52, + 52, + 52, + 24, + 12, + 28, + 12, + 12, + 12, + 56, + 60, + 60, + 60, + 56, + 60, + 60, + 60, + 56, + 60, + 60, + 60, + 60, + 60, + 56, + 60, + 60, + 60, + 60, + 60, + 56, + 60, + 60, + 60, + 60, + 60, + 24, + 12, + 28, + 12, + 0, + + /* UTF8 continuation byte range. */ + 0, + 1, + 0, + 1, + 0, + 1, + 0, + 1, + 0, + 1, + 0, + 1, + 0, + 1, + 0, + 1, + 0, + 1, + 0, + 1, + 0, + 1, + 0, + 1, + 0, + 1, + 0, + 1, + 0, + 1, + 0, + 1, + 0, + 1, + 0, + 1, + 0, + 1, + 0, + 1, + 0, + 1, + 0, + 1, + 0, + 1, + 0, + 1, + 0, + 1, + 0, + 1, + 0, + 1, + 0, + 1, + 0, + 1, + 0, + 1, + 0, + 1, + 0, + 1, + + /* UTF8 lead byte range. */ + 2, + 3, + 2, + 3, + 2, + 3, + 2, + 3, + 2, + 3, + 2, + 3, + 2, + 3, + 2, + 3, + 2, + 3, + 2, + 3, + 2, + 3, + 2, + 3, + 2, + 3, + 2, + 3, + 2, + 3, + 2, + 3, + 2, + 3, + 2, + 3, + 2, + 3, + 2, + 3, + 2, + 3, + 2, + 3, + 2, + 3, + 2, + 3, + 2, + 3, + 2, + 3, + 2, + 3, + 2, + 3, + 2, + 3, + 2, + 3, + 2, + 3, + 2, + 3, + + /* CONTEXT_UTF8 second last byte. */ + /* ASCII range. */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 1, + 1, + 0, + + /* UTF8 continuation byte range. */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* UTF8 lead byte range. */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + + /* CONTEXT_SIGNED, last byte, same as the above values shifted by 3 bits. */ + 0, + 8, + 8, + 8, + 8, + 8, + 8, + 8, + 8, + 8, + 8, + 8, + 8, + 8, + 8, + 8, + 16, + 16, + 16, + 16, + 16, + 16, + 16, + 16, + 16, + 16, + 16, + 16, + 16, + 16, + 16, + 16, + 16, + 16, + 16, + 16, + 16, + 16, + 16, + 16, + 16, + 16, + 16, + 16, + 16, + 16, + 16, + 16, + 16, + 16, + 16, + 16, + 16, + 16, + 16, + 16, + 16, + 16, + 16, + 16, + 16, + 16, + 16, + 16, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 32, + 32, + 32, + 32, + 32, + 32, + 32, + 32, + 32, + 32, + 32, + 32, + 32, + 32, + 32, + 32, + 32, + 32, + 32, + 32, + 32, + 32, + 32, + 32, + 32, + 32, + 32, + 32, + 32, + 32, + 32, + 32, + 32, + 32, + 32, + 32, + 32, + 32, + 32, + 32, + 32, + 32, + 32, + 32, + 32, + 32, + 32, + 32, + 32, + 32, + 32, + 32, + 32, + 32, + 32, + 32, + 32, + 32, + 32, + 32, + 32, + 32, + 32, + 32, + 40, + 40, + 40, + 40, + 40, + 40, + 40, + 40, + 40, + 40, + 40, + 40, + 40, + 40, + 40, + 40, + 40, + 40, + 40, + 40, + 40, + 40, + 40, + 40, + 40, + 40, + 40, + 40, + 40, + 40, + 40, + 40, + 40, + 40, + 40, + 40, + 40, + 40, + 40, + 40, + 40, + 40, + 40, + 40, + 40, + 40, + 40, + 40, + 48, + 48, + 48, + 48, + 48, + 48, + 48, + 48, + 48, + 48, + 48, + 48, + 48, + 48, + 48, + 56, + + /* CONTEXT_SIGNED, second last byte. */ + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 7, +} + +type contextLUT []byte + +func getContextLUT(mode int) contextLUT { + return kContextLookup[mode<<9:] +} + +func getContext(p1 byte, p2 byte, lut contextLUT) byte { + return lut[p1] | lut[256+int(p2)] +} diff --git a/vendor/github.com/andybalholm/brotli/decode.go b/vendor/github.com/andybalholm/brotli/decode.go new file mode 100644 index 000000000..9d9513b7c --- /dev/null +++ b/vendor/github.com/andybalholm/brotli/decode.go @@ -0,0 +1,2581 @@ +package brotli + +/* Copyright 2013 Google Inc. All Rights Reserved. + + Distributed under MIT license. + See file LICENSE for detail or copy at https://opensource.org/licenses/MIT +*/ + +const ( + decoderResultError = 0 + decoderResultSuccess = 1 + decoderResultNeedsMoreInput = 2 + decoderResultNeedsMoreOutput = 3 +) + +/** + * Error code for detailed logging / production debugging. + * + * See ::BrotliDecoderGetErrorCode and ::BROTLI_LAST_ERROR_CODE. + */ +const ( + decoderNoError = 0 + decoderSuccess = 1 + decoderNeedsMoreInput = 2 + decoderNeedsMoreOutput = 3 + decoderErrorFormatExuberantNibble = -1 + decoderErrorFormatReserved = -2 + decoderErrorFormatExuberantMetaNibble = -3 + decoderErrorFormatSimpleHuffmanAlphabet = -4 + decoderErrorFormatSimpleHuffmanSame = -5 + decoderErrorFormatClSpace = -6 + decoderErrorFormatHuffmanSpace = -7 + decoderErrorFormatContextMapRepeat = -8 + decoderErrorFormatBlockLength1 = -9 + decoderErrorFormatBlockLength2 = -10 + decoderErrorFormatTransform = -11 + decoderErrorFormatDictionary = -12 + decoderErrorFormatWindowBits = -13 + decoderErrorFormatPadding1 = -14 + decoderErrorFormatPadding2 = -15 + decoderErrorFormatDistance = -16 + decoderErrorDictionaryNotSet = -19 + decoderErrorInvalidArguments = -20 + decoderErrorAllocContextModes = -21 + decoderErrorAllocTreeGroups = -22 + decoderErrorAllocContextMap = -25 + decoderErrorAllocRingBuffer1 = -26 + decoderErrorAllocRingBuffer2 = -27 + decoderErrorAllocBlockTypeTrees = -30 + decoderErrorUnreachable = -31 +) + +const huffmanTableBits = 8 + +const huffmanTableMask = 0xFF + +/* We need the slack region for the following reasons: + - doing up to two 16-byte copies for fast backward copying + - inserting transformed dictionary word (5 prefix + 24 base + 8 suffix) */ +const kRingBufferWriteAheadSlack uint32 = 42 + +var kCodeLengthCodeOrder = [codeLengthCodes]byte{1, 2, 3, 4, 0, 5, 17, 6, 16, 7, 8, 9, 10, 11, 12, 13, 14, 15} + +/* Static prefix code for the complex code length code lengths. */ +var kCodeLengthPrefixLength = [16]byte{2, 2, 2, 3, 2, 2, 2, 4, 2, 2, 2, 3, 2, 2, 2, 4} + +var kCodeLengthPrefixValue = [16]byte{0, 4, 3, 2, 0, 4, 3, 1, 0, 4, 3, 2, 0, 4, 3, 5} + +/* Saves error code and converts it to BrotliDecoderResult. */ +func saveErrorCode(s *Reader, e int) int { + s.error_code = int(e) + switch e { + case decoderSuccess: + return decoderResultSuccess + + case decoderNeedsMoreInput: + return decoderResultNeedsMoreInput + + case decoderNeedsMoreOutput: + return decoderResultNeedsMoreOutput + + default: + return decoderResultError + } +} + +/* Decodes WBITS by reading 1 - 7 bits, or 0x11 for "Large Window Brotli". + Precondition: bit-reader accumulator has at least 8 bits. */ +func decodeWindowBits(s *Reader, br *bitReader) int { + var n uint32 + var large_window bool = s.large_window + s.large_window = false + takeBits(br, 1, &n) + if n == 0 { + s.window_bits = 16 + return decoderSuccess + } + + takeBits(br, 3, &n) + if n != 0 { + s.window_bits = 17 + n + return decoderSuccess + } + + takeBits(br, 3, &n) + if n == 1 { + if large_window { + takeBits(br, 1, &n) + if n == 1 { + return decoderErrorFormatWindowBits + } + + s.large_window = true + return decoderSuccess + } else { + return decoderErrorFormatWindowBits + } + } + + if n != 0 { + s.window_bits = 8 + n + return decoderSuccess + } + + s.window_bits = 17 + return decoderSuccess +} + +/* Decodes a number in the range [0..255], by reading 1 - 11 bits. */ +func decodeVarLenUint8(s *Reader, br *bitReader, value *uint32) int { + var bits uint32 + switch s.substate_decode_uint8 { + case stateDecodeUint8None: + if !safeReadBits(br, 1, &bits) { + return decoderNeedsMoreInput + } + + if bits == 0 { + *value = 0 + return decoderSuccess + } + fallthrough + + /* Fall through. */ + case stateDecodeUint8Short: + if !safeReadBits(br, 3, &bits) { + s.substate_decode_uint8 = stateDecodeUint8Short + return decoderNeedsMoreInput + } + + if bits == 0 { + *value = 1 + s.substate_decode_uint8 = stateDecodeUint8None + return decoderSuccess + } + + /* Use output value as a temporary storage. It MUST be persisted. */ + *value = bits + fallthrough + + /* Fall through. */ + case stateDecodeUint8Long: + if !safeReadBits(br, *value, &bits) { + s.substate_decode_uint8 = stateDecodeUint8Long + return decoderNeedsMoreInput + } + + *value = (1 << *value) + bits + s.substate_decode_uint8 = stateDecodeUint8None + return decoderSuccess + + default: + return decoderErrorUnreachable + } +} + +/* Decodes a metablock length and flags by reading 2 - 31 bits. */ +func decodeMetaBlockLength(s *Reader, br *bitReader) int { + var bits uint32 + var i int + for { + switch s.substate_metablock_header { + case stateMetablockHeaderNone: + if !safeReadBits(br, 1, &bits) { + return decoderNeedsMoreInput + } + + if bits != 0 { + s.is_last_metablock = 1 + } else { + s.is_last_metablock = 0 + } + s.meta_block_remaining_len = 0 + s.is_uncompressed = 0 + s.is_metadata = 0 + if s.is_last_metablock == 0 { + s.substate_metablock_header = stateMetablockHeaderNibbles + break + } + + s.substate_metablock_header = stateMetablockHeaderEmpty + fallthrough + + /* Fall through. */ + case stateMetablockHeaderEmpty: + if !safeReadBits(br, 1, &bits) { + return decoderNeedsMoreInput + } + + if bits != 0 { + s.substate_metablock_header = stateMetablockHeaderNone + return decoderSuccess + } + + s.substate_metablock_header = stateMetablockHeaderNibbles + fallthrough + + /* Fall through. */ + case stateMetablockHeaderNibbles: + if !safeReadBits(br, 2, &bits) { + return decoderNeedsMoreInput + } + + s.size_nibbles = uint(byte(bits + 4)) + s.loop_counter = 0 + if bits == 3 { + s.is_metadata = 1 + s.substate_metablock_header = stateMetablockHeaderReserved + break + } + + s.substate_metablock_header = stateMetablockHeaderSize + fallthrough + + /* Fall through. */ + case stateMetablockHeaderSize: + i = s.loop_counter + + for ; i < int(s.size_nibbles); i++ { + if !safeReadBits(br, 4, &bits) { + s.loop_counter = i + return decoderNeedsMoreInput + } + + if uint(i+1) == s.size_nibbles && s.size_nibbles > 4 && bits == 0 { + return decoderErrorFormatExuberantNibble + } + + s.meta_block_remaining_len |= int(bits << uint(i*4)) + } + + s.substate_metablock_header = stateMetablockHeaderUncompressed + fallthrough + + /* Fall through. */ + case stateMetablockHeaderUncompressed: + if s.is_last_metablock == 0 { + if !safeReadBits(br, 1, &bits) { + return decoderNeedsMoreInput + } + + if bits != 0 { + s.is_uncompressed = 1 + } else { + s.is_uncompressed = 0 + } + } + + s.meta_block_remaining_len++ + s.substate_metablock_header = stateMetablockHeaderNone + return decoderSuccess + + case stateMetablockHeaderReserved: + if !safeReadBits(br, 1, &bits) { + return decoderNeedsMoreInput + } + + if bits != 0 { + return decoderErrorFormatReserved + } + + s.substate_metablock_header = stateMetablockHeaderBytes + fallthrough + + /* Fall through. */ + case stateMetablockHeaderBytes: + if !safeReadBits(br, 2, &bits) { + return decoderNeedsMoreInput + } + + if bits == 0 { + s.substate_metablock_header = stateMetablockHeaderNone + return decoderSuccess + } + + s.size_nibbles = uint(byte(bits)) + s.substate_metablock_header = stateMetablockHeaderMetadata + fallthrough + + /* Fall through. */ + case stateMetablockHeaderMetadata: + i = s.loop_counter + + for ; i < int(s.size_nibbles); i++ { + if !safeReadBits(br, 8, &bits) { + s.loop_counter = i + return decoderNeedsMoreInput + } + + if uint(i+1) == s.size_nibbles && s.size_nibbles > 1 && bits == 0 { + return decoderErrorFormatExuberantMetaNibble + } + + s.meta_block_remaining_len |= int(bits << uint(i*8)) + } + + s.meta_block_remaining_len++ + s.substate_metablock_header = stateMetablockHeaderNone + return decoderSuccess + + default: + return decoderErrorUnreachable + } + } +} + +/* Decodes the Huffman code. + This method doesn't read data from the bit reader, BUT drops the amount of + bits that correspond to the decoded symbol. + bits MUST contain at least 15 (BROTLI_HUFFMAN_MAX_CODE_LENGTH) valid bits. */ +func decodeSymbol(bits uint32, table []huffmanCode, br *bitReader) uint32 { + table = table[bits&huffmanTableMask:] + if table[0].bits > huffmanTableBits { + var nbits uint32 = uint32(table[0].bits) - huffmanTableBits + dropBits(br, huffmanTableBits) + table = table[uint32(table[0].value)+((bits>>huffmanTableBits)&bitMask(nbits)):] + } + + dropBits(br, uint32(table[0].bits)) + return uint32(table[0].value) +} + +/* Reads and decodes the next Huffman code from bit-stream. + This method peeks 16 bits of input and drops 0 - 15 of them. */ +func readSymbol(table []huffmanCode, br *bitReader) uint32 { + return decodeSymbol(get16BitsUnmasked(br), table, br) +} + +/* Same as DecodeSymbol, but it is known that there is less than 15 bits of + input are currently available. */ +func safeDecodeSymbol(table []huffmanCode, br *bitReader, result *uint32) bool { + var val uint32 + var available_bits uint32 = getAvailableBits(br) + if available_bits == 0 { + if table[0].bits == 0 { + *result = uint32(table[0].value) + return true + } + + return false /* No valid bits at all. */ + } + + val = uint32(getBitsUnmasked(br)) + table = table[val&huffmanTableMask:] + if table[0].bits <= huffmanTableBits { + if uint32(table[0].bits) <= available_bits { + dropBits(br, uint32(table[0].bits)) + *result = uint32(table[0].value) + return true + } else { + return false /* Not enough bits for the first level. */ + } + } + + if available_bits <= huffmanTableBits { + return false /* Not enough bits to move to the second level. */ + } + + /* Speculatively drop HUFFMAN_TABLE_BITS. */ + val = (val & bitMask(uint32(table[0].bits))) >> huffmanTableBits + + available_bits -= huffmanTableBits + table = table[uint32(table[0].value)+val:] + if available_bits < uint32(table[0].bits) { + return false /* Not enough bits for the second level. */ + } + + dropBits(br, huffmanTableBits+uint32(table[0].bits)) + *result = uint32(table[0].value) + return true +} + +func safeReadSymbol(table []huffmanCode, br *bitReader, result *uint32) bool { + var val uint32 + if safeGetBits(br, 15, &val) { + *result = decodeSymbol(val, table, br) + return true + } + + return safeDecodeSymbol(table, br, result) +} + +/* Makes a look-up in first level Huffman table. Peeks 8 bits. */ +func preloadSymbol(safe int, table []huffmanCode, br *bitReader, bits *uint32, value *uint32) { + if safe != 0 { + return + } + + table = table[getBits(br, huffmanTableBits):] + *bits = uint32(table[0].bits) + *value = uint32(table[0].value) +} + +/* Decodes the next Huffman code using data prepared by PreloadSymbol. + Reads 0 - 15 bits. Also peeks 8 following bits. */ +func readPreloadedSymbol(table []huffmanCode, br *bitReader, bits *uint32, value *uint32) uint32 { + var result uint32 = *value + var ext []huffmanCode + if *bits > huffmanTableBits { + var val uint32 = get16BitsUnmasked(br) + ext = table[val&huffmanTableMask:][*value:] + var mask uint32 = bitMask((*bits - huffmanTableBits)) + dropBits(br, huffmanTableBits) + ext = ext[(val>>huffmanTableBits)&mask:] + dropBits(br, uint32(ext[0].bits)) + result = uint32(ext[0].value) + } else { + dropBits(br, *bits) + } + + preloadSymbol(0, table, br, bits, value) + return result +} + +func log2Floor(x uint32) uint32 { + var result uint32 = 0 + for x != 0 { + x >>= 1 + result++ + } + + return result +} + +/* Reads (s->symbol + 1) symbols. + Totally 1..4 symbols are read, 1..11 bits each. + The list of symbols MUST NOT contain duplicates. */ +func readSimpleHuffmanSymbols(alphabet_size uint32, max_symbol uint32, s *Reader) int { + var br *bitReader = &s.br + var max_bits uint32 = log2Floor(alphabet_size - 1) + var i uint32 = s.sub_loop_counter + /* max_bits == 1..11; symbol == 0..3; 1..44 bits will be read. */ + + var num_symbols uint32 = s.symbol + for i <= num_symbols { + var v uint32 + if !safeReadBits(br, max_bits, &v) { + s.sub_loop_counter = i + s.substate_huffman = stateHuffmanSimpleRead + return decoderNeedsMoreInput + } + + if v >= max_symbol { + return decoderErrorFormatSimpleHuffmanAlphabet + } + + s.symbols_lists_array[i] = uint16(v) + i++ + } + + for i = 0; i < num_symbols; i++ { + var k uint32 = i + 1 + for ; k <= num_symbols; k++ { + if s.symbols_lists_array[i] == s.symbols_lists_array[k] { + return decoderErrorFormatSimpleHuffmanSame + } + } + } + + return decoderSuccess +} + +/* Process single decoded symbol code length: + A) reset the repeat variable + B) remember code length (if it is not 0) + C) extend corresponding index-chain + D) reduce the Huffman space + E) update the histogram */ +func processSingleCodeLength(code_len uint32, symbol *uint32, repeat *uint32, space *uint32, prev_code_len *uint32, symbol_lists symbolList, code_length_histo []uint16, next_symbol []int) { + *repeat = 0 + if code_len != 0 { /* code_len == 1..15 */ + symbolListPut(symbol_lists, next_symbol[code_len], uint16(*symbol)) + next_symbol[code_len] = int(*symbol) + *prev_code_len = code_len + *space -= 32768 >> code_len + code_length_histo[code_len]++ + } + + (*symbol)++ +} + +/* Process repeated symbol code length. + A) Check if it is the extension of previous repeat sequence; if the decoded + value is not BROTLI_REPEAT_PREVIOUS_CODE_LENGTH, then it is a new + symbol-skip + B) Update repeat variable + C) Check if operation is feasible (fits alphabet) + D) For each symbol do the same operations as in ProcessSingleCodeLength + + PRECONDITION: code_len == BROTLI_REPEAT_PREVIOUS_CODE_LENGTH or + code_len == BROTLI_REPEAT_ZERO_CODE_LENGTH */ +func processRepeatedCodeLength(code_len uint32, repeat_delta uint32, alphabet_size uint32, symbol *uint32, repeat *uint32, space *uint32, prev_code_len *uint32, repeat_code_len *uint32, symbol_lists symbolList, code_length_histo []uint16, next_symbol []int) { + var old_repeat uint32 /* for BROTLI_REPEAT_ZERO_CODE_LENGTH */ /* for BROTLI_REPEAT_ZERO_CODE_LENGTH */ + var extra_bits uint32 = 3 + var new_len uint32 = 0 + if code_len == repeatPreviousCodeLength { + new_len = *prev_code_len + extra_bits = 2 + } + + if *repeat_code_len != new_len { + *repeat = 0 + *repeat_code_len = new_len + } + + old_repeat = *repeat + if *repeat > 0 { + *repeat -= 2 + *repeat <<= extra_bits + } + + *repeat += repeat_delta + 3 + repeat_delta = *repeat - old_repeat + if *symbol+repeat_delta > alphabet_size { + *symbol = alphabet_size + *space = 0xFFFFF + return + } + + if *repeat_code_len != 0 { + var last uint = uint(*symbol + repeat_delta) + var next int = next_symbol[*repeat_code_len] + for { + symbolListPut(symbol_lists, next, uint16(*symbol)) + next = int(*symbol) + (*symbol)++ + if (*symbol) == uint32(last) { + break + } + } + + next_symbol[*repeat_code_len] = next + *space -= repeat_delta << (15 - *repeat_code_len) + code_length_histo[*repeat_code_len] = uint16(uint32(code_length_histo[*repeat_code_len]) + repeat_delta) + } else { + *symbol += repeat_delta + } +} + +/* Reads and decodes symbol codelengths. */ +func readSymbolCodeLengths(alphabet_size uint32, s *Reader) int { + var br *bitReader = &s.br + var symbol uint32 = s.symbol + var repeat uint32 = s.repeat + var space uint32 = s.space + var prev_code_len uint32 = s.prev_code_len + var repeat_code_len uint32 = s.repeat_code_len + var symbol_lists symbolList = s.symbol_lists + var code_length_histo []uint16 = s.code_length_histo[:] + var next_symbol []int = s.next_symbol[:] + if !warmupBitReader(br) { + return decoderNeedsMoreInput + } + var p []huffmanCode + for symbol < alphabet_size && space > 0 { + p = s.table[:] + var code_len uint32 + if !checkInputAmount(br, shortFillBitWindowRead) { + s.symbol = symbol + s.repeat = repeat + s.prev_code_len = prev_code_len + s.repeat_code_len = repeat_code_len + s.space = space + return decoderNeedsMoreInput + } + + fillBitWindow16(br) + p = p[getBitsUnmasked(br)&uint64(bitMask(huffmanMaxCodeLengthCodeLength)):] + dropBits(br, uint32(p[0].bits)) /* Use 1..5 bits. */ + code_len = uint32(p[0].value) /* code_len == 0..17 */ + if code_len < repeatPreviousCodeLength { + processSingleCodeLength(code_len, &symbol, &repeat, &space, &prev_code_len, symbol_lists, code_length_histo, next_symbol) /* code_len == 16..17, extra_bits == 2..3 */ + } else { + var extra_bits uint32 + if code_len == repeatPreviousCodeLength { + extra_bits = 2 + } else { + extra_bits = 3 + } + var repeat_delta uint32 = uint32(getBitsUnmasked(br)) & bitMask(extra_bits) + dropBits(br, extra_bits) + processRepeatedCodeLength(code_len, repeat_delta, alphabet_size, &symbol, &repeat, &space, &prev_code_len, &repeat_code_len, symbol_lists, code_length_histo, next_symbol) + } + } + + s.space = space + return decoderSuccess +} + +func safeReadSymbolCodeLengths(alphabet_size uint32, s *Reader) int { + var br *bitReader = &s.br + var get_byte bool = false + var p []huffmanCode + for s.symbol < alphabet_size && s.space > 0 { + p = s.table[:] + var code_len uint32 + var available_bits uint32 + var bits uint32 = 0 + if get_byte && !pullByte(br) { + return decoderNeedsMoreInput + } + get_byte = false + available_bits = getAvailableBits(br) + if available_bits != 0 { + bits = uint32(getBitsUnmasked(br)) + } + + p = p[bits&bitMask(huffmanMaxCodeLengthCodeLength):] + if uint32(p[0].bits) > available_bits { + get_byte = true + continue + } + + code_len = uint32(p[0].value) /* code_len == 0..17 */ + if code_len < repeatPreviousCodeLength { + dropBits(br, uint32(p[0].bits)) + processSingleCodeLength(code_len, &s.symbol, &s.repeat, &s.space, &s.prev_code_len, s.symbol_lists, s.code_length_histo[:], s.next_symbol[:]) /* code_len == 16..17, extra_bits == 2..3 */ + } else { + var extra_bits uint32 = code_len - 14 + var repeat_delta uint32 = (bits >> p[0].bits) & bitMask(extra_bits) + if available_bits < uint32(p[0].bits)+extra_bits { + get_byte = true + continue + } + + dropBits(br, uint32(p[0].bits)+extra_bits) + processRepeatedCodeLength(code_len, repeat_delta, alphabet_size, &s.symbol, &s.repeat, &s.space, &s.prev_code_len, &s.repeat_code_len, s.symbol_lists, s.code_length_histo[:], s.next_symbol[:]) + } + } + + return decoderSuccess +} + +/* Reads and decodes 15..18 codes using static prefix code. + Each code is 2..4 bits long. In total 30..72 bits are used. */ +func readCodeLengthCodeLengths(s *Reader) int { + var br *bitReader = &s.br + var num_codes uint32 = s.repeat + var space uint32 = s.space + var i uint32 = s.sub_loop_counter + for ; i < codeLengthCodes; i++ { + var code_len_idx byte = kCodeLengthCodeOrder[i] + var ix uint32 + var v uint32 + if !safeGetBits(br, 4, &ix) { + var available_bits uint32 = getAvailableBits(br) + if available_bits != 0 { + ix = uint32(getBitsUnmasked(br) & 0xF) + } else { + ix = 0 + } + + if uint32(kCodeLengthPrefixLength[ix]) > available_bits { + s.sub_loop_counter = i + s.repeat = num_codes + s.space = space + s.substate_huffman = stateHuffmanComplex + return decoderNeedsMoreInput + } + } + + v = uint32(kCodeLengthPrefixValue[ix]) + dropBits(br, uint32(kCodeLengthPrefixLength[ix])) + s.code_length_code_lengths[code_len_idx] = byte(v) + if v != 0 { + space = space - (32 >> v) + num_codes++ + s.code_length_histo[v]++ + if space-1 >= 32 { + /* space is 0 or wrapped around. */ + break + } + } + } + + if num_codes != 1 && space != 0 { + return decoderErrorFormatClSpace + } + + return decoderSuccess +} + +/* Decodes the Huffman tables. + There are 2 scenarios: + A) Huffman code contains only few symbols (1..4). Those symbols are read + directly; their code lengths are defined by the number of symbols. + For this scenario 4 - 49 bits will be read. + + B) 2-phase decoding: + B.1) Small Huffman table is decoded; it is specified with code lengths + encoded with predefined entropy code. 32 - 74 bits are used. + B.2) Decoded table is used to decode code lengths of symbols in resulting + Huffman table. In worst case 3520 bits are read. */ +func readHuffmanCode(alphabet_size uint32, max_symbol uint32, table []huffmanCode, opt_table_size *uint32, s *Reader) int { + var br *bitReader = &s.br + + /* Unnecessary masking, but might be good for safety. */ + alphabet_size &= 0x7FF + + /* State machine. */ + for { + switch s.substate_huffman { + case stateHuffmanNone: + if !safeReadBits(br, 2, &s.sub_loop_counter) { + return decoderNeedsMoreInput + } + + /* The value is used as follows: + 1 for simple code; + 0 for no skipping, 2 skips 2 code lengths, 3 skips 3 code lengths */ + if s.sub_loop_counter != 1 { + s.space = 32 + s.repeat = 0 /* num_codes */ + var i int + for i = 0; i <= huffmanMaxCodeLengthCodeLength; i++ { + s.code_length_histo[i] = 0 + } + + for i = 0; i < codeLengthCodes; i++ { + s.code_length_code_lengths[i] = 0 + } + + s.substate_huffman = stateHuffmanComplex + continue + } + fallthrough + + /* Read symbols, codes & code lengths directly. */ + case stateHuffmanSimpleSize: + if !safeReadBits(br, 2, &s.symbol) { /* num_symbols */ + s.substate_huffman = stateHuffmanSimpleSize + return decoderNeedsMoreInput + } + + s.sub_loop_counter = 0 + fallthrough + + case stateHuffmanSimpleRead: + { + var result int = readSimpleHuffmanSymbols(alphabet_size, max_symbol, s) + if result != decoderSuccess { + return result + } + } + fallthrough + + case stateHuffmanSimpleBuild: + var table_size uint32 + if s.symbol == 3 { + var bits uint32 + if !safeReadBits(br, 1, &bits) { + s.substate_huffman = stateHuffmanSimpleBuild + return decoderNeedsMoreInput + } + + s.symbol += bits + } + + table_size = buildSimpleHuffmanTable(table, huffmanTableBits, s.symbols_lists_array[:], s.symbol) + if opt_table_size != nil { + *opt_table_size = table_size + } + + s.substate_huffman = stateHuffmanNone + return decoderSuccess + + /* Decode Huffman-coded code lengths. */ + case stateHuffmanComplex: + { + var i uint32 + var result int = readCodeLengthCodeLengths(s) + if result != decoderSuccess { + return result + } + + buildCodeLengthsHuffmanTable(s.table[:], s.code_length_code_lengths[:], s.code_length_histo[:]) + for i = 0; i < 16; i++ { + s.code_length_histo[i] = 0 + } + + for i = 0; i <= huffmanMaxCodeLength; i++ { + s.next_symbol[i] = int(i) - (huffmanMaxCodeLength + 1) + symbolListPut(s.symbol_lists, s.next_symbol[i], 0xFFFF) + } + + s.symbol = 0 + s.prev_code_len = initialRepeatedCodeLength + s.repeat = 0 + s.repeat_code_len = 0 + s.space = 32768 + s.substate_huffman = stateHuffmanLengthSymbols + } + fallthrough + + case stateHuffmanLengthSymbols: + var table_size uint32 + var result int = readSymbolCodeLengths(max_symbol, s) + if result == decoderNeedsMoreInput { + result = safeReadSymbolCodeLengths(max_symbol, s) + } + + if result != decoderSuccess { + return result + } + + if s.space != 0 { + return decoderErrorFormatHuffmanSpace + } + + table_size = buildHuffmanTable(table, huffmanTableBits, s.symbol_lists, s.code_length_histo[:]) + if opt_table_size != nil { + *opt_table_size = table_size + } + + s.substate_huffman = stateHuffmanNone + return decoderSuccess + + default: + return decoderErrorUnreachable + } + } +} + +/* Decodes a block length by reading 3..39 bits. */ +func readBlockLength(table []huffmanCode, br *bitReader) uint32 { + var code uint32 + var nbits uint32 + code = readSymbol(table, br) + nbits = kBlockLengthPrefixCode[code].nbits /* nbits == 2..24 */ + return kBlockLengthPrefixCode[code].offset + readBits(br, nbits) +} + +/* WARNING: if state is not BROTLI_STATE_READ_BLOCK_LENGTH_NONE, then + reading can't be continued with ReadBlockLength. */ +func safeReadBlockLength(s *Reader, result *uint32, table []huffmanCode, br *bitReader) bool { + var index uint32 + if s.substate_read_block_length == stateReadBlockLengthNone { + if !safeReadSymbol(table, br, &index) { + return false + } + } else { + index = s.block_length_index + } + { + var bits uint32 /* nbits == 2..24 */ + var nbits uint32 = kBlockLengthPrefixCode[index].nbits + if !safeReadBits(br, nbits, &bits) { + s.block_length_index = index + s.substate_read_block_length = stateReadBlockLengthSuffix + return false + } + + *result = kBlockLengthPrefixCode[index].offset + bits + s.substate_read_block_length = stateReadBlockLengthNone + return true + } +} + +/* Transform: + 1) initialize list L with values 0, 1,... 255 + 2) For each input element X: + 2.1) let Y = L[X] + 2.2) remove X-th element from L + 2.3) prepend Y to L + 2.4) append Y to output + + In most cases max(Y) <= 7, so most of L remains intact. + To reduce the cost of initialization, we reuse L, remember the upper bound + of Y values, and reinitialize only first elements in L. + + Most of input values are 0 and 1. To reduce number of branches, we replace + inner for loop with do-while. */ +func inverseMoveToFrontTransform(v []byte, v_len uint32, state *Reader) { + var mtf [256]byte + var i int + for i = 1; i < 256; i++ { + mtf[i] = byte(i) + } + var mtf_1 byte + + /* Transform the input. */ + for i = 0; uint32(i) < v_len; i++ { + var index int = int(v[i]) + var value byte = mtf[index] + v[i] = value + mtf_1 = value + for index >= 1 { + index-- + mtf[index+1] = mtf[index] + } + + mtf[0] = mtf_1 + } +} + +/* Decodes a series of Huffman table using ReadHuffmanCode function. */ +func huffmanTreeGroupDecode(group *huffmanTreeGroup, s *Reader) int { + if s.substate_tree_group != stateTreeGroupLoop { + s.next = group.codes + s.htree_index = 0 + s.substate_tree_group = stateTreeGroupLoop + } + + for s.htree_index < int(group.num_htrees) { + var table_size uint32 + var result int = readHuffmanCode(uint32(group.alphabet_size), uint32(group.max_symbol), s.next, &table_size, s) + if result != decoderSuccess { + return result + } + group.htrees[s.htree_index] = s.next + s.next = s.next[table_size:] + s.htree_index++ + } + + s.substate_tree_group = stateTreeGroupNone + return decoderSuccess +} + +/* Decodes a context map. + Decoding is done in 4 phases: + 1) Read auxiliary information (6..16 bits) and allocate memory. + In case of trivial context map, decoding is finished at this phase. + 2) Decode Huffman table using ReadHuffmanCode function. + This table will be used for reading context map items. + 3) Read context map items; "0" values could be run-length encoded. + 4) Optionally, apply InverseMoveToFront transform to the resulting map. */ +func decodeContextMap(context_map_size uint32, num_htrees *uint32, context_map_arg *[]byte, s *Reader) int { + var br *bitReader = &s.br + var result int = decoderSuccess + + switch int(s.substate_context_map) { + case stateContextMapNone: + result = decodeVarLenUint8(s, br, num_htrees) + if result != decoderSuccess { + return result + } + + (*num_htrees)++ + s.context_index = 0 + *context_map_arg = make([]byte, uint(context_map_size)) + if *context_map_arg == nil { + return decoderErrorAllocContextMap + } + + if *num_htrees <= 1 { + for i := 0; i < int(context_map_size); i++ { + (*context_map_arg)[i] = 0 + } + return decoderSuccess + } + + s.substate_context_map = stateContextMapReadPrefix + fallthrough + /* Fall through. */ + case stateContextMapReadPrefix: + { + var bits uint32 + + /* In next stage ReadHuffmanCode uses at least 4 bits, so it is safe + to peek 4 bits ahead. */ + if !safeGetBits(br, 5, &bits) { + return decoderNeedsMoreInput + } + + if bits&1 != 0 { /* Use RLE for zeros. */ + s.max_run_length_prefix = (bits >> 1) + 1 + dropBits(br, 5) + } else { + s.max_run_length_prefix = 0 + dropBits(br, 1) + } + + s.substate_context_map = stateContextMapHuffman + } + fallthrough + + /* Fall through. */ + case stateContextMapHuffman: + { + var alphabet_size uint32 = *num_htrees + s.max_run_length_prefix + result = readHuffmanCode(alphabet_size, alphabet_size, s.context_map_table[:], nil, s) + if result != decoderSuccess { + return result + } + s.code = 0xFFFF + s.substate_context_map = stateContextMapDecode + } + fallthrough + + /* Fall through. */ + case stateContextMapDecode: + { + var context_index uint32 = s.context_index + var max_run_length_prefix uint32 = s.max_run_length_prefix + var context_map []byte = *context_map_arg + var code uint32 = s.code + var skip_preamble bool = (code != 0xFFFF) + for context_index < context_map_size || skip_preamble { + if !skip_preamble { + if !safeReadSymbol(s.context_map_table[:], br, &code) { + s.code = 0xFFFF + s.context_index = context_index + return decoderNeedsMoreInput + } + + if code == 0 { + context_map[context_index] = 0 + context_index++ + continue + } + + if code > max_run_length_prefix { + context_map[context_index] = byte(code - max_run_length_prefix) + context_index++ + continue + } + } else { + skip_preamble = false + } + + /* RLE sub-stage. */ + { + var reps uint32 + if !safeReadBits(br, code, &reps) { + s.code = code + s.context_index = context_index + return decoderNeedsMoreInput + } + + reps += 1 << code + if context_index+reps > context_map_size { + return decoderErrorFormatContextMapRepeat + } + + for { + context_map[context_index] = 0 + context_index++ + reps-- + if reps == 0 { + break + } + } + } + } + } + fallthrough + + case stateContextMapTransform: + var bits uint32 + if !safeReadBits(br, 1, &bits) { + s.substate_context_map = stateContextMapTransform + return decoderNeedsMoreInput + } + + if bits != 0 { + inverseMoveToFrontTransform(*context_map_arg, context_map_size, s) + } + + s.substate_context_map = stateContextMapNone + return decoderSuccess + + default: + return decoderErrorUnreachable + } +} + +/* Decodes a command or literal and updates block type ring-buffer. + Reads 3..54 bits. */ +func decodeBlockTypeAndLength(safe int, s *Reader, tree_type int) bool { + var max_block_type uint32 = s.num_block_types[tree_type] + type_tree := s.block_type_trees[tree_type*huffmanMaxSize258:] + len_tree := s.block_len_trees[tree_type*huffmanMaxSize26:] + var br *bitReader = &s.br + var ringbuffer []uint32 = s.block_type_rb[tree_type*2:] + var block_type uint32 + if max_block_type <= 1 { + return false + } + + /* Read 0..15 + 3..39 bits. */ + if safe == 0 { + block_type = readSymbol(type_tree, br) + s.block_length[tree_type] = readBlockLength(len_tree, br) + } else { + var memento bitReaderState + bitReaderSaveState(br, &memento) + if !safeReadSymbol(type_tree, br, &block_type) { + return false + } + if !safeReadBlockLength(s, &s.block_length[tree_type], len_tree, br) { + s.substate_read_block_length = stateReadBlockLengthNone + bitReaderRestoreState(br, &memento) + return false + } + } + + if block_type == 1 { + block_type = ringbuffer[1] + 1 + } else if block_type == 0 { + block_type = ringbuffer[0] + } else { + block_type -= 2 + } + + if block_type >= max_block_type { + block_type -= max_block_type + } + + ringbuffer[0] = ringbuffer[1] + ringbuffer[1] = block_type + return true +} + +func detectTrivialLiteralBlockTypes(s *Reader) { + var i uint + for i = 0; i < 8; i++ { + s.trivial_literal_contexts[i] = 0 + } + for i = 0; uint32(i) < s.num_block_types[0]; i++ { + var offset uint = i << literalContextBits + var error uint = 0 + var sample uint = uint(s.context_map[offset]) + var j uint + for j = 0; j < 1<>5] |= 1 << (i & 31) + } + } +} + +func prepareLiteralDecoding(s *Reader) { + var context_mode byte + var trivial uint + var block_type uint32 = s.block_type_rb[1] + var context_offset uint32 = block_type << literalContextBits + s.context_map_slice = s.context_map[context_offset:] + trivial = uint(s.trivial_literal_contexts[block_type>>5]) + s.trivial_literal_context = int((trivial >> (block_type & 31)) & 1) + s.literal_htree = []huffmanCode(s.literal_hgroup.htrees[s.context_map_slice[0]]) + context_mode = s.context_modes[block_type] & 3 + s.context_lookup = getContextLUT(int(context_mode)) +} + +/* Decodes the block type and updates the state for literal context. + Reads 3..54 bits. */ +func decodeLiteralBlockSwitchInternal(safe int, s *Reader) bool { + if !decodeBlockTypeAndLength(safe, s, 0) { + return false + } + + prepareLiteralDecoding(s) + return true +} + +func decodeLiteralBlockSwitch(s *Reader) { + decodeLiteralBlockSwitchInternal(0, s) +} + +func safeDecodeLiteralBlockSwitch(s *Reader) bool { + return decodeLiteralBlockSwitchInternal(1, s) +} + +/* Block switch for insert/copy length. + Reads 3..54 bits. */ +func decodeCommandBlockSwitchInternal(safe int, s *Reader) bool { + if !decodeBlockTypeAndLength(safe, s, 1) { + return false + } + + s.htree_command = []huffmanCode(s.insert_copy_hgroup.htrees[s.block_type_rb[3]]) + return true +} + +func decodeCommandBlockSwitch(s *Reader) { + decodeCommandBlockSwitchInternal(0, s) +} + +func safeDecodeCommandBlockSwitch(s *Reader) bool { + return decodeCommandBlockSwitchInternal(1, s) +} + +/* Block switch for distance codes. + Reads 3..54 bits. */ +func decodeDistanceBlockSwitchInternal(safe int, s *Reader) bool { + if !decodeBlockTypeAndLength(safe, s, 2) { + return false + } + + s.dist_context_map_slice = s.dist_context_map[s.block_type_rb[5]< s.ringbuffer_size { + pos = uint(s.ringbuffer_size) + } else { + pos = uint(s.pos) + } + var partial_pos_rb uint = (s.rb_roundtrips * uint(s.ringbuffer_size)) + pos + return partial_pos_rb - s.partial_pos_out +} + +/* Dumps output. + Returns BROTLI_DECODER_NEEDS_MORE_OUTPUT only if there is more output to push + and either ring-buffer is as big as window size, or |force| is true. */ +func writeRingBuffer(s *Reader, available_out *uint, next_out *[]byte, total_out *uint, force bool) int { + start := s.ringbuffer[s.partial_pos_out&uint(s.ringbuffer_mask):] + var to_write uint = unwrittenBytes(s, true) + var num_written uint = *available_out + if num_written > to_write { + num_written = to_write + } + + if s.meta_block_remaining_len < 0 { + return decoderErrorFormatBlockLength1 + } + + if next_out != nil && *next_out == nil { + *next_out = start + } else { + if next_out != nil { + copy(*next_out, start[:num_written]) + *next_out = (*next_out)[num_written:] + } + } + + *available_out -= num_written + s.partial_pos_out += num_written + if total_out != nil { + *total_out = s.partial_pos_out + } + + if num_written < to_write { + if s.ringbuffer_size == 1<= s.ringbuffer_size { + s.pos -= s.ringbuffer_size + s.rb_roundtrips++ + if uint(s.pos) != 0 { + s.should_wrap_ringbuffer = 1 + } else { + s.should_wrap_ringbuffer = 0 + } + } + + return decoderSuccess +} + +func wrapRingBuffer(s *Reader) { + if s.should_wrap_ringbuffer != 0 { + copy(s.ringbuffer, s.ringbuffer_end[:uint(s.pos)]) + s.should_wrap_ringbuffer = 0 + } +} + +/* Allocates ring-buffer. + + s->ringbuffer_size MUST be updated by BrotliCalculateRingBufferSize before + this function is called. + + Last two bytes of ring-buffer are initialized to 0, so context calculation + could be done uniformly for the first two and all other positions. */ +func ensureRingBuffer(s *Reader) bool { + var old_ringbuffer []byte + if s.ringbuffer_size == s.new_ringbuffer_size { + return true + } + spaceNeeded := int(s.new_ringbuffer_size) + int(kRingBufferWriteAheadSlack) + if len(s.ringbuffer) < spaceNeeded { + old_ringbuffer = s.ringbuffer + s.ringbuffer = make([]byte, spaceNeeded) + } + + s.ringbuffer[s.new_ringbuffer_size-2] = 0 + s.ringbuffer[s.new_ringbuffer_size-1] = 0 + + if old_ringbuffer != nil { + copy(s.ringbuffer, old_ringbuffer[:uint(s.pos)]) + } + + s.ringbuffer_size = s.new_ringbuffer_size + s.ringbuffer_mask = s.new_ringbuffer_size - 1 + s.ringbuffer_end = s.ringbuffer[s.ringbuffer_size:] + + return true +} + +func copyUncompressedBlockToOutput(available_out *uint, next_out *[]byte, total_out *uint, s *Reader) int { + /* TODO: avoid allocation for single uncompressed block. */ + if !ensureRingBuffer(s) { + return decoderErrorAllocRingBuffer1 + } + + /* State machine */ + for { + switch s.substate_uncompressed { + case stateUncompressedNone: + { + var nbytes int = int(getRemainingBytes(&s.br)) + if nbytes > s.meta_block_remaining_len { + nbytes = s.meta_block_remaining_len + } + + if s.pos+nbytes > s.ringbuffer_size { + nbytes = s.ringbuffer_size - s.pos + } + + /* Copy remaining bytes from s->br.buf_ to ring-buffer. */ + copyBytes(s.ringbuffer[s.pos:], &s.br, uint(nbytes)) + + s.pos += nbytes + s.meta_block_remaining_len -= nbytes + if s.pos < 1<>1 >= min_size { + new_ringbuffer_size >>= 1 + } + } + + s.new_ringbuffer_size = new_ringbuffer_size +} + +/* Reads 1..256 2-bit context modes. */ +func readContextModes(s *Reader) int { + var br *bitReader = &s.br + var i int = s.loop_counter + + for i < int(s.num_block_types[0]) { + var bits uint32 + if !safeReadBits(br, 2, &bits) { + s.loop_counter = i + return decoderNeedsMoreInput + } + + s.context_modes[i] = byte(bits) + i++ + } + + return decoderSuccess +} + +func takeDistanceFromRingBuffer(s *Reader) { + if s.distance_code == 0 { + s.dist_rb_idx-- + s.distance_code = s.dist_rb[s.dist_rb_idx&3] + + /* Compensate double distance-ring-buffer roll for dictionary items. */ + s.distance_context = 1 + } else { + var distance_code int = s.distance_code << 1 + const kDistanceShortCodeIndexOffset uint32 = 0xAAAFFF1B + const kDistanceShortCodeValueOffset uint32 = 0xFA5FA500 + var v int = (s.dist_rb_idx + int(kDistanceShortCodeIndexOffset>>uint(distance_code))) & 0x3 + /* kDistanceShortCodeIndexOffset has 2-bit values from LSB: + 3, 2, 1, 0, 3, 3, 3, 3, 3, 3, 2, 2, 2, 2, 2, 2 */ + + /* kDistanceShortCodeValueOffset has 2-bit values from LSB: + -0, 0,-0, 0,-1, 1,-2, 2,-3, 3,-1, 1,-2, 2,-3, 3 */ + s.distance_code = s.dist_rb[v] + + v = int(kDistanceShortCodeValueOffset>>uint(distance_code)) & 0x3 + if distance_code&0x3 != 0 { + s.distance_code += v + } else { + s.distance_code -= v + if s.distance_code <= 0 { + /* A huge distance will cause a () soon. + This is a little faster than failing here. */ + s.distance_code = 0x7FFFFFFF + } + } + } +} + +func safeReadBitsMaybeZero(br *bitReader, n_bits uint32, val *uint32) bool { + if n_bits != 0 { + return safeReadBits(br, n_bits, val) + } else { + *val = 0 + return true + } +} + +/* Precondition: s->distance_code < 0. */ +func readDistanceInternal(safe int, s *Reader, br *bitReader) bool { + var distval int + var memento bitReaderState + var distance_tree []huffmanCode = []huffmanCode(s.distance_hgroup.htrees[s.dist_htree_index]) + if safe == 0 { + s.distance_code = int(readSymbol(distance_tree, br)) + } else { + var code uint32 + bitReaderSaveState(br, &memento) + if !safeReadSymbol(distance_tree, br, &code) { + return false + } + + s.distance_code = int(code) + } + + /* Convert the distance code to the actual distance by possibly + looking up past distances from the s->ringbuffer. */ + s.distance_context = 0 + + if s.distance_code&^0xF == 0 { + takeDistanceFromRingBuffer(s) + s.block_length[2]-- + return true + } + + distval = s.distance_code - int(s.num_direct_distance_codes) + if distval >= 0 { + var nbits uint32 + var postfix int + var offset int + if safe == 0 && (s.distance_postfix_bits == 0) { + nbits = (uint32(distval) >> 1) + 1 + offset = ((2 + (distval & 1)) << nbits) - 4 + s.distance_code = int(s.num_direct_distance_codes) + offset + int(readBits(br, nbits)) + } else { + /* This branch also works well when s->distance_postfix_bits == 0. */ + var bits uint32 + postfix = distval & s.distance_postfix_mask + distval >>= s.distance_postfix_bits + nbits = (uint32(distval) >> 1) + 1 + if safe != 0 { + if !safeReadBitsMaybeZero(br, nbits, &bits) { + s.distance_code = -1 /* Restore precondition. */ + bitReaderRestoreState(br, &memento) + return false + } + } else { + bits = readBits(br, nbits) + } + + offset = ((2 + (distval & 1)) << nbits) - 4 + s.distance_code = int(s.num_direct_distance_codes) + ((offset + int(bits)) << s.distance_postfix_bits) + postfix + } + } + + s.distance_code = s.distance_code - numDistanceShortCodes + 1 + s.block_length[2]-- + return true +} + +func readDistance(s *Reader, br *bitReader) { + readDistanceInternal(0, s, br) +} + +func safeReadDistance(s *Reader, br *bitReader) bool { + return readDistanceInternal(1, s, br) +} + +func readCommandInternal(safe int, s *Reader, br *bitReader, insert_length *int) bool { + var cmd_code uint32 + var insert_len_extra uint32 = 0 + var copy_length uint32 + var v cmdLutElement + var memento bitReaderState + if safe == 0 { + cmd_code = readSymbol(s.htree_command, br) + } else { + bitReaderSaveState(br, &memento) + if !safeReadSymbol(s.htree_command, br, &cmd_code) { + return false + } + } + + v = kCmdLut[cmd_code] + s.distance_code = int(v.distance_code) + s.distance_context = int(v.context) + s.dist_htree_index = s.dist_context_map_slice[s.distance_context] + *insert_length = int(v.insert_len_offset) + if safe == 0 { + if v.insert_len_extra_bits != 0 { + insert_len_extra = readBits(br, uint32(v.insert_len_extra_bits)) + } + + copy_length = readBits(br, uint32(v.copy_len_extra_bits)) + } else { + if !safeReadBitsMaybeZero(br, uint32(v.insert_len_extra_bits), &insert_len_extra) || !safeReadBitsMaybeZero(br, uint32(v.copy_len_extra_bits), ©_length) { + bitReaderRestoreState(br, &memento) + return false + } + } + + s.copy_length = int(copy_length) + int(v.copy_len_offset) + s.block_length[1]-- + *insert_length += int(insert_len_extra) + return true +} + +func readCommand(s *Reader, br *bitReader, insert_length *int) { + readCommandInternal(0, s, br, insert_length) +} + +func safeReadCommand(s *Reader, br *bitReader, insert_length *int) bool { + return readCommandInternal(1, s, br, insert_length) +} + +func checkInputAmountMaybeSafe(safe int, br *bitReader, num uint) bool { + if safe != 0 { + return true + } + + return checkInputAmount(br, num) +} + +func processCommandsInternal(safe int, s *Reader) int { + var pos int = s.pos + var i int = s.loop_counter + var result int = decoderSuccess + var br *bitReader = &s.br + var hc []huffmanCode + + if !checkInputAmountMaybeSafe(safe, br, 28) { + result = decoderNeedsMoreInput + goto saveStateAndReturn + } + + if safe == 0 { + warmupBitReader(br) + } + + /* Jump into state machine. */ + if s.state == stateCommandBegin { + goto CommandBegin + } else if s.state == stateCommandInner { + goto CommandInner + } else if s.state == stateCommandPostDecodeLiterals { + goto CommandPostDecodeLiterals + } else if s.state == stateCommandPostWrapCopy { + goto CommandPostWrapCopy + } else { + return decoderErrorUnreachable + } + +CommandBegin: + if safe != 0 { + s.state = stateCommandBegin + } + + if !checkInputAmountMaybeSafe(safe, br, 28) { /* 156 bits + 7 bytes */ + s.state = stateCommandBegin + result = decoderNeedsMoreInput + goto saveStateAndReturn + } + + if s.block_length[1] == 0 { + if safe != 0 { + if !safeDecodeCommandBlockSwitch(s) { + result = decoderNeedsMoreInput + goto saveStateAndReturn + } + } else { + decodeCommandBlockSwitch(s) + } + + goto CommandBegin + } + + /* Read the insert/copy length in the command. */ + if safe != 0 { + if !safeReadCommand(s, br, &i) { + result = decoderNeedsMoreInput + goto saveStateAndReturn + } + } else { + readCommand(s, br, &i) + } + + if i == 0 { + goto CommandPostDecodeLiterals + } + + s.meta_block_remaining_len -= i + +CommandInner: + if safe != 0 { + s.state = stateCommandInner + } + + /* Read the literals in the command. */ + if s.trivial_literal_context != 0 { + var bits uint32 + var value uint32 + preloadSymbol(safe, s.literal_htree, br, &bits, &value) + for { + if !checkInputAmountMaybeSafe(safe, br, 28) { /* 162 bits + 7 bytes */ + s.state = stateCommandInner + result = decoderNeedsMoreInput + goto saveStateAndReturn + } + + if s.block_length[0] == 0 { + if safe != 0 { + if !safeDecodeLiteralBlockSwitch(s) { + result = decoderNeedsMoreInput + goto saveStateAndReturn + } + } else { + decodeLiteralBlockSwitch(s) + } + + preloadSymbol(safe, s.literal_htree, br, &bits, &value) + if s.trivial_literal_context == 0 { + goto CommandInner + } + } + + if safe == 0 { + s.ringbuffer[pos] = byte(readPreloadedSymbol(s.literal_htree, br, &bits, &value)) + } else { + var literal uint32 + if !safeReadSymbol(s.literal_htree, br, &literal) { + result = decoderNeedsMoreInput + goto saveStateAndReturn + } + + s.ringbuffer[pos] = byte(literal) + } + + s.block_length[0]-- + pos++ + if pos == s.ringbuffer_size { + s.state = stateCommandInnerWrite + i-- + goto saveStateAndReturn + } + i-- + if i == 0 { + break + } + } + } else { + var p1 byte = s.ringbuffer[(pos-1)&s.ringbuffer_mask] + var p2 byte = s.ringbuffer[(pos-2)&s.ringbuffer_mask] + for { + var context byte + if !checkInputAmountMaybeSafe(safe, br, 28) { /* 162 bits + 7 bytes */ + s.state = stateCommandInner + result = decoderNeedsMoreInput + goto saveStateAndReturn + } + + if s.block_length[0] == 0 { + if safe != 0 { + if !safeDecodeLiteralBlockSwitch(s) { + result = decoderNeedsMoreInput + goto saveStateAndReturn + } + } else { + decodeLiteralBlockSwitch(s) + } + + if s.trivial_literal_context != 0 { + goto CommandInner + } + } + + context = getContext(p1, p2, s.context_lookup) + hc = []huffmanCode(s.literal_hgroup.htrees[s.context_map_slice[context]]) + p2 = p1 + if safe == 0 { + p1 = byte(readSymbol(hc, br)) + } else { + var literal uint32 + if !safeReadSymbol(hc, br, &literal) { + result = decoderNeedsMoreInput + goto saveStateAndReturn + } + + p1 = byte(literal) + } + + s.ringbuffer[pos] = p1 + s.block_length[0]-- + pos++ + if pos == s.ringbuffer_size { + s.state = stateCommandInnerWrite + i-- + goto saveStateAndReturn + } + i-- + if i == 0 { + break + } + } + } + + if s.meta_block_remaining_len <= 0 { + s.state = stateMetablockDone + goto saveStateAndReturn + } + +CommandPostDecodeLiterals: + if safe != 0 { + s.state = stateCommandPostDecodeLiterals + } + + if s.distance_code >= 0 { + /* Implicit distance case. */ + if s.distance_code != 0 { + s.distance_context = 0 + } else { + s.distance_context = 1 + } + + s.dist_rb_idx-- + s.distance_code = s.dist_rb[s.dist_rb_idx&3] + } else { + /* Read distance code in the command, unless it was implicitly zero. */ + if s.block_length[2] == 0 { + if safe != 0 { + if !safeDecodeDistanceBlockSwitch(s) { + result = decoderNeedsMoreInput + goto saveStateAndReturn + } + } else { + decodeDistanceBlockSwitch(s) + } + } + + if safe != 0 { + if !safeReadDistance(s, br) { + result = decoderNeedsMoreInput + goto saveStateAndReturn + } + } else { + readDistance(s, br) + } + } + + if s.max_distance != s.max_backward_distance { + if pos < s.max_backward_distance { + s.max_distance = pos + } else { + s.max_distance = s.max_backward_distance + } + } + + i = s.copy_length + + /* Apply copy of LZ77 back-reference, or static dictionary reference if + the distance is larger than the max LZ77 distance */ + if s.distance_code > s.max_distance { + /* The maximum allowed distance is BROTLI_MAX_ALLOWED_DISTANCE = 0x7FFFFFFC. + With this choice, no signed overflow can occur after decoding + a special distance code (e.g., after adding 3 to the last distance). */ + if s.distance_code > maxAllowedDistance { + return decoderErrorFormatDistance + } + + if i >= minDictionaryWordLength && i <= maxDictionaryWordLength { + var address int = s.distance_code - s.max_distance - 1 + var words *dictionary = s.dictionary + var trans *transforms = s.transforms + var offset int = int(s.dictionary.offsets_by_length[i]) + var shift uint32 = uint32(s.dictionary.size_bits_by_length[i]) + var mask int = int(bitMask(shift)) + var word_idx int = address & mask + var transform_idx int = address >> shift + + /* Compensate double distance-ring-buffer roll. */ + s.dist_rb_idx += s.distance_context + + offset += word_idx * i + if words.data == nil { + return decoderErrorDictionaryNotSet + } + + if transform_idx < int(trans.num_transforms) { + word := words.data[offset:] + var len int = i + if transform_idx == int(trans.cutOffTransforms[0]) { + copy(s.ringbuffer[pos:], word[:uint(len)]) + } else { + len = transformDictionaryWord(s.ringbuffer[pos:], word, int(len), trans, transform_idx) + } + + pos += int(len) + s.meta_block_remaining_len -= int(len) + if pos >= s.ringbuffer_size { + s.state = stateCommandPostWrite1 + goto saveStateAndReturn + } + } else { + return decoderErrorFormatTransform + } + } else { + return decoderErrorFormatDictionary + } + } else { + var src_start int = (pos - s.distance_code) & s.ringbuffer_mask + copy_dst := s.ringbuffer[pos:] + copy_src := s.ringbuffer[src_start:] + var dst_end int = pos + i + var src_end int = src_start + i + + /* Update the recent distances cache. */ + s.dist_rb[s.dist_rb_idx&3] = s.distance_code + + s.dist_rb_idx++ + s.meta_block_remaining_len -= i + + /* There are 32+ bytes of slack in the ring-buffer allocation. + Also, we have 16 short codes, that make these 16 bytes irrelevant + in the ring-buffer. Let's copy over them as a first guess. */ + copy(copy_dst, copy_src[:16]) + + if src_end > pos && dst_end > src_start { + /* Regions intersect. */ + goto CommandPostWrapCopy + } + + if dst_end >= s.ringbuffer_size || src_end >= s.ringbuffer_size { + /* At least one region wraps. */ + goto CommandPostWrapCopy + } + + pos += i + if i > 16 { + if i > 32 { + copy(copy_dst[16:], copy_src[16:][:uint(i-16)]) + } else { + /* This branch covers about 45% cases. + Fixed size short copy allows more compiler optimizations. */ + copy(copy_dst[16:], copy_src[16:][:16]) + } + } + } + + if s.meta_block_remaining_len <= 0 { + /* Next metablock, if any. */ + s.state = stateMetablockDone + + goto saveStateAndReturn + } else { + goto CommandBegin + } +CommandPostWrapCopy: + { + var wrap_guard int = s.ringbuffer_size - pos + for { + i-- + if i < 0 { + break + } + s.ringbuffer[pos] = s.ringbuffer[(pos-s.distance_code)&s.ringbuffer_mask] + pos++ + wrap_guard-- + if wrap_guard == 0 { + s.state = stateCommandPostWrite2 + goto saveStateAndReturn + } + } + } + + if s.meta_block_remaining_len <= 0 { + /* Next metablock, if any. */ + s.state = stateMetablockDone + + goto saveStateAndReturn + } else { + goto CommandBegin + } + +saveStateAndReturn: + s.pos = pos + s.loop_counter = i + return result +} + +func processCommands(s *Reader) int { + return processCommandsInternal(0, s) +} + +func safeProcessCommands(s *Reader) int { + return processCommandsInternal(1, s) +} + +/* Returns the maximum number of distance symbols which can only represent + distances not exceeding BROTLI_MAX_ALLOWED_DISTANCE. */ + +var maxDistanceSymbol_bound = [maxNpostfix + 1]uint32{0, 4, 12, 28} +var maxDistanceSymbol_diff = [maxNpostfix + 1]uint32{73, 126, 228, 424} + +func maxDistanceSymbol(ndirect uint32, npostfix uint32) uint32 { + var postfix uint32 = 1 << npostfix + if ndirect < maxDistanceSymbol_bound[npostfix] { + return ndirect + maxDistanceSymbol_diff[npostfix] + postfix + } else if ndirect > maxDistanceSymbol_bound[npostfix]+postfix { + return ndirect + maxDistanceSymbol_diff[npostfix] + } else { + return maxDistanceSymbol_bound[npostfix] + maxDistanceSymbol_diff[npostfix] + postfix + } +} + +/* Invariant: input stream is never overconsumed: + - invalid input implies that the whole stream is invalid -> any amount of + input could be read and discarded + - when result is "needs more input", then at least one more byte is REQUIRED + to complete decoding; all input data MUST be consumed by decoder, so + client could swap the input buffer + - when result is "needs more output" decoder MUST ensure that it doesn't + hold more than 7 bits in bit reader; this saves client from swapping input + buffer ahead of time + - when result is "success" decoder MUST return all unused data back to input + buffer; this is possible because the invariant is held on enter */ +func decoderDecompressStream(s *Reader, available_in *uint, next_in *[]byte, available_out *uint, next_out *[]byte) int { + var result int = decoderSuccess + var br *bitReader = &s.br + + /* Do not try to process further in a case of unrecoverable error. */ + if int(s.error_code) < 0 { + return decoderResultError + } + + if *available_out != 0 && (next_out == nil || *next_out == nil) { + return saveErrorCode(s, decoderErrorInvalidArguments) + } + + if *available_out == 0 { + next_out = nil + } + if s.buffer_length == 0 { /* Just connect bit reader to input stream. */ + br.input_len = *available_in + br.input = *next_in + br.byte_pos = 0 + } else { + /* At least one byte of input is required. More than one byte of input may + be required to complete the transaction -> reading more data must be + done in a loop -> do it in a main loop. */ + result = decoderNeedsMoreInput + + br.input = s.buffer.u8[:] + br.byte_pos = 0 + } + + /* State machine */ + for { + if result != decoderSuccess { + /* Error, needs more input/output. */ + if result == decoderNeedsMoreInput { + if s.ringbuffer != nil { /* Pro-actively push output. */ + var intermediate_result int = writeRingBuffer(s, available_out, next_out, nil, true) + + /* WriteRingBuffer checks s->meta_block_remaining_len validity. */ + if int(intermediate_result) < 0 { + result = intermediate_result + break + } + } + + if s.buffer_length != 0 { /* Used with internal buffer. */ + if br.byte_pos == br.input_len { + /* Successfully finished read transaction. + Accumulator contains less than 8 bits, because internal buffer + is expanded byte-by-byte until it is enough to complete read. */ + s.buffer_length = 0 + + /* Switch to input stream and restart. */ + result = decoderSuccess + + br.input_len = *available_in + br.input = *next_in + br.byte_pos = 0 + continue + } else if *available_in != 0 { + /* Not enough data in buffer, but can take one more byte from + input stream. */ + result = decoderSuccess + + s.buffer.u8[s.buffer_length] = (*next_in)[0] + s.buffer_length++ + br.input_len = uint(s.buffer_length) + *next_in = (*next_in)[1:] + (*available_in)-- + + /* Retry with more data in buffer. */ + continue + } + + /* Can't finish reading and no more input. */ + break + /* Input stream doesn't contain enough input. */ + } else { + /* Copy tail to internal buffer and return. */ + *next_in = br.input[br.byte_pos:] + + *available_in = br.input_len - br.byte_pos + for *available_in != 0 { + s.buffer.u8[s.buffer_length] = (*next_in)[0] + s.buffer_length++ + *next_in = (*next_in)[1:] + (*available_in)-- + } + + break + } + } + + /* Unreachable. */ + + /* Fail or needs more output. */ + if s.buffer_length != 0 { + /* Just consumed the buffered input and produced some output. Otherwise + it would result in "needs more input". Reset internal buffer. */ + s.buffer_length = 0 + } else { + /* Using input stream in last iteration. When decoder switches to input + stream it has less than 8 bits in accumulator, so it is safe to + return unused accumulator bits there. */ + bitReaderUnload(br) + + *available_in = br.input_len - br.byte_pos + *next_in = br.input[br.byte_pos:] + } + + break + } + + switch s.state { + /* Prepare to the first read. */ + case stateUninited: + if !warmupBitReader(br) { + result = decoderNeedsMoreInput + break + } + + /* Decode window size. */ + result = decodeWindowBits(s, br) /* Reads 1..8 bits. */ + if result != decoderSuccess { + break + } + + if s.large_window { + s.state = stateLargeWindowBits + break + } + + s.state = stateInitialize + + case stateLargeWindowBits: + if !safeReadBits(br, 6, &s.window_bits) { + result = decoderNeedsMoreInput + break + } + + if s.window_bits < largeMinWbits || s.window_bits > largeMaxWbits { + result = decoderErrorFormatWindowBits + break + } + + s.state = stateInitialize + fallthrough + + /* Maximum distance, see section 9.1. of the spec. */ + /* Fall through. */ + case stateInitialize: + s.max_backward_distance = (1 << s.window_bits) - windowGap + + /* Allocate memory for both block_type_trees and block_len_trees. */ + s.block_type_trees = make([]huffmanCode, (3 * (huffmanMaxSize258 + huffmanMaxSize26))) + + if s.block_type_trees == nil { + result = decoderErrorAllocBlockTypeTrees + break + } + + s.block_len_trees = s.block_type_trees[3*huffmanMaxSize258:] + + s.state = stateMetablockBegin + fallthrough + + /* Fall through. */ + case stateMetablockBegin: + decoderStateMetablockBegin(s) + + s.state = stateMetablockHeader + fallthrough + + /* Fall through. */ + case stateMetablockHeader: + result = decodeMetaBlockLength(s, br) + /* Reads 2 - 31 bits. */ + if result != decoderSuccess { + break + } + + if s.is_metadata != 0 || s.is_uncompressed != 0 { + if !bitReaderJumpToByteBoundary(br) { + result = decoderErrorFormatPadding1 + break + } + } + + if s.is_metadata != 0 { + s.state = stateMetadata + break + } + + if s.meta_block_remaining_len == 0 { + s.state = stateMetablockDone + break + } + + calculateRingBufferSize(s) + if s.is_uncompressed != 0 { + s.state = stateUncompressed + break + } + + s.loop_counter = 0 + s.state = stateHuffmanCode0 + + case stateUncompressed: + result = copyUncompressedBlockToOutput(available_out, next_out, nil, s) + if result == decoderSuccess { + s.state = stateMetablockDone + } + + case stateMetadata: + for ; s.meta_block_remaining_len > 0; s.meta_block_remaining_len-- { + var bits uint32 + + /* Read one byte and ignore it. */ + if !safeReadBits(br, 8, &bits) { + result = decoderNeedsMoreInput + break + } + } + + if result == decoderSuccess { + s.state = stateMetablockDone + } + + case stateHuffmanCode0: + if s.loop_counter >= 3 { + s.state = stateMetablockHeader2 + break + } + + /* Reads 1..11 bits. */ + result = decodeVarLenUint8(s, br, &s.num_block_types[s.loop_counter]) + + if result != decoderSuccess { + break + } + + s.num_block_types[s.loop_counter]++ + if s.num_block_types[s.loop_counter] < 2 { + s.loop_counter++ + break + } + + s.state = stateHuffmanCode1 + fallthrough + + case stateHuffmanCode1: + { + var alphabet_size uint32 = s.num_block_types[s.loop_counter] + 2 + var tree_offset int = s.loop_counter * huffmanMaxSize258 + result = readHuffmanCode(alphabet_size, alphabet_size, s.block_type_trees[tree_offset:], nil, s) + if result != decoderSuccess { + break + } + s.state = stateHuffmanCode2 + } + fallthrough + + case stateHuffmanCode2: + { + var alphabet_size uint32 = numBlockLenSymbols + var tree_offset int = s.loop_counter * huffmanMaxSize26 + result = readHuffmanCode(alphabet_size, alphabet_size, s.block_len_trees[tree_offset:], nil, s) + if result != decoderSuccess { + break + } + s.state = stateHuffmanCode3 + } + fallthrough + + case stateHuffmanCode3: + var tree_offset int = s.loop_counter * huffmanMaxSize26 + if !safeReadBlockLength(s, &s.block_length[s.loop_counter], s.block_len_trees[tree_offset:], br) { + result = decoderNeedsMoreInput + break + } + + s.loop_counter++ + s.state = stateHuffmanCode0 + + case stateMetablockHeader2: + { + var bits uint32 + if !safeReadBits(br, 6, &bits) { + result = decoderNeedsMoreInput + break + } + + s.distance_postfix_bits = bits & bitMask(2) + bits >>= 2 + s.num_direct_distance_codes = numDistanceShortCodes + (bits << s.distance_postfix_bits) + s.distance_postfix_mask = int(bitMask(s.distance_postfix_bits)) + s.context_modes = make([]byte, uint(s.num_block_types[0])) + if s.context_modes == nil { + result = decoderErrorAllocContextModes + break + } + + s.loop_counter = 0 + s.state = stateContextModes + } + fallthrough + + case stateContextModes: + result = readContextModes(s) + + if result != decoderSuccess { + break + } + + s.state = stateContextMap1 + fallthrough + + case stateContextMap1: + result = decodeContextMap(s.num_block_types[0]<= 3 { + prepareLiteralDecoding(s) + s.dist_context_map_slice = s.dist_context_map + s.htree_command = []huffmanCode(s.insert_copy_hgroup.htrees[0]) + if !ensureRingBuffer(s) { + result = decoderErrorAllocRingBuffer2 + break + } + + s.state = stateCommandBegin + } + + case stateCommandBegin, stateCommandInner, stateCommandPostDecodeLiterals, stateCommandPostWrapCopy: + result = processCommands(s) + + if result == decoderNeedsMoreInput { + result = safeProcessCommands(s) + } + + case stateCommandInnerWrite, stateCommandPostWrite1, stateCommandPostWrite2: + result = writeRingBuffer(s, available_out, next_out, nil, false) + + if result != decoderSuccess { + break + } + + wrapRingBuffer(s) + if s.ringbuffer_size == 1<= uint64(block_size) { + return 0 + } + return block_size - uint(delta) +} + +/* Wraps 64-bit input position to 32-bit ring-buffer position preserving + "not-a-first-lap" feature. */ +func wrapPosition(position uint64) uint32 { + var result uint32 = uint32(position) + var gb uint64 = position >> 30 + if gb > 2 { + /* Wrap every 2GiB; The first 3GB are continuous. */ + result = result&((1<<30)-1) | (uint32((gb-1)&1)+1)<<30 + } + + return result +} + +func (s *Writer) getStorage(size int) []byte { + if len(s.storage) < size { + s.storage = make([]byte, size) + } + + return s.storage +} + +func hashTableSize(max_table_size uint, input_size uint) uint { + var htsize uint = 256 + for htsize < max_table_size && htsize < input_size { + htsize <<= 1 + } + + return htsize +} + +func getHashTable(s *Writer, quality int, input_size uint, table_size *uint) []int { + var max_table_size uint = maxHashTableSize(quality) + var htsize uint = hashTableSize(max_table_size, input_size) + /* Use smaller hash table when input.size() is smaller, since we + fill the table, incurring O(hash table size) overhead for + compression, and if the input is short, we won't need that + many hash table entries anyway. */ + + var table []int + assert(max_table_size >= 256) + if quality == fastOnePassCompressionQuality { + /* Only odd shifts are supported by fast-one-pass. */ + if htsize&0xAAAAA == 0 { + htsize <<= 1 + } + } + + if htsize <= uint(len(s.small_table_)) { + table = s.small_table_[:] + } else { + if htsize > s.large_table_size_ { + s.large_table_size_ = htsize + s.large_table_ = nil + s.large_table_ = make([]int, htsize) + } + + table = s.large_table_ + } + + *table_size = htsize + for i := 0; i < int(htsize); i++ { + table[i] = 0 + } + return table +} + +func encodeWindowBits(lgwin int, large_window bool, last_bytes *uint16, last_bytes_bits *byte) { + if large_window { + *last_bytes = uint16((lgwin&0x3F)<<8 | 0x11) + *last_bytes_bits = 14 + } else { + if lgwin == 16 { + *last_bytes = 0 + *last_bytes_bits = 1 + } else if lgwin == 17 { + *last_bytes = 1 + *last_bytes_bits = 7 + } else if lgwin > 17 { + *last_bytes = uint16((lgwin-17)<<1 | 0x01) + *last_bytes_bits = 4 + } else { + *last_bytes = uint16((lgwin-8)<<4 | 0x01) + *last_bytes_bits = 7 + } + } +} + +/* Decide about the context map based on the ability of the prediction + ability of the previous byte UTF8-prefix on the next byte. The + prediction ability is calculated as Shannon entropy. Here we need + Shannon entropy instead of 'BitsEntropy' since the prefix will be + encoded with the remaining 6 bits of the following byte, and + BitsEntropy will assume that symbol to be stored alone using Huffman + coding. */ + +var kStaticContextMapContinuation = [64]uint32{ + 1, 1, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +} +var kStaticContextMapSimpleUTF8 = [64]uint32{ + 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +} + +func chooseContextMap(quality int, bigram_histo []uint32, num_literal_contexts *uint, literal_context_map *[]uint32) { + var monogram_histo = [3]uint32{0} + var two_prefix_histo = [6]uint32{0} + var total uint + var i uint + var dummy uint + var entropy [4]float64 + for i = 0; i < 9; i++ { + monogram_histo[i%3] += bigram_histo[i] + two_prefix_histo[i%6] += bigram_histo[i] + } + + entropy[1] = shannonEntropy(monogram_histo[:], 3, &dummy) + entropy[2] = (shannonEntropy(two_prefix_histo[:], 3, &dummy) + shannonEntropy(two_prefix_histo[3:], 3, &dummy)) + entropy[3] = 0 + for i = 0; i < 3; i++ { + entropy[3] += shannonEntropy(bigram_histo[3*i:], 3, &dummy) + } + + total = uint(monogram_histo[0] + monogram_histo[1] + monogram_histo[2]) + assert(total != 0) + entropy[0] = 1.0 / float64(total) + entropy[1] *= entropy[0] + entropy[2] *= entropy[0] + entropy[3] *= entropy[0] + + if quality < minQualityForHqContextModeling { + /* 3 context models is a bit slower, don't use it at lower qualities. */ + entropy[3] = entropy[1] * 10 + } + + /* If expected savings by symbol are less than 0.2 bits, skip the + context modeling -- in exchange for faster decoding speed. */ + if entropy[1]-entropy[2] < 0.2 && entropy[1]-entropy[3] < 0.2 { + *num_literal_contexts = 1 + } else if entropy[2]-entropy[3] < 0.02 { + *num_literal_contexts = 2 + *literal_context_map = kStaticContextMapSimpleUTF8[:] + } else { + *num_literal_contexts = 3 + *literal_context_map = kStaticContextMapContinuation[:] + } +} + +/* Decide if we want to use a more complex static context map containing 13 + context values, based on the entropy reduction of histograms over the + first 5 bits of literals. */ + +var kStaticContextMapComplexUTF8 = [64]uint32{ + 11, 11, 12, 12, /* 0 special */ + 0, 0, 0, 0, /* 4 lf */ + 1, 1, 9, 9, /* 8 space */ + 2, 2, 2, 2, /* !, first after space/lf and after something else. */ + 1, 1, 1, 1, /* " */ + 8, 3, 3, 3, /* % */ + 1, 1, 1, 1, /* ({[ */ + 2, 2, 2, 2, /* }]) */ + 8, 4, 4, 4, /* :; */ + 8, 7, 4, 4, /* . */ + 8, 0, 0, 0, /* > */ + 3, 3, 3, 3, /* [0..9] */ + 5, 5, 10, 5, /* [A-Z] */ + 5, 5, 10, 5, + 6, 6, 6, 6, /* [a-z] */ + 6, 6, 6, 6, +} + +func shouldUseComplexStaticContextMap(input []byte, start_pos uint, length uint, mask uint, quality int, size_hint uint, num_literal_contexts *uint, literal_context_map *[]uint32) bool { + /* Try the more complex static context map only for long data. */ + if size_hint < 1<<20 { + return false + } else { + var end_pos uint = start_pos + length + var combined_histo = [32]uint32{0} + var context_histo = [13][32]uint32{[32]uint32{0}} + var total uint32 = 0 + var entropy [3]float64 + var dummy uint + var i uint + var utf8_lut contextLUT = getContextLUT(contextUTF8) + /* To make entropy calculations faster and to fit on the stack, we collect + histograms over the 5 most significant bits of literals. One histogram + without context and 13 additional histograms for each context value. */ + for ; start_pos+64 <= end_pos; start_pos += 4096 { + var stride_end_pos uint = start_pos + 64 + var prev2 byte = input[start_pos&mask] + var prev1 byte = input[(start_pos+1)&mask] + var pos uint + + /* To make the analysis of the data faster we only examine 64 byte long + strides at every 4kB intervals. */ + for pos = start_pos + 2; pos < stride_end_pos; pos++ { + var literal byte = input[pos&mask] + var context byte = byte(kStaticContextMapComplexUTF8[getContext(prev1, prev2, utf8_lut)]) + total++ + combined_histo[literal>>3]++ + context_histo[context][literal>>3]++ + prev2 = prev1 + prev1 = literal + } + } + + entropy[1] = shannonEntropy(combined_histo[:], 32, &dummy) + entropy[2] = 0 + for i = 0; i < 13; i++ { + entropy[2] += shannonEntropy(context_histo[i][0:], 32, &dummy) + } + + entropy[0] = 1.0 / float64(total) + entropy[1] *= entropy[0] + entropy[2] *= entropy[0] + + /* The triggering heuristics below were tuned by compressing the individual + files of the silesia corpus. If we skip this kind of context modeling + for not very well compressible input (i.e. entropy using context modeling + is 60% of maximal entropy) or if expected savings by symbol are less + than 0.2 bits, then in every case when it triggers, the final compression + ratio is improved. Note however that this heuristics might be too strict + for some cases and could be tuned further. */ + if entropy[2] > 3.0 || entropy[1]-entropy[2] < 0.2 { + return false + } else { + *num_literal_contexts = 13 + *literal_context_map = kStaticContextMapComplexUTF8[:] + return true + } + } +} + +func decideOverLiteralContextModeling(input []byte, start_pos uint, length uint, mask uint, quality int, size_hint uint, num_literal_contexts *uint, literal_context_map *[]uint32) { + if quality < minQualityForContextModeling || length < 64 { + return + } else if shouldUseComplexStaticContextMap(input, start_pos, length, mask, quality, size_hint, num_literal_contexts, literal_context_map) { + } else /* Context map was already set, nothing else to do. */ + { + var end_pos uint = start_pos + length + /* Gather bi-gram data of the UTF8 byte prefixes. To make the analysis of + UTF8 data faster we only examine 64 byte long strides at every 4kB + intervals. */ + + var bigram_prefix_histo = [9]uint32{0} + for ; start_pos+64 <= end_pos; start_pos += 4096 { + var lut = [4]int{0, 0, 1, 2} + var stride_end_pos uint = start_pos + 64 + var prev int = lut[input[start_pos&mask]>>6] * 3 + var pos uint + for pos = start_pos + 1; pos < stride_end_pos; pos++ { + var literal byte = input[pos&mask] + bigram_prefix_histo[prev+lut[literal>>6]]++ + prev = lut[literal>>6] * 3 + } + } + + chooseContextMap(quality, bigram_prefix_histo[0:], num_literal_contexts, literal_context_map) + } +} + +func shouldCompress_encode(data []byte, mask uint, last_flush_pos uint64, bytes uint, num_literals uint, num_commands uint) bool { + /* TODO: find more precise minimal block overhead. */ + if bytes <= 2 { + return false + } + if num_commands < (bytes>>8)+2 { + if float64(num_literals) > 0.99*float64(bytes) { + var literal_histo = [256]uint32{0} + const kSampleRate uint32 = 13 + const kMinEntropy float64 = 7.92 + var bit_cost_threshold float64 = float64(bytes) * kMinEntropy / float64(kSampleRate) + var t uint = uint((uint32(bytes) + kSampleRate - 1) / kSampleRate) + var pos uint32 = uint32(last_flush_pos) + var i uint + for i = 0; i < t; i++ { + literal_histo[data[pos&uint32(mask)]]++ + pos += kSampleRate + } + + if bitsEntropy(literal_histo[:], 256) > bit_cost_threshold { + return false + } + } + } + + return true +} + +/* Chooses the literal context mode for a metablock */ +func chooseContextMode(params *encoderParams, data []byte, pos uint, mask uint, length uint) int { + /* We only do the computation for the option of something else than + CONTEXT_UTF8 for the highest qualities */ + if params.quality >= minQualityForHqBlockSplitting && !isMostlyUTF8(data, pos, mask, length, kMinUTF8Ratio) { + return contextSigned + } + + return contextUTF8 +} + +func writeMetaBlockInternal(data []byte, mask uint, last_flush_pos uint64, bytes uint, is_last bool, literal_context_mode int, params *encoderParams, prev_byte byte, prev_byte2 byte, num_literals uint, commands []command, saved_dist_cache []int, dist_cache []int, storage_ix *uint, storage []byte) { + var wrapped_last_flush_pos uint32 = wrapPosition(last_flush_pos) + var last_bytes uint16 + var last_bytes_bits byte + var literal_context_lut contextLUT = getContextLUT(literal_context_mode) + var block_params encoderParams = *params + + if bytes == 0 { + /* Write the ISLAST and ISEMPTY bits. */ + writeBits(2, 3, storage_ix, storage) + + *storage_ix = (*storage_ix + 7) &^ 7 + return + } + + if !shouldCompress_encode(data, mask, last_flush_pos, bytes, num_literals, uint(len(commands))) { + /* Restore the distance cache, as its last update by + CreateBackwardReferences is now unused. */ + copy(dist_cache, saved_dist_cache[:4]) + + storeUncompressedMetaBlock(is_last, data, uint(wrapped_last_flush_pos), mask, bytes, storage_ix, storage) + return + } + + assert(*storage_ix <= 14) + last_bytes = uint16(storage[1])<<8 | uint16(storage[0]) + last_bytes_bits = byte(*storage_ix) + if params.quality <= maxQualityForStaticEntropyCodes { + storeMetaBlockFast(data, uint(wrapped_last_flush_pos), bytes, mask, is_last, params, commands, storage_ix, storage) + } else if params.quality < minQualityForBlockSplit { + storeMetaBlockTrivial(data, uint(wrapped_last_flush_pos), bytes, mask, is_last, params, commands, storage_ix, storage) + } else { + mb := getMetaBlockSplit() + if params.quality < minQualityForHqBlockSplitting { + var num_literal_contexts uint = 1 + var literal_context_map []uint32 = nil + if !params.disable_literal_context_modeling { + decideOverLiteralContextModeling(data, uint(wrapped_last_flush_pos), bytes, mask, params.quality, params.size_hint, &num_literal_contexts, &literal_context_map) + } + + buildMetaBlockGreedy(data, uint(wrapped_last_flush_pos), mask, prev_byte, prev_byte2, literal_context_lut, num_literal_contexts, literal_context_map, commands, mb) + } else { + buildMetaBlock(data, uint(wrapped_last_flush_pos), mask, &block_params, prev_byte, prev_byte2, commands, literal_context_mode, mb) + } + + if params.quality >= minQualityForOptimizeHistograms { + /* The number of distance symbols effectively used for distance + histograms. It might be less than distance alphabet size + for "Large Window Brotli" (32-bit). */ + var num_effective_dist_codes uint32 = block_params.dist.alphabet_size + if num_effective_dist_codes > numHistogramDistanceSymbols { + num_effective_dist_codes = numHistogramDistanceSymbols + } + + optimizeHistograms(num_effective_dist_codes, mb) + } + + storeMetaBlock(data, uint(wrapped_last_flush_pos), bytes, mask, prev_byte, prev_byte2, is_last, &block_params, literal_context_mode, commands, mb, storage_ix, storage) + freeMetaBlockSplit(mb) + } + + if bytes+4 < *storage_ix>>3 { + /* Restore the distance cache and last byte. */ + copy(dist_cache, saved_dist_cache[:4]) + + storage[0] = byte(last_bytes) + storage[1] = byte(last_bytes >> 8) + *storage_ix = uint(last_bytes_bits) + storeUncompressedMetaBlock(is_last, data, uint(wrapped_last_flush_pos), mask, bytes, storage_ix, storage) + } +} + +func chooseDistanceParams(params *encoderParams) { + var distance_postfix_bits uint32 = 0 + var num_direct_distance_codes uint32 = 0 + + if params.quality >= minQualityForNonzeroDistanceParams { + var ndirect_msb uint32 + if params.mode == modeFont { + distance_postfix_bits = 1 + num_direct_distance_codes = 12 + } else { + distance_postfix_bits = params.dist.distance_postfix_bits + num_direct_distance_codes = params.dist.num_direct_distance_codes + } + + ndirect_msb = (num_direct_distance_codes >> distance_postfix_bits) & 0x0F + if distance_postfix_bits > maxNpostfix || num_direct_distance_codes > maxNdirect || ndirect_msb<>25)), (last_command.dist_prefix_&0x3FF == 0), &last_command.cmd_prefix_) + } +} + +/* + Processes the accumulated input data and writes + the new output meta-block to s.dest, if one has been + created (otherwise the processed input data is buffered internally). + If |is_last| or |force_flush| is true, an output meta-block is + always created. However, until |is_last| is true encoder may retain up + to 7 bits of the last byte of output. To force encoder to dump the remaining + bits use WriteMetadata() to append an empty meta-data block. + Returns false if the size of the input data is larger than + input_block_size(). +*/ +func encodeData(s *Writer, is_last bool, force_flush bool) bool { + var delta uint64 = unprocessedInputSize(s) + var bytes uint32 = uint32(delta) + var wrapped_last_processed_pos uint32 = wrapPosition(s.last_processed_pos_) + var data []byte + var mask uint32 + var literal_context_mode int + + data = s.ringbuffer_.buffer_ + mask = s.ringbuffer_.mask_ + + /* Adding more blocks after "last" block is forbidden. */ + if s.is_last_block_emitted_ { + return false + } + if is_last { + s.is_last_block_emitted_ = true + } + + if delta > uint64(inputBlockSize(s)) { + return false + } + + if s.params.quality == fastTwoPassCompressionQuality { + if s.command_buf_ == nil || cap(s.command_buf_) < int(kCompressFragmentTwoPassBlockSize) { + s.command_buf_ = make([]uint32, kCompressFragmentTwoPassBlockSize) + s.literal_buf_ = make([]byte, kCompressFragmentTwoPassBlockSize) + } else { + s.command_buf_ = s.command_buf_[:kCompressFragmentTwoPassBlockSize] + s.literal_buf_ = s.literal_buf_[:kCompressFragmentTwoPassBlockSize] + } + } + + if s.params.quality == fastOnePassCompressionQuality || s.params.quality == fastTwoPassCompressionQuality { + var storage []byte + var storage_ix uint = uint(s.last_bytes_bits_) + var table_size uint + var table []int + + if delta == 0 && !is_last { + /* We have no new input data and we don't have to finish the stream, so + nothing to do. */ + return true + } + + storage = s.getStorage(int(2*bytes + 503)) + storage[0] = byte(s.last_bytes_) + storage[1] = byte(s.last_bytes_ >> 8) + table = getHashTable(s, s.params.quality, uint(bytes), &table_size) + if s.params.quality == fastOnePassCompressionQuality { + compressFragmentFast(data[wrapped_last_processed_pos&mask:], uint(bytes), is_last, table, table_size, s.cmd_depths_[:], s.cmd_bits_[:], &s.cmd_code_numbits_, s.cmd_code_[:], &storage_ix, storage) + } else { + compressFragmentTwoPass(data[wrapped_last_processed_pos&mask:], uint(bytes), is_last, s.command_buf_, s.literal_buf_, table, table_size, &storage_ix, storage) + } + + s.last_bytes_ = uint16(storage[storage_ix>>3]) + s.last_bytes_bits_ = byte(storage_ix & 7) + updateLastProcessedPos(s) + s.writeOutput(storage[:storage_ix>>3]) + return true + } + { + /* Theoretical max number of commands is 1 per 2 bytes. */ + newsize := len(s.commands) + int(bytes)/2 + 1 + if newsize > cap(s.commands) { + /* Reserve a bit more memory to allow merging with a next block + without reallocation: that would impact speed. */ + newsize += int(bytes/4) + 16 + + new_commands := make([]command, len(s.commands), newsize) + if s.commands != nil { + copy(new_commands, s.commands) + } + + s.commands = new_commands + } + } + + initOrStitchToPreviousBlock(&s.hasher_, data, uint(mask), &s.params, uint(wrapped_last_processed_pos), uint(bytes), is_last) + + literal_context_mode = chooseContextMode(&s.params, data, uint(wrapPosition(s.last_flush_pos_)), uint(mask), uint(s.input_pos_-s.last_flush_pos_)) + + if len(s.commands) != 0 && s.last_insert_len_ == 0 { + extendLastCommand(s, &bytes, &wrapped_last_processed_pos) + } + + if s.params.quality == zopflificationQuality { + assert(s.params.hasher.type_ == 10) + createZopfliBackwardReferences(uint(bytes), uint(wrapped_last_processed_pos), data, uint(mask), &s.params, s.hasher_.(*h10), s.dist_cache_[:], &s.last_insert_len_, &s.commands, &s.num_literals_) + } else if s.params.quality == hqZopflificationQuality { + assert(s.params.hasher.type_ == 10) + createHqZopfliBackwardReferences(uint(bytes), uint(wrapped_last_processed_pos), data, uint(mask), &s.params, s.hasher_, s.dist_cache_[:], &s.last_insert_len_, &s.commands, &s.num_literals_) + } else { + createBackwardReferences(uint(bytes), uint(wrapped_last_processed_pos), data, uint(mask), &s.params, s.hasher_, s.dist_cache_[:], &s.last_insert_len_, &s.commands, &s.num_literals_) + } + { + var max_length uint = maxMetablockSize(&s.params) + var max_literals uint = max_length / 8 + max_commands := int(max_length / 8) + var processed_bytes uint = uint(s.input_pos_ - s.last_flush_pos_) + var next_input_fits_metablock bool = (processed_bytes+inputBlockSize(s) <= max_length) + var should_flush bool = (s.params.quality < minQualityForBlockSplit && s.num_literals_+uint(len(s.commands)) >= maxNumDelayedSymbols) + /* If maximal possible additional block doesn't fit metablock, flush now. */ + /* TODO: Postpone decision until next block arrives? */ + + /* If block splitting is not used, then flush as soon as there is some + amount of commands / literals produced. */ + if !is_last && !force_flush && !should_flush && next_input_fits_metablock && s.num_literals_ < max_literals && len(s.commands) < max_commands { + /* Merge with next input block. Everything will happen later. */ + if updateLastProcessedPos(s) { + hasherReset(s.hasher_) + } + + return true + } + } + + /* Create the last insert-only command. */ + if s.last_insert_len_ > 0 { + s.commands = append(s.commands, makeInsertCommand(s.last_insert_len_)) + s.num_literals_ += s.last_insert_len_ + s.last_insert_len_ = 0 + } + + if !is_last && s.input_pos_ == s.last_flush_pos_ { + /* We have no new input data and we don't have to finish the stream, so + nothing to do. */ + return true + } + + assert(s.input_pos_ >= s.last_flush_pos_) + assert(s.input_pos_ > s.last_flush_pos_ || is_last) + assert(s.input_pos_-s.last_flush_pos_ <= 1<<24) + { + var metablock_size uint32 = uint32(s.input_pos_ - s.last_flush_pos_) + var storage []byte = s.getStorage(int(2*metablock_size + 503)) + var storage_ix uint = uint(s.last_bytes_bits_) + storage[0] = byte(s.last_bytes_) + storage[1] = byte(s.last_bytes_ >> 8) + writeMetaBlockInternal(data, uint(mask), s.last_flush_pos_, uint(metablock_size), is_last, literal_context_mode, &s.params, s.prev_byte_, s.prev_byte2_, s.num_literals_, s.commands, s.saved_dist_cache_[:], s.dist_cache_[:], &storage_ix, storage) + s.last_bytes_ = uint16(storage[storage_ix>>3]) + s.last_bytes_bits_ = byte(storage_ix & 7) + s.last_flush_pos_ = s.input_pos_ + if updateLastProcessedPos(s) { + hasherReset(s.hasher_) + } + + if s.last_flush_pos_ > 0 { + s.prev_byte_ = data[(uint32(s.last_flush_pos_)-1)&mask] + } + + if s.last_flush_pos_ > 1 { + s.prev_byte2_ = data[uint32(s.last_flush_pos_-2)&mask] + } + + s.commands = s.commands[:0] + s.num_literals_ = 0 + + /* Save the state of the distance cache in case we need to restore it for + emitting an uncompressed block. */ + copy(s.saved_dist_cache_[:], s.dist_cache_[:]) + + s.writeOutput(storage[:storage_ix>>3]) + return true + } +} + +/* Dumps remaining output bits and metadata header to |header|. + Returns number of produced bytes. + REQUIRED: |header| should be 8-byte aligned and at least 16 bytes long. + REQUIRED: |block_size| <= (1 << 24). */ +func writeMetadataHeader(s *Writer, block_size uint, header []byte) uint { + storage_ix := uint(s.last_bytes_bits_) + header[0] = byte(s.last_bytes_) + header[1] = byte(s.last_bytes_ >> 8) + s.last_bytes_ = 0 + s.last_bytes_bits_ = 0 + + writeBits(1, 0, &storage_ix, header) + writeBits(2, 3, &storage_ix, header) + writeBits(1, 0, &storage_ix, header) + if block_size == 0 { + writeBits(2, 0, &storage_ix, header) + } else { + var nbits uint32 + if block_size == 1 { + nbits = 0 + } else { + nbits = log2FloorNonZero(uint(uint32(block_size)-1)) + 1 + } + var nbytes uint32 = (nbits + 7) / 8 + writeBits(2, uint64(nbytes), &storage_ix, header) + writeBits(uint(8*nbytes), uint64(block_size)-1, &storage_ix, header) + } + + return (storage_ix + 7) >> 3 +} + +func injectBytePaddingBlock(s *Writer) { + var seal uint32 = uint32(s.last_bytes_) + var seal_bits uint = uint(s.last_bytes_bits_) + s.last_bytes_ = 0 + s.last_bytes_bits_ = 0 + + /* is_last = 0, data_nibbles = 11, reserved = 0, meta_nibbles = 00 */ + seal |= 0x6 << seal_bits + + seal_bits += 6 + + destination := s.tiny_buf_.u8[:] + + destination[0] = byte(seal) + if seal_bits > 8 { + destination[1] = byte(seal >> 8) + } + if seal_bits > 16 { + destination[2] = byte(seal >> 16) + } + s.writeOutput(destination[:(seal_bits+7)>>3]) +} + +func checkFlushComplete(s *Writer) { + if s.stream_state_ == streamFlushRequested && s.err == nil { + s.stream_state_ = streamProcessing + } +} + +func encoderCompressStreamFast(s *Writer, op int, available_in *uint, next_in *[]byte) bool { + var block_size_limit uint = uint(1) << s.params.lgwin + var buf_size uint = brotli_min_size_t(kCompressFragmentTwoPassBlockSize, brotli_min_size_t(*available_in, block_size_limit)) + var command_buf []uint32 = nil + var literal_buf []byte = nil + if s.params.quality != fastOnePassCompressionQuality && s.params.quality != fastTwoPassCompressionQuality { + return false + } + + if s.params.quality == fastTwoPassCompressionQuality { + if s.command_buf_ == nil || cap(s.command_buf_) < int(buf_size) { + s.command_buf_ = make([]uint32, buf_size) + s.literal_buf_ = make([]byte, buf_size) + } else { + s.command_buf_ = s.command_buf_[:buf_size] + s.literal_buf_ = s.literal_buf_[:buf_size] + } + + command_buf = s.command_buf_ + literal_buf = s.literal_buf_ + } + + for { + if s.stream_state_ == streamFlushRequested && s.last_bytes_bits_ != 0 { + injectBytePaddingBlock(s) + continue + } + + /* Compress block only when stream is not + finished, there is no pending flush request, and there is either + additional input or pending operation. */ + if s.stream_state_ == streamProcessing && (*available_in != 0 || op != int(operationProcess)) { + var block_size uint = brotli_min_size_t(block_size_limit, *available_in) + var is_last bool = (*available_in == block_size) && (op == int(operationFinish)) + var force_flush bool = (*available_in == block_size) && (op == int(operationFlush)) + var max_out_size uint = 2*block_size + 503 + var storage []byte = nil + var storage_ix uint = uint(s.last_bytes_bits_) + var table_size uint + var table []int + + if force_flush && block_size == 0 { + s.stream_state_ = streamFlushRequested + continue + } + + storage = s.getStorage(int(max_out_size)) + + storage[0] = byte(s.last_bytes_) + storage[1] = byte(s.last_bytes_ >> 8) + table = getHashTable(s, s.params.quality, block_size, &table_size) + + if s.params.quality == fastOnePassCompressionQuality { + compressFragmentFast(*next_in, block_size, is_last, table, table_size, s.cmd_depths_[:], s.cmd_bits_[:], &s.cmd_code_numbits_, s.cmd_code_[:], &storage_ix, storage) + } else { + compressFragmentTwoPass(*next_in, block_size, is_last, command_buf, literal_buf, table, table_size, &storage_ix, storage) + } + + *next_in = (*next_in)[block_size:] + *available_in -= block_size + var out_bytes uint = storage_ix >> 3 + s.writeOutput(storage[:out_bytes]) + + s.last_bytes_ = uint16(storage[storage_ix>>3]) + s.last_bytes_bits_ = byte(storage_ix & 7) + + if force_flush { + s.stream_state_ = streamFlushRequested + } + if is_last { + s.stream_state_ = streamFinished + } + continue + } + + break + } + + checkFlushComplete(s) + return true +} + +func processMetadata(s *Writer, available_in *uint, next_in *[]byte) bool { + if *available_in > 1<<24 { + return false + } + + /* Switch to metadata block workflow, if required. */ + if s.stream_state_ == streamProcessing { + s.remaining_metadata_bytes_ = uint32(*available_in) + s.stream_state_ = streamMetadataHead + } + + if s.stream_state_ != streamMetadataHead && s.stream_state_ != streamMetadataBody { + return false + } + + for { + if s.stream_state_ == streamFlushRequested && s.last_bytes_bits_ != 0 { + injectBytePaddingBlock(s) + continue + } + + if s.input_pos_ != s.last_flush_pos_ { + var result bool = encodeData(s, false, true) + if !result { + return false + } + continue + } + + if s.stream_state_ == streamMetadataHead { + n := writeMetadataHeader(s, uint(s.remaining_metadata_bytes_), s.tiny_buf_.u8[:]) + s.writeOutput(s.tiny_buf_.u8[:n]) + s.stream_state_ = streamMetadataBody + continue + } else { + /* Exit workflow only when there is no more input and no more output. + Otherwise client may continue producing empty metadata blocks. */ + if s.remaining_metadata_bytes_ == 0 { + s.remaining_metadata_bytes_ = math.MaxUint32 + s.stream_state_ = streamProcessing + break + } + + /* This guarantees progress in "TakeOutput" workflow. */ + var c uint32 = brotli_min_uint32_t(s.remaining_metadata_bytes_, 16) + copy(s.tiny_buf_.u8[:], (*next_in)[:c]) + *next_in = (*next_in)[c:] + *available_in -= uint(c) + s.remaining_metadata_bytes_ -= c + s.writeOutput(s.tiny_buf_.u8[:c]) + + continue + } + } + + return true +} + +func updateSizeHint(s *Writer, available_in uint) { + if s.params.size_hint == 0 { + var delta uint64 = unprocessedInputSize(s) + var tail uint64 = uint64(available_in) + var limit uint32 = 1 << 30 + var total uint32 + if (delta >= uint64(limit)) || (tail >= uint64(limit)) || ((delta + tail) >= uint64(limit)) { + total = limit + } else { + total = uint32(delta + tail) + } + + s.params.size_hint = uint(total) + } +} + +func encoderCompressStream(s *Writer, op int, available_in *uint, next_in *[]byte) bool { + if !ensureInitialized(s) { + return false + } + + /* Unfinished metadata block; check requirements. */ + if s.remaining_metadata_bytes_ != math.MaxUint32 { + if uint32(*available_in) != s.remaining_metadata_bytes_ { + return false + } + if op != int(operationEmitMetadata) { + return false + } + } + + if op == int(operationEmitMetadata) { + updateSizeHint(s, 0) /* First data metablock might be emitted here. */ + return processMetadata(s, available_in, next_in) + } + + if s.stream_state_ == streamMetadataHead || s.stream_state_ == streamMetadataBody { + return false + } + + if s.stream_state_ != streamProcessing && *available_in != 0 { + return false + } + + if s.params.quality == fastOnePassCompressionQuality || s.params.quality == fastTwoPassCompressionQuality { + return encoderCompressStreamFast(s, op, available_in, next_in) + } + + for { + var remaining_block_size uint = remainingInputBlockSize(s) + + if remaining_block_size != 0 && *available_in != 0 { + var copy_input_size uint = brotli_min_size_t(remaining_block_size, *available_in) + copyInputToRingBuffer(s, copy_input_size, *next_in) + *next_in = (*next_in)[copy_input_size:] + *available_in -= copy_input_size + continue + } + + if s.stream_state_ == streamFlushRequested && s.last_bytes_bits_ != 0 { + injectBytePaddingBlock(s) + continue + } + + /* Compress data only when stream is not + finished and there is no pending flush request. */ + if s.stream_state_ == streamProcessing { + if remaining_block_size == 0 || op != int(operationProcess) { + var is_last bool = ((*available_in == 0) && op == int(operationFinish)) + var force_flush bool = ((*available_in == 0) && op == int(operationFlush)) + var result bool + updateSizeHint(s, *available_in) + result = encodeData(s, is_last, force_flush) + if !result { + return false + } + if force_flush { + s.stream_state_ = streamFlushRequested + } + if is_last { + s.stream_state_ = streamFinished + } + continue + } + } + + break + } + + checkFlushComplete(s) + return true +} + +func (w *Writer) writeOutput(data []byte) { + if w.err != nil { + return + } + + _, w.err = w.dst.Write(data) + if w.err == nil { + checkFlushComplete(w) + } +} diff --git a/vendor/github.com/andybalholm/brotli/encoder.go b/vendor/github.com/andybalholm/brotli/encoder.go new file mode 100644 index 000000000..192838259 --- /dev/null +++ b/vendor/github.com/andybalholm/brotli/encoder.go @@ -0,0 +1,177 @@ +package brotli + +import "github.com/andybalholm/brotli/matchfinder" + +// An Encoder implements the matchfinder.Encoder interface, writing in Brotli format. +type Encoder struct { + wroteHeader bool + bw bitWriter + distCache []distanceCode +} + +func (e *Encoder) Reset() { + e.wroteHeader = false + e.bw = bitWriter{} +} + +func (e *Encoder) Encode(dst []byte, src []byte, matches []matchfinder.Match, lastBlock bool) []byte { + e.bw.dst = dst + if !e.wroteHeader { + e.bw.writeBits(4, 15) + e.wroteHeader = true + } + + if len(src) == 0 { + if lastBlock { + e.bw.writeBits(2, 3) // islast + isempty + e.bw.jumpToByteBoundary() + return e.bw.dst + } + return dst + } + + var literalHisto [256]uint32 + var commandHisto [704]uint32 + var distanceHisto [64]uint32 + literalCount := 0 + commandCount := 0 + distanceCount := 0 + + if len(e.distCache) < len(matches) { + e.distCache = make([]distanceCode, len(matches)) + } + + // first pass: build the histograms + pos := 0 + + // d is the ring buffer of the last 4 distances. + d := [4]int{-10, -10, -10, -10} + for i, m := range matches { + if m.Unmatched > 0 { + for _, c := range src[pos : pos+m.Unmatched] { + literalHisto[c]++ + } + literalCount += m.Unmatched + } + + insertCode := getInsertLengthCode(uint(m.Unmatched)) + copyCode := getCopyLengthCode(uint(m.Length)) + if m.Length == 0 { + // If the stream ends with unmatched bytes, we need a dummy copy length. + copyCode = 2 + } + command := combineLengthCodes(insertCode, copyCode, false) + commandHisto[command]++ + commandCount++ + + if command >= 128 && m.Length != 0 { + var distCode distanceCode + switch m.Distance { + case d[3]: + distCode.code = 0 + case d[2]: + distCode.code = 1 + case d[1]: + distCode.code = 2 + case d[0]: + distCode.code = 3 + case d[3] - 1: + distCode.code = 4 + case d[3] + 1: + distCode.code = 5 + case d[3] - 2: + distCode.code = 6 + case d[3] + 2: + distCode.code = 7 + case d[3] - 3: + distCode.code = 8 + case d[3] + 3: + distCode.code = 9 + + // In my testing, codes 10–15 actually reduced the compression ratio. + + default: + distCode = getDistanceCode(m.Distance) + } + e.distCache[i] = distCode + distanceHisto[distCode.code]++ + distanceCount++ + if distCode.code != 0 { + d[0], d[1], d[2], d[3] = d[1], d[2], d[3], m.Distance + } + } + + pos += m.Unmatched + m.Length + } + + storeMetaBlockHeaderBW(uint(len(src)), false, &e.bw) + e.bw.writeBits(13, 0) + + var literalDepths [256]byte + var literalBits [256]uint16 + buildAndStoreHuffmanTreeFastBW(literalHisto[:], uint(literalCount), 8, literalDepths[:], literalBits[:], &e.bw) + + var commandDepths [704]byte + var commandBits [704]uint16 + buildAndStoreHuffmanTreeFastBW(commandHisto[:], uint(commandCount), 10, commandDepths[:], commandBits[:], &e.bw) + + var distanceDepths [64]byte + var distanceBits [64]uint16 + buildAndStoreHuffmanTreeFastBW(distanceHisto[:], uint(distanceCount), 6, distanceDepths[:], distanceBits[:], &e.bw) + + pos = 0 + for i, m := range matches { + insertCode := getInsertLengthCode(uint(m.Unmatched)) + copyCode := getCopyLengthCode(uint(m.Length)) + if m.Length == 0 { + // If the stream ends with unmatched bytes, we need a dummy copy length. + copyCode = 2 + } + command := combineLengthCodes(insertCode, copyCode, false) + e.bw.writeBits(uint(commandDepths[command]), uint64(commandBits[command])) + if kInsExtra[insertCode] > 0 { + e.bw.writeBits(uint(kInsExtra[insertCode]), uint64(m.Unmatched)-uint64(kInsBase[insertCode])) + } + if kCopyExtra[copyCode] > 0 { + e.bw.writeBits(uint(kCopyExtra[copyCode]), uint64(m.Length)-uint64(kCopyBase[copyCode])) + } + + if m.Unmatched > 0 { + for _, c := range src[pos : pos+m.Unmatched] { + e.bw.writeBits(uint(literalDepths[c]), uint64(literalBits[c])) + } + } + + if command >= 128 && m.Length != 0 { + distCode := e.distCache[i] + e.bw.writeBits(uint(distanceDepths[distCode.code]), uint64(distanceBits[distCode.code])) + if distCode.nExtra > 0 { + e.bw.writeBits(distCode.nExtra, distCode.extraBits) + } + } + + pos += m.Unmatched + m.Length + } + + if lastBlock { + e.bw.writeBits(2, 3) // islast + isempty + e.bw.jumpToByteBoundary() + } + return e.bw.dst +} + +type distanceCode struct { + code int + nExtra uint + extraBits uint64 +} + +func getDistanceCode(distance int) distanceCode { + d := distance + 3 + nbits := log2FloorNonZero(uint(d)) - 1 + prefix := (d >> nbits) & 1 + offset := (2 + prefix) << nbits + distcode := int(2*(nbits-1)) + prefix + 16 + extra := d - offset + return distanceCode{distcode, uint(nbits), uint64(extra)} +} diff --git a/vendor/github.com/andybalholm/brotli/encoder_dict.go b/vendor/github.com/andybalholm/brotli/encoder_dict.go new file mode 100644 index 000000000..55c051c62 --- /dev/null +++ b/vendor/github.com/andybalholm/brotli/encoder_dict.go @@ -0,0 +1,22 @@ +package brotli + +/* Dictionary data (words and transforms) for 1 possible context */ +type encoderDictionary struct { + words *dictionary + cutoffTransformsCount uint32 + cutoffTransforms uint64 + hash_table []uint16 + buckets []uint16 + dict_words []dictWord +} + +func initEncoderDictionary(dict *encoderDictionary) { + dict.words = getDictionary() + + dict.hash_table = kStaticDictionaryHash[:] + dict.buckets = kStaticDictionaryBuckets[:] + dict.dict_words = kStaticDictionaryWords[:] + + dict.cutoffTransformsCount = kCutoffTransformsCount + dict.cutoffTransforms = kCutoffTransforms +} diff --git a/vendor/github.com/andybalholm/brotli/entropy_encode.go b/vendor/github.com/andybalholm/brotli/entropy_encode.go new file mode 100644 index 000000000..3f469a3dd --- /dev/null +++ b/vendor/github.com/andybalholm/brotli/entropy_encode.go @@ -0,0 +1,592 @@ +package brotli + +import "math" + +/* Copyright 2010 Google Inc. All Rights Reserved. + + Distributed under MIT license. + See file LICENSE for detail or copy at https://opensource.org/licenses/MIT +*/ + +/* Entropy encoding (Huffman) utilities. */ + +/* A node of a Huffman tree. */ +type huffmanTree struct { + total_count_ uint32 + index_left_ int16 + index_right_or_value_ int16 +} + +func initHuffmanTree(self *huffmanTree, count uint32, left int16, right int16) { + self.total_count_ = count + self.index_left_ = left + self.index_right_or_value_ = right +} + +/* Input size optimized Shell sort. */ +type huffmanTreeComparator func(huffmanTree, huffmanTree) bool + +var sortHuffmanTreeItems_gaps = []uint{132, 57, 23, 10, 4, 1} + +func sortHuffmanTreeItems(items []huffmanTree, n uint, comparator huffmanTreeComparator) { + if n < 13 { + /* Insertion sort. */ + var i uint + for i = 1; i < n; i++ { + var tmp huffmanTree = items[i] + var k uint = i + var j uint = i - 1 + for comparator(tmp, items[j]) { + items[k] = items[j] + k = j + if j == 0 { + break + } + j-- + } + + items[k] = tmp + } + + return + } else { + var g int + if n < 57 { + g = 2 + } else { + g = 0 + } + for ; g < 6; g++ { + var gap uint = sortHuffmanTreeItems_gaps[g] + var i uint + for i = gap; i < n; i++ { + var j uint = i + var tmp huffmanTree = items[i] + for ; j >= gap && comparator(tmp, items[j-gap]); j -= gap { + items[j] = items[j-gap] + } + + items[j] = tmp + } + } + } +} + +/* Returns 1 if assignment of depths succeeded, otherwise 0. */ +func setDepth(p0 int, pool []huffmanTree, depth []byte, max_depth int) bool { + var stack [16]int + var level int = 0 + var p int = p0 + assert(max_depth <= 15) + stack[0] = -1 + for { + if pool[p].index_left_ >= 0 { + level++ + if level > max_depth { + return false + } + stack[level] = int(pool[p].index_right_or_value_) + p = int(pool[p].index_left_) + continue + } else { + depth[pool[p].index_right_or_value_] = byte(level) + } + + for level >= 0 && stack[level] == -1 { + level-- + } + if level < 0 { + return true + } + p = stack[level] + stack[level] = -1 + } +} + +/* Sort the root nodes, least popular first. */ +func sortHuffmanTree(v0 huffmanTree, v1 huffmanTree) bool { + if v0.total_count_ != v1.total_count_ { + return v0.total_count_ < v1.total_count_ + } + + return v0.index_right_or_value_ > v1.index_right_or_value_ +} + +/* This function will create a Huffman tree. + + The catch here is that the tree cannot be arbitrarily deep. + Brotli specifies a maximum depth of 15 bits for "code trees" + and 7 bits for "code length code trees." + + count_limit is the value that is to be faked as the minimum value + and this minimum value is raised until the tree matches the + maximum length requirement. + + This algorithm is not of excellent performance for very long data blocks, + especially when population counts are longer than 2**tree_limit, but + we are not planning to use this with extremely long blocks. + + See http://en.wikipedia.org/wiki/Huffman_coding */ +func createHuffmanTree(data []uint32, length uint, tree_limit int, tree []huffmanTree, depth []byte) { + var count_limit uint32 + var sentinel huffmanTree + initHuffmanTree(&sentinel, math.MaxUint32, -1, -1) + + /* For block sizes below 64 kB, we never need to do a second iteration + of this loop. Probably all of our block sizes will be smaller than + that, so this loop is mostly of academic interest. If we actually + would need this, we would be better off with the Katajainen algorithm. */ + for count_limit = 1; ; count_limit *= 2 { + var n uint = 0 + var i uint + var j uint + var k uint + for i = length; i != 0; { + i-- + if data[i] != 0 { + var count uint32 = brotli_max_uint32_t(data[i], count_limit) + initHuffmanTree(&tree[n], count, -1, int16(i)) + n++ + } + } + + if n == 1 { + depth[tree[0].index_right_or_value_] = 1 /* Only one element. */ + break + } + + sortHuffmanTreeItems(tree, n, huffmanTreeComparator(sortHuffmanTree)) + + /* The nodes are: + [0, n): the sorted leaf nodes that we start with. + [n]: we add a sentinel here. + [n + 1, 2n): new parent nodes are added here, starting from + (n+1). These are naturally in ascending order. + [2n]: we add a sentinel at the end as well. + There will be (2n+1) elements at the end. */ + tree[n] = sentinel + + tree[n+1] = sentinel + + i = 0 /* Points to the next leaf node. */ + j = n + 1 /* Points to the next non-leaf node. */ + for k = n - 1; k != 0; k-- { + var left uint + var right uint + if tree[i].total_count_ <= tree[j].total_count_ { + left = i + i++ + } else { + left = j + j++ + } + + if tree[i].total_count_ <= tree[j].total_count_ { + right = i + i++ + } else { + right = j + j++ + } + { + /* The sentinel node becomes the parent node. */ + var j_end uint = 2*n - k + tree[j_end].total_count_ = tree[left].total_count_ + tree[right].total_count_ + tree[j_end].index_left_ = int16(left) + tree[j_end].index_right_or_value_ = int16(right) + + /* Add back the last sentinel node. */ + tree[j_end+1] = sentinel + } + } + + if setDepth(int(2*n-1), tree[0:], depth, tree_limit) { + /* We need to pack the Huffman tree in tree_limit bits. If this was not + successful, add fake entities to the lowest values and retry. */ + break + } + } +} + +func reverse(v []byte, start uint, end uint) { + end-- + for start < end { + var tmp byte = v[start] + v[start] = v[end] + v[end] = tmp + start++ + end-- + } +} + +func writeHuffmanTreeRepetitions(previous_value byte, value byte, repetitions uint, tree_size *uint, tree []byte, extra_bits_data []byte) { + assert(repetitions > 0) + if previous_value != value { + tree[*tree_size] = value + extra_bits_data[*tree_size] = 0 + (*tree_size)++ + repetitions-- + } + + if repetitions == 7 { + tree[*tree_size] = value + extra_bits_data[*tree_size] = 0 + (*tree_size)++ + repetitions-- + } + + if repetitions < 3 { + var i uint + for i = 0; i < repetitions; i++ { + tree[*tree_size] = value + extra_bits_data[*tree_size] = 0 + (*tree_size)++ + } + } else { + var start uint = *tree_size + repetitions -= 3 + for { + tree[*tree_size] = repeatPreviousCodeLength + extra_bits_data[*tree_size] = byte(repetitions & 0x3) + (*tree_size)++ + repetitions >>= 2 + if repetitions == 0 { + break + } + + repetitions-- + } + + reverse(tree, start, *tree_size) + reverse(extra_bits_data, start, *tree_size) + } +} + +func writeHuffmanTreeRepetitionsZeros(repetitions uint, tree_size *uint, tree []byte, extra_bits_data []byte) { + if repetitions == 11 { + tree[*tree_size] = 0 + extra_bits_data[*tree_size] = 0 + (*tree_size)++ + repetitions-- + } + + if repetitions < 3 { + var i uint + for i = 0; i < repetitions; i++ { + tree[*tree_size] = 0 + extra_bits_data[*tree_size] = 0 + (*tree_size)++ + } + } else { + var start uint = *tree_size + repetitions -= 3 + for { + tree[*tree_size] = repeatZeroCodeLength + extra_bits_data[*tree_size] = byte(repetitions & 0x7) + (*tree_size)++ + repetitions >>= 3 + if repetitions == 0 { + break + } + + repetitions-- + } + + reverse(tree, start, *tree_size) + reverse(extra_bits_data, start, *tree_size) + } +} + +/* Change the population counts in a way that the consequent + Huffman tree compression, especially its RLE-part will be more + likely to compress this data more efficiently. + + length contains the size of the histogram. + counts contains the population counts. + good_for_rle is a buffer of at least length size */ +func optimizeHuffmanCountsForRLE(length uint, counts []uint32, good_for_rle []byte) { + var nonzero_count uint = 0 + var stride uint + var limit uint + var sum uint + var streak_limit uint = 1240 + var i uint + /* Let's make the Huffman code more compatible with RLE encoding. */ + for i = 0; i < length; i++ { + if counts[i] != 0 { + nonzero_count++ + } + } + + if nonzero_count < 16 { + return + } + + for length != 0 && counts[length-1] == 0 { + length-- + } + + if length == 0 { + return /* All zeros. */ + } + + /* Now counts[0..length - 1] does not have trailing zeros. */ + { + var nonzeros uint = 0 + var smallest_nonzero uint32 = 1 << 30 + for i = 0; i < length; i++ { + if counts[i] != 0 { + nonzeros++ + if smallest_nonzero > counts[i] { + smallest_nonzero = counts[i] + } + } + } + + if nonzeros < 5 { + /* Small histogram will model it well. */ + return + } + + if smallest_nonzero < 4 { + var zeros uint = length - nonzeros + if zeros < 6 { + for i = 1; i < length-1; i++ { + if counts[i-1] != 0 && counts[i] == 0 && counts[i+1] != 0 { + counts[i] = 1 + } + } + } + } + + if nonzeros < 28 { + return + } + } + + /* 2) Let's mark all population counts that already can be encoded + with an RLE code. */ + for i := 0; i < int(length); i++ { + good_for_rle[i] = 0 + } + { + var symbol uint32 = counts[0] + /* Let's not spoil any of the existing good RLE codes. + Mark any seq of 0's that is longer as 5 as a good_for_rle. + Mark any seq of non-0's that is longer as 7 as a good_for_rle. */ + + var step uint = 0 + for i = 0; i <= length; i++ { + if i == length || counts[i] != symbol { + if (symbol == 0 && step >= 5) || (symbol != 0 && step >= 7) { + var k uint + for k = 0; k < step; k++ { + good_for_rle[i-k-1] = 1 + } + } + + step = 1 + if i != length { + symbol = counts[i] + } + } else { + step++ + } + } + } + + /* 3) Let's replace those population counts that lead to more RLE codes. + Math here is in 24.8 fixed point representation. */ + stride = 0 + + limit = uint(256*(counts[0]+counts[1]+counts[2])/3 + 420) + sum = 0 + for i = 0; i <= length; i++ { + if i == length || good_for_rle[i] != 0 || (i != 0 && good_for_rle[i-1] != 0) || (256*counts[i]-uint32(limit)+uint32(streak_limit)) >= uint32(2*streak_limit) { + if stride >= 4 || (stride >= 3 && sum == 0) { + var k uint + var count uint = (sum + stride/2) / stride + /* The stride must end, collapse what we have, if we have enough (4). */ + if count == 0 { + count = 1 + } + + if sum == 0 { + /* Don't make an all zeros stride to be upgraded to ones. */ + count = 0 + } + + for k = 0; k < stride; k++ { + /* We don't want to change value at counts[i], + that is already belonging to the next stride. Thus - 1. */ + counts[i-k-1] = uint32(count) + } + } + + stride = 0 + sum = 0 + if i < length-2 { + /* All interesting strides have a count of at least 4, */ + /* at least when non-zeros. */ + limit = uint(256*(counts[i]+counts[i+1]+counts[i+2])/3 + 420) + } else if i < length { + limit = uint(256 * counts[i]) + } else { + limit = 0 + } + } + + stride++ + if i != length { + sum += uint(counts[i]) + if stride >= 4 { + limit = (256*sum + stride/2) / stride + } + + if stride == 4 { + limit += 120 + } + } + } +} + +func decideOverRLEUse(depth []byte, length uint, use_rle_for_non_zero *bool, use_rle_for_zero *bool) { + var total_reps_zero uint = 0 + var total_reps_non_zero uint = 0 + var count_reps_zero uint = 1 + var count_reps_non_zero uint = 1 + var i uint + for i = 0; i < length; { + var value byte = depth[i] + var reps uint = 1 + var k uint + for k = i + 1; k < length && depth[k] == value; k++ { + reps++ + } + + if reps >= 3 && value == 0 { + total_reps_zero += reps + count_reps_zero++ + } + + if reps >= 4 && value != 0 { + total_reps_non_zero += reps + count_reps_non_zero++ + } + + i += reps + } + + *use_rle_for_non_zero = total_reps_non_zero > count_reps_non_zero*2 + *use_rle_for_zero = total_reps_zero > count_reps_zero*2 +} + +/* Write a Huffman tree from bit depths into the bit-stream representation + of a Huffman tree. The generated Huffman tree is to be compressed once + more using a Huffman tree */ +func writeHuffmanTree(depth []byte, length uint, tree_size *uint, tree []byte, extra_bits_data []byte) { + var previous_value byte = initialRepeatedCodeLength + var i uint + var use_rle_for_non_zero bool = false + var use_rle_for_zero bool = false + var new_length uint = length + /* Throw away trailing zeros. */ + for i = 0; i < length; i++ { + if depth[length-i-1] == 0 { + new_length-- + } else { + break + } + } + + /* First gather statistics on if it is a good idea to do RLE. */ + if length > 50 { + /* Find RLE coding for longer codes. + Shorter codes seem not to benefit from RLE. */ + decideOverRLEUse(depth, new_length, &use_rle_for_non_zero, &use_rle_for_zero) + } + + /* Actual RLE coding. */ + for i = 0; i < new_length; { + var value byte = depth[i] + var reps uint = 1 + if (value != 0 && use_rle_for_non_zero) || (value == 0 && use_rle_for_zero) { + var k uint + for k = i + 1; k < new_length && depth[k] == value; k++ { + reps++ + } + } + + if value == 0 { + writeHuffmanTreeRepetitionsZeros(reps, tree_size, tree, extra_bits_data) + } else { + writeHuffmanTreeRepetitions(previous_value, value, reps, tree_size, tree, extra_bits_data) + previous_value = value + } + + i += reps + } +} + +var reverseBits_kLut = [16]uint{ + 0x00, + 0x08, + 0x04, + 0x0C, + 0x02, + 0x0A, + 0x06, + 0x0E, + 0x01, + 0x09, + 0x05, + 0x0D, + 0x03, + 0x0B, + 0x07, + 0x0F, +} + +func reverseBits(num_bits uint, bits uint16) uint16 { + var retval uint = reverseBits_kLut[bits&0x0F] + var i uint + for i = 4; i < num_bits; i += 4 { + retval <<= 4 + bits = uint16(bits >> 4) + retval |= reverseBits_kLut[bits&0x0F] + } + + retval >>= ((0 - num_bits) & 0x03) + return uint16(retval) +} + +/* 0..15 are values for bits */ +const maxHuffmanBits = 16 + +/* Get the actual bit values for a tree of bit depths. */ +func convertBitDepthsToSymbols(depth []byte, len uint, bits []uint16) { + var bl_count = [maxHuffmanBits]uint16{0} + var next_code [maxHuffmanBits]uint16 + var i uint + /* In Brotli, all bit depths are [1..15] + 0 bit depth means that the symbol does not exist. */ + + var code int = 0 + for i = 0; i < len; i++ { + bl_count[depth[i]]++ + } + + bl_count[0] = 0 + next_code[0] = 0 + for i = 1; i < maxHuffmanBits; i++ { + code = (code + int(bl_count[i-1])) << 1 + next_code[i] = uint16(code) + } + + for i = 0; i < len; i++ { + if depth[i] != 0 { + bits[i] = reverseBits(uint(depth[i]), next_code[depth[i]]) + next_code[depth[i]]++ + } + } +} diff --git a/vendor/github.com/andybalholm/brotli/entropy_encode_static.go b/vendor/github.com/andybalholm/brotli/entropy_encode_static.go new file mode 100644 index 000000000..294aff4f4 --- /dev/null +++ b/vendor/github.com/andybalholm/brotli/entropy_encode_static.go @@ -0,0 +1,4399 @@ +package brotli + +var kCodeLengthDepth = [18]byte{4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 0, 4, 4} + +var kStaticCommandCodeDepth = [numCommandSymbols]byte{ + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 11, +} + +var kStaticDistanceCodeDepth = [64]byte{ + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, +} + +var kCodeLengthBits = [18]uint32{0, 8, 4, 12, 2, 10, 6, 14, 1, 9, 5, 13, 3, 15, 31, 0, 11, 7} + +func storeStaticCodeLengthCode(storage_ix *uint, storage []byte) { + writeBits(40, 0x0000FF55555554, storage_ix, storage) +} + +func storeStaticCodeLengthCodeBW(bw *bitWriter) { + bw.writeBits(32, 0x55555554) + bw.writeBits(8, 0xFF) +} + +var kZeroRepsBits = [numCommandSymbols]uint64{ + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000007, + 0x00000017, + 0x00000027, + 0x00000037, + 0x00000047, + 0x00000057, + 0x00000067, + 0x00000077, + 0x00000770, + 0x00000b87, + 0x00001387, + 0x00001b87, + 0x00002387, + 0x00002b87, + 0x00003387, + 0x00003b87, + 0x00000397, + 0x00000b97, + 0x00001397, + 0x00001b97, + 0x00002397, + 0x00002b97, + 0x00003397, + 0x00003b97, + 0x000003a7, + 0x00000ba7, + 0x000013a7, + 0x00001ba7, + 0x000023a7, + 0x00002ba7, + 0x000033a7, + 0x00003ba7, + 0x000003b7, + 0x00000bb7, + 0x000013b7, + 0x00001bb7, + 0x000023b7, + 0x00002bb7, + 0x000033b7, + 0x00003bb7, + 0x000003c7, + 0x00000bc7, + 0x000013c7, + 0x00001bc7, + 0x000023c7, + 0x00002bc7, + 0x000033c7, + 0x00003bc7, + 0x000003d7, + 0x00000bd7, + 0x000013d7, + 0x00001bd7, + 0x000023d7, + 0x00002bd7, + 0x000033d7, + 0x00003bd7, + 0x000003e7, + 0x00000be7, + 0x000013e7, + 0x00001be7, + 0x000023e7, + 0x00002be7, + 0x000033e7, + 0x00003be7, + 0x000003f7, + 0x00000bf7, + 0x000013f7, + 0x00001bf7, + 0x000023f7, + 0x00002bf7, + 0x000033f7, + 0x00003bf7, + 0x0001c387, + 0x0005c387, + 0x0009c387, + 0x000dc387, + 0x0011c387, + 0x0015c387, + 0x0019c387, + 0x001dc387, + 0x0001cb87, + 0x0005cb87, + 0x0009cb87, + 0x000dcb87, + 0x0011cb87, + 0x0015cb87, + 0x0019cb87, + 0x001dcb87, + 0x0001d387, + 0x0005d387, + 0x0009d387, + 0x000dd387, + 0x0011d387, + 0x0015d387, + 0x0019d387, + 0x001dd387, + 0x0001db87, + 0x0005db87, + 0x0009db87, + 0x000ddb87, + 0x0011db87, + 0x0015db87, + 0x0019db87, + 0x001ddb87, + 0x0001e387, + 0x0005e387, + 0x0009e387, + 0x000de387, + 0x0011e387, + 0x0015e387, + 0x0019e387, + 0x001de387, + 0x0001eb87, + 0x0005eb87, + 0x0009eb87, + 0x000deb87, + 0x0011eb87, + 0x0015eb87, + 0x0019eb87, + 0x001deb87, + 0x0001f387, + 0x0005f387, + 0x0009f387, + 0x000df387, + 0x0011f387, + 0x0015f387, + 0x0019f387, + 0x001df387, + 0x0001fb87, + 0x0005fb87, + 0x0009fb87, + 0x000dfb87, + 0x0011fb87, + 0x0015fb87, + 0x0019fb87, + 0x001dfb87, + 0x0001c397, + 0x0005c397, + 0x0009c397, + 0x000dc397, + 0x0011c397, + 0x0015c397, + 0x0019c397, + 0x001dc397, + 0x0001cb97, + 0x0005cb97, + 0x0009cb97, + 0x000dcb97, + 0x0011cb97, + 0x0015cb97, + 0x0019cb97, + 0x001dcb97, + 0x0001d397, + 0x0005d397, + 0x0009d397, + 0x000dd397, + 0x0011d397, + 0x0015d397, + 0x0019d397, + 0x001dd397, + 0x0001db97, + 0x0005db97, + 0x0009db97, + 0x000ddb97, + 0x0011db97, + 0x0015db97, + 0x0019db97, + 0x001ddb97, + 0x0001e397, + 0x0005e397, + 0x0009e397, + 0x000de397, + 0x0011e397, + 0x0015e397, + 0x0019e397, + 0x001de397, + 0x0001eb97, + 0x0005eb97, + 0x0009eb97, + 0x000deb97, + 0x0011eb97, + 0x0015eb97, + 0x0019eb97, + 0x001deb97, + 0x0001f397, + 0x0005f397, + 0x0009f397, + 0x000df397, + 0x0011f397, + 0x0015f397, + 0x0019f397, + 0x001df397, + 0x0001fb97, + 0x0005fb97, + 0x0009fb97, + 0x000dfb97, + 0x0011fb97, + 0x0015fb97, + 0x0019fb97, + 0x001dfb97, + 0x0001c3a7, + 0x0005c3a7, + 0x0009c3a7, + 0x000dc3a7, + 0x0011c3a7, + 0x0015c3a7, + 0x0019c3a7, + 0x001dc3a7, + 0x0001cba7, + 0x0005cba7, + 0x0009cba7, + 0x000dcba7, + 0x0011cba7, + 0x0015cba7, + 0x0019cba7, + 0x001dcba7, + 0x0001d3a7, + 0x0005d3a7, + 0x0009d3a7, + 0x000dd3a7, + 0x0011d3a7, + 0x0015d3a7, + 0x0019d3a7, + 0x001dd3a7, + 0x0001dba7, + 0x0005dba7, + 0x0009dba7, + 0x000ddba7, + 0x0011dba7, + 0x0015dba7, + 0x0019dba7, + 0x001ddba7, + 0x0001e3a7, + 0x0005e3a7, + 0x0009e3a7, + 0x000de3a7, + 0x0011e3a7, + 0x0015e3a7, + 0x0019e3a7, + 0x001de3a7, + 0x0001eba7, + 0x0005eba7, + 0x0009eba7, + 0x000deba7, + 0x0011eba7, + 0x0015eba7, + 0x0019eba7, + 0x001deba7, + 0x0001f3a7, + 0x0005f3a7, + 0x0009f3a7, + 0x000df3a7, + 0x0011f3a7, + 0x0015f3a7, + 0x0019f3a7, + 0x001df3a7, + 0x0001fba7, + 0x0005fba7, + 0x0009fba7, + 0x000dfba7, + 0x0011fba7, + 0x0015fba7, + 0x0019fba7, + 0x001dfba7, + 0x0001c3b7, + 0x0005c3b7, + 0x0009c3b7, + 0x000dc3b7, + 0x0011c3b7, + 0x0015c3b7, + 0x0019c3b7, + 0x001dc3b7, + 0x0001cbb7, + 0x0005cbb7, + 0x0009cbb7, + 0x000dcbb7, + 0x0011cbb7, + 0x0015cbb7, + 0x0019cbb7, + 0x001dcbb7, + 0x0001d3b7, + 0x0005d3b7, + 0x0009d3b7, + 0x000dd3b7, + 0x0011d3b7, + 0x0015d3b7, + 0x0019d3b7, + 0x001dd3b7, + 0x0001dbb7, + 0x0005dbb7, + 0x0009dbb7, + 0x000ddbb7, + 0x0011dbb7, + 0x0015dbb7, + 0x0019dbb7, + 0x001ddbb7, + 0x0001e3b7, + 0x0005e3b7, + 0x0009e3b7, + 0x000de3b7, + 0x0011e3b7, + 0x0015e3b7, + 0x0019e3b7, + 0x001de3b7, + 0x0001ebb7, + 0x0005ebb7, + 0x0009ebb7, + 0x000debb7, + 0x0011ebb7, + 0x0015ebb7, + 0x0019ebb7, + 0x001debb7, + 0x0001f3b7, + 0x0005f3b7, + 0x0009f3b7, + 0x000df3b7, + 0x0011f3b7, + 0x0015f3b7, + 0x0019f3b7, + 0x001df3b7, + 0x0001fbb7, + 0x0005fbb7, + 0x0009fbb7, + 0x000dfbb7, + 0x0011fbb7, + 0x0015fbb7, + 0x0019fbb7, + 0x001dfbb7, + 0x0001c3c7, + 0x0005c3c7, + 0x0009c3c7, + 0x000dc3c7, + 0x0011c3c7, + 0x0015c3c7, + 0x0019c3c7, + 0x001dc3c7, + 0x0001cbc7, + 0x0005cbc7, + 0x0009cbc7, + 0x000dcbc7, + 0x0011cbc7, + 0x0015cbc7, + 0x0019cbc7, + 0x001dcbc7, + 0x0001d3c7, + 0x0005d3c7, + 0x0009d3c7, + 0x000dd3c7, + 0x0011d3c7, + 0x0015d3c7, + 0x0019d3c7, + 0x001dd3c7, + 0x0001dbc7, + 0x0005dbc7, + 0x0009dbc7, + 0x000ddbc7, + 0x0011dbc7, + 0x0015dbc7, + 0x0019dbc7, + 0x001ddbc7, + 0x0001e3c7, + 0x0005e3c7, + 0x0009e3c7, + 0x000de3c7, + 0x0011e3c7, + 0x0015e3c7, + 0x0019e3c7, + 0x001de3c7, + 0x0001ebc7, + 0x0005ebc7, + 0x0009ebc7, + 0x000debc7, + 0x0011ebc7, + 0x0015ebc7, + 0x0019ebc7, + 0x001debc7, + 0x0001f3c7, + 0x0005f3c7, + 0x0009f3c7, + 0x000df3c7, + 0x0011f3c7, + 0x0015f3c7, + 0x0019f3c7, + 0x001df3c7, + 0x0001fbc7, + 0x0005fbc7, + 0x0009fbc7, + 0x000dfbc7, + 0x0011fbc7, + 0x0015fbc7, + 0x0019fbc7, + 0x001dfbc7, + 0x0001c3d7, + 0x0005c3d7, + 0x0009c3d7, + 0x000dc3d7, + 0x0011c3d7, + 0x0015c3d7, + 0x0019c3d7, + 0x001dc3d7, + 0x0001cbd7, + 0x0005cbd7, + 0x0009cbd7, + 0x000dcbd7, + 0x0011cbd7, + 0x0015cbd7, + 0x0019cbd7, + 0x001dcbd7, + 0x0001d3d7, + 0x0005d3d7, + 0x0009d3d7, + 0x000dd3d7, + 0x0011d3d7, + 0x0015d3d7, + 0x0019d3d7, + 0x001dd3d7, + 0x0001dbd7, + 0x0005dbd7, + 0x0009dbd7, + 0x000ddbd7, + 0x0011dbd7, + 0x0015dbd7, + 0x0019dbd7, + 0x001ddbd7, + 0x0001e3d7, + 0x0005e3d7, + 0x0009e3d7, + 0x000de3d7, + 0x0011e3d7, + 0x0015e3d7, + 0x0019e3d7, + 0x001de3d7, + 0x0001ebd7, + 0x0005ebd7, + 0x0009ebd7, + 0x000debd7, + 0x0011ebd7, + 0x0015ebd7, + 0x0019ebd7, + 0x001debd7, + 0x0001f3d7, + 0x0005f3d7, + 0x0009f3d7, + 0x000df3d7, + 0x0011f3d7, + 0x0015f3d7, + 0x0019f3d7, + 0x001df3d7, + 0x0001fbd7, + 0x0005fbd7, + 0x0009fbd7, + 0x000dfbd7, + 0x0011fbd7, + 0x0015fbd7, + 0x0019fbd7, + 0x001dfbd7, + 0x0001c3e7, + 0x0005c3e7, + 0x0009c3e7, + 0x000dc3e7, + 0x0011c3e7, + 0x0015c3e7, + 0x0019c3e7, + 0x001dc3e7, + 0x0001cbe7, + 0x0005cbe7, + 0x0009cbe7, + 0x000dcbe7, + 0x0011cbe7, + 0x0015cbe7, + 0x0019cbe7, + 0x001dcbe7, + 0x0001d3e7, + 0x0005d3e7, + 0x0009d3e7, + 0x000dd3e7, + 0x0011d3e7, + 0x0015d3e7, + 0x0019d3e7, + 0x001dd3e7, + 0x0001dbe7, + 0x0005dbe7, + 0x0009dbe7, + 0x000ddbe7, + 0x0011dbe7, + 0x0015dbe7, + 0x0019dbe7, + 0x001ddbe7, + 0x0001e3e7, + 0x0005e3e7, + 0x0009e3e7, + 0x000de3e7, + 0x0011e3e7, + 0x0015e3e7, + 0x0019e3e7, + 0x001de3e7, + 0x0001ebe7, + 0x0005ebe7, + 0x0009ebe7, + 0x000debe7, + 0x0011ebe7, + 0x0015ebe7, + 0x0019ebe7, + 0x001debe7, + 0x0001f3e7, + 0x0005f3e7, + 0x0009f3e7, + 0x000df3e7, + 0x0011f3e7, + 0x0015f3e7, + 0x0019f3e7, + 0x001df3e7, + 0x0001fbe7, + 0x0005fbe7, + 0x0009fbe7, + 0x000dfbe7, + 0x0011fbe7, + 0x0015fbe7, + 0x0019fbe7, + 0x001dfbe7, + 0x0001c3f7, + 0x0005c3f7, + 0x0009c3f7, + 0x000dc3f7, + 0x0011c3f7, + 0x0015c3f7, + 0x0019c3f7, + 0x001dc3f7, + 0x0001cbf7, + 0x0005cbf7, + 0x0009cbf7, + 0x000dcbf7, + 0x0011cbf7, + 0x0015cbf7, + 0x0019cbf7, + 0x001dcbf7, + 0x0001d3f7, + 0x0005d3f7, + 0x0009d3f7, + 0x000dd3f7, + 0x0011d3f7, + 0x0015d3f7, + 0x0019d3f7, + 0x001dd3f7, + 0x0001dbf7, + 0x0005dbf7, + 0x0009dbf7, + 0x000ddbf7, + 0x0011dbf7, + 0x0015dbf7, + 0x0019dbf7, + 0x001ddbf7, + 0x0001e3f7, + 0x0005e3f7, + 0x0009e3f7, + 0x000de3f7, + 0x0011e3f7, + 0x0015e3f7, + 0x0019e3f7, + 0x001de3f7, + 0x0001ebf7, + 0x0005ebf7, + 0x0009ebf7, + 0x000debf7, + 0x0011ebf7, + 0x0015ebf7, + 0x0019ebf7, + 0x001debf7, + 0x0001f3f7, + 0x0005f3f7, + 0x0009f3f7, + 0x000df3f7, + 0x0011f3f7, + 0x0015f3f7, + 0x0019f3f7, + 0x001df3f7, + 0x0001fbf7, + 0x0005fbf7, + 0x0009fbf7, + 0x000dfbf7, + 0x0011fbf7, + 0x0015fbf7, + 0x0019fbf7, + 0x001dfbf7, + 0x00e1c387, + 0x02e1c387, + 0x04e1c387, + 0x06e1c387, + 0x08e1c387, + 0x0ae1c387, + 0x0ce1c387, + 0x0ee1c387, + 0x00e5c387, + 0x02e5c387, + 0x04e5c387, + 0x06e5c387, + 0x08e5c387, + 0x0ae5c387, + 0x0ce5c387, + 0x0ee5c387, + 0x00e9c387, + 0x02e9c387, + 0x04e9c387, + 0x06e9c387, + 0x08e9c387, + 0x0ae9c387, + 0x0ce9c387, + 0x0ee9c387, + 0x00edc387, + 0x02edc387, + 0x04edc387, + 0x06edc387, + 0x08edc387, + 0x0aedc387, + 0x0cedc387, + 0x0eedc387, + 0x00f1c387, + 0x02f1c387, + 0x04f1c387, + 0x06f1c387, + 0x08f1c387, + 0x0af1c387, + 0x0cf1c387, + 0x0ef1c387, + 0x00f5c387, + 0x02f5c387, + 0x04f5c387, + 0x06f5c387, + 0x08f5c387, + 0x0af5c387, + 0x0cf5c387, + 0x0ef5c387, + 0x00f9c387, + 0x02f9c387, + 0x04f9c387, + 0x06f9c387, + 0x08f9c387, + 0x0af9c387, + 0x0cf9c387, + 0x0ef9c387, + 0x00fdc387, + 0x02fdc387, + 0x04fdc387, + 0x06fdc387, + 0x08fdc387, + 0x0afdc387, + 0x0cfdc387, + 0x0efdc387, + 0x00e1cb87, + 0x02e1cb87, + 0x04e1cb87, + 0x06e1cb87, + 0x08e1cb87, + 0x0ae1cb87, + 0x0ce1cb87, + 0x0ee1cb87, + 0x00e5cb87, + 0x02e5cb87, + 0x04e5cb87, + 0x06e5cb87, + 0x08e5cb87, + 0x0ae5cb87, + 0x0ce5cb87, + 0x0ee5cb87, + 0x00e9cb87, + 0x02e9cb87, + 0x04e9cb87, + 0x06e9cb87, + 0x08e9cb87, + 0x0ae9cb87, + 0x0ce9cb87, + 0x0ee9cb87, + 0x00edcb87, + 0x02edcb87, + 0x04edcb87, + 0x06edcb87, + 0x08edcb87, + 0x0aedcb87, + 0x0cedcb87, + 0x0eedcb87, + 0x00f1cb87, + 0x02f1cb87, + 0x04f1cb87, + 0x06f1cb87, + 0x08f1cb87, + 0x0af1cb87, + 0x0cf1cb87, + 0x0ef1cb87, + 0x00f5cb87, + 0x02f5cb87, + 0x04f5cb87, + 0x06f5cb87, + 0x08f5cb87, + 0x0af5cb87, + 0x0cf5cb87, + 0x0ef5cb87, + 0x00f9cb87, + 0x02f9cb87, + 0x04f9cb87, + 0x06f9cb87, + 0x08f9cb87, +} + +var kZeroRepsDepth = [numCommandSymbols]uint32{ + 0, + 4, + 8, + 7, + 7, + 7, + 7, + 7, + 7, + 7, + 7, + 11, + 14, + 14, + 14, + 14, + 14, + 14, + 14, + 14, + 14, + 14, + 14, + 14, + 14, + 14, + 14, + 14, + 14, + 14, + 14, + 14, + 14, + 14, + 14, + 14, + 14, + 14, + 14, + 14, + 14, + 14, + 14, + 14, + 14, + 14, + 14, + 14, + 14, + 14, + 14, + 14, + 14, + 14, + 14, + 14, + 14, + 14, + 14, + 14, + 14, + 14, + 14, + 14, + 14, + 14, + 14, + 14, + 14, + 14, + 14, + 14, + 14, + 14, + 14, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 28, + 28, + 28, + 28, + 28, + 28, + 28, + 28, + 28, + 28, + 28, + 28, + 28, + 28, + 28, + 28, + 28, + 28, + 28, + 28, + 28, + 28, + 28, + 28, + 28, + 28, + 28, + 28, + 28, + 28, + 28, + 28, + 28, + 28, + 28, + 28, + 28, + 28, + 28, + 28, + 28, + 28, + 28, + 28, + 28, + 28, + 28, + 28, + 28, + 28, + 28, + 28, + 28, + 28, + 28, + 28, + 28, + 28, + 28, + 28, + 28, + 28, + 28, + 28, + 28, + 28, + 28, + 28, + 28, + 28, + 28, + 28, + 28, + 28, + 28, + 28, + 28, + 28, + 28, + 28, + 28, + 28, + 28, + 28, + 28, + 28, + 28, + 28, + 28, + 28, + 28, + 28, + 28, + 28, + 28, + 28, + 28, + 28, + 28, + 28, + 28, + 28, + 28, + 28, + 28, + 28, + 28, + 28, + 28, + 28, + 28, + 28, + 28, + 28, + 28, + 28, + 28, +} + +var kNonZeroRepsBits = [numCommandSymbols]uint64{ + 0x0000000b, + 0x0000001b, + 0x0000002b, + 0x0000003b, + 0x000002cb, + 0x000006cb, + 0x00000acb, + 0x00000ecb, + 0x000002db, + 0x000006db, + 0x00000adb, + 0x00000edb, + 0x000002eb, + 0x000006eb, + 0x00000aeb, + 0x00000eeb, + 0x000002fb, + 0x000006fb, + 0x00000afb, + 0x00000efb, + 0x0000b2cb, + 0x0001b2cb, + 0x0002b2cb, + 0x0003b2cb, + 0x0000b6cb, + 0x0001b6cb, + 0x0002b6cb, + 0x0003b6cb, + 0x0000bacb, + 0x0001bacb, + 0x0002bacb, + 0x0003bacb, + 0x0000becb, + 0x0001becb, + 0x0002becb, + 0x0003becb, + 0x0000b2db, + 0x0001b2db, + 0x0002b2db, + 0x0003b2db, + 0x0000b6db, + 0x0001b6db, + 0x0002b6db, + 0x0003b6db, + 0x0000badb, + 0x0001badb, + 0x0002badb, + 0x0003badb, + 0x0000bedb, + 0x0001bedb, + 0x0002bedb, + 0x0003bedb, + 0x0000b2eb, + 0x0001b2eb, + 0x0002b2eb, + 0x0003b2eb, + 0x0000b6eb, + 0x0001b6eb, + 0x0002b6eb, + 0x0003b6eb, + 0x0000baeb, + 0x0001baeb, + 0x0002baeb, + 0x0003baeb, + 0x0000beeb, + 0x0001beeb, + 0x0002beeb, + 0x0003beeb, + 0x0000b2fb, + 0x0001b2fb, + 0x0002b2fb, + 0x0003b2fb, + 0x0000b6fb, + 0x0001b6fb, + 0x0002b6fb, + 0x0003b6fb, + 0x0000bafb, + 0x0001bafb, + 0x0002bafb, + 0x0003bafb, + 0x0000befb, + 0x0001befb, + 0x0002befb, + 0x0003befb, + 0x002cb2cb, + 0x006cb2cb, + 0x00acb2cb, + 0x00ecb2cb, + 0x002db2cb, + 0x006db2cb, + 0x00adb2cb, + 0x00edb2cb, + 0x002eb2cb, + 0x006eb2cb, + 0x00aeb2cb, + 0x00eeb2cb, + 0x002fb2cb, + 0x006fb2cb, + 0x00afb2cb, + 0x00efb2cb, + 0x002cb6cb, + 0x006cb6cb, + 0x00acb6cb, + 0x00ecb6cb, + 0x002db6cb, + 0x006db6cb, + 0x00adb6cb, + 0x00edb6cb, + 0x002eb6cb, + 0x006eb6cb, + 0x00aeb6cb, + 0x00eeb6cb, + 0x002fb6cb, + 0x006fb6cb, + 0x00afb6cb, + 0x00efb6cb, + 0x002cbacb, + 0x006cbacb, + 0x00acbacb, + 0x00ecbacb, + 0x002dbacb, + 0x006dbacb, + 0x00adbacb, + 0x00edbacb, + 0x002ebacb, + 0x006ebacb, + 0x00aebacb, + 0x00eebacb, + 0x002fbacb, + 0x006fbacb, + 0x00afbacb, + 0x00efbacb, + 0x002cbecb, + 0x006cbecb, + 0x00acbecb, + 0x00ecbecb, + 0x002dbecb, + 0x006dbecb, + 0x00adbecb, + 0x00edbecb, + 0x002ebecb, + 0x006ebecb, + 0x00aebecb, + 0x00eebecb, + 0x002fbecb, + 0x006fbecb, + 0x00afbecb, + 0x00efbecb, + 0x002cb2db, + 0x006cb2db, + 0x00acb2db, + 0x00ecb2db, + 0x002db2db, + 0x006db2db, + 0x00adb2db, + 0x00edb2db, + 0x002eb2db, + 0x006eb2db, + 0x00aeb2db, + 0x00eeb2db, + 0x002fb2db, + 0x006fb2db, + 0x00afb2db, + 0x00efb2db, + 0x002cb6db, + 0x006cb6db, + 0x00acb6db, + 0x00ecb6db, + 0x002db6db, + 0x006db6db, + 0x00adb6db, + 0x00edb6db, + 0x002eb6db, + 0x006eb6db, + 0x00aeb6db, + 0x00eeb6db, + 0x002fb6db, + 0x006fb6db, + 0x00afb6db, + 0x00efb6db, + 0x002cbadb, + 0x006cbadb, + 0x00acbadb, + 0x00ecbadb, + 0x002dbadb, + 0x006dbadb, + 0x00adbadb, + 0x00edbadb, + 0x002ebadb, + 0x006ebadb, + 0x00aebadb, + 0x00eebadb, + 0x002fbadb, + 0x006fbadb, + 0x00afbadb, + 0x00efbadb, + 0x002cbedb, + 0x006cbedb, + 0x00acbedb, + 0x00ecbedb, + 0x002dbedb, + 0x006dbedb, + 0x00adbedb, + 0x00edbedb, + 0x002ebedb, + 0x006ebedb, + 0x00aebedb, + 0x00eebedb, + 0x002fbedb, + 0x006fbedb, + 0x00afbedb, + 0x00efbedb, + 0x002cb2eb, + 0x006cb2eb, + 0x00acb2eb, + 0x00ecb2eb, + 0x002db2eb, + 0x006db2eb, + 0x00adb2eb, + 0x00edb2eb, + 0x002eb2eb, + 0x006eb2eb, + 0x00aeb2eb, + 0x00eeb2eb, + 0x002fb2eb, + 0x006fb2eb, + 0x00afb2eb, + 0x00efb2eb, + 0x002cb6eb, + 0x006cb6eb, + 0x00acb6eb, + 0x00ecb6eb, + 0x002db6eb, + 0x006db6eb, + 0x00adb6eb, + 0x00edb6eb, + 0x002eb6eb, + 0x006eb6eb, + 0x00aeb6eb, + 0x00eeb6eb, + 0x002fb6eb, + 0x006fb6eb, + 0x00afb6eb, + 0x00efb6eb, + 0x002cbaeb, + 0x006cbaeb, + 0x00acbaeb, + 0x00ecbaeb, + 0x002dbaeb, + 0x006dbaeb, + 0x00adbaeb, + 0x00edbaeb, + 0x002ebaeb, + 0x006ebaeb, + 0x00aebaeb, + 0x00eebaeb, + 0x002fbaeb, + 0x006fbaeb, + 0x00afbaeb, + 0x00efbaeb, + 0x002cbeeb, + 0x006cbeeb, + 0x00acbeeb, + 0x00ecbeeb, + 0x002dbeeb, + 0x006dbeeb, + 0x00adbeeb, + 0x00edbeeb, + 0x002ebeeb, + 0x006ebeeb, + 0x00aebeeb, + 0x00eebeeb, + 0x002fbeeb, + 0x006fbeeb, + 0x00afbeeb, + 0x00efbeeb, + 0x002cb2fb, + 0x006cb2fb, + 0x00acb2fb, + 0x00ecb2fb, + 0x002db2fb, + 0x006db2fb, + 0x00adb2fb, + 0x00edb2fb, + 0x002eb2fb, + 0x006eb2fb, + 0x00aeb2fb, + 0x00eeb2fb, + 0x002fb2fb, + 0x006fb2fb, + 0x00afb2fb, + 0x00efb2fb, + 0x002cb6fb, + 0x006cb6fb, + 0x00acb6fb, + 0x00ecb6fb, + 0x002db6fb, + 0x006db6fb, + 0x00adb6fb, + 0x00edb6fb, + 0x002eb6fb, + 0x006eb6fb, + 0x00aeb6fb, + 0x00eeb6fb, + 0x002fb6fb, + 0x006fb6fb, + 0x00afb6fb, + 0x00efb6fb, + 0x002cbafb, + 0x006cbafb, + 0x00acbafb, + 0x00ecbafb, + 0x002dbafb, + 0x006dbafb, + 0x00adbafb, + 0x00edbafb, + 0x002ebafb, + 0x006ebafb, + 0x00aebafb, + 0x00eebafb, + 0x002fbafb, + 0x006fbafb, + 0x00afbafb, + 0x00efbafb, + 0x002cbefb, + 0x006cbefb, + 0x00acbefb, + 0x00ecbefb, + 0x002dbefb, + 0x006dbefb, + 0x00adbefb, + 0x00edbefb, + 0x002ebefb, + 0x006ebefb, + 0x00aebefb, + 0x00eebefb, + 0x002fbefb, + 0x006fbefb, + 0x00afbefb, + 0x00efbefb, + 0x0b2cb2cb, + 0x1b2cb2cb, + 0x2b2cb2cb, + 0x3b2cb2cb, + 0x0b6cb2cb, + 0x1b6cb2cb, + 0x2b6cb2cb, + 0x3b6cb2cb, + 0x0bacb2cb, + 0x1bacb2cb, + 0x2bacb2cb, + 0x3bacb2cb, + 0x0becb2cb, + 0x1becb2cb, + 0x2becb2cb, + 0x3becb2cb, + 0x0b2db2cb, + 0x1b2db2cb, + 0x2b2db2cb, + 0x3b2db2cb, + 0x0b6db2cb, + 0x1b6db2cb, + 0x2b6db2cb, + 0x3b6db2cb, + 0x0badb2cb, + 0x1badb2cb, + 0x2badb2cb, + 0x3badb2cb, + 0x0bedb2cb, + 0x1bedb2cb, + 0x2bedb2cb, + 0x3bedb2cb, + 0x0b2eb2cb, + 0x1b2eb2cb, + 0x2b2eb2cb, + 0x3b2eb2cb, + 0x0b6eb2cb, + 0x1b6eb2cb, + 0x2b6eb2cb, + 0x3b6eb2cb, + 0x0baeb2cb, + 0x1baeb2cb, + 0x2baeb2cb, + 0x3baeb2cb, + 0x0beeb2cb, + 0x1beeb2cb, + 0x2beeb2cb, + 0x3beeb2cb, + 0x0b2fb2cb, + 0x1b2fb2cb, + 0x2b2fb2cb, + 0x3b2fb2cb, + 0x0b6fb2cb, + 0x1b6fb2cb, + 0x2b6fb2cb, + 0x3b6fb2cb, + 0x0bafb2cb, + 0x1bafb2cb, + 0x2bafb2cb, + 0x3bafb2cb, + 0x0befb2cb, + 0x1befb2cb, + 0x2befb2cb, + 0x3befb2cb, + 0x0b2cb6cb, + 0x1b2cb6cb, + 0x2b2cb6cb, + 0x3b2cb6cb, + 0x0b6cb6cb, + 0x1b6cb6cb, + 0x2b6cb6cb, + 0x3b6cb6cb, + 0x0bacb6cb, + 0x1bacb6cb, + 0x2bacb6cb, + 0x3bacb6cb, + 0x0becb6cb, + 0x1becb6cb, + 0x2becb6cb, + 0x3becb6cb, + 0x0b2db6cb, + 0x1b2db6cb, + 0x2b2db6cb, + 0x3b2db6cb, + 0x0b6db6cb, + 0x1b6db6cb, + 0x2b6db6cb, + 0x3b6db6cb, + 0x0badb6cb, + 0x1badb6cb, + 0x2badb6cb, + 0x3badb6cb, + 0x0bedb6cb, + 0x1bedb6cb, + 0x2bedb6cb, + 0x3bedb6cb, + 0x0b2eb6cb, + 0x1b2eb6cb, + 0x2b2eb6cb, + 0x3b2eb6cb, + 0x0b6eb6cb, + 0x1b6eb6cb, + 0x2b6eb6cb, + 0x3b6eb6cb, + 0x0baeb6cb, + 0x1baeb6cb, + 0x2baeb6cb, + 0x3baeb6cb, + 0x0beeb6cb, + 0x1beeb6cb, + 0x2beeb6cb, + 0x3beeb6cb, + 0x0b2fb6cb, + 0x1b2fb6cb, + 0x2b2fb6cb, + 0x3b2fb6cb, + 0x0b6fb6cb, + 0x1b6fb6cb, + 0x2b6fb6cb, + 0x3b6fb6cb, + 0x0bafb6cb, + 0x1bafb6cb, + 0x2bafb6cb, + 0x3bafb6cb, + 0x0befb6cb, + 0x1befb6cb, + 0x2befb6cb, + 0x3befb6cb, + 0x0b2cbacb, + 0x1b2cbacb, + 0x2b2cbacb, + 0x3b2cbacb, + 0x0b6cbacb, + 0x1b6cbacb, + 0x2b6cbacb, + 0x3b6cbacb, + 0x0bacbacb, + 0x1bacbacb, + 0x2bacbacb, + 0x3bacbacb, + 0x0becbacb, + 0x1becbacb, + 0x2becbacb, + 0x3becbacb, + 0x0b2dbacb, + 0x1b2dbacb, + 0x2b2dbacb, + 0x3b2dbacb, + 0x0b6dbacb, + 0x1b6dbacb, + 0x2b6dbacb, + 0x3b6dbacb, + 0x0badbacb, + 0x1badbacb, + 0x2badbacb, + 0x3badbacb, + 0x0bedbacb, + 0x1bedbacb, + 0x2bedbacb, + 0x3bedbacb, + 0x0b2ebacb, + 0x1b2ebacb, + 0x2b2ebacb, + 0x3b2ebacb, + 0x0b6ebacb, + 0x1b6ebacb, + 0x2b6ebacb, + 0x3b6ebacb, + 0x0baebacb, + 0x1baebacb, + 0x2baebacb, + 0x3baebacb, + 0x0beebacb, + 0x1beebacb, + 0x2beebacb, + 0x3beebacb, + 0x0b2fbacb, + 0x1b2fbacb, + 0x2b2fbacb, + 0x3b2fbacb, + 0x0b6fbacb, + 0x1b6fbacb, + 0x2b6fbacb, + 0x3b6fbacb, + 0x0bafbacb, + 0x1bafbacb, + 0x2bafbacb, + 0x3bafbacb, + 0x0befbacb, + 0x1befbacb, + 0x2befbacb, + 0x3befbacb, + 0x0b2cbecb, + 0x1b2cbecb, + 0x2b2cbecb, + 0x3b2cbecb, + 0x0b6cbecb, + 0x1b6cbecb, + 0x2b6cbecb, + 0x3b6cbecb, + 0x0bacbecb, + 0x1bacbecb, + 0x2bacbecb, + 0x3bacbecb, + 0x0becbecb, + 0x1becbecb, + 0x2becbecb, + 0x3becbecb, + 0x0b2dbecb, + 0x1b2dbecb, + 0x2b2dbecb, + 0x3b2dbecb, + 0x0b6dbecb, + 0x1b6dbecb, + 0x2b6dbecb, + 0x3b6dbecb, + 0x0badbecb, + 0x1badbecb, + 0x2badbecb, + 0x3badbecb, + 0x0bedbecb, + 0x1bedbecb, + 0x2bedbecb, + 0x3bedbecb, + 0x0b2ebecb, + 0x1b2ebecb, + 0x2b2ebecb, + 0x3b2ebecb, + 0x0b6ebecb, + 0x1b6ebecb, + 0x2b6ebecb, + 0x3b6ebecb, + 0x0baebecb, + 0x1baebecb, + 0x2baebecb, + 0x3baebecb, + 0x0beebecb, + 0x1beebecb, + 0x2beebecb, + 0x3beebecb, + 0x0b2fbecb, + 0x1b2fbecb, + 0x2b2fbecb, + 0x3b2fbecb, + 0x0b6fbecb, + 0x1b6fbecb, + 0x2b6fbecb, + 0x3b6fbecb, + 0x0bafbecb, + 0x1bafbecb, + 0x2bafbecb, + 0x3bafbecb, + 0x0befbecb, + 0x1befbecb, + 0x2befbecb, + 0x3befbecb, + 0x0b2cb2db, + 0x1b2cb2db, + 0x2b2cb2db, + 0x3b2cb2db, + 0x0b6cb2db, + 0x1b6cb2db, + 0x2b6cb2db, + 0x3b6cb2db, + 0x0bacb2db, + 0x1bacb2db, + 0x2bacb2db, + 0x3bacb2db, + 0x0becb2db, + 0x1becb2db, + 0x2becb2db, + 0x3becb2db, + 0x0b2db2db, + 0x1b2db2db, + 0x2b2db2db, + 0x3b2db2db, + 0x0b6db2db, + 0x1b6db2db, + 0x2b6db2db, + 0x3b6db2db, + 0x0badb2db, + 0x1badb2db, + 0x2badb2db, + 0x3badb2db, + 0x0bedb2db, + 0x1bedb2db, + 0x2bedb2db, + 0x3bedb2db, + 0x0b2eb2db, + 0x1b2eb2db, + 0x2b2eb2db, + 0x3b2eb2db, + 0x0b6eb2db, + 0x1b6eb2db, + 0x2b6eb2db, + 0x3b6eb2db, + 0x0baeb2db, + 0x1baeb2db, + 0x2baeb2db, + 0x3baeb2db, + 0x0beeb2db, + 0x1beeb2db, + 0x2beeb2db, + 0x3beeb2db, + 0x0b2fb2db, + 0x1b2fb2db, + 0x2b2fb2db, + 0x3b2fb2db, + 0x0b6fb2db, + 0x1b6fb2db, + 0x2b6fb2db, + 0x3b6fb2db, + 0x0bafb2db, + 0x1bafb2db, + 0x2bafb2db, + 0x3bafb2db, + 0x0befb2db, + 0x1befb2db, + 0x2befb2db, + 0x3befb2db, + 0x0b2cb6db, + 0x1b2cb6db, + 0x2b2cb6db, + 0x3b2cb6db, + 0x0b6cb6db, + 0x1b6cb6db, + 0x2b6cb6db, + 0x3b6cb6db, + 0x0bacb6db, + 0x1bacb6db, + 0x2bacb6db, + 0x3bacb6db, + 0x0becb6db, + 0x1becb6db, + 0x2becb6db, + 0x3becb6db, + 0x0b2db6db, + 0x1b2db6db, + 0x2b2db6db, + 0x3b2db6db, + 0x0b6db6db, + 0x1b6db6db, + 0x2b6db6db, + 0x3b6db6db, + 0x0badb6db, + 0x1badb6db, + 0x2badb6db, + 0x3badb6db, + 0x0bedb6db, + 0x1bedb6db, + 0x2bedb6db, + 0x3bedb6db, + 0x0b2eb6db, + 0x1b2eb6db, + 0x2b2eb6db, + 0x3b2eb6db, + 0x0b6eb6db, + 0x1b6eb6db, + 0x2b6eb6db, + 0x3b6eb6db, + 0x0baeb6db, + 0x1baeb6db, + 0x2baeb6db, + 0x3baeb6db, +} + +var kNonZeroRepsDepth = [numCommandSymbols]uint32{ + 6, + 6, + 6, + 6, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, +} + +var kStaticCommandCodeBits = [numCommandSymbols]uint16{ + 0, + 256, + 128, + 384, + 64, + 320, + 192, + 448, + 32, + 288, + 160, + 416, + 96, + 352, + 224, + 480, + 16, + 272, + 144, + 400, + 80, + 336, + 208, + 464, + 48, + 304, + 176, + 432, + 112, + 368, + 240, + 496, + 8, + 264, + 136, + 392, + 72, + 328, + 200, + 456, + 40, + 296, + 168, + 424, + 104, + 360, + 232, + 488, + 24, + 280, + 152, + 408, + 88, + 344, + 216, + 472, + 56, + 312, + 184, + 440, + 120, + 376, + 248, + 504, + 4, + 260, + 132, + 388, + 68, + 324, + 196, + 452, + 36, + 292, + 164, + 420, + 100, + 356, + 228, + 484, + 20, + 276, + 148, + 404, + 84, + 340, + 212, + 468, + 52, + 308, + 180, + 436, + 116, + 372, + 244, + 500, + 12, + 268, + 140, + 396, + 76, + 332, + 204, + 460, + 44, + 300, + 172, + 428, + 108, + 364, + 236, + 492, + 28, + 284, + 156, + 412, + 92, + 348, + 220, + 476, + 60, + 316, + 188, + 444, + 124, + 380, + 252, + 508, + 2, + 258, + 130, + 386, + 66, + 322, + 194, + 450, + 34, + 290, + 162, + 418, + 98, + 354, + 226, + 482, + 18, + 274, + 146, + 402, + 82, + 338, + 210, + 466, + 50, + 306, + 178, + 434, + 114, + 370, + 242, + 498, + 10, + 266, + 138, + 394, + 74, + 330, + 202, + 458, + 42, + 298, + 170, + 426, + 106, + 362, + 234, + 490, + 26, + 282, + 154, + 410, + 90, + 346, + 218, + 474, + 58, + 314, + 186, + 442, + 122, + 378, + 250, + 506, + 6, + 262, + 134, + 390, + 70, + 326, + 198, + 454, + 38, + 294, + 166, + 422, + 102, + 358, + 230, + 486, + 22, + 278, + 150, + 406, + 86, + 342, + 214, + 470, + 54, + 310, + 182, + 438, + 118, + 374, + 246, + 502, + 14, + 270, + 142, + 398, + 78, + 334, + 206, + 462, + 46, + 302, + 174, + 430, + 110, + 366, + 238, + 494, + 30, + 286, + 158, + 414, + 94, + 350, + 222, + 478, + 62, + 318, + 190, + 446, + 126, + 382, + 254, + 510, + 1, + 257, + 129, + 385, + 65, + 321, + 193, + 449, + 33, + 289, + 161, + 417, + 97, + 353, + 225, + 481, + 17, + 273, + 145, + 401, + 81, + 337, + 209, + 465, + 49, + 305, + 177, + 433, + 113, + 369, + 241, + 497, + 9, + 265, + 137, + 393, + 73, + 329, + 201, + 457, + 41, + 297, + 169, + 425, + 105, + 361, + 233, + 489, + 25, + 281, + 153, + 409, + 89, + 345, + 217, + 473, + 57, + 313, + 185, + 441, + 121, + 377, + 249, + 505, + 5, + 261, + 133, + 389, + 69, + 325, + 197, + 453, + 37, + 293, + 165, + 421, + 101, + 357, + 229, + 485, + 21, + 277, + 149, + 405, + 85, + 341, + 213, + 469, + 53, + 309, + 181, + 437, + 117, + 373, + 245, + 501, + 13, + 269, + 141, + 397, + 77, + 333, + 205, + 461, + 45, + 301, + 173, + 429, + 109, + 365, + 237, + 493, + 29, + 285, + 157, + 413, + 93, + 349, + 221, + 477, + 61, + 317, + 189, + 445, + 125, + 381, + 253, + 509, + 3, + 259, + 131, + 387, + 67, + 323, + 195, + 451, + 35, + 291, + 163, + 419, + 99, + 355, + 227, + 483, + 19, + 275, + 147, + 403, + 83, + 339, + 211, + 467, + 51, + 307, + 179, + 435, + 115, + 371, + 243, + 499, + 11, + 267, + 139, + 395, + 75, + 331, + 203, + 459, + 43, + 299, + 171, + 427, + 107, + 363, + 235, + 491, + 27, + 283, + 155, + 411, + 91, + 347, + 219, + 475, + 59, + 315, + 187, + 443, + 123, + 379, + 251, + 507, + 7, + 1031, + 519, + 1543, + 263, + 1287, + 775, + 1799, + 135, + 1159, + 647, + 1671, + 391, + 1415, + 903, + 1927, + 71, + 1095, + 583, + 1607, + 327, + 1351, + 839, + 1863, + 199, + 1223, + 711, + 1735, + 455, + 1479, + 967, + 1991, + 39, + 1063, + 551, + 1575, + 295, + 1319, + 807, + 1831, + 167, + 1191, + 679, + 1703, + 423, + 1447, + 935, + 1959, + 103, + 1127, + 615, + 1639, + 359, + 1383, + 871, + 1895, + 231, + 1255, + 743, + 1767, + 487, + 1511, + 999, + 2023, + 23, + 1047, + 535, + 1559, + 279, + 1303, + 791, + 1815, + 151, + 1175, + 663, + 1687, + 407, + 1431, + 919, + 1943, + 87, + 1111, + 599, + 1623, + 343, + 1367, + 855, + 1879, + 215, + 1239, + 727, + 1751, + 471, + 1495, + 983, + 2007, + 55, + 1079, + 567, + 1591, + 311, + 1335, + 823, + 1847, + 183, + 1207, + 695, + 1719, + 439, + 1463, + 951, + 1975, + 119, + 1143, + 631, + 1655, + 375, + 1399, + 887, + 1911, + 247, + 1271, + 759, + 1783, + 503, + 1527, + 1015, + 2039, + 15, + 1039, + 527, + 1551, + 271, + 1295, + 783, + 1807, + 143, + 1167, + 655, + 1679, + 399, + 1423, + 911, + 1935, + 79, + 1103, + 591, + 1615, + 335, + 1359, + 847, + 1871, + 207, + 1231, + 719, + 1743, + 463, + 1487, + 975, + 1999, + 47, + 1071, + 559, + 1583, + 303, + 1327, + 815, + 1839, + 175, + 1199, + 687, + 1711, + 431, + 1455, + 943, + 1967, + 111, + 1135, + 623, + 1647, + 367, + 1391, + 879, + 1903, + 239, + 1263, + 751, + 1775, + 495, + 1519, + 1007, + 2031, + 31, + 1055, + 543, + 1567, + 287, + 1311, + 799, + 1823, + 159, + 1183, + 671, + 1695, + 415, + 1439, + 927, + 1951, + 95, + 1119, + 607, + 1631, + 351, + 1375, + 863, + 1887, + 223, + 1247, + 735, + 1759, + 479, + 1503, + 991, + 2015, + 63, + 1087, + 575, + 1599, + 319, + 1343, + 831, + 1855, + 191, + 1215, + 703, + 1727, + 447, + 1471, + 959, + 1983, + 127, + 1151, + 639, + 1663, + 383, + 1407, + 895, + 1919, + 255, + 1279, + 767, + 1791, + 511, + 1535, + 1023, + 2047, +} + +func storeStaticCommandHuffmanTree(storage_ix *uint, storage []byte) { + writeBits(56, 0x92624416307003, storage_ix, storage) + writeBits(3, 0x00000000, storage_ix, storage) +} + +var kStaticDistanceCodeBits = [64]uint16{ + 0, + 32, + 16, + 48, + 8, + 40, + 24, + 56, + 4, + 36, + 20, + 52, + 12, + 44, + 28, + 60, + 2, + 34, + 18, + 50, + 10, + 42, + 26, + 58, + 6, + 38, + 22, + 54, + 14, + 46, + 30, + 62, + 1, + 33, + 17, + 49, + 9, + 41, + 25, + 57, + 5, + 37, + 21, + 53, + 13, + 45, + 29, + 61, + 3, + 35, + 19, + 51, + 11, + 43, + 27, + 59, + 7, + 39, + 23, + 55, + 15, + 47, + 31, + 63, +} + +func storeStaticDistanceHuffmanTree(storage_ix *uint, storage []byte) { + writeBits(28, 0x0369DC03, storage_ix, storage) +} diff --git a/vendor/github.com/andybalholm/brotli/fast_log.go b/vendor/github.com/andybalholm/brotli/fast_log.go new file mode 100644 index 000000000..9d6607f7e --- /dev/null +++ b/vendor/github.com/andybalholm/brotli/fast_log.go @@ -0,0 +1,290 @@ +package brotli + +import ( + "math" + "math/bits" +) + +/* Copyright 2013 Google Inc. All Rights Reserved. + + Distributed under MIT license. + See file LICENSE for detail or copy at https://opensource.org/licenses/MIT +*/ + +/* Utilities for fast computation of logarithms. */ + +func log2FloorNonZero(n uint) uint32 { + return uint32(bits.Len(n)) - 1 +} + +/* A lookup table for small values of log2(int) to be used in entropy + computation. + + ", ".join(["%.16ff" % x for x in [0.0]+[log2(x) for x in range(1, 256)]]) */ +var kLog2Table = []float32{ + 0.0000000000000000, + 0.0000000000000000, + 1.0000000000000000, + 1.5849625007211563, + 2.0000000000000000, + 2.3219280948873622, + 2.5849625007211561, + 2.8073549220576042, + 3.0000000000000000, + 3.1699250014423126, + 3.3219280948873626, + 3.4594316186372978, + 3.5849625007211565, + 3.7004397181410922, + 3.8073549220576037, + 3.9068905956085187, + 4.0000000000000000, + 4.0874628412503400, + 4.1699250014423122, + 4.2479275134435852, + 4.3219280948873626, + 4.3923174227787607, + 4.4594316186372973, + 4.5235619560570131, + 4.5849625007211570, + 4.6438561897747244, + 4.7004397181410926, + 4.7548875021634691, + 4.8073549220576037, + 4.8579809951275728, + 4.9068905956085187, + 4.9541963103868758, + 5.0000000000000000, + 5.0443941193584534, + 5.0874628412503400, + 5.1292830169449664, + 5.1699250014423122, + 5.2094533656289501, + 5.2479275134435852, + 5.2854022188622487, + 5.3219280948873626, + 5.3575520046180838, + 5.3923174227787607, + 5.4262647547020979, + 5.4594316186372973, + 5.4918530963296748, + 5.5235619560570131, + 5.5545888516776376, + 5.5849625007211570, + 5.6147098441152083, + 5.6438561897747244, + 5.6724253419714961, + 5.7004397181410926, + 5.7279204545631996, + 5.7548875021634691, + 5.7813597135246599, + 5.8073549220576046, + 5.8328900141647422, + 5.8579809951275719, + 5.8826430493618416, + 5.9068905956085187, + 5.9307373375628867, + 5.9541963103868758, + 5.9772799234999168, + 6.0000000000000000, + 6.0223678130284544, + 6.0443941193584534, + 6.0660891904577721, + 6.0874628412503400, + 6.1085244567781700, + 6.1292830169449672, + 6.1497471195046822, + 6.1699250014423122, + 6.1898245588800176, + 6.2094533656289510, + 6.2288186904958804, + 6.2479275134435861, + 6.2667865406949019, + 6.2854022188622487, + 6.3037807481771031, + 6.3219280948873617, + 6.3398500028846252, + 6.3575520046180847, + 6.3750394313469254, + 6.3923174227787598, + 6.4093909361377026, + 6.4262647547020979, + 6.4429434958487288, + 6.4594316186372982, + 6.4757334309663976, + 6.4918530963296748, + 6.5077946401986964, + 6.5235619560570131, + 6.5391588111080319, + 6.5545888516776376, + 6.5698556083309478, + 6.5849625007211561, + 6.5999128421871278, + 6.6147098441152092, + 6.6293566200796095, + 6.6438561897747253, + 6.6582114827517955, + 6.6724253419714952, + 6.6865005271832185, + 6.7004397181410917, + 6.7142455176661224, + 6.7279204545631988, + 6.7414669864011465, + 6.7548875021634691, + 6.7681843247769260, + 6.7813597135246599, + 6.7944158663501062, + 6.8073549220576037, + 6.8201789624151887, + 6.8328900141647422, + 6.8454900509443757, + 6.8579809951275719, + 6.8703647195834048, + 6.8826430493618416, + 6.8948177633079437, + 6.9068905956085187, + 6.9188632372745955, + 6.9307373375628867, + 6.9425145053392399, + 6.9541963103868758, + 6.9657842846620879, + 6.9772799234999168, + 6.9886846867721664, + 7.0000000000000000, + 7.0112272554232540, + 7.0223678130284544, + 7.0334230015374501, + 7.0443941193584534, + 7.0552824355011898, + 7.0660891904577721, + 7.0768155970508317, + 7.0874628412503400, + 7.0980320829605272, + 7.1085244567781700, + 7.1189410727235076, + 7.1292830169449664, + 7.1395513523987937, + 7.1497471195046822, + 7.1598713367783891, + 7.1699250014423130, + 7.1799090900149345, + 7.1898245588800176, + 7.1996723448363644, + 7.2094533656289492, + 7.2191685204621621, + 7.2288186904958804, + 7.2384047393250794, + 7.2479275134435861, + 7.2573878426926521, + 7.2667865406949019, + 7.2761244052742384, + 7.2854022188622487, + 7.2946207488916270, + 7.3037807481771031, + 7.3128829552843557, + 7.3219280948873617, + 7.3309168781146177, + 7.3398500028846243, + 7.3487281542310781, + 7.3575520046180847, + 7.3663222142458151, + 7.3750394313469254, + 7.3837042924740528, + 7.3923174227787607, + 7.4008794362821844, + 7.4093909361377026, + 7.4178525148858991, + 7.4262647547020979, + 7.4346282276367255, + 7.4429434958487288, + 7.4512111118323299, + 7.4594316186372973, + 7.4676055500829976, + 7.4757334309663976, + 7.4838157772642564, + 7.4918530963296748, + 7.4998458870832057, + 7.5077946401986964, + 7.5156998382840436, + 7.5235619560570131, + 7.5313814605163119, + 7.5391588111080319, + 7.5468944598876373, + 7.5545888516776376, + 7.5622424242210728, + 7.5698556083309478, + 7.5774288280357487, + 7.5849625007211561, + 7.5924570372680806, + 7.5999128421871278, + 7.6073303137496113, + 7.6147098441152075, + 7.6220518194563764, + 7.6293566200796095, + 7.6366246205436488, + 7.6438561897747244, + 7.6510516911789290, + 7.6582114827517955, + 7.6653359171851765, + 7.6724253419714952, + 7.6794800995054464, + 7.6865005271832185, + 7.6934869574993252, + 7.7004397181410926, + 7.7073591320808825, + 7.7142455176661224, + 7.7210991887071856, + 7.7279204545631996, + 7.7347096202258392, + 7.7414669864011465, + 7.7481928495894596, + 7.7548875021634691, + 7.7615512324444795, + 7.7681843247769260, + 7.7747870596011737, + 7.7813597135246608, + 7.7879025593914317, + 7.7944158663501062, + 7.8008998999203047, + 7.8073549220576037, + 7.8137811912170374, + 7.8201789624151887, + 7.8265484872909159, + 7.8328900141647422, + 7.8392037880969445, + 7.8454900509443757, + 7.8517490414160571, + 7.8579809951275719, + 7.8641861446542798, + 7.8703647195834048, + 7.8765169465650002, + 7.8826430493618425, + 7.8887432488982601, + 7.8948177633079446, + 7.9008668079807496, + 7.9068905956085187, + 7.9128893362299619, + 7.9188632372745955, + 7.9248125036057813, + 7.9307373375628867, + 7.9366379390025719, + 7.9425145053392399, + 7.9483672315846778, + 7.9541963103868758, + 7.9600019320680806, + 7.9657842846620870, + 7.9715435539507720, + 7.9772799234999168, + 7.9829935746943104, + 7.9886846867721664, + 7.9943534368588578, +} + +/* Faster logarithm for small integers, with the property of log2(0) == 0. */ +func fastLog2(v uint) float64 { + if v < uint(len(kLog2Table)) { + return float64(kLog2Table[v]) + } + + return math.Log2(float64(v)) +} diff --git a/vendor/github.com/andybalholm/brotli/find_match_length.go b/vendor/github.com/andybalholm/brotli/find_match_length.go new file mode 100644 index 000000000..09d2ae672 --- /dev/null +++ b/vendor/github.com/andybalholm/brotli/find_match_length.go @@ -0,0 +1,45 @@ +package brotli + +import ( + "encoding/binary" + "math/bits" + "runtime" +) + +/* Copyright 2010 Google Inc. All Rights Reserved. + + Distributed under MIT license. + See file LICENSE for detail or copy at https://opensource.org/licenses/MIT +*/ + +/* Function to find maximal matching prefixes of strings. */ +func findMatchLengthWithLimit(s1 []byte, s2 []byte, limit uint) uint { + var matched uint = 0 + _, _ = s1[limit-1], s2[limit-1] // bounds check + switch runtime.GOARCH { + case "amd64": + // Compare 8 bytes at at time. + for matched+8 <= limit { + w1 := binary.LittleEndian.Uint64(s1[matched:]) + w2 := binary.LittleEndian.Uint64(s2[matched:]) + if w1 != w2 { + return matched + uint(bits.TrailingZeros64(w1^w2)>>3) + } + matched += 8 + } + case "386": + // Compare 4 bytes at at time. + for matched+4 <= limit { + w1 := binary.LittleEndian.Uint32(s1[matched:]) + w2 := binary.LittleEndian.Uint32(s2[matched:]) + if w1 != w2 { + return matched + uint(bits.TrailingZeros32(w1^w2)>>3) + } + matched += 4 + } + } + for matched < limit && s1[matched] == s2[matched] { + matched++ + } + return matched +} diff --git a/vendor/github.com/andybalholm/brotli/h10.go b/vendor/github.com/andybalholm/brotli/h10.go new file mode 100644 index 000000000..5662fbbbb --- /dev/null +++ b/vendor/github.com/andybalholm/brotli/h10.go @@ -0,0 +1,287 @@ +package brotli + +import "encoding/binary" + +/* Copyright 2016 Google Inc. All Rights Reserved. + + Distributed under MIT license. + See file LICENSE for detail or copy at https://opensource.org/licenses/MIT +*/ + +func (*h10) HashTypeLength() uint { + return 4 +} + +func (*h10) StoreLookahead() uint { + return 128 +} + +func hashBytesH10(data []byte) uint32 { + var h uint32 = binary.LittleEndian.Uint32(data) * kHashMul32 + + /* The higher bits contain more mixture from the multiplication, + so we take our results from there. */ + return h >> (32 - 17) +} + +/* A (forgetful) hash table where each hash bucket contains a binary tree of + sequences whose first 4 bytes share the same hash code. + Each sequence is 128 long and is identified by its starting + position in the input data. The binary tree is sorted by the lexicographic + order of the sequences, and it is also a max-heap with respect to the + starting positions. */ +type h10 struct { + hasherCommon + window_mask_ uint + buckets_ [1 << 17]uint32 + invalid_pos_ uint32 + forest []uint32 +} + +func (h *h10) Initialize(params *encoderParams) { + h.window_mask_ = (1 << params.lgwin) - 1 + h.invalid_pos_ = uint32(0 - h.window_mask_) + var num_nodes uint = uint(1) << params.lgwin + h.forest = make([]uint32, 2*num_nodes) +} + +func (h *h10) Prepare(one_shot bool, input_size uint, data []byte) { + var invalid_pos uint32 = h.invalid_pos_ + var i uint32 + for i = 0; i < 1<<17; i++ { + h.buckets_[i] = invalid_pos + } +} + +func leftChildIndexH10(self *h10, pos uint) uint { + return 2 * (pos & self.window_mask_) +} + +func rightChildIndexH10(self *h10, pos uint) uint { + return 2*(pos&self.window_mask_) + 1 +} + +/* Stores the hash of the next 4 bytes and in a single tree-traversal, the + hash bucket's binary tree is searched for matches and is re-rooted at the + current position. + + If less than 128 data is available, the hash bucket of the + current position is searched for matches, but the state of the hash table + is not changed, since we can not know the final sorting order of the + current (incomplete) sequence. + + This function must be called with increasing cur_ix positions. */ +func storeAndFindMatchesH10(self *h10, data []byte, cur_ix uint, ring_buffer_mask uint, max_length uint, max_backward uint, best_len *uint, matches []backwardMatch) []backwardMatch { + var cur_ix_masked uint = cur_ix & ring_buffer_mask + var max_comp_len uint = brotli_min_size_t(max_length, 128) + var should_reroot_tree bool = (max_length >= 128) + var key uint32 = hashBytesH10(data[cur_ix_masked:]) + var forest []uint32 = self.forest + var prev_ix uint = uint(self.buckets_[key]) + var node_left uint = leftChildIndexH10(self, cur_ix) + var node_right uint = rightChildIndexH10(self, cur_ix) + var best_len_left uint = 0 + var best_len_right uint = 0 + var depth_remaining uint + /* The forest index of the rightmost node of the left subtree of the new + root, updated as we traverse and re-root the tree of the hash bucket. */ + + /* The forest index of the leftmost node of the right subtree of the new + root, updated as we traverse and re-root the tree of the hash bucket. */ + + /* The match length of the rightmost node of the left subtree of the new + root, updated as we traverse and re-root the tree of the hash bucket. */ + + /* The match length of the leftmost node of the right subtree of the new + root, updated as we traverse and re-root the tree of the hash bucket. */ + if should_reroot_tree { + self.buckets_[key] = uint32(cur_ix) + } + + for depth_remaining = 64; ; depth_remaining-- { + var backward uint = cur_ix - prev_ix + var prev_ix_masked uint = prev_ix & ring_buffer_mask + if backward == 0 || backward > max_backward || depth_remaining == 0 { + if should_reroot_tree { + forest[node_left] = self.invalid_pos_ + forest[node_right] = self.invalid_pos_ + } + + break + } + { + var cur_len uint = brotli_min_size_t(best_len_left, best_len_right) + var len uint + assert(cur_len <= 128) + len = cur_len + findMatchLengthWithLimit(data[cur_ix_masked+cur_len:], data[prev_ix_masked+cur_len:], max_length-cur_len) + if matches != nil && len > *best_len { + *best_len = uint(len) + initBackwardMatch(&matches[0], backward, uint(len)) + matches = matches[1:] + } + + if len >= max_comp_len { + if should_reroot_tree { + forest[node_left] = forest[leftChildIndexH10(self, prev_ix)] + forest[node_right] = forest[rightChildIndexH10(self, prev_ix)] + } + + break + } + + if data[cur_ix_masked+len] > data[prev_ix_masked+len] { + best_len_left = uint(len) + if should_reroot_tree { + forest[node_left] = uint32(prev_ix) + } + + node_left = rightChildIndexH10(self, prev_ix) + prev_ix = uint(forest[node_left]) + } else { + best_len_right = uint(len) + if should_reroot_tree { + forest[node_right] = uint32(prev_ix) + } + + node_right = leftChildIndexH10(self, prev_ix) + prev_ix = uint(forest[node_right]) + } + } + } + + return matches +} + +/* Finds all backward matches of &data[cur_ix & ring_buffer_mask] up to the + length of max_length and stores the position cur_ix in the hash table. + + Sets *num_matches to the number of matches found, and stores the found + matches in matches[0] to matches[*num_matches - 1]. The matches will be + sorted by strictly increasing length and (non-strictly) increasing + distance. */ +func findAllMatchesH10(handle *h10, dictionary *encoderDictionary, data []byte, ring_buffer_mask uint, cur_ix uint, max_length uint, max_backward uint, gap uint, params *encoderParams, matches []backwardMatch) uint { + var orig_matches []backwardMatch = matches + var cur_ix_masked uint = cur_ix & ring_buffer_mask + var best_len uint = 1 + var short_match_max_backward uint + if params.quality != hqZopflificationQuality { + short_match_max_backward = 16 + } else { + short_match_max_backward = 64 + } + var stop uint = cur_ix - short_match_max_backward + var dict_matches [maxStaticDictionaryMatchLen + 1]uint32 + var i uint + if cur_ix < short_match_max_backward { + stop = 0 + } + for i = cur_ix - 1; i > stop && best_len <= 2; i-- { + var prev_ix uint = i + var backward uint = cur_ix - prev_ix + if backward > max_backward { + break + } + + prev_ix &= ring_buffer_mask + if data[cur_ix_masked] != data[prev_ix] || data[cur_ix_masked+1] != data[prev_ix+1] { + continue + } + { + var len uint = findMatchLengthWithLimit(data[prev_ix:], data[cur_ix_masked:], max_length) + if len > best_len { + best_len = uint(len) + initBackwardMatch(&matches[0], backward, uint(len)) + matches = matches[1:] + } + } + } + + if best_len < max_length { + matches = storeAndFindMatchesH10(handle, data, cur_ix, ring_buffer_mask, max_length, max_backward, &best_len, matches) + } + + for i = 0; i <= maxStaticDictionaryMatchLen; i++ { + dict_matches[i] = kInvalidMatch + } + { + var minlen uint = brotli_max_size_t(4, best_len+1) + if findAllStaticDictionaryMatches(dictionary, data[cur_ix_masked:], minlen, max_length, dict_matches[0:]) { + var maxlen uint = brotli_min_size_t(maxStaticDictionaryMatchLen, max_length) + var l uint + for l = minlen; l <= maxlen; l++ { + var dict_id uint32 = dict_matches[l] + if dict_id < kInvalidMatch { + var distance uint = max_backward + gap + uint(dict_id>>5) + 1 + if distance <= params.dist.max_distance { + initDictionaryBackwardMatch(&matches[0], distance, l, uint(dict_id&31)) + matches = matches[1:] + } + } + } + } + } + + return uint(-cap(matches) + cap(orig_matches)) +} + +/* Stores the hash of the next 4 bytes and re-roots the binary tree at the + current sequence, without returning any matches. + REQUIRES: ix + 128 <= end-of-current-block */ +func (h *h10) Store(data []byte, mask uint, ix uint) { + var max_backward uint = h.window_mask_ - windowGap + 1 + /* Maximum distance is window size - 16, see section 9.1. of the spec. */ + storeAndFindMatchesH10(h, data, ix, mask, 128, max_backward, nil, nil) +} + +func (h *h10) StoreRange(data []byte, mask uint, ix_start uint, ix_end uint) { + var i uint = ix_start + var j uint = ix_start + if ix_start+63 <= ix_end { + i = ix_end - 63 + } + + if ix_start+512 <= i { + for ; j < i; j += 8 { + h.Store(data, mask, j) + } + } + + for ; i < ix_end; i++ { + h.Store(data, mask, i) + } +} + +func (h *h10) StitchToPreviousBlock(num_bytes uint, position uint, ringbuffer []byte, ringbuffer_mask uint) { + if num_bytes >= h.HashTypeLength()-1 && position >= 128 { + var i_start uint = position - 128 + 1 + var i_end uint = brotli_min_size_t(position, i_start+num_bytes) + /* Store the last `128 - 1` positions in the hasher. + These could not be calculated before, since they require knowledge + of both the previous and the current block. */ + + var i uint + for i = i_start; i < i_end; i++ { + /* Maximum distance is window size - 16, see section 9.1. of the spec. + Furthermore, we have to make sure that we don't look further back + from the start of the next block than the window size, otherwise we + could access already overwritten areas of the ring-buffer. */ + var max_backward uint = h.window_mask_ - brotli_max_size_t(windowGap-1, position-i) + + /* We know that i + 128 <= position + num_bytes, i.e. the + end of the current block and that we have at least + 128 tail in the ring-buffer. */ + storeAndFindMatchesH10(h, ringbuffer, i, ringbuffer_mask, 128, max_backward, nil, nil) + } + } +} + +/* MAX_NUM_MATCHES == 64 + MAX_TREE_SEARCH_DEPTH */ +const maxNumMatchesH10 = 128 + +func (*h10) FindLongestMatch(dictionary *encoderDictionary, data []byte, ring_buffer_mask uint, distance_cache []int, cur_ix uint, max_length uint, max_backward uint, gap uint, max_distance uint, out *hasherSearchResult) { + panic("unimplemented") +} + +func (*h10) PrepareDistanceCache(distance_cache []int) { + panic("unimplemented") +} diff --git a/vendor/github.com/andybalholm/brotli/h5.go b/vendor/github.com/andybalholm/brotli/h5.go new file mode 100644 index 000000000..f391b73fd --- /dev/null +++ b/vendor/github.com/andybalholm/brotli/h5.go @@ -0,0 +1,214 @@ +package brotli + +import "encoding/binary" + +/* Copyright 2010 Google Inc. All Rights Reserved. + + Distributed under MIT license. + See file LICENSE for detail or copy at https://opensource.org/licenses/MIT +*/ + +/* A (forgetful) hash table to the data seen by the compressor, to + help create backward references to previous data. + + This is a hash map of fixed size (bucket_size_) to a ring buffer of + fixed size (block_size_). The ring buffer contains the last block_size_ + index positions of the given hash key in the compressed data. */ +func (*h5) HashTypeLength() uint { + return 4 +} + +func (*h5) StoreLookahead() uint { + return 4 +} + +/* HashBytes is the function that chooses the bucket to place the address in. */ +func hashBytesH5(data []byte, shift int) uint32 { + var h uint32 = binary.LittleEndian.Uint32(data) * kHashMul32 + + /* The higher bits contain more mixture from the multiplication, + so we take our results from there. */ + return uint32(h >> uint(shift)) +} + +type h5 struct { + hasherCommon + bucket_size_ uint + block_size_ uint + hash_shift_ int + block_mask_ uint32 + num []uint16 + buckets []uint32 +} + +func (h *h5) Initialize(params *encoderParams) { + h.hash_shift_ = 32 - h.params.bucket_bits + h.bucket_size_ = uint(1) << uint(h.params.bucket_bits) + h.block_size_ = uint(1) << uint(h.params.block_bits) + h.block_mask_ = uint32(h.block_size_ - 1) + h.num = make([]uint16, h.bucket_size_) + h.buckets = make([]uint32, h.block_size_*h.bucket_size_) +} + +func (h *h5) Prepare(one_shot bool, input_size uint, data []byte) { + var num []uint16 = h.num + var partial_prepare_threshold uint = h.bucket_size_ >> 6 + /* Partial preparation is 100 times slower (per socket). */ + if one_shot && input_size <= partial_prepare_threshold { + var i uint + for i = 0; i < input_size; i++ { + var key uint32 = hashBytesH5(data[i:], h.hash_shift_) + num[key] = 0 + } + } else { + for i := 0; i < int(h.bucket_size_); i++ { + num[i] = 0 + } + } +} + +/* Look at 4 bytes at &data[ix & mask]. + Compute a hash from these, and store the value of ix at that position. */ +func (h *h5) Store(data []byte, mask uint, ix uint) { + var num []uint16 = h.num + var key uint32 = hashBytesH5(data[ix&mask:], h.hash_shift_) + var minor_ix uint = uint(num[key]) & uint(h.block_mask_) + var offset uint = minor_ix + uint(key<= h.HashTypeLength()-1 && position >= 3 { + /* Prepare the hashes for three last bytes of the last write. + These could not be calculated before, since they require knowledge + of both the previous and the current block. */ + h.Store(ringbuffer, ringbuffer_mask, position-3) + h.Store(ringbuffer, ringbuffer_mask, position-2) + h.Store(ringbuffer, ringbuffer_mask, position-1) + } +} + +func (h *h5) PrepareDistanceCache(distance_cache []int) { + prepareDistanceCache(distance_cache, h.params.num_last_distances_to_check) +} + +/* Find a longest backward match of &data[cur_ix] up to the length of + max_length and stores the position cur_ix in the hash table. + + REQUIRES: PrepareDistanceCacheH5 must be invoked for current distance cache + values; if this method is invoked repeatedly with the same distance + cache values, it is enough to invoke PrepareDistanceCacheH5 once. + + Does not look for matches longer than max_length. + Does not look for matches further away than max_backward. + Writes the best match into |out|. + |out|->score is updated only if a better match is found. */ +func (h *h5) FindLongestMatch(dictionary *encoderDictionary, data []byte, ring_buffer_mask uint, distance_cache []int, cur_ix uint, max_length uint, max_backward uint, gap uint, max_distance uint, out *hasherSearchResult) { + var num []uint16 = h.num + var buckets []uint32 = h.buckets + var cur_ix_masked uint = cur_ix & ring_buffer_mask + var min_score uint = out.score + var best_score uint = out.score + var best_len uint = out.len + var i uint + var bucket []uint32 + /* Don't accept a short copy from far away. */ + out.len = 0 + + out.len_code_delta = 0 + + /* Try last distance first. */ + for i = 0; i < uint(h.params.num_last_distances_to_check); i++ { + var backward uint = uint(distance_cache[i]) + var prev_ix uint = uint(cur_ix - backward) + if prev_ix >= cur_ix { + continue + } + + if backward > max_backward { + continue + } + + prev_ix &= ring_buffer_mask + + if cur_ix_masked+best_len > ring_buffer_mask || prev_ix+best_len > ring_buffer_mask || data[cur_ix_masked+best_len] != data[prev_ix+best_len] { + continue + } + { + var len uint = findMatchLengthWithLimit(data[prev_ix:], data[cur_ix_masked:], max_length) + if len >= 3 || (len == 2 && i < 2) { + /* Comparing for >= 2 does not change the semantics, but just saves for + a few unnecessary binary logarithms in backward reference score, + since we are not interested in such short matches. */ + var score uint = backwardReferenceScoreUsingLastDistance(uint(len)) + if best_score < score { + if i != 0 { + score -= backwardReferencePenaltyUsingLastDistance(i) + } + if best_score < score { + best_score = score + best_len = uint(len) + out.len = best_len + out.distance = backward + out.score = best_score + } + } + } + } + } + { + var key uint32 = hashBytesH5(data[cur_ix_masked:], h.hash_shift_) + bucket = buckets[key< h.block_size_ { + down = uint(num[key]) - h.block_size_ + } else { + down = 0 + } + for i = uint(num[key]); i > down; { + var prev_ix uint + i-- + prev_ix = uint(bucket[uint32(i)&h.block_mask_]) + var backward uint = cur_ix - prev_ix + if backward > max_backward { + break + } + + prev_ix &= ring_buffer_mask + if cur_ix_masked+best_len > ring_buffer_mask || prev_ix+best_len > ring_buffer_mask || data[cur_ix_masked+best_len] != data[prev_ix+best_len] { + continue + } + { + var len uint = findMatchLengthWithLimit(data[prev_ix:], data[cur_ix_masked:], max_length) + if len >= 4 { + /* Comparing for >= 3 does not change the semantics, but just saves + for a few unnecessary binary logarithms in backward reference + score, since we are not interested in such short matches. */ + var score uint = backwardReferenceScore(uint(len), backward) + if best_score < score { + best_score = score + best_len = uint(len) + out.len = best_len + out.distance = backward + out.score = best_score + } + } + } + } + + bucket[uint32(num[key])&h.block_mask_] = uint32(cur_ix) + num[key]++ + } + + if min_score == out.score { + searchInStaticDictionary(dictionary, h, data[cur_ix_masked:], max_length, max_backward+gap, max_distance, out, false) + } +} diff --git a/vendor/github.com/andybalholm/brotli/h6.go b/vendor/github.com/andybalholm/brotli/h6.go new file mode 100644 index 000000000..80bb224aa --- /dev/null +++ b/vendor/github.com/andybalholm/brotli/h6.go @@ -0,0 +1,216 @@ +package brotli + +import "encoding/binary" + +/* Copyright 2010 Google Inc. All Rights Reserved. + + Distributed under MIT license. + See file LICENSE for detail or copy at https://opensource.org/licenses/MIT +*/ + +/* A (forgetful) hash table to the data seen by the compressor, to + help create backward references to previous data. + + This is a hash map of fixed size (bucket_size_) to a ring buffer of + fixed size (block_size_). The ring buffer contains the last block_size_ + index positions of the given hash key in the compressed data. */ +func (*h6) HashTypeLength() uint { + return 8 +} + +func (*h6) StoreLookahead() uint { + return 8 +} + +/* HashBytes is the function that chooses the bucket to place the address in. */ +func hashBytesH6(data []byte, mask uint64, shift int) uint32 { + var h uint64 = (binary.LittleEndian.Uint64(data) & mask) * kHashMul64Long + + /* The higher bits contain more mixture from the multiplication, + so we take our results from there. */ + return uint32(h >> uint(shift)) +} + +type h6 struct { + hasherCommon + bucket_size_ uint + block_size_ uint + hash_shift_ int + hash_mask_ uint64 + block_mask_ uint32 + num []uint16 + buckets []uint32 +} + +func (h *h6) Initialize(params *encoderParams) { + h.hash_shift_ = 64 - h.params.bucket_bits + h.hash_mask_ = (^(uint64(0))) >> uint(64-8*h.params.hash_len) + h.bucket_size_ = uint(1) << uint(h.params.bucket_bits) + h.block_size_ = uint(1) << uint(h.params.block_bits) + h.block_mask_ = uint32(h.block_size_ - 1) + h.num = make([]uint16, h.bucket_size_) + h.buckets = make([]uint32, h.block_size_*h.bucket_size_) +} + +func (h *h6) Prepare(one_shot bool, input_size uint, data []byte) { + var num []uint16 = h.num + var partial_prepare_threshold uint = h.bucket_size_ >> 6 + /* Partial preparation is 100 times slower (per socket). */ + if one_shot && input_size <= partial_prepare_threshold { + var i uint + for i = 0; i < input_size; i++ { + var key uint32 = hashBytesH6(data[i:], h.hash_mask_, h.hash_shift_) + num[key] = 0 + } + } else { + for i := 0; i < int(h.bucket_size_); i++ { + num[i] = 0 + } + } +} + +/* Look at 4 bytes at &data[ix & mask]. + Compute a hash from these, and store the value of ix at that position. */ +func (h *h6) Store(data []byte, mask uint, ix uint) { + var num []uint16 = h.num + var key uint32 = hashBytesH6(data[ix&mask:], h.hash_mask_, h.hash_shift_) + var minor_ix uint = uint(num[key]) & uint(h.block_mask_) + var offset uint = minor_ix + uint(key<= h.HashTypeLength()-1 && position >= 3 { + /* Prepare the hashes for three last bytes of the last write. + These could not be calculated before, since they require knowledge + of both the previous and the current block. */ + h.Store(ringbuffer, ringbuffer_mask, position-3) + h.Store(ringbuffer, ringbuffer_mask, position-2) + h.Store(ringbuffer, ringbuffer_mask, position-1) + } +} + +func (h *h6) PrepareDistanceCache(distance_cache []int) { + prepareDistanceCache(distance_cache, h.params.num_last_distances_to_check) +} + +/* Find a longest backward match of &data[cur_ix] up to the length of + max_length and stores the position cur_ix in the hash table. + + REQUIRES: PrepareDistanceCacheH6 must be invoked for current distance cache + values; if this method is invoked repeatedly with the same distance + cache values, it is enough to invoke PrepareDistanceCacheH6 once. + + Does not look for matches longer than max_length. + Does not look for matches further away than max_backward. + Writes the best match into |out|. + |out|->score is updated only if a better match is found. */ +func (h *h6) FindLongestMatch(dictionary *encoderDictionary, data []byte, ring_buffer_mask uint, distance_cache []int, cur_ix uint, max_length uint, max_backward uint, gap uint, max_distance uint, out *hasherSearchResult) { + var num []uint16 = h.num + var buckets []uint32 = h.buckets + var cur_ix_masked uint = cur_ix & ring_buffer_mask + var min_score uint = out.score + var best_score uint = out.score + var best_len uint = out.len + var i uint + var bucket []uint32 + /* Don't accept a short copy from far away. */ + out.len = 0 + + out.len_code_delta = 0 + + /* Try last distance first. */ + for i = 0; i < uint(h.params.num_last_distances_to_check); i++ { + var backward uint = uint(distance_cache[i]) + var prev_ix uint = uint(cur_ix - backward) + if prev_ix >= cur_ix { + continue + } + + if backward > max_backward { + continue + } + + prev_ix &= ring_buffer_mask + + if cur_ix_masked+best_len > ring_buffer_mask || prev_ix+best_len > ring_buffer_mask || data[cur_ix_masked+best_len] != data[prev_ix+best_len] { + continue + } + { + var len uint = findMatchLengthWithLimit(data[prev_ix:], data[cur_ix_masked:], max_length) + if len >= 3 || (len == 2 && i < 2) { + /* Comparing for >= 2 does not change the semantics, but just saves for + a few unnecessary binary logarithms in backward reference score, + since we are not interested in such short matches. */ + var score uint = backwardReferenceScoreUsingLastDistance(uint(len)) + if best_score < score { + if i != 0 { + score -= backwardReferencePenaltyUsingLastDistance(i) + } + if best_score < score { + best_score = score + best_len = uint(len) + out.len = best_len + out.distance = backward + out.score = best_score + } + } + } + } + } + { + var key uint32 = hashBytesH6(data[cur_ix_masked:], h.hash_mask_, h.hash_shift_) + bucket = buckets[key< h.block_size_ { + down = uint(num[key]) - h.block_size_ + } else { + down = 0 + } + for i = uint(num[key]); i > down; { + var prev_ix uint + i-- + prev_ix = uint(bucket[uint32(i)&h.block_mask_]) + var backward uint = cur_ix - prev_ix + if backward > max_backward { + break + } + + prev_ix &= ring_buffer_mask + if cur_ix_masked+best_len > ring_buffer_mask || prev_ix+best_len > ring_buffer_mask || data[cur_ix_masked+best_len] != data[prev_ix+best_len] { + continue + } + { + var len uint = findMatchLengthWithLimit(data[prev_ix:], data[cur_ix_masked:], max_length) + if len >= 4 { + /* Comparing for >= 3 does not change the semantics, but just saves + for a few unnecessary binary logarithms in backward reference + score, since we are not interested in such short matches. */ + var score uint = backwardReferenceScore(uint(len), backward) + if best_score < score { + best_score = score + best_len = uint(len) + out.len = best_len + out.distance = backward + out.score = best_score + } + } + } + } + + bucket[uint32(num[key])&h.block_mask_] = uint32(cur_ix) + num[key]++ + } + + if min_score == out.score { + searchInStaticDictionary(dictionary, h, data[cur_ix_masked:], max_length, max_backward+gap, max_distance, out, false) + } +} diff --git a/vendor/github.com/andybalholm/brotli/hash.go b/vendor/github.com/andybalholm/brotli/hash.go new file mode 100644 index 000000000..00f812e87 --- /dev/null +++ b/vendor/github.com/andybalholm/brotli/hash.go @@ -0,0 +1,342 @@ +package brotli + +import ( + "encoding/binary" + "fmt" +) + +type hasherCommon struct { + params hasherParams + is_prepared_ bool + dict_num_lookups uint + dict_num_matches uint +} + +func (h *hasherCommon) Common() *hasherCommon { + return h +} + +type hasherHandle interface { + Common() *hasherCommon + Initialize(params *encoderParams) + Prepare(one_shot bool, input_size uint, data []byte) + StitchToPreviousBlock(num_bytes uint, position uint, ringbuffer []byte, ringbuffer_mask uint) + HashTypeLength() uint + StoreLookahead() uint + PrepareDistanceCache(distance_cache []int) + FindLongestMatch(dictionary *encoderDictionary, data []byte, ring_buffer_mask uint, distance_cache []int, cur_ix uint, max_length uint, max_backward uint, gap uint, max_distance uint, out *hasherSearchResult) + StoreRange(data []byte, mask uint, ix_start uint, ix_end uint) + Store(data []byte, mask uint, ix uint) +} + +const kCutoffTransformsCount uint32 = 10 + +/* 0, 12, 27, 23, 42, 63, 56, 48, 59, 64 */ +/* 0+0, 4+8, 8+19, 12+11, 16+26, 20+43, 24+32, 28+20, 32+27, 36+28 */ +const kCutoffTransforms uint64 = 0x071B520ADA2D3200 + +type hasherSearchResult struct { + len uint + distance uint + score uint + len_code_delta int +} + +/* kHashMul32 multiplier has these properties: + * The multiplier must be odd. Otherwise we may lose the highest bit. + * No long streaks of ones or zeros. + * There is no effort to ensure that it is a prime, the oddity is enough + for this use. + * The number has been tuned heuristically against compression benchmarks. */ +const kHashMul32 uint32 = 0x1E35A7BD + +const kHashMul64 uint64 = 0x1E35A7BD1E35A7BD + +const kHashMul64Long uint64 = 0x1FE35A7BD3579BD3 + +func hash14(data []byte) uint32 { + var h uint32 = binary.LittleEndian.Uint32(data) * kHashMul32 + + /* The higher bits contain more mixture from the multiplication, + so we take our results from there. */ + return h >> (32 - 14) +} + +func prepareDistanceCache(distance_cache []int, num_distances int) { + if num_distances > 4 { + var last_distance int = distance_cache[0] + distance_cache[4] = last_distance - 1 + distance_cache[5] = last_distance + 1 + distance_cache[6] = last_distance - 2 + distance_cache[7] = last_distance + 2 + distance_cache[8] = last_distance - 3 + distance_cache[9] = last_distance + 3 + if num_distances > 10 { + var next_last_distance int = distance_cache[1] + distance_cache[10] = next_last_distance - 1 + distance_cache[11] = next_last_distance + 1 + distance_cache[12] = next_last_distance - 2 + distance_cache[13] = next_last_distance + 2 + distance_cache[14] = next_last_distance - 3 + distance_cache[15] = next_last_distance + 3 + } + } +} + +const literalByteScore = 135 + +const distanceBitPenalty = 30 + +/* Score must be positive after applying maximal penalty. */ +const scoreBase = (distanceBitPenalty * 8 * 8) + +/* Usually, we always choose the longest backward reference. This function + allows for the exception of that rule. + + If we choose a backward reference that is further away, it will + usually be coded with more bits. We approximate this by assuming + log2(distance). If the distance can be expressed in terms of the + last four distances, we use some heuristic constants to estimate + the bits cost. For the first up to four literals we use the bit + cost of the literals from the literal cost model, after that we + use the average bit cost of the cost model. + + This function is used to sometimes discard a longer backward reference + when it is not much longer and the bit cost for encoding it is more + than the saved literals. + + backward_reference_offset MUST be positive. */ +func backwardReferenceScore(copy_length uint, backward_reference_offset uint) uint { + return scoreBase + literalByteScore*uint(copy_length) - distanceBitPenalty*uint(log2FloorNonZero(backward_reference_offset)) +} + +func backwardReferenceScoreUsingLastDistance(copy_length uint) uint { + return literalByteScore*uint(copy_length) + scoreBase + 15 +} + +func backwardReferencePenaltyUsingLastDistance(distance_short_code uint) uint { + return uint(39) + ((0x1CA10 >> (distance_short_code & 0xE)) & 0xE) +} + +func testStaticDictionaryItem(dictionary *encoderDictionary, item uint, data []byte, max_length uint, max_backward uint, max_distance uint, out *hasherSearchResult) bool { + var len uint + var word_idx uint + var offset uint + var matchlen uint + var backward uint + var score uint + len = item & 0x1F + word_idx = item >> 5 + offset = uint(dictionary.words.offsets_by_length[len]) + len*word_idx + if len > max_length { + return false + } + + matchlen = findMatchLengthWithLimit(data, dictionary.words.data[offset:], uint(len)) + if matchlen+uint(dictionary.cutoffTransformsCount) <= len || matchlen == 0 { + return false + } + { + var cut uint = len - matchlen + var transform_id uint = (cut << 2) + uint((dictionary.cutoffTransforms>>(cut*6))&0x3F) + backward = max_backward + 1 + word_idx + (transform_id << dictionary.words.size_bits_by_length[len]) + } + + if backward > max_distance { + return false + } + + score = backwardReferenceScore(matchlen, backward) + if score < out.score { + return false + } + + out.len = matchlen + out.len_code_delta = int(len) - int(matchlen) + out.distance = backward + out.score = score + return true +} + +func searchInStaticDictionary(dictionary *encoderDictionary, handle hasherHandle, data []byte, max_length uint, max_backward uint, max_distance uint, out *hasherSearchResult, shallow bool) { + var key uint + var i uint + var self *hasherCommon = handle.Common() + if self.dict_num_matches < self.dict_num_lookups>>7 { + return + } + + key = uint(hash14(data) << 1) + for i = 0; ; (func() { i++; key++ })() { + var tmp uint + if shallow { + tmp = 1 + } else { + tmp = 2 + } + if i >= tmp { + break + } + var item uint = uint(dictionary.hash_table[key]) + self.dict_num_lookups++ + if item != 0 { + var item_matches bool = testStaticDictionaryItem(dictionary, item, data, max_length, max_backward, max_distance, out) + if item_matches { + self.dict_num_matches++ + } + } + } +} + +type backwardMatch struct { + distance uint32 + length_and_code uint32 +} + +func initBackwardMatch(self *backwardMatch, dist uint, len uint) { + self.distance = uint32(dist) + self.length_and_code = uint32(len << 5) +} + +func initDictionaryBackwardMatch(self *backwardMatch, dist uint, len uint, len_code uint) { + self.distance = uint32(dist) + var tmp uint + if len == len_code { + tmp = 0 + } else { + tmp = len_code + } + self.length_and_code = uint32(len<<5 | tmp) +} + +func backwardMatchLength(self *backwardMatch) uint { + return uint(self.length_and_code >> 5) +} + +func backwardMatchLengthCode(self *backwardMatch) uint { + var code uint = uint(self.length_and_code) & 31 + if code != 0 { + return code + } else { + return backwardMatchLength(self) + } +} + +func hasherReset(handle hasherHandle) { + if handle == nil { + return + } + handle.Common().is_prepared_ = false +} + +func newHasher(typ int) hasherHandle { + switch typ { + case 2: + return &hashLongestMatchQuickly{ + bucketBits: 16, + bucketSweep: 1, + hashLen: 5, + useDictionary: true, + } + case 3: + return &hashLongestMatchQuickly{ + bucketBits: 16, + bucketSweep: 2, + hashLen: 5, + useDictionary: false, + } + case 4: + return &hashLongestMatchQuickly{ + bucketBits: 17, + bucketSweep: 4, + hashLen: 5, + useDictionary: true, + } + case 5: + return new(h5) + case 6: + return new(h6) + case 10: + return new(h10) + case 35: + return &hashComposite{ + ha: newHasher(3), + hb: &hashRolling{jump: 4}, + } + case 40: + return &hashForgetfulChain{ + bucketBits: 15, + numBanks: 1, + bankBits: 16, + numLastDistancesToCheck: 4, + } + case 41: + return &hashForgetfulChain{ + bucketBits: 15, + numBanks: 1, + bankBits: 16, + numLastDistancesToCheck: 10, + } + case 42: + return &hashForgetfulChain{ + bucketBits: 15, + numBanks: 512, + bankBits: 9, + numLastDistancesToCheck: 16, + } + case 54: + return &hashLongestMatchQuickly{ + bucketBits: 20, + bucketSweep: 4, + hashLen: 7, + useDictionary: false, + } + case 55: + return &hashComposite{ + ha: newHasher(54), + hb: &hashRolling{jump: 4}, + } + case 65: + return &hashComposite{ + ha: newHasher(6), + hb: &hashRolling{jump: 1}, + } + } + + panic(fmt.Sprintf("unknown hasher type: %d", typ)) +} + +func hasherSetup(handle *hasherHandle, params *encoderParams, data []byte, position uint, input_size uint, is_last bool) { + var self hasherHandle = nil + var common *hasherCommon = nil + var one_shot bool = (position == 0 && is_last) + if *handle == nil { + chooseHasher(params, ¶ms.hasher) + self = newHasher(params.hasher.type_) + + *handle = self + common = self.Common() + common.params = params.hasher + self.Initialize(params) + } + + self = *handle + common = self.Common() + if !common.is_prepared_ { + self.Prepare(one_shot, input_size, data) + + if position == 0 { + common.dict_num_lookups = 0 + common.dict_num_matches = 0 + } + + common.is_prepared_ = true + } +} + +func initOrStitchToPreviousBlock(handle *hasherHandle, data []byte, mask uint, params *encoderParams, position uint, input_size uint, is_last bool) { + var self hasherHandle + hasherSetup(handle, params, data, position, input_size, is_last) + self = *handle + self.StitchToPreviousBlock(input_size, position, data, mask) +} diff --git a/vendor/github.com/andybalholm/brotli/hash_composite.go b/vendor/github.com/andybalholm/brotli/hash_composite.go new file mode 100644 index 000000000..a65fe2e6a --- /dev/null +++ b/vendor/github.com/andybalholm/brotli/hash_composite.go @@ -0,0 +1,93 @@ +package brotli + +/* Copyright 2018 Google Inc. All Rights Reserved. + + Distributed under MIT license. + See file LICENSE for detail or copy at https://opensource.org/licenses/MIT +*/ + +func (h *hashComposite) HashTypeLength() uint { + var a uint = h.ha.HashTypeLength() + var b uint = h.hb.HashTypeLength() + if a > b { + return a + } else { + return b + } +} + +func (h *hashComposite) StoreLookahead() uint { + var a uint = h.ha.StoreLookahead() + var b uint = h.hb.StoreLookahead() + if a > b { + return a + } else { + return b + } +} + +/* Composite hasher: This hasher allows to combine two other hashers, HASHER_A + and HASHER_B. */ +type hashComposite struct { + hasherCommon + ha hasherHandle + hb hasherHandle + params *encoderParams +} + +func (h *hashComposite) Initialize(params *encoderParams) { + h.params = params +} + +/* TODO: Initialize of the hashers is defered to Prepare (and params + remembered here) because we don't get the one_shot and input_size params + here that are needed to know the memory size of them. Instead provide + those params to all hashers InitializehashComposite */ +func (h *hashComposite) Prepare(one_shot bool, input_size uint, data []byte) { + if h.ha == nil { + var common_a *hasherCommon + var common_b *hasherCommon + + common_a = h.ha.Common() + common_a.params = h.params.hasher + common_a.is_prepared_ = false + common_a.dict_num_lookups = 0 + common_a.dict_num_matches = 0 + h.ha.Initialize(h.params) + + common_b = h.hb.Common() + common_b.params = h.params.hasher + common_b.is_prepared_ = false + common_b.dict_num_lookups = 0 + common_b.dict_num_matches = 0 + h.hb.Initialize(h.params) + } + + h.ha.Prepare(one_shot, input_size, data) + h.hb.Prepare(one_shot, input_size, data) +} + +func (h *hashComposite) Store(data []byte, mask uint, ix uint) { + h.ha.Store(data, mask, ix) + h.hb.Store(data, mask, ix) +} + +func (h *hashComposite) StoreRange(data []byte, mask uint, ix_start uint, ix_end uint) { + h.ha.StoreRange(data, mask, ix_start, ix_end) + h.hb.StoreRange(data, mask, ix_start, ix_end) +} + +func (h *hashComposite) StitchToPreviousBlock(num_bytes uint, position uint, ringbuffer []byte, ring_buffer_mask uint) { + h.ha.StitchToPreviousBlock(num_bytes, position, ringbuffer, ring_buffer_mask) + h.hb.StitchToPreviousBlock(num_bytes, position, ringbuffer, ring_buffer_mask) +} + +func (h *hashComposite) PrepareDistanceCache(distance_cache []int) { + h.ha.PrepareDistanceCache(distance_cache) + h.hb.PrepareDistanceCache(distance_cache) +} + +func (h *hashComposite) FindLongestMatch(dictionary *encoderDictionary, data []byte, ring_buffer_mask uint, distance_cache []int, cur_ix uint, max_length uint, max_backward uint, gap uint, max_distance uint, out *hasherSearchResult) { + h.ha.FindLongestMatch(dictionary, data, ring_buffer_mask, distance_cache, cur_ix, max_length, max_backward, gap, max_distance, out) + h.hb.FindLongestMatch(dictionary, data, ring_buffer_mask, distance_cache, cur_ix, max_length, max_backward, gap, max_distance, out) +} diff --git a/vendor/github.com/andybalholm/brotli/hash_forgetful_chain.go b/vendor/github.com/andybalholm/brotli/hash_forgetful_chain.go new file mode 100644 index 000000000..306e46d3d --- /dev/null +++ b/vendor/github.com/andybalholm/brotli/hash_forgetful_chain.go @@ -0,0 +1,252 @@ +package brotli + +import "encoding/binary" + +/* Copyright 2016 Google Inc. All Rights Reserved. + + Distributed under MIT license. + See file LICENSE for detail or copy at https://opensource.org/licenses/MIT +*/ + +func (*hashForgetfulChain) HashTypeLength() uint { + return 4 +} + +func (*hashForgetfulChain) StoreLookahead() uint { + return 4 +} + +/* HashBytes is the function that chooses the bucket to place the address in.*/ +func (h *hashForgetfulChain) HashBytes(data []byte) uint { + var hash uint32 = binary.LittleEndian.Uint32(data) * kHashMul32 + + /* The higher bits contain more mixture from the multiplication, + so we take our results from there. */ + return uint(hash >> (32 - h.bucketBits)) +} + +type slot struct { + delta uint16 + next uint16 +} + +/* A (forgetful) hash table to the data seen by the compressor, to + help create backward references to previous data. + + Hashes are stored in chains which are bucketed to groups. Group of chains + share a storage "bank". When more than "bank size" chain nodes are added, + oldest nodes are replaced; this way several chains may share a tail. */ +type hashForgetfulChain struct { + hasherCommon + + bucketBits uint + numBanks uint + bankBits uint + numLastDistancesToCheck int + + addr []uint32 + head []uint16 + tiny_hash [65536]byte + banks [][]slot + free_slot_idx []uint16 + max_hops uint +} + +func (h *hashForgetfulChain) Initialize(params *encoderParams) { + var q uint + if params.quality > 6 { + q = 7 + } else { + q = 8 + } + h.max_hops = q << uint(params.quality-4) + + bankSize := 1 << h.bankBits + bucketSize := 1 << h.bucketBits + + h.addr = make([]uint32, bucketSize) + h.head = make([]uint16, bucketSize) + h.banks = make([][]slot, h.numBanks) + for i := range h.banks { + h.banks[i] = make([]slot, bankSize) + } + h.free_slot_idx = make([]uint16, h.numBanks) +} + +func (h *hashForgetfulChain) Prepare(one_shot bool, input_size uint, data []byte) { + var partial_prepare_threshold uint = (1 << h.bucketBits) >> 6 + /* Partial preparation is 100 times slower (per socket). */ + if one_shot && input_size <= partial_prepare_threshold { + var i uint + for i = 0; i < input_size; i++ { + var bucket uint = h.HashBytes(data[i:]) + + /* See InitEmpty comment. */ + h.addr[bucket] = 0xCCCCCCCC + + h.head[bucket] = 0xCCCC + } + } else { + /* Fill |addr| array with 0xCCCCCCCC value. Because of wrapping, position + processed by hasher never reaches 3GB + 64M; this makes all new chains + to be terminated after the first node. */ + for i := range h.addr { + h.addr[i] = 0xCCCCCCCC + } + + for i := range h.head { + h.head[i] = 0 + } + } + + h.tiny_hash = [65536]byte{} + for i := range h.free_slot_idx { + h.free_slot_idx[i] = 0 + } +} + +/* Look at 4 bytes at &data[ix & mask]. Compute a hash from these, and prepend + node to corresponding chain; also update tiny_hash for current position. */ +func (h *hashForgetfulChain) Store(data []byte, mask uint, ix uint) { + var key uint = h.HashBytes(data[ix&mask:]) + var bank uint = key & (h.numBanks - 1) + idx := uint(h.free_slot_idx[bank]) & ((1 << h.bankBits) - 1) + h.free_slot_idx[bank]++ + var delta uint = ix - uint(h.addr[key]) + h.tiny_hash[uint16(ix)] = byte(key) + if delta > 0xFFFF { + delta = 0xFFFF + } + h.banks[bank][idx].delta = uint16(delta) + h.banks[bank][idx].next = h.head[key] + h.addr[key] = uint32(ix) + h.head[key] = uint16(idx) +} + +func (h *hashForgetfulChain) StoreRange(data []byte, mask uint, ix_start uint, ix_end uint) { + var i uint + for i = ix_start; i < ix_end; i++ { + h.Store(data, mask, i) + } +} + +func (h *hashForgetfulChain) StitchToPreviousBlock(num_bytes uint, position uint, ringbuffer []byte, ring_buffer_mask uint) { + if num_bytes >= h.HashTypeLength()-1 && position >= 3 { + /* Prepare the hashes for three last bytes of the last write. + These could not be calculated before, since they require knowledge + of both the previous and the current block. */ + h.Store(ringbuffer, ring_buffer_mask, position-3) + h.Store(ringbuffer, ring_buffer_mask, position-2) + h.Store(ringbuffer, ring_buffer_mask, position-1) + } +} + +func (h *hashForgetfulChain) PrepareDistanceCache(distance_cache []int) { + prepareDistanceCache(distance_cache, h.numLastDistancesToCheck) +} + +/* Find a longest backward match of &data[cur_ix] up to the length of + max_length and stores the position cur_ix in the hash table. + + REQUIRES: PrepareDistanceCachehashForgetfulChain must be invoked for current distance cache + values; if this method is invoked repeatedly with the same distance + cache values, it is enough to invoke PrepareDistanceCachehashForgetfulChain once. + + Does not look for matches longer than max_length. + Does not look for matches further away than max_backward. + Writes the best match into |out|. + |out|->score is updated only if a better match is found. */ +func (h *hashForgetfulChain) FindLongestMatch(dictionary *encoderDictionary, data []byte, ring_buffer_mask uint, distance_cache []int, cur_ix uint, max_length uint, max_backward uint, gap uint, max_distance uint, out *hasherSearchResult) { + var cur_ix_masked uint = cur_ix & ring_buffer_mask + var min_score uint = out.score + var best_score uint = out.score + var best_len uint = out.len + var key uint = h.HashBytes(data[cur_ix_masked:]) + var tiny_hash byte = byte(key) + /* Don't accept a short copy from far away. */ + out.len = 0 + + out.len_code_delta = 0 + + /* Try last distance first. */ + for i := 0; i < h.numLastDistancesToCheck; i++ { + var backward uint = uint(distance_cache[i]) + var prev_ix uint = (cur_ix - backward) + + /* For distance code 0 we want to consider 2-byte matches. */ + if i > 0 && h.tiny_hash[uint16(prev_ix)] != tiny_hash { + continue + } + if prev_ix >= cur_ix || backward > max_backward { + continue + } + + prev_ix &= ring_buffer_mask + { + var len uint = findMatchLengthWithLimit(data[prev_ix:], data[cur_ix_masked:], max_length) + if len >= 2 { + var score uint = backwardReferenceScoreUsingLastDistance(uint(len)) + if best_score < score { + if i != 0 { + score -= backwardReferencePenaltyUsingLastDistance(uint(i)) + } + if best_score < score { + best_score = score + best_len = uint(len) + out.len = best_len + out.distance = backward + out.score = best_score + } + } + } + } + } + { + var bank uint = key & (h.numBanks - 1) + var backward uint = 0 + var hops uint = h.max_hops + var delta uint = cur_ix - uint(h.addr[key]) + var slot uint = uint(h.head[key]) + for { + tmp6 := hops + hops-- + if tmp6 == 0 { + break + } + var prev_ix uint + var last uint = slot + backward += delta + if backward > max_backward { + break + } + prev_ix = (cur_ix - backward) & ring_buffer_mask + slot = uint(h.banks[bank][last].next) + delta = uint(h.banks[bank][last].delta) + if cur_ix_masked+best_len > ring_buffer_mask || prev_ix+best_len > ring_buffer_mask || data[cur_ix_masked+best_len] != data[prev_ix+best_len] { + continue + } + { + var len uint = findMatchLengthWithLimit(data[prev_ix:], data[cur_ix_masked:], max_length) + if len >= 4 { + /* Comparing for >= 3 does not change the semantics, but just saves + for a few unnecessary binary logarithms in backward reference + score, since we are not interested in such short matches. */ + var score uint = backwardReferenceScore(uint(len), backward) + if best_score < score { + best_score = score + best_len = uint(len) + out.len = best_len + out.distance = backward + out.score = best_score + } + } + } + } + + h.Store(data, ring_buffer_mask, cur_ix) + } + + if out.score == min_score { + searchInStaticDictionary(dictionary, h, data[cur_ix_masked:], max_length, max_backward+gap, max_distance, out, false) + } +} diff --git a/vendor/github.com/andybalholm/brotli/hash_longest_match_quickly.go b/vendor/github.com/andybalholm/brotli/hash_longest_match_quickly.go new file mode 100644 index 000000000..9375dc155 --- /dev/null +++ b/vendor/github.com/andybalholm/brotli/hash_longest_match_quickly.go @@ -0,0 +1,214 @@ +package brotli + +import "encoding/binary" + +/* Copyright 2010 Google Inc. All Rights Reserved. + + Distributed under MIT license. + See file LICENSE for detail or copy at https://opensource.org/licenses/MIT +*/ + +/* For BUCKET_SWEEP == 1, enabling the dictionary lookup makes compression + a little faster (0.5% - 1%) and it compresses 0.15% better on small text + and HTML inputs. */ + +func (*hashLongestMatchQuickly) HashTypeLength() uint { + return 8 +} + +func (*hashLongestMatchQuickly) StoreLookahead() uint { + return 8 +} + +/* HashBytes is the function that chooses the bucket to place + the address in. The HashLongestMatch and hashLongestMatchQuickly + classes have separate, different implementations of hashing. */ +func (h *hashLongestMatchQuickly) HashBytes(data []byte) uint32 { + var hash uint64 = ((binary.LittleEndian.Uint64(data) << (64 - 8*h.hashLen)) * kHashMul64) + + /* The higher bits contain more mixture from the multiplication, + so we take our results from there. */ + return uint32(hash >> (64 - h.bucketBits)) +} + +/* A (forgetful) hash table to the data seen by the compressor, to + help create backward references to previous data. + + This is a hash map of fixed size (1 << 16). Starting from the + given index, 1 buckets are used to store values of a key. */ +type hashLongestMatchQuickly struct { + hasherCommon + + bucketBits uint + bucketSweep int + hashLen uint + useDictionary bool + + buckets []uint32 +} + +func (h *hashLongestMatchQuickly) Initialize(params *encoderParams) { + h.buckets = make([]uint32, 1<> 7 + /* Partial preparation is 100 times slower (per socket). */ + if one_shot && input_size <= partial_prepare_threshold { + var i uint + for i = 0; i < input_size; i++ { + var key uint32 = h.HashBytes(data[i:]) + for j := 0; j < h.bucketSweep; j++ { + h.buckets[key+uint32(j)] = 0 + } + } + } else { + /* It is not strictly necessary to fill this buffer here, but + not filling will make the results of the compression stochastic + (but correct). This is because random data would cause the + system to find accidentally good backward references here and there. */ + for i := range h.buckets { + h.buckets[i] = 0 + } + } +} + +/* Look at 5 bytes at &data[ix & mask]. + Compute a hash from these, and store the value somewhere within + [ix .. ix+3]. */ +func (h *hashLongestMatchQuickly) Store(data []byte, mask uint, ix uint) { + var key uint32 = h.HashBytes(data[ix&mask:]) + var off uint32 = uint32(ix>>3) % uint32(h.bucketSweep) + /* Wiggle the value with the bucket sweep range. */ + h.buckets[key+off] = uint32(ix) +} + +func (h *hashLongestMatchQuickly) StoreRange(data []byte, mask uint, ix_start uint, ix_end uint) { + var i uint + for i = ix_start; i < ix_end; i++ { + h.Store(data, mask, i) + } +} + +func (h *hashLongestMatchQuickly) StitchToPreviousBlock(num_bytes uint, position uint, ringbuffer []byte, ringbuffer_mask uint) { + if num_bytes >= h.HashTypeLength()-1 && position >= 3 { + /* Prepare the hashes for three last bytes of the last write. + These could not be calculated before, since they require knowledge + of both the previous and the current block. */ + h.Store(ringbuffer, ringbuffer_mask, position-3) + h.Store(ringbuffer, ringbuffer_mask, position-2) + h.Store(ringbuffer, ringbuffer_mask, position-1) + } +} + +func (*hashLongestMatchQuickly) PrepareDistanceCache(distance_cache []int) { +} + +/* Find a longest backward match of &data[cur_ix & ring_buffer_mask] + up to the length of max_length and stores the position cur_ix in the + hash table. + + Does not look for matches longer than max_length. + Does not look for matches further away than max_backward. + Writes the best match into |out|. + |out|->score is updated only if a better match is found. */ +func (h *hashLongestMatchQuickly) FindLongestMatch(dictionary *encoderDictionary, data []byte, ring_buffer_mask uint, distance_cache []int, cur_ix uint, max_length uint, max_backward uint, gap uint, max_distance uint, out *hasherSearchResult) { + var best_len_in uint = out.len + var cur_ix_masked uint = cur_ix & ring_buffer_mask + var key uint32 = h.HashBytes(data[cur_ix_masked:]) + var compare_char int = int(data[cur_ix_masked+best_len_in]) + var min_score uint = out.score + var best_score uint = out.score + var best_len uint = best_len_in + var cached_backward uint = uint(distance_cache[0]) + var prev_ix uint = cur_ix - cached_backward + var bucket []uint32 + out.len_code_delta = 0 + if prev_ix < cur_ix { + prev_ix &= uint(uint32(ring_buffer_mask)) + if compare_char == int(data[prev_ix+best_len]) { + var len uint = findMatchLengthWithLimit(data[prev_ix:], data[cur_ix_masked:], max_length) + if len >= 4 { + var score uint = backwardReferenceScoreUsingLastDistance(uint(len)) + if best_score < score { + best_score = score + best_len = uint(len) + out.len = uint(len) + out.distance = cached_backward + out.score = best_score + compare_char = int(data[cur_ix_masked+best_len]) + if h.bucketSweep == 1 { + h.buckets[key] = uint32(cur_ix) + return + } + } + } + } + } + + if h.bucketSweep == 1 { + var backward uint + var len uint + + /* Only one to look for, don't bother to prepare for a loop. */ + prev_ix = uint(h.buckets[key]) + + h.buckets[key] = uint32(cur_ix) + backward = cur_ix - prev_ix + prev_ix &= uint(uint32(ring_buffer_mask)) + if compare_char != int(data[prev_ix+best_len_in]) { + return + } + + if backward == 0 || backward > max_backward { + return + } + + len = findMatchLengthWithLimit(data[prev_ix:], data[cur_ix_masked:], max_length) + if len >= 4 { + var score uint = backwardReferenceScore(uint(len), backward) + if best_score < score { + out.len = uint(len) + out.distance = backward + out.score = score + return + } + } + } else { + bucket = h.buckets[key:] + var i int + prev_ix = uint(bucket[0]) + bucket = bucket[1:] + for i = 0; i < h.bucketSweep; (func() { i++; tmp3 := bucket; bucket = bucket[1:]; prev_ix = uint(tmp3[0]) })() { + var backward uint = cur_ix - prev_ix + var len uint + prev_ix &= uint(uint32(ring_buffer_mask)) + if compare_char != int(data[prev_ix+best_len]) { + continue + } + + if backward == 0 || backward > max_backward { + continue + } + + len = findMatchLengthWithLimit(data[prev_ix:], data[cur_ix_masked:], max_length) + if len >= 4 { + var score uint = backwardReferenceScore(uint(len), backward) + if best_score < score { + best_score = score + best_len = uint(len) + out.len = best_len + out.distance = backward + out.score = score + compare_char = int(data[cur_ix_masked+best_len]) + } + } + } + } + + if h.useDictionary && min_score == out.score { + searchInStaticDictionary(dictionary, h, data[cur_ix_masked:], max_length, max_backward+gap, max_distance, out, true) + } + + h.buckets[key+uint32((cur_ix>>3)%uint(h.bucketSweep))] = uint32(cur_ix) +} diff --git a/vendor/github.com/andybalholm/brotli/hash_rolling.go b/vendor/github.com/andybalholm/brotli/hash_rolling.go new file mode 100644 index 000000000..6630fc07e --- /dev/null +++ b/vendor/github.com/andybalholm/brotli/hash_rolling.go @@ -0,0 +1,168 @@ +package brotli + +/* Copyright 2018 Google Inc. All Rights Reserved. + + Distributed under MIT license. + See file LICENSE for detail or copy at https://opensource.org/licenses/MIT +*/ + +/* NOTE: this hasher does not search in the dictionary. It is used as + backup-hasher, the main hasher already searches in it. */ + +const kRollingHashMul32 uint32 = 69069 + +const kInvalidPosHashRolling uint32 = 0xffffffff + +/* This hasher uses a longer forward length, but returning a higher value here + will hurt compression by the main hasher when combined with a composite + hasher. The hasher tests for forward itself instead. */ +func (*hashRolling) HashTypeLength() uint { + return 4 +} + +func (*hashRolling) StoreLookahead() uint { + return 4 +} + +/* Computes a code from a single byte. A lookup table of 256 values could be + used, but simply adding 1 works about as good. */ +func (*hashRolling) HashByte(b byte) uint32 { + return uint32(b) + 1 +} + +func (h *hashRolling) HashRollingFunctionInitial(state uint32, add byte, factor uint32) uint32 { + return uint32(factor*state + h.HashByte(add)) +} + +func (h *hashRolling) HashRollingFunction(state uint32, add byte, rem byte, factor uint32, factor_remove uint32) uint32 { + return uint32(factor*state + h.HashByte(add) - factor_remove*h.HashByte(rem)) +} + +/* Rolling hash for long distance long string matches. Stores one position + per bucket, bucket key is computed over a long region. */ +type hashRolling struct { + hasherCommon + + jump int + + state uint32 + table []uint32 + next_ix uint + factor uint32 + factor_remove uint32 +} + +func (h *hashRolling) Initialize(params *encoderParams) { + h.state = 0 + h.next_ix = 0 + + h.factor = kRollingHashMul32 + + /* Compute the factor of the oldest byte to remove: factor**steps modulo + 0xffffffff (the multiplications rely on 32-bit overflow) */ + h.factor_remove = 1 + + for i := 0; i < 32; i += h.jump { + h.factor_remove *= h.factor + } + + h.table = make([]uint32, 16777216) + for i := 0; i < 16777216; i++ { + h.table[i] = kInvalidPosHashRolling + } +} + +func (h *hashRolling) Prepare(one_shot bool, input_size uint, data []byte) { + /* Too small size, cannot use this hasher. */ + if input_size < 32 { + return + } + h.state = 0 + for i := 0; i < 32; i += h.jump { + h.state = h.HashRollingFunctionInitial(h.state, data[i], h.factor) + } +} + +func (*hashRolling) Store(data []byte, mask uint, ix uint) { +} + +func (*hashRolling) StoreRange(data []byte, mask uint, ix_start uint, ix_end uint) { +} + +func (h *hashRolling) StitchToPreviousBlock(num_bytes uint, position uint, ringbuffer []byte, ring_buffer_mask uint) { + var position_masked uint + /* In this case we must re-initialize the hasher from scratch from the + current position. */ + + var available uint = num_bytes + if position&uint(h.jump-1) != 0 { + var diff uint = uint(h.jump) - (position & uint(h.jump-1)) + if diff > available { + available = 0 + } else { + available = available - diff + } + position += diff + } + + position_masked = position & ring_buffer_mask + + /* wrapping around ringbuffer not handled. */ + if available > ring_buffer_mask-position_masked { + available = ring_buffer_mask - position_masked + } + + h.Prepare(false, available, ringbuffer[position&ring_buffer_mask:]) + h.next_ix = position +} + +func (*hashRolling) PrepareDistanceCache(distance_cache []int) { +} + +func (h *hashRolling) FindLongestMatch(dictionary *encoderDictionary, data []byte, ring_buffer_mask uint, distance_cache []int, cur_ix uint, max_length uint, max_backward uint, gap uint, max_distance uint, out *hasherSearchResult) { + var cur_ix_masked uint = cur_ix & ring_buffer_mask + var pos uint = h.next_ix + + if cur_ix&uint(h.jump-1) != 0 { + return + } + + /* Not enough lookahead */ + if max_length < 32 { + return + } + + for pos = h.next_ix; pos <= cur_ix; pos += uint(h.jump) { + var code uint32 = h.state & ((16777216 * 64) - 1) + var rem byte = data[pos&ring_buffer_mask] + var add byte = data[(pos+32)&ring_buffer_mask] + var found_ix uint = uint(kInvalidPosHashRolling) + + h.state = h.HashRollingFunction(h.state, add, rem, h.factor, h.factor_remove) + + if code < 16777216 { + found_ix = uint(h.table[code]) + h.table[code] = uint32(pos) + if pos == cur_ix && uint32(found_ix) != kInvalidPosHashRolling { + /* The cast to 32-bit makes backward distances up to 4GB work even + if cur_ix is above 4GB, despite using 32-bit values in the table. */ + var backward uint = uint(uint32(cur_ix - found_ix)) + if backward <= max_backward { + var found_ix_masked uint = found_ix & ring_buffer_mask + var len uint = findMatchLengthWithLimit(data[found_ix_masked:], data[cur_ix_masked:], max_length) + if len >= 4 && len > out.len { + var score uint = backwardReferenceScore(uint(len), backward) + if score > out.score { + out.len = uint(len) + out.distance = backward + out.score = score + out.len_code_delta = 0 + } + } + } + } + } + } + + h.next_ix = cur_ix + uint(h.jump) +} diff --git a/vendor/github.com/andybalholm/brotli/histogram.go b/vendor/github.com/andybalholm/brotli/histogram.go new file mode 100644 index 000000000..0346622be --- /dev/null +++ b/vendor/github.com/andybalholm/brotli/histogram.go @@ -0,0 +1,226 @@ +package brotli + +import "math" + +/* The distance symbols effectively used by "Large Window Brotli" (32-bit). */ +const numHistogramDistanceSymbols = 544 + +type histogramLiteral struct { + data_ [numLiteralSymbols]uint32 + total_count_ uint + bit_cost_ float64 +} + +func histogramClearLiteral(self *histogramLiteral) { + self.data_ = [numLiteralSymbols]uint32{} + self.total_count_ = 0 + self.bit_cost_ = math.MaxFloat64 +} + +func clearHistogramsLiteral(array []histogramLiteral, length uint) { + var i uint + for i = 0; i < length; i++ { + histogramClearLiteral(&array[i:][0]) + } +} + +func histogramAddLiteral(self *histogramLiteral, val uint) { + self.data_[val]++ + self.total_count_++ +} + +func histogramAddVectorLiteral(self *histogramLiteral, p []byte, n uint) { + self.total_count_ += n + n += 1 + for { + n-- + if n == 0 { + break + } + self.data_[p[0]]++ + p = p[1:] + } +} + +func histogramAddHistogramLiteral(self *histogramLiteral, v *histogramLiteral) { + var i uint + self.total_count_ += v.total_count_ + for i = 0; i < numLiteralSymbols; i++ { + self.data_[i] += v.data_[i] + } +} + +func histogramDataSizeLiteral() uint { + return numLiteralSymbols +} + +type histogramCommand struct { + data_ [numCommandSymbols]uint32 + total_count_ uint + bit_cost_ float64 +} + +func histogramClearCommand(self *histogramCommand) { + self.data_ = [numCommandSymbols]uint32{} + self.total_count_ = 0 + self.bit_cost_ = math.MaxFloat64 +} + +func clearHistogramsCommand(array []histogramCommand, length uint) { + var i uint + for i = 0; i < length; i++ { + histogramClearCommand(&array[i:][0]) + } +} + +func histogramAddCommand(self *histogramCommand, val uint) { + self.data_[val]++ + self.total_count_++ +} + +func histogramAddVectorCommand(self *histogramCommand, p []uint16, n uint) { + self.total_count_ += n + n += 1 + for { + n-- + if n == 0 { + break + } + self.data_[p[0]]++ + p = p[1:] + } +} + +func histogramAddHistogramCommand(self *histogramCommand, v *histogramCommand) { + var i uint + self.total_count_ += v.total_count_ + for i = 0; i < numCommandSymbols; i++ { + self.data_[i] += v.data_[i] + } +} + +func histogramDataSizeCommand() uint { + return numCommandSymbols +} + +type histogramDistance struct { + data_ [numDistanceSymbols]uint32 + total_count_ uint + bit_cost_ float64 +} + +func histogramClearDistance(self *histogramDistance) { + self.data_ = [numDistanceSymbols]uint32{} + self.total_count_ = 0 + self.bit_cost_ = math.MaxFloat64 +} + +func clearHistogramsDistance(array []histogramDistance, length uint) { + var i uint + for i = 0; i < length; i++ { + histogramClearDistance(&array[i:][0]) + } +} + +func histogramAddDistance(self *histogramDistance, val uint) { + self.data_[val]++ + self.total_count_++ +} + +func histogramAddVectorDistance(self *histogramDistance, p []uint16, n uint) { + self.total_count_ += n + n += 1 + for { + n-- + if n == 0 { + break + } + self.data_[p[0]]++ + p = p[1:] + } +} + +func histogramAddHistogramDistance(self *histogramDistance, v *histogramDistance) { + var i uint + self.total_count_ += v.total_count_ + for i = 0; i < numDistanceSymbols; i++ { + self.data_[i] += v.data_[i] + } +} + +func histogramDataSizeDistance() uint { + return numDistanceSymbols +} + +type blockSplitIterator struct { + split_ *blockSplit + idx_ uint + type_ uint + length_ uint +} + +func initBlockSplitIterator(self *blockSplitIterator, split *blockSplit) { + self.split_ = split + self.idx_ = 0 + self.type_ = 0 + if len(split.lengths) > 0 { + self.length_ = uint(split.lengths[0]) + } else { + self.length_ = 0 + } +} + +func blockSplitIteratorNext(self *blockSplitIterator) { + if self.length_ == 0 { + self.idx_++ + self.type_ = uint(self.split_.types[self.idx_]) + self.length_ = uint(self.split_.lengths[self.idx_]) + } + + self.length_-- +} + +func buildHistogramsWithContext(cmds []command, literal_split *blockSplit, insert_and_copy_split *blockSplit, dist_split *blockSplit, ringbuffer []byte, start_pos uint, mask uint, prev_byte byte, prev_byte2 byte, context_modes []int, literal_histograms []histogramLiteral, insert_and_copy_histograms []histogramCommand, copy_dist_histograms []histogramDistance) { + var pos uint = start_pos + var literal_it blockSplitIterator + var insert_and_copy_it blockSplitIterator + var dist_it blockSplitIterator + + initBlockSplitIterator(&literal_it, literal_split) + initBlockSplitIterator(&insert_and_copy_it, insert_and_copy_split) + initBlockSplitIterator(&dist_it, dist_split) + for i := range cmds { + var cmd *command = &cmds[i] + var j uint + blockSplitIteratorNext(&insert_and_copy_it) + histogramAddCommand(&insert_and_copy_histograms[insert_and_copy_it.type_], uint(cmd.cmd_prefix_)) + + /* TODO: unwrap iterator blocks. */ + for j = uint(cmd.insert_len_); j != 0; j-- { + var context uint + blockSplitIteratorNext(&literal_it) + context = literal_it.type_ + if context_modes != nil { + var lut contextLUT = getContextLUT(context_modes[context]) + context = (context << literalContextBits) + uint(getContext(prev_byte, prev_byte2, lut)) + } + + histogramAddLiteral(&literal_histograms[context], uint(ringbuffer[pos&mask])) + prev_byte2 = prev_byte + prev_byte = ringbuffer[pos&mask] + pos++ + } + + pos += uint(commandCopyLen(cmd)) + if commandCopyLen(cmd) != 0 { + prev_byte2 = ringbuffer[(pos-2)&mask] + prev_byte = ringbuffer[(pos-1)&mask] + if cmd.cmd_prefix_ >= 128 { + var context uint + blockSplitIteratorNext(&dist_it) + context = uint(uint32(dist_it.type_< bestQ && + (spec.Value == "*" || spec.Value == offer) { + bestQ = spec.Q + bestOffer = offer + } + } + } + if bestQ == 0 { + bestOffer = "" + } + return bestOffer +} + +// acceptSpec describes an Accept* header. +type acceptSpec struct { + Value string + Q float64 +} + +// parseAccept parses Accept* headers. +func parseAccept(header http.Header, key string) (specs []acceptSpec) { +loop: + for _, s := range header[key] { + for { + var spec acceptSpec + spec.Value, s = expectTokenSlash(s) + if spec.Value == "" { + continue loop + } + spec.Q = 1.0 + s = skipSpace(s) + if strings.HasPrefix(s, ";") { + s = skipSpace(s[1:]) + if !strings.HasPrefix(s, "q=") { + continue loop + } + spec.Q, s = expectQuality(s[2:]) + if spec.Q < 0.0 { + continue loop + } + } + specs = append(specs, spec) + s = skipSpace(s) + if !strings.HasPrefix(s, ",") { + continue loop + } + s = skipSpace(s[1:]) + } + } + return +} + +func skipSpace(s string) (rest string) { + i := 0 + for ; i < len(s); i++ { + if octetTypes[s[i]]&isSpace == 0 { + break + } + } + return s[i:] +} + +func expectTokenSlash(s string) (token, rest string) { + i := 0 + for ; i < len(s); i++ { + b := s[i] + if (octetTypes[b]&isToken == 0) && b != '/' { + break + } + } + return s[:i], s[i:] +} + +func expectQuality(s string) (q float64, rest string) { + switch { + case len(s) == 0: + return -1, "" + case s[0] == '0': + q = 0 + case s[0] == '1': + q = 1 + default: + return -1, "" + } + s = s[1:] + if !strings.HasPrefix(s, ".") { + return q, s + } + s = s[1:] + i := 0 + n := 0 + d := 1 + for ; i < len(s); i++ { + b := s[i] + if b < '0' || b > '9' { + break + } + n = n*10 + int(b) - '0' + d *= 10 + } + return q + float64(n)/float64(d), s[i:] +} + +// Octet types from RFC 2616. +var octetTypes [256]octetType + +type octetType byte + +const ( + isToken octetType = 1 << iota + isSpace +) + +func init() { + // OCTET = + // CHAR = + // CTL = + // CR = + // LF = + // SP = + // HT = + // <"> = + // CRLF = CR LF + // LWS = [CRLF] 1*( SP | HT ) + // TEXT = + // separators = "(" | ")" | "<" | ">" | "@" | "," | ";" | ":" | "\" | <"> + // | "/" | "[" | "]" | "?" | "=" | "{" | "}" | SP | HT + // token = 1* + // qdtext = > + + for c := 0; c < 256; c++ { + var t octetType + isCtl := c <= 31 || c == 127 + isChar := 0 <= c && c <= 127 + isSeparator := strings.ContainsRune(" \t\"(),/:;<=>?@[]\\{}", rune(c)) + if strings.ContainsRune(" \t\r\n", rune(c)) { + t |= isSpace + } + if isChar && !isCtl && !isSeparator { + t |= isToken + } + octetTypes[c] = t + } +} diff --git a/vendor/github.com/andybalholm/brotli/huffman.go b/vendor/github.com/andybalholm/brotli/huffman.go new file mode 100644 index 000000000..182f3d2a5 --- /dev/null +++ b/vendor/github.com/andybalholm/brotli/huffman.go @@ -0,0 +1,653 @@ +package brotli + +/* Copyright 2013 Google Inc. All Rights Reserved. + + Distributed under MIT license. + See file LICENSE for detail or copy at https://opensource.org/licenses/MIT +*/ + +/* Utilities for building Huffman decoding tables. */ + +const huffmanMaxCodeLength = 15 + +/* Maximum possible Huffman table size for an alphabet size of (index * 32), + max code length 15 and root table bits 8. */ +var kMaxHuffmanTableSize = []uint16{ + 256, + 402, + 436, + 468, + 500, + 534, + 566, + 598, + 630, + 662, + 694, + 726, + 758, + 790, + 822, + 854, + 886, + 920, + 952, + 984, + 1016, + 1048, + 1080, + 1112, + 1144, + 1176, + 1208, + 1240, + 1272, + 1304, + 1336, + 1368, + 1400, + 1432, + 1464, + 1496, + 1528, +} + +/* BROTLI_NUM_BLOCK_LEN_SYMBOLS == 26 */ +const huffmanMaxSize26 = 396 + +/* BROTLI_MAX_BLOCK_TYPE_SYMBOLS == 258 */ +const huffmanMaxSize258 = 632 + +/* BROTLI_MAX_CONTEXT_MAP_SYMBOLS == 272 */ +const huffmanMaxSize272 = 646 + +const huffmanMaxCodeLengthCodeLength = 5 + +/* Do not create this struct directly - use the ConstructHuffmanCode + * constructor below! */ +type huffmanCode struct { + bits byte + value uint16 +} + +func constructHuffmanCode(bits byte, value uint16) huffmanCode { + var h huffmanCode + h.bits = bits + h.value = value + return h +} + +/* Builds Huffman lookup table assuming code lengths are in symbol order. */ + +/* Builds Huffman lookup table assuming code lengths are in symbol order. + Returns size of resulting table. */ + +/* Builds a simple Huffman table. The |num_symbols| parameter is to be + interpreted as follows: 0 means 1 symbol, 1 means 2 symbols, + 2 means 3 symbols, 3 means 4 symbols with lengths [2, 2, 2, 2], + 4 means 4 symbols with lengths [1, 2, 3, 3]. */ + +/* Contains a collection of Huffman trees with the same alphabet size. */ +/* max_symbol is needed due to simple codes since log2(alphabet_size) could be + greater than log2(max_symbol). */ +type huffmanTreeGroup struct { + htrees [][]huffmanCode + codes []huffmanCode + alphabet_size uint16 + max_symbol uint16 + num_htrees uint16 +} + +const reverseBitsMax = 8 + +const reverseBitsBase = 0 + +var kReverseBits = [1 << reverseBitsMax]byte{ + 0x00, + 0x80, + 0x40, + 0xC0, + 0x20, + 0xA0, + 0x60, + 0xE0, + 0x10, + 0x90, + 0x50, + 0xD0, + 0x30, + 0xB0, + 0x70, + 0xF0, + 0x08, + 0x88, + 0x48, + 0xC8, + 0x28, + 0xA8, + 0x68, + 0xE8, + 0x18, + 0x98, + 0x58, + 0xD8, + 0x38, + 0xB8, + 0x78, + 0xF8, + 0x04, + 0x84, + 0x44, + 0xC4, + 0x24, + 0xA4, + 0x64, + 0xE4, + 0x14, + 0x94, + 0x54, + 0xD4, + 0x34, + 0xB4, + 0x74, + 0xF4, + 0x0C, + 0x8C, + 0x4C, + 0xCC, + 0x2C, + 0xAC, + 0x6C, + 0xEC, + 0x1C, + 0x9C, + 0x5C, + 0xDC, + 0x3C, + 0xBC, + 0x7C, + 0xFC, + 0x02, + 0x82, + 0x42, + 0xC2, + 0x22, + 0xA2, + 0x62, + 0xE2, + 0x12, + 0x92, + 0x52, + 0xD2, + 0x32, + 0xB2, + 0x72, + 0xF2, + 0x0A, + 0x8A, + 0x4A, + 0xCA, + 0x2A, + 0xAA, + 0x6A, + 0xEA, + 0x1A, + 0x9A, + 0x5A, + 0xDA, + 0x3A, + 0xBA, + 0x7A, + 0xFA, + 0x06, + 0x86, + 0x46, + 0xC6, + 0x26, + 0xA6, + 0x66, + 0xE6, + 0x16, + 0x96, + 0x56, + 0xD6, + 0x36, + 0xB6, + 0x76, + 0xF6, + 0x0E, + 0x8E, + 0x4E, + 0xCE, + 0x2E, + 0xAE, + 0x6E, + 0xEE, + 0x1E, + 0x9E, + 0x5E, + 0xDE, + 0x3E, + 0xBE, + 0x7E, + 0xFE, + 0x01, + 0x81, + 0x41, + 0xC1, + 0x21, + 0xA1, + 0x61, + 0xE1, + 0x11, + 0x91, + 0x51, + 0xD1, + 0x31, + 0xB1, + 0x71, + 0xF1, + 0x09, + 0x89, + 0x49, + 0xC9, + 0x29, + 0xA9, + 0x69, + 0xE9, + 0x19, + 0x99, + 0x59, + 0xD9, + 0x39, + 0xB9, + 0x79, + 0xF9, + 0x05, + 0x85, + 0x45, + 0xC5, + 0x25, + 0xA5, + 0x65, + 0xE5, + 0x15, + 0x95, + 0x55, + 0xD5, + 0x35, + 0xB5, + 0x75, + 0xF5, + 0x0D, + 0x8D, + 0x4D, + 0xCD, + 0x2D, + 0xAD, + 0x6D, + 0xED, + 0x1D, + 0x9D, + 0x5D, + 0xDD, + 0x3D, + 0xBD, + 0x7D, + 0xFD, + 0x03, + 0x83, + 0x43, + 0xC3, + 0x23, + 0xA3, + 0x63, + 0xE3, + 0x13, + 0x93, + 0x53, + 0xD3, + 0x33, + 0xB3, + 0x73, + 0xF3, + 0x0B, + 0x8B, + 0x4B, + 0xCB, + 0x2B, + 0xAB, + 0x6B, + 0xEB, + 0x1B, + 0x9B, + 0x5B, + 0xDB, + 0x3B, + 0xBB, + 0x7B, + 0xFB, + 0x07, + 0x87, + 0x47, + 0xC7, + 0x27, + 0xA7, + 0x67, + 0xE7, + 0x17, + 0x97, + 0x57, + 0xD7, + 0x37, + 0xB7, + 0x77, + 0xF7, + 0x0F, + 0x8F, + 0x4F, + 0xCF, + 0x2F, + 0xAF, + 0x6F, + 0xEF, + 0x1F, + 0x9F, + 0x5F, + 0xDF, + 0x3F, + 0xBF, + 0x7F, + 0xFF, +} + +const reverseBitsLowest = (uint64(1) << (reverseBitsMax - 1 + reverseBitsBase)) + +/* Returns reverse(num >> BROTLI_REVERSE_BITS_BASE, BROTLI_REVERSE_BITS_MAX), + where reverse(value, len) is the bit-wise reversal of the len least + significant bits of value. */ +func reverseBits8(num uint64) uint64 { + return uint64(kReverseBits[num]) +} + +/* Stores code in table[0], table[step], table[2*step], ..., table[end] */ +/* Assumes that end is an integer multiple of step */ +func replicateValue(table []huffmanCode, step int, end int, code huffmanCode) { + for { + end -= step + table[end] = code + if end <= 0 { + break + } + } +} + +/* Returns the table width of the next 2nd level table. |count| is the histogram + of bit lengths for the remaining symbols, |len| is the code length of the + next processed symbol. */ +func nextTableBitSize(count []uint16, len int, root_bits int) int { + var left int = 1 << uint(len-root_bits) + for len < huffmanMaxCodeLength { + left -= int(count[len]) + if left <= 0 { + break + } + len++ + left <<= 1 + } + + return len - root_bits +} + +func buildCodeLengthsHuffmanTable(table []huffmanCode, code_lengths []byte, count []uint16) { + var code huffmanCode /* current table entry */ /* symbol index in original or sorted table */ /* prefix code */ /* prefix code addend */ /* step size to replicate values in current table */ /* size of current table */ /* symbols sorted by code length */ + var symbol int + var key uint64 + var key_step uint64 + var step int + var table_size int + var sorted [codeLengthCodes]int + var offset [huffmanMaxCodeLengthCodeLength + 1]int + var bits int + var bits_count int + /* offsets in sorted table for each length */ + assert(huffmanMaxCodeLengthCodeLength <= reverseBitsMax) + + /* Generate offsets into sorted symbol table by code length. */ + symbol = -1 + + bits = 1 + var i int + for i = 0; i < huffmanMaxCodeLengthCodeLength; i++ { + symbol += int(count[bits]) + offset[bits] = symbol + bits++ + } + + /* Symbols with code length 0 are placed after all other symbols. */ + offset[0] = codeLengthCodes - 1 + + /* Sort symbols by length, by symbol order within each length. */ + symbol = codeLengthCodes + + for { + var i int + for i = 0; i < 6; i++ { + symbol-- + sorted[offset[code_lengths[symbol]]] = symbol + offset[code_lengths[symbol]]-- + } + if symbol == 0 { + break + } + } + + table_size = 1 << huffmanMaxCodeLengthCodeLength + + /* Special case: all symbols but one have 0 code length. */ + if offset[0] == 0 { + code = constructHuffmanCode(0, uint16(sorted[0])) + for key = 0; key < uint64(table_size); key++ { + table[key] = code + } + + return + } + + /* Fill in table. */ + key = 0 + + key_step = reverseBitsLowest + symbol = 0 + bits = 1 + step = 2 + for { + for bits_count = int(count[bits]); bits_count != 0; bits_count-- { + code = constructHuffmanCode(byte(bits), uint16(sorted[symbol])) + symbol++ + replicateValue(table[reverseBits8(key):], step, table_size, code) + key += key_step + } + + step <<= 1 + key_step >>= 1 + bits++ + if bits > huffmanMaxCodeLengthCodeLength { + break + } + } +} + +func buildHuffmanTable(root_table []huffmanCode, root_bits int, symbol_lists symbolList, count []uint16) uint32 { + var code huffmanCode /* current table entry */ /* next available space in table */ /* current code length */ /* symbol index in original or sorted table */ /* prefix code */ /* prefix code addend */ /* 2nd level table prefix code */ /* 2nd level table prefix code addend */ /* step size to replicate values in current table */ /* key length of current table */ /* size of current table */ /* sum of root table size and 2nd level table sizes */ + var table []huffmanCode + var len int + var symbol int + var key uint64 + var key_step uint64 + var sub_key uint64 + var sub_key_step uint64 + var step int + var table_bits int + var table_size int + var total_size int + var max_length int = -1 + var bits int + var bits_count int + + assert(root_bits <= reverseBitsMax) + assert(huffmanMaxCodeLength-root_bits <= reverseBitsMax) + + for symbolListGet(symbol_lists, max_length) == 0xFFFF { + max_length-- + } + max_length += huffmanMaxCodeLength + 1 + + table = root_table + table_bits = root_bits + table_size = 1 << uint(table_bits) + total_size = table_size + + /* Fill in the root table. Reduce the table size to if possible, + and create the repetitions by memcpy. */ + if table_bits > max_length { + table_bits = max_length + table_size = 1 << uint(table_bits) + } + + key = 0 + key_step = reverseBitsLowest + bits = 1 + step = 2 + for { + symbol = bits - (huffmanMaxCodeLength + 1) + for bits_count = int(count[bits]); bits_count != 0; bits_count-- { + symbol = int(symbolListGet(symbol_lists, symbol)) + code = constructHuffmanCode(byte(bits), uint16(symbol)) + replicateValue(table[reverseBits8(key):], step, table_size, code) + key += key_step + } + + step <<= 1 + key_step >>= 1 + bits++ + if bits > table_bits { + break + } + } + + /* If root_bits != table_bits then replicate to fill the remaining slots. */ + for total_size != table_size { + copy(table[table_size:], table[:uint(table_size)]) + table_size <<= 1 + } + + /* Fill in 2nd level tables and add pointers to root table. */ + key_step = reverseBitsLowest >> uint(root_bits-1) + + sub_key = reverseBitsLowest << 1 + sub_key_step = reverseBitsLowest + len = root_bits + 1 + step = 2 + for ; len <= max_length; len++ { + symbol = len - (huffmanMaxCodeLength + 1) + for ; count[len] != 0; count[len]-- { + if sub_key == reverseBitsLowest<<1 { + table = table[table_size:] + table_bits = nextTableBitSize(count, int(len), root_bits) + table_size = 1 << uint(table_bits) + total_size += table_size + sub_key = reverseBits8(key) + key += key_step + root_table[sub_key] = constructHuffmanCode(byte(table_bits+root_bits), uint16(uint64(uint(-cap(table)+cap(root_table)))-sub_key)) + sub_key = 0 + } + + symbol = int(symbolListGet(symbol_lists, symbol)) + code = constructHuffmanCode(byte(len-root_bits), uint16(symbol)) + replicateValue(table[reverseBits8(sub_key):], step, table_size, code) + sub_key += sub_key_step + } + + step <<= 1 + sub_key_step >>= 1 + } + + return uint32(total_size) +} + +func buildSimpleHuffmanTable(table []huffmanCode, root_bits int, val []uint16, num_symbols uint32) uint32 { + var table_size uint32 = 1 + var goal_size uint32 = 1 << uint(root_bits) + switch num_symbols { + case 0: + table[0] = constructHuffmanCode(0, val[0]) + + case 1: + if val[1] > val[0] { + table[0] = constructHuffmanCode(1, val[0]) + table[1] = constructHuffmanCode(1, val[1]) + } else { + table[0] = constructHuffmanCode(1, val[1]) + table[1] = constructHuffmanCode(1, val[0]) + } + + table_size = 2 + + case 2: + table[0] = constructHuffmanCode(1, val[0]) + table[2] = constructHuffmanCode(1, val[0]) + if val[2] > val[1] { + table[1] = constructHuffmanCode(2, val[1]) + table[3] = constructHuffmanCode(2, val[2]) + } else { + table[1] = constructHuffmanCode(2, val[2]) + table[3] = constructHuffmanCode(2, val[1]) + } + + table_size = 4 + + case 3: + var i int + var k int + for i = 0; i < 3; i++ { + for k = i + 1; k < 4; k++ { + if val[k] < val[i] { + var t uint16 = val[k] + val[k] = val[i] + val[i] = t + } + } + } + + table[0] = constructHuffmanCode(2, val[0]) + table[2] = constructHuffmanCode(2, val[1]) + table[1] = constructHuffmanCode(2, val[2]) + table[3] = constructHuffmanCode(2, val[3]) + table_size = 4 + + case 4: + if val[3] < val[2] { + var t uint16 = val[3] + val[3] = val[2] + val[2] = t + } + + table[0] = constructHuffmanCode(1, val[0]) + table[1] = constructHuffmanCode(2, val[1]) + table[2] = constructHuffmanCode(1, val[0]) + table[3] = constructHuffmanCode(3, val[2]) + table[4] = constructHuffmanCode(1, val[0]) + table[5] = constructHuffmanCode(2, val[1]) + table[6] = constructHuffmanCode(1, val[0]) + table[7] = constructHuffmanCode(3, val[3]) + table_size = 8 + } + + for table_size != goal_size { + copy(table[table_size:], table[:uint(table_size)]) + table_size <<= 1 + } + + return goal_size +} diff --git a/vendor/github.com/andybalholm/brotli/literal_cost.go b/vendor/github.com/andybalholm/brotli/literal_cost.go new file mode 100644 index 000000000..5a9ace94e --- /dev/null +++ b/vendor/github.com/andybalholm/brotli/literal_cost.go @@ -0,0 +1,182 @@ +package brotli + +func utf8Position(last uint, c uint, clamp uint) uint { + if c < 128 { + return 0 /* Next one is the 'Byte 1' again. */ + } else if c >= 192 { /* Next one is the 'Byte 2' of utf-8 encoding. */ + return brotli_min_size_t(1, clamp) + } else { + /* Let's decide over the last byte if this ends the sequence. */ + if last < 0xE0 { + return 0 /* Completed two or three byte coding. */ /* Next one is the 'Byte 3' of utf-8 encoding. */ + } else { + return brotli_min_size_t(2, clamp) + } + } +} + +func decideMultiByteStatsLevel(pos uint, len uint, mask uint, data []byte) uint { + var counts = [3]uint{0} /* should be 2, but 1 compresses better. */ + var max_utf8 uint = 1 + var last_c uint = 0 + var i uint + for i = 0; i < len; i++ { + var c uint = uint(data[(pos+i)&mask]) + counts[utf8Position(last_c, c, 2)]++ + last_c = c + } + + if counts[2] < 500 { + max_utf8 = 1 + } + + if counts[1]+counts[2] < 25 { + max_utf8 = 0 + } + + return max_utf8 +} + +func estimateBitCostsForLiteralsUTF8(pos uint, len uint, mask uint, data []byte, cost []float32) { + var max_utf8 uint = decideMultiByteStatsLevel(pos, uint(len), mask, data) + /* Bootstrap histograms. */ + var histogram = [3][256]uint{[256]uint{0}} + var window_half uint = 495 + var in_window uint = brotli_min_size_t(window_half, uint(len)) + var in_window_utf8 = [3]uint{0} + /* max_utf8 is 0 (normal ASCII single byte modeling), + 1 (for 2-byte UTF-8 modeling), or 2 (for 3-byte UTF-8 modeling). */ + + var i uint + { + var last_c uint = 0 + var utf8_pos uint = 0 + for i = 0; i < in_window; i++ { + var c uint = uint(data[(pos+i)&mask]) + histogram[utf8_pos][c]++ + in_window_utf8[utf8_pos]++ + utf8_pos = utf8Position(last_c, c, max_utf8) + last_c = c + } + } + + /* Compute bit costs with sliding window. */ + for i = 0; i < len; i++ { + if i >= window_half { + var c uint + var last_c uint + if i < window_half+1 { + c = 0 + } else { + c = uint(data[(pos+i-window_half-1)&mask]) + } + if i < window_half+2 { + last_c = 0 + } else { + last_c = uint(data[(pos+i-window_half-2)&mask]) + } + /* Remove a byte in the past. */ + + var utf8_pos2 uint = utf8Position(last_c, c, max_utf8) + histogram[utf8_pos2][data[(pos+i-window_half)&mask]]-- + in_window_utf8[utf8_pos2]-- + } + + if i+window_half < len { + var c uint = uint(data[(pos+i+window_half-1)&mask]) + var last_c uint = uint(data[(pos+i+window_half-2)&mask]) + /* Add a byte in the future. */ + + var utf8_pos2 uint = utf8Position(last_c, c, max_utf8) + histogram[utf8_pos2][data[(pos+i+window_half)&mask]]++ + in_window_utf8[utf8_pos2]++ + } + { + var c uint + var last_c uint + if i < 1 { + c = 0 + } else { + c = uint(data[(pos+i-1)&mask]) + } + if i < 2 { + last_c = 0 + } else { + last_c = uint(data[(pos+i-2)&mask]) + } + var utf8_pos uint = utf8Position(last_c, c, max_utf8) + var masked_pos uint = (pos + i) & mask + var histo uint = histogram[utf8_pos][data[masked_pos]] + var lit_cost float64 + if histo == 0 { + histo = 1 + } + + lit_cost = fastLog2(in_window_utf8[utf8_pos]) - fastLog2(histo) + lit_cost += 0.02905 + if lit_cost < 1.0 { + lit_cost *= 0.5 + lit_cost += 0.5 + } + + /* Make the first bytes more expensive -- seems to help, not sure why. + Perhaps because the entropy source is changing its properties + rapidly in the beginning of the file, perhaps because the beginning + of the data is a statistical "anomaly". */ + if i < 2000 { + lit_cost += 0.7 - (float64(2000-i) / 2000.0 * 0.35) + } + + cost[i] = float32(lit_cost) + } + } +} + +func estimateBitCostsForLiterals(pos uint, len uint, mask uint, data []byte, cost []float32) { + if isMostlyUTF8(data, pos, mask, uint(len), kMinUTF8Ratio) { + estimateBitCostsForLiteralsUTF8(pos, uint(len), mask, data, cost) + return + } else { + var histogram = [256]uint{0} + var window_half uint = 2000 + var in_window uint = brotli_min_size_t(window_half, uint(len)) + var i uint + /* Bootstrap histogram. */ + for i = 0; i < in_window; i++ { + histogram[data[(pos+i)&mask]]++ + } + + /* Compute bit costs with sliding window. */ + for i = 0; i < len; i++ { + var histo uint + if i >= window_half { + /* Remove a byte in the past. */ + histogram[data[(pos+i-window_half)&mask]]-- + + in_window-- + } + + if i+window_half < len { + /* Add a byte in the future. */ + histogram[data[(pos+i+window_half)&mask]]++ + + in_window++ + } + + histo = histogram[data[(pos+i)&mask]] + if histo == 0 { + histo = 1 + } + { + var lit_cost float64 = fastLog2(in_window) - fastLog2(histo) + lit_cost += 0.029 + if lit_cost < 1.0 { + lit_cost *= 0.5 + lit_cost += 0.5 + } + + cost[i] = float32(lit_cost) + } + } + } +} diff --git a/vendor/github.com/andybalholm/brotli/matchfinder/emitter.go b/vendor/github.com/andybalholm/brotli/matchfinder/emitter.go new file mode 100644 index 000000000..507d1cae6 --- /dev/null +++ b/vendor/github.com/andybalholm/brotli/matchfinder/emitter.go @@ -0,0 +1,34 @@ +package matchfinder + +// An absoluteMatch is like a Match, but it stores indexes into the byte +// stream instead of lengths. +type absoluteMatch struct { + // Start is the index of the first byte. + Start int + + // End is the index of the byte after the last byte + // (so that End - Start = Length). + End int + + // Match is the index of the previous data that matches + // (Start - Match = Distance). + Match int +} + +// A matchEmitter manages the output of matches for a MatchFinder. +type matchEmitter struct { + // Dst is the destination slice that Matches are added to. + Dst []Match + + // NextEmit is the index of the next byte to emit. + NextEmit int +} + +func (e *matchEmitter) emit(m absoluteMatch) { + e.Dst = append(e.Dst, Match{ + Unmatched: m.Start - e.NextEmit, + Length: m.End - m.Start, + Distance: m.Start - m.Match, + }) + e.NextEmit = m.End +} diff --git a/vendor/github.com/andybalholm/brotli/matchfinder/m0.go b/vendor/github.com/andybalholm/brotli/matchfinder/m0.go new file mode 100644 index 000000000..773b7c49f --- /dev/null +++ b/vendor/github.com/andybalholm/brotli/matchfinder/m0.go @@ -0,0 +1,169 @@ +package matchfinder + +import ( + "encoding/binary" +) + +// M0 is an implementation of the MatchFinder interface based +// on the algorithm used by snappy, but modified to be more like the algorithm +// used by compression level 0 of the brotli reference implementation. +// +// It has a maximum block size of 65536 bytes. +type M0 struct { + // Lazy turns on "lazy matching," for higher compression but less speed. + Lazy bool + + MaxDistance int + MaxLength int +} + +func (M0) Reset() {} + +const ( + m0HashLen = 5 + + m0TableBits = 14 + m0TableSize = 1 << m0TableBits + m0Shift = 32 - m0TableBits + // m0TableMask is redundant, but helps the compiler eliminate bounds + // checks. + m0TableMask = m0TableSize - 1 +) + +func (m M0) hash(data uint64) uint64 { + hash := (data << (64 - 8*m0HashLen)) * hashMul64 + return hash >> (64 - m0TableBits) +} + +// FindMatches looks for matches in src, appends them to dst, and returns dst. +// src must not be longer than 65536 bytes. +func (m M0) FindMatches(dst []Match, src []byte) []Match { + const inputMargin = 16 - 1 + const minNonLiteralBlockSize = 1 + 1 + inputMargin + + if len(src) < minNonLiteralBlockSize { + dst = append(dst, Match{ + Unmatched: len(src), + }) + return dst + } + if len(src) > 65536 { + panic("block too long") + } + + var table [m0TableSize]uint16 + + // sLimit is when to stop looking for offset/length copies. The inputMargin + // lets us use a fast path for emitLiteral in the main loop, while we are + // looking for copies. + sLimit := len(src) - inputMargin + + // nextEmit is where in src the next emitLiteral should start from. + nextEmit := 0 + + // The encoded form must start with a literal, as there are no previous + // bytes to copy, so we start looking for hash matches at s == 1. + s := 1 + nextHash := m.hash(binary.LittleEndian.Uint64(src[s:])) + + for { + // Copied from the C++ snappy implementation: + // + // Heuristic match skipping: If 32 bytes are scanned with no matches + // found, start looking only at every other byte. If 32 more bytes are + // scanned (or skipped), look at every third byte, etc.. When a match + // is found, immediately go back to looking at every byte. This is a + // small loss (~5% performance, ~0.1% density) for compressible data + // due to more bookkeeping, but for non-compressible data (such as + // JPEG) it's a huge win since the compressor quickly "realizes" the + // data is incompressible and doesn't bother looking for matches + // everywhere. + // + // The "skip" variable keeps track of how many bytes there are since + // the last match; dividing it by 32 (ie. right-shifting by five) gives + // the number of bytes to move ahead for each iteration. + skip := 32 + + nextS := s + candidate := 0 + for { + s = nextS + bytesBetweenHashLookups := skip >> 5 + nextS = s + bytesBetweenHashLookups + skip += bytesBetweenHashLookups + if nextS > sLimit { + goto emitRemainder + } + candidate = int(table[nextHash&m0TableMask]) + table[nextHash&m0TableMask] = uint16(s) + nextHash = m.hash(binary.LittleEndian.Uint64(src[nextS:])) + if m.MaxDistance != 0 && s-candidate > m.MaxDistance { + continue + } + if binary.LittleEndian.Uint32(src[s:]) == binary.LittleEndian.Uint32(src[candidate:]) { + break + } + } + + // Invariant: we have a 4-byte match at s. + base := s + s = extendMatch(src, candidate+4, s+4) + + origBase := base + if m.Lazy && base+1 < sLimit { + newBase := base + 1 + h := m.hash(binary.LittleEndian.Uint64(src[newBase:])) + newCandidate := int(table[h&m0TableMask]) + table[h&m0TableMask] = uint16(newBase) + okDistance := true + if m.MaxDistance != 0 && newBase-newCandidate > m.MaxDistance { + okDistance = false + } + if okDistance && binary.LittleEndian.Uint32(src[newBase:]) == binary.LittleEndian.Uint32(src[newCandidate:]) { + newS := extendMatch(src, newCandidate+4, newBase+4) + if newS-newBase > s-base+1 { + s = newS + base = newBase + candidate = newCandidate + } + } + } + + if m.MaxLength != 0 && s-base > m.MaxLength { + s = base + m.MaxLength + } + dst = append(dst, Match{ + Unmatched: base - nextEmit, + Length: s - base, + Distance: base - candidate, + }) + nextEmit = s + if s >= sLimit { + goto emitRemainder + } + + if m.Lazy { + // If lazy matching is enabled, we update the hash table for + // every byte in the match. + for i := origBase + 2; i < s-1; i++ { + x := binary.LittleEndian.Uint64(src[i:]) + table[m.hash(x)&m0TableMask] = uint16(i) + } + } + + // We could immediately start working at s now, but to improve + // compression we first update the hash table at s-1 and at s. + x := binary.LittleEndian.Uint64(src[s-1:]) + prevHash := m.hash(x >> 0) + table[prevHash&m0TableMask] = uint16(s - 1) + nextHash = m.hash(x >> 8) + } + +emitRemainder: + if nextEmit < len(src) { + dst = append(dst, Match{ + Unmatched: len(src) - nextEmit, + }) + } + return dst +} diff --git a/vendor/github.com/andybalholm/brotli/matchfinder/m4.go b/vendor/github.com/andybalholm/brotli/matchfinder/m4.go new file mode 100644 index 000000000..818947255 --- /dev/null +++ b/vendor/github.com/andybalholm/brotli/matchfinder/m4.go @@ -0,0 +1,308 @@ +package matchfinder + +import ( + "encoding/binary" + "math/bits" + "runtime" +) + +// M4 is an implementation of the MatchFinder +// interface that uses a hash table to find matches, +// optional match chains, +// and the advanced parsing technique from +// https://fastcompression.blogspot.com/2011/12/advanced-parsing-strategies.html. +type M4 struct { + // MaxDistance is the maximum distance (in bytes) to look back for + // a match. The default is 65535. + MaxDistance int + + // MinLength is the length of the shortest match to return. + // The default is 4. + MinLength int + + // HashLen is the number of bytes to use to calculate the hashes. + // The maximum is 8 and the default is 6. + HashLen int + + // TableBits is the number of bits in the hash table indexes. + // The default is 17 (128K entries). + TableBits int + + // ChainLength is how many entries to search on the "match chain" of older + // locations with the same hash as the current location. + ChainLength int + + // DistanceBitCost is used when comparing two matches to see + // which is better. The comparison is primarily based on the length + // of the matches, but it can also take the distance into account, + // in terms of the number of bits needed to represent the distance. + // One byte of length is given a score of 256, so 32 (256/8) would + // be a reasonable first guess for the value of one bit. + // (The default is 0, which bases the comparison solely on length.) + DistanceBitCost int + + table []uint32 + chain []uint16 + + history []byte +} + +func (q *M4) Reset() { + for i := range q.table { + q.table[i] = 0 + } + q.history = q.history[:0] + q.chain = q.chain[:0] +} + +func (q *M4) score(m absoluteMatch) int { + return (m.End-m.Start)*256 + (bits.LeadingZeros32(uint32(m.Start-m.Match))-32)*q.DistanceBitCost +} + +func (q *M4) FindMatches(dst []Match, src []byte) []Match { + if q.MaxDistance == 0 { + q.MaxDistance = 65535 + } + if q.MinLength == 0 { + q.MinLength = 4 + } + if q.HashLen == 0 { + q.HashLen = 6 + } + if q.TableBits == 0 { + q.TableBits = 17 + } + if len(q.table) < 1< q.MaxDistance*2 { + // Trim down the history buffer. + delta := len(q.history) - q.MaxDistance + copy(q.history, q.history[delta:]) + q.history = q.history[:q.MaxDistance] + if q.ChainLength > 0 { + q.chain = q.chain[:q.MaxDistance] + } + + for i, v := range q.table { + newV := int(v) - delta + if newV < 0 { + newV = 0 + } + q.table[i] = uint32(newV) + } + } + + // Append src to the history buffer. + e.NextEmit = len(q.history) + q.history = append(q.history, src...) + if q.ChainLength > 0 { + q.chain = append(q.chain, make([]uint16, len(src))...) + } + src = q.history + + // matches stores the matches that have been found but not emitted, + // in reverse order. (matches[0] is the most recent one.) + var matches [3]absoluteMatch + for i := e.NextEmit; i < len(src)-7; i++ { + if matches[0] != (absoluteMatch{}) && i >= matches[0].End { + // We have found some matches, and we're far enough along that we probably + // won't find overlapping matches, so we might as well emit them. + if matches[1] != (absoluteMatch{}) { + if matches[1].End > matches[0].Start { + matches[1].End = matches[0].Start + } + if matches[1].End-matches[1].Start >= q.MinLength && q.score(matches[1]) > 0 { + e.emit(matches[1]) + } + } + e.emit(matches[0]) + matches = [3]absoluteMatch{} + } + + // Calculate and store the hash. + h := ((binary.LittleEndian.Uint64(src[i:]) & (1<<(8*q.HashLen) - 1)) * hashMul64) >> (64 - q.TableBits) + candidate := int(q.table[h]) + q.table[h] = uint32(i) + if q.ChainLength > 0 && candidate != 0 { + delta := i - candidate + if delta < 1<<16 { + q.chain[i] = uint16(delta) + } + } + + if i < matches[0].End && i != matches[0].End+2-q.HashLen { + continue + } + if candidate == 0 || i-candidate > q.MaxDistance { + continue + } + + // Look for a match. + var currentMatch absoluteMatch + + if binary.LittleEndian.Uint32(src[candidate:]) == binary.LittleEndian.Uint32(src[i:]) { + m := extendMatch2(src, i, candidate, e.NextEmit) + if m.End-m.Start > q.MinLength && q.score(m) > 0 { + currentMatch = m + } + } + + for j := 0; j < q.ChainLength; j++ { + delta := q.chain[candidate] + if delta == 0 { + break + } + candidate -= int(delta) + if candidate <= 0 || i-candidate > q.MaxDistance { + break + } + if binary.LittleEndian.Uint32(src[candidate:]) == binary.LittleEndian.Uint32(src[i:]) { + m := extendMatch2(src, i, candidate, e.NextEmit) + if m.End-m.Start > q.MinLength && q.score(m) > q.score(currentMatch) { + currentMatch = m + } + } + } + + if currentMatch.End-currentMatch.Start < q.MinLength { + continue + } + + overlapPenalty := 0 + if matches[0] != (absoluteMatch{}) { + overlapPenalty = 275 + if currentMatch.Start <= matches[1].End { + // This match would completely replace the previous match, + // so there is no penalty for overlap. + overlapPenalty = 0 + } + } + + if q.score(currentMatch) <= q.score(matches[0])+overlapPenalty { + continue + } + + matches = [3]absoluteMatch{ + currentMatch, + matches[0], + matches[1], + } + + if matches[2] == (absoluteMatch{}) { + continue + } + + // We have three matches, so it's time to emit one and/or eliminate one. + switch { + case matches[0].Start < matches[2].End: + // The first and third matches overlap; discard the one in between. + matches = [3]absoluteMatch{ + matches[0], + matches[2], + absoluteMatch{}, + } + + case matches[0].Start < matches[2].End+q.MinLength: + // The first and third matches don't overlap, but there's no room for + // another match between them. Emit the first match and discard the second. + e.emit(matches[2]) + matches = [3]absoluteMatch{ + matches[0], + absoluteMatch{}, + absoluteMatch{}, + } + + default: + // Emit the first match, shortening it if necessary to avoid overlap with the second. + if matches[2].End > matches[1].Start { + matches[2].End = matches[1].Start + } + if matches[2].End-matches[2].Start >= q.MinLength && q.score(matches[2]) > 0 { + e.emit(matches[2]) + } + matches[2] = absoluteMatch{} + } + } + + // We've found all the matches now; emit the remaining ones. + if matches[1] != (absoluteMatch{}) { + if matches[1].End > matches[0].Start { + matches[1].End = matches[0].Start + } + if matches[1].End-matches[1].Start >= q.MinLength && q.score(matches[1]) > 0 { + e.emit(matches[1]) + } + } + if matches[0] != (absoluteMatch{}) { + e.emit(matches[0]) + } + + dst = e.Dst + if e.NextEmit < len(src) { + dst = append(dst, Match{ + Unmatched: len(src) - e.NextEmit, + }) + } + + return dst +} + +const hashMul64 = 0x1E35A7BD1E35A7BD + +// extendMatch returns the largest k such that k <= len(src) and that +// src[i:i+k-j] and src[j:k] have the same contents. +// +// It assumes that: +// +// 0 <= i && i < j && j <= len(src) +func extendMatch(src []byte, i, j int) int { + switch runtime.GOARCH { + case "amd64": + // As long as we are 8 or more bytes before the end of src, we can load and + // compare 8 bytes at a time. If those 8 bytes are equal, repeat. + for j+8 < len(src) { + iBytes := binary.LittleEndian.Uint64(src[i:]) + jBytes := binary.LittleEndian.Uint64(src[j:]) + if iBytes != jBytes { + // If those 8 bytes were not equal, XOR the two 8 byte values, and return + // the index of the first byte that differs. The BSF instruction finds the + // least significant 1 bit, the amd64 architecture is little-endian, and + // the shift by 3 converts a bit index to a byte index. + return j + bits.TrailingZeros64(iBytes^jBytes)>>3 + } + i, j = i+8, j+8 + } + case "386": + // On a 32-bit CPU, we do it 4 bytes at a time. + for j+4 < len(src) { + iBytes := binary.LittleEndian.Uint32(src[i:]) + jBytes := binary.LittleEndian.Uint32(src[j:]) + if iBytes != jBytes { + return j + bits.TrailingZeros32(iBytes^jBytes)>>3 + } + i, j = i+4, j+4 + } + } + for ; j < len(src) && src[i] == src[j]; i, j = i+1, j+1 { + } + return j +} + +// Given a 4-byte match at src[start] and src[candidate], extendMatch2 extends it +// upward as far as possible, and downward no farther than to min. +func extendMatch2(src []byte, start, candidate, min int) absoluteMatch { + end := extendMatch(src, candidate+4, start+4) + for start > min && candidate > 0 && src[start-1] == src[candidate-1] { + start-- + candidate-- + } + return absoluteMatch{ + Start: start, + End: end, + Match: candidate, + } +} diff --git a/vendor/github.com/andybalholm/brotli/matchfinder/matchfinder.go b/vendor/github.com/andybalholm/brotli/matchfinder/matchfinder.go new file mode 100644 index 000000000..f6bcfdb39 --- /dev/null +++ b/vendor/github.com/andybalholm/brotli/matchfinder/matchfinder.go @@ -0,0 +1,103 @@ +// The matchfinder package defines reusable components for data compression. +// +// Many compression libraries have two main parts: +// - Something that looks for repeated sequences of bytes +// - An encoder for the compressed data format (often an entropy coder) +// +// Although these are logically two separate steps, the implementations are +// usually closely tied together. You can't use flate's matcher with snappy's +// encoder, for example. This package defines interfaces and an intermediate +// representation to allow mixing and matching compression components. +package matchfinder + +import "io" + +// A Match is the basic unit of LZ77 compression. +type Match struct { + Unmatched int // the number of unmatched bytes since the previous match + Length int // the number of bytes in the matched string; it may be 0 at the end of the input + Distance int // how far back in the stream to copy from +} + +// A MatchFinder performs the LZ77 stage of compression, looking for matches. +type MatchFinder interface { + // FindMatches looks for matches in src, appends them to dst, and returns dst. + FindMatches(dst []Match, src []byte) []Match + + // Reset clears any internal state, preparing the MatchFinder to be used with + // a new stream. + Reset() +} + +// An Encoder encodes the data in its final format. +type Encoder interface { + // Encode appends the encoded format of src to dst, using the match + // information from matches. + Encode(dst []byte, src []byte, matches []Match, lastBlock bool) []byte + + // Reset clears any internal state, preparing the Encoder to be used with + // a new stream. + Reset() +} + +// A Writer uses MatchFinder and Encoder to write compressed data to Dest. +type Writer struct { + Dest io.Writer + MatchFinder MatchFinder + Encoder Encoder + + // BlockSize is the number of bytes to compress at a time. If it is zero, + // each Write operation will be treated as one block. + BlockSize int + + err error + inBuf []byte + outBuf []byte + matches []Match +} + +func (w *Writer) Write(p []byte) (n int, err error) { + if w.err != nil { + return 0, w.err + } + + if w.BlockSize == 0 { + return w.writeBlock(p, false) + } + + w.inBuf = append(w.inBuf, p...) + var pos int + for pos = 0; pos+w.BlockSize <= len(w.inBuf) && w.err == nil; pos += w.BlockSize { + w.writeBlock(w.inBuf[pos:pos+w.BlockSize], false) + } + if pos > 0 { + n := copy(w.inBuf, w.inBuf[pos:]) + w.inBuf = w.inBuf[:n] + } + + return len(p), w.err +} + +func (w *Writer) writeBlock(p []byte, lastBlock bool) (n int, err error) { + w.outBuf = w.outBuf[:0] + w.matches = w.MatchFinder.FindMatches(w.matches[:0], p) + w.outBuf = w.Encoder.Encode(w.outBuf, p, w.matches, lastBlock) + _, w.err = w.Dest.Write(w.outBuf) + return len(p), w.err +} + +func (w *Writer) Close() error { + w.writeBlock(w.inBuf, true) + w.inBuf = w.inBuf[:0] + return w.err +} + +func (w *Writer) Reset(newDest io.Writer) { + w.MatchFinder.Reset() + w.Encoder.Reset() + w.err = nil + w.inBuf = w.inBuf[:0] + w.outBuf = w.outBuf[:0] + w.matches = w.matches[:0] + w.Dest = newDest +} diff --git a/vendor/github.com/andybalholm/brotli/matchfinder/textencoder.go b/vendor/github.com/andybalholm/brotli/matchfinder/textencoder.go new file mode 100644 index 000000000..75ecc5908 --- /dev/null +++ b/vendor/github.com/andybalholm/brotli/matchfinder/textencoder.go @@ -0,0 +1,53 @@ +package matchfinder + +import "fmt" + +// A TextEncoder is an Encoder that produces a human-readable representation of +// the LZ77 compression. Matches are replaced with symbols. +type TextEncoder struct{} + +func (t TextEncoder) Reset() {} + +func (t TextEncoder) Encode(dst []byte, src []byte, matches []Match, lastBlock bool) []byte { + pos := 0 + for _, m := range matches { + if m.Unmatched > 0 { + dst = append(dst, src[pos:pos+m.Unmatched]...) + pos += m.Unmatched + } + if m.Length > 0 { + dst = append(dst, []byte(fmt.Sprintf("<%d,%d>", m.Length, m.Distance))...) + pos += m.Length + } + } + if pos < len(src) { + dst = append(dst, src[pos:]...) + } + return dst +} + +// A NoMatchFinder implements MatchFinder, but doesn't find any matches. +// It can be used to implement the equivalent of the standard library flate package's +// HuffmanOnly setting. +type NoMatchFinder struct{} + +func (n NoMatchFinder) Reset() {} + +func (n NoMatchFinder) FindMatches(dst []Match, src []byte) []Match { + return append(dst, Match{ + Unmatched: len(src), + }) +} + +// AutoReset wraps a MatchFinder that can return references to data in previous +// blocks, and calls Reset before each block. It is useful for (e.g.) using a +// snappy Encoder with a MatchFinder designed for flate. (Snappy doesn't +// support references between blocks.) +type AutoReset struct { + MatchFinder +} + +func (a AutoReset) FindMatches(dst []Match, src []byte) []Match { + a.Reset() + return a.MatchFinder.FindMatches(dst, src) +} diff --git a/vendor/github.com/andybalholm/brotli/memory.go b/vendor/github.com/andybalholm/brotli/memory.go new file mode 100644 index 000000000..a07c7050a --- /dev/null +++ b/vendor/github.com/andybalholm/brotli/memory.go @@ -0,0 +1,66 @@ +package brotli + +/* Copyright 2016 Google Inc. All Rights Reserved. + + Distributed under MIT license. + See file LICENSE for detail or copy at https://opensource.org/licenses/MIT +*/ + +/* +Dynamically grows array capacity to at least the requested size +T: data type +A: array +C: capacity +R: requested size +*/ +func brotli_ensure_capacity_uint8_t(a *[]byte, c *uint, r uint) { + if *c < r { + var new_size uint = *c + if new_size == 0 { + new_size = r + } + + for new_size < r { + new_size *= 2 + } + + if cap(*a) < int(new_size) { + var new_array []byte = make([]byte, new_size) + if *c != 0 { + copy(new_array, (*a)[:*c]) + } + + *a = new_array + } else { + *a = (*a)[:new_size] + } + + *c = new_size + } +} + +func brotli_ensure_capacity_uint32_t(a *[]uint32, c *uint, r uint) { + var new_array []uint32 + if *c < r { + var new_size uint = *c + if new_size == 0 { + new_size = r + } + + for new_size < r { + new_size *= 2 + } + + if cap(*a) < int(new_size) { + new_array = make([]uint32, new_size) + if *c != 0 { + copy(new_array, (*a)[:*c]) + } + + *a = new_array + } else { + *a = (*a)[:new_size] + } + *c = new_size + } +} diff --git a/vendor/github.com/andybalholm/brotli/metablock.go b/vendor/github.com/andybalholm/brotli/metablock.go new file mode 100644 index 000000000..3014df8cd --- /dev/null +++ b/vendor/github.com/andybalholm/brotli/metablock.go @@ -0,0 +1,574 @@ +package brotli + +import ( + "sync" +) + +/* Copyright 2014 Google Inc. All Rights Reserved. + + Distributed under MIT license. + See file LICENSE for detail or copy at https://opensource.org/licenses/MIT +*/ + +/* Algorithms for distributing the literals and commands of a metablock between + block types and contexts. */ + +type metaBlockSplit struct { + literal_split blockSplit + command_split blockSplit + distance_split blockSplit + literal_context_map []uint32 + literal_context_map_size uint + distance_context_map []uint32 + distance_context_map_size uint + literal_histograms []histogramLiteral + literal_histograms_size uint + command_histograms []histogramCommand + command_histograms_size uint + distance_histograms []histogramDistance + distance_histograms_size uint +} + +var metaBlockPool sync.Pool + +func getMetaBlockSplit() *metaBlockSplit { + mb, _ := metaBlockPool.Get().(*metaBlockSplit) + + if mb == nil { + mb = &metaBlockSplit{} + } else { + initBlockSplit(&mb.literal_split) + initBlockSplit(&mb.command_split) + initBlockSplit(&mb.distance_split) + mb.literal_context_map = mb.literal_context_map[:0] + mb.literal_context_map_size = 0 + mb.distance_context_map = mb.distance_context_map[:0] + mb.distance_context_map_size = 0 + mb.literal_histograms = mb.literal_histograms[:0] + mb.command_histograms = mb.command_histograms[:0] + mb.distance_histograms = mb.distance_histograms[:0] + } + return mb +} + +func freeMetaBlockSplit(mb *metaBlockSplit) { + metaBlockPool.Put(mb) +} + +func initDistanceParams(params *encoderParams, npostfix uint32, ndirect uint32) { + var dist_params *distanceParams = ¶ms.dist + var alphabet_size uint32 + var max_distance uint32 + + dist_params.distance_postfix_bits = npostfix + dist_params.num_direct_distance_codes = ndirect + + alphabet_size = uint32(distanceAlphabetSize(uint(npostfix), uint(ndirect), maxDistanceBits)) + max_distance = ndirect + (1 << (maxDistanceBits + npostfix + 2)) - (1 << (npostfix + 2)) + + if params.large_window { + var bound = [maxNpostfix + 1]uint32{0, 4, 12, 28} + var postfix uint32 = 1 << npostfix + alphabet_size = uint32(distanceAlphabetSize(uint(npostfix), uint(ndirect), largeMaxDistanceBits)) + + /* The maximum distance is set so that no distance symbol used can encode + a distance larger than BROTLI_MAX_ALLOWED_DISTANCE with all + its extra bits set. */ + if ndirect < bound[npostfix] { + max_distance = maxAllowedDistance - (bound[npostfix] - ndirect) + } else if ndirect >= bound[npostfix]+postfix { + max_distance = (3 << 29) - 4 + (ndirect - bound[npostfix]) + } else { + max_distance = maxAllowedDistance + } + } + + dist_params.alphabet_size = alphabet_size + dist_params.max_distance = uint(max_distance) +} + +func recomputeDistancePrefixes(cmds []command, orig_params *distanceParams, new_params *distanceParams) { + if orig_params.distance_postfix_bits == new_params.distance_postfix_bits && orig_params.num_direct_distance_codes == new_params.num_direct_distance_codes { + return + } + + for i := range cmds { + var cmd *command = &cmds[i] + if commandCopyLen(cmd) != 0 && cmd.cmd_prefix_ >= 128 { + prefixEncodeCopyDistance(uint(commandRestoreDistanceCode(cmd, orig_params)), uint(new_params.num_direct_distance_codes), uint(new_params.distance_postfix_bits), &cmd.dist_prefix_, &cmd.dist_extra_) + } + } +} + +func computeDistanceCost(cmds []command, orig_params *distanceParams, new_params *distanceParams, cost *float64) bool { + var equal_params bool = false + var dist_prefix uint16 + var dist_extra uint32 + var extra_bits float64 = 0.0 + var histo histogramDistance + histogramClearDistance(&histo) + + if orig_params.distance_postfix_bits == new_params.distance_postfix_bits && orig_params.num_direct_distance_codes == new_params.num_direct_distance_codes { + equal_params = true + } + + for i := range cmds { + cmd := &cmds[i] + if commandCopyLen(cmd) != 0 && cmd.cmd_prefix_ >= 128 { + if equal_params { + dist_prefix = cmd.dist_prefix_ + } else { + var distance uint32 = commandRestoreDistanceCode(cmd, orig_params) + if distance > uint32(new_params.max_distance) { + return false + } + + prefixEncodeCopyDistance(uint(distance), uint(new_params.num_direct_distance_codes), uint(new_params.distance_postfix_bits), &dist_prefix, &dist_extra) + } + + histogramAddDistance(&histo, uint(dist_prefix)&0x3FF) + extra_bits += float64(dist_prefix >> 10) + } + } + + *cost = populationCostDistance(&histo) + extra_bits + return true +} + +var buildMetaBlock_kMaxNumberOfHistograms uint = 256 + +func buildMetaBlock(ringbuffer []byte, pos uint, mask uint, params *encoderParams, prev_byte byte, prev_byte2 byte, cmds []command, literal_context_mode int, mb *metaBlockSplit) { + var distance_histograms []histogramDistance + var literal_histograms []histogramLiteral + var literal_context_modes []int = nil + var literal_histograms_size uint + var distance_histograms_size uint + var i uint + var literal_context_multiplier uint = 1 + var npostfix uint32 + var ndirect_msb uint32 = 0 + var check_orig bool = true + var best_dist_cost float64 = 1e99 + var orig_params encoderParams = *params + /* Histogram ids need to fit in one byte. */ + + var new_params encoderParams = *params + + for npostfix = 0; npostfix <= maxNpostfix; npostfix++ { + for ; ndirect_msb < 16; ndirect_msb++ { + var ndirect uint32 = ndirect_msb << npostfix + var skip bool + var dist_cost float64 + initDistanceParams(&new_params, npostfix, ndirect) + if npostfix == orig_params.dist.distance_postfix_bits && ndirect == orig_params.dist.num_direct_distance_codes { + check_orig = false + } + + skip = !computeDistanceCost(cmds, &orig_params.dist, &new_params.dist, &dist_cost) + if skip || (dist_cost > best_dist_cost) { + break + } + + best_dist_cost = dist_cost + params.dist = new_params.dist + } + + if ndirect_msb > 0 { + ndirect_msb-- + } + ndirect_msb /= 2 + } + + if check_orig { + var dist_cost float64 + computeDistanceCost(cmds, &orig_params.dist, &orig_params.dist, &dist_cost) + if dist_cost < best_dist_cost { + /* NB: currently unused; uncomment when more param tuning is added. */ + /* best_dist_cost = dist_cost; */ + params.dist = orig_params.dist + } + } + + recomputeDistancePrefixes(cmds, &orig_params.dist, ¶ms.dist) + + splitBlock(cmds, ringbuffer, pos, mask, params, &mb.literal_split, &mb.command_split, &mb.distance_split) + + if !params.disable_literal_context_modeling { + literal_context_multiplier = 1 << literalContextBits + literal_context_modes = make([]int, (mb.literal_split.num_types)) + for i = 0; i < mb.literal_split.num_types; i++ { + literal_context_modes[i] = literal_context_mode + } + } + + literal_histograms_size = mb.literal_split.num_types * literal_context_multiplier + literal_histograms = make([]histogramLiteral, literal_histograms_size) + clearHistogramsLiteral(literal_histograms, literal_histograms_size) + + distance_histograms_size = mb.distance_split.num_types << distanceContextBits + distance_histograms = make([]histogramDistance, distance_histograms_size) + clearHistogramsDistance(distance_histograms, distance_histograms_size) + + mb.command_histograms_size = mb.command_split.num_types + if cap(mb.command_histograms) < int(mb.command_histograms_size) { + mb.command_histograms = make([]histogramCommand, (mb.command_histograms_size)) + } else { + mb.command_histograms = mb.command_histograms[:mb.command_histograms_size] + } + clearHistogramsCommand(mb.command_histograms, mb.command_histograms_size) + + buildHistogramsWithContext(cmds, &mb.literal_split, &mb.command_split, &mb.distance_split, ringbuffer, pos, mask, prev_byte, prev_byte2, literal_context_modes, literal_histograms, mb.command_histograms, distance_histograms) + literal_context_modes = nil + + mb.literal_context_map_size = mb.literal_split.num_types << literalContextBits + if cap(mb.literal_context_map) < int(mb.literal_context_map_size) { + mb.literal_context_map = make([]uint32, (mb.literal_context_map_size)) + } else { + mb.literal_context_map = mb.literal_context_map[:mb.literal_context_map_size] + } + + mb.literal_histograms_size = mb.literal_context_map_size + if cap(mb.literal_histograms) < int(mb.literal_histograms_size) { + mb.literal_histograms = make([]histogramLiteral, (mb.literal_histograms_size)) + } else { + mb.literal_histograms = mb.literal_histograms[:mb.literal_histograms_size] + } + + clusterHistogramsLiteral(literal_histograms, literal_histograms_size, buildMetaBlock_kMaxNumberOfHistograms, mb.literal_histograms, &mb.literal_histograms_size, mb.literal_context_map) + literal_histograms = nil + + if params.disable_literal_context_modeling { + /* Distribute assignment to all contexts. */ + for i = mb.literal_split.num_types; i != 0; { + var j uint = 0 + i-- + for ; j < 1< 0 { + var entropy [maxStaticContexts]float64 + var combined_histo []histogramLiteral = make([]histogramLiteral, (2 * num_contexts)) + var combined_entropy [2 * maxStaticContexts]float64 + var diff = [2]float64{0.0} + /* Try merging the set of histograms for the current block type with the + respective set of histograms for the last and second last block types. + Decide over the split based on the total reduction of entropy across + all contexts. */ + + var i uint + for i = 0; i < num_contexts; i++ { + var curr_histo_ix uint = self.curr_histogram_ix_ + i + var j uint + entropy[i] = bitsEntropy(histograms[curr_histo_ix].data_[:], self.alphabet_size_) + for j = 0; j < 2; j++ { + var jx uint = j*num_contexts + i + var last_histogram_ix uint = self.last_histogram_ix_[j] + i + combined_histo[jx] = histograms[curr_histo_ix] + histogramAddHistogramLiteral(&combined_histo[jx], &histograms[last_histogram_ix]) + combined_entropy[jx] = bitsEntropy(combined_histo[jx].data_[0:], self.alphabet_size_) + diff[j] += combined_entropy[jx] - entropy[i] - last_entropy[jx] + } + } + + if split.num_types < self.max_block_types_ && diff[0] > self.split_threshold_ && diff[1] > self.split_threshold_ { + /* Create new block. */ + split.lengths[self.num_blocks_] = uint32(self.block_size_) + + split.types[self.num_blocks_] = byte(split.num_types) + self.last_histogram_ix_[1] = self.last_histogram_ix_[0] + self.last_histogram_ix_[0] = split.num_types * num_contexts + for i = 0; i < num_contexts; i++ { + last_entropy[num_contexts+i] = last_entropy[i] + last_entropy[i] = entropy[i] + } + + self.num_blocks_++ + split.num_types++ + self.curr_histogram_ix_ += num_contexts + if self.curr_histogram_ix_ < *self.histograms_size_ { + clearHistogramsLiteral(self.histograms_[self.curr_histogram_ix_:], self.num_contexts_) + } + + self.block_size_ = 0 + self.merge_last_count_ = 0 + self.target_block_size_ = self.min_block_size_ + } else if diff[1] < diff[0]-20.0 { + split.lengths[self.num_blocks_] = uint32(self.block_size_) + split.types[self.num_blocks_] = split.types[self.num_blocks_-2] + /* Combine this block with second last block. */ + + var tmp uint = self.last_histogram_ix_[0] + self.last_histogram_ix_[0] = self.last_histogram_ix_[1] + self.last_histogram_ix_[1] = tmp + for i = 0; i < num_contexts; i++ { + histograms[self.last_histogram_ix_[0]+i] = combined_histo[num_contexts+i] + last_entropy[num_contexts+i] = last_entropy[i] + last_entropy[i] = combined_entropy[num_contexts+i] + histogramClearLiteral(&histograms[self.curr_histogram_ix_+i]) + } + + self.num_blocks_++ + self.block_size_ = 0 + self.merge_last_count_ = 0 + self.target_block_size_ = self.min_block_size_ + } else { + /* Combine this block with last block. */ + split.lengths[self.num_blocks_-1] += uint32(self.block_size_) + + for i = 0; i < num_contexts; i++ { + histograms[self.last_histogram_ix_[0]+i] = combined_histo[i] + last_entropy[i] = combined_entropy[i] + if split.num_types == 1 { + last_entropy[num_contexts+i] = last_entropy[i] + } + + histogramClearLiteral(&histograms[self.curr_histogram_ix_+i]) + } + + self.block_size_ = 0 + self.merge_last_count_++ + if self.merge_last_count_ > 1 { + self.target_block_size_ += self.min_block_size_ + } + } + + combined_histo = nil + } + + if is_final { + *self.histograms_size_ = split.num_types * num_contexts + split.num_blocks = self.num_blocks_ + } +} + +/* Adds the next symbol to the current block type and context. When the + current block reaches the target size, decides on merging the block. */ +func contextBlockSplitterAddSymbol(self *contextBlockSplitter, symbol uint, context uint) { + histogramAddLiteral(&self.histograms_[self.curr_histogram_ix_+context], symbol) + self.block_size_++ + if self.block_size_ == self.target_block_size_ { + contextBlockSplitterFinishBlock(self, false) /* is_final = */ + } +} + +func mapStaticContexts(num_contexts uint, static_context_map []uint32, mb *metaBlockSplit) { + var i uint + mb.literal_context_map_size = mb.literal_split.num_types << literalContextBits + if cap(mb.literal_context_map) < int(mb.literal_context_map_size) { + mb.literal_context_map = make([]uint32, (mb.literal_context_map_size)) + } else { + mb.literal_context_map = mb.literal_context_map[:mb.literal_context_map_size] + } + + for i = 0; i < mb.literal_split.num_types; i++ { + var offset uint32 = uint32(i * num_contexts) + var j uint + for j = 0; j < 1<= 128 { + blockSplitterAddSymbolDistance(&dist_blocks, uint(cmd.dist_prefix_)&0x3FF) + } + } + } + + if num_contexts == 1 { + blockSplitterFinishBlockLiteral(&lit_blocks.plain, true) /* is_final = */ + } else { + contextBlockSplitterFinishBlock(&lit_blocks.ctx, true) /* is_final = */ + } + + blockSplitterFinishBlockCommand(&cmd_blocks, true) /* is_final = */ + blockSplitterFinishBlockDistance(&dist_blocks, true) /* is_final = */ + + if num_contexts > 1 { + mapStaticContexts(num_contexts, static_context_map, mb) + } +} + +func buildMetaBlockGreedy(ringbuffer []byte, pos uint, mask uint, prev_byte byte, prev_byte2 byte, literal_context_lut contextLUT, num_contexts uint, static_context_map []uint32, commands []command, mb *metaBlockSplit) { + if num_contexts == 1 { + buildMetaBlockGreedyInternal(ringbuffer, pos, mask, prev_byte, prev_byte2, literal_context_lut, 1, nil, commands, mb) + } else { + buildMetaBlockGreedyInternal(ringbuffer, pos, mask, prev_byte, prev_byte2, literal_context_lut, num_contexts, static_context_map, commands, mb) + } +} + +func optimizeHistograms(num_distance_codes uint32, mb *metaBlockSplit) { + var good_for_rle [numCommandSymbols]byte + var i uint + for i = 0; i < mb.literal_histograms_size; i++ { + optimizeHuffmanCountsForRLE(256, mb.literal_histograms[i].data_[:], good_for_rle[:]) + } + + for i = 0; i < mb.command_histograms_size; i++ { + optimizeHuffmanCountsForRLE(numCommandSymbols, mb.command_histograms[i].data_[:], good_for_rle[:]) + } + + for i = 0; i < mb.distance_histograms_size; i++ { + optimizeHuffmanCountsForRLE(uint(num_distance_codes), mb.distance_histograms[i].data_[:], good_for_rle[:]) + } +} diff --git a/vendor/github.com/andybalholm/brotli/metablock_command.go b/vendor/github.com/andybalholm/brotli/metablock_command.go new file mode 100644 index 000000000..14c7b7713 --- /dev/null +++ b/vendor/github.com/andybalholm/brotli/metablock_command.go @@ -0,0 +1,165 @@ +package brotli + +/* Copyright 2015 Google Inc. All Rights Reserved. + + Distributed under MIT license. + See file LICENSE for detail or copy at https://opensource.org/licenses/MIT +*/ + +/* Greedy block splitter for one block category (literal, command or distance). + */ +type blockSplitterCommand struct { + alphabet_size_ uint + min_block_size_ uint + split_threshold_ float64 + num_blocks_ uint + split_ *blockSplit + histograms_ []histogramCommand + histograms_size_ *uint + target_block_size_ uint + block_size_ uint + curr_histogram_ix_ uint + last_histogram_ix_ [2]uint + last_entropy_ [2]float64 + merge_last_count_ uint +} + +func initBlockSplitterCommand(self *blockSplitterCommand, alphabet_size uint, min_block_size uint, split_threshold float64, num_symbols uint, split *blockSplit, histograms *[]histogramCommand, histograms_size *uint) { + var max_num_blocks uint = num_symbols/min_block_size + 1 + var max_num_types uint = brotli_min_size_t(max_num_blocks, maxNumberOfBlockTypes+1) + /* We have to allocate one more histogram than the maximum number of block + types for the current histogram when the meta-block is too big. */ + self.alphabet_size_ = alphabet_size + + self.min_block_size_ = min_block_size + self.split_threshold_ = split_threshold + self.num_blocks_ = 0 + self.split_ = split + self.histograms_size_ = histograms_size + self.target_block_size_ = min_block_size + self.block_size_ = 0 + self.curr_histogram_ix_ = 0 + self.merge_last_count_ = 0 + brotli_ensure_capacity_uint8_t(&split.types, &split.types_alloc_size, max_num_blocks) + brotli_ensure_capacity_uint32_t(&split.lengths, &split.lengths_alloc_size, max_num_blocks) + self.split_.num_blocks = max_num_blocks + *histograms_size = max_num_types + if histograms == nil || cap(*histograms) < int(*histograms_size) { + *histograms = make([]histogramCommand, (*histograms_size)) + } else { + *histograms = (*histograms)[:*histograms_size] + } + self.histograms_ = *histograms + + /* Clear only current histogram. */ + histogramClearCommand(&self.histograms_[0]) + + self.last_histogram_ix_[1] = 0 + self.last_histogram_ix_[0] = self.last_histogram_ix_[1] +} + +/* Does either of three things: + (1) emits the current block with a new block type; + (2) emits the current block with the type of the second last block; + (3) merges the current block with the last block. */ +func blockSplitterFinishBlockCommand(self *blockSplitterCommand, is_final bool) { + var split *blockSplit = self.split_ + var last_entropy []float64 = self.last_entropy_[:] + var histograms []histogramCommand = self.histograms_ + self.block_size_ = brotli_max_size_t(self.block_size_, self.min_block_size_) + if self.num_blocks_ == 0 { + /* Create first block. */ + split.lengths[0] = uint32(self.block_size_) + + split.types[0] = 0 + last_entropy[0] = bitsEntropy(histograms[0].data_[:], self.alphabet_size_) + last_entropy[1] = last_entropy[0] + self.num_blocks_++ + split.num_types++ + self.curr_histogram_ix_++ + if self.curr_histogram_ix_ < *self.histograms_size_ { + histogramClearCommand(&histograms[self.curr_histogram_ix_]) + } + self.block_size_ = 0 + } else if self.block_size_ > 0 { + var entropy float64 = bitsEntropy(histograms[self.curr_histogram_ix_].data_[:], self.alphabet_size_) + var combined_histo [2]histogramCommand + var combined_entropy [2]float64 + var diff [2]float64 + var j uint + for j = 0; j < 2; j++ { + var last_histogram_ix uint = self.last_histogram_ix_[j] + combined_histo[j] = histograms[self.curr_histogram_ix_] + histogramAddHistogramCommand(&combined_histo[j], &histograms[last_histogram_ix]) + combined_entropy[j] = bitsEntropy(combined_histo[j].data_[0:], self.alphabet_size_) + diff[j] = combined_entropy[j] - entropy - last_entropy[j] + } + + if split.num_types < maxNumberOfBlockTypes && diff[0] > self.split_threshold_ && diff[1] > self.split_threshold_ { + /* Create new block. */ + split.lengths[self.num_blocks_] = uint32(self.block_size_) + + split.types[self.num_blocks_] = byte(split.num_types) + self.last_histogram_ix_[1] = self.last_histogram_ix_[0] + self.last_histogram_ix_[0] = uint(byte(split.num_types)) + last_entropy[1] = last_entropy[0] + last_entropy[0] = entropy + self.num_blocks_++ + split.num_types++ + self.curr_histogram_ix_++ + if self.curr_histogram_ix_ < *self.histograms_size_ { + histogramClearCommand(&histograms[self.curr_histogram_ix_]) + } + self.block_size_ = 0 + self.merge_last_count_ = 0 + self.target_block_size_ = self.min_block_size_ + } else if diff[1] < diff[0]-20.0 { + split.lengths[self.num_blocks_] = uint32(self.block_size_) + split.types[self.num_blocks_] = split.types[self.num_blocks_-2] + /* Combine this block with second last block. */ + + var tmp uint = self.last_histogram_ix_[0] + self.last_histogram_ix_[0] = self.last_histogram_ix_[1] + self.last_histogram_ix_[1] = tmp + histograms[self.last_histogram_ix_[0]] = combined_histo[1] + last_entropy[1] = last_entropy[0] + last_entropy[0] = combined_entropy[1] + self.num_blocks_++ + self.block_size_ = 0 + histogramClearCommand(&histograms[self.curr_histogram_ix_]) + self.merge_last_count_ = 0 + self.target_block_size_ = self.min_block_size_ + } else { + /* Combine this block with last block. */ + split.lengths[self.num_blocks_-1] += uint32(self.block_size_) + + histograms[self.last_histogram_ix_[0]] = combined_histo[0] + last_entropy[0] = combined_entropy[0] + if split.num_types == 1 { + last_entropy[1] = last_entropy[0] + } + + self.block_size_ = 0 + histogramClearCommand(&histograms[self.curr_histogram_ix_]) + self.merge_last_count_++ + if self.merge_last_count_ > 1 { + self.target_block_size_ += self.min_block_size_ + } + } + } + + if is_final { + *self.histograms_size_ = split.num_types + split.num_blocks = self.num_blocks_ + } +} + +/* Adds the next symbol to the current histogram. When the current histogram + reaches the target size, decides on merging the block. */ +func blockSplitterAddSymbolCommand(self *blockSplitterCommand, symbol uint) { + histogramAddCommand(&self.histograms_[self.curr_histogram_ix_], symbol) + self.block_size_++ + if self.block_size_ == self.target_block_size_ { + blockSplitterFinishBlockCommand(self, false) /* is_final = */ + } +} diff --git a/vendor/github.com/andybalholm/brotli/metablock_distance.go b/vendor/github.com/andybalholm/brotli/metablock_distance.go new file mode 100644 index 000000000..5110a810e --- /dev/null +++ b/vendor/github.com/andybalholm/brotli/metablock_distance.go @@ -0,0 +1,165 @@ +package brotli + +/* Copyright 2015 Google Inc. All Rights Reserved. + + Distributed under MIT license. + See file LICENSE for detail or copy at https://opensource.org/licenses/MIT +*/ + +/* Greedy block splitter for one block category (literal, command or distance). + */ +type blockSplitterDistance struct { + alphabet_size_ uint + min_block_size_ uint + split_threshold_ float64 + num_blocks_ uint + split_ *blockSplit + histograms_ []histogramDistance + histograms_size_ *uint + target_block_size_ uint + block_size_ uint + curr_histogram_ix_ uint + last_histogram_ix_ [2]uint + last_entropy_ [2]float64 + merge_last_count_ uint +} + +func initBlockSplitterDistance(self *blockSplitterDistance, alphabet_size uint, min_block_size uint, split_threshold float64, num_symbols uint, split *blockSplit, histograms *[]histogramDistance, histograms_size *uint) { + var max_num_blocks uint = num_symbols/min_block_size + 1 + var max_num_types uint = brotli_min_size_t(max_num_blocks, maxNumberOfBlockTypes+1) + /* We have to allocate one more histogram than the maximum number of block + types for the current histogram when the meta-block is too big. */ + self.alphabet_size_ = alphabet_size + + self.min_block_size_ = min_block_size + self.split_threshold_ = split_threshold + self.num_blocks_ = 0 + self.split_ = split + self.histograms_size_ = histograms_size + self.target_block_size_ = min_block_size + self.block_size_ = 0 + self.curr_histogram_ix_ = 0 + self.merge_last_count_ = 0 + brotli_ensure_capacity_uint8_t(&split.types, &split.types_alloc_size, max_num_blocks) + brotli_ensure_capacity_uint32_t(&split.lengths, &split.lengths_alloc_size, max_num_blocks) + self.split_.num_blocks = max_num_blocks + *histograms_size = max_num_types + if histograms == nil || cap(*histograms) < int(*histograms_size) { + *histograms = make([]histogramDistance, *histograms_size) + } else { + *histograms = (*histograms)[:*histograms_size] + } + self.histograms_ = *histograms + + /* Clear only current histogram. */ + histogramClearDistance(&self.histograms_[0]) + + self.last_histogram_ix_[1] = 0 + self.last_histogram_ix_[0] = self.last_histogram_ix_[1] +} + +/* Does either of three things: + (1) emits the current block with a new block type; + (2) emits the current block with the type of the second last block; + (3) merges the current block with the last block. */ +func blockSplitterFinishBlockDistance(self *blockSplitterDistance, is_final bool) { + var split *blockSplit = self.split_ + var last_entropy []float64 = self.last_entropy_[:] + var histograms []histogramDistance = self.histograms_ + self.block_size_ = brotli_max_size_t(self.block_size_, self.min_block_size_) + if self.num_blocks_ == 0 { + /* Create first block. */ + split.lengths[0] = uint32(self.block_size_) + + split.types[0] = 0 + last_entropy[0] = bitsEntropy(histograms[0].data_[:], self.alphabet_size_) + last_entropy[1] = last_entropy[0] + self.num_blocks_++ + split.num_types++ + self.curr_histogram_ix_++ + if self.curr_histogram_ix_ < *self.histograms_size_ { + histogramClearDistance(&histograms[self.curr_histogram_ix_]) + } + self.block_size_ = 0 + } else if self.block_size_ > 0 { + var entropy float64 = bitsEntropy(histograms[self.curr_histogram_ix_].data_[:], self.alphabet_size_) + var combined_histo [2]histogramDistance + var combined_entropy [2]float64 + var diff [2]float64 + var j uint + for j = 0; j < 2; j++ { + var last_histogram_ix uint = self.last_histogram_ix_[j] + combined_histo[j] = histograms[self.curr_histogram_ix_] + histogramAddHistogramDistance(&combined_histo[j], &histograms[last_histogram_ix]) + combined_entropy[j] = bitsEntropy(combined_histo[j].data_[0:], self.alphabet_size_) + diff[j] = combined_entropy[j] - entropy - last_entropy[j] + } + + if split.num_types < maxNumberOfBlockTypes && diff[0] > self.split_threshold_ && diff[1] > self.split_threshold_ { + /* Create new block. */ + split.lengths[self.num_blocks_] = uint32(self.block_size_) + + split.types[self.num_blocks_] = byte(split.num_types) + self.last_histogram_ix_[1] = self.last_histogram_ix_[0] + self.last_histogram_ix_[0] = uint(byte(split.num_types)) + last_entropy[1] = last_entropy[0] + last_entropy[0] = entropy + self.num_blocks_++ + split.num_types++ + self.curr_histogram_ix_++ + if self.curr_histogram_ix_ < *self.histograms_size_ { + histogramClearDistance(&histograms[self.curr_histogram_ix_]) + } + self.block_size_ = 0 + self.merge_last_count_ = 0 + self.target_block_size_ = self.min_block_size_ + } else if diff[1] < diff[0]-20.0 { + split.lengths[self.num_blocks_] = uint32(self.block_size_) + split.types[self.num_blocks_] = split.types[self.num_blocks_-2] + /* Combine this block with second last block. */ + + var tmp uint = self.last_histogram_ix_[0] + self.last_histogram_ix_[0] = self.last_histogram_ix_[1] + self.last_histogram_ix_[1] = tmp + histograms[self.last_histogram_ix_[0]] = combined_histo[1] + last_entropy[1] = last_entropy[0] + last_entropy[0] = combined_entropy[1] + self.num_blocks_++ + self.block_size_ = 0 + histogramClearDistance(&histograms[self.curr_histogram_ix_]) + self.merge_last_count_ = 0 + self.target_block_size_ = self.min_block_size_ + } else { + /* Combine this block with last block. */ + split.lengths[self.num_blocks_-1] += uint32(self.block_size_) + + histograms[self.last_histogram_ix_[0]] = combined_histo[0] + last_entropy[0] = combined_entropy[0] + if split.num_types == 1 { + last_entropy[1] = last_entropy[0] + } + + self.block_size_ = 0 + histogramClearDistance(&histograms[self.curr_histogram_ix_]) + self.merge_last_count_++ + if self.merge_last_count_ > 1 { + self.target_block_size_ += self.min_block_size_ + } + } + } + + if is_final { + *self.histograms_size_ = split.num_types + split.num_blocks = self.num_blocks_ + } +} + +/* Adds the next symbol to the current histogram. When the current histogram + reaches the target size, decides on merging the block. */ +func blockSplitterAddSymbolDistance(self *blockSplitterDistance, symbol uint) { + histogramAddDistance(&self.histograms_[self.curr_histogram_ix_], symbol) + self.block_size_++ + if self.block_size_ == self.target_block_size_ { + blockSplitterFinishBlockDistance(self, false) /* is_final = */ + } +} diff --git a/vendor/github.com/andybalholm/brotli/metablock_literal.go b/vendor/github.com/andybalholm/brotli/metablock_literal.go new file mode 100644 index 000000000..307f8da88 --- /dev/null +++ b/vendor/github.com/andybalholm/brotli/metablock_literal.go @@ -0,0 +1,165 @@ +package brotli + +/* Copyright 2015 Google Inc. All Rights Reserved. + + Distributed under MIT license. + See file LICENSE for detail or copy at https://opensource.org/licenses/MIT +*/ + +/* Greedy block splitter for one block category (literal, command or distance). + */ +type blockSplitterLiteral struct { + alphabet_size_ uint + min_block_size_ uint + split_threshold_ float64 + num_blocks_ uint + split_ *blockSplit + histograms_ []histogramLiteral + histograms_size_ *uint + target_block_size_ uint + block_size_ uint + curr_histogram_ix_ uint + last_histogram_ix_ [2]uint + last_entropy_ [2]float64 + merge_last_count_ uint +} + +func initBlockSplitterLiteral(self *blockSplitterLiteral, alphabet_size uint, min_block_size uint, split_threshold float64, num_symbols uint, split *blockSplit, histograms *[]histogramLiteral, histograms_size *uint) { + var max_num_blocks uint = num_symbols/min_block_size + 1 + var max_num_types uint = brotli_min_size_t(max_num_blocks, maxNumberOfBlockTypes+1) + /* We have to allocate one more histogram than the maximum number of block + types for the current histogram when the meta-block is too big. */ + self.alphabet_size_ = alphabet_size + + self.min_block_size_ = min_block_size + self.split_threshold_ = split_threshold + self.num_blocks_ = 0 + self.split_ = split + self.histograms_size_ = histograms_size + self.target_block_size_ = min_block_size + self.block_size_ = 0 + self.curr_histogram_ix_ = 0 + self.merge_last_count_ = 0 + brotli_ensure_capacity_uint8_t(&split.types, &split.types_alloc_size, max_num_blocks) + brotli_ensure_capacity_uint32_t(&split.lengths, &split.lengths_alloc_size, max_num_blocks) + self.split_.num_blocks = max_num_blocks + *histograms_size = max_num_types + if histograms == nil || cap(*histograms) < int(*histograms_size) { + *histograms = make([]histogramLiteral, *histograms_size) + } else { + *histograms = (*histograms)[:*histograms_size] + } + self.histograms_ = *histograms + + /* Clear only current histogram. */ + histogramClearLiteral(&self.histograms_[0]) + + self.last_histogram_ix_[1] = 0 + self.last_histogram_ix_[0] = self.last_histogram_ix_[1] +} + +/* Does either of three things: + (1) emits the current block with a new block type; + (2) emits the current block with the type of the second last block; + (3) merges the current block with the last block. */ +func blockSplitterFinishBlockLiteral(self *blockSplitterLiteral, is_final bool) { + var split *blockSplit = self.split_ + var last_entropy []float64 = self.last_entropy_[:] + var histograms []histogramLiteral = self.histograms_ + self.block_size_ = brotli_max_size_t(self.block_size_, self.min_block_size_) + if self.num_blocks_ == 0 { + /* Create first block. */ + split.lengths[0] = uint32(self.block_size_) + + split.types[0] = 0 + last_entropy[0] = bitsEntropy(histograms[0].data_[:], self.alphabet_size_) + last_entropy[1] = last_entropy[0] + self.num_blocks_++ + split.num_types++ + self.curr_histogram_ix_++ + if self.curr_histogram_ix_ < *self.histograms_size_ { + histogramClearLiteral(&histograms[self.curr_histogram_ix_]) + } + self.block_size_ = 0 + } else if self.block_size_ > 0 { + var entropy float64 = bitsEntropy(histograms[self.curr_histogram_ix_].data_[:], self.alphabet_size_) + var combined_histo [2]histogramLiteral + var combined_entropy [2]float64 + var diff [2]float64 + var j uint + for j = 0; j < 2; j++ { + var last_histogram_ix uint = self.last_histogram_ix_[j] + combined_histo[j] = histograms[self.curr_histogram_ix_] + histogramAddHistogramLiteral(&combined_histo[j], &histograms[last_histogram_ix]) + combined_entropy[j] = bitsEntropy(combined_histo[j].data_[0:], self.alphabet_size_) + diff[j] = combined_entropy[j] - entropy - last_entropy[j] + } + + if split.num_types < maxNumberOfBlockTypes && diff[0] > self.split_threshold_ && diff[1] > self.split_threshold_ { + /* Create new block. */ + split.lengths[self.num_blocks_] = uint32(self.block_size_) + + split.types[self.num_blocks_] = byte(split.num_types) + self.last_histogram_ix_[1] = self.last_histogram_ix_[0] + self.last_histogram_ix_[0] = uint(byte(split.num_types)) + last_entropy[1] = last_entropy[0] + last_entropy[0] = entropy + self.num_blocks_++ + split.num_types++ + self.curr_histogram_ix_++ + if self.curr_histogram_ix_ < *self.histograms_size_ { + histogramClearLiteral(&histograms[self.curr_histogram_ix_]) + } + self.block_size_ = 0 + self.merge_last_count_ = 0 + self.target_block_size_ = self.min_block_size_ + } else if diff[1] < diff[0]-20.0 { + split.lengths[self.num_blocks_] = uint32(self.block_size_) + split.types[self.num_blocks_] = split.types[self.num_blocks_-2] + /* Combine this block with second last block. */ + + var tmp uint = self.last_histogram_ix_[0] + self.last_histogram_ix_[0] = self.last_histogram_ix_[1] + self.last_histogram_ix_[1] = tmp + histograms[self.last_histogram_ix_[0]] = combined_histo[1] + last_entropy[1] = last_entropy[0] + last_entropy[0] = combined_entropy[1] + self.num_blocks_++ + self.block_size_ = 0 + histogramClearLiteral(&histograms[self.curr_histogram_ix_]) + self.merge_last_count_ = 0 + self.target_block_size_ = self.min_block_size_ + } else { + /* Combine this block with last block. */ + split.lengths[self.num_blocks_-1] += uint32(self.block_size_) + + histograms[self.last_histogram_ix_[0]] = combined_histo[0] + last_entropy[0] = combined_entropy[0] + if split.num_types == 1 { + last_entropy[1] = last_entropy[0] + } + + self.block_size_ = 0 + histogramClearLiteral(&histograms[self.curr_histogram_ix_]) + self.merge_last_count_++ + if self.merge_last_count_ > 1 { + self.target_block_size_ += self.min_block_size_ + } + } + } + + if is_final { + *self.histograms_size_ = split.num_types + split.num_blocks = self.num_blocks_ + } +} + +/* Adds the next symbol to the current histogram. When the current histogram + reaches the target size, decides on merging the block. */ +func blockSplitterAddSymbolLiteral(self *blockSplitterLiteral, symbol uint) { + histogramAddLiteral(&self.histograms_[self.curr_histogram_ix_], symbol) + self.block_size_++ + if self.block_size_ == self.target_block_size_ { + blockSplitterFinishBlockLiteral(self, false) /* is_final = */ + } +} diff --git a/vendor/github.com/andybalholm/brotli/params.go b/vendor/github.com/andybalholm/brotli/params.go new file mode 100644 index 000000000..0a4c68752 --- /dev/null +++ b/vendor/github.com/andybalholm/brotli/params.go @@ -0,0 +1,37 @@ +package brotli + +/* Copyright 2017 Google Inc. All Rights Reserved. + + Distributed under MIT license. + See file LICENSE for detail or copy at https://opensource.org/licenses/MIT +*/ + +/* Parameters for the Brotli encoder with chosen quality levels. */ +type hasherParams struct { + type_ int + bucket_bits int + block_bits int + hash_len int + num_last_distances_to_check int +} + +type distanceParams struct { + distance_postfix_bits uint32 + num_direct_distance_codes uint32 + alphabet_size uint32 + max_distance uint +} + +/* Encoding parameters */ +type encoderParams struct { + mode int + quality int + lgwin uint + lgblock int + size_hint uint + disable_literal_context_modeling bool + large_window bool + hasher hasherParams + dist distanceParams + dictionary encoderDictionary +} diff --git a/vendor/github.com/andybalholm/brotli/platform.go b/vendor/github.com/andybalholm/brotli/platform.go new file mode 100644 index 000000000..4ebfb1528 --- /dev/null +++ b/vendor/github.com/andybalholm/brotli/platform.go @@ -0,0 +1,103 @@ +package brotli + +/* Copyright 2013 Google Inc. All Rights Reserved. + + Distributed under MIT license. + See file LICENSE for detail or copy at https://opensource.org/licenses/MIT +*/ + +func brotli_min_double(a float64, b float64) float64 { + if a < b { + return a + } else { + return b + } +} + +func brotli_max_double(a float64, b float64) float64 { + if a > b { + return a + } else { + return b + } +} + +func brotli_min_float(a float32, b float32) float32 { + if a < b { + return a + } else { + return b + } +} + +func brotli_max_float(a float32, b float32) float32 { + if a > b { + return a + } else { + return b + } +} + +func brotli_min_int(a int, b int) int { + if a < b { + return a + } else { + return b + } +} + +func brotli_max_int(a int, b int) int { + if a > b { + return a + } else { + return b + } +} + +func brotli_min_size_t(a uint, b uint) uint { + if a < b { + return a + } else { + return b + } +} + +func brotli_max_size_t(a uint, b uint) uint { + if a > b { + return a + } else { + return b + } +} + +func brotli_min_uint32_t(a uint32, b uint32) uint32 { + if a < b { + return a + } else { + return b + } +} + +func brotli_max_uint32_t(a uint32, b uint32) uint32 { + if a > b { + return a + } else { + return b + } +} + +func brotli_min_uint8_t(a byte, b byte) byte { + if a < b { + return a + } else { + return b + } +} + +func brotli_max_uint8_t(a byte, b byte) byte { + if a > b { + return a + } else { + return b + } +} diff --git a/vendor/github.com/andybalholm/brotli/prefix.go b/vendor/github.com/andybalholm/brotli/prefix.go new file mode 100644 index 000000000..484df0d61 --- /dev/null +++ b/vendor/github.com/andybalholm/brotli/prefix.go @@ -0,0 +1,30 @@ +package brotli + +/* Copyright 2013 Google Inc. All Rights Reserved. + + Distributed under MIT license. + See file LICENSE for detail or copy at https://opensource.org/licenses/MIT +*/ + +/* Functions for encoding of integers into prefix codes the amount of extra + bits, and the actual values of the extra bits. */ + +/* Here distance_code is an intermediate code, i.e. one of the special codes or + the actual distance increased by BROTLI_NUM_DISTANCE_SHORT_CODES - 1. */ +func prefixEncodeCopyDistance(distance_code uint, num_direct_codes uint, postfix_bits uint, code *uint16, extra_bits *uint32) { + if distance_code < numDistanceShortCodes+num_direct_codes { + *code = uint16(distance_code) + *extra_bits = 0 + return + } else { + var dist uint = (uint(1) << (postfix_bits + 2)) + (distance_code - numDistanceShortCodes - num_direct_codes) + var bucket uint = uint(log2FloorNonZero(dist) - 1) + var postfix_mask uint = (1 << postfix_bits) - 1 + var postfix uint = dist & postfix_mask + var prefix uint = (dist >> bucket) & 1 + var offset uint = (2 + prefix) << bucket + var nbits uint = bucket - postfix_bits + *code = uint16(nbits<<10 | (numDistanceShortCodes + num_direct_codes + ((2*(nbits-1) + prefix) << postfix_bits) + postfix)) + *extra_bits = uint32((dist - offset) >> postfix_bits) + } +} diff --git a/vendor/github.com/andybalholm/brotli/prefix_dec.go b/vendor/github.com/andybalholm/brotli/prefix_dec.go new file mode 100644 index 000000000..183f0d53f --- /dev/null +++ b/vendor/github.com/andybalholm/brotli/prefix_dec.go @@ -0,0 +1,723 @@ +package brotli + +/* Copyright 2013 Google Inc. All Rights Reserved. + + Distributed under MIT license. + See file LICENSE for detail or copy at https://opensource.org/licenses/MIT +*/ + +type cmdLutElement struct { + insert_len_extra_bits byte + copy_len_extra_bits byte + distance_code int8 + context byte + insert_len_offset uint16 + copy_len_offset uint16 +} + +var kCmdLut = [numCommandSymbols]cmdLutElement{ + cmdLutElement{0x00, 0x00, 0, 0x00, 0x0000, 0x0002}, + cmdLutElement{0x00, 0x00, 0, 0x01, 0x0000, 0x0003}, + cmdLutElement{0x00, 0x00, 0, 0x02, 0x0000, 0x0004}, + cmdLutElement{0x00, 0x00, 0, 0x03, 0x0000, 0x0005}, + cmdLutElement{0x00, 0x00, 0, 0x03, 0x0000, 0x0006}, + cmdLutElement{0x00, 0x00, 0, 0x03, 0x0000, 0x0007}, + cmdLutElement{0x00, 0x00, 0, 0x03, 0x0000, 0x0008}, + cmdLutElement{0x00, 0x00, 0, 0x03, 0x0000, 0x0009}, + cmdLutElement{0x00, 0x00, 0, 0x00, 0x0001, 0x0002}, + cmdLutElement{0x00, 0x00, 0, 0x01, 0x0001, 0x0003}, + cmdLutElement{0x00, 0x00, 0, 0x02, 0x0001, 0x0004}, + cmdLutElement{0x00, 0x00, 0, 0x03, 0x0001, 0x0005}, + cmdLutElement{0x00, 0x00, 0, 0x03, 0x0001, 0x0006}, + cmdLutElement{0x00, 0x00, 0, 0x03, 0x0001, 0x0007}, + cmdLutElement{0x00, 0x00, 0, 0x03, 0x0001, 0x0008}, + cmdLutElement{0x00, 0x00, 0, 0x03, 0x0001, 0x0009}, + cmdLutElement{0x00, 0x00, 0, 0x00, 0x0002, 0x0002}, + cmdLutElement{0x00, 0x00, 0, 0x01, 0x0002, 0x0003}, + cmdLutElement{0x00, 0x00, 0, 0x02, 0x0002, 0x0004}, + cmdLutElement{0x00, 0x00, 0, 0x03, 0x0002, 0x0005}, + cmdLutElement{0x00, 0x00, 0, 0x03, 0x0002, 0x0006}, + cmdLutElement{0x00, 0x00, 0, 0x03, 0x0002, 0x0007}, + cmdLutElement{0x00, 0x00, 0, 0x03, 0x0002, 0x0008}, + cmdLutElement{0x00, 0x00, 0, 0x03, 0x0002, 0x0009}, + cmdLutElement{0x00, 0x00, 0, 0x00, 0x0003, 0x0002}, + cmdLutElement{0x00, 0x00, 0, 0x01, 0x0003, 0x0003}, + cmdLutElement{0x00, 0x00, 0, 0x02, 0x0003, 0x0004}, + cmdLutElement{0x00, 0x00, 0, 0x03, 0x0003, 0x0005}, + cmdLutElement{0x00, 0x00, 0, 0x03, 0x0003, 0x0006}, + cmdLutElement{0x00, 0x00, 0, 0x03, 0x0003, 0x0007}, + cmdLutElement{0x00, 0x00, 0, 0x03, 0x0003, 0x0008}, + cmdLutElement{0x00, 0x00, 0, 0x03, 0x0003, 0x0009}, + cmdLutElement{0x00, 0x00, 0, 0x00, 0x0004, 0x0002}, + cmdLutElement{0x00, 0x00, 0, 0x01, 0x0004, 0x0003}, + cmdLutElement{0x00, 0x00, 0, 0x02, 0x0004, 0x0004}, + cmdLutElement{0x00, 0x00, 0, 0x03, 0x0004, 0x0005}, + cmdLutElement{0x00, 0x00, 0, 0x03, 0x0004, 0x0006}, + cmdLutElement{0x00, 0x00, 0, 0x03, 0x0004, 0x0007}, + cmdLutElement{0x00, 0x00, 0, 0x03, 0x0004, 0x0008}, + cmdLutElement{0x00, 0x00, 0, 0x03, 0x0004, 0x0009}, + cmdLutElement{0x00, 0x00, 0, 0x00, 0x0005, 0x0002}, + cmdLutElement{0x00, 0x00, 0, 0x01, 0x0005, 0x0003}, + cmdLutElement{0x00, 0x00, 0, 0x02, 0x0005, 0x0004}, + cmdLutElement{0x00, 0x00, 0, 0x03, 0x0005, 0x0005}, + cmdLutElement{0x00, 0x00, 0, 0x03, 0x0005, 0x0006}, + cmdLutElement{0x00, 0x00, 0, 0x03, 0x0005, 0x0007}, + cmdLutElement{0x00, 0x00, 0, 0x03, 0x0005, 0x0008}, + cmdLutElement{0x00, 0x00, 0, 0x03, 0x0005, 0x0009}, + cmdLutElement{0x01, 0x00, 0, 0x00, 0x0006, 0x0002}, + cmdLutElement{0x01, 0x00, 0, 0x01, 0x0006, 0x0003}, + cmdLutElement{0x01, 0x00, 0, 0x02, 0x0006, 0x0004}, + cmdLutElement{0x01, 0x00, 0, 0x03, 0x0006, 0x0005}, + cmdLutElement{0x01, 0x00, 0, 0x03, 0x0006, 0x0006}, + cmdLutElement{0x01, 0x00, 0, 0x03, 0x0006, 0x0007}, + cmdLutElement{0x01, 0x00, 0, 0x03, 0x0006, 0x0008}, + cmdLutElement{0x01, 0x00, 0, 0x03, 0x0006, 0x0009}, + cmdLutElement{0x01, 0x00, 0, 0x00, 0x0008, 0x0002}, + cmdLutElement{0x01, 0x00, 0, 0x01, 0x0008, 0x0003}, + cmdLutElement{0x01, 0x00, 0, 0x02, 0x0008, 0x0004}, + cmdLutElement{0x01, 0x00, 0, 0x03, 0x0008, 0x0005}, + cmdLutElement{0x01, 0x00, 0, 0x03, 0x0008, 0x0006}, + cmdLutElement{0x01, 0x00, 0, 0x03, 0x0008, 0x0007}, + cmdLutElement{0x01, 0x00, 0, 0x03, 0x0008, 0x0008}, + cmdLutElement{0x01, 0x00, 0, 0x03, 0x0008, 0x0009}, + cmdLutElement{0x00, 0x01, 0, 0x03, 0x0000, 0x000a}, + cmdLutElement{0x00, 0x01, 0, 0x03, 0x0000, 0x000c}, + cmdLutElement{0x00, 0x02, 0, 0x03, 0x0000, 0x000e}, + cmdLutElement{0x00, 0x02, 0, 0x03, 0x0000, 0x0012}, + cmdLutElement{0x00, 0x03, 0, 0x03, 0x0000, 0x0016}, + cmdLutElement{0x00, 0x03, 0, 0x03, 0x0000, 0x001e}, + cmdLutElement{0x00, 0x04, 0, 0x03, 0x0000, 0x0026}, + cmdLutElement{0x00, 0x04, 0, 0x03, 0x0000, 0x0036}, + cmdLutElement{0x00, 0x01, 0, 0x03, 0x0001, 0x000a}, + cmdLutElement{0x00, 0x01, 0, 0x03, 0x0001, 0x000c}, + cmdLutElement{0x00, 0x02, 0, 0x03, 0x0001, 0x000e}, + cmdLutElement{0x00, 0x02, 0, 0x03, 0x0001, 0x0012}, + cmdLutElement{0x00, 0x03, 0, 0x03, 0x0001, 0x0016}, + cmdLutElement{0x00, 0x03, 0, 0x03, 0x0001, 0x001e}, + cmdLutElement{0x00, 0x04, 0, 0x03, 0x0001, 0x0026}, + cmdLutElement{0x00, 0x04, 0, 0x03, 0x0001, 0x0036}, + cmdLutElement{0x00, 0x01, 0, 0x03, 0x0002, 0x000a}, + cmdLutElement{0x00, 0x01, 0, 0x03, 0x0002, 0x000c}, + cmdLutElement{0x00, 0x02, 0, 0x03, 0x0002, 0x000e}, + cmdLutElement{0x00, 0x02, 0, 0x03, 0x0002, 0x0012}, + cmdLutElement{0x00, 0x03, 0, 0x03, 0x0002, 0x0016}, + cmdLutElement{0x00, 0x03, 0, 0x03, 0x0002, 0x001e}, + cmdLutElement{0x00, 0x04, 0, 0x03, 0x0002, 0x0026}, + cmdLutElement{0x00, 0x04, 0, 0x03, 0x0002, 0x0036}, + cmdLutElement{0x00, 0x01, 0, 0x03, 0x0003, 0x000a}, + cmdLutElement{0x00, 0x01, 0, 0x03, 0x0003, 0x000c}, + cmdLutElement{0x00, 0x02, 0, 0x03, 0x0003, 0x000e}, + cmdLutElement{0x00, 0x02, 0, 0x03, 0x0003, 0x0012}, + cmdLutElement{0x00, 0x03, 0, 0x03, 0x0003, 0x0016}, + cmdLutElement{0x00, 0x03, 0, 0x03, 0x0003, 0x001e}, + cmdLutElement{0x00, 0x04, 0, 0x03, 0x0003, 0x0026}, + cmdLutElement{0x00, 0x04, 0, 0x03, 0x0003, 0x0036}, + cmdLutElement{0x00, 0x01, 0, 0x03, 0x0004, 0x000a}, + cmdLutElement{0x00, 0x01, 0, 0x03, 0x0004, 0x000c}, + cmdLutElement{0x00, 0x02, 0, 0x03, 0x0004, 0x000e}, + cmdLutElement{0x00, 0x02, 0, 0x03, 0x0004, 0x0012}, + cmdLutElement{0x00, 0x03, 0, 0x03, 0x0004, 0x0016}, + cmdLutElement{0x00, 0x03, 0, 0x03, 0x0004, 0x001e}, + cmdLutElement{0x00, 0x04, 0, 0x03, 0x0004, 0x0026}, + cmdLutElement{0x00, 0x04, 0, 0x03, 0x0004, 0x0036}, + cmdLutElement{0x00, 0x01, 0, 0x03, 0x0005, 0x000a}, + cmdLutElement{0x00, 0x01, 0, 0x03, 0x0005, 0x000c}, + cmdLutElement{0x00, 0x02, 0, 0x03, 0x0005, 0x000e}, + cmdLutElement{0x00, 0x02, 0, 0x03, 0x0005, 0x0012}, + cmdLutElement{0x00, 0x03, 0, 0x03, 0x0005, 0x0016}, + cmdLutElement{0x00, 0x03, 0, 0x03, 0x0005, 0x001e}, + cmdLutElement{0x00, 0x04, 0, 0x03, 0x0005, 0x0026}, + cmdLutElement{0x00, 0x04, 0, 0x03, 0x0005, 0x0036}, + cmdLutElement{0x01, 0x01, 0, 0x03, 0x0006, 0x000a}, + cmdLutElement{0x01, 0x01, 0, 0x03, 0x0006, 0x000c}, + cmdLutElement{0x01, 0x02, 0, 0x03, 0x0006, 0x000e}, + cmdLutElement{0x01, 0x02, 0, 0x03, 0x0006, 0x0012}, + cmdLutElement{0x01, 0x03, 0, 0x03, 0x0006, 0x0016}, + cmdLutElement{0x01, 0x03, 0, 0x03, 0x0006, 0x001e}, + cmdLutElement{0x01, 0x04, 0, 0x03, 0x0006, 0x0026}, + cmdLutElement{0x01, 0x04, 0, 0x03, 0x0006, 0x0036}, + cmdLutElement{0x01, 0x01, 0, 0x03, 0x0008, 0x000a}, + cmdLutElement{0x01, 0x01, 0, 0x03, 0x0008, 0x000c}, + cmdLutElement{0x01, 0x02, 0, 0x03, 0x0008, 0x000e}, + cmdLutElement{0x01, 0x02, 0, 0x03, 0x0008, 0x0012}, + cmdLutElement{0x01, 0x03, 0, 0x03, 0x0008, 0x0016}, + cmdLutElement{0x01, 0x03, 0, 0x03, 0x0008, 0x001e}, + cmdLutElement{0x01, 0x04, 0, 0x03, 0x0008, 0x0026}, + cmdLutElement{0x01, 0x04, 0, 0x03, 0x0008, 0x0036}, + cmdLutElement{0x00, 0x00, -1, 0x00, 0x0000, 0x0002}, + cmdLutElement{0x00, 0x00, -1, 0x01, 0x0000, 0x0003}, + cmdLutElement{0x00, 0x00, -1, 0x02, 0x0000, 0x0004}, + cmdLutElement{0x00, 0x00, -1, 0x03, 0x0000, 0x0005}, + cmdLutElement{0x00, 0x00, -1, 0x03, 0x0000, 0x0006}, + cmdLutElement{0x00, 0x00, -1, 0x03, 0x0000, 0x0007}, + cmdLutElement{0x00, 0x00, -1, 0x03, 0x0000, 0x0008}, + cmdLutElement{0x00, 0x00, -1, 0x03, 0x0000, 0x0009}, + cmdLutElement{0x00, 0x00, -1, 0x00, 0x0001, 0x0002}, + cmdLutElement{0x00, 0x00, -1, 0x01, 0x0001, 0x0003}, + cmdLutElement{0x00, 0x00, -1, 0x02, 0x0001, 0x0004}, + cmdLutElement{0x00, 0x00, -1, 0x03, 0x0001, 0x0005}, + cmdLutElement{0x00, 0x00, -1, 0x03, 0x0001, 0x0006}, + cmdLutElement{0x00, 0x00, -1, 0x03, 0x0001, 0x0007}, + cmdLutElement{0x00, 0x00, -1, 0x03, 0x0001, 0x0008}, + cmdLutElement{0x00, 0x00, -1, 0x03, 0x0001, 0x0009}, + cmdLutElement{0x00, 0x00, -1, 0x00, 0x0002, 0x0002}, + cmdLutElement{0x00, 0x00, -1, 0x01, 0x0002, 0x0003}, + cmdLutElement{0x00, 0x00, -1, 0x02, 0x0002, 0x0004}, + cmdLutElement{0x00, 0x00, -1, 0x03, 0x0002, 0x0005}, + cmdLutElement{0x00, 0x00, -1, 0x03, 0x0002, 0x0006}, + cmdLutElement{0x00, 0x00, -1, 0x03, 0x0002, 0x0007}, + cmdLutElement{0x00, 0x00, -1, 0x03, 0x0002, 0x0008}, + cmdLutElement{0x00, 0x00, -1, 0x03, 0x0002, 0x0009}, + cmdLutElement{0x00, 0x00, -1, 0x00, 0x0003, 0x0002}, + cmdLutElement{0x00, 0x00, -1, 0x01, 0x0003, 0x0003}, + cmdLutElement{0x00, 0x00, -1, 0x02, 0x0003, 0x0004}, + cmdLutElement{0x00, 0x00, -1, 0x03, 0x0003, 0x0005}, + cmdLutElement{0x00, 0x00, -1, 0x03, 0x0003, 0x0006}, + cmdLutElement{0x00, 0x00, -1, 0x03, 0x0003, 0x0007}, + cmdLutElement{0x00, 0x00, -1, 0x03, 0x0003, 0x0008}, + cmdLutElement{0x00, 0x00, -1, 0x03, 0x0003, 0x0009}, + cmdLutElement{0x00, 0x00, -1, 0x00, 0x0004, 0x0002}, + cmdLutElement{0x00, 0x00, -1, 0x01, 0x0004, 0x0003}, + cmdLutElement{0x00, 0x00, -1, 0x02, 0x0004, 0x0004}, + cmdLutElement{0x00, 0x00, -1, 0x03, 0x0004, 0x0005}, + cmdLutElement{0x00, 0x00, -1, 0x03, 0x0004, 0x0006}, + cmdLutElement{0x00, 0x00, -1, 0x03, 0x0004, 0x0007}, + cmdLutElement{0x00, 0x00, -1, 0x03, 0x0004, 0x0008}, + cmdLutElement{0x00, 0x00, -1, 0x03, 0x0004, 0x0009}, + cmdLutElement{0x00, 0x00, -1, 0x00, 0x0005, 0x0002}, + cmdLutElement{0x00, 0x00, -1, 0x01, 0x0005, 0x0003}, + cmdLutElement{0x00, 0x00, -1, 0x02, 0x0005, 0x0004}, + cmdLutElement{0x00, 0x00, -1, 0x03, 0x0005, 0x0005}, + cmdLutElement{0x00, 0x00, -1, 0x03, 0x0005, 0x0006}, + cmdLutElement{0x00, 0x00, -1, 0x03, 0x0005, 0x0007}, + cmdLutElement{0x00, 0x00, -1, 0x03, 0x0005, 0x0008}, + cmdLutElement{0x00, 0x00, -1, 0x03, 0x0005, 0x0009}, + cmdLutElement{0x01, 0x00, -1, 0x00, 0x0006, 0x0002}, + cmdLutElement{0x01, 0x00, -1, 0x01, 0x0006, 0x0003}, + cmdLutElement{0x01, 0x00, -1, 0x02, 0x0006, 0x0004}, + cmdLutElement{0x01, 0x00, -1, 0x03, 0x0006, 0x0005}, + cmdLutElement{0x01, 0x00, -1, 0x03, 0x0006, 0x0006}, + cmdLutElement{0x01, 0x00, -1, 0x03, 0x0006, 0x0007}, + cmdLutElement{0x01, 0x00, -1, 0x03, 0x0006, 0x0008}, + cmdLutElement{0x01, 0x00, -1, 0x03, 0x0006, 0x0009}, + cmdLutElement{0x01, 0x00, -1, 0x00, 0x0008, 0x0002}, + cmdLutElement{0x01, 0x00, -1, 0x01, 0x0008, 0x0003}, + cmdLutElement{0x01, 0x00, -1, 0x02, 0x0008, 0x0004}, + cmdLutElement{0x01, 0x00, -1, 0x03, 0x0008, 0x0005}, + cmdLutElement{0x01, 0x00, -1, 0x03, 0x0008, 0x0006}, + cmdLutElement{0x01, 0x00, -1, 0x03, 0x0008, 0x0007}, + cmdLutElement{0x01, 0x00, -1, 0x03, 0x0008, 0x0008}, + cmdLutElement{0x01, 0x00, -1, 0x03, 0x0008, 0x0009}, + cmdLutElement{0x00, 0x01, -1, 0x03, 0x0000, 0x000a}, + cmdLutElement{0x00, 0x01, -1, 0x03, 0x0000, 0x000c}, + cmdLutElement{0x00, 0x02, -1, 0x03, 0x0000, 0x000e}, + cmdLutElement{0x00, 0x02, -1, 0x03, 0x0000, 0x0012}, + cmdLutElement{0x00, 0x03, -1, 0x03, 0x0000, 0x0016}, + cmdLutElement{0x00, 0x03, -1, 0x03, 0x0000, 0x001e}, + cmdLutElement{0x00, 0x04, -1, 0x03, 0x0000, 0x0026}, + cmdLutElement{0x00, 0x04, -1, 0x03, 0x0000, 0x0036}, + cmdLutElement{0x00, 0x01, -1, 0x03, 0x0001, 0x000a}, + cmdLutElement{0x00, 0x01, -1, 0x03, 0x0001, 0x000c}, + cmdLutElement{0x00, 0x02, -1, 0x03, 0x0001, 0x000e}, + cmdLutElement{0x00, 0x02, -1, 0x03, 0x0001, 0x0012}, + cmdLutElement{0x00, 0x03, -1, 0x03, 0x0001, 0x0016}, + cmdLutElement{0x00, 0x03, -1, 0x03, 0x0001, 0x001e}, + cmdLutElement{0x00, 0x04, -1, 0x03, 0x0001, 0x0026}, + cmdLutElement{0x00, 0x04, -1, 0x03, 0x0001, 0x0036}, + cmdLutElement{0x00, 0x01, -1, 0x03, 0x0002, 0x000a}, + cmdLutElement{0x00, 0x01, -1, 0x03, 0x0002, 0x000c}, + cmdLutElement{0x00, 0x02, -1, 0x03, 0x0002, 0x000e}, + cmdLutElement{0x00, 0x02, -1, 0x03, 0x0002, 0x0012}, + cmdLutElement{0x00, 0x03, -1, 0x03, 0x0002, 0x0016}, + cmdLutElement{0x00, 0x03, -1, 0x03, 0x0002, 0x001e}, + cmdLutElement{0x00, 0x04, -1, 0x03, 0x0002, 0x0026}, + cmdLutElement{0x00, 0x04, -1, 0x03, 0x0002, 0x0036}, + cmdLutElement{0x00, 0x01, -1, 0x03, 0x0003, 0x000a}, + cmdLutElement{0x00, 0x01, -1, 0x03, 0x0003, 0x000c}, + cmdLutElement{0x00, 0x02, -1, 0x03, 0x0003, 0x000e}, + cmdLutElement{0x00, 0x02, -1, 0x03, 0x0003, 0x0012}, + cmdLutElement{0x00, 0x03, -1, 0x03, 0x0003, 0x0016}, + cmdLutElement{0x00, 0x03, -1, 0x03, 0x0003, 0x001e}, + cmdLutElement{0x00, 0x04, -1, 0x03, 0x0003, 0x0026}, + cmdLutElement{0x00, 0x04, -1, 0x03, 0x0003, 0x0036}, + cmdLutElement{0x00, 0x01, -1, 0x03, 0x0004, 0x000a}, + cmdLutElement{0x00, 0x01, -1, 0x03, 0x0004, 0x000c}, + cmdLutElement{0x00, 0x02, -1, 0x03, 0x0004, 0x000e}, + cmdLutElement{0x00, 0x02, -1, 0x03, 0x0004, 0x0012}, + cmdLutElement{0x00, 0x03, -1, 0x03, 0x0004, 0x0016}, + cmdLutElement{0x00, 0x03, -1, 0x03, 0x0004, 0x001e}, + cmdLutElement{0x00, 0x04, -1, 0x03, 0x0004, 0x0026}, + cmdLutElement{0x00, 0x04, -1, 0x03, 0x0004, 0x0036}, + cmdLutElement{0x00, 0x01, -1, 0x03, 0x0005, 0x000a}, + cmdLutElement{0x00, 0x01, -1, 0x03, 0x0005, 0x000c}, + cmdLutElement{0x00, 0x02, -1, 0x03, 0x0005, 0x000e}, + cmdLutElement{0x00, 0x02, -1, 0x03, 0x0005, 0x0012}, + cmdLutElement{0x00, 0x03, -1, 0x03, 0x0005, 0x0016}, + cmdLutElement{0x00, 0x03, -1, 0x03, 0x0005, 0x001e}, + cmdLutElement{0x00, 0x04, -1, 0x03, 0x0005, 0x0026}, + cmdLutElement{0x00, 0x04, -1, 0x03, 0x0005, 0x0036}, + cmdLutElement{0x01, 0x01, -1, 0x03, 0x0006, 0x000a}, + cmdLutElement{0x01, 0x01, -1, 0x03, 0x0006, 0x000c}, + cmdLutElement{0x01, 0x02, -1, 0x03, 0x0006, 0x000e}, + cmdLutElement{0x01, 0x02, -1, 0x03, 0x0006, 0x0012}, + cmdLutElement{0x01, 0x03, -1, 0x03, 0x0006, 0x0016}, + cmdLutElement{0x01, 0x03, -1, 0x03, 0x0006, 0x001e}, + cmdLutElement{0x01, 0x04, -1, 0x03, 0x0006, 0x0026}, + cmdLutElement{0x01, 0x04, -1, 0x03, 0x0006, 0x0036}, + cmdLutElement{0x01, 0x01, -1, 0x03, 0x0008, 0x000a}, + cmdLutElement{0x01, 0x01, -1, 0x03, 0x0008, 0x000c}, + cmdLutElement{0x01, 0x02, -1, 0x03, 0x0008, 0x000e}, + cmdLutElement{0x01, 0x02, -1, 0x03, 0x0008, 0x0012}, + cmdLutElement{0x01, 0x03, -1, 0x03, 0x0008, 0x0016}, + cmdLutElement{0x01, 0x03, -1, 0x03, 0x0008, 0x001e}, + cmdLutElement{0x01, 0x04, -1, 0x03, 0x0008, 0x0026}, + cmdLutElement{0x01, 0x04, -1, 0x03, 0x0008, 0x0036}, + cmdLutElement{0x02, 0x00, -1, 0x00, 0x000a, 0x0002}, + cmdLutElement{0x02, 0x00, -1, 0x01, 0x000a, 0x0003}, + cmdLutElement{0x02, 0x00, -1, 0x02, 0x000a, 0x0004}, + cmdLutElement{0x02, 0x00, -1, 0x03, 0x000a, 0x0005}, + cmdLutElement{0x02, 0x00, -1, 0x03, 0x000a, 0x0006}, + cmdLutElement{0x02, 0x00, -1, 0x03, 0x000a, 0x0007}, + cmdLutElement{0x02, 0x00, -1, 0x03, 0x000a, 0x0008}, + cmdLutElement{0x02, 0x00, -1, 0x03, 0x000a, 0x0009}, + cmdLutElement{0x02, 0x00, -1, 0x00, 0x000e, 0x0002}, + cmdLutElement{0x02, 0x00, -1, 0x01, 0x000e, 0x0003}, + cmdLutElement{0x02, 0x00, -1, 0x02, 0x000e, 0x0004}, + cmdLutElement{0x02, 0x00, -1, 0x03, 0x000e, 0x0005}, + cmdLutElement{0x02, 0x00, -1, 0x03, 0x000e, 0x0006}, + cmdLutElement{0x02, 0x00, -1, 0x03, 0x000e, 0x0007}, + cmdLutElement{0x02, 0x00, -1, 0x03, 0x000e, 0x0008}, + cmdLutElement{0x02, 0x00, -1, 0x03, 0x000e, 0x0009}, + cmdLutElement{0x03, 0x00, -1, 0x00, 0x0012, 0x0002}, + cmdLutElement{0x03, 0x00, -1, 0x01, 0x0012, 0x0003}, + cmdLutElement{0x03, 0x00, -1, 0x02, 0x0012, 0x0004}, + cmdLutElement{0x03, 0x00, -1, 0x03, 0x0012, 0x0005}, + cmdLutElement{0x03, 0x00, -1, 0x03, 0x0012, 0x0006}, + cmdLutElement{0x03, 0x00, -1, 0x03, 0x0012, 0x0007}, + cmdLutElement{0x03, 0x00, -1, 0x03, 0x0012, 0x0008}, + cmdLutElement{0x03, 0x00, -1, 0x03, 0x0012, 0x0009}, + cmdLutElement{0x03, 0x00, -1, 0x00, 0x001a, 0x0002}, + cmdLutElement{0x03, 0x00, -1, 0x01, 0x001a, 0x0003}, + cmdLutElement{0x03, 0x00, -1, 0x02, 0x001a, 0x0004}, + cmdLutElement{0x03, 0x00, -1, 0x03, 0x001a, 0x0005}, + cmdLutElement{0x03, 0x00, -1, 0x03, 0x001a, 0x0006}, + cmdLutElement{0x03, 0x00, -1, 0x03, 0x001a, 0x0007}, + cmdLutElement{0x03, 0x00, -1, 0x03, 0x001a, 0x0008}, + cmdLutElement{0x03, 0x00, -1, 0x03, 0x001a, 0x0009}, + cmdLutElement{0x04, 0x00, -1, 0x00, 0x0022, 0x0002}, + cmdLutElement{0x04, 0x00, -1, 0x01, 0x0022, 0x0003}, + cmdLutElement{0x04, 0x00, -1, 0x02, 0x0022, 0x0004}, + cmdLutElement{0x04, 0x00, -1, 0x03, 0x0022, 0x0005}, + cmdLutElement{0x04, 0x00, -1, 0x03, 0x0022, 0x0006}, + cmdLutElement{0x04, 0x00, -1, 0x03, 0x0022, 0x0007}, + cmdLutElement{0x04, 0x00, -1, 0x03, 0x0022, 0x0008}, + cmdLutElement{0x04, 0x00, -1, 0x03, 0x0022, 0x0009}, + cmdLutElement{0x04, 0x00, -1, 0x00, 0x0032, 0x0002}, + cmdLutElement{0x04, 0x00, -1, 0x01, 0x0032, 0x0003}, + cmdLutElement{0x04, 0x00, -1, 0x02, 0x0032, 0x0004}, + cmdLutElement{0x04, 0x00, -1, 0x03, 0x0032, 0x0005}, + cmdLutElement{0x04, 0x00, -1, 0x03, 0x0032, 0x0006}, + cmdLutElement{0x04, 0x00, -1, 0x03, 0x0032, 0x0007}, + cmdLutElement{0x04, 0x00, -1, 0x03, 0x0032, 0x0008}, + cmdLutElement{0x04, 0x00, -1, 0x03, 0x0032, 0x0009}, + cmdLutElement{0x05, 0x00, -1, 0x00, 0x0042, 0x0002}, + cmdLutElement{0x05, 0x00, -1, 0x01, 0x0042, 0x0003}, + cmdLutElement{0x05, 0x00, -1, 0x02, 0x0042, 0x0004}, + cmdLutElement{0x05, 0x00, -1, 0x03, 0x0042, 0x0005}, + cmdLutElement{0x05, 0x00, -1, 0x03, 0x0042, 0x0006}, + cmdLutElement{0x05, 0x00, -1, 0x03, 0x0042, 0x0007}, + cmdLutElement{0x05, 0x00, -1, 0x03, 0x0042, 0x0008}, + cmdLutElement{0x05, 0x00, -1, 0x03, 0x0042, 0x0009}, + cmdLutElement{0x05, 0x00, -1, 0x00, 0x0062, 0x0002}, + cmdLutElement{0x05, 0x00, -1, 0x01, 0x0062, 0x0003}, + cmdLutElement{0x05, 0x00, -1, 0x02, 0x0062, 0x0004}, + cmdLutElement{0x05, 0x00, -1, 0x03, 0x0062, 0x0005}, + cmdLutElement{0x05, 0x00, -1, 0x03, 0x0062, 0x0006}, + cmdLutElement{0x05, 0x00, -1, 0x03, 0x0062, 0x0007}, + cmdLutElement{0x05, 0x00, -1, 0x03, 0x0062, 0x0008}, + cmdLutElement{0x05, 0x00, -1, 0x03, 0x0062, 0x0009}, + cmdLutElement{0x02, 0x01, -1, 0x03, 0x000a, 0x000a}, + cmdLutElement{0x02, 0x01, -1, 0x03, 0x000a, 0x000c}, + cmdLutElement{0x02, 0x02, -1, 0x03, 0x000a, 0x000e}, + cmdLutElement{0x02, 0x02, -1, 0x03, 0x000a, 0x0012}, + cmdLutElement{0x02, 0x03, -1, 0x03, 0x000a, 0x0016}, + cmdLutElement{0x02, 0x03, -1, 0x03, 0x000a, 0x001e}, + cmdLutElement{0x02, 0x04, -1, 0x03, 0x000a, 0x0026}, + cmdLutElement{0x02, 0x04, -1, 0x03, 0x000a, 0x0036}, + cmdLutElement{0x02, 0x01, -1, 0x03, 0x000e, 0x000a}, + cmdLutElement{0x02, 0x01, -1, 0x03, 0x000e, 0x000c}, + cmdLutElement{0x02, 0x02, -1, 0x03, 0x000e, 0x000e}, + cmdLutElement{0x02, 0x02, -1, 0x03, 0x000e, 0x0012}, + cmdLutElement{0x02, 0x03, -1, 0x03, 0x000e, 0x0016}, + cmdLutElement{0x02, 0x03, -1, 0x03, 0x000e, 0x001e}, + cmdLutElement{0x02, 0x04, -1, 0x03, 0x000e, 0x0026}, + cmdLutElement{0x02, 0x04, -1, 0x03, 0x000e, 0x0036}, + cmdLutElement{0x03, 0x01, -1, 0x03, 0x0012, 0x000a}, + cmdLutElement{0x03, 0x01, -1, 0x03, 0x0012, 0x000c}, + cmdLutElement{0x03, 0x02, -1, 0x03, 0x0012, 0x000e}, + cmdLutElement{0x03, 0x02, -1, 0x03, 0x0012, 0x0012}, + cmdLutElement{0x03, 0x03, -1, 0x03, 0x0012, 0x0016}, + cmdLutElement{0x03, 0x03, -1, 0x03, 0x0012, 0x001e}, + cmdLutElement{0x03, 0x04, -1, 0x03, 0x0012, 0x0026}, + cmdLutElement{0x03, 0x04, -1, 0x03, 0x0012, 0x0036}, + cmdLutElement{0x03, 0x01, -1, 0x03, 0x001a, 0x000a}, + cmdLutElement{0x03, 0x01, -1, 0x03, 0x001a, 0x000c}, + cmdLutElement{0x03, 0x02, -1, 0x03, 0x001a, 0x000e}, + cmdLutElement{0x03, 0x02, -1, 0x03, 0x001a, 0x0012}, + cmdLutElement{0x03, 0x03, -1, 0x03, 0x001a, 0x0016}, + cmdLutElement{0x03, 0x03, -1, 0x03, 0x001a, 0x001e}, + cmdLutElement{0x03, 0x04, -1, 0x03, 0x001a, 0x0026}, + cmdLutElement{0x03, 0x04, -1, 0x03, 0x001a, 0x0036}, + cmdLutElement{0x04, 0x01, -1, 0x03, 0x0022, 0x000a}, + cmdLutElement{0x04, 0x01, -1, 0x03, 0x0022, 0x000c}, + cmdLutElement{0x04, 0x02, -1, 0x03, 0x0022, 0x000e}, + cmdLutElement{0x04, 0x02, -1, 0x03, 0x0022, 0x0012}, + cmdLutElement{0x04, 0x03, -1, 0x03, 0x0022, 0x0016}, + cmdLutElement{0x04, 0x03, -1, 0x03, 0x0022, 0x001e}, + cmdLutElement{0x04, 0x04, -1, 0x03, 0x0022, 0x0026}, + cmdLutElement{0x04, 0x04, -1, 0x03, 0x0022, 0x0036}, + cmdLutElement{0x04, 0x01, -1, 0x03, 0x0032, 0x000a}, + cmdLutElement{0x04, 0x01, -1, 0x03, 0x0032, 0x000c}, + cmdLutElement{0x04, 0x02, -1, 0x03, 0x0032, 0x000e}, + cmdLutElement{0x04, 0x02, -1, 0x03, 0x0032, 0x0012}, + cmdLutElement{0x04, 0x03, -1, 0x03, 0x0032, 0x0016}, + cmdLutElement{0x04, 0x03, -1, 0x03, 0x0032, 0x001e}, + cmdLutElement{0x04, 0x04, -1, 0x03, 0x0032, 0x0026}, + cmdLutElement{0x04, 0x04, -1, 0x03, 0x0032, 0x0036}, + cmdLutElement{0x05, 0x01, -1, 0x03, 0x0042, 0x000a}, + cmdLutElement{0x05, 0x01, -1, 0x03, 0x0042, 0x000c}, + cmdLutElement{0x05, 0x02, -1, 0x03, 0x0042, 0x000e}, + cmdLutElement{0x05, 0x02, -1, 0x03, 0x0042, 0x0012}, + cmdLutElement{0x05, 0x03, -1, 0x03, 0x0042, 0x0016}, + cmdLutElement{0x05, 0x03, -1, 0x03, 0x0042, 0x001e}, + cmdLutElement{0x05, 0x04, -1, 0x03, 0x0042, 0x0026}, + cmdLutElement{0x05, 0x04, -1, 0x03, 0x0042, 0x0036}, + cmdLutElement{0x05, 0x01, -1, 0x03, 0x0062, 0x000a}, + cmdLutElement{0x05, 0x01, -1, 0x03, 0x0062, 0x000c}, + cmdLutElement{0x05, 0x02, -1, 0x03, 0x0062, 0x000e}, + cmdLutElement{0x05, 0x02, -1, 0x03, 0x0062, 0x0012}, + cmdLutElement{0x05, 0x03, -1, 0x03, 0x0062, 0x0016}, + cmdLutElement{0x05, 0x03, -1, 0x03, 0x0062, 0x001e}, + cmdLutElement{0x05, 0x04, -1, 0x03, 0x0062, 0x0026}, + cmdLutElement{0x05, 0x04, -1, 0x03, 0x0062, 0x0036}, + cmdLutElement{0x00, 0x05, -1, 0x03, 0x0000, 0x0046}, + cmdLutElement{0x00, 0x05, -1, 0x03, 0x0000, 0x0066}, + cmdLutElement{0x00, 0x06, -1, 0x03, 0x0000, 0x0086}, + cmdLutElement{0x00, 0x07, -1, 0x03, 0x0000, 0x00c6}, + cmdLutElement{0x00, 0x08, -1, 0x03, 0x0000, 0x0146}, + cmdLutElement{0x00, 0x09, -1, 0x03, 0x0000, 0x0246}, + cmdLutElement{0x00, 0x0a, -1, 0x03, 0x0000, 0x0446}, + cmdLutElement{0x00, 0x18, -1, 0x03, 0x0000, 0x0846}, + cmdLutElement{0x00, 0x05, -1, 0x03, 0x0001, 0x0046}, + cmdLutElement{0x00, 0x05, -1, 0x03, 0x0001, 0x0066}, + cmdLutElement{0x00, 0x06, -1, 0x03, 0x0001, 0x0086}, + cmdLutElement{0x00, 0x07, -1, 0x03, 0x0001, 0x00c6}, + cmdLutElement{0x00, 0x08, -1, 0x03, 0x0001, 0x0146}, + cmdLutElement{0x00, 0x09, -1, 0x03, 0x0001, 0x0246}, + cmdLutElement{0x00, 0x0a, -1, 0x03, 0x0001, 0x0446}, + cmdLutElement{0x00, 0x18, -1, 0x03, 0x0001, 0x0846}, + cmdLutElement{0x00, 0x05, -1, 0x03, 0x0002, 0x0046}, + cmdLutElement{0x00, 0x05, -1, 0x03, 0x0002, 0x0066}, + cmdLutElement{0x00, 0x06, -1, 0x03, 0x0002, 0x0086}, + cmdLutElement{0x00, 0x07, -1, 0x03, 0x0002, 0x00c6}, + cmdLutElement{0x00, 0x08, -1, 0x03, 0x0002, 0x0146}, + cmdLutElement{0x00, 0x09, -1, 0x03, 0x0002, 0x0246}, + cmdLutElement{0x00, 0x0a, -1, 0x03, 0x0002, 0x0446}, + cmdLutElement{0x00, 0x18, -1, 0x03, 0x0002, 0x0846}, + cmdLutElement{0x00, 0x05, -1, 0x03, 0x0003, 0x0046}, + cmdLutElement{0x00, 0x05, -1, 0x03, 0x0003, 0x0066}, + cmdLutElement{0x00, 0x06, -1, 0x03, 0x0003, 0x0086}, + cmdLutElement{0x00, 0x07, -1, 0x03, 0x0003, 0x00c6}, + cmdLutElement{0x00, 0x08, -1, 0x03, 0x0003, 0x0146}, + cmdLutElement{0x00, 0x09, -1, 0x03, 0x0003, 0x0246}, + cmdLutElement{0x00, 0x0a, -1, 0x03, 0x0003, 0x0446}, + cmdLutElement{0x00, 0x18, -1, 0x03, 0x0003, 0x0846}, + cmdLutElement{0x00, 0x05, -1, 0x03, 0x0004, 0x0046}, + cmdLutElement{0x00, 0x05, -1, 0x03, 0x0004, 0x0066}, + cmdLutElement{0x00, 0x06, -1, 0x03, 0x0004, 0x0086}, + cmdLutElement{0x00, 0x07, -1, 0x03, 0x0004, 0x00c6}, + cmdLutElement{0x00, 0x08, -1, 0x03, 0x0004, 0x0146}, + cmdLutElement{0x00, 0x09, -1, 0x03, 0x0004, 0x0246}, + cmdLutElement{0x00, 0x0a, -1, 0x03, 0x0004, 0x0446}, + cmdLutElement{0x00, 0x18, -1, 0x03, 0x0004, 0x0846}, + cmdLutElement{0x00, 0x05, -1, 0x03, 0x0005, 0x0046}, + cmdLutElement{0x00, 0x05, -1, 0x03, 0x0005, 0x0066}, + cmdLutElement{0x00, 0x06, -1, 0x03, 0x0005, 0x0086}, + cmdLutElement{0x00, 0x07, -1, 0x03, 0x0005, 0x00c6}, + cmdLutElement{0x00, 0x08, -1, 0x03, 0x0005, 0x0146}, + cmdLutElement{0x00, 0x09, -1, 0x03, 0x0005, 0x0246}, + cmdLutElement{0x00, 0x0a, -1, 0x03, 0x0005, 0x0446}, + cmdLutElement{0x00, 0x18, -1, 0x03, 0x0005, 0x0846}, + cmdLutElement{0x01, 0x05, -1, 0x03, 0x0006, 0x0046}, + cmdLutElement{0x01, 0x05, -1, 0x03, 0x0006, 0x0066}, + cmdLutElement{0x01, 0x06, -1, 0x03, 0x0006, 0x0086}, + cmdLutElement{0x01, 0x07, -1, 0x03, 0x0006, 0x00c6}, + cmdLutElement{0x01, 0x08, -1, 0x03, 0x0006, 0x0146}, + cmdLutElement{0x01, 0x09, -1, 0x03, 0x0006, 0x0246}, + cmdLutElement{0x01, 0x0a, -1, 0x03, 0x0006, 0x0446}, + cmdLutElement{0x01, 0x18, -1, 0x03, 0x0006, 0x0846}, + cmdLutElement{0x01, 0x05, -1, 0x03, 0x0008, 0x0046}, + cmdLutElement{0x01, 0x05, -1, 0x03, 0x0008, 0x0066}, + cmdLutElement{0x01, 0x06, -1, 0x03, 0x0008, 0x0086}, + cmdLutElement{0x01, 0x07, -1, 0x03, 0x0008, 0x00c6}, + cmdLutElement{0x01, 0x08, -1, 0x03, 0x0008, 0x0146}, + cmdLutElement{0x01, 0x09, -1, 0x03, 0x0008, 0x0246}, + cmdLutElement{0x01, 0x0a, -1, 0x03, 0x0008, 0x0446}, + cmdLutElement{0x01, 0x18, -1, 0x03, 0x0008, 0x0846}, + cmdLutElement{0x06, 0x00, -1, 0x00, 0x0082, 0x0002}, + cmdLutElement{0x06, 0x00, -1, 0x01, 0x0082, 0x0003}, + cmdLutElement{0x06, 0x00, -1, 0x02, 0x0082, 0x0004}, + cmdLutElement{0x06, 0x00, -1, 0x03, 0x0082, 0x0005}, + cmdLutElement{0x06, 0x00, -1, 0x03, 0x0082, 0x0006}, + cmdLutElement{0x06, 0x00, -1, 0x03, 0x0082, 0x0007}, + cmdLutElement{0x06, 0x00, -1, 0x03, 0x0082, 0x0008}, + cmdLutElement{0x06, 0x00, -1, 0x03, 0x0082, 0x0009}, + cmdLutElement{0x07, 0x00, -1, 0x00, 0x00c2, 0x0002}, + cmdLutElement{0x07, 0x00, -1, 0x01, 0x00c2, 0x0003}, + cmdLutElement{0x07, 0x00, -1, 0x02, 0x00c2, 0x0004}, + cmdLutElement{0x07, 0x00, -1, 0x03, 0x00c2, 0x0005}, + cmdLutElement{0x07, 0x00, -1, 0x03, 0x00c2, 0x0006}, + cmdLutElement{0x07, 0x00, -1, 0x03, 0x00c2, 0x0007}, + cmdLutElement{0x07, 0x00, -1, 0x03, 0x00c2, 0x0008}, + cmdLutElement{0x07, 0x00, -1, 0x03, 0x00c2, 0x0009}, + cmdLutElement{0x08, 0x00, -1, 0x00, 0x0142, 0x0002}, + cmdLutElement{0x08, 0x00, -1, 0x01, 0x0142, 0x0003}, + cmdLutElement{0x08, 0x00, -1, 0x02, 0x0142, 0x0004}, + cmdLutElement{0x08, 0x00, -1, 0x03, 0x0142, 0x0005}, + cmdLutElement{0x08, 0x00, -1, 0x03, 0x0142, 0x0006}, + cmdLutElement{0x08, 0x00, -1, 0x03, 0x0142, 0x0007}, + cmdLutElement{0x08, 0x00, -1, 0x03, 0x0142, 0x0008}, + cmdLutElement{0x08, 0x00, -1, 0x03, 0x0142, 0x0009}, + cmdLutElement{0x09, 0x00, -1, 0x00, 0x0242, 0x0002}, + cmdLutElement{0x09, 0x00, -1, 0x01, 0x0242, 0x0003}, + cmdLutElement{0x09, 0x00, -1, 0x02, 0x0242, 0x0004}, + cmdLutElement{0x09, 0x00, -1, 0x03, 0x0242, 0x0005}, + cmdLutElement{0x09, 0x00, -1, 0x03, 0x0242, 0x0006}, + cmdLutElement{0x09, 0x00, -1, 0x03, 0x0242, 0x0007}, + cmdLutElement{0x09, 0x00, -1, 0x03, 0x0242, 0x0008}, + cmdLutElement{0x09, 0x00, -1, 0x03, 0x0242, 0x0009}, + cmdLutElement{0x0a, 0x00, -1, 0x00, 0x0442, 0x0002}, + cmdLutElement{0x0a, 0x00, -1, 0x01, 0x0442, 0x0003}, + cmdLutElement{0x0a, 0x00, -1, 0x02, 0x0442, 0x0004}, + cmdLutElement{0x0a, 0x00, -1, 0x03, 0x0442, 0x0005}, + cmdLutElement{0x0a, 0x00, -1, 0x03, 0x0442, 0x0006}, + cmdLutElement{0x0a, 0x00, -1, 0x03, 0x0442, 0x0007}, + cmdLutElement{0x0a, 0x00, -1, 0x03, 0x0442, 0x0008}, + cmdLutElement{0x0a, 0x00, -1, 0x03, 0x0442, 0x0009}, + cmdLutElement{0x0c, 0x00, -1, 0x00, 0x0842, 0x0002}, + cmdLutElement{0x0c, 0x00, -1, 0x01, 0x0842, 0x0003}, + cmdLutElement{0x0c, 0x00, -1, 0x02, 0x0842, 0x0004}, + cmdLutElement{0x0c, 0x00, -1, 0x03, 0x0842, 0x0005}, + cmdLutElement{0x0c, 0x00, -1, 0x03, 0x0842, 0x0006}, + cmdLutElement{0x0c, 0x00, -1, 0x03, 0x0842, 0x0007}, + cmdLutElement{0x0c, 0x00, -1, 0x03, 0x0842, 0x0008}, + cmdLutElement{0x0c, 0x00, -1, 0x03, 0x0842, 0x0009}, + cmdLutElement{0x0e, 0x00, -1, 0x00, 0x1842, 0x0002}, + cmdLutElement{0x0e, 0x00, -1, 0x01, 0x1842, 0x0003}, + cmdLutElement{0x0e, 0x00, -1, 0x02, 0x1842, 0x0004}, + cmdLutElement{0x0e, 0x00, -1, 0x03, 0x1842, 0x0005}, + cmdLutElement{0x0e, 0x00, -1, 0x03, 0x1842, 0x0006}, + cmdLutElement{0x0e, 0x00, -1, 0x03, 0x1842, 0x0007}, + cmdLutElement{0x0e, 0x00, -1, 0x03, 0x1842, 0x0008}, + cmdLutElement{0x0e, 0x00, -1, 0x03, 0x1842, 0x0009}, + cmdLutElement{0x18, 0x00, -1, 0x00, 0x5842, 0x0002}, + cmdLutElement{0x18, 0x00, -1, 0x01, 0x5842, 0x0003}, + cmdLutElement{0x18, 0x00, -1, 0x02, 0x5842, 0x0004}, + cmdLutElement{0x18, 0x00, -1, 0x03, 0x5842, 0x0005}, + cmdLutElement{0x18, 0x00, -1, 0x03, 0x5842, 0x0006}, + cmdLutElement{0x18, 0x00, -1, 0x03, 0x5842, 0x0007}, + cmdLutElement{0x18, 0x00, -1, 0x03, 0x5842, 0x0008}, + cmdLutElement{0x18, 0x00, -1, 0x03, 0x5842, 0x0009}, + cmdLutElement{0x02, 0x05, -1, 0x03, 0x000a, 0x0046}, + cmdLutElement{0x02, 0x05, -1, 0x03, 0x000a, 0x0066}, + cmdLutElement{0x02, 0x06, -1, 0x03, 0x000a, 0x0086}, + cmdLutElement{0x02, 0x07, -1, 0x03, 0x000a, 0x00c6}, + cmdLutElement{0x02, 0x08, -1, 0x03, 0x000a, 0x0146}, + cmdLutElement{0x02, 0x09, -1, 0x03, 0x000a, 0x0246}, + cmdLutElement{0x02, 0x0a, -1, 0x03, 0x000a, 0x0446}, + cmdLutElement{0x02, 0x18, -1, 0x03, 0x000a, 0x0846}, + cmdLutElement{0x02, 0x05, -1, 0x03, 0x000e, 0x0046}, + cmdLutElement{0x02, 0x05, -1, 0x03, 0x000e, 0x0066}, + cmdLutElement{0x02, 0x06, -1, 0x03, 0x000e, 0x0086}, + cmdLutElement{0x02, 0x07, -1, 0x03, 0x000e, 0x00c6}, + cmdLutElement{0x02, 0x08, -1, 0x03, 0x000e, 0x0146}, + cmdLutElement{0x02, 0x09, -1, 0x03, 0x000e, 0x0246}, + cmdLutElement{0x02, 0x0a, -1, 0x03, 0x000e, 0x0446}, + cmdLutElement{0x02, 0x18, -1, 0x03, 0x000e, 0x0846}, + cmdLutElement{0x03, 0x05, -1, 0x03, 0x0012, 0x0046}, + cmdLutElement{0x03, 0x05, -1, 0x03, 0x0012, 0x0066}, + cmdLutElement{0x03, 0x06, -1, 0x03, 0x0012, 0x0086}, + cmdLutElement{0x03, 0x07, -1, 0x03, 0x0012, 0x00c6}, + cmdLutElement{0x03, 0x08, -1, 0x03, 0x0012, 0x0146}, + cmdLutElement{0x03, 0x09, -1, 0x03, 0x0012, 0x0246}, + cmdLutElement{0x03, 0x0a, -1, 0x03, 0x0012, 0x0446}, + cmdLutElement{0x03, 0x18, -1, 0x03, 0x0012, 0x0846}, + cmdLutElement{0x03, 0x05, -1, 0x03, 0x001a, 0x0046}, + cmdLutElement{0x03, 0x05, -1, 0x03, 0x001a, 0x0066}, + cmdLutElement{0x03, 0x06, -1, 0x03, 0x001a, 0x0086}, + cmdLutElement{0x03, 0x07, -1, 0x03, 0x001a, 0x00c6}, + cmdLutElement{0x03, 0x08, -1, 0x03, 0x001a, 0x0146}, + cmdLutElement{0x03, 0x09, -1, 0x03, 0x001a, 0x0246}, + cmdLutElement{0x03, 0x0a, -1, 0x03, 0x001a, 0x0446}, + cmdLutElement{0x03, 0x18, -1, 0x03, 0x001a, 0x0846}, + cmdLutElement{0x04, 0x05, -1, 0x03, 0x0022, 0x0046}, + cmdLutElement{0x04, 0x05, -1, 0x03, 0x0022, 0x0066}, + cmdLutElement{0x04, 0x06, -1, 0x03, 0x0022, 0x0086}, + cmdLutElement{0x04, 0x07, -1, 0x03, 0x0022, 0x00c6}, + cmdLutElement{0x04, 0x08, -1, 0x03, 0x0022, 0x0146}, + cmdLutElement{0x04, 0x09, -1, 0x03, 0x0022, 0x0246}, + cmdLutElement{0x04, 0x0a, -1, 0x03, 0x0022, 0x0446}, + cmdLutElement{0x04, 0x18, -1, 0x03, 0x0022, 0x0846}, + cmdLutElement{0x04, 0x05, -1, 0x03, 0x0032, 0x0046}, + cmdLutElement{0x04, 0x05, -1, 0x03, 0x0032, 0x0066}, + cmdLutElement{0x04, 0x06, -1, 0x03, 0x0032, 0x0086}, + cmdLutElement{0x04, 0x07, -1, 0x03, 0x0032, 0x00c6}, + cmdLutElement{0x04, 0x08, -1, 0x03, 0x0032, 0x0146}, + cmdLutElement{0x04, 0x09, -1, 0x03, 0x0032, 0x0246}, + cmdLutElement{0x04, 0x0a, -1, 0x03, 0x0032, 0x0446}, + cmdLutElement{0x04, 0x18, -1, 0x03, 0x0032, 0x0846}, + cmdLutElement{0x05, 0x05, -1, 0x03, 0x0042, 0x0046}, + cmdLutElement{0x05, 0x05, -1, 0x03, 0x0042, 0x0066}, + cmdLutElement{0x05, 0x06, -1, 0x03, 0x0042, 0x0086}, + cmdLutElement{0x05, 0x07, -1, 0x03, 0x0042, 0x00c6}, + cmdLutElement{0x05, 0x08, -1, 0x03, 0x0042, 0x0146}, + cmdLutElement{0x05, 0x09, -1, 0x03, 0x0042, 0x0246}, + cmdLutElement{0x05, 0x0a, -1, 0x03, 0x0042, 0x0446}, + cmdLutElement{0x05, 0x18, -1, 0x03, 0x0042, 0x0846}, + cmdLutElement{0x05, 0x05, -1, 0x03, 0x0062, 0x0046}, + cmdLutElement{0x05, 0x05, -1, 0x03, 0x0062, 0x0066}, + cmdLutElement{0x05, 0x06, -1, 0x03, 0x0062, 0x0086}, + cmdLutElement{0x05, 0x07, -1, 0x03, 0x0062, 0x00c6}, + cmdLutElement{0x05, 0x08, -1, 0x03, 0x0062, 0x0146}, + cmdLutElement{0x05, 0x09, -1, 0x03, 0x0062, 0x0246}, + cmdLutElement{0x05, 0x0a, -1, 0x03, 0x0062, 0x0446}, + cmdLutElement{0x05, 0x18, -1, 0x03, 0x0062, 0x0846}, + cmdLutElement{0x06, 0x01, -1, 0x03, 0x0082, 0x000a}, + cmdLutElement{0x06, 0x01, -1, 0x03, 0x0082, 0x000c}, + cmdLutElement{0x06, 0x02, -1, 0x03, 0x0082, 0x000e}, + cmdLutElement{0x06, 0x02, -1, 0x03, 0x0082, 0x0012}, + cmdLutElement{0x06, 0x03, -1, 0x03, 0x0082, 0x0016}, + cmdLutElement{0x06, 0x03, -1, 0x03, 0x0082, 0x001e}, + cmdLutElement{0x06, 0x04, -1, 0x03, 0x0082, 0x0026}, + cmdLutElement{0x06, 0x04, -1, 0x03, 0x0082, 0x0036}, + cmdLutElement{0x07, 0x01, -1, 0x03, 0x00c2, 0x000a}, + cmdLutElement{0x07, 0x01, -1, 0x03, 0x00c2, 0x000c}, + cmdLutElement{0x07, 0x02, -1, 0x03, 0x00c2, 0x000e}, + cmdLutElement{0x07, 0x02, -1, 0x03, 0x00c2, 0x0012}, + cmdLutElement{0x07, 0x03, -1, 0x03, 0x00c2, 0x0016}, + cmdLutElement{0x07, 0x03, -1, 0x03, 0x00c2, 0x001e}, + cmdLutElement{0x07, 0x04, -1, 0x03, 0x00c2, 0x0026}, + cmdLutElement{0x07, 0x04, -1, 0x03, 0x00c2, 0x0036}, + cmdLutElement{0x08, 0x01, -1, 0x03, 0x0142, 0x000a}, + cmdLutElement{0x08, 0x01, -1, 0x03, 0x0142, 0x000c}, + cmdLutElement{0x08, 0x02, -1, 0x03, 0x0142, 0x000e}, + cmdLutElement{0x08, 0x02, -1, 0x03, 0x0142, 0x0012}, + cmdLutElement{0x08, 0x03, -1, 0x03, 0x0142, 0x0016}, + cmdLutElement{0x08, 0x03, -1, 0x03, 0x0142, 0x001e}, + cmdLutElement{0x08, 0x04, -1, 0x03, 0x0142, 0x0026}, + cmdLutElement{0x08, 0x04, -1, 0x03, 0x0142, 0x0036}, + cmdLutElement{0x09, 0x01, -1, 0x03, 0x0242, 0x000a}, + cmdLutElement{0x09, 0x01, -1, 0x03, 0x0242, 0x000c}, + cmdLutElement{0x09, 0x02, -1, 0x03, 0x0242, 0x000e}, + cmdLutElement{0x09, 0x02, -1, 0x03, 0x0242, 0x0012}, + cmdLutElement{0x09, 0x03, -1, 0x03, 0x0242, 0x0016}, + cmdLutElement{0x09, 0x03, -1, 0x03, 0x0242, 0x001e}, + cmdLutElement{0x09, 0x04, -1, 0x03, 0x0242, 0x0026}, + cmdLutElement{0x09, 0x04, -1, 0x03, 0x0242, 0x0036}, + cmdLutElement{0x0a, 0x01, -1, 0x03, 0x0442, 0x000a}, + cmdLutElement{0x0a, 0x01, -1, 0x03, 0x0442, 0x000c}, + cmdLutElement{0x0a, 0x02, -1, 0x03, 0x0442, 0x000e}, + cmdLutElement{0x0a, 0x02, -1, 0x03, 0x0442, 0x0012}, + cmdLutElement{0x0a, 0x03, -1, 0x03, 0x0442, 0x0016}, + cmdLutElement{0x0a, 0x03, -1, 0x03, 0x0442, 0x001e}, + cmdLutElement{0x0a, 0x04, -1, 0x03, 0x0442, 0x0026}, + cmdLutElement{0x0a, 0x04, -1, 0x03, 0x0442, 0x0036}, + cmdLutElement{0x0c, 0x01, -1, 0x03, 0x0842, 0x000a}, + cmdLutElement{0x0c, 0x01, -1, 0x03, 0x0842, 0x000c}, + cmdLutElement{0x0c, 0x02, -1, 0x03, 0x0842, 0x000e}, + cmdLutElement{0x0c, 0x02, -1, 0x03, 0x0842, 0x0012}, + cmdLutElement{0x0c, 0x03, -1, 0x03, 0x0842, 0x0016}, + cmdLutElement{0x0c, 0x03, -1, 0x03, 0x0842, 0x001e}, + cmdLutElement{0x0c, 0x04, -1, 0x03, 0x0842, 0x0026}, + cmdLutElement{0x0c, 0x04, -1, 0x03, 0x0842, 0x0036}, + cmdLutElement{0x0e, 0x01, -1, 0x03, 0x1842, 0x000a}, + cmdLutElement{0x0e, 0x01, -1, 0x03, 0x1842, 0x000c}, + cmdLutElement{0x0e, 0x02, -1, 0x03, 0x1842, 0x000e}, + cmdLutElement{0x0e, 0x02, -1, 0x03, 0x1842, 0x0012}, + cmdLutElement{0x0e, 0x03, -1, 0x03, 0x1842, 0x0016}, + cmdLutElement{0x0e, 0x03, -1, 0x03, 0x1842, 0x001e}, + cmdLutElement{0x0e, 0x04, -1, 0x03, 0x1842, 0x0026}, + cmdLutElement{0x0e, 0x04, -1, 0x03, 0x1842, 0x0036}, + cmdLutElement{0x18, 0x01, -1, 0x03, 0x5842, 0x000a}, + cmdLutElement{0x18, 0x01, -1, 0x03, 0x5842, 0x000c}, + cmdLutElement{0x18, 0x02, -1, 0x03, 0x5842, 0x000e}, + cmdLutElement{0x18, 0x02, -1, 0x03, 0x5842, 0x0012}, + cmdLutElement{0x18, 0x03, -1, 0x03, 0x5842, 0x0016}, + cmdLutElement{0x18, 0x03, -1, 0x03, 0x5842, 0x001e}, + cmdLutElement{0x18, 0x04, -1, 0x03, 0x5842, 0x0026}, + cmdLutElement{0x18, 0x04, -1, 0x03, 0x5842, 0x0036}, + cmdLutElement{0x06, 0x05, -1, 0x03, 0x0082, 0x0046}, + cmdLutElement{0x06, 0x05, -1, 0x03, 0x0082, 0x0066}, + cmdLutElement{0x06, 0x06, -1, 0x03, 0x0082, 0x0086}, + cmdLutElement{0x06, 0x07, -1, 0x03, 0x0082, 0x00c6}, + cmdLutElement{0x06, 0x08, -1, 0x03, 0x0082, 0x0146}, + cmdLutElement{0x06, 0x09, -1, 0x03, 0x0082, 0x0246}, + cmdLutElement{0x06, 0x0a, -1, 0x03, 0x0082, 0x0446}, + cmdLutElement{0x06, 0x18, -1, 0x03, 0x0082, 0x0846}, + cmdLutElement{0x07, 0x05, -1, 0x03, 0x00c2, 0x0046}, + cmdLutElement{0x07, 0x05, -1, 0x03, 0x00c2, 0x0066}, + cmdLutElement{0x07, 0x06, -1, 0x03, 0x00c2, 0x0086}, + cmdLutElement{0x07, 0x07, -1, 0x03, 0x00c2, 0x00c6}, + cmdLutElement{0x07, 0x08, -1, 0x03, 0x00c2, 0x0146}, + cmdLutElement{0x07, 0x09, -1, 0x03, 0x00c2, 0x0246}, + cmdLutElement{0x07, 0x0a, -1, 0x03, 0x00c2, 0x0446}, + cmdLutElement{0x07, 0x18, -1, 0x03, 0x00c2, 0x0846}, + cmdLutElement{0x08, 0x05, -1, 0x03, 0x0142, 0x0046}, + cmdLutElement{0x08, 0x05, -1, 0x03, 0x0142, 0x0066}, + cmdLutElement{0x08, 0x06, -1, 0x03, 0x0142, 0x0086}, + cmdLutElement{0x08, 0x07, -1, 0x03, 0x0142, 0x00c6}, + cmdLutElement{0x08, 0x08, -1, 0x03, 0x0142, 0x0146}, + cmdLutElement{0x08, 0x09, -1, 0x03, 0x0142, 0x0246}, + cmdLutElement{0x08, 0x0a, -1, 0x03, 0x0142, 0x0446}, + cmdLutElement{0x08, 0x18, -1, 0x03, 0x0142, 0x0846}, + cmdLutElement{0x09, 0x05, -1, 0x03, 0x0242, 0x0046}, + cmdLutElement{0x09, 0x05, -1, 0x03, 0x0242, 0x0066}, + cmdLutElement{0x09, 0x06, -1, 0x03, 0x0242, 0x0086}, + cmdLutElement{0x09, 0x07, -1, 0x03, 0x0242, 0x00c6}, + cmdLutElement{0x09, 0x08, -1, 0x03, 0x0242, 0x0146}, + cmdLutElement{0x09, 0x09, -1, 0x03, 0x0242, 0x0246}, + cmdLutElement{0x09, 0x0a, -1, 0x03, 0x0242, 0x0446}, + cmdLutElement{0x09, 0x18, -1, 0x03, 0x0242, 0x0846}, + cmdLutElement{0x0a, 0x05, -1, 0x03, 0x0442, 0x0046}, + cmdLutElement{0x0a, 0x05, -1, 0x03, 0x0442, 0x0066}, + cmdLutElement{0x0a, 0x06, -1, 0x03, 0x0442, 0x0086}, + cmdLutElement{0x0a, 0x07, -1, 0x03, 0x0442, 0x00c6}, + cmdLutElement{0x0a, 0x08, -1, 0x03, 0x0442, 0x0146}, + cmdLutElement{0x0a, 0x09, -1, 0x03, 0x0442, 0x0246}, + cmdLutElement{0x0a, 0x0a, -1, 0x03, 0x0442, 0x0446}, + cmdLutElement{0x0a, 0x18, -1, 0x03, 0x0442, 0x0846}, + cmdLutElement{0x0c, 0x05, -1, 0x03, 0x0842, 0x0046}, + cmdLutElement{0x0c, 0x05, -1, 0x03, 0x0842, 0x0066}, + cmdLutElement{0x0c, 0x06, -1, 0x03, 0x0842, 0x0086}, + cmdLutElement{0x0c, 0x07, -1, 0x03, 0x0842, 0x00c6}, + cmdLutElement{0x0c, 0x08, -1, 0x03, 0x0842, 0x0146}, + cmdLutElement{0x0c, 0x09, -1, 0x03, 0x0842, 0x0246}, + cmdLutElement{0x0c, 0x0a, -1, 0x03, 0x0842, 0x0446}, + cmdLutElement{0x0c, 0x18, -1, 0x03, 0x0842, 0x0846}, + cmdLutElement{0x0e, 0x05, -1, 0x03, 0x1842, 0x0046}, + cmdLutElement{0x0e, 0x05, -1, 0x03, 0x1842, 0x0066}, + cmdLutElement{0x0e, 0x06, -1, 0x03, 0x1842, 0x0086}, + cmdLutElement{0x0e, 0x07, -1, 0x03, 0x1842, 0x00c6}, + cmdLutElement{0x0e, 0x08, -1, 0x03, 0x1842, 0x0146}, + cmdLutElement{0x0e, 0x09, -1, 0x03, 0x1842, 0x0246}, + cmdLutElement{0x0e, 0x0a, -1, 0x03, 0x1842, 0x0446}, + cmdLutElement{0x0e, 0x18, -1, 0x03, 0x1842, 0x0846}, + cmdLutElement{0x18, 0x05, -1, 0x03, 0x5842, 0x0046}, + cmdLutElement{0x18, 0x05, -1, 0x03, 0x5842, 0x0066}, + cmdLutElement{0x18, 0x06, -1, 0x03, 0x5842, 0x0086}, + cmdLutElement{0x18, 0x07, -1, 0x03, 0x5842, 0x00c6}, + cmdLutElement{0x18, 0x08, -1, 0x03, 0x5842, 0x0146}, + cmdLutElement{0x18, 0x09, -1, 0x03, 0x5842, 0x0246}, + cmdLutElement{0x18, 0x0a, -1, 0x03, 0x5842, 0x0446}, + cmdLutElement{0x18, 0x18, -1, 0x03, 0x5842, 0x0846}, +} diff --git a/vendor/github.com/andybalholm/brotli/quality.go b/vendor/github.com/andybalholm/brotli/quality.go new file mode 100644 index 000000000..49709a382 --- /dev/null +++ b/vendor/github.com/andybalholm/brotli/quality.go @@ -0,0 +1,196 @@ +package brotli + +const fastOnePassCompressionQuality = 0 + +const fastTwoPassCompressionQuality = 1 + +const zopflificationQuality = 10 + +const hqZopflificationQuality = 11 + +const maxQualityForStaticEntropyCodes = 2 + +const minQualityForBlockSplit = 4 + +const minQualityForNonzeroDistanceParams = 4 + +const minQualityForOptimizeHistograms = 4 + +const minQualityForExtensiveReferenceSearch = 5 + +const minQualityForContextModeling = 5 + +const minQualityForHqContextModeling = 7 + +const minQualityForHqBlockSplitting = 10 + +/* For quality below MIN_QUALITY_FOR_BLOCK_SPLIT there is no block splitting, + so we buffer at most this much literals and commands. */ +const maxNumDelayedSymbols = 0x2FFF + +/* Returns hash-table size for quality levels 0 and 1. */ +func maxHashTableSize(quality int) uint { + if quality == fastOnePassCompressionQuality { + return 1 << 15 + } else { + return 1 << 17 + } +} + +/* The maximum length for which the zopflification uses distinct distances. */ +const maxZopfliLenQuality10 = 150 + +const maxZopfliLenQuality11 = 325 + +/* Do not thoroughly search when a long copy is found. */ +const longCopyQuickStep = 16384 + +func maxZopfliLen(params *encoderParams) uint { + if params.quality <= 10 { + return maxZopfliLenQuality10 + } else { + return maxZopfliLenQuality11 + } +} + +/* Number of best candidates to evaluate to expand Zopfli chain. */ +func maxZopfliCandidates(params *encoderParams) uint { + if params.quality <= 10 { + return 1 + } else { + return 5 + } +} + +func sanitizeParams(params *encoderParams) { + params.quality = brotli_min_int(maxQuality, brotli_max_int(minQuality, params.quality)) + if params.quality <= maxQualityForStaticEntropyCodes { + params.large_window = false + } + + if params.lgwin < minWindowBits { + params.lgwin = minWindowBits + } else { + var max_lgwin int + if params.large_window { + max_lgwin = largeMaxWindowBits + } else { + max_lgwin = maxWindowBits + } + if params.lgwin > uint(max_lgwin) { + params.lgwin = uint(max_lgwin) + } + } +} + +/* Returns optimized lg_block value. */ +func computeLgBlock(params *encoderParams) int { + var lgblock int = params.lgblock + if params.quality == fastOnePassCompressionQuality || params.quality == fastTwoPassCompressionQuality { + lgblock = int(params.lgwin) + } else if params.quality < minQualityForBlockSplit { + lgblock = 14 + } else if lgblock == 0 { + lgblock = 16 + if params.quality >= 9 && params.lgwin > uint(lgblock) { + lgblock = brotli_min_int(18, int(params.lgwin)) + } + } else { + lgblock = brotli_min_int(maxInputBlockBits, brotli_max_int(minInputBlockBits, lgblock)) + } + + return lgblock +} + +/* Returns log2 of the size of main ring buffer area. + Allocate at least lgwin + 1 bits for the ring buffer so that the newly + added block fits there completely and we still get lgwin bits and at least + read_block_size_bits + 1 bits because the copy tail length needs to be + smaller than ring-buffer size. */ +func computeRbBits(params *encoderParams) int { + return 1 + brotli_max_int(int(params.lgwin), params.lgblock) +} + +func maxMetablockSize(params *encoderParams) uint { + var bits int = brotli_min_int(computeRbBits(params), maxInputBlockBits) + return uint(1) << uint(bits) +} + +/* When searching for backward references and have not seen matches for a long + time, we can skip some match lookups. Unsuccessful match lookups are very + expensive and this kind of a heuristic speeds up compression quite a lot. + At first 8 byte strides are taken and every second byte is put to hasher. + After 4x more literals stride by 16 bytes, every put 4-th byte to hasher. + Applied only to qualities 2 to 9. */ +func literalSpreeLengthForSparseSearch(params *encoderParams) uint { + if params.quality < 9 { + return 64 + } else { + return 512 + } +} + +func chooseHasher(params *encoderParams, hparams *hasherParams) { + if params.quality > 9 { + hparams.type_ = 10 + } else if params.quality == 4 && params.size_hint >= 1<<20 { + hparams.type_ = 54 + } else if params.quality < 5 { + hparams.type_ = params.quality + } else if params.lgwin <= 16 { + if params.quality < 7 { + hparams.type_ = 40 + } else if params.quality < 9 { + hparams.type_ = 41 + } else { + hparams.type_ = 42 + } + } else if params.size_hint >= 1<<20 && params.lgwin >= 19 { + hparams.type_ = 6 + hparams.block_bits = params.quality - 1 + hparams.bucket_bits = 15 + hparams.hash_len = 5 + if params.quality < 7 { + hparams.num_last_distances_to_check = 4 + } else if params.quality < 9 { + hparams.num_last_distances_to_check = 10 + } else { + hparams.num_last_distances_to_check = 16 + } + } else { + hparams.type_ = 5 + hparams.block_bits = params.quality - 1 + if params.quality < 7 { + hparams.bucket_bits = 14 + } else { + hparams.bucket_bits = 15 + } + if params.quality < 7 { + hparams.num_last_distances_to_check = 4 + } else if params.quality < 9 { + hparams.num_last_distances_to_check = 10 + } else { + hparams.num_last_distances_to_check = 16 + } + } + + if params.lgwin > 24 { + /* Different hashers for large window brotli: not for qualities <= 2, + these are too fast for large window. Not for qualities >= 10: their + hasher already works well with large window. So the changes are: + H3 --> H35: for quality 3. + H54 --> H55: for quality 4 with size hint > 1MB + H6 --> H65: for qualities 5, 6, 7, 8, 9. */ + if hparams.type_ == 3 { + hparams.type_ = 35 + } + + if hparams.type_ == 54 { + hparams.type_ = 55 + } + + if hparams.type_ == 6 { + hparams.type_ = 65 + } + } +} diff --git a/vendor/github.com/andybalholm/brotli/reader.go b/vendor/github.com/andybalholm/brotli/reader.go new file mode 100644 index 000000000..9419c79c1 --- /dev/null +++ b/vendor/github.com/andybalholm/brotli/reader.go @@ -0,0 +1,108 @@ +package brotli + +import ( + "errors" + "io" +) + +type decodeError int + +func (err decodeError) Error() string { + return "brotli: " + string(decoderErrorString(int(err))) +} + +var errExcessiveInput = errors.New("brotli: excessive input") +var errInvalidState = errors.New("brotli: invalid state") + +// readBufSize is a "good" buffer size that avoids excessive round-trips +// between C and Go but doesn't waste too much memory on buffering. +// It is arbitrarily chosen to be equal to the constant used in io.Copy. +const readBufSize = 32 * 1024 + +// NewReader creates a new Reader reading the given reader. +func NewReader(src io.Reader) *Reader { + r := new(Reader) + r.Reset(src) + return r +} + +// Reset discards the Reader's state and makes it equivalent to the result of +// its original state from NewReader, but reading from src instead. +// This permits reusing a Reader rather than allocating a new one. +// Error is always nil +func (r *Reader) Reset(src io.Reader) error { + if r.error_code < 0 { + // There was an unrecoverable error, leaving the Reader's state + // undefined. Clear out everything but the buffer. + *r = Reader{buf: r.buf} + } + + decoderStateInit(r) + r.src = src + if r.buf == nil { + r.buf = make([]byte, readBufSize) + } + return nil +} + +func (r *Reader) Read(p []byte) (n int, err error) { + if !decoderHasMoreOutput(r) && len(r.in) == 0 { + m, readErr := r.src.Read(r.buf) + if m == 0 { + // If readErr is `nil`, we just proxy underlying stream behavior. + return 0, readErr + } + r.in = r.buf[:m] + } + + if len(p) == 0 { + return 0, nil + } + + for { + var written uint + in_len := uint(len(r.in)) + out_len := uint(len(p)) + in_remaining := in_len + out_remaining := out_len + result := decoderDecompressStream(r, &in_remaining, &r.in, &out_remaining, &p) + written = out_len - out_remaining + n = int(written) + + switch result { + case decoderResultSuccess: + if len(r.in) > 0 { + return n, errExcessiveInput + } + return n, nil + case decoderResultError: + return n, decodeError(decoderGetErrorCode(r)) + case decoderResultNeedsMoreOutput: + if n == 0 { + return 0, io.ErrShortBuffer + } + return n, nil + case decoderNeedsMoreInput: + } + + if len(r.in) != 0 { + return 0, errInvalidState + } + + // Calling r.src.Read may block. Don't block if we have data to return. + if n > 0 { + return n, nil + } + + // Top off the buffer. + encN, err := r.src.Read(r.buf) + if encN == 0 { + // Not enough data to complete decoding. + if err == io.EOF { + return 0, io.ErrUnexpectedEOF + } + return 0, err + } + r.in = r.buf[:encN] + } +} diff --git a/vendor/github.com/andybalholm/brotli/ringbuffer.go b/vendor/github.com/andybalholm/brotli/ringbuffer.go new file mode 100644 index 000000000..1c8f86fee --- /dev/null +++ b/vendor/github.com/andybalholm/brotli/ringbuffer.go @@ -0,0 +1,134 @@ +package brotli + +/* Copyright 2013 Google Inc. All Rights Reserved. + + Distributed under MIT license. + See file LICENSE for detail or copy at https://opensource.org/licenses/MIT +*/ + +/* A ringBuffer(window_bits, tail_bits) contains `1 << window_bits' bytes of + data in a circular manner: writing a byte writes it to: + `position() % (1 << window_bits)'. + For convenience, the ringBuffer array contains another copy of the + first `1 << tail_bits' bytes: + buffer_[i] == buffer_[i + (1 << window_bits)], if i < (1 << tail_bits), + and another copy of the last two bytes: + buffer_[-1] == buffer_[(1 << window_bits) - 1] and + buffer_[-2] == buffer_[(1 << window_bits) - 2]. */ +type ringBuffer struct { + size_ uint32 + mask_ uint32 + tail_size_ uint32 + total_size_ uint32 + cur_size_ uint32 + pos_ uint32 + data_ []byte + buffer_ []byte +} + +func ringBufferInit(rb *ringBuffer) { + rb.pos_ = 0 +} + +func ringBufferSetup(params *encoderParams, rb *ringBuffer) { + var window_bits int = computeRbBits(params) + var tail_bits int = params.lgblock + *(*uint32)(&rb.size_) = 1 << uint(window_bits) + *(*uint32)(&rb.mask_) = (1 << uint(window_bits)) - 1 + *(*uint32)(&rb.tail_size_) = 1 << uint(tail_bits) + *(*uint32)(&rb.total_size_) = rb.size_ + rb.tail_size_ +} + +const kSlackForEightByteHashingEverywhere uint = 7 + +/* Allocates or re-allocates data_ to the given length + plus some slack + region before and after. Fills the slack regions with zeros. */ +func ringBufferInitBuffer(buflen uint32, rb *ringBuffer) { + var new_data []byte + var i uint + size := 2 + int(buflen) + int(kSlackForEightByteHashingEverywhere) + if cap(rb.data_) < size { + new_data = make([]byte, size) + } else { + new_data = rb.data_[:size] + } + if rb.data_ != nil { + copy(new_data, rb.data_[:2+rb.cur_size_+uint32(kSlackForEightByteHashingEverywhere)]) + } + + rb.data_ = new_data + rb.cur_size_ = buflen + rb.buffer_ = rb.data_[2:] + rb.data_[1] = 0 + rb.data_[0] = rb.data_[1] + for i = 0; i < kSlackForEightByteHashingEverywhere; i++ { + rb.buffer_[rb.cur_size_+uint32(i)] = 0 + } +} + +func ringBufferWriteTail(bytes []byte, n uint, rb *ringBuffer) { + var masked_pos uint = uint(rb.pos_ & rb.mask_) + if uint32(masked_pos) < rb.tail_size_ { + /* Just fill the tail buffer with the beginning data. */ + var p uint = uint(rb.size_ + uint32(masked_pos)) + copy(rb.buffer_[p:], bytes[:brotli_min_size_t(n, uint(rb.tail_size_-uint32(masked_pos)))]) + } +} + +/* Push bytes into the ring buffer. */ +func ringBufferWrite(bytes []byte, n uint, rb *ringBuffer) { + if rb.pos_ == 0 && uint32(n) < rb.tail_size_ { + /* Special case for the first write: to process the first block, we don't + need to allocate the whole ring-buffer and we don't need the tail + either. However, we do this memory usage optimization only if the + first write is less than the tail size, which is also the input block + size, otherwise it is likely that other blocks will follow and we + will need to reallocate to the full size anyway. */ + rb.pos_ = uint32(n) + + ringBufferInitBuffer(rb.pos_, rb) + copy(rb.buffer_, bytes[:n]) + return + } + + if rb.cur_size_ < rb.total_size_ { + /* Lazily allocate the full buffer. */ + ringBufferInitBuffer(rb.total_size_, rb) + + /* Initialize the last two bytes to zero, so that we don't have to worry + later when we copy the last two bytes to the first two positions. */ + rb.buffer_[rb.size_-2] = 0 + + rb.buffer_[rb.size_-1] = 0 + } + { + var masked_pos uint = uint(rb.pos_ & rb.mask_) + + /* The length of the writes is limited so that we do not need to worry + about a write */ + ringBufferWriteTail(bytes, n, rb) + + if uint32(masked_pos+n) <= rb.size_ { + /* A single write fits. */ + copy(rb.buffer_[masked_pos:], bytes[:n]) + } else { + /* Split into two writes. + Copy into the end of the buffer, including the tail buffer. */ + copy(rb.buffer_[masked_pos:], bytes[:brotli_min_size_t(n, uint(rb.total_size_-uint32(masked_pos)))]) + + /* Copy into the beginning of the buffer */ + copy(rb.buffer_, bytes[rb.size_-uint32(masked_pos):][:uint32(n)-(rb.size_-uint32(masked_pos))]) + } + } + { + var not_first_lap bool = rb.pos_&(1<<31) != 0 + var rb_pos_mask uint32 = (1 << 31) - 1 + rb.data_[0] = rb.buffer_[rb.size_-2] + rb.data_[1] = rb.buffer_[rb.size_-1] + rb.pos_ = (rb.pos_ & rb_pos_mask) + uint32(uint32(n)&rb_pos_mask) + if not_first_lap { + /* Wrap, but preserve not-a-first-lap feature. */ + rb.pos_ |= 1 << 31 + } + } +} diff --git a/vendor/github.com/andybalholm/brotli/state.go b/vendor/github.com/andybalholm/brotli/state.go new file mode 100644 index 000000000..38d753ebe --- /dev/null +++ b/vendor/github.com/andybalholm/brotli/state.go @@ -0,0 +1,294 @@ +package brotli + +import "io" + +/* Copyright 2015 Google Inc. All Rights Reserved. + + Distributed under MIT license. + See file LICENSE for detail or copy at https://opensource.org/licenses/MIT +*/ + +/* Brotli state for partial streaming decoding. */ +const ( + stateUninited = iota + stateLargeWindowBits + stateInitialize + stateMetablockBegin + stateMetablockHeader + stateMetablockHeader2 + stateContextModes + stateCommandBegin + stateCommandInner + stateCommandPostDecodeLiterals + stateCommandPostWrapCopy + stateUncompressed + stateMetadata + stateCommandInnerWrite + stateMetablockDone + stateCommandPostWrite1 + stateCommandPostWrite2 + stateHuffmanCode0 + stateHuffmanCode1 + stateHuffmanCode2 + stateHuffmanCode3 + stateContextMap1 + stateContextMap2 + stateTreeGroup + stateDone +) + +const ( + stateMetablockHeaderNone = iota + stateMetablockHeaderEmpty + stateMetablockHeaderNibbles + stateMetablockHeaderSize + stateMetablockHeaderUncompressed + stateMetablockHeaderReserved + stateMetablockHeaderBytes + stateMetablockHeaderMetadata +) + +const ( + stateUncompressedNone = iota + stateUncompressedWrite +) + +const ( + stateTreeGroupNone = iota + stateTreeGroupLoop +) + +const ( + stateContextMapNone = iota + stateContextMapReadPrefix + stateContextMapHuffman + stateContextMapDecode + stateContextMapTransform +) + +const ( + stateHuffmanNone = iota + stateHuffmanSimpleSize + stateHuffmanSimpleRead + stateHuffmanSimpleBuild + stateHuffmanComplex + stateHuffmanLengthSymbols +) + +const ( + stateDecodeUint8None = iota + stateDecodeUint8Short + stateDecodeUint8Long +) + +const ( + stateReadBlockLengthNone = iota + stateReadBlockLengthSuffix +) + +type Reader struct { + src io.Reader + buf []byte // scratch space for reading from src + in []byte // current chunk to decode; usually aliases buf + + state int + loop_counter int + br bitReader + buffer struct { + u64 uint64 + u8 [8]byte + } + buffer_length uint32 + pos int + max_backward_distance int + max_distance int + ringbuffer_size int + ringbuffer_mask int + dist_rb_idx int + dist_rb [4]int + error_code int + sub_loop_counter uint32 + ringbuffer []byte + ringbuffer_end []byte + htree_command []huffmanCode + context_lookup []byte + context_map_slice []byte + dist_context_map_slice []byte + literal_hgroup huffmanTreeGroup + insert_copy_hgroup huffmanTreeGroup + distance_hgroup huffmanTreeGroup + block_type_trees []huffmanCode + block_len_trees []huffmanCode + trivial_literal_context int + distance_context int + meta_block_remaining_len int + block_length_index uint32 + block_length [3]uint32 + num_block_types [3]uint32 + block_type_rb [6]uint32 + distance_postfix_bits uint32 + num_direct_distance_codes uint32 + distance_postfix_mask int + num_dist_htrees uint32 + dist_context_map []byte + literal_htree []huffmanCode + dist_htree_index byte + repeat_code_len uint32 + prev_code_len uint32 + copy_length int + distance_code int + rb_roundtrips uint + partial_pos_out uint + symbol uint32 + repeat uint32 + space uint32 + table [32]huffmanCode + symbol_lists symbolList + symbols_lists_array [huffmanMaxCodeLength + 1 + numCommandSymbols]uint16 + next_symbol [32]int + code_length_code_lengths [codeLengthCodes]byte + code_length_histo [16]uint16 + htree_index int + next []huffmanCode + context_index uint32 + max_run_length_prefix uint32 + code uint32 + context_map_table [huffmanMaxSize272]huffmanCode + substate_metablock_header int + substate_tree_group int + substate_context_map int + substate_uncompressed int + substate_huffman int + substate_decode_uint8 int + substate_read_block_length int + is_last_metablock uint + is_uncompressed uint + is_metadata uint + should_wrap_ringbuffer uint + canny_ringbuffer_allocation uint + large_window bool + size_nibbles uint + window_bits uint32 + new_ringbuffer_size int + num_literal_htrees uint32 + context_map []byte + context_modes []byte + dictionary *dictionary + transforms *transforms + trivial_literal_contexts [8]uint32 +} + +func decoderStateInit(s *Reader) bool { + s.error_code = 0 /* BROTLI_DECODER_NO_ERROR */ + + initBitReader(&s.br) + s.state = stateUninited + s.large_window = false + s.substate_metablock_header = stateMetablockHeaderNone + s.substate_tree_group = stateTreeGroupNone + s.substate_context_map = stateContextMapNone + s.substate_uncompressed = stateUncompressedNone + s.substate_huffman = stateHuffmanNone + s.substate_decode_uint8 = stateDecodeUint8None + s.substate_read_block_length = stateReadBlockLengthNone + + s.buffer_length = 0 + s.loop_counter = 0 + s.pos = 0 + s.rb_roundtrips = 0 + s.partial_pos_out = 0 + + s.block_type_trees = nil + s.block_len_trees = nil + s.ringbuffer_size = 0 + s.new_ringbuffer_size = 0 + s.ringbuffer_mask = 0 + + s.context_map = nil + s.context_modes = nil + s.dist_context_map = nil + s.context_map_slice = nil + s.dist_context_map_slice = nil + + s.sub_loop_counter = 0 + + s.literal_hgroup.codes = nil + s.literal_hgroup.htrees = nil + s.insert_copy_hgroup.codes = nil + s.insert_copy_hgroup.htrees = nil + s.distance_hgroup.codes = nil + s.distance_hgroup.htrees = nil + + s.is_last_metablock = 0 + s.is_uncompressed = 0 + s.is_metadata = 0 + s.should_wrap_ringbuffer = 0 + s.canny_ringbuffer_allocation = 1 + + s.window_bits = 0 + s.max_distance = 0 + s.dist_rb[0] = 16 + s.dist_rb[1] = 15 + s.dist_rb[2] = 11 + s.dist_rb[3] = 4 + s.dist_rb_idx = 0 + s.block_type_trees = nil + s.block_len_trees = nil + + s.symbol_lists.storage = s.symbols_lists_array[:] + s.symbol_lists.offset = huffmanMaxCodeLength + 1 + + s.dictionary = getDictionary() + s.transforms = getTransforms() + + return true +} + +func decoderStateMetablockBegin(s *Reader) { + s.meta_block_remaining_len = 0 + s.block_length[0] = 1 << 24 + s.block_length[1] = 1 << 24 + s.block_length[2] = 1 << 24 + s.num_block_types[0] = 1 + s.num_block_types[1] = 1 + s.num_block_types[2] = 1 + s.block_type_rb[0] = 1 + s.block_type_rb[1] = 0 + s.block_type_rb[2] = 1 + s.block_type_rb[3] = 0 + s.block_type_rb[4] = 1 + s.block_type_rb[5] = 0 + s.context_map = nil + s.context_modes = nil + s.dist_context_map = nil + s.context_map_slice = nil + s.literal_htree = nil + s.dist_context_map_slice = nil + s.dist_htree_index = 0 + s.context_lookup = nil + s.literal_hgroup.codes = nil + s.literal_hgroup.htrees = nil + s.insert_copy_hgroup.codes = nil + s.insert_copy_hgroup.htrees = nil + s.distance_hgroup.codes = nil + s.distance_hgroup.htrees = nil +} + +func decoderStateCleanupAfterMetablock(s *Reader) { + s.context_modes = nil + s.context_map = nil + s.dist_context_map = nil + s.literal_hgroup.htrees = nil + s.insert_copy_hgroup.htrees = nil + s.distance_hgroup.htrees = nil +} + +func decoderHuffmanTreeGroupInit(s *Reader, group *huffmanTreeGroup, alphabet_size uint32, max_symbol uint32, ntrees uint32) bool { + var max_table_size uint = uint(kMaxHuffmanTableSize[(alphabet_size+31)>>5]) + group.alphabet_size = uint16(alphabet_size) + group.max_symbol = uint16(max_symbol) + group.num_htrees = uint16(ntrees) + group.htrees = make([][]huffmanCode, ntrees) + group.codes = make([]huffmanCode, (uint(ntrees) * max_table_size)) + return !(group.codes == nil) +} diff --git a/vendor/github.com/andybalholm/brotli/static_dict.go b/vendor/github.com/andybalholm/brotli/static_dict.go new file mode 100644 index 000000000..bc05566d6 --- /dev/null +++ b/vendor/github.com/andybalholm/brotli/static_dict.go @@ -0,0 +1,662 @@ +package brotli + +import "encoding/binary" + +/* Copyright 2013 Google Inc. All Rights Reserved. + + Distributed under MIT license. + See file LICENSE for detail or copy at https://opensource.org/licenses/MIT +*/ + +/* Class to model the static dictionary. */ + +const maxStaticDictionaryMatchLen = 37 + +const kInvalidMatch uint32 = 0xFFFFFFF + +/* Copyright 2013 Google Inc. All Rights Reserved. + + Distributed under MIT license. + See file LICENSE for detail or copy at https://opensource.org/licenses/MIT +*/ +func hash(data []byte) uint32 { + var h uint32 = binary.LittleEndian.Uint32(data) * kDictHashMul32 + + /* The higher bits contain more mixture from the multiplication, + so we take our results from there. */ + return h >> uint(32-kDictNumBits) +} + +func addMatch(distance uint, len uint, len_code uint, matches []uint32) { + var match uint32 = uint32((distance << 5) + len_code) + matches[len] = brotli_min_uint32_t(matches[len], match) +} + +func dictMatchLength(dict *dictionary, data []byte, id uint, len uint, maxlen uint) uint { + var offset uint = uint(dict.offsets_by_length[len]) + len*id + return findMatchLengthWithLimit(dict.data[offset:], data, brotli_min_size_t(uint(len), maxlen)) +} + +func isMatch(d *dictionary, w dictWord, data []byte, max_length uint) bool { + if uint(w.len) > max_length { + return false + } else { + var offset uint = uint(d.offsets_by_length[w.len]) + uint(w.len)*uint(w.idx) + var dict []byte = d.data[offset:] + if w.transform == 0 { + /* Match against base dictionary word. */ + return findMatchLengthWithLimit(dict, data, uint(w.len)) == uint(w.len) + } else if w.transform == 10 { + /* Match against uppercase first transform. + Note that there are only ASCII uppercase words in the lookup table. */ + return dict[0] >= 'a' && dict[0] <= 'z' && (dict[0]^32) == data[0] && findMatchLengthWithLimit(dict[1:], data[1:], uint(w.len)-1) == uint(w.len-1) + } else { + /* Match against uppercase all transform. + Note that there are only ASCII uppercase words in the lookup table. */ + var i uint + for i = 0; i < uint(w.len); i++ { + if dict[i] >= 'a' && dict[i] <= 'z' { + if (dict[i] ^ 32) != data[i] { + return false + } + } else { + if dict[i] != data[i] { + return false + } + } + } + + return true + } + } +} + +func findAllStaticDictionaryMatches(dict *encoderDictionary, data []byte, min_length uint, max_length uint, matches []uint32) bool { + var has_found_match bool = false + { + var offset uint = uint(dict.buckets[hash(data)]) + var end bool = offset == 0 + for !end { + w := dict.dict_words[offset] + offset++ + var l uint = uint(w.len) & 0x1F + var n uint = uint(1) << dict.words.size_bits_by_length[l] + var id uint = uint(w.idx) + end = !(w.len&0x80 == 0) + w.len = byte(l) + if w.transform == 0 { + var matchlen uint = dictMatchLength(dict.words, data, id, l, max_length) + var s []byte + var minlen uint + var maxlen uint + var len uint + + /* Transform "" + BROTLI_TRANSFORM_IDENTITY + "" */ + if matchlen == l { + addMatch(id, l, l, matches) + has_found_match = true + } + + /* Transforms "" + BROTLI_TRANSFORM_OMIT_LAST_1 + "" and + "" + BROTLI_TRANSFORM_OMIT_LAST_1 + "ing " */ + if matchlen >= l-1 { + addMatch(id+12*n, l-1, l, matches) + if l+2 < max_length && data[l-1] == 'i' && data[l] == 'n' && data[l+1] == 'g' && data[l+2] == ' ' { + addMatch(id+49*n, l+3, l, matches) + } + + has_found_match = true + } + + /* Transform "" + BROTLI_TRANSFORM_OMIT_LAST_# + "" (# = 2 .. 9) */ + minlen = min_length + + if l > 9 { + minlen = brotli_max_size_t(minlen, l-9) + } + maxlen = brotli_min_size_t(matchlen, l-2) + for len = minlen; len <= maxlen; len++ { + var cut uint = l - len + var transform_id uint = (cut << 2) + uint((dict.cutoffTransforms>>(cut*6))&0x3F) + addMatch(id+transform_id*n, uint(len), l, matches) + has_found_match = true + } + + if matchlen < l || l+6 >= max_length { + continue + } + + s = data[l:] + + /* Transforms "" + BROTLI_TRANSFORM_IDENTITY + */ + if s[0] == ' ' { + addMatch(id+n, l+1, l, matches) + if s[1] == 'a' { + if s[2] == ' ' { + addMatch(id+28*n, l+3, l, matches) + } else if s[2] == 's' { + if s[3] == ' ' { + addMatch(id+46*n, l+4, l, matches) + } + } else if s[2] == 't' { + if s[3] == ' ' { + addMatch(id+60*n, l+4, l, matches) + } + } else if s[2] == 'n' { + if s[3] == 'd' && s[4] == ' ' { + addMatch(id+10*n, l+5, l, matches) + } + } + } else if s[1] == 'b' { + if s[2] == 'y' && s[3] == ' ' { + addMatch(id+38*n, l+4, l, matches) + } + } else if s[1] == 'i' { + if s[2] == 'n' { + if s[3] == ' ' { + addMatch(id+16*n, l+4, l, matches) + } + } else if s[2] == 's' { + if s[3] == ' ' { + addMatch(id+47*n, l+4, l, matches) + } + } + } else if s[1] == 'f' { + if s[2] == 'o' { + if s[3] == 'r' && s[4] == ' ' { + addMatch(id+25*n, l+5, l, matches) + } + } else if s[2] == 'r' { + if s[3] == 'o' && s[4] == 'm' && s[5] == ' ' { + addMatch(id+37*n, l+6, l, matches) + } + } + } else if s[1] == 'o' { + if s[2] == 'f' { + if s[3] == ' ' { + addMatch(id+8*n, l+4, l, matches) + } + } else if s[2] == 'n' { + if s[3] == ' ' { + addMatch(id+45*n, l+4, l, matches) + } + } + } else if s[1] == 'n' { + if s[2] == 'o' && s[3] == 't' && s[4] == ' ' { + addMatch(id+80*n, l+5, l, matches) + } + } else if s[1] == 't' { + if s[2] == 'h' { + if s[3] == 'e' { + if s[4] == ' ' { + addMatch(id+5*n, l+5, l, matches) + } + } else if s[3] == 'a' { + if s[4] == 't' && s[5] == ' ' { + addMatch(id+29*n, l+6, l, matches) + } + } + } else if s[2] == 'o' { + if s[3] == ' ' { + addMatch(id+17*n, l+4, l, matches) + } + } + } else if s[1] == 'w' { + if s[2] == 'i' && s[3] == 't' && s[4] == 'h' && s[5] == ' ' { + addMatch(id+35*n, l+6, l, matches) + } + } + } else if s[0] == '"' { + addMatch(id+19*n, l+1, l, matches) + if s[1] == '>' { + addMatch(id+21*n, l+2, l, matches) + } + } else if s[0] == '.' { + addMatch(id+20*n, l+1, l, matches) + if s[1] == ' ' { + addMatch(id+31*n, l+2, l, matches) + if s[2] == 'T' && s[3] == 'h' { + if s[4] == 'e' { + if s[5] == ' ' { + addMatch(id+43*n, l+6, l, matches) + } + } else if s[4] == 'i' { + if s[5] == 's' && s[6] == ' ' { + addMatch(id+75*n, l+7, l, matches) + } + } + } + } + } else if s[0] == ',' { + addMatch(id+76*n, l+1, l, matches) + if s[1] == ' ' { + addMatch(id+14*n, l+2, l, matches) + } + } else if s[0] == '\n' { + addMatch(id+22*n, l+1, l, matches) + if s[1] == '\t' { + addMatch(id+50*n, l+2, l, matches) + } + } else if s[0] == ']' { + addMatch(id+24*n, l+1, l, matches) + } else if s[0] == '\'' { + addMatch(id+36*n, l+1, l, matches) + } else if s[0] == ':' { + addMatch(id+51*n, l+1, l, matches) + } else if s[0] == '(' { + addMatch(id+57*n, l+1, l, matches) + } else if s[0] == '=' { + if s[1] == '"' { + addMatch(id+70*n, l+2, l, matches) + } else if s[1] == '\'' { + addMatch(id+86*n, l+2, l, matches) + } + } else if s[0] == 'a' { + if s[1] == 'l' && s[2] == ' ' { + addMatch(id+84*n, l+3, l, matches) + } + } else if s[0] == 'e' { + if s[1] == 'd' { + if s[2] == ' ' { + addMatch(id+53*n, l+3, l, matches) + } + } else if s[1] == 'r' { + if s[2] == ' ' { + addMatch(id+82*n, l+3, l, matches) + } + } else if s[1] == 's' { + if s[2] == 't' && s[3] == ' ' { + addMatch(id+95*n, l+4, l, matches) + } + } + } else if s[0] == 'f' { + if s[1] == 'u' && s[2] == 'l' && s[3] == ' ' { + addMatch(id+90*n, l+4, l, matches) + } + } else if s[0] == 'i' { + if s[1] == 'v' { + if s[2] == 'e' && s[3] == ' ' { + addMatch(id+92*n, l+4, l, matches) + } + } else if s[1] == 'z' { + if s[2] == 'e' && s[3] == ' ' { + addMatch(id+100*n, l+4, l, matches) + } + } + } else if s[0] == 'l' { + if s[1] == 'e' { + if s[2] == 's' && s[3] == 's' && s[4] == ' ' { + addMatch(id+93*n, l+5, l, matches) + } + } else if s[1] == 'y' { + if s[2] == ' ' { + addMatch(id+61*n, l+3, l, matches) + } + } + } else if s[0] == 'o' { + if s[1] == 'u' && s[2] == 's' && s[3] == ' ' { + addMatch(id+106*n, l+4, l, matches) + } + } + } else { + var is_all_caps bool = (w.transform != transformUppercaseFirst) + /* Set is_all_caps=0 for BROTLI_TRANSFORM_UPPERCASE_FIRST and + is_all_caps=1 otherwise (BROTLI_TRANSFORM_UPPERCASE_ALL) + transform. */ + + var s []byte + if !isMatch(dict.words, w, data, max_length) { + continue + } + + /* Transform "" + kUppercase{First,All} + "" */ + var tmp int + if is_all_caps { + tmp = 44 + } else { + tmp = 9 + } + addMatch(id+uint(tmp)*n, l, l, matches) + + has_found_match = true + if l+1 >= max_length { + continue + } + + /* Transforms "" + kUppercase{First,All} + */ + s = data[l:] + + if s[0] == ' ' { + var tmp int + if is_all_caps { + tmp = 68 + } else { + tmp = 4 + } + addMatch(id+uint(tmp)*n, l+1, l, matches) + } else if s[0] == '"' { + var tmp int + if is_all_caps { + tmp = 87 + } else { + tmp = 66 + } + addMatch(id+uint(tmp)*n, l+1, l, matches) + if s[1] == '>' { + var tmp int + if is_all_caps { + tmp = 97 + } else { + tmp = 69 + } + addMatch(id+uint(tmp)*n, l+2, l, matches) + } + } else if s[0] == '.' { + var tmp int + if is_all_caps { + tmp = 101 + } else { + tmp = 79 + } + addMatch(id+uint(tmp)*n, l+1, l, matches) + if s[1] == ' ' { + var tmp int + if is_all_caps { + tmp = 114 + } else { + tmp = 88 + } + addMatch(id+uint(tmp)*n, l+2, l, matches) + } + } else if s[0] == ',' { + var tmp int + if is_all_caps { + tmp = 112 + } else { + tmp = 99 + } + addMatch(id+uint(tmp)*n, l+1, l, matches) + if s[1] == ' ' { + var tmp int + if is_all_caps { + tmp = 107 + } else { + tmp = 58 + } + addMatch(id+uint(tmp)*n, l+2, l, matches) + } + } else if s[0] == '\'' { + var tmp int + if is_all_caps { + tmp = 94 + } else { + tmp = 74 + } + addMatch(id+uint(tmp)*n, l+1, l, matches) + } else if s[0] == '(' { + var tmp int + if is_all_caps { + tmp = 113 + } else { + tmp = 78 + } + addMatch(id+uint(tmp)*n, l+1, l, matches) + } else if s[0] == '=' { + if s[1] == '"' { + var tmp int + if is_all_caps { + tmp = 105 + } else { + tmp = 104 + } + addMatch(id+uint(tmp)*n, l+2, l, matches) + } else if s[1] == '\'' { + var tmp int + if is_all_caps { + tmp = 116 + } else { + tmp = 108 + } + addMatch(id+uint(tmp)*n, l+2, l, matches) + } + } + } + } + } + + /* Transforms with prefixes " " and "." */ + if max_length >= 5 && (data[0] == ' ' || data[0] == '.') { + var is_space bool = (data[0] == ' ') + var offset uint = uint(dict.buckets[hash(data[1:])]) + var end bool = offset == 0 + for !end { + w := dict.dict_words[offset] + offset++ + var l uint = uint(w.len) & 0x1F + var n uint = uint(1) << dict.words.size_bits_by_length[l] + var id uint = uint(w.idx) + end = !(w.len&0x80 == 0) + w.len = byte(l) + if w.transform == 0 { + var s []byte + if !isMatch(dict.words, w, data[1:], max_length-1) { + continue + } + + /* Transforms " " + BROTLI_TRANSFORM_IDENTITY + "" and + "." + BROTLI_TRANSFORM_IDENTITY + "" */ + var tmp int + if is_space { + tmp = 6 + } else { + tmp = 32 + } + addMatch(id+uint(tmp)*n, l+1, l, matches) + + has_found_match = true + if l+2 >= max_length { + continue + } + + /* Transforms " " + BROTLI_TRANSFORM_IDENTITY + and + "." + BROTLI_TRANSFORM_IDENTITY + + */ + s = data[l+1:] + + if s[0] == ' ' { + var tmp int + if is_space { + tmp = 2 + } else { + tmp = 77 + } + addMatch(id+uint(tmp)*n, l+2, l, matches) + } else if s[0] == '(' { + var tmp int + if is_space { + tmp = 89 + } else { + tmp = 67 + } + addMatch(id+uint(tmp)*n, l+2, l, matches) + } else if is_space { + if s[0] == ',' { + addMatch(id+103*n, l+2, l, matches) + if s[1] == ' ' { + addMatch(id+33*n, l+3, l, matches) + } + } else if s[0] == '.' { + addMatch(id+71*n, l+2, l, matches) + if s[1] == ' ' { + addMatch(id+52*n, l+3, l, matches) + } + } else if s[0] == '=' { + if s[1] == '"' { + addMatch(id+81*n, l+3, l, matches) + } else if s[1] == '\'' { + addMatch(id+98*n, l+3, l, matches) + } + } + } + } else if is_space { + var is_all_caps bool = (w.transform != transformUppercaseFirst) + /* Set is_all_caps=0 for BROTLI_TRANSFORM_UPPERCASE_FIRST and + is_all_caps=1 otherwise (BROTLI_TRANSFORM_UPPERCASE_ALL) + transform. */ + + var s []byte + if !isMatch(dict.words, w, data[1:], max_length-1) { + continue + } + + /* Transforms " " + kUppercase{First,All} + "" */ + var tmp int + if is_all_caps { + tmp = 85 + } else { + tmp = 30 + } + addMatch(id+uint(tmp)*n, l+1, l, matches) + + has_found_match = true + if l+2 >= max_length { + continue + } + + /* Transforms " " + kUppercase{First,All} + */ + s = data[l+1:] + + if s[0] == ' ' { + var tmp int + if is_all_caps { + tmp = 83 + } else { + tmp = 15 + } + addMatch(id+uint(tmp)*n, l+2, l, matches) + } else if s[0] == ',' { + if !is_all_caps { + addMatch(id+109*n, l+2, l, matches) + } + + if s[1] == ' ' { + var tmp int + if is_all_caps { + tmp = 111 + } else { + tmp = 65 + } + addMatch(id+uint(tmp)*n, l+3, l, matches) + } + } else if s[0] == '.' { + var tmp int + if is_all_caps { + tmp = 115 + } else { + tmp = 96 + } + addMatch(id+uint(tmp)*n, l+2, l, matches) + if s[1] == ' ' { + var tmp int + if is_all_caps { + tmp = 117 + } else { + tmp = 91 + } + addMatch(id+uint(tmp)*n, l+3, l, matches) + } + } else if s[0] == '=' { + if s[1] == '"' { + var tmp int + if is_all_caps { + tmp = 110 + } else { + tmp = 118 + } + addMatch(id+uint(tmp)*n, l+3, l, matches) + } else if s[1] == '\'' { + var tmp int + if is_all_caps { + tmp = 119 + } else { + tmp = 120 + } + addMatch(id+uint(tmp)*n, l+3, l, matches) + } + } + } + } + } + + if max_length >= 6 { + /* Transforms with prefixes "e ", "s ", ", " and "\xC2\xA0" */ + if (data[1] == ' ' && (data[0] == 'e' || data[0] == 's' || data[0] == ',')) || (data[0] == 0xC2 && data[1] == 0xA0) { + var offset uint = uint(dict.buckets[hash(data[2:])]) + var end bool = offset == 0 + for !end { + w := dict.dict_words[offset] + offset++ + var l uint = uint(w.len) & 0x1F + var n uint = uint(1) << dict.words.size_bits_by_length[l] + var id uint = uint(w.idx) + end = !(w.len&0x80 == 0) + w.len = byte(l) + if w.transform == 0 && isMatch(dict.words, w, data[2:], max_length-2) { + if data[0] == 0xC2 { + addMatch(id+102*n, l+2, l, matches) + has_found_match = true + } else if l+2 < max_length && data[l+2] == ' ' { + var t uint = 13 + if data[0] == 'e' { + t = 18 + } else if data[0] == 's' { + t = 7 + } + addMatch(id+t*n, l+3, l, matches) + has_found_match = true + } + } + } + } + } + + if max_length >= 9 { + /* Transforms with prefixes " the " and ".com/" */ + if (data[0] == ' ' && data[1] == 't' && data[2] == 'h' && data[3] == 'e' && data[4] == ' ') || (data[0] == '.' && data[1] == 'c' && data[2] == 'o' && data[3] == 'm' && data[4] == '/') { + var offset uint = uint(dict.buckets[hash(data[5:])]) + var end bool = offset == 0 + for !end { + w := dict.dict_words[offset] + offset++ + var l uint = uint(w.len) & 0x1F + var n uint = uint(1) << dict.words.size_bits_by_length[l] + var id uint = uint(w.idx) + end = !(w.len&0x80 == 0) + w.len = byte(l) + if w.transform == 0 && isMatch(dict.words, w, data[5:], max_length-5) { + var tmp int + if data[0] == ' ' { + tmp = 41 + } else { + tmp = 72 + } + addMatch(id+uint(tmp)*n, l+5, l, matches) + has_found_match = true + if l+5 < max_length { + var s []byte = data[l+5:] + if data[0] == ' ' { + if l+8 < max_length && s[0] == ' ' && s[1] == 'o' && s[2] == 'f' && s[3] == ' ' { + addMatch(id+62*n, l+9, l, matches) + if l+12 < max_length && s[4] == 't' && s[5] == 'h' && s[6] == 'e' && s[7] == ' ' { + addMatch(id+73*n, l+13, l, matches) + } + } + } + } + } + } + } + } + + return has_found_match +} diff --git a/vendor/github.com/andybalholm/brotli/static_dict_lut.go b/vendor/github.com/andybalholm/brotli/static_dict_lut.go new file mode 100644 index 000000000..b33963e96 --- /dev/null +++ b/vendor/github.com/andybalholm/brotli/static_dict_lut.go @@ -0,0 +1,75094 @@ +package brotli + +/* Copyright 2017 Google Inc. All Rights Reserved. + + Distributed under MIT license. + See file LICENSE for detail or copy at https://opensource.org/licenses/MIT +*/ + +/* Lookup table for static dictionary and transforms. */ + +type dictWord struct { + len byte + transform byte + idx uint16 +} + +const kDictNumBits int = 15 + +const kDictHashMul32 uint32 = 0x1E35A7BD + +var kStaticDictionaryBuckets = [32768]uint16{ + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 3, + 6, + 0, + 0, + 0, + 0, + 0, + 20, + 0, + 0, + 0, + 21, + 0, + 22, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 23, + 0, + 0, + 25, + 0, + 29, + 0, + 53, + 0, + 0, + 0, + 0, + 0, + 0, + 55, + 0, + 0, + 0, + 0, + 0, + 0, + 61, + 76, + 0, + 0, + 0, + 94, + 0, + 0, + 0, + 0, + 0, + 0, + 96, + 0, + 97, + 0, + 98, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 99, + 101, + 106, + 108, + 0, + 0, + 0, + 0, + 0, + 110, + 0, + 111, + 112, + 0, + 113, + 118, + 124, + 0, + 0, + 0, + 0, + 0, + 125, + 128, + 0, + 0, + 0, + 0, + 129, + 0, + 0, + 131, + 0, + 0, + 0, + 0, + 0, + 0, + 132, + 0, + 0, + 135, + 0, + 0, + 0, + 137, + 0, + 0, + 0, + 0, + 0, + 138, + 139, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 142, + 143, + 144, + 0, + 0, + 0, + 0, + 0, + 145, + 0, + 0, + 0, + 146, + 149, + 151, + 152, + 0, + 0, + 153, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 154, + 0, + 0, + 0, + 0, + 0, + 0, + 155, + 0, + 0, + 0, + 0, + 160, + 182, + 0, + 0, + 0, + 0, + 0, + 0, + 183, + 0, + 0, + 0, + 188, + 189, + 0, + 0, + 192, + 0, + 0, + 0, + 0, + 0, + 0, + 194, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 197, + 202, + 209, + 0, + 0, + 210, + 0, + 224, + 0, + 0, + 0, + 225, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 231, + 0, + 0, + 0, + 232, + 0, + 240, + 0, + 0, + 242, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 244, + 0, + 0, + 0, + 246, + 0, + 0, + 249, + 251, + 253, + 0, + 0, + 0, + 0, + 0, + 258, + 0, + 0, + 261, + 263, + 0, + 0, + 0, + 267, + 0, + 0, + 268, + 0, + 269, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 271, + 0, + 0, + 0, + 0, + 0, + 0, + 272, + 0, + 273, + 0, + 277, + 0, + 278, + 286, + 0, + 0, + 0, + 0, + 287, + 0, + 289, + 290, + 291, + 0, + 0, + 0, + 295, + 0, + 0, + 296, + 297, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 298, + 0, + 0, + 0, + 299, + 0, + 0, + 305, + 0, + 324, + 0, + 0, + 0, + 0, + 0, + 327, + 0, + 328, + 329, + 0, + 0, + 0, + 0, + 336, + 0, + 0, + 340, + 0, + 341, + 342, + 343, + 0, + 0, + 346, + 0, + 348, + 0, + 0, + 0, + 0, + 0, + 0, + 349, + 351, + 0, + 0, + 355, + 0, + 363, + 0, + 364, + 0, + 368, + 369, + 0, + 370, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 372, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 373, + 0, + 375, + 0, + 0, + 0, + 0, + 376, + 377, + 0, + 0, + 394, + 395, + 396, + 0, + 0, + 398, + 0, + 0, + 0, + 0, + 400, + 0, + 0, + 408, + 0, + 0, + 0, + 0, + 420, + 0, + 0, + 0, + 0, + 0, + 0, + 421, + 0, + 0, + 422, + 423, + 0, + 0, + 429, + 435, + 436, + 442, + 0, + 0, + 443, + 0, + 444, + 445, + 453, + 456, + 0, + 457, + 0, + 0, + 0, + 0, + 0, + 458, + 0, + 0, + 0, + 459, + 0, + 0, + 0, + 460, + 0, + 462, + 463, + 465, + 0, + 0, + 0, + 0, + 0, + 0, + 466, + 469, + 0, + 0, + 0, + 0, + 0, + 0, + 470, + 0, + 0, + 0, + 474, + 0, + 476, + 0, + 0, + 0, + 0, + 483, + 0, + 485, + 0, + 0, + 0, + 486, + 0, + 0, + 488, + 491, + 492, + 0, + 0, + 497, + 499, + 500, + 0, + 501, + 0, + 0, + 0, + 505, + 0, + 0, + 506, + 0, + 0, + 0, + 507, + 0, + 0, + 0, + 509, + 0, + 0, + 0, + 0, + 511, + 512, + 519, + 0, + 0, + 0, + 0, + 0, + 0, + 529, + 530, + 0, + 0, + 0, + 534, + 0, + 0, + 0, + 0, + 543, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 553, + 0, + 0, + 0, + 0, + 557, + 560, + 0, + 0, + 0, + 0, + 0, + 0, + 561, + 0, + 564, + 0, + 0, + 0, + 0, + 0, + 0, + 565, + 566, + 0, + 575, + 0, + 619, + 0, + 620, + 0, + 0, + 623, + 624, + 0, + 0, + 0, + 625, + 0, + 0, + 626, + 627, + 0, + 0, + 628, + 0, + 0, + 0, + 0, + 630, + 0, + 631, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 641, + 0, + 0, + 0, + 0, + 643, + 656, + 668, + 0, + 0, + 0, + 673, + 0, + 0, + 0, + 674, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 682, + 0, + 687, + 0, + 690, + 0, + 693, + 699, + 700, + 0, + 0, + 0, + 0, + 0, + 0, + 704, + 705, + 0, + 0, + 0, + 0, + 707, + 710, + 0, + 711, + 0, + 0, + 0, + 0, + 726, + 0, + 0, + 729, + 0, + 0, + 0, + 730, + 731, + 0, + 0, + 0, + 0, + 0, + 752, + 0, + 0, + 0, + 762, + 0, + 763, + 0, + 0, + 767, + 0, + 0, + 0, + 770, + 774, + 0, + 0, + 775, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 776, + 0, + 0, + 0, + 777, + 783, + 0, + 0, + 0, + 785, + 788, + 0, + 0, + 0, + 0, + 790, + 0, + 0, + 0, + 793, + 0, + 0, + 0, + 0, + 794, + 0, + 0, + 804, + 819, + 821, + 0, + 827, + 0, + 0, + 0, + 834, + 0, + 0, + 835, + 0, + 0, + 0, + 841, + 0, + 844, + 0, + 850, + 851, + 859, + 0, + 860, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 874, + 0, + 876, + 0, + 877, + 890, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 893, + 894, + 898, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 899, + 0, + 0, + 0, + 900, + 904, + 906, + 0, + 0, + 0, + 907, + 0, + 908, + 909, + 0, + 910, + 0, + 0, + 0, + 0, + 911, + 0, + 0, + 0, + 0, + 0, + 916, + 0, + 0, + 0, + 922, + 925, + 0, + 930, + 0, + 934, + 0, + 0, + 0, + 0, + 0, + 943, + 0, + 0, + 944, + 0, + 953, + 954, + 0, + 0, + 0, + 0, + 0, + 0, + 955, + 0, + 962, + 963, + 0, + 0, + 976, + 0, + 0, + 977, + 978, + 979, + 980, + 0, + 981, + 0, + 0, + 0, + 0, + 984, + 0, + 0, + 985, + 0, + 0, + 987, + 989, + 991, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 992, + 0, + 0, + 0, + 993, + 0, + 0, + 0, + 0, + 0, + 0, + 996, + 0, + 0, + 0, + 1000, + 0, + 0, + 0, + 0, + 0, + 1002, + 0, + 0, + 0, + 0, + 1005, + 1007, + 0, + 0, + 0, + 1009, + 0, + 0, + 0, + 1010, + 0, + 0, + 0, + 0, + 0, + 0, + 1011, + 0, + 1012, + 0, + 0, + 0, + 0, + 1014, + 1016, + 0, + 0, + 0, + 1020, + 0, + 1021, + 0, + 0, + 0, + 0, + 1022, + 0, + 0, + 0, + 1024, + 0, + 0, + 0, + 0, + 0, + 0, + 1025, + 0, + 0, + 1026, + 1027, + 0, + 0, + 0, + 0, + 0, + 1031, + 0, + 1033, + 0, + 0, + 0, + 0, + 1034, + 0, + 0, + 0, + 1037, + 1040, + 0, + 0, + 0, + 1042, + 1043, + 0, + 0, + 1053, + 0, + 1054, + 0, + 0, + 1057, + 0, + 0, + 0, + 1058, + 0, + 0, + 1060, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1061, + 0, + 0, + 1062, + 0, + 0, + 0, + 0, + 1063, + 0, + 0, + 0, + 0, + 1064, + 0, + 0, + 0, + 0, + 0, + 1065, + 0, + 0, + 0, + 0, + 1066, + 1067, + 0, + 0, + 0, + 1069, + 1070, + 1072, + 0, + 0, + 0, + 0, + 0, + 0, + 1073, + 0, + 1075, + 0, + 0, + 0, + 0, + 0, + 0, + 1080, + 1084, + 0, + 0, + 0, + 0, + 1088, + 0, + 0, + 0, + 0, + 0, + 0, + 1094, + 0, + 1095, + 0, + 1107, + 0, + 0, + 0, + 1112, + 1114, + 0, + 1119, + 0, + 1122, + 0, + 0, + 1126, + 0, + 1129, + 0, + 1130, + 0, + 0, + 0, + 0, + 0, + 1132, + 0, + 0, + 0, + 0, + 0, + 0, + 1144, + 0, + 0, + 1145, + 1146, + 0, + 1148, + 1149, + 0, + 0, + 1150, + 1151, + 0, + 0, + 0, + 0, + 1152, + 0, + 1153, + 0, + 0, + 0, + 0, + 0, + 1154, + 0, + 1163, + 0, + 0, + 0, + 1164, + 0, + 0, + 0, + 0, + 0, + 1165, + 0, + 1167, + 0, + 1170, + 0, + 0, + 0, + 0, + 0, + 1171, + 1172, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1173, + 1175, + 1177, + 0, + 1186, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1195, + 0, + 0, + 1221, + 0, + 0, + 1224, + 0, + 0, + 1227, + 0, + 0, + 0, + 0, + 0, + 1228, + 1229, + 0, + 0, + 1230, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1231, + 0, + 0, + 0, + 1233, + 0, + 0, + 1243, + 1244, + 1246, + 1248, + 0, + 0, + 0, + 0, + 1254, + 1255, + 1258, + 1259, + 0, + 0, + 0, + 1260, + 0, + 0, + 1261, + 0, + 0, + 0, + 1262, + 1264, + 0, + 0, + 1265, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1266, + 0, + 1267, + 0, + 0, + 0, + 0, + 1273, + 1274, + 1276, + 1289, + 0, + 0, + 1291, + 1292, + 1293, + 0, + 0, + 1294, + 1295, + 1296, + 0, + 0, + 0, + 0, + 1302, + 0, + 1304, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1311, + 1312, + 0, + 1314, + 0, + 1316, + 1320, + 1321, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1322, + 1323, + 1324, + 0, + 1335, + 0, + 1336, + 0, + 0, + 0, + 0, + 1341, + 1342, + 0, + 1346, + 0, + 1357, + 0, + 0, + 0, + 1358, + 1360, + 0, + 0, + 0, + 0, + 0, + 0, + 1361, + 0, + 0, + 0, + 1362, + 1365, + 0, + 1366, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1379, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1386, + 0, + 1388, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1395, + 0, + 0, + 0, + 0, + 1403, + 0, + 1405, + 0, + 0, + 1407, + 0, + 0, + 0, + 0, + 0, + 1408, + 1409, + 0, + 1410, + 0, + 0, + 0, + 1412, + 1413, + 1416, + 0, + 0, + 1429, + 1451, + 0, + 0, + 1454, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1455, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1456, + 0, + 0, + 0, + 0, + 1459, + 1460, + 1461, + 1475, + 0, + 0, + 0, + 0, + 0, + 0, + 1477, + 0, + 1480, + 0, + 1481, + 0, + 0, + 1486, + 0, + 0, + 1495, + 0, + 0, + 0, + 1496, + 0, + 0, + 1498, + 1499, + 1501, + 1520, + 1521, + 0, + 0, + 0, + 1526, + 0, + 0, + 0, + 0, + 1528, + 1529, + 0, + 1533, + 1536, + 0, + 0, + 0, + 1537, + 1538, + 1549, + 0, + 1550, + 1558, + 1559, + 1572, + 0, + 1573, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1575, + 0, + 0, + 0, + 0, + 0, + 1579, + 0, + 1599, + 0, + 1603, + 0, + 1604, + 0, + 1605, + 0, + 0, + 0, + 0, + 0, + 1608, + 1610, + 0, + 0, + 0, + 0, + 1611, + 0, + 1615, + 0, + 1616, + 1618, + 0, + 1619, + 0, + 0, + 1622, + 0, + 0, + 0, + 0, + 1634, + 0, + 0, + 0, + 1635, + 0, + 0, + 0, + 1641, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1643, + 0, + 0, + 0, + 1650, + 0, + 0, + 1652, + 0, + 0, + 0, + 0, + 0, + 1653, + 0, + 0, + 0, + 1654, + 0, + 0, + 0, + 0, + 1655, + 0, + 1662, + 0, + 0, + 1663, + 1664, + 0, + 0, + 1668, + 0, + 0, + 1669, + 1670, + 0, + 1672, + 1673, + 0, + 0, + 0, + 0, + 0, + 1674, + 0, + 0, + 0, + 1675, + 1676, + 1680, + 0, + 1682, + 0, + 0, + 1687, + 0, + 0, + 0, + 0, + 0, + 1704, + 0, + 0, + 1705, + 0, + 0, + 1721, + 0, + 0, + 0, + 0, + 1734, + 1735, + 0, + 0, + 0, + 0, + 1737, + 0, + 0, + 0, + 0, + 1739, + 0, + 0, + 1740, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1741, + 1743, + 0, + 0, + 0, + 0, + 1745, + 0, + 0, + 0, + 1749, + 0, + 0, + 0, + 1751, + 0, + 0, + 0, + 0, + 0, + 0, + 1760, + 0, + 0, + 0, + 0, + 1765, + 0, + 0, + 0, + 0, + 0, + 1784, + 0, + 1785, + 1787, + 0, + 0, + 0, + 0, + 1788, + 1789, + 0, + 0, + 0, + 0, + 1790, + 1791, + 1793, + 0, + 1798, + 1799, + 0, + 0, + 0, + 0, + 1801, + 0, + 1803, + 1805, + 0, + 0, + 0, + 1806, + 1811, + 0, + 1812, + 1814, + 0, + 1821, + 0, + 0, + 0, + 0, + 0, + 1822, + 1833, + 0, + 0, + 0, + 0, + 0, + 0, + 1848, + 0, + 0, + 0, + 0, + 0, + 0, + 1857, + 0, + 0, + 0, + 1859, + 0, + 0, + 0, + 0, + 1861, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1866, + 0, + 1921, + 1925, + 0, + 0, + 0, + 1929, + 1930, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1931, + 0, + 0, + 0, + 0, + 1932, + 0, + 0, + 0, + 1934, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1946, + 0, + 0, + 1948, + 0, + 0, + 0, + 0, + 1950, + 0, + 1957, + 0, + 1958, + 0, + 0, + 0, + 0, + 0, + 1965, + 1967, + 0, + 0, + 0, + 0, + 1968, + 0, + 1969, + 0, + 1971, + 1972, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1973, + 0, + 0, + 0, + 0, + 1975, + 0, + 0, + 0, + 0, + 1976, + 1979, + 0, + 1982, + 0, + 0, + 0, + 0, + 1984, + 1988, + 0, + 0, + 0, + 0, + 1990, + 2004, + 2008, + 0, + 0, + 0, + 2012, + 2013, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 2015, + 0, + 2016, + 2017, + 0, + 0, + 0, + 0, + 2021, + 0, + 0, + 2025, + 0, + 0, + 0, + 0, + 0, + 2029, + 2036, + 2040, + 0, + 2042, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 2043, + 0, + 0, + 0, + 0, + 0, + 2045, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 2046, + 2047, + 0, + 2048, + 2049, + 0, + 2059, + 0, + 0, + 2063, + 0, + 2064, + 2065, + 0, + 0, + 2066, + 0, + 0, + 0, + 0, + 0, + 0, + 2069, + 0, + 0, + 0, + 0, + 2070, + 0, + 2071, + 0, + 2072, + 0, + 0, + 0, + 0, + 2080, + 2082, + 2083, + 0, + 0, + 0, + 0, + 0, + 2085, + 0, + 2086, + 2088, + 2089, + 2105, + 0, + 0, + 0, + 0, + 2107, + 0, + 0, + 2116, + 2117, + 0, + 2120, + 0, + 0, + 2122, + 0, + 0, + 0, + 0, + 0, + 2123, + 0, + 0, + 2125, + 2127, + 2128, + 0, + 0, + 0, + 2130, + 0, + 0, + 0, + 2137, + 2139, + 2140, + 2141, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 2144, + 2145, + 0, + 0, + 2146, + 2149, + 0, + 0, + 0, + 0, + 2150, + 0, + 0, + 2151, + 2158, + 0, + 2159, + 0, + 2160, + 0, + 0, + 0, + 0, + 0, + 0, + 2161, + 2162, + 0, + 0, + 2194, + 2202, + 0, + 0, + 0, + 0, + 0, + 0, + 2205, + 2217, + 0, + 2220, + 0, + 2221, + 0, + 2222, + 2224, + 0, + 0, + 0, + 0, + 2237, + 0, + 0, + 0, + 0, + 0, + 2238, + 0, + 2239, + 2241, + 0, + 0, + 2242, + 0, + 0, + 0, + 0, + 0, + 2243, + 0, + 0, + 0, + 0, + 0, + 0, + 2252, + 0, + 0, + 2253, + 0, + 0, + 0, + 2257, + 2258, + 0, + 0, + 0, + 2260, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 2262, + 0, + 2264, + 0, + 0, + 0, + 0, + 0, + 2269, + 2270, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 2271, + 0, + 2273, + 0, + 0, + 0, + 0, + 2277, + 0, + 0, + 0, + 0, + 2278, + 0, + 0, + 0, + 0, + 2279, + 0, + 2280, + 0, + 2283, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 2287, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 2289, + 2290, + 0, + 0, + 0, + 0, + 2291, + 0, + 2292, + 0, + 0, + 0, + 2293, + 2295, + 2296, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 2298, + 0, + 0, + 0, + 0, + 0, + 2303, + 0, + 2305, + 0, + 0, + 2306, + 0, + 2307, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 2313, + 2314, + 2315, + 2316, + 0, + 0, + 2318, + 0, + 2319, + 0, + 2322, + 0, + 0, + 2323, + 0, + 2324, + 0, + 2326, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 2335, + 0, + 2336, + 2338, + 2339, + 0, + 2340, + 0, + 0, + 0, + 2355, + 0, + 2375, + 0, + 2382, + 2386, + 0, + 2387, + 0, + 0, + 2394, + 0, + 0, + 0, + 0, + 2395, + 0, + 2397, + 0, + 0, + 0, + 0, + 0, + 2398, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 2399, + 2402, + 2404, + 2408, + 2411, + 0, + 0, + 0, + 2413, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 2415, + 0, + 0, + 2416, + 2417, + 2419, + 0, + 2420, + 0, + 0, + 0, + 0, + 0, + 2425, + 0, + 0, + 0, + 2426, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 2427, + 2428, + 0, + 2429, + 0, + 0, + 2430, + 2434, + 0, + 2436, + 0, + 0, + 0, + 0, + 0, + 0, + 2441, + 2442, + 0, + 2445, + 0, + 0, + 2446, + 2457, + 0, + 2459, + 0, + 0, + 2462, + 0, + 2464, + 0, + 2477, + 0, + 2478, + 2486, + 0, + 0, + 0, + 2491, + 0, + 0, + 2493, + 0, + 0, + 2494, + 0, + 2495, + 0, + 2513, + 2523, + 0, + 0, + 0, + 0, + 2524, + 0, + 0, + 0, + 0, + 0, + 0, + 2528, + 2529, + 2530, + 0, + 0, + 2531, + 0, + 2533, + 0, + 0, + 2534, + 2535, + 0, + 2536, + 2537, + 0, + 2538, + 0, + 2539, + 2540, + 0, + 0, + 0, + 2545, + 2546, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 2548, + 0, + 0, + 2549, + 0, + 2550, + 2555, + 0, + 0, + 0, + 0, + 0, + 2557, + 0, + 2560, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 2561, + 0, + 2576, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 2577, + 2578, + 0, + 0, + 0, + 2579, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 2580, + 0, + 0, + 0, + 0, + 2581, + 0, + 0, + 0, + 0, + 2583, + 0, + 2584, + 0, + 2588, + 2590, + 0, + 0, + 0, + 2591, + 0, + 0, + 0, + 0, + 2593, + 2594, + 0, + 2595, + 0, + 2601, + 2602, + 0, + 0, + 2603, + 0, + 2605, + 0, + 0, + 0, + 2606, + 2607, + 2611, + 0, + 2615, + 0, + 0, + 0, + 2617, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 2619, + 0, + 0, + 2620, + 0, + 0, + 0, + 2621, + 0, + 2623, + 0, + 2625, + 0, + 0, + 2628, + 2629, + 0, + 0, + 2635, + 2636, + 2637, + 0, + 0, + 2639, + 0, + 0, + 0, + 2642, + 0, + 0, + 0, + 0, + 2643, + 0, + 2644, + 0, + 2649, + 0, + 0, + 0, + 0, + 0, + 0, + 2655, + 2656, + 0, + 0, + 2657, + 0, + 0, + 0, + 0, + 0, + 2658, + 0, + 0, + 0, + 0, + 0, + 2659, + 0, + 0, + 0, + 0, + 2664, + 2685, + 0, + 2687, + 0, + 2688, + 0, + 0, + 2689, + 0, + 0, + 2694, + 0, + 2695, + 0, + 0, + 2698, + 0, + 2701, + 2706, + 0, + 0, + 0, + 2707, + 0, + 2709, + 2710, + 2711, + 0, + 0, + 0, + 2720, + 2730, + 2735, + 0, + 0, + 0, + 0, + 2738, + 2740, + 0, + 0, + 0, + 0, + 2747, + 0, + 0, + 0, + 0, + 0, + 0, + 2748, + 0, + 0, + 2749, + 0, + 0, + 0, + 0, + 0, + 2750, + 0, + 0, + 2752, + 2754, + 0, + 0, + 0, + 0, + 0, + 2758, + 0, + 0, + 0, + 0, + 2762, + 0, + 0, + 0, + 0, + 2763, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 2764, + 2767, + 0, + 0, + 0, + 0, + 2768, + 0, + 0, + 2770, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 2771, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 2772, + 0, + 0, + 0, + 0, + 0, + 2773, + 2776, + 0, + 0, + 2783, + 0, + 0, + 2784, + 0, + 2789, + 0, + 2790, + 0, + 0, + 0, + 2792, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 2793, + 2795, + 0, + 0, + 0, + 0, + 0, + 0, + 2796, + 0, + 0, + 0, + 0, + 0, + 0, + 2797, + 2799, + 0, + 0, + 0, + 0, + 2803, + 0, + 0, + 0, + 0, + 2806, + 0, + 2807, + 2808, + 2817, + 2819, + 0, + 0, + 0, + 0, + 0, + 2821, + 0, + 0, + 0, + 0, + 2822, + 2823, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 2824, + 0, + 0, + 2828, + 0, + 2834, + 0, + 0, + 0, + 0, + 0, + 0, + 2836, + 0, + 2838, + 0, + 0, + 2839, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 2841, + 0, + 0, + 0, + 2842, + 0, + 0, + 0, + 0, + 0, + 2843, + 2844, + 0, + 0, + 0, + 0, + 2846, + 0, + 0, + 2847, + 0, + 2849, + 0, + 2853, + 0, + 0, + 0, + 0, + 0, + 2857, + 0, + 0, + 0, + 0, + 2858, + 0, + 2859, + 0, + 0, + 2860, + 0, + 2862, + 2868, + 0, + 0, + 0, + 0, + 2875, + 0, + 2876, + 0, + 0, + 2877, + 2878, + 2884, + 2889, + 2890, + 0, + 0, + 2891, + 0, + 0, + 2892, + 0, + 0, + 0, + 2906, + 2912, + 0, + 2913, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 2916, + 0, + 2934, + 0, + 0, + 0, + 0, + 0, + 2935, + 0, + 0, + 0, + 0, + 2939, + 0, + 2940, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 2941, + 0, + 0, + 0, + 2946, + 0, + 2949, + 0, + 0, + 2950, + 2954, + 2955, + 0, + 0, + 0, + 2959, + 2961, + 0, + 0, + 2962, + 0, + 2963, + 0, + 0, + 0, + 0, + 0, + 0, + 2964, + 2965, + 2966, + 2967, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 2969, + 0, + 0, + 0, + 0, + 0, + 2970, + 2975, + 0, + 2982, + 2983, + 2984, + 0, + 0, + 0, + 0, + 0, + 2989, + 0, + 0, + 2990, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 2991, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 2998, + 0, + 3000, + 3001, + 0, + 0, + 3002, + 0, + 0, + 0, + 3003, + 0, + 0, + 3012, + 0, + 0, + 3022, + 0, + 0, + 3024, + 0, + 0, + 3025, + 3027, + 0, + 0, + 0, + 3030, + 0, + 0, + 0, + 0, + 3034, + 3035, + 0, + 0, + 3036, + 0, + 3039, + 0, + 3049, + 0, + 0, + 3050, + 0, + 0, + 0, + 0, + 0, + 0, + 3051, + 0, + 3053, + 0, + 0, + 0, + 0, + 3057, + 0, + 3058, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 3063, + 0, + 0, + 3073, + 3074, + 3078, + 3079, + 0, + 3080, + 3086, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 3087, + 0, + 3092, + 0, + 3095, + 0, + 3099, + 0, + 0, + 0, + 3100, + 0, + 3101, + 3102, + 0, + 3122, + 0, + 0, + 0, + 3124, + 0, + 3125, + 0, + 0, + 0, + 0, + 0, + 0, + 3132, + 3134, + 0, + 0, + 3136, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 3147, + 0, + 0, + 3149, + 0, + 0, + 0, + 0, + 0, + 3150, + 3151, + 3152, + 0, + 0, + 0, + 0, + 3158, + 0, + 0, + 3160, + 0, + 0, + 3161, + 0, + 0, + 3162, + 0, + 3163, + 3166, + 3168, + 0, + 0, + 3169, + 3170, + 0, + 0, + 3171, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 3182, + 0, + 3184, + 0, + 0, + 3188, + 0, + 0, + 3194, + 0, + 0, + 0, + 0, + 0, + 0, + 3204, + 0, + 0, + 0, + 0, + 3209, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 3216, + 3217, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 3219, + 0, + 0, + 3220, + 3222, + 0, + 3223, + 0, + 0, + 0, + 0, + 3224, + 0, + 3225, + 3226, + 0, + 3228, + 3233, + 0, + 3239, + 3241, + 3242, + 0, + 0, + 3251, + 3252, + 3253, + 3255, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 3260, + 0, + 0, + 3261, + 0, + 0, + 0, + 3267, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 3271, + 0, + 0, + 0, + 3278, + 0, + 3282, + 0, + 0, + 0, + 3284, + 0, + 0, + 0, + 3285, + 3286, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 3287, + 3292, + 0, + 0, + 0, + 0, + 3294, + 3296, + 0, + 0, + 3299, + 3300, + 3301, + 0, + 3302, + 0, + 0, + 0, + 0, + 0, + 3304, + 3306, + 0, + 0, + 0, + 0, + 0, + 0, + 3308, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 3311, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 3312, + 3314, + 3315, + 0, + 3318, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 3319, + 0, + 0, + 0, + 0, + 0, + 3321, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 3322, + 0, + 0, + 3324, + 3325, + 0, + 0, + 3326, + 0, + 0, + 3328, + 3329, + 3331, + 0, + 0, + 3335, + 0, + 0, + 3337, + 0, + 3338, + 0, + 0, + 0, + 0, + 3343, + 3347, + 0, + 0, + 0, + 3348, + 0, + 0, + 3351, + 0, + 0, + 0, + 0, + 0, + 0, + 3354, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 3355, + 0, + 0, + 3365, + 3366, + 3367, + 0, + 0, + 0, + 0, + 0, + 0, + 3368, + 3369, + 0, + 3370, + 0, + 0, + 3373, + 0, + 0, + 3376, + 0, + 0, + 3377, + 0, + 3379, + 3387, + 0, + 0, + 0, + 0, + 0, + 3390, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 3402, + 0, + 3403, + 3436, + 3437, + 3439, + 0, + 0, + 3441, + 0, + 0, + 0, + 3442, + 0, + 0, + 3449, + 0, + 0, + 0, + 3450, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 3451, + 0, + 0, + 3452, + 0, + 3453, + 3456, + 0, + 3457, + 0, + 0, + 3458, + 0, + 3459, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 3460, + 0, + 0, + 3469, + 3470, + 0, + 0, + 3475, + 0, + 0, + 0, + 3480, + 3487, + 3489, + 0, + 3490, + 0, + 0, + 3491, + 3499, + 0, + 3500, + 0, + 0, + 3501, + 0, + 0, + 0, + 3502, + 0, + 3514, + 0, + 0, + 0, + 3516, + 3517, + 0, + 0, + 0, + 3518, + 0, + 0, + 0, + 0, + 3520, + 3521, + 3522, + 0, + 0, + 3526, + 3530, + 0, + 0, + 0, + 0, + 3531, + 0, + 0, + 0, + 0, + 3536, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 3539, + 3541, + 0, + 0, + 3542, + 3544, + 0, + 3547, + 3548, + 0, + 0, + 3550, + 0, + 3553, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 3554, + 0, + 3555, + 0, + 3558, + 0, + 3559, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 3563, + 0, + 3581, + 0, + 0, + 0, + 3599, + 0, + 0, + 0, + 3600, + 0, + 3601, + 0, + 3602, + 3603, + 0, + 0, + 3606, + 3608, + 0, + 3610, + 3611, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 3612, + 3616, + 3619, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 3624, + 3628, + 0, + 3629, + 3634, + 3635, + 0, + 0, + 0, + 0, + 0, + 0, + 3636, + 0, + 3637, + 0, + 0, + 3638, + 3651, + 0, + 0, + 0, + 0, + 0, + 0, + 3652, + 3653, + 0, + 0, + 0, + 0, + 3656, + 3657, + 0, + 0, + 0, + 0, + 0, + 3658, + 0, + 0, + 0, + 0, + 3659, + 0, + 3661, + 3663, + 3664, + 0, + 3665, + 0, + 3692, + 0, + 0, + 0, + 3694, + 3696, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 3698, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 3700, + 0, + 0, + 3701, + 0, + 0, + 0, + 3708, + 3709, + 0, + 0, + 0, + 3711, + 3712, + 0, + 0, + 0, + 0, + 0, + 3723, + 0, + 3724, + 3725, + 0, + 0, + 3726, + 0, + 0, + 0, + 0, + 0, + 0, + 3728, + 3729, + 0, + 3734, + 3735, + 3737, + 0, + 0, + 0, + 3743, + 0, + 3745, + 0, + 0, + 3746, + 0, + 0, + 3747, + 3748, + 0, + 3757, + 0, + 3759, + 3766, + 3767, + 0, + 3768, + 0, + 0, + 0, + 0, + 3769, + 0, + 0, + 3771, + 0, + 3774, + 0, + 0, + 0, + 0, + 0, + 0, + 3775, + 0, + 0, + 0, + 0, + 0, + 0, + 3776, + 0, + 3777, + 3786, + 0, + 3788, + 3789, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 3791, + 0, + 3811, + 0, + 0, + 0, + 0, + 0, + 3814, + 3815, + 3816, + 3820, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 3821, + 0, + 0, + 3825, + 0, + 0, + 0, + 0, + 3835, + 0, + 0, + 3848, + 3849, + 0, + 0, + 0, + 0, + 3850, + 3851, + 3853, + 0, + 0, + 0, + 0, + 3859, + 0, + 3860, + 3862, + 0, + 0, + 0, + 0, + 0, + 3863, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 3873, + 0, + 3874, + 0, + 3875, + 3886, + 0, + 3887, + 0, + 0, + 0, + 0, + 3892, + 3913, + 0, + 3914, + 0, + 0, + 0, + 3925, + 3931, + 0, + 0, + 0, + 0, + 3934, + 3941, + 3942, + 0, + 0, + 0, + 0, + 3943, + 0, + 0, + 0, + 3944, + 0, + 0, + 0, + 0, + 0, + 3945, + 0, + 3947, + 0, + 0, + 0, + 3956, + 3957, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 3958, + 0, + 3959, + 3965, + 0, + 0, + 0, + 0, + 3966, + 0, + 0, + 0, + 3967, + 0, + 0, + 0, + 3968, + 3974, + 0, + 0, + 0, + 0, + 0, + 3975, + 3977, + 3978, + 0, + 0, + 0, + 0, + 3980, + 0, + 3985, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 3986, + 4011, + 0, + 0, + 4017, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 4018, + 0, + 0, + 0, + 0, + 4019, + 0, + 4023, + 0, + 0, + 0, + 4027, + 4028, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 4031, + 4034, + 0, + 0, + 4035, + 4037, + 4039, + 4040, + 0, + 0, + 0, + 0, + 0, + 4059, + 0, + 4060, + 4061, + 0, + 4062, + 4063, + 4066, + 0, + 0, + 4072, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 4088, + 0, + 0, + 0, + 0, + 0, + 4091, + 0, + 0, + 0, + 0, + 4094, + 4095, + 0, + 0, + 4096, + 0, + 0, + 0, + 0, + 0, + 4098, + 4099, + 0, + 0, + 0, + 4101, + 0, + 4104, + 0, + 0, + 0, + 4105, + 4108, + 0, + 4113, + 0, + 0, + 4115, + 4116, + 0, + 4126, + 0, + 0, + 4127, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 4128, + 4132, + 4133, + 0, + 4134, + 0, + 0, + 0, + 4137, + 0, + 0, + 4141, + 0, + 0, + 0, + 0, + 4144, + 4146, + 4147, + 0, + 0, + 0, + 0, + 4148, + 0, + 0, + 4311, + 0, + 0, + 0, + 4314, + 4329, + 0, + 4331, + 4332, + 0, + 4333, + 0, + 4334, + 0, + 0, + 0, + 4335, + 0, + 4336, + 0, + 0, + 0, + 4337, + 0, + 0, + 0, + 4342, + 4345, + 4346, + 4350, + 0, + 4351, + 4352, + 0, + 4354, + 4355, + 0, + 0, + 4364, + 0, + 0, + 0, + 0, + 4369, + 0, + 0, + 0, + 4373, + 0, + 4374, + 0, + 0, + 0, + 0, + 4377, + 0, + 0, + 0, + 0, + 4378, + 0, + 0, + 0, + 4380, + 0, + 0, + 0, + 4381, + 4382, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 4384, + 0, + 0, + 0, + 0, + 4385, + 0, + 0, + 0, + 4386, + 0, + 0, + 0, + 4391, + 4398, + 0, + 0, + 0, + 0, + 4407, + 4409, + 0, + 0, + 0, + 0, + 4410, + 0, + 0, + 4411, + 0, + 4414, + 4415, + 4418, + 0, + 4427, + 4428, + 4430, + 0, + 4431, + 0, + 4448, + 0, + 0, + 0, + 0, + 0, + 4449, + 0, + 0, + 0, + 4451, + 4452, + 0, + 4453, + 4454, + 0, + 4456, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 4459, + 0, + 4463, + 0, + 0, + 0, + 0, + 0, + 4466, + 0, + 4467, + 0, + 4469, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 4470, + 4471, + 0, + 4473, + 0, + 0, + 4475, + 0, + 0, + 0, + 0, + 4477, + 4478, + 0, + 0, + 0, + 4479, + 4481, + 0, + 4482, + 0, + 4484, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 4486, + 0, + 0, + 4488, + 0, + 0, + 4497, + 0, + 4508, + 0, + 0, + 4510, + 4511, + 0, + 4520, + 4523, + 0, + 4524, + 0, + 4525, + 0, + 4527, + 0, + 0, + 4528, + 0, + 0, + 0, + 0, + 4530, + 0, + 4531, + 0, + 0, + 4532, + 0, + 0, + 0, + 4533, + 0, + 0, + 0, + 0, + 0, + 4535, + 0, + 0, + 0, + 4536, + 0, + 0, + 0, + 0, + 0, + 4541, + 4543, + 4544, + 4545, + 4547, + 0, + 4548, + 0, + 0, + 0, + 0, + 4550, + 4551, + 0, + 4553, + 0, + 0, + 0, + 0, + 4562, + 0, + 0, + 4571, + 0, + 0, + 0, + 4574, + 0, + 0, + 0, + 4575, + 0, + 4576, + 0, + 4577, + 0, + 0, + 0, + 4581, + 0, + 0, + 0, + 0, + 0, + 4582, + 0, + 0, + 4586, + 0, + 0, + 0, + 4588, + 0, + 0, + 4597, + 0, + 4598, + 0, + 0, + 0, + 0, + 4616, + 4617, + 0, + 4618, + 0, + 0, + 0, + 0, + 4619, + 0, + 4620, + 0, + 0, + 4621, + 0, + 4624, + 0, + 0, + 0, + 0, + 0, + 4625, + 0, + 0, + 0, + 0, + 4657, + 0, + 4659, + 0, + 4667, + 0, + 0, + 0, + 4668, + 4670, + 0, + 4672, + 0, + 0, + 0, + 0, + 0, + 4673, + 4676, + 0, + 0, + 0, + 0, + 4687, + 0, + 0, + 0, + 0, + 4697, + 0, + 0, + 0, + 0, + 4699, + 0, + 4701, + 0, + 0, + 0, + 0, + 4702, + 0, + 0, + 4706, + 0, + 0, + 4713, + 0, + 0, + 0, + 4714, + 4715, + 4716, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 4717, + 0, + 0, + 4720, + 0, + 4721, + 4729, + 4735, + 0, + 0, + 0, + 4737, + 0, + 0, + 0, + 4739, + 0, + 0, + 0, + 4740, + 0, + 0, + 0, + 4741, + 0, + 0, + 0, + 0, + 0, + 4742, + 0, + 4745, + 4746, + 4747, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 4748, + 0, + 0, + 0, + 4749, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 4751, + 4786, + 0, + 4787, + 0, + 4788, + 4796, + 0, + 0, + 4797, + 4798, + 0, + 4799, + 4806, + 4807, + 0, + 0, + 0, + 0, + 4809, + 4810, + 0, + 0, + 0, + 0, + 0, + 0, + 4811, + 0, + 0, + 0, + 0, + 0, + 4812, + 0, + 4813, + 0, + 0, + 4815, + 0, + 4821, + 4822, + 0, + 0, + 0, + 0, + 4823, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 4824, + 0, + 0, + 0, + 0, + 4826, + 0, + 0, + 0, + 4828, + 0, + 4829, + 0, + 0, + 0, + 4843, + 0, + 0, + 4847, + 0, + 4853, + 4855, + 4858, + 0, + 0, + 0, + 0, + 0, + 4859, + 0, + 4864, + 0, + 0, + 4879, + 0, + 0, + 0, + 0, + 4880, + 0, + 0, + 0, + 0, + 4881, + 0, + 4882, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 4883, + 0, + 0, + 0, + 0, + 4884, + 0, + 0, + 0, + 0, + 0, + 4886, + 4887, + 4888, + 4894, + 4896, + 0, + 4902, + 0, + 0, + 4905, + 0, + 0, + 4915, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 4916, + 4917, + 4919, + 4921, + 0, + 0, + 0, + 0, + 0, + 4926, + 0, + 0, + 0, + 0, + 4927, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 4929, + 0, + 4930, + 4931, + 0, + 4938, + 0, + 4952, + 0, + 4953, + 4957, + 4960, + 4964, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 5019, + 5020, + 5022, + 0, + 0, + 0, + 0, + 0, + 5023, + 0, + 0, + 0, + 5024, + 0, + 0, + 0, + 5025, + 0, + 0, + 0, + 0, + 5028, + 0, + 0, + 0, + 0, + 5029, + 5030, + 5031, + 0, + 5033, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 5034, + 5035, + 0, + 5036, + 0, + 0, + 5037, + 0, + 0, + 0, + 0, + 5038, + 0, + 0, + 5039, + 0, + 0, + 0, + 5041, + 5042, + 0, + 0, + 0, + 0, + 5044, + 5049, + 5054, + 0, + 5055, + 0, + 5057, + 0, + 0, + 0, + 5060, + 0, + 0, + 0, + 0, + 0, + 5063, + 0, + 5064, + 5065, + 0, + 5067, + 0, + 0, + 0, + 5068, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 5076, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 5077, + 0, + 0, + 5078, + 5080, + 0, + 0, + 5083, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 5085, + 0, + 0, + 0, + 0, + 0, + 0, + 5098, + 5099, + 5101, + 5105, + 5107, + 0, + 5108, + 0, + 5109, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 5110, + 0, + 0, + 0, + 0, + 0, + 5117, + 5118, + 0, + 5121, + 0, + 5122, + 0, + 0, + 5130, + 0, + 0, + 0, + 5137, + 0, + 0, + 0, + 5148, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 5151, + 5154, + 0, + 0, + 0, + 5155, + 0, + 0, + 5156, + 5159, + 5161, + 0, + 0, + 0, + 0, + 5162, + 0, + 0, + 0, + 0, + 5163, + 5164, + 0, + 5166, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 5167, + 0, + 0, + 0, + 5172, + 0, + 0, + 0, + 0, + 0, + 0, + 5178, + 5179, + 0, + 0, + 5190, + 0, + 0, + 5191, + 5192, + 5194, + 0, + 0, + 5198, + 5201, + 0, + 0, + 0, + 0, + 0, + 5203, + 0, + 5206, + 5209, + 0, + 0, + 0, + 0, + 0, + 0, + 5213, + 0, + 5214, + 5216, + 0, + 0, + 0, + 0, + 0, + 5217, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 5218, + 5219, + 0, + 5231, + 0, + 0, + 5244, + 5249, + 0, + 5254, + 0, + 5255, + 0, + 0, + 5257, + 0, + 0, + 0, + 0, + 0, + 5258, + 0, + 5260, + 5270, + 0, + 5277, + 0, + 0, + 0, + 0, + 0, + 0, + 5280, + 5281, + 5282, + 5283, + 0, + 0, + 0, + 0, + 0, + 5284, + 0, + 5285, + 0, + 0, + 0, + 0, + 0, + 5287, + 5288, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 5289, + 5291, + 0, + 0, + 5294, + 0, + 0, + 5295, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 5304, + 0, + 0, + 5306, + 5307, + 5308, + 0, + 5309, + 0, + 0, + 5310, + 0, + 0, + 0, + 0, + 5311, + 5312, + 0, + 5313, + 0, + 0, + 0, + 0, + 0, + 5316, + 0, + 0, + 0, + 5317, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 5325, + 0, + 0, + 0, + 0, + 0, + 0, + 5326, + 0, + 5327, + 5329, + 0, + 5332, + 0, + 0, + 0, + 0, + 5338, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 5340, + 0, + 0, + 5341, + 0, + 0, + 0, + 5342, + 0, + 5343, + 5344, + 0, + 0, + 5345, + 0, + 0, + 0, + 0, + 0, + 0, + 5347, + 5348, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 5349, + 0, + 5350, + 0, + 5354, + 0, + 0, + 0, + 0, + 5358, + 0, + 0, + 5359, + 0, + 0, + 5361, + 0, + 0, + 5365, + 0, + 5367, + 0, + 5373, + 0, + 0, + 0, + 5379, + 0, + 0, + 0, + 5380, + 0, + 0, + 0, + 5382, + 0, + 5384, + 0, + 0, + 0, + 0, + 0, + 0, + 5385, + 0, + 0, + 0, + 0, + 5387, + 0, + 0, + 0, + 0, + 0, + 0, + 5388, + 5390, + 5393, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 5396, + 0, + 0, + 0, + 0, + 5397, + 5402, + 0, + 0, + 0, + 0, + 0, + 5403, + 0, + 0, + 0, + 5404, + 5405, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 5406, + 0, + 0, + 0, + 0, + 5410, + 0, + 0, + 5411, + 0, + 5415, + 0, + 0, + 0, + 0, + 5416, + 5434, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 5438, + 0, + 5440, + 0, + 0, + 0, + 0, + 0, + 0, + 5441, + 5442, + 0, + 0, + 0, + 5443, + 5444, + 5447, + 0, + 0, + 5448, + 5449, + 5451, + 0, + 0, + 0, + 5456, + 5457, + 0, + 0, + 0, + 5459, + 0, + 0, + 0, + 5461, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 5464, + 0, + 5466, + 0, + 0, + 5467, + 0, + 5470, + 0, + 0, + 5473, + 0, + 0, + 5474, + 0, + 0, + 5476, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 5477, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 5484, + 0, + 0, + 5485, + 5486, + 0, + 0, + 0, + 0, + 0, + 5488, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 5489, + 0, + 0, + 0, + 0, + 0, + 5507, + 0, + 0, + 0, + 5510, + 0, + 5511, + 0, + 0, + 5512, + 0, + 0, + 0, + 5513, + 0, + 5515, + 0, + 0, + 5516, + 5517, + 0, + 5518, + 0, + 0, + 5522, + 0, + 0, + 0, + 0, + 0, + 5534, + 5535, + 0, + 0, + 5536, + 0, + 5538, + 0, + 0, + 5543, + 0, + 5544, + 0, + 0, + 5545, + 0, + 5547, + 0, + 5557, + 0, + 0, + 5558, + 0, + 5560, + 5567, + 0, + 0, + 0, + 0, + 5568, + 0, + 0, + 0, + 5571, + 5573, + 0, + 5574, + 0, + 5575, + 0, + 0, + 0, + 0, + 5577, + 0, + 0, + 5598, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 5600, + 5609, + 0, + 0, + 0, + 0, + 5610, + 0, + 0, + 5612, + 0, + 5624, + 0, + 5625, + 0, + 0, + 0, + 5629, + 0, + 5641, + 0, + 5642, + 5643, + 0, + 0, + 0, + 0, + 0, + 0, + 5651, + 0, + 0, + 0, + 5652, + 5653, + 0, + 5661, + 5662, + 5678, + 0, + 5679, + 0, + 0, + 0, + 0, + 5685, + 5686, + 0, + 0, + 0, + 0, + 0, + 5690, + 5692, + 0, + 5703, + 0, + 0, + 0, + 0, + 0, + 5706, + 0, + 0, + 0, + 0, + 5707, + 0, + 0, + 0, + 0, + 0, + 0, + 5708, + 0, + 0, + 5709, + 0, + 5710, + 0, + 0, + 0, + 5712, + 0, + 5733, + 0, + 5734, + 5735, + 0, + 0, + 5744, + 5751, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 5752, + 0, + 5754, + 0, + 0, + 0, + 0, + 0, + 0, + 5757, + 5758, + 0, + 5760, + 5761, + 0, + 0, + 0, + 0, + 5763, + 5764, + 5765, + 0, + 5766, + 0, + 5767, + 5768, + 0, + 5770, + 0, + 0, + 0, + 0, + 5776, + 5780, + 0, + 0, + 0, + 0, + 5782, + 0, + 0, + 0, + 0, + 5784, + 0, + 0, + 5788, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 5797, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 5799, + 0, + 0, + 5801, + 0, + 0, + 0, + 5811, + 0, + 0, + 0, + 0, + 0, + 0, + 5816, + 0, + 0, + 5827, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 5830, + 5831, + 0, + 0, + 5832, + 0, + 0, + 5833, + 0, + 5835, + 5844, + 5845, + 0, + 5846, + 0, + 0, + 0, + 0, + 0, + 5850, + 0, + 0, + 0, + 0, + 0, + 5852, + 0, + 5855, + 5857, + 0, + 0, + 5859, + 0, + 5861, + 0, + 0, + 5863, + 0, + 5865, + 0, + 0, + 0, + 5873, + 5875, + 0, + 0, + 0, + 5877, + 0, + 5879, + 0, + 0, + 0, + 5888, + 0, + 0, + 5889, + 5891, + 0, + 5894, + 0, + 0, + 0, + 0, + 0, + 0, + 5895, + 0, + 5897, + 0, + 0, + 0, + 0, + 0, + 0, + 5907, + 0, + 5911, + 0, + 0, + 5912, + 0, + 5913, + 5922, + 5924, + 0, + 5927, + 5928, + 0, + 0, + 0, + 0, + 5929, + 5930, + 0, + 5933, + 0, + 0, + 0, + 0, + 5949, + 0, + 0, + 5951, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 5953, + 0, + 0, + 5954, + 0, + 5959, + 5960, + 5961, + 0, + 5964, + 0, + 0, + 0, + 5976, + 5978, + 5987, + 5990, + 0, + 0, + 0, + 0, + 0, + 5991, + 0, + 5992, + 0, + 0, + 0, + 5994, + 5995, + 0, + 0, + 5996, + 0, + 0, + 6001, + 6003, + 0, + 0, + 0, + 0, + 6007, + 0, + 0, + 0, + 0, + 0, + 6008, + 0, + 0, + 6009, + 0, + 6010, + 0, + 0, + 0, + 6011, + 6015, + 0, + 6017, + 0, + 6019, + 0, + 6023, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 6025, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 6026, + 0, + 6030, + 0, + 0, + 6032, + 0, + 0, + 0, + 6033, + 6038, + 6040, + 0, + 0, + 0, + 6041, + 6045, + 0, + 0, + 6046, + 0, + 0, + 6053, + 0, + 0, + 6054, + 0, + 6055, + 0, + 0, + 0, + 0, + 0, + 0, + 6057, + 0, + 6063, + 0, + 0, + 0, + 6064, + 0, + 6066, + 6071, + 6072, + 0, + 0, + 0, + 0, + 0, + 0, + 6075, + 6076, + 0, + 0, + 6077, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 6078, + 6079, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 6080, + 0, + 6083, + 0, + 0, + 0, + 0, + 0, + 6084, + 0, + 0, + 6088, + 0, + 6089, + 0, + 0, + 6093, + 6105, + 0, + 0, + 6107, + 0, + 6110, + 0, + 0, + 0, + 6111, + 6125, + 6126, + 0, + 0, + 0, + 6129, + 0, + 0, + 0, + 0, + 6130, + 0, + 0, + 0, + 6131, + 6134, + 0, + 0, + 0, + 0, + 0, + 0, + 6142, + 0, + 0, + 0, + 0, + 0, + 6144, + 0, + 0, + 6146, + 6151, + 6153, + 0, + 6156, + 0, + 6163, + 0, + 6180, + 6181, + 0, + 0, + 0, + 0, + 0, + 6182, + 0, + 0, + 0, + 0, + 6184, + 6195, + 0, + 0, + 6206, + 0, + 6208, + 0, + 0, + 6212, + 6213, + 6214, + 0, + 6215, + 0, + 0, + 0, + 6228, + 0, + 0, + 0, + 6234, + 0, + 0, + 0, + 0, + 0, + 0, + 6235, + 6240, + 0, + 6242, + 6243, + 6244, + 0, + 6250, + 6255, + 0, + 0, + 0, + 0, + 0, + 6257, + 0, + 0, + 0, + 6258, + 6278, + 0, + 6284, + 0, + 0, + 0, + 6285, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 6286, + 0, + 0, + 0, + 6320, + 0, + 0, + 6322, + 6332, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 6334, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 6335, + 0, + 0, + 6337, + 0, + 6338, + 0, + 6339, + 6340, + 0, + 0, + 6356, + 6357, + 6369, + 0, + 0, + 0, + 6370, + 6371, + 6372, + 0, + 6373, + 0, + 0, + 0, + 0, + 0, + 6376, + 0, + 0, + 0, + 0, + 0, + 6382, + 6383, + 6384, + 0, + 0, + 0, + 0, + 6386, + 0, + 6389, + 6397, + 6400, + 6411, + 0, + 6414, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 6415, + 6416, + 0, + 0, + 0, + 0, + 0, + 0, + 6417, + 0, + 0, + 0, + 0, + 6418, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 6420, + 0, + 6421, + 6423, + 6425, + 0, + 6429, + 6430, + 0, + 6433, + 6438, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 6439, + 6440, + 0, + 0, + 6441, + 0, + 0, + 6444, + 0, + 0, + 0, + 0, + 6446, + 0, + 0, + 0, + 0, + 6447, + 6448, + 0, + 0, + 6450, + 0, + 0, + 0, + 6454, + 0, + 0, + 6455, + 0, + 6461, + 0, + 0, + 0, + 0, + 0, + 0, + 6462, + 0, + 0, + 6463, + 0, + 6464, + 0, + 6465, + 6467, + 0, + 0, + 0, + 6468, + 0, + 6479, + 6480, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 6481, + 0, + 0, + 6485, + 6487, + 0, + 0, + 0, + 0, + 0, + 0, + 6493, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 6494, + 6495, + 6496, + 0, + 0, + 0, + 0, + 0, + 6498, + 0, + 0, + 0, + 6507, + 6508, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 6511, + 6512, + 0, + 0, + 0, + 0, + 6513, + 0, + 0, + 0, + 6514, + 0, + 0, + 0, + 0, + 0, + 6516, + 0, + 0, + 6517, + 6518, + 0, + 0, + 0, + 6519, + 6520, + 6521, + 0, + 6523, + 0, + 0, + 0, + 0, + 6524, + 6528, + 0, + 6530, + 0, + 0, + 6532, + 0, + 6578, + 0, + 0, + 0, + 6583, + 0, + 6584, + 0, + 0, + 0, + 6587, + 0, + 0, + 0, + 6590, + 0, + 6591, + 0, + 0, + 0, + 0, + 0, + 6592, + 0, + 0, + 0, + 0, + 6593, + 6594, + 0, + 0, + 0, + 0, + 0, + 6599, + 6600, + 0, + 0, + 6601, + 6602, + 6604, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 6608, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 6610, + 6611, + 0, + 6615, + 0, + 6616, + 6618, + 6620, + 0, + 6637, + 0, + 0, + 0, + 0, + 6639, + 0, + 0, + 0, + 0, + 6641, + 0, + 6642, + 0, + 0, + 0, + 6647, + 0, + 6660, + 6663, + 0, + 6664, + 0, + 6666, + 6669, + 0, + 6675, + 6676, + 6677, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 6678, + 0, + 0, + 0, + 6679, + 0, + 6680, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 6693, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 6704, + 6705, + 6706, + 0, + 0, + 6711, + 6713, + 0, + 0, + 0, + 0, + 0, + 6716, + 0, + 0, + 0, + 6717, + 0, + 6719, + 6724, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 6725, + 6726, + 0, + 0, + 0, + 0, + 0, + 6728, + 6729, + 6735, + 0, + 6737, + 6742, + 0, + 0, + 6743, + 6750, + 0, + 6751, + 0, + 0, + 6752, + 6753, + 0, + 0, + 0, + 0, + 0, + 0, + 6754, + 0, + 0, + 0, + 0, + 0, + 6756, + 0, + 0, + 0, + 0, + 0, + 0, + 6763, + 0, + 0, + 6764, + 6765, + 0, + 0, + 0, + 6770, + 0, + 0, + 0, + 6776, + 6780, + 0, + 6781, + 0, + 0, + 0, + 6783, + 0, + 6784, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 6785, + 0, + 0, + 0, + 6792, + 0, + 0, + 0, + 6793, + 0, + 0, + 6802, + 0, + 0, + 0, + 0, + 0, + 6803, + 0, + 0, + 0, + 6804, + 0, + 0, + 0, + 6812, + 0, + 0, + 6823, + 0, + 6824, + 6839, + 0, + 0, + 0, + 0, + 6852, + 0, + 0, + 6854, + 0, + 6856, + 6857, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 6867, + 0, + 6868, + 6870, + 6872, + 0, + 0, + 0, + 6873, + 6874, + 0, + 0, + 0, + 0, + 0, + 6875, + 0, + 0, + 6877, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 6878, + 0, + 0, + 0, + 6879, + 0, + 6880, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 6887, + 0, + 6888, + 6891, + 6893, + 0, + 6895, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 6899, + 0, + 0, + 0, + 0, + 6901, + 0, + 0, + 0, + 0, + 6910, + 0, + 6911, + 0, + 0, + 6912, + 0, + 0, + 6913, + 6914, + 0, + 0, + 0, + 6915, + 0, + 0, + 0, + 6916, + 6919, + 0, + 0, + 0, + 0, + 0, + 0, + 6924, + 0, + 6925, + 0, + 0, + 0, + 6926, + 6927, + 6928, + 0, + 6929, + 0, + 6930, + 0, + 0, + 6931, + 6935, + 0, + 6936, + 0, + 0, + 0, + 0, + 6939, + 6940, + 6941, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 6942, + 6948, + 6949, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 6952, + 6954, + 6963, + 6965, + 6966, + 0, + 0, + 6967, + 6968, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 6969, + 0, + 0, + 6970, + 6979, + 0, + 0, + 6980, + 0, + 0, + 6983, + 0, + 0, + 0, + 0, + 0, + 6984, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 6988, + 6990, + 6992, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 6995, + 0, + 0, + 0, + 7012, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 7019, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 7021, + 0, + 0, + 7022, + 7023, + 7028, + 0, + 7030, + 7033, + 0, + 0, + 0, + 0, + 0, + 0, + 7038, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 7039, + 0, + 0, + 0, + 0, + 0, + 7046, + 0, + 7047, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 7048, + 7052, + 0, + 0, + 0, + 0, + 0, + 7054, + 0, + 7060, + 0, + 0, + 0, + 0, + 7061, + 0, + 7065, + 0, + 0, + 0, + 0, + 7067, + 7069, + 0, + 7070, + 7071, + 7072, + 0, + 0, + 7078, + 0, + 7080, + 7081, + 0, + 7083, + 0, + 0, + 0, + 7084, + 7087, + 7088, + 0, + 0, + 7090, + 0, + 7093, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 7107, + 0, + 0, + 7108, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 7110, + 0, + 7114, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 7115, + 0, + 7116, + 0, + 0, + 0, + 0, + 0, + 7117, + 0, + 0, + 7118, + 0, + 0, + 7124, + 0, + 7125, + 0, + 0, + 7126, + 0, + 0, + 0, + 0, + 7128, + 0, + 0, + 0, + 0, + 0, + 7129, + 0, + 7130, + 0, + 7132, + 7133, + 0, + 0, + 7134, + 0, + 0, + 7139, + 0, + 7148, + 7150, + 0, + 0, + 0, + 0, + 7152, + 0, + 0, + 0, + 7153, + 7156, + 7157, + 0, + 0, + 0, + 0, + 0, + 7158, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 7163, + 7165, + 7169, + 0, + 7171, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 7172, + 0, + 7173, + 7181, + 0, + 0, + 0, + 0, + 0, + 7182, + 7185, + 0, + 0, + 0, + 0, + 7187, + 0, + 7201, + 7204, + 0, + 0, + 0, + 0, + 0, + 7206, + 7207, + 0, + 0, + 0, + 0, + 7211, + 7216, + 0, + 7218, + 0, + 0, + 0, + 0, + 7226, + 7228, + 7230, + 7232, + 7233, + 7235, + 7237, + 0, + 0, + 0, + 0, + 7238, + 7241, + 0, + 7242, + 0, + 0, + 7247, + 0, + 0, + 0, + 7266, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 7289, + 0, + 0, + 7290, + 7291, + 0, + 0, + 7292, + 0, + 7297, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 7300, + 0, + 7301, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 7302, + 0, + 0, + 0, + 0, + 7305, + 0, + 0, + 0, + 0, + 7307, + 0, + 7308, + 0, + 7310, + 0, + 7335, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 7337, + 0, + 7343, + 7347, + 0, + 0, + 0, + 0, + 0, + 7348, + 0, + 7349, + 7350, + 7352, + 7354, + 0, + 0, + 0, + 0, + 7357, + 0, + 7358, + 7366, + 0, + 7367, + 7368, + 0, + 0, + 7373, + 0, + 0, + 0, + 7374, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 7376, + 0, + 0, + 0, + 7377, + 0, + 0, + 0, + 0, + 0, + 7378, + 0, + 7379, + 7380, + 0, + 0, + 0, + 0, + 0, + 7383, + 0, + 0, + 7386, + 0, + 0, + 0, + 0, + 7398, + 0, + 0, + 0, + 7399, + 7400, + 0, + 7401, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 7402, + 0, + 0, + 0, + 0, + 0, + 7405, + 0, + 0, + 0, + 0, + 0, + 7406, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 7421, + 7427, + 7429, + 0, + 0, + 0, + 7435, + 0, + 0, + 7436, + 0, + 0, + 0, + 7437, + 0, + 0, + 0, + 0, + 0, + 0, + 7438, + 7443, + 0, + 7446, + 0, + 7448, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 7456, + 0, + 0, + 0, + 0, + 0, + 7457, + 0, + 0, + 7461, + 0, + 0, + 0, + 0, + 0, + 7462, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 7463, + 7466, + 7472, + 0, + 7476, + 0, + 0, + 7490, + 0, + 7491, + 0, + 0, + 7493, + 0, + 0, + 0, + 7498, + 7499, + 0, + 0, + 7508, + 0, + 0, + 0, + 0, + 0, + 7512, + 0, + 0, + 0, + 7513, + 7514, + 7516, + 0, + 0, + 0, + 0, + 7518, + 0, + 0, + 7519, + 7521, + 7522, + 0, + 0, + 0, + 7526, + 0, + 0, + 7529, + 0, + 0, + 7531, + 0, + 7536, + 0, + 7538, + 0, + 7539, + 0, + 0, + 7541, + 7542, + 7546, + 0, + 0, + 0, + 0, + 0, + 7547, + 0, + 7548, + 0, + 0, + 0, + 0, + 0, + 7550, + 0, + 0, + 7552, + 7553, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 7554, + 7563, + 0, + 7573, + 0, + 0, + 0, + 0, + 0, + 0, + 7574, + 7576, + 0, + 7578, + 7581, + 7583, + 0, + 0, + 0, + 7584, + 0, + 7587, + 0, + 0, + 0, + 0, + 0, + 7589, + 0, + 0, + 0, + 7594, + 0, + 0, + 7595, + 0, + 0, + 7600, + 7602, + 7610, + 0, + 0, + 0, + 0, + 0, + 7612, + 0, + 7613, + 7614, + 0, + 0, + 7615, + 0, + 0, + 7616, + 0, + 7620, + 0, + 7621, + 7622, + 0, + 7623, + 0, + 0, + 0, + 0, + 7626, + 0, + 0, + 0, + 0, + 7627, + 7629, + 7631, + 0, + 0, + 7633, + 0, + 0, + 0, + 0, + 0, + 7639, + 0, + 7640, + 7642, + 0, + 0, + 7643, + 0, + 0, + 0, + 0, + 7644, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 7645, + 0, + 0, + 0, + 0, + 0, + 7661, + 7662, + 7663, + 7665, + 0, + 7666, + 0, + 7667, + 0, + 7684, + 7688, + 7690, + 0, + 7691, + 0, + 0, + 0, + 0, + 0, + 0, + 7692, + 0, + 0, + 7700, + 0, + 7707, + 0, + 7708, + 0, + 7709, + 0, + 7721, + 0, + 0, + 0, + 7722, + 0, + 7724, + 0, + 0, + 0, + 0, + 0, + 0, + 7729, + 7731, + 0, + 7732, + 0, + 7733, + 7735, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 7739, + 0, + 0, + 7741, + 7745, + 0, + 7748, + 0, + 0, + 0, + 7751, + 0, + 0, + 0, + 7752, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 7753, + 0, + 0, + 7756, + 0, + 7757, + 0, + 7759, + 0, + 7760, + 0, + 0, + 0, + 0, + 7761, + 7768, + 0, + 0, + 7769, + 0, + 0, + 7770, + 0, + 0, + 7771, + 0, + 0, + 7772, + 0, + 0, + 7773, + 0, + 0, + 0, + 0, + 0, + 7778, + 7783, + 0, + 0, + 0, + 0, + 0, + 7784, + 7785, + 0, + 7790, + 0, + 0, + 0, + 0, + 7792, + 0, + 7798, + 0, + 0, + 0, + 0, + 0, + 7799, + 0, + 7810, + 0, + 0, + 7813, + 0, + 7814, + 0, + 7816, + 0, + 7818, + 7824, + 7825, + 7826, + 0, + 7828, + 7830, + 0, + 0, + 0, + 7840, + 0, + 7842, + 0, + 7843, + 0, + 0, + 0, + 0, + 7844, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 7846, + 0, + 0, + 0, + 0, + 0, + 7856, + 7857, + 7858, + 7862, + 0, + 7865, + 0, + 0, + 7866, + 0, + 0, + 7913, + 0, + 0, + 0, + 0, + 7914, + 0, + 0, + 7915, + 7917, + 7918, + 7919, + 0, + 7920, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 7921, + 7922, + 0, + 7924, + 0, + 0, + 7925, + 0, + 0, + 7927, + 0, + 7930, + 7935, + 0, + 0, + 7937, + 0, + 0, + 0, + 0, + 0, + 0, + 7939, + 0, + 7940, + 0, + 0, + 0, + 0, + 0, + 7941, + 0, + 0, + 0, + 0, + 7945, + 0, + 0, + 0, + 0, + 7949, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 7950, + 0, + 7953, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 7968, + 0, + 0, + 0, + 0, + 7969, + 7972, + 7992, + 0, + 7993, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 7994, + 0, + 0, + 0, + 0, + 8007, + 8008, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 8010, + 0, + 0, + 0, + 8012, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 8018, + 0, + 8028, + 8029, + 0, + 0, + 8030, + 0, + 0, + 8032, + 8033, + 0, + 0, + 8034, + 8036, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 8037, + 0, + 0, + 0, + 8043, + 8052, + 8059, + 8060, + 0, + 0, + 8061, + 0, + 0, + 0, + 8062, + 0, + 8063, + 0, + 8064, + 0, + 8066, + 8068, + 0, + 0, + 0, + 8080, + 8081, + 0, + 8089, + 0, + 0, + 0, + 0, + 0, + 8092, + 0, + 0, + 0, + 0, + 0, + 0, + 8093, + 8110, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 8111, + 0, + 0, + 0, + 0, + 0, + 8112, + 8115, + 0, + 8117, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 8120, + 8121, + 8122, + 8128, + 8129, + 8130, + 8131, + 0, + 0, + 8139, + 0, + 0, + 8144, + 0, + 0, + 0, + 0, + 8145, + 8146, + 8153, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 8154, + 0, + 8157, + 8160, + 8162, + 0, + 8164, + 8165, + 0, + 0, + 0, + 0, + 8166, + 8167, + 0, + 0, + 8179, + 0, + 0, + 0, + 8185, + 0, + 0, + 0, + 8186, + 0, + 0, + 8187, + 0, + 0, + 0, + 8188, + 0, + 0, + 0, + 0, + 0, + 8204, + 0, + 0, + 0, + 0, + 8210, + 0, + 0, + 0, + 0, + 0, + 8213, + 0, + 8214, + 0, + 0, + 8215, + 0, + 0, + 0, + 0, + 0, + 0, + 8218, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 8219, + 0, + 8221, + 0, + 0, + 8222, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 8225, + 0, + 0, + 0, + 8233, + 0, + 0, + 8242, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 8247, + 0, + 8248, + 8252, + 0, + 8256, + 8257, + 0, + 0, + 8261, + 0, + 8264, + 8265, + 0, + 0, + 0, + 0, + 8267, + 0, + 0, + 0, + 8269, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 8270, + 0, + 0, + 0, + 8278, + 0, + 8279, + 8283, + 0, + 0, + 8285, + 8286, + 8289, + 8292, + 0, + 0, + 0, + 0, + 8293, + 8295, + 8299, + 8300, + 8301, + 0, + 0, + 0, + 0, + 0, + 0, + 8304, + 8307, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 8321, + 0, + 0, + 0, + 8322, + 8323, + 8325, + 8326, + 8327, + 0, + 0, + 8332, + 8338, + 0, + 0, + 8340, + 0, + 0, + 0, + 0, + 0, + 8350, + 0, + 0, + 8351, + 0, + 8354, + 8355, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 8360, + 8372, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 8377, + 0, + 0, + 0, + 0, + 8380, + 0, + 0, + 0, + 8383, + 0, + 8384, + 0, + 0, + 0, + 0, + 8386, + 8392, + 0, + 0, + 8394, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 8396, + 8397, + 0, + 8398, + 0, + 8399, + 0, + 0, + 0, + 0, + 0, + 8400, + 0, + 8401, + 8410, + 8411, + 0, + 8412, + 8413, + 8422, + 0, + 0, + 0, + 0, + 8423, + 0, + 0, + 0, + 0, + 8424, + 0, + 0, + 8425, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 8441, + 8442, + 0, + 0, + 0, + 0, + 0, + 0, + 8443, + 0, + 0, + 8444, + 0, + 8447, + 0, + 0, + 0, + 0, + 8451, + 0, + 8458, + 0, + 8462, + 0, + 0, + 8468, + 0, + 8469, + 0, + 0, + 0, + 8470, + 0, + 8473, + 8479, + 8480, + 0, + 0, + 0, + 0, + 8481, + 8483, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 8484, + 0, + 0, + 8490, + 0, + 0, + 0, + 0, + 0, + 0, + 8491, + 8493, + 8494, + 0, + 8528, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 8530, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 8534, + 8538, + 8540, + 0, + 0, + 8541, + 0, + 0, + 8545, + 0, + 8557, + 0, + 0, + 8569, + 8570, + 0, + 0, + 8571, + 8574, + 8575, + 8579, + 0, + 8583, + 0, + 0, + 0, + 0, + 8591, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 8606, + 0, + 8607, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 8608, + 0, + 0, + 8609, + 0, + 0, + 0, + 8610, + 0, + 0, + 0, + 8611, + 0, + 0, + 8613, + 8617, + 8621, + 0, + 0, + 8622, + 0, + 8623, + 0, + 8624, + 8625, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 8637, + 8638, + 8639, + 8650, + 0, + 0, + 0, + 0, + 8652, + 8654, + 8655, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 8656, + 0, + 0, + 0, + 0, + 0, + 8657, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 8658, + 0, + 0, + 8659, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 8660, + 0, + 0, + 0, + 0, + 0, + 0, + 8661, + 8663, + 8664, + 0, + 0, + 0, + 0, + 8665, + 0, + 8669, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 8671, + 8674, + 0, + 8684, + 0, + 8686, + 0, + 0, + 0, + 8689, + 0, + 0, + 0, + 8690, + 0, + 8706, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 8710, + 0, + 8711, + 8713, + 8714, + 8724, + 8727, + 8728, + 8733, + 8736, + 0, + 8737, + 8739, + 0, + 0, + 0, + 0, + 8742, + 8743, + 8745, + 8754, + 0, + 0, + 0, + 0, + 8756, + 0, + 0, + 0, + 0, + 0, + 0, + 8757, + 8760, + 0, + 0, + 0, + 0, + 0, + 8762, + 8763, + 8764, + 0, + 8766, + 8769, + 8770, + 8773, + 0, + 8774, + 0, + 8779, + 0, + 0, + 0, + 0, + 8780, + 0, + 0, + 8781, + 0, + 0, + 8783, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 8784, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 8785, + 0, + 0, + 0, + 0, + 8786, + 0, + 0, + 0, + 0, + 8788, + 8790, + 0, + 0, + 0, + 8803, + 0, + 8813, + 8814, + 0, + 0, + 0, + 0, + 0, + 8815, + 8816, + 0, + 0, + 0, + 0, + 8818, + 0, + 0, + 0, + 0, + 8822, + 8828, + 8829, + 0, + 8831, + 0, + 0, + 0, + 0, + 8833, + 0, + 0, + 0, + 8834, + 0, + 0, + 0, + 8835, + 0, + 8836, + 0, + 0, + 0, + 8837, + 0, + 0, + 0, + 0, + 0, + 0, + 8838, + 8839, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 8840, + 0, + 0, + 0, + 8841, + 0, + 8842, + 0, + 0, + 0, + 8846, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 8847, + 0, + 8848, + 0, + 0, + 8864, + 0, + 0, + 8866, + 0, + 0, + 8870, + 8872, + 0, + 0, + 8873, + 8874, + 0, + 0, + 0, + 0, + 0, + 0, + 8875, + 0, + 8876, + 0, + 0, + 0, + 0, + 8896, + 8900, + 0, + 0, + 0, + 0, + 8901, + 0, + 0, + 0, + 0, + 0, + 8904, + 0, + 8907, + 0, + 0, + 0, + 0, + 8911, + 8912, + 8913, + 0, + 0, + 0, + 8914, + 0, + 8915, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 8916, + 0, + 0, + 0, + 8929, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 8930, + 0, + 8932, + 0, + 8943, + 0, + 0, + 0, + 8945, + 8947, + 0, + 0, + 0, + 0, + 8949, + 0, + 8950, + 0, + 8954, + 8957, + 0, + 0, + 8970, + 0, + 0, + 0, + 0, + 8971, + 0, + 8996, + 0, + 0, + 0, + 0, + 8997, + 9000, + 0, + 0, + 0, + 0, + 9001, + 9002, + 0, + 9004, + 9009, + 9024, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 9027, + 9082, + 0, + 0, + 9083, + 9089, + 0, + 0, + 0, + 0, + 0, + 0, + 9090, + 0, + 0, + 0, + 9092, + 0, + 0, + 9093, + 0, + 9095, + 0, + 0, + 9096, + 9097, + 9101, + 9102, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 9112, + 0, + 0, + 0, + 0, + 0, + 0, + 9114, + 0, + 0, + 9120, + 0, + 9121, + 9122, + 0, + 0, + 0, + 9123, + 9124, + 0, + 0, + 9125, + 0, + 0, + 9126, + 0, + 9127, + 0, + 0, + 9129, + 9131, + 0, + 0, + 0, + 9132, + 0, + 0, + 9136, + 0, + 9144, + 0, + 0, + 9148, + 0, + 0, + 0, + 0, + 0, + 0, + 9149, + 0, + 9152, + 9163, + 0, + 0, + 9165, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 9166, + 0, + 9169, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 9170, + 0, + 0, + 0, + 0, + 9172, + 0, + 9174, + 9175, + 9176, + 0, + 9177, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 9186, + 0, + 9187, + 0, + 0, + 0, + 9188, + 9189, + 0, + 0, + 9190, + 0, + 0, + 0, + 0, + 9191, + 0, + 0, + 0, + 9193, + 0, + 0, + 0, + 0, + 9197, + 9198, + 0, + 0, + 0, + 9208, + 9211, + 0, + 0, + 0, + 0, + 9216, + 9217, + 0, + 9220, + 0, + 0, + 0, + 0, + 9221, + 9222, + 9223, + 0, + 9224, + 9225, + 0, + 0, + 9227, + 0, + 9228, + 9229, + 0, + 0, + 9230, + 0, + 9232, + 0, + 9233, + 0, + 0, + 0, + 0, + 0, + 9234, + 9235, + 0, + 0, + 9237, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 9238, + 9240, + 0, + 0, + 9241, + 0, + 0, + 0, + 0, + 9244, + 0, + 0, + 0, + 0, + 9247, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 9248, + 0, + 0, + 0, + 9249, + 0, + 0, + 0, + 0, + 0, + 9250, + 0, + 0, + 0, + 0, + 9251, + 0, + 0, + 9252, + 9255, + 0, + 0, + 0, + 9256, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 9257, + 0, + 0, + 9258, + 0, + 0, + 0, + 0, + 0, + 0, + 9259, + 0, + 0, + 0, + 0, + 0, + 9262, + 9263, + 0, + 0, + 9265, + 9266, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 9268, + 9271, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 9273, + 0, + 0, + 0, + 9276, + 9277, + 9279, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 9280, + 0, + 0, + 9293, + 0, + 0, + 0, + 0, + 0, + 9297, + 9301, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 9308, + 9309, + 9313, + 9321, + 9322, + 0, + 9326, + 9327, + 0, + 0, + 9477, + 0, + 9479, + 0, + 0, + 0, + 0, + 9482, + 0, + 0, + 0, + 9483, + 0, + 9484, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 9485, + 0, + 0, + 9486, + 0, + 0, + 0, + 9489, + 0, + 0, + 0, + 0, + 9490, + 9491, + 0, + 0, + 0, + 0, + 9493, + 0, + 9495, + 9496, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 9500, + 0, + 9502, + 0, + 0, + 0, + 0, + 0, + 9504, + 9507, + 0, + 9509, + 0, + 9511, + 0, + 0, + 9513, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 9515, + 0, + 0, + 0, + 0, + 0, + 0, + 9516, + 9517, + 0, + 0, + 0, + 0, + 9532, + 0, + 0, + 9533, + 0, + 0, + 9538, + 0, + 9539, + 9540, + 0, + 0, + 0, + 0, + 9541, + 0, + 0, + 0, + 9542, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 9544, + 9545, + 0, + 9546, + 0, + 0, + 0, + 0, + 0, + 0, + 9547, + 9548, + 0, + 0, + 0, + 9550, + 0, + 9557, + 0, + 9558, + 0, + 9561, + 0, + 9563, + 9570, + 0, + 9572, + 9574, + 9575, + 0, + 0, + 0, + 9577, + 9592, + 0, + 0, + 9596, + 0, + 0, + 0, + 9598, + 0, + 9600, + 0, + 9601, + 0, + 0, + 0, + 0, + 0, + 0, + 9608, + 0, + 9638, + 9639, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 9641, + 0, + 0, + 9643, + 9644, + 9645, + 9646, + 0, + 0, + 0, + 9648, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 9650, + 9654, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 9655, + 0, + 0, + 0, + 0, + 0, + 9656, + 0, + 9657, + 0, + 0, + 0, + 0, + 9658, + 0, + 0, + 9659, + 0, + 0, + 9664, + 0, + 0, + 9665, + 0, + 9667, + 9669, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 9671, + 0, + 9673, + 9681, + 0, + 0, + 0, + 0, + 9682, + 9683, + 9684, + 0, + 0, + 0, + 0, + 9686, + 9698, + 0, + 0, + 9700, + 9701, + 9702, + 0, + 9703, + 9717, + 0, + 0, + 0, + 0, + 9718, + 0, + 9726, + 0, + 0, + 0, + 0, + 9727, + 0, + 0, + 0, + 9728, + 0, + 9742, + 0, + 9744, + 0, + 0, + 0, + 9750, + 0, + 9754, + 9755, + 0, + 0, + 0, + 0, + 0, + 9756, + 0, + 9757, + 9768, + 0, + 9769, + 0, + 0, + 0, + 9770, + 9771, + 0, + 9773, + 0, + 9774, + 0, + 9775, + 0, + 0, + 0, + 9776, + 9777, + 9784, + 0, + 0, + 0, + 9786, + 0, + 9789, + 0, + 0, + 0, + 0, + 9793, + 9794, + 0, + 0, + 0, + 9808, + 0, + 0, + 0, + 0, + 0, + 9811, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 9812, + 0, + 9820, + 0, + 9823, + 0, + 9828, + 0, + 0, + 0, + 0, + 9830, + 0, + 0, + 9833, + 9836, + 0, + 0, + 0, + 9840, + 0, + 0, + 0, + 9841, + 0, + 0, + 9842, + 0, + 9845, + 0, + 0, + 0, + 9847, + 9848, + 0, + 0, + 9855, + 0, + 0, + 0, + 0, + 0, + 0, + 9856, + 9863, + 9865, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 9866, + 9867, + 9868, + 9873, + 9875, + 0, + 0, + 0, + 0, + 0, + 0, + 9880, + 0, + 9886, + 0, + 0, + 0, + 9887, + 0, + 0, + 9891, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 9906, + 9907, + 9908, + 0, + 0, + 0, + 9909, + 0, + 0, + 0, + 0, + 0, + 0, + 9910, + 0, + 0, + 0, + 0, + 9913, + 0, + 0, + 0, + 0, + 9914, + 0, + 0, + 0, + 0, + 0, + 9922, + 0, + 0, + 0, + 0, + 9923, + 9925, + 0, + 0, + 0, + 0, + 0, + 0, + 9930, + 0, + 0, + 0, + 9931, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 9932, + 0, + 9939, + 0, + 0, + 9940, + 9962, + 9966, + 0, + 9969, + 9970, + 0, + 0, + 9974, + 0, + 9979, + 9981, + 9982, + 0, + 0, + 0, + 9985, + 0, + 0, + 0, + 0, + 0, + 0, + 9987, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 9988, + 9993, + 0, + 0, + 9994, + 0, + 0, + 0, + 9997, + 0, + 10004, + 0, + 0, + 0, + 0, + 0, + 10007, + 10019, + 10020, + 10022, + 0, + 0, + 0, + 10031, + 0, + 0, + 0, + 0, + 0, + 10032, + 0, + 0, + 10034, + 0, + 10036, + 0, + 0, + 0, + 0, + 10038, + 0, + 10039, + 10040, + 10041, + 10042, + 0, + 0, + 0, + 0, + 0, + 10043, + 0, + 0, + 0, + 0, + 0, + 10045, + 10054, + 0, + 0, + 0, + 0, + 10055, + 0, + 0, + 10057, + 10058, + 0, + 0, + 0, + 0, + 0, + 0, + 10059, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 10060, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 10063, + 0, + 10066, + 0, + 0, + 0, + 10070, + 0, + 10072, + 0, + 0, + 10076, + 10077, + 0, + 0, + 10084, + 0, + 10087, + 10090, + 10091, + 0, + 0, + 0, + 10094, + 10097, + 0, + 0, + 0, + 0, + 0, + 0, + 10098, + 0, + 0, + 0, + 0, + 0, + 0, + 10103, + 0, + 10104, + 0, + 10108, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 10120, + 0, + 0, + 0, + 10122, + 0, + 0, + 10125, + 0, + 0, + 0, + 0, + 10127, + 10128, + 0, + 0, + 10134, + 0, + 10135, + 10136, + 0, + 10137, + 0, + 0, + 10147, + 0, + 10149, + 10150, + 0, + 0, + 10156, + 0, + 10158, + 10159, + 10160, + 10168, + 0, + 0, + 10171, + 0, + 10173, + 0, + 0, + 0, + 10176, + 0, + 0, + 0, + 0, + 10177, + 0, + 0, + 0, + 0, + 10178, + 0, + 0, + 0, + 0, + 10194, + 0, + 10202, + 0, + 0, + 10203, + 10204, + 0, + 10205, + 10206, + 0, + 10207, + 0, + 0, + 0, + 0, + 10209, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 10213, + 0, + 0, + 0, + 0, + 0, + 0, + 10217, + 0, + 10229, + 0, + 10230, + 10231, + 0, + 0, + 10232, + 0, + 0, + 10237, + 10238, + 10244, + 0, + 0, + 0, + 0, + 0, + 10250, + 0, + 10252, + 0, + 0, + 0, + 0, + 0, + 0, + 10255, + 0, + 0, + 10257, + 0, + 0, + 0, + 0, + 0, + 0, + 10258, + 0, + 10259, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 10260, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 10284, + 10288, + 10289, + 0, + 0, + 0, + 10290, + 0, + 10296, + 0, + 0, + 0, + 0, + 0, + 10297, + 0, + 0, + 0, + 0, + 0, + 0, + 10298, + 0, + 0, + 0, + 0, + 10299, + 10303, + 0, + 0, + 0, + 0, + 0, + 10306, + 0, + 0, + 0, + 10307, + 0, + 10308, + 0, + 0, + 0, + 0, + 10311, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 10315, + 10317, + 0, + 0, + 0, + 10318, + 10319, + 0, + 10321, + 0, + 10326, + 0, + 10328, + 0, + 0, + 0, + 0, + 10329, + 0, + 0, + 10331, + 0, + 10332, + 0, + 0, + 0, + 0, + 0, + 0, + 10334, + 0, + 0, + 10335, + 10338, + 0, + 0, + 0, + 0, + 0, + 10339, + 10349, + 0, + 0, + 0, + 0, + 0, + 0, + 10351, + 0, + 10353, + 0, + 0, + 0, + 0, + 0, + 0, + 10362, + 0, + 10368, + 0, + 10369, + 0, + 0, + 0, + 10372, + 10373, + 0, + 0, + 0, + 0, + 0, + 10374, + 0, + 0, + 0, + 10375, + 0, + 10376, + 0, + 0, + 10386, + 10388, + 10390, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 10391, + 0, + 0, + 10392, + 10394, + 0, + 0, + 10396, + 0, + 10397, + 0, + 10403, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 10404, + 0, + 10405, + 10410, + 0, + 0, + 10411, + 0, + 10412, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 10421, + 10422, + 10423, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 10425, + 0, + 0, + 10427, + 0, + 0, + 10430, + 0, + 0, + 0, + 0, + 0, + 10432, + 0, + 10433, + 10434, + 0, + 0, + 0, + 0, + 10436, + 10437, + 0, + 10438, + 0, + 10439, + 0, + 10444, + 10446, + 0, + 0, + 0, + 0, + 0, + 10448, + 0, + 0, + 0, + 0, + 0, + 10449, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 10451, + 0, + 10453, + 0, + 0, + 0, + 10454, + 10457, + 0, + 0, + 10459, + 0, + 10469, + 0, + 0, + 0, + 0, + 0, + 10472, + 10481, + 0, + 0, + 0, + 0, + 0, + 10482, + 10483, + 0, + 10492, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 10499, + 0, + 0, + 0, + 10502, + 0, + 0, + 10510, + 0, + 10521, + 10524, + 0, + 0, + 10525, + 10526, + 10528, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 10530, + 0, + 0, + 0, + 0, + 10533, + 0, + 10534, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 10535, + 10536, + 0, + 0, + 10544, + 0, + 10553, + 10556, + 0, + 10557, + 10559, + 0, + 0, + 0, + 0, + 0, + 10562, + 10563, + 10564, + 0, + 10565, + 0, + 0, + 0, + 10566, + 0, + 10567, + 0, + 0, + 0, + 0, + 10575, + 0, + 0, + 10576, + 0, + 10578, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 10585, + 10586, + 10587, + 10589, + 0, + 10590, + 0, + 0, + 10594, + 0, + 0, + 0, + 0, + 0, + 10598, + 0, + 0, + 10601, + 0, + 0, + 0, + 10602, + 0, + 10603, + 0, + 10604, + 0, + 10605, + 0, + 0, + 10607, + 0, + 10626, + 0, + 10627, + 0, + 0, + 0, + 0, + 0, + 10629, + 10630, + 10631, + 0, + 0, + 0, + 10646, + 0, + 0, + 0, + 10647, + 0, + 10650, + 0, + 10651, + 0, + 0, + 0, + 10652, + 10653, + 10655, + 0, + 10658, + 0, + 0, + 10659, + 0, + 10667, + 0, + 0, + 0, + 0, + 10669, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 10670, + 0, + 0, + 0, + 10671, + 0, + 0, + 0, + 0, + 10672, + 10673, + 0, + 10674, + 0, + 0, + 0, + 10676, + 0, + 0, + 0, + 0, + 0, + 0, + 10678, + 0, + 10682, + 0, + 0, + 10692, + 0, + 10697, + 0, + 0, + 0, + 0, + 10698, + 0, + 0, + 0, + 10700, + 0, + 0, + 0, + 0, + 0, + 10703, + 0, + 10704, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 10705, + 0, + 10715, + 10718, + 10720, + 0, + 0, + 10722, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 10723, + 0, + 0, + 0, + 0, + 10726, + 0, + 0, + 0, + 0, + 0, + 10727, + 10730, + 10743, + 0, + 0, + 0, + 0, + 0, + 0, + 10744, + 0, + 0, + 10745, + 0, + 0, + 0, + 0, + 0, + 0, + 10748, + 0, + 0, + 0, + 0, + 10750, + 0, + 0, + 10752, + 10753, + 0, + 0, + 0, + 10756, + 0, + 0, + 0, + 0, + 0, + 0, + 10758, + 0, + 0, + 0, + 10759, + 0, + 10769, + 0, + 0, + 10772, + 0, + 0, + 0, + 0, + 0, + 0, + 10773, + 0, + 0, + 0, + 10777, + 0, + 0, + 10779, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 10780, + 10784, + 0, + 0, + 0, + 10789, + 0, + 0, + 0, + 10791, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 10795, + 0, + 0, + 10796, + 0, + 10808, + 0, + 10809, + 0, + 0, + 0, + 10810, + 0, + 0, + 0, + 10812, + 0, + 0, + 10814, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 10815, + 0, + 0, + 0, + 0, + 10816, + 10817, + 0, + 0, + 0, + 0, + 10819, + 0, + 10820, + 0, + 0, + 0, + 0, + 10821, + 10822, + 10823, + 0, + 10826, + 10849, + 0, + 0, + 0, + 0, + 10850, + 0, + 0, + 10852, + 0, + 10853, + 0, + 0, + 10856, + 0, + 0, + 10857, + 10858, + 10859, + 10860, + 0, + 0, + 0, + 0, + 0, + 0, + 10863, + 0, + 10866, + 10867, + 10872, + 10890, + 0, + 0, + 10891, + 10892, + 0, + 0, + 0, + 0, + 0, + 10893, + 0, + 0, + 0, + 10896, + 10899, + 0, + 0, + 10900, + 10902, + 0, + 0, + 0, + 0, + 0, + 10903, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 10905, + 0, + 10906, + 0, + 0, + 0, + 0, + 10908, + 10911, + 0, + 10912, + 0, + 0, + 10916, + 0, + 0, + 0, + 0, + 0, + 10917, + 0, + 10918, + 0, + 0, + 0, + 10923, + 0, + 0, + 0, + 0, + 0, + 10924, + 0, + 0, + 10928, + 10929, + 0, + 0, + 10930, + 0, + 0, + 0, + 10932, + 0, + 0, + 0, + 0, + 10939, + 0, + 0, + 10945, + 0, + 0, + 0, + 10947, + 0, + 0, + 10948, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 10958, + 0, + 10960, + 10962, + 0, + 0, + 10964, + 0, + 0, + 0, + 10966, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 10967, + 0, + 0, + 0, + 10968, + 0, + 0, + 0, + 10973, + 0, + 0, + 0, + 0, + 0, + 10975, + 0, + 0, + 0, + 10976, + 10978, + 0, + 0, + 10982, + 10984, + 10987, + 0, + 0, + 10988, + 0, + 10989, + 0, + 0, + 10991, + 0, + 0, + 0, + 0, + 10992, + 0, + 0, + 0, + 10993, + 0, + 10995, + 0, + 0, + 0, + 10996, + 10997, + 0, + 0, + 0, + 10998, + 0, + 10999, + 0, + 11001, + 0, + 0, + 0, + 0, + 0, + 0, + 11010, + 11012, + 0, + 11013, + 11016, + 11017, + 0, + 0, + 11019, + 11020, + 11021, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 11022, + 0, + 0, + 11023, + 11029, + 0, + 0, + 0, + 0, + 11031, + 0, + 0, + 0, + 11034, + 0, + 0, + 0, + 0, + 11055, + 0, + 0, + 0, + 0, + 0, + 11056, + 11060, + 0, + 0, + 0, + 0, + 0, + 0, + 11061, + 0, + 0, + 11064, + 11065, + 0, + 11066, + 0, + 11069, + 0, + 11085, + 0, + 0, + 0, + 0, + 0, + 11086, + 0, + 0, + 0, + 11088, + 0, + 0, + 0, + 11094, + 0, + 0, + 0, + 11095, + 11096, + 0, + 0, + 0, + 0, + 0, + 0, + 11097, + 11098, + 0, + 0, + 0, + 0, + 0, + 0, + 11099, + 0, + 0, + 11102, + 11108, + 0, + 0, + 0, + 11109, + 0, + 11114, + 11119, + 0, + 11131, + 0, + 0, + 0, + 11142, + 0, + 0, + 11143, + 0, + 11146, + 0, + 11147, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 11148, + 0, + 11149, + 11152, + 11153, + 11154, + 0, + 11156, + 0, + 11157, + 0, + 0, + 0, + 11158, + 0, + 0, + 11159, + 11160, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 11163, + 0, + 0, + 11164, + 11166, + 0, + 0, + 0, + 11172, + 11174, + 0, + 0, + 0, + 11176, + 0, + 0, + 0, + 0, + 0, + 11182, + 11183, + 0, + 0, + 0, + 11184, + 11187, + 0, + 0, + 11188, + 11189, + 0, + 0, + 0, + 0, + 0, + 0, + 11194, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 11200, + 11202, + 0, + 0, + 0, + 0, + 0, + 0, + 11203, + 0, + 11204, + 0, + 0, + 0, + 0, + 0, + 11205, + 0, + 0, + 0, + 11206, + 0, + 11207, + 0, + 0, + 11209, + 0, + 11211, + 0, + 11214, + 0, + 0, + 11231, + 0, + 0, + 0, + 11293, + 11295, + 0, + 0, + 11296, + 11297, + 11302, + 0, + 0, + 0, + 11307, + 0, + 0, + 0, + 0, + 11309, + 11310, + 0, + 11311, + 0, + 0, + 0, + 11313, + 0, + 11314, + 0, + 0, + 0, + 0, + 11334, + 0, + 11338, + 0, + 0, + 0, + 11339, + 0, + 0, + 0, + 0, + 0, + 11340, + 0, + 11341, + 11342, + 0, + 11344, + 0, + 11345, + 0, + 0, + 0, + 11348, + 11349, + 0, + 0, + 11350, + 0, + 0, + 0, + 11355, + 0, + 0, + 0, + 0, + 0, + 0, + 11356, + 0, + 11357, + 11370, + 0, + 0, + 11371, + 0, + 11374, + 11376, + 0, + 0, + 0, + 11377, + 0, + 0, + 11378, + 11383, + 0, + 11386, + 11399, + 0, + 11400, + 11406, + 0, + 0, + 0, + 11408, + 0, + 0, + 11409, + 11412, + 0, + 0, + 0, + 0, + 11417, + 0, + 0, + 0, + 11418, + 0, + 11421, + 0, + 11426, + 11429, + 0, + 0, + 0, + 0, + 0, + 11430, + 0, + 11437, + 0, + 11438, + 0, + 0, + 0, + 0, + 0, + 11440, + 11453, + 0, + 0, + 0, + 0, + 0, + 0, + 11454, + 0, + 0, + 0, + 0, + 11455, + 0, + 0, + 11456, + 11460, + 11461, + 11463, + 0, + 11469, + 0, + 11473, + 0, + 0, + 0, + 0, + 11474, + 0, + 0, + 0, + 11475, + 0, + 11476, + 11477, + 11480, + 0, + 0, + 0, + 0, + 11481, + 0, + 0, + 11484, + 0, + 0, + 11487, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 11497, + 0, + 0, + 11502, + 0, + 11509, + 0, + 0, + 11510, + 11511, + 11513, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 11515, + 0, + 0, + 0, + 0, + 11516, + 0, + 11520, + 11521, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 11529, + 11530, + 11531, + 11534, + 0, + 0, + 11543, + 0, + 0, + 0, + 0, + 0, + 11547, + 0, + 11548, + 0, + 0, + 0, + 0, + 0, + 11552, + 11556, + 0, + 11557, + 0, + 0, + 11559, + 0, + 11560, + 0, + 0, + 0, + 0, + 0, + 0, + 11561, + 0, + 0, + 11563, + 11564, + 0, + 11565, + 0, + 0, + 0, + 0, + 11567, + 0, + 0, + 0, + 11569, + 0, + 11574, + 0, + 11575, + 0, + 0, + 0, + 11577, + 0, + 11578, + 0, + 0, + 0, + 11580, + 11581, + 0, + 0, + 0, + 11582, + 11584, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 11587, + 0, + 11588, + 11591, + 0, + 11595, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 11596, + 0, + 11597, + 0, + 0, + 0, + 0, + 11598, + 11601, + 0, + 0, + 0, + 11602, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 11603, + 11604, + 0, + 11606, + 0, + 0, + 11608, + 0, + 0, + 0, + 0, + 11610, + 0, + 0, + 11611, + 0, + 0, + 0, + 0, + 11613, + 0, + 11622, + 0, + 0, + 0, + 11623, + 0, + 0, + 0, + 0, + 11625, + 0, + 0, + 11626, + 11627, + 11628, + 11630, + 0, + 0, + 0, + 0, + 0, + 0, + 11639, + 0, + 0, + 11646, + 0, + 11648, + 11649, + 0, + 11650, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 11651, + 0, + 0, + 11652, + 11653, + 11656, + 0, + 0, + 11677, + 11679, + 0, + 0, + 0, + 0, + 11680, + 0, + 0, + 11681, + 0, + 11685, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 11688, + 0, + 0, + 0, + 11716, + 0, + 11719, + 0, + 0, + 0, + 0, + 0, + 11721, + 0, + 0, + 11724, + 11743, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 11745, + 11748, + 11750, + 0, + 0, + 0, + 0, + 0, + 11751, + 0, + 0, + 0, + 11752, + 11754, + 0, + 11755, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 11759, + 0, + 0, + 0, + 0, + 0, + 0, + 11760, + 0, + 0, + 0, + 11761, + 0, + 0, + 0, + 0, + 0, + 0, + 11766, + 11767, + 0, + 11772, + 11773, + 0, + 11774, + 0, + 0, + 11775, + 0, + 11777, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 11778, + 11780, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 11783, + 0, + 11784, + 0, + 0, + 0, + 11785, + 0, + 0, + 0, + 11786, + 0, + 0, + 0, + 0, + 11788, + 0, + 0, + 11789, + 11791, + 11792, + 0, + 0, + 0, + 0, + 11795, + 11834, + 11835, + 11836, + 0, + 0, + 11837, + 0, + 0, + 0, + 11838, + 0, + 0, + 11846, + 11851, + 0, + 11852, + 0, + 11869, + 0, + 0, + 0, + 11871, + 0, + 0, + 0, + 11872, + 11874, + 0, + 0, + 0, + 0, + 0, + 0, + 11875, + 0, + 11876, + 11877, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 11883, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 11884, + 0, + 11885, + 0, + 11886, + 0, + 0, + 11887, + 0, + 11894, + 11895, + 11897, + 11909, + 11910, + 0, + 11912, + 11918, + 0, + 0, + 11920, + 0, + 11922, + 11924, + 11927, + 11928, + 0, + 0, + 0, + 0, + 11929, + 0, + 11934, + 0, + 0, + 0, + 0, + 0, + 11941, + 11943, + 11944, + 0, + 11945, + 0, + 0, + 0, + 0, + 11948, + 11949, + 0, + 0, + 0, + 0, + 11953, + 0, + 11954, + 0, + 11955, + 0, + 11956, + 0, + 0, + 0, + 0, + 0, + 11957, + 0, + 0, + 11959, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 11961, + 0, + 0, + 0, + 0, + 0, + 11978, + 0, + 0, + 0, + 11979, + 11980, + 11986, + 11987, + 0, + 11992, + 0, + 0, + 0, + 0, + 0, + 11993, + 0, + 0, + 0, + 11994, + 0, + 11999, + 12004, + 12005, + 12006, + 0, + 0, + 0, + 0, + 0, + 12011, + 0, + 0, + 12012, + 12014, + 0, + 0, + 12015, + 0, + 0, + 12019, + 12028, + 0, + 0, + 12029, + 0, + 0, + 12032, + 12033, + 0, + 0, + 0, + 0, + 12034, + 0, + 12041, + 12043, + 0, + 0, + 12044, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 12046, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 12054, + 12055, + 0, + 12056, + 0, + 0, + 0, + 12060, + 12064, + 0, + 0, + 0, + 0, + 0, + 12065, + 12067, + 12068, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 12074, + 0, + 0, + 0, + 12075, + 12076, + 0, + 0, + 0, + 12079, + 0, + 12081, + 12086, + 12087, + 0, + 0, + 12088, + 0, + 0, + 0, + 0, + 12089, + 0, + 12092, + 0, + 0, + 0, + 0, + 12097, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 12098, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 12102, + 12103, + 12104, + 12111, + 0, + 0, + 12114, + 12116, + 0, + 0, + 0, + 12118, + 0, + 0, + 0, + 12119, + 12120, + 12128, + 0, + 0, + 0, + 0, + 12130, + 0, + 0, + 0, + 0, + 0, + 0, + 12131, + 0, + 0, + 0, + 12132, + 12134, + 0, + 0, + 0, + 0, + 12137, + 0, + 12139, + 0, + 12141, + 0, + 0, + 12142, + 0, + 0, + 0, + 12144, + 0, + 0, + 0, + 0, + 0, + 12145, + 0, + 12148, + 0, + 12153, + 0, + 0, + 0, + 0, + 12154, + 12171, + 12173, + 0, + 0, + 0, + 12175, + 0, + 0, + 0, + 0, + 12178, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 12183, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 12184, + 0, + 0, + 0, + 12186, + 0, + 0, + 0, + 0, + 0, + 12187, + 12188, + 0, + 0, + 12189, + 0, + 12196, + 0, + 12197, + 0, + 0, + 12198, + 0, + 12201, + 0, + 0, + 0, + 0, + 12203, + 0, + 12209, + 0, + 0, + 0, + 0, + 12210, + 12211, + 12212, + 12213, + 0, + 12217, + 12218, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 12222, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 12223, + 0, + 0, + 12229, + 0, + 0, + 0, + 0, + 12233, + 0, + 0, + 0, + 0, + 12234, + 0, + 0, + 12236, + 12242, + 0, + 0, + 0, + 12243, + 0, + 0, + 0, + 12244, + 12253, + 0, + 12254, + 12256, + 0, + 12257, + 0, + 0, + 12275, + 0, + 0, + 0, + 0, + 0, + 12277, + 0, + 0, + 0, + 0, + 0, + 12278, + 0, + 12289, + 0, + 0, + 12290, + 0, + 12292, + 12293, + 0, + 0, + 12294, + 0, + 12295, + 0, + 0, + 12296, + 0, + 12297, + 0, + 12298, + 0, + 0, + 0, + 0, + 12301, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 12309, + 0, + 12338, + 12340, + 0, + 0, + 0, + 0, + 12341, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 12342, + 12343, + 0, + 12344, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 12345, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 12346, + 0, + 0, + 0, + 0, + 12348, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 12350, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 12351, + 0, + 12355, + 12356, + 12357, + 0, + 0, + 12367, + 12370, + 12371, + 0, + 0, + 0, + 0, + 0, + 12372, + 12376, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 12379, + 0, + 12382, + 0, + 12383, + 0, + 0, + 12384, + 0, + 0, + 0, + 0, + 12393, + 0, + 0, + 12394, + 0, + 0, + 0, + 0, + 12398, + 12403, + 0, + 0, + 12404, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 12410, + 0, + 0, + 0, + 12411, + 0, + 0, + 0, + 12412, + 0, + 0, + 0, + 0, + 12420, + 0, + 12421, + 0, + 0, + 0, + 0, + 0, + 12423, + 0, + 12425, + 12429, + 0, + 0, + 0, + 12431, + 12432, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 12434, + 0, + 0, + 0, + 0, + 0, + 12435, + 12436, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 12437, + 0, + 0, + 0, + 0, + 0, + 12438, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 12445, + 0, + 0, + 0, + 12450, + 12451, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 12452, + 12475, + 0, + 0, + 12493, + 12494, + 0, + 0, + 0, + 12495, + 0, + 0, + 0, + 0, + 12496, + 12502, + 12509, + 0, + 0, + 0, + 0, + 12510, + 0, + 12512, + 12513, + 0, + 0, + 0, + 0, + 12514, + 0, + 0, + 0, + 12515, + 0, + 12520, + 0, + 0, + 0, + 12524, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 12527, + 0, + 0, + 0, + 12528, + 0, + 0, + 0, + 12529, + 0, + 0, + 0, + 0, + 0, + 12530, + 0, + 12535, + 0, + 0, + 12536, + 0, + 12538, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 12540, + 0, + 12548, + 0, + 0, + 0, + 0, + 0, + 12550, + 0, + 0, + 0, + 12551, + 12552, + 0, + 0, + 0, + 12554, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 12555, + 0, + 0, + 12562, + 0, + 12565, + 0, + 12566, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 12569, + 0, + 0, + 0, + 12571, + 12574, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 12577, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 12578, + 12579, + 12603, + 0, + 12608, + 0, + 0, + 12611, + 0, + 12612, + 0, + 12615, + 0, + 12625, + 0, + 0, + 0, + 0, + 12627, + 12646, + 0, + 12648, + 0, + 0, + 12657, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 12670, + 0, + 0, + 12671, + 0, + 12673, + 12677, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 12679, + 0, + 12681, + 0, + 12682, + 12693, + 0, + 12694, + 0, + 12697, + 0, + 12701, + 0, + 0, + 0, + 12703, + 12704, + 0, + 0, + 0, + 0, + 12707, + 12737, + 0, + 0, + 12739, + 0, + 0, + 12740, + 0, + 0, + 12742, + 12743, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 12745, + 0, + 12746, + 12747, + 0, + 12748, + 0, + 0, + 12759, + 12767, + 0, + 0, + 0, + 0, + 12773, + 0, + 12774, + 12778, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 12779, + 0, + 0, + 0, + 0, + 0, + 12780, + 12793, + 0, + 12824, + 0, + 12825, + 0, + 12836, + 0, + 0, + 0, + 0, + 12839, + 0, + 12842, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 12843, + 12845, + 0, + 12846, + 0, + 0, + 0, + 0, + 12847, + 0, + 0, + 12850, + 12852, + 12853, + 0, + 0, + 0, + 12854, + 0, + 0, + 0, + 12855, + 0, + 12856, + 0, + 12858, + 0, + 0, + 12859, + 0, + 12862, + 0, + 12863, + 0, + 0, + 12866, + 0, + 12869, + 12872, + 12873, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 12875, + 0, + 12877, + 0, + 0, + 12878, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 12884, + 12885, + 12888, + 0, + 12889, + 0, + 0, + 0, + 0, + 12893, + 0, + 0, + 0, + 12895, + 12896, + 12898, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 12902, + 0, + 12909, + 12910, + 0, + 12926, + 0, + 12928, + 0, + 0, + 0, + 12929, + 0, + 12930, + 0, + 0, + 0, + 0, + 12931, + 0, + 12932, + 12933, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 12934, + 0, + 12942, + 0, + 0, + 0, + 0, + 12944, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 12946, + 0, + 0, + 12948, + 0, + 0, + 12949, + 0, + 0, + 0, + 0, + 12950, + 0, + 0, + 0, + 0, + 12951, + 0, + 12952, + 0, + 12953, + 0, + 0, + 0, + 12954, + 12958, + 12959, + 0, + 0, + 0, + 0, + 0, + 12960, + 12964, + 0, + 0, + 0, + 0, + 0, + 12966, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 12970, + 0, + 12971, + 0, + 0, + 0, + 0, + 0, + 0, + 12972, + 0, + 0, + 12982, + 0, + 0, + 0, + 12984, + 12985, + 0, + 12986, + 12996, + 12997, + 13001, + 13002, + 0, + 0, + 0, + 0, + 13004, + 0, + 0, + 13005, + 0, + 0, + 13007, + 13009, + 0, + 13017, + 0, + 0, + 0, + 13020, + 0, + 13021, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 13022, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 13024, + 13027, + 0, + 0, + 0, + 0, + 0, + 13028, + 0, + 0, + 13029, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 13032, + 0, + 13037, + 0, + 0, + 0, + 0, + 0, + 0, + 13040, + 0, + 0, + 13041, + 0, + 0, + 0, + 13043, + 13044, + 13046, + 0, + 0, + 0, + 0, + 13047, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 13049, + 13054, + 0, + 13056, + 0, + 0, + 13060, + 13061, + 0, + 0, + 0, + 0, + 0, + 13067, + 0, + 0, + 13068, + 0, + 13071, + 0, + 0, + 0, + 0, + 0, + 13077, + 13078, + 0, + 0, + 0, + 0, + 0, + 13079, + 13080, + 13081, + 0, + 13082, + 0, + 0, + 0, + 13085, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 13086, + 0, + 13087, + 13088, + 0, + 0, + 0, + 0, + 0, + 13094, + 0, + 13099, + 0, + 13100, + 0, + 0, + 0, + 13101, + 0, + 13125, + 13126, + 13128, + 13129, + 0, + 0, + 13130, + 0, + 13131, + 0, + 0, + 0, + 0, + 0, + 0, + 13134, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 13150, + 0, + 13168, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 13169, + 0, + 0, + 13170, + 0, + 0, + 0, + 0, + 13174, + 0, + 0, + 0, + 13176, + 0, + 0, + 0, + 0, + 0, + 13177, + 0, + 13178, + 13183, + 13187, + 0, + 0, + 0, + 13189, + 0, + 0, + 13190, + 0, + 0, + 13191, + 0, + 0, + 13206, + 0, + 0, + 0, + 13207, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 13212, + 0, + 0, + 13219, + 13232, + 0, + 0, + 0, + 13241, + 0, + 13249, + 13253, + 0, + 0, + 0, + 0, + 0, + 13255, + 13259, + 0, + 13260, + 13261, + 0, + 13262, + 0, + 13272, + 0, + 0, + 0, + 0, + 13276, + 0, + 0, + 0, + 0, + 13277, + 13299, + 0, + 0, + 13301, + 13302, + 0, + 0, + 13303, + 0, + 0, + 13305, + 0, + 13310, + 0, + 0, + 0, + 13311, + 0, + 0, + 0, + 0, + 13325, + 0, + 13328, + 0, + 0, + 0, + 13329, + 0, + 0, + 0, + 0, + 0, + 0, + 13330, + 0, + 0, + 13331, + 0, + 13335, + 0, + 0, + 13342, + 0, + 0, + 0, + 0, + 0, + 13343, + 0, + 13354, + 0, + 13362, + 0, + 13366, + 13367, + 13369, + 0, + 0, + 13371, + 13372, + 0, + 13373, + 13374, + 0, + 13376, + 0, + 13380, + 13381, + 13386, + 0, + 13387, + 13388, + 0, + 13389, + 13391, + 13395, + 0, + 0, + 0, + 0, + 0, + 13401, + 13409, + 0, + 13410, + 0, + 0, + 0, + 0, + 13420, + 0, + 0, + 0, + 0, + 0, + 13422, + 0, + 0, + 0, + 0, + 13423, + 0, + 0, + 0, + 0, + 13425, + 0, + 0, + 0, + 0, + 0, + 13427, + 0, + 0, + 0, + 13428, + 0, + 0, + 13430, + 13438, + 0, + 13439, + 0, + 13445, + 0, + 13448, + 13449, + 0, + 0, + 0, + 0, + 0, + 0, + 13451, + 0, + 13457, + 0, + 0, + 0, + 0, + 13458, + 13459, + 0, + 13460, + 0, + 0, + 0, + 0, + 13464, + 13465, + 13466, + 13470, + 0, + 13471, + 13472, + 13474, + 13475, + 0, + 13476, + 0, + 0, + 13478, + 13479, + 0, + 13481, + 0, + 0, + 0, + 0, + 13487, + 0, + 13490, + 0, + 13493, + 0, + 0, + 13494, + 0, + 0, + 13495, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 13496, + 13497, + 0, + 13500, + 0, + 0, + 13516, + 13522, + 0, + 0, + 13525, + 13528, + 0, + 0, + 0, + 13530, + 13535, + 0, + 13537, + 13539, + 0, + 13540, + 0, + 13543, + 0, + 13544, + 0, + 0, + 0, + 0, + 0, + 0, + 13545, + 0, + 0, + 0, + 0, + 0, + 0, + 13547, + 0, + 0, + 0, + 13549, + 13555, + 0, + 0, + 0, + 13556, + 13557, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 13558, + 0, + 13563, + 0, + 0, + 0, + 0, + 13564, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 13566, + 0, + 0, + 0, + 0, + 0, + 0, + 13569, + 0, + 0, + 13571, + 0, + 0, + 0, + 0, + 13573, + 0, + 0, + 0, + 0, + 0, + 0, + 13578, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 13581, + 0, + 13586, + 0, + 13595, + 0, + 13600, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 13601, + 13603, + 0, + 13604, + 13605, + 13606, + 13607, + 0, + 0, + 13617, + 13618, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 13623, + 0, + 13625, + 13627, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 13629, + 0, + 0, + 0, + 13634, + 0, + 0, + 0, + 13638, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 13654, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 13656, + 0, + 13659, + 0, + 0, + 13660, + 0, + 0, + 13662, + 0, + 0, + 0, + 13663, + 0, + 13664, + 0, + 0, + 0, + 0, + 0, + 13668, + 0, + 13669, + 13671, + 0, + 0, + 13672, + 0, + 0, + 0, + 0, + 0, + 0, + 13675, + 13685, + 0, + 13686, + 0, + 0, + 0, + 13687, + 0, + 0, + 0, + 13692, + 13694, + 13697, + 0, + 0, + 0, + 13702, + 0, + 0, + 0, + 0, + 0, + 13705, + 0, + 0, + 0, + 0, + 13707, + 0, + 0, + 0, + 13714, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 13715, + 0, + 13716, + 13717, + 0, + 0, + 13719, + 13724, + 13730, + 13731, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 13732, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 13734, + 0, + 13736, + 0, + 0, + 13737, + 13738, + 13747, + 0, + 13751, + 0, + 0, + 13752, + 0, + 0, + 0, + 13753, + 0, + 13757, + 0, + 0, + 13762, + 13763, + 0, + 13764, + 13765, + 0, + 13766, + 0, + 0, + 13767, + 0, + 0, + 0, + 13768, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 13769, + 0, + 0, + 13772, + 0, + 13775, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 13776, + 13778, + 13787, + 0, + 0, + 0, + 13797, + 0, + 13798, + 0, + 13801, + 0, + 13804, + 13806, + 0, + 0, + 0, + 0, + 13816, + 13817, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 13834, + 0, + 13836, + 0, + 0, + 13838, + 0, + 0, + 13839, + 0, + 13840, + 0, + 0, + 0, + 0, + 13842, + 0, + 0, + 0, + 0, + 0, + 0, + 13843, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 13845, + 0, + 0, + 0, + 0, + 0, + 13858, + 0, + 0, + 13860, + 0, + 0, + 13861, + 0, + 0, + 13862, + 13863, + 0, + 13868, + 0, + 13869, + 13870, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 13872, + 0, + 0, + 0, + 0, + 13873, + 13878, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 13886, + 0, + 13888, + 13889, + 13890, + 0, + 0, + 13891, + 13894, + 0, + 13897, + 13899, + 13900, + 13904, + 0, + 0, + 13906, + 0, + 0, + 0, + 13909, + 0, + 0, + 0, + 13910, + 0, + 0, + 0, + 13911, + 0, + 0, + 0, + 0, + 0, + 13912, + 13917, + 0, + 0, + 0, + 0, + 13918, + 0, + 13919, + 0, + 0, + 13920, + 0, + 0, + 0, + 13921, + 0, + 0, + 13922, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 13924, + 0, + 13927, + 0, + 0, + 0, + 0, + 0, + 13932, + 0, + 13933, + 0, + 13934, + 0, + 0, + 13935, + 0, + 13944, + 0, + 0, + 0, + 13954, + 0, + 0, + 13955, + 0, + 0, + 0, + 0, + 13956, + 0, + 13957, + 0, + 13967, + 13969, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 13970, + 13990, + 0, + 13991, + 13994, + 0, + 13995, + 0, + 0, + 0, + 0, + 13996, + 0, + 0, + 13999, + 0, + 0, + 0, + 14018, + 0, + 14019, + 0, + 14021, + 0, + 0, + 0, + 0, + 0, + 0, + 14041, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 14043, + 0, + 0, + 0, + 0, + 14046, + 0, + 0, + 0, + 14048, + 14049, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 14051, + 0, + 0, + 14052, + 14056, + 0, + 14063, + 0, + 14064, + 14066, + 0, + 0, + 14067, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 14068, + 0, + 0, + 0, + 14072, + 0, + 14074, + 14075, + 0, + 14076, + 14079, + 14085, + 14086, + 14087, + 14093, + 0, + 0, + 0, + 0, + 14095, + 0, + 0, + 0, + 0, + 0, + 0, + 14096, + 14097, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 14098, + 0, + 14102, + 0, + 0, + 0, + 0, + 0, + 14103, + 0, + 0, + 0, + 14104, + 0, + 0, + 14105, + 0, + 0, + 0, + 14107, + 14108, + 0, + 0, + 14109, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 14117, + 0, + 0, + 0, + 0, + 14118, + 0, + 0, + 0, + 0, + 14119, + 0, + 0, + 14120, + 0, + 0, + 14121, + 0, + 14122, + 14127, + 0, + 14128, + 14136, + 0, + 0, + 14138, + 0, + 14140, + 0, + 0, + 0, + 14141, + 14142, + 0, + 0, + 0, + 0, + 14146, + 0, + 0, + 14149, + 0, + 14151, + 0, + 0, + 0, + 14152, + 0, + 0, + 14153, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 14154, + 0, + 14156, + 14157, + 0, + 0, + 14159, + 0, + 14161, + 0, + 0, + 0, + 0, + 14162, + 0, + 0, + 0, + 0, + 0, + 0, + 14163, + 0, + 0, + 14173, + 0, + 0, + 0, + 0, + 0, + 0, + 14174, + 0, + 0, + 14176, + 0, + 0, + 14178, + 0, + 0, + 14179, + 14181, + 0, + 0, + 14182, + 14185, + 14187, + 0, + 14190, + 0, + 0, + 14197, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 14198, + 0, + 0, + 0, + 0, + 0, + 0, + 14199, + 14200, + 0, + 0, + 0, + 14204, + 0, + 0, + 14208, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 14231, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 14234, + 0, + 0, + 14235, + 0, + 0, + 0, + 14240, + 14241, + 0, + 0, + 0, + 14246, + 0, + 0, + 0, + 14247, + 0, + 14250, + 0, + 0, + 14251, + 0, + 0, + 14254, + 0, + 0, + 14256, + 0, + 0, + 0, + 14260, + 0, + 14261, + 0, + 0, + 0, + 0, + 14262, + 14267, + 14269, + 0, + 0, + 14277, + 0, + 0, + 14278, + 0, + 14279, + 14282, + 0, + 0, + 0, + 14283, + 0, + 0, + 0, + 14284, + 14285, + 0, + 0, + 0, + 0, + 14286, + 0, + 0, + 0, + 14288, + 0, + 0, + 0, + 14289, + 0, + 14290, + 0, + 14293, + 14301, + 14302, + 14304, + 14305, + 0, + 14307, + 0, + 14308, + 14309, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 14311, + 14312, + 0, + 0, + 14317, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 14318, + 0, + 0, + 0, + 0, + 14320, + 0, + 0, + 0, + 0, + 14321, + 14322, + 0, + 0, + 0, + 0, + 0, + 14326, + 14329, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 14330, + 14331, + 0, + 0, + 0, + 0, + 14332, + 0, + 0, + 0, + 14333, + 0, + 0, + 14337, + 14340, + 0, + 14341, + 0, + 0, + 14342, + 0, + 14345, + 14346, + 0, + 0, + 14347, + 0, + 14362, + 0, + 0, + 0, + 0, + 0, + 14364, + 14365, + 14371, + 0, + 14373, + 0, + 0, + 14374, + 0, + 14379, + 0, + 14400, + 0, + 0, + 0, + 0, + 0, + 14401, + 0, + 0, + 14405, + 0, + 14406, + 0, + 14408, + 14409, + 0, + 0, + 0, + 14417, + 0, + 0, + 14424, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 14430, + 0, + 0, + 0, + 14431, + 0, + 0, + 14435, + 0, + 14440, + 0, + 0, + 0, + 0, + 0, + 0, + 14442, + 0, + 0, + 14443, + 0, + 0, + 0, + 0, + 0, + 14446, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 14454, + 0, + 14457, + 0, + 14460, + 0, + 0, + 14466, + 0, + 0, + 0, + 0, + 0, + 14467, + 0, + 0, + 0, + 0, + 0, + 0, + 14469, + 0, + 14477, + 0, + 0, + 0, + 0, + 0, + 0, + 14478, + 14482, + 0, + 0, + 0, + 14483, + 0, + 0, + 0, + 14485, + 14486, + 0, + 0, + 0, + 14487, + 14488, + 14489, + 14492, + 14493, + 14494, + 14495, + 14496, + 14497, + 0, + 14499, + 0, + 14501, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 14502, + 0, + 14507, + 14512, + 14513, + 14514, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 14515, + 14526, + 14530, + 0, + 14537, + 0, + 14544, + 0, + 14547, + 0, + 0, + 14548, + 14550, + 14551, + 0, + 0, + 14552, + 0, + 0, + 0, + 14553, + 0, + 14554, + 0, + 0, + 0, + 0, + 14556, + 14564, + 0, + 0, + 14565, + 14566, + 0, + 0, + 0, + 0, + 0, + 0, + 14568, + 0, + 0, + 14569, + 0, + 0, + 0, + 14571, + 14576, + 0, + 0, + 14577, + 14578, + 14579, + 0, + 0, + 14580, + 0, + 0, + 0, + 0, + 14582, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 14583, + 0, + 0, + 0, + 0, + 0, + 14587, + 0, + 14588, + 0, + 0, + 14600, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 14601, + 0, + 0, + 14604, + 14605, + 14611, + 0, + 14613, + 0, + 0, + 0, + 0, + 14615, + 0, + 0, + 0, + 0, + 0, + 0, + 14627, + 0, + 14628, + 0, + 0, + 0, + 0, + 14631, + 0, + 14633, + 14634, + 0, + 0, + 0, + 0, + 14635, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 14636, + 0, + 0, + 14639, + 14642, + 0, + 0, + 0, + 0, + 14644, + 0, + 0, + 0, + 0, + 14645, + 14646, + 0, + 14653, + 0, + 0, + 14654, + 0, + 14658, + 0, + 14661, + 0, + 0, + 0, + 14665, + 0, + 0, + 0, + 14668, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 14669, + 0, + 0, + 14670, + 0, + 0, + 0, + 14680, + 0, + 0, + 14681, + 0, + 0, + 0, + 0, + 0, + 14682, + 14683, + 0, + 0, + 0, + 0, + 14686, + 0, + 0, + 0, + 0, + 14687, + 14697, + 0, + 0, + 0, + 0, + 14699, + 14705, + 14711, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 14712, + 0, + 0, + 0, + 14713, + 0, + 0, + 0, + 0, + 14719, + 0, + 14720, + 14721, + 14726, + 0, + 0, + 0, + 14728, + 14729, + 0, + 0, + 0, + 0, + 14731, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 14733, + 14736, + 14737, + 0, + 0, + 14740, + 14742, + 0, + 0, + 0, + 14744, + 14753, + 0, + 0, + 0, + 0, + 14755, + 14758, + 14760, + 0, + 0, + 0, + 0, + 0, + 14761, + 14762, + 14765, + 14771, + 0, + 14772, + 0, + 14773, + 14774, + 0, + 0, + 14775, + 0, + 0, + 14776, + 0, + 0, + 0, + 0, + 14777, + 0, + 14779, + 0, + 0, + 14782, + 0, + 0, + 14785, + 14786, + 14788, + 0, + 0, + 0, + 0, + 0, + 14795, + 0, + 0, + 0, + 0, + 0, + 0, + 14798, + 0, + 14803, + 14804, + 14806, + 0, + 0, + 0, + 14809, + 0, + 0, + 0, + 0, + 0, + 0, + 14810, + 0, + 0, + 0, + 0, + 14811, + 0, + 14812, + 0, + 0, + 0, + 0, + 0, + 14815, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 14816, + 0, + 14818, + 0, + 0, + 0, + 0, + 0, + 0, + 14819, + 0, + 14820, + 0, + 14823, + 0, + 0, + 0, + 14824, + 0, + 0, + 14826, + 14827, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 14830, + 0, + 0, + 0, + 0, + 0, + 14833, + 0, + 14845, + 0, + 0, + 0, + 0, + 0, + 14846, + 0, + 0, + 14847, + 14871, + 0, + 14873, + 0, + 14876, + 0, + 14877, + 14878, + 14880, + 0, + 0, + 0, + 0, + 0, + 14881, + 0, + 14882, + 14894, + 0, + 0, + 0, + 0, + 14895, + 0, + 14907, + 0, + 14908, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 14911, + 0, + 0, + 0, + 0, + 14920, + 0, + 0, + 14931, + 0, + 14932, + 14934, + 14935, + 0, + 0, + 14936, + 0, + 14945, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 14947, + 0, + 0, + 14948, + 14949, + 14951, + 0, + 0, + 14952, + 0, + 0, + 0, + 14964, + 14973, + 0, + 0, + 14990, + 0, + 0, + 0, + 0, + 14995, + 0, + 0, + 14998, + 15001, + 0, + 0, + 15002, + 15020, + 0, + 0, + 0, + 0, + 0, + 0, + 15021, + 0, + 15022, + 0, + 0, + 0, + 0, + 15023, + 0, + 0, + 15025, + 15029, + 15033, + 0, + 0, + 0, + 15034, + 0, + 0, + 0, + 15035, + 0, + 0, + 0, + 0, + 0, + 15043, + 15044, + 0, + 0, + 0, + 15045, + 15046, + 15048, + 15050, + 0, + 15065, + 0, + 0, + 0, + 0, + 15066, + 0, + 0, + 15075, + 15082, + 15084, + 0, + 0, + 15085, + 15086, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 15088, + 0, + 0, + 0, + 15089, + 0, + 0, + 0, + 0, + 15094, + 0, + 15096, + 0, + 15097, + 0, + 15100, + 0, + 0, + 15102, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 15105, + 0, + 0, + 15106, + 0, + 15109, + 15113, + 0, + 0, + 0, + 15115, + 0, + 15118, + 0, + 0, + 0, + 0, + 0, + 0, + 15119, + 0, + 0, + 15120, + 0, + 0, + 0, + 0, + 0, + 15123, + 15129, + 0, + 0, + 0, + 15130, + 0, + 15131, + 0, + 0, + 15134, + 0, + 15135, + 0, + 0, + 0, + 15137, + 15138, + 0, + 0, + 0, + 0, + 0, + 0, + 15139, + 0, + 0, + 0, + 0, + 0, + 15140, + 0, + 0, + 15154, + 15162, + 0, + 15169, + 15170, + 0, + 15175, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 15177, + 0, + 15178, + 15179, + 0, + 0, + 0, + 0, + 0, + 15183, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 15185, + 15187, + 0, + 15194, + 15195, + 15196, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 15204, + 0, + 0, + 0, + 0, + 15206, + 0, + 0, + 0, + 0, + 0, + 15207, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 15213, + 0, + 15214, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 15232, + 0, + 0, + 0, + 0, + 15234, + 0, + 15238, + 15240, + 0, + 15248, + 0, + 0, + 0, + 0, + 15250, + 15251, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 15252, + 0, + 0, + 0, + 15255, + 15262, + 15266, + 0, + 0, + 0, + 15267, + 0, + 0, + 0, + 15277, + 15279, + 0, + 0, + 0, + 15280, + 15281, + 15282, + 0, + 0, + 0, + 0, + 0, + 15285, + 0, + 0, + 0, + 0, + 15289, + 0, + 0, + 15291, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 15296, + 15297, + 0, + 0, + 15304, + 0, + 0, + 0, + 0, + 15306, + 0, + 0, + 0, + 0, + 0, + 0, + 15307, + 15308, + 0, + 15309, + 0, + 0, + 15311, + 0, + 0, + 15312, + 15313, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 15314, + 15317, + 0, + 0, + 0, + 15318, + 15319, + 0, + 0, + 0, + 0, + 15320, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 15321, + 0, + 0, + 0, + 0, + 0, + 15324, + 0, + 15325, + 15326, + 0, + 15330, + 0, + 0, + 0, + 0, + 15334, + 0, + 15335, + 0, + 15341, + 0, + 0, + 15342, + 0, + 0, + 15343, + 15344, + 0, + 0, + 0, + 0, + 15345, + 0, + 0, + 0, + 0, + 15347, + 0, + 0, + 15348, + 15349, + 15350, + 0, + 15356, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 15357, + 0, + 15358, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 15359, + 15360, + 15364, + 0, + 15380, + 0, + 0, + 0, + 0, + 0, + 15392, + 0, + 0, + 15393, + 0, + 15395, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 15396, + 0, + 0, + 15397, + 15398, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 15399, + 0, + 15400, + 0, + 0, + 0, + 15402, + 0, + 15405, + 15410, + 0, + 0, + 0, + 0, + 15411, + 0, + 0, + 0, + 15412, + 0, + 15416, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 15428, + 0, + 15435, + 0, + 0, + 15438, + 0, + 0, + 0, + 0, + 15439, + 0, + 0, + 0, + 15440, + 0, + 0, + 0, + 15441, + 15449, + 15451, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 15452, + 0, + 0, + 15455, + 0, + 0, + 0, + 15456, + 0, + 0, + 15458, + 0, + 15460, + 15461, + 0, + 0, + 0, + 0, + 0, + 15462, + 15464, + 0, + 15465, + 0, + 0, + 15466, + 0, + 0, + 15467, + 0, + 0, + 0, + 0, + 0, + 15468, + 0, + 0, + 0, + 0, + 15481, + 0, + 0, + 15484, + 0, + 15485, + 15486, + 0, + 0, + 0, + 15487, + 0, + 0, + 0, + 0, + 0, + 15488, + 0, + 15492, + 15498, + 0, + 0, + 0, + 15499, + 0, + 0, + 0, + 15500, + 0, + 15501, + 0, + 0, + 15512, + 0, + 15522, + 0, + 0, + 0, + 15524, + 0, + 15525, + 15526, + 0, + 0, + 15527, + 0, + 0, + 15545, + 15546, + 0, + 15548, + 15552, + 0, + 15553, + 0, + 0, + 0, + 15554, + 0, + 15555, + 0, + 15557, + 15565, + 15573, + 15577, + 15578, + 0, + 15582, + 0, + 15583, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 15586, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 15588, + 0, + 0, + 0, + 0, + 0, + 15589, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 15593, + 15594, + 0, + 0, + 0, + 0, + 15595, + 0, + 0, + 0, + 0, + 0, + 0, + 15596, + 0, + 0, + 0, + 15597, + 0, + 0, + 0, + 0, + 15600, + 0, + 0, + 15601, + 0, + 0, + 0, + 0, + 15602, + 15603, + 0, + 0, + 0, + 0, + 0, + 0, + 15604, + 0, + 15609, + 0, + 0, + 15612, + 0, + 0, + 15613, + 0, + 0, + 15615, + 15617, + 15618, + 0, + 0, + 15620, + 0, + 15636, + 15637, + 0, + 0, + 15649, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 15650, + 0, + 0, + 15651, + 0, + 0, + 0, + 15656, + 0, + 15658, + 0, + 0, + 0, + 15664, + 0, + 0, + 15665, + 0, + 0, + 15668, + 0, + 0, + 0, + 0, + 0, + 15669, + 0, + 0, + 15674, + 0, + 0, + 15675, + 0, + 0, + 0, + 0, + 15676, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 15677, + 0, + 0, + 0, + 0, + 15678, + 0, + 0, + 0, + 0, + 0, + 15679, + 0, + 0, + 15681, + 0, + 15686, + 0, + 0, + 0, + 0, + 15687, + 0, + 15688, + 0, + 0, + 15690, + 0, + 0, + 0, + 15697, + 0, + 15699, + 15700, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 15701, + 0, + 15702, + 15703, + 0, + 15704, + 0, + 15705, + 0, + 15707, + 0, + 15709, + 0, + 15712, + 15716, + 0, + 15717, + 0, + 15718, + 15720, + 0, + 0, + 0, + 0, + 0, + 15724, + 0, + 0, + 0, + 15725, + 0, + 15726, + 0, + 0, + 0, + 15740, + 0, + 15745, + 15746, + 0, + 0, + 15747, + 0, + 15748, + 0, + 0, + 0, + 0, + 0, + 15749, + 0, + 0, + 0, + 15752, + 0, + 15753, + 0, + 0, + 0, + 0, + 0, + 0, + 15759, + 0, + 0, + 0, + 15765, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 15767, + 0, + 0, + 0, + 15771, + 0, + 0, + 15784, + 0, + 0, + 0, + 0, + 15785, + 15790, + 15791, + 0, + 0, + 15792, + 0, + 0, + 0, + 15807, + 0, + 15811, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 15818, + 0, + 0, + 0, + 15819, + 0, + 0, + 0, + 0, + 15821, + 0, + 0, + 0, + 0, + 0, + 15822, + 15824, + 0, + 0, + 15827, + 0, + 0, + 15829, + 15831, + 0, + 15832, + 0, + 0, + 15833, + 0, + 15835, + 15838, + 15839, + 15843, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 15844, + 0, + 0, + 0, + 0, + 15845, + 15851, + 15856, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 15858, + 15860, + 0, + 15861, + 0, + 0, + 0, + 15864, + 0, + 0, + 0, + 0, + 15865, + 0, + 0, + 0, + 0, + 0, + 0, + 15866, + 0, + 15872, + 0, + 0, + 15876, + 0, + 0, + 0, + 0, + 15877, + 15878, + 15883, + 15885, + 0, + 0, + 15888, + 0, + 0, + 0, + 0, + 0, + 15889, + 15890, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 15892, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 15893, + 0, + 0, + 15894, + 0, + 0, + 0, + 15895, + 0, + 15896, + 15897, + 0, + 15898, + 15901, + 15902, + 0, + 15911, + 15915, + 0, + 15916, + 0, + 15924, + 15935, + 0, + 15937, + 0, + 0, + 0, + 0, + 0, + 15950, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 15958, + 0, + 0, + 0, + 15961, + 0, + 0, + 15966, + 0, + 15967, + 0, + 0, + 15977, + 0, + 0, + 15978, + 0, + 0, + 15981, + 15982, + 15983, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 15986, + 0, + 0, + 0, + 15990, + 0, + 15991, + 15995, + 15998, + 0, + 15999, + 0, + 16000, + 0, + 0, + 0, + 0, + 16008, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 16009, + 16011, + 0, + 16013, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 16014, + 0, + 0, + 16015, + 16023, + 16024, + 16025, + 0, + 0, + 16026, + 0, + 16030, + 0, + 16032, + 0, + 16033, + 0, + 0, + 0, + 0, + 0, + 0, + 16035, + 16036, + 16037, + 0, + 0, + 0, + 0, + 0, + 16039, + 0, + 0, + 0, + 0, + 16041, + 0, + 0, + 0, + 0, + 0, + 16043, + 16044, + 0, + 0, + 16047, + 0, + 0, + 0, + 16048, + 0, + 0, + 16049, + 16050, + 16052, + 0, + 0, + 0, + 0, + 0, + 16055, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 16056, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 16058, + 16060, + 16061, + 0, + 0, + 16063, + 0, + 0, + 16064, + 0, + 0, + 0, + 16067, + 16068, + 0, + 0, + 16069, + 16078, + 0, + 0, + 0, + 16079, + 0, + 0, + 0, + 16080, + 0, + 16081, + 0, + 0, + 0, + 16088, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 16089, + 16093, + 0, + 16097, + 0, + 16103, + 0, + 16104, + 16105, + 0, + 0, + 16256, + 0, + 0, + 16259, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 16260, + 16261, + 0, + 0, + 16262, + 0, + 0, + 16263, + 0, + 16268, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 16269, + 0, + 0, + 16270, + 16273, + 0, + 16274, + 0, + 0, + 0, + 0, + 16275, + 16276, + 16277, + 16280, + 0, + 0, + 0, + 16281, + 16284, + 0, + 0, + 0, + 16286, + 0, + 16289, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 16290, + 0, + 0, + 0, + 0, + 16291, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 16292, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 16293, + 16295, + 16297, + 0, + 16302, + 0, + 16304, + 0, + 16305, + 0, + 16306, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 16307, + 16308, + 16312, + 0, + 0, + 0, + 0, + 0, + 0, + 16313, + 16315, + 0, + 16318, + 0, + 0, + 0, + 16321, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 16326, + 16333, + 16336, + 0, + 0, + 0, + 0, + 16337, + 16340, + 0, + 0, + 0, + 0, + 0, + 16345, + 0, + 0, + 16346, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 16347, + 0, + 0, + 16348, + 0, + 0, + 0, + 0, + 16349, + 0, + 0, + 0, + 16350, + 0, + 16357, + 0, + 0, + 0, + 0, + 16359, + 16360, + 0, + 0, + 0, + 0, + 16362, + 16363, + 16364, + 16365, + 0, + 0, + 16366, + 0, + 0, + 0, + 0, + 16367, + 16368, + 0, + 16369, + 16374, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 16376, + 0, + 0, + 0, + 0, + 16378, + 16379, + 0, + 16380, + 0, + 0, + 0, + 16381, + 16383, + 0, + 0, + 0, + 0, + 0, + 16390, + 0, + 0, + 0, + 16399, + 0, + 16402, + 16404, + 16406, + 16407, + 0, + 0, + 0, + 16409, + 16411, + 0, + 0, + 0, + 0, + 16412, + 0, + 16413, + 16415, + 16423, + 0, + 0, + 0, + 0, + 0, + 16424, + 0, + 0, + 0, + 16428, + 16434, + 16435, + 16449, + 0, + 16450, + 16451, + 0, + 0, + 0, + 16453, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 16454, + 0, + 0, + 16456, + 16458, + 0, + 0, + 16459, + 0, + 0, + 16460, + 0, + 0, + 0, + 0, + 16462, + 0, + 16463, + 0, + 0, + 16466, + 0, + 0, + 0, + 0, + 0, + 16479, + 0, + 0, + 16480, + 0, + 16481, + 16484, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 16485, + 0, + 0, + 0, + 0, + 0, + 0, + 16489, + 0, + 0, + 0, + 0, + 0, + 16491, + 0, + 0, + 16498, + 0, + 0, + 16503, + 0, + 16505, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 16506, + 0, + 0, + 0, + 16508, + 16509, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 16511, + 16513, + 0, + 0, + 0, + 16516, + 0, + 16517, + 0, + 16519, + 0, + 16529, + 0, + 0, + 16531, + 0, + 0, + 0, + 0, + 0, + 0, + 16534, + 0, + 0, + 16541, + 16542, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 16543, + 16547, + 16548, + 0, + 0, + 0, + 16551, + 0, + 16552, + 0, + 0, + 0, + 16553, + 0, + 0, + 16558, + 0, + 0, + 16562, + 16565, + 0, + 0, + 0, + 16570, + 0, + 0, + 0, + 16573, + 16585, + 0, + 0, + 0, + 16586, + 16587, + 16595, + 0, + 16596, + 0, + 16598, + 0, + 0, + 0, + 16600, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 16601, + 0, + 0, + 0, + 0, + 16603, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 16604, + 16612, + 0, + 0, + 0, + 0, + 16613, + 0, + 16618, + 0, + 0, + 0, + 16640, + 0, + 0, + 16641, + 0, + 0, + 0, + 0, + 0, + 0, + 16645, + 0, + 0, + 0, + 0, + 16646, + 0, + 0, + 0, + 0, + 0, + 0, + 16651, + 0, + 0, + 0, + 0, + 16653, + 16654, + 0, + 0, + 0, + 16655, + 0, + 0, + 16656, + 16667, + 0, + 0, + 0, + 0, + 16671, + 0, + 16672, + 0, + 0, + 0, + 16673, + 0, + 0, + 0, + 0, + 0, + 16676, + 0, + 16686, + 0, + 0, + 0, + 0, + 16689, + 0, + 16690, + 0, + 16692, + 0, + 16693, + 0, + 16694, + 0, + 16696, + 0, + 0, + 0, + 16705, + 0, + 0, + 0, + 0, + 0, + 0, + 16707, + 0, + 0, + 0, + 16709, + 0, + 0, + 0, + 0, + 16711, + 0, + 16712, + 16713, + 0, + 0, + 0, + 16715, + 0, + 0, + 0, + 0, + 16716, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 16718, + 16724, + 0, + 0, + 16726, + 16727, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 16728, + 0, + 16729, + 0, + 0, + 16730, + 0, + 0, + 0, + 0, + 0, + 16731, + 0, + 0, + 0, + 16732, + 0, + 0, + 0, + 0, + 16734, + 16738, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 16743, + 0, + 0, + 16745, + 0, + 0, + 0, + 0, + 0, + 16749, + 0, + 16752, + 0, + 0, + 0, + 0, + 16756, + 0, + 0, + 16758, + 0, + 16759, + 0, + 0, + 0, + 0, + 0, + 16760, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 16762, + 0, + 16769, + 0, + 16770, + 0, + 16772, + 0, + 0, + 0, + 16777, + 16780, + 0, + 0, + 0, + 0, + 0, + 0, + 16781, + 0, + 0, + 16782, + 0, + 16784, + 0, + 0, + 16785, + 16787, + 16792, + 0, + 0, + 16794, + 0, + 0, + 0, + 16798, + 0, + 0, + 16809, + 0, + 0, + 16814, + 16816, + 16817, + 0, + 16819, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 16820, + 0, + 0, + 16836, + 16839, + 0, + 0, + 16841, + 16851, + 16857, + 0, + 0, + 16858, + 16859, + 0, + 0, + 16860, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 16862, + 0, + 16863, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 16864, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 16876, + 0, + 16881, + 16882, + 0, + 16885, + 16886, + 0, + 16887, + 0, + 0, + 0, + 16889, + 16891, + 0, + 0, + 0, + 0, + 0, + 16894, + 16895, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 16897, + 0, + 16898, + 0, + 0, + 0, + 0, + 0, + 16913, + 0, + 0, + 16924, + 16925, + 16926, + 0, + 0, + 16927, + 0, + 0, + 0, + 16937, + 16938, + 0, + 0, + 0, + 16940, + 16941, + 0, + 0, + 0, + 16942, + 16945, + 0, + 16946, + 16949, + 16950, + 0, + 0, + 0, + 16952, + 16955, + 0, + 0, + 0, + 16965, + 0, + 16969, + 0, + 0, + 16975, + 0, + 0, + 16976, + 0, + 0, + 0, + 0, + 16978, + 0, + 0, + 16981, + 0, + 16983, + 16989, + 0, + 0, + 0, + 0, + 16990, + 0, + 0, + 16991, + 0, + 0, + 0, + 16993, + 0, + 16994, + 16996, + 17000, + 0, + 0, + 0, + 0, + 0, + 17002, + 17004, + 0, + 17006, + 0, + 0, + 17007, + 0, + 0, + 0, + 0, + 17008, + 17013, + 17014, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 17021, + 0, + 17031, + 0, + 0, + 0, + 0, + 0, + 17033, + 17036, + 0, + 17038, + 0, + 0, + 17039, + 0, + 17045, + 0, + 0, + 17046, + 17047, + 0, + 0, + 0, + 0, + 17048, + 0, + 17049, + 17050, + 0, + 17051, + 17053, + 0, + 17054, + 0, + 17055, + 0, + 0, + 0, + 0, + 0, + 17063, + 0, + 0, + 17064, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 17065, + 0, + 0, + 17068, + 0, + 0, + 0, + 0, + 0, + 17072, + 0, + 0, + 0, + 0, + 0, + 0, + 17073, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 17074, + 0, + 17080, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 17081, + 17083, + 17084, + 0, + 0, + 0, + 17085, + 0, + 0, + 0, + 0, + 17092, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 17093, + 0, + 17095, + 17102, + 0, + 0, + 0, + 0, + 0, + 0, + 17103, + 0, + 0, + 17105, + 0, + 17107, + 0, + 0, + 0, + 0, + 17114, + 0, + 0, + 0, + 0, + 0, + 17115, + 17125, + 17127, + 0, + 0, + 17128, + 0, + 0, + 0, + 17129, + 17130, + 0, + 17131, + 0, + 0, + 0, + 0, + 0, + 17132, + 17135, + 17145, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 17146, + 0, + 17147, + 0, + 17148, + 0, + 0, + 0, + 0, + 0, + 0, + 17149, + 17150, + 0, + 17151, + 17153, + 0, + 17155, + 0, + 0, + 0, + 0, + 17163, + 17171, + 0, + 17174, + 0, + 0, + 0, + 0, + 17179, + 0, + 0, + 17182, + 17185, + 0, + 0, + 0, + 0, + 0, + 17186, + 0, + 0, + 17188, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 17189, + 17191, + 0, + 17194, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 17195, + 17196, + 17203, + 17204, + 0, + 0, + 17205, + 17217, + 0, + 0, + 0, + 0, + 0, + 17218, + 0, + 0, + 0, + 0, + 17219, + 0, + 17220, + 0, + 17221, + 0, + 0, + 17230, + 0, + 0, + 0, + 0, + 0, + 17236, + 0, + 17238, + 17239, + 0, + 0, + 0, + 17241, + 17244, + 0, + 0, + 17245, + 0, + 17248, + 0, + 0, + 17251, + 0, + 17252, + 0, + 0, + 17264, + 0, + 17266, + 0, + 0, + 0, + 17268, + 0, + 0, + 0, + 0, + 17271, + 17272, + 0, + 17273, + 0, + 17295, + 0, + 17302, + 0, + 17305, + 0, + 0, + 0, + 17306, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 17308, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 17309, + 0, + 17310, + 17313, + 0, + 0, + 0, + 0, + 17314, + 17315, + 0, + 17317, + 0, + 0, + 0, + 0, + 17318, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 17320, + 0, + 0, + 0, + 0, + 0, + 0, + 17334, + 0, + 17344, + 17348, + 0, + 0, + 0, + 17350, + 17351, + 0, + 0, + 17353, + 0, + 0, + 17354, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 17355, + 0, + 0, + 0, + 0, + 0, + 0, + 17356, + 17357, + 0, + 0, + 17359, + 0, + 0, + 0, + 17371, + 0, + 17372, + 0, + 0, + 0, + 17393, + 0, + 0, + 0, + 0, + 17394, + 0, + 0, + 0, + 0, + 0, + 17395, + 0, + 0, + 17399, + 0, + 0, + 0, + 17401, + 17417, + 0, + 17418, + 0, + 17419, + 0, + 0, + 0, + 0, + 0, + 17422, + 17423, + 0, + 0, + 0, + 0, + 0, + 17424, + 0, + 0, + 0, + 0, + 0, + 17428, + 17429, + 17433, + 0, + 0, + 0, + 17437, + 0, + 0, + 17441, + 0, + 0, + 17442, + 0, + 0, + 17453, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 17454, + 17456, + 17462, + 0, + 0, + 17466, + 0, + 0, + 17468, + 0, + 0, + 17469, + 0, + 0, + 0, + 0, + 17470, + 0, + 17475, + 0, + 0, + 0, + 0, + 0, + 17479, + 0, + 0, + 0, + 17483, + 17484, + 0, + 17485, + 0, + 17486, + 0, + 17491, + 17492, + 0, + 0, + 17493, + 0, + 17494, + 17495, + 0, + 0, + 0, + 17496, + 0, + 0, + 0, + 17497, + 0, + 0, + 0, + 17502, + 0, + 0, + 0, + 0, + 0, + 17503, + 0, + 17505, + 0, + 17507, + 0, + 0, + 0, + 17512, + 17513, + 17514, + 0, + 0, + 17515, + 0, + 0, + 0, + 17519, + 0, + 0, + 0, + 17522, + 0, + 0, + 17523, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 17527, + 0, + 0, + 0, + 17528, + 0, + 0, + 0, + 17534, + 0, + 0, + 0, + 0, + 17536, + 0, + 0, + 0, + 17539, + 0, + 17540, + 17543, + 17549, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 17556, + 0, + 0, + 17558, + 0, + 17559, + 0, + 0, + 17560, + 0, + 0, + 0, + 17563, + 0, + 0, + 0, + 0, + 0, + 0, + 17564, + 0, + 0, + 17565, + 17566, + 0, + 17567, + 0, + 0, + 0, + 0, + 0, + 0, + 17569, + 17570, + 0, + 17575, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 17581, + 0, + 0, + 0, + 17582, + 17583, + 0, + 17586, + 0, + 0, + 17587, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 17588, + 0, + 0, + 0, + 0, + 17596, + 17597, + 0, + 0, + 17598, + 17600, + 0, + 0, + 0, + 0, + 0, + 0, + 17601, + 0, + 0, + 0, + 17604, + 0, + 0, + 17605, + 0, + 0, + 17607, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 17612, + 0, + 0, + 17618, + 0, + 17621, + 17622, + 0, + 0, + 0, + 0, + 17623, + 0, + 0, + 17624, + 0, + 0, + 17630, + 0, + 0, + 17631, + 17633, + 17634, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 17635, + 0, + 0, + 17636, + 0, + 0, + 17637, + 0, + 17638, + 0, + 17640, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 17641, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 17643, + 0, + 0, + 0, + 0, + 17645, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 17646, + 17662, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 17663, + 17664, + 0, + 17665, + 17666, + 0, + 0, + 0, + 17669, + 17671, + 17673, + 0, + 17679, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 17684, + 0, + 0, + 0, + 17686, + 0, + 17714, + 0, + 0, + 17720, + 17722, + 17726, + 0, + 0, + 17728, + 0, + 0, + 17729, + 0, + 0, + 0, + 17732, + 0, + 17733, + 0, + 17734, + 0, + 0, + 0, + 17735, + 0, + 0, + 0, + 0, + 17737, + 0, + 0, + 0, + 0, + 17739, + 0, + 0, + 0, + 17741, + 17742, + 0, + 0, + 0, + 0, + 17743, + 17744, + 17745, + 0, + 0, + 0, + 17749, + 0, + 17750, + 17751, + 17752, + 17754, + 17761, + 17762, + 0, + 17763, + 0, + 17766, + 0, + 17772, + 0, + 0, + 0, + 0, + 0, + 17775, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 17776, + 0, + 0, + 17777, + 0, + 0, + 17778, + 17779, + 0, + 17782, + 17783, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 17784, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 17821, + 0, + 0, + 0, + 17822, + 0, + 0, + 0, + 17823, + 17825, + 0, + 0, + 0, + 0, + 0, + 17826, + 17831, + 17832, + 17833, + 0, + 0, + 17845, + 0, + 0, + 0, + 17846, + 0, + 0, + 0, + 17848, + 17850, + 17854, + 0, + 17855, + 0, + 0, + 17859, + 0, + 0, + 0, + 0, + 0, + 0, + 17860, + 17861, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 17870, + 17871, + 0, + 0, + 0, + 0, + 0, + 0, + 17872, + 0, + 0, + 0, + 17879, + 0, + 0, + 0, + 17881, + 17883, + 0, + 17884, + 0, + 17885, + 0, + 0, + 17886, + 0, + 0, + 17887, + 17891, + 17953, + 0, + 0, + 0, + 0, + 17954, + 0, + 0, + 17955, + 0, + 17968, + 0, + 0, + 17972, + 0, + 0, + 0, + 0, + 0, + 17974, + 0, + 0, + 0, + 0, + 17976, + 17978, + 0, + 0, + 17983, + 0, + 0, + 0, + 0, + 18003, + 0, + 0, + 0, + 0, + 0, + 18007, + 0, + 0, + 0, + 0, + 0, + 18009, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 18010, + 0, + 0, + 0, + 0, + 0, + 0, + 18012, + 0, + 0, + 18014, + 0, + 0, + 0, + 18015, + 0, + 0, + 0, + 18016, + 0, + 18017, + 0, + 0, + 0, + 18030, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 18031, + 0, + 0, + 18036, + 18037, + 18038, + 0, + 0, + 18049, + 18056, + 0, + 18057, + 18058, + 0, + 18059, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 18062, + 0, + 0, + 0, + 0, + 18064, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 18067, + 0, + 0, + 0, + 18068, + 0, + 0, + 18075, + 0, + 0, + 18078, + 18093, + 18094, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 18097, + 0, + 0, + 0, + 0, + 0, + 18098, + 18100, + 0, + 0, + 0, + 18108, + 0, + 18111, + 0, + 0, + 18112, + 0, + 18113, + 0, + 0, + 18115, + 18116, + 0, + 18118, + 0, + 0, + 0, + 0, + 18121, + 0, + 0, + 0, + 0, + 18123, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 18124, + 0, + 0, + 0, + 0, + 18125, + 18126, + 0, + 18127, + 0, + 0, + 18128, + 18135, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 18150, + 0, + 0, + 0, + 0, + 0, + 18151, + 18152, + 0, + 0, + 18156, + 18164, + 0, + 18166, + 18171, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 18172, + 18183, + 0, + 18184, + 0, + 0, + 0, + 0, + 18185, + 0, + 18187, + 0, + 0, + 0, + 0, + 0, + 18188, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 18189, + 0, + 0, + 18190, + 0, + 0, + 18191, + 18192, + 0, + 0, + 18194, + 18195, + 18196, + 0, + 0, + 0, + 18197, + 0, + 18203, + 0, + 18204, + 0, + 0, + 0, + 0, + 18205, + 0, + 0, + 0, + 18207, + 18208, + 0, + 0, + 18214, + 0, + 0, + 0, + 18215, + 18216, + 0, + 0, + 0, + 18220, + 0, + 0, + 18222, + 0, + 0, + 0, + 0, + 0, + 18223, + 0, + 18225, + 18231, + 0, + 18234, + 0, + 18235, + 0, + 0, + 0, + 0, + 18240, + 0, + 0, + 18241, + 18242, + 0, + 0, + 0, + 0, + 0, + 18243, + 18251, + 0, + 18253, + 0, + 18254, + 0, + 0, + 0, + 18266, + 0, + 0, + 0, + 0, + 0, + 0, + 18269, + 18270, + 18271, + 18273, + 18281, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 18282, + 0, + 18283, + 0, + 18284, + 0, + 0, + 0, + 0, + 0, + 0, + 18285, + 0, + 18287, + 18289, + 0, + 0, + 18290, + 0, + 0, + 0, + 0, + 18308, + 0, + 0, + 0, + 18310, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 18311, + 0, + 18312, + 18313, + 0, + 18315, + 0, + 0, + 18316, + 18320, + 0, + 18331, + 0, + 18332, + 0, + 18336, + 0, + 0, + 0, + 0, + 18337, + 0, + 18340, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 18341, + 0, + 18344, + 18345, + 0, + 18346, + 0, + 0, + 0, + 0, + 0, + 18348, + 0, + 18351, + 0, + 0, + 18356, + 0, + 0, + 0, + 0, + 0, + 0, + 18357, + 0, + 0, + 0, + 0, + 0, + 18367, + 0, + 0, + 0, + 18368, + 0, + 18369, + 0, + 18370, + 18371, + 0, + 0, + 0, + 18437, + 18444, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 18445, + 18450, + 0, + 0, + 0, + 0, + 18451, + 0, + 18452, + 0, + 0, + 0, + 18453, + 0, + 0, + 0, + 0, + 0, + 18455, + 0, + 0, + 0, + 18456, + 0, + 18457, + 0, + 18460, + 0, + 0, + 18461, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 18466, + 0, + 0, + 18467, + 0, + 0, + 0, + 0, + 18473, + 0, + 0, + 0, + 18476, + 0, + 18477, + 0, + 0, + 0, + 18478, + 18479, + 18480, + 0, + 0, + 0, + 18485, + 0, + 0, + 0, + 18486, + 0, + 0, + 0, + 0, + 0, + 0, + 18488, + 18490, + 0, + 0, + 0, + 0, + 0, + 0, + 18491, + 0, + 0, + 0, + 0, + 0, + 18495, + 0, + 0, + 18496, + 0, + 0, + 0, + 0, + 0, + 0, + 18505, + 0, + 18521, + 0, + 18522, + 18523, + 0, + 0, + 0, + 18525, + 18526, + 0, + 0, + 0, + 0, + 0, + 18527, + 0, + 0, + 0, + 0, + 18532, + 18533, + 0, + 18534, + 0, + 0, + 0, + 0, + 0, + 0, + 18535, + 18537, + 0, + 18538, + 0, + 0, + 0, + 0, + 0, + 0, + 18540, + 18541, + 18542, + 18543, + 0, + 18546, + 0, + 0, + 0, + 0, + 18553, + 18556, + 0, + 0, + 18558, + 0, + 0, + 18569, + 18571, + 0, + 0, + 0, + 18572, + 0, + 18574, + 0, + 0, + 0, + 0, + 18586, + 0, + 0, + 0, + 0, + 0, + 18588, + 0, + 0, + 18589, + 0, + 0, + 0, + 0, + 0, + 0, + 18590, + 0, + 18592, + 0, + 0, + 0, + 0, + 18594, + 0, + 0, + 0, + 18596, + 0, + 0, + 18597, + 18598, + 0, + 0, + 18601, + 0, + 0, + 0, + 0, + 18602, + 0, + 0, + 0, + 18603, + 18604, + 0, + 18605, + 0, + 0, + 0, + 0, + 18608, + 0, + 0, + 18611, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 18612, + 0, + 18616, + 0, + 0, + 18617, + 18619, + 0, + 0, + 0, + 18628, + 0, + 0, + 0, + 18629, + 0, + 0, + 18630, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 18631, + 0, + 18632, + 0, + 0, + 18635, + 18637, + 0, + 0, + 0, + 0, + 0, + 0, + 18641, + 18643, + 18648, + 0, + 18652, + 0, + 0, + 18653, + 0, + 18655, + 18656, + 0, + 0, + 0, + 18657, + 0, + 0, + 18666, + 18674, + 0, + 0, + 0, + 0, + 18677, + 18684, + 18685, + 0, + 0, + 18686, + 0, + 0, + 18690, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 18695, + 18696, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 18697, + 0, + 0, + 18700, + 0, + 0, + 0, + 0, + 0, + 0, + 18702, + 0, + 18708, + 0, + 0, + 18709, + 0, + 18710, + 0, + 0, + 18711, + 0, + 18714, + 0, + 0, + 18718, + 0, + 0, + 0, + 0, + 0, + 0, + 18719, + 0, + 0, + 18722, + 0, + 18726, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 18731, + 0, + 0, + 0, + 0, + 0, + 18739, + 18741, + 0, + 0, + 18742, + 0, + 18743, + 18744, + 18746, + 18748, + 0, + 18752, + 18753, + 0, + 0, + 18754, + 18763, + 0, + 18765, + 0, + 0, + 0, + 18766, + 0, + 0, + 0, + 18769, + 0, + 0, + 0, + 0, + 0, + 18773, + 18778, + 18779, + 18781, + 0, + 0, + 18784, + 18787, + 0, + 18788, + 0, + 18793, + 0, + 0, + 0, + 0, + 0, + 0, + 18795, + 0, + 0, + 18800, + 0, + 0, + 0, + 0, + 0, + 18801, + 18804, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 18806, + 0, + 0, + 0, + 18811, + 18815, + 18816, + 0, + 0, + 0, + 0, + 18825, + 0, + 0, + 18827, + 18829, + 0, + 0, + 18830, + 0, + 0, + 0, + 0, + 18831, + 0, + 0, + 18832, + 0, + 0, + 0, + 0, + 18833, + 0, + 18840, + 0, + 18841, + 0, + 18842, + 0, + 0, + 0, + 0, + 18843, + 0, + 18844, + 0, + 0, + 0, + 0, + 0, + 0, + 18845, + 18846, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 18848, + 0, + 0, + 0, + 18853, + 18860, + 0, + 0, + 18862, + 18866, + 0, + 0, + 18867, + 18869, + 0, + 0, + 18874, + 18881, + 18891, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 18892, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 18895, + 0, + 18896, + 0, + 0, + 0, + 18900, + 0, + 0, + 0, + 18901, + 0, + 18902, + 18915, + 18916, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 18919, + 0, + 0, + 0, + 0, + 0, + 18920, + 0, + 0, + 0, + 18921, + 18929, + 0, + 0, + 0, + 0, + 18930, + 0, + 0, + 0, + 0, + 0, + 0, + 18932, + 0, + 0, + 0, + 0, + 18934, + 18942, + 0, + 0, + 0, + 18951, + 18957, + 0, + 0, + 0, + 0, + 18958, + 0, + 0, + 0, + 0, + 18959, + 18960, + 0, + 0, + 18961, + 0, + 0, + 18962, + 0, + 0, + 0, + 0, + 18963, + 18964, + 0, + 0, + 0, + 18965, + 0, + 18967, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 18968, + 0, + 18969, + 0, + 18970, + 18973, + 18976, + 0, + 0, + 0, + 0, + 0, + 0, + 18977, + 0, + 0, + 0, + 18981, + 0, + 0, + 0, + 18990, + 0, + 18998, + 0, + 0, + 0, + 0, + 0, + 18999, + 19003, + 0, + 0, + 19005, + 0, + 0, + 0, + 19006, + 0, + 0, + 0, + 0, + 0, + 0, + 19008, + 19011, + 0, + 0, + 19018, + 0, + 0, + 19019, + 0, + 19024, + 0, + 19031, + 19032, + 0, + 19039, + 0, + 19041, + 19050, + 0, + 0, + 0, + 19051, + 19055, + 19056, + 0, + 19059, + 19063, + 19064, + 0, + 0, + 19088, + 0, + 0, + 0, + 19093, + 19094, + 0, + 0, + 0, + 0, + 19095, + 0, + 19096, + 0, + 0, + 0, + 19097, + 0, + 0, + 19098, + 0, + 19099, + 19100, + 0, + 0, + 19103, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 19111, + 0, + 0, + 0, + 0, + 0, + 0, + 19112, + 0, + 0, + 0, + 19116, + 19117, + 0, + 19121, + 19122, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 19123, + 19124, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 19125, + 19126, + 0, + 19128, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 19129, + 19130, + 19131, + 19132, + 0, + 0, + 19146, + 0, + 0, + 19147, + 19156, + 19158, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 19182, + 19185, + 0, + 0, + 19187, + 0, + 0, + 0, + 19193, + 0, + 0, + 0, + 0, + 0, + 19194, + 0, + 19197, + 0, + 0, + 0, + 0, + 19198, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 19202, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 19203, + 0, + 19205, + 19210, + 0, + 0, + 0, + 19213, + 0, + 19218, + 0, + 0, + 0, + 19223, + 19229, + 0, + 0, + 19230, + 0, + 0, + 19231, + 19232, + 19233, + 19239, + 0, + 0, + 0, + 0, + 0, + 19240, + 0, + 19248, + 19249, + 0, + 0, + 0, + 0, + 19254, + 0, + 19256, + 19258, + 19259, + 0, + 0, + 19261, + 0, + 19266, + 0, + 0, + 0, + 19272, + 0, + 19278, + 19281, + 19282, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 19283, + 0, + 0, + 19284, + 0, + 0, + 19285, + 19287, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 19288, + 19291, + 0, + 19292, + 0, + 0, + 0, + 0, + 19297, + 0, + 19298, + 0, + 0, + 0, + 0, + 19302, + 19303, + 0, + 0, + 0, + 0, + 19304, + 19305, + 0, + 0, + 0, + 0, + 19314, + 0, + 0, + 19315, + 0, + 0, + 19321, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 19322, + 0, + 19333, + 0, + 19334, + 19335, + 0, + 19336, + 19337, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 19346, + 0, + 0, + 19353, + 0, + 19354, + 19362, + 0, + 19366, + 19367, + 0, + 0, + 19369, + 0, + 19375, + 0, + 19377, + 19380, + 19388, + 0, + 0, + 0, + 0, + 0, + 19389, + 19390, + 0, + 0, + 0, + 0, + 19392, + 0, + 0, + 0, + 0, + 0, + 19402, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 19412, + 0, + 0, + 19413, + 19422, + 0, + 19424, + 0, + 0, + 0, + 19425, + 0, + 0, + 0, + 19428, + 0, + 0, + 0, + 0, + 19431, + 0, + 0, + 0, + 0, + 0, + 19432, + 0, + 0, + 0, + 0, + 0, + 19448, + 19459, + 0, + 0, + 19461, + 0, + 19462, + 19463, + 0, + 19467, + 19474, + 19482, + 0, + 0, + 0, + 0, + 19494, + 0, + 0, + 0, + 0, + 19501, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 19502, + 19504, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 19505, + 0, + 0, + 0, + 0, + 19506, + 19507, + 0, + 0, + 0, + 19508, + 0, + 0, + 19511, + 0, + 0, + 19514, + 0, + 19515, + 0, + 19516, + 0, + 19518, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 19530, + 0, + 19537, + 19538, + 0, + 19543, + 19546, + 0, + 19547, + 19551, + 0, + 0, + 0, + 0, + 0, + 0, + 19552, + 19553, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 19555, + 0, + 0, + 19556, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 19560, + 19561, + 0, + 0, + 19562, + 0, + 0, + 0, + 0, + 0, + 0, + 19565, + 19567, + 0, + 19568, + 0, + 0, + 0, + 19569, + 19570, + 0, + 19578, + 0, + 0, + 0, + 0, + 19580, + 0, + 0, + 0, + 0, + 19581, + 19584, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 19585, + 19586, + 0, + 0, + 0, + 19587, + 19588, + 0, + 19589, + 0, + 0, + 0, + 0, + 0, + 0, + 19592, + 19593, + 19599, + 0, + 19600, + 0, + 0, + 19604, + 0, + 0, + 19605, + 0, + 19606, + 19608, + 19610, + 0, + 19613, + 19614, + 0, + 0, + 0, + 0, + 0, + 0, + 19616, + 19617, + 0, + 0, + 19618, + 0, + 0, + 19619, + 0, + 0, + 0, + 19620, + 19621, + 19631, + 0, + 0, + 19632, + 19634, + 19636, + 0, + 19643, + 0, + 0, + 19644, + 19658, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 19659, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 19675, + 19677, + 0, + 0, + 0, + 0, + 19679, + 0, + 19683, + 0, + 19684, + 0, + 0, + 0, + 0, + 0, + 0, + 19687, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 19688, + 19689, + 19692, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 19695, + 19697, + 0, + 0, + 0, + 0, + 0, + 19698, + 19699, + 0, + 0, + 19700, + 0, + 19702, + 0, + 0, + 19703, + 0, + 0, + 0, + 0, + 0, + 0, + 19704, + 19708, + 0, + 19710, + 0, + 19713, + 0, + 0, + 0, + 19715, + 0, + 0, + 0, + 0, + 19718, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 19720, + 0, + 19722, + 0, + 0, + 19725, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 19730, + 0, + 0, + 0, + 0, + 0, + 19731, + 0, + 19734, + 19735, + 19739, + 0, + 0, + 19740, + 0, + 19741, + 0, + 0, + 0, + 19746, + 0, + 0, + 19747, + 0, + 19771, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 19772, + 19775, + 0, + 0, + 0, + 0, + 0, + 0, + 19778, + 0, + 0, + 0, + 0, + 0, + 19779, + 0, + 0, + 19780, + 19790, + 0, + 19791, + 0, + 0, + 19792, + 0, + 0, + 0, + 19793, + 0, + 0, + 19796, + 19797, + 0, + 0, + 0, + 19799, + 0, + 0, + 0, + 19801, + 0, + 0, + 0, + 0, + 19803, + 0, + 19804, + 0, + 19805, + 0, + 0, + 19807, + 0, + 0, + 0, + 19808, + 0, + 0, + 0, + 0, + 0, + 0, + 19809, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 19816, + 0, + 19821, + 0, + 19822, + 19830, + 19831, + 0, + 0, + 0, + 19833, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 19838, + 0, + 0, + 0, + 0, + 19839, + 0, + 0, + 19843, + 0, + 0, + 0, + 0, + 19845, + 0, + 0, + 0, + 0, + 19847, + 0, + 0, + 19848, + 0, + 19849, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 19851, + 0, + 0, + 0, + 19854, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 19864, + 0, + 19865, + 0, + 19866, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 19868, + 0, + 0, + 19870, + 0, + 0, + 19871, + 0, + 0, + 19872, + 19873, + 19875, + 0, + 19880, + 19882, + 19884, + 0, + 0, + 19885, + 19886, + 19888, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 19890, + 19892, + 19893, + 0, + 0, + 19894, + 0, + 0, + 0, + 19895, + 0, + 19896, + 19902, + 0, + 0, + 19903, + 0, + 0, + 19905, + 0, + 0, + 0, + 19906, + 0, + 19908, + 0, + 19909, + 19911, + 0, + 0, + 0, + 19913, + 19920, + 0, + 19938, + 19939, + 19940, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 19942, + 0, + 19943, + 0, + 19945, + 0, + 0, + 0, + 19951, + 19952, + 19954, + 19960, + 0, + 19965, + 0, + 19971, + 0, + 0, + 0, + 0, + 0, + 19975, + 0, + 19976, + 0, + 19990, + 0, + 0, + 19991, + 0, + 19993, + 0, + 19995, + 0, + 0, + 0, + 19998, + 19999, + 20001, + 0, + 20003, + 20005, + 0, + 20011, + 20012, + 0, + 0, + 0, + 0, + 0, + 0, + 20014, + 0, + 20020, + 0, + 0, + 0, + 0, + 20021, + 0, + 0, + 0, + 0, + 0, + 20023, + 20024, + 0, + 0, + 0, + 0, + 0, + 20025, + 0, + 0, + 20027, + 0, + 0, + 20029, + 0, + 0, + 20032, + 0, + 0, + 0, + 0, + 20044, + 20045, + 0, + 20048, + 20049, + 0, + 0, + 20050, + 0, + 20052, + 0, + 0, + 20054, + 20057, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 20059, + 0, + 0, + 20061, + 0, + 20062, + 0, + 20064, + 0, + 0, + 20066, + 0, + 0, + 20067, + 0, + 0, + 0, + 0, + 20069, + 0, + 0, + 0, + 0, + 0, + 0, + 20070, + 20071, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 20072, + 0, + 0, + 20073, + 20074, + 0, + 0, + 0, + 0, + 0, + 20075, + 0, + 20078, + 0, + 0, + 0, + 0, + 20080, + 0, + 20081, + 0, + 0, + 0, + 0, + 0, + 0, + 20095, + 0, + 20098, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 20107, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 20112, + 0, + 0, + 0, + 20113, + 20114, + 0, + 0, + 0, + 20115, + 20123, + 20124, + 0, + 0, + 0, + 20131, + 20133, + 20134, + 0, + 0, + 0, + 0, + 20136, + 0, + 0, + 20137, + 20138, + 20150, + 0, + 20152, + 0, + 0, + 0, + 20153, + 0, + 0, + 20154, + 0, + 0, + 0, + 20158, + 0, + 20163, + 0, + 0, + 20164, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 20166, + 0, + 20168, + 0, + 20170, + 0, + 20175, + 0, + 0, + 20178, + 0, + 0, + 0, + 0, + 20223, + 0, + 0, + 0, + 0, + 20224, + 0, + 20226, + 0, + 0, + 20230, + 0, + 20231, + 0, + 0, + 0, + 0, + 20232, + 0, + 0, + 20233, + 20234, + 0, + 20244, + 0, + 20247, + 0, + 0, + 0, + 0, + 0, + 0, + 20249, + 0, + 0, + 0, + 20250, + 0, + 0, + 0, + 0, + 20251, + 0, + 20253, + 0, + 20254, + 0, + 0, + 0, + 0, + 20256, + 0, + 0, + 20264, + 0, + 0, + 0, + 0, + 20266, + 0, + 0, + 0, + 20278, + 0, + 0, + 20279, + 20282, + 0, + 0, + 0, + 0, + 0, + 20283, + 0, + 20284, + 0, + 20285, + 0, + 20287, + 20290, + 0, + 0, + 0, + 0, + 20292, + 0, + 0, + 0, + 0, + 20293, + 20297, + 0, + 0, + 0, + 0, + 0, + 0, + 20299, + 0, + 20300, + 20303, + 0, + 0, + 0, + 0, + 0, + 0, + 20307, + 0, + 0, + 20308, + 0, + 20309, + 0, + 20310, + 0, + 0, + 0, + 0, + 0, + 0, + 20312, + 0, + 0, + 0, + 20314, + 0, + 0, + 0, + 0, + 20315, + 20316, + 0, + 20322, + 0, + 0, + 0, + 0, + 0, + 0, + 20339, + 0, + 0, + 0, + 20342, + 0, + 0, + 0, + 0, + 20352, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 20362, + 0, + 0, + 20365, + 0, + 20375, + 20377, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 20378, + 20379, + 0, + 20380, + 0, + 0, + 20381, + 0, + 20382, + 0, + 20383, + 0, + 20388, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 20390, + 20392, + 20393, + 0, + 0, + 20395, + 0, + 0, + 0, + 0, + 0, + 20396, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 20398, + 20415, + 0, + 0, + 0, + 20417, + 0, + 0, + 20420, + 0, + 0, + 20426, + 20428, + 0, + 20431, + 0, + 0, + 20432, + 0, + 20433, + 20434, + 20435, + 0, + 0, + 0, + 0, + 20440, + 0, + 0, + 0, + 0, + 0, + 20442, + 0, + 20443, + 0, + 20446, + 0, + 0, + 0, + 0, + 20448, + 0, + 20451, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 20452, + 20453, + 0, + 0, + 20454, + 0, + 0, + 0, + 0, + 0, + 0, + 20457, + 0, + 20458, + 0, + 0, + 0, + 20465, + 0, + 0, + 0, + 0, + 0, + 20469, + 0, + 0, + 0, + 20473, + 0, + 20476, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 20477, + 0, + 0, + 20485, + 0, + 0, + 20486, + 0, + 0, + 20487, + 0, + 20496, + 0, + 20497, + 0, + 0, + 20498, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 20499, + 20500, + 0, + 20501, + 0, + 0, + 0, + 0, + 0, + 20520, + 20527, + 0, + 20529, + 0, + 0, + 0, + 0, + 20539, + 0, + 0, + 20540, + 0, + 0, + 0, + 20543, + 0, + 0, + 0, + 20546, + 0, + 0, + 0, + 0, + 0, + 20548, + 0, + 0, + 20563, + 0, + 0, + 20564, + 0, + 20566, + 0, + 0, + 0, + 0, + 0, + 20589, + 0, + 0, + 0, + 0, + 20590, + 0, + 0, + 20593, + 20594, + 0, + 0, + 0, + 0, + 20595, + 0, + 20597, + 20598, + 0, + 0, + 0, + 20618, + 20620, + 0, + 0, + 0, + 0, + 20621, + 0, + 0, + 0, + 0, + 20627, + 0, + 0, + 0, + 0, + 0, + 20628, + 0, + 0, + 0, + 20629, + 0, + 20630, + 0, + 0, + 20639, + 0, + 0, + 0, + 0, + 0, + 20707, + 0, + 0, + 20709, + 0, + 0, + 0, + 20713, + 20714, + 0, + 0, + 0, + 0, + 0, + 20724, + 20725, + 0, + 0, + 0, + 0, + 20726, + 20728, + 20729, + 0, + 20733, + 0, + 20734, + 0, + 20735, + 20736, + 0, + 20737, + 0, + 0, + 20744, + 0, + 20745, + 0, + 20748, + 0, + 0, + 20749, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 20750, + 0, + 0, + 0, + 0, + 20754, + 0, + 0, + 0, + 20761, + 0, + 0, + 20763, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 20766, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 20767, + 0, + 0, + 0, + 0, + 20768, + 0, + 20769, + 20777, + 0, + 0, + 0, + 0, + 0, + 0, + 20785, + 0, + 0, + 0, + 20786, + 20795, + 20801, + 0, + 20802, + 0, + 20807, + 0, + 0, + 20808, + 0, + 0, + 20810, + 0, + 0, + 20811, + 0, + 20812, + 0, + 0, + 0, + 0, + 0, + 20813, + 0, + 0, + 20818, + 20820, + 20821, + 0, + 0, + 0, + 20822, + 0, + 20823, + 0, + 0, + 0, + 20826, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 20829, + 20830, + 20831, + 0, + 20832, + 20836, + 0, + 0, + 20839, + 0, + 0, + 20840, + 20842, + 0, + 20843, + 0, + 20844, + 0, + 20854, + 0, + 0, + 0, + 20855, + 0, + 0, + 0, + 0, + 20856, + 0, + 0, + 0, + 20869, + 0, + 0, + 20871, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 20873, + 0, + 0, + 0, + 0, + 0, + 20876, + 0, + 0, + 0, + 0, + 0, + 20880, + 0, + 0, + 20882, + 0, + 0, + 0, + 0, + 20883, + 20884, + 0, + 0, + 20890, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 20891, + 0, + 0, + 0, + 0, + 0, + 20905, + 0, + 20906, + 20910, + 0, + 0, + 20912, + 20915, + 0, + 0, + 0, + 0, + 0, + 20916, + 0, + 20917, + 0, + 20919, + 20920, + 20922, + 0, + 20927, + 0, + 20928, + 20929, + 20930, + 0, + 0, + 20935, + 0, + 0, + 20939, + 0, + 0, + 20941, + 0, + 0, + 0, + 20943, + 0, + 0, + 0, + 20946, + 20947, + 0, + 0, + 0, + 0, + 0, + 20950, + 0, + 20954, + 0, + 0, + 20955, + 20964, + 0, + 0, + 20967, + 0, + 0, + 0, + 0, + 0, + 20973, + 20975, + 0, + 0, + 0, + 20984, + 0, + 20987, + 20988, + 0, + 0, + 0, + 0, + 0, + 20989, + 0, + 0, + 0, + 20995, + 0, + 20998, + 0, + 20999, + 0, + 0, + 0, + 0, + 21000, + 21001, + 0, + 0, + 0, + 0, + 21008, + 0, + 21010, + 0, + 21016, + 0, + 0, + 0, + 21017, + 21018, + 0, + 0, + 0, + 0, + 0, + 21021, + 21026, + 21027, + 21028, + 0, + 0, + 21029, + 0, + 0, + 0, + 0, + 0, + 21030, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 21031, + 21032, + 0, + 0, + 0, + 0, + 0, + 21037, + 0, + 0, + 21038, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 21039, + 0, + 21041, + 0, + 21046, + 21047, + 0, + 0, + 0, + 21049, + 21053, + 0, + 0, + 21057, + 21064, + 21065, + 0, + 0, + 21066, + 21067, + 0, + 0, + 0, + 21069, + 0, + 0, + 0, + 21071, + 21072, + 0, + 0, + 21073, + 0, + 21074, + 0, + 0, + 21078, + 0, + 0, + 0, + 0, + 21079, + 0, + 0, + 21080, + 21081, + 0, + 0, + 21086, + 21087, + 0, + 21089, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 21091, + 0, + 21093, + 0, + 21094, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 21095, + 0, + 0, + 0, + 0, + 0, + 21096, + 0, + 21098, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 21099, + 0, + 0, + 21100, + 21101, + 21102, + 0, + 0, + 0, + 0, + 0, + 21103, + 0, + 21104, + 0, + 0, + 0, + 0, + 0, + 21105, + 21108, + 21109, + 0, + 0, + 21112, + 21113, + 0, + 0, + 0, + 0, + 0, + 0, + 21115, + 21122, + 21123, + 0, + 0, + 0, + 0, + 0, + 21125, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 21129, + 21131, + 0, + 0, + 21134, + 0, + 0, + 0, + 21137, + 21142, + 0, + 21143, + 0, + 0, + 21144, + 0, + 21145, + 21146, + 0, + 21152, + 21154, + 21155, + 21156, + 0, + 0, + 0, + 21160, + 0, + 0, + 0, + 0, + 0, + 0, + 21161, + 0, + 21164, + 0, + 21166, + 0, + 0, + 0, + 0, + 21170, + 0, + 0, + 0, + 0, + 21171, + 0, + 0, + 21172, + 0, + 21174, + 0, + 21175, + 0, + 0, + 0, + 0, + 0, + 21176, + 21179, + 21188, + 0, + 0, + 0, + 21189, + 0, + 0, + 21190, + 0, + 0, + 0, + 21192, + 0, + 0, + 21193, + 0, + 0, + 0, + 21198, + 0, + 21212, + 0, + 0, + 21213, + 0, + 0, + 0, + 0, + 0, + 0, + 21215, + 21216, + 0, + 0, + 21223, + 21225, + 0, + 21226, + 0, + 0, + 0, + 0, + 21227, + 21228, + 0, + 0, + 21229, + 0, + 0, + 0, + 0, + 21230, + 21236, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 21237, + 0, + 0, + 21238, + 21239, + 0, + 0, + 0, + 0, + 21256, + 0, + 0, + 0, + 0, + 0, + 21257, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 21259, + 0, + 0, + 0, + 21263, + 0, + 21272, + 0, + 21274, + 0, + 21282, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 21283, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 21294, + 0, + 0, + 21297, + 0, + 0, + 0, + 0, + 21298, + 0, + 0, + 0, + 21299, + 0, + 21300, + 21302, + 0, + 21316, + 0, + 21318, + 21322, + 21323, + 0, + 21324, + 0, + 21326, + 0, + 0, + 0, + 21327, + 21328, + 0, + 0, + 0, + 21352, + 0, + 0, + 21354, + 21361, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 21362, + 0, + 0, + 0, + 21363, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 21366, + 0, + 0, + 21367, + 21372, + 21374, + 0, + 0, + 0, + 21375, + 21377, + 0, + 21378, + 0, + 0, + 0, + 21380, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 21381, + 0, + 0, + 0, + 0, + 0, + 0, + 21382, + 0, + 21383, + 0, + 0, + 21384, + 0, + 0, + 21385, + 0, + 0, + 0, + 0, + 21389, + 21390, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 21397, + 21398, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 21399, + 0, + 21400, + 0, + 0, + 0, + 0, + 21402, + 0, + 0, + 0, + 21403, + 21404, + 0, + 21405, + 21406, + 0, + 0, + 0, + 21407, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 21408, + 0, + 0, + 0, + 0, + 21409, + 0, + 21421, + 0, + 21422, + 0, + 0, + 0, + 21425, + 21428, + 0, + 0, + 0, + 0, + 21429, + 0, + 0, + 0, + 0, + 0, + 21433, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 21434, + 0, + 21443, + 0, + 21444, + 21449, + 0, + 21452, + 0, + 21453, + 21454, + 0, + 0, + 0, + 21457, + 0, + 0, + 21458, + 0, + 0, + 0, + 21460, + 21461, + 0, + 0, + 21464, + 0, + 0, + 0, + 21473, + 21478, + 0, + 0, + 21479, + 0, + 0, + 21481, + 21483, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 21484, + 0, + 0, + 21485, + 21486, + 0, + 0, + 21488, + 0, + 0, + 0, + 0, + 0, + 0, + 21523, + 0, + 0, + 21525, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 21526, + 0, + 0, + 0, + 0, + 0, + 0, + 21529, + 21530, + 0, + 0, + 21531, + 0, + 0, + 21533, + 0, + 0, + 21539, + 21564, + 0, + 21567, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 21575, + 0, + 0, + 0, + 0, + 21577, + 0, + 0, + 0, + 0, + 0, + 21591, + 0, + 0, + 21604, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 21605, + 0, + 21606, + 0, + 0, + 21617, + 21618, + 21619, + 21620, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 21623, + 0, + 0, + 0, + 0, + 21631, + 0, + 21635, + 0, + 0, + 0, + 0, + 21639, + 21646, + 21653, + 21662, + 0, + 0, + 21663, + 21664, + 0, + 21666, + 0, + 0, + 21667, + 0, + 21670, + 21672, + 21673, + 0, + 21674, + 21683, + 0, + 0, + 0, + 0, + 0, + 21684, + 0, + 21694, + 0, + 0, + 0, + 0, + 21695, + 21700, + 0, + 21703, + 0, + 21704, + 0, + 0, + 21709, + 0, + 0, + 0, + 21710, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 21711, + 0, + 0, + 0, + 21712, + 0, + 21717, + 0, + 21730, + 0, + 0, + 0, + 21731, + 21733, + 0, + 0, + 0, + 0, + 21737, + 21741, + 21742, + 0, + 21747, + 0, + 0, + 0, + 21749, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 21750, + 0, + 0, + 0, + 0, + 0, + 21752, + 0, + 0, + 0, + 0, + 21753, + 0, + 0, + 0, + 0, + 0, + 0, + 21755, + 21756, + 0, + 21757, + 0, + 0, + 0, + 0, + 0, + 0, + 21760, + 0, + 0, + 21763, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 21764, + 0, + 0, + 21766, + 0, + 0, + 21767, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 21773, + 0, + 21774, + 0, + 0, + 21775, + 0, + 0, + 0, + 0, + 21776, + 0, + 0, + 21777, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 21780, + 21787, + 21788, + 21791, + 0, + 0, + 0, + 21797, + 0, + 0, + 0, + 0, + 0, + 21805, + 0, + 0, + 0, + 0, + 21806, + 0, + 21807, + 21809, + 0, + 21810, + 21811, + 0, + 21817, + 21819, + 21820, + 0, + 21823, + 0, + 21824, + 0, + 0, + 21825, + 0, + 0, + 21826, + 21832, + 0, + 0, + 0, + 0, + 0, + 21833, + 21848, + 21849, + 0, + 0, + 21867, + 21870, + 21871, + 21873, + 0, + 0, + 0, + 21874, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 21875, + 0, + 21878, + 0, + 0, + 0, + 21879, + 0, + 21881, + 21886, + 0, + 0, + 0, + 0, + 21887, + 0, + 0, + 21888, + 21894, + 21895, + 21897, + 0, + 21901, + 0, + 21904, + 0, + 0, + 21906, + 0, + 0, + 0, + 21909, + 21910, + 21911, + 0, + 0, + 21912, + 0, + 0, + 21913, + 21914, + 21915, + 0, + 21919, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 21921, + 0, + 0, + 21922, + 21933, + 21939, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 21944, + 0, + 0, + 0, + 0, + 0, + 21945, + 0, + 21947, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 21949, + 0, + 0, + 0, + 21950, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 21951, + 0, + 21952, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 21954, + 21957, + 0, + 0, + 0, + 0, + 21958, + 0, + 21959, + 0, + 0, + 0, + 0, + 0, + 0, + 21962, + 21963, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 21964, + 21965, + 0, + 0, + 21969, + 21970, + 0, + 0, + 0, + 21974, + 0, + 0, + 21980, + 21981, + 0, + 21982, + 0, + 0, + 0, + 0, + 0, + 21985, + 0, + 21988, + 0, + 21992, + 0, + 21999, + 0, + 0, + 0, + 0, + 0, + 0, + 22001, + 0, + 22002, + 0, + 0, + 0, + 0, + 0, + 0, + 22003, + 0, + 0, + 0, + 0, + 0, + 22004, + 0, + 0, + 0, + 22008, + 0, + 22009, + 22015, + 0, + 0, + 22016, + 0, + 0, + 0, + 22017, + 22019, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 22020, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 22021, + 22037, + 0, + 22039, + 0, + 0, + 0, + 22040, + 0, + 0, + 0, + 22048, + 22049, + 0, + 0, + 22053, + 22055, + 22056, + 22059, + 0, + 0, + 22060, + 22061, + 0, + 0, + 22064, + 0, + 0, + 0, + 0, + 22066, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 22073, + 0, + 0, + 0, + 22074, + 22075, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 22076, + 0, + 0, + 0, + 0, + 22077, + 22084, + 22099, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 22104, + 0, + 0, + 22107, + 0, + 22108, + 0, + 22109, + 0, + 22110, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 22111, + 22119, + 0, + 22120, + 22122, + 0, + 0, + 0, + 0, + 22125, + 0, + 0, + 0, + 22128, + 22129, + 0, + 0, + 0, + 0, + 0, + 0, + 22141, + 0, + 0, + 0, + 22142, + 0, + 0, + 22144, + 22146, + 0, + 22148, + 22149, + 22151, + 22154, + 0, + 0, + 0, + 22162, + 0, + 0, + 0, + 0, + 22164, + 22177, + 0, + 0, + 0, + 0, + 22179, + 0, + 22182, + 22183, + 0, + 0, + 22184, + 22188, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 22190, + 0, + 22194, + 22201, + 0, + 0, + 22208, + 0, + 22209, + 0, + 22212, + 0, + 0, + 22215, + 0, + 22223, + 22231, + 0, + 0, + 22232, + 0, + 22234, + 0, + 0, + 22235, + 22236, + 0, + 22237, + 0, + 22240, + 0, + 0, + 0, + 0, + 0, + 22241, + 0, + 0, + 0, + 22242, + 22246, + 22247, + 0, + 0, + 0, + 22259, + 22268, + 0, + 22269, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 22270, + 0, + 0, + 0, + 0, + 22271, + 0, + 22272, + 0, + 22277, + 0, + 0, + 0, + 0, + 0, + 22278, + 22280, + 22283, + 22286, + 0, + 0, + 22287, + 22289, + 0, + 0, + 22290, + 0, + 22293, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 22295, + 0, + 22301, + 22302, + 0, + 0, + 0, + 22305, + 0, + 22308, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 22315, + 0, + 0, + 0, + 22317, + 0, + 22334, + 0, + 0, + 0, + 22335, + 0, + 0, + 0, + 0, + 0, + 22336, + 0, + 22338, + 22344, + 0, + 22347, + 22349, + 0, + 22350, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 22357, + 0, + 0, + 0, + 0, + 0, + 22358, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 22359, + 22360, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 22361, + 22366, + 0, + 0, + 22369, + 0, + 22370, + 22373, + 0, + 0, + 0, + 0, + 0, + 22375, + 0, + 22377, + 0, + 0, + 0, + 0, + 0, + 22378, + 0, + 0, + 0, + 0, + 22381, + 0, + 0, + 0, + 0, + 22382, + 0, + 22383, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 22391, + 0, + 0, + 22392, + 22395, + 22396, + 22402, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 22405, + 0, + 0, + 22406, + 0, + 0, + 22408, + 0, + 0, + 22409, + 22410, + 0, + 0, + 0, + 0, + 0, + 0, + 22424, + 0, + 0, + 0, + 0, + 22426, + 0, + 0, + 0, + 22427, + 0, + 22428, + 0, + 22432, + 0, + 22435, + 22442, + 22443, + 0, + 0, + 0, + 0, + 22444, + 0, + 0, + 0, + 0, + 0, + 22446, + 0, + 22454, + 0, + 22455, + 0, + 0, + 0, + 22465, + 0, + 22470, + 0, + 22471, + 0, + 0, + 0, + 0, + 22472, + 22473, + 0, + 22487, + 0, + 0, + 0, + 22488, + 0, + 0, + 0, + 0, + 22489, + 0, + 0, + 22499, + 0, + 0, + 0, + 0, + 0, + 0, + 22514, + 0, + 0, + 22515, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 22516, + 0, + 0, + 0, + 22517, + 22520, + 0, + 0, + 0, + 22534, + 0, + 0, + 22535, + 0, + 0, + 22536, + 0, + 22540, + 22553, + 0, + 22555, + 0, + 0, + 0, + 0, + 22561, + 0, + 0, + 22562, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 22566, + 0, + 0, + 0, + 0, + 22567, + 22568, + 0, + 0, + 22575, + 0, + 22579, + 0, + 22582, + 22583, + 22585, + 0, + 0, + 0, + 0, + 0, + 22586, + 0, + 0, + 22587, + 0, + 0, + 22590, + 0, + 0, + 0, + 0, + 0, + 22591, + 0, + 22592, + 0, + 0, + 0, + 0, + 0, + 22593, + 0, + 22602, + 0, + 0, + 22604, + 0, + 0, + 22609, + 0, + 0, + 22618, + 0, + 0, + 0, + 0, + 0, + 0, + 22619, + 0, + 22624, + 22625, + 0, + 0, + 22638, + 0, + 0, + 0, + 0, + 0, + 22639, + 0, + 0, + 22640, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 22644, + 0, + 22645, + 22647, + 0, + 0, + 0, + 0, + 22652, + 22653, + 0, + 0, + 0, + 22654, + 0, + 22655, + 0, + 0, + 0, + 22656, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 22673, + 22675, + 22676, + 0, + 0, + 22678, + 22679, + 0, + 22691, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 22693, + 0, + 0, + 22696, + 0, + 22699, + 22707, + 22708, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 22718, + 0, + 22719, + 0, + 0, + 0, + 0, + 22723, + 0, + 0, + 0, + 22724, + 22725, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 22726, + 22728, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 22729, + 0, + 0, + 22731, + 0, + 0, + 0, + 0, + 22732, + 22735, + 22736, + 0, + 0, + 0, + 0, + 22739, + 0, + 22749, + 0, + 0, + 22751, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 22758, + 0, + 0, + 0, + 0, + 0, + 22760, + 0, + 0, + 0, + 0, + 0, + 22764, + 22765, + 22766, + 0, + 22768, + 0, + 0, + 0, + 0, + 0, + 22769, + 22770, + 0, + 0, + 0, + 0, + 0, + 0, + 22771, + 0, + 0, + 22772, + 22775, + 0, + 22776, + 22777, + 22780, + 0, + 0, + 22782, + 22784, + 0, + 22787, + 0, + 22789, + 22796, + 0, + 0, + 0, + 0, + 0, + 22798, + 0, + 0, + 0, + 0, + 0, + 0, + 22802, + 0, + 22803, + 22804, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 22805, + 0, + 0, + 22810, + 22811, + 22814, + 22816, + 0, + 22825, + 22826, + 0, + 22831, + 22833, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 22834, + 0, + 22836, + 22838, + 0, + 22839, + 0, + 0, + 0, + 0, + 0, + 22840, + 0, + 22847, + 0, + 0, + 0, + 0, + 0, + 22856, + 22857, + 0, + 22858, + 22859, + 0, + 0, + 22862, + 0, + 0, + 22864, + 0, + 0, + 0, + 0, + 22865, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 22866, + 0, + 22867, + 22868, + 0, + 0, + 0, + 0, + 22869, + 0, + 22871, + 0, + 22872, + 0, + 22873, + 22881, + 22882, + 22884, + 22885, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 22886, + 22887, + 0, + 22894, + 0, + 22895, + 0, + 0, + 0, + 22900, + 0, + 22901, + 0, + 0, + 0, + 0, + 22904, + 0, + 0, + 0, + 0, + 22905, + 22907, + 0, + 0, + 0, + 22915, + 22917, + 0, + 0, + 22918, + 0, + 0, + 0, + 22920, + 0, + 0, + 0, + 22929, + 22930, + 0, + 0, + 0, + 22941, + 22942, + 0, + 0, + 0, + 22943, + 0, + 0, + 0, + 22944, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 22946, + 0, + 22947, + 0, + 0, + 22954, + 0, + 22956, + 0, + 0, + 22962, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 22963, + 0, + 0, + 22964, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 22965, + 0, + 22968, + 0, + 0, + 0, + 22969, + 0, + 0, + 0, + 0, + 0, + 22970, + 0, + 22971, + 0, + 0, + 0, + 0, + 0, + 22978, + 0, + 0, + 22979, + 0, + 22987, + 0, + 0, + 22989, + 0, + 0, + 0, + 0, + 0, + 0, + 22990, + 0, + 23005, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 23006, + 23007, + 23008, + 0, + 0, + 23023, + 23024, + 23029, + 0, + 0, + 0, + 0, + 23030, + 0, + 0, + 0, + 0, + 0, + 23032, + 0, + 0, + 0, + 0, + 0, + 23035, + 0, + 0, + 0, + 0, + 23038, + 0, + 0, + 0, + 23048, + 0, + 23049, + 23052, + 23053, + 23060, + 23061, + 0, + 23063, + 0, + 0, + 0, + 0, + 23067, + 23068, + 0, + 0, + 0, + 23069, + 23073, + 0, + 0, + 0, + 23127, + 0, + 23128, + 0, + 0, + 0, + 0, + 0, + 23129, + 0, + 23138, + 23141, + 0, + 23149, + 0, + 0, + 23150, + 0, + 0, + 0, + 23152, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 23154, + 0, + 0, + 0, + 0, + 23157, + 23159, + 23160, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 23180, + 0, + 0, + 0, + 0, + 23181, + 0, + 0, + 23188, + 0, + 23189, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 23195, + 0, + 0, + 23196, + 23199, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 23202, + 0, + 23204, + 0, + 23207, + 0, + 23209, + 23210, + 0, + 0, + 0, + 0, + 0, + 0, + 23227, + 23229, + 0, + 0, + 23230, + 23234, + 23238, + 0, + 0, + 0, + 23245, + 23246, + 23248, + 0, + 0, + 0, + 0, + 23249, + 23254, + 0, + 0, + 0, + 23265, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 23268, + 0, + 23276, + 0, + 0, + 0, + 0, + 23277, + 0, + 23297, + 0, + 23298, + 0, + 0, + 0, + 0, + 23299, + 0, + 23302, + 0, + 0, + 23303, + 23312, + 0, + 0, + 23314, + 0, + 23320, + 0, + 0, + 0, + 0, + 23324, + 0, + 23325, + 0, + 23328, + 0, + 23334, + 0, + 0, + 0, + 23337, + 0, + 0, + 0, + 0, + 23343, + 23344, + 23346, + 0, + 23348, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 23353, + 0, + 0, + 0, + 0, + 23355, + 0, + 23356, + 23358, + 0, + 0, + 0, + 23359, + 23360, + 0, + 23361, + 0, + 23367, + 0, + 23369, + 0, + 0, + 23373, + 0, + 23378, + 23379, + 0, + 23382, + 23383, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 23387, + 0, + 0, + 0, + 0, + 0, + 0, + 23388, + 23390, + 0, + 0, + 23393, + 23398, + 0, + 0, + 0, + 23399, + 0, + 0, + 0, + 23400, + 0, + 0, + 0, + 0, + 23401, + 0, + 0, + 0, + 23415, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 23416, + 0, + 23422, + 0, + 23443, + 23444, + 0, + 0, + 0, + 0, + 23448, + 0, + 23454, + 0, + 0, + 0, + 0, + 0, + 0, + 23456, + 0, + 0, + 23458, + 23464, + 0, + 0, + 0, + 0, + 0, + 0, + 23465, + 0, + 0, + 0, + 23470, + 23471, + 0, + 0, + 23472, + 0, + 0, + 0, + 23473, + 23496, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 23497, + 0, + 23499, + 0, + 0, + 23502, + 0, + 0, + 23503, + 0, + 0, + 23513, + 0, + 0, + 23515, + 0, + 0, + 0, + 23517, + 0, + 0, + 0, + 0, + 23518, + 23519, + 23521, + 23524, + 0, + 23525, + 23528, + 23539, + 0, + 0, + 0, + 0, + 0, + 23541, + 0, + 0, + 23544, + 0, + 0, + 23556, + 0, + 0, + 23557, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 23559, + 0, + 23560, + 0, + 0, + 23561, + 0, + 0, + 23566, + 0, + 0, + 0, + 0, + 0, + 23568, + 23569, + 23570, + 0, + 0, + 0, + 0, + 23571, + 0, + 23574, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 23575, + 0, + 23579, + 0, + 0, + 23581, + 0, + 0, + 0, + 0, + 0, + 0, + 23587, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 23596, + 23598, + 0, + 0, + 0, + 0, + 23602, + 23606, + 0, + 0, + 23607, + 0, + 23608, + 0, + 0, + 0, + 23614, + 23616, + 0, + 0, + 0, + 0, + 0, + 23618, + 0, + 0, + 23619, + 0, + 0, + 0, + 0, + 23621, + 23626, + 0, + 23627, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 23629, + 0, + 23630, + 0, + 0, + 0, + 0, + 23634, + 0, + 23636, + 0, + 0, + 0, + 0, + 0, + 0, + 23638, + 0, + 0, + 0, + 0, + 23640, + 23667, + 0, + 23669, + 0, + 0, + 0, + 23681, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 23682, + 0, + 23683, + 0, + 0, + 0, + 0, + 0, + 23684, + 0, + 0, + 0, + 23685, + 23689, + 0, + 23693, + 23694, + 23700, + 0, + 23702, + 0, + 23709, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 23712, + 0, + 0, + 0, + 0, + 0, + 23714, + 0, + 0, + 23715, + 0, + 0, + 0, + 0, + 23718, + 0, + 0, + 23720, + 0, + 0, + 0, + 0, + 23722, + 0, + 0, + 0, + 23726, + 23729, + 0, + 23741, + 23746, + 0, + 23748, + 0, + 0, + 0, + 0, + 23749, + 0, + 0, + 0, + 0, + 0, + 23750, + 0, + 0, + 0, + 0, + 23751, + 0, + 23753, + 0, + 0, + 0, + 0, + 23757, + 23765, + 0, + 0, + 0, + 23770, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 23771, + 0, + 23772, + 23781, + 0, + 0, + 23796, + 0, + 0, + 0, + 0, + 23798, + 0, + 23799, + 0, + 0, + 0, + 23802, + 0, + 0, + 23806, + 0, + 23807, + 0, + 0, + 23808, + 0, + 23809, + 0, + 23819, + 0, + 0, + 0, + 23821, + 0, + 23827, + 0, + 0, + 0, + 23829, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 23830, + 0, + 0, + 0, + 0, + 0, + 0, + 23832, + 23833, + 23834, + 23835, + 0, + 0, + 0, + 0, + 23837, + 23838, + 0, + 0, + 0, + 0, + 0, + 23846, + 0, + 0, + 0, + 0, + 0, + 0, + 23847, + 0, + 0, + 0, + 0, + 0, + 23879, + 23881, + 0, + 0, + 23882, + 23883, + 23895, + 0, + 23899, + 0, + 0, + 0, + 0, + 23901, + 0, + 0, + 0, + 0, + 0, + 0, + 23902, + 0, + 0, + 0, + 0, + 0, + 23903, + 23905, + 0, + 23906, + 0, + 23907, + 23918, + 23919, + 23920, + 0, + 23922, + 0, + 23924, + 0, + 23927, + 0, + 23934, + 0, + 23937, + 23941, + 0, + 23942, + 23946, + 0, + 0, + 0, + 0, + 0, + 23955, + 23956, + 23958, + 0, + 0, + 0, + 0, + 0, + 0, + 23959, + 0, + 23962, + 23965, + 0, + 23966, + 0, + 0, + 0, + 0, + 23967, + 23968, + 0, + 0, + 23973, + 0, + 0, + 23974, + 0, + 0, + 0, + 0, + 23975, + 0, + 23976, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 23977, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 23980, + 0, + 0, + 23984, + 0, + 23985, + 0, + 0, + 23987, + 0, + 0, + 23988, + 23990, + 23991, + 0, + 0, + 0, + 0, + 0, + 0, + 23992, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 23994, + 0, + 0, + 0, + 23998, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 23999, + 0, + 0, + 24003, + 0, + 24004, + 0, + 24006, + 0, + 0, + 0, + 24007, + 0, + 0, + 24008, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 24009, + 0, + 0, + 24010, + 0, + 0, + 24011, + 0, + 0, + 24013, + 24014, + 0, + 0, + 24015, + 24016, + 24027, + 0, + 24028, + 24029, + 0, + 24030, + 0, + 0, + 0, + 0, + 0, + 24033, + 24034, + 0, + 24035, + 0, + 0, + 24036, + 0, + 0, + 24044, + 0, + 24048, + 24049, + 24063, + 24067, + 0, + 24068, + 24070, + 0, + 0, + 24071, + 24078, + 24087, + 0, + 24090, + 0, + 0, + 0, + 24095, + 0, + 24098, + 24101, + 24104, + 24106, + 0, + 24107, + 0, + 0, + 0, + 24108, + 0, + 0, + 0, + 0, + 24110, + 24111, + 0, + 24113, + 0, + 0, + 24115, + 24120, + 0, + 0, + 0, + 0, + 0, + 0, + 24124, + 0, + 24125, + 0, + 24126, + 0, + 24127, + 0, + 0, + 0, + 0, + 0, + 24135, + 0, + 0, + 24136, + 0, + 24137, + 24142, + 0, + 0, + 0, + 24146, + 0, + 0, + 24147, + 24149, + 24154, + 0, + 24163, + 0, + 0, + 0, + 24165, + 24166, + 24167, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 24169, + 24170, + 24175, + 0, + 0, + 0, + 24178, + 0, + 0, + 24179, + 0, + 0, + 24181, + 0, + 24184, + 24197, + 0, + 24201, + 24204, + 0, + 0, + 0, + 0, + 0, + 0, + 24206, + 24212, + 24220, + 0, + 0, + 0, + 24224, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 24226, + 0, + 24234, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 24235, + 0, + 24236, + 0, + 0, + 0, + 0, + 0, + 24239, + 24240, + 24241, + 0, + 0, + 24248, + 0, + 0, + 24249, + 0, + 24251, + 0, + 0, + 0, + 0, + 0, + 0, + 24253, + 0, + 24268, + 0, + 0, + 0, + 24269, + 0, + 24271, + 24272, + 0, + 0, + 0, + 0, + 24273, + 0, + 0, + 24274, + 0, + 0, + 24279, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 24280, + 0, + 24293, + 24294, + 0, + 0, + 0, + 0, + 0, + 0, + 24296, + 0, + 0, + 24323, + 0, + 0, + 0, + 24329, + 24330, + 24331, + 24339, + 0, + 24351, + 0, + 0, + 24369, + 24370, + 0, + 0, + 0, + 24371, + 0, + 0, + 0, + 0, + 24372, + 24373, + 24374, + 0, + 0, + 0, + 0, + 0, + 24378, + 0, + 0, + 0, + 0, + 24379, + 0, + 24381, + 0, + 24383, + 24389, + 0, + 24390, + 0, + 0, + 24394, + 24395, + 24400, + 0, + 0, + 0, + 24401, + 24402, + 0, + 24406, + 0, + 0, + 0, + 24411, + 0, + 0, + 0, + 24415, + 0, + 24416, + 0, + 0, + 0, + 0, + 0, + 24417, + 0, + 24419, + 0, + 24422, + 0, + 24423, + 24428, + 0, + 24435, + 0, + 0, + 0, + 24439, + 0, + 0, + 0, + 24440, + 24442, + 24446, + 0, + 0, + 0, + 24447, + 24448, + 24449, + 24452, + 0, + 0, + 0, + 0, + 24453, + 24457, + 0, + 0, + 24458, + 24459, + 24460, + 0, + 24465, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 24470, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 24471, + 0, + 24473, + 24474, + 24475, + 24476, + 0, + 24478, + 0, + 0, + 0, + 0, + 24480, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 24481, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 24482, + 24485, + 0, + 0, + 0, + 0, + 24486, + 0, + 0, + 0, + 24488, + 0, + 0, + 0, + 24494, + 0, + 0, + 0, + 0, + 24497, + 0, + 0, + 24498, + 0, + 0, + 0, + 24499, + 24506, + 0, + 0, + 0, + 24507, + 0, + 0, + 24511, + 0, + 0, + 24513, + 24514, + 0, + 0, + 0, + 0, + 0, + 24517, + 0, + 24518, + 0, + 24520, + 0, + 24521, + 24524, + 24525, + 0, + 0, + 0, + 0, + 0, + 24527, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 24528, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 24537, + 24539, + 0, + 24540, + 0, + 0, + 0, + 24548, + 0, + 0, + 0, + 0, + 0, + 24549, + 24550, + 0, + 0, + 0, + 24553, + 24554, + 0, + 24555, + 0, + 24556, + 0, + 24558, + 0, + 0, + 0, + 0, + 0, + 24560, + 0, + 0, + 0, + 24561, + 0, + 0, + 0, + 0, + 0, + 24562, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 24567, + 0, + 0, + 0, + 0, + 0, + 24569, + 0, + 0, + 0, + 24574, + 0, + 24575, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 24577, + 24581, + 0, + 24584, + 0, + 0, + 0, + 0, + 0, + 24585, + 0, + 0, + 0, + 0, + 0, + 24586, + 0, + 0, + 24587, + 0, + 24588, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 24590, + 24591, + 0, + 0, + 0, + 0, + 24592, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 24594, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 24596, + 24597, + 0, + 0, + 0, + 0, + 24602, + 24603, + 0, + 0, + 0, + 0, + 24604, + 0, + 0, + 24605, + 0, + 24610, + 0, + 0, + 24611, + 0, + 0, + 0, + 0, + 24612, + 24615, + 24616, + 24624, + 0, + 0, + 0, + 24627, + 0, + 24638, + 24639, + 0, + 0, + 0, + 0, + 24640, + 0, + 0, + 0, + 24655, + 24656, + 24657, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 24662, + 0, + 24663, + 24664, + 0, + 0, + 0, + 0, + 0, + 24665, + 0, + 0, + 0, + 0, + 24667, + 0, + 0, + 0, + 0, + 0, + 0, + 24668, + 24669, + 0, + 24670, + 24674, + 0, + 0, + 0, + 24675, + 0, + 24678, + 0, + 0, + 24679, + 0, + 0, + 0, + 24681, + 0, + 24683, + 0, + 0, + 0, + 0, + 24684, + 0, + 24685, + 0, + 0, + 24686, + 0, + 0, + 24688, + 24689, + 0, + 0, + 0, + 0, + 24690, + 24691, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 24697, + 0, + 24698, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 24709, + 0, + 0, + 0, + 0, + 0, + 24710, + 0, + 24712, + 0, + 0, + 0, + 0, + 0, + 0, + 24713, + 24714, + 0, + 24715, + 0, + 24716, + 24718, + 0, + 24719, + 0, + 0, + 0, + 0, + 24720, + 0, + 0, + 24725, + 0, + 0, + 24738, + 0, + 24749, + 24750, + 0, + 0, + 0, + 24752, + 0, + 0, + 0, + 24753, + 0, + 0, + 0, + 24758, + 0, + 0, + 0, + 0, + 0, + 24762, + 0, + 24763, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 24764, + 0, + 0, + 0, + 0, + 0, + 24765, + 24767, + 24768, + 0, + 24772, + 0, + 0, + 0, + 0, + 24773, + 0, + 0, + 0, + 0, + 24777, + 0, + 0, + 0, + 0, + 0, + 24785, + 0, + 24786, + 24788, + 0, + 0, + 0, + 24789, + 0, + 0, + 0, + 0, + 24794, + 24798, + 0, + 24799, + 24800, + 0, + 0, + 0, + 24803, + 0, + 24804, + 24806, + 0, + 24807, + 0, + 0, + 0, + 24810, + 0, + 0, + 0, + 0, + 0, + 0, + 24827, + 24828, + 0, + 24835, + 0, + 0, + 0, + 0, + 0, + 0, + 24836, + 0, + 0, + 0, + 0, + 0, + 24839, + 0, + 24843, + 24844, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 24847, + 0, + 0, + 24848, + 0, + 0, + 0, + 0, + 0, + 0, + 24849, + 0, + 24850, + 24851, + 0, + 0, + 0, + 24852, + 0, + 24853, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 24854, + 0, + 24855, + 0, + 0, + 24868, + 0, + 0, + 0, + 24883, + 0, + 0, + 0, + 24884, + 0, + 24895, + 24897, + 0, + 0, + 0, + 0, + 0, + 24899, + 0, + 0, + 0, + 0, + 0, + 24900, + 0, + 24913, + 0, + 0, + 0, + 0, + 0, + 0, + 24914, + 0, + 0, + 24917, + 24930, + 24931, + 0, + 0, + 0, + 24932, + 0, + 0, + 24939, + 0, + 0, + 24942, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 24945, + 24950, + 0, + 24951, + 0, + 0, + 24953, + 0, + 0, + 0, + 24954, + 0, + 24959, + 0, + 0, + 0, + 24961, + 0, + 0, + 24962, + 0, + 24964, + 24968, + 24970, + 24972, + 0, + 0, + 0, + 0, + 0, + 24976, + 0, + 0, + 0, + 24977, + 0, + 24982, + 0, + 0, + 24983, + 0, + 0, + 24984, + 0, + 0, + 0, + 24993, + 0, + 0, + 0, + 24994, + 0, + 0, + 25001, + 0, + 0, + 0, + 25003, + 0, + 0, + 25018, + 0, + 0, + 25023, + 0, + 0, + 0, + 25034, + 0, + 0, + 25035, + 25036, + 0, + 25037, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 25039, + 0, + 0, + 0, + 0, + 0, + 25040, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 25042, + 0, + 0, + 25043, + 25045, + 0, + 0, + 0, + 0, + 0, + 0, + 25049, + 0, + 0, + 25051, + 0, + 25052, + 25053, + 0, + 0, + 25054, + 0, + 0, + 0, + 25055, + 0, + 0, + 0, + 0, + 25057, + 25059, + 0, + 0, + 25060, + 25064, + 0, + 25065, + 25069, + 25070, + 0, + 0, + 0, + 0, + 25072, + 0, + 25073, + 0, + 25090, + 0, + 0, + 25092, + 25093, + 25101, + 0, + 0, + 0, + 0, + 0, + 0, + 25105, + 25108, + 0, + 0, + 25113, + 0, + 0, + 25115, + 25116, + 0, + 0, + 0, + 0, + 0, + 0, + 25117, + 0, + 0, + 0, + 25120, + 25121, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 25125, + 0, + 0, + 0, + 25126, + 0, + 25130, + 25134, + 0, + 25139, + 0, + 25143, + 0, + 0, + 0, + 25151, + 0, + 25161, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 25163, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 25174, + 0, + 25175, + 0, + 25207, + 0, + 0, + 0, + 25209, + 0, + 0, + 0, + 0, + 25213, + 0, + 25219, + 0, + 25223, + 0, + 25225, + 0, + 0, + 0, + 25227, + 0, + 0, + 0, + 25228, + 0, + 0, + 0, + 25229, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 25231, + 25233, + 0, + 0, + 0, + 0, + 25237, + 25239, + 0, + 0, + 0, + 25243, + 0, + 0, + 0, + 25252, + 0, + 25257, + 25258, + 0, + 0, + 0, + 0, + 25260, + 25265, + 0, + 25268, + 0, + 0, + 25273, + 25324, + 0, + 25325, + 0, + 25326, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 25327, + 0, + 0, + 0, + 0, + 0, + 25328, + 0, + 0, + 0, + 0, + 0, + 0, + 25332, + 0, + 0, + 0, + 25333, + 0, + 0, + 0, + 25336, + 25337, + 25338, + 0, + 0, + 25343, + 0, + 25350, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 25352, + 0, + 25354, + 0, + 25375, + 0, + 25379, + 0, + 0, + 0, + 0, + 25384, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 25386, + 0, + 25388, + 0, + 25390, + 0, + 0, + 25399, + 0, + 0, + 25401, + 0, + 0, + 0, + 25402, + 0, + 0, + 0, + 25407, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 25413, + 25415, + 0, + 0, + 25417, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 25419, + 0, + 0, + 0, + 25421, + 0, + 0, + 0, + 25424, + 0, + 0, + 0, + 0, + 25433, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 25435, + 0, + 0, + 0, + 0, + 0, + 0, + 25436, + 0, + 0, + 0, + 25437, + 0, + 0, + 25440, + 0, + 0, + 0, + 0, + 0, + 0, + 25442, + 0, + 0, + 25443, + 0, + 25446, + 0, + 0, + 25449, + 0, + 0, + 0, + 25450, + 0, + 0, + 0, + 0, + 25452, + 0, + 25453, + 25454, + 25455, + 0, + 0, + 0, + 25456, + 0, + 25457, + 0, + 0, + 0, + 25459, + 0, + 25461, + 0, + 25468, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 25469, + 0, + 0, + 0, + 0, + 0, + 25471, + 0, + 0, + 0, + 0, + 0, + 25474, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 25475, + 0, + 0, + 0, + 0, + 25477, + 0, + 0, + 0, + 0, + 25483, + 0, + 0, + 0, + 0, + 0, + 25484, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 25485, + 0, + 25497, + 0, + 0, + 25498, + 0, + 25504, + 0, + 25510, + 0, + 25512, + 0, + 0, + 25513, + 25514, + 0, + 0, + 0, + 0, + 0, + 0, + 25517, + 25518, + 25519, + 0, + 25520, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 25521, + 0, + 25522, + 25527, + 25534, + 0, + 25536, + 0, + 25537, + 0, + 0, + 25548, + 25550, + 0, + 0, + 25551, + 0, + 25552, + 0, + 0, + 0, + 0, + 0, + 25554, + 0, + 25555, + 0, + 25556, + 25557, + 25568, + 0, + 0, + 0, + 25570, + 25571, + 0, + 0, + 0, + 0, + 0, + 0, + 25574, + 0, + 0, + 0, + 0, + 25579, + 0, + 0, + 0, + 25581, + 0, + 0, + 0, + 25582, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 25588, + 0, + 0, + 0, + 0, + 25589, + 0, + 0, + 0, + 0, + 25590, + 0, + 25591, + 25592, + 25593, + 0, + 25594, + 0, + 0, + 0, + 25596, + 0, + 25597, + 25615, + 0, + 0, + 0, + 0, + 0, + 25618, + 0, + 0, + 0, + 0, + 25619, + 25623, + 0, + 0, + 25629, + 0, + 0, + 25631, + 0, + 0, + 0, + 25635, + 25636, + 0, + 0, + 25649, + 0, + 0, + 0, + 0, + 25654, + 0, + 0, + 0, + 25661, + 25663, + 0, + 0, + 25671, + 0, + 0, + 25678, + 25698, + 0, + 25699, + 25702, + 25703, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 25704, + 0, + 0, + 0, + 0, + 0, + 25706, + 0, + 0, + 25710, + 0, + 25711, + 0, + 25712, + 0, + 25715, + 25716, + 25717, + 0, + 0, + 25718, + 25728, + 25732, + 0, + 0, + 0, + 25734, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 25737, + 0, + 0, + 25739, + 0, + 0, + 0, + 25740, + 0, + 25741, + 25745, + 0, + 25746, + 0, + 25748, + 25772, + 25778, + 0, + 0, + 0, + 0, + 0, + 25780, + 0, + 0, + 0, + 0, + 25781, + 0, + 25782, + 25784, + 25785, + 0, + 0, + 0, + 25789, + 0, + 0, + 0, + 0, + 0, + 0, + 25797, + 25801, + 0, + 0, + 0, + 25808, + 25809, + 0, + 0, + 25811, + 25814, + 25815, + 0, + 0, + 25817, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 25820, + 0, + 0, + 0, + 0, + 25832, + 25833, + 0, + 0, + 0, + 25846, + 0, + 0, + 0, + 25847, + 25848, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 25849, + 25850, + 0, + 0, + 25851, + 0, + 0, + 25852, + 0, + 25862, + 0, + 0, + 0, + 25863, + 25865, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 25867, + 25868, + 0, + 25869, + 25874, + 0, + 25875, + 0, + 25876, + 25877, + 0, + 0, + 0, + 0, + 25878, + 25902, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 25903, + 25904, + 25905, + 0, + 0, + 0, + 25908, + 25909, + 0, + 0, + 0, + 0, + 25910, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 25912, + 0, + 25913, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 25914, + 0, + 0, + 25916, + 0, + 0, + 0, + 0, + 0, + 25917, + 25927, + 0, + 0, + 0, + 0, + 25928, + 0, + 0, + 25930, + 0, + 0, + 0, + 25933, + 0, + 0, + 25938, + 25942, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 25945, + 0, + 25950, + 0, + 25956, + 0, + 0, + 25961, + 25962, + 0, + 0, + 25963, + 0, + 25964, + 25965, + 25966, + 0, + 0, + 0, + 0, + 0, + 25967, + 0, + 0, + 0, + 0, + 25968, + 0, + 0, + 0, + 25969, + 25971, + 0, + 0, + 0, + 0, + 0, + 25973, + 25975, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 25978, + 0, + 25981, + 0, + 0, + 0, + 25982, + 0, + 0, + 0, + 25984, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 25993, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 26002, + 0, + 0, + 0, + 26005, + 0, + 0, + 0, + 26006, + 26007, + 0, + 0, + 26014, + 26015, + 26016, + 0, + 0, + 0, + 0, + 0, + 0, + 26017, + 26018, + 26020, + 0, + 26022, + 26023, + 0, + 0, + 0, + 26024, + 26028, + 0, + 26029, + 26033, + 26034, + 26044, + 0, + 0, + 0, + 0, + 0, + 26046, + 0, + 0, + 26047, + 0, + 0, + 26049, + 0, + 26050, + 0, + 26051, + 0, + 0, + 0, + 0, + 0, + 26053, + 0, + 0, + 0, + 0, + 26054, + 26059, + 0, + 0, + 0, + 0, + 0, + 0, + 26060, + 0, + 26066, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 26067, + 0, + 26069, + 0, + 0, + 26071, + 0, + 0, + 0, + 26073, + 0, + 26074, + 26077, + 0, + 0, + 0, + 0, + 26078, + 0, + 0, + 0, + 26079, + 0, + 26090, + 0, + 0, + 26094, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 26095, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 26096, + 26101, + 0, + 26107, + 26122, + 0, + 26124, + 0, + 0, + 26125, + 0, + 0, + 0, + 0, + 0, + 0, + 26136, + 26141, + 26155, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 26164, + 26166, + 0, + 0, + 0, + 26167, + 0, + 26170, + 26171, + 0, + 0, + 26172, + 0, + 0, + 26174, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 26175, + 0, + 0, + 0, + 26176, + 26177, + 0, + 26321, + 26322, + 0, + 26323, + 0, + 0, + 26324, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 26325, + 0, + 26331, + 0, + 0, + 0, + 0, + 0, + 0, + 26335, + 0, + 0, + 0, + 26350, + 0, + 0, + 0, + 26379, + 0, + 0, + 26382, + 26383, + 26385, + 0, + 0, + 26392, + 26406, + 0, + 0, + 0, + 0, + 26411, + 0, + 0, + 0, + 0, + 0, + 26412, + 0, + 0, + 26420, + 0, + 0, + 26423, + 0, + 26424, + 26426, + 26432, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 26435, + 0, + 26436, + 0, + 0, + 0, + 0, + 0, + 26441, + 0, + 26444, + 0, + 0, + 0, + 26446, + 0, + 0, + 0, + 0, + 26447, + 0, + 0, + 0, + 0, + 26449, + 0, + 26450, + 26452, + 0, + 26453, + 26454, + 0, + 0, + 0, + 26455, + 0, + 0, + 0, + 26456, + 0, + 0, + 26458, + 0, + 0, + 26460, + 0, + 26463, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 26464, + 26470, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 26473, + 0, + 0, + 26474, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 26475, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 26477, + 0, + 26485, + 0, + 0, + 26486, + 0, + 26487, + 0, + 0, + 26488, + 26493, + 26494, + 0, + 0, + 26495, + 0, + 26497, + 26504, + 26506, + 0, + 0, + 0, + 0, + 0, + 26507, + 0, + 0, + 0, + 0, + 0, + 26509, + 0, + 0, + 26510, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 26512, + 0, + 26513, + 26515, + 0, + 0, + 0, + 26518, + 0, + 0, + 0, + 26519, + 0, + 26524, + 26526, + 0, + 0, + 0, + 26527, + 0, + 26532, + 0, + 26533, + 26537, + 26558, + 0, + 0, + 0, + 26559, + 0, + 0, + 0, + 26571, + 0, + 0, + 26573, + 0, + 26588, + 0, + 26593, + 0, + 0, + 0, + 0, + 0, + 0, + 26603, + 0, + 26604, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 26606, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 26607, + 26609, + 26611, + 26614, + 0, + 0, + 0, + 26616, + 26620, + 0, + 26621, + 0, + 0, + 0, + 0, + 0, + 26627, + 0, + 26629, + 0, + 0, + 26630, + 0, + 0, + 26632, + 26643, + 0, + 0, + 0, + 26644, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 26646, + 26647, + 0, + 0, + 0, + 26650, + 0, + 0, + 26656, + 0, + 0, + 0, + 0, + 26663, + 26670, + 26671, + 0, + 0, + 0, + 26685, + 26686, + 26687, + 0, + 26689, + 0, + 0, + 0, + 0, + 26744, + 0, + 26745, + 0, + 26747, + 26748, + 0, + 26749, + 26750, + 26751, + 0, + 0, + 0, + 0, + 26752, + 26755, + 0, + 0, + 0, + 26756, + 26769, + 0, + 0, + 0, + 26774, + 0, + 0, + 0, + 0, + 0, + 26775, + 0, + 26777, + 26778, + 0, + 26786, + 0, + 0, + 0, + 26787, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 26788, + 0, + 0, + 26789, + 0, + 0, + 0, + 0, + 0, + 26791, + 0, + 26792, + 26793, + 0, + 0, + 0, + 26794, + 0, + 26797, + 26798, + 0, + 0, + 0, + 26800, + 0, + 0, + 26803, + 0, + 26804, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 26805, + 0, + 0, + 26808, + 0, + 0, + 26809, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 26812, + 0, + 26825, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 26826, + 0, + 0, + 26827, + 26829, + 26834, + 0, + 0, + 0, + 0, + 26835, + 0, + 0, + 26849, + 0, + 26851, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 26852, + 0, + 26853, + 26857, + 0, + 26858, + 0, + 26859, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 26876, + 0, + 26878, + 26882, + 26883, + 0, + 0, + 0, + 0, + 26890, + 26894, + 0, + 0, + 0, + 0, + 26895, + 26896, + 0, + 0, + 0, + 0, + 0, + 26900, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 26911, + 26913, + 26914, + 26915, + 26916, + 26919, + 0, + 0, + 0, + 26921, + 26922, + 0, + 0, + 26925, + 0, + 0, + 0, + 26928, + 0, + 0, + 26929, + 26930, + 0, + 0, + 0, + 26931, + 0, + 26932, + 0, + 0, + 0, + 0, + 0, + 26933, + 0, + 0, + 0, + 0, + 0, + 0, + 26937, + 0, + 0, + 26943, + 0, + 0, + 26944, + 0, + 0, + 0, + 26946, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 26956, + 0, + 26958, + 0, + 0, + 26963, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 26965, + 0, + 26969, + 26970, + 26972, + 0, + 0, + 0, + 0, + 0, + 26973, + 0, + 26974, + 0, + 26978, + 0, + 26980, + 0, + 0, + 0, + 0, + 0, + 0, + 26982, + 0, + 26986, + 26987, + 0, + 26990, + 0, + 0, + 0, + 0, + 27003, + 27006, + 0, + 0, + 27007, + 27010, + 27012, + 27013, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 27014, + 27015, + 27018, + 0, + 27019, + 0, + 0, + 0, + 0, + 0, + 27025, + 0, + 0, + 0, + 27026, + 0, + 0, + 0, + 0, + 27029, + 27030, + 27031, + 27034, + 0, + 0, + 27036, + 27037, + 0, + 0, + 0, + 27038, + 27042, + 0, + 0, + 0, + 27044, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 27045, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 27046, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 27047, + 27049, + 0, + 27050, + 0, + 0, + 0, + 27051, + 27052, + 0, + 27055, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 27056, + 27058, + 27059, + 0, + 27061, + 0, + 27064, + 0, + 0, + 0, + 0, + 0, + 27069, + 0, + 0, + 27070, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 27072, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 27076, + 0, + 0, + 0, + 0, + 0, + 27078, + 0, + 27079, + 0, + 0, + 0, + 27081, + 0, + 0, + 0, + 0, + 0, + 0, + 27082, + 0, + 27083, + 27086, + 0, + 0, + 0, + 0, + 27087, + 0, + 0, + 0, + 0, + 0, + 27088, + 27090, + 0, + 27094, + 0, + 0, + 27095, + 0, + 27099, + 27102, + 0, + 0, + 0, + 27103, + 0, + 0, + 0, + 0, + 27105, + 0, + 0, + 0, + 27106, + 0, + 0, + 0, + 0, + 0, + 0, + 27107, + 0, + 0, + 0, + 0, + 27108, + 27117, + 0, + 0, + 0, + 0, + 27118, + 0, + 0, + 27124, + 0, + 27126, + 0, + 0, + 27130, + 27131, + 0, + 0, + 0, + 0, + 0, + 0, + 27147, + 0, + 0, + 0, + 0, + 27148, + 27149, + 0, + 0, + 0, + 0, + 27150, + 27151, + 0, + 27152, + 0, + 27159, + 0, + 0, + 0, + 27164, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 27175, + 0, + 27189, + 0, + 0, + 27191, + 0, + 27193, + 0, + 27195, + 0, + 27198, + 0, + 0, + 0, + 0, + 0, + 27200, + 0, + 0, + 0, + 0, + 27202, + 0, + 0, + 0, + 0, + 27203, + 0, + 0, + 27204, + 0, + 0, + 27206, + 0, + 27207, + 0, + 0, + 0, + 0, + 27209, + 0, + 0, + 0, + 27213, + 0, + 0, + 27216, + 27219, + 27220, + 27222, + 27223, + 0, + 27224, + 0, + 27225, + 27226, + 0, + 0, + 27233, + 0, + 0, + 0, + 0, + 27235, + 0, + 27237, + 0, + 27238, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 27239, + 0, + 27242, + 27243, + 0, + 27250, + 0, + 0, + 0, + 27251, + 0, + 27253, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 27254, + 27255, + 27258, + 0, + 0, + 0, + 27259, + 0, + 0, + 0, + 0, + 0, + 0, + 27267, + 0, + 27276, + 27278, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 27296, + 27297, + 27301, + 0, + 0, + 0, + 0, + 0, + 0, + 27302, + 0, + 0, + 0, + 0, + 0, + 0, + 27312, + 27313, + 0, + 0, + 0, + 0, + 0, + 27318, + 0, + 27320, + 0, + 27329, + 0, + 27330, + 27331, + 0, + 27332, + 0, + 0, + 0, + 0, + 27340, + 0, + 0, + 0, + 27348, + 0, + 0, + 0, + 0, + 0, + 0, + 27350, + 0, + 27351, + 0, + 0, + 0, + 0, + 27355, + 0, + 0, + 27358, + 27359, + 27361, + 0, + 0, + 0, + 27365, + 0, + 27367, + 0, + 27376, + 27378, + 0, + 0, + 27379, + 0, + 0, + 0, + 0, + 0, + 0, + 27396, + 0, + 27397, + 27404, + 0, + 0, + 0, + 0, + 0, + 27408, + 0, + 0, + 0, + 0, + 27453, + 0, + 0, + 0, + 27456, + 0, + 0, + 0, + 27458, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 27459, + 0, + 0, + 0, + 27460, + 0, + 0, + 27461, + 0, + 27465, + 27467, + 0, + 0, + 27469, + 0, + 27470, + 0, + 27471, + 0, + 27477, + 27482, + 0, + 0, + 0, + 0, + 0, + 0, + 27484, + 0, + 0, + 0, + 0, + 0, + 0, + 27485, + 0, + 0, + 0, + 0, + 0, + 27493, + 0, + 27494, + 27502, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 27511, + 27532, + 0, + 0, + 0, + 27533, + 27545, + 0, + 0, + 0, + 27546, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 27547, + 0, + 0, + 27549, + 27550, + 0, + 27551, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 27555, + 0, + 0, + 27571, + 0, + 27573, + 27574, + 27575, + 27577, + 0, + 27578, + 0, + 0, + 27579, + 27585, + 0, + 0, + 0, + 0, + 0, + 27586, + 0, + 0, + 27588, + 27589, + 0, + 0, + 0, + 0, + 27596, + 0, + 0, + 27600, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 27608, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 27610, + 0, + 0, + 0, + 27618, + 0, + 0, + 27620, + 0, + 0, + 0, + 27631, + 0, + 0, + 27632, + 27634, + 0, + 27636, + 27638, + 0, + 0, + 0, + 27643, + 0, + 27644, + 27649, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 27651, + 27660, + 0, + 27661, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 27662, + 0, + 0, + 27664, + 0, + 27665, + 0, + 0, + 0, + 27669, + 0, + 27671, + 0, + 0, + 0, + 27673, + 27674, + 0, + 0, + 0, + 27682, + 0, + 0, + 0, + 27711, + 0, + 27712, + 27713, + 27719, + 27720, + 0, + 0, + 27728, + 0, + 27729, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 27731, + 0, + 0, + 27732, + 0, + 27733, + 0, + 27738, + 0, + 0, + 0, + 27742, + 0, + 0, + 0, + 27743, + 27744, + 0, + 0, + 0, + 0, + 0, + 0, + 27745, + 27746, + 0, + 0, + 0, + 27747, + 27748, + 27751, + 27752, + 0, + 0, + 0, + 27768, + 27770, + 0, + 0, + 0, + 27774, + 27775, + 0, + 27776, + 27777, + 0, + 0, + 27781, + 0, + 27784, + 0, + 27786, + 0, + 0, + 27791, + 0, + 27792, + 27793, + 27804, + 0, + 27812, + 27813, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 27814, + 0, + 27825, + 0, + 27827, + 0, + 0, + 0, + 0, + 27828, + 27861, + 27862, + 0, + 0, + 0, + 27864, + 0, + 0, + 0, + 27865, + 27884, + 0, + 27889, + 0, + 0, + 0, + 0, + 0, + 27890, + 0, + 27891, + 0, + 0, + 0, + 27892, + 0, + 0, + 0, + 0, + 0, + 27897, + 27898, + 0, + 0, + 27899, + 0, + 0, + 0, + 27901, + 27905, + 0, + 0, + 27920, + 0, + 0, + 27921, + 0, + 27922, + 0, + 0, + 0, + 27931, + 27934, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 27941, + 0, + 27942, + 0, + 27945, + 0, + 27947, + 27954, + 0, + 0, + 0, + 0, + 27960, + 27963, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 27964, + 27965, + 0, + 0, + 0, + 27967, + 0, + 27969, + 27975, + 0, + 27976, + 27977, + 0, + 27981, + 0, + 27983, + 28051, + 28052, + 0, + 0, + 0, + 0, + 0, + 28056, + 0, + 0, + 0, + 0, + 0, + 0, + 28058, + 28059, + 0, + 0, + 28061, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 28063, + 0, + 0, + 0, + 0, + 0, + 0, + 28066, + 0, + 0, + 0, + 0, + 0, + 0, + 28069, + 28070, + 28072, + 0, + 28073, + 0, + 0, + 28074, + 0, + 0, + 0, + 0, + 28075, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 28078, + 0, + 0, + 0, + 0, + 28085, + 0, + 0, + 0, + 0, + 28086, + 0, + 0, + 0, + 0, + 0, + 0, + 28088, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 28090, + 0, + 28097, + 28114, + 28115, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 28116, + 0, + 0, + 0, + 0, + 0, + 28118, + 0, + 28129, + 0, + 28131, + 0, + 0, + 28135, + 0, + 0, + 0, + 28140, + 28141, + 0, + 0, + 0, + 28146, + 0, + 0, + 0, + 0, + 28152, + 0, + 0, + 0, + 0, + 28155, + 28157, + 28161, + 0, + 0, + 0, + 0, + 28166, + 0, + 28167, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 28172, + 0, + 0, + 0, + 0, + 0, + 0, + 28173, + 0, + 0, + 28175, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 28178, + 28188, + 0, + 28190, + 0, + 0, + 0, + 0, + 0, + 28191, + 0, + 28193, + 28206, + 0, + 0, + 28207, + 28209, + 0, + 28211, + 0, + 28213, + 0, + 0, + 0, + 28215, + 28216, + 28217, + 0, + 28222, + 0, + 28223, + 28225, + 0, + 0, + 0, + 28226, + 0, + 28227, + 28229, + 28232, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 28235, + 0, + 28241, + 0, + 0, + 28242, + 0, + 0, + 0, + 0, + 28243, + 0, + 0, + 0, + 28245, + 0, + 0, + 0, + 28248, + 28250, + 0, + 28251, + 28252, + 0, + 0, + 0, + 0, + 0, + 0, + 28253, + 0, + 0, + 28254, + 28255, + 0, + 0, + 28256, + 0, + 0, + 28258, + 0, + 0, + 0, + 0, + 0, + 28259, + 0, + 0, + 28260, + 0, + 0, + 28261, + 0, + 0, + 0, + 0, + 28262, + 28263, + 0, + 0, + 28264, + 0, + 0, + 0, + 28266, + 0, + 28268, + 28269, + 0, + 28270, + 28272, + 28274, + 0, + 28277, + 28278, + 0, + 0, + 0, + 28279, + 0, + 28280, + 28281, + 28283, + 0, + 28292, + 0, + 28294, + 0, + 28297, + 0, + 0, + 0, + 0, + 28299, + 0, + 0, + 0, + 0, + 0, + 28300, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 28301, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 28302, + 28303, + 0, + 0, + 0, + 0, + 28304, + 0, + 0, + 28305, + 0, + 28312, + 0, + 28313, + 28314, + 0, + 0, + 0, + 0, + 0, + 0, + 28315, + 0, + 0, + 0, + 28320, + 28321, + 0, + 0, + 28328, + 0, + 0, + 0, + 28329, + 28338, + 0, + 28339, + 0, + 0, + 28344, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 28347, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 28348, + 0, + 0, + 0, + 0, + 0, + 28411, + 0, + 28412, + 28413, + 0, + 28416, + 0, + 0, + 0, + 28420, + 0, + 0, + 0, + 0, + 0, + 28421, + 0, + 0, + 0, + 0, + 28423, + 0, + 0, + 0, + 28424, + 0, + 0, + 28428, + 0, + 0, + 0, + 0, + 0, + 28429, + 0, + 0, + 0, + 28431, + 28434, + 0, + 28458, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 28464, + 0, + 0, + 0, + 0, + 28465, + 0, + 28467, + 0, + 0, + 0, + 0, + 0, + 0, + 28471, + 0, + 0, + 0, + 0, + 28474, + 0, + 28480, + 0, + 28481, + 0, + 0, + 28485, + 0, + 0, + 0, + 0, + 28486, + 28488, + 0, + 0, + 28489, + 0, + 0, + 0, + 0, + 28492, + 0, + 0, + 0, + 28495, + 0, + 28497, + 0, + 28499, + 0, + 0, + 0, + 0, + 28500, + 0, + 0, + 28502, + 28503, + 0, + 0, + 0, + 28508, + 0, + 0, + 0, + 28510, + 0, + 0, + 28512, + 28513, + 28514, + 28521, + 0, + 28526, + 0, + 28527, + 28528, + 0, + 0, + 0, + 0, + 28529, + 0, + 0, + 28532, + 0, + 0, + 28537, + 28538, + 0, + 0, + 0, + 28539, + 0, + 28548, + 0, + 28553, + 28554, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 28560, + 28563, + 0, + 0, + 28564, + 0, + 0, + 0, + 0, + 28565, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 28566, + 28568, + 0, + 0, + 0, + 0, + 0, + 0, + 28569, + 0, + 0, + 0, + 28570, + 0, + 28572, + 28573, + 0, + 0, + 0, + 0, + 28575, + 0, + 0, + 0, + 0, + 28576, + 28581, + 28588, + 0, + 0, + 28589, + 0, + 0, + 0, + 28590, + 28595, + 0, + 28598, + 0, + 0, + 28601, + 0, + 0, + 28605, + 0, + 0, + 0, + 0, + 28614, + 28615, + 28619, + 0, + 0, + 0, + 0, + 0, + 0, + 28620, + 0, + 28626, + 0, + 0, + 28628, + 0, + 28631, + 0, + 28632, + 0, + 0, + 0, + 0, + 0, + 0, + 28635, + 0, + 0, + 0, + 28637, + 28638, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 28639, + 0, + 28643, + 0, + 0, + 28652, + 0, + 0, + 0, + 28662, + 0, + 28670, + 28671, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 28672, + 28673, + 28675, + 28676, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 28691, + 0, + 0, + 0, + 28695, + 0, + 0, + 0, + 28696, + 0, + 28697, + 28698, + 0, + 28705, + 0, + 28707, + 28708, + 28710, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 28711, + 28728, + 0, + 0, + 0, + 28736, + 0, + 0, + 0, + 28737, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 28738, + 0, + 28739, + 0, + 28741, + 0, + 0, + 28742, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 28745, + 0, + 0, + 0, + 0, + 0, + 0, + 28749, + 28750, + 28752, + 28754, + 28756, + 0, + 28757, + 0, + 0, + 0, + 0, + 28759, + 28760, + 0, + 0, + 0, + 0, + 0, + 0, + 28762, + 0, + 0, + 0, + 28764, + 0, + 0, + 0, + 0, + 0, + 0, + 28766, + 0, + 28767, + 28768, + 0, + 0, + 0, + 0, + 28769, + 28770, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 28771, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 28772, + 0, + 28773, + 0, + 28782, + 0, + 0, + 0, + 0, + 0, + 0, + 28784, + 0, + 28785, + 0, + 28786, + 0, + 0, + 0, + 28787, + 0, + 0, + 0, + 28797, + 0, + 0, + 0, + 0, + 0, + 0, + 28799, + 0, + 0, + 28801, + 0, + 0, + 0, + 0, + 28802, + 0, + 28805, + 0, + 0, + 28806, + 0, + 0, + 28807, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 28808, + 0, + 0, + 0, + 0, + 0, + 28810, + 28812, + 0, + 0, + 28816, + 28819, + 0, + 0, + 28821, + 0, + 28826, + 0, + 0, + 0, + 28842, + 28852, + 0, + 0, + 28853, + 0, + 28854, + 28855, + 0, + 0, + 0, + 28857, + 0, + 0, + 0, + 28858, + 0, + 28867, + 28868, + 28869, + 0, + 0, + 0, + 28874, + 28880, + 28882, + 28890, + 28892, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 28895, + 0, + 0, + 0, + 28898, + 28899, + 0, + 0, + 0, + 28900, + 0, + 0, + 28904, + 0, + 28906, + 0, + 0, + 0, + 0, + 28907, + 0, + 0, + 0, + 0, + 0, + 0, + 28908, + 0, + 0, + 0, + 28910, + 0, + 28914, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 28915, + 28916, + 28919, + 0, + 0, + 28920, + 0, + 28921, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 28924, + 0, + 0, + 0, + 0, + 28926, + 28929, + 0, + 0, + 0, + 28930, + 0, + 28936, + 0, + 28939, + 0, + 0, + 0, + 0, + 28942, + 0, + 0, + 0, + 0, + 0, + 0, + 28956, + 0, + 0, + 0, + 28966, + 0, + 0, + 0, + 0, + 28967, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 28968, + 0, + 28971, + 0, + 28975, + 28976, + 0, + 28982, + 28983, + 0, + 0, + 28984, + 28989, + 28996, + 28997, + 28998, + 0, + 0, + 0, + 0, + 0, + 0, + 28999, + 0, + 0, + 0, + 0, + 0, + 29000, + 0, + 29001, + 0, + 0, + 0, + 29009, + 0, + 0, + 29011, + 0, + 0, + 29021, + 0, + 0, + 0, + 0, + 29024, + 0, + 29025, + 0, + 0, + 0, + 0, + 0, + 29026, + 0, + 0, + 0, + 29036, + 0, + 0, + 0, + 29037, + 0, + 0, + 0, + 0, + 29038, + 0, + 29045, + 0, + 29047, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 29051, + 0, + 0, + 0, + 29054, + 29056, + 29062, + 0, + 29070, + 29082, + 0, + 0, + 0, + 29083, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 29084, + 0, + 0, + 0, + 0, + 29085, + 29088, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 29090, + 29097, + 0, + 0, + 0, + 29103, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 29105, + 0, + 0, + 0, + 0, + 0, + 29107, + 0, + 29109, + 0, + 0, + 0, + 29115, + 0, + 0, + 29120, + 0, + 0, + 29138, + 29140, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 29152, + 0, + 29160, + 29174, + 0, + 29176, + 0, + 0, + 29180, + 0, + 29181, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 29228, + 0, + 0, + 29229, + 0, + 0, + 29230, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 29234, + 0, + 0, + 0, + 29241, + 0, + 29245, + 0, + 29248, + 0, + 29250, + 29256, + 29280, + 0, + 29282, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 29285, + 0, + 0, + 29286, + 29291, + 29292, + 0, + 0, + 0, + 0, + 29294, + 0, + 29295, + 0, + 0, + 0, + 0, + 0, + 29296, + 29297, + 29298, + 29300, + 0, + 29302, + 0, + 0, + 29304, + 29307, + 0, + 29312, + 0, + 0, + 0, + 29322, + 0, + 0, + 29323, + 0, + 0, + 29324, + 29326, + 29328, + 0, + 29335, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 29338, + 29339, + 0, + 0, + 0, + 0, + 0, + 29341, + 29343, + 0, + 0, + 0, + 0, + 29344, + 0, + 0, + 0, + 0, + 0, + 29345, + 0, + 0, + 0, + 0, + 29346, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 29347, + 29348, + 29349, + 0, + 0, + 29354, + 0, + 0, + 29355, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 29357, + 0, + 0, + 0, + 0, + 29364, + 0, + 29365, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 29366, + 0, + 0, + 29368, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 29378, + 0, + 29381, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 29386, + 0, + 0, + 0, + 0, + 0, + 0, + 29389, + 0, + 0, + 0, + 29390, + 0, + 0, + 29391, + 29397, + 0, + 29398, + 29412, + 29414, + 29418, + 29419, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 29420, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 29423, + 0, + 0, + 0, + 29435, + 0, + 0, + 0, + 29437, + 0, + 0, + 29439, + 0, + 29441, + 0, + 0, + 0, + 0, + 29443, + 0, + 29446, + 29450, + 29452, + 0, + 0, + 0, + 0, + 0, + 29456, + 0, + 0, + 0, + 0, + 0, + 29461, + 0, + 0, + 0, + 29464, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 29468, + 0, + 29473, + 0, + 0, + 0, + 29486, + 0, + 0, + 0, + 29490, + 0, + 0, + 0, + 29491, + 29492, + 0, + 0, + 29497, + 0, + 0, + 0, + 29498, + 0, + 29499, + 0, + 29502, + 29505, + 0, + 29509, + 0, + 0, + 0, + 29510, + 0, + 0, + 0, + 29512, + 0, + 0, + 0, + 29516, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 29518, + 0, + 29519, + 0, + 0, + 0, + 0, + 0, + 29520, + 29521, + 29529, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 29530, + 0, + 0, + 29531, + 29538, + 0, + 29540, + 0, + 0, + 0, + 29542, + 0, + 29543, + 29544, + 29547, + 0, + 0, + 29548, + 0, + 0, + 0, + 29549, + 0, + 0, + 0, + 29550, + 0, + 0, + 29552, + 0, + 0, + 0, + 0, + 29558, + 29561, + 0, + 29562, + 29564, + 0, + 0, + 29565, + 0, + 0, + 29566, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 29578, + 29584, + 29586, + 29591, + 0, + 0, + 0, + 0, + 29593, + 29594, + 0, + 0, + 29597, + 0, + 0, + 29613, + 0, + 29614, + 0, + 29615, + 0, + 0, + 0, + 0, + 29616, + 29617, + 0, + 0, + 29625, + 0, + 0, + 0, + 29632, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 29633, + 0, + 0, + 0, + 0, + 0, + 29634, + 29635, + 29637, + 0, + 29638, + 0, + 29641, + 29643, + 0, + 0, + 0, + 0, + 0, + 0, + 29644, + 0, + 29645, + 0, + 29649, + 0, + 0, + 0, + 29650, + 0, + 29653, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 29656, + 29659, + 0, + 0, + 29660, + 0, + 0, + 0, + 29661, + 0, + 0, + 0, + 0, + 0, + 29664, + 0, + 0, + 0, + 29671, + 29673, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 29675, + 0, + 29677, + 29679, + 0, + 0, + 29684, + 0, + 0, + 0, + 0, + 0, + 29685, + 0, + 0, + 0, + 29687, + 0, + 0, + 0, + 29688, + 0, + 29689, + 29690, + 29700, + 0, + 29701, + 0, + 0, + 0, + 29702, + 0, + 29706, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 29720, + 0, + 29721, + 0, + 29727, + 0, + 29733, + 29734, + 0, + 29750, + 29761, + 0, + 29763, + 0, + 0, + 0, + 0, + 0, + 29764, + 0, + 0, + 29765, + 0, + 0, + 0, + 29771, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 29772, + 0, + 0, + 0, + 29773, + 29774, + 29775, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 29822, + 0, + 0, + 0, + 29824, + 0, + 29825, + 0, + 0, + 0, + 0, + 0, + 29827, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 29829, + 0, + 29832, + 29834, + 0, + 0, + 29835, + 0, + 0, + 29837, + 29838, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 29843, + 0, + 0, + 0, + 0, + 29844, + 29845, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 29849, + 0, + 0, + 29869, + 29872, + 29890, + 29905, + 0, + 0, + 0, + 0, + 0, + 29907, + 29921, + 0, + 29922, + 0, + 0, + 29923, + 29926, + 29944, + 29946, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 29947, + 29948, + 0, + 0, + 0, + 29951, + 0, + 0, + 0, + 0, + 0, + 29953, + 0, + 0, + 29956, + 0, + 29957, + 0, + 0, + 29962, + 0, + 0, + 0, + 0, + 29971, + 0, + 0, + 0, + 29972, + 0, + 0, + 0, + 0, + 0, + 29978, + 0, + 29979, + 29992, + 30007, + 30008, + 30010, + 0, + 0, + 0, + 30013, + 0, + 0, + 0, + 0, + 30014, + 30016, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 30017, + 0, + 0, + 0, + 0, + 0, + 30023, + 30031, + 0, + 0, + 30033, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 30034, + 0, + 30038, + 0, + 30039, + 0, + 30040, + 0, + 0, + 0, + 0, + 0, + 0, + 30067, + 30068, + 0, + 0, + 0, + 30069, + 0, + 30072, + 0, + 0, + 0, + 30073, + 0, + 0, + 0, + 0, + 30075, + 0, + 0, + 0, + 0, + 0, + 0, + 30079, + 0, + 0, + 30080, + 0, + 0, + 0, + 0, + 0, + 30082, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 30084, + 30090, + 0, + 0, + 30091, + 0, + 0, + 0, + 0, + 30098, + 30118, + 0, + 30119, + 0, + 30121, + 30130, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 30131, + 30132, + 30133, + 0, + 0, + 0, + 0, + 0, + 0, + 30135, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 30136, + 0, + 0, + 30137, + 30138, + 0, + 0, + 0, + 30139, + 30146, + 0, + 0, + 0, + 0, + 0, + 30147, + 0, + 0, + 30148, + 30151, + 0, + 0, + 0, + 30168, + 0, + 30172, + 30173, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 30180, + 30181, + 0, + 30192, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 30194, + 30196, + 0, + 0, + 30199, + 0, + 0, + 30202, + 0, + 0, + 0, + 0, + 30203, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 30213, + 0, + 0, + 0, + 30216, + 0, + 0, + 30217, + 0, + 0, + 0, + 30218, + 0, + 0, + 0, + 0, + 30219, + 0, + 30220, + 0, + 30222, + 30227, + 0, + 0, + 0, + 0, + 0, + 30231, + 0, + 0, + 30233, + 30235, + 0, + 0, + 0, + 0, + 30238, + 0, + 30240, + 30243, + 30245, + 0, + 30250, + 30252, + 0, + 0, + 0, + 30269, + 0, + 0, + 30271, + 30272, + 0, + 0, + 0, + 30278, + 30280, + 0, + 0, + 30282, + 0, + 30284, + 0, + 30294, + 0, + 0, + 0, + 0, + 30295, + 30296, + 0, + 0, + 0, + 0, + 0, + 30298, + 30299, + 30302, + 30304, + 30306, + 0, + 0, + 0, + 0, + 0, + 0, + 30316, + 30317, + 0, + 0, + 0, + 30318, + 0, + 0, + 0, + 30319, + 0, + 30320, + 30322, + 30326, + 0, + 0, + 0, + 0, + 0, + 30327, + 0, + 30332, + 30348, + 30349, + 0, + 0, + 30356, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 30357, + 0, + 30358, + 0, + 30359, + 30360, + 0, + 0, + 30365, + 30366, + 30378, + 0, + 0, + 0, + 0, + 30379, + 0, + 0, + 30381, + 0, + 30385, + 0, + 30388, + 30397, + 0, + 0, + 0, + 30401, + 0, + 0, + 0, + 0, + 30403, + 0, + 0, + 0, + 0, + 0, + 30404, + 0, + 0, + 30405, + 0, + 30406, + 30408, + 0, + 30409, + 0, + 30410, + 0, + 0, + 0, + 30417, + 0, + 0, + 30418, + 30419, + 0, + 30420, + 0, + 30424, + 0, + 0, + 0, + 30427, + 30430, + 30432, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 30433, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 30436, + 0, + 30437, + 30438, + 0, + 30441, + 30442, + 0, + 0, + 0, + 30445, + 0, + 0, + 0, + 0, + 30452, + 30456, + 30457, + 0, + 0, + 0, + 30458, + 0, + 30464, + 0, + 0, + 0, + 0, + 0, + 0, + 30467, + 0, + 30469, + 0, + 0, + 0, + 0, + 0, + 30477, + 0, + 0, + 30484, + 0, + 0, + 0, + 0, + 0, + 30485, + 0, + 0, + 0, + 0, + 0, + 30486, + 30487, + 30497, + 30498, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 30505, + 0, + 30508, + 0, + 0, + 0, + 30509, + 30510, + 0, + 30514, + 30516, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 30523, + 0, + 30524, + 0, + 30525, + 0, + 0, + 0, + 0, + 30537, + 0, + 0, + 30538, + 0, + 0, + 0, + 0, + 0, + 30553, + 0, + 0, + 30555, + 30556, + 30558, + 30559, + 30560, + 0, + 0, + 30561, + 0, + 30562, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 30563, + 30570, + 30571, + 0, + 30586, + 30587, + 0, + 0, + 30590, + 0, + 0, + 30594, + 0, + 0, + 0, + 0, + 30611, + 30612, + 30623, + 30634, + 0, + 0, + 30636, + 30640, + 30655, + 30656, + 0, + 30657, + 0, + 0, + 30658, + 30669, + 0, + 30670, + 0, + 30676, + 30678, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 30679, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 30695, + 0, + 0, + 30698, + 0, + 0, + 0, + 0, + 30700, + 0, + 0, + 0, + 0, + 30701, + 0, + 30702, + 30703, + 0, + 0, + 0, + 0, + 30707, + 0, + 0, + 0, + 30709, + 0, + 0, + 30710, + 30719, + 30729, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 30731, + 0, + 0, + 30733, + 0, + 0, + 0, + 30734, + 0, + 0, + 0, + 0, + 0, + 30736, + 30737, + 0, + 0, + 0, + 30740, + 0, + 0, + 0, + 30743, + 0, + 30746, + 0, + 30747, + 30748, + 0, + 0, + 30751, + 30752, + 30753, + 0, + 0, + 0, + 30754, + 0, + 0, + 30760, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 30763, + 0, + 30764, + 0, + 0, + 30766, + 0, + 30769, + 30770, + 30771, + 30774, + 30777, + 0, + 0, + 30779, + 30780, + 30781, + 0, + 0, + 0, + 0, + 30790, + 0, + 0, + 0, + 30792, + 0, + 0, + 0, + 0, + 30810, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 30812, + 30819, + 0, + 0, + 30823, + 30824, + 0, + 30825, + 0, + 30827, + 0, + 0, + 0, + 0, + 0, + 0, + 30828, + 0, + 0, + 30830, + 0, + 0, + 0, + 30834, + 0, + 30835, + 0, + 30837, + 30838, + 0, + 30845, + 0, + 0, + 0, + 0, + 0, + 30846, + 30847, + 0, + 0, + 30849, + 0, + 30851, + 0, + 0, + 0, + 0, + 0, + 30852, + 30858, + 0, + 0, + 30859, + 0, + 30865, + 0, + 0, + 30866, + 0, + 0, + 30868, + 0, + 0, + 30869, + 0, + 0, + 0, + 30881, + 30883, + 0, + 0, + 0, + 0, + 0, + 30889, + 0, + 30891, + 0, + 0, + 0, + 0, + 30894, + 0, + 30895, + 0, + 30897, + 0, + 30898, + 0, + 0, + 0, + 30904, + 30906, + 0, + 30909, + 0, + 0, + 0, + 0, + 0, + 0, + 30910, + 0, + 0, + 0, + 30915, + 30933, + 30942, + 0, + 0, + 0, + 0, + 30943, + 0, + 0, + 30945, + 0, + 0, + 0, + 0, + 0, + 0, + 30946, + 0, + 0, + 30947, + 0, + 0, + 30955, + 30956, + 0, + 0, + 30960, + 0, + 0, + 30961, + 30962, + 30966, + 0, + 0, + 30969, + 30974, + 0, + 0, + 0, + 30976, + 0, + 0, + 30977, + 0, + 30978, + 30982, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 30994, + 30995, + 30998, + 0, + 31000, + 0, + 0, + 31001, + 0, + 0, + 31003, + 31005, + 0, + 0, + 31006, + 31011, + 0, + 0, + 31014, + 0, + 31016, + 0, + 0, + 0, + 0, + 31018, + 0, + 0, + 31020, + 31023, + 31024, + 31025, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 31027, + 31028, + 31029, + 0, + 0, + 0, + 0, + 0, + 0, + 31032, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 31036, + 31037, + 31038, + 0, + 0, + 0, + 31041, + 31043, + 31045, + 0, + 31047, + 0, + 0, + 0, + 31048, + 0, + 31049, + 0, + 0, + 0, + 31053, + 31054, + 31055, + 0, + 0, + 31063, + 0, + 0, + 0, + 0, + 0, + 31066, + 0, + 31068, + 31071, + 0, + 0, + 0, + 31072, + 31073, + 0, + 0, + 0, + 0, + 31075, + 0, + 0, + 31076, + 0, + 0, + 0, + 31077, + 31079, + 0, + 31080, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 31087, + 0, + 31142, + 0, + 31144, + 0, + 0, + 31145, + 31146, + 31147, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 31149, + 0, + 31151, + 31152, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 31162, + 31171, + 31174, + 31175, + 0, + 0, + 0, + 31176, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 31179, + 0, + 0, + 0, + 31186, + 0, + 0, + 0, + 31192, + 31195, + 0, + 0, + 31196, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 31198, + 0, + 0, + 0, + 0, + 0, + 31199, + 0, + 0, + 0, + 31205, + 0, + 0, + 0, + 0, + 31211, + 31215, + 0, + 0, + 0, + 0, + 31231, + 0, + 31232, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 31233, + 31236, + 31253, + 0, + 31254, + 0, + 0, + 0, + 0, + 0, + 0, + 31255, + 0, + 0, + 31257, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 31258, + 31259, + 0, + 0, + 31260, + 0, + 31261, + 0, + 0, + 0, + 0, + 0, + 31262, + 31263, + 0, + 0, + 31264, + 0, + 31266, + 0, + 31267, + 0, + 0, + 0, + 0, + 0, + 31281, + 0, + 31282, + 0, + 31284, + 0, + 0, + 31285, + 31287, + 31288, + 0, + 0, + 31290, + 0, + 0, + 0, + 31292, + 31295, + 0, + 31299, + 0, + 31300, + 0, + 0, + 0, + 0, + 0, + 31302, + 0, + 0, + 0, + 0, + 31303, + 0, + 0, + 0, + 0, + 0, + 0, + 31304, + 0, + 0, + 0, + 0, + 0, + 31305, + 31308, + 31309, + 31315, + 0, + 31317, + 0, + 0, + 0, + 0, + 0, + 31323, + 0, + 31324, + 0, + 0, + 0, + 0, + 0, + 31325, + 31327, + 0, + 0, + 31331, + 0, + 0, + 0, + 0, + 0, + 31333, + 0, + 0, + 0, + 0, + 0, + 31336, + 0, + 0, + 31337, + 0, + 0, + 0, + 0, + 0, + 0, + 31338, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 31339, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 31342, + 0, + 0, + 0, + 0, + 31345, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 31347, + 0, + 0, + 0, + 0, + 0, + 0, + 31348, + 0, + 0, + 31350, + 31351, + 0, + 31352, + 0, + 0, + 31354, + 0, + 0, + 0, + 0, + 31355, + 0, + 0, + 31356, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 31363, + 0, + 31372, + 0, + 0, + 31373, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 31376, + 0, + 31388, + 0, + 31389, + 0, + 31392, + 0, + 31401, + 0, + 31405, + 31407, + 31408, + 0, + 31409, + 0, + 0, + 0, + 0, + 0, + 0, + 31413, + 31415, + 0, + 0, + 0, + 31416, + 31418, + 0, + 0, + 0, + 0, + 0, + 0, + 31422, + 31423, + 0, + 0, + 31424, + 0, + 31425, + 31432, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 31433, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 31434, + 0, + 0, + 0, + 0, + 0, + 0, + 31435, + 0, + 0, + 0, + 0, + 31438, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 31442, + 0, + 31444, + 0, + 31448, + 0, + 0, + 31451, + 0, + 0, + 0, + 0, + 31452, + 0, + 31461, + 31465, + 0, + 0, + 31466, + 0, + 0, + 31467, + 0, + 0, + 31468, + 0, + 0, + 0, + 31469, + 31473, + 0, + 31476, + 0, + 0, + 0, + 0, + 31489, + 31490, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 31492, + 31493, + 31494, + 0, + 0, + 0, + 0, + 31501, + 31504, + 31505, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 31509, + 0, + 0, + 0, + 0, + 31510, + 0, + 0, + 31511, + 0, + 0, + 31513, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 31514, + 0, + 31522, + 31536, + 31539, + 31540, + 0, + 31541, + 0, + 0, + 0, + 0, + 0, + 0, + 31546, + 31553, + 31559, + 0, + 0, + 0, + 31560, + 31561, + 31562, + 0, + 0, + 31564, + 31567, + 0, + 31569, + 0, + 0, + 0, + 31570, + 0, + 0, + 0, + 0, + 31571, + 0, + 0, + 0, + 0, + 0, + 0, + 31572, + 31574, + 31580, + 31581, + 0, + 0, + 31582, + 31584, + 31585, + 31586, + 31595, + 0, + 31596, + 0, + 0, + 0, + 0, + 31597, + 0, + 31599, + 0, + 31600, + 31601, + 0, + 0, + 31603, + 31604, + 0, + 0, + 31608, + 31610, + 0, + 0, + 0, + 31611, + 0, + 31615, + 0, + 0, + 0, + 0, + 31616, + 0, + 0, + 0, + 0, + 0, + 0, + 31617, + 0, + 0, + 0, + 0, + 0, + 31618, + 0, + 0, + 0, + 0, + 0, + 0, + 31621, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 31622, + 31625, + 0, + 0, + 0, + 0, + 31627, + 0, + 31641, + 0, + 0, + 31642, + 0, + 0, + 31643, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 31644, + 0, + 31646, + 0, + 0, + 0, + 0, + 31648, + 0, + 0, + 0, + 31652, + 0, + 0, + 0, + 31657, + 0, + 0, + 31676, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 31689, + 31691, + 31692, + 0, + 31694, + 0, + 0, + 0, + 31696, + 0, + 31702, + 0, + 31703, + 0, +} + +var kStaticDictionaryWords = [31705]dictWord{ + dictWord{0, 0, 0}, + dictWord{8, 0, 1002}, + dictWord{136, 0, 1015}, + dictWord{4, 0, 683}, + dictWord{4, 10, 325}, + dictWord{138, 10, 125}, + dictWord{7, 11, 572}, + dictWord{ + 9, + 11, + 592, + }, + dictWord{11, 11, 680}, + dictWord{11, 11, 842}, + dictWord{11, 11, 924}, + dictWord{12, 11, 356}, + dictWord{12, 11, 550}, + dictWord{13, 11, 317}, + dictWord{13, 11, 370}, + dictWord{13, 11, 469}, + dictWord{13, 11, 471}, + dictWord{14, 11, 397}, + dictWord{18, 11, 69}, + dictWord{146, 11, 145}, + dictWord{ + 134, + 0, + 1265, + }, + dictWord{136, 11, 534}, + dictWord{134, 0, 1431}, + dictWord{11, 0, 138}, + dictWord{140, 0, 40}, + dictWord{4, 0, 155}, + dictWord{7, 0, 1689}, + dictWord{ + 4, + 10, + 718, + }, + dictWord{135, 10, 1216}, + dictWord{4, 0, 245}, + dictWord{5, 0, 151}, + dictWord{5, 0, 741}, + dictWord{6, 0, 1147}, + dictWord{7, 0, 498}, + dictWord{7, 0, 870}, + dictWord{7, 0, 1542}, + dictWord{12, 0, 213}, + dictWord{14, 0, 36}, + dictWord{14, 0, 391}, + dictWord{17, 0, 111}, + dictWord{18, 0, 6}, + dictWord{18, 0, 46}, + dictWord{ + 18, + 0, + 151, + }, + dictWord{19, 0, 36}, + dictWord{20, 0, 32}, + dictWord{20, 0, 56}, + dictWord{20, 0, 69}, + dictWord{20, 0, 102}, + dictWord{21, 0, 4}, + dictWord{22, 0, 8}, + dictWord{ + 22, + 0, + 10, + }, + dictWord{22, 0, 14}, + dictWord{150, 0, 31}, + dictWord{4, 0, 624}, + dictWord{135, 0, 1752}, + dictWord{5, 10, 124}, + dictWord{5, 10, 144}, + dictWord{6, 10, 548}, + dictWord{7, 10, 15}, + dictWord{7, 10, 153}, + dictWord{137, 10, 629}, + dictWord{6, 0, 503}, + dictWord{9, 0, 586}, + dictWord{13, 0, 468}, + dictWord{14, 0, 66}, + dictWord{ + 16, + 0, + 58, + }, + dictWord{7, 10, 1531}, + dictWord{8, 10, 416}, + dictWord{9, 10, 275}, + dictWord{10, 10, 100}, + dictWord{11, 10, 658}, + dictWord{11, 10, 979}, + dictWord{ + 12, + 10, + 86, + }, + dictWord{14, 10, 207}, + dictWord{15, 10, 20}, + dictWord{143, 10, 25}, + dictWord{5, 0, 603}, + dictWord{7, 0, 1212}, + dictWord{9, 0, 565}, + dictWord{ + 14, + 0, + 301, + }, + dictWord{5, 10, 915}, + dictWord{6, 10, 1783}, + dictWord{7, 10, 211}, + dictWord{7, 10, 1353}, + dictWord{9, 10, 83}, + dictWord{10, 10, 376}, + dictWord{ + 10, + 10, + 431, + }, + dictWord{11, 10, 543}, + dictWord{12, 10, 664}, + dictWord{13, 10, 280}, + dictWord{13, 10, 428}, + dictWord{14, 10, 128}, + dictWord{17, 10, 52}, + dictWord{ + 145, + 10, + 81, + }, + dictWord{4, 0, 492}, + dictWord{133, 0, 451}, + dictWord{135, 0, 835}, + dictWord{141, 0, 70}, + dictWord{132, 0, 539}, + dictWord{7, 11, 748}, + dictWord{ + 139, + 11, + 700, + }, + dictWord{7, 11, 1517}, + dictWord{11, 11, 597}, + dictWord{14, 11, 76}, + dictWord{14, 11, 335}, + dictWord{148, 11, 33}, + dictWord{6, 0, 113}, + dictWord{135, 0, 436}, + dictWord{4, 10, 338}, + dictWord{133, 10, 400}, + dictWord{136, 0, 718}, + dictWord{133, 11, 127}, + dictWord{133, 11, 418}, + dictWord{ + 6, + 0, + 1505, + }, + dictWord{7, 0, 520}, + dictWord{6, 11, 198}, + dictWord{11, 10, 892}, + dictWord{140, 11, 83}, + dictWord{4, 10, 221}, + dictWord{5, 10, 659}, + dictWord{ + 5, + 10, + 989, + }, + dictWord{7, 10, 697}, + dictWord{7, 10, 1211}, + dictWord{138, 10, 284}, + dictWord{135, 0, 1070}, + dictWord{5, 11, 276}, + dictWord{6, 11, 55}, + dictWord{ + 135, + 11, + 1369, + }, + dictWord{134, 0, 1515}, + dictWord{6, 11, 1752}, + dictWord{136, 11, 726}, + dictWord{138, 10, 507}, + dictWord{15, 0, 78}, + dictWord{4, 10, 188}, + dictWord{135, 10, 805}, + dictWord{5, 10, 884}, + dictWord{139, 10, 991}, + dictWord{133, 11, 764}, + dictWord{134, 10, 1653}, + dictWord{6, 11, 309}, + dictWord{ + 7, + 11, + 331, + }, + dictWord{138, 11, 550}, + dictWord{135, 11, 1861}, + dictWord{132, 11, 348}, + dictWord{135, 11, 986}, + dictWord{135, 11, 1573}, + dictWord{ + 12, + 0, + 610, + }, + dictWord{13, 0, 431}, + dictWord{144, 0, 59}, + dictWord{9, 11, 799}, + dictWord{140, 10, 166}, + dictWord{134, 0, 1530}, + dictWord{132, 0, 750}, + dictWord{132, 0, 307}, + dictWord{133, 0, 964}, + dictWord{6, 11, 194}, + dictWord{7, 11, 133}, + dictWord{10, 11, 493}, + dictWord{10, 11, 570}, + dictWord{139, 11, 664}, + dictWord{5, 11, 24}, + dictWord{5, 11, 569}, + dictWord{6, 11, 3}, + dictWord{6, 11, 119}, + dictWord{6, 11, 143}, + dictWord{6, 11, 440}, + dictWord{7, 11, 295}, + dictWord{ + 7, + 11, + 599, + }, + dictWord{7, 11, 1686}, + dictWord{7, 11, 1854}, + dictWord{8, 11, 424}, + dictWord{9, 11, 43}, + dictWord{9, 11, 584}, + dictWord{9, 11, 760}, + dictWord{ + 10, + 11, + 148, + }, + dictWord{10, 11, 328}, + dictWord{11, 11, 159}, + dictWord{11, 11, 253}, + dictWord{11, 11, 506}, + dictWord{12, 11, 487}, + dictWord{12, 11, 531}, + dictWord{144, 11, 33}, + dictWord{136, 10, 760}, + dictWord{5, 11, 14}, + dictWord{5, 11, 892}, + dictWord{6, 11, 283}, + dictWord{7, 11, 234}, + dictWord{136, 11, 537}, + dictWord{135, 11, 1251}, + dictWord{4, 11, 126}, + dictWord{8, 11, 635}, + dictWord{147, 11, 34}, + dictWord{4, 11, 316}, + dictWord{135, 11, 1561}, + dictWord{ + 6, + 0, + 999, + }, + dictWord{6, 0, 1310}, + dictWord{137, 11, 861}, + dictWord{4, 11, 64}, + dictWord{5, 11, 352}, + dictWord{5, 11, 720}, + dictWord{6, 11, 368}, + dictWord{ + 139, + 11, + 359, + }, + dictWord{4, 0, 75}, + dictWord{5, 0, 180}, + dictWord{6, 0, 500}, + dictWord{7, 0, 58}, + dictWord{7, 0, 710}, + dictWord{10, 0, 645}, + dictWord{136, 10, 770}, + dictWord{133, 0, 649}, + dictWord{6, 0, 276}, + dictWord{7, 0, 282}, + dictWord{7, 0, 879}, + dictWord{7, 0, 924}, + dictWord{8, 0, 459}, + dictWord{9, 0, 599}, + dictWord{9, 0, 754}, + dictWord{11, 0, 574}, + dictWord{12, 0, 128}, + dictWord{12, 0, 494}, + dictWord{13, 0, 52}, + dictWord{13, 0, 301}, + dictWord{15, 0, 30}, + dictWord{143, 0, 132}, + dictWord{132, 0, 200}, + dictWord{4, 10, 89}, + dictWord{5, 10, 489}, + dictWord{6, 10, 315}, + dictWord{7, 10, 553}, + dictWord{7, 10, 1745}, + dictWord{138, 10, 243}, + dictWord{135, 11, 1050}, + dictWord{7, 0, 1621}, + dictWord{6, 10, 1658}, + dictWord{9, 10, 3}, + dictWord{10, 10, 154}, + dictWord{11, 10, 641}, + dictWord{13, 10, 85}, + dictWord{13, 10, 201}, + dictWord{141, 10, 346}, + dictWord{6, 11, 175}, + dictWord{137, 11, 289}, + dictWord{5, 11, 432}, + dictWord{133, 11, 913}, + dictWord{ + 6, + 0, + 225, + }, + dictWord{137, 0, 211}, + dictWord{7, 0, 718}, + dictWord{8, 0, 687}, + dictWord{139, 0, 374}, + dictWord{4, 10, 166}, + dictWord{133, 10, 505}, + dictWord{ + 9, + 0, + 110, + }, + dictWord{134, 10, 1670}, + dictWord{8, 0, 58}, + dictWord{9, 0, 724}, + dictWord{11, 0, 809}, + dictWord{13, 0, 113}, + dictWord{145, 0, 72}, + dictWord{6, 0, 345}, + dictWord{7, 0, 1247}, + dictWord{144, 11, 82}, + dictWord{5, 11, 931}, + dictWord{134, 11, 1698}, + dictWord{8, 0, 767}, + dictWord{8, 0, 803}, + dictWord{9, 0, 301}, + dictWord{137, 0, 903}, + dictWord{139, 0, 203}, + dictWord{134, 0, 1154}, + dictWord{7, 0, 1949}, + dictWord{136, 0, 674}, + dictWord{134, 0, 259}, + dictWord{ + 135, + 0, + 1275, + }, + dictWord{5, 11, 774}, + dictWord{6, 11, 1637}, + dictWord{6, 11, 1686}, + dictWord{134, 11, 1751}, + dictWord{134, 0, 1231}, + dictWord{7, 10, 445}, + dictWord{8, 10, 307}, + dictWord{8, 10, 704}, + dictWord{10, 10, 41}, + dictWord{10, 10, 439}, + dictWord{11, 10, 237}, + dictWord{11, 10, 622}, + dictWord{140, 10, 201}, + dictWord{136, 0, 254}, + dictWord{6, 11, 260}, + dictWord{135, 11, 1484}, + dictWord{139, 0, 277}, + dictWord{135, 10, 1977}, + dictWord{4, 10, 189}, + dictWord{ + 5, + 10, + 713, + }, + dictWord{6, 11, 573}, + dictWord{136, 10, 57}, + dictWord{138, 10, 371}, + dictWord{132, 10, 552}, + dictWord{134, 11, 344}, + dictWord{133, 0, 248}, + dictWord{9, 0, 800}, + dictWord{10, 0, 693}, + dictWord{11, 0, 482}, + dictWord{11, 0, 734}, + dictWord{11, 0, 789}, + dictWord{134, 11, 240}, + dictWord{4, 0, 116}, + dictWord{ + 5, + 0, + 95, + }, + dictWord{5, 0, 445}, + dictWord{7, 0, 1688}, + dictWord{8, 0, 29}, + dictWord{9, 0, 272}, + dictWord{11, 0, 509}, + dictWord{11, 0, 915}, + dictWord{4, 11, 292}, + dictWord{4, 11, 736}, + dictWord{5, 11, 871}, + dictWord{6, 11, 171}, + dictWord{6, 11, 1689}, + dictWord{7, 11, 1324}, + dictWord{7, 11, 1944}, + dictWord{9, 11, 415}, + dictWord{9, 11, 580}, + dictWord{14, 11, 230}, + dictWord{146, 11, 68}, + dictWord{7, 0, 490}, + dictWord{13, 0, 100}, + dictWord{143, 0, 75}, + dictWord{135, 0, 1641}, + dictWord{133, 0, 543}, + dictWord{7, 11, 209}, + dictWord{8, 11, 661}, + dictWord{10, 11, 42}, + dictWord{11, 11, 58}, + dictWord{12, 11, 58}, + dictWord{12, 11, 118}, + dictWord{141, 11, 32}, + dictWord{5, 0, 181}, + dictWord{8, 0, 41}, + dictWord{6, 11, 63}, + dictWord{135, 11, 920}, + dictWord{133, 0, 657}, + dictWord{133, 11, 793}, + dictWord{138, 0, 709}, + dictWord{7, 0, 25}, + dictWord{8, 0, 202}, + dictWord{138, 0, 536}, + dictWord{5, 11, 665}, + dictWord{135, 10, 1788}, + dictWord{145, 10, 49}, + dictWord{9, 0, 423}, + dictWord{140, 0, 89}, + dictWord{5, 11, 67}, + dictWord{6, 11, 62}, + dictWord{6, 11, 374}, + dictWord{135, 11, 1391}, + dictWord{8, 0, 113}, + dictWord{ + 9, + 0, + 877, + }, + dictWord{10, 0, 554}, + dictWord{11, 0, 83}, + dictWord{12, 0, 136}, + dictWord{19, 0, 109}, + dictWord{9, 11, 790}, + dictWord{140, 11, 47}, + dictWord{ + 138, + 10, + 661, + }, + dictWord{4, 0, 963}, + dictWord{10, 0, 927}, + dictWord{14, 0, 442}, + dictWord{135, 10, 1945}, + dictWord{133, 0, 976}, + dictWord{132, 0, 206}, + dictWord{ + 4, + 11, + 391, + }, + dictWord{135, 11, 1169}, + dictWord{134, 0, 2002}, + dictWord{6, 0, 696}, + dictWord{134, 0, 1008}, + dictWord{134, 0, 1170}, + dictWord{132, 11, 271}, + dictWord{7, 0, 13}, + dictWord{8, 0, 226}, + dictWord{10, 0, 537}, + dictWord{11, 0, 570}, + dictWord{11, 0, 605}, + dictWord{11, 0, 799}, + dictWord{11, 0, 804}, + dictWord{ + 12, + 0, + 85, + }, + dictWord{12, 0, 516}, + dictWord{12, 0, 623}, + dictWord{13, 0, 112}, + dictWord{13, 0, 361}, + dictWord{14, 0, 77}, + dictWord{14, 0, 78}, + dictWord{17, 0, 28}, + dictWord{19, 0, 110}, + dictWord{140, 11, 314}, + dictWord{132, 0, 769}, + dictWord{134, 0, 1544}, + dictWord{4, 0, 551}, + dictWord{137, 0, 678}, + dictWord{5, 10, 84}, + dictWord{134, 10, 163}, + dictWord{9, 0, 57}, + dictWord{9, 0, 459}, + dictWord{10, 0, 425}, + dictWord{11, 0, 119}, + dictWord{12, 0, 184}, + dictWord{12, 0, 371}, + dictWord{ + 13, + 0, + 358, + }, + dictWord{145, 0, 51}, + dictWord{5, 0, 188}, + dictWord{5, 0, 814}, + dictWord{8, 0, 10}, + dictWord{9, 0, 421}, + dictWord{9, 0, 729}, + dictWord{10, 0, 609}, + dictWord{11, 0, 689}, + dictWord{4, 11, 253}, + dictWord{5, 10, 410}, + dictWord{5, 11, 544}, + dictWord{7, 11, 300}, + dictWord{137, 11, 340}, + dictWord{134, 0, 624}, + dictWord{138, 11, 321}, + dictWord{135, 0, 1941}, + dictWord{18, 0, 130}, + dictWord{5, 10, 322}, + dictWord{8, 10, 186}, + dictWord{9, 10, 262}, + dictWord{10, 10, 187}, + dictWord{142, 10, 208}, + dictWord{5, 11, 53}, + dictWord{5, 11, 541}, + dictWord{6, 11, 94}, + dictWord{6, 11, 499}, + dictWord{7, 11, 230}, + dictWord{139, 11, 321}, + dictWord{133, 10, 227}, + dictWord{4, 0, 378}, + dictWord{4, 11, 920}, + dictWord{5, 11, 25}, + dictWord{5, 11, 790}, + dictWord{6, 11, 457}, + dictWord{135, 11, 853}, + dictWord{137, 0, 269}, + dictWord{132, 0, 528}, + dictWord{134, 0, 1146}, + dictWord{7, 10, 1395}, + dictWord{8, 10, 486}, + dictWord{9, 10, 236}, + dictWord{9, 10, 878}, + dictWord{10, 10, 218}, + dictWord{11, 10, 95}, + dictWord{19, 10, 17}, + dictWord{147, 10, 31}, + dictWord{7, 10, 2043}, + dictWord{8, 10, 672}, + dictWord{ + 141, + 10, + 448, + }, + dictWord{134, 0, 1105}, + dictWord{134, 0, 1616}, + dictWord{134, 11, 1765}, + dictWord{140, 11, 163}, + dictWord{5, 10, 412}, + dictWord{133, 11, 822}, + dictWord{132, 11, 634}, + dictWord{6, 0, 656}, + dictWord{134, 11, 1730}, + dictWord{134, 0, 1940}, + dictWord{5, 0, 104}, + dictWord{6, 0, 173}, + dictWord{ + 135, + 0, + 1631, + }, + dictWord{136, 10, 562}, + dictWord{6, 11, 36}, + dictWord{7, 11, 658}, + dictWord{8, 11, 454}, + dictWord{147, 11, 86}, + dictWord{5, 0, 457}, + dictWord{ + 134, + 10, + 1771, + }, + dictWord{7, 0, 810}, + dictWord{8, 0, 138}, + dictWord{8, 0, 342}, + dictWord{9, 0, 84}, + dictWord{10, 0, 193}, + dictWord{11, 0, 883}, + dictWord{140, 0, 359}, + dictWord{9, 0, 620}, + dictWord{135, 10, 1190}, + dictWord{137, 10, 132}, + dictWord{7, 11, 975}, + dictWord{137, 11, 789}, + dictWord{6, 0, 95}, + dictWord{6, 0, 1934}, + dictWord{136, 0, 967}, + dictWord{141, 11, 335}, + dictWord{6, 0, 406}, + dictWord{10, 0, 409}, + dictWord{10, 0, 447}, + dictWord{11, 0, 44}, + dictWord{140, 0, 100}, + dictWord{4, 10, 317}, + dictWord{135, 10, 1279}, + dictWord{132, 0, 477}, + dictWord{134, 0, 1268}, + dictWord{6, 0, 1941}, + dictWord{8, 0, 944}, + dictWord{5, 10, 63}, + dictWord{133, 10, 509}, + dictWord{132, 0, 629}, + dictWord{132, 11, 104}, + dictWord{4, 0, 246}, + dictWord{133, 0, 375}, + dictWord{6, 0, 1636}, + dictWord{ + 132, + 10, + 288, + }, + dictWord{135, 11, 1614}, + dictWord{9, 0, 49}, + dictWord{10, 0, 774}, + dictWord{8, 10, 89}, + dictWord{8, 10, 620}, + dictWord{11, 10, 628}, + dictWord{ + 12, + 10, + 322, + }, + dictWord{143, 10, 124}, + dictWord{4, 0, 282}, + dictWord{7, 0, 1034}, + dictWord{11, 0, 398}, + dictWord{11, 0, 634}, + dictWord{12, 0, 1}, + dictWord{12, 0, 79}, + dictWord{12, 0, 544}, + dictWord{14, 0, 237}, + dictWord{17, 0, 10}, + dictWord{146, 0, 20}, + dictWord{132, 0, 824}, + dictWord{7, 11, 45}, + dictWord{9, 11, 542}, + dictWord{ + 9, + 11, + 566, + }, + dictWord{138, 11, 728}, + dictWord{5, 0, 118}, + dictWord{5, 0, 499}, + dictWord{6, 0, 476}, + dictWord{6, 0, 665}, + dictWord{6, 0, 1176}, + dictWord{ + 6, + 0, + 1196, + }, + dictWord{7, 0, 600}, + dictWord{7, 0, 888}, + dictWord{135, 0, 1096}, + dictWord{7, 0, 296}, + dictWord{7, 0, 596}, + dictWord{8, 0, 560}, + dictWord{8, 0, 586}, + dictWord{9, 0, 612}, + dictWord{11, 0, 304}, + dictWord{12, 0, 46}, + dictWord{13, 0, 89}, + dictWord{14, 0, 112}, + dictWord{145, 0, 122}, + dictWord{5, 0, 894}, + dictWord{ + 6, + 0, + 1772, + }, + dictWord{9, 0, 1009}, + dictWord{138, 10, 120}, + dictWord{5, 11, 533}, + dictWord{7, 11, 755}, + dictWord{138, 11, 780}, + dictWord{151, 10, 1}, + dictWord{ + 6, + 0, + 1474, + }, + dictWord{7, 11, 87}, + dictWord{142, 11, 288}, + dictWord{139, 0, 366}, + dictWord{137, 10, 461}, + dictWord{7, 11, 988}, + dictWord{7, 11, 1939}, + dictWord{ + 9, + 11, + 64, + }, + dictWord{9, 11, 502}, + dictWord{12, 11, 7}, + dictWord{12, 11, 34}, + dictWord{13, 11, 12}, + dictWord{13, 11, 234}, + dictWord{147, 11, 77}, + dictWord{ + 7, + 0, + 1599, + }, + dictWord{7, 0, 1723}, + dictWord{8, 0, 79}, + dictWord{8, 0, 106}, + dictWord{8, 0, 190}, + dictWord{8, 0, 302}, + dictWord{8, 0, 383}, + dictWord{8, 0, 713}, + dictWord{ + 9, + 0, + 119, + }, + dictWord{9, 0, 233}, + dictWord{9, 0, 419}, + dictWord{9, 0, 471}, + dictWord{10, 0, 181}, + dictWord{10, 0, 406}, + dictWord{11, 0, 57}, + dictWord{11, 0, 85}, + dictWord{11, 0, 120}, + dictWord{11, 0, 177}, + dictWord{11, 0, 296}, + dictWord{11, 0, 382}, + dictWord{11, 0, 454}, + dictWord{11, 0, 758}, + dictWord{11, 0, 999}, + dictWord{ + 12, + 0, + 27, + }, + dictWord{12, 0, 98}, + dictWord{12, 0, 131}, + dictWord{12, 0, 245}, + dictWord{12, 0, 312}, + dictWord{12, 0, 446}, + dictWord{12, 0, 454}, + dictWord{13, 0, 25}, + dictWord{13, 0, 98}, + dictWord{13, 0, 426}, + dictWord{13, 0, 508}, + dictWord{14, 0, 70}, + dictWord{14, 0, 163}, + dictWord{14, 0, 272}, + dictWord{14, 0, 277}, + dictWord{ + 14, + 0, + 370, + }, + dictWord{15, 0, 95}, + dictWord{15, 0, 138}, + dictWord{15, 0, 167}, + dictWord{17, 0, 38}, + dictWord{148, 0, 96}, + dictWord{135, 10, 1346}, + dictWord{ + 10, + 0, + 200, + }, + dictWord{19, 0, 2}, + dictWord{151, 0, 22}, + dictWord{135, 11, 141}, + dictWord{134, 10, 85}, + dictWord{134, 0, 1759}, + dictWord{138, 0, 372}, + dictWord{ + 145, + 0, + 16, + }, + dictWord{8, 0, 943}, + dictWord{132, 11, 619}, + dictWord{139, 11, 88}, + dictWord{5, 11, 246}, + dictWord{8, 11, 189}, + dictWord{9, 11, 355}, + dictWord{ + 9, + 11, + 512, + }, + dictWord{10, 11, 124}, + dictWord{10, 11, 453}, + dictWord{11, 11, 143}, + dictWord{11, 11, 416}, + dictWord{11, 11, 859}, + dictWord{141, 11, 341}, + dictWord{ + 5, + 0, + 258, + }, + dictWord{134, 0, 719}, + dictWord{6, 0, 1798}, + dictWord{6, 0, 1839}, + dictWord{8, 0, 900}, + dictWord{10, 0, 874}, + dictWord{10, 0, 886}, + dictWord{ + 12, + 0, + 698, + }, + dictWord{12, 0, 732}, + dictWord{12, 0, 770}, + dictWord{16, 0, 106}, + dictWord{18, 0, 163}, + dictWord{18, 0, 170}, + dictWord{18, 0, 171}, + dictWord{152, 0, 20}, + dictWord{9, 0, 707}, + dictWord{11, 0, 326}, + dictWord{11, 0, 339}, + dictWord{12, 0, 423}, + dictWord{12, 0, 502}, + dictWord{20, 0, 62}, + dictWord{9, 11, 707}, + dictWord{ + 11, + 11, + 326, + }, + dictWord{11, 11, 339}, + dictWord{12, 11, 423}, + dictWord{12, 11, 502}, + dictWord{148, 11, 62}, + dictWord{5, 0, 30}, + dictWord{7, 0, 495}, + dictWord{ + 8, + 0, + 134, + }, + dictWord{9, 0, 788}, + dictWord{140, 0, 438}, + dictWord{133, 11, 678}, + dictWord{5, 10, 279}, + dictWord{6, 10, 235}, + dictWord{7, 10, 468}, + dictWord{ + 8, + 10, + 446, + }, + dictWord{9, 10, 637}, + dictWord{10, 10, 717}, + dictWord{11, 10, 738}, + dictWord{140, 10, 514}, + dictWord{5, 11, 35}, + dictWord{6, 11, 287}, + dictWord{ + 7, + 11, + 862, + }, + dictWord{7, 11, 1886}, + dictWord{138, 11, 179}, + dictWord{7, 0, 1948}, + dictWord{7, 0, 2004}, + dictWord{132, 11, 517}, + dictWord{5, 10, 17}, + dictWord{ + 6, + 10, + 371, + }, + dictWord{137, 10, 528}, + dictWord{4, 0, 115}, + dictWord{5, 0, 669}, + dictWord{6, 0, 407}, + dictWord{8, 0, 311}, + dictWord{11, 0, 10}, + dictWord{141, 0, 5}, + dictWord{137, 0, 381}, + dictWord{5, 0, 50}, + dictWord{6, 0, 439}, + dictWord{7, 0, 780}, + dictWord{135, 0, 1040}, + dictWord{136, 11, 667}, + dictWord{11, 11, 403}, + dictWord{146, 11, 83}, + dictWord{5, 0, 1}, + dictWord{6, 0, 81}, + dictWord{138, 0, 520}, + dictWord{134, 0, 738}, + dictWord{5, 0, 482}, + dictWord{8, 0, 98}, + dictWord{9, 0, 172}, + dictWord{10, 0, 360}, + dictWord{10, 0, 700}, + dictWord{10, 0, 822}, + dictWord{11, 0, 302}, + dictWord{11, 0, 778}, + dictWord{12, 0, 50}, + dictWord{12, 0, 127}, + dictWord{ + 12, + 0, + 396, + }, + dictWord{13, 0, 62}, + dictWord{13, 0, 328}, + dictWord{14, 0, 122}, + dictWord{147, 0, 72}, + dictWord{9, 11, 157}, + dictWord{10, 11, 131}, + dictWord{ + 140, + 11, + 72, + }, + dictWord{135, 11, 714}, + dictWord{135, 11, 539}, + dictWord{5, 0, 2}, + dictWord{6, 0, 512}, + dictWord{7, 0, 797}, + dictWord{7, 0, 1494}, + dictWord{8, 0, 253}, + dictWord{8, 0, 589}, + dictWord{9, 0, 77}, + dictWord{10, 0, 1}, + dictWord{10, 0, 129}, + dictWord{10, 0, 225}, + dictWord{11, 0, 118}, + dictWord{11, 0, 226}, + dictWord{ + 11, + 0, + 251, + }, + dictWord{11, 0, 430}, + dictWord{11, 0, 701}, + dictWord{11, 0, 974}, + dictWord{11, 0, 982}, + dictWord{12, 0, 64}, + dictWord{12, 0, 260}, + dictWord{12, 0, 488}, + dictWord{140, 0, 690}, + dictWord{5, 11, 394}, + dictWord{7, 11, 367}, + dictWord{7, 11, 487}, + dictWord{7, 11, 857}, + dictWord{7, 11, 1713}, + dictWord{8, 11, 246}, + dictWord{9, 11, 537}, + dictWord{10, 11, 165}, + dictWord{12, 11, 219}, + dictWord{140, 11, 561}, + dictWord{136, 0, 557}, + dictWord{5, 10, 779}, + dictWord{5, 10, 807}, + dictWord{6, 10, 1655}, + dictWord{134, 10, 1676}, + dictWord{4, 10, 196}, + dictWord{5, 10, 558}, + dictWord{133, 10, 949}, + dictWord{11, 11, 827}, + dictWord{ + 12, + 11, + 56, + }, + dictWord{14, 11, 34}, + dictWord{143, 11, 148}, + dictWord{137, 0, 347}, + dictWord{133, 0, 572}, + dictWord{134, 0, 832}, + dictWord{4, 0, 12}, + dictWord{ + 7, + 0, + 504, + }, + dictWord{7, 0, 522}, + dictWord{7, 0, 809}, + dictWord{8, 0, 797}, + dictWord{141, 0, 88}, + dictWord{4, 10, 752}, + dictWord{133, 11, 449}, + dictWord{7, 11, 86}, + dictWord{8, 11, 103}, + dictWord{145, 11, 69}, + dictWord{7, 11, 2028}, + dictWord{138, 11, 641}, + dictWord{5, 0, 528}, + dictWord{6, 11, 1}, + dictWord{142, 11, 2}, + dictWord{134, 0, 861}, + dictWord{10, 0, 294}, + dictWord{4, 10, 227}, + dictWord{5, 10, 159}, + dictWord{5, 10, 409}, + dictWord{7, 10, 80}, + dictWord{10, 10, 479}, + dictWord{ + 12, + 10, + 418, + }, + dictWord{14, 10, 50}, + dictWord{14, 10, 249}, + dictWord{142, 10, 295}, + dictWord{7, 10, 1470}, + dictWord{8, 10, 66}, + dictWord{8, 10, 137}, + dictWord{ + 8, + 10, + 761, + }, + dictWord{9, 10, 638}, + dictWord{11, 10, 80}, + dictWord{11, 10, 212}, + dictWord{11, 10, 368}, + dictWord{11, 10, 418}, + dictWord{12, 10, 8}, + dictWord{ + 13, + 10, + 15, + }, + dictWord{16, 10, 61}, + dictWord{17, 10, 59}, + dictWord{19, 10, 28}, + dictWord{148, 10, 84}, + dictWord{20, 0, 109}, + dictWord{135, 11, 1148}, + dictWord{ + 6, + 11, + 277, + }, + dictWord{7, 11, 1274}, + dictWord{7, 11, 1386}, + dictWord{7, 11, 1392}, + dictWord{12, 11, 129}, + dictWord{146, 11, 87}, + dictWord{6, 11, 187}, + dictWord{7, 11, 39}, + dictWord{7, 11, 1203}, + dictWord{8, 11, 380}, + dictWord{8, 11, 542}, + dictWord{14, 11, 117}, + dictWord{149, 11, 28}, + dictWord{134, 0, 1187}, + dictWord{5, 0, 266}, + dictWord{9, 0, 290}, + dictWord{9, 0, 364}, + dictWord{10, 0, 293}, + dictWord{11, 0, 606}, + dictWord{142, 0, 45}, + dictWord{6, 11, 297}, + dictWord{ + 7, + 11, + 793, + }, + dictWord{139, 11, 938}, + dictWord{4, 0, 50}, + dictWord{6, 0, 594}, + dictWord{9, 0, 121}, + dictWord{10, 0, 49}, + dictWord{10, 0, 412}, + dictWord{139, 0, 834}, + dictWord{136, 0, 748}, + dictWord{7, 11, 464}, + dictWord{8, 11, 438}, + dictWord{11, 11, 105}, + dictWord{11, 11, 363}, + dictWord{12, 11, 231}, + dictWord{ + 14, + 11, + 386, + }, + dictWord{15, 11, 102}, + dictWord{148, 11, 75}, + dictWord{132, 0, 466}, + dictWord{13, 0, 399}, + dictWord{14, 0, 337}, + dictWord{6, 10, 38}, + dictWord{ + 7, + 10, + 1220, + }, + dictWord{8, 10, 185}, + dictWord{8, 10, 256}, + dictWord{9, 10, 22}, + dictWord{9, 10, 331}, + dictWord{10, 10, 738}, + dictWord{11, 10, 205}, + dictWord{ + 11, + 10, + 540, + }, + dictWord{11, 10, 746}, + dictWord{13, 10, 465}, + dictWord{142, 10, 194}, + dictWord{9, 0, 378}, + dictWord{141, 0, 162}, + dictWord{137, 0, 519}, + dictWord{ + 4, + 10, + 159, + }, + dictWord{6, 10, 115}, + dictWord{7, 10, 252}, + dictWord{7, 10, 257}, + dictWord{7, 10, 1928}, + dictWord{8, 10, 69}, + dictWord{9, 10, 384}, + dictWord{ + 10, + 10, + 91, + }, + dictWord{10, 10, 615}, + dictWord{12, 10, 375}, + dictWord{14, 10, 235}, + dictWord{18, 10, 117}, + dictWord{147, 10, 123}, + dictWord{5, 11, 604}, + dictWord{ + 5, + 10, + 911, + }, + dictWord{136, 10, 278}, + dictWord{132, 0, 667}, + dictWord{8, 0, 351}, + dictWord{9, 0, 322}, + dictWord{4, 10, 151}, + dictWord{135, 10, 1567}, + dictWord{134, 0, 902}, + dictWord{133, 10, 990}, + dictWord{12, 0, 180}, + dictWord{5, 10, 194}, + dictWord{7, 10, 1662}, + dictWord{137, 10, 90}, + dictWord{4, 0, 869}, + dictWord{134, 0, 1996}, + dictWord{134, 0, 813}, + dictWord{133, 10, 425}, + dictWord{137, 11, 761}, + dictWord{132, 0, 260}, + dictWord{133, 10, 971}, + dictWord{ + 5, + 11, + 20, + }, + dictWord{6, 11, 298}, + dictWord{7, 11, 659}, + dictWord{7, 11, 1366}, + dictWord{137, 11, 219}, + dictWord{4, 0, 39}, + dictWord{5, 0, 36}, + dictWord{ + 7, + 0, + 1843, + }, + dictWord{8, 0, 407}, + dictWord{11, 0, 144}, + dictWord{140, 0, 523}, + dictWord{4, 0, 510}, + dictWord{10, 0, 587}, + dictWord{139, 10, 752}, + dictWord{7, 0, 29}, + dictWord{7, 0, 66}, + dictWord{7, 0, 1980}, + dictWord{10, 0, 487}, + dictWord{138, 0, 809}, + dictWord{13, 0, 260}, + dictWord{14, 0, 82}, + dictWord{18, 0, 63}, + dictWord{ + 137, + 10, + 662, + }, + dictWord{5, 10, 72}, + dictWord{6, 10, 264}, + dictWord{7, 10, 21}, + dictWord{7, 10, 46}, + dictWord{7, 10, 2013}, + dictWord{8, 10, 215}, + dictWord{ + 8, + 10, + 513, + }, + dictWord{10, 10, 266}, + dictWord{139, 10, 22}, + dictWord{134, 0, 570}, + dictWord{6, 0, 565}, + dictWord{7, 0, 1667}, + dictWord{4, 11, 439}, + dictWord{ + 10, + 10, + 95, + }, + dictWord{11, 10, 603}, + dictWord{12, 11, 242}, + dictWord{13, 10, 443}, + dictWord{14, 10, 160}, + dictWord{143, 10, 4}, + dictWord{134, 0, 1464}, + dictWord{ + 134, + 10, + 431, + }, + dictWord{9, 0, 372}, + dictWord{15, 0, 2}, + dictWord{19, 0, 10}, + dictWord{19, 0, 18}, + dictWord{5, 10, 874}, + dictWord{6, 10, 1677}, + dictWord{143, 10, 0}, + dictWord{132, 0, 787}, + dictWord{6, 0, 380}, + dictWord{12, 0, 399}, + dictWord{21, 0, 19}, + dictWord{7, 10, 939}, + dictWord{7, 10, 1172}, + dictWord{7, 10, 1671}, + dictWord{9, 10, 540}, + dictWord{10, 10, 696}, + dictWord{11, 10, 265}, + dictWord{11, 10, 732}, + dictWord{11, 10, 928}, + dictWord{11, 10, 937}, + dictWord{ + 141, + 10, + 438, + }, + dictWord{137, 0, 200}, + dictWord{132, 11, 233}, + dictWord{132, 0, 516}, + dictWord{134, 11, 577}, + dictWord{132, 0, 844}, + dictWord{11, 0, 887}, + dictWord{14, 0, 365}, + dictWord{142, 0, 375}, + dictWord{132, 11, 482}, + dictWord{8, 0, 821}, + dictWord{140, 0, 44}, + dictWord{7, 0, 1655}, + dictWord{136, 0, 305}, + dictWord{5, 10, 682}, + dictWord{135, 10, 1887}, + dictWord{135, 11, 346}, + dictWord{132, 10, 696}, + dictWord{4, 0, 10}, + dictWord{7, 0, 917}, + dictWord{139, 0, 786}, + dictWord{5, 11, 795}, + dictWord{6, 11, 1741}, + dictWord{8, 11, 417}, + dictWord{137, 11, 782}, + dictWord{4, 0, 1016}, + dictWord{134, 0, 2031}, + dictWord{5, 0, 684}, + dictWord{4, 10, 726}, + dictWord{133, 10, 630}, + dictWord{6, 0, 1021}, + dictWord{134, 0, 1480}, + dictWord{8, 10, 802}, + dictWord{136, 10, 838}, + dictWord{ + 134, + 0, + 27, + }, + dictWord{134, 0, 395}, + dictWord{135, 11, 622}, + dictWord{7, 11, 625}, + dictWord{135, 11, 1750}, + dictWord{4, 11, 203}, + dictWord{135, 11, 1936}, + dictWord{6, 10, 118}, + dictWord{7, 10, 215}, + dictWord{7, 10, 1521}, + dictWord{140, 10, 11}, + dictWord{132, 0, 813}, + dictWord{136, 0, 511}, + dictWord{7, 10, 615}, + dictWord{138, 10, 251}, + dictWord{135, 10, 1044}, + dictWord{145, 0, 56}, + dictWord{133, 10, 225}, + dictWord{6, 0, 342}, + dictWord{6, 0, 496}, + dictWord{8, 0, 275}, + dictWord{137, 0, 206}, + dictWord{4, 0, 909}, + dictWord{133, 0, 940}, + dictWord{132, 0, 891}, + dictWord{7, 11, 311}, + dictWord{9, 11, 308}, + dictWord{ + 140, + 11, + 255, + }, + dictWord{4, 10, 370}, + dictWord{5, 10, 756}, + dictWord{135, 10, 1326}, + dictWord{4, 0, 687}, + dictWord{134, 0, 1596}, + dictWord{134, 0, 1342}, + dictWord{ + 6, + 10, + 1662, + }, + dictWord{7, 10, 48}, + dictWord{8, 10, 771}, + dictWord{10, 10, 116}, + dictWord{13, 10, 104}, + dictWord{14, 10, 105}, + dictWord{14, 10, 184}, + dictWord{15, 10, 168}, + dictWord{19, 10, 92}, + dictWord{148, 10, 68}, + dictWord{138, 10, 209}, + dictWord{4, 11, 400}, + dictWord{5, 11, 267}, + dictWord{135, 11, 232}, + dictWord{151, 11, 12}, + dictWord{6, 0, 41}, + dictWord{141, 0, 160}, + dictWord{141, 11, 314}, + dictWord{134, 0, 1718}, + dictWord{136, 0, 778}, + dictWord{ + 142, + 11, + 261, + }, + dictWord{134, 0, 1610}, + dictWord{133, 0, 115}, + dictWord{132, 0, 294}, + dictWord{14, 0, 314}, + dictWord{132, 10, 120}, + dictWord{132, 0, 983}, + dictWord{5, 0, 193}, + dictWord{140, 0, 178}, + dictWord{138, 10, 429}, + dictWord{5, 10, 820}, + dictWord{135, 10, 931}, + dictWord{6, 0, 994}, + dictWord{6, 0, 1051}, + dictWord{6, 0, 1439}, + dictWord{7, 0, 174}, + dictWord{133, 11, 732}, + dictWord{4, 11, 100}, + dictWord{7, 11, 679}, + dictWord{8, 11, 313}, + dictWord{138, 10, 199}, + dictWord{6, 10, 151}, + dictWord{6, 10, 1675}, + dictWord{7, 10, 383}, + dictWord{151, 10, 10}, + dictWord{6, 0, 1796}, + dictWord{8, 0, 848}, + dictWord{8, 0, 867}, + dictWord{ + 8, + 0, + 907, + }, + dictWord{10, 0, 855}, + dictWord{140, 0, 703}, + dictWord{140, 0, 221}, + dictWord{4, 0, 122}, + dictWord{5, 0, 796}, + dictWord{5, 0, 952}, + dictWord{6, 0, 1660}, + dictWord{6, 0, 1671}, + dictWord{8, 0, 567}, + dictWord{9, 0, 687}, + dictWord{9, 0, 742}, + dictWord{10, 0, 686}, + dictWord{11, 0, 682}, + dictWord{11, 0, 909}, + dictWord{ + 140, + 0, + 281, + }, + dictWord{5, 11, 362}, + dictWord{5, 11, 443}, + dictWord{6, 11, 318}, + dictWord{7, 11, 1019}, + dictWord{139, 11, 623}, + dictWord{5, 11, 463}, + dictWord{136, 11, 296}, + dictWord{11, 0, 583}, + dictWord{13, 0, 262}, + dictWord{6, 10, 1624}, + dictWord{12, 10, 422}, + dictWord{142, 10, 360}, + dictWord{5, 0, 179}, + dictWord{7, 0, 1095}, + dictWord{135, 0, 1213}, + dictWord{4, 10, 43}, + dictWord{4, 11, 454}, + dictWord{5, 10, 344}, + dictWord{133, 10, 357}, + dictWord{4, 0, 66}, + dictWord{7, 0, 722}, + dictWord{135, 0, 904}, + dictWord{134, 0, 773}, + dictWord{7, 0, 352}, + dictWord{133, 10, 888}, + dictWord{5, 11, 48}, + dictWord{5, 11, 404}, + dictWord{ + 6, + 11, + 557, + }, + dictWord{7, 11, 458}, + dictWord{8, 11, 597}, + dictWord{10, 11, 455}, + dictWord{10, 11, 606}, + dictWord{11, 11, 49}, + dictWord{11, 11, 548}, + dictWord{ + 12, + 11, + 476, + }, + dictWord{13, 11, 18}, + dictWord{141, 11, 450}, + dictWord{134, 11, 418}, + dictWord{132, 10, 711}, + dictWord{5, 11, 442}, + dictWord{ + 135, + 11, + 1984, + }, + dictWord{141, 0, 35}, + dictWord{137, 0, 152}, + dictWord{134, 0, 1197}, + dictWord{135, 11, 1093}, + dictWord{137, 11, 203}, + dictWord{137, 10, 440}, + dictWord{10, 0, 592}, + dictWord{10, 0, 753}, + dictWord{12, 0, 317}, + dictWord{12, 0, 355}, + dictWord{12, 0, 465}, + dictWord{12, 0, 469}, + dictWord{12, 0, 560}, + dictWord{12, 0, 578}, + dictWord{141, 0, 243}, + dictWord{133, 0, 564}, + dictWord{134, 0, 797}, + dictWord{5, 10, 958}, + dictWord{133, 10, 987}, + dictWord{5, 11, 55}, + dictWord{7, 11, 376}, + dictWord{140, 11, 161}, + dictWord{133, 11, 450}, + dictWord{134, 0, 556}, + dictWord{134, 0, 819}, + dictWord{11, 10, 276}, + dictWord{ + 142, + 10, + 293, + }, + dictWord{7, 0, 544}, + dictWord{138, 0, 61}, + dictWord{8, 0, 719}, + dictWord{4, 10, 65}, + dictWord{5, 10, 479}, + dictWord{5, 10, 1004}, + dictWord{7, 10, 1913}, + dictWord{8, 10, 317}, + dictWord{9, 10, 302}, + dictWord{10, 10, 612}, + dictWord{141, 10, 22}, + dictWord{4, 0, 5}, + dictWord{5, 0, 498}, + dictWord{8, 0, 637}, + dictWord{ + 9, + 0, + 521, + }, + dictWord{4, 11, 213}, + dictWord{4, 10, 261}, + dictWord{7, 11, 223}, + dictWord{7, 10, 510}, + dictWord{136, 11, 80}, + dictWord{5, 0, 927}, + dictWord{7, 0, 101}, + dictWord{4, 10, 291}, + dictWord{7, 11, 381}, + dictWord{7, 11, 806}, + dictWord{7, 11, 820}, + dictWord{8, 11, 354}, + dictWord{8, 11, 437}, + dictWord{8, 11, 787}, + dictWord{9, 10, 515}, + dictWord{9, 11, 657}, + dictWord{10, 11, 58}, + dictWord{10, 11, 339}, + dictWord{10, 11, 749}, + dictWord{11, 11, 914}, + dictWord{12, 10, 152}, + dictWord{12, 11, 162}, + dictWord{12, 10, 443}, + dictWord{13, 11, 75}, + dictWord{13, 10, 392}, + dictWord{14, 11, 106}, + dictWord{14, 11, 198}, + dictWord{ + 14, + 11, + 320, + }, + dictWord{14, 10, 357}, + dictWord{14, 11, 413}, + dictWord{146, 11, 43}, + dictWord{6, 0, 1153}, + dictWord{7, 0, 1441}, + dictWord{136, 11, 747}, + dictWord{ + 4, + 0, + 893, + }, + dictWord{5, 0, 780}, + dictWord{133, 0, 893}, + dictWord{138, 11, 654}, + dictWord{133, 11, 692}, + dictWord{133, 0, 238}, + dictWord{134, 11, 191}, + dictWord{4, 10, 130}, + dictWord{135, 10, 843}, + dictWord{6, 0, 1296}, + dictWord{5, 10, 42}, + dictWord{5, 10, 879}, + dictWord{7, 10, 245}, + dictWord{7, 10, 324}, + dictWord{ + 7, + 10, + 1532, + }, + dictWord{11, 10, 463}, + dictWord{11, 10, 472}, + dictWord{13, 10, 363}, + dictWord{144, 10, 52}, + dictWord{134, 0, 1729}, + dictWord{6, 0, 1999}, + dictWord{136, 0, 969}, + dictWord{4, 10, 134}, + dictWord{133, 10, 372}, + dictWord{4, 0, 60}, + dictWord{7, 0, 941}, + dictWord{7, 0, 1800}, + dictWord{8, 0, 314}, + dictWord{ + 9, + 0, + 700, + }, + dictWord{139, 0, 487}, + dictWord{134, 0, 1144}, + dictWord{6, 11, 162}, + dictWord{7, 11, 1960}, + dictWord{136, 11, 831}, + dictWord{132, 11, 706}, + dictWord{135, 0, 1147}, + dictWord{138, 11, 426}, + dictWord{138, 11, 89}, + dictWord{7, 0, 1853}, + dictWord{138, 0, 437}, + dictWord{136, 0, 419}, + dictWord{ + 135, + 10, + 1634, + }, + dictWord{133, 0, 828}, + dictWord{5, 0, 806}, + dictWord{7, 0, 176}, + dictWord{7, 0, 178}, + dictWord{7, 0, 1240}, + dictWord{7, 0, 1976}, + dictWord{ + 132, + 10, + 644, + }, + dictWord{135, 11, 1877}, + dictWord{5, 11, 420}, + dictWord{135, 11, 1449}, + dictWord{4, 0, 51}, + dictWord{5, 0, 39}, + dictWord{6, 0, 4}, + dictWord{7, 0, 591}, + dictWord{7, 0, 849}, + dictWord{7, 0, 951}, + dictWord{7, 0, 1613}, + dictWord{7, 0, 1760}, + dictWord{7, 0, 1988}, + dictWord{9, 0, 434}, + dictWord{10, 0, 754}, + dictWord{ + 11, + 0, + 25, + }, + dictWord{139, 0, 37}, + dictWord{10, 11, 57}, + dictWord{138, 11, 277}, + dictWord{135, 10, 540}, + dictWord{132, 11, 204}, + dictWord{135, 0, 159}, + dictWord{139, 11, 231}, + dictWord{133, 0, 902}, + dictWord{7, 0, 928}, + dictWord{7, 11, 366}, + dictWord{9, 11, 287}, + dictWord{12, 11, 199}, + dictWord{12, 11, 556}, + dictWord{140, 11, 577}, + dictWord{6, 10, 623}, + dictWord{136, 10, 789}, + dictWord{4, 10, 908}, + dictWord{5, 10, 359}, + dictWord{5, 10, 508}, + dictWord{6, 10, 1723}, + dictWord{7, 10, 343}, + dictWord{7, 10, 1996}, + dictWord{135, 10, 2026}, + dictWord{134, 0, 270}, + dictWord{4, 10, 341}, + dictWord{135, 10, 480}, + dictWord{ + 5, + 11, + 356, + }, + dictWord{135, 11, 224}, + dictWord{11, 11, 588}, + dictWord{11, 11, 864}, + dictWord{11, 11, 968}, + dictWord{143, 11, 160}, + dictWord{132, 0, 556}, + dictWord{137, 0, 801}, + dictWord{132, 0, 416}, + dictWord{142, 0, 372}, + dictWord{5, 0, 152}, + dictWord{5, 0, 197}, + dictWord{7, 0, 340}, + dictWord{7, 0, 867}, + dictWord{ + 10, + 0, + 548, + }, + dictWord{10, 0, 581}, + dictWord{11, 0, 6}, + dictWord{12, 0, 3}, + dictWord{12, 0, 19}, + dictWord{14, 0, 110}, + dictWord{142, 0, 289}, + dictWord{139, 0, 369}, + dictWord{7, 11, 630}, + dictWord{9, 11, 567}, + dictWord{11, 11, 150}, + dictWord{11, 11, 444}, + dictWord{141, 11, 119}, + dictWord{134, 11, 539}, + dictWord{ + 7, + 10, + 1995, + }, + dictWord{8, 10, 299}, + dictWord{11, 10, 890}, + dictWord{140, 10, 674}, + dictWord{7, 0, 34}, + dictWord{7, 0, 190}, + dictWord{8, 0, 28}, + dictWord{8, 0, 141}, + dictWord{8, 0, 444}, + dictWord{8, 0, 811}, + dictWord{9, 0, 468}, + dictWord{11, 0, 334}, + dictWord{12, 0, 24}, + dictWord{12, 0, 386}, + dictWord{140, 0, 576}, + dictWord{ + 133, + 0, + 757, + }, + dictWord{7, 0, 1553}, + dictWord{136, 0, 898}, + dictWord{133, 0, 721}, + dictWord{136, 0, 1012}, + dictWord{4, 0, 789}, + dictWord{5, 0, 647}, + dictWord{ + 135, + 0, + 1102, + }, + dictWord{132, 0, 898}, + dictWord{10, 0, 183}, + dictWord{4, 10, 238}, + dictWord{5, 10, 503}, + dictWord{6, 10, 179}, + dictWord{7, 10, 2003}, + dictWord{ + 8, + 10, + 381, + }, + dictWord{8, 10, 473}, + dictWord{9, 10, 149}, + dictWord{10, 10, 788}, + dictWord{15, 10, 45}, + dictWord{15, 10, 86}, + dictWord{20, 10, 110}, + dictWord{ + 150, + 10, + 57, + }, + dictWord{9, 0, 136}, + dictWord{19, 0, 107}, + dictWord{4, 10, 121}, + dictWord{5, 10, 156}, + dictWord{5, 10, 349}, + dictWord{10, 10, 605}, + dictWord{ + 142, + 10, + 342, + }, + dictWord{4, 11, 235}, + dictWord{135, 11, 255}, + dictWord{4, 11, 194}, + dictWord{5, 11, 584}, + dictWord{6, 11, 384}, + dictWord{7, 11, 583}, + dictWord{ + 10, + 11, + 761, + }, + dictWord{11, 11, 760}, + dictWord{139, 11, 851}, + dictWord{6, 10, 80}, + dictWord{6, 10, 1694}, + dictWord{7, 10, 173}, + dictWord{7, 10, 1974}, + dictWord{ + 9, + 10, + 547, + }, + dictWord{10, 10, 730}, + dictWord{14, 10, 18}, + dictWord{150, 10, 39}, + dictWord{4, 10, 923}, + dictWord{134, 10, 1711}, + dictWord{5, 0, 277}, + dictWord{141, 0, 247}, + dictWord{132, 0, 435}, + dictWord{133, 11, 562}, + dictWord{134, 0, 1311}, + dictWord{5, 11, 191}, + dictWord{137, 11, 271}, + dictWord{ + 132, + 10, + 595, + }, + dictWord{7, 11, 1537}, + dictWord{14, 11, 96}, + dictWord{143, 11, 73}, + dictWord{5, 0, 437}, + dictWord{7, 0, 502}, + dictWord{7, 0, 519}, + dictWord{7, 0, 1122}, + dictWord{7, 0, 1751}, + dictWord{14, 0, 211}, + dictWord{6, 10, 459}, + dictWord{7, 10, 1753}, + dictWord{7, 10, 1805}, + dictWord{8, 10, 658}, + dictWord{9, 10, 1}, + dictWord{11, 10, 959}, + dictWord{141, 10, 446}, + dictWord{6, 0, 814}, + dictWord{4, 11, 470}, + dictWord{5, 11, 473}, + dictWord{6, 11, 153}, + dictWord{7, 11, 1503}, + dictWord{7, 11, 1923}, + dictWord{10, 11, 701}, + dictWord{11, 11, 132}, + dictWord{11, 11, 168}, + dictWord{11, 11, 227}, + dictWord{11, 11, 320}, + dictWord{ + 11, + 11, + 436, + }, + dictWord{11, 11, 525}, + dictWord{11, 11, 855}, + dictWord{12, 11, 41}, + dictWord{12, 11, 286}, + dictWord{13, 11, 103}, + dictWord{13, 11, 284}, + dictWord{ + 14, + 11, + 255, + }, + dictWord{14, 11, 262}, + dictWord{15, 11, 117}, + dictWord{143, 11, 127}, + dictWord{5, 0, 265}, + dictWord{6, 0, 212}, + dictWord{135, 0, 28}, + dictWord{ + 138, + 0, + 750, + }, + dictWord{133, 11, 327}, + dictWord{6, 11, 552}, + dictWord{7, 11, 1754}, + dictWord{137, 11, 604}, + dictWord{134, 0, 2012}, + dictWord{132, 0, 702}, + dictWord{5, 11, 80}, + dictWord{6, 11, 405}, + dictWord{7, 11, 403}, + dictWord{7, 11, 1502}, + dictWord{7, 11, 1626}, + dictWord{8, 11, 456}, + dictWord{9, 11, 487}, + dictWord{9, 11, 853}, + dictWord{9, 11, 889}, + dictWord{10, 11, 309}, + dictWord{11, 11, 721}, + dictWord{11, 11, 994}, + dictWord{12, 11, 430}, + dictWord{ + 141, + 11, + 165, + }, + dictWord{5, 0, 808}, + dictWord{135, 0, 2045}, + dictWord{5, 0, 166}, + dictWord{8, 0, 739}, + dictWord{140, 0, 511}, + dictWord{134, 10, 490}, + dictWord{ + 4, + 11, + 453, + }, + dictWord{5, 11, 887}, + dictWord{6, 11, 535}, + dictWord{8, 11, 6}, + dictWord{136, 11, 543}, + dictWord{4, 0, 119}, + dictWord{5, 0, 170}, + dictWord{5, 0, 447}, + dictWord{7, 0, 1708}, + dictWord{7, 0, 1889}, + dictWord{9, 0, 357}, + dictWord{9, 0, 719}, + dictWord{12, 0, 486}, + dictWord{140, 0, 596}, + dictWord{137, 0, 500}, + dictWord{ + 7, + 10, + 250, + }, + dictWord{136, 10, 507}, + dictWord{132, 10, 158}, + dictWord{6, 0, 809}, + dictWord{134, 0, 1500}, + dictWord{9, 0, 327}, + dictWord{11, 0, 350}, + dictWord{11, 0, 831}, + dictWord{13, 0, 352}, + dictWord{4, 10, 140}, + dictWord{7, 10, 362}, + dictWord{8, 10, 209}, + dictWord{9, 10, 10}, + dictWord{9, 10, 503}, + dictWord{ + 9, + 10, + 614, + }, + dictWord{10, 10, 689}, + dictWord{11, 10, 327}, + dictWord{11, 10, 725}, + dictWord{12, 10, 252}, + dictWord{12, 10, 583}, + dictWord{13, 10, 192}, + dictWord{14, 10, 269}, + dictWord{14, 10, 356}, + dictWord{148, 10, 50}, + dictWord{135, 11, 741}, + dictWord{4, 0, 450}, + dictWord{7, 0, 1158}, + dictWord{19, 10, 1}, + dictWord{19, 10, 26}, + dictWord{150, 10, 9}, + dictWord{6, 0, 597}, + dictWord{135, 0, 1318}, + dictWord{134, 0, 1602}, + dictWord{6, 10, 228}, + dictWord{7, 10, 1341}, + dictWord{9, 10, 408}, + dictWord{138, 10, 343}, + dictWord{7, 0, 1375}, + dictWord{7, 0, 1466}, + dictWord{138, 0, 331}, + dictWord{132, 0, 754}, + dictWord{ + 132, + 10, + 557, + }, + dictWord{5, 11, 101}, + dictWord{6, 11, 88}, + dictWord{6, 11, 543}, + dictWord{7, 11, 1677}, + dictWord{9, 11, 100}, + dictWord{10, 11, 677}, + dictWord{ + 14, + 11, + 169, + }, + dictWord{14, 11, 302}, + dictWord{14, 11, 313}, + dictWord{15, 11, 48}, + dictWord{143, 11, 84}, + dictWord{134, 0, 1368}, + dictWord{4, 11, 310}, + dictWord{ + 9, + 11, + 795, + }, + dictWord{10, 11, 733}, + dictWord{11, 11, 451}, + dictWord{12, 11, 249}, + dictWord{14, 11, 115}, + dictWord{14, 11, 286}, + dictWord{143, 11, 100}, + dictWord{132, 10, 548}, + dictWord{10, 0, 557}, + dictWord{7, 10, 197}, + dictWord{8, 10, 142}, + dictWord{8, 10, 325}, + dictWord{9, 10, 150}, + dictWord{9, 10, 596}, + dictWord{10, 10, 353}, + dictWord{11, 10, 74}, + dictWord{11, 10, 315}, + dictWord{12, 10, 662}, + dictWord{12, 10, 681}, + dictWord{14, 10, 423}, + dictWord{ + 143, + 10, + 141, + }, + dictWord{133, 11, 587}, + dictWord{5, 0, 850}, + dictWord{136, 0, 799}, + dictWord{10, 0, 908}, + dictWord{12, 0, 701}, + dictWord{12, 0, 757}, + dictWord{ + 142, + 0, + 466, + }, + dictWord{4, 0, 62}, + dictWord{5, 0, 275}, + dictWord{18, 0, 19}, + dictWord{6, 10, 399}, + dictWord{6, 10, 579}, + dictWord{7, 10, 692}, + dictWord{7, 10, 846}, + dictWord{ + 7, + 10, + 1015, + }, + dictWord{7, 10, 1799}, + dictWord{8, 10, 403}, + dictWord{9, 10, 394}, + dictWord{10, 10, 133}, + dictWord{12, 10, 4}, + dictWord{12, 10, 297}, + dictWord{12, 10, 452}, + dictWord{16, 10, 81}, + dictWord{18, 10, 25}, + dictWord{21, 10, 14}, + dictWord{22, 10, 12}, + dictWord{151, 10, 18}, + dictWord{12, 0, 459}, + dictWord{ + 7, + 10, + 1546, + }, + dictWord{11, 10, 299}, + dictWord{142, 10, 407}, + dictWord{132, 10, 177}, + dictWord{132, 11, 498}, + dictWord{7, 11, 217}, + dictWord{ + 8, + 11, + 140, + }, + dictWord{138, 11, 610}, + dictWord{5, 10, 411}, + dictWord{135, 10, 653}, + dictWord{134, 0, 1802}, + dictWord{7, 10, 439}, + dictWord{10, 10, 727}, + dictWord{11, 10, 260}, + dictWord{139, 10, 684}, + dictWord{133, 11, 905}, + dictWord{11, 11, 580}, + dictWord{142, 11, 201}, + dictWord{134, 0, 1397}, + dictWord{ + 5, + 10, + 208, + }, + dictWord{7, 10, 753}, + dictWord{135, 10, 1528}, + dictWord{7, 0, 238}, + dictWord{7, 0, 2033}, + dictWord{8, 0, 120}, + dictWord{8, 0, 188}, + dictWord{8, 0, 659}, + dictWord{9, 0, 598}, + dictWord{10, 0, 466}, + dictWord{12, 0, 342}, + dictWord{12, 0, 588}, + dictWord{13, 0, 503}, + dictWord{14, 0, 246}, + dictWord{143, 0, 92}, + dictWord{135, 11, 1041}, + dictWord{4, 11, 456}, + dictWord{7, 11, 105}, + dictWord{7, 11, 358}, + dictWord{7, 11, 1637}, + dictWord{8, 11, 643}, + dictWord{139, 11, 483}, + dictWord{6, 0, 1318}, + dictWord{134, 0, 1324}, + dictWord{4, 0, 201}, + dictWord{7, 0, 1744}, + dictWord{8, 0, 602}, + dictWord{11, 0, 247}, + dictWord{11, 0, 826}, + dictWord{17, 0, 65}, + dictWord{133, 10, 242}, + dictWord{8, 0, 164}, + dictWord{146, 0, 62}, + dictWord{133, 10, 953}, + dictWord{139, 10, 802}, + dictWord{133, 0, 615}, + dictWord{7, 11, 1566}, + dictWord{8, 11, 269}, + dictWord{9, 11, 212}, + dictWord{9, 11, 718}, + dictWord{14, 11, 15}, + dictWord{14, 11, 132}, + dictWord{142, 11, 227}, + dictWord{133, 10, 290}, + dictWord{132, 10, 380}, + dictWord{5, 10, 52}, + dictWord{7, 10, 277}, + dictWord{9, 10, 368}, + dictWord{139, 10, 791}, + dictWord{ + 135, + 0, + 1243, + }, + dictWord{133, 11, 539}, + dictWord{11, 11, 919}, + dictWord{141, 11, 409}, + dictWord{136, 0, 968}, + dictWord{133, 11, 470}, + dictWord{134, 0, 882}, + dictWord{132, 0, 907}, + dictWord{5, 0, 100}, + dictWord{10, 0, 329}, + dictWord{12, 0, 416}, + dictWord{149, 0, 29}, + dictWord{10, 10, 138}, + dictWord{139, 10, 476}, + dictWord{5, 10, 725}, + dictWord{5, 10, 727}, + dictWord{6, 11, 91}, + dictWord{7, 11, 435}, + dictWord{135, 10, 1811}, + dictWord{4, 11, 16}, + dictWord{5, 11, 316}, + dictWord{5, 11, 842}, + dictWord{6, 11, 370}, + dictWord{6, 11, 1778}, + dictWord{8, 11, 166}, + dictWord{11, 11, 812}, + dictWord{12, 11, 206}, + dictWord{12, 11, 351}, + dictWord{14, 11, 418}, + dictWord{16, 11, 15}, + dictWord{16, 11, 34}, + dictWord{18, 11, 3}, + dictWord{19, 11, 3}, + dictWord{19, 11, 7}, + dictWord{20, 11, 4}, + dictWord{ + 149, + 11, + 21, + }, + dictWord{132, 0, 176}, + dictWord{5, 0, 636}, + dictWord{5, 0, 998}, + dictWord{7, 0, 9}, + dictWord{7, 0, 1508}, + dictWord{8, 0, 26}, + dictWord{9, 0, 317}, + dictWord{ + 9, + 0, + 358, + }, + dictWord{10, 0, 210}, + dictWord{10, 0, 292}, + dictWord{10, 0, 533}, + dictWord{11, 0, 555}, + dictWord{12, 0, 526}, + dictWord{12, 0, 607}, + dictWord{ + 13, + 0, + 263, + }, + dictWord{13, 0, 459}, + dictWord{142, 0, 271}, + dictWord{6, 0, 256}, + dictWord{8, 0, 265}, + dictWord{4, 10, 38}, + dictWord{7, 10, 307}, + dictWord{7, 10, 999}, + dictWord{7, 10, 1481}, + dictWord{7, 10, 1732}, + dictWord{7, 10, 1738}, + dictWord{9, 10, 414}, + dictWord{11, 10, 316}, + dictWord{12, 10, 52}, + dictWord{13, 10, 420}, + dictWord{147, 10, 100}, + dictWord{135, 10, 1296}, + dictWord{4, 11, 611}, + dictWord{133, 11, 606}, + dictWord{4, 0, 643}, + dictWord{142, 11, 21}, + dictWord{ + 133, + 11, + 715, + }, + dictWord{133, 10, 723}, + dictWord{6, 0, 610}, + dictWord{135, 11, 597}, + dictWord{10, 0, 127}, + dictWord{141, 0, 27}, + dictWord{6, 0, 1995}, + dictWord{ + 6, + 0, + 2001, + }, + dictWord{8, 0, 119}, + dictWord{136, 0, 973}, + dictWord{4, 11, 149}, + dictWord{138, 11, 368}, + dictWord{12, 0, 522}, + dictWord{4, 11, 154}, + dictWord{ + 5, + 10, + 109, + }, + dictWord{6, 10, 1784}, + dictWord{7, 11, 1134}, + dictWord{7, 10, 1895}, + dictWord{8, 11, 105}, + dictWord{12, 10, 296}, + dictWord{140, 10, 302}, + dictWord{4, 11, 31}, + dictWord{6, 11, 429}, + dictWord{7, 11, 962}, + dictWord{9, 11, 458}, + dictWord{139, 11, 691}, + dictWord{10, 0, 553}, + dictWord{11, 0, 876}, + dictWord{13, 0, 193}, + dictWord{13, 0, 423}, + dictWord{14, 0, 166}, + dictWord{19, 0, 84}, + dictWord{4, 11, 312}, + dictWord{5, 10, 216}, + dictWord{7, 10, 1879}, + dictWord{ + 9, + 10, + 141, + }, + dictWord{9, 10, 270}, + dictWord{9, 10, 679}, + dictWord{10, 10, 159}, + dictWord{11, 10, 197}, + dictWord{12, 10, 538}, + dictWord{12, 10, 559}, + dictWord{14, 10, 144}, + dictWord{14, 10, 167}, + dictWord{143, 10, 67}, + dictWord{134, 0, 1582}, + dictWord{7, 0, 1578}, + dictWord{135, 11, 1578}, + dictWord{ + 137, + 10, + 81, + }, + dictWord{132, 11, 236}, + dictWord{134, 10, 391}, + dictWord{134, 0, 795}, + dictWord{7, 10, 322}, + dictWord{136, 10, 249}, + dictWord{5, 11, 836}, + dictWord{ + 5, + 11, + 857, + }, + dictWord{6, 11, 1680}, + dictWord{7, 11, 59}, + dictWord{147, 11, 53}, + dictWord{135, 0, 432}, + dictWord{10, 11, 68}, + dictWord{139, 11, 494}, + dictWord{4, 11, 81}, + dictWord{139, 11, 867}, + dictWord{7, 0, 126}, + dictWord{136, 0, 84}, + dictWord{142, 11, 280}, + dictWord{5, 11, 282}, + dictWord{8, 11, 650}, + dictWord{ + 9, + 11, + 295, + }, + dictWord{9, 11, 907}, + dictWord{138, 11, 443}, + dictWord{136, 0, 790}, + dictWord{5, 10, 632}, + dictWord{138, 10, 526}, + dictWord{6, 0, 64}, + dictWord{12, 0, 377}, + dictWord{13, 0, 309}, + dictWord{14, 0, 141}, + dictWord{14, 0, 429}, + dictWord{14, 11, 141}, + dictWord{142, 11, 429}, + dictWord{134, 0, 1529}, + dictWord{6, 0, 321}, + dictWord{7, 0, 1857}, + dictWord{9, 0, 530}, + dictWord{19, 0, 99}, + dictWord{7, 10, 948}, + dictWord{7, 10, 1042}, + dictWord{8, 10, 235}, + dictWord{ + 8, + 10, + 461, + }, + dictWord{9, 10, 453}, + dictWord{10, 10, 354}, + dictWord{145, 10, 77}, + dictWord{7, 0, 1104}, + dictWord{11, 0, 269}, + dictWord{11, 0, 539}, + dictWord{ + 11, + 0, + 627, + }, + dictWord{11, 0, 706}, + dictWord{11, 0, 975}, + dictWord{12, 0, 248}, + dictWord{12, 0, 434}, + dictWord{12, 0, 600}, + dictWord{12, 0, 622}, + dictWord{ + 13, + 0, + 297, + }, + dictWord{13, 0, 485}, + dictWord{14, 0, 69}, + dictWord{14, 0, 409}, + dictWord{143, 0, 108}, + dictWord{4, 10, 362}, + dictWord{7, 10, 52}, + dictWord{7, 10, 303}, + dictWord{10, 11, 70}, + dictWord{12, 11, 26}, + dictWord{14, 11, 17}, + dictWord{14, 11, 178}, + dictWord{15, 11, 34}, + dictWord{149, 11, 12}, + dictWord{11, 0, 977}, + dictWord{141, 0, 507}, + dictWord{9, 0, 34}, + dictWord{139, 0, 484}, + dictWord{5, 10, 196}, + dictWord{6, 10, 486}, + dictWord{7, 10, 212}, + dictWord{8, 10, 309}, + dictWord{136, 10, 346}, + dictWord{6, 0, 1700}, + dictWord{7, 0, 26}, + dictWord{7, 0, 293}, + dictWord{7, 0, 382}, + dictWord{7, 0, 1026}, + dictWord{7, 0, 1087}, + dictWord{ + 7, + 0, + 2027, + }, + dictWord{8, 0, 24}, + dictWord{8, 0, 114}, + dictWord{8, 0, 252}, + dictWord{8, 0, 727}, + dictWord{8, 0, 729}, + dictWord{9, 0, 30}, + dictWord{9, 0, 199}, + dictWord{ + 9, + 0, + 231, + }, + dictWord{9, 0, 251}, + dictWord{9, 0, 334}, + dictWord{9, 0, 361}, + dictWord{9, 0, 712}, + dictWord{10, 0, 55}, + dictWord{10, 0, 60}, + dictWord{10, 0, 232}, + dictWord{ + 10, + 0, + 332, + }, + dictWord{10, 0, 384}, + dictWord{10, 0, 396}, + dictWord{10, 0, 504}, + dictWord{10, 0, 542}, + dictWord{10, 0, 652}, + dictWord{11, 0, 20}, + dictWord{11, 0, 48}, + dictWord{11, 0, 207}, + dictWord{11, 0, 291}, + dictWord{11, 0, 298}, + dictWord{11, 0, 342}, + dictWord{11, 0, 365}, + dictWord{11, 0, 394}, + dictWord{11, 0, 620}, + dictWord{11, 0, 705}, + dictWord{11, 0, 1017}, + dictWord{12, 0, 123}, + dictWord{12, 0, 340}, + dictWord{12, 0, 406}, + dictWord{12, 0, 643}, + dictWord{13, 0, 61}, + dictWord{ + 13, + 0, + 269, + }, + dictWord{13, 0, 311}, + dictWord{13, 0, 319}, + dictWord{13, 0, 486}, + dictWord{14, 0, 234}, + dictWord{15, 0, 62}, + dictWord{15, 0, 85}, + dictWord{16, 0, 71}, + dictWord{18, 0, 119}, + dictWord{20, 0, 105}, + dictWord{135, 10, 1912}, + dictWord{4, 11, 71}, + dictWord{5, 11, 376}, + dictWord{7, 11, 119}, + dictWord{138, 11, 665}, + dictWord{10, 0, 918}, + dictWord{10, 0, 926}, + dictWord{4, 10, 686}, + dictWord{136, 11, 55}, + dictWord{138, 10, 625}, + dictWord{136, 10, 706}, + dictWord{ + 132, + 11, + 479, + }, + dictWord{4, 10, 30}, + dictWord{133, 10, 43}, + dictWord{6, 0, 379}, + dictWord{7, 0, 270}, + dictWord{8, 0, 176}, + dictWord{8, 0, 183}, + dictWord{9, 0, 432}, + dictWord{ + 9, + 0, + 661, + }, + dictWord{12, 0, 247}, + dictWord{12, 0, 617}, + dictWord{18, 0, 125}, + dictWord{7, 11, 607}, + dictWord{8, 11, 99}, + dictWord{152, 11, 4}, + dictWord{ + 5, + 0, + 792, + }, + dictWord{133, 0, 900}, + dictWord{4, 11, 612}, + dictWord{133, 11, 561}, + dictWord{4, 11, 41}, + dictWord{4, 10, 220}, + dictWord{5, 11, 74}, + dictWord{ + 7, + 10, + 1535, + }, + dictWord{7, 11, 1627}, + dictWord{11, 11, 871}, + dictWord{140, 11, 619}, + dictWord{135, 0, 1920}, + dictWord{7, 11, 94}, + dictWord{11, 11, 329}, + dictWord{11, 11, 965}, + dictWord{12, 11, 241}, + dictWord{14, 11, 354}, + dictWord{15, 11, 22}, + dictWord{148, 11, 63}, + dictWord{9, 11, 209}, + dictWord{137, 11, 300}, + dictWord{134, 0, 771}, + dictWord{135, 0, 1979}, + dictWord{4, 0, 901}, + dictWord{133, 0, 776}, + dictWord{142, 0, 254}, + dictWord{133, 11, 98}, + dictWord{ + 9, + 11, + 16, + }, + dictWord{141, 11, 386}, + dictWord{133, 11, 984}, + dictWord{4, 11, 182}, + dictWord{6, 11, 205}, + dictWord{135, 11, 220}, + dictWord{7, 10, 1725}, + dictWord{ + 7, + 10, + 1774, + }, + dictWord{138, 10, 393}, + dictWord{5, 10, 263}, + dictWord{134, 10, 414}, + dictWord{4, 11, 42}, + dictWord{9, 11, 205}, + dictWord{9, 11, 786}, + dictWord{138, 11, 659}, + dictWord{14, 0, 140}, + dictWord{148, 0, 41}, + dictWord{8, 0, 440}, + dictWord{10, 0, 359}, + dictWord{6, 10, 178}, + dictWord{6, 11, 289}, + dictWord{ + 6, + 10, + 1750, + }, + dictWord{7, 11, 1670}, + dictWord{9, 10, 690}, + dictWord{10, 10, 155}, + dictWord{10, 10, 373}, + dictWord{11, 10, 698}, + dictWord{12, 11, 57}, + dictWord{13, 10, 155}, + dictWord{20, 10, 93}, + dictWord{151, 11, 4}, + dictWord{4, 0, 37}, + dictWord{5, 0, 334}, + dictWord{7, 0, 1253}, + dictWord{151, 11, 25}, + dictWord{ + 4, + 0, + 508, + }, + dictWord{4, 11, 635}, + dictWord{5, 10, 97}, + dictWord{137, 10, 393}, + dictWord{139, 11, 533}, + dictWord{4, 0, 640}, + dictWord{133, 0, 513}, + dictWord{ + 134, + 10, + 1639, + }, + dictWord{132, 11, 371}, + dictWord{4, 11, 272}, + dictWord{7, 11, 836}, + dictWord{7, 11, 1651}, + dictWord{145, 11, 89}, + dictWord{5, 11, 825}, + dictWord{6, 11, 444}, + dictWord{6, 11, 1640}, + dictWord{136, 11, 308}, + dictWord{4, 10, 191}, + dictWord{7, 10, 934}, + dictWord{8, 10, 647}, + dictWord{145, 10, 97}, + dictWord{12, 0, 246}, + dictWord{15, 0, 162}, + dictWord{19, 0, 64}, + dictWord{20, 0, 8}, + dictWord{20, 0, 95}, + dictWord{22, 0, 24}, + dictWord{152, 0, 17}, + dictWord{4, 0, 533}, + dictWord{5, 10, 165}, + dictWord{9, 10, 346}, + dictWord{138, 10, 655}, + dictWord{5, 11, 737}, + dictWord{139, 10, 885}, + dictWord{133, 10, 877}, + dictWord{ + 8, + 10, + 128, + }, + dictWord{139, 10, 179}, + dictWord{137, 11, 307}, + dictWord{140, 0, 752}, + dictWord{133, 0, 920}, + dictWord{135, 0, 1048}, + dictWord{5, 0, 153}, + dictWord{ + 6, + 0, + 580, + }, + dictWord{6, 10, 1663}, + dictWord{7, 10, 132}, + dictWord{7, 10, 1154}, + dictWord{7, 10, 1415}, + dictWord{7, 10, 1507}, + dictWord{12, 10, 493}, + dictWord{15, 10, 105}, + dictWord{151, 10, 15}, + dictWord{5, 10, 459}, + dictWord{7, 10, 1073}, + dictWord{8, 10, 241}, + dictWord{136, 10, 334}, + dictWord{138, 0, 391}, + dictWord{135, 0, 1952}, + dictWord{133, 11, 525}, + dictWord{8, 11, 641}, + dictWord{11, 11, 388}, + dictWord{140, 11, 580}, + dictWord{142, 0, 126}, + dictWord{ + 134, + 0, + 640, + }, + dictWord{132, 0, 483}, + dictWord{7, 0, 1616}, + dictWord{9, 0, 69}, + dictWord{6, 10, 324}, + dictWord{6, 10, 520}, + dictWord{7, 10, 338}, + dictWord{ + 7, + 10, + 1729, + }, + dictWord{8, 10, 228}, + dictWord{139, 10, 750}, + dictWord{5, 11, 493}, + dictWord{134, 11, 528}, + dictWord{135, 0, 734}, + dictWord{4, 11, 174}, + dictWord{135, 11, 911}, + dictWord{138, 0, 480}, + dictWord{9, 0, 495}, + dictWord{146, 0, 104}, + dictWord{135, 10, 705}, + dictWord{9, 0, 472}, + dictWord{4, 10, 73}, + dictWord{6, 10, 612}, + dictWord{7, 10, 927}, + dictWord{7, 10, 1330}, + dictWord{7, 10, 1822}, + dictWord{8, 10, 217}, + dictWord{9, 10, 765}, + dictWord{9, 10, 766}, + dictWord{10, 10, 408}, + dictWord{11, 10, 51}, + dictWord{11, 10, 793}, + dictWord{12, 10, 266}, + dictWord{15, 10, 158}, + dictWord{20, 10, 89}, + dictWord{150, 10, 32}, + dictWord{7, 11, 548}, + dictWord{137, 11, 58}, + dictWord{4, 11, 32}, + dictWord{5, 11, 215}, + dictWord{6, 11, 269}, + dictWord{7, 11, 1782}, + dictWord{7, 11, 1892}, + dictWord{10, 11, 16}, + dictWord{11, 11, 822}, + dictWord{11, 11, 954}, + dictWord{141, 11, 481}, + dictWord{132, 0, 874}, + dictWord{9, 0, 229}, + dictWord{5, 10, 389}, + dictWord{136, 10, 636}, + dictWord{7, 11, 1749}, + dictWord{136, 11, 477}, + dictWord{134, 0, 948}, + dictWord{5, 11, 308}, + dictWord{135, 11, 1088}, + dictWord{ + 4, + 0, + 748, + }, + dictWord{139, 0, 1009}, + dictWord{136, 10, 21}, + dictWord{6, 0, 555}, + dictWord{135, 0, 485}, + dictWord{5, 11, 126}, + dictWord{8, 11, 297}, + dictWord{ + 9, + 11, + 366, + }, + dictWord{9, 11, 445}, + dictWord{12, 11, 53}, + dictWord{12, 11, 374}, + dictWord{141, 11, 492}, + dictWord{7, 11, 1551}, + dictWord{139, 11, 361}, + dictWord{136, 0, 193}, + dictWord{136, 0, 472}, + dictWord{8, 0, 653}, + dictWord{13, 0, 93}, + dictWord{147, 0, 14}, + dictWord{132, 0, 984}, + dictWord{132, 11, 175}, + dictWord{5, 0, 172}, + dictWord{6, 0, 1971}, + dictWord{132, 11, 685}, + dictWord{149, 11, 8}, + dictWord{133, 11, 797}, + dictWord{13, 0, 83}, + dictWord{5, 10, 189}, + dictWord{ + 7, + 10, + 442, + }, + dictWord{7, 10, 443}, + dictWord{8, 10, 281}, + dictWord{12, 10, 174}, + dictWord{141, 10, 261}, + dictWord{134, 0, 1568}, + dictWord{133, 11, 565}, + dictWord{139, 0, 384}, + dictWord{133, 0, 260}, + dictWord{7, 0, 758}, + dictWord{7, 0, 880}, + dictWord{7, 0, 1359}, + dictWord{9, 0, 164}, + dictWord{9, 0, 167}, + dictWord{ + 10, + 0, + 156, + }, + dictWord{10, 0, 588}, + dictWord{12, 0, 101}, + dictWord{14, 0, 48}, + dictWord{15, 0, 70}, + dictWord{6, 10, 2}, + dictWord{7, 10, 1262}, + dictWord{ + 7, + 10, + 1737, + }, + dictWord{8, 10, 22}, + dictWord{8, 10, 270}, + dictWord{8, 10, 612}, + dictWord{9, 10, 312}, + dictWord{9, 10, 436}, + dictWord{10, 10, 311}, + dictWord{ + 10, + 10, + 623, + }, + dictWord{11, 10, 72}, + dictWord{11, 10, 330}, + dictWord{11, 10, 455}, + dictWord{12, 10, 321}, + dictWord{12, 10, 504}, + dictWord{12, 10, 530}, + dictWord{ + 12, + 10, + 543, + }, + dictWord{13, 10, 17}, + dictWord{13, 10, 156}, + dictWord{13, 10, 334}, + dictWord{17, 10, 60}, + dictWord{148, 10, 64}, + dictWord{4, 11, 252}, + dictWord{ + 7, + 11, + 1068, + }, + dictWord{10, 11, 434}, + dictWord{11, 11, 228}, + dictWord{11, 11, 426}, + dictWord{13, 11, 231}, + dictWord{18, 11, 106}, + dictWord{148, 11, 87}, + dictWord{7, 10, 354}, + dictWord{10, 10, 410}, + dictWord{139, 10, 815}, + dictWord{6, 0, 367}, + dictWord{7, 10, 670}, + dictWord{7, 10, 1327}, + dictWord{8, 10, 411}, + dictWord{8, 10, 435}, + dictWord{9, 10, 653}, + dictWord{9, 10, 740}, + dictWord{10, 10, 385}, + dictWord{11, 10, 222}, + dictWord{11, 10, 324}, + dictWord{11, 10, 829}, + dictWord{140, 10, 611}, + dictWord{7, 0, 1174}, + dictWord{6, 10, 166}, + dictWord{135, 10, 374}, + dictWord{146, 0, 121}, + dictWord{132, 0, 828}, + dictWord{ + 5, + 11, + 231, + }, + dictWord{138, 11, 509}, + dictWord{7, 11, 601}, + dictWord{9, 11, 277}, + dictWord{9, 11, 674}, + dictWord{10, 11, 178}, + dictWord{10, 11, 257}, + dictWord{ + 10, + 11, + 418, + }, + dictWord{11, 11, 531}, + dictWord{11, 11, 544}, + dictWord{11, 11, 585}, + dictWord{12, 11, 113}, + dictWord{12, 11, 475}, + dictWord{13, 11, 99}, + dictWord{142, 11, 428}, + dictWord{134, 0, 1541}, + dictWord{135, 11, 1779}, + dictWord{5, 0, 343}, + dictWord{134, 10, 398}, + dictWord{135, 10, 50}, + dictWord{ + 135, + 11, + 1683, + }, + dictWord{4, 0, 440}, + dictWord{7, 0, 57}, + dictWord{8, 0, 167}, + dictWord{8, 0, 375}, + dictWord{9, 0, 82}, + dictWord{9, 0, 561}, + dictWord{9, 0, 744}, + dictWord{ + 10, + 0, + 620, + }, + dictWord{137, 11, 744}, + dictWord{134, 0, 926}, + dictWord{6, 10, 517}, + dictWord{7, 10, 1159}, + dictWord{10, 10, 621}, + dictWord{139, 10, 192}, + dictWord{137, 0, 827}, + dictWord{8, 0, 194}, + dictWord{136, 0, 756}, + dictWord{10, 10, 223}, + dictWord{139, 10, 645}, + dictWord{7, 10, 64}, + dictWord{ + 136, + 10, + 245, + }, + dictWord{4, 11, 399}, + dictWord{5, 11, 119}, + dictWord{5, 11, 494}, + dictWord{7, 11, 751}, + dictWord{137, 11, 556}, + dictWord{132, 0, 808}, + dictWord{ + 135, + 0, + 22, + }, + dictWord{7, 10, 1763}, + dictWord{140, 10, 310}, + dictWord{5, 0, 639}, + dictWord{7, 0, 1249}, + dictWord{11, 0, 896}, + dictWord{134, 11, 584}, + dictWord{ + 134, + 0, + 1614, + }, + dictWord{135, 0, 860}, + dictWord{135, 11, 1121}, + dictWord{5, 10, 129}, + dictWord{6, 10, 61}, + dictWord{135, 10, 947}, + dictWord{4, 0, 102}, + dictWord{ + 7, + 0, + 815, + }, + dictWord{7, 0, 1699}, + dictWord{139, 0, 964}, + dictWord{13, 10, 505}, + dictWord{141, 10, 506}, + dictWord{139, 10, 1000}, + dictWord{ + 132, + 11, + 679, + }, + dictWord{132, 0, 899}, + dictWord{132, 0, 569}, + dictWord{5, 11, 694}, + dictWord{137, 11, 714}, + dictWord{136, 0, 795}, + dictWord{6, 0, 2045}, + dictWord{ + 139, + 11, + 7, + }, + dictWord{6, 0, 52}, + dictWord{9, 0, 104}, + dictWord{9, 0, 559}, + dictWord{12, 0, 308}, + dictWord{147, 0, 87}, + dictWord{4, 0, 301}, + dictWord{132, 0, 604}, + dictWord{133, 10, 637}, + dictWord{136, 0, 779}, + dictWord{5, 11, 143}, + dictWord{5, 11, 769}, + dictWord{6, 11, 1760}, + dictWord{7, 11, 682}, + dictWord{7, 11, 1992}, + dictWord{136, 11, 736}, + dictWord{137, 10, 590}, + dictWord{147, 0, 32}, + dictWord{137, 11, 527}, + dictWord{5, 10, 280}, + dictWord{135, 10, 1226}, + dictWord{134, 0, 494}, + dictWord{6, 0, 677}, + dictWord{6, 0, 682}, + dictWord{134, 0, 1044}, + dictWord{133, 10, 281}, + dictWord{135, 10, 1064}, + dictWord{7, 0, 508}, + dictWord{133, 11, 860}, + dictWord{6, 11, 422}, + dictWord{7, 11, 0}, + dictWord{7, 11, 1544}, + dictWord{9, 11, 577}, + dictWord{11, 11, 990}, + dictWord{12, 11, 141}, + dictWord{12, 11, 453}, + dictWord{13, 11, 47}, + dictWord{141, 11, 266}, + dictWord{134, 0, 1014}, + dictWord{5, 11, 515}, + dictWord{137, 11, 131}, + dictWord{ + 134, + 0, + 957, + }, + dictWord{132, 11, 646}, + dictWord{6, 0, 310}, + dictWord{7, 0, 1849}, + dictWord{8, 0, 72}, + dictWord{8, 0, 272}, + dictWord{8, 0, 431}, + dictWord{9, 0, 12}, + dictWord{ + 9, + 0, + 376, + }, + dictWord{10, 0, 563}, + dictWord{10, 0, 630}, + dictWord{10, 0, 796}, + dictWord{10, 0, 810}, + dictWord{11, 0, 367}, + dictWord{11, 0, 599}, + dictWord{ + 11, + 0, + 686, + }, + dictWord{140, 0, 672}, + dictWord{7, 0, 570}, + dictWord{4, 11, 396}, + dictWord{7, 10, 120}, + dictWord{7, 11, 728}, + dictWord{8, 10, 489}, + dictWord{9, 11, 117}, + dictWord{9, 10, 319}, + dictWord{10, 10, 820}, + dictWord{11, 10, 1004}, + dictWord{12, 10, 379}, + dictWord{12, 10, 679}, + dictWord{13, 10, 117}, + dictWord{ + 13, + 11, + 202, + }, + dictWord{13, 10, 412}, + dictWord{14, 10, 25}, + dictWord{15, 10, 52}, + dictWord{15, 10, 161}, + dictWord{16, 10, 47}, + dictWord{20, 11, 51}, + dictWord{ + 149, + 10, + 2, + }, + dictWord{6, 11, 121}, + dictWord{6, 11, 124}, + dictWord{6, 11, 357}, + dictWord{7, 11, 1138}, + dictWord{7, 11, 1295}, + dictWord{8, 11, 162}, + dictWord{ + 139, + 11, + 655, + }, + dictWord{8, 0, 449}, + dictWord{4, 10, 937}, + dictWord{5, 10, 801}, + dictWord{136, 11, 449}, + dictWord{139, 11, 958}, + dictWord{6, 0, 181}, + dictWord{ + 7, + 0, + 537, + }, + dictWord{8, 0, 64}, + dictWord{9, 0, 127}, + dictWord{10, 0, 496}, + dictWord{12, 0, 510}, + dictWord{141, 0, 384}, + dictWord{138, 11, 253}, + dictWord{4, 0, 244}, + dictWord{135, 0, 233}, + dictWord{133, 11, 237}, + dictWord{132, 10, 365}, + dictWord{6, 0, 1650}, + dictWord{10, 0, 702}, + dictWord{139, 0, 245}, + dictWord{ + 5, + 10, + 7, + }, + dictWord{139, 10, 774}, + dictWord{13, 0, 463}, + dictWord{20, 0, 49}, + dictWord{13, 11, 463}, + dictWord{148, 11, 49}, + dictWord{4, 10, 734}, + dictWord{ + 5, + 10, + 662, + }, + dictWord{134, 10, 430}, + dictWord{4, 10, 746}, + dictWord{135, 10, 1090}, + dictWord{5, 10, 360}, + dictWord{136, 10, 237}, + dictWord{137, 0, 338}, + dictWord{143, 11, 10}, + dictWord{7, 11, 571}, + dictWord{138, 11, 366}, + dictWord{134, 0, 1279}, + dictWord{9, 11, 513}, + dictWord{10, 11, 22}, + dictWord{10, 11, 39}, + dictWord{12, 11, 122}, + dictWord{140, 11, 187}, + dictWord{133, 0, 896}, + dictWord{146, 0, 178}, + dictWord{134, 0, 695}, + dictWord{137, 0, 808}, + dictWord{ + 134, + 11, + 587, + }, + dictWord{7, 11, 107}, + dictWord{7, 11, 838}, + dictWord{8, 11, 550}, + dictWord{138, 11, 401}, + dictWord{7, 0, 1117}, + dictWord{136, 0, 539}, + dictWord{ + 4, + 10, + 277, + }, + dictWord{5, 10, 608}, + dictWord{6, 10, 493}, + dictWord{7, 10, 457}, + dictWord{140, 10, 384}, + dictWord{133, 11, 768}, + dictWord{12, 0, 257}, + dictWord{ + 7, + 10, + 27, + }, + dictWord{135, 10, 316}, + dictWord{140, 0, 1003}, + dictWord{4, 0, 207}, + dictWord{5, 0, 586}, + dictWord{5, 0, 676}, + dictWord{6, 0, 448}, + dictWord{ + 8, + 0, + 244, + }, + dictWord{11, 0, 1}, + dictWord{13, 0, 3}, + dictWord{16, 0, 54}, + dictWord{17, 0, 4}, + dictWord{18, 0, 13}, + dictWord{133, 10, 552}, + dictWord{4, 10, 401}, + dictWord{ + 137, + 10, + 264, + }, + dictWord{5, 0, 516}, + dictWord{7, 0, 1883}, + dictWord{135, 11, 1883}, + dictWord{12, 0, 960}, + dictWord{132, 11, 894}, + dictWord{5, 0, 4}, + dictWord{ + 5, + 0, + 810, + }, + dictWord{6, 0, 13}, + dictWord{6, 0, 538}, + dictWord{6, 0, 1690}, + dictWord{6, 0, 1726}, + dictWord{7, 0, 499}, + dictWord{7, 0, 1819}, + dictWord{8, 0, 148}, + dictWord{ + 8, + 0, + 696, + }, + dictWord{8, 0, 791}, + dictWord{12, 0, 125}, + dictWord{143, 0, 9}, + dictWord{135, 0, 1268}, + dictWord{11, 0, 30}, + dictWord{14, 0, 315}, + dictWord{ + 9, + 10, + 543, + }, + dictWord{10, 10, 524}, + dictWord{12, 10, 524}, + dictWord{16, 10, 18}, + dictWord{20, 10, 26}, + dictWord{148, 10, 65}, + dictWord{6, 0, 748}, + dictWord{ + 4, + 10, + 205, + }, + dictWord{5, 10, 623}, + dictWord{7, 10, 104}, + dictWord{136, 10, 519}, + dictWord{11, 0, 542}, + dictWord{139, 0, 852}, + dictWord{140, 0, 6}, + dictWord{ + 132, + 0, + 848, + }, + dictWord{7, 0, 1385}, + dictWord{11, 0, 582}, + dictWord{11, 0, 650}, + dictWord{11, 0, 901}, + dictWord{11, 0, 949}, + dictWord{12, 0, 232}, + dictWord{12, 0, 236}, + dictWord{13, 0, 413}, + dictWord{13, 0, 501}, + dictWord{18, 0, 116}, + dictWord{7, 10, 579}, + dictWord{9, 10, 41}, + dictWord{9, 10, 244}, + dictWord{9, 10, 669}, + dictWord{10, 10, 5}, + dictWord{11, 10, 861}, + dictWord{11, 10, 951}, + dictWord{139, 10, 980}, + dictWord{4, 0, 945}, + dictWord{6, 0, 1811}, + dictWord{6, 0, 1845}, + dictWord{ + 6, + 0, + 1853, + }, + dictWord{6, 0, 1858}, + dictWord{8, 0, 862}, + dictWord{12, 0, 782}, + dictWord{12, 0, 788}, + dictWord{18, 0, 160}, + dictWord{148, 0, 117}, + dictWord{ + 132, + 10, + 717, + }, + dictWord{4, 0, 925}, + dictWord{5, 0, 803}, + dictWord{8, 0, 698}, + dictWord{138, 0, 828}, + dictWord{134, 0, 1416}, + dictWord{132, 0, 610}, + dictWord{ + 139, + 0, + 992, + }, + dictWord{6, 0, 878}, + dictWord{134, 0, 1477}, + dictWord{135, 0, 1847}, + dictWord{138, 11, 531}, + dictWord{137, 11, 539}, + dictWord{134, 11, 272}, + dictWord{133, 0, 383}, + dictWord{134, 0, 1404}, + dictWord{132, 10, 489}, + dictWord{4, 11, 9}, + dictWord{5, 11, 128}, + dictWord{7, 11, 368}, + dictWord{ + 11, + 11, + 480, + }, + dictWord{148, 11, 3}, + dictWord{136, 0, 986}, + dictWord{9, 0, 660}, + dictWord{138, 0, 347}, + dictWord{135, 10, 892}, + dictWord{136, 11, 682}, + dictWord{ + 7, + 0, + 572, + }, + dictWord{9, 0, 592}, + dictWord{11, 0, 680}, + dictWord{12, 0, 356}, + dictWord{140, 0, 550}, + dictWord{7, 0, 1411}, + dictWord{138, 11, 527}, + dictWord{ + 4, + 11, + 2, + }, + dictWord{7, 11, 545}, + dictWord{135, 11, 894}, + dictWord{137, 10, 473}, + dictWord{11, 0, 64}, + dictWord{7, 11, 481}, + dictWord{7, 10, 819}, + dictWord{9, 10, 26}, + dictWord{9, 10, 392}, + dictWord{9, 11, 792}, + dictWord{10, 10, 152}, + dictWord{10, 10, 226}, + dictWord{12, 10, 276}, + dictWord{12, 10, 426}, + dictWord{ + 12, + 10, + 589, + }, + dictWord{13, 10, 460}, + dictWord{15, 10, 97}, + dictWord{19, 10, 48}, + dictWord{148, 10, 104}, + dictWord{135, 10, 51}, + dictWord{136, 11, 445}, + dictWord{136, 11, 646}, + dictWord{135, 0, 606}, + dictWord{132, 10, 674}, + dictWord{6, 0, 1829}, + dictWord{134, 0, 1830}, + dictWord{132, 10, 770}, + dictWord{ + 5, + 10, + 79, + }, + dictWord{7, 10, 1027}, + dictWord{7, 10, 1477}, + dictWord{139, 10, 52}, + dictWord{5, 11, 530}, + dictWord{142, 11, 113}, + dictWord{134, 10, 1666}, + dictWord{ + 7, + 0, + 748, + }, + dictWord{139, 0, 700}, + dictWord{134, 10, 195}, + dictWord{133, 10, 789}, + dictWord{9, 0, 87}, + dictWord{10, 0, 365}, + dictWord{4, 10, 251}, + dictWord{ + 4, + 10, + 688, + }, + dictWord{7, 10, 513}, + dictWord{135, 10, 1284}, + dictWord{136, 11, 111}, + dictWord{133, 0, 127}, + dictWord{6, 0, 198}, + dictWord{140, 0, 83}, + dictWord{133, 11, 556}, + dictWord{133, 10, 889}, + dictWord{4, 10, 160}, + dictWord{5, 10, 330}, + dictWord{7, 10, 1434}, + dictWord{136, 10, 174}, + dictWord{5, 0, 276}, + dictWord{6, 0, 55}, + dictWord{7, 0, 1369}, + dictWord{138, 0, 864}, + dictWord{8, 11, 16}, + dictWord{140, 11, 568}, + dictWord{6, 0, 1752}, + dictWord{136, 0, 726}, + dictWord{135, 0, 1066}, + dictWord{133, 0, 764}, + dictWord{6, 11, 186}, + dictWord{137, 11, 426}, + dictWord{11, 0, 683}, + dictWord{139, 11, 683}, + dictWord{ + 6, + 0, + 309, + }, + dictWord{7, 0, 331}, + dictWord{138, 0, 550}, + dictWord{133, 10, 374}, + dictWord{6, 0, 1212}, + dictWord{6, 0, 1852}, + dictWord{7, 0, 1062}, + dictWord{ + 8, + 0, + 874, + }, + dictWord{8, 0, 882}, + dictWord{138, 0, 936}, + dictWord{132, 11, 585}, + dictWord{134, 0, 1364}, + dictWord{7, 0, 986}, + dictWord{133, 10, 731}, + dictWord{ + 6, + 0, + 723, + }, + dictWord{6, 0, 1408}, + dictWord{138, 0, 381}, + dictWord{135, 0, 1573}, + dictWord{134, 0, 1025}, + dictWord{4, 10, 626}, + dictWord{5, 10, 642}, + dictWord{ + 6, + 10, + 425, + }, + dictWord{10, 10, 202}, + dictWord{139, 10, 141}, + dictWord{4, 11, 93}, + dictWord{5, 11, 252}, + dictWord{6, 11, 229}, + dictWord{7, 11, 291}, + dictWord{ + 9, + 11, + 550, + }, + dictWord{139, 11, 644}, + dictWord{137, 11, 749}, + dictWord{137, 11, 162}, + dictWord{132, 11, 381}, + dictWord{135, 0, 1559}, + dictWord{ + 6, + 0, + 194, + }, + dictWord{7, 0, 133}, + dictWord{10, 0, 493}, + dictWord{10, 0, 570}, + dictWord{139, 0, 664}, + dictWord{5, 0, 24}, + dictWord{5, 0, 569}, + dictWord{6, 0, 3}, + dictWord{ + 6, + 0, + 119, + }, + dictWord{6, 0, 143}, + dictWord{6, 0, 440}, + dictWord{7, 0, 295}, + dictWord{7, 0, 599}, + dictWord{7, 0, 1686}, + dictWord{7, 0, 1854}, + dictWord{8, 0, 424}, + dictWord{ + 9, + 0, + 43, + }, + dictWord{9, 0, 584}, + dictWord{9, 0, 760}, + dictWord{10, 0, 148}, + dictWord{10, 0, 328}, + dictWord{11, 0, 159}, + dictWord{11, 0, 253}, + dictWord{11, 0, 506}, + dictWord{12, 0, 487}, + dictWord{140, 0, 531}, + dictWord{6, 0, 661}, + dictWord{134, 0, 1517}, + dictWord{136, 10, 835}, + dictWord{151, 10, 17}, + dictWord{5, 0, 14}, + dictWord{5, 0, 892}, + dictWord{6, 0, 283}, + dictWord{7, 0, 234}, + dictWord{136, 0, 537}, + dictWord{139, 0, 541}, + dictWord{4, 0, 126}, + dictWord{8, 0, 635}, + dictWord{ + 147, + 0, + 34, + }, + dictWord{4, 0, 316}, + dictWord{4, 0, 495}, + dictWord{135, 0, 1561}, + dictWord{4, 11, 187}, + dictWord{5, 11, 184}, + dictWord{5, 11, 690}, + dictWord{ + 7, + 11, + 1869, + }, + dictWord{138, 11, 756}, + dictWord{139, 11, 783}, + dictWord{4, 0, 998}, + dictWord{137, 0, 861}, + dictWord{136, 0, 1009}, + dictWord{139, 11, 292}, + dictWord{5, 11, 21}, + dictWord{6, 11, 77}, + dictWord{6, 11, 157}, + dictWord{7, 11, 974}, + dictWord{7, 11, 1301}, + dictWord{7, 11, 1339}, + dictWord{7, 11, 1490}, + dictWord{ + 7, + 11, + 1873, + }, + dictWord{137, 11, 628}, + dictWord{7, 11, 1283}, + dictWord{9, 11, 227}, + dictWord{9, 11, 499}, + dictWord{10, 11, 341}, + dictWord{11, 11, 325}, + dictWord{11, 11, 408}, + dictWord{14, 11, 180}, + dictWord{15, 11, 144}, + dictWord{18, 11, 47}, + dictWord{147, 11, 49}, + dictWord{4, 0, 64}, + dictWord{5, 0, 352}, + dictWord{5, 0, 720}, + dictWord{6, 0, 368}, + dictWord{139, 0, 359}, + dictWord{5, 10, 384}, + dictWord{8, 10, 455}, + dictWord{140, 10, 48}, + dictWord{5, 10, 264}, + dictWord{ + 134, + 10, + 184, + }, + dictWord{7, 0, 1577}, + dictWord{10, 0, 304}, + dictWord{10, 0, 549}, + dictWord{12, 0, 365}, + dictWord{13, 0, 220}, + dictWord{13, 0, 240}, + dictWord{ + 142, + 0, + 33, + }, + dictWord{134, 0, 1107}, + dictWord{134, 0, 929}, + dictWord{135, 0, 1142}, + dictWord{6, 0, 175}, + dictWord{137, 0, 289}, + dictWord{5, 0, 432}, + dictWord{ + 133, + 0, + 913, + }, + dictWord{6, 0, 279}, + dictWord{7, 0, 219}, + dictWord{5, 10, 633}, + dictWord{135, 10, 1323}, + dictWord{7, 0, 785}, + dictWord{7, 10, 359}, + dictWord{ + 8, + 10, + 243, + }, + dictWord{140, 10, 175}, + dictWord{139, 0, 595}, + dictWord{132, 10, 105}, + dictWord{8, 11, 398}, + dictWord{9, 11, 681}, + dictWord{139, 11, 632}, + dictWord{140, 0, 80}, + dictWord{5, 0, 931}, + dictWord{134, 0, 1698}, + dictWord{142, 11, 241}, + dictWord{134, 11, 20}, + dictWord{134, 0, 1323}, + dictWord{11, 0, 526}, + dictWord{11, 0, 939}, + dictWord{141, 0, 290}, + dictWord{5, 0, 774}, + dictWord{6, 0, 780}, + dictWord{6, 0, 1637}, + dictWord{6, 0, 1686}, + dictWord{6, 0, 1751}, + dictWord{ + 8, + 0, + 559, + }, + dictWord{141, 0, 109}, + dictWord{141, 0, 127}, + dictWord{7, 0, 1167}, + dictWord{11, 0, 934}, + dictWord{13, 0, 391}, + dictWord{17, 0, 76}, + dictWord{ + 135, + 11, + 709, + }, + dictWord{135, 0, 963}, + dictWord{6, 0, 260}, + dictWord{135, 0, 1484}, + dictWord{134, 0, 573}, + dictWord{4, 10, 758}, + dictWord{139, 11, 941}, + dictWord{135, 10, 1649}, + dictWord{145, 11, 36}, + dictWord{4, 0, 292}, + dictWord{137, 0, 580}, + dictWord{4, 0, 736}, + dictWord{5, 0, 871}, + dictWord{6, 0, 1689}, + dictWord{135, 0, 1944}, + dictWord{7, 11, 945}, + dictWord{11, 11, 713}, + dictWord{139, 11, 744}, + dictWord{134, 0, 1164}, + dictWord{135, 11, 937}, + dictWord{ + 6, + 0, + 1922, + }, + dictWord{9, 0, 982}, + dictWord{15, 0, 173}, + dictWord{15, 0, 178}, + dictWord{15, 0, 200}, + dictWord{18, 0, 189}, + dictWord{18, 0, 207}, + dictWord{21, 0, 47}, + dictWord{135, 11, 1652}, + dictWord{7, 0, 1695}, + dictWord{139, 10, 128}, + dictWord{6, 0, 63}, + dictWord{135, 0, 920}, + dictWord{133, 0, 793}, + dictWord{ + 143, + 11, + 134, + }, + dictWord{133, 10, 918}, + dictWord{5, 0, 67}, + dictWord{6, 0, 62}, + dictWord{6, 0, 374}, + dictWord{135, 0, 1391}, + dictWord{9, 0, 790}, + dictWord{12, 0, 47}, + dictWord{4, 11, 579}, + dictWord{5, 11, 226}, + dictWord{5, 11, 323}, + dictWord{135, 11, 960}, + dictWord{10, 11, 784}, + dictWord{141, 11, 191}, + dictWord{4, 0, 391}, + dictWord{135, 0, 1169}, + dictWord{137, 0, 443}, + dictWord{13, 11, 232}, + dictWord{146, 11, 35}, + dictWord{132, 10, 340}, + dictWord{132, 0, 271}, + dictWord{ + 137, + 11, + 313, + }, + dictWord{5, 11, 973}, + dictWord{137, 11, 659}, + dictWord{134, 0, 1140}, + dictWord{6, 11, 135}, + dictWord{135, 11, 1176}, + dictWord{4, 0, 253}, + dictWord{5, 0, 544}, + dictWord{7, 0, 300}, + dictWord{137, 0, 340}, + dictWord{7, 0, 897}, + dictWord{5, 10, 985}, + dictWord{7, 10, 509}, + dictWord{145, 10, 96}, + dictWord{ + 138, + 11, + 735, + }, + dictWord{135, 10, 1919}, + dictWord{138, 0, 890}, + dictWord{5, 0, 818}, + dictWord{134, 0, 1122}, + dictWord{5, 0, 53}, + dictWord{5, 0, 541}, + dictWord{ + 6, + 0, + 94, + }, + dictWord{6, 0, 499}, + dictWord{7, 0, 230}, + dictWord{139, 0, 321}, + dictWord{4, 0, 920}, + dictWord{5, 0, 25}, + dictWord{5, 0, 790}, + dictWord{6, 0, 457}, + dictWord{ + 7, + 0, + 853, + }, + dictWord{8, 0, 788}, + dictWord{142, 11, 31}, + dictWord{132, 10, 247}, + dictWord{135, 11, 314}, + dictWord{132, 0, 468}, + dictWord{7, 0, 243}, + dictWord{ + 6, + 10, + 337, + }, + dictWord{7, 10, 494}, + dictWord{8, 10, 27}, + dictWord{8, 10, 599}, + dictWord{138, 10, 153}, + dictWord{4, 10, 184}, + dictWord{5, 10, 390}, + dictWord{ + 7, + 10, + 618, + }, + dictWord{7, 10, 1456}, + dictWord{139, 10, 710}, + dictWord{134, 0, 870}, + dictWord{134, 0, 1238}, + dictWord{134, 0, 1765}, + dictWord{10, 0, 853}, + dictWord{10, 0, 943}, + dictWord{14, 0, 437}, + dictWord{14, 0, 439}, + dictWord{14, 0, 443}, + dictWord{14, 0, 446}, + dictWord{14, 0, 452}, + dictWord{14, 0, 469}, + dictWord{ + 14, + 0, + 471, + }, + dictWord{14, 0, 473}, + dictWord{16, 0, 93}, + dictWord{16, 0, 102}, + dictWord{16, 0, 110}, + dictWord{148, 0, 121}, + dictWord{4, 0, 605}, + dictWord{ + 7, + 0, + 518, + }, + dictWord{7, 0, 1282}, + dictWord{7, 0, 1918}, + dictWord{10, 0, 180}, + dictWord{139, 0, 218}, + dictWord{133, 0, 822}, + dictWord{4, 0, 634}, + dictWord{ + 11, + 0, + 916, + }, + dictWord{142, 0, 419}, + dictWord{6, 11, 281}, + dictWord{7, 11, 6}, + dictWord{8, 11, 282}, + dictWord{8, 11, 480}, + dictWord{8, 11, 499}, + dictWord{9, 11, 198}, + dictWord{10, 11, 143}, + dictWord{10, 11, 169}, + dictWord{10, 11, 211}, + dictWord{10, 11, 417}, + dictWord{10, 11, 574}, + dictWord{11, 11, 147}, + dictWord{ + 11, + 11, + 395, + }, + dictWord{12, 11, 75}, + dictWord{12, 11, 407}, + dictWord{12, 11, 608}, + dictWord{13, 11, 500}, + dictWord{142, 11, 251}, + dictWord{134, 0, 898}, + dictWord{ + 6, + 0, + 36, + }, + dictWord{7, 0, 658}, + dictWord{8, 0, 454}, + dictWord{150, 11, 48}, + dictWord{133, 11, 674}, + dictWord{135, 11, 1776}, + dictWord{4, 11, 419}, + dictWord{ + 10, + 10, + 227, + }, + dictWord{11, 10, 497}, + dictWord{11, 10, 709}, + dictWord{140, 10, 415}, + dictWord{6, 10, 360}, + dictWord{7, 10, 1664}, + dictWord{136, 10, 478}, + dictWord{137, 0, 806}, + dictWord{12, 11, 508}, + dictWord{14, 11, 102}, + dictWord{14, 11, 226}, + dictWord{144, 11, 57}, + dictWord{135, 11, 1123}, + dictWord{ + 4, + 11, + 138, + }, + dictWord{7, 11, 1012}, + dictWord{7, 11, 1280}, + dictWord{137, 11, 76}, + dictWord{5, 11, 29}, + dictWord{140, 11, 638}, + dictWord{136, 10, 699}, + dictWord{134, 0, 1326}, + dictWord{132, 0, 104}, + dictWord{135, 11, 735}, + dictWord{132, 10, 739}, + dictWord{134, 0, 1331}, + dictWord{7, 0, 260}, + dictWord{ + 135, + 11, + 260, + }, + dictWord{135, 11, 1063}, + dictWord{7, 0, 45}, + dictWord{9, 0, 542}, + dictWord{9, 0, 566}, + dictWord{10, 0, 728}, + dictWord{137, 10, 869}, + dictWord{ + 4, + 10, + 67, + }, + dictWord{5, 10, 422}, + dictWord{7, 10, 1037}, + dictWord{7, 10, 1289}, + dictWord{7, 10, 1555}, + dictWord{9, 10, 741}, + dictWord{145, 10, 108}, + dictWord{ + 139, + 0, + 263, + }, + dictWord{134, 0, 1516}, + dictWord{14, 0, 146}, + dictWord{15, 0, 42}, + dictWord{16, 0, 23}, + dictWord{17, 0, 86}, + dictWord{146, 0, 17}, + dictWord{ + 138, + 0, + 468, + }, + dictWord{136, 0, 1005}, + dictWord{4, 11, 17}, + dictWord{5, 11, 23}, + dictWord{7, 11, 995}, + dictWord{11, 11, 383}, + dictWord{11, 11, 437}, + dictWord{ + 12, + 11, + 460, + }, + dictWord{140, 11, 532}, + dictWord{7, 0, 87}, + dictWord{142, 0, 288}, + dictWord{138, 10, 96}, + dictWord{135, 11, 626}, + dictWord{144, 10, 26}, + dictWord{ + 7, + 0, + 988, + }, + dictWord{7, 0, 1939}, + dictWord{9, 0, 64}, + dictWord{9, 0, 502}, + dictWord{12, 0, 22}, + dictWord{12, 0, 34}, + dictWord{13, 0, 12}, + dictWord{13, 0, 234}, + dictWord{147, 0, 77}, + dictWord{13, 0, 133}, + dictWord{8, 10, 203}, + dictWord{11, 10, 823}, + dictWord{11, 10, 846}, + dictWord{12, 10, 482}, + dictWord{13, 10, 277}, + dictWord{13, 10, 302}, + dictWord{13, 10, 464}, + dictWord{14, 10, 205}, + dictWord{142, 10, 221}, + dictWord{4, 10, 449}, + dictWord{133, 10, 718}, + dictWord{ + 135, + 0, + 141, + }, + dictWord{6, 0, 1842}, + dictWord{136, 0, 872}, + dictWord{8, 11, 70}, + dictWord{12, 11, 171}, + dictWord{141, 11, 272}, + dictWord{4, 10, 355}, + dictWord{ + 6, + 10, + 311, + }, + dictWord{9, 10, 256}, + dictWord{138, 10, 404}, + dictWord{132, 0, 619}, + dictWord{137, 0, 261}, + dictWord{10, 11, 233}, + dictWord{10, 10, 758}, + dictWord{139, 11, 76}, + dictWord{5, 0, 246}, + dictWord{8, 0, 189}, + dictWord{9, 0, 355}, + dictWord{9, 0, 512}, + dictWord{10, 0, 124}, + dictWord{10, 0, 453}, + dictWord{ + 11, + 0, + 143, + }, + dictWord{11, 0, 416}, + dictWord{11, 0, 859}, + dictWord{141, 0, 341}, + dictWord{134, 11, 442}, + dictWord{133, 10, 827}, + dictWord{5, 10, 64}, + dictWord{ + 140, + 10, + 581, + }, + dictWord{4, 10, 442}, + dictWord{7, 10, 1047}, + dictWord{7, 10, 1352}, + dictWord{135, 10, 1643}, + dictWord{134, 11, 1709}, + dictWord{5, 0, 678}, + dictWord{6, 0, 305}, + dictWord{7, 0, 775}, + dictWord{7, 0, 1065}, + dictWord{133, 10, 977}, + dictWord{11, 11, 69}, + dictWord{12, 11, 105}, + dictWord{12, 11, 117}, + dictWord{13, 11, 213}, + dictWord{14, 11, 13}, + dictWord{14, 11, 62}, + dictWord{14, 11, 177}, + dictWord{14, 11, 421}, + dictWord{15, 11, 19}, + dictWord{146, 11, 141}, + dictWord{137, 11, 309}, + dictWord{5, 0, 35}, + dictWord{7, 0, 862}, + dictWord{7, 0, 1886}, + dictWord{138, 0, 179}, + dictWord{136, 0, 285}, + dictWord{132, 0, 517}, + dictWord{7, 11, 976}, + dictWord{9, 11, 146}, + dictWord{10, 11, 206}, + dictWord{10, 11, 596}, + dictWord{13, 11, 218}, + dictWord{142, 11, 153}, + dictWord{ + 132, + 10, + 254, + }, + dictWord{6, 0, 214}, + dictWord{12, 0, 540}, + dictWord{4, 10, 275}, + dictWord{7, 10, 1219}, + dictWord{140, 10, 376}, + dictWord{8, 0, 667}, + dictWord{ + 11, + 0, + 403, + }, + dictWord{146, 0, 83}, + dictWord{12, 0, 74}, + dictWord{10, 11, 648}, + dictWord{11, 11, 671}, + dictWord{143, 11, 46}, + dictWord{135, 0, 125}, + dictWord{ + 134, + 10, + 1753, + }, + dictWord{133, 0, 761}, + dictWord{6, 0, 912}, + dictWord{4, 11, 518}, + dictWord{6, 10, 369}, + dictWord{6, 10, 502}, + dictWord{7, 10, 1036}, + dictWord{ + 7, + 11, + 1136, + }, + dictWord{8, 10, 348}, + dictWord{9, 10, 452}, + dictWord{10, 10, 26}, + dictWord{11, 10, 224}, + dictWord{11, 10, 387}, + dictWord{11, 10, 772}, + dictWord{12, 10, 95}, + dictWord{12, 10, 629}, + dictWord{13, 10, 195}, + dictWord{13, 10, 207}, + dictWord{13, 10, 241}, + dictWord{14, 10, 260}, + dictWord{14, 10, 270}, + dictWord{143, 10, 140}, + dictWord{10, 0, 131}, + dictWord{140, 0, 72}, + dictWord{132, 10, 269}, + dictWord{5, 10, 480}, + dictWord{7, 10, 532}, + dictWord{ + 7, + 10, + 1197, + }, + dictWord{7, 10, 1358}, + dictWord{8, 10, 291}, + dictWord{11, 10, 349}, + dictWord{142, 10, 396}, + dictWord{8, 11, 689}, + dictWord{137, 11, 863}, + dictWord{ + 8, + 0, + 333, + }, + dictWord{138, 0, 182}, + dictWord{4, 11, 18}, + dictWord{7, 11, 145}, + dictWord{7, 11, 444}, + dictWord{7, 11, 1278}, + dictWord{8, 11, 49}, + dictWord{ + 8, + 11, + 400, + }, + dictWord{9, 11, 71}, + dictWord{9, 11, 250}, + dictWord{10, 11, 459}, + dictWord{12, 11, 160}, + dictWord{144, 11, 24}, + dictWord{14, 11, 35}, + dictWord{ + 142, + 11, + 191, + }, + dictWord{135, 11, 1864}, + dictWord{135, 0, 1338}, + dictWord{148, 10, 15}, + dictWord{14, 0, 94}, + dictWord{15, 0, 65}, + dictWord{16, 0, 4}, + dictWord{ + 16, + 0, + 77, + }, + dictWord{16, 0, 80}, + dictWord{145, 0, 5}, + dictWord{12, 11, 82}, + dictWord{143, 11, 36}, + dictWord{133, 11, 1010}, + dictWord{133, 0, 449}, + dictWord{ + 133, + 0, + 646, + }, + dictWord{7, 0, 86}, + dictWord{8, 0, 103}, + dictWord{135, 10, 657}, + dictWord{7, 0, 2028}, + dictWord{138, 0, 641}, + dictWord{136, 10, 533}, + dictWord{ + 134, + 0, + 1, + }, + dictWord{139, 11, 970}, + dictWord{5, 11, 87}, + dictWord{7, 11, 313}, + dictWord{7, 11, 1103}, + dictWord{10, 11, 112}, + dictWord{10, 11, 582}, + dictWord{ + 11, + 11, + 389, + }, + dictWord{11, 11, 813}, + dictWord{12, 11, 385}, + dictWord{13, 11, 286}, + dictWord{14, 11, 124}, + dictWord{146, 11, 108}, + dictWord{6, 0, 869}, + dictWord{ + 132, + 11, + 267, + }, + dictWord{6, 0, 277}, + dictWord{7, 0, 1274}, + dictWord{7, 0, 1386}, + dictWord{146, 0, 87}, + dictWord{6, 0, 187}, + dictWord{7, 0, 39}, + dictWord{7, 0, 1203}, + dictWord{8, 0, 380}, + dictWord{14, 0, 117}, + dictWord{149, 0, 28}, + dictWord{4, 10, 211}, + dictWord{4, 10, 332}, + dictWord{5, 10, 335}, + dictWord{6, 10, 238}, + dictWord{ + 7, + 10, + 269, + }, + dictWord{7, 10, 811}, + dictWord{7, 10, 1797}, + dictWord{8, 10, 836}, + dictWord{9, 10, 507}, + dictWord{141, 10, 242}, + dictWord{4, 0, 785}, + dictWord{ + 5, + 0, + 368, + }, + dictWord{6, 0, 297}, + dictWord{7, 0, 793}, + dictWord{139, 0, 938}, + dictWord{7, 0, 464}, + dictWord{8, 0, 558}, + dictWord{11, 0, 105}, + dictWord{12, 0, 231}, + dictWord{14, 0, 386}, + dictWord{15, 0, 102}, + dictWord{148, 0, 75}, + dictWord{133, 10, 1009}, + dictWord{8, 0, 877}, + dictWord{140, 0, 731}, + dictWord{ + 139, + 11, + 289, + }, + dictWord{10, 11, 249}, + dictWord{139, 11, 209}, + dictWord{132, 11, 561}, + dictWord{134, 0, 1608}, + dictWord{132, 11, 760}, + dictWord{134, 0, 1429}, + dictWord{9, 11, 154}, + dictWord{140, 11, 485}, + dictWord{5, 10, 228}, + dictWord{6, 10, 203}, + dictWord{7, 10, 156}, + dictWord{8, 10, 347}, + dictWord{ + 137, + 10, + 265, + }, + dictWord{7, 0, 1010}, + dictWord{11, 0, 733}, + dictWord{11, 0, 759}, + dictWord{13, 0, 34}, + dictWord{14, 0, 427}, + dictWord{146, 0, 45}, + dictWord{7, 10, 1131}, + dictWord{135, 10, 1468}, + dictWord{136, 11, 255}, + dictWord{7, 0, 1656}, + dictWord{9, 0, 369}, + dictWord{10, 0, 338}, + dictWord{10, 0, 490}, + dictWord{ + 11, + 0, + 154, + }, + dictWord{11, 0, 545}, + dictWord{11, 0, 775}, + dictWord{13, 0, 77}, + dictWord{141, 0, 274}, + dictWord{133, 11, 621}, + dictWord{134, 0, 1038}, + dictWord{ + 4, + 11, + 368, + }, + dictWord{135, 11, 641}, + dictWord{6, 0, 2010}, + dictWord{8, 0, 979}, + dictWord{8, 0, 985}, + dictWord{10, 0, 951}, + dictWord{138, 0, 1011}, + dictWord{ + 134, + 0, + 1005, + }, + dictWord{19, 0, 121}, + dictWord{5, 10, 291}, + dictWord{5, 10, 318}, + dictWord{7, 10, 765}, + dictWord{9, 10, 389}, + dictWord{140, 10, 548}, + dictWord{ + 5, + 0, + 20, + }, + dictWord{6, 0, 298}, + dictWord{7, 0, 659}, + dictWord{137, 0, 219}, + dictWord{7, 0, 1440}, + dictWord{11, 0, 854}, + dictWord{11, 0, 872}, + dictWord{11, 0, 921}, + dictWord{12, 0, 551}, + dictWord{13, 0, 472}, + dictWord{142, 0, 367}, + dictWord{5, 0, 490}, + dictWord{6, 0, 615}, + dictWord{6, 0, 620}, + dictWord{135, 0, 683}, + dictWord{ + 6, + 0, + 1070, + }, + dictWord{134, 0, 1597}, + dictWord{139, 0, 522}, + dictWord{132, 0, 439}, + dictWord{136, 0, 669}, + dictWord{6, 0, 766}, + dictWord{6, 0, 1143}, + dictWord{ + 6, + 0, + 1245, + }, + dictWord{10, 10, 525}, + dictWord{139, 10, 82}, + dictWord{9, 11, 92}, + dictWord{147, 11, 91}, + dictWord{6, 0, 668}, + dictWord{134, 0, 1218}, + dictWord{ + 6, + 11, + 525, + }, + dictWord{9, 11, 876}, + dictWord{140, 11, 284}, + dictWord{132, 0, 233}, + dictWord{136, 0, 547}, + dictWord{132, 10, 422}, + dictWord{5, 10, 355}, + dictWord{145, 10, 0}, + dictWord{6, 11, 300}, + dictWord{135, 11, 1515}, + dictWord{4, 0, 482}, + dictWord{137, 10, 905}, + dictWord{4, 0, 886}, + dictWord{7, 0, 346}, + dictWord{133, 11, 594}, + dictWord{133, 10, 865}, + dictWord{5, 10, 914}, + dictWord{134, 10, 1625}, + dictWord{135, 0, 334}, + dictWord{5, 0, 795}, + dictWord{ + 6, + 0, + 1741, + }, + dictWord{133, 10, 234}, + dictWord{135, 10, 1383}, + dictWord{6, 11, 1641}, + dictWord{136, 11, 820}, + dictWord{135, 0, 371}, + dictWord{7, 11, 1313}, + dictWord{138, 11, 660}, + dictWord{135, 10, 1312}, + dictWord{135, 0, 622}, + dictWord{7, 0, 625}, + dictWord{135, 0, 1750}, + dictWord{135, 0, 339}, + dictWord{ + 4, + 0, + 203, + }, + dictWord{135, 0, 1936}, + dictWord{15, 0, 29}, + dictWord{16, 0, 38}, + dictWord{15, 11, 29}, + dictWord{144, 11, 38}, + dictWord{5, 0, 338}, + dictWord{ + 135, + 0, + 1256, + }, + dictWord{135, 10, 1493}, + dictWord{10, 0, 130}, + dictWord{6, 10, 421}, + dictWord{7, 10, 61}, + dictWord{7, 10, 1540}, + dictWord{138, 10, 501}, + dictWord{ + 6, + 11, + 389, + }, + dictWord{7, 11, 149}, + dictWord{9, 11, 142}, + dictWord{138, 11, 94}, + dictWord{137, 10, 341}, + dictWord{11, 0, 678}, + dictWord{12, 0, 307}, + dictWord{142, 10, 98}, + dictWord{6, 11, 8}, + dictWord{7, 11, 1881}, + dictWord{136, 11, 91}, + dictWord{135, 0, 2044}, + dictWord{6, 0, 770}, + dictWord{6, 0, 802}, + dictWord{ + 6, + 0, + 812, + }, + dictWord{7, 0, 311}, + dictWord{9, 0, 308}, + dictWord{12, 0, 255}, + dictWord{6, 10, 102}, + dictWord{7, 10, 72}, + dictWord{15, 10, 142}, + dictWord{ + 147, + 10, + 67, + }, + dictWord{151, 10, 30}, + dictWord{135, 10, 823}, + dictWord{135, 0, 1266}, + dictWord{135, 11, 1746}, + dictWord{135, 10, 1870}, + dictWord{4, 0, 400}, + dictWord{5, 0, 267}, + dictWord{135, 0, 232}, + dictWord{7, 11, 24}, + dictWord{11, 11, 542}, + dictWord{139, 11, 852}, + dictWord{135, 11, 1739}, + dictWord{4, 11, 503}, + dictWord{135, 11, 1661}, + dictWord{5, 11, 130}, + dictWord{7, 11, 1314}, + dictWord{9, 11, 610}, + dictWord{10, 11, 718}, + dictWord{11, 11, 601}, + dictWord{ + 11, + 11, + 819, + }, + dictWord{11, 11, 946}, + dictWord{140, 11, 536}, + dictWord{10, 11, 149}, + dictWord{11, 11, 280}, + dictWord{142, 11, 336}, + dictWord{7, 0, 739}, + dictWord{11, 0, 690}, + dictWord{7, 11, 1946}, + dictWord{8, 10, 48}, + dictWord{8, 10, 88}, + dictWord{8, 10, 582}, + dictWord{8, 10, 681}, + dictWord{9, 10, 373}, + dictWord{ + 9, + 10, + 864, + }, + dictWord{11, 10, 157}, + dictWord{11, 10, 843}, + dictWord{148, 10, 27}, + dictWord{134, 0, 990}, + dictWord{4, 10, 88}, + dictWord{5, 10, 137}, + dictWord{ + 5, + 10, + 174, + }, + dictWord{5, 10, 777}, + dictWord{6, 10, 1664}, + dictWord{6, 10, 1725}, + dictWord{7, 10, 77}, + dictWord{7, 10, 426}, + dictWord{7, 10, 1317}, + dictWord{ + 7, + 10, + 1355, + }, + dictWord{8, 10, 126}, + dictWord{8, 10, 563}, + dictWord{9, 10, 523}, + dictWord{9, 10, 750}, + dictWord{10, 10, 310}, + dictWord{10, 10, 836}, + dictWord{ + 11, + 10, + 42, + }, + dictWord{11, 10, 318}, + dictWord{11, 10, 731}, + dictWord{12, 10, 68}, + dictWord{12, 10, 92}, + dictWord{12, 10, 507}, + dictWord{12, 10, 692}, + dictWord{ + 13, + 10, + 81, + }, + dictWord{13, 10, 238}, + dictWord{13, 10, 374}, + dictWord{14, 10, 436}, + dictWord{18, 10, 138}, + dictWord{19, 10, 78}, + dictWord{19, 10, 111}, + dictWord{20, 10, 55}, + dictWord{20, 10, 77}, + dictWord{148, 10, 92}, + dictWord{141, 10, 418}, + dictWord{7, 0, 1831}, + dictWord{132, 10, 938}, + dictWord{6, 0, 776}, + dictWord{134, 0, 915}, + dictWord{138, 10, 351}, + dictWord{5, 11, 348}, + dictWord{6, 11, 522}, + dictWord{6, 10, 1668}, + dictWord{7, 10, 1499}, + dictWord{8, 10, 117}, + dictWord{9, 10, 314}, + dictWord{138, 10, 174}, + dictWord{135, 10, 707}, + dictWord{132, 0, 613}, + dictWord{133, 10, 403}, + dictWord{132, 11, 392}, + dictWord{ + 5, + 11, + 433, + }, + dictWord{9, 11, 633}, + dictWord{139, 11, 629}, + dictWord{133, 0, 763}, + dictWord{132, 0, 878}, + dictWord{132, 0, 977}, + dictWord{132, 0, 100}, + dictWord{6, 0, 463}, + dictWord{4, 10, 44}, + dictWord{5, 10, 311}, + dictWord{7, 10, 639}, + dictWord{7, 10, 762}, + dictWord{7, 10, 1827}, + dictWord{9, 10, 8}, + dictWord{ + 9, + 10, + 462, + }, + dictWord{148, 10, 83}, + dictWord{134, 11, 234}, + dictWord{4, 10, 346}, + dictWord{7, 10, 115}, + dictWord{9, 10, 180}, + dictWord{9, 10, 456}, + dictWord{ + 138, + 10, + 363, + }, + dictWord{5, 0, 362}, + dictWord{5, 0, 443}, + dictWord{6, 0, 318}, + dictWord{7, 0, 1019}, + dictWord{139, 0, 623}, + dictWord{5, 0, 463}, + dictWord{8, 0, 296}, + dictWord{7, 11, 140}, + dictWord{7, 11, 1950}, + dictWord{8, 11, 680}, + dictWord{11, 11, 817}, + dictWord{147, 11, 88}, + dictWord{7, 11, 1222}, + dictWord{ + 138, + 11, + 386, + }, + dictWord{142, 0, 137}, + dictWord{132, 0, 454}, + dictWord{7, 0, 1914}, + dictWord{6, 11, 5}, + dictWord{7, 10, 1051}, + dictWord{9, 10, 545}, + dictWord{ + 11, + 11, + 249, + }, + dictWord{12, 11, 313}, + dictWord{16, 11, 66}, + dictWord{145, 11, 26}, + dictWord{135, 0, 1527}, + dictWord{145, 0, 58}, + dictWord{148, 11, 59}, + dictWord{ + 5, + 0, + 48, + }, + dictWord{5, 0, 404}, + dictWord{6, 0, 557}, + dictWord{7, 0, 458}, + dictWord{8, 0, 597}, + dictWord{10, 0, 455}, + dictWord{10, 0, 606}, + dictWord{11, 0, 49}, + dictWord{ + 11, + 0, + 548, + }, + dictWord{12, 0, 476}, + dictWord{13, 0, 18}, + dictWord{141, 0, 450}, + dictWord{5, 11, 963}, + dictWord{134, 11, 1773}, + dictWord{133, 0, 729}, + dictWord{138, 11, 586}, + dictWord{5, 0, 442}, + dictWord{135, 0, 1984}, + dictWord{134, 0, 449}, + dictWord{144, 0, 40}, + dictWord{4, 0, 853}, + dictWord{7, 11, 180}, + dictWord{8, 11, 509}, + dictWord{136, 11, 792}, + dictWord{6, 10, 185}, + dictWord{7, 10, 1899}, + dictWord{9, 10, 875}, + dictWord{139, 10, 673}, + dictWord{ + 134, + 11, + 524, + }, + dictWord{12, 0, 227}, + dictWord{4, 10, 327}, + dictWord{5, 10, 478}, + dictWord{7, 10, 1332}, + dictWord{136, 10, 753}, + dictWord{6, 0, 1491}, + dictWord{ + 5, + 10, + 1020, + }, + dictWord{133, 10, 1022}, + dictWord{4, 10, 103}, + dictWord{133, 10, 401}, + dictWord{132, 11, 931}, + dictWord{4, 10, 499}, + dictWord{135, 10, 1421}, + dictWord{5, 0, 55}, + dictWord{7, 0, 376}, + dictWord{140, 0, 161}, + dictWord{133, 0, 450}, + dictWord{6, 0, 1174}, + dictWord{134, 0, 1562}, + dictWord{10, 0, 62}, + dictWord{13, 0, 400}, + dictWord{135, 11, 1837}, + dictWord{140, 0, 207}, + dictWord{135, 0, 869}, + dictWord{4, 11, 773}, + dictWord{5, 11, 618}, + dictWord{ + 137, + 11, + 756, + }, + dictWord{132, 10, 96}, + dictWord{4, 0, 213}, + dictWord{7, 0, 223}, + dictWord{8, 0, 80}, + dictWord{135, 10, 968}, + dictWord{4, 11, 90}, + dictWord{5, 11, 337}, + dictWord{5, 11, 545}, + dictWord{7, 11, 754}, + dictWord{9, 11, 186}, + dictWord{10, 11, 72}, + dictWord{10, 11, 782}, + dictWord{11, 11, 513}, + dictWord{11, 11, 577}, + dictWord{11, 11, 610}, + dictWord{11, 11, 889}, + dictWord{11, 11, 961}, + dictWord{12, 11, 354}, + dictWord{12, 11, 362}, + dictWord{12, 11, 461}, + dictWord{ + 12, + 11, + 595, + }, + dictWord{13, 11, 79}, + dictWord{143, 11, 121}, + dictWord{7, 0, 381}, + dictWord{7, 0, 806}, + dictWord{7, 0, 820}, + dictWord{8, 0, 354}, + dictWord{8, 0, 437}, + dictWord{8, 0, 787}, + dictWord{9, 0, 657}, + dictWord{10, 0, 58}, + dictWord{10, 0, 339}, + dictWord{10, 0, 749}, + dictWord{11, 0, 914}, + dictWord{12, 0, 162}, + dictWord{ + 13, + 0, + 75, + }, + dictWord{14, 0, 106}, + dictWord{14, 0, 198}, + dictWord{14, 0, 320}, + dictWord{14, 0, 413}, + dictWord{146, 0, 43}, + dictWord{136, 0, 747}, + dictWord{ + 136, + 0, + 954, + }, + dictWord{134, 0, 1073}, + dictWord{135, 0, 556}, + dictWord{7, 11, 151}, + dictWord{9, 11, 329}, + dictWord{139, 11, 254}, + dictWord{5, 0, 692}, + dictWord{ + 134, + 0, + 1395, + }, + dictWord{6, 10, 563}, + dictWord{137, 10, 224}, + dictWord{134, 0, 191}, + dictWord{132, 0, 804}, + dictWord{9, 11, 187}, + dictWord{10, 11, 36}, + dictWord{17, 11, 44}, + dictWord{146, 11, 64}, + dictWord{7, 11, 165}, + dictWord{7, 11, 919}, + dictWord{136, 11, 517}, + dictWord{4, 11, 506}, + dictWord{5, 11, 295}, + dictWord{7, 11, 1680}, + dictWord{15, 11, 14}, + dictWord{144, 11, 5}, + dictWord{4, 0, 706}, + dictWord{6, 0, 162}, + dictWord{7, 0, 1960}, + dictWord{136, 0, 831}, + dictWord{ + 135, + 11, + 1376, + }, + dictWord{7, 11, 987}, + dictWord{9, 11, 688}, + dictWord{10, 11, 522}, + dictWord{11, 11, 788}, + dictWord{140, 11, 566}, + dictWord{150, 0, 35}, + dictWord{138, 0, 426}, + dictWord{135, 0, 1235}, + dictWord{135, 11, 1741}, + dictWord{7, 11, 389}, + dictWord{7, 11, 700}, + dictWord{7, 11, 940}, + dictWord{ + 8, + 11, + 514, + }, + dictWord{9, 11, 116}, + dictWord{9, 11, 535}, + dictWord{10, 11, 118}, + dictWord{11, 11, 107}, + dictWord{11, 11, 148}, + dictWord{11, 11, 922}, + dictWord{ + 12, + 11, + 254, + }, + dictWord{12, 11, 421}, + dictWord{142, 11, 238}, + dictWord{134, 0, 1234}, + dictWord{132, 11, 743}, + dictWord{4, 10, 910}, + dictWord{5, 10, 832}, + dictWord{135, 11, 1335}, + dictWord{141, 0, 96}, + dictWord{135, 11, 185}, + dictWord{146, 0, 149}, + dictWord{4, 0, 204}, + dictWord{137, 0, 902}, + dictWord{ + 4, + 11, + 784, + }, + dictWord{133, 11, 745}, + dictWord{136, 0, 833}, + dictWord{136, 0, 949}, + dictWord{7, 0, 366}, + dictWord{9, 0, 287}, + dictWord{12, 0, 199}, + dictWord{ + 12, + 0, + 556, + }, + dictWord{12, 0, 577}, + dictWord{5, 11, 81}, + dictWord{7, 11, 146}, + dictWord{7, 11, 1342}, + dictWord{7, 11, 1446}, + dictWord{8, 11, 53}, + dictWord{8, 11, 561}, + dictWord{8, 11, 694}, + dictWord{8, 11, 754}, + dictWord{9, 11, 97}, + dictWord{9, 11, 115}, + dictWord{9, 11, 894}, + dictWord{10, 11, 462}, + dictWord{10, 11, 813}, + dictWord{11, 11, 230}, + dictWord{11, 11, 657}, + dictWord{11, 11, 699}, + dictWord{11, 11, 748}, + dictWord{12, 11, 119}, + dictWord{12, 11, 200}, + dictWord{ + 12, + 11, + 283, + }, + dictWord{14, 11, 273}, + dictWord{145, 11, 15}, + dictWord{5, 11, 408}, + dictWord{137, 11, 747}, + dictWord{9, 11, 498}, + dictWord{140, 11, 181}, + dictWord{ + 6, + 0, + 2020, + }, + dictWord{136, 0, 992}, + dictWord{5, 0, 356}, + dictWord{135, 0, 224}, + dictWord{134, 0, 784}, + dictWord{7, 0, 630}, + dictWord{9, 0, 567}, + dictWord{ + 11, + 0, + 150, + }, + dictWord{11, 0, 444}, + dictWord{13, 0, 119}, + dictWord{8, 10, 528}, + dictWord{137, 10, 348}, + dictWord{134, 0, 539}, + dictWord{4, 10, 20}, + dictWord{ + 133, + 10, + 616, + }, + dictWord{142, 0, 27}, + dictWord{7, 11, 30}, + dictWord{8, 11, 86}, + dictWord{8, 11, 315}, + dictWord{8, 11, 700}, + dictWord{9, 11, 576}, + dictWord{9, 11, 858}, + dictWord{11, 11, 310}, + dictWord{11, 11, 888}, + dictWord{11, 11, 904}, + dictWord{12, 11, 361}, + dictWord{141, 11, 248}, + dictWord{138, 11, 839}, + dictWord{ + 134, + 0, + 755, + }, + dictWord{134, 0, 1063}, + dictWord{7, 10, 1091}, + dictWord{135, 10, 1765}, + dictWord{134, 11, 428}, + dictWord{7, 11, 524}, + dictWord{8, 11, 169}, + dictWord{8, 11, 234}, + dictWord{9, 11, 480}, + dictWord{138, 11, 646}, + dictWord{139, 0, 814}, + dictWord{7, 11, 1462}, + dictWord{139, 11, 659}, + dictWord{ + 4, + 10, + 26, + }, + dictWord{5, 10, 429}, + dictWord{6, 10, 245}, + dictWord{7, 10, 704}, + dictWord{7, 10, 1379}, + dictWord{135, 10, 1474}, + dictWord{7, 11, 1205}, + dictWord{ + 138, + 11, + 637, + }, + dictWord{139, 11, 803}, + dictWord{132, 10, 621}, + dictWord{136, 0, 987}, + dictWord{4, 11, 266}, + dictWord{8, 11, 4}, + dictWord{9, 11, 39}, + dictWord{ + 10, + 11, + 166, + }, + dictWord{11, 11, 918}, + dictWord{12, 11, 635}, + dictWord{20, 11, 10}, + dictWord{22, 11, 27}, + dictWord{150, 11, 43}, + dictWord{4, 0, 235}, + dictWord{ + 135, + 0, + 255, + }, + dictWord{4, 0, 194}, + dictWord{5, 0, 584}, + dictWord{6, 0, 384}, + dictWord{7, 0, 583}, + dictWord{10, 0, 761}, + dictWord{11, 0, 760}, + dictWord{139, 0, 851}, + dictWord{133, 10, 542}, + dictWord{134, 0, 1086}, + dictWord{133, 10, 868}, + dictWord{8, 0, 1016}, + dictWord{136, 0, 1018}, + dictWord{7, 0, 1396}, + dictWord{ + 7, + 11, + 1396, + }, + dictWord{136, 10, 433}, + dictWord{135, 10, 1495}, + dictWord{138, 10, 215}, + dictWord{141, 10, 124}, + dictWord{7, 11, 157}, + dictWord{ + 8, + 11, + 279, + }, + dictWord{9, 11, 759}, + dictWord{16, 11, 31}, + dictWord{16, 11, 39}, + dictWord{16, 11, 75}, + dictWord{18, 11, 24}, + dictWord{20, 11, 42}, + dictWord{152, 11, 1}, + dictWord{5, 0, 562}, + dictWord{134, 11, 604}, + dictWord{134, 0, 913}, + dictWord{5, 0, 191}, + dictWord{137, 0, 271}, + dictWord{4, 0, 470}, + dictWord{6, 0, 153}, + dictWord{7, 0, 1503}, + dictWord{7, 0, 1923}, + dictWord{10, 0, 701}, + dictWord{11, 0, 132}, + dictWord{11, 0, 227}, + dictWord{11, 0, 320}, + dictWord{11, 0, 436}, + dictWord{ + 11, + 0, + 525, + }, + dictWord{11, 0, 855}, + dictWord{11, 0, 873}, + dictWord{12, 0, 41}, + dictWord{12, 0, 286}, + dictWord{13, 0, 103}, + dictWord{13, 0, 284}, + dictWord{ + 14, + 0, + 255, + }, + dictWord{14, 0, 262}, + dictWord{15, 0, 117}, + dictWord{143, 0, 127}, + dictWord{7, 0, 475}, + dictWord{12, 0, 45}, + dictWord{147, 10, 112}, + dictWord{ + 132, + 11, + 567, + }, + dictWord{137, 11, 859}, + dictWord{6, 0, 713}, + dictWord{6, 0, 969}, + dictWord{6, 0, 1290}, + dictWord{134, 0, 1551}, + dictWord{133, 0, 327}, + dictWord{ + 6, + 0, + 552, + }, + dictWord{6, 0, 1292}, + dictWord{7, 0, 1754}, + dictWord{137, 0, 604}, + dictWord{4, 0, 223}, + dictWord{6, 0, 359}, + dictWord{11, 0, 3}, + dictWord{13, 0, 108}, + dictWord{14, 0, 89}, + dictWord{16, 0, 22}, + dictWord{5, 11, 762}, + dictWord{7, 11, 1880}, + dictWord{9, 11, 680}, + dictWord{139, 11, 798}, + dictWord{5, 0, 80}, + dictWord{ + 6, + 0, + 405, + }, + dictWord{7, 0, 403}, + dictWord{7, 0, 1502}, + dictWord{8, 0, 456}, + dictWord{9, 0, 487}, + dictWord{9, 0, 853}, + dictWord{9, 0, 889}, + dictWord{10, 0, 309}, + dictWord{ + 11, + 0, + 721, + }, + dictWord{11, 0, 994}, + dictWord{12, 0, 430}, + dictWord{141, 0, 165}, + dictWord{133, 11, 298}, + dictWord{132, 10, 647}, + dictWord{134, 0, 2016}, + dictWord{18, 10, 10}, + dictWord{146, 11, 10}, + dictWord{4, 0, 453}, + dictWord{5, 0, 887}, + dictWord{6, 0, 535}, + dictWord{8, 0, 6}, + dictWord{8, 0, 543}, + dictWord{ + 136, + 0, + 826, + }, + dictWord{136, 0, 975}, + dictWord{10, 0, 961}, + dictWord{138, 0, 962}, + dictWord{138, 10, 220}, + dictWord{6, 0, 1891}, + dictWord{6, 0, 1893}, + dictWord{ + 9, + 0, + 916, + }, + dictWord{9, 0, 965}, + dictWord{9, 0, 972}, + dictWord{12, 0, 801}, + dictWord{12, 0, 859}, + dictWord{12, 0, 883}, + dictWord{15, 0, 226}, + dictWord{149, 0, 51}, + dictWord{132, 10, 109}, + dictWord{135, 11, 267}, + dictWord{7, 11, 92}, + dictWord{7, 11, 182}, + dictWord{8, 11, 453}, + dictWord{9, 11, 204}, + dictWord{11, 11, 950}, + dictWord{12, 11, 94}, + dictWord{12, 11, 644}, + dictWord{16, 11, 20}, + dictWord{16, 11, 70}, + dictWord{16, 11, 90}, + dictWord{147, 11, 55}, + dictWord{ + 134, + 10, + 1746, + }, + dictWord{6, 11, 71}, + dictWord{7, 11, 845}, + dictWord{7, 11, 1308}, + dictWord{8, 11, 160}, + dictWord{137, 11, 318}, + dictWord{5, 0, 101}, + dictWord{6, 0, 88}, + dictWord{7, 0, 263}, + dictWord{7, 0, 628}, + dictWord{7, 0, 1677}, + dictWord{8, 0, 349}, + dictWord{9, 0, 100}, + dictWord{10, 0, 677}, + dictWord{14, 0, 169}, + dictWord{ + 14, + 0, + 302, + }, + dictWord{14, 0, 313}, + dictWord{15, 0, 48}, + dictWord{15, 0, 84}, + dictWord{7, 11, 237}, + dictWord{8, 11, 664}, + dictWord{9, 11, 42}, + dictWord{9, 11, 266}, + dictWord{9, 11, 380}, + dictWord{9, 11, 645}, + dictWord{10, 11, 177}, + dictWord{138, 11, 276}, + dictWord{138, 11, 69}, + dictWord{4, 0, 310}, + dictWord{7, 0, 708}, + dictWord{7, 0, 996}, + dictWord{9, 0, 795}, + dictWord{10, 0, 390}, + dictWord{10, 0, 733}, + dictWord{11, 0, 451}, + dictWord{12, 0, 249}, + dictWord{14, 0, 115}, + dictWord{ + 14, + 0, + 286, + }, + dictWord{143, 0, 100}, + dictWord{5, 0, 587}, + dictWord{4, 10, 40}, + dictWord{10, 10, 67}, + dictWord{11, 10, 117}, + dictWord{11, 10, 768}, + dictWord{ + 139, + 10, + 935, + }, + dictWord{6, 0, 1942}, + dictWord{7, 0, 512}, + dictWord{136, 0, 983}, + dictWord{7, 10, 992}, + dictWord{8, 10, 301}, + dictWord{9, 10, 722}, + dictWord{12, 10, 63}, + dictWord{13, 10, 29}, + dictWord{14, 10, 161}, + dictWord{143, 10, 18}, + dictWord{136, 11, 76}, + dictWord{139, 10, 923}, + dictWord{134, 0, 645}, + dictWord{ + 134, + 0, + 851, + }, + dictWord{4, 0, 498}, + dictWord{132, 11, 293}, + dictWord{7, 0, 217}, + dictWord{8, 0, 140}, + dictWord{10, 0, 610}, + dictWord{14, 11, 352}, + dictWord{ + 17, + 11, + 53, + }, + dictWord{18, 11, 146}, + dictWord{18, 11, 152}, + dictWord{19, 11, 11}, + dictWord{150, 11, 54}, + dictWord{134, 0, 1448}, + dictWord{138, 11, 841}, + dictWord{133, 0, 905}, + dictWord{4, 11, 605}, + dictWord{7, 11, 518}, + dictWord{7, 11, 1282}, + dictWord{7, 11, 1918}, + dictWord{10, 11, 180}, + dictWord{139, 11, 218}, + dictWord{139, 11, 917}, + dictWord{135, 10, 825}, + dictWord{140, 10, 328}, + dictWord{4, 0, 456}, + dictWord{7, 0, 105}, + dictWord{7, 0, 358}, + dictWord{7, 0, 1637}, + dictWord{8, 0, 643}, + dictWord{139, 0, 483}, + dictWord{134, 0, 792}, + dictWord{6, 11, 96}, + dictWord{135, 11, 1426}, + dictWord{137, 11, 691}, + dictWord{ + 4, + 11, + 651, + }, + dictWord{133, 11, 289}, + dictWord{7, 11, 688}, + dictWord{8, 11, 35}, + dictWord{9, 11, 511}, + dictWord{10, 11, 767}, + dictWord{147, 11, 118}, + dictWord{ + 150, + 0, + 56, + }, + dictWord{5, 0, 243}, + dictWord{5, 0, 535}, + dictWord{6, 10, 204}, + dictWord{10, 10, 320}, + dictWord{10, 10, 583}, + dictWord{13, 10, 502}, + dictWord{ + 14, + 10, + 72, + }, + dictWord{14, 10, 274}, + dictWord{14, 10, 312}, + dictWord{14, 10, 344}, + dictWord{15, 10, 159}, + dictWord{16, 10, 62}, + dictWord{16, 10, 69}, + dictWord{ + 17, + 10, + 30, + }, + dictWord{18, 10, 42}, + dictWord{18, 10, 53}, + dictWord{18, 10, 84}, + dictWord{18, 10, 140}, + dictWord{19, 10, 68}, + dictWord{19, 10, 85}, + dictWord{20, 10, 5}, + dictWord{20, 10, 45}, + dictWord{20, 10, 101}, + dictWord{22, 10, 7}, + dictWord{150, 10, 20}, + dictWord{4, 10, 558}, + dictWord{6, 10, 390}, + dictWord{7, 10, 162}, + dictWord{7, 10, 689}, + dictWord{9, 10, 360}, + dictWord{138, 10, 653}, + dictWord{146, 11, 23}, + dictWord{135, 0, 1748}, + dictWord{5, 10, 856}, + dictWord{ + 6, + 10, + 1672, + }, + dictWord{6, 10, 1757}, + dictWord{134, 10, 1781}, + dictWord{5, 0, 539}, + dictWord{5, 0, 754}, + dictWord{6, 0, 876}, + dictWord{132, 11, 704}, + dictWord{ + 135, + 11, + 1078, + }, + dictWord{5, 10, 92}, + dictWord{10, 10, 736}, + dictWord{140, 10, 102}, + dictWord{17, 0, 91}, + dictWord{5, 10, 590}, + dictWord{137, 10, 213}, + dictWord{134, 0, 1565}, + dictWord{6, 0, 91}, + dictWord{135, 0, 435}, + dictWord{4, 0, 939}, + dictWord{140, 0, 792}, + dictWord{134, 0, 1399}, + dictWord{4, 0, 16}, + dictWord{ + 5, + 0, + 316, + }, + dictWord{5, 0, 842}, + dictWord{6, 0, 370}, + dictWord{6, 0, 1778}, + dictWord{8, 0, 166}, + dictWord{11, 0, 812}, + dictWord{12, 0, 206}, + dictWord{12, 0, 351}, + dictWord{14, 0, 418}, + dictWord{16, 0, 15}, + dictWord{16, 0, 34}, + dictWord{18, 0, 3}, + dictWord{19, 0, 3}, + dictWord{19, 0, 7}, + dictWord{20, 0, 4}, + dictWord{21, 0, 21}, + dictWord{ + 4, + 11, + 720, + }, + dictWord{133, 11, 306}, + dictWord{144, 0, 95}, + dictWord{133, 11, 431}, + dictWord{132, 11, 234}, + dictWord{135, 0, 551}, + dictWord{4, 0, 999}, + dictWord{6, 0, 1966}, + dictWord{134, 0, 2042}, + dictWord{7, 0, 619}, + dictWord{10, 0, 547}, + dictWord{11, 0, 122}, + dictWord{12, 0, 601}, + dictWord{15, 0, 7}, + dictWord{148, 0, 20}, + dictWord{5, 11, 464}, + dictWord{6, 11, 236}, + dictWord{7, 11, 276}, + dictWord{7, 11, 696}, + dictWord{7, 11, 914}, + dictWord{7, 11, 1108}, + dictWord{ + 7, + 11, + 1448, + }, + dictWord{9, 11, 15}, + dictWord{9, 11, 564}, + dictWord{10, 11, 14}, + dictWord{12, 11, 565}, + dictWord{13, 11, 449}, + dictWord{14, 11, 53}, + dictWord{ + 15, + 11, + 13, + }, + dictWord{16, 11, 64}, + dictWord{145, 11, 41}, + dictWord{6, 0, 884}, + dictWord{6, 0, 1019}, + dictWord{134, 0, 1150}, + dictWord{6, 11, 1767}, + dictWord{ + 12, + 11, + 194, + }, + dictWord{145, 11, 107}, + dictWord{136, 10, 503}, + dictWord{133, 11, 840}, + dictWord{7, 0, 671}, + dictWord{134, 10, 466}, + dictWord{132, 0, 888}, + dictWord{4, 0, 149}, + dictWord{138, 0, 368}, + dictWord{4, 0, 154}, + dictWord{7, 0, 1134}, + dictWord{136, 0, 105}, + dictWord{135, 0, 983}, + dictWord{9, 11, 642}, + dictWord{11, 11, 236}, + dictWord{142, 11, 193}, + dictWord{4, 0, 31}, + dictWord{6, 0, 429}, + dictWord{7, 0, 962}, + dictWord{9, 0, 458}, + dictWord{139, 0, 691}, + dictWord{ + 6, + 0, + 643, + }, + dictWord{134, 0, 1102}, + dictWord{132, 0, 312}, + dictWord{4, 11, 68}, + dictWord{5, 11, 634}, + dictWord{6, 11, 386}, + dictWord{7, 11, 794}, + dictWord{ + 8, + 11, + 273, + }, + dictWord{9, 11, 563}, + dictWord{10, 11, 105}, + dictWord{10, 11, 171}, + dictWord{11, 11, 94}, + dictWord{139, 11, 354}, + dictWord{133, 0, 740}, + dictWord{ + 135, + 0, + 1642, + }, + dictWord{4, 11, 95}, + dictWord{7, 11, 416}, + dictWord{8, 11, 211}, + dictWord{139, 11, 830}, + dictWord{132, 0, 236}, + dictWord{138, 10, 241}, + dictWord{7, 11, 731}, + dictWord{13, 11, 20}, + dictWord{143, 11, 11}, + dictWord{5, 0, 836}, + dictWord{5, 0, 857}, + dictWord{6, 0, 1680}, + dictWord{135, 0, 59}, + dictWord{ + 10, + 0, + 68, + }, + dictWord{11, 0, 494}, + dictWord{152, 11, 6}, + dictWord{4, 0, 81}, + dictWord{139, 0, 867}, + dictWord{135, 0, 795}, + dictWord{133, 11, 689}, + dictWord{ + 4, + 0, + 1001, + }, + dictWord{5, 0, 282}, + dictWord{6, 0, 1932}, + dictWord{6, 0, 1977}, + dictWord{6, 0, 1987}, + dictWord{6, 0, 1992}, + dictWord{8, 0, 650}, + dictWord{8, 0, 919}, + dictWord{8, 0, 920}, + dictWord{8, 0, 923}, + dictWord{8, 0, 926}, + dictWord{8, 0, 927}, + dictWord{8, 0, 931}, + dictWord{8, 0, 939}, + dictWord{8, 0, 947}, + dictWord{8, 0, 956}, + dictWord{8, 0, 997}, + dictWord{9, 0, 907}, + dictWord{10, 0, 950}, + dictWord{10, 0, 953}, + dictWord{10, 0, 954}, + dictWord{10, 0, 956}, + dictWord{10, 0, 958}, + dictWord{ + 10, + 0, + 959, + }, + dictWord{10, 0, 964}, + dictWord{10, 0, 970}, + dictWord{10, 0, 972}, + dictWord{10, 0, 973}, + dictWord{10, 0, 975}, + dictWord{10, 0, 976}, + dictWord{ + 10, + 0, + 980, + }, + dictWord{10, 0, 981}, + dictWord{10, 0, 984}, + dictWord{10, 0, 988}, + dictWord{10, 0, 990}, + dictWord{10, 0, 995}, + dictWord{10, 0, 999}, + dictWord{ + 10, + 0, + 1002, + }, + dictWord{10, 0, 1003}, + dictWord{10, 0, 1005}, + dictWord{10, 0, 1006}, + dictWord{10, 0, 1008}, + dictWord{10, 0, 1009}, + dictWord{10, 0, 1012}, + dictWord{10, 0, 1014}, + dictWord{10, 0, 1015}, + dictWord{10, 0, 1019}, + dictWord{10, 0, 1020}, + dictWord{10, 0, 1022}, + dictWord{12, 0, 959}, + dictWord{12, 0, 961}, + dictWord{12, 0, 962}, + dictWord{12, 0, 963}, + dictWord{12, 0, 964}, + dictWord{12, 0, 965}, + dictWord{12, 0, 967}, + dictWord{12, 0, 968}, + dictWord{12, 0, 969}, + dictWord{12, 0, 970}, + dictWord{12, 0, 971}, + dictWord{12, 0, 972}, + dictWord{12, 0, 973}, + dictWord{12, 0, 974}, + dictWord{12, 0, 975}, + dictWord{12, 0, 976}, + dictWord{ + 12, + 0, + 977, + }, + dictWord{12, 0, 979}, + dictWord{12, 0, 981}, + dictWord{12, 0, 982}, + dictWord{12, 0, 983}, + dictWord{12, 0, 984}, + dictWord{12, 0, 985}, + dictWord{ + 12, + 0, + 986, + }, + dictWord{12, 0, 987}, + dictWord{12, 0, 989}, + dictWord{12, 0, 990}, + dictWord{12, 0, 992}, + dictWord{12, 0, 993}, + dictWord{12, 0, 995}, + dictWord{12, 0, 998}, + dictWord{12, 0, 999}, + dictWord{12, 0, 1000}, + dictWord{12, 0, 1001}, + dictWord{12, 0, 1002}, + dictWord{12, 0, 1004}, + dictWord{12, 0, 1005}, + dictWord{ + 12, + 0, + 1006, + }, + dictWord{12, 0, 1007}, + dictWord{12, 0, 1008}, + dictWord{12, 0, 1009}, + dictWord{12, 0, 1010}, + dictWord{12, 0, 1011}, + dictWord{12, 0, 1012}, + dictWord{12, 0, 1014}, + dictWord{12, 0, 1015}, + dictWord{12, 0, 1016}, + dictWord{12, 0, 1017}, + dictWord{12, 0, 1018}, + dictWord{12, 0, 1019}, + dictWord{ + 12, + 0, + 1022, + }, + dictWord{12, 0, 1023}, + dictWord{14, 0, 475}, + dictWord{14, 0, 477}, + dictWord{14, 0, 478}, + dictWord{14, 0, 479}, + dictWord{14, 0, 480}, + dictWord{ + 14, + 0, + 482, + }, + dictWord{14, 0, 483}, + dictWord{14, 0, 484}, + dictWord{14, 0, 485}, + dictWord{14, 0, 486}, + dictWord{14, 0, 487}, + dictWord{14, 0, 488}, + dictWord{14, 0, 489}, + dictWord{14, 0, 490}, + dictWord{14, 0, 491}, + dictWord{14, 0, 492}, + dictWord{14, 0, 493}, + dictWord{14, 0, 494}, + dictWord{14, 0, 495}, + dictWord{14, 0, 496}, + dictWord{14, 0, 497}, + dictWord{14, 0, 498}, + dictWord{14, 0, 499}, + dictWord{14, 0, 500}, + dictWord{14, 0, 501}, + dictWord{14, 0, 502}, + dictWord{14, 0, 503}, + dictWord{ + 14, + 0, + 504, + }, + dictWord{14, 0, 506}, + dictWord{14, 0, 507}, + dictWord{14, 0, 508}, + dictWord{14, 0, 509}, + dictWord{14, 0, 510}, + dictWord{14, 0, 511}, + dictWord{ + 16, + 0, + 113, + }, + dictWord{16, 0, 114}, + dictWord{16, 0, 115}, + dictWord{16, 0, 117}, + dictWord{16, 0, 118}, + dictWord{16, 0, 119}, + dictWord{16, 0, 121}, + dictWord{16, 0, 122}, + dictWord{16, 0, 123}, + dictWord{16, 0, 124}, + dictWord{16, 0, 125}, + dictWord{16, 0, 126}, + dictWord{16, 0, 127}, + dictWord{18, 0, 242}, + dictWord{18, 0, 243}, + dictWord{18, 0, 244}, + dictWord{18, 0, 245}, + dictWord{18, 0, 248}, + dictWord{18, 0, 249}, + dictWord{18, 0, 250}, + dictWord{18, 0, 251}, + dictWord{18, 0, 252}, + dictWord{ + 18, + 0, + 253, + }, + dictWord{18, 0, 254}, + dictWord{18, 0, 255}, + dictWord{20, 0, 125}, + dictWord{20, 0, 126}, + dictWord{148, 0, 127}, + dictWord{7, 11, 1717}, + dictWord{ + 7, + 11, + 1769, + }, + dictWord{138, 11, 546}, + dictWord{7, 11, 1127}, + dictWord{7, 11, 1572}, + dictWord{10, 11, 297}, + dictWord{10, 11, 422}, + dictWord{11, 11, 764}, + dictWord{11, 11, 810}, + dictWord{12, 11, 264}, + dictWord{13, 11, 102}, + dictWord{13, 11, 300}, + dictWord{13, 11, 484}, + dictWord{14, 11, 147}, + dictWord{ + 14, + 11, + 229, + }, + dictWord{17, 11, 71}, + dictWord{18, 11, 118}, + dictWord{147, 11, 120}, + dictWord{6, 0, 1148}, + dictWord{134, 0, 1586}, + dictWord{132, 0, 775}, + dictWord{135, 10, 954}, + dictWord{133, 11, 864}, + dictWord{133, 11, 928}, + dictWord{138, 11, 189}, + dictWord{135, 10, 1958}, + dictWord{6, 10, 549}, + dictWord{ + 8, + 10, + 34, + }, + dictWord{8, 10, 283}, + dictWord{9, 10, 165}, + dictWord{138, 10, 475}, + dictWord{5, 10, 652}, + dictWord{5, 10, 701}, + dictWord{135, 10, 449}, + dictWord{135, 11, 695}, + dictWord{4, 10, 655}, + dictWord{7, 10, 850}, + dictWord{17, 10, 75}, + dictWord{146, 10, 137}, + dictWord{140, 11, 682}, + dictWord{ + 133, + 11, + 523, + }, + dictWord{8, 0, 970}, + dictWord{136, 10, 670}, + dictWord{136, 11, 555}, + dictWord{7, 11, 76}, + dictWord{8, 11, 44}, + dictWord{9, 11, 884}, + dictWord{ + 10, + 11, + 580, + }, + dictWord{11, 11, 399}, + dictWord{11, 11, 894}, + dictWord{15, 11, 122}, + dictWord{18, 11, 144}, + dictWord{147, 11, 61}, + dictWord{6, 10, 159}, + dictWord{ + 6, + 10, + 364, + }, + dictWord{7, 10, 516}, + dictWord{7, 10, 1439}, + dictWord{137, 10, 518}, + dictWord{4, 0, 71}, + dictWord{5, 0, 376}, + dictWord{7, 0, 119}, + dictWord{ + 138, + 0, + 665, + }, + dictWord{141, 10, 151}, + dictWord{11, 0, 827}, + dictWord{14, 0, 34}, + dictWord{143, 0, 148}, + dictWord{133, 11, 518}, + dictWord{4, 0, 479}, + dictWord{ + 135, + 11, + 1787, + }, + dictWord{135, 11, 1852}, + dictWord{135, 10, 993}, + dictWord{7, 0, 607}, + dictWord{136, 0, 99}, + dictWord{134, 0, 1960}, + dictWord{132, 0, 793}, + dictWord{4, 0, 41}, + dictWord{5, 0, 74}, + dictWord{7, 0, 1627}, + dictWord{11, 0, 871}, + dictWord{140, 0, 619}, + dictWord{7, 0, 94}, + dictWord{11, 0, 329}, + dictWord{ + 11, + 0, + 965, + }, + dictWord{12, 0, 241}, + dictWord{14, 0, 354}, + dictWord{15, 0, 22}, + dictWord{148, 0, 63}, + dictWord{7, 10, 501}, + dictWord{9, 10, 111}, + dictWord{10, 10, 141}, + dictWord{11, 10, 332}, + dictWord{13, 10, 43}, + dictWord{13, 10, 429}, + dictWord{14, 10, 130}, + dictWord{14, 10, 415}, + dictWord{145, 10, 102}, + dictWord{ + 9, + 0, + 209, + }, + dictWord{137, 0, 300}, + dictWord{134, 0, 1497}, + dictWord{138, 11, 255}, + dictWord{4, 11, 934}, + dictWord{5, 11, 138}, + dictWord{136, 11, 610}, + dictWord{133, 0, 98}, + dictWord{6, 0, 1316}, + dictWord{10, 11, 804}, + dictWord{138, 11, 832}, + dictWord{8, 11, 96}, + dictWord{9, 11, 36}, + dictWord{10, 11, 607}, + dictWord{11, 11, 423}, + dictWord{11, 11, 442}, + dictWord{12, 11, 309}, + dictWord{14, 11, 199}, + dictWord{15, 11, 90}, + dictWord{145, 11, 110}, + dictWord{ + 132, + 0, + 463, + }, + dictWord{5, 10, 149}, + dictWord{136, 10, 233}, + dictWord{133, 10, 935}, + dictWord{4, 11, 652}, + dictWord{8, 11, 320}, + dictWord{9, 11, 13}, + dictWord{ + 9, + 11, + 398, + }, + dictWord{9, 11, 727}, + dictWord{10, 11, 75}, + dictWord{10, 11, 184}, + dictWord{10, 11, 230}, + dictWord{10, 11, 564}, + dictWord{10, 11, 569}, + dictWord{ + 11, + 11, + 973, + }, + dictWord{12, 11, 70}, + dictWord{12, 11, 189}, + dictWord{13, 11, 57}, + dictWord{13, 11, 257}, + dictWord{22, 11, 6}, + dictWord{150, 11, 16}, + dictWord{ + 142, + 0, + 291, + }, + dictWord{12, 10, 582}, + dictWord{146, 10, 131}, + dictWord{136, 10, 801}, + dictWord{133, 0, 984}, + dictWord{145, 11, 116}, + dictWord{4, 11, 692}, + dictWord{133, 11, 321}, + dictWord{4, 0, 182}, + dictWord{6, 0, 205}, + dictWord{135, 0, 220}, + dictWord{4, 0, 42}, + dictWord{9, 0, 205}, + dictWord{9, 0, 786}, + dictWord{ + 138, + 0, + 659, + }, + dictWord{6, 0, 801}, + dictWord{11, 11, 130}, + dictWord{140, 11, 609}, + dictWord{132, 0, 635}, + dictWord{5, 11, 345}, + dictWord{135, 11, 1016}, + dictWord{139, 0, 533}, + dictWord{132, 0, 371}, + dictWord{4, 0, 272}, + dictWord{135, 0, 836}, + dictWord{6, 0, 1282}, + dictWord{135, 11, 1100}, + dictWord{5, 0, 825}, + dictWord{134, 0, 1640}, + dictWord{135, 11, 1325}, + dictWord{133, 11, 673}, + dictWord{4, 11, 287}, + dictWord{133, 11, 1018}, + dictWord{135, 0, 357}, + dictWord{ + 6, + 0, + 467, + }, + dictWord{137, 0, 879}, + dictWord{7, 0, 317}, + dictWord{135, 0, 569}, + dictWord{6, 0, 924}, + dictWord{134, 0, 1588}, + dictWord{5, 11, 34}, + dictWord{ + 5, + 10, + 406, + }, + dictWord{10, 11, 724}, + dictWord{12, 11, 444}, + dictWord{13, 11, 354}, + dictWord{18, 11, 32}, + dictWord{23, 11, 24}, + dictWord{23, 11, 31}, + dictWord{ + 152, + 11, + 5, + }, + dictWord{6, 0, 1795}, + dictWord{6, 0, 1835}, + dictWord{6, 0, 1836}, + dictWord{6, 0, 1856}, + dictWord{8, 0, 844}, + dictWord{8, 0, 849}, + dictWord{8, 0, 854}, + dictWord{8, 0, 870}, + dictWord{8, 0, 887}, + dictWord{10, 0, 852}, + dictWord{138, 0, 942}, + dictWord{6, 10, 69}, + dictWord{135, 10, 117}, + dictWord{137, 0, 307}, + dictWord{ + 4, + 0, + 944, + }, + dictWord{6, 0, 1799}, + dictWord{6, 0, 1825}, + dictWord{10, 0, 848}, + dictWord{10, 0, 875}, + dictWord{10, 0, 895}, + dictWord{10, 0, 899}, + dictWord{ + 10, + 0, + 902, + }, + dictWord{140, 0, 773}, + dictWord{11, 0, 43}, + dictWord{13, 0, 72}, + dictWord{141, 0, 142}, + dictWord{135, 10, 1830}, + dictWord{134, 11, 382}, + dictWord{ + 4, + 10, + 432, + }, + dictWord{135, 10, 824}, + dictWord{132, 11, 329}, + dictWord{7, 0, 1820}, + dictWord{139, 11, 124}, + dictWord{133, 10, 826}, + dictWord{ + 133, + 0, + 525, + }, + dictWord{132, 11, 906}, + dictWord{7, 11, 1940}, + dictWord{136, 11, 366}, + dictWord{138, 11, 10}, + dictWord{4, 11, 123}, + dictWord{4, 11, 649}, + dictWord{ + 5, + 11, + 605, + }, + dictWord{7, 11, 1509}, + dictWord{136, 11, 36}, + dictWord{6, 0, 110}, + dictWord{135, 0, 1681}, + dictWord{133, 0, 493}, + dictWord{133, 11, 767}, + dictWord{4, 0, 174}, + dictWord{135, 0, 911}, + dictWord{138, 11, 786}, + dictWord{8, 0, 417}, + dictWord{137, 0, 782}, + dictWord{133, 10, 1000}, + dictWord{7, 0, 733}, + dictWord{137, 0, 583}, + dictWord{4, 10, 297}, + dictWord{6, 10, 529}, + dictWord{7, 10, 152}, + dictWord{7, 10, 713}, + dictWord{7, 10, 1845}, + dictWord{8, 10, 710}, + dictWord{8, 10, 717}, + dictWord{12, 10, 639}, + dictWord{140, 10, 685}, + dictWord{4, 0, 32}, + dictWord{5, 0, 215}, + dictWord{6, 0, 269}, + dictWord{7, 0, 1782}, + dictWord{ + 7, + 0, + 1892, + }, + dictWord{10, 0, 16}, + dictWord{11, 0, 822}, + dictWord{11, 0, 954}, + dictWord{141, 0, 481}, + dictWord{4, 11, 273}, + dictWord{5, 11, 658}, + dictWord{ + 133, + 11, + 995, + }, + dictWord{136, 0, 477}, + dictWord{134, 11, 72}, + dictWord{135, 11, 1345}, + dictWord{5, 0, 308}, + dictWord{7, 0, 1088}, + dictWord{4, 10, 520}, + dictWord{ + 135, + 10, + 575, + }, + dictWord{133, 11, 589}, + dictWord{5, 0, 126}, + dictWord{8, 0, 297}, + dictWord{9, 0, 366}, + dictWord{140, 0, 374}, + dictWord{7, 0, 1551}, + dictWord{ + 139, + 0, + 361, + }, + dictWord{5, 11, 117}, + dictWord{6, 11, 514}, + dictWord{6, 11, 541}, + dictWord{7, 11, 1164}, + dictWord{7, 11, 1436}, + dictWord{8, 11, 220}, + dictWord{ + 8, + 11, + 648, + }, + dictWord{10, 11, 688}, + dictWord{139, 11, 560}, + dictWord{133, 11, 686}, + dictWord{4, 0, 946}, + dictWord{6, 0, 1807}, + dictWord{8, 0, 871}, + dictWord{ + 10, + 0, + 854, + }, + dictWord{10, 0, 870}, + dictWord{10, 0, 888}, + dictWord{10, 0, 897}, + dictWord{10, 0, 920}, + dictWord{12, 0, 722}, + dictWord{12, 0, 761}, + dictWord{ + 12, + 0, + 763, + }, + dictWord{12, 0, 764}, + dictWord{14, 0, 454}, + dictWord{14, 0, 465}, + dictWord{16, 0, 107}, + dictWord{18, 0, 167}, + dictWord{18, 0, 168}, + dictWord{ + 146, + 0, + 172, + }, + dictWord{132, 0, 175}, + dictWord{135, 0, 1307}, + dictWord{132, 0, 685}, + dictWord{135, 11, 1834}, + dictWord{133, 0, 797}, + dictWord{6, 0, 745}, + dictWord{ + 6, + 0, + 858, + }, + dictWord{134, 0, 963}, + dictWord{133, 0, 565}, + dictWord{5, 10, 397}, + dictWord{6, 10, 154}, + dictWord{7, 11, 196}, + dictWord{7, 10, 676}, + dictWord{ + 8, + 10, + 443, + }, + dictWord{8, 10, 609}, + dictWord{9, 10, 24}, + dictWord{9, 10, 325}, + dictWord{10, 10, 35}, + dictWord{10, 11, 765}, + dictWord{11, 11, 347}, + dictWord{ + 11, + 10, + 535, + }, + dictWord{11, 11, 552}, + dictWord{11, 11, 576}, + dictWord{11, 10, 672}, + dictWord{11, 11, 790}, + dictWord{11, 10, 1018}, + dictWord{12, 11, 263}, + dictWord{12, 10, 637}, + dictWord{13, 11, 246}, + dictWord{13, 11, 270}, + dictWord{13, 11, 395}, + dictWord{14, 11, 74}, + dictWord{14, 11, 176}, + dictWord{ + 14, + 11, + 190, + }, + dictWord{14, 11, 398}, + dictWord{14, 11, 412}, + dictWord{15, 11, 32}, + dictWord{15, 11, 63}, + dictWord{16, 10, 30}, + dictWord{16, 11, 88}, + dictWord{ + 147, + 11, + 105, + }, + dictWord{13, 11, 84}, + dictWord{141, 11, 122}, + dictWord{4, 0, 252}, + dictWord{7, 0, 1068}, + dictWord{10, 0, 434}, + dictWord{11, 0, 228}, + dictWord{ + 11, + 0, + 426, + }, + dictWord{13, 0, 231}, + dictWord{18, 0, 106}, + dictWord{148, 0, 87}, + dictWord{137, 0, 826}, + dictWord{4, 11, 589}, + dictWord{139, 11, 282}, + dictWord{ + 5, + 11, + 381, + }, + dictWord{135, 11, 1792}, + dictWord{132, 0, 791}, + dictWord{5, 0, 231}, + dictWord{10, 0, 509}, + dictWord{133, 10, 981}, + dictWord{7, 0, 601}, + dictWord{ + 9, + 0, + 277, + }, + dictWord{9, 0, 674}, + dictWord{10, 0, 178}, + dictWord{10, 0, 418}, + dictWord{10, 0, 571}, + dictWord{11, 0, 531}, + dictWord{12, 0, 113}, + dictWord{12, 0, 475}, + dictWord{13, 0, 99}, + dictWord{142, 0, 428}, + dictWord{4, 10, 56}, + dictWord{7, 11, 616}, + dictWord{7, 10, 1791}, + dictWord{8, 10, 607}, + dictWord{8, 10, 651}, + dictWord{10, 11, 413}, + dictWord{11, 10, 465}, + dictWord{11, 10, 835}, + dictWord{12, 10, 337}, + dictWord{141, 10, 480}, + dictWord{7, 0, 1591}, + dictWord{144, 0, 43}, + dictWord{9, 10, 158}, + dictWord{138, 10, 411}, + dictWord{135, 0, 1683}, + dictWord{8, 0, 289}, + dictWord{11, 0, 45}, + dictWord{12, 0, 278}, + dictWord{140, 0, 537}, + dictWord{6, 11, 120}, + dictWord{7, 11, 1188}, + dictWord{7, 11, 1710}, + dictWord{8, 11, 286}, + dictWord{9, 11, 667}, + dictWord{11, 11, 592}, + dictWord{ + 139, + 11, + 730, + }, + dictWord{136, 10, 617}, + dictWord{135, 0, 1120}, + dictWord{135, 11, 1146}, + dictWord{139, 10, 563}, + dictWord{4, 11, 352}, + dictWord{4, 10, 369}, + dictWord{135, 11, 687}, + dictWord{143, 11, 38}, + dictWord{4, 0, 399}, + dictWord{5, 0, 119}, + dictWord{5, 0, 494}, + dictWord{7, 0, 751}, + dictWord{9, 0, 556}, + dictWord{ + 14, + 11, + 179, + }, + dictWord{15, 11, 151}, + dictWord{150, 11, 11}, + dictWord{4, 11, 192}, + dictWord{5, 11, 49}, + dictWord{6, 11, 200}, + dictWord{6, 11, 293}, + dictWord{ + 6, + 11, + 1696, + }, + dictWord{135, 11, 488}, + dictWord{4, 0, 398}, + dictWord{133, 0, 660}, + dictWord{7, 0, 1030}, + dictWord{134, 10, 622}, + dictWord{135, 11, 595}, + dictWord{141, 0, 168}, + dictWord{132, 11, 147}, + dictWord{7, 0, 973}, + dictWord{10, 10, 624}, + dictWord{142, 10, 279}, + dictWord{132, 10, 363}, + dictWord{ + 132, + 0, + 642, + }, + dictWord{133, 11, 934}, + dictWord{134, 0, 1615}, + dictWord{7, 11, 505}, + dictWord{135, 11, 523}, + dictWord{7, 0, 594}, + dictWord{7, 0, 851}, + dictWord{ + 7, + 0, + 1858, + }, + dictWord{9, 0, 411}, + dictWord{9, 0, 574}, + dictWord{9, 0, 666}, + dictWord{9, 0, 737}, + dictWord{10, 0, 346}, + dictWord{10, 0, 712}, + dictWord{11, 0, 246}, + dictWord{11, 0, 432}, + dictWord{11, 0, 517}, + dictWord{11, 0, 647}, + dictWord{11, 0, 679}, + dictWord{11, 0, 727}, + dictWord{12, 0, 304}, + dictWord{12, 0, 305}, + dictWord{ + 12, + 0, + 323, + }, + dictWord{12, 0, 483}, + dictWord{12, 0, 572}, + dictWord{12, 0, 593}, + dictWord{12, 0, 602}, + dictWord{13, 0, 95}, + dictWord{13, 0, 101}, + dictWord{ + 13, + 0, + 171, + }, + dictWord{13, 0, 315}, + dictWord{13, 0, 378}, + dictWord{13, 0, 425}, + dictWord{13, 0, 475}, + dictWord{14, 0, 63}, + dictWord{14, 0, 380}, + dictWord{14, 0, 384}, + dictWord{15, 0, 133}, + dictWord{18, 0, 112}, + dictWord{148, 0, 72}, + dictWord{135, 0, 1093}, + dictWord{132, 0, 679}, + dictWord{8, 0, 913}, + dictWord{10, 0, 903}, + dictWord{10, 0, 915}, + dictWord{12, 0, 648}, + dictWord{12, 0, 649}, + dictWord{14, 0, 455}, + dictWord{16, 0, 112}, + dictWord{138, 11, 438}, + dictWord{137, 0, 203}, + dictWord{134, 10, 292}, + dictWord{134, 0, 1492}, + dictWord{7, 0, 1374}, + dictWord{8, 0, 540}, + dictWord{5, 10, 177}, + dictWord{6, 10, 616}, + dictWord{7, 10, 827}, + dictWord{9, 10, 525}, + dictWord{138, 10, 656}, + dictWord{135, 0, 1486}, + dictWord{9, 0, 714}, + dictWord{138, 10, 31}, + dictWord{136, 0, 825}, + dictWord{ + 134, + 0, + 1511, + }, + dictWord{132, 11, 637}, + dictWord{134, 0, 952}, + dictWord{4, 10, 161}, + dictWord{133, 10, 631}, + dictWord{5, 0, 143}, + dictWord{5, 0, 769}, + dictWord{ + 6, + 0, + 1760, + }, + dictWord{7, 0, 682}, + dictWord{7, 0, 1992}, + dictWord{136, 0, 736}, + dictWord{132, 0, 700}, + dictWord{134, 0, 1540}, + dictWord{132, 11, 777}, + dictWord{ + 9, + 11, + 867, + }, + dictWord{138, 11, 837}, + dictWord{7, 0, 1557}, + dictWord{135, 10, 1684}, + dictWord{133, 0, 860}, + dictWord{6, 0, 422}, + dictWord{7, 0, 0}, + dictWord{ + 7, + 0, + 1544, + }, + dictWord{9, 0, 605}, + dictWord{11, 0, 990}, + dictWord{12, 0, 235}, + dictWord{12, 0, 453}, + dictWord{13, 0, 47}, + dictWord{13, 0, 266}, + dictWord{9, 10, 469}, + dictWord{9, 10, 709}, + dictWord{12, 10, 512}, + dictWord{14, 10, 65}, + dictWord{145, 10, 12}, + dictWord{11, 0, 807}, + dictWord{10, 10, 229}, + dictWord{11, 10, 73}, + dictWord{139, 10, 376}, + dictWord{6, 11, 170}, + dictWord{7, 11, 1080}, + dictWord{8, 11, 395}, + dictWord{8, 11, 487}, + dictWord{11, 11, 125}, + dictWord{ + 141, + 11, + 147, + }, + dictWord{5, 0, 515}, + dictWord{137, 0, 131}, + dictWord{7, 0, 1605}, + dictWord{11, 0, 962}, + dictWord{146, 0, 139}, + dictWord{132, 0, 646}, + dictWord{ + 4, + 0, + 396, + }, + dictWord{7, 0, 728}, + dictWord{9, 0, 117}, + dictWord{13, 0, 202}, + dictWord{148, 0, 51}, + dictWord{6, 0, 121}, + dictWord{6, 0, 124}, + dictWord{6, 0, 357}, + dictWord{ + 7, + 0, + 1138, + }, + dictWord{7, 0, 1295}, + dictWord{8, 0, 162}, + dictWord{8, 0, 508}, + dictWord{11, 0, 655}, + dictWord{4, 11, 535}, + dictWord{6, 10, 558}, + dictWord{ + 7, + 10, + 651, + }, + dictWord{8, 11, 618}, + dictWord{9, 10, 0}, + dictWord{10, 10, 34}, + dictWord{139, 10, 1008}, + dictWord{135, 11, 1245}, + dictWord{138, 0, 357}, + dictWord{ + 150, + 11, + 23, + }, + dictWord{133, 0, 237}, + dictWord{135, 0, 1784}, + dictWord{7, 10, 1832}, + dictWord{138, 10, 374}, + dictWord{132, 0, 713}, + dictWord{132, 11, 46}, + dictWord{6, 0, 1536}, + dictWord{10, 0, 348}, + dictWord{5, 11, 811}, + dictWord{6, 11, 1679}, + dictWord{6, 11, 1714}, + dictWord{135, 11, 2032}, + dictWord{ + 11, + 11, + 182, + }, + dictWord{142, 11, 195}, + dictWord{6, 0, 523}, + dictWord{7, 0, 738}, + dictWord{7, 10, 771}, + dictWord{7, 10, 1731}, + dictWord{9, 10, 405}, + dictWord{ + 138, + 10, + 421, + }, + dictWord{7, 11, 1458}, + dictWord{9, 11, 407}, + dictWord{139, 11, 15}, + dictWord{6, 11, 34}, + dictWord{7, 11, 69}, + dictWord{7, 11, 640}, + dictWord{ + 7, + 11, + 1089, + }, + dictWord{8, 11, 708}, + dictWord{8, 11, 721}, + dictWord{9, 11, 363}, + dictWord{9, 11, 643}, + dictWord{10, 11, 628}, + dictWord{148, 11, 98}, + dictWord{ + 133, + 0, + 434, + }, + dictWord{135, 0, 1877}, + dictWord{7, 0, 571}, + dictWord{138, 0, 366}, + dictWord{5, 10, 881}, + dictWord{133, 10, 885}, + dictWord{9, 0, 513}, + dictWord{ + 10, + 0, + 25, + }, + dictWord{10, 0, 39}, + dictWord{12, 0, 122}, + dictWord{140, 0, 187}, + dictWord{132, 0, 580}, + dictWord{5, 10, 142}, + dictWord{134, 10, 546}, + dictWord{ + 132, + 11, + 462, + }, + dictWord{137, 0, 873}, + dictWord{5, 10, 466}, + dictWord{11, 10, 571}, + dictWord{12, 10, 198}, + dictWord{13, 10, 283}, + dictWord{14, 10, 186}, + dictWord{15, 10, 21}, + dictWord{143, 10, 103}, + dictWord{7, 0, 171}, + dictWord{4, 10, 185}, + dictWord{5, 10, 257}, + dictWord{5, 10, 839}, + dictWord{5, 10, 936}, + dictWord{ + 9, + 10, + 399, + }, + dictWord{10, 10, 258}, + dictWord{10, 10, 395}, + dictWord{10, 10, 734}, + dictWord{11, 10, 1014}, + dictWord{12, 10, 23}, + dictWord{13, 10, 350}, + dictWord{14, 10, 150}, + dictWord{147, 10, 6}, + dictWord{134, 0, 625}, + dictWord{7, 0, 107}, + dictWord{7, 0, 838}, + dictWord{8, 0, 550}, + dictWord{138, 0, 401}, + dictWord{ + 5, + 11, + 73, + }, + dictWord{6, 11, 23}, + dictWord{134, 11, 338}, + dictWord{4, 0, 943}, + dictWord{6, 0, 1850}, + dictWord{12, 0, 713}, + dictWord{142, 0, 434}, + dictWord{ + 11, + 0, + 588, + }, + dictWord{11, 0, 864}, + dictWord{11, 0, 936}, + dictWord{11, 0, 968}, + dictWord{12, 0, 73}, + dictWord{12, 0, 343}, + dictWord{12, 0, 394}, + dictWord{13, 0, 275}, + dictWord{14, 0, 257}, + dictWord{15, 0, 160}, + dictWord{7, 10, 404}, + dictWord{7, 10, 1377}, + dictWord{7, 10, 1430}, + dictWord{7, 10, 2017}, + dictWord{8, 10, 149}, + dictWord{8, 10, 239}, + dictWord{8, 10, 512}, + dictWord{8, 10, 793}, + dictWord{8, 10, 818}, + dictWord{9, 10, 474}, + dictWord{9, 10, 595}, + dictWord{10, 10, 122}, + dictWord{10, 10, 565}, + dictWord{10, 10, 649}, + dictWord{10, 10, 783}, + dictWord{11, 10, 239}, + dictWord{11, 10, 295}, + dictWord{11, 10, 447}, + dictWord{ + 11, + 10, + 528, + }, + dictWord{11, 10, 639}, + dictWord{11, 10, 800}, + dictWord{12, 10, 25}, + dictWord{12, 10, 157}, + dictWord{12, 10, 316}, + dictWord{12, 10, 390}, + dictWord{ + 12, + 10, + 391, + }, + dictWord{12, 10, 395}, + dictWord{12, 10, 478}, + dictWord{12, 10, 503}, + dictWord{12, 10, 592}, + dictWord{12, 10, 680}, + dictWord{13, 10, 50}, + dictWord{13, 10, 53}, + dictWord{13, 10, 132}, + dictWord{13, 10, 198}, + dictWord{13, 10, 322}, + dictWord{13, 10, 415}, + dictWord{13, 10, 511}, + dictWord{14, 10, 71}, + dictWord{14, 10, 395}, + dictWord{15, 10, 71}, + dictWord{15, 10, 136}, + dictWord{17, 10, 123}, + dictWord{18, 10, 93}, + dictWord{147, 10, 58}, + dictWord{ + 133, + 0, + 768, + }, + dictWord{11, 0, 103}, + dictWord{142, 0, 0}, + dictWord{136, 10, 712}, + dictWord{132, 0, 799}, + dictWord{132, 0, 894}, + dictWord{7, 11, 725}, + dictWord{ + 8, + 11, + 498, + }, + dictWord{139, 11, 268}, + dictWord{135, 11, 1798}, + dictWord{135, 11, 773}, + dictWord{141, 11, 360}, + dictWord{4, 10, 377}, + dictWord{152, 10, 13}, + dictWord{135, 0, 1673}, + dictWord{132, 11, 583}, + dictWord{134, 0, 1052}, + dictWord{133, 11, 220}, + dictWord{140, 11, 69}, + dictWord{132, 11, 544}, + dictWord{ + 4, + 10, + 180, + }, + dictWord{135, 10, 1906}, + dictWord{134, 0, 272}, + dictWord{4, 0, 441}, + dictWord{134, 0, 1421}, + dictWord{4, 0, 9}, + dictWord{5, 0, 128}, + dictWord{ + 7, + 0, + 368, + }, + dictWord{11, 0, 480}, + dictWord{148, 0, 3}, + dictWord{5, 11, 176}, + dictWord{6, 11, 437}, + dictWord{6, 11, 564}, + dictWord{11, 11, 181}, + dictWord{ + 141, + 11, + 183, + }, + dictWord{132, 10, 491}, + dictWord{7, 0, 1182}, + dictWord{141, 11, 67}, + dictWord{6, 0, 1346}, + dictWord{4, 10, 171}, + dictWord{138, 10, 234}, + dictWord{ + 4, + 10, + 586, + }, + dictWord{7, 10, 1186}, + dictWord{138, 10, 631}, + dictWord{136, 0, 682}, + dictWord{134, 0, 1004}, + dictWord{15, 0, 24}, + dictWord{143, 11, 24}, + dictWord{134, 0, 968}, + dictWord{4, 0, 2}, + dictWord{6, 0, 742}, + dictWord{6, 0, 793}, + dictWord{7, 0, 545}, + dictWord{7, 0, 894}, + dictWord{9, 10, 931}, + dictWord{ + 10, + 10, + 334, + }, + dictWord{148, 10, 71}, + dictWord{136, 11, 600}, + dictWord{133, 10, 765}, + dictWord{9, 0, 769}, + dictWord{140, 0, 185}, + dictWord{4, 11, 790}, + dictWord{ + 5, + 11, + 273, + }, + dictWord{134, 11, 394}, + dictWord{7, 0, 474}, + dictWord{137, 0, 578}, + dictWord{4, 11, 135}, + dictWord{6, 11, 127}, + dictWord{7, 11, 1185}, + dictWord{ + 7, + 11, + 1511, + }, + dictWord{8, 11, 613}, + dictWord{11, 11, 5}, + dictWord{12, 11, 133}, + dictWord{12, 11, 495}, + dictWord{12, 11, 586}, + dictWord{14, 11, 385}, + dictWord{15, 11, 118}, + dictWord{17, 11, 20}, + dictWord{146, 11, 98}, + dictWord{133, 10, 424}, + dictWord{5, 0, 530}, + dictWord{142, 0, 113}, + dictWord{6, 11, 230}, + dictWord{7, 11, 961}, + dictWord{7, 11, 1085}, + dictWord{136, 11, 462}, + dictWord{7, 11, 1954}, + dictWord{137, 11, 636}, + dictWord{136, 10, 714}, + dictWord{ + 149, + 11, + 6, + }, + dictWord{135, 10, 685}, + dictWord{9, 10, 420}, + dictWord{10, 10, 269}, + dictWord{10, 10, 285}, + dictWord{10, 10, 576}, + dictWord{11, 10, 397}, + dictWord{13, 10, 175}, + dictWord{145, 10, 90}, + dictWord{132, 10, 429}, + dictWord{5, 0, 556}, + dictWord{5, 11, 162}, + dictWord{136, 11, 68}, + dictWord{132, 11, 654}, + dictWord{4, 11, 156}, + dictWord{7, 11, 998}, + dictWord{7, 11, 1045}, + dictWord{7, 11, 1860}, + dictWord{9, 11, 48}, + dictWord{9, 11, 692}, + dictWord{11, 11, 419}, + dictWord{139, 11, 602}, + dictWord{6, 0, 1317}, + dictWord{8, 0, 16}, + dictWord{9, 0, 825}, + dictWord{12, 0, 568}, + dictWord{7, 11, 1276}, + dictWord{8, 11, 474}, + dictWord{137, 11, 652}, + dictWord{18, 0, 97}, + dictWord{7, 10, 18}, + dictWord{7, 10, 699}, + dictWord{7, 10, 1966}, + dictWord{8, 10, 752}, + dictWord{9, 10, 273}, + dictWord{ + 9, + 10, + 412, + }, + dictWord{9, 10, 703}, + dictWord{10, 10, 71}, + dictWord{10, 10, 427}, + dictWord{138, 10, 508}, + dictWord{10, 0, 703}, + dictWord{7, 11, 1454}, + dictWord{138, 11, 703}, + dictWord{4, 10, 53}, + dictWord{5, 10, 186}, + dictWord{135, 10, 752}, + dictWord{134, 0, 892}, + dictWord{134, 0, 1571}, + dictWord{8, 10, 575}, + dictWord{10, 10, 289}, + dictWord{139, 10, 319}, + dictWord{6, 0, 186}, + dictWord{137, 0, 426}, + dictWord{134, 0, 1101}, + dictWord{132, 10, 675}, + dictWord{ + 132, + 0, + 585, + }, + dictWord{6, 0, 1870}, + dictWord{137, 0, 937}, + dictWord{152, 11, 10}, + dictWord{9, 11, 197}, + dictWord{10, 11, 300}, + dictWord{12, 11, 473}, + dictWord{ + 13, + 11, + 90, + }, + dictWord{141, 11, 405}, + dictWord{4, 0, 93}, + dictWord{5, 0, 252}, + dictWord{6, 0, 229}, + dictWord{7, 0, 291}, + dictWord{9, 0, 550}, + dictWord{139, 0, 644}, + dictWord{137, 0, 749}, + dictWord{9, 0, 162}, + dictWord{6, 10, 209}, + dictWord{8, 10, 468}, + dictWord{9, 10, 210}, + dictWord{11, 10, 36}, + dictWord{12, 10, 28}, + dictWord{12, 10, 630}, + dictWord{13, 10, 21}, + dictWord{13, 10, 349}, + dictWord{14, 10, 7}, + dictWord{145, 10, 13}, + dictWord{132, 0, 381}, + dictWord{132, 11, 606}, + dictWord{4, 10, 342}, + dictWord{135, 10, 1179}, + dictWord{7, 11, 1587}, + dictWord{7, 11, 1707}, + dictWord{10, 11, 528}, + dictWord{139, 11, 504}, + dictWord{ + 12, + 11, + 39, + }, + dictWord{13, 11, 265}, + dictWord{141, 11, 439}, + dictWord{4, 10, 928}, + dictWord{133, 10, 910}, + dictWord{7, 10, 1838}, + dictWord{7, 11, 1978}, + dictWord{136, 11, 676}, + dictWord{6, 0, 762}, + dictWord{6, 0, 796}, + dictWord{134, 0, 956}, + dictWord{4, 10, 318}, + dictWord{4, 10, 496}, + dictWord{7, 10, 856}, + dictWord{139, 10, 654}, + dictWord{137, 11, 242}, + dictWord{4, 11, 361}, + dictWord{133, 11, 315}, + dictWord{132, 11, 461}, + dictWord{132, 11, 472}, + dictWord{ + 132, + 0, + 857, + }, + dictWord{5, 0, 21}, + dictWord{6, 0, 77}, + dictWord{6, 0, 157}, + dictWord{7, 0, 974}, + dictWord{7, 0, 1301}, + dictWord{7, 0, 1339}, + dictWord{7, 0, 1490}, + dictWord{ + 7, + 0, + 1873, + }, + dictWord{9, 0, 628}, + dictWord{7, 10, 915}, + dictWord{8, 10, 247}, + dictWord{147, 10, 0}, + dictWord{4, 10, 202}, + dictWord{5, 10, 382}, + dictWord{ + 6, + 10, + 454, + }, + dictWord{7, 10, 936}, + dictWord{7, 10, 1803}, + dictWord{8, 10, 758}, + dictWord{9, 10, 375}, + dictWord{9, 10, 895}, + dictWord{10, 10, 743}, + dictWord{ + 10, + 10, + 792, + }, + dictWord{11, 10, 978}, + dictWord{11, 10, 1012}, + dictWord{142, 10, 109}, + dictWord{7, 11, 617}, + dictWord{10, 11, 498}, + dictWord{11, 11, 501}, + dictWord{12, 11, 16}, + dictWord{140, 11, 150}, + dictWord{7, 10, 1150}, + dictWord{7, 10, 1425}, + dictWord{7, 10, 1453}, + dictWord{10, 11, 747}, + dictWord{ + 140, + 10, + 513, + }, + dictWord{133, 11, 155}, + dictWord{11, 0, 919}, + dictWord{141, 0, 409}, + dictWord{138, 10, 791}, + dictWord{10, 0, 633}, + dictWord{139, 11, 729}, + dictWord{ + 7, + 11, + 163, + }, + dictWord{8, 11, 319}, + dictWord{9, 11, 402}, + dictWord{10, 11, 24}, + dictWord{10, 11, 681}, + dictWord{11, 11, 200}, + dictWord{11, 11, 567}, + dictWord{12, 11, 253}, + dictWord{12, 11, 410}, + dictWord{142, 11, 219}, + dictWord{5, 11, 475}, + dictWord{7, 11, 1780}, + dictWord{9, 11, 230}, + dictWord{11, 11, 297}, + dictWord{11, 11, 558}, + dictWord{14, 11, 322}, + dictWord{147, 11, 76}, + dictWord{7, 0, 332}, + dictWord{6, 10, 445}, + dictWord{137, 10, 909}, + dictWord{ + 135, + 11, + 1956, + }, + dictWord{136, 11, 274}, + dictWord{134, 10, 578}, + dictWord{135, 0, 1489}, + dictWord{135, 11, 1848}, + dictWord{5, 11, 944}, + dictWord{ + 134, + 11, + 1769, + }, + dictWord{132, 11, 144}, + dictWord{136, 10, 766}, + dictWord{4, 0, 832}, + dictWord{135, 10, 541}, + dictWord{8, 0, 398}, + dictWord{9, 0, 681}, + dictWord{ + 139, + 0, + 632, + }, + dictWord{136, 0, 645}, + dictWord{9, 0, 791}, + dictWord{10, 0, 93}, + dictWord{16, 0, 13}, + dictWord{17, 0, 23}, + dictWord{18, 0, 135}, + dictWord{19, 0, 12}, + dictWord{20, 0, 1}, + dictWord{20, 0, 12}, + dictWord{148, 0, 14}, + dictWord{6, 11, 247}, + dictWord{137, 11, 555}, + dictWord{134, 0, 20}, + dictWord{132, 0, 800}, + dictWord{135, 0, 1841}, + dictWord{139, 10, 983}, + dictWord{137, 10, 768}, + dictWord{132, 10, 584}, + dictWord{141, 11, 51}, + dictWord{6, 0, 1993}, + dictWord{ + 4, + 11, + 620, + }, + dictWord{138, 11, 280}, + dictWord{136, 0, 769}, + dictWord{11, 0, 290}, + dictWord{11, 0, 665}, + dictWord{7, 11, 1810}, + dictWord{11, 11, 866}, + dictWord{ + 12, + 11, + 103, + }, + dictWord{13, 11, 495}, + dictWord{17, 11, 67}, + dictWord{147, 11, 74}, + dictWord{134, 0, 1426}, + dictWord{139, 0, 60}, + dictWord{4, 10, 326}, + dictWord{135, 10, 1770}, + dictWord{7, 0, 1874}, + dictWord{9, 0, 641}, + dictWord{132, 10, 226}, + dictWord{6, 0, 644}, + dictWord{5, 10, 426}, + dictWord{8, 10, 30}, + dictWord{ + 9, + 10, + 2, + }, + dictWord{11, 10, 549}, + dictWord{147, 10, 122}, + dictWord{5, 11, 428}, + dictWord{138, 11, 442}, + dictWord{135, 11, 1871}, + dictWord{ + 135, + 0, + 1757, + }, + dictWord{147, 10, 117}, + dictWord{135, 0, 937}, + dictWord{135, 0, 1652}, + dictWord{6, 0, 654}, + dictWord{134, 0, 1476}, + dictWord{133, 11, 99}, + dictWord{135, 0, 527}, + dictWord{132, 10, 345}, + dictWord{4, 10, 385}, + dictWord{4, 11, 397}, + dictWord{7, 10, 265}, + dictWord{135, 10, 587}, + dictWord{4, 0, 579}, + dictWord{5, 0, 226}, + dictWord{5, 0, 323}, + dictWord{135, 0, 960}, + dictWord{134, 0, 1486}, + dictWord{8, 11, 502}, + dictWord{144, 11, 9}, + dictWord{4, 10, 347}, + dictWord{ + 5, + 10, + 423, + }, + dictWord{5, 10, 996}, + dictWord{135, 10, 1329}, + dictWord{7, 11, 727}, + dictWord{146, 11, 73}, + dictWord{4, 11, 485}, + dictWord{7, 11, 353}, + dictWord{7, 10, 1259}, + dictWord{7, 11, 1523}, + dictWord{9, 10, 125}, + dictWord{139, 10, 65}, + dictWord{6, 0, 325}, + dictWord{5, 10, 136}, + dictWord{6, 11, 366}, + dictWord{ + 7, + 11, + 1384, + }, + dictWord{7, 11, 1601}, + dictWord{136, 10, 644}, + dictWord{138, 11, 160}, + dictWord{6, 0, 1345}, + dictWord{137, 11, 282}, + dictWord{18, 0, 91}, + dictWord{147, 0, 70}, + dictWord{136, 0, 404}, + dictWord{4, 11, 157}, + dictWord{133, 11, 471}, + dictWord{133, 0, 973}, + dictWord{6, 0, 135}, + dictWord{ + 135, + 0, + 1176, + }, + dictWord{8, 11, 116}, + dictWord{11, 11, 551}, + dictWord{142, 11, 159}, + dictWord{4, 0, 549}, + dictWord{4, 10, 433}, + dictWord{133, 10, 719}, + dictWord{ + 136, + 0, + 976, + }, + dictWord{5, 11, 160}, + dictWord{7, 11, 363}, + dictWord{7, 11, 589}, + dictWord{10, 11, 170}, + dictWord{141, 11, 55}, + dictWord{144, 0, 21}, + dictWord{ + 144, + 0, + 51, + }, + dictWord{135, 0, 314}, + dictWord{135, 10, 1363}, + dictWord{4, 11, 108}, + dictWord{7, 11, 405}, + dictWord{10, 11, 491}, + dictWord{139, 11, 498}, + dictWord{146, 0, 4}, + dictWord{4, 10, 555}, + dictWord{8, 10, 536}, + dictWord{10, 10, 288}, + dictWord{139, 10, 1005}, + dictWord{135, 11, 1005}, + dictWord{6, 0, 281}, + dictWord{7, 0, 6}, + dictWord{8, 0, 282}, + dictWord{8, 0, 480}, + dictWord{8, 0, 499}, + dictWord{9, 0, 198}, + dictWord{10, 0, 143}, + dictWord{10, 0, 169}, + dictWord{ + 10, + 0, + 211, + }, + dictWord{10, 0, 417}, + dictWord{10, 0, 574}, + dictWord{11, 0, 147}, + dictWord{11, 0, 395}, + dictWord{12, 0, 75}, + dictWord{12, 0, 407}, + dictWord{12, 0, 608}, + dictWord{13, 0, 500}, + dictWord{142, 0, 251}, + dictWord{6, 0, 1093}, + dictWord{6, 0, 1405}, + dictWord{9, 10, 370}, + dictWord{138, 10, 90}, + dictWord{4, 11, 926}, + dictWord{133, 11, 983}, + dictWord{135, 0, 1776}, + dictWord{134, 0, 1528}, + dictWord{132, 0, 419}, + dictWord{132, 11, 538}, + dictWord{6, 11, 294}, + dictWord{ + 7, + 11, + 1267, + }, + dictWord{136, 11, 624}, + dictWord{135, 11, 1772}, + dictWord{138, 11, 301}, + dictWord{4, 10, 257}, + dictWord{135, 10, 2031}, + dictWord{4, 0, 138}, + dictWord{7, 0, 1012}, + dictWord{7, 0, 1280}, + dictWord{9, 0, 76}, + dictWord{135, 10, 1768}, + dictWord{132, 11, 757}, + dictWord{5, 0, 29}, + dictWord{140, 0, 638}, + dictWord{7, 11, 655}, + dictWord{135, 11, 1844}, + dictWord{7, 0, 1418}, + dictWord{6, 11, 257}, + dictWord{135, 11, 1522}, + dictWord{8, 11, 469}, + dictWord{ + 138, + 11, + 47, + }, + dictWord{142, 11, 278}, + dictWord{6, 10, 83}, + dictWord{6, 10, 1733}, + dictWord{135, 10, 1389}, + dictWord{11, 11, 204}, + dictWord{11, 11, 243}, + dictWord{140, 11, 293}, + dictWord{135, 11, 1875}, + dictWord{6, 0, 1710}, + dictWord{135, 0, 2038}, + dictWord{137, 11, 299}, + dictWord{4, 0, 17}, + dictWord{5, 0, 23}, + dictWord{7, 0, 995}, + dictWord{11, 0, 383}, + dictWord{11, 0, 437}, + dictWord{12, 0, 460}, + dictWord{140, 0, 532}, + dictWord{133, 0, 862}, + dictWord{137, 10, 696}, + dictWord{6, 0, 592}, + dictWord{138, 0, 946}, + dictWord{138, 11, 599}, + dictWord{7, 10, 1718}, + dictWord{9, 10, 95}, + dictWord{9, 10, 274}, + dictWord{10, 10, 279}, + dictWord{10, 10, 317}, + dictWord{10, 10, 420}, + dictWord{11, 10, 303}, + dictWord{11, 10, 808}, + dictWord{12, 10, 134}, + dictWord{12, 10, 367}, + dictWord{ + 13, + 10, + 149, + }, + dictWord{13, 10, 347}, + dictWord{14, 10, 349}, + dictWord{14, 10, 406}, + dictWord{18, 10, 22}, + dictWord{18, 10, 89}, + dictWord{18, 10, 122}, + dictWord{ + 147, + 10, + 47, + }, + dictWord{8, 0, 70}, + dictWord{12, 0, 171}, + dictWord{141, 0, 272}, + dictWord{133, 10, 26}, + dictWord{132, 10, 550}, + dictWord{137, 0, 812}, + dictWord{ + 10, + 0, + 233, + }, + dictWord{139, 0, 76}, + dictWord{134, 0, 988}, + dictWord{134, 0, 442}, + dictWord{136, 10, 822}, + dictWord{7, 0, 896}, + dictWord{4, 10, 902}, + dictWord{ + 5, + 10, + 809, + }, + dictWord{134, 10, 122}, + dictWord{5, 11, 150}, + dictWord{7, 11, 106}, + dictWord{8, 11, 603}, + dictWord{9, 11, 593}, + dictWord{9, 11, 634}, + dictWord{ + 10, + 11, + 44, + }, + dictWord{10, 11, 173}, + dictWord{11, 11, 462}, + dictWord{11, 11, 515}, + dictWord{13, 11, 216}, + dictWord{13, 11, 288}, + dictWord{142, 11, 400}, + dictWord{136, 0, 483}, + dictWord{135, 10, 262}, + dictWord{6, 0, 1709}, + dictWord{133, 10, 620}, + dictWord{4, 10, 34}, + dictWord{5, 10, 574}, + dictWord{7, 10, 279}, + dictWord{7, 10, 1624}, + dictWord{136, 10, 601}, + dictWord{137, 10, 170}, + dictWord{147, 0, 119}, + dictWord{12, 11, 108}, + dictWord{141, 11, 291}, + dictWord{ + 11, + 0, + 69, + }, + dictWord{12, 0, 105}, + dictWord{12, 0, 117}, + dictWord{13, 0, 213}, + dictWord{14, 0, 13}, + dictWord{14, 0, 62}, + dictWord{14, 0, 177}, + dictWord{14, 0, 421}, + dictWord{15, 0, 19}, + dictWord{146, 0, 141}, + dictWord{137, 0, 309}, + dictWord{11, 11, 278}, + dictWord{142, 11, 73}, + dictWord{7, 0, 608}, + dictWord{7, 0, 976}, + dictWord{9, 0, 146}, + dictWord{10, 0, 206}, + dictWord{10, 0, 596}, + dictWord{13, 0, 218}, + dictWord{142, 0, 153}, + dictWord{133, 10, 332}, + dictWord{6, 10, 261}, + dictWord{ + 8, + 10, + 182, + }, + dictWord{139, 10, 943}, + dictWord{4, 11, 493}, + dictWord{144, 11, 55}, + dictWord{134, 10, 1721}, + dictWord{132, 0, 768}, + dictWord{4, 10, 933}, + dictWord{133, 10, 880}, + dictWord{7, 11, 555}, + dictWord{7, 11, 1316}, + dictWord{7, 11, 1412}, + dictWord{7, 11, 1839}, + dictWord{9, 11, 192}, + dictWord{ + 9, + 11, + 589, + }, + dictWord{11, 11, 241}, + dictWord{11, 11, 676}, + dictWord{11, 11, 811}, + dictWord{11, 11, 891}, + dictWord{12, 11, 140}, + dictWord{12, 11, 346}, + dictWord{ + 12, + 11, + 479, + }, + dictWord{13, 11, 30}, + dictWord{13, 11, 49}, + dictWord{13, 11, 381}, + dictWord{14, 11, 188}, + dictWord{15, 11, 150}, + dictWord{16, 11, 76}, + dictWord{18, 11, 30}, + dictWord{148, 11, 52}, + dictWord{4, 0, 518}, + dictWord{135, 0, 1136}, + dictWord{6, 11, 568}, + dictWord{7, 11, 112}, + dictWord{7, 11, 1804}, + dictWord{8, 11, 362}, + dictWord{8, 11, 410}, + dictWord{8, 11, 830}, + dictWord{9, 11, 514}, + dictWord{11, 11, 649}, + dictWord{142, 11, 157}, + dictWord{135, 11, 673}, + dictWord{8, 0, 689}, + dictWord{137, 0, 863}, + dictWord{4, 0, 18}, + dictWord{7, 0, 145}, + dictWord{7, 0, 444}, + dictWord{7, 0, 1278}, + dictWord{8, 0, 49}, + dictWord{8, 0, 400}, + dictWord{9, 0, 71}, + dictWord{9, 0, 250}, + dictWord{10, 0, 459}, + dictWord{12, 0, 160}, + dictWord{16, 0, 24}, + dictWord{132, 11, 625}, + dictWord{140, 0, 1020}, + dictWord{4, 0, 997}, + dictWord{6, 0, 1946}, + dictWord{6, 0, 1984}, + dictWord{134, 0, 1998}, + dictWord{6, 11, 16}, + dictWord{6, 11, 158}, + dictWord{7, 11, 43}, + dictWord{ + 7, + 11, + 129, + }, + dictWord{7, 11, 181}, + dictWord{8, 11, 276}, + dictWord{8, 11, 377}, + dictWord{10, 11, 523}, + dictWord{11, 11, 816}, + dictWord{12, 11, 455}, + dictWord{ + 13, + 11, + 303, + }, + dictWord{142, 11, 135}, + dictWord{133, 10, 812}, + dictWord{134, 0, 658}, + dictWord{4, 11, 1}, + dictWord{7, 11, 1143}, + dictWord{7, 11, 1463}, + dictWord{8, 11, 61}, + dictWord{9, 11, 207}, + dictWord{9, 11, 390}, + dictWord{9, 11, 467}, + dictWord{139, 11, 836}, + dictWord{150, 11, 26}, + dictWord{140, 0, 106}, + dictWord{6, 0, 1827}, + dictWord{10, 0, 931}, + dictWord{18, 0, 166}, + dictWord{20, 0, 114}, + dictWord{4, 10, 137}, + dictWord{7, 10, 1178}, + dictWord{7, 11, 1319}, + dictWord{135, 10, 1520}, + dictWord{133, 0, 1010}, + dictWord{4, 11, 723}, + dictWord{5, 11, 895}, + dictWord{7, 11, 1031}, + dictWord{8, 11, 199}, + dictWord{8, 11, 340}, + dictWord{9, 11, 153}, + dictWord{9, 11, 215}, + dictWord{10, 11, 21}, + dictWord{10, 11, 59}, + dictWord{10, 11, 80}, + dictWord{10, 11, 224}, + dictWord{11, 11, 229}, + dictWord{11, 11, 652}, + dictWord{12, 11, 192}, + dictWord{13, 11, 146}, + dictWord{142, 11, 91}, + dictWord{132, 11, 295}, + dictWord{6, 11, 619}, + dictWord{ + 7, + 11, + 898, + }, + dictWord{7, 11, 1092}, + dictWord{8, 11, 485}, + dictWord{18, 11, 28}, + dictWord{147, 11, 116}, + dictWord{137, 11, 51}, + dictWord{6, 10, 1661}, + dictWord{ + 7, + 10, + 1975, + }, + dictWord{7, 10, 2009}, + dictWord{135, 10, 2011}, + dictWord{5, 11, 309}, + dictWord{140, 11, 211}, + dictWord{5, 0, 87}, + dictWord{7, 0, 313}, + dictWord{ + 7, + 0, + 1103, + }, + dictWord{10, 0, 208}, + dictWord{10, 0, 582}, + dictWord{11, 0, 389}, + dictWord{11, 0, 813}, + dictWord{12, 0, 385}, + dictWord{13, 0, 286}, + dictWord{ + 14, + 0, + 124, + }, + dictWord{146, 0, 108}, + dictWord{5, 11, 125}, + dictWord{8, 11, 77}, + dictWord{138, 11, 15}, + dictWord{132, 0, 267}, + dictWord{133, 0, 703}, + dictWord{ + 137, + 11, + 155, + }, + dictWord{133, 11, 439}, + dictWord{11, 11, 164}, + dictWord{140, 11, 76}, + dictWord{9, 0, 496}, + dictWord{5, 10, 89}, + dictWord{7, 10, 1915}, + dictWord{ + 9, + 10, + 185, + }, + dictWord{9, 10, 235}, + dictWord{10, 10, 64}, + dictWord{10, 10, 270}, + dictWord{10, 10, 403}, + dictWord{10, 10, 469}, + dictWord{10, 10, 529}, + dictWord{10, 10, 590}, + dictWord{11, 10, 140}, + dictWord{11, 10, 860}, + dictWord{13, 10, 1}, + dictWord{13, 10, 422}, + dictWord{14, 10, 341}, + dictWord{14, 10, 364}, + dictWord{17, 10, 93}, + dictWord{18, 10, 113}, + dictWord{19, 10, 97}, + dictWord{147, 10, 113}, + dictWord{133, 10, 695}, + dictWord{135, 0, 1121}, + dictWord{ + 5, + 10, + 6, + }, + dictWord{6, 10, 183}, + dictWord{7, 10, 680}, + dictWord{7, 10, 978}, + dictWord{7, 10, 1013}, + dictWord{7, 10, 1055}, + dictWord{12, 10, 230}, + dictWord{ + 13, + 10, + 172, + }, + dictWord{146, 10, 29}, + dictWord{4, 11, 8}, + dictWord{7, 11, 1152}, + dictWord{7, 11, 1153}, + dictWord{7, 11, 1715}, + dictWord{9, 11, 374}, + dictWord{ + 10, + 11, + 478, + }, + dictWord{139, 11, 648}, + dictWord{135, 11, 1099}, + dictWord{6, 10, 29}, + dictWord{139, 10, 63}, + dictWord{4, 0, 561}, + dictWord{10, 0, 249}, + dictWord{ + 139, + 0, + 209, + }, + dictWord{132, 0, 760}, + dictWord{7, 11, 799}, + dictWord{138, 11, 511}, + dictWord{136, 11, 87}, + dictWord{9, 0, 154}, + dictWord{140, 0, 485}, + dictWord{136, 0, 255}, + dictWord{132, 0, 323}, + dictWord{140, 0, 419}, + dictWord{132, 10, 311}, + dictWord{134, 10, 1740}, + dictWord{4, 0, 368}, + dictWord{ + 135, + 0, + 641, + }, + dictWord{7, 10, 170}, + dictWord{8, 10, 90}, + dictWord{8, 10, 177}, + dictWord{8, 10, 415}, + dictWord{11, 10, 714}, + dictWord{142, 10, 281}, + dictWord{ + 4, + 11, + 69, + }, + dictWord{5, 11, 122}, + dictWord{9, 11, 656}, + dictWord{138, 11, 464}, + dictWord{5, 11, 849}, + dictWord{134, 11, 1633}, + dictWord{8, 0, 522}, + dictWord{ + 142, + 0, + 328, + }, + dictWord{11, 10, 91}, + dictWord{13, 10, 129}, + dictWord{15, 10, 101}, + dictWord{145, 10, 125}, + dictWord{7, 0, 562}, + dictWord{8, 0, 551}, + dictWord{ + 4, + 10, + 494, + }, + dictWord{6, 10, 74}, + dictWord{7, 10, 44}, + dictWord{11, 11, 499}, + dictWord{12, 10, 17}, + dictWord{15, 10, 5}, + dictWord{148, 10, 11}, + dictWord{4, 10, 276}, + dictWord{133, 10, 296}, + dictWord{9, 0, 92}, + dictWord{147, 0, 91}, + dictWord{4, 10, 7}, + dictWord{5, 10, 90}, + dictWord{5, 10, 158}, + dictWord{6, 10, 542}, + dictWord{ + 7, + 10, + 221, + }, + dictWord{7, 10, 1574}, + dictWord{9, 10, 490}, + dictWord{10, 10, 540}, + dictWord{11, 10, 443}, + dictWord{139, 10, 757}, + dictWord{6, 0, 525}, + dictWord{ + 6, + 0, + 1976, + }, + dictWord{8, 0, 806}, + dictWord{9, 0, 876}, + dictWord{140, 0, 284}, + dictWord{5, 11, 859}, + dictWord{7, 10, 588}, + dictWord{7, 11, 1160}, + dictWord{ + 8, + 11, + 107, + }, + dictWord{9, 10, 175}, + dictWord{9, 11, 291}, + dictWord{9, 11, 439}, + dictWord{10, 10, 530}, + dictWord{10, 11, 663}, + dictWord{11, 11, 609}, + dictWord{ + 140, + 11, + 197, + }, + dictWord{7, 11, 168}, + dictWord{13, 11, 196}, + dictWord{141, 11, 237}, + dictWord{139, 0, 958}, + dictWord{133, 0, 594}, + dictWord{135, 10, 580}, + dictWord{7, 10, 88}, + dictWord{136, 10, 627}, + dictWord{6, 0, 479}, + dictWord{6, 0, 562}, + dictWord{7, 0, 1060}, + dictWord{13, 0, 6}, + dictWord{5, 10, 872}, + dictWord{ + 6, + 10, + 57, + }, + dictWord{7, 10, 471}, + dictWord{9, 10, 447}, + dictWord{137, 10, 454}, + dictWord{136, 11, 413}, + dictWord{145, 11, 19}, + dictWord{4, 11, 117}, + dictWord{ + 6, + 11, + 372, + }, + dictWord{7, 11, 1905}, + dictWord{142, 11, 323}, + dictWord{4, 11, 722}, + dictWord{139, 11, 471}, + dictWord{17, 0, 61}, + dictWord{5, 10, 31}, + dictWord{134, 10, 614}, + dictWord{8, 10, 330}, + dictWord{140, 10, 477}, + dictWord{7, 10, 1200}, + dictWord{138, 10, 460}, + dictWord{6, 10, 424}, + dictWord{ + 135, + 10, + 1866, + }, + dictWord{6, 0, 1641}, + dictWord{136, 0, 820}, + dictWord{6, 0, 1556}, + dictWord{134, 0, 1618}, + dictWord{9, 11, 5}, + dictWord{12, 11, 216}, + dictWord{ + 12, + 11, + 294, + }, + dictWord{12, 11, 298}, + dictWord{12, 11, 400}, + dictWord{12, 11, 518}, + dictWord{13, 11, 229}, + dictWord{143, 11, 139}, + dictWord{15, 11, 155}, + dictWord{144, 11, 79}, + dictWord{4, 0, 302}, + dictWord{135, 0, 1766}, + dictWord{5, 10, 13}, + dictWord{134, 10, 142}, + dictWord{6, 0, 148}, + dictWord{7, 0, 1313}, + dictWord{ + 7, + 10, + 116, + }, + dictWord{8, 10, 322}, + dictWord{8, 10, 755}, + dictWord{9, 10, 548}, + dictWord{10, 10, 714}, + dictWord{11, 10, 884}, + dictWord{141, 10, 324}, + dictWord{137, 0, 676}, + dictWord{9, 11, 88}, + dictWord{139, 11, 270}, + dictWord{5, 11, 12}, + dictWord{7, 11, 375}, + dictWord{137, 11, 438}, + dictWord{134, 0, 1674}, + dictWord{7, 10, 1472}, + dictWord{135, 10, 1554}, + dictWord{11, 0, 178}, + dictWord{7, 10, 1071}, + dictWord{7, 10, 1541}, + dictWord{7, 10, 1767}, + dictWord{ + 7, + 10, + 1806, + }, + dictWord{11, 10, 162}, + dictWord{11, 10, 242}, + dictWord{12, 10, 605}, + dictWord{15, 10, 26}, + dictWord{144, 10, 44}, + dictWord{6, 0, 389}, + dictWord{ + 7, + 0, + 149, + }, + dictWord{9, 0, 142}, + dictWord{138, 0, 94}, + dictWord{140, 11, 71}, + dictWord{145, 10, 115}, + dictWord{6, 0, 8}, + dictWord{7, 0, 1881}, + dictWord{8, 0, 91}, + dictWord{11, 11, 966}, + dictWord{12, 11, 287}, + dictWord{13, 11, 342}, + dictWord{13, 11, 402}, + dictWord{15, 11, 110}, + dictWord{143, 11, 163}, + dictWord{ + 4, + 11, + 258, + }, + dictWord{136, 11, 639}, + dictWord{6, 11, 22}, + dictWord{7, 11, 903}, + dictWord{138, 11, 577}, + dictWord{133, 11, 681}, + dictWord{135, 10, 1111}, + dictWord{135, 11, 1286}, + dictWord{9, 0, 112}, + dictWord{8, 10, 1}, + dictWord{138, 10, 326}, + dictWord{5, 10, 488}, + dictWord{6, 10, 527}, + dictWord{7, 10, 489}, + dictWord{ + 7, + 10, + 1636, + }, + dictWord{8, 10, 121}, + dictWord{8, 10, 144}, + dictWord{8, 10, 359}, + dictWord{9, 10, 193}, + dictWord{9, 10, 241}, + dictWord{9, 10, 336}, + dictWord{ + 9, + 10, + 882, + }, + dictWord{11, 10, 266}, + dictWord{11, 10, 372}, + dictWord{11, 10, 944}, + dictWord{12, 10, 401}, + dictWord{140, 10, 641}, + dictWord{4, 11, 664}, + dictWord{133, 11, 804}, + dictWord{6, 0, 747}, + dictWord{134, 0, 1015}, + dictWord{135, 0, 1746}, + dictWord{9, 10, 31}, + dictWord{10, 10, 244}, + dictWord{ + 10, + 10, + 699, + }, + dictWord{12, 10, 149}, + dictWord{141, 10, 497}, + dictWord{133, 10, 377}, + dictWord{135, 0, 24}, + dictWord{6, 0, 1352}, + dictWord{5, 11, 32}, + dictWord{ + 145, + 10, + 101, + }, + dictWord{7, 0, 1530}, + dictWord{10, 0, 158}, + dictWord{13, 0, 13}, + dictWord{13, 0, 137}, + dictWord{13, 0, 258}, + dictWord{14, 0, 111}, + dictWord{ + 14, + 0, + 225, + }, + dictWord{14, 0, 253}, + dictWord{14, 0, 304}, + dictWord{14, 0, 339}, + dictWord{14, 0, 417}, + dictWord{146, 0, 33}, + dictWord{4, 0, 503}, + dictWord{ + 135, + 0, + 1661, + }, + dictWord{5, 0, 130}, + dictWord{6, 0, 845}, + dictWord{7, 0, 1314}, + dictWord{9, 0, 610}, + dictWord{10, 0, 718}, + dictWord{11, 0, 601}, + dictWord{11, 0, 819}, + dictWord{11, 0, 946}, + dictWord{140, 0, 536}, + dictWord{10, 0, 149}, + dictWord{11, 0, 280}, + dictWord{142, 0, 336}, + dictWord{134, 0, 1401}, + dictWord{ + 135, + 0, + 1946, + }, + dictWord{8, 0, 663}, + dictWord{144, 0, 8}, + dictWord{134, 0, 1607}, + dictWord{135, 10, 2023}, + dictWord{4, 11, 289}, + dictWord{7, 11, 629}, + dictWord{ + 7, + 11, + 1698, + }, + dictWord{7, 11, 1711}, + dictWord{140, 11, 215}, + dictWord{6, 11, 450}, + dictWord{136, 11, 109}, + dictWord{10, 0, 882}, + dictWord{10, 0, 883}, + dictWord{10, 0, 914}, + dictWord{138, 0, 928}, + dictWord{133, 10, 843}, + dictWord{136, 11, 705}, + dictWord{132, 10, 554}, + dictWord{133, 10, 536}, + dictWord{ + 5, + 0, + 417, + }, + dictWord{9, 10, 79}, + dictWord{11, 10, 625}, + dictWord{145, 10, 7}, + dictWord{7, 11, 1238}, + dictWord{142, 11, 37}, + dictWord{4, 0, 392}, + dictWord{ + 135, + 0, + 1597, + }, + dictWord{5, 0, 433}, + dictWord{9, 0, 633}, + dictWord{11, 0, 629}, + dictWord{132, 10, 424}, + dictWord{7, 10, 336}, + dictWord{136, 10, 785}, + dictWord{ + 134, + 11, + 355, + }, + dictWord{6, 0, 234}, + dictWord{7, 0, 769}, + dictWord{9, 0, 18}, + dictWord{138, 0, 358}, + dictWord{4, 10, 896}, + dictWord{134, 10, 1777}, + dictWord{ + 138, + 11, + 323, + }, + dictWord{7, 0, 140}, + dictWord{7, 0, 1950}, + dictWord{8, 0, 680}, + dictWord{11, 0, 817}, + dictWord{147, 0, 88}, + dictWord{7, 0, 1222}, + dictWord{ + 138, + 0, + 386, + }, + dictWord{139, 11, 908}, + dictWord{11, 0, 249}, + dictWord{12, 0, 313}, + dictWord{16, 0, 66}, + dictWord{145, 0, 26}, + dictWord{134, 0, 5}, + dictWord{7, 10, 750}, + dictWord{9, 10, 223}, + dictWord{11, 10, 27}, + dictWord{11, 10, 466}, + dictWord{12, 10, 624}, + dictWord{14, 10, 265}, + dictWord{146, 10, 61}, + dictWord{ + 134, + 11, + 26, + }, + dictWord{134, 0, 1216}, + dictWord{5, 0, 963}, + dictWord{134, 0, 1773}, + dictWord{4, 11, 414}, + dictWord{5, 11, 467}, + dictWord{9, 11, 654}, + dictWord{ + 10, + 11, + 451, + }, + dictWord{12, 11, 59}, + dictWord{141, 11, 375}, + dictWord{135, 11, 17}, + dictWord{4, 10, 603}, + dictWord{133, 10, 661}, + dictWord{4, 10, 11}, + dictWord{ + 6, + 10, + 128, + }, + dictWord{7, 10, 231}, + dictWord{7, 10, 1533}, + dictWord{138, 10, 725}, + dictWord{135, 11, 955}, + dictWord{7, 0, 180}, + dictWord{8, 0, 509}, + dictWord{ + 136, + 0, + 792, + }, + dictWord{132, 10, 476}, + dictWord{132, 0, 1002}, + dictWord{133, 11, 538}, + dictWord{135, 10, 1807}, + dictWord{132, 0, 931}, + dictWord{7, 0, 943}, + dictWord{11, 0, 614}, + dictWord{140, 0, 747}, + dictWord{135, 0, 1837}, + dictWord{9, 10, 20}, + dictWord{10, 10, 324}, + dictWord{10, 10, 807}, + dictWord{ + 139, + 10, + 488, + }, + dictWord{134, 0, 641}, + dictWord{6, 11, 280}, + dictWord{10, 11, 502}, + dictWord{11, 11, 344}, + dictWord{140, 11, 38}, + dictWord{5, 11, 45}, + dictWord{ + 7, + 11, + 1161, + }, + dictWord{11, 11, 448}, + dictWord{11, 11, 880}, + dictWord{13, 11, 139}, + dictWord{13, 11, 407}, + dictWord{15, 11, 16}, + dictWord{17, 11, 95}, + dictWord{ + 18, + 11, + 66, + }, + dictWord{18, 11, 88}, + dictWord{18, 11, 123}, + dictWord{149, 11, 7}, + dictWord{9, 0, 280}, + dictWord{138, 0, 134}, + dictWord{22, 0, 22}, + dictWord{23, 0, 5}, + dictWord{151, 0, 29}, + dictWord{136, 11, 777}, + dictWord{4, 0, 90}, + dictWord{5, 0, 545}, + dictWord{7, 0, 754}, + dictWord{9, 0, 186}, + dictWord{10, 0, 72}, + dictWord{ + 10, + 0, + 782, + }, + dictWord{11, 0, 577}, + dictWord{11, 0, 610}, + dictWord{11, 0, 960}, + dictWord{12, 0, 354}, + dictWord{12, 0, 362}, + dictWord{12, 0, 595}, + dictWord{ + 4, + 11, + 410, + }, + dictWord{135, 11, 521}, + dictWord{135, 11, 1778}, + dictWord{5, 10, 112}, + dictWord{6, 10, 103}, + dictWord{134, 10, 150}, + dictWord{138, 10, 356}, + dictWord{132, 0, 742}, + dictWord{7, 0, 151}, + dictWord{9, 0, 329}, + dictWord{139, 0, 254}, + dictWord{8, 0, 853}, + dictWord{8, 0, 881}, + dictWord{8, 0, 911}, + dictWord{ + 8, + 0, + 912, + }, + dictWord{10, 0, 872}, + dictWord{12, 0, 741}, + dictWord{12, 0, 742}, + dictWord{152, 0, 18}, + dictWord{4, 11, 573}, + dictWord{136, 11, 655}, + dictWord{ + 6, + 0, + 921, + }, + dictWord{134, 0, 934}, + dictWord{9, 0, 187}, + dictWord{10, 0, 36}, + dictWord{11, 0, 1016}, + dictWord{17, 0, 44}, + dictWord{146, 0, 64}, + dictWord{7, 0, 833}, + dictWord{136, 0, 517}, + dictWord{4, 0, 506}, + dictWord{5, 0, 295}, + dictWord{135, 0, 1680}, + dictWord{4, 10, 708}, + dictWord{8, 10, 15}, + dictWord{9, 10, 50}, + dictWord{ + 9, + 10, + 386, + }, + dictWord{11, 10, 18}, + dictWord{11, 10, 529}, + dictWord{140, 10, 228}, + dictWord{7, 0, 251}, + dictWord{7, 0, 1701}, + dictWord{8, 0, 436}, + dictWord{ + 4, + 10, + 563, + }, + dictWord{7, 10, 592}, + dictWord{7, 10, 637}, + dictWord{7, 10, 770}, + dictWord{8, 10, 463}, + dictWord{9, 10, 60}, + dictWord{9, 10, 335}, + dictWord{9, 10, 904}, + dictWord{10, 10, 73}, + dictWord{11, 10, 434}, + dictWord{12, 10, 585}, + dictWord{13, 10, 331}, + dictWord{18, 10, 110}, + dictWord{148, 10, 60}, + dictWord{ + 132, + 10, + 502, + }, + dictWord{136, 0, 584}, + dictWord{6, 10, 347}, + dictWord{138, 10, 161}, + dictWord{7, 0, 987}, + dictWord{9, 0, 688}, + dictWord{10, 0, 522}, + dictWord{ + 11, + 0, + 788, + }, + dictWord{12, 0, 137}, + dictWord{12, 0, 566}, + dictWord{14, 0, 9}, + dictWord{14, 0, 24}, + dictWord{14, 0, 64}, + dictWord{7, 11, 899}, + dictWord{142, 11, 325}, + dictWord{4, 0, 214}, + dictWord{5, 0, 500}, + dictWord{5, 10, 102}, + dictWord{6, 10, 284}, + dictWord{7, 10, 1079}, + dictWord{7, 10, 1423}, + dictWord{7, 10, 1702}, + dictWord{ + 8, + 10, + 470, + }, + dictWord{9, 10, 554}, + dictWord{9, 10, 723}, + dictWord{139, 10, 333}, + dictWord{7, 10, 246}, + dictWord{135, 10, 840}, + dictWord{6, 10, 10}, + dictWord{ + 8, + 10, + 571, + }, + dictWord{9, 10, 739}, + dictWord{143, 10, 91}, + dictWord{133, 10, 626}, + dictWord{146, 0, 195}, + dictWord{134, 0, 1775}, + dictWord{7, 0, 389}, + dictWord{7, 0, 700}, + dictWord{7, 0, 940}, + dictWord{8, 0, 514}, + dictWord{9, 0, 116}, + dictWord{9, 0, 535}, + dictWord{10, 0, 118}, + dictWord{11, 0, 107}, + dictWord{ + 11, + 0, + 148, + }, + dictWord{11, 0, 922}, + dictWord{12, 0, 254}, + dictWord{12, 0, 421}, + dictWord{142, 0, 238}, + dictWord{5, 10, 18}, + dictWord{6, 10, 526}, + dictWord{13, 10, 24}, + dictWord{13, 10, 110}, + dictWord{19, 10, 5}, + dictWord{147, 10, 44}, + dictWord{132, 0, 743}, + dictWord{11, 0, 292}, + dictWord{4, 10, 309}, + dictWord{5, 10, 462}, + dictWord{7, 10, 970}, + dictWord{135, 10, 1097}, + dictWord{22, 10, 30}, + dictWord{150, 10, 33}, + dictWord{139, 11, 338}, + dictWord{135, 11, 1598}, + dictWord{ + 7, + 0, + 1283, + }, + dictWord{9, 0, 227}, + dictWord{11, 0, 325}, + dictWord{11, 0, 408}, + dictWord{14, 0, 180}, + dictWord{146, 0, 47}, + dictWord{4, 0, 953}, + dictWord{6, 0, 1805}, + dictWord{6, 0, 1814}, + dictWord{6, 0, 1862}, + dictWord{140, 0, 774}, + dictWord{6, 11, 611}, + dictWord{135, 11, 1733}, + dictWord{135, 11, 1464}, + dictWord{ + 5, + 0, + 81, + }, + dictWord{7, 0, 146}, + dictWord{7, 0, 1342}, + dictWord{8, 0, 53}, + dictWord{8, 0, 561}, + dictWord{8, 0, 694}, + dictWord{8, 0, 754}, + dictWord{9, 0, 115}, + dictWord{ + 9, + 0, + 179, + }, + dictWord{9, 0, 894}, + dictWord{10, 0, 462}, + dictWord{10, 0, 813}, + dictWord{11, 0, 230}, + dictWord{11, 0, 657}, + dictWord{11, 0, 699}, + dictWord{11, 0, 748}, + dictWord{12, 0, 119}, + dictWord{12, 0, 200}, + dictWord{12, 0, 283}, + dictWord{142, 0, 273}, + dictWord{5, 0, 408}, + dictWord{6, 0, 789}, + dictWord{6, 0, 877}, + dictWord{ + 6, + 0, + 1253, + }, + dictWord{6, 0, 1413}, + dictWord{137, 0, 747}, + dictWord{134, 10, 1704}, + dictWord{135, 11, 663}, + dictWord{6, 0, 1910}, + dictWord{6, 0, 1915}, + dictWord{6, 0, 1923}, + dictWord{9, 0, 913}, + dictWord{9, 0, 928}, + dictWord{9, 0, 950}, + dictWord{9, 0, 954}, + dictWord{9, 0, 978}, + dictWord{9, 0, 993}, + dictWord{12, 0, 812}, + dictWord{12, 0, 819}, + dictWord{12, 0, 831}, + dictWord{12, 0, 833}, + dictWord{12, 0, 838}, + dictWord{12, 0, 909}, + dictWord{12, 0, 928}, + dictWord{12, 0, 931}, + dictWord{12, 0, 950}, + dictWord{15, 0, 186}, + dictWord{15, 0, 187}, + dictWord{15, 0, 195}, + dictWord{15, 0, 196}, + dictWord{15, 0, 209}, + dictWord{15, 0, 215}, + dictWord{ + 15, + 0, + 236, + }, + dictWord{15, 0, 241}, + dictWord{15, 0, 249}, + dictWord{15, 0, 253}, + dictWord{18, 0, 180}, + dictWord{18, 0, 221}, + dictWord{18, 0, 224}, + dictWord{ + 18, + 0, + 227, + }, + dictWord{18, 0, 229}, + dictWord{149, 0, 60}, + dictWord{7, 0, 1826}, + dictWord{135, 0, 1938}, + dictWord{11, 0, 490}, + dictWord{18, 0, 143}, + dictWord{ + 5, + 10, + 86, + }, + dictWord{7, 10, 743}, + dictWord{9, 10, 85}, + dictWord{10, 10, 281}, + dictWord{10, 10, 432}, + dictWord{12, 10, 251}, + dictWord{13, 10, 118}, + dictWord{ + 142, + 10, + 378, + }, + dictWord{5, 10, 524}, + dictWord{133, 10, 744}, + dictWord{141, 11, 442}, + dictWord{10, 10, 107}, + dictWord{140, 10, 436}, + dictWord{135, 11, 503}, + dictWord{134, 0, 1162}, + dictWord{132, 10, 927}, + dictWord{7, 0, 30}, + dictWord{8, 0, 86}, + dictWord{8, 0, 315}, + dictWord{8, 0, 700}, + dictWord{9, 0, 576}, + dictWord{ + 9, + 0, + 858, + }, + dictWord{10, 0, 414}, + dictWord{11, 0, 310}, + dictWord{11, 0, 888}, + dictWord{11, 0, 904}, + dictWord{12, 0, 361}, + dictWord{13, 0, 248}, + dictWord{13, 0, 371}, + dictWord{14, 0, 142}, + dictWord{12, 10, 670}, + dictWord{146, 10, 94}, + dictWord{134, 0, 721}, + dictWord{4, 11, 113}, + dictWord{5, 11, 163}, + dictWord{5, 11, 735}, + dictWord{7, 11, 1009}, + dictWord{7, 10, 1149}, + dictWord{9, 11, 9}, + dictWord{9, 10, 156}, + dictWord{9, 11, 771}, + dictWord{12, 11, 90}, + dictWord{13, 11, 138}, + dictWord{13, 11, 410}, + dictWord{143, 11, 128}, + dictWord{138, 0, 839}, + dictWord{133, 10, 778}, + dictWord{137, 0, 617}, + dictWord{133, 10, 502}, + dictWord{ + 8, + 10, + 196, + }, + dictWord{10, 10, 283}, + dictWord{139, 10, 406}, + dictWord{6, 0, 428}, + dictWord{7, 0, 524}, + dictWord{8, 0, 169}, + dictWord{8, 0, 234}, + dictWord{9, 0, 480}, + dictWord{138, 0, 646}, + dictWord{133, 10, 855}, + dictWord{134, 0, 1648}, + dictWord{7, 0, 1205}, + dictWord{138, 0, 637}, + dictWord{7, 0, 1596}, + dictWord{ + 4, + 11, + 935, + }, + dictWord{133, 11, 823}, + dictWord{5, 11, 269}, + dictWord{7, 11, 434}, + dictWord{7, 11, 891}, + dictWord{8, 11, 339}, + dictWord{9, 11, 702}, + dictWord{ + 11, + 11, + 594, + }, + dictWord{11, 11, 718}, + dictWord{145, 11, 100}, + dictWord{7, 11, 878}, + dictWord{9, 11, 485}, + dictWord{141, 11, 264}, + dictWord{4, 0, 266}, + dictWord{ + 8, + 0, + 4, + }, + dictWord{9, 0, 39}, + dictWord{10, 0, 166}, + dictWord{11, 0, 918}, + dictWord{12, 0, 635}, + dictWord{20, 0, 10}, + dictWord{22, 0, 27}, + dictWord{22, 0, 43}, + dictWord{ + 22, + 0, + 52, + }, + dictWord{134, 11, 1713}, + dictWord{7, 10, 1400}, + dictWord{9, 10, 446}, + dictWord{138, 10, 45}, + dictWord{135, 11, 900}, + dictWord{132, 0, 862}, + dictWord{134, 0, 1554}, + dictWord{135, 11, 1033}, + dictWord{19, 0, 16}, + dictWord{147, 11, 16}, + dictWord{135, 11, 1208}, + dictWord{7, 0, 157}, + dictWord{ + 136, + 0, + 279, + }, + dictWord{6, 0, 604}, + dictWord{136, 0, 391}, + dictWord{13, 10, 455}, + dictWord{15, 10, 99}, + dictWord{15, 10, 129}, + dictWord{144, 10, 68}, + dictWord{ + 135, + 10, + 172, + }, + dictWord{7, 0, 945}, + dictWord{11, 0, 713}, + dictWord{139, 0, 744}, + dictWord{4, 0, 973}, + dictWord{10, 0, 877}, + dictWord{10, 0, 937}, + dictWord{ + 10, + 0, + 938, + }, + dictWord{140, 0, 711}, + dictWord{139, 0, 1022}, + dictWord{132, 10, 568}, + dictWord{142, 11, 143}, + dictWord{4, 0, 567}, + dictWord{9, 0, 859}, + dictWord{ + 132, + 10, + 732, + }, + dictWord{7, 0, 1846}, + dictWord{136, 0, 628}, + dictWord{136, 10, 733}, + dictWord{133, 0, 762}, + dictWord{4, 10, 428}, + dictWord{135, 10, 1789}, + dictWord{10, 0, 784}, + dictWord{13, 0, 191}, + dictWord{7, 10, 2015}, + dictWord{140, 10, 665}, + dictWord{133, 0, 298}, + dictWord{7, 0, 633}, + dictWord{7, 0, 905}, + dictWord{7, 0, 909}, + dictWord{7, 0, 1538}, + dictWord{9, 0, 767}, + dictWord{140, 0, 636}, + dictWord{138, 10, 806}, + dictWord{132, 0, 795}, + dictWord{139, 0, 301}, + dictWord{135, 0, 1970}, + dictWord{5, 11, 625}, + dictWord{135, 11, 1617}, + dictWord{135, 11, 275}, + dictWord{7, 11, 37}, + dictWord{8, 11, 425}, + dictWord{ + 8, + 11, + 693, + }, + dictWord{9, 11, 720}, + dictWord{10, 11, 380}, + dictWord{10, 11, 638}, + dictWord{11, 11, 273}, + dictWord{11, 11, 307}, + dictWord{11, 11, 473}, + dictWord{ + 12, + 11, + 61, + }, + dictWord{143, 11, 43}, + dictWord{135, 11, 198}, + dictWord{134, 0, 1236}, + dictWord{7, 0, 369}, + dictWord{12, 0, 644}, + dictWord{12, 0, 645}, + dictWord{144, 0, 90}, + dictWord{19, 0, 15}, + dictWord{149, 0, 27}, + dictWord{6, 0, 71}, + dictWord{7, 0, 845}, + dictWord{8, 0, 160}, + dictWord{9, 0, 318}, + dictWord{6, 10, 1623}, + dictWord{134, 10, 1681}, + dictWord{134, 0, 1447}, + dictWord{134, 0, 1255}, + dictWord{138, 0, 735}, + dictWord{8, 0, 76}, + dictWord{132, 11, 168}, + dictWord{ + 6, + 10, + 1748, + }, + dictWord{8, 10, 715}, + dictWord{9, 10, 802}, + dictWord{10, 10, 46}, + dictWord{10, 10, 819}, + dictWord{13, 10, 308}, + dictWord{14, 10, 351}, + dictWord{14, 10, 363}, + dictWord{146, 10, 67}, + dictWord{135, 11, 91}, + dictWord{6, 0, 474}, + dictWord{4, 10, 63}, + dictWord{133, 10, 347}, + dictWord{133, 10, 749}, + dictWord{138, 0, 841}, + dictWord{133, 10, 366}, + dictWord{6, 0, 836}, + dictWord{132, 11, 225}, + dictWord{135, 0, 1622}, + dictWord{135, 10, 89}, + dictWord{ + 140, + 0, + 735, + }, + dictWord{134, 0, 1601}, + dictWord{138, 11, 145}, + dictWord{6, 0, 1390}, + dictWord{137, 0, 804}, + dictWord{142, 0, 394}, + dictWord{6, 11, 15}, + dictWord{ + 7, + 11, + 70, + }, + dictWord{10, 11, 240}, + dictWord{147, 11, 93}, + dictWord{6, 0, 96}, + dictWord{135, 0, 1426}, + dictWord{4, 0, 651}, + dictWord{133, 0, 289}, + dictWord{ + 7, + 11, + 956, + }, + dictWord{7, 10, 977}, + dictWord{7, 11, 1157}, + dictWord{7, 11, 1506}, + dictWord{7, 11, 1606}, + dictWord{7, 11, 1615}, + dictWord{7, 11, 1619}, + dictWord{ + 7, + 11, + 1736, + }, + dictWord{7, 11, 1775}, + dictWord{8, 11, 590}, + dictWord{9, 11, 324}, + dictWord{9, 11, 736}, + dictWord{9, 11, 774}, + dictWord{9, 11, 776}, + dictWord{ + 9, + 11, + 784, + }, + dictWord{10, 11, 567}, + dictWord{10, 11, 708}, + dictWord{11, 11, 518}, + dictWord{11, 11, 613}, + dictWord{11, 11, 695}, + dictWord{11, 11, 716}, + dictWord{11, 11, 739}, + dictWord{11, 11, 770}, + dictWord{11, 11, 771}, + dictWord{11, 11, 848}, + dictWord{11, 11, 857}, + dictWord{11, 11, 931}, + dictWord{ + 11, + 11, + 947, + }, + dictWord{12, 11, 326}, + dictWord{12, 11, 387}, + dictWord{12, 11, 484}, + dictWord{12, 11, 528}, + dictWord{12, 11, 552}, + dictWord{12, 11, 613}, + dictWord{ + 13, + 11, + 189, + }, + dictWord{13, 11, 256}, + dictWord{13, 11, 340}, + dictWord{13, 11, 432}, + dictWord{13, 11, 436}, + dictWord{13, 11, 440}, + dictWord{13, 11, 454}, + dictWord{14, 11, 174}, + dictWord{14, 11, 220}, + dictWord{14, 11, 284}, + dictWord{14, 11, 390}, + dictWord{145, 11, 121}, + dictWord{7, 0, 688}, + dictWord{8, 0, 35}, + dictWord{9, 0, 511}, + dictWord{10, 0, 767}, + dictWord{147, 0, 118}, + dictWord{134, 0, 667}, + dictWord{4, 0, 513}, + dictWord{5, 10, 824}, + dictWord{133, 10, 941}, + dictWord{7, 10, 440}, + dictWord{8, 10, 230}, + dictWord{139, 10, 106}, + dictWord{134, 0, 2034}, + dictWord{135, 11, 1399}, + dictWord{143, 11, 66}, + dictWord{ + 135, + 11, + 1529, + }, + dictWord{4, 11, 145}, + dictWord{6, 11, 176}, + dictWord{7, 11, 395}, + dictWord{9, 11, 562}, + dictWord{144, 11, 28}, + dictWord{132, 11, 501}, + dictWord{132, 0, 704}, + dictWord{134, 0, 1524}, + dictWord{7, 0, 1078}, + dictWord{134, 11, 464}, + dictWord{6, 11, 509}, + dictWord{10, 11, 82}, + dictWord{20, 11, 91}, + dictWord{151, 11, 13}, + dictWord{4, 0, 720}, + dictWord{133, 0, 306}, + dictWord{133, 0, 431}, + dictWord{7, 0, 1196}, + dictWord{4, 10, 914}, + dictWord{5, 10, 800}, + dictWord{133, 10, 852}, + dictWord{135, 11, 1189}, + dictWord{10, 0, 54}, + dictWord{141, 10, 115}, + dictWord{7, 10, 564}, + dictWord{142, 10, 168}, + dictWord{ + 5, + 0, + 464, + }, + dictWord{6, 0, 236}, + dictWord{7, 0, 696}, + dictWord{7, 0, 914}, + dictWord{7, 0, 1108}, + dictWord{7, 0, 1448}, + dictWord{9, 0, 15}, + dictWord{9, 0, 564}, + dictWord{ + 10, + 0, + 14, + }, + dictWord{12, 0, 565}, + dictWord{13, 0, 449}, + dictWord{14, 0, 53}, + dictWord{15, 0, 13}, + dictWord{16, 0, 64}, + dictWord{17, 0, 41}, + dictWord{4, 10, 918}, + dictWord{133, 10, 876}, + dictWord{6, 0, 1418}, + dictWord{134, 10, 1764}, + dictWord{4, 10, 92}, + dictWord{133, 10, 274}, + dictWord{134, 0, 907}, + dictWord{ + 4, + 11, + 114, + }, + dictWord{8, 10, 501}, + dictWord{9, 11, 492}, + dictWord{13, 11, 462}, + dictWord{142, 11, 215}, + dictWord{4, 11, 77}, + dictWord{5, 11, 361}, + dictWord{ + 6, + 11, + 139, + }, + dictWord{6, 11, 401}, + dictWord{6, 11, 404}, + dictWord{7, 11, 413}, + dictWord{7, 11, 715}, + dictWord{7, 11, 1716}, + dictWord{11, 11, 279}, + dictWord{ + 12, + 11, + 179, + }, + dictWord{12, 11, 258}, + dictWord{13, 11, 244}, + dictWord{142, 11, 358}, + dictWord{6, 0, 1767}, + dictWord{12, 0, 194}, + dictWord{145, 0, 107}, + dictWord{ + 134, + 11, + 1717, + }, + dictWord{5, 10, 743}, + dictWord{142, 11, 329}, + dictWord{4, 10, 49}, + dictWord{7, 10, 280}, + dictWord{135, 10, 1633}, + dictWord{5, 0, 840}, + dictWord{7, 11, 1061}, + dictWord{8, 11, 82}, + dictWord{11, 11, 250}, + dictWord{12, 11, 420}, + dictWord{141, 11, 184}, + dictWord{135, 11, 724}, + dictWord{ + 134, + 0, + 900, + }, + dictWord{136, 10, 47}, + dictWord{134, 0, 1436}, + dictWord{144, 11, 0}, + dictWord{6, 0, 675}, + dictWord{7, 0, 1008}, + dictWord{7, 0, 1560}, + dictWord{ + 9, + 0, + 642, + }, + dictWord{11, 0, 236}, + dictWord{14, 0, 193}, + dictWord{5, 10, 272}, + dictWord{5, 10, 908}, + dictWord{5, 10, 942}, + dictWord{8, 10, 197}, + dictWord{9, 10, 47}, + dictWord{11, 10, 538}, + dictWord{139, 10, 742}, + dictWord{4, 0, 68}, + dictWord{5, 0, 628}, + dictWord{5, 0, 634}, + dictWord{6, 0, 386}, + dictWord{7, 0, 794}, + dictWord{ + 8, + 0, + 273, + }, + dictWord{9, 0, 563}, + dictWord{10, 0, 105}, + dictWord{10, 0, 171}, + dictWord{11, 0, 94}, + dictWord{139, 0, 354}, + dictWord{135, 10, 1911}, + dictWord{ + 137, + 10, + 891, + }, + dictWord{4, 0, 95}, + dictWord{6, 0, 1297}, + dictWord{6, 0, 1604}, + dictWord{7, 0, 416}, + dictWord{139, 0, 830}, + dictWord{6, 11, 513}, + dictWord{ + 135, + 11, + 1052, + }, + dictWord{7, 0, 731}, + dictWord{13, 0, 20}, + dictWord{143, 0, 11}, + dictWord{137, 11, 899}, + dictWord{10, 0, 850}, + dictWord{140, 0, 697}, + dictWord{ + 4, + 0, + 662, + }, + dictWord{7, 11, 1417}, + dictWord{12, 11, 382}, + dictWord{17, 11, 48}, + dictWord{152, 11, 12}, + dictWord{133, 0, 736}, + dictWord{132, 0, 861}, + dictWord{ + 4, + 10, + 407, + }, + dictWord{132, 10, 560}, + dictWord{141, 10, 490}, + dictWord{6, 11, 545}, + dictWord{7, 11, 565}, + dictWord{7, 11, 1669}, + dictWord{10, 11, 114}, + dictWord{11, 11, 642}, + dictWord{140, 11, 618}, + dictWord{6, 0, 871}, + dictWord{134, 0, 1000}, + dictWord{5, 0, 864}, + dictWord{10, 0, 648}, + dictWord{11, 0, 671}, + dictWord{15, 0, 46}, + dictWord{133, 11, 5}, + dictWord{133, 0, 928}, + dictWord{11, 0, 90}, + dictWord{13, 0, 7}, + dictWord{4, 10, 475}, + dictWord{11, 10, 35}, + dictWord{ + 13, + 10, + 71, + }, + dictWord{13, 10, 177}, + dictWord{142, 10, 422}, + dictWord{136, 0, 332}, + dictWord{135, 11, 192}, + dictWord{134, 0, 1055}, + dictWord{136, 11, 763}, + dictWord{11, 0, 986}, + dictWord{140, 0, 682}, + dictWord{7, 0, 76}, + dictWord{8, 0, 44}, + dictWord{9, 0, 884}, + dictWord{10, 0, 580}, + dictWord{11, 0, 399}, + dictWord{ + 11, + 0, + 894, + }, + dictWord{143, 0, 122}, + dictWord{135, 11, 1237}, + dictWord{135, 10, 636}, + dictWord{11, 0, 300}, + dictWord{6, 10, 222}, + dictWord{7, 10, 1620}, + dictWord{ + 8, + 10, + 409, + }, + dictWord{137, 10, 693}, + dictWord{4, 11, 87}, + dictWord{5, 11, 250}, + dictWord{10, 11, 601}, + dictWord{13, 11, 298}, + dictWord{13, 11, 353}, + dictWord{141, 11, 376}, + dictWord{5, 0, 518}, + dictWord{10, 0, 340}, + dictWord{11, 0, 175}, + dictWord{149, 0, 16}, + dictWord{140, 0, 771}, + dictWord{6, 0, 1108}, + dictWord{137, 0, 831}, + dictWord{132, 0, 836}, + dictWord{135, 0, 1852}, + dictWord{4, 0, 957}, + dictWord{6, 0, 1804}, + dictWord{8, 0, 842}, + dictWord{8, 0, 843}, + dictWord{ + 8, + 0, + 851, + }, + dictWord{8, 0, 855}, + dictWord{140, 0, 767}, + dictWord{135, 11, 814}, + dictWord{4, 11, 57}, + dictWord{7, 11, 1195}, + dictWord{7, 11, 1438}, + dictWord{ + 7, + 11, + 1548, + }, + dictWord{7, 11, 1835}, + dictWord{7, 11, 1904}, + dictWord{9, 11, 757}, + dictWord{10, 11, 604}, + dictWord{139, 11, 519}, + dictWord{133, 10, 882}, + dictWord{138, 0, 246}, + dictWord{4, 0, 934}, + dictWord{5, 0, 202}, + dictWord{8, 0, 610}, + dictWord{7, 11, 1897}, + dictWord{12, 11, 290}, + dictWord{13, 11, 80}, + dictWord{13, 11, 437}, + dictWord{145, 11, 74}, + dictWord{8, 0, 96}, + dictWord{9, 0, 36}, + dictWord{10, 0, 607}, + dictWord{10, 0, 804}, + dictWord{10, 0, 832}, + dictWord{ + 11, + 0, + 423, + }, + dictWord{11, 0, 442}, + dictWord{12, 0, 309}, + dictWord{14, 0, 199}, + dictWord{15, 0, 90}, + dictWord{145, 0, 110}, + dictWord{132, 10, 426}, + dictWord{ + 7, + 0, + 654, + }, + dictWord{8, 0, 240}, + dictWord{6, 10, 58}, + dictWord{7, 10, 745}, + dictWord{7, 10, 1969}, + dictWord{8, 10, 675}, + dictWord{9, 10, 479}, + dictWord{9, 10, 731}, + dictWord{10, 10, 330}, + dictWord{10, 10, 593}, + dictWord{10, 10, 817}, + dictWord{11, 10, 32}, + dictWord{11, 10, 133}, + dictWord{11, 10, 221}, + dictWord{ + 145, + 10, + 68, + }, + dictWord{9, 0, 13}, + dictWord{9, 0, 398}, + dictWord{9, 0, 727}, + dictWord{10, 0, 75}, + dictWord{10, 0, 184}, + dictWord{10, 0, 230}, + dictWord{10, 0, 564}, + dictWord{ + 10, + 0, + 569, + }, + dictWord{11, 0, 973}, + dictWord{12, 0, 70}, + dictWord{12, 0, 189}, + dictWord{13, 0, 57}, + dictWord{141, 0, 257}, + dictWord{4, 11, 209}, + dictWord{ + 135, + 11, + 902, + }, + dictWord{7, 0, 391}, + dictWord{137, 10, 538}, + dictWord{134, 0, 403}, + dictWord{6, 11, 303}, + dictWord{7, 11, 335}, + dictWord{7, 11, 1437}, + dictWord{ + 7, + 11, + 1668, + }, + dictWord{8, 11, 553}, + dictWord{8, 11, 652}, + dictWord{8, 11, 656}, + dictWord{9, 11, 558}, + dictWord{11, 11, 743}, + dictWord{149, 11, 18}, + dictWord{ + 132, + 11, + 559, + }, + dictWord{11, 0, 75}, + dictWord{142, 0, 267}, + dictWord{6, 0, 815}, + dictWord{141, 11, 2}, + dictWord{141, 0, 366}, + dictWord{137, 0, 631}, + dictWord{ + 133, + 11, + 1017, + }, + dictWord{5, 0, 345}, + dictWord{135, 0, 1016}, + dictWord{133, 11, 709}, + dictWord{134, 11, 1745}, + dictWord{133, 10, 566}, + dictWord{7, 0, 952}, + dictWord{6, 10, 48}, + dictWord{9, 10, 139}, + dictWord{10, 10, 399}, + dictWord{11, 10, 469}, + dictWord{12, 10, 634}, + dictWord{141, 10, 223}, + dictWord{ + 133, + 0, + 673, + }, + dictWord{9, 0, 850}, + dictWord{7, 11, 8}, + dictWord{136, 11, 206}, + dictWord{6, 0, 662}, + dictWord{149, 0, 35}, + dictWord{4, 0, 287}, + dictWord{133, 0, 1018}, + dictWord{6, 10, 114}, + dictWord{7, 10, 1224}, + dictWord{7, 10, 1556}, + dictWord{136, 10, 3}, + dictWord{8, 10, 576}, + dictWord{137, 10, 267}, + dictWord{4, 0, 884}, + dictWord{5, 0, 34}, + dictWord{10, 0, 724}, + dictWord{12, 0, 444}, + dictWord{13, 0, 354}, + dictWord{18, 0, 32}, + dictWord{23, 0, 24}, + dictWord{23, 0, 31}, + dictWord{ + 152, + 0, + 5, + }, + dictWord{133, 10, 933}, + dictWord{132, 11, 776}, + dictWord{138, 0, 151}, + dictWord{136, 0, 427}, + dictWord{134, 0, 382}, + dictWord{132, 0, 329}, + dictWord{ + 9, + 0, + 846, + }, + dictWord{10, 0, 827}, + dictWord{138, 11, 33}, + dictWord{9, 0, 279}, + dictWord{10, 0, 407}, + dictWord{14, 0, 84}, + dictWord{22, 0, 18}, + dictWord{ + 135, + 11, + 1297, + }, + dictWord{136, 11, 406}, + dictWord{132, 0, 906}, + dictWord{136, 0, 366}, + dictWord{134, 0, 843}, + dictWord{134, 0, 1443}, + dictWord{135, 0, 1372}, + dictWord{138, 0, 992}, + dictWord{4, 0, 123}, + dictWord{5, 0, 605}, + dictWord{7, 0, 1509}, + dictWord{136, 0, 36}, + dictWord{132, 0, 649}, + dictWord{8, 11, 175}, + dictWord{10, 11, 168}, + dictWord{138, 11, 573}, + dictWord{133, 0, 767}, + dictWord{134, 0, 1018}, + dictWord{135, 11, 1305}, + dictWord{12, 10, 30}, + dictWord{ + 13, + 10, + 148, + }, + dictWord{14, 10, 87}, + dictWord{14, 10, 182}, + dictWord{16, 10, 42}, + dictWord{148, 10, 70}, + dictWord{134, 11, 607}, + dictWord{4, 0, 273}, + dictWord{ + 5, + 0, + 658, + }, + dictWord{133, 0, 995}, + dictWord{6, 0, 72}, + dictWord{139, 11, 174}, + dictWord{10, 0, 483}, + dictWord{12, 0, 368}, + dictWord{7, 10, 56}, + dictWord{ + 7, + 10, + 1989, + }, + dictWord{8, 10, 337}, + dictWord{8, 10, 738}, + dictWord{9, 10, 600}, + dictWord{13, 10, 447}, + dictWord{142, 10, 92}, + dictWord{5, 11, 784}, + dictWord{ + 138, + 10, + 666, + }, + dictWord{135, 0, 1345}, + dictWord{139, 11, 882}, + dictWord{134, 0, 1293}, + dictWord{133, 0, 589}, + dictWord{134, 0, 1988}, + dictWord{5, 0, 117}, + dictWord{6, 0, 514}, + dictWord{6, 0, 541}, + dictWord{7, 0, 1164}, + dictWord{7, 0, 1436}, + dictWord{8, 0, 220}, + dictWord{8, 0, 648}, + dictWord{10, 0, 688}, + dictWord{ + 139, + 0, + 560, + }, + dictWord{136, 0, 379}, + dictWord{5, 0, 686}, + dictWord{7, 10, 866}, + dictWord{135, 10, 1163}, + dictWord{132, 10, 328}, + dictWord{9, 11, 14}, + dictWord{ + 9, + 11, + 441, + }, + dictWord{10, 11, 306}, + dictWord{139, 11, 9}, + dictWord{4, 10, 101}, + dictWord{135, 10, 1171}, + dictWord{5, 10, 833}, + dictWord{136, 10, 744}, + dictWord{5, 11, 161}, + dictWord{7, 11, 839}, + dictWord{135, 11, 887}, + dictWord{7, 0, 196}, + dictWord{10, 0, 765}, + dictWord{11, 0, 347}, + dictWord{11, 0, 552}, + dictWord{11, 0, 790}, + dictWord{12, 0, 263}, + dictWord{13, 0, 246}, + dictWord{13, 0, 270}, + dictWord{13, 0, 395}, + dictWord{14, 0, 176}, + dictWord{14, 0, 190}, + dictWord{ + 14, + 0, + 398, + }, + dictWord{14, 0, 412}, + dictWord{15, 0, 32}, + dictWord{15, 0, 63}, + dictWord{16, 0, 88}, + dictWord{147, 0, 105}, + dictWord{6, 10, 9}, + dictWord{6, 10, 397}, + dictWord{7, 10, 53}, + dictWord{7, 10, 1742}, + dictWord{10, 10, 632}, + dictWord{11, 10, 828}, + dictWord{140, 10, 146}, + dictWord{5, 0, 381}, + dictWord{135, 0, 1792}, + dictWord{134, 0, 1452}, + dictWord{135, 11, 429}, + dictWord{8, 0, 367}, + dictWord{10, 0, 760}, + dictWord{14, 0, 79}, + dictWord{20, 0, 17}, + dictWord{152, 0, 0}, + dictWord{7, 0, 616}, + dictWord{138, 0, 413}, + dictWord{11, 10, 417}, + dictWord{12, 10, 223}, + dictWord{140, 10, 265}, + dictWord{7, 11, 1611}, + dictWord{13, 11, 14}, + dictWord{15, 11, 44}, + dictWord{19, 11, 13}, + dictWord{148, 11, 76}, + dictWord{135, 0, 1229}, + dictWord{6, 0, 120}, + dictWord{7, 0, 1188}, + dictWord{7, 0, 1710}, + dictWord{8, 0, 286}, + dictWord{9, 0, 667}, + dictWord{11, 0, 592}, + dictWord{139, 0, 730}, + dictWord{135, 11, 1814}, + dictWord{135, 0, 1146}, + dictWord{4, 10, 186}, + dictWord{5, 10, 157}, + dictWord{8, 10, 168}, + dictWord{138, 10, 6}, + dictWord{4, 0, 352}, + dictWord{135, 0, 687}, + dictWord{4, 0, 192}, + dictWord{5, 0, 49}, + dictWord{ + 6, + 0, + 200, + }, + dictWord{6, 0, 293}, + dictWord{6, 0, 1696}, + dictWord{135, 0, 1151}, + dictWord{133, 10, 875}, + dictWord{5, 10, 773}, + dictWord{5, 10, 991}, + dictWord{ + 6, + 10, + 1635, + }, + dictWord{134, 10, 1788}, + dictWord{7, 10, 111}, + dictWord{136, 10, 581}, + dictWord{6, 0, 935}, + dictWord{134, 0, 1151}, + dictWord{134, 0, 1050}, + dictWord{132, 0, 650}, + dictWord{132, 0, 147}, + dictWord{11, 0, 194}, + dictWord{12, 0, 62}, + dictWord{12, 0, 88}, + dictWord{11, 11, 194}, + dictWord{12, 11, 62}, + dictWord{140, 11, 88}, + dictWord{6, 0, 339}, + dictWord{135, 0, 923}, + dictWord{134, 10, 1747}, + dictWord{7, 11, 643}, + dictWord{136, 11, 236}, + dictWord{ + 133, + 0, + 934, + }, + dictWord{7, 10, 1364}, + dictWord{7, 10, 1907}, + dictWord{141, 10, 158}, + dictWord{132, 10, 659}, + dictWord{4, 10, 404}, + dictWord{135, 10, 675}, + dictWord{7, 11, 581}, + dictWord{9, 11, 644}, + dictWord{137, 11, 699}, + dictWord{13, 0, 211}, + dictWord{14, 0, 133}, + dictWord{14, 0, 204}, + dictWord{15, 0, 64}, + dictWord{ + 15, + 0, + 69, + }, + dictWord{15, 0, 114}, + dictWord{16, 0, 10}, + dictWord{19, 0, 23}, + dictWord{19, 0, 35}, + dictWord{19, 0, 39}, + dictWord{19, 0, 51}, + dictWord{19, 0, 71}, + dictWord{19, 0, 75}, + dictWord{152, 0, 15}, + dictWord{133, 10, 391}, + dictWord{5, 11, 54}, + dictWord{135, 11, 1513}, + dictWord{7, 0, 222}, + dictWord{8, 0, 341}, + dictWord{ + 5, + 10, + 540, + }, + dictWord{134, 10, 1697}, + dictWord{134, 10, 78}, + dictWord{132, 11, 744}, + dictWord{136, 0, 293}, + dictWord{137, 11, 701}, + dictWord{ + 7, + 11, + 930, + }, + dictWord{10, 11, 402}, + dictWord{10, 11, 476}, + dictWord{13, 11, 452}, + dictWord{18, 11, 55}, + dictWord{147, 11, 104}, + dictWord{132, 0, 637}, + dictWord{133, 10, 460}, + dictWord{8, 11, 50}, + dictWord{137, 11, 624}, + dictWord{132, 11, 572}, + dictWord{134, 0, 1159}, + dictWord{4, 10, 199}, + dictWord{ + 139, + 10, + 34, + }, + dictWord{134, 0, 847}, + dictWord{134, 10, 388}, + dictWord{6, 11, 43}, + dictWord{7, 11, 38}, + dictWord{8, 11, 248}, + dictWord{9, 11, 504}, + dictWord{ + 138, + 11, + 513, + }, + dictWord{9, 0, 683}, + dictWord{4, 10, 511}, + dictWord{6, 10, 608}, + dictWord{9, 10, 333}, + dictWord{10, 10, 602}, + dictWord{11, 10, 441}, + dictWord{ + 11, + 10, + 723, + }, + dictWord{11, 10, 976}, + dictWord{140, 10, 357}, + dictWord{9, 0, 867}, + dictWord{138, 0, 837}, + dictWord{6, 0, 944}, + dictWord{135, 11, 326}, + dictWord{ + 135, + 0, + 1809, + }, + dictWord{5, 10, 938}, + dictWord{7, 11, 783}, + dictWord{136, 10, 707}, + dictWord{133, 11, 766}, + dictWord{133, 11, 363}, + dictWord{6, 0, 170}, + dictWord{7, 0, 1080}, + dictWord{8, 0, 395}, + dictWord{8, 0, 487}, + dictWord{141, 0, 147}, + dictWord{6, 11, 258}, + dictWord{140, 11, 409}, + dictWord{4, 0, 535}, + dictWord{ + 8, + 0, + 618, + }, + dictWord{5, 11, 249}, + dictWord{148, 11, 82}, + dictWord{6, 0, 1379}, + dictWord{149, 11, 15}, + dictWord{135, 0, 1625}, + dictWord{150, 0, 23}, + dictWord{ + 5, + 11, + 393, + }, + dictWord{6, 11, 378}, + dictWord{7, 11, 1981}, + dictWord{9, 11, 32}, + dictWord{9, 11, 591}, + dictWord{10, 11, 685}, + dictWord{10, 11, 741}, + dictWord{ + 142, + 11, + 382, + }, + dictWord{133, 11, 788}, + dictWord{7, 11, 1968}, + dictWord{10, 11, 19}, + dictWord{139, 11, 911}, + dictWord{7, 11, 1401}, + dictWord{ + 135, + 11, + 1476, + }, + dictWord{4, 11, 61}, + dictWord{5, 11, 58}, + dictWord{5, 11, 171}, + dictWord{5, 11, 635}, + dictWord{5, 11, 683}, + dictWord{5, 11, 700}, + dictWord{6, 11, 291}, + dictWord{6, 11, 566}, + dictWord{7, 11, 1650}, + dictWord{11, 11, 523}, + dictWord{12, 11, 273}, + dictWord{12, 11, 303}, + dictWord{15, 11, 39}, + dictWord{ + 143, + 11, + 111, + }, + dictWord{6, 10, 469}, + dictWord{7, 10, 1709}, + dictWord{138, 10, 515}, + dictWord{4, 0, 778}, + dictWord{134, 11, 589}, + dictWord{132, 0, 46}, + dictWord{ + 5, + 0, + 811, + }, + dictWord{6, 0, 1679}, + dictWord{6, 0, 1714}, + dictWord{135, 0, 2032}, + dictWord{7, 0, 1458}, + dictWord{9, 0, 407}, + dictWord{11, 0, 15}, + dictWord{12, 0, 651}, + dictWord{149, 0, 37}, + dictWord{7, 0, 938}, + dictWord{132, 10, 500}, + dictWord{6, 0, 34}, + dictWord{7, 0, 69}, + dictWord{7, 0, 1089}, + dictWord{7, 0, 1281}, + dictWord{ + 8, + 0, + 708, + }, + dictWord{8, 0, 721}, + dictWord{9, 0, 363}, + dictWord{148, 0, 98}, + dictWord{10, 11, 231}, + dictWord{147, 11, 124}, + dictWord{7, 11, 726}, + dictWord{ + 152, + 11, + 9, + }, + dictWord{5, 10, 68}, + dictWord{134, 10, 383}, + dictWord{136, 11, 583}, + dictWord{4, 11, 917}, + dictWord{133, 11, 1005}, + dictWord{11, 10, 216}, + dictWord{139, 10, 340}, + dictWord{135, 11, 1675}, + dictWord{8, 0, 441}, + dictWord{10, 0, 314}, + dictWord{143, 0, 3}, + dictWord{132, 11, 919}, + dictWord{4, 10, 337}, + dictWord{6, 10, 353}, + dictWord{7, 10, 1934}, + dictWord{8, 10, 488}, + dictWord{137, 10, 429}, + dictWord{7, 0, 889}, + dictWord{7, 10, 1795}, + dictWord{8, 10, 259}, + dictWord{9, 10, 135}, + dictWord{9, 10, 177}, + dictWord{9, 10, 860}, + dictWord{10, 10, 825}, + dictWord{11, 10, 115}, + dictWord{11, 10, 370}, + dictWord{11, 10, 405}, + dictWord{11, 10, 604}, + dictWord{12, 10, 10}, + dictWord{12, 10, 667}, + dictWord{12, 10, 669}, + dictWord{13, 10, 76}, + dictWord{14, 10, 310}, + dictWord{ + 15, + 10, + 76, + }, + dictWord{15, 10, 147}, + dictWord{148, 10, 23}, + dictWord{4, 10, 15}, + dictWord{4, 11, 255}, + dictWord{5, 10, 22}, + dictWord{5, 11, 302}, + dictWord{6, 11, 132}, + dictWord{6, 10, 244}, + dictWord{7, 10, 40}, + dictWord{7, 11, 128}, + dictWord{7, 10, 200}, + dictWord{7, 11, 283}, + dictWord{7, 10, 906}, + dictWord{7, 10, 1199}, + dictWord{ + 7, + 11, + 1299, + }, + dictWord{9, 10, 616}, + dictWord{10, 11, 52}, + dictWord{10, 11, 514}, + dictWord{10, 10, 716}, + dictWord{11, 10, 635}, + dictWord{11, 10, 801}, + dictWord{11, 11, 925}, + dictWord{12, 10, 458}, + dictWord{13, 11, 92}, + dictWord{142, 11, 309}, + dictWord{132, 0, 462}, + dictWord{137, 11, 173}, + dictWord{ + 135, + 10, + 1735, + }, + dictWord{8, 0, 525}, + dictWord{5, 10, 598}, + dictWord{7, 10, 791}, + dictWord{8, 10, 108}, + dictWord{137, 10, 123}, + dictWord{5, 0, 73}, + dictWord{6, 0, 23}, + dictWord{134, 0, 338}, + dictWord{132, 0, 676}, + dictWord{132, 10, 683}, + dictWord{7, 0, 725}, + dictWord{8, 0, 498}, + dictWord{139, 0, 268}, + dictWord{12, 0, 21}, + dictWord{151, 0, 7}, + dictWord{135, 0, 773}, + dictWord{4, 10, 155}, + dictWord{135, 10, 1689}, + dictWord{4, 0, 164}, + dictWord{5, 0, 730}, + dictWord{5, 10, 151}, + dictWord{ + 5, + 10, + 741, + }, + dictWord{6, 11, 210}, + dictWord{7, 10, 498}, + dictWord{7, 10, 870}, + dictWord{7, 10, 1542}, + dictWord{12, 10, 213}, + dictWord{14, 10, 36}, + dictWord{ + 14, + 10, + 391, + }, + dictWord{17, 10, 111}, + dictWord{18, 10, 6}, + dictWord{18, 10, 46}, + dictWord{18, 10, 151}, + dictWord{19, 10, 36}, + dictWord{20, 10, 32}, + dictWord{ + 20, + 10, + 56, + }, + dictWord{20, 10, 69}, + dictWord{20, 10, 102}, + dictWord{21, 10, 4}, + dictWord{22, 10, 8}, + dictWord{22, 10, 10}, + dictWord{22, 10, 14}, + dictWord{ + 150, + 10, + 31, + }, + dictWord{4, 10, 624}, + dictWord{135, 10, 1752}, + dictWord{4, 0, 583}, + dictWord{9, 0, 936}, + dictWord{15, 0, 214}, + dictWord{18, 0, 199}, + dictWord{24, 0, 26}, + dictWord{134, 11, 588}, + dictWord{7, 0, 1462}, + dictWord{11, 0, 659}, + dictWord{4, 11, 284}, + dictWord{134, 11, 223}, + dictWord{133, 0, 220}, + dictWord{ + 139, + 0, + 803, + }, + dictWord{132, 0, 544}, + dictWord{4, 10, 492}, + dictWord{133, 10, 451}, + dictWord{16, 0, 98}, + dictWord{148, 0, 119}, + dictWord{4, 11, 218}, + dictWord{ + 7, + 11, + 526, + }, + dictWord{143, 11, 137}, + dictWord{135, 10, 835}, + dictWord{4, 11, 270}, + dictWord{5, 11, 192}, + dictWord{6, 11, 332}, + dictWord{7, 11, 1322}, + dictWord{ + 13, + 11, + 9, + }, + dictWord{13, 10, 70}, + dictWord{14, 11, 104}, + dictWord{142, 11, 311}, + dictWord{132, 10, 539}, + dictWord{140, 11, 661}, + dictWord{5, 0, 176}, + dictWord{ + 6, + 0, + 437, + }, + dictWord{6, 0, 564}, + dictWord{11, 0, 181}, + dictWord{141, 0, 183}, + dictWord{135, 0, 1192}, + dictWord{6, 10, 113}, + dictWord{135, 10, 436}, + dictWord{136, 10, 718}, + dictWord{135, 10, 520}, + dictWord{135, 0, 1878}, + dictWord{140, 11, 196}, + dictWord{7, 11, 379}, + dictWord{8, 11, 481}, + dictWord{ + 137, + 11, + 377, + }, + dictWord{5, 11, 1003}, + dictWord{6, 11, 149}, + dictWord{137, 11, 746}, + dictWord{8, 11, 262}, + dictWord{9, 11, 627}, + dictWord{10, 11, 18}, + dictWord{ + 11, + 11, + 214, + }, + dictWord{11, 11, 404}, + dictWord{11, 11, 457}, + dictWord{11, 11, 780}, + dictWord{11, 11, 849}, + dictWord{11, 11, 913}, + dictWord{13, 11, 330}, + dictWord{13, 11, 401}, + dictWord{142, 11, 200}, + dictWord{149, 0, 26}, + dictWord{136, 11, 304}, + dictWord{132, 11, 142}, + dictWord{135, 0, 944}, + dictWord{ + 4, + 0, + 790, + }, + dictWord{5, 0, 273}, + dictWord{134, 0, 394}, + dictWord{134, 0, 855}, + dictWord{4, 0, 135}, + dictWord{6, 0, 127}, + dictWord{7, 0, 1185}, + dictWord{7, 0, 1511}, + dictWord{8, 0, 613}, + dictWord{11, 0, 5}, + dictWord{12, 0, 336}, + dictWord{12, 0, 495}, + dictWord{12, 0, 586}, + dictWord{12, 0, 660}, + dictWord{12, 0, 668}, + dictWord{ + 14, + 0, + 385, + }, + dictWord{15, 0, 118}, + dictWord{17, 0, 20}, + dictWord{146, 0, 98}, + dictWord{6, 0, 230}, + dictWord{9, 0, 752}, + dictWord{18, 0, 109}, + dictWord{12, 10, 610}, + dictWord{13, 10, 431}, + dictWord{144, 10, 59}, + dictWord{7, 0, 1954}, + dictWord{135, 11, 925}, + dictWord{4, 11, 471}, + dictWord{5, 11, 51}, + dictWord{6, 11, 602}, + dictWord{8, 11, 484}, + dictWord{10, 11, 195}, + dictWord{140, 11, 159}, + dictWord{132, 10, 307}, + dictWord{136, 11, 688}, + dictWord{132, 11, 697}, + dictWord{ + 7, + 11, + 812, + }, + dictWord{7, 11, 1261}, + dictWord{7, 11, 1360}, + dictWord{9, 11, 632}, + dictWord{140, 11, 352}, + dictWord{5, 0, 162}, + dictWord{8, 0, 68}, + dictWord{ + 133, + 10, + 964, + }, + dictWord{4, 0, 654}, + dictWord{136, 11, 212}, + dictWord{4, 0, 156}, + dictWord{7, 0, 998}, + dictWord{7, 0, 1045}, + dictWord{7, 0, 1860}, + dictWord{9, 0, 48}, + dictWord{9, 0, 692}, + dictWord{11, 0, 419}, + dictWord{139, 0, 602}, + dictWord{133, 11, 221}, + dictWord{4, 11, 373}, + dictWord{5, 11, 283}, + dictWord{6, 11, 480}, + dictWord{135, 11, 609}, + dictWord{142, 11, 216}, + dictWord{132, 0, 240}, + dictWord{6, 11, 192}, + dictWord{9, 11, 793}, + dictWord{145, 11, 55}, + dictWord{ + 4, + 10, + 75, + }, + dictWord{5, 10, 180}, + dictWord{6, 10, 500}, + dictWord{7, 10, 58}, + dictWord{7, 10, 710}, + dictWord{138, 10, 645}, + dictWord{4, 11, 132}, + dictWord{5, 11, 69}, + dictWord{5, 10, 649}, + dictWord{135, 11, 1242}, + dictWord{6, 10, 276}, + dictWord{7, 10, 282}, + dictWord{7, 10, 879}, + dictWord{7, 10, 924}, + dictWord{8, 10, 459}, + dictWord{9, 10, 599}, + dictWord{9, 10, 754}, + dictWord{11, 10, 574}, + dictWord{12, 10, 128}, + dictWord{12, 10, 494}, + dictWord{13, 10, 52}, + dictWord{13, 10, 301}, + dictWord{15, 10, 30}, + dictWord{143, 10, 132}, + dictWord{132, 10, 200}, + dictWord{4, 11, 111}, + dictWord{135, 11, 302}, + dictWord{9, 0, 197}, + dictWord{ + 10, + 0, + 300, + }, + dictWord{12, 0, 473}, + dictWord{13, 0, 90}, + dictWord{141, 0, 405}, + dictWord{132, 11, 767}, + dictWord{6, 11, 42}, + dictWord{7, 11, 1416}, + dictWord{ + 7, + 11, + 1590, + }, + dictWord{7, 11, 2005}, + dictWord{8, 11, 131}, + dictWord{8, 11, 466}, + dictWord{9, 11, 672}, + dictWord{13, 11, 252}, + dictWord{148, 11, 103}, + dictWord{ + 8, + 0, + 958, + }, + dictWord{8, 0, 999}, + dictWord{10, 0, 963}, + dictWord{138, 0, 1001}, + dictWord{135, 10, 1621}, + dictWord{135, 0, 858}, + dictWord{4, 0, 606}, + dictWord{ + 137, + 11, + 444, + }, + dictWord{6, 11, 44}, + dictWord{136, 11, 368}, + dictWord{139, 11, 172}, + dictWord{4, 11, 570}, + dictWord{133, 11, 120}, + dictWord{139, 11, 624}, + dictWord{7, 0, 1978}, + dictWord{8, 0, 676}, + dictWord{6, 10, 225}, + dictWord{137, 10, 211}, + dictWord{7, 0, 972}, + dictWord{11, 0, 102}, + dictWord{136, 10, 687}, + dictWord{6, 11, 227}, + dictWord{135, 11, 1589}, + dictWord{8, 10, 58}, + dictWord{9, 10, 724}, + dictWord{11, 10, 809}, + dictWord{13, 10, 113}, + dictWord{ + 145, + 10, + 72, + }, + dictWord{4, 0, 361}, + dictWord{133, 0, 315}, + dictWord{132, 0, 461}, + dictWord{6, 10, 345}, + dictWord{135, 10, 1247}, + dictWord{132, 0, 472}, + dictWord{ + 8, + 10, + 767, + }, + dictWord{8, 10, 803}, + dictWord{9, 10, 301}, + dictWord{137, 10, 903}, + dictWord{135, 11, 1333}, + dictWord{135, 11, 477}, + dictWord{7, 10, 1949}, + dictWord{136, 10, 674}, + dictWord{6, 0, 905}, + dictWord{138, 0, 747}, + dictWord{133, 0, 155}, + dictWord{134, 10, 259}, + dictWord{7, 0, 163}, + dictWord{8, 0, 319}, + dictWord{9, 0, 402}, + dictWord{10, 0, 24}, + dictWord{10, 0, 681}, + dictWord{11, 0, 200}, + dictWord{12, 0, 253}, + dictWord{12, 0, 410}, + dictWord{142, 0, 219}, + dictWord{ + 5, + 0, + 475, + }, + dictWord{7, 0, 1780}, + dictWord{9, 0, 230}, + dictWord{11, 0, 297}, + dictWord{11, 0, 558}, + dictWord{14, 0, 322}, + dictWord{19, 0, 76}, + dictWord{6, 11, 1667}, + dictWord{7, 11, 2036}, + dictWord{138, 11, 600}, + dictWord{136, 10, 254}, + dictWord{6, 0, 848}, + dictWord{135, 0, 1956}, + dictWord{6, 11, 511}, + dictWord{ + 140, + 11, + 132, + }, + dictWord{5, 11, 568}, + dictWord{6, 11, 138}, + dictWord{135, 11, 1293}, + dictWord{6, 0, 631}, + dictWord{137, 0, 838}, + dictWord{149, 0, 36}, + dictWord{ + 4, + 11, + 565, + }, + dictWord{8, 11, 23}, + dictWord{136, 11, 827}, + dictWord{5, 0, 944}, + dictWord{134, 0, 1769}, + dictWord{4, 0, 144}, + dictWord{6, 0, 842}, + dictWord{ + 6, + 0, + 1400, + }, + dictWord{4, 11, 922}, + dictWord{133, 11, 1023}, + dictWord{133, 10, 248}, + dictWord{9, 10, 800}, + dictWord{10, 10, 693}, + dictWord{11, 10, 482}, + dictWord{11, 10, 734}, + dictWord{139, 10, 789}, + dictWord{7, 11, 1002}, + dictWord{139, 11, 145}, + dictWord{4, 10, 116}, + dictWord{5, 10, 95}, + dictWord{5, 10, 445}, + dictWord{7, 10, 1688}, + dictWord{8, 10, 29}, + dictWord{9, 10, 272}, + dictWord{11, 10, 509}, + dictWord{139, 10, 915}, + dictWord{14, 0, 369}, + dictWord{146, 0, 72}, + dictWord{135, 10, 1641}, + dictWord{132, 11, 740}, + dictWord{133, 10, 543}, + dictWord{140, 11, 116}, + dictWord{6, 0, 247}, + dictWord{9, 0, 555}, + dictWord{ + 5, + 10, + 181, + }, + dictWord{136, 10, 41}, + dictWord{133, 10, 657}, + dictWord{136, 0, 996}, + dictWord{138, 10, 709}, + dictWord{7, 0, 189}, + dictWord{8, 10, 202}, + dictWord{ + 138, + 10, + 536, + }, + dictWord{136, 11, 402}, + dictWord{4, 11, 716}, + dictWord{141, 11, 31}, + dictWord{10, 0, 280}, + dictWord{138, 0, 797}, + dictWord{9, 10, 423}, + dictWord{140, 10, 89}, + dictWord{8, 10, 113}, + dictWord{9, 10, 877}, + dictWord{10, 10, 554}, + dictWord{11, 10, 83}, + dictWord{12, 10, 136}, + dictWord{147, 10, 109}, + dictWord{133, 10, 976}, + dictWord{7, 0, 746}, + dictWord{132, 10, 206}, + dictWord{136, 0, 526}, + dictWord{139, 0, 345}, + dictWord{136, 0, 1017}, + dictWord{ + 8, + 11, + 152, + }, + dictWord{9, 11, 53}, + dictWord{9, 11, 268}, + dictWord{9, 11, 901}, + dictWord{10, 11, 518}, + dictWord{10, 11, 829}, + dictWord{11, 11, 188}, + dictWord{ + 13, + 11, + 74, + }, + dictWord{14, 11, 46}, + dictWord{15, 11, 17}, + dictWord{15, 11, 33}, + dictWord{17, 11, 40}, + dictWord{18, 11, 36}, + dictWord{19, 11, 20}, + dictWord{22, 11, 1}, + dictWord{152, 11, 2}, + dictWord{133, 11, 736}, + dictWord{136, 11, 532}, + dictWord{5, 0, 428}, + dictWord{138, 0, 651}, + dictWord{135, 11, 681}, + dictWord{ + 135, + 0, + 1162, + }, + dictWord{7, 0, 327}, + dictWord{13, 0, 230}, + dictWord{17, 0, 113}, + dictWord{8, 10, 226}, + dictWord{10, 10, 537}, + dictWord{11, 10, 570}, + dictWord{ + 11, + 10, + 605, + }, + dictWord{11, 10, 799}, + dictWord{11, 10, 804}, + dictWord{12, 10, 85}, + dictWord{12, 10, 516}, + dictWord{12, 10, 623}, + dictWord{12, 11, 677}, + dictWord{ + 13, + 10, + 361, + }, + dictWord{14, 10, 77}, + dictWord{14, 10, 78}, + dictWord{147, 10, 110}, + dictWord{4, 0, 792}, + dictWord{7, 0, 1717}, + dictWord{10, 0, 546}, + dictWord{ + 132, + 10, + 769, + }, + dictWord{4, 11, 684}, + dictWord{136, 11, 384}, + dictWord{132, 10, 551}, + dictWord{134, 0, 1203}, + dictWord{9, 10, 57}, + dictWord{9, 10, 459}, + dictWord{10, 10, 425}, + dictWord{11, 10, 119}, + dictWord{12, 10, 184}, + dictWord{12, 10, 371}, + dictWord{13, 10, 358}, + dictWord{145, 10, 51}, + dictWord{5, 0, 672}, + dictWord{5, 10, 814}, + dictWord{8, 10, 10}, + dictWord{9, 10, 421}, + dictWord{9, 10, 729}, + dictWord{10, 10, 609}, + dictWord{139, 10, 689}, + dictWord{138, 0, 189}, + dictWord{134, 10, 624}, + dictWord{7, 11, 110}, + dictWord{7, 11, 188}, + dictWord{8, 11, 290}, + dictWord{8, 11, 591}, + dictWord{9, 11, 382}, + dictWord{9, 11, 649}, + dictWord{11, 11, 71}, + dictWord{11, 11, 155}, + dictWord{11, 11, 313}, + dictWord{12, 11, 5}, + dictWord{13, 11, 325}, + dictWord{142, 11, 287}, + dictWord{133, 0, 99}, + dictWord{6, 0, 1053}, + dictWord{135, 0, 298}, + dictWord{7, 11, 360}, + dictWord{7, 11, 425}, + dictWord{9, 11, 66}, + dictWord{9, 11, 278}, + dictWord{138, 11, 644}, + dictWord{4, 0, 397}, + dictWord{136, 0, 555}, + dictWord{137, 10, 269}, + dictWord{132, 10, 528}, + dictWord{4, 11, 900}, + dictWord{133, 11, 861}, + dictWord{ + 6, + 0, + 1157, + }, + dictWord{5, 11, 254}, + dictWord{7, 11, 985}, + dictWord{136, 11, 73}, + dictWord{7, 11, 1959}, + dictWord{136, 11, 683}, + dictWord{12, 0, 398}, + dictWord{ + 20, + 0, + 39, + }, + dictWord{21, 0, 11}, + dictWord{150, 0, 41}, + dictWord{4, 0, 485}, + dictWord{7, 0, 353}, + dictWord{135, 0, 1523}, + dictWord{6, 0, 366}, + dictWord{7, 0, 1384}, + dictWord{135, 0, 1601}, + dictWord{138, 0, 787}, + dictWord{137, 0, 282}, + dictWord{5, 10, 104}, + dictWord{6, 10, 173}, + dictWord{135, 10, 1631}, + dictWord{ + 139, + 11, + 146, + }, + dictWord{4, 0, 157}, + dictWord{133, 0, 471}, + dictWord{134, 0, 941}, + dictWord{132, 11, 725}, + dictWord{7, 0, 1336}, + dictWord{8, 10, 138}, + dictWord{ + 8, + 10, + 342, + }, + dictWord{9, 10, 84}, + dictWord{10, 10, 193}, + dictWord{11, 10, 883}, + dictWord{140, 10, 359}, + dictWord{134, 11, 196}, + dictWord{136, 0, 116}, + dictWord{133, 11, 831}, + dictWord{134, 0, 787}, + dictWord{134, 10, 95}, + dictWord{6, 10, 406}, + dictWord{10, 10, 409}, + dictWord{10, 10, 447}, + dictWord{ + 11, + 10, + 44, + }, + dictWord{140, 10, 100}, + dictWord{5, 0, 160}, + dictWord{7, 0, 363}, + dictWord{7, 0, 589}, + dictWord{10, 0, 170}, + dictWord{141, 0, 55}, + dictWord{134, 0, 1815}, + dictWord{132, 0, 866}, + dictWord{6, 0, 889}, + dictWord{6, 0, 1067}, + dictWord{6, 0, 1183}, + dictWord{4, 11, 321}, + dictWord{134, 11, 569}, + dictWord{5, 11, 848}, + dictWord{134, 11, 66}, + dictWord{4, 11, 36}, + dictWord{6, 10, 1636}, + dictWord{7, 11, 1387}, + dictWord{10, 11, 205}, + dictWord{11, 11, 755}, + dictWord{ + 141, + 11, + 271, + }, + dictWord{132, 0, 689}, + dictWord{9, 0, 820}, + dictWord{4, 10, 282}, + dictWord{7, 10, 1034}, + dictWord{11, 10, 398}, + dictWord{11, 10, 634}, + dictWord{ + 12, + 10, + 1, + }, + dictWord{12, 10, 79}, + dictWord{12, 10, 544}, + dictWord{14, 10, 237}, + dictWord{17, 10, 10}, + dictWord{146, 10, 20}, + dictWord{4, 0, 108}, + dictWord{7, 0, 804}, + dictWord{139, 0, 498}, + dictWord{132, 11, 887}, + dictWord{6, 0, 1119}, + dictWord{135, 11, 620}, + dictWord{6, 11, 165}, + dictWord{138, 11, 388}, + dictWord{ + 5, + 0, + 244, + }, + dictWord{5, 10, 499}, + dictWord{6, 10, 476}, + dictWord{7, 10, 600}, + dictWord{7, 10, 888}, + dictWord{135, 10, 1096}, + dictWord{140, 0, 609}, + dictWord{ + 135, + 0, + 1005, + }, + dictWord{4, 0, 412}, + dictWord{133, 0, 581}, + dictWord{4, 11, 719}, + dictWord{135, 11, 155}, + dictWord{7, 10, 296}, + dictWord{7, 10, 596}, + dictWord{ + 8, + 10, + 560, + }, + dictWord{8, 10, 586}, + dictWord{9, 10, 612}, + dictWord{11, 10, 304}, + dictWord{12, 10, 46}, + dictWord{13, 10, 89}, + dictWord{14, 10, 112}, + dictWord{ + 145, + 10, + 122, + }, + dictWord{4, 0, 895}, + dictWord{133, 0, 772}, + dictWord{142, 11, 307}, + dictWord{135, 0, 1898}, + dictWord{4, 0, 926}, + dictWord{133, 0, 983}, + dictWord{4, 11, 353}, + dictWord{6, 11, 146}, + dictWord{6, 11, 1789}, + dictWord{7, 11, 288}, + dictWord{7, 11, 990}, + dictWord{7, 11, 1348}, + dictWord{9, 11, 665}, + dictWord{ + 9, + 11, + 898, + }, + dictWord{11, 11, 893}, + dictWord{142, 11, 212}, + dictWord{132, 0, 538}, + dictWord{133, 11, 532}, + dictWord{6, 0, 294}, + dictWord{7, 0, 1267}, + dictWord{8, 0, 624}, + dictWord{141, 0, 496}, + dictWord{7, 0, 1325}, + dictWord{4, 11, 45}, + dictWord{135, 11, 1257}, + dictWord{138, 0, 301}, + dictWord{9, 0, 298}, + dictWord{12, 0, 291}, + dictWord{13, 0, 276}, + dictWord{14, 0, 6}, + dictWord{17, 0, 18}, + dictWord{21, 0, 32}, + dictWord{7, 10, 1599}, + dictWord{7, 10, 1723}, + dictWord{ + 8, + 10, + 79, + }, + dictWord{8, 10, 106}, + dictWord{8, 10, 190}, + dictWord{8, 10, 302}, + dictWord{8, 10, 383}, + dictWord{8, 10, 713}, + dictWord{9, 10, 119}, + dictWord{9, 10, 233}, + dictWord{9, 10, 419}, + dictWord{9, 10, 471}, + dictWord{10, 10, 181}, + dictWord{10, 10, 406}, + dictWord{11, 10, 57}, + dictWord{11, 10, 85}, + dictWord{11, 10, 120}, + dictWord{11, 10, 177}, + dictWord{11, 10, 296}, + dictWord{11, 10, 382}, + dictWord{11, 10, 454}, + dictWord{11, 10, 758}, + dictWord{11, 10, 999}, + dictWord{ + 12, + 10, + 27, + }, + dictWord{12, 10, 131}, + dictWord{12, 10, 245}, + dictWord{12, 10, 312}, + dictWord{12, 10, 446}, + dictWord{12, 10, 454}, + dictWord{13, 10, 98}, + dictWord{ + 13, + 10, + 426, + }, + dictWord{13, 10, 508}, + dictWord{14, 10, 163}, + dictWord{14, 10, 272}, + dictWord{14, 10, 277}, + dictWord{14, 10, 370}, + dictWord{15, 10, 95}, + dictWord{15, 10, 138}, + dictWord{15, 10, 167}, + dictWord{17, 10, 38}, + dictWord{148, 10, 96}, + dictWord{132, 0, 757}, + dictWord{134, 0, 1263}, + dictWord{4, 0, 820}, + dictWord{134, 10, 1759}, + dictWord{133, 0, 722}, + dictWord{136, 11, 816}, + dictWord{138, 10, 372}, + dictWord{145, 10, 16}, + dictWord{134, 0, 1039}, + dictWord{ + 4, + 0, + 991, + }, + dictWord{134, 0, 2028}, + dictWord{133, 10, 258}, + dictWord{7, 0, 1875}, + dictWord{139, 0, 124}, + dictWord{6, 11, 559}, + dictWord{6, 11, 1691}, + dictWord{135, 11, 586}, + dictWord{5, 0, 324}, + dictWord{7, 0, 881}, + dictWord{8, 10, 134}, + dictWord{9, 10, 788}, + dictWord{140, 10, 438}, + dictWord{7, 11, 1823}, + dictWord{139, 11, 693}, + dictWord{6, 0, 1348}, + dictWord{134, 0, 1545}, + dictWord{134, 0, 911}, + dictWord{132, 0, 954}, + dictWord{8, 0, 329}, + dictWord{8, 0, 414}, + dictWord{7, 10, 1948}, + dictWord{135, 10, 2004}, + dictWord{5, 0, 517}, + dictWord{6, 10, 439}, + dictWord{7, 10, 780}, + dictWord{135, 10, 1040}, + dictWord{ + 132, + 0, + 816, + }, + dictWord{5, 10, 1}, + dictWord{6, 10, 81}, + dictWord{138, 10, 520}, + dictWord{9, 0, 713}, + dictWord{10, 0, 222}, + dictWord{5, 10, 482}, + dictWord{8, 10, 98}, + dictWord{10, 10, 700}, + dictWord{10, 10, 822}, + dictWord{11, 10, 302}, + dictWord{11, 10, 778}, + dictWord{12, 10, 50}, + dictWord{12, 10, 127}, + dictWord{12, 10, 396}, + dictWord{13, 10, 62}, + dictWord{13, 10, 328}, + dictWord{14, 10, 122}, + dictWord{147, 10, 72}, + dictWord{137, 0, 33}, + dictWord{5, 10, 2}, + dictWord{7, 10, 1494}, + dictWord{136, 10, 589}, + dictWord{6, 10, 512}, + dictWord{7, 10, 797}, + dictWord{8, 10, 253}, + dictWord{9, 10, 77}, + dictWord{10, 10, 1}, + dictWord{10, 11, 108}, + dictWord{10, 10, 129}, + dictWord{10, 10, 225}, + dictWord{11, 11, 116}, + dictWord{11, 10, 118}, + dictWord{11, 10, 226}, + dictWord{11, 10, 251}, + dictWord{ + 11, + 10, + 430, + }, + dictWord{11, 10, 701}, + dictWord{11, 10, 974}, + dictWord{11, 10, 982}, + dictWord{12, 10, 64}, + dictWord{12, 10, 260}, + dictWord{12, 10, 488}, + dictWord{ + 140, + 10, + 690, + }, + dictWord{134, 11, 456}, + dictWord{133, 11, 925}, + dictWord{5, 0, 150}, + dictWord{7, 0, 106}, + dictWord{7, 0, 774}, + dictWord{8, 0, 603}, + dictWord{ + 9, + 0, + 593, + }, + dictWord{9, 0, 634}, + dictWord{10, 0, 44}, + dictWord{10, 0, 173}, + dictWord{11, 0, 462}, + dictWord{11, 0, 515}, + dictWord{13, 0, 216}, + dictWord{13, 0, 288}, + dictWord{142, 0, 400}, + dictWord{137, 10, 347}, + dictWord{5, 0, 748}, + dictWord{134, 0, 553}, + dictWord{12, 0, 108}, + dictWord{141, 0, 291}, + dictWord{7, 0, 420}, + dictWord{4, 10, 12}, + dictWord{7, 10, 522}, + dictWord{7, 10, 809}, + dictWord{8, 10, 797}, + dictWord{141, 10, 88}, + dictWord{6, 11, 193}, + dictWord{7, 11, 240}, + dictWord{ + 7, + 11, + 1682, + }, + dictWord{10, 11, 51}, + dictWord{10, 11, 640}, + dictWord{11, 11, 410}, + dictWord{13, 11, 82}, + dictWord{14, 11, 247}, + dictWord{14, 11, 331}, + dictWord{142, 11, 377}, + dictWord{133, 10, 528}, + dictWord{135, 0, 1777}, + dictWord{4, 0, 493}, + dictWord{144, 0, 55}, + dictWord{136, 11, 633}, + dictWord{ + 139, + 0, + 81, + }, + dictWord{6, 0, 980}, + dictWord{136, 0, 321}, + dictWord{148, 10, 109}, + dictWord{5, 10, 266}, + dictWord{9, 10, 290}, + dictWord{9, 10, 364}, + dictWord{ + 10, + 10, + 293, + }, + dictWord{11, 10, 606}, + dictWord{142, 10, 45}, + dictWord{6, 0, 568}, + dictWord{7, 0, 112}, + dictWord{7, 0, 1804}, + dictWord{8, 0, 362}, + dictWord{8, 0, 410}, + dictWord{8, 0, 830}, + dictWord{9, 0, 514}, + dictWord{11, 0, 649}, + dictWord{142, 0, 157}, + dictWord{4, 0, 74}, + dictWord{6, 0, 510}, + dictWord{6, 10, 594}, + dictWord{ + 9, + 10, + 121, + }, + dictWord{10, 10, 49}, + dictWord{10, 10, 412}, + dictWord{139, 10, 834}, + dictWord{134, 0, 838}, + dictWord{136, 10, 748}, + dictWord{132, 10, 466}, + dictWord{132, 0, 625}, + dictWord{135, 11, 1443}, + dictWord{4, 11, 237}, + dictWord{135, 11, 514}, + dictWord{9, 10, 378}, + dictWord{141, 10, 162}, + dictWord{6, 0, 16}, + dictWord{6, 0, 158}, + dictWord{7, 0, 43}, + dictWord{7, 0, 129}, + dictWord{7, 0, 181}, + dictWord{8, 0, 276}, + dictWord{8, 0, 377}, + dictWord{10, 0, 523}, + dictWord{ + 11, + 0, + 816, + }, + dictWord{12, 0, 455}, + dictWord{13, 0, 303}, + dictWord{142, 0, 135}, + dictWord{135, 0, 281}, + dictWord{4, 0, 1}, + dictWord{7, 0, 1143}, + dictWord{7, 0, 1463}, + dictWord{8, 0, 61}, + dictWord{9, 0, 207}, + dictWord{9, 0, 390}, + dictWord{9, 0, 467}, + dictWord{139, 0, 836}, + dictWord{6, 11, 392}, + dictWord{7, 11, 65}, + dictWord{ + 135, + 11, + 2019, + }, + dictWord{132, 10, 667}, + dictWord{4, 0, 723}, + dictWord{5, 0, 895}, + dictWord{7, 0, 1031}, + dictWord{8, 0, 199}, + dictWord{8, 0, 340}, + dictWord{9, 0, 153}, + dictWord{9, 0, 215}, + dictWord{10, 0, 21}, + dictWord{10, 0, 59}, + dictWord{10, 0, 80}, + dictWord{10, 0, 224}, + dictWord{10, 0, 838}, + dictWord{11, 0, 229}, + dictWord{ + 11, + 0, + 652, + }, + dictWord{12, 0, 192}, + dictWord{13, 0, 146}, + dictWord{142, 0, 91}, + dictWord{132, 0, 295}, + dictWord{137, 0, 51}, + dictWord{9, 11, 222}, + dictWord{ + 10, + 11, + 43, + }, + dictWord{139, 11, 900}, + dictWord{5, 0, 309}, + dictWord{140, 0, 211}, + dictWord{5, 0, 125}, + dictWord{8, 0, 77}, + dictWord{138, 0, 15}, + dictWord{136, 11, 604}, + dictWord{138, 0, 789}, + dictWord{5, 0, 173}, + dictWord{4, 10, 39}, + dictWord{7, 10, 1843}, + dictWord{8, 10, 407}, + dictWord{11, 10, 144}, + dictWord{140, 10, 523}, + dictWord{138, 11, 265}, + dictWord{133, 0, 439}, + dictWord{132, 10, 510}, + dictWord{7, 0, 648}, + dictWord{7, 0, 874}, + dictWord{11, 0, 164}, + dictWord{12, 0, 76}, + dictWord{18, 0, 9}, + dictWord{7, 10, 1980}, + dictWord{10, 10, 487}, + dictWord{138, 10, 809}, + dictWord{12, 0, 111}, + dictWord{14, 0, 294}, + dictWord{19, 0, 45}, + dictWord{13, 10, 260}, + dictWord{146, 10, 63}, + dictWord{133, 11, 549}, + dictWord{134, 10, 570}, + dictWord{4, 0, 8}, + dictWord{7, 0, 1152}, + dictWord{7, 0, 1153}, + dictWord{7, 0, 1715}, + dictWord{9, 0, 374}, + dictWord{10, 0, 478}, + dictWord{139, 0, 648}, + dictWord{135, 0, 1099}, + dictWord{5, 0, 575}, + dictWord{6, 0, 354}, + dictWord{ + 135, + 0, + 701, + }, + dictWord{7, 11, 36}, + dictWord{8, 11, 201}, + dictWord{136, 11, 605}, + dictWord{4, 10, 787}, + dictWord{136, 11, 156}, + dictWord{6, 0, 518}, + dictWord{ + 149, + 11, + 13, + }, + dictWord{140, 11, 224}, + dictWord{134, 0, 702}, + dictWord{132, 10, 516}, + dictWord{5, 11, 724}, + dictWord{10, 11, 305}, + dictWord{11, 11, 151}, + dictWord{12, 11, 33}, + dictWord{12, 11, 121}, + dictWord{12, 11, 381}, + dictWord{17, 11, 3}, + dictWord{17, 11, 27}, + dictWord{17, 11, 78}, + dictWord{18, 11, 18}, + dictWord{19, 11, 54}, + dictWord{149, 11, 5}, + dictWord{8, 0, 87}, + dictWord{4, 11, 523}, + dictWord{5, 11, 638}, + dictWord{11, 10, 887}, + dictWord{14, 10, 365}, + dictWord{ + 142, + 10, + 375, + }, + dictWord{138, 0, 438}, + dictWord{136, 10, 821}, + dictWord{135, 11, 1908}, + dictWord{6, 11, 242}, + dictWord{7, 11, 227}, + dictWord{7, 11, 1581}, + dictWord{8, 11, 104}, + dictWord{9, 11, 113}, + dictWord{9, 11, 220}, + dictWord{9, 11, 427}, + dictWord{10, 11, 74}, + dictWord{10, 11, 239}, + dictWord{11, 11, 579}, + dictWord{11, 11, 1023}, + dictWord{13, 11, 4}, + dictWord{13, 11, 204}, + dictWord{13, 11, 316}, + dictWord{18, 11, 95}, + dictWord{148, 11, 86}, + dictWord{4, 0, 69}, + dictWord{5, 0, 122}, + dictWord{5, 0, 849}, + dictWord{6, 0, 1633}, + dictWord{9, 0, 656}, + dictWord{138, 0, 464}, + dictWord{7, 0, 1802}, + dictWord{4, 10, 10}, + dictWord{ + 139, + 10, + 786, + }, + dictWord{135, 11, 861}, + dictWord{139, 0, 499}, + dictWord{7, 0, 476}, + dictWord{7, 0, 1592}, + dictWord{138, 0, 87}, + dictWord{133, 10, 684}, + dictWord{ + 4, + 0, + 840, + }, + dictWord{134, 10, 27}, + dictWord{142, 0, 283}, + dictWord{6, 0, 1620}, + dictWord{7, 11, 1328}, + dictWord{136, 11, 494}, + dictWord{5, 0, 859}, + dictWord{ + 7, + 0, + 1160, + }, + dictWord{8, 0, 107}, + dictWord{9, 0, 291}, + dictWord{9, 0, 439}, + dictWord{10, 0, 663}, + dictWord{11, 0, 609}, + dictWord{140, 0, 197}, + dictWord{ + 7, + 11, + 1306, + }, + dictWord{8, 11, 505}, + dictWord{9, 11, 482}, + dictWord{10, 11, 126}, + dictWord{11, 11, 225}, + dictWord{12, 11, 347}, + dictWord{12, 11, 449}, + dictWord{ + 13, + 11, + 19, + }, + dictWord{142, 11, 218}, + dictWord{5, 11, 268}, + dictWord{10, 11, 764}, + dictWord{12, 11, 120}, + dictWord{13, 11, 39}, + dictWord{145, 11, 127}, + dictWord{145, 10, 56}, + dictWord{7, 11, 1672}, + dictWord{10, 11, 472}, + dictWord{11, 11, 189}, + dictWord{143, 11, 51}, + dictWord{6, 10, 342}, + dictWord{6, 10, 496}, + dictWord{8, 10, 275}, + dictWord{137, 10, 206}, + dictWord{133, 0, 600}, + dictWord{4, 0, 117}, + dictWord{6, 0, 372}, + dictWord{7, 0, 1905}, + dictWord{142, 0, 323}, + dictWord{4, 10, 909}, + dictWord{5, 10, 940}, + dictWord{135, 11, 1471}, + dictWord{132, 10, 891}, + dictWord{4, 0, 722}, + dictWord{139, 0, 471}, + dictWord{4, 11, 384}, + dictWord{135, 11, 1022}, + dictWord{132, 10, 687}, + dictWord{9, 0, 5}, + dictWord{12, 0, 216}, + dictWord{12, 0, 294}, + dictWord{12, 0, 298}, + dictWord{12, 0, 400}, + dictWord{12, 0, 518}, + dictWord{13, 0, 229}, + dictWord{143, 0, 139}, + dictWord{135, 11, 1703}, + dictWord{7, 11, 1602}, + dictWord{10, 11, 698}, + dictWord{ + 12, + 11, + 212, + }, + dictWord{141, 11, 307}, + dictWord{6, 10, 41}, + dictWord{141, 10, 160}, + dictWord{135, 11, 1077}, + dictWord{9, 11, 159}, + dictWord{11, 11, 28}, + dictWord{140, 11, 603}, + dictWord{4, 0, 514}, + dictWord{7, 0, 1304}, + dictWord{138, 0, 477}, + dictWord{134, 0, 1774}, + dictWord{9, 0, 88}, + dictWord{139, 0, 270}, + dictWord{5, 0, 12}, + dictWord{7, 0, 375}, + dictWord{9, 0, 438}, + dictWord{134, 10, 1718}, + dictWord{132, 11, 515}, + dictWord{136, 10, 778}, + dictWord{8, 11, 632}, + dictWord{8, 11, 697}, + dictWord{137, 11, 854}, + dictWord{6, 0, 362}, + dictWord{6, 0, 997}, + dictWord{146, 0, 51}, + dictWord{7, 0, 816}, + dictWord{7, 0, 1241}, + dictWord{ + 9, + 0, + 283, + }, + dictWord{9, 0, 520}, + dictWord{10, 0, 213}, + dictWord{10, 0, 307}, + dictWord{10, 0, 463}, + dictWord{10, 0, 671}, + dictWord{10, 0, 746}, + dictWord{11, 0, 401}, + dictWord{11, 0, 794}, + dictWord{12, 0, 517}, + dictWord{18, 0, 107}, + dictWord{147, 0, 115}, + dictWord{133, 10, 115}, + dictWord{150, 11, 28}, + dictWord{4, 11, 136}, + dictWord{133, 11, 551}, + dictWord{142, 10, 314}, + dictWord{132, 0, 258}, + dictWord{6, 0, 22}, + dictWord{7, 0, 903}, + dictWord{7, 0, 1963}, + dictWord{8, 0, 639}, + dictWord{138, 0, 577}, + dictWord{5, 0, 681}, + dictWord{8, 0, 782}, + dictWord{13, 0, 130}, + dictWord{17, 0, 84}, + dictWord{5, 10, 193}, + dictWord{140, 10, 178}, + dictWord{ + 9, + 11, + 17, + }, + dictWord{138, 11, 291}, + dictWord{7, 11, 1287}, + dictWord{9, 11, 44}, + dictWord{10, 11, 552}, + dictWord{10, 11, 642}, + dictWord{11, 11, 839}, + dictWord{12, 11, 274}, + dictWord{12, 11, 275}, + dictWord{12, 11, 372}, + dictWord{13, 11, 91}, + dictWord{142, 11, 125}, + dictWord{135, 10, 174}, + dictWord{4, 0, 664}, + dictWord{5, 0, 804}, + dictWord{139, 0, 1013}, + dictWord{134, 0, 942}, + dictWord{6, 0, 1349}, + dictWord{6, 0, 1353}, + dictWord{6, 0, 1450}, + dictWord{7, 11, 1518}, + dictWord{139, 11, 694}, + dictWord{11, 0, 356}, + dictWord{4, 10, 122}, + dictWord{5, 10, 796}, + dictWord{5, 10, 952}, + dictWord{6, 10, 1660}, + dictWord{ + 6, + 10, + 1671, + }, + dictWord{8, 10, 567}, + dictWord{9, 10, 687}, + dictWord{9, 10, 742}, + dictWord{10, 10, 686}, + dictWord{11, 10, 682}, + dictWord{140, 10, 281}, + dictWord{ + 5, + 0, + 32, + }, + dictWord{6, 11, 147}, + dictWord{7, 11, 886}, + dictWord{9, 11, 753}, + dictWord{138, 11, 268}, + dictWord{5, 10, 179}, + dictWord{7, 10, 1095}, + dictWord{ + 135, + 10, + 1213, + }, + dictWord{4, 10, 66}, + dictWord{7, 10, 722}, + dictWord{135, 10, 904}, + dictWord{135, 10, 352}, + dictWord{9, 11, 245}, + dictWord{138, 11, 137}, + dictWord{4, 0, 289}, + dictWord{7, 0, 629}, + dictWord{7, 0, 1698}, + dictWord{7, 0, 1711}, + dictWord{12, 0, 215}, + dictWord{133, 11, 414}, + dictWord{6, 0, 1975}, + dictWord{135, 11, 1762}, + dictWord{6, 0, 450}, + dictWord{136, 0, 109}, + dictWord{141, 10, 35}, + dictWord{134, 11, 599}, + dictWord{136, 0, 705}, + dictWord{ + 133, + 0, + 664, + }, + dictWord{134, 11, 1749}, + dictWord{11, 11, 402}, + dictWord{12, 11, 109}, + dictWord{12, 11, 431}, + dictWord{13, 11, 179}, + dictWord{13, 11, 206}, + dictWord{14, 11, 175}, + dictWord{14, 11, 217}, + dictWord{16, 11, 3}, + dictWord{148, 11, 53}, + dictWord{135, 0, 1238}, + dictWord{134, 11, 1627}, + dictWord{ + 132, + 11, + 488, + }, + dictWord{13, 0, 318}, + dictWord{10, 10, 592}, + dictWord{10, 10, 753}, + dictWord{12, 10, 317}, + dictWord{12, 10, 355}, + dictWord{12, 10, 465}, + dictWord{ + 12, + 10, + 469, + }, + dictWord{12, 10, 560}, + dictWord{140, 10, 578}, + dictWord{133, 10, 564}, + dictWord{132, 11, 83}, + dictWord{140, 11, 676}, + dictWord{6, 0, 1872}, + dictWord{6, 0, 1906}, + dictWord{6, 0, 1907}, + dictWord{9, 0, 934}, + dictWord{9, 0, 956}, + dictWord{9, 0, 960}, + dictWord{9, 0, 996}, + dictWord{12, 0, 794}, + dictWord{ + 12, + 0, + 876, + }, + dictWord{12, 0, 880}, + dictWord{12, 0, 918}, + dictWord{15, 0, 230}, + dictWord{18, 0, 234}, + dictWord{18, 0, 238}, + dictWord{21, 0, 38}, + dictWord{149, 0, 62}, + dictWord{134, 10, 556}, + dictWord{134, 11, 278}, + dictWord{137, 0, 103}, + dictWord{7, 10, 544}, + dictWord{8, 10, 719}, + dictWord{138, 10, 61}, + dictWord{ + 4, + 10, + 5, + }, + dictWord{5, 10, 498}, + dictWord{8, 10, 637}, + dictWord{137, 10, 521}, + dictWord{7, 0, 777}, + dictWord{12, 0, 229}, + dictWord{12, 0, 239}, + dictWord{15, 0, 12}, + dictWord{12, 11, 229}, + dictWord{12, 11, 239}, + dictWord{143, 11, 12}, + dictWord{6, 0, 26}, + dictWord{7, 11, 388}, + dictWord{7, 11, 644}, + dictWord{139, 11, 781}, + dictWord{7, 11, 229}, + dictWord{8, 11, 59}, + dictWord{9, 11, 190}, + dictWord{9, 11, 257}, + dictWord{10, 11, 378}, + dictWord{140, 11, 191}, + dictWord{133, 10, 927}, + dictWord{135, 10, 1441}, + dictWord{4, 10, 893}, + dictWord{5, 10, 780}, + dictWord{133, 10, 893}, + dictWord{4, 0, 414}, + dictWord{5, 0, 467}, + dictWord{9, 0, 654}, + dictWord{10, 0, 451}, + dictWord{12, 0, 59}, + dictWord{141, 0, 375}, + dictWord{142, 0, 173}, + dictWord{135, 0, 17}, + dictWord{7, 0, 1350}, + dictWord{133, 10, 238}, + dictWord{135, 0, 955}, + dictWord{4, 0, 960}, + dictWord{10, 0, 887}, + dictWord{12, 0, 753}, + dictWord{18, 0, 161}, + dictWord{18, 0, 162}, + dictWord{152, 0, 19}, + dictWord{136, 11, 344}, + dictWord{6, 10, 1729}, + dictWord{137, 11, 288}, + dictWord{132, 11, 660}, + dictWord{4, 0, 217}, + dictWord{5, 0, 710}, + dictWord{7, 0, 760}, + dictWord{7, 0, 1926}, + dictWord{9, 0, 428}, + dictWord{9, 0, 708}, + dictWord{10, 0, 254}, + dictWord{10, 0, 296}, + dictWord{10, 0, 720}, + dictWord{11, 0, 109}, + dictWord{ + 11, + 0, + 255, + }, + dictWord{12, 0, 165}, + dictWord{12, 0, 315}, + dictWord{13, 0, 107}, + dictWord{13, 0, 203}, + dictWord{14, 0, 54}, + dictWord{14, 0, 99}, + dictWord{14, 0, 114}, + dictWord{14, 0, 388}, + dictWord{16, 0, 85}, + dictWord{17, 0, 9}, + dictWord{17, 0, 33}, + dictWord{20, 0, 25}, + dictWord{20, 0, 28}, + dictWord{20, 0, 29}, + dictWord{21, 0, 9}, + dictWord{21, 0, 10}, + dictWord{21, 0, 34}, + dictWord{22, 0, 17}, + dictWord{4, 10, 60}, + dictWord{7, 10, 1800}, + dictWord{8, 10, 314}, + dictWord{9, 10, 700}, + dictWord{ + 139, + 10, + 487, + }, + dictWord{7, 11, 1035}, + dictWord{138, 11, 737}, + dictWord{7, 11, 690}, + dictWord{9, 11, 217}, + dictWord{9, 11, 587}, + dictWord{140, 11, 521}, + dictWord{6, 0, 919}, + dictWord{7, 11, 706}, + dictWord{7, 11, 1058}, + dictWord{138, 11, 538}, + dictWord{7, 10, 1853}, + dictWord{138, 10, 437}, + dictWord{ + 136, + 10, + 419, + }, + dictWord{6, 0, 280}, + dictWord{10, 0, 502}, + dictWord{11, 0, 344}, + dictWord{140, 0, 38}, + dictWord{5, 0, 45}, + dictWord{7, 0, 1161}, + dictWord{11, 0, 448}, + dictWord{11, 0, 880}, + dictWord{13, 0, 139}, + dictWord{13, 0, 407}, + dictWord{15, 0, 16}, + dictWord{17, 0, 95}, + dictWord{18, 0, 66}, + dictWord{18, 0, 88}, + dictWord{ + 18, + 0, + 123, + }, + dictWord{149, 0, 7}, + dictWord{11, 11, 92}, + dictWord{11, 11, 196}, + dictWord{11, 11, 409}, + dictWord{11, 11, 450}, + dictWord{11, 11, 666}, + dictWord{ + 11, + 11, + 777, + }, + dictWord{12, 11, 262}, + dictWord{13, 11, 385}, + dictWord{13, 11, 393}, + dictWord{15, 11, 115}, + dictWord{16, 11, 45}, + dictWord{145, 11, 82}, + dictWord{136, 0, 777}, + dictWord{134, 11, 1744}, + dictWord{4, 0, 410}, + dictWord{7, 0, 521}, + dictWord{133, 10, 828}, + dictWord{134, 0, 673}, + dictWord{7, 0, 1110}, + dictWord{7, 0, 1778}, + dictWord{7, 10, 176}, + dictWord{135, 10, 178}, + dictWord{5, 10, 806}, + dictWord{7, 11, 268}, + dictWord{7, 10, 1976}, + dictWord{ + 136, + 11, + 569, + }, + dictWord{4, 11, 733}, + dictWord{9, 11, 194}, + dictWord{10, 11, 92}, + dictWord{11, 11, 198}, + dictWord{12, 11, 84}, + dictWord{12, 11, 87}, + dictWord{ + 13, + 11, + 128, + }, + dictWord{144, 11, 74}, + dictWord{5, 0, 341}, + dictWord{7, 0, 1129}, + dictWord{11, 0, 414}, + dictWord{4, 10, 51}, + dictWord{6, 10, 4}, + dictWord{7, 10, 591}, + dictWord{7, 10, 849}, + dictWord{7, 10, 951}, + dictWord{7, 10, 1613}, + dictWord{7, 10, 1760}, + dictWord{7, 10, 1988}, + dictWord{9, 10, 434}, + dictWord{10, 10, 754}, + dictWord{11, 10, 25}, + dictWord{139, 10, 37}, + dictWord{133, 10, 902}, + dictWord{135, 10, 928}, + dictWord{135, 0, 787}, + dictWord{132, 0, 436}, + dictWord{ + 134, + 10, + 270, + }, + dictWord{7, 0, 1587}, + dictWord{135, 0, 1707}, + dictWord{6, 0, 377}, + dictWord{7, 0, 1025}, + dictWord{9, 0, 613}, + dictWord{145, 0, 104}, + dictWord{ + 7, + 11, + 982, + }, + dictWord{7, 11, 1361}, + dictWord{10, 11, 32}, + dictWord{143, 11, 56}, + dictWord{139, 0, 96}, + dictWord{132, 0, 451}, + dictWord{132, 10, 416}, + dictWord{ + 142, + 10, + 372, + }, + dictWord{5, 10, 152}, + dictWord{5, 10, 197}, + dictWord{7, 11, 306}, + dictWord{7, 10, 340}, + dictWord{7, 10, 867}, + dictWord{10, 10, 548}, + dictWord{ + 10, + 10, + 581, + }, + dictWord{11, 10, 6}, + dictWord{12, 10, 3}, + dictWord{12, 10, 19}, + dictWord{14, 10, 110}, + dictWord{142, 10, 289}, + dictWord{134, 0, 680}, + dictWord{ + 134, + 11, + 609, + }, + dictWord{7, 0, 483}, + dictWord{7, 10, 190}, + dictWord{8, 10, 28}, + dictWord{8, 10, 141}, + dictWord{8, 10, 444}, + dictWord{8, 10, 811}, + dictWord{ + 9, + 10, + 468, + }, + dictWord{11, 10, 334}, + dictWord{12, 10, 24}, + dictWord{12, 10, 386}, + dictWord{140, 10, 576}, + dictWord{10, 0, 916}, + dictWord{133, 10, 757}, + dictWord{ + 5, + 10, + 721, + }, + dictWord{135, 10, 1553}, + dictWord{133, 11, 178}, + dictWord{134, 0, 937}, + dictWord{132, 10, 898}, + dictWord{133, 0, 739}, + dictWord{ + 147, + 0, + 82, + }, + dictWord{135, 0, 663}, + dictWord{146, 0, 128}, + dictWord{5, 10, 277}, + dictWord{141, 10, 247}, + dictWord{134, 0, 1087}, + dictWord{132, 10, 435}, + dictWord{ + 6, + 11, + 381, + }, + dictWord{7, 11, 645}, + dictWord{7, 11, 694}, + dictWord{136, 11, 546}, + dictWord{7, 0, 503}, + dictWord{135, 0, 1885}, + dictWord{6, 0, 1965}, + dictWord{ + 8, + 0, + 925, + }, + dictWord{138, 0, 955}, + dictWord{4, 0, 113}, + dictWord{5, 0, 163}, + dictWord{5, 0, 735}, + dictWord{7, 0, 1009}, + dictWord{9, 0, 9}, + dictWord{9, 0, 771}, + dictWord{12, 0, 90}, + dictWord{13, 0, 138}, + dictWord{13, 0, 410}, + dictWord{143, 0, 128}, + dictWord{4, 0, 324}, + dictWord{138, 0, 104}, + dictWord{7, 0, 460}, + dictWord{ + 5, + 10, + 265, + }, + dictWord{134, 10, 212}, + dictWord{133, 11, 105}, + dictWord{7, 11, 261}, + dictWord{7, 11, 1107}, + dictWord{7, 11, 1115}, + dictWord{7, 11, 1354}, + dictWord{7, 11, 1588}, + dictWord{7, 11, 1705}, + dictWord{7, 11, 1902}, + dictWord{9, 11, 465}, + dictWord{10, 11, 248}, + dictWord{10, 11, 349}, + dictWord{10, 11, 647}, + dictWord{11, 11, 527}, + dictWord{11, 11, 660}, + dictWord{11, 11, 669}, + dictWord{12, 11, 529}, + dictWord{141, 11, 305}, + dictWord{5, 11, 438}, + dictWord{ + 9, + 11, + 694, + }, + dictWord{12, 11, 627}, + dictWord{141, 11, 210}, + dictWord{152, 11, 11}, + dictWord{4, 0, 935}, + dictWord{133, 0, 823}, + dictWord{132, 10, 702}, + dictWord{ + 5, + 0, + 269, + }, + dictWord{7, 0, 434}, + dictWord{7, 0, 891}, + dictWord{8, 0, 339}, + dictWord{9, 0, 702}, + dictWord{11, 0, 594}, + dictWord{11, 0, 718}, + dictWord{17, 0, 100}, + dictWord{5, 10, 808}, + dictWord{135, 10, 2045}, + dictWord{7, 0, 1014}, + dictWord{9, 0, 485}, + dictWord{141, 0, 264}, + dictWord{134, 0, 1713}, + dictWord{7, 0, 1810}, + dictWord{11, 0, 866}, + dictWord{12, 0, 103}, + dictWord{13, 0, 495}, + dictWord{140, 11, 233}, + dictWord{4, 0, 423}, + dictWord{10, 0, 949}, + dictWord{138, 0, 1013}, + dictWord{135, 0, 900}, + dictWord{8, 11, 25}, + dictWord{138, 11, 826}, + dictWord{5, 10, 166}, + dictWord{8, 10, 739}, + dictWord{140, 10, 511}, + dictWord{ + 134, + 0, + 2018, + }, + dictWord{7, 11, 1270}, + dictWord{139, 11, 612}, + dictWord{4, 10, 119}, + dictWord{5, 10, 170}, + dictWord{5, 10, 447}, + dictWord{7, 10, 1708}, + dictWord{ + 7, + 10, + 1889, + }, + dictWord{9, 10, 357}, + dictWord{9, 10, 719}, + dictWord{12, 10, 486}, + dictWord{140, 10, 596}, + dictWord{12, 0, 574}, + dictWord{140, 11, 574}, + dictWord{132, 11, 308}, + dictWord{6, 0, 964}, + dictWord{6, 0, 1206}, + dictWord{134, 0, 1302}, + dictWord{4, 10, 450}, + dictWord{135, 10, 1158}, + dictWord{ + 135, + 11, + 150, + }, + dictWord{136, 11, 649}, + dictWord{14, 0, 213}, + dictWord{148, 0, 38}, + dictWord{9, 11, 45}, + dictWord{9, 11, 311}, + dictWord{141, 11, 42}, + dictWord{ + 134, + 11, + 521, + }, + dictWord{7, 10, 1375}, + dictWord{7, 10, 1466}, + dictWord{138, 10, 331}, + dictWord{132, 10, 754}, + dictWord{5, 11, 339}, + dictWord{7, 11, 1442}, + dictWord{14, 11, 3}, + dictWord{15, 11, 41}, + dictWord{147, 11, 66}, + dictWord{136, 11, 378}, + dictWord{134, 0, 1022}, + dictWord{5, 10, 850}, + dictWord{136, 10, 799}, + dictWord{142, 0, 143}, + dictWord{135, 0, 2029}, + dictWord{134, 11, 1628}, + dictWord{8, 0, 523}, + dictWord{150, 0, 34}, + dictWord{5, 0, 625}, + dictWord{ + 135, + 0, + 1617, + }, + dictWord{7, 0, 275}, + dictWord{7, 10, 238}, + dictWord{7, 10, 2033}, + dictWord{8, 10, 120}, + dictWord{8, 10, 188}, + dictWord{8, 10, 659}, + dictWord{ + 9, + 10, + 598, + }, + dictWord{10, 10, 466}, + dictWord{12, 10, 342}, + dictWord{12, 10, 588}, + dictWord{13, 10, 503}, + dictWord{14, 10, 246}, + dictWord{143, 10, 92}, + dictWord{ + 7, + 0, + 37, + }, + dictWord{8, 0, 425}, + dictWord{8, 0, 693}, + dictWord{9, 0, 720}, + dictWord{10, 0, 380}, + dictWord{10, 0, 638}, + dictWord{11, 0, 273}, + dictWord{11, 0, 473}, + dictWord{12, 0, 61}, + dictWord{143, 0, 43}, + dictWord{135, 11, 829}, + dictWord{135, 0, 1943}, + dictWord{132, 0, 765}, + dictWord{5, 11, 486}, + dictWord{ + 135, + 11, + 1349, + }, + dictWord{7, 11, 1635}, + dictWord{8, 11, 17}, + dictWord{10, 11, 217}, + dictWord{138, 11, 295}, + dictWord{4, 10, 201}, + dictWord{7, 10, 1744}, + dictWord{ + 8, + 10, + 602, + }, + dictWord{11, 10, 247}, + dictWord{11, 10, 826}, + dictWord{145, 10, 65}, + dictWord{138, 11, 558}, + dictWord{11, 0, 551}, + dictWord{142, 0, 159}, + dictWord{8, 10, 164}, + dictWord{146, 10, 62}, + dictWord{139, 11, 176}, + dictWord{132, 0, 168}, + dictWord{136, 0, 1010}, + dictWord{134, 0, 1994}, + dictWord{ + 135, + 0, + 91, + }, + dictWord{138, 0, 532}, + dictWord{135, 10, 1243}, + dictWord{135, 0, 1884}, + dictWord{132, 10, 907}, + dictWord{5, 10, 100}, + dictWord{10, 10, 329}, + dictWord{12, 10, 416}, + dictWord{149, 10, 29}, + dictWord{134, 11, 447}, + dictWord{132, 10, 176}, + dictWord{5, 10, 636}, + dictWord{5, 10, 998}, + dictWord{7, 10, 9}, + dictWord{7, 10, 1508}, + dictWord{8, 10, 26}, + dictWord{9, 10, 317}, + dictWord{9, 10, 358}, + dictWord{10, 10, 210}, + dictWord{10, 10, 292}, + dictWord{10, 10, 533}, + dictWord{11, 10, 555}, + dictWord{12, 10, 526}, + dictWord{12, 10, 607}, + dictWord{13, 10, 263}, + dictWord{13, 10, 459}, + dictWord{142, 10, 271}, + dictWord{ + 4, + 11, + 609, + }, + dictWord{135, 11, 756}, + dictWord{6, 0, 15}, + dictWord{7, 0, 70}, + dictWord{10, 0, 240}, + dictWord{147, 0, 93}, + dictWord{4, 11, 930}, + dictWord{133, 11, 947}, + dictWord{134, 0, 1227}, + dictWord{134, 0, 1534}, + dictWord{133, 11, 939}, + dictWord{133, 11, 962}, + dictWord{5, 11, 651}, + dictWord{8, 11, 170}, + dictWord{ + 9, + 11, + 61, + }, + dictWord{9, 11, 63}, + dictWord{10, 11, 23}, + dictWord{10, 11, 37}, + dictWord{10, 11, 834}, + dictWord{11, 11, 4}, + dictWord{11, 11, 187}, + dictWord{ + 11, + 11, + 281, + }, + dictWord{11, 11, 503}, + dictWord{11, 11, 677}, + dictWord{12, 11, 96}, + dictWord{12, 11, 130}, + dictWord{12, 11, 244}, + dictWord{14, 11, 5}, + dictWord{ + 14, + 11, + 40, + }, + dictWord{14, 11, 162}, + dictWord{14, 11, 202}, + dictWord{146, 11, 133}, + dictWord{4, 11, 406}, + dictWord{5, 11, 579}, + dictWord{12, 11, 492}, + dictWord{ + 150, + 11, + 15, + }, + dictWord{139, 0, 392}, + dictWord{6, 10, 610}, + dictWord{10, 10, 127}, + dictWord{141, 10, 27}, + dictWord{7, 0, 655}, + dictWord{7, 0, 1844}, + dictWord{ + 136, + 10, + 119, + }, + dictWord{4, 0, 145}, + dictWord{6, 0, 176}, + dictWord{7, 0, 395}, + dictWord{137, 0, 562}, + dictWord{132, 0, 501}, + dictWord{140, 11, 145}, + dictWord{ + 136, + 0, + 1019, + }, + dictWord{134, 0, 509}, + dictWord{139, 0, 267}, + dictWord{6, 11, 17}, + dictWord{7, 11, 16}, + dictWord{7, 11, 1001}, + dictWord{7, 11, 1982}, + dictWord{ + 9, + 11, + 886, + }, + dictWord{10, 11, 489}, + dictWord{10, 11, 800}, + dictWord{11, 11, 782}, + dictWord{12, 11, 320}, + dictWord{13, 11, 467}, + dictWord{14, 11, 145}, + dictWord{14, 11, 387}, + dictWord{143, 11, 119}, + dictWord{145, 11, 17}, + dictWord{6, 0, 1099}, + dictWord{133, 11, 458}, + dictWord{7, 11, 1983}, + dictWord{8, 11, 0}, + dictWord{8, 11, 171}, + dictWord{9, 11, 120}, + dictWord{9, 11, 732}, + dictWord{10, 11, 473}, + dictWord{11, 11, 656}, + dictWord{11, 11, 998}, + dictWord{18, 11, 0}, + dictWord{18, 11, 2}, + dictWord{147, 11, 21}, + dictWord{12, 11, 427}, + dictWord{146, 11, 38}, + dictWord{10, 0, 948}, + dictWord{138, 0, 968}, + dictWord{7, 10, 126}, + dictWord{136, 10, 84}, + dictWord{136, 10, 790}, + dictWord{4, 0, 114}, + dictWord{9, 0, 492}, + dictWord{13, 0, 462}, + dictWord{142, 0, 215}, + dictWord{6, 10, 64}, + dictWord{12, 10, 377}, + dictWord{141, 10, 309}, + dictWord{4, 0, 77}, + dictWord{5, 0, 361}, + dictWord{6, 0, 139}, + dictWord{6, 0, 401}, + dictWord{6, 0, 404}, + dictWord{ + 7, + 0, + 413, + }, + dictWord{7, 0, 715}, + dictWord{7, 0, 1716}, + dictWord{11, 0, 279}, + dictWord{12, 0, 179}, + dictWord{12, 0, 258}, + dictWord{13, 0, 244}, + dictWord{142, 0, 358}, + dictWord{134, 0, 1717}, + dictWord{7, 0, 772}, + dictWord{7, 0, 1061}, + dictWord{7, 0, 1647}, + dictWord{8, 0, 82}, + dictWord{11, 0, 250}, + dictWord{11, 0, 607}, + dictWord{12, 0, 311}, + dictWord{12, 0, 420}, + dictWord{13, 0, 184}, + dictWord{13, 0, 367}, + dictWord{7, 10, 1104}, + dictWord{11, 10, 269}, + dictWord{11, 10, 539}, + dictWord{11, 10, 627}, + dictWord{11, 10, 706}, + dictWord{11, 10, 975}, + dictWord{12, 10, 248}, + dictWord{12, 10, 434}, + dictWord{12, 10, 600}, + dictWord{ + 12, + 10, + 622, + }, + dictWord{13, 10, 297}, + dictWord{13, 10, 485}, + dictWord{14, 10, 69}, + dictWord{14, 10, 409}, + dictWord{143, 10, 108}, + dictWord{135, 0, 724}, + dictWord{ + 4, + 11, + 512, + }, + dictWord{4, 11, 519}, + dictWord{133, 11, 342}, + dictWord{134, 0, 1133}, + dictWord{145, 11, 29}, + dictWord{11, 10, 977}, + dictWord{141, 10, 507}, + dictWord{6, 0, 841}, + dictWord{6, 0, 1042}, + dictWord{6, 0, 1194}, + dictWord{10, 0, 993}, + dictWord{140, 0, 1021}, + dictWord{6, 11, 31}, + dictWord{7, 11, 491}, + dictWord{7, 11, 530}, + dictWord{8, 11, 592}, + dictWord{9, 10, 34}, + dictWord{11, 11, 53}, + dictWord{11, 10, 484}, + dictWord{11, 11, 779}, + dictWord{12, 11, 167}, + dictWord{12, 11, 411}, + dictWord{14, 11, 14}, + dictWord{14, 11, 136}, + dictWord{15, 11, 72}, + dictWord{16, 11, 17}, + dictWord{144, 11, 72}, + dictWord{4, 0, 1021}, + dictWord{6, 0, 2037}, + dictWord{133, 11, 907}, + dictWord{7, 0, 373}, + dictWord{8, 0, 335}, + dictWord{8, 0, 596}, + dictWord{9, 0, 488}, + dictWord{6, 10, 1700}, + dictWord{ + 7, + 10, + 293, + }, + dictWord{7, 10, 382}, + dictWord{7, 10, 1026}, + dictWord{7, 10, 1087}, + dictWord{7, 10, 2027}, + dictWord{8, 10, 252}, + dictWord{8, 10, 727}, + dictWord{ + 8, + 10, + 729, + }, + dictWord{9, 10, 30}, + dictWord{9, 10, 199}, + dictWord{9, 10, 231}, + dictWord{9, 10, 251}, + dictWord{9, 10, 334}, + dictWord{9, 10, 361}, + dictWord{9, 10, 712}, + dictWord{10, 10, 55}, + dictWord{10, 10, 60}, + dictWord{10, 10, 232}, + dictWord{10, 10, 332}, + dictWord{10, 10, 384}, + dictWord{10, 10, 396}, + dictWord{ + 10, + 10, + 504, + }, + dictWord{10, 10, 542}, + dictWord{10, 10, 652}, + dictWord{11, 10, 20}, + dictWord{11, 10, 48}, + dictWord{11, 10, 207}, + dictWord{11, 10, 291}, + dictWord{ + 11, + 10, + 298, + }, + dictWord{11, 10, 342}, + dictWord{11, 10, 365}, + dictWord{11, 10, 394}, + dictWord{11, 10, 620}, + dictWord{11, 10, 705}, + dictWord{11, 10, 1017}, + dictWord{12, 10, 123}, + dictWord{12, 10, 340}, + dictWord{12, 10, 406}, + dictWord{12, 10, 643}, + dictWord{13, 10, 61}, + dictWord{13, 10, 269}, + dictWord{ + 13, + 10, + 311, + }, + dictWord{13, 10, 319}, + dictWord{13, 10, 486}, + dictWord{14, 10, 234}, + dictWord{15, 10, 62}, + dictWord{15, 10, 85}, + dictWord{16, 10, 71}, + dictWord{ + 18, + 10, + 119, + }, + dictWord{148, 10, 105}, + dictWord{150, 0, 37}, + dictWord{4, 11, 208}, + dictWord{5, 11, 106}, + dictWord{6, 11, 531}, + dictWord{8, 11, 408}, + dictWord{ + 9, + 11, + 188, + }, + dictWord{138, 11, 572}, + dictWord{132, 0, 564}, + dictWord{6, 0, 513}, + dictWord{135, 0, 1052}, + dictWord{132, 0, 825}, + dictWord{9, 0, 899}, + dictWord{ + 140, + 11, + 441, + }, + dictWord{134, 0, 778}, + dictWord{133, 11, 379}, + dictWord{7, 0, 1417}, + dictWord{12, 0, 382}, + dictWord{17, 0, 48}, + dictWord{152, 0, 12}, + dictWord{ + 132, + 11, + 241, + }, + dictWord{7, 0, 1116}, + dictWord{6, 10, 379}, + dictWord{7, 10, 270}, + dictWord{8, 10, 176}, + dictWord{8, 10, 183}, + dictWord{9, 10, 432}, + dictWord{ + 9, + 10, + 661, + }, + dictWord{12, 10, 247}, + dictWord{12, 10, 617}, + dictWord{146, 10, 125}, + dictWord{5, 10, 792}, + dictWord{133, 10, 900}, + dictWord{6, 0, 545}, + dictWord{ + 7, + 0, + 565, + }, + dictWord{7, 0, 1669}, + dictWord{10, 0, 114}, + dictWord{11, 0, 642}, + dictWord{140, 0, 618}, + dictWord{133, 0, 5}, + dictWord{138, 11, 7}, + dictWord{ + 132, + 11, + 259, + }, + dictWord{135, 0, 192}, + dictWord{134, 0, 701}, + dictWord{136, 0, 763}, + dictWord{135, 10, 1979}, + dictWord{4, 10, 901}, + dictWord{133, 10, 776}, + dictWord{10, 0, 755}, + dictWord{147, 0, 29}, + dictWord{133, 0, 759}, + dictWord{4, 11, 173}, + dictWord{5, 11, 312}, + dictWord{5, 11, 512}, + dictWord{135, 11, 1285}, + dictWord{7, 11, 1603}, + dictWord{7, 11, 1691}, + dictWord{9, 11, 464}, + dictWord{11, 11, 195}, + dictWord{12, 11, 279}, + dictWord{12, 11, 448}, + dictWord{ + 14, + 11, + 11, + }, + dictWord{147, 11, 102}, + dictWord{7, 0, 370}, + dictWord{7, 0, 1007}, + dictWord{7, 0, 1177}, + dictWord{135, 0, 1565}, + dictWord{135, 0, 1237}, + dictWord{ + 4, + 0, + 87, + }, + dictWord{5, 0, 250}, + dictWord{141, 0, 298}, + dictWord{4, 11, 452}, + dictWord{5, 11, 583}, + dictWord{5, 11, 817}, + dictWord{6, 11, 433}, + dictWord{7, 11, 593}, + dictWord{7, 11, 720}, + dictWord{7, 11, 1378}, + dictWord{8, 11, 161}, + dictWord{9, 11, 284}, + dictWord{10, 11, 313}, + dictWord{139, 11, 886}, + dictWord{4, 11, 547}, + dictWord{135, 11, 1409}, + dictWord{136, 11, 722}, + dictWord{4, 10, 37}, + dictWord{5, 10, 334}, + dictWord{135, 10, 1253}, + dictWord{132, 10, 508}, + dictWord{ + 12, + 0, + 107, + }, + dictWord{146, 0, 31}, + dictWord{8, 11, 420}, + dictWord{139, 11, 193}, + dictWord{135, 0, 814}, + dictWord{135, 11, 409}, + dictWord{140, 0, 991}, + dictWord{4, 0, 57}, + dictWord{7, 0, 1195}, + dictWord{7, 0, 1438}, + dictWord{7, 0, 1548}, + dictWord{7, 0, 1835}, + dictWord{7, 0, 1904}, + dictWord{9, 0, 757}, + dictWord{ + 10, + 0, + 604, + }, + dictWord{139, 0, 519}, + dictWord{132, 0, 540}, + dictWord{138, 11, 308}, + dictWord{132, 10, 533}, + dictWord{136, 0, 608}, + dictWord{144, 11, 65}, + dictWord{4, 0, 1014}, + dictWord{134, 0, 2029}, + dictWord{4, 0, 209}, + dictWord{7, 0, 902}, + dictWord{5, 11, 1002}, + dictWord{136, 11, 745}, + dictWord{134, 0, 2030}, + dictWord{6, 0, 303}, + dictWord{7, 0, 335}, + dictWord{7, 0, 1437}, + dictWord{7, 0, 1668}, + dictWord{8, 0, 553}, + dictWord{8, 0, 652}, + dictWord{8, 0, 656}, + dictWord{ + 9, + 0, + 558, + }, + dictWord{11, 0, 743}, + dictWord{149, 0, 18}, + dictWord{5, 11, 575}, + dictWord{6, 11, 354}, + dictWord{135, 11, 701}, + dictWord{4, 11, 239}, + dictWord{ + 6, + 11, + 477, + }, + dictWord{7, 11, 1607}, + dictWord{11, 11, 68}, + dictWord{139, 11, 617}, + dictWord{132, 0, 559}, + dictWord{8, 0, 527}, + dictWord{18, 0, 60}, + dictWord{ + 147, + 0, + 24, + }, + dictWord{133, 10, 920}, + dictWord{138, 0, 511}, + dictWord{133, 0, 1017}, + dictWord{133, 0, 675}, + dictWord{138, 10, 391}, + dictWord{11, 0, 156}, + dictWord{135, 10, 1952}, + dictWord{138, 11, 369}, + dictWord{132, 11, 367}, + dictWord{133, 0, 709}, + dictWord{6, 0, 698}, + dictWord{134, 0, 887}, + dictWord{ + 142, + 10, + 126, + }, + dictWord{134, 0, 1745}, + dictWord{132, 10, 483}, + dictWord{13, 11, 299}, + dictWord{142, 11, 75}, + dictWord{133, 0, 714}, + dictWord{7, 0, 8}, + dictWord{ + 136, + 0, + 206, + }, + dictWord{138, 10, 480}, + dictWord{4, 11, 694}, + dictWord{9, 10, 495}, + dictWord{146, 10, 104}, + dictWord{7, 11, 1248}, + dictWord{11, 11, 621}, + dictWord{139, 11, 702}, + dictWord{140, 11, 687}, + dictWord{132, 0, 776}, + dictWord{139, 10, 1009}, + dictWord{135, 0, 1272}, + dictWord{134, 0, 1059}, + dictWord{ + 8, + 10, + 653, + }, + dictWord{13, 10, 93}, + dictWord{147, 10, 14}, + dictWord{135, 11, 213}, + dictWord{136, 0, 406}, + dictWord{133, 10, 172}, + dictWord{132, 0, 947}, + dictWord{8, 0, 175}, + dictWord{10, 0, 168}, + dictWord{138, 0, 573}, + dictWord{132, 0, 870}, + dictWord{6, 0, 1567}, + dictWord{151, 11, 28}, + dictWord{ + 134, + 11, + 472, + }, + dictWord{5, 10, 260}, + dictWord{136, 11, 132}, + dictWord{4, 11, 751}, + dictWord{11, 11, 390}, + dictWord{140, 11, 32}, + dictWord{4, 11, 409}, + dictWord{ + 133, + 11, + 78, + }, + dictWord{12, 0, 554}, + dictWord{6, 11, 473}, + dictWord{145, 11, 105}, + dictWord{133, 0, 784}, + dictWord{8, 0, 908}, + dictWord{136, 11, 306}, + dictWord{139, 0, 882}, + dictWord{6, 0, 358}, + dictWord{7, 0, 1393}, + dictWord{8, 0, 396}, + dictWord{10, 0, 263}, + dictWord{14, 0, 154}, + dictWord{16, 0, 48}, + dictWord{ + 17, + 0, + 8, + }, + dictWord{7, 11, 1759}, + dictWord{8, 11, 396}, + dictWord{10, 11, 263}, + dictWord{14, 11, 154}, + dictWord{16, 11, 48}, + dictWord{145, 11, 8}, + dictWord{ + 13, + 11, + 163, + }, + dictWord{13, 11, 180}, + dictWord{18, 11, 78}, + dictWord{148, 11, 35}, + dictWord{14, 0, 32}, + dictWord{18, 0, 85}, + dictWord{20, 0, 2}, + dictWord{152, 0, 16}, + dictWord{7, 0, 228}, + dictWord{10, 0, 770}, + dictWord{8, 10, 167}, + dictWord{8, 10, 375}, + dictWord{9, 10, 82}, + dictWord{9, 10, 561}, + dictWord{138, 10, 620}, + dictWord{132, 0, 845}, + dictWord{9, 0, 14}, + dictWord{9, 0, 441}, + dictWord{10, 0, 306}, + dictWord{139, 0, 9}, + dictWord{11, 0, 966}, + dictWord{12, 0, 287}, + dictWord{ + 13, + 0, + 342, + }, + dictWord{13, 0, 402}, + dictWord{15, 0, 110}, + dictWord{15, 0, 163}, + dictWord{8, 10, 194}, + dictWord{136, 10, 756}, + dictWord{134, 0, 1578}, + dictWord{ + 4, + 0, + 967, + }, + dictWord{6, 0, 1820}, + dictWord{6, 0, 1847}, + dictWord{140, 0, 716}, + dictWord{136, 0, 594}, + dictWord{7, 0, 1428}, + dictWord{7, 0, 1640}, + dictWord{ + 7, + 0, + 1867, + }, + dictWord{9, 0, 169}, + dictWord{9, 0, 182}, + dictWord{9, 0, 367}, + dictWord{9, 0, 478}, + dictWord{9, 0, 506}, + dictWord{9, 0, 551}, + dictWord{9, 0, 557}, + dictWord{ + 9, + 0, + 648, + }, + dictWord{9, 0, 697}, + dictWord{9, 0, 705}, + dictWord{9, 0, 725}, + dictWord{9, 0, 787}, + dictWord{9, 0, 794}, + dictWord{10, 0, 198}, + dictWord{10, 0, 214}, + dictWord{10, 0, 267}, + dictWord{10, 0, 275}, + dictWord{10, 0, 456}, + dictWord{10, 0, 551}, + dictWord{10, 0, 561}, + dictWord{10, 0, 613}, + dictWord{10, 0, 627}, + dictWord{ + 10, + 0, + 668, + }, + dictWord{10, 0, 675}, + dictWord{10, 0, 691}, + dictWord{10, 0, 695}, + dictWord{10, 0, 707}, + dictWord{10, 0, 715}, + dictWord{11, 0, 183}, + dictWord{ + 11, + 0, + 201, + }, + dictWord{11, 0, 244}, + dictWord{11, 0, 262}, + dictWord{11, 0, 352}, + dictWord{11, 0, 439}, + dictWord{11, 0, 493}, + dictWord{11, 0, 572}, + dictWord{11, 0, 591}, + dictWord{11, 0, 608}, + dictWord{11, 0, 611}, + dictWord{11, 0, 646}, + dictWord{11, 0, 674}, + dictWord{11, 0, 711}, + dictWord{11, 0, 751}, + dictWord{11, 0, 761}, + dictWord{11, 0, 776}, + dictWord{11, 0, 785}, + dictWord{11, 0, 850}, + dictWord{11, 0, 853}, + dictWord{11, 0, 862}, + dictWord{11, 0, 865}, + dictWord{11, 0, 868}, + dictWord{ + 11, + 0, + 875, + }, + dictWord{11, 0, 898}, + dictWord{11, 0, 902}, + dictWord{11, 0, 903}, + dictWord{11, 0, 910}, + dictWord{11, 0, 932}, + dictWord{11, 0, 942}, + dictWord{ + 11, + 0, + 957, + }, + dictWord{11, 0, 967}, + dictWord{11, 0, 972}, + dictWord{12, 0, 148}, + dictWord{12, 0, 195}, + dictWord{12, 0, 220}, + dictWord{12, 0, 237}, + dictWord{12, 0, 318}, + dictWord{12, 0, 339}, + dictWord{12, 0, 393}, + dictWord{12, 0, 445}, + dictWord{12, 0, 450}, + dictWord{12, 0, 474}, + dictWord{12, 0, 505}, + dictWord{12, 0, 509}, + dictWord{12, 0, 533}, + dictWord{12, 0, 591}, + dictWord{12, 0, 594}, + dictWord{12, 0, 597}, + dictWord{12, 0, 621}, + dictWord{12, 0, 633}, + dictWord{12, 0, 642}, + dictWord{ + 13, + 0, + 59, + }, + dictWord{13, 0, 60}, + dictWord{13, 0, 145}, + dictWord{13, 0, 239}, + dictWord{13, 0, 250}, + dictWord{13, 0, 329}, + dictWord{13, 0, 344}, + dictWord{13, 0, 365}, + dictWord{13, 0, 372}, + dictWord{13, 0, 387}, + dictWord{13, 0, 403}, + dictWord{13, 0, 414}, + dictWord{13, 0, 456}, + dictWord{13, 0, 470}, + dictWord{13, 0, 478}, + dictWord{13, 0, 483}, + dictWord{13, 0, 489}, + dictWord{14, 0, 55}, + dictWord{14, 0, 57}, + dictWord{14, 0, 81}, + dictWord{14, 0, 90}, + dictWord{14, 0, 148}, + dictWord{ + 14, + 0, + 239, + }, + dictWord{14, 0, 266}, + dictWord{14, 0, 321}, + dictWord{14, 0, 326}, + dictWord{14, 0, 327}, + dictWord{14, 0, 330}, + dictWord{14, 0, 347}, + dictWord{14, 0, 355}, + dictWord{14, 0, 401}, + dictWord{14, 0, 404}, + dictWord{14, 0, 411}, + dictWord{14, 0, 414}, + dictWord{14, 0, 416}, + dictWord{14, 0, 420}, + dictWord{15, 0, 61}, + dictWord{15, 0, 74}, + dictWord{15, 0, 87}, + dictWord{15, 0, 88}, + dictWord{15, 0, 94}, + dictWord{15, 0, 96}, + dictWord{15, 0, 116}, + dictWord{15, 0, 149}, + dictWord{15, 0, 154}, + dictWord{16, 0, 50}, + dictWord{16, 0, 63}, + dictWord{16, 0, 73}, + dictWord{17, 0, 2}, + dictWord{17, 0, 66}, + dictWord{17, 0, 92}, + dictWord{17, 0, 103}, + dictWord{ + 17, + 0, + 112, + }, + dictWord{17, 0, 120}, + dictWord{18, 0, 50}, + dictWord{18, 0, 54}, + dictWord{18, 0, 82}, + dictWord{18, 0, 86}, + dictWord{18, 0, 90}, + dictWord{18, 0, 111}, + dictWord{ + 18, + 0, + 115, + }, + dictWord{18, 0, 156}, + dictWord{19, 0, 40}, + dictWord{19, 0, 79}, + dictWord{20, 0, 78}, + dictWord{21, 0, 22}, + dictWord{135, 11, 883}, + dictWord{5, 0, 161}, + dictWord{135, 0, 839}, + dictWord{4, 0, 782}, + dictWord{13, 11, 293}, + dictWord{142, 11, 56}, + dictWord{133, 11, 617}, + dictWord{139, 11, 50}, + dictWord{ + 135, + 10, + 22, + }, + dictWord{145, 0, 64}, + dictWord{5, 10, 639}, + dictWord{7, 10, 1249}, + dictWord{139, 10, 896}, + dictWord{138, 0, 998}, + dictWord{135, 11, 2042}, + dictWord{ + 4, + 11, + 546, + }, + dictWord{142, 11, 233}, + dictWord{6, 0, 1043}, + dictWord{134, 0, 1574}, + dictWord{134, 0, 1496}, + dictWord{4, 10, 102}, + dictWord{7, 10, 815}, + dictWord{7, 10, 1699}, + dictWord{139, 10, 964}, + dictWord{12, 0, 781}, + dictWord{142, 0, 461}, + dictWord{4, 11, 313}, + dictWord{133, 11, 577}, + dictWord{ + 6, + 0, + 639, + }, + dictWord{6, 0, 1114}, + dictWord{137, 0, 817}, + dictWord{8, 11, 184}, + dictWord{141, 11, 433}, + dictWord{7, 0, 1814}, + dictWord{135, 11, 935}, + dictWord{ + 10, + 0, + 997, + }, + dictWord{140, 0, 958}, + dictWord{4, 0, 812}, + dictWord{137, 11, 625}, + dictWord{132, 10, 899}, + dictWord{136, 10, 795}, + dictWord{5, 11, 886}, + dictWord{6, 11, 46}, + dictWord{6, 11, 1790}, + dictWord{7, 11, 14}, + dictWord{7, 11, 732}, + dictWord{7, 11, 1654}, + dictWord{8, 11, 95}, + dictWord{8, 11, 327}, + dictWord{ + 8, + 11, + 616, + }, + dictWord{10, 11, 598}, + dictWord{10, 11, 769}, + dictWord{11, 11, 134}, + dictWord{11, 11, 747}, + dictWord{12, 11, 378}, + dictWord{142, 11, 97}, + dictWord{136, 0, 139}, + dictWord{6, 10, 52}, + dictWord{9, 10, 104}, + dictWord{9, 10, 559}, + dictWord{12, 10, 308}, + dictWord{147, 10, 87}, + dictWord{133, 11, 1021}, + dictWord{132, 10, 604}, + dictWord{132, 10, 301}, + dictWord{136, 10, 779}, + dictWord{7, 0, 643}, + dictWord{136, 0, 236}, + dictWord{132, 11, 153}, + dictWord{ + 134, + 0, + 1172, + }, + dictWord{147, 10, 32}, + dictWord{133, 11, 798}, + dictWord{6, 0, 1338}, + dictWord{132, 11, 587}, + dictWord{6, 11, 598}, + dictWord{7, 11, 42}, + dictWord{ + 8, + 11, + 695, + }, + dictWord{10, 11, 212}, + dictWord{11, 11, 158}, + dictWord{14, 11, 196}, + dictWord{145, 11, 85}, + dictWord{135, 10, 508}, + dictWord{5, 11, 957}, + dictWord{5, 11, 1008}, + dictWord{135, 11, 249}, + dictWord{4, 11, 129}, + dictWord{135, 11, 465}, + dictWord{5, 0, 54}, + dictWord{7, 11, 470}, + dictWord{7, 11, 1057}, + dictWord{7, 11, 1201}, + dictWord{9, 11, 755}, + dictWord{11, 11, 906}, + dictWord{140, 11, 527}, + dictWord{7, 11, 908}, + dictWord{146, 11, 7}, + dictWord{ + 5, + 11, + 148, + }, + dictWord{136, 11, 450}, + dictWord{144, 11, 1}, + dictWord{4, 0, 256}, + dictWord{135, 0, 1488}, + dictWord{9, 0, 351}, + dictWord{6, 10, 310}, + dictWord{ + 7, + 10, + 1849, + }, + dictWord{8, 10, 72}, + dictWord{8, 10, 272}, + dictWord{8, 10, 431}, + dictWord{9, 10, 12}, + dictWord{10, 10, 563}, + dictWord{10, 10, 630}, + dictWord{ + 10, + 10, + 796, + }, + dictWord{10, 10, 810}, + dictWord{11, 10, 367}, + dictWord{11, 10, 599}, + dictWord{11, 10, 686}, + dictWord{140, 10, 672}, + dictWord{6, 0, 1885}, + dictWord{ + 6, + 0, + 1898, + }, + dictWord{6, 0, 1899}, + dictWord{140, 0, 955}, + dictWord{4, 0, 714}, + dictWord{133, 0, 469}, + dictWord{6, 0, 1270}, + dictWord{134, 0, 1456}, + dictWord{132, 0, 744}, + dictWord{6, 0, 313}, + dictWord{7, 10, 537}, + dictWord{8, 10, 64}, + dictWord{9, 10, 127}, + dictWord{10, 10, 496}, + dictWord{12, 10, 510}, + dictWord{141, 10, 384}, + dictWord{4, 11, 217}, + dictWord{4, 10, 244}, + dictWord{5, 11, 710}, + dictWord{7, 10, 233}, + dictWord{7, 11, 1926}, + dictWord{9, 11, 428}, + dictWord{9, 11, 708}, + dictWord{10, 11, 254}, + dictWord{10, 11, 296}, + dictWord{10, 11, 720}, + dictWord{11, 11, 109}, + dictWord{11, 11, 255}, + dictWord{12, 11, 165}, + dictWord{12, 11, 315}, + dictWord{13, 11, 107}, + dictWord{13, 11, 203}, + dictWord{14, 11, 54}, + dictWord{14, 11, 99}, + dictWord{14, 11, 114}, + dictWord{ + 14, + 11, + 388, + }, + dictWord{16, 11, 85}, + dictWord{17, 11, 9}, + dictWord{17, 11, 33}, + dictWord{20, 11, 25}, + dictWord{20, 11, 28}, + dictWord{20, 11, 29}, + dictWord{21, 11, 9}, + dictWord{21, 11, 10}, + dictWord{21, 11, 34}, + dictWord{150, 11, 17}, + dictWord{138, 0, 402}, + dictWord{7, 0, 969}, + dictWord{146, 0, 55}, + dictWord{8, 0, 50}, + dictWord{ + 137, + 0, + 624, + }, + dictWord{134, 0, 1355}, + dictWord{132, 0, 572}, + dictWord{134, 10, 1650}, + dictWord{10, 10, 702}, + dictWord{139, 10, 245}, + dictWord{ + 10, + 0, + 847, + }, + dictWord{142, 0, 445}, + dictWord{6, 0, 43}, + dictWord{7, 0, 38}, + dictWord{8, 0, 248}, + dictWord{138, 0, 513}, + dictWord{133, 0, 369}, + dictWord{137, 10, 338}, + dictWord{133, 0, 766}, + dictWord{133, 0, 363}, + dictWord{133, 10, 896}, + dictWord{8, 11, 392}, + dictWord{11, 11, 54}, + dictWord{13, 11, 173}, + dictWord{ + 13, + 11, + 294, + }, + dictWord{148, 11, 7}, + dictWord{134, 0, 678}, + dictWord{7, 11, 1230}, + dictWord{136, 11, 531}, + dictWord{6, 0, 258}, + dictWord{140, 0, 409}, + dictWord{ + 5, + 0, + 249, + }, + dictWord{148, 0, 82}, + dictWord{7, 10, 1117}, + dictWord{136, 10, 539}, + dictWord{5, 0, 393}, + dictWord{6, 0, 378}, + dictWord{7, 0, 1981}, + dictWord{9, 0, 32}, + dictWord{9, 0, 591}, + dictWord{10, 0, 685}, + dictWord{10, 0, 741}, + dictWord{142, 0, 382}, + dictWord{133, 0, 788}, + dictWord{134, 0, 1281}, + dictWord{ + 134, + 0, + 1295, + }, + dictWord{7, 0, 1968}, + dictWord{141, 0, 509}, + dictWord{4, 0, 61}, + dictWord{5, 0, 58}, + dictWord{5, 0, 171}, + dictWord{5, 0, 683}, + dictWord{6, 0, 291}, + dictWord{ + 6, + 0, + 566, + }, + dictWord{7, 0, 1650}, + dictWord{11, 0, 523}, + dictWord{12, 0, 273}, + dictWord{12, 0, 303}, + dictWord{15, 0, 39}, + dictWord{143, 0, 111}, + dictWord{ + 6, + 0, + 706, + }, + dictWord{134, 0, 1283}, + dictWord{134, 0, 589}, + dictWord{135, 11, 1433}, + dictWord{133, 11, 435}, + dictWord{7, 0, 1059}, + dictWord{13, 0, 54}, + dictWord{ + 5, + 10, + 4, + }, + dictWord{5, 10, 810}, + dictWord{6, 10, 13}, + dictWord{6, 10, 538}, + dictWord{6, 10, 1690}, + dictWord{6, 10, 1726}, + dictWord{7, 10, 1819}, + dictWord{ + 8, + 10, + 148, + }, + dictWord{8, 10, 696}, + dictWord{8, 10, 791}, + dictWord{12, 10, 125}, + dictWord{143, 10, 9}, + dictWord{135, 10, 1268}, + dictWord{5, 11, 85}, + dictWord{ + 6, + 11, + 419, + }, + dictWord{7, 11, 134}, + dictWord{7, 11, 305}, + dictWord{7, 11, 361}, + dictWord{7, 11, 1337}, + dictWord{8, 11, 71}, + dictWord{140, 11, 519}, + dictWord{ + 137, + 0, + 824, + }, + dictWord{140, 11, 688}, + dictWord{5, 11, 691}, + dictWord{7, 11, 345}, + dictWord{7, 10, 1385}, + dictWord{9, 11, 94}, + dictWord{11, 10, 582}, + dictWord{ + 11, + 10, + 650, + }, + dictWord{11, 10, 901}, + dictWord{11, 10, 949}, + dictWord{12, 11, 169}, + dictWord{12, 10, 232}, + dictWord{12, 10, 236}, + dictWord{13, 10, 413}, + dictWord{13, 10, 501}, + dictWord{146, 10, 116}, + dictWord{4, 0, 917}, + dictWord{133, 0, 1005}, + dictWord{7, 0, 1598}, + dictWord{5, 11, 183}, + dictWord{6, 11, 582}, + dictWord{9, 11, 344}, + dictWord{10, 11, 679}, + dictWord{140, 11, 435}, + dictWord{4, 10, 925}, + dictWord{5, 10, 803}, + dictWord{8, 10, 698}, + dictWord{ + 138, + 10, + 828, + }, + dictWord{132, 0, 919}, + dictWord{135, 11, 511}, + dictWord{139, 10, 992}, + dictWord{4, 0, 255}, + dictWord{5, 0, 302}, + dictWord{6, 0, 132}, + dictWord{ + 7, + 0, + 128, + }, + dictWord{7, 0, 283}, + dictWord{7, 0, 1299}, + dictWord{10, 0, 52}, + dictWord{10, 0, 514}, + dictWord{11, 0, 925}, + dictWord{13, 0, 92}, + dictWord{142, 0, 309}, + dictWord{134, 0, 1369}, + dictWord{135, 10, 1847}, + dictWord{134, 0, 328}, + dictWord{7, 11, 1993}, + dictWord{136, 11, 684}, + dictWord{133, 10, 383}, + dictWord{137, 0, 173}, + dictWord{134, 11, 583}, + dictWord{134, 0, 1411}, + dictWord{19, 0, 65}, + dictWord{5, 11, 704}, + dictWord{8, 11, 357}, + dictWord{10, 11, 745}, + dictWord{14, 11, 426}, + dictWord{17, 11, 94}, + dictWord{147, 11, 57}, + dictWord{9, 10, 660}, + dictWord{138, 10, 347}, + dictWord{4, 11, 179}, + dictWord{5, 11, 198}, + dictWord{133, 11, 697}, + dictWord{7, 11, 347}, + dictWord{7, 11, 971}, + dictWord{8, 11, 181}, + dictWord{138, 11, 711}, + dictWord{141, 0, 442}, + dictWord{ + 11, + 0, + 842, + }, + dictWord{11, 0, 924}, + dictWord{13, 0, 317}, + dictWord{13, 0, 370}, + dictWord{13, 0, 469}, + dictWord{13, 0, 471}, + dictWord{14, 0, 397}, + dictWord{18, 0, 69}, + dictWord{18, 0, 145}, + dictWord{7, 10, 572}, + dictWord{9, 10, 592}, + dictWord{11, 10, 680}, + dictWord{12, 10, 356}, + dictWord{140, 10, 550}, + dictWord{14, 11, 19}, + dictWord{14, 11, 28}, + dictWord{144, 11, 29}, + dictWord{136, 0, 534}, + dictWord{4, 11, 243}, + dictWord{5, 11, 203}, + dictWord{7, 11, 19}, + dictWord{7, 11, 71}, + dictWord{7, 11, 113}, + dictWord{10, 11, 405}, + dictWord{11, 11, 357}, + dictWord{142, 11, 240}, + dictWord{6, 0, 210}, + dictWord{10, 0, 845}, + dictWord{138, 0, 862}, + dictWord{7, 11, 1351}, + dictWord{9, 11, 581}, + dictWord{10, 11, 639}, + dictWord{11, 11, 453}, + dictWord{140, 11, 584}, + dictWord{7, 11, 1450}, + dictWord{ + 139, + 11, + 99, + }, + dictWord{10, 0, 892}, + dictWord{12, 0, 719}, + dictWord{144, 0, 105}, + dictWord{4, 0, 284}, + dictWord{6, 0, 223}, + dictWord{134, 11, 492}, + dictWord{5, 11, 134}, + dictWord{6, 11, 408}, + dictWord{6, 11, 495}, + dictWord{135, 11, 1593}, + dictWord{136, 0, 529}, + dictWord{137, 0, 807}, + dictWord{4, 0, 218}, + dictWord{7, 0, 526}, + dictWord{143, 0, 137}, + dictWord{6, 0, 1444}, + dictWord{142, 11, 4}, + dictWord{132, 11, 665}, + dictWord{4, 0, 270}, + dictWord{5, 0, 192}, + dictWord{6, 0, 332}, + dictWord{7, 0, 1322}, + dictWord{4, 11, 248}, + dictWord{7, 11, 137}, + dictWord{137, 11, 349}, + dictWord{140, 0, 661}, + dictWord{7, 0, 1517}, + dictWord{11, 0, 597}, + dictWord{14, 0, 76}, + dictWord{14, 0, 335}, + dictWord{20, 0, 33}, + dictWord{7, 10, 748}, + dictWord{139, 10, 700}, + dictWord{5, 11, 371}, + dictWord{135, 11, 563}, + dictWord{146, 11, 57}, + dictWord{133, 10, 127}, + dictWord{133, 0, 418}, + dictWord{4, 11, 374}, + dictWord{7, 11, 547}, + dictWord{7, 11, 1700}, + dictWord{7, 11, 1833}, + dictWord{139, 11, 858}, + dictWord{6, 10, 198}, + dictWord{140, 10, 83}, + dictWord{7, 11, 1812}, + dictWord{13, 11, 259}, + dictWord{13, 11, 356}, + dictWord{ + 14, + 11, + 242, + }, + dictWord{147, 11, 114}, + dictWord{7, 0, 379}, + dictWord{8, 0, 481}, + dictWord{9, 0, 377}, + dictWord{5, 10, 276}, + dictWord{6, 10, 55}, + dictWord{ + 135, + 10, + 1369, + }, + dictWord{138, 11, 286}, + dictWord{5, 0, 1003}, + dictWord{6, 0, 149}, + dictWord{6, 10, 1752}, + dictWord{136, 10, 726}, + dictWord{8, 0, 262}, + dictWord{ + 9, + 0, + 627, + }, + dictWord{10, 0, 18}, + dictWord{11, 0, 214}, + dictWord{11, 0, 404}, + dictWord{11, 0, 457}, + dictWord{11, 0, 780}, + dictWord{11, 0, 913}, + dictWord{13, 0, 401}, + dictWord{14, 0, 200}, + dictWord{6, 11, 1647}, + dictWord{7, 11, 1552}, + dictWord{7, 11, 2010}, + dictWord{9, 11, 494}, + dictWord{137, 11, 509}, + dictWord{ + 135, + 0, + 742, + }, + dictWord{136, 0, 304}, + dictWord{132, 0, 142}, + dictWord{133, 10, 764}, + dictWord{6, 10, 309}, + dictWord{7, 10, 331}, + dictWord{138, 10, 550}, + dictWord{135, 10, 1062}, + dictWord{6, 11, 123}, + dictWord{7, 11, 214}, + dictWord{7, 10, 986}, + dictWord{9, 11, 728}, + dictWord{10, 11, 157}, + dictWord{11, 11, 346}, + dictWord{11, 11, 662}, + dictWord{143, 11, 106}, + dictWord{135, 10, 1573}, + dictWord{7, 0, 925}, + dictWord{137, 0, 799}, + dictWord{4, 0, 471}, + dictWord{5, 0, 51}, + dictWord{6, 0, 602}, + dictWord{8, 0, 484}, + dictWord{138, 0, 195}, + dictWord{136, 0, 688}, + dictWord{132, 0, 697}, + dictWord{6, 0, 1169}, + dictWord{6, 0, 1241}, + dictWord{6, 10, 194}, + dictWord{7, 10, 133}, + dictWord{10, 10, 493}, + dictWord{10, 10, 570}, + dictWord{139, 10, 664}, + dictWord{140, 0, 751}, + dictWord{7, 0, 929}, + dictWord{10, 0, 452}, + dictWord{11, 0, 878}, + dictWord{16, 0, 33}, + dictWord{5, 10, 24}, + dictWord{5, 10, 569}, + dictWord{6, 10, 3}, + dictWord{6, 10, 119}, + dictWord{ + 6, + 10, + 143, + }, + dictWord{6, 10, 440}, + dictWord{7, 10, 599}, + dictWord{7, 10, 1686}, + dictWord{7, 10, 1854}, + dictWord{8, 10, 424}, + dictWord{9, 10, 43}, + dictWord{ + 9, + 10, + 584, + }, + dictWord{9, 10, 760}, + dictWord{10, 10, 328}, + dictWord{11, 10, 159}, + dictWord{11, 10, 253}, + dictWord{12, 10, 487}, + dictWord{140, 10, 531}, + dictWord{ + 4, + 11, + 707, + }, + dictWord{13, 11, 106}, + dictWord{18, 11, 49}, + dictWord{147, 11, 41}, + dictWord{5, 0, 221}, + dictWord{5, 11, 588}, + dictWord{134, 11, 393}, + dictWord{134, 0, 1437}, + dictWord{6, 11, 211}, + dictWord{7, 11, 1690}, + dictWord{11, 11, 486}, + dictWord{140, 11, 369}, + dictWord{5, 10, 14}, + dictWord{5, 10, 892}, + dictWord{6, 10, 283}, + dictWord{7, 10, 234}, + dictWord{136, 10, 537}, + dictWord{4, 0, 988}, + dictWord{136, 0, 955}, + dictWord{135, 0, 1251}, + dictWord{4, 10, 126}, + dictWord{8, 10, 635}, + dictWord{147, 10, 34}, + dictWord{4, 10, 316}, + dictWord{135, 10, 1561}, + dictWord{137, 10, 861}, + dictWord{4, 10, 64}, + dictWord{ + 5, + 10, + 352, + }, + dictWord{5, 10, 720}, + dictWord{6, 10, 368}, + dictWord{139, 10, 359}, + dictWord{134, 0, 192}, + dictWord{4, 0, 132}, + dictWord{5, 0, 69}, + dictWord{ + 135, + 0, + 1242, + }, + dictWord{7, 10, 1577}, + dictWord{10, 10, 304}, + dictWord{10, 10, 549}, + dictWord{12, 10, 365}, + dictWord{13, 10, 220}, + dictWord{13, 10, 240}, + dictWord{142, 10, 33}, + dictWord{4, 0, 111}, + dictWord{7, 0, 865}, + dictWord{134, 11, 219}, + dictWord{5, 11, 582}, + dictWord{6, 11, 1646}, + dictWord{7, 11, 99}, + dictWord{ + 7, + 11, + 1962, + }, + dictWord{7, 11, 1986}, + dictWord{8, 11, 515}, + dictWord{8, 11, 773}, + dictWord{9, 11, 23}, + dictWord{9, 11, 491}, + dictWord{12, 11, 620}, + dictWord{ + 14, + 11, + 52, + }, + dictWord{145, 11, 50}, + dictWord{132, 0, 767}, + dictWord{7, 11, 568}, + dictWord{148, 11, 21}, + dictWord{6, 0, 42}, + dictWord{7, 0, 1416}, + dictWord{ + 7, + 0, + 2005, + }, + dictWord{8, 0, 131}, + dictWord{8, 0, 466}, + dictWord{9, 0, 672}, + dictWord{13, 0, 252}, + dictWord{20, 0, 103}, + dictWord{133, 11, 851}, + dictWord{ + 135, + 0, + 1050, + }, + dictWord{6, 10, 175}, + dictWord{137, 10, 289}, + dictWord{5, 10, 432}, + dictWord{133, 10, 913}, + dictWord{6, 0, 44}, + dictWord{136, 0, 368}, + dictWord{ + 135, + 11, + 784, + }, + dictWord{132, 0, 570}, + dictWord{133, 0, 120}, + dictWord{139, 10, 595}, + dictWord{140, 0, 29}, + dictWord{6, 0, 227}, + dictWord{135, 0, 1589}, + dictWord{4, 11, 98}, + dictWord{7, 11, 1365}, + dictWord{9, 11, 422}, + dictWord{9, 11, 670}, + dictWord{10, 11, 775}, + dictWord{11, 11, 210}, + dictWord{13, 11, 26}, + dictWord{13, 11, 457}, + dictWord{141, 11, 476}, + dictWord{140, 10, 80}, + dictWord{5, 10, 931}, + dictWord{134, 10, 1698}, + dictWord{133, 0, 522}, + dictWord{ + 134, + 0, + 1120, + }, + dictWord{135, 0, 1529}, + dictWord{12, 0, 739}, + dictWord{14, 0, 448}, + dictWord{142, 0, 467}, + dictWord{11, 10, 526}, + dictWord{11, 10, 939}, + dictWord{141, 10, 290}, + dictWord{5, 10, 774}, + dictWord{6, 10, 1637}, + dictWord{6, 10, 1686}, + dictWord{134, 10, 1751}, + dictWord{6, 0, 1667}, + dictWord{ + 135, + 0, + 2036, + }, + dictWord{7, 10, 1167}, + dictWord{11, 10, 934}, + dictWord{13, 10, 391}, + dictWord{145, 10, 76}, + dictWord{137, 11, 147}, + dictWord{6, 10, 260}, + dictWord{ + 7, + 10, + 1484, + }, + dictWord{11, 11, 821}, + dictWord{12, 11, 110}, + dictWord{12, 11, 153}, + dictWord{18, 11, 41}, + dictWord{150, 11, 19}, + dictWord{6, 0, 511}, + dictWord{12, 0, 132}, + dictWord{134, 10, 573}, + dictWord{5, 0, 568}, + dictWord{6, 0, 138}, + dictWord{135, 0, 1293}, + dictWord{132, 0, 1020}, + dictWord{8, 0, 258}, + dictWord{9, 0, 208}, + dictWord{137, 0, 359}, + dictWord{4, 0, 565}, + dictWord{8, 0, 23}, + dictWord{136, 0, 827}, + dictWord{134, 0, 344}, + dictWord{4, 0, 922}, + dictWord{ + 5, + 0, + 1023, + }, + dictWord{13, 11, 477}, + dictWord{14, 11, 120}, + dictWord{148, 11, 61}, + dictWord{134, 0, 240}, + dictWord{5, 11, 209}, + dictWord{6, 11, 30}, + dictWord{ + 11, + 11, + 56, + }, + dictWord{139, 11, 305}, + dictWord{6, 0, 171}, + dictWord{7, 0, 1002}, + dictWord{7, 0, 1324}, + dictWord{9, 0, 415}, + dictWord{14, 0, 230}, + dictWord{ + 18, + 0, + 68, + }, + dictWord{4, 10, 292}, + dictWord{4, 10, 736}, + dictWord{5, 10, 871}, + dictWord{6, 10, 1689}, + dictWord{7, 10, 1944}, + dictWord{137, 10, 580}, + dictWord{ + 9, + 11, + 635, + }, + dictWord{139, 11, 559}, + dictWord{4, 11, 150}, + dictWord{5, 11, 303}, + dictWord{134, 11, 327}, + dictWord{6, 10, 63}, + dictWord{135, 10, 920}, + dictWord{ + 133, + 10, + 793, + }, + dictWord{8, 11, 192}, + dictWord{10, 11, 78}, + dictWord{10, 11, 555}, + dictWord{11, 11, 308}, + dictWord{13, 11, 359}, + dictWord{147, 11, 95}, + dictWord{135, 11, 786}, + dictWord{135, 11, 1712}, + dictWord{136, 0, 402}, + dictWord{6, 0, 754}, + dictWord{6, 11, 1638}, + dictWord{7, 11, 79}, + dictWord{7, 11, 496}, + dictWord{9, 11, 138}, + dictWord{10, 11, 336}, + dictWord{11, 11, 12}, + dictWord{12, 11, 412}, + dictWord{12, 11, 440}, + dictWord{142, 11, 305}, + dictWord{4, 0, 716}, + dictWord{141, 0, 31}, + dictWord{133, 0, 982}, + dictWord{8, 0, 691}, + dictWord{8, 0, 731}, + dictWord{5, 10, 67}, + dictWord{6, 10, 62}, + dictWord{6, 10, 374}, + dictWord{ + 135, + 10, + 1391, + }, + dictWord{9, 10, 790}, + dictWord{140, 10, 47}, + dictWord{139, 11, 556}, + dictWord{151, 11, 1}, + dictWord{7, 11, 204}, + dictWord{7, 11, 415}, + dictWord{8, 11, 42}, + dictWord{10, 11, 85}, + dictWord{11, 11, 33}, + dictWord{11, 11, 564}, + dictWord{12, 11, 571}, + dictWord{149, 11, 1}, + dictWord{8, 0, 888}, + dictWord{ + 7, + 11, + 610, + }, + dictWord{135, 11, 1501}, + dictWord{4, 10, 391}, + dictWord{135, 10, 1169}, + dictWord{5, 0, 847}, + dictWord{9, 0, 840}, + dictWord{138, 0, 803}, + dictWord{137, 0, 823}, + dictWord{134, 0, 785}, + dictWord{8, 0, 152}, + dictWord{9, 0, 53}, + dictWord{9, 0, 268}, + dictWord{9, 0, 901}, + dictWord{10, 0, 518}, + dictWord{ + 10, + 0, + 829, + }, + dictWord{11, 0, 188}, + dictWord{13, 0, 74}, + dictWord{14, 0, 46}, + dictWord{15, 0, 17}, + dictWord{15, 0, 33}, + dictWord{17, 0, 40}, + dictWord{18, 0, 36}, + dictWord{ + 19, + 0, + 20, + }, + dictWord{22, 0, 1}, + dictWord{152, 0, 2}, + dictWord{4, 11, 3}, + dictWord{5, 11, 247}, + dictWord{5, 11, 644}, + dictWord{7, 11, 744}, + dictWord{7, 11, 1207}, + dictWord{7, 11, 1225}, + dictWord{7, 11, 1909}, + dictWord{146, 11, 147}, + dictWord{136, 0, 532}, + dictWord{135, 0, 681}, + dictWord{132, 10, 271}, + dictWord{ + 140, + 0, + 314, + }, + dictWord{140, 0, 677}, + dictWord{4, 0, 684}, + dictWord{136, 0, 384}, + dictWord{5, 11, 285}, + dictWord{9, 11, 67}, + dictWord{13, 11, 473}, + dictWord{ + 143, + 11, + 82, + }, + dictWord{4, 10, 253}, + dictWord{5, 10, 544}, + dictWord{7, 10, 300}, + dictWord{137, 10, 340}, + dictWord{7, 0, 110}, + dictWord{7, 0, 447}, + dictWord{8, 0, 290}, + dictWord{8, 0, 591}, + dictWord{9, 0, 382}, + dictWord{9, 0, 649}, + dictWord{11, 0, 71}, + dictWord{11, 0, 155}, + dictWord{11, 0, 313}, + dictWord{12, 0, 5}, + dictWord{13, 0, 325}, + dictWord{142, 0, 287}, + dictWord{134, 0, 1818}, + dictWord{136, 0, 1007}, + dictWord{138, 0, 321}, + dictWord{7, 0, 360}, + dictWord{7, 0, 425}, + dictWord{9, 0, 66}, + dictWord{9, 0, 278}, + dictWord{138, 0, 644}, + dictWord{133, 10, 818}, + dictWord{5, 0, 385}, + dictWord{5, 10, 541}, + dictWord{6, 10, 94}, + dictWord{6, 10, 499}, + dictWord{ + 7, + 10, + 230, + }, + dictWord{139, 10, 321}, + dictWord{4, 10, 920}, + dictWord{5, 10, 25}, + dictWord{5, 10, 790}, + dictWord{6, 10, 457}, + dictWord{7, 10, 853}, + dictWord{ + 136, + 10, + 788, + }, + dictWord{4, 0, 900}, + dictWord{133, 0, 861}, + dictWord{5, 0, 254}, + dictWord{7, 0, 985}, + dictWord{136, 0, 73}, + dictWord{7, 0, 1959}, + dictWord{ + 136, + 0, + 683, + }, + dictWord{134, 10, 1765}, + dictWord{133, 10, 822}, + dictWord{132, 10, 634}, + dictWord{4, 11, 29}, + dictWord{6, 11, 532}, + dictWord{7, 11, 1628}, + dictWord{ + 7, + 11, + 1648, + }, + dictWord{9, 11, 303}, + dictWord{9, 11, 350}, + dictWord{10, 11, 433}, + dictWord{11, 11, 97}, + dictWord{11, 11, 557}, + dictWord{11, 11, 745}, + dictWord{12, 11, 289}, + dictWord{12, 11, 335}, + dictWord{12, 11, 348}, + dictWord{12, 11, 606}, + dictWord{13, 11, 116}, + dictWord{13, 11, 233}, + dictWord{ + 13, + 11, + 466, + }, + dictWord{14, 11, 181}, + dictWord{14, 11, 209}, + dictWord{14, 11, 232}, + dictWord{14, 11, 236}, + dictWord{14, 11, 300}, + dictWord{16, 11, 41}, + dictWord{ + 148, + 11, + 97, + }, + dictWord{19, 0, 86}, + dictWord{6, 10, 36}, + dictWord{7, 10, 658}, + dictWord{136, 10, 454}, + dictWord{135, 11, 1692}, + dictWord{132, 0, 725}, + dictWord{ + 5, + 11, + 501, + }, + dictWord{7, 11, 1704}, + dictWord{9, 11, 553}, + dictWord{11, 11, 520}, + dictWord{12, 11, 557}, + dictWord{141, 11, 249}, + dictWord{134, 0, 196}, + dictWord{133, 0, 831}, + dictWord{136, 0, 723}, + dictWord{7, 0, 1897}, + dictWord{13, 0, 80}, + dictWord{13, 0, 437}, + dictWord{145, 0, 74}, + dictWord{4, 0, 992}, + dictWord{ + 6, + 0, + 627, + }, + dictWord{136, 0, 994}, + dictWord{135, 11, 1294}, + dictWord{132, 10, 104}, + dictWord{5, 0, 848}, + dictWord{6, 0, 66}, + dictWord{136, 0, 764}, + dictWord{ + 4, + 0, + 36, + }, + dictWord{7, 0, 1387}, + dictWord{10, 0, 205}, + dictWord{139, 0, 755}, + dictWord{6, 0, 1046}, + dictWord{134, 0, 1485}, + dictWord{134, 0, 950}, + dictWord{132, 0, 887}, + dictWord{14, 0, 450}, + dictWord{148, 0, 111}, + dictWord{7, 0, 620}, + dictWord{7, 0, 831}, + dictWord{9, 10, 542}, + dictWord{9, 10, 566}, + dictWord{ + 138, + 10, + 728, + }, + dictWord{6, 0, 165}, + dictWord{138, 0, 388}, + dictWord{139, 10, 263}, + dictWord{4, 0, 719}, + dictWord{135, 0, 155}, + dictWord{138, 10, 468}, + dictWord{6, 11, 453}, + dictWord{144, 11, 36}, + dictWord{134, 11, 129}, + dictWord{5, 0, 533}, + dictWord{7, 0, 755}, + dictWord{138, 0, 780}, + dictWord{134, 0, 1465}, + dictWord{4, 0, 353}, + dictWord{6, 0, 146}, + dictWord{6, 0, 1789}, + dictWord{7, 0, 427}, + dictWord{7, 0, 990}, + dictWord{7, 0, 1348}, + dictWord{9, 0, 665}, + dictWord{9, 0, 898}, + dictWord{11, 0, 893}, + dictWord{142, 0, 212}, + dictWord{7, 10, 87}, + dictWord{142, 10, 288}, + dictWord{4, 0, 45}, + dictWord{135, 0, 1257}, + dictWord{12, 0, 7}, + dictWord{7, 10, 988}, + dictWord{7, 10, 1939}, + dictWord{9, 10, 64}, + dictWord{9, 10, 502}, + dictWord{12, 10, 34}, + dictWord{13, 10, 12}, + dictWord{13, 10, 234}, + dictWord{147, 10, 77}, + dictWord{4, 0, 607}, + dictWord{5, 11, 60}, + dictWord{6, 11, 504}, + dictWord{7, 11, 614}, + dictWord{7, 11, 1155}, + dictWord{140, 11, 0}, + dictWord{ + 135, + 10, + 141, + }, + dictWord{8, 11, 198}, + dictWord{11, 11, 29}, + dictWord{140, 11, 534}, + dictWord{140, 0, 65}, + dictWord{136, 0, 816}, + dictWord{132, 10, 619}, + dictWord{139, 0, 88}, + dictWord{5, 10, 246}, + dictWord{8, 10, 189}, + dictWord{9, 10, 355}, + dictWord{9, 10, 512}, + dictWord{10, 10, 124}, + dictWord{10, 10, 453}, + dictWord{11, 10, 143}, + dictWord{11, 10, 416}, + dictWord{11, 10, 859}, + dictWord{141, 10, 341}, + dictWord{4, 11, 379}, + dictWord{135, 11, 1397}, + dictWord{ + 4, + 0, + 600, + }, + dictWord{137, 0, 621}, + dictWord{133, 0, 367}, + dictWord{134, 0, 561}, + dictWord{6, 0, 559}, + dictWord{134, 0, 1691}, + dictWord{6, 0, 585}, + dictWord{ + 134, + 11, + 585, + }, + dictWord{135, 11, 1228}, + dictWord{4, 11, 118}, + dictWord{5, 10, 678}, + dictWord{6, 11, 274}, + dictWord{6, 11, 361}, + dictWord{7, 11, 75}, + dictWord{ + 141, + 11, + 441, + }, + dictWord{135, 11, 1818}, + dictWord{137, 11, 841}, + dictWord{5, 0, 573}, + dictWord{6, 0, 287}, + dictWord{7, 10, 862}, + dictWord{7, 10, 1886}, + dictWord{138, 10, 179}, + dictWord{132, 10, 517}, + dictWord{140, 11, 693}, + dictWord{5, 11, 314}, + dictWord{6, 11, 221}, + dictWord{7, 11, 419}, + dictWord{ + 10, + 11, + 650, + }, + dictWord{11, 11, 396}, + dictWord{12, 11, 156}, + dictWord{13, 11, 369}, + dictWord{14, 11, 333}, + dictWord{145, 11, 47}, + dictWord{140, 10, 540}, + dictWord{136, 10, 667}, + dictWord{11, 10, 403}, + dictWord{146, 10, 83}, + dictWord{6, 0, 672}, + dictWord{133, 10, 761}, + dictWord{9, 0, 157}, + dictWord{10, 10, 131}, + dictWord{140, 10, 72}, + dictWord{7, 0, 714}, + dictWord{134, 11, 460}, + dictWord{134, 0, 456}, + dictWord{133, 0, 925}, + dictWord{5, 11, 682}, + dictWord{ + 135, + 11, + 1887, + }, + dictWord{136, 11, 510}, + dictWord{136, 11, 475}, + dictWord{133, 11, 1016}, + dictWord{9, 0, 19}, + dictWord{7, 11, 602}, + dictWord{8, 11, 179}, + dictWord{ + 10, + 11, + 781, + }, + dictWord{140, 11, 126}, + dictWord{6, 11, 329}, + dictWord{138, 11, 111}, + dictWord{6, 0, 822}, + dictWord{134, 0, 1473}, + dictWord{144, 11, 86}, + dictWord{11, 0, 113}, + dictWord{139, 11, 113}, + dictWord{5, 11, 821}, + dictWord{134, 11, 1687}, + dictWord{133, 10, 449}, + dictWord{7, 0, 463}, + dictWord{ + 17, + 0, + 69, + }, + dictWord{136, 10, 103}, + dictWord{7, 10, 2028}, + dictWord{138, 10, 641}, + dictWord{6, 0, 193}, + dictWord{7, 0, 240}, + dictWord{7, 0, 1682}, + dictWord{ + 10, + 0, + 51, + }, + dictWord{10, 0, 640}, + dictWord{11, 0, 410}, + dictWord{13, 0, 82}, + dictWord{14, 0, 247}, + dictWord{14, 0, 331}, + dictWord{142, 0, 377}, + dictWord{6, 0, 471}, + dictWord{11, 0, 411}, + dictWord{142, 0, 2}, + dictWord{5, 11, 71}, + dictWord{7, 11, 1407}, + dictWord{9, 11, 388}, + dictWord{9, 11, 704}, + dictWord{10, 11, 261}, + dictWord{ + 10, + 11, + 619, + }, + dictWord{11, 11, 547}, + dictWord{11, 11, 619}, + dictWord{143, 11, 157}, + dictWord{136, 0, 633}, + dictWord{135, 0, 1148}, + dictWord{6, 0, 554}, + dictWord{7, 0, 1392}, + dictWord{12, 0, 129}, + dictWord{7, 10, 1274}, + dictWord{7, 10, 1386}, + dictWord{7, 11, 2008}, + dictWord{9, 11, 337}, + dictWord{10, 11, 517}, + dictWord{146, 10, 87}, + dictWord{7, 0, 803}, + dictWord{8, 0, 542}, + dictWord{6, 10, 187}, + dictWord{7, 10, 1203}, + dictWord{8, 10, 380}, + dictWord{14, 10, 117}, + dictWord{149, 10, 28}, + dictWord{6, 10, 297}, + dictWord{7, 10, 793}, + dictWord{139, 10, 938}, + dictWord{8, 0, 438}, + dictWord{11, 0, 363}, + dictWord{7, 10, 464}, + dictWord{11, 10, 105}, + dictWord{12, 10, 231}, + dictWord{14, 10, 386}, + dictWord{15, 10, 102}, + dictWord{148, 10, 75}, + dictWord{5, 11, 16}, + dictWord{6, 11, 86}, + dictWord{6, 11, 603}, + dictWord{7, 11, 292}, + dictWord{7, 11, 561}, + dictWord{8, 11, 257}, + dictWord{8, 11, 382}, + dictWord{9, 11, 721}, + dictWord{9, 11, 778}, + dictWord{ + 11, + 11, + 581, + }, + dictWord{140, 11, 466}, + dictWord{6, 0, 717}, + dictWord{4, 11, 486}, + dictWord{133, 11, 491}, + dictWord{132, 0, 875}, + dictWord{132, 11, 72}, + dictWord{6, 11, 265}, + dictWord{135, 11, 847}, + dictWord{4, 0, 237}, + dictWord{135, 0, 514}, + dictWord{6, 0, 392}, + dictWord{7, 0, 65}, + dictWord{135, 0, 2019}, + dictWord{140, 11, 261}, + dictWord{135, 11, 922}, + dictWord{137, 11, 404}, + dictWord{12, 0, 563}, + dictWord{14, 0, 101}, + dictWord{18, 0, 129}, + dictWord{ + 7, + 10, + 1010, + }, + dictWord{11, 10, 733}, + dictWord{11, 10, 759}, + dictWord{13, 10, 34}, + dictWord{146, 10, 45}, + dictWord{7, 10, 1656}, + dictWord{9, 10, 369}, + dictWord{ + 10, + 10, + 338, + }, + dictWord{10, 10, 490}, + dictWord{11, 10, 154}, + dictWord{11, 10, 545}, + dictWord{11, 10, 775}, + dictWord{13, 10, 77}, + dictWord{141, 10, 274}, + dictWord{4, 0, 444}, + dictWord{10, 0, 146}, + dictWord{140, 0, 9}, + dictWord{139, 11, 163}, + dictWord{7, 0, 1260}, + dictWord{135, 0, 1790}, + dictWord{9, 0, 222}, + dictWord{10, 0, 43}, + dictWord{139, 0, 900}, + dictWord{137, 11, 234}, + dictWord{138, 0, 971}, + dictWord{137, 0, 761}, + dictWord{134, 0, 699}, + dictWord{ + 136, + 11, + 434, + }, + dictWord{6, 0, 1116}, + dictWord{7, 0, 1366}, + dictWord{5, 10, 20}, + dictWord{6, 11, 197}, + dictWord{6, 10, 298}, + dictWord{7, 10, 659}, + dictWord{8, 11, 205}, + dictWord{137, 10, 219}, + dictWord{132, 11, 490}, + dictWord{11, 11, 820}, + dictWord{150, 11, 51}, + dictWord{7, 10, 1440}, + dictWord{11, 10, 854}, + dictWord{ + 11, + 10, + 872, + }, + dictWord{11, 10, 921}, + dictWord{12, 10, 551}, + dictWord{13, 10, 472}, + dictWord{142, 10, 367}, + dictWord{140, 11, 13}, + dictWord{132, 0, 829}, + dictWord{12, 0, 242}, + dictWord{132, 10, 439}, + dictWord{136, 10, 669}, + dictWord{6, 0, 593}, + dictWord{6, 11, 452}, + dictWord{7, 11, 312}, + dictWord{ + 138, + 11, + 219, + }, + dictWord{4, 11, 333}, + dictWord{9, 11, 176}, + dictWord{12, 11, 353}, + dictWord{141, 11, 187}, + dictWord{7, 0, 36}, + dictWord{8, 0, 201}, + dictWord{ + 136, + 0, + 605, + }, + dictWord{140, 0, 224}, + dictWord{132, 10, 233}, + dictWord{134, 0, 1430}, + dictWord{134, 0, 1806}, + dictWord{4, 0, 523}, + dictWord{133, 0, 638}, + dictWord{ + 6, + 0, + 1889, + }, + dictWord{9, 0, 958}, + dictWord{9, 0, 971}, + dictWord{9, 0, 976}, + dictWord{12, 0, 796}, + dictWord{12, 0, 799}, + dictWord{12, 0, 808}, + dictWord{ + 12, + 0, + 835, + }, + dictWord{12, 0, 836}, + dictWord{12, 0, 914}, + dictWord{12, 0, 946}, + dictWord{15, 0, 216}, + dictWord{15, 0, 232}, + dictWord{18, 0, 183}, + dictWord{18, 0, 187}, + dictWord{18, 0, 194}, + dictWord{18, 0, 212}, + dictWord{18, 0, 232}, + dictWord{149, 0, 49}, + dictWord{132, 10, 482}, + dictWord{6, 0, 827}, + dictWord{134, 0, 1434}, + dictWord{135, 10, 346}, + dictWord{134, 0, 2043}, + dictWord{6, 0, 242}, + dictWord{7, 0, 227}, + dictWord{7, 0, 1581}, + dictWord{8, 0, 104}, + dictWord{9, 0, 113}, + dictWord{9, 0, 220}, + dictWord{9, 0, 427}, + dictWord{10, 0, 136}, + dictWord{10, 0, 239}, + dictWord{11, 0, 579}, + dictWord{11, 0, 1023}, + dictWord{13, 0, 4}, + dictWord{ + 13, + 0, + 204, + }, + dictWord{13, 0, 316}, + dictWord{148, 0, 86}, + dictWord{134, 11, 1685}, + dictWord{7, 0, 148}, + dictWord{8, 0, 284}, + dictWord{141, 0, 63}, + dictWord{ + 142, + 0, + 10, + }, + dictWord{135, 11, 584}, + dictWord{134, 0, 1249}, + dictWord{7, 0, 861}, + dictWord{135, 10, 334}, + dictWord{5, 10, 795}, + dictWord{6, 10, 1741}, + dictWord{ + 137, + 11, + 70, + }, + dictWord{132, 0, 807}, + dictWord{7, 11, 135}, + dictWord{8, 11, 7}, + dictWord{8, 11, 62}, + dictWord{9, 11, 243}, + dictWord{10, 11, 658}, + dictWord{ + 10, + 11, + 697, + }, + dictWord{11, 11, 456}, + dictWord{139, 11, 756}, + dictWord{9, 11, 395}, + dictWord{138, 11, 79}, + dictWord{137, 11, 108}, + dictWord{147, 0, 94}, + dictWord{136, 0, 494}, + dictWord{135, 11, 631}, + dictWord{135, 10, 622}, + dictWord{7, 0, 1510}, + dictWord{135, 10, 1750}, + dictWord{4, 10, 203}, + dictWord{ + 135, + 10, + 1936, + }, + dictWord{7, 11, 406}, + dictWord{7, 11, 459}, + dictWord{8, 11, 606}, + dictWord{139, 11, 726}, + dictWord{7, 0, 1306}, + dictWord{8, 0, 505}, + dictWord{ + 9, + 0, + 482, + }, + dictWord{10, 0, 126}, + dictWord{11, 0, 225}, + dictWord{12, 0, 347}, + dictWord{12, 0, 449}, + dictWord{13, 0, 19}, + dictWord{14, 0, 218}, + dictWord{142, 0, 435}, + dictWord{5, 0, 268}, + dictWord{10, 0, 764}, + dictWord{12, 0, 120}, + dictWord{13, 0, 39}, + dictWord{145, 0, 127}, + dictWord{142, 11, 68}, + dictWord{11, 10, 678}, + dictWord{140, 10, 307}, + dictWord{12, 11, 268}, + dictWord{12, 11, 640}, + dictWord{142, 11, 119}, + dictWord{135, 10, 2044}, + dictWord{133, 11, 612}, + dictWord{ + 4, + 11, + 372, + }, + dictWord{7, 11, 482}, + dictWord{8, 11, 158}, + dictWord{9, 11, 602}, + dictWord{9, 11, 615}, + dictWord{10, 11, 245}, + dictWord{10, 11, 678}, + dictWord{ + 10, + 11, + 744, + }, + dictWord{11, 11, 248}, + dictWord{139, 11, 806}, + dictWord{7, 10, 311}, + dictWord{9, 10, 308}, + dictWord{140, 10, 255}, + dictWord{4, 0, 384}, + dictWord{135, 0, 1022}, + dictWord{5, 11, 854}, + dictWord{135, 11, 1991}, + dictWord{135, 10, 1266}, + dictWord{4, 10, 400}, + dictWord{5, 10, 267}, + dictWord{ + 135, + 10, + 232, + }, + dictWord{135, 0, 1703}, + dictWord{9, 0, 159}, + dictWord{11, 0, 661}, + dictWord{140, 0, 603}, + dictWord{4, 0, 964}, + dictWord{14, 0, 438}, + dictWord{ + 14, + 0, + 444, + }, + dictWord{14, 0, 456}, + dictWord{22, 0, 60}, + dictWord{22, 0, 63}, + dictWord{9, 11, 106}, + dictWord{9, 11, 163}, + dictWord{9, 11, 296}, + dictWord{10, 11, 167}, + dictWord{10, 11, 172}, + dictWord{10, 11, 777}, + dictWord{139, 11, 16}, + dictWord{136, 0, 583}, + dictWord{132, 0, 515}, + dictWord{8, 0, 632}, + dictWord{8, 0, 697}, + dictWord{137, 0, 854}, + dictWord{5, 11, 195}, + dictWord{135, 11, 1685}, + dictWord{6, 0, 1123}, + dictWord{134, 0, 1365}, + dictWord{134, 11, 328}, + dictWord{ + 7, + 11, + 1997, + }, + dictWord{8, 11, 730}, + dictWord{139, 11, 1006}, + dictWord{4, 0, 136}, + dictWord{133, 0, 551}, + dictWord{134, 0, 1782}, + dictWord{7, 0, 1287}, + dictWord{ + 9, + 0, + 44, + }, + dictWord{10, 0, 552}, + dictWord{10, 0, 642}, + dictWord{11, 0, 839}, + dictWord{12, 0, 274}, + dictWord{12, 0, 275}, + dictWord{12, 0, 372}, + dictWord{ + 13, + 0, + 91, + }, + dictWord{142, 0, 125}, + dictWord{5, 11, 751}, + dictWord{11, 11, 797}, + dictWord{140, 11, 203}, + dictWord{133, 0, 732}, + dictWord{7, 0, 679}, + dictWord{ + 8, + 0, + 313, + }, + dictWord{4, 10, 100}, + dictWord{135, 11, 821}, + dictWord{10, 0, 361}, + dictWord{142, 0, 316}, + dictWord{134, 0, 595}, + dictWord{6, 0, 147}, + dictWord{ + 7, + 0, + 886, + }, + dictWord{9, 0, 753}, + dictWord{138, 0, 268}, + dictWord{5, 10, 362}, + dictWord{5, 10, 443}, + dictWord{6, 10, 318}, + dictWord{7, 10, 1019}, + dictWord{ + 139, + 10, + 623, + }, + dictWord{5, 10, 463}, + dictWord{136, 10, 296}, + dictWord{4, 10, 454}, + dictWord{5, 11, 950}, + dictWord{5, 11, 994}, + dictWord{134, 11, 351}, + dictWord{ + 138, + 0, + 137, + }, + dictWord{5, 10, 48}, + dictWord{5, 10, 404}, + dictWord{6, 10, 557}, + dictWord{7, 10, 458}, + dictWord{8, 10, 597}, + dictWord{10, 10, 455}, + dictWord{ + 10, + 10, + 606, + }, + dictWord{11, 10, 49}, + dictWord{11, 10, 548}, + dictWord{12, 10, 476}, + dictWord{13, 10, 18}, + dictWord{141, 10, 450}, + dictWord{133, 0, 414}, + dictWord{ + 135, + 0, + 1762, + }, + dictWord{5, 11, 421}, + dictWord{135, 11, 47}, + dictWord{5, 10, 442}, + dictWord{135, 10, 1984}, + dictWord{134, 0, 599}, + dictWord{134, 0, 1749}, + dictWord{134, 0, 1627}, + dictWord{4, 0, 488}, + dictWord{132, 11, 350}, + dictWord{137, 11, 751}, + dictWord{132, 0, 83}, + dictWord{140, 0, 676}, + dictWord{ + 133, + 11, + 967, + }, + dictWord{7, 0, 1639}, + dictWord{5, 10, 55}, + dictWord{140, 10, 161}, + dictWord{4, 11, 473}, + dictWord{7, 11, 623}, + dictWord{8, 11, 808}, + dictWord{ + 9, + 11, + 871, + }, + dictWord{9, 11, 893}, + dictWord{11, 11, 38}, + dictWord{11, 11, 431}, + dictWord{12, 11, 112}, + dictWord{12, 11, 217}, + dictWord{12, 11, 243}, + dictWord{ + 12, + 11, + 562, + }, + dictWord{12, 11, 683}, + dictWord{13, 11, 141}, + dictWord{13, 11, 197}, + dictWord{13, 11, 227}, + dictWord{13, 11, 406}, + dictWord{13, 11, 487}, + dictWord{14, 11, 156}, + dictWord{14, 11, 203}, + dictWord{14, 11, 224}, + dictWord{14, 11, 256}, + dictWord{18, 11, 58}, + dictWord{150, 11, 0}, + dictWord{ + 133, + 10, + 450, + }, + dictWord{7, 11, 736}, + dictWord{139, 11, 264}, + dictWord{134, 0, 278}, + dictWord{4, 11, 222}, + dictWord{7, 11, 286}, + dictWord{136, 11, 629}, + dictWord{ + 135, + 10, + 869, + }, + dictWord{140, 0, 97}, + dictWord{144, 0, 14}, + dictWord{134, 0, 1085}, + dictWord{4, 10, 213}, + dictWord{7, 10, 223}, + dictWord{136, 10, 80}, + dictWord{ + 7, + 0, + 388, + }, + dictWord{7, 0, 644}, + dictWord{139, 0, 781}, + dictWord{132, 0, 849}, + dictWord{7, 0, 229}, + dictWord{8, 0, 59}, + dictWord{9, 0, 190}, + dictWord{10, 0, 378}, + dictWord{140, 0, 191}, + dictWord{7, 10, 381}, + dictWord{7, 10, 806}, + dictWord{7, 10, 820}, + dictWord{8, 10, 354}, + dictWord{8, 10, 437}, + dictWord{8, 10, 787}, + dictWord{9, 10, 657}, + dictWord{10, 10, 58}, + dictWord{10, 10, 339}, + dictWord{10, 10, 749}, + dictWord{11, 10, 914}, + dictWord{12, 10, 162}, + dictWord{13, 10, 75}, + dictWord{14, 10, 106}, + dictWord{14, 10, 198}, + dictWord{14, 10, 320}, + dictWord{14, 10, 413}, + dictWord{146, 10, 43}, + dictWord{141, 11, 306}, + dictWord{ + 136, + 10, + 747, + }, + dictWord{134, 0, 1115}, + dictWord{16, 0, 94}, + dictWord{16, 0, 108}, + dictWord{136, 11, 146}, + dictWord{6, 0, 700}, + dictWord{6, 0, 817}, + dictWord{ + 134, + 0, + 1002, + }, + dictWord{133, 10, 692}, + dictWord{4, 11, 465}, + dictWord{135, 11, 1663}, + dictWord{134, 10, 191}, + dictWord{6, 0, 1414}, + dictWord{ + 135, + 11, + 913, + }, + dictWord{132, 0, 660}, + dictWord{7, 0, 1035}, + dictWord{138, 0, 737}, + dictWord{6, 10, 162}, + dictWord{7, 10, 1960}, + dictWord{136, 10, 831}, + dictWord{ + 132, + 10, + 706, + }, + dictWord{7, 0, 690}, + dictWord{9, 0, 217}, + dictWord{9, 0, 587}, + dictWord{140, 0, 521}, + dictWord{138, 10, 426}, + dictWord{135, 10, 1235}, + dictWord{ + 6, + 11, + 82, + }, + dictWord{7, 11, 138}, + dictWord{7, 11, 517}, + dictWord{9, 11, 673}, + dictWord{139, 11, 238}, + dictWord{138, 0, 272}, + dictWord{5, 11, 495}, + dictWord{ + 7, + 11, + 834, + }, + dictWord{9, 11, 733}, + dictWord{139, 11, 378}, + dictWord{134, 0, 1744}, + dictWord{132, 0, 1011}, + dictWord{7, 11, 828}, + dictWord{142, 11, 116}, + dictWord{4, 0, 733}, + dictWord{9, 0, 194}, + dictWord{10, 0, 92}, + dictWord{11, 0, 198}, + dictWord{12, 0, 84}, + dictWord{13, 0, 128}, + dictWord{133, 11, 559}, + dictWord{ + 10, + 0, + 57, + }, + dictWord{10, 0, 277}, + dictWord{6, 11, 21}, + dictWord{6, 11, 1737}, + dictWord{7, 11, 1444}, + dictWord{136, 11, 224}, + dictWord{4, 10, 204}, + dictWord{ + 137, + 10, + 902, + }, + dictWord{136, 10, 833}, + dictWord{11, 0, 348}, + dictWord{12, 0, 99}, + dictWord{18, 0, 1}, + dictWord{18, 0, 11}, + dictWord{19, 0, 4}, + dictWord{7, 10, 366}, + dictWord{9, 10, 287}, + dictWord{12, 10, 199}, + dictWord{12, 10, 556}, + dictWord{140, 10, 577}, + dictWord{6, 0, 1981}, + dictWord{136, 0, 936}, + dictWord{ + 21, + 0, + 33, + }, + dictWord{150, 0, 40}, + dictWord{5, 11, 519}, + dictWord{138, 11, 204}, + dictWord{5, 10, 356}, + dictWord{135, 10, 224}, + dictWord{134, 0, 775}, + dictWord{ + 135, + 0, + 306, + }, + dictWord{7, 10, 630}, + dictWord{9, 10, 567}, + dictWord{11, 10, 150}, + dictWord{11, 10, 444}, + dictWord{141, 10, 119}, + dictWord{5, 0, 979}, + dictWord{ + 134, + 10, + 539, + }, + dictWord{133, 0, 611}, + dictWord{4, 11, 402}, + dictWord{135, 11, 1679}, + dictWord{5, 0, 178}, + dictWord{7, 11, 2}, + dictWord{8, 11, 323}, + dictWord{ + 136, + 11, + 479, + }, + dictWord{5, 11, 59}, + dictWord{135, 11, 672}, + dictWord{4, 0, 1010}, + dictWord{6, 0, 1969}, + dictWord{138, 11, 237}, + dictWord{133, 11, 412}, + dictWord{146, 11, 34}, + dictWord{7, 11, 1740}, + dictWord{146, 11, 48}, + dictWord{134, 0, 664}, + dictWord{139, 10, 814}, + dictWord{4, 11, 85}, + dictWord{ + 135, + 11, + 549, + }, + dictWord{133, 11, 94}, + dictWord{133, 11, 457}, + dictWord{132, 0, 390}, + dictWord{134, 0, 1510}, + dictWord{4, 10, 235}, + dictWord{135, 10, 255}, + dictWord{4, 10, 194}, + dictWord{5, 10, 584}, + dictWord{6, 11, 11}, + dictWord{6, 10, 384}, + dictWord{7, 11, 187}, + dictWord{7, 10, 583}, + dictWord{10, 10, 761}, + dictWord{ + 11, + 10, + 760, + }, + dictWord{139, 10, 851}, + dictWord{4, 11, 522}, + dictWord{139, 11, 802}, + dictWord{135, 0, 493}, + dictWord{10, 11, 776}, + dictWord{13, 11, 345}, + dictWord{142, 11, 425}, + dictWord{146, 0, 37}, + dictWord{4, 11, 52}, + dictWord{135, 11, 661}, + dictWord{134, 0, 724}, + dictWord{134, 0, 829}, + dictWord{ + 133, + 11, + 520, + }, + dictWord{133, 10, 562}, + dictWord{4, 11, 281}, + dictWord{5, 11, 38}, + dictWord{7, 11, 194}, + dictWord{7, 11, 668}, + dictWord{7, 11, 1893}, + dictWord{ + 137, + 11, + 397, + }, + dictWord{5, 10, 191}, + dictWord{137, 10, 271}, + dictWord{7, 0, 1537}, + dictWord{14, 0, 96}, + dictWord{143, 0, 73}, + dictWord{5, 0, 473}, + dictWord{ + 11, + 0, + 168, + }, + dictWord{4, 10, 470}, + dictWord{6, 10, 153}, + dictWord{7, 10, 1503}, + dictWord{7, 10, 1923}, + dictWord{10, 10, 701}, + dictWord{11, 10, 132}, + dictWord{ + 11, + 10, + 227, + }, + dictWord{11, 10, 320}, + dictWord{11, 10, 436}, + dictWord{11, 10, 525}, + dictWord{11, 10, 855}, + dictWord{12, 10, 41}, + dictWord{12, 10, 286}, + dictWord{13, 10, 103}, + dictWord{13, 10, 284}, + dictWord{14, 10, 255}, + dictWord{14, 10, 262}, + dictWord{15, 10, 117}, + dictWord{143, 10, 127}, + dictWord{ + 133, + 0, + 105, + }, + dictWord{5, 0, 438}, + dictWord{9, 0, 694}, + dictWord{12, 0, 627}, + dictWord{141, 0, 210}, + dictWord{133, 10, 327}, + dictWord{6, 10, 552}, + dictWord{ + 7, + 10, + 1754, + }, + dictWord{137, 10, 604}, + dictWord{134, 0, 1256}, + dictWord{152, 0, 11}, + dictWord{5, 11, 448}, + dictWord{11, 11, 98}, + dictWord{139, 11, 524}, + dictWord{ + 7, + 0, + 1626, + }, + dictWord{5, 10, 80}, + dictWord{6, 10, 405}, + dictWord{7, 10, 403}, + dictWord{7, 10, 1502}, + dictWord{8, 10, 456}, + dictWord{9, 10, 487}, + dictWord{ + 9, + 10, + 853, + }, + dictWord{9, 10, 889}, + dictWord{10, 10, 309}, + dictWord{11, 10, 721}, + dictWord{11, 10, 994}, + dictWord{12, 10, 430}, + dictWord{13, 10, 165}, + dictWord{ + 14, + 11, + 16, + }, + dictWord{146, 11, 44}, + dictWord{132, 0, 779}, + dictWord{8, 0, 25}, + dictWord{138, 0, 826}, + dictWord{4, 10, 453}, + dictWord{5, 10, 887}, + dictWord{ + 6, + 10, + 535, + }, + dictWord{8, 10, 6}, + dictWord{8, 10, 543}, + dictWord{136, 10, 826}, + dictWord{137, 11, 461}, + dictWord{140, 11, 632}, + dictWord{132, 0, 308}, + dictWord{135, 0, 741}, + dictWord{132, 0, 671}, + dictWord{7, 0, 150}, + dictWord{8, 0, 649}, + dictWord{136, 0, 1020}, + dictWord{9, 0, 99}, + dictWord{6, 11, 336}, + dictWord{ + 8, + 11, + 552, + }, + dictWord{9, 11, 285}, + dictWord{10, 11, 99}, + dictWord{139, 11, 568}, + dictWord{134, 0, 521}, + dictWord{5, 0, 339}, + dictWord{14, 0, 3}, + dictWord{ + 15, + 0, + 41, + }, + dictWord{15, 0, 166}, + dictWord{147, 0, 66}, + dictWord{6, 11, 423}, + dictWord{7, 11, 665}, + dictWord{7, 11, 1210}, + dictWord{9, 11, 218}, + dictWord{ + 141, + 11, + 222, + }, + dictWord{6, 0, 543}, + dictWord{5, 10, 101}, + dictWord{5, 11, 256}, + dictWord{6, 10, 88}, + dictWord{7, 10, 1677}, + dictWord{9, 10, 100}, + dictWord{10, 10, 677}, + dictWord{14, 10, 169}, + dictWord{14, 10, 302}, + dictWord{14, 10, 313}, + dictWord{15, 10, 48}, + dictWord{143, 10, 84}, + dictWord{4, 10, 310}, + dictWord{ + 7, + 10, + 708, + }, + dictWord{7, 10, 996}, + dictWord{9, 10, 795}, + dictWord{10, 10, 390}, + dictWord{10, 10, 733}, + dictWord{11, 10, 451}, + dictWord{12, 10, 249}, + dictWord{ + 14, + 10, + 115, + }, + dictWord{14, 10, 286}, + dictWord{143, 10, 100}, + dictWord{133, 10, 587}, + dictWord{13, 11, 417}, + dictWord{14, 11, 129}, + dictWord{143, 11, 15}, + dictWord{134, 0, 1358}, + dictWord{136, 11, 554}, + dictWord{132, 10, 498}, + dictWord{7, 10, 217}, + dictWord{8, 10, 140}, + dictWord{138, 10, 610}, + dictWord{ + 135, + 11, + 989, + }, + dictWord{135, 11, 634}, + dictWord{6, 0, 155}, + dictWord{140, 0, 234}, + dictWord{135, 11, 462}, + dictWord{132, 11, 618}, + dictWord{ + 134, + 0, + 1628, + }, + dictWord{132, 0, 766}, + dictWord{4, 11, 339}, + dictWord{5, 10, 905}, + dictWord{135, 11, 259}, + dictWord{135, 0, 829}, + dictWord{4, 11, 759}, + dictWord{ + 141, + 11, + 169, + }, + dictWord{7, 0, 1445}, + dictWord{4, 10, 456}, + dictWord{7, 10, 358}, + dictWord{7, 10, 1637}, + dictWord{8, 10, 643}, + dictWord{139, 10, 483}, + dictWord{ + 5, + 0, + 486, + }, + dictWord{135, 0, 1349}, + dictWord{5, 11, 688}, + dictWord{135, 11, 712}, + dictWord{7, 0, 1635}, + dictWord{8, 0, 17}, + dictWord{10, 0, 217}, + dictWord{ + 10, + 0, + 295, + }, + dictWord{12, 0, 2}, + dictWord{140, 11, 2}, + dictWord{138, 0, 558}, + dictWord{150, 10, 56}, + dictWord{4, 11, 278}, + dictWord{5, 11, 465}, + dictWord{ + 135, + 11, + 1367, + }, + dictWord{136, 11, 482}, + dictWord{133, 10, 535}, + dictWord{6, 0, 1362}, + dictWord{6, 0, 1461}, + dictWord{10, 11, 274}, + dictWord{10, 11, 625}, + dictWord{139, 11, 530}, + dictWord{5, 0, 599}, + dictWord{5, 11, 336}, + dictWord{6, 11, 341}, + dictWord{6, 11, 478}, + dictWord{6, 11, 1763}, + dictWord{136, 11, 386}, + dictWord{7, 10, 1748}, + dictWord{137, 11, 151}, + dictWord{134, 0, 1376}, + dictWord{133, 10, 539}, + dictWord{135, 11, 73}, + dictWord{135, 11, 1971}, + dictWord{139, 11, 283}, + dictWord{9, 0, 93}, + dictWord{139, 0, 474}, + dictWord{6, 10, 91}, + dictWord{135, 10, 435}, + dictWord{6, 0, 447}, + dictWord{5, 11, 396}, + dictWord{134, 11, 501}, + dictWord{4, 10, 16}, + dictWord{5, 10, 316}, + dictWord{5, 10, 842}, + dictWord{6, 10, 370}, + dictWord{6, 10, 1778}, + dictWord{8, 10, 166}, + dictWord{11, 10, 812}, + dictWord{12, 10, 206}, + dictWord{12, 10, 351}, + dictWord{14, 10, 418}, + dictWord{16, 10, 15}, + dictWord{16, 10, 34}, + dictWord{18, 10, 3}, + dictWord{19, 10, 3}, + dictWord{19, 10, 7}, + dictWord{20, 10, 4}, + dictWord{149, 10, 21}, + dictWord{7, 0, 577}, + dictWord{7, 0, 1432}, + dictWord{9, 0, 475}, + dictWord{9, 0, 505}, + dictWord{9, 0, 526}, + dictWord{9, 0, 609}, + dictWord{9, 0, 689}, + dictWord{9, 0, 726}, + dictWord{9, 0, 735}, + dictWord{9, 0, 738}, + dictWord{10, 0, 556}, + dictWord{ + 10, + 0, + 674, + }, + dictWord{10, 0, 684}, + dictWord{11, 0, 89}, + dictWord{11, 0, 202}, + dictWord{11, 0, 272}, + dictWord{11, 0, 380}, + dictWord{11, 0, 415}, + dictWord{11, 0, 505}, + dictWord{11, 0, 537}, + dictWord{11, 0, 550}, + dictWord{11, 0, 562}, + dictWord{11, 0, 640}, + dictWord{11, 0, 667}, + dictWord{11, 0, 688}, + dictWord{11, 0, 847}, + dictWord{11, 0, 927}, + dictWord{11, 0, 930}, + dictWord{11, 0, 940}, + dictWord{12, 0, 144}, + dictWord{12, 0, 325}, + dictWord{12, 0, 329}, + dictWord{12, 0, 389}, + dictWord{ + 12, + 0, + 403, + }, + dictWord{12, 0, 451}, + dictWord{12, 0, 515}, + dictWord{12, 0, 604}, + dictWord{12, 0, 616}, + dictWord{12, 0, 626}, + dictWord{13, 0, 66}, + dictWord{ + 13, + 0, + 131, + }, + dictWord{13, 0, 167}, + dictWord{13, 0, 236}, + dictWord{13, 0, 368}, + dictWord{13, 0, 411}, + dictWord{13, 0, 434}, + dictWord{13, 0, 453}, + dictWord{13, 0, 461}, + dictWord{13, 0, 474}, + dictWord{14, 0, 59}, + dictWord{14, 0, 60}, + dictWord{14, 0, 139}, + dictWord{14, 0, 152}, + dictWord{14, 0, 276}, + dictWord{14, 0, 353}, + dictWord{ + 14, + 0, + 402, + }, + dictWord{15, 0, 28}, + dictWord{15, 0, 81}, + dictWord{15, 0, 123}, + dictWord{15, 0, 152}, + dictWord{18, 0, 136}, + dictWord{148, 0, 88}, + dictWord{ + 4, + 11, + 929, + }, + dictWord{133, 11, 799}, + dictWord{136, 11, 46}, + dictWord{142, 0, 307}, + dictWord{4, 0, 609}, + dictWord{7, 0, 756}, + dictWord{9, 0, 544}, + dictWord{ + 11, + 0, + 413, + }, + dictWord{144, 0, 25}, + dictWord{10, 0, 687}, + dictWord{7, 10, 619}, + dictWord{10, 10, 547}, + dictWord{11, 10, 122}, + dictWord{140, 10, 601}, + dictWord{ + 4, + 0, + 930, + }, + dictWord{133, 0, 947}, + dictWord{133, 0, 939}, + dictWord{142, 0, 21}, + dictWord{4, 11, 892}, + dictWord{133, 11, 770}, + dictWord{133, 0, 962}, + dictWord{ + 5, + 0, + 651, + }, + dictWord{8, 0, 170}, + dictWord{9, 0, 61}, + dictWord{9, 0, 63}, + dictWord{10, 0, 23}, + dictWord{10, 0, 37}, + dictWord{10, 0, 834}, + dictWord{11, 0, 4}, + dictWord{ + 11, + 0, + 187, + }, + dictWord{11, 0, 281}, + dictWord{11, 0, 503}, + dictWord{11, 0, 677}, + dictWord{12, 0, 96}, + dictWord{12, 0, 130}, + dictWord{12, 0, 244}, + dictWord{14, 0, 5}, + dictWord{14, 0, 40}, + dictWord{14, 0, 162}, + dictWord{14, 0, 202}, + dictWord{146, 0, 133}, + dictWord{4, 0, 406}, + dictWord{5, 0, 579}, + dictWord{12, 0, 492}, + dictWord{ + 150, + 0, + 15, + }, + dictWord{135, 11, 158}, + dictWord{135, 0, 597}, + dictWord{132, 0, 981}, + dictWord{132, 10, 888}, + dictWord{4, 10, 149}, + dictWord{138, 10, 368}, + dictWord{132, 0, 545}, + dictWord{4, 10, 154}, + dictWord{7, 10, 1134}, + dictWord{136, 10, 105}, + dictWord{135, 11, 2001}, + dictWord{134, 0, 1558}, + dictWord{ + 4, + 10, + 31, + }, + dictWord{6, 10, 429}, + dictWord{7, 10, 962}, + dictWord{9, 10, 458}, + dictWord{139, 10, 691}, + dictWord{132, 10, 312}, + dictWord{135, 10, 1642}, + dictWord{ + 6, + 0, + 17, + }, + dictWord{6, 0, 1304}, + dictWord{7, 0, 16}, + dictWord{7, 0, 1001}, + dictWord{9, 0, 886}, + dictWord{10, 0, 489}, + dictWord{10, 0, 800}, + dictWord{11, 0, 782}, + dictWord{12, 0, 320}, + dictWord{13, 0, 467}, + dictWord{14, 0, 145}, + dictWord{14, 0, 387}, + dictWord{143, 0, 119}, + dictWord{135, 0, 1982}, + dictWord{17, 0, 17}, + dictWord{7, 11, 1461}, + dictWord{140, 11, 91}, + dictWord{4, 10, 236}, + dictWord{132, 11, 602}, + dictWord{138, 0, 907}, + dictWord{136, 0, 110}, + dictWord{7, 0, 272}, + dictWord{19, 0, 53}, + dictWord{5, 10, 836}, + dictWord{5, 10, 857}, + dictWord{134, 10, 1680}, + dictWord{5, 0, 458}, + dictWord{7, 11, 1218}, + dictWord{136, 11, 303}, + dictWord{7, 0, 1983}, + dictWord{8, 0, 0}, + dictWord{8, 0, 171}, + dictWord{9, 0, 120}, + dictWord{9, 0, 732}, + dictWord{10, 0, 473}, + dictWord{11, 0, 656}, + dictWord{ + 11, + 0, + 998, + }, + dictWord{18, 0, 0}, + dictWord{18, 0, 2}, + dictWord{19, 0, 21}, + dictWord{10, 10, 68}, + dictWord{139, 10, 494}, + dictWord{137, 11, 662}, + dictWord{4, 11, 13}, + dictWord{5, 11, 567}, + dictWord{7, 11, 1498}, + dictWord{9, 11, 124}, + dictWord{11, 11, 521}, + dictWord{140, 11, 405}, + dictWord{4, 10, 81}, + dictWord{139, 10, 867}, + dictWord{135, 11, 1006}, + dictWord{7, 11, 800}, + dictWord{7, 11, 1783}, + dictWord{138, 11, 12}, + dictWord{9, 0, 295}, + dictWord{10, 0, 443}, + dictWord{ + 5, + 10, + 282, + }, + dictWord{8, 10, 650}, + dictWord{137, 10, 907}, + dictWord{132, 11, 735}, + dictWord{4, 11, 170}, + dictWord{4, 10, 775}, + dictWord{135, 11, 323}, + dictWord{ + 6, + 0, + 1844, + }, + dictWord{10, 0, 924}, + dictWord{11, 11, 844}, + dictWord{12, 11, 104}, + dictWord{140, 11, 625}, + dictWord{5, 11, 304}, + dictWord{7, 11, 1403}, + dictWord{140, 11, 498}, + dictWord{134, 0, 1232}, + dictWord{4, 0, 519}, + dictWord{10, 0, 70}, + dictWord{12, 0, 26}, + dictWord{14, 0, 17}, + dictWord{14, 0, 178}, + dictWord{ + 15, + 0, + 34, + }, + dictWord{149, 0, 12}, + dictWord{132, 0, 993}, + dictWord{4, 11, 148}, + dictWord{133, 11, 742}, + dictWord{6, 0, 31}, + dictWord{7, 0, 491}, + dictWord{7, 0, 530}, + dictWord{8, 0, 592}, + dictWord{11, 0, 53}, + dictWord{11, 0, 779}, + dictWord{12, 0, 167}, + dictWord{12, 0, 411}, + dictWord{14, 0, 14}, + dictWord{14, 0, 136}, + dictWord{ + 15, + 0, + 72, + }, + dictWord{16, 0, 17}, + dictWord{144, 0, 72}, + dictWord{133, 0, 907}, + dictWord{134, 0, 733}, + dictWord{133, 11, 111}, + dictWord{4, 10, 71}, + dictWord{ + 5, + 10, + 376, + }, + dictWord{7, 10, 119}, + dictWord{138, 10, 665}, + dictWord{136, 0, 55}, + dictWord{8, 0, 430}, + dictWord{136, 11, 430}, + dictWord{4, 0, 208}, + dictWord{ + 5, + 0, + 106, + }, + dictWord{6, 0, 531}, + dictWord{8, 0, 408}, + dictWord{9, 0, 188}, + dictWord{138, 0, 572}, + dictWord{12, 0, 56}, + dictWord{11, 10, 827}, + dictWord{14, 10, 34}, + dictWord{143, 10, 148}, + dictWord{134, 0, 1693}, + dictWord{133, 11, 444}, + dictWord{132, 10, 479}, + dictWord{140, 0, 441}, + dictWord{9, 0, 449}, + dictWord{ + 10, + 0, + 192, + }, + dictWord{138, 0, 740}, + dictWord{134, 0, 928}, + dictWord{4, 0, 241}, + dictWord{7, 10, 607}, + dictWord{136, 10, 99}, + dictWord{8, 11, 123}, + dictWord{ + 15, + 11, + 6, + }, + dictWord{144, 11, 7}, + dictWord{6, 11, 285}, + dictWord{8, 11, 654}, + dictWord{11, 11, 749}, + dictWord{12, 11, 190}, + dictWord{12, 11, 327}, + dictWord{ + 13, + 11, + 120, + }, + dictWord{13, 11, 121}, + dictWord{13, 11, 327}, + dictWord{15, 11, 47}, + dictWord{146, 11, 40}, + dictWord{4, 10, 41}, + dictWord{5, 10, 74}, + dictWord{ + 7, + 10, + 1627, + }, + dictWord{11, 10, 871}, + dictWord{140, 10, 619}, + dictWord{7, 0, 1525}, + dictWord{11, 10, 329}, + dictWord{11, 10, 965}, + dictWord{12, 10, 241}, + dictWord{14, 10, 354}, + dictWord{15, 10, 22}, + dictWord{148, 10, 63}, + dictWord{132, 0, 259}, + dictWord{135, 11, 183}, + dictWord{9, 10, 209}, + dictWord{ + 137, + 10, + 300, + }, + dictWord{5, 11, 937}, + dictWord{135, 11, 100}, + dictWord{133, 10, 98}, + dictWord{4, 0, 173}, + dictWord{5, 0, 312}, + dictWord{5, 0, 512}, + dictWord{ + 135, + 0, + 1285, + }, + dictWord{141, 0, 185}, + dictWord{7, 0, 1603}, + dictWord{7, 0, 1691}, + dictWord{9, 0, 464}, + dictWord{11, 0, 195}, + dictWord{12, 0, 279}, + dictWord{ + 12, + 0, + 448, + }, + dictWord{14, 0, 11}, + dictWord{147, 0, 102}, + dictWord{135, 0, 1113}, + dictWord{133, 10, 984}, + dictWord{4, 0, 452}, + dictWord{5, 0, 583}, + dictWord{ + 135, + 0, + 720, + }, + dictWord{4, 0, 547}, + dictWord{5, 0, 817}, + dictWord{6, 0, 433}, + dictWord{7, 0, 593}, + dictWord{7, 0, 1378}, + dictWord{8, 0, 161}, + dictWord{9, 0, 284}, + dictWord{ + 10, + 0, + 313, + }, + dictWord{139, 0, 886}, + dictWord{8, 0, 722}, + dictWord{4, 10, 182}, + dictWord{6, 10, 205}, + dictWord{135, 10, 220}, + dictWord{150, 0, 13}, + dictWord{ + 4, + 10, + 42, + }, + dictWord{9, 10, 205}, + dictWord{9, 10, 786}, + dictWord{138, 10, 659}, + dictWord{6, 0, 289}, + dictWord{7, 0, 1670}, + dictWord{12, 0, 57}, + dictWord{151, 0, 4}, + dictWord{132, 10, 635}, + dictWord{14, 0, 43}, + dictWord{146, 0, 21}, + dictWord{139, 10, 533}, + dictWord{135, 0, 1694}, + dictWord{8, 0, 420}, + dictWord{ + 139, + 0, + 193, + }, + dictWord{135, 0, 409}, + dictWord{132, 10, 371}, + dictWord{4, 10, 272}, + dictWord{135, 10, 836}, + dictWord{5, 10, 825}, + dictWord{134, 10, 1640}, + dictWord{5, 11, 251}, + dictWord{5, 11, 956}, + dictWord{8, 11, 268}, + dictWord{9, 11, 214}, + dictWord{146, 11, 142}, + dictWord{138, 0, 308}, + dictWord{6, 0, 1863}, + dictWord{141, 11, 37}, + dictWord{137, 10, 879}, + dictWord{7, 10, 317}, + dictWord{135, 10, 569}, + dictWord{132, 11, 294}, + dictWord{134, 0, 790}, + dictWord{ + 5, + 0, + 1002, + }, + dictWord{136, 0, 745}, + dictWord{5, 11, 346}, + dictWord{5, 11, 711}, + dictWord{136, 11, 390}, + dictWord{135, 0, 289}, + dictWord{5, 0, 504}, + dictWord{ + 11, + 0, + 68, + }, + dictWord{137, 10, 307}, + dictWord{4, 0, 239}, + dictWord{6, 0, 477}, + dictWord{7, 0, 1607}, + dictWord{139, 0, 617}, + dictWord{149, 0, 13}, + dictWord{ + 133, + 0, + 609, + }, + dictWord{133, 11, 624}, + dictWord{5, 11, 783}, + dictWord{7, 11, 1998}, + dictWord{135, 11, 2047}, + dictWord{133, 10, 525}, + dictWord{132, 0, 367}, + dictWord{132, 11, 594}, + dictWord{6, 0, 528}, + dictWord{133, 10, 493}, + dictWord{4, 10, 174}, + dictWord{135, 10, 911}, + dictWord{8, 10, 417}, + dictWord{ + 137, + 10, + 782, + }, + dictWord{132, 0, 694}, + dictWord{7, 0, 548}, + dictWord{137, 0, 58}, + dictWord{4, 10, 32}, + dictWord{5, 10, 215}, + dictWord{6, 10, 269}, + dictWord{7, 10, 1782}, + dictWord{7, 10, 1892}, + dictWord{10, 10, 16}, + dictWord{11, 10, 822}, + dictWord{11, 10, 954}, + dictWord{141, 10, 481}, + dictWord{140, 0, 687}, + dictWord{ + 7, + 0, + 1749, + }, + dictWord{136, 10, 477}, + dictWord{132, 11, 569}, + dictWord{133, 10, 308}, + dictWord{135, 10, 1088}, + dictWord{4, 0, 661}, + dictWord{138, 0, 1004}, + dictWord{5, 11, 37}, + dictWord{6, 11, 39}, + dictWord{6, 11, 451}, + dictWord{7, 11, 218}, + dictWord{7, 11, 667}, + dictWord{7, 11, 1166}, + dictWord{7, 11, 1687}, + dictWord{8, 11, 662}, + dictWord{144, 11, 2}, + dictWord{9, 0, 445}, + dictWord{12, 0, 53}, + dictWord{13, 0, 492}, + dictWord{5, 10, 126}, + dictWord{8, 10, 297}, + dictWord{ + 9, + 10, + 366, + }, + dictWord{140, 10, 374}, + dictWord{7, 10, 1551}, + dictWord{139, 10, 361}, + dictWord{148, 0, 74}, + dictWord{134, 11, 508}, + dictWord{135, 0, 213}, + dictWord{132, 10, 175}, + dictWord{132, 10, 685}, + dictWord{6, 0, 760}, + dictWord{6, 0, 834}, + dictWord{134, 0, 1248}, + dictWord{7, 11, 453}, + dictWord{7, 11, 635}, + dictWord{7, 11, 796}, + dictWord{8, 11, 331}, + dictWord{9, 11, 328}, + dictWord{9, 11, 330}, + dictWord{9, 11, 865}, + dictWord{10, 11, 119}, + dictWord{10, 11, 235}, + dictWord{11, 11, 111}, + dictWord{11, 11, 129}, + dictWord{11, 11, 240}, + dictWord{12, 11, 31}, + dictWord{12, 11, 66}, + dictWord{12, 11, 222}, + dictWord{12, 11, 269}, + dictWord{12, 11, 599}, + dictWord{12, 11, 689}, + dictWord{13, 11, 186}, + dictWord{13, 11, 364}, + dictWord{142, 11, 345}, + dictWord{7, 0, 1672}, + dictWord{ + 139, + 0, + 189, + }, + dictWord{133, 10, 797}, + dictWord{133, 10, 565}, + dictWord{6, 0, 1548}, + dictWord{6, 11, 98}, + dictWord{7, 11, 585}, + dictWord{135, 11, 702}, + dictWord{ + 9, + 0, + 968, + }, + dictWord{15, 0, 192}, + dictWord{149, 0, 56}, + dictWord{4, 10, 252}, + dictWord{6, 11, 37}, + dictWord{7, 11, 299}, + dictWord{7, 10, 1068}, + dictWord{ + 7, + 11, + 1666, + }, + dictWord{8, 11, 195}, + dictWord{8, 11, 316}, + dictWord{9, 11, 178}, + dictWord{9, 11, 276}, + dictWord{9, 11, 339}, + dictWord{9, 11, 536}, + dictWord{ + 10, + 11, + 102, + }, + dictWord{10, 11, 362}, + dictWord{10, 10, 434}, + dictWord{10, 11, 785}, + dictWord{11, 11, 55}, + dictWord{11, 11, 149}, + dictWord{11, 10, 228}, + dictWord{ + 11, + 10, + 426, + }, + dictWord{11, 11, 773}, + dictWord{13, 10, 231}, + dictWord{13, 11, 416}, + dictWord{13, 11, 419}, + dictWord{14, 11, 38}, + dictWord{14, 11, 41}, + dictWord{14, 11, 210}, + dictWord{18, 10, 106}, + dictWord{148, 10, 87}, + dictWord{4, 0, 751}, + dictWord{11, 0, 390}, + dictWord{140, 0, 32}, + dictWord{4, 0, 409}, + dictWord{133, 0, 78}, + dictWord{11, 11, 458}, + dictWord{12, 11, 15}, + dictWord{140, 11, 432}, + dictWord{7, 0, 1602}, + dictWord{10, 0, 257}, + dictWord{10, 0, 698}, + dictWord{11, 0, 544}, + dictWord{11, 0, 585}, + dictWord{12, 0, 212}, + dictWord{13, 0, 307}, + dictWord{5, 10, 231}, + dictWord{7, 10, 601}, + dictWord{9, 10, 277}, + dictWord{ + 9, + 10, + 674, + }, + dictWord{10, 10, 178}, + dictWord{10, 10, 418}, + dictWord{10, 10, 509}, + dictWord{11, 10, 531}, + dictWord{12, 10, 113}, + dictWord{12, 10, 475}, + dictWord{13, 10, 99}, + dictWord{142, 10, 428}, + dictWord{6, 0, 473}, + dictWord{145, 0, 105}, + dictWord{6, 0, 1949}, + dictWord{15, 0, 156}, + dictWord{133, 11, 645}, + dictWord{7, 10, 1591}, + dictWord{144, 10, 43}, + dictWord{135, 0, 1779}, + dictWord{135, 10, 1683}, + dictWord{4, 11, 290}, + dictWord{135, 11, 1356}, + dictWord{134, 0, 763}, + dictWord{6, 11, 70}, + dictWord{7, 11, 1292}, + dictWord{10, 11, 762}, + dictWord{139, 11, 288}, + dictWord{142, 0, 29}, + dictWord{140, 11, 428}, + dictWord{7, 0, 883}, + dictWord{7, 11, 131}, + dictWord{7, 11, 422}, + dictWord{8, 11, 210}, + dictWord{140, 11, 573}, + dictWord{134, 0, 488}, + dictWord{4, 10, 399}, + dictWord{5, 10, 119}, + dictWord{5, 10, 494}, + dictWord{7, 10, 751}, + dictWord{137, 10, 556}, + dictWord{133, 0, 617}, + dictWord{132, 11, 936}, + dictWord{ + 139, + 0, + 50, + }, + dictWord{7, 0, 1518}, + dictWord{139, 0, 694}, + dictWord{137, 0, 785}, + dictWord{4, 0, 546}, + dictWord{135, 0, 2042}, + dictWord{7, 11, 716}, + dictWord{ + 13, + 11, + 97, + }, + dictWord{141, 11, 251}, + dictWord{132, 11, 653}, + dictWord{145, 0, 22}, + dictWord{134, 0, 1016}, + dictWord{4, 0, 313}, + dictWord{133, 0, 577}, + dictWord{ + 136, + 11, + 657, + }, + dictWord{8, 0, 184}, + dictWord{141, 0, 433}, + dictWord{135, 0, 935}, + dictWord{6, 0, 720}, + dictWord{9, 0, 114}, + dictWord{146, 11, 80}, + dictWord{ + 12, + 0, + 186, + }, + dictWord{12, 0, 292}, + dictWord{14, 0, 100}, + dictWord{18, 0, 70}, + dictWord{7, 10, 594}, + dictWord{7, 10, 851}, + dictWord{7, 10, 1858}, + dictWord{ + 9, + 10, + 411, + }, + dictWord{9, 10, 574}, + dictWord{9, 10, 666}, + dictWord{9, 10, 737}, + dictWord{10, 10, 346}, + dictWord{10, 10, 712}, + dictWord{11, 10, 246}, + dictWord{ + 11, + 10, + 432, + }, + dictWord{11, 10, 517}, + dictWord{11, 10, 647}, + dictWord{11, 10, 679}, + dictWord{11, 10, 727}, + dictWord{12, 10, 304}, + dictWord{12, 10, 305}, + dictWord{12, 10, 323}, + dictWord{12, 10, 483}, + dictWord{12, 10, 572}, + dictWord{12, 10, 593}, + dictWord{12, 10, 602}, + dictWord{13, 10, 95}, + dictWord{13, 10, 101}, + dictWord{13, 10, 171}, + dictWord{13, 10, 315}, + dictWord{13, 10, 378}, + dictWord{13, 10, 425}, + dictWord{13, 10, 475}, + dictWord{14, 10, 63}, + dictWord{ + 14, + 10, + 380, + }, + dictWord{14, 10, 384}, + dictWord{15, 10, 133}, + dictWord{18, 10, 112}, + dictWord{148, 10, 72}, + dictWord{135, 10, 1093}, + dictWord{135, 11, 1836}, + dictWord{132, 10, 679}, + dictWord{137, 10, 203}, + dictWord{11, 0, 402}, + dictWord{12, 0, 109}, + dictWord{12, 0, 431}, + dictWord{13, 0, 179}, + dictWord{13, 0, 206}, + dictWord{14, 0, 217}, + dictWord{16, 0, 3}, + dictWord{148, 0, 53}, + dictWord{7, 11, 1368}, + dictWord{8, 11, 232}, + dictWord{8, 11, 361}, + dictWord{10, 11, 682}, + dictWord{138, 11, 742}, + dictWord{137, 10, 714}, + dictWord{5, 0, 886}, + dictWord{6, 0, 46}, + dictWord{6, 0, 1790}, + dictWord{7, 0, 14}, + dictWord{7, 0, 732}, + dictWord{ + 7, + 0, + 1654, + }, + dictWord{8, 0, 95}, + dictWord{8, 0, 327}, + dictWord{8, 0, 616}, + dictWord{9, 0, 892}, + dictWord{10, 0, 598}, + dictWord{10, 0, 769}, + dictWord{11, 0, 134}, + dictWord{11, 0, 747}, + dictWord{12, 0, 378}, + dictWord{14, 0, 97}, + dictWord{137, 11, 534}, + dictWord{4, 0, 969}, + dictWord{136, 10, 825}, + dictWord{137, 11, 27}, + dictWord{6, 0, 727}, + dictWord{142, 11, 12}, + dictWord{133, 0, 1021}, + dictWord{134, 0, 1190}, + dictWord{134, 11, 1657}, + dictWord{5, 10, 143}, + dictWord{ + 5, + 10, + 769, + }, + dictWord{6, 10, 1760}, + dictWord{7, 10, 682}, + dictWord{7, 10, 1992}, + dictWord{136, 10, 736}, + dictWord{132, 0, 153}, + dictWord{135, 11, 127}, + dictWord{133, 0, 798}, + dictWord{132, 0, 587}, + dictWord{6, 0, 598}, + dictWord{7, 0, 42}, + dictWord{8, 0, 695}, + dictWord{10, 0, 212}, + dictWord{11, 0, 158}, + dictWord{ + 14, + 0, + 196, + }, + dictWord{145, 0, 85}, + dictWord{133, 10, 860}, + dictWord{6, 0, 1929}, + dictWord{134, 0, 1933}, + dictWord{5, 0, 957}, + dictWord{5, 0, 1008}, + dictWord{ + 9, + 0, + 577, + }, + dictWord{12, 0, 141}, + dictWord{6, 10, 422}, + dictWord{7, 10, 0}, + dictWord{7, 10, 1544}, + dictWord{8, 11, 364}, + dictWord{11, 10, 990}, + dictWord{ + 12, + 10, + 453, + }, + dictWord{13, 10, 47}, + dictWord{141, 10, 266}, + dictWord{134, 0, 1319}, + dictWord{4, 0, 129}, + dictWord{135, 0, 465}, + dictWord{7, 0, 470}, + dictWord{ + 7, + 0, + 1057, + }, + dictWord{7, 0, 1201}, + dictWord{9, 0, 755}, + dictWord{11, 0, 906}, + dictWord{140, 0, 527}, + dictWord{7, 0, 908}, + dictWord{146, 0, 7}, + dictWord{5, 0, 148}, + dictWord{136, 0, 450}, + dictWord{5, 10, 515}, + dictWord{137, 10, 131}, + dictWord{7, 10, 1605}, + dictWord{11, 10, 962}, + dictWord{146, 10, 139}, + dictWord{ + 132, + 10, + 646, + }, + dictWord{134, 0, 1166}, + dictWord{4, 10, 396}, + dictWord{7, 10, 728}, + dictWord{9, 10, 117}, + dictWord{13, 10, 202}, + dictWord{148, 10, 51}, + dictWord{ + 6, + 10, + 121, + }, + dictWord{6, 10, 124}, + dictWord{6, 10, 357}, + dictWord{7, 10, 1138}, + dictWord{7, 10, 1295}, + dictWord{8, 10, 162}, + dictWord{139, 10, 655}, + dictWord{14, 0, 374}, + dictWord{142, 11, 374}, + dictWord{138, 0, 253}, + dictWord{139, 0, 1003}, + dictWord{5, 11, 909}, + dictWord{9, 11, 849}, + dictWord{ + 138, + 11, + 805, + }, + dictWord{133, 10, 237}, + dictWord{7, 11, 525}, + dictWord{7, 11, 1579}, + dictWord{8, 11, 497}, + dictWord{136, 11, 573}, + dictWord{137, 0, 46}, + dictWord{ + 132, + 0, + 879, + }, + dictWord{134, 0, 806}, + dictWord{135, 0, 1868}, + dictWord{6, 0, 1837}, + dictWord{134, 0, 1846}, + dictWord{6, 0, 730}, + dictWord{134, 0, 881}, + dictWord{7, 0, 965}, + dictWord{7, 0, 1460}, + dictWord{7, 0, 1604}, + dictWord{7, 11, 193}, + dictWord{7, 11, 397}, + dictWord{7, 11, 1105}, + dictWord{8, 11, 124}, + dictWord{ + 8, + 11, + 619, + }, + dictWord{9, 11, 305}, + dictWord{10, 11, 264}, + dictWord{11, 11, 40}, + dictWord{12, 11, 349}, + dictWord{13, 11, 134}, + dictWord{13, 11, 295}, + dictWord{14, 11, 155}, + dictWord{15, 11, 120}, + dictWord{146, 11, 105}, + dictWord{136, 0, 506}, + dictWord{143, 0, 10}, + dictWord{4, 11, 262}, + dictWord{7, 11, 342}, + dictWord{7, 10, 571}, + dictWord{7, 10, 1877}, + dictWord{10, 10, 366}, + dictWord{141, 11, 23}, + dictWord{133, 11, 641}, + dictWord{10, 0, 22}, + dictWord{9, 10, 513}, + dictWord{10, 10, 39}, + dictWord{12, 10, 122}, + dictWord{140, 10, 187}, + dictWord{135, 11, 1431}, + dictWord{150, 11, 49}, + dictWord{4, 11, 99}, + dictWord{ + 6, + 11, + 250, + }, + dictWord{6, 11, 346}, + dictWord{8, 11, 127}, + dictWord{138, 11, 81}, + dictWord{6, 0, 2014}, + dictWord{8, 0, 928}, + dictWord{10, 0, 960}, + dictWord{10, 0, 979}, + dictWord{140, 0, 996}, + dictWord{134, 0, 296}, + dictWord{132, 11, 915}, + dictWord{5, 11, 75}, + dictWord{9, 11, 517}, + dictWord{10, 11, 470}, + dictWord{ + 12, + 11, + 155, + }, + dictWord{141, 11, 224}, + dictWord{137, 10, 873}, + dictWord{4, 0, 854}, + dictWord{140, 11, 18}, + dictWord{134, 0, 587}, + dictWord{7, 10, 107}, + dictWord{ + 7, + 10, + 838, + }, + dictWord{8, 10, 550}, + dictWord{138, 10, 401}, + dictWord{11, 0, 636}, + dictWord{15, 0, 145}, + dictWord{17, 0, 34}, + dictWord{19, 0, 50}, + dictWord{ + 23, + 0, + 20, + }, + dictWord{11, 10, 588}, + dictWord{11, 10, 864}, + dictWord{11, 10, 968}, + dictWord{143, 10, 160}, + dictWord{135, 11, 216}, + dictWord{7, 0, 982}, + dictWord{ + 10, + 0, + 32, + }, + dictWord{143, 0, 56}, + dictWord{133, 10, 768}, + dictWord{133, 11, 954}, + dictWord{6, 11, 304}, + dictWord{7, 11, 1114}, + dictWord{8, 11, 418}, + dictWord{ + 10, + 11, + 345, + }, + dictWord{11, 11, 341}, + dictWord{11, 11, 675}, + dictWord{141, 11, 40}, + dictWord{9, 11, 410}, + dictWord{139, 11, 425}, + dictWord{136, 0, 941}, + dictWord{5, 0, 435}, + dictWord{132, 10, 894}, + dictWord{5, 0, 85}, + dictWord{6, 0, 419}, + dictWord{7, 0, 134}, + dictWord{7, 0, 305}, + dictWord{7, 0, 361}, + dictWord{ + 7, + 0, + 1337, + }, + dictWord{8, 0, 71}, + dictWord{140, 0, 519}, + dictWord{140, 0, 688}, + dictWord{135, 0, 740}, + dictWord{5, 0, 691}, + dictWord{7, 0, 345}, + dictWord{9, 0, 94}, + dictWord{140, 0, 169}, + dictWord{5, 0, 183}, + dictWord{6, 0, 582}, + dictWord{10, 0, 679}, + dictWord{140, 0, 435}, + dictWord{134, 11, 14}, + dictWord{6, 0, 945}, + dictWord{135, 0, 511}, + dictWord{134, 11, 1708}, + dictWord{5, 11, 113}, + dictWord{6, 11, 243}, + dictWord{7, 11, 1865}, + dictWord{11, 11, 161}, + dictWord{16, 11, 37}, + dictWord{145, 11, 99}, + dictWord{132, 11, 274}, + dictWord{137, 0, 539}, + dictWord{7, 0, 1993}, + dictWord{8, 0, 684}, + dictWord{134, 10, 272}, + dictWord{ + 6, + 0, + 659, + }, + dictWord{134, 0, 982}, + dictWord{4, 10, 9}, + dictWord{5, 10, 128}, + dictWord{7, 10, 368}, + dictWord{11, 10, 480}, + dictWord{148, 10, 3}, + dictWord{ + 134, + 0, + 583, + }, + dictWord{132, 0, 803}, + dictWord{133, 0, 704}, + dictWord{4, 0, 179}, + dictWord{5, 0, 198}, + dictWord{133, 0, 697}, + dictWord{7, 0, 347}, + dictWord{7, 0, 971}, + dictWord{8, 0, 181}, + dictWord{10, 0, 711}, + dictWord{135, 11, 166}, + dictWord{136, 10, 682}, + dictWord{4, 10, 2}, + dictWord{7, 10, 545}, + dictWord{7, 10, 894}, + dictWord{136, 11, 521}, + dictWord{135, 0, 481}, + dictWord{132, 0, 243}, + dictWord{5, 0, 203}, + dictWord{7, 0, 19}, + dictWord{7, 0, 71}, + dictWord{7, 0, 113}, + dictWord{ + 10, + 0, + 405, + }, + dictWord{11, 0, 357}, + dictWord{142, 0, 240}, + dictWord{5, 11, 725}, + dictWord{5, 11, 727}, + dictWord{135, 11, 1811}, + dictWord{6, 0, 826}, + dictWord{ + 137, + 11, + 304, + }, + dictWord{7, 0, 1450}, + dictWord{139, 0, 99}, + dictWord{133, 11, 654}, + dictWord{134, 0, 492}, + dictWord{5, 0, 134}, + dictWord{6, 0, 408}, + dictWord{ + 6, + 0, + 495, + }, + dictWord{7, 0, 1593}, + dictWord{6, 11, 273}, + dictWord{10, 11, 188}, + dictWord{13, 11, 377}, + dictWord{146, 11, 77}, + dictWord{9, 10, 769}, + dictWord{ + 140, + 10, + 185, + }, + dictWord{135, 11, 410}, + dictWord{142, 0, 4}, + dictWord{4, 0, 665}, + dictWord{134, 11, 1785}, + dictWord{4, 0, 248}, + dictWord{7, 0, 137}, + dictWord{ + 137, + 0, + 349, + }, + dictWord{5, 10, 530}, + dictWord{142, 10, 113}, + dictWord{7, 0, 1270}, + dictWord{139, 0, 612}, + dictWord{132, 11, 780}, + dictWord{5, 0, 371}, + dictWord{135, 0, 563}, + dictWord{135, 0, 826}, + dictWord{6, 0, 1535}, + dictWord{23, 0, 21}, + dictWord{151, 0, 23}, + dictWord{4, 0, 374}, + dictWord{7, 0, 547}, + dictWord{ + 7, + 0, + 1700, + }, + dictWord{7, 0, 1833}, + dictWord{139, 0, 858}, + dictWord{133, 10, 556}, + dictWord{7, 11, 612}, + dictWord{8, 11, 545}, + dictWord{8, 11, 568}, + dictWord{ + 8, + 11, + 642, + }, + dictWord{9, 11, 717}, + dictWord{10, 11, 541}, + dictWord{10, 11, 763}, + dictWord{11, 11, 449}, + dictWord{12, 11, 489}, + dictWord{13, 11, 153}, + dictWord{ + 13, + 11, + 296, + }, + dictWord{14, 11, 138}, + dictWord{14, 11, 392}, + dictWord{15, 11, 50}, + dictWord{16, 11, 6}, + dictWord{16, 11, 12}, + dictWord{148, 11, 9}, + dictWord{ + 9, + 0, + 311, + }, + dictWord{141, 0, 42}, + dictWord{8, 10, 16}, + dictWord{140, 10, 568}, + dictWord{6, 0, 1968}, + dictWord{6, 0, 2027}, + dictWord{138, 0, 991}, + dictWord{ + 6, + 0, + 1647, + }, + dictWord{7, 0, 1552}, + dictWord{7, 0, 2010}, + dictWord{9, 0, 494}, + dictWord{137, 0, 509}, + dictWord{133, 11, 948}, + dictWord{6, 10, 186}, + dictWord{ + 137, + 10, + 426, + }, + dictWord{134, 0, 769}, + dictWord{134, 0, 642}, + dictWord{132, 10, 585}, + dictWord{6, 0, 123}, + dictWord{7, 0, 214}, + dictWord{9, 0, 728}, + dictWord{ + 10, + 0, + 157, + }, + dictWord{11, 0, 346}, + dictWord{11, 0, 662}, + dictWord{143, 0, 106}, + dictWord{142, 11, 381}, + dictWord{135, 0, 1435}, + dictWord{4, 11, 532}, + dictWord{ + 5, + 11, + 706, + }, + dictWord{135, 11, 662}, + dictWord{5, 11, 837}, + dictWord{134, 11, 1651}, + dictWord{4, 10, 93}, + dictWord{5, 10, 252}, + dictWord{6, 10, 229}, + dictWord{ + 7, + 10, + 291, + }, + dictWord{9, 10, 550}, + dictWord{139, 10, 644}, + dictWord{148, 0, 79}, + dictWord{137, 10, 749}, + dictWord{134, 0, 1425}, + dictWord{ + 137, + 10, + 162, + }, + dictWord{4, 11, 362}, + dictWord{7, 11, 52}, + dictWord{7, 11, 303}, + dictWord{140, 11, 166}, + dictWord{132, 10, 381}, + dictWord{4, 11, 330}, + dictWord{ + 7, + 11, + 933, + }, + dictWord{7, 11, 2012}, + dictWord{136, 11, 292}, + dictWord{135, 11, 767}, + dictWord{4, 0, 707}, + dictWord{5, 0, 588}, + dictWord{6, 0, 393}, + dictWord{ + 13, + 0, + 106, + }, + dictWord{18, 0, 49}, + dictWord{147, 0, 41}, + dictWord{6, 0, 211}, + dictWord{7, 0, 1690}, + dictWord{11, 0, 486}, + dictWord{140, 0, 369}, + dictWord{ + 137, + 11, + 883, + }, + dictWord{4, 11, 703}, + dictWord{135, 11, 207}, + dictWord{4, 0, 187}, + dictWord{5, 0, 184}, + dictWord{5, 0, 690}, + dictWord{7, 0, 1869}, + dictWord{10, 0, 756}, + dictWord{139, 0, 783}, + dictWord{132, 11, 571}, + dictWord{134, 0, 1382}, + dictWord{5, 0, 175}, + dictWord{6, 10, 77}, + dictWord{6, 10, 157}, + dictWord{7, 10, 974}, + dictWord{7, 10, 1301}, + dictWord{7, 10, 1339}, + dictWord{7, 10, 1490}, + dictWord{7, 10, 1873}, + dictWord{137, 10, 628}, + dictWord{134, 0, 1493}, + dictWord{ + 5, + 11, + 873, + }, + dictWord{133, 11, 960}, + dictWord{134, 0, 1007}, + dictWord{12, 11, 93}, + dictWord{12, 11, 501}, + dictWord{13, 11, 362}, + dictWord{14, 11, 151}, + dictWord{15, 11, 40}, + dictWord{15, 11, 59}, + dictWord{16, 11, 46}, + dictWord{17, 11, 25}, + dictWord{18, 11, 14}, + dictWord{18, 11, 134}, + dictWord{19, 11, 25}, + dictWord{ + 19, + 11, + 69, + }, + dictWord{20, 11, 16}, + dictWord{20, 11, 19}, + dictWord{20, 11, 66}, + dictWord{21, 11, 23}, + dictWord{21, 11, 25}, + dictWord{150, 11, 42}, + dictWord{ + 11, + 10, + 919, + }, + dictWord{141, 10, 409}, + dictWord{134, 0, 219}, + dictWord{5, 0, 582}, + dictWord{6, 0, 1646}, + dictWord{7, 0, 99}, + dictWord{7, 0, 1962}, + dictWord{ + 7, + 0, + 1986, + }, + dictWord{8, 0, 515}, + dictWord{8, 0, 773}, + dictWord{9, 0, 23}, + dictWord{9, 0, 491}, + dictWord{12, 0, 620}, + dictWord{142, 0, 93}, + dictWord{133, 0, 851}, + dictWord{5, 11, 33}, + dictWord{134, 11, 470}, + dictWord{135, 11, 1291}, + dictWord{134, 0, 1278}, + dictWord{135, 11, 1882}, + dictWord{135, 10, 1489}, + dictWord{132, 0, 1000}, + dictWord{138, 0, 982}, + dictWord{8, 0, 762}, + dictWord{8, 0, 812}, + dictWord{137, 0, 910}, + dictWord{6, 11, 47}, + dictWord{7, 11, 90}, + dictWord{ + 7, + 11, + 664, + }, + dictWord{7, 11, 830}, + dictWord{7, 11, 1380}, + dictWord{7, 11, 2025}, + dictWord{8, 11, 448}, + dictWord{136, 11, 828}, + dictWord{4, 0, 98}, + dictWord{ + 4, + 0, + 940, + }, + dictWord{6, 0, 1819}, + dictWord{6, 0, 1834}, + dictWord{6, 0, 1841}, + dictWord{7, 0, 1365}, + dictWord{8, 0, 859}, + dictWord{8, 0, 897}, + dictWord{8, 0, 918}, + dictWord{9, 0, 422}, + dictWord{9, 0, 670}, + dictWord{10, 0, 775}, + dictWord{10, 0, 894}, + dictWord{10, 0, 909}, + dictWord{10, 0, 910}, + dictWord{10, 0, 935}, + dictWord{ + 11, + 0, + 210, + }, + dictWord{12, 0, 750}, + dictWord{12, 0, 755}, + dictWord{13, 0, 26}, + dictWord{13, 0, 457}, + dictWord{13, 0, 476}, + dictWord{16, 0, 100}, + dictWord{16, 0, 109}, + dictWord{18, 0, 173}, + dictWord{18, 0, 175}, + dictWord{8, 10, 398}, + dictWord{9, 10, 681}, + dictWord{139, 10, 632}, + dictWord{9, 11, 417}, + dictWord{ + 137, + 11, + 493, + }, + dictWord{136, 10, 645}, + dictWord{138, 0, 906}, + dictWord{134, 0, 1730}, + dictWord{134, 10, 20}, + dictWord{133, 11, 1019}, + dictWord{134, 0, 1185}, + dictWord{10, 0, 40}, + dictWord{136, 10, 769}, + dictWord{9, 0, 147}, + dictWord{134, 11, 208}, + dictWord{140, 0, 650}, + dictWord{5, 0, 209}, + dictWord{6, 0, 30}, + dictWord{11, 0, 56}, + dictWord{139, 0, 305}, + dictWord{132, 0, 553}, + dictWord{138, 11, 344}, + dictWord{6, 11, 68}, + dictWord{7, 11, 398}, + dictWord{7, 11, 448}, + dictWord{ + 7, + 11, + 1629, + }, + dictWord{7, 11, 1813}, + dictWord{8, 11, 387}, + dictWord{8, 11, 442}, + dictWord{9, 11, 710}, + dictWord{10, 11, 282}, + dictWord{138, 11, 722}, + dictWord{5, 0, 597}, + dictWord{14, 0, 20}, + dictWord{142, 11, 20}, + dictWord{135, 0, 1614}, + dictWord{135, 10, 1757}, + dictWord{4, 0, 150}, + dictWord{5, 0, 303}, + dictWord{6, 0, 327}, + dictWord{135, 10, 937}, + dictWord{16, 0, 49}, + dictWord{7, 10, 1652}, + dictWord{144, 11, 49}, + dictWord{8, 0, 192}, + dictWord{10, 0, 78}, + dictWord{ + 141, + 0, + 359, + }, + dictWord{135, 0, 786}, + dictWord{143, 0, 134}, + dictWord{6, 0, 1638}, + dictWord{7, 0, 79}, + dictWord{7, 0, 496}, + dictWord{9, 0, 138}, + dictWord{ + 10, + 0, + 336, + }, + dictWord{11, 0, 12}, + dictWord{12, 0, 412}, + dictWord{12, 0, 440}, + dictWord{142, 0, 305}, + dictWord{136, 11, 491}, + dictWord{4, 10, 579}, + dictWord{ + 5, + 10, + 226, + }, + dictWord{5, 10, 323}, + dictWord{135, 10, 960}, + dictWord{7, 0, 204}, + dictWord{7, 0, 415}, + dictWord{8, 0, 42}, + dictWord{10, 0, 85}, + dictWord{139, 0, 564}, + dictWord{132, 0, 614}, + dictWord{4, 11, 403}, + dictWord{5, 11, 441}, + dictWord{7, 11, 450}, + dictWord{11, 11, 101}, + dictWord{12, 11, 193}, + dictWord{141, 11, 430}, + dictWord{135, 11, 1927}, + dictWord{135, 11, 1330}, + dictWord{4, 0, 3}, + dictWord{5, 0, 247}, + dictWord{5, 0, 644}, + dictWord{7, 0, 744}, + dictWord{7, 0, 1207}, + dictWord{7, 0, 1225}, + dictWord{7, 0, 1909}, + dictWord{146, 0, 147}, + dictWord{136, 0, 942}, + dictWord{4, 0, 1019}, + dictWord{134, 0, 2023}, + dictWord{5, 11, 679}, + dictWord{133, 10, 973}, + dictWord{5, 0, 285}, + dictWord{9, 0, 67}, + dictWord{13, 0, 473}, + dictWord{143, 0, 82}, + dictWord{7, 11, 328}, + dictWord{137, 11, 326}, + dictWord{151, 0, 8}, + dictWord{6, 10, 135}, + dictWord{135, 10, 1176}, + dictWord{135, 11, 1128}, + dictWord{134, 0, 1309}, + dictWord{135, 11, 1796}, + dictWord{ + 135, + 10, + 314, + }, + dictWord{4, 11, 574}, + dictWord{7, 11, 350}, + dictWord{7, 11, 1024}, + dictWord{8, 11, 338}, + dictWord{9, 11, 677}, + dictWord{10, 11, 808}, + dictWord{ + 139, + 11, + 508, + }, + dictWord{7, 11, 818}, + dictWord{17, 11, 14}, + dictWord{17, 11, 45}, + dictWord{18, 11, 75}, + dictWord{148, 11, 18}, + dictWord{146, 10, 4}, + dictWord{ + 135, + 11, + 1081, + }, + dictWord{4, 0, 29}, + dictWord{6, 0, 532}, + dictWord{7, 0, 1628}, + dictWord{7, 0, 1648}, + dictWord{9, 0, 350}, + dictWord{10, 0, 433}, + dictWord{11, 0, 97}, + dictWord{11, 0, 557}, + dictWord{11, 0, 745}, + dictWord{12, 0, 289}, + dictWord{12, 0, 335}, + dictWord{12, 0, 348}, + dictWord{12, 0, 606}, + dictWord{13, 0, 116}, + dictWord{13, 0, 233}, + dictWord{13, 0, 466}, + dictWord{14, 0, 181}, + dictWord{14, 0, 209}, + dictWord{14, 0, 232}, + dictWord{14, 0, 236}, + dictWord{14, 0, 300}, + dictWord{ + 16, + 0, + 41, + }, + dictWord{148, 0, 97}, + dictWord{7, 0, 318}, + dictWord{6, 10, 281}, + dictWord{8, 10, 282}, + dictWord{8, 10, 480}, + dictWord{8, 10, 499}, + dictWord{9, 10, 198}, + dictWord{10, 10, 143}, + dictWord{10, 10, 169}, + dictWord{10, 10, 211}, + dictWord{10, 10, 417}, + dictWord{10, 10, 574}, + dictWord{11, 10, 147}, + dictWord{ + 11, + 10, + 395, + }, + dictWord{12, 10, 75}, + dictWord{12, 10, 407}, + dictWord{12, 10, 608}, + dictWord{13, 10, 500}, + dictWord{142, 10, 251}, + dictWord{135, 11, 1676}, + dictWord{135, 11, 2037}, + dictWord{135, 0, 1692}, + dictWord{5, 0, 501}, + dictWord{7, 0, 1704}, + dictWord{9, 0, 553}, + dictWord{11, 0, 520}, + dictWord{12, 0, 557}, + dictWord{141, 0, 249}, + dictWord{6, 0, 1527}, + dictWord{14, 0, 324}, + dictWord{15, 0, 55}, + dictWord{15, 0, 80}, + dictWord{14, 11, 324}, + dictWord{15, 11, 55}, + dictWord{143, 11, 80}, + dictWord{135, 10, 1776}, + dictWord{8, 0, 988}, + dictWord{137, 11, 297}, + dictWord{132, 10, 419}, + dictWord{142, 0, 223}, + dictWord{ + 139, + 11, + 234, + }, + dictWord{7, 0, 1123}, + dictWord{12, 0, 508}, + dictWord{14, 0, 102}, + dictWord{14, 0, 226}, + dictWord{144, 0, 57}, + dictWord{4, 10, 138}, + dictWord{ + 7, + 10, + 1012, + }, + dictWord{7, 10, 1280}, + dictWord{137, 10, 76}, + dictWord{7, 0, 1764}, + dictWord{5, 10, 29}, + dictWord{140, 10, 638}, + dictWord{134, 0, 2015}, + dictWord{134, 0, 1599}, + dictWord{138, 11, 56}, + dictWord{6, 11, 306}, + dictWord{7, 11, 1140}, + dictWord{7, 11, 1340}, + dictWord{8, 11, 133}, + dictWord{ + 138, + 11, + 449, + }, + dictWord{139, 11, 1011}, + dictWord{6, 10, 1710}, + dictWord{135, 10, 2038}, + dictWord{7, 11, 1763}, + dictWord{140, 11, 310}, + dictWord{6, 0, 129}, + dictWord{4, 10, 17}, + dictWord{5, 10, 23}, + dictWord{7, 10, 995}, + dictWord{11, 10, 383}, + dictWord{11, 10, 437}, + dictWord{12, 10, 460}, + dictWord{140, 10, 532}, + dictWord{5, 11, 329}, + dictWord{136, 11, 260}, + dictWord{133, 10, 862}, + dictWord{132, 0, 534}, + dictWord{6, 0, 811}, + dictWord{135, 0, 626}, + dictWord{ + 132, + 11, + 657, + }, + dictWord{4, 0, 25}, + dictWord{5, 0, 60}, + dictWord{6, 0, 504}, + dictWord{7, 0, 614}, + dictWord{7, 0, 1155}, + dictWord{12, 0, 0}, + dictWord{152, 11, 7}, + dictWord{ + 7, + 0, + 1248, + }, + dictWord{11, 0, 621}, + dictWord{139, 0, 702}, + dictWord{137, 0, 321}, + dictWord{8, 10, 70}, + dictWord{12, 10, 171}, + dictWord{141, 10, 272}, + dictWord{ + 10, + 10, + 233, + }, + dictWord{139, 10, 76}, + dictWord{4, 0, 379}, + dictWord{7, 0, 1397}, + dictWord{134, 10, 442}, + dictWord{5, 11, 66}, + dictWord{7, 11, 1896}, + dictWord{ + 136, + 11, + 288, + }, + dictWord{134, 11, 1643}, + dictWord{134, 10, 1709}, + dictWord{4, 11, 21}, + dictWord{5, 11, 91}, + dictWord{5, 11, 570}, + dictWord{5, 11, 648}, + dictWord{5, 11, 750}, + dictWord{5, 11, 781}, + dictWord{6, 11, 54}, + dictWord{6, 11, 112}, + dictWord{6, 11, 402}, + dictWord{6, 11, 1732}, + dictWord{7, 11, 315}, + dictWord{ + 7, + 11, + 749, + }, + dictWord{7, 11, 1347}, + dictWord{7, 11, 1900}, + dictWord{9, 11, 78}, + dictWord{9, 11, 508}, + dictWord{10, 11, 611}, + dictWord{11, 11, 510}, + dictWord{ + 11, + 11, + 728, + }, + dictWord{13, 11, 36}, + dictWord{14, 11, 39}, + dictWord{16, 11, 83}, + dictWord{17, 11, 124}, + dictWord{148, 11, 30}, + dictWord{4, 0, 118}, + dictWord{ + 6, + 0, + 274, + }, + dictWord{6, 0, 361}, + dictWord{7, 0, 75}, + dictWord{141, 0, 441}, + dictWord{10, 11, 322}, + dictWord{10, 11, 719}, + dictWord{139, 11, 407}, + dictWord{ + 147, + 10, + 119, + }, + dictWord{12, 11, 549}, + dictWord{14, 11, 67}, + dictWord{147, 11, 60}, + dictWord{11, 10, 69}, + dictWord{12, 10, 105}, + dictWord{12, 10, 117}, + dictWord{13, 10, 213}, + dictWord{14, 10, 13}, + dictWord{14, 10, 62}, + dictWord{14, 10, 177}, + dictWord{14, 10, 421}, + dictWord{15, 10, 19}, + dictWord{146, 10, 141}, + dictWord{9, 0, 841}, + dictWord{137, 10, 309}, + dictWord{7, 10, 608}, + dictWord{7, 10, 976}, + dictWord{8, 11, 125}, + dictWord{8, 11, 369}, + dictWord{8, 11, 524}, + dictWord{9, 10, 146}, + dictWord{10, 10, 206}, + dictWord{10, 11, 486}, + dictWord{10, 10, 596}, + dictWord{11, 11, 13}, + dictWord{11, 11, 381}, + dictWord{11, 11, 736}, + dictWord{11, 11, 766}, + dictWord{11, 11, 845}, + dictWord{13, 11, 114}, + dictWord{13, 10, 218}, + dictWord{13, 11, 292}, + dictWord{14, 11, 47}, + dictWord{ + 142, + 10, + 153, + }, + dictWord{12, 0, 693}, + dictWord{135, 11, 759}, + dictWord{5, 0, 314}, + dictWord{6, 0, 221}, + dictWord{7, 0, 419}, + dictWord{10, 0, 650}, + dictWord{11, 0, 396}, + dictWord{12, 0, 156}, + dictWord{13, 0, 369}, + dictWord{14, 0, 333}, + dictWord{145, 0, 47}, + dictWord{6, 11, 1684}, + dictWord{6, 11, 1731}, + dictWord{7, 11, 356}, + dictWord{7, 11, 1932}, + dictWord{8, 11, 54}, + dictWord{8, 11, 221}, + dictWord{9, 11, 225}, + dictWord{9, 11, 356}, + dictWord{10, 11, 77}, + dictWord{10, 11, 446}, + dictWord{10, 11, 731}, + dictWord{12, 11, 404}, + dictWord{141, 11, 491}, + dictWord{132, 11, 375}, + dictWord{4, 10, 518}, + dictWord{135, 10, 1136}, + dictWord{ + 4, + 0, + 913, + }, + dictWord{4, 11, 411}, + dictWord{11, 11, 643}, + dictWord{140, 11, 115}, + dictWord{4, 11, 80}, + dictWord{133, 11, 44}, + dictWord{8, 10, 689}, + dictWord{ + 137, + 10, + 863, + }, + dictWord{138, 0, 880}, + dictWord{4, 10, 18}, + dictWord{7, 10, 145}, + dictWord{7, 10, 444}, + dictWord{7, 10, 1278}, + dictWord{8, 10, 49}, + dictWord{ + 8, + 10, + 400, + }, + dictWord{9, 10, 71}, + dictWord{9, 10, 250}, + dictWord{10, 10, 459}, + dictWord{12, 10, 160}, + dictWord{144, 10, 24}, + dictWord{136, 0, 475}, + dictWord{ + 5, + 0, + 1016, + }, + dictWord{5, 11, 299}, + dictWord{135, 11, 1083}, + dictWord{7, 0, 602}, + dictWord{8, 0, 179}, + dictWord{10, 0, 781}, + dictWord{140, 0, 126}, + dictWord{ + 6, + 0, + 329, + }, + dictWord{138, 0, 111}, + dictWord{135, 0, 1864}, + dictWord{4, 11, 219}, + dictWord{7, 11, 1761}, + dictWord{137, 11, 86}, + dictWord{6, 0, 1888}, + dictWord{ + 6, + 0, + 1892, + }, + dictWord{6, 0, 1901}, + dictWord{6, 0, 1904}, + dictWord{9, 0, 953}, + dictWord{9, 0, 985}, + dictWord{9, 0, 991}, + dictWord{9, 0, 1001}, + dictWord{12, 0, 818}, + dictWord{12, 0, 846}, + dictWord{12, 0, 847}, + dictWord{12, 0, 861}, + dictWord{12, 0, 862}, + dictWord{12, 0, 873}, + dictWord{12, 0, 875}, + dictWord{12, 0, 877}, + dictWord{12, 0, 879}, + dictWord{12, 0, 881}, + dictWord{12, 0, 884}, + dictWord{12, 0, 903}, + dictWord{12, 0, 915}, + dictWord{12, 0, 926}, + dictWord{12, 0, 939}, + dictWord{ + 15, + 0, + 182, + }, + dictWord{15, 0, 219}, + dictWord{15, 0, 255}, + dictWord{18, 0, 191}, + dictWord{18, 0, 209}, + dictWord{18, 0, 211}, + dictWord{149, 0, 41}, + dictWord{ + 5, + 11, + 328, + }, + dictWord{135, 11, 918}, + dictWord{137, 0, 780}, + dictWord{12, 0, 82}, + dictWord{143, 0, 36}, + dictWord{133, 10, 1010}, + dictWord{5, 0, 821}, + dictWord{ + 134, + 0, + 1687, + }, + dictWord{133, 11, 514}, + dictWord{132, 0, 956}, + dictWord{134, 0, 1180}, + dictWord{10, 0, 112}, + dictWord{5, 10, 87}, + dictWord{7, 10, 313}, + dictWord{ + 7, + 10, + 1103, + }, + dictWord{10, 10, 582}, + dictWord{11, 10, 389}, + dictWord{11, 10, 813}, + dictWord{12, 10, 385}, + dictWord{13, 10, 286}, + dictWord{14, 10, 124}, + dictWord{146, 10, 108}, + dictWord{5, 0, 71}, + dictWord{7, 0, 1407}, + dictWord{9, 0, 704}, + dictWord{10, 0, 261}, + dictWord{10, 0, 619}, + dictWord{11, 0, 547}, + dictWord{11, 0, 619}, + dictWord{143, 0, 157}, + dictWord{4, 0, 531}, + dictWord{5, 0, 455}, + dictWord{5, 11, 301}, + dictWord{6, 11, 571}, + dictWord{14, 11, 49}, + dictWord{ + 146, + 11, + 102, + }, + dictWord{132, 10, 267}, + dictWord{6, 0, 385}, + dictWord{7, 0, 2008}, + dictWord{9, 0, 337}, + dictWord{138, 0, 517}, + dictWord{133, 11, 726}, + dictWord{133, 11, 364}, + dictWord{4, 11, 76}, + dictWord{7, 11, 1550}, + dictWord{9, 11, 306}, + dictWord{9, 11, 430}, + dictWord{9, 11, 663}, + dictWord{10, 11, 683}, + dictWord{11, 11, 427}, + dictWord{11, 11, 753}, + dictWord{12, 11, 334}, + dictWord{12, 11, 442}, + dictWord{14, 11, 258}, + dictWord{14, 11, 366}, + dictWord{ + 143, + 11, + 131, + }, + dictWord{6, 0, 1865}, + dictWord{6, 0, 1879}, + dictWord{6, 0, 1881}, + dictWord{6, 0, 1894}, + dictWord{6, 0, 1908}, + dictWord{9, 0, 915}, + dictWord{9, 0, 926}, + dictWord{9, 0, 940}, + dictWord{9, 0, 943}, + dictWord{9, 0, 966}, + dictWord{9, 0, 980}, + dictWord{9, 0, 989}, + dictWord{9, 0, 1005}, + dictWord{9, 0, 1010}, + dictWord{ + 12, + 0, + 813, + }, + dictWord{12, 0, 817}, + dictWord{12, 0, 840}, + dictWord{12, 0, 843}, + dictWord{12, 0, 855}, + dictWord{12, 0, 864}, + dictWord{12, 0, 871}, + dictWord{12, 0, 872}, + dictWord{12, 0, 899}, + dictWord{12, 0, 905}, + dictWord{12, 0, 924}, + dictWord{15, 0, 171}, + dictWord{15, 0, 181}, + dictWord{15, 0, 224}, + dictWord{15, 0, 235}, + dictWord{15, 0, 251}, + dictWord{146, 0, 184}, + dictWord{137, 11, 52}, + dictWord{5, 0, 16}, + dictWord{6, 0, 86}, + dictWord{6, 0, 603}, + dictWord{7, 0, 292}, + dictWord{7, 0, 561}, + dictWord{8, 0, 257}, + dictWord{8, 0, 382}, + dictWord{9, 0, 721}, + dictWord{9, 0, 778}, + dictWord{11, 0, 581}, + dictWord{140, 0, 466}, + dictWord{4, 0, 486}, + dictWord{ + 5, + 0, + 491, + }, + dictWord{135, 10, 1121}, + dictWord{4, 0, 72}, + dictWord{6, 0, 265}, + dictWord{135, 0, 1300}, + dictWord{135, 11, 1183}, + dictWord{10, 10, 249}, + dictWord{139, 10, 209}, + dictWord{132, 10, 561}, + dictWord{137, 11, 519}, + dictWord{4, 11, 656}, + dictWord{4, 10, 760}, + dictWord{135, 11, 779}, + dictWord{ + 9, + 10, + 154, + }, + dictWord{140, 10, 485}, + dictWord{135, 11, 1793}, + dictWord{135, 11, 144}, + dictWord{136, 10, 255}, + dictWord{133, 0, 621}, + dictWord{4, 10, 368}, + dictWord{135, 10, 641}, + dictWord{135, 11, 1373}, + dictWord{7, 11, 554}, + dictWord{7, 11, 605}, + dictWord{141, 11, 10}, + dictWord{137, 0, 234}, + dictWord{ + 5, + 0, + 815, + }, + dictWord{6, 0, 1688}, + dictWord{134, 0, 1755}, + dictWord{5, 11, 838}, + dictWord{5, 11, 841}, + dictWord{134, 11, 1649}, + dictWord{7, 0, 1987}, + dictWord{ + 7, + 0, + 2040, + }, + dictWord{136, 0, 743}, + dictWord{133, 11, 1012}, + dictWord{6, 0, 197}, + dictWord{136, 0, 205}, + dictWord{6, 0, 314}, + dictWord{134, 11, 314}, + dictWord{144, 11, 53}, + dictWord{6, 11, 251}, + dictWord{7, 11, 365}, + dictWord{7, 11, 1357}, + dictWord{7, 11, 1497}, + dictWord{8, 11, 154}, + dictWord{141, 11, 281}, + dictWord{133, 11, 340}, + dictWord{6, 0, 452}, + dictWord{7, 0, 312}, + dictWord{138, 0, 219}, + dictWord{138, 0, 589}, + dictWord{4, 0, 333}, + dictWord{9, 0, 176}, + dictWord{12, 0, 353}, + dictWord{141, 0, 187}, + dictWord{9, 10, 92}, + dictWord{147, 10, 91}, + dictWord{134, 0, 1110}, + dictWord{11, 0, 47}, + dictWord{139, 11, 495}, + dictWord{6, 10, 525}, + dictWord{8, 10, 806}, + dictWord{9, 10, 876}, + dictWord{140, 10, 284}, + dictWord{8, 11, 261}, + dictWord{9, 11, 144}, + dictWord{9, 11, 466}, + dictWord{10, 11, 370}, + dictWord{12, 11, 470}, + dictWord{13, 11, 144}, + dictWord{142, 11, 348}, + dictWord{137, 11, 897}, + dictWord{8, 0, 863}, + dictWord{8, 0, 864}, + dictWord{8, 0, 868}, + dictWord{8, 0, 884}, + dictWord{10, 0, 866}, + dictWord{10, 0, 868}, + dictWord{10, 0, 873}, + dictWord{10, 0, 911}, + dictWord{10, 0, 912}, + dictWord{ + 10, + 0, + 944, + }, + dictWord{12, 0, 727}, + dictWord{6, 11, 248}, + dictWord{9, 11, 546}, + dictWord{10, 11, 535}, + dictWord{11, 11, 681}, + dictWord{141, 11, 135}, + dictWord{ + 6, + 0, + 300, + }, + dictWord{135, 0, 1515}, + dictWord{134, 0, 1237}, + dictWord{139, 10, 958}, + dictWord{133, 10, 594}, + dictWord{140, 11, 250}, + dictWord{ + 134, + 0, + 1685, + }, + dictWord{134, 11, 567}, + dictWord{7, 0, 135}, + dictWord{8, 0, 7}, + dictWord{8, 0, 62}, + dictWord{9, 0, 243}, + dictWord{10, 0, 658}, + dictWord{10, 0, 697}, + dictWord{11, 0, 456}, + dictWord{139, 0, 756}, + dictWord{9, 0, 395}, + dictWord{138, 0, 79}, + dictWord{6, 10, 1641}, + dictWord{136, 10, 820}, + dictWord{4, 10, 302}, + dictWord{135, 10, 1766}, + dictWord{134, 11, 174}, + dictWord{135, 10, 1313}, + dictWord{135, 0, 631}, + dictWord{134, 10, 1674}, + dictWord{134, 11, 395}, + dictWord{138, 0, 835}, + dictWord{7, 0, 406}, + dictWord{7, 0, 459}, + dictWord{8, 0, 606}, + dictWord{139, 0, 726}, + dictWord{134, 11, 617}, + dictWord{134, 0, 979}, + dictWord{ + 6, + 10, + 389, + }, + dictWord{7, 10, 149}, + dictWord{9, 10, 142}, + dictWord{138, 10, 94}, + dictWord{5, 11, 878}, + dictWord{133, 11, 972}, + dictWord{6, 10, 8}, + dictWord{ + 7, + 10, + 1881, + }, + dictWord{8, 10, 91}, + dictWord{136, 11, 511}, + dictWord{133, 0, 612}, + dictWord{132, 11, 351}, + dictWord{4, 0, 372}, + dictWord{7, 0, 482}, + dictWord{ + 8, + 0, + 158, + }, + dictWord{9, 0, 602}, + dictWord{9, 0, 615}, + dictWord{10, 0, 245}, + dictWord{10, 0, 678}, + dictWord{10, 0, 744}, + dictWord{11, 0, 248}, + dictWord{ + 139, + 0, + 806, + }, + dictWord{5, 0, 854}, + dictWord{135, 0, 1991}, + dictWord{132, 11, 286}, + dictWord{135, 11, 344}, + dictWord{7, 11, 438}, + dictWord{7, 11, 627}, + dictWord{ + 7, + 11, + 1516, + }, + dictWord{8, 11, 40}, + dictWord{9, 11, 56}, + dictWord{9, 11, 294}, + dictWord{10, 11, 30}, + dictWord{10, 11, 259}, + dictWord{11, 11, 969}, + dictWord{ + 146, + 11, + 148, + }, + dictWord{135, 0, 1492}, + dictWord{5, 11, 259}, + dictWord{7, 11, 414}, + dictWord{7, 11, 854}, + dictWord{142, 11, 107}, + dictWord{135, 10, 1746}, + dictWord{6, 0, 833}, + dictWord{134, 0, 998}, + dictWord{135, 10, 24}, + dictWord{6, 0, 750}, + dictWord{135, 0, 1739}, + dictWord{4, 10, 503}, + dictWord{ + 135, + 10, + 1661, + }, + dictWord{5, 10, 130}, + dictWord{7, 10, 1314}, + dictWord{9, 10, 610}, + dictWord{10, 10, 718}, + dictWord{11, 10, 601}, + dictWord{11, 10, 819}, + dictWord{ + 11, + 10, + 946, + }, + dictWord{140, 10, 536}, + dictWord{10, 10, 149}, + dictWord{11, 10, 280}, + dictWord{142, 10, 336}, + dictWord{132, 11, 738}, + dictWord{ + 135, + 10, + 1946, + }, + dictWord{5, 0, 195}, + dictWord{135, 0, 1685}, + dictWord{7, 0, 1997}, + dictWord{8, 0, 730}, + dictWord{139, 0, 1006}, + dictWord{151, 11, 17}, + dictWord{ + 133, + 11, + 866, + }, + dictWord{14, 0, 463}, + dictWord{14, 0, 470}, + dictWord{150, 0, 61}, + dictWord{5, 0, 751}, + dictWord{8, 0, 266}, + dictWord{11, 0, 578}, + dictWord{ + 4, + 10, + 392, + }, + dictWord{135, 10, 1597}, + dictWord{5, 10, 433}, + dictWord{9, 10, 633}, + dictWord{139, 10, 629}, + dictWord{135, 0, 821}, + dictWord{6, 0, 715}, + dictWord{ + 134, + 0, + 1325, + }, + dictWord{133, 11, 116}, + dictWord{6, 0, 868}, + dictWord{132, 11, 457}, + dictWord{134, 0, 959}, + dictWord{6, 10, 234}, + dictWord{138, 11, 199}, + dictWord{7, 0, 1053}, + dictWord{7, 10, 1950}, + dictWord{8, 10, 680}, + dictWord{11, 10, 817}, + dictWord{147, 10, 88}, + dictWord{7, 10, 1222}, + dictWord{ + 138, + 10, + 386, + }, + dictWord{5, 0, 950}, + dictWord{5, 0, 994}, + dictWord{6, 0, 351}, + dictWord{134, 0, 1124}, + dictWord{134, 0, 1081}, + dictWord{7, 0, 1595}, + dictWord{6, 10, 5}, + dictWord{11, 10, 249}, + dictWord{12, 10, 313}, + dictWord{16, 10, 66}, + dictWord{145, 10, 26}, + dictWord{148, 0, 59}, + dictWord{5, 11, 527}, + dictWord{6, 11, 189}, + dictWord{135, 11, 859}, + dictWord{5, 10, 963}, + dictWord{6, 10, 1773}, + dictWord{11, 11, 104}, + dictWord{11, 11, 554}, + dictWord{15, 11, 60}, + dictWord{ + 143, + 11, + 125, + }, + dictWord{135, 0, 47}, + dictWord{137, 0, 684}, + dictWord{134, 11, 116}, + dictWord{134, 0, 1606}, + dictWord{134, 0, 777}, + dictWord{7, 0, 1020}, + dictWord{ + 8, + 10, + 509, + }, + dictWord{136, 10, 792}, + dictWord{135, 0, 1094}, + dictWord{132, 0, 350}, + dictWord{133, 11, 487}, + dictWord{4, 11, 86}, + dictWord{5, 11, 667}, + dictWord{5, 11, 753}, + dictWord{6, 11, 316}, + dictWord{6, 11, 455}, + dictWord{135, 11, 946}, + dictWord{7, 0, 1812}, + dictWord{13, 0, 259}, + dictWord{13, 0, 356}, + dictWord{14, 0, 242}, + dictWord{147, 0, 114}, + dictWord{132, 10, 931}, + dictWord{133, 0, 967}, + dictWord{4, 0, 473}, + dictWord{7, 0, 623}, + dictWord{8, 0, 808}, + dictWord{ + 9, + 0, + 871, + }, + dictWord{9, 0, 893}, + dictWord{11, 0, 38}, + dictWord{11, 0, 431}, + dictWord{12, 0, 112}, + dictWord{12, 0, 217}, + dictWord{12, 0, 243}, + dictWord{12, 0, 562}, + dictWord{12, 0, 663}, + dictWord{12, 0, 683}, + dictWord{13, 0, 141}, + dictWord{13, 0, 197}, + dictWord{13, 0, 227}, + dictWord{13, 0, 406}, + dictWord{13, 0, 487}, + dictWord{14, 0, 156}, + dictWord{14, 0, 203}, + dictWord{14, 0, 224}, + dictWord{14, 0, 256}, + dictWord{18, 0, 58}, + dictWord{150, 0, 0}, + dictWord{138, 0, 286}, + dictWord{ + 7, + 10, + 943, + }, + dictWord{139, 10, 614}, + dictWord{135, 10, 1837}, + dictWord{150, 11, 45}, + dictWord{132, 0, 798}, + dictWord{4, 0, 222}, + dictWord{7, 0, 286}, + dictWord{136, 0, 629}, + dictWord{4, 11, 79}, + dictWord{7, 11, 1773}, + dictWord{10, 11, 450}, + dictWord{11, 11, 589}, + dictWord{13, 11, 332}, + dictWord{13, 11, 493}, + dictWord{14, 11, 183}, + dictWord{14, 11, 334}, + dictWord{14, 11, 362}, + dictWord{14, 11, 368}, + dictWord{14, 11, 376}, + dictWord{14, 11, 379}, + dictWord{ + 19, + 11, + 90, + }, + dictWord{19, 11, 103}, + dictWord{19, 11, 127}, + dictWord{148, 11, 90}, + dictWord{5, 0, 337}, + dictWord{11, 0, 513}, + dictWord{11, 0, 889}, + dictWord{ + 11, + 0, + 961, + }, + dictWord{12, 0, 461}, + dictWord{13, 0, 79}, + dictWord{15, 0, 121}, + dictWord{4, 10, 90}, + dictWord{5, 10, 545}, + dictWord{7, 10, 754}, + dictWord{9, 10, 186}, + dictWord{10, 10, 72}, + dictWord{10, 10, 782}, + dictWord{11, 10, 577}, + dictWord{11, 10, 610}, + dictWord{12, 10, 354}, + dictWord{12, 10, 362}, + dictWord{ + 140, + 10, + 595, + }, + dictWord{141, 0, 306}, + dictWord{136, 0, 146}, + dictWord{7, 0, 1646}, + dictWord{9, 10, 329}, + dictWord{11, 10, 254}, + dictWord{141, 11, 124}, + dictWord{ + 4, + 0, + 465, + }, + dictWord{135, 0, 1663}, + dictWord{132, 0, 525}, + dictWord{133, 11, 663}, + dictWord{10, 0, 299}, + dictWord{18, 0, 74}, + dictWord{9, 10, 187}, + dictWord{ + 11, + 10, + 1016, + }, + dictWord{145, 10, 44}, + dictWord{7, 0, 165}, + dictWord{7, 0, 919}, + dictWord{4, 10, 506}, + dictWord{136, 10, 517}, + dictWord{5, 10, 295}, + dictWord{ + 135, + 10, + 1680, + }, + dictWord{133, 11, 846}, + dictWord{134, 0, 1064}, + dictWord{5, 11, 378}, + dictWord{7, 11, 1402}, + dictWord{7, 11, 1414}, + dictWord{8, 11, 465}, + dictWord{9, 11, 286}, + dictWord{10, 11, 185}, + dictWord{10, 11, 562}, + dictWord{10, 11, 635}, + dictWord{11, 11, 31}, + dictWord{11, 11, 393}, + dictWord{ + 12, + 11, + 456, + }, + dictWord{13, 11, 312}, + dictWord{18, 11, 65}, + dictWord{18, 11, 96}, + dictWord{147, 11, 89}, + dictWord{132, 0, 596}, + dictWord{7, 10, 987}, + dictWord{ + 9, + 10, + 688, + }, + dictWord{10, 10, 522}, + dictWord{11, 10, 788}, + dictWord{140, 10, 566}, + dictWord{6, 0, 82}, + dictWord{7, 0, 138}, + dictWord{7, 0, 517}, + dictWord{7, 0, 1741}, + dictWord{11, 0, 238}, + dictWord{4, 11, 648}, + dictWord{134, 10, 1775}, + dictWord{7, 0, 1233}, + dictWord{7, 10, 700}, + dictWord{7, 10, 940}, + dictWord{8, 10, 514}, + dictWord{9, 10, 116}, + dictWord{9, 10, 535}, + dictWord{10, 10, 118}, + dictWord{11, 10, 107}, + dictWord{11, 10, 148}, + dictWord{11, 10, 922}, + dictWord{ + 12, + 10, + 254, + }, + dictWord{12, 10, 421}, + dictWord{142, 10, 238}, + dictWord{4, 0, 962}, + dictWord{6, 0, 1824}, + dictWord{8, 0, 894}, + dictWord{12, 0, 708}, + dictWord{ + 12, + 0, + 725, + }, + dictWord{14, 0, 451}, + dictWord{20, 0, 94}, + dictWord{22, 0, 59}, + dictWord{150, 0, 62}, + dictWord{5, 11, 945}, + dictWord{6, 11, 1656}, + dictWord{6, 11, 1787}, + dictWord{7, 11, 167}, + dictWord{8, 11, 824}, + dictWord{9, 11, 391}, + dictWord{10, 11, 375}, + dictWord{139, 11, 185}, + dictWord{5, 0, 495}, + dictWord{7, 0, 834}, + dictWord{9, 0, 733}, + dictWord{139, 0, 378}, + dictWord{4, 10, 743}, + dictWord{135, 11, 1273}, + dictWord{6, 0, 1204}, + dictWord{7, 11, 1645}, + dictWord{8, 11, 352}, + dictWord{137, 11, 249}, + dictWord{139, 10, 292}, + dictWord{133, 0, 559}, + dictWord{132, 11, 152}, + dictWord{9, 0, 499}, + dictWord{10, 0, 341}, + dictWord{ + 15, + 0, + 144, + }, + dictWord{19, 0, 49}, + dictWord{7, 10, 1283}, + dictWord{9, 10, 227}, + dictWord{11, 10, 325}, + dictWord{11, 10, 408}, + dictWord{14, 10, 180}, + dictWord{ + 146, + 10, + 47, + }, + dictWord{6, 0, 21}, + dictWord{6, 0, 1737}, + dictWord{7, 0, 1444}, + dictWord{136, 0, 224}, + dictWord{133, 11, 1006}, + dictWord{7, 0, 1446}, + dictWord{ + 9, + 0, + 97, + }, + dictWord{17, 0, 15}, + dictWord{5, 10, 81}, + dictWord{7, 10, 146}, + dictWord{7, 10, 1342}, + dictWord{8, 10, 53}, + dictWord{8, 10, 561}, + dictWord{8, 10, 694}, + dictWord{8, 10, 754}, + dictWord{9, 10, 115}, + dictWord{9, 10, 894}, + dictWord{10, 10, 462}, + dictWord{10, 10, 813}, + dictWord{11, 10, 230}, + dictWord{11, 10, 657}, + dictWord{11, 10, 699}, + dictWord{11, 10, 748}, + dictWord{12, 10, 119}, + dictWord{12, 10, 200}, + dictWord{12, 10, 283}, + dictWord{142, 10, 273}, + dictWord{ + 5, + 10, + 408, + }, + dictWord{137, 10, 747}, + dictWord{135, 11, 431}, + dictWord{135, 11, 832}, + dictWord{6, 0, 729}, + dictWord{134, 0, 953}, + dictWord{4, 0, 727}, + dictWord{ + 8, + 0, + 565, + }, + dictWord{5, 11, 351}, + dictWord{7, 11, 264}, + dictWord{136, 11, 565}, + dictWord{134, 0, 1948}, + dictWord{5, 0, 519}, + dictWord{5, 11, 40}, + dictWord{ + 7, + 11, + 598, + }, + dictWord{7, 11, 1638}, + dictWord{8, 11, 78}, + dictWord{9, 11, 166}, + dictWord{9, 11, 640}, + dictWord{9, 11, 685}, + dictWord{9, 11, 773}, + dictWord{ + 11, + 11, + 215, + }, + dictWord{13, 11, 65}, + dictWord{14, 11, 172}, + dictWord{14, 11, 317}, + dictWord{145, 11, 6}, + dictWord{8, 11, 60}, + dictWord{9, 11, 343}, + dictWord{ + 139, + 11, + 769, + }, + dictWord{137, 11, 455}, + dictWord{134, 0, 1193}, + dictWord{140, 0, 790}, + dictWord{7, 11, 1951}, + dictWord{8, 11, 765}, + dictWord{8, 11, 772}, + dictWord{140, 11, 671}, + dictWord{7, 11, 108}, + dictWord{8, 11, 219}, + dictWord{8, 11, 388}, + dictWord{9, 11, 639}, + dictWord{9, 11, 775}, + dictWord{11, 11, 275}, + dictWord{140, 11, 464}, + dictWord{132, 11, 468}, + dictWord{7, 10, 30}, + dictWord{8, 10, 86}, + dictWord{8, 10, 315}, + dictWord{8, 10, 700}, + dictWord{9, 10, 576}, + dictWord{ + 9, + 10, + 858, + }, + dictWord{11, 10, 310}, + dictWord{11, 10, 888}, + dictWord{11, 10, 904}, + dictWord{12, 10, 361}, + dictWord{141, 10, 248}, + dictWord{5, 11, 15}, + dictWord{6, 11, 56}, + dictWord{7, 11, 1758}, + dictWord{8, 11, 500}, + dictWord{9, 11, 730}, + dictWord{11, 11, 331}, + dictWord{13, 11, 150}, + dictWord{142, 11, 282}, + dictWord{4, 0, 402}, + dictWord{7, 0, 2}, + dictWord{8, 0, 323}, + dictWord{136, 0, 479}, + dictWord{138, 10, 839}, + dictWord{11, 0, 580}, + dictWord{142, 0, 201}, + dictWord{ + 5, + 0, + 59, + }, + dictWord{135, 0, 672}, + dictWord{137, 10, 617}, + dictWord{146, 0, 34}, + dictWord{134, 11, 1886}, + dictWord{4, 0, 961}, + dictWord{136, 0, 896}, + dictWord{ + 6, + 0, + 1285, + }, + dictWord{5, 11, 205}, + dictWord{6, 11, 438}, + dictWord{137, 11, 711}, + dictWord{134, 10, 428}, + dictWord{7, 10, 524}, + dictWord{8, 10, 169}, + dictWord{8, 10, 234}, + dictWord{9, 10, 480}, + dictWord{138, 10, 646}, + dictWord{148, 0, 46}, + dictWord{141, 0, 479}, + dictWord{133, 11, 534}, + dictWord{6, 0, 2019}, + dictWord{134, 10, 1648}, + dictWord{4, 0, 85}, + dictWord{7, 0, 549}, + dictWord{7, 10, 1205}, + dictWord{138, 10, 637}, + dictWord{4, 0, 663}, + dictWord{5, 0, 94}, + dictWord{ + 7, + 11, + 235, + }, + dictWord{7, 11, 1475}, + dictWord{15, 11, 68}, + dictWord{146, 11, 120}, + dictWord{6, 11, 443}, + dictWord{9, 11, 237}, + dictWord{9, 11, 571}, + dictWord{ + 9, + 11, + 695, + }, + dictWord{10, 11, 139}, + dictWord{11, 11, 715}, + dictWord{12, 11, 417}, + dictWord{141, 11, 421}, + dictWord{132, 0, 783}, + dictWord{4, 0, 682}, + dictWord{8, 0, 65}, + dictWord{9, 10, 39}, + dictWord{10, 10, 166}, + dictWord{11, 10, 918}, + dictWord{12, 10, 635}, + dictWord{20, 10, 10}, + dictWord{22, 10, 27}, + dictWord{ + 22, + 10, + 43, + }, + dictWord{150, 10, 52}, + dictWord{6, 0, 11}, + dictWord{135, 0, 187}, + dictWord{132, 0, 522}, + dictWord{4, 0, 52}, + dictWord{135, 0, 661}, + dictWord{ + 4, + 0, + 383, + }, + dictWord{133, 0, 520}, + dictWord{135, 11, 546}, + dictWord{11, 0, 343}, + dictWord{142, 0, 127}, + dictWord{4, 11, 578}, + dictWord{7, 10, 157}, + dictWord{ + 7, + 11, + 624, + }, + dictWord{7, 11, 916}, + dictWord{8, 10, 279}, + dictWord{10, 11, 256}, + dictWord{11, 11, 87}, + dictWord{139, 11, 703}, + dictWord{134, 10, 604}, + dictWord{ + 4, + 0, + 281, + }, + dictWord{5, 0, 38}, + dictWord{7, 0, 194}, + dictWord{7, 0, 668}, + dictWord{7, 0, 1893}, + dictWord{137, 0, 397}, + dictWord{7, 10, 945}, + dictWord{11, 10, 713}, + dictWord{139, 10, 744}, + dictWord{139, 10, 1022}, + dictWord{9, 0, 635}, + dictWord{139, 0, 559}, + dictWord{5, 11, 923}, + dictWord{7, 11, 490}, + dictWord{ + 12, + 11, + 553, + }, + dictWord{13, 11, 100}, + dictWord{14, 11, 118}, + dictWord{143, 11, 75}, + dictWord{132, 0, 975}, + dictWord{132, 10, 567}, + dictWord{137, 10, 859}, + dictWord{7, 10, 1846}, + dictWord{7, 11, 1846}, + dictWord{8, 10, 628}, + dictWord{136, 11, 628}, + dictWord{148, 0, 116}, + dictWord{138, 11, 750}, + dictWord{14, 0, 51}, + dictWord{14, 11, 51}, + dictWord{15, 11, 7}, + dictWord{148, 11, 20}, + dictWord{132, 0, 858}, + dictWord{134, 0, 1075}, + dictWord{4, 11, 924}, + dictWord{ + 133, + 10, + 762, + }, + dictWord{136, 0, 535}, + dictWord{133, 0, 448}, + dictWord{10, 10, 784}, + dictWord{141, 10, 191}, + dictWord{133, 10, 298}, + dictWord{7, 0, 610}, + dictWord{135, 0, 1501}, + dictWord{7, 10, 633}, + dictWord{7, 10, 905}, + dictWord{7, 10, 909}, + dictWord{7, 10, 1538}, + dictWord{9, 10, 767}, + dictWord{140, 10, 636}, + dictWord{4, 11, 265}, + dictWord{7, 11, 807}, + dictWord{135, 11, 950}, + dictWord{5, 11, 93}, + dictWord{12, 11, 267}, + dictWord{144, 11, 26}, + dictWord{136, 0, 191}, + dictWord{139, 10, 301}, + dictWord{135, 10, 1970}, + dictWord{135, 0, 267}, + dictWord{4, 0, 319}, + dictWord{5, 0, 699}, + dictWord{138, 0, 673}, + dictWord{ + 6, + 0, + 336, + }, + dictWord{7, 0, 92}, + dictWord{7, 0, 182}, + dictWord{8, 0, 453}, + dictWord{8, 0, 552}, + dictWord{9, 0, 204}, + dictWord{9, 0, 285}, + dictWord{10, 0, 99}, + dictWord{ + 11, + 0, + 568, + }, + dictWord{11, 0, 950}, + dictWord{12, 0, 94}, + dictWord{16, 0, 20}, + dictWord{16, 0, 70}, + dictWord{19, 0, 55}, + dictWord{12, 10, 644}, + dictWord{144, 10, 90}, + dictWord{6, 0, 551}, + dictWord{7, 0, 1308}, + dictWord{7, 10, 845}, + dictWord{7, 11, 994}, + dictWord{8, 10, 160}, + dictWord{137, 10, 318}, + dictWord{19, 11, 1}, + dictWord{ + 19, + 11, + 26, + }, + dictWord{150, 11, 9}, + dictWord{7, 0, 1406}, + dictWord{9, 0, 218}, + dictWord{141, 0, 222}, + dictWord{5, 0, 256}, + dictWord{138, 0, 69}, + dictWord{ + 5, + 11, + 233, + }, + dictWord{5, 11, 320}, + dictWord{6, 11, 140}, + dictWord{7, 11, 330}, + dictWord{136, 11, 295}, + dictWord{6, 0, 1980}, + dictWord{136, 0, 952}, + dictWord{ + 4, + 0, + 833, + }, + dictWord{137, 11, 678}, + dictWord{133, 11, 978}, + dictWord{4, 11, 905}, + dictWord{6, 11, 1701}, + dictWord{137, 11, 843}, + dictWord{138, 10, 735}, + dictWord{136, 10, 76}, + dictWord{17, 0, 39}, + dictWord{148, 0, 36}, + dictWord{18, 0, 81}, + dictWord{146, 11, 81}, + dictWord{14, 0, 352}, + dictWord{17, 0, 53}, + dictWord{ + 18, + 0, + 146, + }, + dictWord{18, 0, 152}, + dictWord{19, 0, 11}, + dictWord{150, 0, 54}, + dictWord{135, 0, 634}, + dictWord{138, 10, 841}, + dictWord{132, 0, 618}, + dictWord{ + 4, + 0, + 339, + }, + dictWord{7, 0, 259}, + dictWord{17, 0, 73}, + dictWord{4, 11, 275}, + dictWord{140, 11, 376}, + dictWord{132, 11, 509}, + dictWord{7, 11, 273}, + dictWord{ + 139, + 11, + 377, + }, + dictWord{4, 0, 759}, + dictWord{13, 0, 169}, + dictWord{137, 10, 804}, + dictWord{6, 10, 96}, + dictWord{135, 10, 1426}, + dictWord{4, 10, 651}, + dictWord{133, 10, 289}, + dictWord{7, 0, 1075}, + dictWord{8, 10, 35}, + dictWord{9, 10, 511}, + dictWord{10, 10, 767}, + dictWord{147, 10, 118}, + dictWord{6, 0, 649}, + dictWord{6, 0, 670}, + dictWord{136, 0, 482}, + dictWord{5, 0, 336}, + dictWord{6, 0, 341}, + dictWord{6, 0, 478}, + dictWord{6, 0, 1763}, + dictWord{136, 0, 386}, + dictWord{ + 5, + 11, + 802, + }, + dictWord{7, 11, 2021}, + dictWord{8, 11, 805}, + dictWord{14, 11, 94}, + dictWord{15, 11, 65}, + dictWord{16, 11, 4}, + dictWord{16, 11, 77}, + dictWord{16, 11, 80}, + dictWord{145, 11, 5}, + dictWord{6, 0, 1035}, + dictWord{5, 11, 167}, + dictWord{5, 11, 899}, + dictWord{6, 11, 410}, + dictWord{137, 11, 777}, + dictWord{ + 134, + 11, + 1705, + }, + dictWord{5, 0, 924}, + dictWord{133, 0, 969}, + dictWord{132, 10, 704}, + dictWord{135, 0, 73}, + dictWord{135, 11, 10}, + dictWord{135, 10, 1078}, + dictWord{ + 5, + 11, + 11, + }, + dictWord{6, 11, 117}, + dictWord{6, 11, 485}, + dictWord{7, 11, 1133}, + dictWord{9, 11, 582}, + dictWord{9, 11, 594}, + dictWord{11, 11, 21}, + dictWord{ + 11, + 11, + 818, + }, + dictWord{12, 11, 535}, + dictWord{141, 11, 86}, + dictWord{135, 0, 1971}, + dictWord{4, 11, 264}, + dictWord{7, 11, 1067}, + dictWord{8, 11, 204}, + dictWord{8, 11, 385}, + dictWord{139, 11, 953}, + dictWord{6, 0, 1458}, + dictWord{135, 0, 1344}, + dictWord{5, 0, 396}, + dictWord{134, 0, 501}, + dictWord{4, 10, 720}, + dictWord{133, 10, 306}, + dictWord{4, 0, 929}, + dictWord{5, 0, 799}, + dictWord{8, 0, 46}, + dictWord{8, 0, 740}, + dictWord{133, 10, 431}, + dictWord{7, 11, 646}, + dictWord{ + 7, + 11, + 1730, + }, + dictWord{11, 11, 446}, + dictWord{141, 11, 178}, + dictWord{7, 0, 276}, + dictWord{5, 10, 464}, + dictWord{6, 10, 236}, + dictWord{7, 10, 696}, + dictWord{ + 7, + 10, + 914, + }, + dictWord{7, 10, 1108}, + dictWord{7, 10, 1448}, + dictWord{9, 10, 15}, + dictWord{9, 10, 564}, + dictWord{10, 10, 14}, + dictWord{12, 10, 565}, + dictWord{ + 13, + 10, + 449, + }, + dictWord{14, 10, 53}, + dictWord{15, 10, 13}, + dictWord{16, 10, 64}, + dictWord{145, 10, 41}, + dictWord{4, 0, 892}, + dictWord{133, 0, 770}, + dictWord{ + 6, + 10, + 1767, + }, + dictWord{12, 10, 194}, + dictWord{145, 10, 107}, + dictWord{135, 0, 158}, + dictWord{5, 10, 840}, + dictWord{138, 11, 608}, + dictWord{134, 0, 1432}, + dictWord{138, 11, 250}, + dictWord{8, 11, 794}, + dictWord{9, 11, 400}, + dictWord{10, 11, 298}, + dictWord{142, 11, 228}, + dictWord{151, 0, 25}, + dictWord{ + 7, + 11, + 1131, + }, + dictWord{135, 11, 1468}, + dictWord{135, 0, 2001}, + dictWord{9, 10, 642}, + dictWord{11, 10, 236}, + dictWord{142, 10, 193}, + dictWord{4, 10, 68}, + dictWord{5, 10, 634}, + dictWord{6, 10, 386}, + dictWord{7, 10, 794}, + dictWord{8, 10, 273}, + dictWord{9, 10, 563}, + dictWord{10, 10, 105}, + dictWord{10, 10, 171}, + dictWord{11, 10, 94}, + dictWord{139, 10, 354}, + dictWord{136, 11, 724}, + dictWord{132, 0, 478}, + dictWord{11, 11, 512}, + dictWord{13, 11, 205}, + dictWord{ + 19, + 11, + 30, + }, + dictWord{22, 11, 36}, + dictWord{151, 11, 19}, + dictWord{7, 0, 1461}, + dictWord{140, 0, 91}, + dictWord{6, 11, 190}, + dictWord{7, 11, 768}, + dictWord{ + 135, + 11, + 1170, + }, + dictWord{4, 0, 602}, + dictWord{8, 0, 211}, + dictWord{4, 10, 95}, + dictWord{7, 10, 416}, + dictWord{139, 10, 830}, + dictWord{7, 10, 731}, + dictWord{13, 10, 20}, + dictWord{143, 10, 11}, + dictWord{6, 0, 1068}, + dictWord{135, 0, 1872}, + dictWord{4, 0, 13}, + dictWord{5, 0, 567}, + dictWord{7, 0, 1498}, + dictWord{9, 0, 124}, + dictWord{11, 0, 521}, + dictWord{12, 0, 405}, + dictWord{135, 11, 1023}, + dictWord{135, 0, 1006}, + dictWord{132, 0, 735}, + dictWord{138, 0, 812}, + dictWord{4, 0, 170}, + dictWord{135, 0, 323}, + dictWord{6, 11, 137}, + dictWord{9, 11, 75}, + dictWord{9, 11, 253}, + dictWord{10, 11, 194}, + dictWord{138, 11, 444}, + dictWord{5, 0, 304}, + dictWord{7, 0, 1403}, + dictWord{5, 10, 864}, + dictWord{10, 10, 648}, + dictWord{11, 10, 671}, + dictWord{143, 10, 46}, + dictWord{135, 11, 1180}, + dictWord{ + 133, + 10, + 928, + }, + dictWord{4, 0, 148}, + dictWord{133, 0, 742}, + dictWord{11, 10, 986}, + dictWord{140, 10, 682}, + dictWord{133, 0, 523}, + dictWord{135, 11, 1743}, + dictWord{7, 0, 730}, + dictWord{18, 0, 144}, + dictWord{19, 0, 61}, + dictWord{8, 10, 44}, + dictWord{9, 10, 884}, + dictWord{10, 10, 580}, + dictWord{11, 10, 399}, + dictWord{ + 11, + 10, + 894, + }, + dictWord{143, 10, 122}, + dictWord{5, 11, 760}, + dictWord{7, 11, 542}, + dictWord{8, 11, 135}, + dictWord{136, 11, 496}, + dictWord{136, 0, 981}, + dictWord{133, 0, 111}, + dictWord{10, 0, 132}, + dictWord{11, 0, 191}, + dictWord{11, 0, 358}, + dictWord{139, 0, 460}, + dictWord{7, 11, 319}, + dictWord{7, 11, 355}, + dictWord{ + 7, + 11, + 763, + }, + dictWord{10, 11, 389}, + dictWord{145, 11, 43}, + dictWord{134, 0, 890}, + dictWord{134, 0, 1420}, + dictWord{136, 11, 557}, + dictWord{ + 133, + 10, + 518, + }, + dictWord{133, 0, 444}, + dictWord{135, 0, 1787}, + dictWord{135, 10, 1852}, + dictWord{8, 0, 123}, + dictWord{15, 0, 6}, + dictWord{144, 0, 7}, + dictWord{ + 6, + 0, + 2041, + }, + dictWord{10, 11, 38}, + dictWord{139, 11, 784}, + dictWord{136, 0, 932}, + dictWord{5, 0, 937}, + dictWord{135, 0, 100}, + dictWord{6, 0, 995}, + dictWord{ + 4, + 11, + 58, + }, + dictWord{5, 11, 286}, + dictWord{6, 11, 319}, + dictWord{7, 11, 402}, + dictWord{7, 11, 1254}, + dictWord{7, 11, 1903}, + dictWord{8, 11, 356}, + dictWord{ + 140, + 11, + 408, + }, + dictWord{4, 11, 389}, + dictWord{9, 11, 181}, + dictWord{9, 11, 255}, + dictWord{10, 11, 8}, + dictWord{10, 11, 29}, + dictWord{10, 11, 816}, + dictWord{ + 11, + 11, + 311, + }, + dictWord{11, 11, 561}, + dictWord{12, 11, 67}, + dictWord{141, 11, 181}, + dictWord{138, 0, 255}, + dictWord{5, 0, 138}, + dictWord{4, 10, 934}, + dictWord{ + 136, + 10, + 610, + }, + dictWord{4, 0, 965}, + dictWord{10, 0, 863}, + dictWord{138, 0, 898}, + dictWord{10, 10, 804}, + dictWord{138, 10, 832}, + dictWord{12, 0, 631}, + dictWord{ + 8, + 10, + 96, + }, + dictWord{9, 10, 36}, + dictWord{10, 10, 607}, + dictWord{11, 10, 423}, + dictWord{11, 10, 442}, + dictWord{12, 10, 309}, + dictWord{14, 10, 199}, + dictWord{ + 15, + 10, + 90, + }, + dictWord{145, 10, 110}, + dictWord{134, 0, 1394}, + dictWord{4, 0, 652}, + dictWord{8, 0, 320}, + dictWord{22, 0, 6}, + dictWord{22, 0, 16}, + dictWord{ + 9, + 10, + 13, + }, + dictWord{9, 10, 398}, + dictWord{9, 10, 727}, + dictWord{10, 10, 75}, + dictWord{10, 10, 184}, + dictWord{10, 10, 230}, + dictWord{10, 10, 564}, + dictWord{ + 10, + 10, + 569, + }, + dictWord{11, 10, 973}, + dictWord{12, 10, 70}, + dictWord{12, 10, 189}, + dictWord{13, 10, 57}, + dictWord{141, 10, 257}, + dictWord{6, 0, 897}, + dictWord{ + 134, + 0, + 1333, + }, + dictWord{4, 0, 692}, + dictWord{133, 0, 321}, + dictWord{133, 11, 373}, + dictWord{135, 0, 922}, + dictWord{5, 0, 619}, + dictWord{133, 0, 698}, + dictWord{ + 137, + 10, + 631, + }, + dictWord{5, 10, 345}, + dictWord{135, 10, 1016}, + dictWord{9, 0, 957}, + dictWord{9, 0, 1018}, + dictWord{12, 0, 828}, + dictWord{12, 0, 844}, + dictWord{ + 12, + 0, + 897, + }, + dictWord{12, 0, 901}, + dictWord{12, 0, 943}, + dictWord{15, 0, 180}, + dictWord{18, 0, 197}, + dictWord{18, 0, 200}, + dictWord{18, 0, 213}, + dictWord{ + 18, + 0, + 214, + }, + dictWord{146, 0, 226}, + dictWord{5, 0, 917}, + dictWord{134, 0, 1659}, + dictWord{135, 0, 1100}, + dictWord{134, 0, 1173}, + dictWord{134, 0, 1930}, + dictWord{5, 0, 251}, + dictWord{5, 0, 956}, + dictWord{8, 0, 268}, + dictWord{9, 0, 214}, + dictWord{146, 0, 142}, + dictWord{133, 10, 673}, + dictWord{137, 10, 850}, + dictWord{ + 4, + 10, + 287, + }, + dictWord{133, 10, 1018}, + dictWord{132, 11, 672}, + dictWord{5, 0, 346}, + dictWord{5, 0, 711}, + dictWord{8, 0, 390}, + dictWord{11, 11, 752}, + dictWord{139, 11, 885}, + dictWord{5, 10, 34}, + dictWord{10, 10, 724}, + dictWord{12, 10, 444}, + dictWord{13, 10, 354}, + dictWord{18, 10, 32}, + dictWord{23, 10, 24}, + dictWord{23, 10, 31}, + dictWord{152, 10, 5}, + dictWord{4, 11, 710}, + dictWord{134, 11, 606}, + dictWord{134, 0, 744}, + dictWord{134, 10, 382}, + dictWord{ + 133, + 11, + 145, + }, + dictWord{4, 10, 329}, + dictWord{7, 11, 884}, + dictWord{140, 11, 124}, + dictWord{4, 11, 467}, + dictWord{5, 11, 405}, + dictWord{134, 11, 544}, + dictWord{ + 9, + 10, + 846, + }, + dictWord{138, 10, 827}, + dictWord{133, 0, 624}, + dictWord{9, 11, 372}, + dictWord{15, 11, 2}, + dictWord{19, 11, 10}, + dictWord{147, 11, 18}, + dictWord{ + 4, + 11, + 387, + }, + dictWord{135, 11, 1288}, + dictWord{5, 0, 783}, + dictWord{7, 0, 1998}, + dictWord{135, 0, 2047}, + dictWord{132, 10, 906}, + dictWord{136, 10, 366}, + dictWord{135, 11, 550}, + dictWord{4, 10, 123}, + dictWord{4, 10, 649}, + dictWord{5, 10, 605}, + dictWord{7, 10, 1509}, + dictWord{136, 10, 36}, + dictWord{ + 134, + 0, + 1125, + }, + dictWord{132, 0, 594}, + dictWord{133, 10, 767}, + dictWord{135, 11, 1227}, + dictWord{136, 11, 467}, + dictWord{4, 11, 576}, + dictWord{ + 135, + 11, + 1263, + }, + dictWord{4, 0, 268}, + dictWord{7, 0, 1534}, + dictWord{135, 11, 1534}, + dictWord{4, 10, 273}, + dictWord{5, 10, 658}, + dictWord{5, 11, 919}, + dictWord{ + 5, + 10, + 995, + }, + dictWord{134, 11, 1673}, + dictWord{133, 0, 563}, + dictWord{134, 10, 72}, + dictWord{135, 10, 1345}, + dictWord{4, 11, 82}, + dictWord{5, 11, 333}, + dictWord{ + 5, + 11, + 904, + }, + dictWord{6, 11, 207}, + dictWord{7, 11, 325}, + dictWord{7, 11, 1726}, + dictWord{8, 11, 101}, + dictWord{10, 11, 778}, + dictWord{139, 11, 220}, + dictWord{5, 0, 37}, + dictWord{6, 0, 39}, + dictWord{6, 0, 451}, + dictWord{7, 0, 218}, + dictWord{7, 0, 667}, + dictWord{7, 0, 1166}, + dictWord{7, 0, 1687}, + dictWord{8, 0, 662}, + dictWord{16, 0, 2}, + dictWord{133, 10, 589}, + dictWord{134, 0, 1332}, + dictWord{133, 11, 903}, + dictWord{134, 0, 508}, + dictWord{5, 10, 117}, + dictWord{6, 10, 514}, + dictWord{6, 10, 541}, + dictWord{7, 10, 1164}, + dictWord{7, 10, 1436}, + dictWord{8, 10, 220}, + dictWord{8, 10, 648}, + dictWord{10, 10, 688}, + dictWord{11, 10, 560}, + dictWord{140, 11, 147}, + dictWord{6, 11, 555}, + dictWord{135, 11, 485}, + dictWord{133, 10, 686}, + dictWord{7, 0, 453}, + dictWord{7, 0, 635}, + dictWord{7, 0, 796}, + dictWord{8, 0, 331}, + dictWord{9, 0, 330}, + dictWord{9, 0, 865}, + dictWord{10, 0, 119}, + dictWord{10, 0, 235}, + dictWord{11, 0, 111}, + dictWord{11, 0, 129}, + dictWord{ + 11, + 0, + 240, + }, + dictWord{12, 0, 31}, + dictWord{12, 0, 66}, + dictWord{12, 0, 222}, + dictWord{12, 0, 269}, + dictWord{12, 0, 599}, + dictWord{12, 0, 684}, + dictWord{12, 0, 689}, + dictWord{12, 0, 691}, + dictWord{142, 0, 345}, + dictWord{135, 0, 1834}, + dictWord{4, 11, 705}, + dictWord{7, 11, 615}, + dictWord{138, 11, 251}, + dictWord{ + 136, + 11, + 345, + }, + dictWord{137, 0, 527}, + dictWord{6, 0, 98}, + dictWord{7, 0, 702}, + dictWord{135, 0, 991}, + dictWord{11, 0, 576}, + dictWord{14, 0, 74}, + dictWord{7, 10, 196}, + dictWord{10, 10, 765}, + dictWord{11, 10, 347}, + dictWord{11, 10, 552}, + dictWord{11, 10, 790}, + dictWord{12, 10, 263}, + dictWord{13, 10, 246}, + dictWord{ + 13, + 10, + 270, + }, + dictWord{13, 10, 395}, + dictWord{14, 10, 176}, + dictWord{14, 10, 190}, + dictWord{14, 10, 398}, + dictWord{14, 10, 412}, + dictWord{15, 10, 32}, + dictWord{ + 15, + 10, + 63, + }, + dictWord{16, 10, 88}, + dictWord{147, 10, 105}, + dictWord{134, 11, 90}, + dictWord{13, 0, 84}, + dictWord{141, 0, 122}, + dictWord{6, 0, 37}, + dictWord{ + 7, + 0, + 299, + }, + dictWord{7, 0, 1666}, + dictWord{8, 0, 195}, + dictWord{8, 0, 316}, + dictWord{9, 0, 178}, + dictWord{9, 0, 276}, + dictWord{9, 0, 339}, + dictWord{9, 0, 536}, + dictWord{ + 10, + 0, + 102, + }, + dictWord{10, 0, 362}, + dictWord{10, 0, 785}, + dictWord{11, 0, 55}, + dictWord{11, 0, 149}, + dictWord{11, 0, 773}, + dictWord{13, 0, 416}, + dictWord{ + 13, + 0, + 419, + }, + dictWord{14, 0, 38}, + dictWord{14, 0, 41}, + dictWord{142, 0, 210}, + dictWord{5, 10, 381}, + dictWord{135, 10, 1792}, + dictWord{7, 11, 813}, + dictWord{ + 12, + 11, + 497, + }, + dictWord{141, 11, 56}, + dictWord{7, 10, 616}, + dictWord{138, 10, 413}, + dictWord{133, 0, 645}, + dictWord{6, 11, 125}, + dictWord{135, 11, 1277}, + dictWord{132, 0, 290}, + dictWord{6, 0, 70}, + dictWord{7, 0, 1292}, + dictWord{10, 0, 762}, + dictWord{139, 0, 288}, + dictWord{6, 10, 120}, + dictWord{7, 10, 1188}, + dictWord{ + 7, + 10, + 1710, + }, + dictWord{8, 10, 286}, + dictWord{9, 10, 667}, + dictWord{11, 10, 592}, + dictWord{139, 10, 730}, + dictWord{135, 11, 1784}, + dictWord{7, 0, 1315}, + dictWord{135, 11, 1315}, + dictWord{134, 0, 1955}, + dictWord{135, 10, 1146}, + dictWord{7, 0, 131}, + dictWord{7, 0, 422}, + dictWord{8, 0, 210}, + dictWord{ + 140, + 0, + 573, + }, + dictWord{4, 10, 352}, + dictWord{135, 10, 687}, + dictWord{139, 0, 797}, + dictWord{143, 0, 38}, + dictWord{14, 0, 179}, + dictWord{15, 0, 151}, + dictWord{ + 150, + 0, + 11, + }, + dictWord{7, 0, 488}, + dictWord{4, 10, 192}, + dictWord{5, 10, 49}, + dictWord{6, 10, 200}, + dictWord{6, 10, 293}, + dictWord{134, 10, 1696}, + dictWord{ + 132, + 0, + 936, + }, + dictWord{135, 11, 703}, + dictWord{6, 11, 160}, + dictWord{7, 11, 1106}, + dictWord{9, 11, 770}, + dictWord{10, 11, 618}, + dictWord{11, 11, 112}, + dictWord{ + 140, + 11, + 413, + }, + dictWord{5, 0, 453}, + dictWord{134, 0, 441}, + dictWord{135, 0, 595}, + dictWord{132, 10, 650}, + dictWord{132, 10, 147}, + dictWord{6, 0, 991}, + dictWord{6, 0, 1182}, + dictWord{12, 11, 271}, + dictWord{145, 11, 109}, + dictWord{133, 10, 934}, + dictWord{140, 11, 221}, + dictWord{132, 0, 653}, + dictWord{ + 7, + 0, + 505, + }, + dictWord{135, 0, 523}, + dictWord{134, 0, 903}, + dictWord{135, 11, 479}, + dictWord{7, 11, 304}, + dictWord{9, 11, 646}, + dictWord{9, 11, 862}, + dictWord{ + 10, + 11, + 262, + }, + dictWord{11, 11, 696}, + dictWord{12, 11, 208}, + dictWord{15, 11, 79}, + dictWord{147, 11, 108}, + dictWord{146, 0, 80}, + dictWord{135, 11, 981}, + dictWord{142, 0, 432}, + dictWord{132, 0, 314}, + dictWord{137, 11, 152}, + dictWord{7, 0, 1368}, + dictWord{8, 0, 232}, + dictWord{8, 0, 361}, + dictWord{10, 0, 682}, + dictWord{138, 0, 742}, + dictWord{135, 11, 1586}, + dictWord{9, 0, 534}, + dictWord{4, 11, 434}, + dictWord{11, 11, 663}, + dictWord{12, 11, 210}, + dictWord{13, 11, 166}, + dictWord{13, 11, 310}, + dictWord{14, 11, 373}, + dictWord{147, 11, 43}, + dictWord{7, 11, 1091}, + dictWord{135, 11, 1765}, + dictWord{6, 11, 550}, + dictWord{ + 135, + 11, + 652, + }, + dictWord{137, 0, 27}, + dictWord{142, 0, 12}, + dictWord{4, 10, 637}, + dictWord{5, 11, 553}, + dictWord{7, 11, 766}, + dictWord{138, 11, 824}, + dictWord{ + 7, + 11, + 737, + }, + dictWord{8, 11, 298}, + dictWord{136, 11, 452}, + dictWord{7, 0, 736}, + dictWord{139, 0, 264}, + dictWord{134, 0, 1657}, + dictWord{133, 11, 292}, + dictWord{138, 11, 135}, + dictWord{6, 0, 844}, + dictWord{134, 0, 1117}, + dictWord{135, 0, 127}, + dictWord{9, 10, 867}, + dictWord{138, 10, 837}, + dictWord{ + 6, + 0, + 1184, + }, + dictWord{134, 0, 1208}, + dictWord{134, 0, 1294}, + dictWord{136, 0, 364}, + dictWord{6, 0, 1415}, + dictWord{7, 0, 1334}, + dictWord{11, 0, 125}, + dictWord{ + 6, + 10, + 170, + }, + dictWord{7, 11, 393}, + dictWord{8, 10, 395}, + dictWord{8, 10, 487}, + dictWord{10, 11, 603}, + dictWord{11, 11, 206}, + dictWord{141, 10, 147}, + dictWord{137, 11, 748}, + dictWord{4, 11, 912}, + dictWord{137, 11, 232}, + dictWord{4, 10, 535}, + dictWord{136, 10, 618}, + dictWord{137, 0, 792}, + dictWord{ + 7, + 11, + 1973, + }, + dictWord{136, 11, 716}, + dictWord{135, 11, 98}, + dictWord{5, 0, 909}, + dictWord{9, 0, 849}, + dictWord{138, 0, 805}, + dictWord{4, 0, 630}, + dictWord{ + 132, + 0, + 699, + }, + dictWord{5, 11, 733}, + dictWord{14, 11, 103}, + dictWord{150, 10, 23}, + dictWord{12, 11, 158}, + dictWord{18, 11, 8}, + dictWord{19, 11, 62}, + dictWord{ + 20, + 11, + 6, + }, + dictWord{22, 11, 4}, + dictWord{23, 11, 2}, + dictWord{151, 11, 9}, + dictWord{132, 0, 968}, + dictWord{132, 10, 778}, + dictWord{132, 10, 46}, + dictWord{5, 10, 811}, + dictWord{6, 10, 1679}, + dictWord{6, 10, 1714}, + dictWord{135, 10, 2032}, + dictWord{6, 0, 1446}, + dictWord{7, 10, 1458}, + dictWord{9, 10, 407}, + dictWord{ + 139, + 10, + 15, + }, + dictWord{7, 0, 206}, + dictWord{7, 0, 397}, + dictWord{7, 0, 621}, + dictWord{7, 0, 640}, + dictWord{8, 0, 124}, + dictWord{8, 0, 619}, + dictWord{9, 0, 305}, + dictWord{ + 9, + 0, + 643, + }, + dictWord{10, 0, 264}, + dictWord{10, 0, 628}, + dictWord{11, 0, 40}, + dictWord{12, 0, 349}, + dictWord{13, 0, 134}, + dictWord{13, 0, 295}, + dictWord{ + 14, + 0, + 155, + }, + dictWord{15, 0, 120}, + dictWord{18, 0, 105}, + dictWord{6, 10, 34}, + dictWord{7, 10, 1089}, + dictWord{8, 10, 708}, + dictWord{8, 10, 721}, + dictWord{9, 10, 363}, + dictWord{148, 10, 98}, + dictWord{4, 0, 262}, + dictWord{5, 0, 641}, + dictWord{135, 0, 342}, + dictWord{137, 11, 72}, + dictWord{4, 0, 99}, + dictWord{6, 0, 250}, + dictWord{ + 6, + 0, + 346, + }, + dictWord{8, 0, 127}, + dictWord{138, 0, 81}, + dictWord{132, 0, 915}, + dictWord{5, 0, 75}, + dictWord{9, 0, 517}, + dictWord{10, 0, 470}, + dictWord{12, 0, 155}, + dictWord{141, 0, 224}, + dictWord{132, 10, 462}, + dictWord{11, 11, 600}, + dictWord{11, 11, 670}, + dictWord{141, 11, 245}, + dictWord{142, 0, 83}, + dictWord{ + 5, + 10, + 73, + }, + dictWord{6, 10, 23}, + dictWord{134, 10, 338}, + dictWord{6, 0, 1031}, + dictWord{139, 11, 923}, + dictWord{7, 11, 164}, + dictWord{7, 11, 1571}, + dictWord{ + 9, + 11, + 107, + }, + dictWord{140, 11, 225}, + dictWord{134, 0, 1470}, + dictWord{133, 0, 954}, + dictWord{6, 0, 304}, + dictWord{8, 0, 418}, + dictWord{10, 0, 345}, + dictWord{ + 11, + 0, + 341, + }, + dictWord{139, 0, 675}, + dictWord{9, 0, 410}, + dictWord{139, 0, 425}, + dictWord{4, 11, 27}, + dictWord{5, 11, 484}, + dictWord{5, 11, 510}, + dictWord{6, 11, 434}, + dictWord{7, 11, 1000}, + dictWord{7, 11, 1098}, + dictWord{8, 11, 2}, + dictWord{136, 11, 200}, + dictWord{134, 0, 734}, + dictWord{140, 11, 257}, + dictWord{ + 7, + 10, + 725, + }, + dictWord{8, 10, 498}, + dictWord{139, 10, 268}, + dictWord{134, 0, 1822}, + dictWord{135, 0, 1798}, + dictWord{135, 10, 773}, + dictWord{132, 11, 460}, + dictWord{4, 11, 932}, + dictWord{133, 11, 891}, + dictWord{134, 0, 14}, + dictWord{132, 10, 583}, + dictWord{7, 10, 1462}, + dictWord{8, 11, 625}, + dictWord{ + 139, + 10, + 659, + }, + dictWord{5, 0, 113}, + dictWord{6, 0, 243}, + dictWord{6, 0, 1708}, + dictWord{7, 0, 1865}, + dictWord{11, 0, 161}, + dictWord{16, 0, 37}, + dictWord{17, 0, 99}, + dictWord{133, 10, 220}, + dictWord{134, 11, 76}, + dictWord{5, 11, 461}, + dictWord{135, 11, 1925}, + dictWord{140, 0, 69}, + dictWord{8, 11, 92}, + dictWord{ + 137, + 11, + 221, + }, + dictWord{139, 10, 803}, + dictWord{132, 10, 544}, + dictWord{4, 0, 274}, + dictWord{134, 0, 922}, + dictWord{132, 0, 541}, + dictWord{5, 0, 627}, + dictWord{ + 6, + 10, + 437, + }, + dictWord{6, 10, 564}, + dictWord{11, 10, 181}, + dictWord{141, 10, 183}, + dictWord{135, 10, 1192}, + dictWord{7, 0, 166}, + dictWord{132, 11, 763}, + dictWord{133, 11, 253}, + dictWord{134, 0, 849}, + dictWord{9, 11, 73}, + dictWord{10, 11, 110}, + dictWord{14, 11, 185}, + dictWord{145, 11, 119}, + dictWord{5, 11, 212}, + dictWord{12, 11, 35}, + dictWord{141, 11, 382}, + dictWord{133, 0, 717}, + dictWord{137, 0, 304}, + dictWord{136, 0, 600}, + dictWord{133, 0, 654}, + dictWord{ + 6, + 0, + 273, + }, + dictWord{10, 0, 188}, + dictWord{13, 0, 377}, + dictWord{146, 0, 77}, + dictWord{4, 10, 790}, + dictWord{5, 10, 273}, + dictWord{134, 10, 394}, + dictWord{ + 132, + 0, + 543, + }, + dictWord{135, 0, 410}, + dictWord{11, 0, 98}, + dictWord{11, 0, 524}, + dictWord{141, 0, 87}, + dictWord{132, 0, 941}, + dictWord{135, 11, 1175}, + dictWord{ + 4, + 0, + 250, + }, + dictWord{7, 0, 1612}, + dictWord{11, 0, 186}, + dictWord{12, 0, 133}, + dictWord{6, 10, 127}, + dictWord{7, 10, 1511}, + dictWord{8, 10, 613}, + dictWord{ + 12, + 10, + 495, + }, + dictWord{12, 10, 586}, + dictWord{12, 10, 660}, + dictWord{12, 10, 668}, + dictWord{14, 10, 385}, + dictWord{15, 10, 118}, + dictWord{17, 10, 20}, + dictWord{ + 146, + 10, + 98, + }, + dictWord{6, 0, 1785}, + dictWord{133, 11, 816}, + dictWord{134, 0, 1339}, + dictWord{7, 0, 961}, + dictWord{7, 0, 1085}, + dictWord{7, 0, 1727}, + dictWord{ + 8, + 0, + 462, + }, + dictWord{6, 10, 230}, + dictWord{135, 11, 1727}, + dictWord{9, 0, 636}, + dictWord{135, 10, 1954}, + dictWord{132, 0, 780}, + dictWord{5, 11, 869}, + dictWord{5, 11, 968}, + dictWord{6, 11, 1626}, + dictWord{8, 11, 734}, + dictWord{136, 11, 784}, + dictWord{4, 11, 542}, + dictWord{6, 11, 1716}, + dictWord{6, 11, 1727}, + dictWord{7, 11, 1082}, + dictWord{7, 11, 1545}, + dictWord{8, 11, 56}, + dictWord{8, 11, 118}, + dictWord{8, 11, 412}, + dictWord{8, 11, 564}, + dictWord{9, 11, 888}, + dictWord{9, 11, 908}, + dictWord{10, 11, 50}, + dictWord{10, 11, 423}, + dictWord{11, 11, 685}, + dictWord{11, 11, 697}, + dictWord{11, 11, 933}, + dictWord{12, 11, 299}, + dictWord{13, 11, 126}, + dictWord{13, 11, 136}, + dictWord{13, 11, 170}, + dictWord{141, 11, 190}, + dictWord{134, 11, 226}, + dictWord{4, 11, 232}, + dictWord{ + 9, + 11, + 202, + }, + dictWord{10, 11, 474}, + dictWord{140, 11, 433}, + dictWord{137, 11, 500}, + dictWord{5, 0, 529}, + dictWord{136, 10, 68}, + dictWord{132, 10, 654}, + dictWord{ + 4, + 10, + 156, + }, + dictWord{7, 10, 998}, + dictWord{7, 10, 1045}, + dictWord{7, 10, 1860}, + dictWord{9, 10, 48}, + dictWord{9, 10, 692}, + dictWord{11, 10, 419}, + dictWord{139, 10, 602}, + dictWord{7, 0, 1276}, + dictWord{8, 0, 474}, + dictWord{9, 0, 652}, + dictWord{6, 11, 108}, + dictWord{7, 11, 1003}, + dictWord{7, 11, 1181}, + dictWord{136, 11, 343}, + dictWord{7, 11, 1264}, + dictWord{7, 11, 1678}, + dictWord{11, 11, 945}, + dictWord{12, 11, 341}, + dictWord{12, 11, 471}, + dictWord{ + 140, + 11, + 569, + }, + dictWord{134, 11, 1712}, + dictWord{5, 0, 948}, + dictWord{12, 0, 468}, + dictWord{19, 0, 96}, + dictWord{148, 0, 24}, + dictWord{4, 11, 133}, + dictWord{ + 7, + 11, + 711, + }, + dictWord{7, 11, 1298}, + dictWord{7, 11, 1585}, + dictWord{135, 11, 1929}, + dictWord{6, 0, 753}, + dictWord{140, 0, 657}, + dictWord{139, 0, 941}, + dictWord{ + 6, + 11, + 99, + }, + dictWord{7, 11, 1808}, + dictWord{145, 11, 57}, + dictWord{6, 11, 574}, + dictWord{7, 11, 428}, + dictWord{7, 11, 1250}, + dictWord{10, 11, 669}, + dictWord{ + 11, + 11, + 485, + }, + dictWord{11, 11, 840}, + dictWord{12, 11, 300}, + dictWord{142, 11, 250}, + dictWord{4, 0, 532}, + dictWord{5, 0, 706}, + dictWord{135, 0, 662}, + dictWord{ + 5, + 0, + 837, + }, + dictWord{6, 0, 1651}, + dictWord{139, 0, 985}, + dictWord{7, 0, 1861}, + dictWord{9, 10, 197}, + dictWord{10, 10, 300}, + dictWord{12, 10, 473}, + dictWord{ + 13, + 10, + 90, + }, + dictWord{141, 10, 405}, + dictWord{137, 11, 252}, + dictWord{6, 11, 323}, + dictWord{135, 11, 1564}, + dictWord{4, 0, 330}, + dictWord{4, 0, 863}, + dictWord{7, 0, 933}, + dictWord{7, 0, 2012}, + dictWord{8, 0, 292}, + dictWord{7, 11, 461}, + dictWord{8, 11, 775}, + dictWord{138, 11, 435}, + dictWord{132, 10, 606}, + dictWord{ + 4, + 11, + 655, + }, + dictWord{7, 11, 850}, + dictWord{17, 11, 75}, + dictWord{146, 11, 137}, + dictWord{135, 0, 767}, + dictWord{7, 10, 1978}, + dictWord{136, 10, 676}, + dictWord{132, 0, 641}, + dictWord{135, 11, 1559}, + dictWord{134, 0, 1233}, + dictWord{137, 0, 242}, + dictWord{17, 0, 114}, + dictWord{4, 10, 361}, + dictWord{ + 133, + 10, + 315, + }, + dictWord{137, 0, 883}, + dictWord{132, 10, 461}, + dictWord{138, 0, 274}, + dictWord{134, 0, 2008}, + dictWord{134, 0, 1794}, + dictWord{4, 0, 703}, + dictWord{135, 0, 207}, + dictWord{12, 0, 285}, + dictWord{132, 10, 472}, + dictWord{132, 0, 571}, + dictWord{5, 0, 873}, + dictWord{5, 0, 960}, + dictWord{8, 0, 823}, + dictWord{9, 0, 881}, + dictWord{136, 11, 577}, + dictWord{7, 0, 617}, + dictWord{10, 0, 498}, + dictWord{11, 0, 501}, + dictWord{12, 0, 16}, + dictWord{140, 0, 150}, + dictWord{ + 138, + 10, + 747, + }, + dictWord{132, 0, 431}, + dictWord{133, 10, 155}, + dictWord{11, 0, 283}, + dictWord{11, 0, 567}, + dictWord{7, 10, 163}, + dictWord{8, 10, 319}, + dictWord{ + 9, + 10, + 402, + }, + dictWord{10, 10, 24}, + dictWord{10, 10, 681}, + dictWord{11, 10, 200}, + dictWord{12, 10, 253}, + dictWord{12, 10, 410}, + dictWord{142, 10, 219}, + dictWord{4, 11, 413}, + dictWord{5, 11, 677}, + dictWord{8, 11, 432}, + dictWord{140, 11, 280}, + dictWord{9, 0, 401}, + dictWord{5, 10, 475}, + dictWord{7, 10, 1780}, + dictWord{11, 10, 297}, + dictWord{11, 10, 558}, + dictWord{14, 10, 322}, + dictWord{147, 10, 76}, + dictWord{6, 0, 781}, + dictWord{9, 0, 134}, + dictWord{10, 0, 2}, + dictWord{ + 10, + 0, + 27, + }, + dictWord{10, 0, 333}, + dictWord{11, 0, 722}, + dictWord{143, 0, 1}, + dictWord{5, 0, 33}, + dictWord{6, 0, 470}, + dictWord{139, 0, 424}, + dictWord{ + 135, + 0, + 2006, + }, + dictWord{12, 0, 783}, + dictWord{135, 10, 1956}, + dictWord{136, 0, 274}, + dictWord{135, 0, 1882}, + dictWord{132, 0, 794}, + dictWord{135, 0, 1848}, + dictWord{5, 10, 944}, + dictWord{134, 10, 1769}, + dictWord{6, 0, 47}, + dictWord{7, 0, 90}, + dictWord{7, 0, 664}, + dictWord{7, 0, 830}, + dictWord{7, 0, 1380}, + dictWord{ + 7, + 0, + 2025, + }, + dictWord{8, 0, 448}, + dictWord{136, 0, 828}, + dictWord{132, 10, 144}, + dictWord{134, 0, 1199}, + dictWord{4, 11, 395}, + dictWord{139, 11, 762}, + dictWord{135, 11, 1504}, + dictWord{9, 0, 417}, + dictWord{137, 0, 493}, + dictWord{9, 11, 174}, + dictWord{10, 11, 164}, + dictWord{11, 11, 440}, + dictWord{11, 11, 841}, + dictWord{143, 11, 98}, + dictWord{134, 11, 426}, + dictWord{139, 11, 1002}, + dictWord{134, 0, 295}, + dictWord{134, 0, 816}, + dictWord{6, 10, 247}, + dictWord{ + 137, + 10, + 555, + }, + dictWord{133, 0, 1019}, + dictWord{4, 0, 620}, + dictWord{5, 11, 476}, + dictWord{10, 10, 280}, + dictWord{138, 10, 797}, + dictWord{139, 0, 464}, + dictWord{5, 11, 76}, + dictWord{6, 11, 458}, + dictWord{6, 11, 497}, + dictWord{7, 11, 764}, + dictWord{7, 11, 868}, + dictWord{9, 11, 658}, + dictWord{10, 11, 594}, + dictWord{ + 11, + 11, + 173, + }, + dictWord{11, 11, 566}, + dictWord{12, 11, 20}, + dictWord{12, 11, 338}, + dictWord{141, 11, 200}, + dictWord{134, 0, 208}, + dictWord{4, 11, 526}, + dictWord{7, 11, 1029}, + dictWord{135, 11, 1054}, + dictWord{132, 11, 636}, + dictWord{6, 11, 233}, + dictWord{7, 11, 660}, + dictWord{7, 11, 1124}, + dictWord{ + 17, + 11, + 31, + }, + dictWord{19, 11, 22}, + dictWord{151, 11, 14}, + dictWord{10, 0, 442}, + dictWord{133, 10, 428}, + dictWord{10, 0, 930}, + dictWord{140, 0, 778}, + dictWord{ + 6, + 0, + 68, + }, + dictWord{7, 0, 448}, + dictWord{7, 0, 1629}, + dictWord{7, 0, 1769}, + dictWord{7, 0, 1813}, + dictWord{8, 0, 442}, + dictWord{8, 0, 516}, + dictWord{9, 0, 710}, + dictWord{ + 10, + 0, + 282, + }, + dictWord{10, 0, 722}, + dictWord{7, 10, 1717}, + dictWord{138, 10, 546}, + dictWord{134, 0, 1128}, + dictWord{11, 0, 844}, + dictWord{12, 0, 104}, + dictWord{140, 0, 625}, + dictWord{4, 11, 432}, + dictWord{135, 11, 824}, + dictWord{138, 10, 189}, + dictWord{133, 0, 787}, + dictWord{133, 10, 99}, + dictWord{ + 4, + 11, + 279, + }, + dictWord{7, 11, 301}, + dictWord{137, 11, 362}, + dictWord{8, 0, 491}, + dictWord{4, 10, 397}, + dictWord{136, 10, 555}, + dictWord{4, 11, 178}, + dictWord{ + 133, + 11, + 399, + }, + dictWord{134, 0, 711}, + dictWord{144, 0, 9}, + dictWord{4, 0, 403}, + dictWord{5, 0, 441}, + dictWord{7, 0, 450}, + dictWord{10, 0, 840}, + dictWord{11, 0, 101}, + dictWord{12, 0, 193}, + dictWord{141, 0, 430}, + dictWord{135, 11, 1246}, + dictWord{12, 10, 398}, + dictWord{20, 10, 39}, + dictWord{21, 10, 11}, + dictWord{ + 150, + 10, + 41, + }, + dictWord{4, 10, 485}, + dictWord{7, 10, 353}, + dictWord{135, 10, 1523}, + dictWord{6, 10, 366}, + dictWord{7, 10, 1384}, + dictWord{7, 10, 1601}, + dictWord{ + 135, + 11, + 1912, + }, + dictWord{7, 0, 396}, + dictWord{10, 0, 160}, + dictWord{135, 11, 396}, + dictWord{137, 10, 282}, + dictWord{134, 11, 1692}, + dictWord{4, 10, 157}, + dictWord{5, 10, 471}, + dictWord{6, 11, 202}, + dictWord{10, 11, 448}, + dictWord{11, 11, 208}, + dictWord{12, 11, 360}, + dictWord{17, 11, 117}, + dictWord{ + 17, + 11, + 118, + }, + dictWord{18, 11, 27}, + dictWord{148, 11, 67}, + dictWord{133, 0, 679}, + dictWord{137, 0, 326}, + dictWord{136, 10, 116}, + dictWord{7, 11, 872}, + dictWord{ + 10, + 11, + 516, + }, + dictWord{139, 11, 167}, + dictWord{132, 11, 224}, + dictWord{5, 11, 546}, + dictWord{7, 11, 35}, + dictWord{8, 11, 11}, + dictWord{8, 11, 12}, + dictWord{ + 9, + 11, + 315, + }, + dictWord{9, 11, 533}, + dictWord{10, 11, 802}, + dictWord{11, 11, 166}, + dictWord{12, 11, 525}, + dictWord{142, 11, 243}, + dictWord{7, 0, 1128}, + dictWord{135, 11, 1920}, + dictWord{5, 11, 241}, + dictWord{8, 11, 242}, + dictWord{9, 11, 451}, + dictWord{10, 11, 667}, + dictWord{11, 11, 598}, + dictWord{ + 140, + 11, + 429, + }, + dictWord{6, 0, 737}, + dictWord{5, 10, 160}, + dictWord{7, 10, 363}, + dictWord{7, 10, 589}, + dictWord{10, 10, 170}, + dictWord{141, 10, 55}, + dictWord{ + 135, + 0, + 1796, + }, + dictWord{142, 11, 254}, + dictWord{4, 0, 574}, + dictWord{7, 0, 350}, + dictWord{7, 0, 1024}, + dictWord{8, 0, 338}, + dictWord{9, 0, 677}, + dictWord{138, 0, 808}, + dictWord{134, 0, 1096}, + dictWord{137, 11, 516}, + dictWord{7, 0, 405}, + dictWord{10, 0, 491}, + dictWord{4, 10, 108}, + dictWord{4, 11, 366}, + dictWord{ + 139, + 10, + 498, + }, + dictWord{11, 11, 337}, + dictWord{142, 11, 303}, + dictWord{134, 11, 1736}, + dictWord{7, 0, 1081}, + dictWord{140, 11, 364}, + dictWord{7, 10, 1005}, + dictWord{140, 10, 609}, + dictWord{7, 0, 1676}, + dictWord{4, 10, 895}, + dictWord{133, 10, 772}, + dictWord{135, 0, 2037}, + dictWord{6, 0, 1207}, + dictWord{ + 11, + 11, + 916, + }, + dictWord{142, 11, 419}, + dictWord{14, 11, 140}, + dictWord{148, 11, 41}, + dictWord{6, 11, 331}, + dictWord{136, 11, 623}, + dictWord{9, 0, 944}, + dictWord{ + 9, + 0, + 969, + }, + dictWord{9, 0, 1022}, + dictWord{12, 0, 913}, + dictWord{12, 0, 936}, + dictWord{15, 0, 177}, + dictWord{15, 0, 193}, + dictWord{4, 10, 926}, + dictWord{ + 133, + 10, + 983, + }, + dictWord{5, 0, 354}, + dictWord{135, 11, 506}, + dictWord{8, 0, 598}, + dictWord{9, 0, 664}, + dictWord{138, 0, 441}, + dictWord{4, 11, 640}, + dictWord{ + 133, + 11, + 513, + }, + dictWord{137, 0, 297}, + dictWord{132, 10, 538}, + dictWord{6, 10, 294}, + dictWord{7, 10, 1267}, + dictWord{136, 10, 624}, + dictWord{7, 0, 1772}, + dictWord{ + 7, + 11, + 1888, + }, + dictWord{8, 11, 289}, + dictWord{11, 11, 45}, + dictWord{12, 11, 278}, + dictWord{140, 11, 537}, + dictWord{135, 10, 1325}, + dictWord{138, 0, 751}, + dictWord{141, 0, 37}, + dictWord{134, 0, 1828}, + dictWord{132, 10, 757}, + dictWord{132, 11, 394}, + dictWord{6, 0, 257}, + dictWord{135, 0, 1522}, + dictWord{ + 4, + 0, + 582, + }, + dictWord{9, 0, 191}, + dictWord{135, 11, 1931}, + dictWord{7, 11, 574}, + dictWord{7, 11, 1719}, + dictWord{137, 11, 145}, + dictWord{132, 11, 658}, + dictWord{10, 0, 790}, + dictWord{132, 11, 369}, + dictWord{9, 11, 781}, + dictWord{10, 11, 144}, + dictWord{11, 11, 385}, + dictWord{13, 11, 161}, + dictWord{13, 11, 228}, + dictWord{13, 11, 268}, + dictWord{148, 11, 107}, + dictWord{8, 0, 469}, + dictWord{10, 0, 47}, + dictWord{136, 11, 374}, + dictWord{6, 0, 306}, + dictWord{7, 0, 1140}, + dictWord{7, 0, 1340}, + dictWord{8, 0, 133}, + dictWord{138, 0, 449}, + dictWord{139, 0, 1011}, + dictWord{7, 10, 1875}, + dictWord{139, 10, 124}, + dictWord{ + 4, + 11, + 344, + }, + dictWord{6, 11, 498}, + dictWord{139, 11, 323}, + dictWord{137, 0, 299}, + dictWord{132, 0, 837}, + dictWord{133, 11, 906}, + dictWord{5, 0, 329}, + dictWord{ + 8, + 0, + 260, + }, + dictWord{138, 0, 10}, + dictWord{134, 0, 1320}, + dictWord{4, 0, 657}, + dictWord{146, 0, 158}, + dictWord{135, 0, 1191}, + dictWord{152, 0, 7}, + dictWord{ + 6, + 0, + 1939, + }, + dictWord{8, 0, 974}, + dictWord{138, 0, 996}, + dictWord{135, 0, 1665}, + dictWord{11, 11, 126}, + dictWord{139, 11, 287}, + dictWord{143, 0, 8}, + dictWord{ + 14, + 11, + 149, + }, + dictWord{14, 11, 399}, + dictWord{143, 11, 57}, + dictWord{5, 0, 66}, + dictWord{7, 0, 1896}, + dictWord{136, 0, 288}, + dictWord{7, 0, 175}, + dictWord{ + 10, + 0, + 494, + }, + dictWord{5, 10, 150}, + dictWord{8, 10, 603}, + dictWord{9, 10, 593}, + dictWord{9, 10, 634}, + dictWord{10, 10, 173}, + dictWord{11, 10, 462}, + dictWord{ + 11, + 10, + 515, + }, + dictWord{13, 10, 216}, + dictWord{13, 10, 288}, + dictWord{142, 10, 400}, + dictWord{134, 0, 1643}, + dictWord{136, 11, 21}, + dictWord{4, 0, 21}, + dictWord{ + 5, + 0, + 91, + }, + dictWord{5, 0, 648}, + dictWord{5, 0, 750}, + dictWord{5, 0, 781}, + dictWord{6, 0, 54}, + dictWord{6, 0, 112}, + dictWord{6, 0, 402}, + dictWord{6, 0, 1732}, + dictWord{ + 7, + 0, + 315, + }, + dictWord{7, 0, 749}, + dictWord{7, 0, 1427}, + dictWord{7, 0, 1900}, + dictWord{9, 0, 78}, + dictWord{9, 0, 508}, + dictWord{10, 0, 611}, + dictWord{10, 0, 811}, + dictWord{11, 0, 510}, + dictWord{11, 0, 728}, + dictWord{13, 0, 36}, + dictWord{14, 0, 39}, + dictWord{16, 0, 83}, + dictWord{17, 0, 124}, + dictWord{148, 0, 30}, + dictWord{ + 4, + 0, + 668, + }, + dictWord{136, 0, 570}, + dictWord{10, 0, 322}, + dictWord{10, 0, 719}, + dictWord{139, 0, 407}, + dictWord{135, 11, 1381}, + dictWord{136, 11, 193}, + dictWord{12, 10, 108}, + dictWord{141, 10, 291}, + dictWord{132, 11, 616}, + dictWord{136, 11, 692}, + dictWord{8, 0, 125}, + dictWord{8, 0, 369}, + dictWord{8, 0, 524}, + dictWord{10, 0, 486}, + dictWord{11, 0, 13}, + dictWord{11, 0, 381}, + dictWord{11, 0, 736}, + dictWord{11, 0, 766}, + dictWord{11, 0, 845}, + dictWord{13, 0, 114}, + dictWord{ + 13, + 0, + 292, + }, + dictWord{142, 0, 47}, + dictWord{134, 0, 1247}, + dictWord{6, 0, 1684}, + dictWord{6, 0, 1731}, + dictWord{7, 0, 356}, + dictWord{8, 0, 54}, + dictWord{8, 0, 221}, + dictWord{9, 0, 225}, + dictWord{9, 0, 356}, + dictWord{10, 0, 77}, + dictWord{10, 0, 446}, + dictWord{10, 0, 731}, + dictWord{12, 0, 404}, + dictWord{141, 0, 491}, + dictWord{135, 10, 1777}, + dictWord{4, 11, 305}, + dictWord{4, 10, 493}, + dictWord{144, 10, 55}, + dictWord{4, 0, 951}, + dictWord{6, 0, 1809}, + dictWord{6, 0, 1849}, + dictWord{8, 0, 846}, + dictWord{8, 0, 866}, + dictWord{8, 0, 899}, + dictWord{10, 0, 896}, + dictWord{12, 0, 694}, + dictWord{142, 0, 468}, + dictWord{5, 11, 214}, + dictWord{ + 7, + 11, + 603, + }, + dictWord{8, 11, 611}, + dictWord{9, 11, 686}, + dictWord{10, 11, 88}, + dictWord{11, 11, 459}, + dictWord{11, 11, 496}, + dictWord{12, 11, 463}, + dictWord{ + 12, + 11, + 590, + }, + dictWord{13, 11, 0}, + dictWord{142, 11, 214}, + dictWord{132, 0, 411}, + dictWord{4, 0, 80}, + dictWord{133, 0, 44}, + dictWord{140, 11, 74}, + dictWord{ + 143, + 0, + 31, + }, + dictWord{7, 0, 669}, + dictWord{6, 10, 568}, + dictWord{7, 10, 1804}, + dictWord{8, 10, 362}, + dictWord{8, 10, 410}, + dictWord{8, 10, 830}, + dictWord{9, 10, 514}, + dictWord{11, 10, 649}, + dictWord{142, 10, 157}, + dictWord{7, 0, 673}, + dictWord{134, 11, 1703}, + dictWord{132, 10, 625}, + dictWord{134, 0, 1303}, + dictWord{ + 5, + 0, + 299, + }, + dictWord{135, 0, 1083}, + dictWord{138, 0, 704}, + dictWord{6, 0, 275}, + dictWord{7, 0, 408}, + dictWord{6, 10, 158}, + dictWord{7, 10, 129}, + dictWord{ + 7, + 10, + 181, + }, + dictWord{8, 10, 276}, + dictWord{8, 10, 377}, + dictWord{10, 10, 523}, + dictWord{11, 10, 816}, + dictWord{12, 10, 455}, + dictWord{13, 10, 303}, + dictWord{ + 142, + 10, + 135, + }, + dictWord{4, 0, 219}, + dictWord{7, 0, 367}, + dictWord{7, 0, 1713}, + dictWord{7, 0, 1761}, + dictWord{9, 0, 86}, + dictWord{9, 0, 537}, + dictWord{10, 0, 165}, + dictWord{12, 0, 219}, + dictWord{140, 0, 561}, + dictWord{8, 0, 216}, + dictWord{4, 10, 1}, + dictWord{4, 11, 737}, + dictWord{6, 11, 317}, + dictWord{7, 10, 1143}, + dictWord{ + 7, + 10, + 1463, + }, + dictWord{9, 10, 207}, + dictWord{9, 10, 390}, + dictWord{9, 10, 467}, + dictWord{10, 11, 98}, + dictWord{11, 11, 294}, + dictWord{11, 10, 836}, + dictWord{ + 12, + 11, + 60, + }, + dictWord{12, 11, 437}, + dictWord{13, 11, 64}, + dictWord{13, 11, 380}, + dictWord{142, 11, 430}, + dictWord{6, 11, 1758}, + dictWord{8, 11, 520}, + dictWord{9, 11, 345}, + dictWord{9, 11, 403}, + dictWord{142, 11, 350}, + dictWord{5, 11, 47}, + dictWord{10, 11, 242}, + dictWord{138, 11, 579}, + dictWord{5, 11, 139}, + dictWord{7, 11, 1168}, + dictWord{138, 11, 539}, + dictWord{135, 0, 1319}, + dictWord{4, 10, 295}, + dictWord{4, 10, 723}, + dictWord{5, 10, 895}, + dictWord{ + 7, + 10, + 1031, + }, + dictWord{8, 10, 199}, + dictWord{8, 10, 340}, + dictWord{9, 10, 153}, + dictWord{9, 10, 215}, + dictWord{10, 10, 21}, + dictWord{10, 10, 59}, + dictWord{ + 10, + 10, + 80, + }, + dictWord{10, 10, 224}, + dictWord{10, 10, 838}, + dictWord{11, 10, 229}, + dictWord{11, 10, 652}, + dictWord{12, 10, 192}, + dictWord{13, 10, 146}, + dictWord{ + 142, + 10, + 91, + }, + dictWord{140, 0, 428}, + dictWord{137, 10, 51}, + dictWord{133, 0, 514}, + dictWord{5, 10, 309}, + dictWord{140, 10, 211}, + dictWord{6, 0, 1010}, + dictWord{5, 10, 125}, + dictWord{8, 10, 77}, + dictWord{138, 10, 15}, + dictWord{4, 0, 55}, + dictWord{5, 0, 301}, + dictWord{6, 0, 571}, + dictWord{142, 0, 49}, + dictWord{ + 146, + 0, + 102, + }, + dictWord{136, 11, 370}, + dictWord{4, 11, 107}, + dictWord{7, 11, 613}, + dictWord{8, 11, 358}, + dictWord{8, 11, 439}, + dictWord{8, 11, 504}, + dictWord{ + 9, + 11, + 501, + }, + dictWord{10, 11, 383}, + dictWord{139, 11, 477}, + dictWord{132, 11, 229}, + dictWord{133, 0, 364}, + dictWord{133, 10, 439}, + dictWord{4, 11, 903}, + dictWord{135, 11, 1816}, + dictWord{11, 0, 379}, + dictWord{140, 10, 76}, + dictWord{4, 0, 76}, + dictWord{4, 0, 971}, + dictWord{7, 0, 1550}, + dictWord{9, 0, 306}, + dictWord{ + 9, + 0, + 430, + }, + dictWord{9, 0, 663}, + dictWord{10, 0, 683}, + dictWord{10, 0, 921}, + dictWord{11, 0, 427}, + dictWord{11, 0, 753}, + dictWord{12, 0, 334}, + dictWord{12, 0, 442}, + dictWord{14, 0, 258}, + dictWord{14, 0, 366}, + dictWord{143, 0, 131}, + dictWord{137, 0, 52}, + dictWord{4, 11, 47}, + dictWord{6, 11, 373}, + dictWord{7, 11, 452}, + dictWord{7, 11, 543}, + dictWord{7, 11, 1714}, + dictWord{7, 11, 1856}, + dictWord{9, 11, 6}, + dictWord{11, 11, 257}, + dictWord{139, 11, 391}, + dictWord{4, 10, 8}, + dictWord{ + 7, + 10, + 1152, + }, + dictWord{7, 10, 1153}, + dictWord{7, 10, 1715}, + dictWord{9, 10, 374}, + dictWord{10, 10, 478}, + dictWord{139, 10, 648}, + dictWord{4, 11, 785}, + dictWord{133, 11, 368}, + dictWord{135, 10, 1099}, + dictWord{135, 11, 860}, + dictWord{5, 11, 980}, + dictWord{134, 11, 1754}, + dictWord{134, 0, 1258}, + dictWord{ + 6, + 0, + 1058, + }, + dictWord{6, 0, 1359}, + dictWord{7, 11, 536}, + dictWord{7, 11, 1331}, + dictWord{136, 11, 143}, + dictWord{4, 0, 656}, + dictWord{135, 0, 779}, + dictWord{136, 10, 87}, + dictWord{5, 11, 19}, + dictWord{6, 11, 533}, + dictWord{146, 11, 126}, + dictWord{7, 0, 144}, + dictWord{138, 10, 438}, + dictWord{5, 11, 395}, + dictWord{5, 11, 951}, + dictWord{134, 11, 1776}, + dictWord{135, 0, 1373}, + dictWord{7, 0, 554}, + dictWord{7, 0, 605}, + dictWord{141, 0, 10}, + dictWord{4, 10, 69}, + dictWord{ + 5, + 10, + 122, + }, + dictWord{9, 10, 656}, + dictWord{138, 10, 464}, + dictWord{5, 10, 849}, + dictWord{134, 10, 1633}, + dictWord{5, 0, 838}, + dictWord{5, 0, 841}, + dictWord{134, 0, 1649}, + dictWord{133, 0, 1012}, + dictWord{139, 10, 499}, + dictWord{7, 10, 476}, + dictWord{7, 10, 1592}, + dictWord{138, 10, 87}, + dictWord{ + 6, + 0, + 251, + }, + dictWord{7, 0, 365}, + dictWord{7, 0, 1357}, + dictWord{7, 0, 1497}, + dictWord{8, 0, 154}, + dictWord{141, 0, 281}, + dictWord{132, 11, 441}, + dictWord{ + 132, + 11, + 695, + }, + dictWord{7, 11, 497}, + dictWord{9, 11, 387}, + dictWord{147, 11, 81}, + dictWord{133, 0, 340}, + dictWord{14, 10, 283}, + dictWord{142, 11, 283}, + dictWord{ + 134, + 0, + 810, + }, + dictWord{135, 11, 1894}, + dictWord{139, 0, 495}, + dictWord{5, 11, 284}, + dictWord{6, 11, 49}, + dictWord{6, 11, 350}, + dictWord{7, 11, 1}, + dictWord{ + 7, + 11, + 377, + }, + dictWord{7, 11, 1693}, + dictWord{8, 11, 18}, + dictWord{8, 11, 678}, + dictWord{9, 11, 161}, + dictWord{9, 11, 585}, + dictWord{9, 11, 671}, + dictWord{ + 9, + 11, + 839, + }, + dictWord{11, 11, 912}, + dictWord{141, 11, 427}, + dictWord{5, 10, 859}, + dictWord{7, 10, 1160}, + dictWord{8, 10, 107}, + dictWord{9, 10, 291}, + dictWord{ + 9, + 10, + 439, + }, + dictWord{10, 10, 663}, + dictWord{11, 10, 609}, + dictWord{140, 10, 197}, + dictWord{8, 0, 261}, + dictWord{9, 0, 144}, + dictWord{9, 0, 466}, + dictWord{ + 10, + 0, + 370, + }, + dictWord{12, 0, 470}, + dictWord{13, 0, 144}, + dictWord{142, 0, 348}, + dictWord{137, 0, 897}, + dictWord{6, 0, 248}, + dictWord{9, 0, 546}, + dictWord{10, 0, 535}, + dictWord{11, 0, 681}, + dictWord{141, 0, 135}, + dictWord{4, 0, 358}, + dictWord{135, 0, 1496}, + dictWord{134, 0, 567}, + dictWord{136, 0, 445}, + dictWord{ + 4, + 10, + 117, + }, + dictWord{6, 10, 372}, + dictWord{7, 10, 1905}, + dictWord{142, 10, 323}, + dictWord{4, 10, 722}, + dictWord{139, 10, 471}, + dictWord{6, 0, 697}, + dictWord{ + 134, + 0, + 996, + }, + dictWord{7, 11, 2007}, + dictWord{9, 11, 101}, + dictWord{9, 11, 450}, + dictWord{10, 11, 66}, + dictWord{10, 11, 842}, + dictWord{11, 11, 536}, + dictWord{ + 140, + 11, + 587, + }, + dictWord{132, 0, 577}, + dictWord{134, 0, 1336}, + dictWord{9, 10, 5}, + dictWord{12, 10, 216}, + dictWord{12, 10, 294}, + dictWord{12, 10, 298}, + dictWord{12, 10, 400}, + dictWord{12, 10, 518}, + dictWord{13, 10, 229}, + dictWord{143, 10, 139}, + dictWord{6, 0, 174}, + dictWord{138, 0, 917}, + dictWord{ + 134, + 10, + 1774, + }, + dictWord{5, 10, 12}, + dictWord{7, 10, 375}, + dictWord{9, 10, 88}, + dictWord{9, 10, 438}, + dictWord{11, 11, 62}, + dictWord{139, 10, 270}, + dictWord{ + 134, + 11, + 1766, + }, + dictWord{6, 11, 0}, + dictWord{7, 11, 84}, + dictWord{7, 10, 816}, + dictWord{7, 10, 1241}, + dictWord{9, 10, 283}, + dictWord{9, 10, 520}, + dictWord{10, 10, 213}, + dictWord{10, 10, 307}, + dictWord{10, 10, 463}, + dictWord{10, 10, 671}, + dictWord{10, 10, 746}, + dictWord{11, 10, 401}, + dictWord{11, 10, 794}, + dictWord{ + 11, + 11, + 895, + }, + dictWord{12, 10, 517}, + dictWord{17, 11, 11}, + dictWord{18, 10, 107}, + dictWord{147, 10, 115}, + dictWord{5, 0, 878}, + dictWord{133, 0, 972}, + dictWord{ + 6, + 11, + 1665, + }, + dictWord{7, 11, 256}, + dictWord{7, 11, 1388}, + dictWord{138, 11, 499}, + dictWord{4, 10, 258}, + dictWord{136, 10, 639}, + dictWord{4, 11, 22}, + dictWord{5, 11, 10}, + dictWord{6, 10, 22}, + dictWord{7, 11, 848}, + dictWord{7, 10, 903}, + dictWord{7, 10, 1963}, + dictWord{8, 11, 97}, + dictWord{138, 10, 577}, + dictWord{ + 5, + 10, + 681, + }, + dictWord{136, 10, 782}, + dictWord{133, 11, 481}, + dictWord{132, 0, 351}, + dictWord{4, 10, 664}, + dictWord{5, 10, 804}, + dictWord{139, 10, 1013}, + dictWord{6, 11, 134}, + dictWord{7, 11, 437}, + dictWord{7, 11, 959}, + dictWord{9, 11, 37}, + dictWord{14, 11, 285}, + dictWord{14, 11, 371}, + dictWord{144, 11, 60}, + dictWord{7, 11, 486}, + dictWord{8, 11, 155}, + dictWord{11, 11, 93}, + dictWord{140, 11, 164}, + dictWord{132, 0, 286}, + dictWord{7, 0, 438}, + dictWord{7, 0, 627}, + dictWord{7, 0, 1516}, + dictWord{8, 0, 40}, + dictWord{9, 0, 56}, + dictWord{9, 0, 294}, + dictWord{10, 0, 30}, + dictWord{11, 0, 969}, + dictWord{11, 0, 995}, + dictWord{146, 0, 148}, + dictWord{5, 11, 591}, + dictWord{135, 11, 337}, + dictWord{134, 0, 1950}, + dictWord{133, 10, 32}, + dictWord{138, 11, 500}, + dictWord{5, 11, 380}, + dictWord{ + 5, + 11, + 650, + }, + dictWord{136, 11, 310}, + dictWord{4, 11, 364}, + dictWord{7, 11, 1156}, + dictWord{7, 11, 1187}, + dictWord{137, 11, 409}, + dictWord{4, 0, 738}, + dictWord{134, 11, 482}, + dictWord{4, 11, 781}, + dictWord{6, 11, 487}, + dictWord{7, 11, 926}, + dictWord{8, 11, 263}, + dictWord{139, 11, 500}, + dictWord{135, 11, 418}, + dictWord{6, 0, 2047}, + dictWord{10, 0, 969}, + dictWord{4, 10, 289}, + dictWord{7, 10, 629}, + dictWord{7, 10, 1698}, + dictWord{7, 10, 1711}, + dictWord{ + 140, + 10, + 215, + }, + dictWord{6, 10, 450}, + dictWord{136, 10, 109}, + dictWord{134, 0, 818}, + dictWord{136, 10, 705}, + dictWord{133, 0, 866}, + dictWord{4, 11, 94}, + dictWord{ + 135, + 11, + 1265, + }, + dictWord{132, 11, 417}, + dictWord{134, 0, 1467}, + dictWord{135, 10, 1238}, + dictWord{4, 0, 972}, + dictWord{6, 0, 1851}, + dictWord{ + 134, + 0, + 1857, + }, + dictWord{134, 0, 355}, + dictWord{133, 0, 116}, + dictWord{132, 0, 457}, + dictWord{135, 11, 1411}, + dictWord{4, 11, 408}, + dictWord{4, 11, 741}, + dictWord{135, 11, 500}, + dictWord{134, 10, 26}, + dictWord{142, 11, 137}, + dictWord{5, 0, 527}, + dictWord{6, 0, 189}, + dictWord{7, 0, 859}, + dictWord{136, 0, 267}, + dictWord{11, 0, 104}, + dictWord{11, 0, 554}, + dictWord{15, 0, 60}, + dictWord{143, 0, 125}, + dictWord{134, 0, 1613}, + dictWord{4, 10, 414}, + dictWord{5, 10, 467}, + dictWord{ + 9, + 10, + 654, + }, + dictWord{10, 10, 451}, + dictWord{12, 10, 59}, + dictWord{141, 10, 375}, + dictWord{135, 10, 17}, + dictWord{134, 0, 116}, + dictWord{135, 11, 541}, + dictWord{135, 10, 955}, + dictWord{6, 11, 73}, + dictWord{135, 11, 177}, + dictWord{133, 11, 576}, + dictWord{134, 0, 886}, + dictWord{133, 0, 487}, + dictWord{ + 4, + 0, + 86, + }, + dictWord{5, 0, 667}, + dictWord{5, 0, 753}, + dictWord{6, 0, 316}, + dictWord{6, 0, 455}, + dictWord{135, 0, 946}, + dictWord{142, 11, 231}, + dictWord{150, 0, 45}, + dictWord{134, 0, 863}, + dictWord{134, 0, 1953}, + dictWord{6, 10, 280}, + dictWord{10, 10, 502}, + dictWord{11, 10, 344}, + dictWord{140, 10, 38}, + dictWord{4, 0, 79}, + dictWord{7, 0, 1773}, + dictWord{10, 0, 450}, + dictWord{11, 0, 589}, + dictWord{13, 0, 332}, + dictWord{13, 0, 493}, + dictWord{14, 0, 183}, + dictWord{14, 0, 334}, + dictWord{14, 0, 362}, + dictWord{14, 0, 368}, + dictWord{14, 0, 376}, + dictWord{14, 0, 379}, + dictWord{19, 0, 90}, + dictWord{19, 0, 103}, + dictWord{19, 0, 127}, + dictWord{ + 148, + 0, + 90, + }, + dictWord{5, 10, 45}, + dictWord{7, 10, 1161}, + dictWord{11, 10, 448}, + dictWord{11, 10, 880}, + dictWord{13, 10, 139}, + dictWord{13, 10, 407}, + dictWord{ + 15, + 10, + 16, + }, + dictWord{17, 10, 95}, + dictWord{18, 10, 66}, + dictWord{18, 10, 88}, + dictWord{18, 10, 123}, + dictWord{149, 10, 7}, + dictWord{136, 10, 777}, + dictWord{ + 4, + 10, + 410, + }, + dictWord{135, 10, 521}, + dictWord{135, 10, 1778}, + dictWord{135, 11, 538}, + dictWord{142, 0, 381}, + dictWord{133, 11, 413}, + dictWord{ + 134, + 0, + 1142, + }, + dictWord{6, 0, 1189}, + dictWord{136, 11, 495}, + dictWord{5, 0, 663}, + dictWord{6, 0, 1962}, + dictWord{134, 0, 2003}, + dictWord{7, 11, 54}, + dictWord{ + 8, + 11, + 312, + }, + dictWord{10, 11, 191}, + dictWord{10, 11, 614}, + dictWord{140, 11, 567}, + dictWord{132, 10, 436}, + dictWord{133, 0, 846}, + dictWord{10, 0, 528}, + dictWord{11, 0, 504}, + dictWord{7, 10, 1587}, + dictWord{135, 10, 1707}, + dictWord{5, 0, 378}, + dictWord{8, 0, 465}, + dictWord{9, 0, 286}, + dictWord{10, 0, 185}, + dictWord{ + 10, + 0, + 562, + }, + dictWord{10, 0, 635}, + dictWord{11, 0, 31}, + dictWord{11, 0, 393}, + dictWord{13, 0, 312}, + dictWord{18, 0, 65}, + dictWord{18, 0, 96}, + dictWord{147, 0, 89}, + dictWord{7, 0, 899}, + dictWord{14, 0, 325}, + dictWord{6, 11, 468}, + dictWord{7, 11, 567}, + dictWord{7, 11, 1478}, + dictWord{8, 11, 530}, + dictWord{142, 11, 290}, + dictWord{7, 0, 1880}, + dictWord{9, 0, 680}, + dictWord{139, 0, 798}, + dictWord{134, 0, 1770}, + dictWord{132, 0, 648}, + dictWord{150, 11, 35}, + dictWord{5, 0, 945}, + dictWord{6, 0, 1656}, + dictWord{6, 0, 1787}, + dictWord{7, 0, 167}, + dictWord{8, 0, 824}, + dictWord{9, 0, 391}, + dictWord{10, 0, 375}, + dictWord{139, 0, 185}, + dictWord{ + 6, + 11, + 484, + }, + dictWord{135, 11, 822}, + dictWord{134, 0, 2046}, + dictWord{7, 0, 1645}, + dictWord{8, 0, 352}, + dictWord{137, 0, 249}, + dictWord{132, 0, 152}, + dictWord{6, 0, 611}, + dictWord{135, 0, 1733}, + dictWord{6, 11, 1724}, + dictWord{135, 11, 2022}, + dictWord{133, 0, 1006}, + dictWord{141, 11, 96}, + dictWord{ + 5, + 0, + 420, + }, + dictWord{135, 0, 1449}, + dictWord{146, 11, 149}, + dictWord{135, 0, 832}, + dictWord{135, 10, 663}, + dictWord{133, 0, 351}, + dictWord{5, 0, 40}, + dictWord{ + 7, + 0, + 598, + }, + dictWord{7, 0, 1638}, + dictWord{8, 0, 78}, + dictWord{9, 0, 166}, + dictWord{9, 0, 640}, + dictWord{9, 0, 685}, + dictWord{9, 0, 773}, + dictWord{11, 0, 215}, + dictWord{13, 0, 65}, + dictWord{14, 0, 172}, + dictWord{14, 0, 317}, + dictWord{145, 0, 6}, + dictWord{8, 0, 60}, + dictWord{9, 0, 343}, + dictWord{139, 0, 769}, + dictWord{ + 134, + 0, + 1354, + }, + dictWord{132, 0, 724}, + dictWord{137, 0, 745}, + dictWord{132, 11, 474}, + dictWord{7, 0, 1951}, + dictWord{8, 0, 765}, + dictWord{8, 0, 772}, + dictWord{ + 140, + 0, + 671, + }, + dictWord{7, 0, 108}, + dictWord{8, 0, 219}, + dictWord{8, 0, 388}, + dictWord{9, 0, 775}, + dictWord{11, 0, 275}, + dictWord{140, 0, 464}, + dictWord{137, 0, 639}, + dictWord{135, 10, 503}, + dictWord{133, 11, 366}, + dictWord{5, 0, 15}, + dictWord{6, 0, 56}, + dictWord{7, 0, 1758}, + dictWord{8, 0, 500}, + dictWord{9, 0, 730}, + dictWord{ + 11, + 0, + 331, + }, + dictWord{13, 0, 150}, + dictWord{14, 0, 282}, + dictWord{5, 11, 305}, + dictWord{9, 11, 560}, + dictWord{141, 11, 208}, + dictWord{4, 10, 113}, + dictWord{ + 5, + 10, + 163, + }, + dictWord{5, 10, 735}, + dictWord{7, 10, 1009}, + dictWord{9, 10, 9}, + dictWord{9, 10, 771}, + dictWord{12, 10, 90}, + dictWord{13, 10, 138}, + dictWord{ + 13, + 10, + 410, + }, + dictWord{143, 10, 128}, + dictWord{4, 10, 324}, + dictWord{138, 10, 104}, + dictWord{135, 11, 466}, + dictWord{142, 11, 27}, + dictWord{134, 0, 1886}, + dictWord{5, 0, 205}, + dictWord{6, 0, 438}, + dictWord{9, 0, 711}, + dictWord{4, 11, 480}, + dictWord{6, 11, 167}, + dictWord{6, 11, 302}, + dictWord{6, 11, 1642}, + dictWord{ + 7, + 11, + 130, + }, + dictWord{7, 11, 656}, + dictWord{7, 11, 837}, + dictWord{7, 11, 1547}, + dictWord{7, 11, 1657}, + dictWord{8, 11, 429}, + dictWord{9, 11, 228}, + dictWord{ + 10, + 11, + 643, + }, + dictWord{13, 11, 289}, + dictWord{13, 11, 343}, + dictWord{147, 11, 101}, + dictWord{134, 0, 865}, + dictWord{6, 0, 2025}, + dictWord{136, 0, 965}, + dictWord{ + 7, + 11, + 278, + }, + dictWord{10, 11, 739}, + dictWord{11, 11, 708}, + dictWord{141, 11, 348}, + dictWord{133, 0, 534}, + dictWord{135, 11, 1922}, + dictWord{ + 137, + 0, + 691, + }, + dictWord{4, 10, 935}, + dictWord{133, 10, 823}, + dictWord{6, 0, 443}, + dictWord{9, 0, 237}, + dictWord{9, 0, 571}, + dictWord{9, 0, 695}, + dictWord{10, 0, 139}, + dictWord{11, 0, 715}, + dictWord{12, 0, 417}, + dictWord{141, 0, 421}, + dictWord{5, 10, 269}, + dictWord{7, 10, 434}, + dictWord{7, 10, 891}, + dictWord{8, 10, 339}, + dictWord{ + 9, + 10, + 702, + }, + dictWord{11, 10, 594}, + dictWord{11, 10, 718}, + dictWord{145, 10, 100}, + dictWord{6, 0, 1555}, + dictWord{7, 0, 878}, + dictWord{9, 10, 485}, + dictWord{141, 10, 264}, + dictWord{134, 10, 1713}, + dictWord{7, 10, 1810}, + dictWord{11, 10, 866}, + dictWord{12, 10, 103}, + dictWord{141, 10, 495}, + dictWord{ + 135, + 10, + 900, + }, + dictWord{6, 0, 1410}, + dictWord{9, 11, 316}, + dictWord{139, 11, 256}, + dictWord{4, 0, 995}, + dictWord{135, 0, 1033}, + dictWord{132, 0, 578}, + dictWord{10, 0, 881}, + dictWord{12, 0, 740}, + dictWord{12, 0, 743}, + dictWord{140, 0, 759}, + dictWord{132, 0, 822}, + dictWord{133, 0, 923}, + dictWord{142, 10, 143}, + dictWord{135, 11, 1696}, + dictWord{6, 11, 363}, + dictWord{7, 11, 1955}, + dictWord{136, 11, 725}, + dictWord{132, 0, 924}, + dictWord{133, 0, 665}, + dictWord{ + 135, + 10, + 2029, + }, + dictWord{135, 0, 1901}, + dictWord{4, 0, 265}, + dictWord{6, 0, 1092}, + dictWord{6, 0, 1417}, + dictWord{7, 0, 807}, + dictWord{135, 0, 950}, + dictWord{ + 5, + 0, + 93, + }, + dictWord{12, 0, 267}, + dictWord{141, 0, 498}, + dictWord{135, 0, 1451}, + dictWord{5, 11, 813}, + dictWord{135, 11, 2046}, + dictWord{5, 10, 625}, + dictWord{135, 10, 1617}, + dictWord{135, 0, 747}, + dictWord{6, 0, 788}, + dictWord{137, 0, 828}, + dictWord{7, 0, 184}, + dictWord{11, 0, 307}, + dictWord{11, 0, 400}, + dictWord{15, 0, 130}, + dictWord{5, 11, 712}, + dictWord{7, 11, 1855}, + dictWord{8, 10, 425}, + dictWord{8, 10, 693}, + dictWord{9, 10, 720}, + dictWord{10, 10, 380}, + dictWord{10, 10, 638}, + dictWord{11, 11, 17}, + dictWord{11, 10, 473}, + dictWord{12, 10, 61}, + dictWord{13, 11, 321}, + dictWord{144, 11, 67}, + dictWord{135, 0, 198}, + dictWord{6, 11, 320}, + dictWord{7, 11, 781}, + dictWord{7, 11, 1921}, + dictWord{9, 11, 55}, + dictWord{10, 11, 186}, + dictWord{10, 11, 273}, + dictWord{10, 11, 664}, + dictWord{10, 11, 801}, + dictWord{11, 11, 996}, + dictWord{11, 11, 997}, + dictWord{13, 11, 157}, + dictWord{142, 11, 170}, + dictWord{136, 11, 271}, + dictWord{ + 135, + 0, + 994, + }, + dictWord{7, 11, 103}, + dictWord{7, 11, 863}, + dictWord{11, 11, 184}, + dictWord{14, 11, 299}, + dictWord{145, 11, 62}, + dictWord{11, 10, 551}, + dictWord{142, 10, 159}, + dictWord{5, 0, 233}, + dictWord{5, 0, 320}, + dictWord{6, 0, 140}, + dictWord{8, 0, 295}, + dictWord{8, 0, 615}, + dictWord{136, 11, 615}, + dictWord{ + 133, + 0, + 978, + }, + dictWord{4, 0, 905}, + dictWord{6, 0, 1701}, + dictWord{137, 0, 843}, + dictWord{132, 10, 168}, + dictWord{4, 0, 974}, + dictWord{8, 0, 850}, + dictWord{ + 12, + 0, + 709, + }, + dictWord{12, 0, 768}, + dictWord{140, 0, 786}, + dictWord{135, 10, 91}, + dictWord{152, 0, 6}, + dictWord{138, 10, 532}, + dictWord{135, 10, 1884}, + dictWord{132, 0, 509}, + dictWord{6, 0, 1307}, + dictWord{135, 0, 273}, + dictWord{5, 11, 77}, + dictWord{7, 11, 1455}, + dictWord{10, 11, 843}, + dictWord{19, 11, 73}, + dictWord{150, 11, 5}, + dictWord{132, 11, 458}, + dictWord{135, 11, 1420}, + dictWord{6, 11, 109}, + dictWord{138, 11, 382}, + dictWord{6, 0, 201}, + dictWord{6, 11, 330}, + dictWord{7, 10, 70}, + dictWord{7, 11, 1084}, + dictWord{10, 10, 240}, + dictWord{11, 11, 142}, + dictWord{147, 10, 93}, + dictWord{7, 0, 1041}, + dictWord{ + 140, + 11, + 328, + }, + dictWord{133, 11, 354}, + dictWord{134, 0, 1040}, + dictWord{133, 0, 693}, + dictWord{134, 0, 774}, + dictWord{139, 0, 234}, + dictWord{132, 0, 336}, + dictWord{7, 0, 1399}, + dictWord{139, 10, 392}, + dictWord{20, 0, 22}, + dictWord{148, 11, 22}, + dictWord{5, 0, 802}, + dictWord{7, 0, 2021}, + dictWord{136, 0, 805}, + dictWord{ + 5, + 0, + 167, + }, + dictWord{5, 0, 899}, + dictWord{6, 0, 410}, + dictWord{137, 0, 777}, + dictWord{137, 0, 789}, + dictWord{134, 0, 1705}, + dictWord{7, 10, 655}, + dictWord{ + 135, + 10, + 1844, + }, + dictWord{4, 10, 145}, + dictWord{6, 10, 176}, + dictWord{7, 10, 395}, + dictWord{137, 10, 562}, + dictWord{132, 10, 501}, + dictWord{135, 0, 10}, + dictWord{5, 0, 11}, + dictWord{6, 0, 117}, + dictWord{6, 0, 485}, + dictWord{7, 0, 1133}, + dictWord{9, 0, 582}, + dictWord{9, 0, 594}, + dictWord{10, 0, 82}, + dictWord{11, 0, 21}, + dictWord{11, 0, 818}, + dictWord{12, 0, 535}, + dictWord{13, 0, 86}, + dictWord{20, 0, 91}, + dictWord{23, 0, 13}, + dictWord{134, 10, 509}, + dictWord{4, 0, 264}, + dictWord{ + 7, + 0, + 1067, + }, + dictWord{8, 0, 204}, + dictWord{8, 0, 385}, + dictWord{139, 0, 953}, + dictWord{139, 11, 737}, + dictWord{138, 0, 56}, + dictWord{134, 0, 1917}, + dictWord{ + 133, + 0, + 470, + }, + dictWord{10, 11, 657}, + dictWord{14, 11, 297}, + dictWord{142, 11, 361}, + dictWord{135, 11, 412}, + dictWord{7, 0, 1198}, + dictWord{7, 11, 1198}, + dictWord{8, 11, 556}, + dictWord{14, 11, 123}, + dictWord{14, 11, 192}, + dictWord{143, 11, 27}, + dictWord{7, 11, 1985}, + dictWord{14, 11, 146}, + dictWord{15, 11, 42}, + dictWord{16, 11, 23}, + dictWord{17, 11, 86}, + dictWord{146, 11, 17}, + dictWord{11, 0, 1015}, + dictWord{136, 11, 122}, + dictWord{4, 10, 114}, + dictWord{ + 9, + 10, + 492, + }, + dictWord{13, 10, 462}, + dictWord{142, 10, 215}, + dictWord{4, 10, 77}, + dictWord{5, 10, 361}, + dictWord{6, 10, 139}, + dictWord{6, 10, 401}, + dictWord{ + 6, + 10, + 404, + }, + dictWord{7, 10, 413}, + dictWord{7, 10, 715}, + dictWord{7, 10, 1716}, + dictWord{11, 10, 279}, + dictWord{12, 10, 179}, + dictWord{12, 10, 258}, + dictWord{ + 13, + 10, + 244, + }, + dictWord{142, 10, 358}, + dictWord{134, 10, 1717}, + dictWord{7, 10, 1061}, + dictWord{8, 10, 82}, + dictWord{11, 10, 250}, + dictWord{12, 10, 420}, + dictWord{141, 10, 184}, + dictWord{133, 0, 715}, + dictWord{135, 10, 724}, + dictWord{9, 0, 919}, + dictWord{9, 0, 922}, + dictWord{9, 0, 927}, + dictWord{9, 0, 933}, + dictWord{9, 0, 962}, + dictWord{9, 0, 1000}, + dictWord{9, 0, 1002}, + dictWord{9, 0, 1021}, + dictWord{12, 0, 890}, + dictWord{12, 0, 907}, + dictWord{12, 0, 930}, + dictWord{ + 15, + 0, + 207, + }, + dictWord{15, 0, 228}, + dictWord{15, 0, 238}, + dictWord{149, 0, 61}, + dictWord{8, 0, 794}, + dictWord{9, 0, 400}, + dictWord{10, 0, 298}, + dictWord{142, 0, 228}, + dictWord{5, 11, 430}, + dictWord{5, 11, 932}, + dictWord{6, 11, 131}, + dictWord{7, 11, 417}, + dictWord{9, 11, 522}, + dictWord{11, 11, 314}, + dictWord{141, 11, 390}, + dictWord{132, 0, 867}, + dictWord{8, 0, 724}, + dictWord{132, 11, 507}, + dictWord{137, 11, 261}, + dictWord{4, 11, 343}, + dictWord{133, 11, 511}, + dictWord{ + 6, + 0, + 190, + }, + dictWord{7, 0, 768}, + dictWord{135, 0, 1170}, + dictWord{6, 10, 513}, + dictWord{135, 10, 1052}, + dictWord{7, 11, 455}, + dictWord{138, 11, 591}, + dictWord{134, 0, 1066}, + dictWord{137, 10, 899}, + dictWord{14, 0, 67}, + dictWord{147, 0, 60}, + dictWord{4, 0, 948}, + dictWord{18, 0, 174}, + dictWord{146, 0, 176}, + dictWord{135, 0, 1023}, + dictWord{7, 10, 1417}, + dictWord{12, 10, 382}, + dictWord{17, 10, 48}, + dictWord{152, 10, 12}, + dictWord{134, 11, 575}, + dictWord{ + 132, + 0, + 764, + }, + dictWord{6, 10, 545}, + dictWord{7, 10, 565}, + dictWord{7, 10, 1669}, + dictWord{10, 10, 114}, + dictWord{11, 10, 642}, + dictWord{140, 10, 618}, + dictWord{ + 6, + 0, + 137, + }, + dictWord{9, 0, 75}, + dictWord{9, 0, 253}, + dictWord{10, 0, 194}, + dictWord{138, 0, 444}, + dictWord{4, 0, 756}, + dictWord{133, 10, 5}, + dictWord{8, 0, 1008}, + dictWord{135, 10, 192}, + dictWord{132, 0, 842}, + dictWord{11, 0, 643}, + dictWord{12, 0, 115}, + dictWord{136, 10, 763}, + dictWord{139, 0, 67}, + dictWord{ + 133, + 10, + 759, + }, + dictWord{4, 0, 821}, + dictWord{5, 0, 760}, + dictWord{7, 0, 542}, + dictWord{8, 0, 135}, + dictWord{8, 0, 496}, + dictWord{135, 11, 580}, + dictWord{7, 10, 370}, + dictWord{7, 10, 1007}, + dictWord{7, 10, 1177}, + dictWord{135, 10, 1565}, + dictWord{135, 10, 1237}, + dictWord{140, 0, 736}, + dictWord{7, 0, 319}, + dictWord{ + 7, + 0, + 355, + }, + dictWord{7, 0, 763}, + dictWord{10, 0, 389}, + dictWord{145, 0, 43}, + dictWord{8, 11, 333}, + dictWord{138, 11, 182}, + dictWord{4, 10, 87}, + dictWord{5, 10, 250}, + dictWord{141, 10, 298}, + dictWord{138, 0, 786}, + dictWord{134, 0, 2044}, + dictWord{8, 11, 330}, + dictWord{140, 11, 477}, + dictWord{135, 11, 1338}, + dictWord{132, 11, 125}, + dictWord{134, 0, 1030}, + dictWord{134, 0, 1083}, + dictWord{132, 11, 721}, + dictWord{135, 10, 814}, + dictWord{7, 11, 776}, + dictWord{ + 8, + 11, + 145, + }, + dictWord{147, 11, 56}, + dictWord{134, 0, 1226}, + dictWord{4, 10, 57}, + dictWord{7, 10, 1195}, + dictWord{7, 10, 1438}, + dictWord{7, 10, 1548}, + dictWord{ + 7, + 10, + 1835, + }, + dictWord{7, 10, 1904}, + dictWord{9, 10, 757}, + dictWord{10, 10, 604}, + dictWord{139, 10, 519}, + dictWord{7, 11, 792}, + dictWord{8, 11, 147}, + dictWord{10, 11, 821}, + dictWord{139, 11, 1021}, + dictWord{137, 11, 797}, + dictWord{4, 0, 58}, + dictWord{5, 0, 286}, + dictWord{6, 0, 319}, + dictWord{7, 0, 402}, + dictWord{ + 7, + 0, + 1254, + }, + dictWord{7, 0, 1903}, + dictWord{8, 0, 356}, + dictWord{140, 0, 408}, + dictWord{4, 0, 389}, + dictWord{4, 0, 815}, + dictWord{9, 0, 181}, + dictWord{9, 0, 255}, + dictWord{10, 0, 8}, + dictWord{10, 0, 29}, + dictWord{10, 0, 816}, + dictWord{11, 0, 311}, + dictWord{11, 0, 561}, + dictWord{12, 0, 67}, + dictWord{141, 0, 181}, + dictWord{ + 7, + 11, + 1472, + }, + dictWord{135, 11, 1554}, + dictWord{7, 11, 1071}, + dictWord{7, 11, 1541}, + dictWord{7, 11, 1767}, + dictWord{7, 11, 1806}, + dictWord{7, 11, 1999}, + dictWord{9, 11, 248}, + dictWord{10, 11, 400}, + dictWord{11, 11, 162}, + dictWord{11, 11, 178}, + dictWord{11, 11, 242}, + dictWord{12, 11, 605}, + dictWord{ + 15, + 11, + 26, + }, + dictWord{144, 11, 44}, + dictWord{5, 11, 168}, + dictWord{5, 11, 930}, + dictWord{8, 11, 74}, + dictWord{9, 11, 623}, + dictWord{12, 11, 500}, + dictWord{ + 12, + 11, + 579, + }, + dictWord{13, 11, 41}, + dictWord{143, 11, 93}, + dictWord{6, 11, 220}, + dictWord{7, 11, 1101}, + dictWord{141, 11, 105}, + dictWord{5, 0, 474}, + dictWord{ + 7, + 0, + 507, + }, + dictWord{4, 10, 209}, + dictWord{7, 11, 507}, + dictWord{135, 10, 902}, + dictWord{132, 0, 427}, + dictWord{6, 0, 413}, + dictWord{7, 10, 335}, + dictWord{ + 7, + 10, + 1437, + }, + dictWord{7, 10, 1668}, + dictWord{8, 10, 553}, + dictWord{8, 10, 652}, + dictWord{8, 10, 656}, + dictWord{9, 10, 558}, + dictWord{11, 10, 743}, + dictWord{ + 149, + 10, + 18, + }, + dictWord{132, 0, 730}, + dictWord{6, 11, 19}, + dictWord{7, 11, 1413}, + dictWord{139, 11, 428}, + dictWord{133, 0, 373}, + dictWord{132, 10, 559}, + dictWord{7, 11, 96}, + dictWord{8, 11, 401}, + dictWord{137, 11, 896}, + dictWord{7, 0, 799}, + dictWord{7, 0, 1972}, + dictWord{5, 10, 1017}, + dictWord{138, 10, 511}, + dictWord{135, 0, 1793}, + dictWord{7, 11, 1961}, + dictWord{7, 11, 1965}, + dictWord{8, 11, 702}, + dictWord{136, 11, 750}, + dictWord{8, 11, 150}, + dictWord{8, 11, 737}, + dictWord{140, 11, 366}, + dictWord{132, 0, 322}, + dictWord{133, 10, 709}, + dictWord{8, 11, 800}, + dictWord{9, 11, 148}, + dictWord{9, 11, 872}, + dictWord{ + 9, + 11, + 890, + }, + dictWord{11, 11, 309}, + dictWord{11, 11, 1001}, + dictWord{13, 11, 267}, + dictWord{141, 11, 323}, + dictWord{134, 10, 1745}, + dictWord{7, 0, 290}, + dictWord{136, 10, 206}, + dictWord{7, 0, 1651}, + dictWord{145, 0, 89}, + dictWord{139, 0, 2}, + dictWord{132, 0, 672}, + dictWord{6, 0, 1860}, + dictWord{8, 0, 905}, + dictWord{ + 10, + 0, + 844, + }, + dictWord{10, 0, 846}, + dictWord{10, 0, 858}, + dictWord{12, 0, 699}, + dictWord{12, 0, 746}, + dictWord{140, 0, 772}, + dictWord{135, 11, 424}, + dictWord{133, 11, 547}, + dictWord{133, 0, 737}, + dictWord{5, 11, 490}, + dictWord{6, 11, 615}, + dictWord{6, 11, 620}, + dictWord{135, 11, 683}, + dictWord{6, 0, 746}, + dictWord{134, 0, 1612}, + dictWord{132, 10, 776}, + dictWord{9, 11, 385}, + dictWord{149, 11, 17}, + dictWord{133, 0, 145}, + dictWord{135, 10, 1272}, + dictWord{ + 7, + 0, + 884, + }, + dictWord{140, 0, 124}, + dictWord{4, 0, 387}, + dictWord{135, 0, 1288}, + dictWord{5, 11, 133}, + dictWord{136, 10, 406}, + dictWord{136, 11, 187}, + dictWord{ + 6, + 0, + 679, + }, + dictWord{8, 11, 8}, + dictWord{138, 11, 0}, + dictWord{135, 0, 550}, + dictWord{135, 11, 798}, + dictWord{136, 11, 685}, + dictWord{7, 11, 1086}, + dictWord{145, 11, 46}, + dictWord{8, 10, 175}, + dictWord{10, 10, 168}, + dictWord{138, 10, 573}, + dictWord{135, 0, 1305}, + dictWord{4, 0, 576}, + dictWord{ + 135, + 0, + 1263, + }, + dictWord{6, 0, 686}, + dictWord{134, 0, 1563}, + dictWord{134, 0, 607}, + dictWord{5, 0, 919}, + dictWord{134, 0, 1673}, + dictWord{148, 0, 37}, + dictWord{ + 8, + 11, + 774, + }, + dictWord{10, 11, 670}, + dictWord{140, 11, 51}, + dictWord{133, 10, 784}, + dictWord{139, 10, 882}, + dictWord{4, 0, 82}, + dictWord{5, 0, 333}, + dictWord{ + 5, + 0, + 904, + }, + dictWord{6, 0, 207}, + dictWord{7, 0, 325}, + dictWord{7, 0, 1726}, + dictWord{8, 0, 101}, + dictWord{10, 0, 778}, + dictWord{139, 0, 220}, + dictWord{135, 11, 371}, + dictWord{132, 0, 958}, + dictWord{133, 0, 903}, + dictWord{4, 11, 127}, + dictWord{5, 11, 350}, + dictWord{6, 11, 356}, + dictWord{8, 11, 426}, + dictWord{9, 11, 572}, + dictWord{10, 11, 247}, + dictWord{139, 11, 312}, + dictWord{140, 0, 147}, + dictWord{6, 11, 59}, + dictWord{7, 11, 885}, + dictWord{9, 11, 603}, + dictWord{ + 141, + 11, + 397, + }, + dictWord{10, 0, 367}, + dictWord{9, 10, 14}, + dictWord{9, 10, 441}, + dictWord{139, 10, 9}, + dictWord{11, 10, 966}, + dictWord{12, 10, 287}, + dictWord{ + 13, + 10, + 342, + }, + dictWord{13, 10, 402}, + dictWord{15, 10, 110}, + dictWord{143, 10, 163}, + dictWord{134, 0, 690}, + dictWord{132, 0, 705}, + dictWord{9, 0, 651}, + dictWord{ + 11, + 0, + 971, + }, + dictWord{13, 0, 273}, + dictWord{7, 10, 1428}, + dictWord{7, 10, 1640}, + dictWord{7, 10, 1867}, + dictWord{9, 10, 169}, + dictWord{9, 10, 182}, + dictWord{ + 9, + 10, + 367, + }, + dictWord{9, 10, 478}, + dictWord{9, 10, 506}, + dictWord{9, 10, 551}, + dictWord{9, 10, 557}, + dictWord{9, 10, 648}, + dictWord{9, 10, 697}, + dictWord{ + 9, + 10, + 705, + }, + dictWord{9, 10, 725}, + dictWord{9, 10, 787}, + dictWord{9, 10, 794}, + dictWord{10, 10, 198}, + dictWord{10, 10, 214}, + dictWord{10, 10, 267}, + dictWord{ + 10, + 10, + 275, + }, + dictWord{10, 10, 456}, + dictWord{10, 10, 551}, + dictWord{10, 10, 561}, + dictWord{10, 10, 613}, + dictWord{10, 10, 627}, + dictWord{10, 10, 668}, + dictWord{10, 10, 675}, + dictWord{10, 10, 691}, + dictWord{10, 10, 695}, + dictWord{10, 10, 707}, + dictWord{10, 10, 715}, + dictWord{11, 10, 183}, + dictWord{ + 11, + 10, + 201, + }, + dictWord{11, 10, 262}, + dictWord{11, 10, 352}, + dictWord{11, 10, 439}, + dictWord{11, 10, 493}, + dictWord{11, 10, 572}, + dictWord{11, 10, 591}, + dictWord{ + 11, + 10, + 608, + }, + dictWord{11, 10, 611}, + dictWord{11, 10, 646}, + dictWord{11, 10, 674}, + dictWord{11, 10, 711}, + dictWord{11, 10, 751}, + dictWord{11, 10, 761}, + dictWord{11, 10, 776}, + dictWord{11, 10, 785}, + dictWord{11, 10, 850}, + dictWord{11, 10, 853}, + dictWord{11, 10, 862}, + dictWord{11, 10, 865}, + dictWord{ + 11, + 10, + 868, + }, + dictWord{11, 10, 875}, + dictWord{11, 10, 898}, + dictWord{11, 10, 902}, + dictWord{11, 10, 903}, + dictWord{11, 10, 910}, + dictWord{11, 10, 932}, + dictWord{ + 11, + 10, + 942, + }, + dictWord{11, 10, 957}, + dictWord{11, 10, 967}, + dictWord{11, 10, 972}, + dictWord{12, 10, 148}, + dictWord{12, 10, 195}, + dictWord{12, 10, 220}, + dictWord{12, 10, 237}, + dictWord{12, 10, 318}, + dictWord{12, 10, 339}, + dictWord{12, 10, 393}, + dictWord{12, 10, 445}, + dictWord{12, 10, 450}, + dictWord{ + 12, + 10, + 474, + }, + dictWord{12, 10, 505}, + dictWord{12, 10, 509}, + dictWord{12, 10, 533}, + dictWord{12, 10, 591}, + dictWord{12, 10, 594}, + dictWord{12, 10, 597}, + dictWord{ + 12, + 10, + 621, + }, + dictWord{12, 10, 633}, + dictWord{12, 10, 642}, + dictWord{13, 10, 59}, + dictWord{13, 10, 60}, + dictWord{13, 10, 145}, + dictWord{13, 10, 239}, + dictWord{13, 10, 250}, + dictWord{13, 10, 329}, + dictWord{13, 10, 344}, + dictWord{13, 10, 365}, + dictWord{13, 10, 372}, + dictWord{13, 10, 387}, + dictWord{ + 13, + 10, + 403, + }, + dictWord{13, 10, 414}, + dictWord{13, 10, 456}, + dictWord{13, 10, 470}, + dictWord{13, 10, 478}, + dictWord{13, 10, 483}, + dictWord{13, 10, 489}, + dictWord{ + 14, + 10, + 55, + }, + dictWord{14, 10, 57}, + dictWord{14, 10, 81}, + dictWord{14, 10, 90}, + dictWord{14, 10, 148}, + dictWord{14, 10, 239}, + dictWord{14, 10, 266}, + dictWord{ + 14, + 10, + 321, + }, + dictWord{14, 10, 326}, + dictWord{14, 10, 327}, + dictWord{14, 10, 330}, + dictWord{14, 10, 347}, + dictWord{14, 10, 355}, + dictWord{14, 10, 401}, + dictWord{14, 10, 404}, + dictWord{14, 10, 411}, + dictWord{14, 10, 414}, + dictWord{14, 10, 416}, + dictWord{14, 10, 420}, + dictWord{15, 10, 61}, + dictWord{ + 15, + 10, + 74, + }, + dictWord{15, 10, 87}, + dictWord{15, 10, 88}, + dictWord{15, 10, 94}, + dictWord{15, 10, 96}, + dictWord{15, 10, 116}, + dictWord{15, 10, 149}, + dictWord{ + 15, + 10, + 154, + }, + dictWord{16, 10, 50}, + dictWord{16, 10, 63}, + dictWord{16, 10, 73}, + dictWord{17, 10, 2}, + dictWord{17, 10, 66}, + dictWord{17, 10, 92}, + dictWord{17, 10, 103}, + dictWord{17, 10, 112}, + dictWord{17, 10, 120}, + dictWord{18, 10, 50}, + dictWord{18, 10, 54}, + dictWord{18, 10, 82}, + dictWord{18, 10, 86}, + dictWord{18, 10, 90}, + dictWord{18, 10, 111}, + dictWord{18, 10, 115}, + dictWord{18, 10, 156}, + dictWord{19, 10, 40}, + dictWord{19, 10, 79}, + dictWord{20, 10, 78}, + dictWord{149, 10, 22}, + dictWord{7, 0, 887}, + dictWord{5, 10, 161}, + dictWord{135, 10, 839}, + dictWord{142, 11, 98}, + dictWord{134, 0, 90}, + dictWord{138, 11, 356}, + dictWord{ + 135, + 11, + 441, + }, + dictWord{6, 11, 111}, + dictWord{7, 11, 4}, + dictWord{8, 11, 163}, + dictWord{8, 11, 776}, + dictWord{138, 11, 566}, + dictWord{134, 0, 908}, + dictWord{ + 134, + 0, + 1261, + }, + dictWord{7, 0, 813}, + dictWord{12, 0, 497}, + dictWord{141, 0, 56}, + dictWord{134, 0, 1235}, + dictWord{135, 0, 429}, + dictWord{135, 11, 1994}, + dictWord{138, 0, 904}, + dictWord{6, 0, 125}, + dictWord{7, 0, 1277}, + dictWord{137, 0, 772}, + dictWord{151, 0, 12}, + dictWord{4, 0, 841}, + dictWord{5, 0, 386}, + dictWord{ + 133, + 11, + 386, + }, + dictWord{5, 11, 297}, + dictWord{135, 11, 1038}, + dictWord{6, 0, 860}, + dictWord{6, 0, 1069}, + dictWord{135, 11, 309}, + dictWord{136, 0, 946}, + dictWord{135, 10, 1814}, + dictWord{141, 11, 418}, + dictWord{136, 11, 363}, + dictWord{10, 0, 768}, + dictWord{139, 0, 787}, + dictWord{22, 11, 30}, + dictWord{ + 150, + 11, + 33, + }, + dictWord{6, 0, 160}, + dictWord{7, 0, 1106}, + dictWord{9, 0, 770}, + dictWord{11, 0, 112}, + dictWord{140, 0, 413}, + dictWord{11, 11, 216}, + dictWord{ + 139, + 11, + 340, + }, + dictWord{136, 10, 139}, + dictWord{135, 11, 1390}, + dictWord{135, 11, 808}, + dictWord{132, 11, 280}, + dictWord{12, 0, 271}, + dictWord{17, 0, 109}, + dictWord{7, 10, 643}, + dictWord{136, 10, 236}, + dictWord{140, 11, 54}, + dictWord{4, 11, 421}, + dictWord{133, 11, 548}, + dictWord{11, 0, 719}, + dictWord{12, 0, 36}, + dictWord{141, 0, 337}, + dictWord{7, 0, 581}, + dictWord{9, 0, 644}, + dictWord{137, 0, 699}, + dictWord{11, 11, 511}, + dictWord{13, 11, 394}, + dictWord{14, 11, 298}, + dictWord{14, 11, 318}, + dictWord{146, 11, 103}, + dictWord{7, 0, 304}, + dictWord{9, 0, 646}, + dictWord{9, 0, 862}, + dictWord{11, 0, 696}, + dictWord{12, 0, 208}, + dictWord{15, 0, 79}, + dictWord{147, 0, 108}, + dictWord{4, 0, 631}, + dictWord{7, 0, 1126}, + dictWord{135, 0, 1536}, + dictWord{135, 11, 1527}, + dictWord{8, 0, 880}, + dictWord{10, 0, 869}, + dictWord{138, 0, 913}, + dictWord{7, 0, 1513}, + dictWord{5, 10, 54}, + dictWord{6, 11, 254}, + dictWord{9, 11, 109}, + dictWord{138, 11, 103}, + dictWord{135, 0, 981}, + dictWord{133, 11, 729}, + dictWord{132, 10, 744}, + dictWord{132, 0, 434}, + dictWord{134, 0, 550}, + dictWord{7, 0, 930}, + dictWord{10, 0, 476}, + dictWord{13, 0, 452}, + dictWord{19, 0, 104}, + dictWord{6, 11, 1630}, + dictWord{10, 10, 402}, + dictWord{146, 10, 55}, + dictWord{5, 0, 553}, + dictWord{138, 0, 824}, + dictWord{136, 0, 452}, + dictWord{8, 0, 151}, + dictWord{137, 10, 624}, + dictWord{132, 10, 572}, + dictWord{132, 0, 772}, + dictWord{133, 11, 671}, + dictWord{ + 133, + 0, + 292, + }, + dictWord{138, 0, 135}, + dictWord{132, 11, 889}, + dictWord{140, 11, 207}, + dictWord{9, 0, 504}, + dictWord{6, 10, 43}, + dictWord{7, 10, 38}, + dictWord{ + 8, + 10, + 248, + }, + dictWord{138, 10, 513}, + dictWord{6, 0, 1089}, + dictWord{135, 11, 1910}, + dictWord{4, 11, 627}, + dictWord{133, 11, 775}, + dictWord{135, 0, 783}, + dictWord{133, 10, 766}, + dictWord{133, 10, 363}, + dictWord{7, 0, 387}, + dictWord{135, 11, 387}, + dictWord{7, 0, 393}, + dictWord{10, 0, 603}, + dictWord{11, 0, 206}, + dictWord{7, 11, 202}, + dictWord{11, 11, 362}, + dictWord{11, 11, 948}, + dictWord{140, 11, 388}, + dictWord{6, 11, 507}, + dictWord{7, 11, 451}, + dictWord{8, 11, 389}, + dictWord{12, 11, 490}, + dictWord{13, 11, 16}, + dictWord{13, 11, 215}, + dictWord{13, 11, 351}, + dictWord{18, 11, 132}, + dictWord{147, 11, 125}, + dictWord{ + 4, + 0, + 912, + }, + dictWord{9, 0, 232}, + dictWord{135, 11, 841}, + dictWord{6, 10, 258}, + dictWord{140, 10, 409}, + dictWord{5, 10, 249}, + dictWord{148, 10, 82}, + dictWord{ + 136, + 11, + 566, + }, + dictWord{6, 0, 977}, + dictWord{135, 11, 1214}, + dictWord{7, 0, 1973}, + dictWord{136, 0, 716}, + dictWord{135, 0, 98}, + dictWord{133, 0, 733}, + dictWord{ + 5, + 11, + 912, + }, + dictWord{134, 11, 1695}, + dictWord{5, 10, 393}, + dictWord{6, 10, 378}, + dictWord{7, 10, 1981}, + dictWord{9, 10, 32}, + dictWord{9, 10, 591}, + dictWord{10, 10, 685}, + dictWord{10, 10, 741}, + dictWord{142, 10, 382}, + dictWord{133, 10, 788}, + dictWord{10, 0, 19}, + dictWord{11, 0, 911}, + dictWord{7, 10, 1968}, + dictWord{141, 10, 509}, + dictWord{5, 0, 668}, + dictWord{5, 11, 236}, + dictWord{6, 11, 572}, + dictWord{8, 11, 492}, + dictWord{11, 11, 618}, + dictWord{144, 11, 56}, + dictWord{135, 11, 1789}, + dictWord{4, 0, 360}, + dictWord{5, 0, 635}, + dictWord{5, 0, 700}, + dictWord{5, 10, 58}, + dictWord{5, 10, 171}, + dictWord{5, 10, 683}, + dictWord{ + 6, + 10, + 291, + }, + dictWord{6, 10, 566}, + dictWord{7, 10, 1650}, + dictWord{11, 10, 523}, + dictWord{12, 10, 273}, + dictWord{12, 10, 303}, + dictWord{15, 10, 39}, + dictWord{143, 10, 111}, + dictWord{133, 0, 901}, + dictWord{134, 10, 589}, + dictWord{5, 11, 190}, + dictWord{136, 11, 318}, + dictWord{140, 0, 656}, + dictWord{ + 7, + 0, + 726, + }, + dictWord{152, 0, 9}, + dictWord{4, 10, 917}, + dictWord{133, 10, 1005}, + dictWord{135, 10, 1598}, + dictWord{134, 11, 491}, + dictWord{4, 10, 919}, + dictWord{133, 11, 434}, + dictWord{137, 0, 72}, + dictWord{6, 0, 1269}, + dictWord{6, 0, 1566}, + dictWord{134, 0, 1621}, + dictWord{9, 0, 463}, + dictWord{10, 0, 595}, + dictWord{4, 10, 255}, + dictWord{5, 10, 302}, + dictWord{6, 10, 132}, + dictWord{7, 10, 128}, + dictWord{7, 10, 283}, + dictWord{7, 10, 1299}, + dictWord{10, 10, 52}, + dictWord{ + 10, + 10, + 514, + }, + dictWord{11, 10, 925}, + dictWord{13, 10, 92}, + dictWord{142, 10, 309}, + dictWord{135, 0, 1454}, + dictWord{134, 0, 1287}, + dictWord{11, 0, 600}, + dictWord{13, 0, 245}, + dictWord{137, 10, 173}, + dictWord{136, 0, 989}, + dictWord{7, 0, 164}, + dictWord{7, 0, 1571}, + dictWord{9, 0, 107}, + dictWord{140, 0, 225}, + dictWord{6, 0, 1061}, + dictWord{141, 10, 442}, + dictWord{4, 0, 27}, + dictWord{5, 0, 484}, + dictWord{5, 0, 510}, + dictWord{6, 0, 434}, + dictWord{7, 0, 1000}, + dictWord{ + 7, + 0, + 1098, + }, + dictWord{136, 0, 2}, + dictWord{7, 11, 85}, + dictWord{7, 11, 247}, + dictWord{8, 11, 585}, + dictWord{10, 11, 163}, + dictWord{138, 11, 316}, + dictWord{ + 11, + 11, + 103, + }, + dictWord{142, 11, 0}, + dictWord{134, 0, 1127}, + dictWord{4, 0, 460}, + dictWord{134, 0, 852}, + dictWord{134, 10, 210}, + dictWord{4, 0, 932}, + dictWord{ + 133, + 0, + 891, + }, + dictWord{6, 0, 588}, + dictWord{147, 11, 83}, + dictWord{8, 0, 625}, + dictWord{4, 10, 284}, + dictWord{134, 10, 223}, + dictWord{134, 0, 76}, + dictWord{8, 0, 92}, + dictWord{137, 0, 221}, + dictWord{4, 11, 124}, + dictWord{10, 11, 457}, + dictWord{11, 11, 121}, + dictWord{11, 11, 169}, + dictWord{11, 11, 422}, + dictWord{ + 11, + 11, + 870, + }, + dictWord{12, 11, 214}, + dictWord{13, 11, 389}, + dictWord{14, 11, 187}, + dictWord{143, 11, 77}, + dictWord{9, 11, 618}, + dictWord{138, 11, 482}, + dictWord{ + 4, + 10, + 218, + }, + dictWord{7, 10, 526}, + dictWord{143, 10, 137}, + dictWord{13, 0, 9}, + dictWord{14, 0, 104}, + dictWord{14, 0, 311}, + dictWord{4, 10, 270}, + dictWord{ + 5, + 10, + 192, + }, + dictWord{6, 10, 332}, + dictWord{135, 10, 1322}, + dictWord{140, 10, 661}, + dictWord{135, 11, 1193}, + dictWord{6, 11, 107}, + dictWord{7, 11, 638}, + dictWord{7, 11, 1632}, + dictWord{137, 11, 396}, + dictWord{132, 0, 763}, + dictWord{4, 0, 622}, + dictWord{5, 11, 370}, + dictWord{134, 11, 1756}, + dictWord{ + 133, + 0, + 253, + }, + dictWord{135, 0, 546}, + dictWord{9, 0, 73}, + dictWord{10, 0, 110}, + dictWord{14, 0, 185}, + dictWord{17, 0, 119}, + dictWord{133, 11, 204}, + dictWord{7, 0, 624}, + dictWord{7, 0, 916}, + dictWord{10, 0, 256}, + dictWord{139, 0, 87}, + dictWord{7, 10, 379}, + dictWord{8, 10, 481}, + dictWord{137, 10, 377}, + dictWord{5, 0, 212}, + dictWord{12, 0, 35}, + dictWord{13, 0, 382}, + dictWord{5, 11, 970}, + dictWord{134, 11, 1706}, + dictWord{9, 0, 746}, + dictWord{5, 10, 1003}, + dictWord{134, 10, 149}, + dictWord{10, 0, 150}, + dictWord{11, 0, 849}, + dictWord{13, 0, 330}, + dictWord{8, 10, 262}, + dictWord{9, 10, 627}, + dictWord{11, 10, 214}, + dictWord{11, 10, 404}, + dictWord{11, 10, 457}, + dictWord{11, 10, 780}, + dictWord{11, 10, 913}, + dictWord{13, 10, 401}, + dictWord{142, 10, 200}, + dictWord{134, 0, 1466}, + dictWord{ + 135, + 11, + 3, + }, + dictWord{6, 0, 1299}, + dictWord{4, 11, 35}, + dictWord{5, 11, 121}, + dictWord{5, 11, 483}, + dictWord{5, 11, 685}, + dictWord{6, 11, 489}, + dictWord{7, 11, 1204}, + dictWord{136, 11, 394}, + dictWord{135, 10, 742}, + dictWord{4, 10, 142}, + dictWord{136, 10, 304}, + dictWord{4, 11, 921}, + dictWord{133, 11, 1007}, + dictWord{ + 134, + 0, + 1518, + }, + dictWord{6, 0, 1229}, + dictWord{135, 0, 1175}, + dictWord{133, 0, 816}, + dictWord{12, 0, 159}, + dictWord{4, 10, 471}, + dictWord{4, 11, 712}, + dictWord{ + 5, + 10, + 51, + }, + dictWord{6, 10, 602}, + dictWord{7, 10, 925}, + dictWord{8, 10, 484}, + dictWord{138, 10, 195}, + dictWord{134, 11, 1629}, + dictWord{5, 0, 869}, + dictWord{ + 5, + 0, + 968, + }, + dictWord{6, 0, 1626}, + dictWord{8, 0, 734}, + dictWord{136, 0, 784}, + dictWord{4, 0, 542}, + dictWord{6, 0, 1716}, + dictWord{6, 0, 1727}, + dictWord{ + 7, + 0, + 1082, + }, + dictWord{7, 0, 1545}, + dictWord{8, 0, 56}, + dictWord{8, 0, 118}, + dictWord{8, 0, 412}, + dictWord{8, 0, 564}, + dictWord{9, 0, 888}, + dictWord{9, 0, 908}, + dictWord{ + 10, + 0, + 50, + }, + dictWord{10, 0, 423}, + dictWord{11, 0, 685}, + dictWord{11, 0, 697}, + dictWord{11, 0, 933}, + dictWord{12, 0, 299}, + dictWord{13, 0, 126}, + dictWord{ + 13, + 0, + 136, + }, + dictWord{13, 0, 170}, + dictWord{13, 0, 190}, + dictWord{136, 10, 688}, + dictWord{132, 10, 697}, + dictWord{4, 0, 232}, + dictWord{9, 0, 202}, + dictWord{ + 10, + 0, + 474, + }, + dictWord{140, 0, 433}, + dictWord{136, 0, 212}, + dictWord{6, 0, 108}, + dictWord{7, 0, 1003}, + dictWord{7, 0, 1181}, + dictWord{8, 0, 111}, + dictWord{ + 136, + 0, + 343, + }, + dictWord{5, 10, 221}, + dictWord{135, 11, 1255}, + dictWord{133, 11, 485}, + dictWord{134, 0, 1712}, + dictWord{142, 0, 216}, + dictWord{5, 0, 643}, + dictWord{ + 6, + 0, + 516, + }, + dictWord{4, 11, 285}, + dictWord{5, 11, 317}, + dictWord{6, 11, 301}, + dictWord{7, 11, 7}, + dictWord{8, 11, 153}, + dictWord{10, 11, 766}, + dictWord{ + 11, + 11, + 468, + }, + dictWord{12, 11, 467}, + dictWord{141, 11, 143}, + dictWord{4, 0, 133}, + dictWord{7, 0, 711}, + dictWord{7, 0, 1298}, + dictWord{135, 0, 1585}, + dictWord{ + 134, + 0, + 650, + }, + dictWord{135, 11, 512}, + dictWord{6, 0, 99}, + dictWord{7, 0, 1808}, + dictWord{145, 0, 57}, + dictWord{6, 0, 246}, + dictWord{6, 0, 574}, + dictWord{7, 0, 428}, + dictWord{9, 0, 793}, + dictWord{10, 0, 669}, + dictWord{11, 0, 485}, + dictWord{11, 0, 840}, + dictWord{12, 0, 300}, + dictWord{14, 0, 250}, + dictWord{145, 0, 55}, + dictWord{ + 4, + 10, + 132, + }, + dictWord{5, 10, 69}, + dictWord{135, 10, 1242}, + dictWord{136, 0, 1023}, + dictWord{7, 0, 302}, + dictWord{132, 10, 111}, + dictWord{135, 0, 1871}, + dictWord{132, 0, 728}, + dictWord{9, 0, 252}, + dictWord{132, 10, 767}, + dictWord{6, 0, 461}, + dictWord{7, 0, 1590}, + dictWord{7, 10, 1416}, + dictWord{7, 10, 2005}, + dictWord{8, 10, 131}, + dictWord{8, 10, 466}, + dictWord{9, 10, 672}, + dictWord{13, 10, 252}, + dictWord{148, 10, 103}, + dictWord{6, 0, 323}, + dictWord{135, 0, 1564}, + dictWord{7, 0, 461}, + dictWord{136, 0, 775}, + dictWord{6, 10, 44}, + dictWord{136, 10, 368}, + dictWord{139, 0, 172}, + dictWord{132, 0, 464}, + dictWord{4, 10, 570}, + dictWord{133, 10, 120}, + dictWord{137, 11, 269}, + dictWord{6, 10, 227}, + dictWord{135, 10, 1589}, + dictWord{6, 11, 1719}, + dictWord{6, 11, 1735}, + dictWord{ + 7, + 11, + 2016, + }, + dictWord{7, 11, 2020}, + dictWord{8, 11, 837}, + dictWord{137, 11, 852}, + dictWord{7, 0, 727}, + dictWord{146, 0, 73}, + dictWord{132, 0, 1023}, + dictWord{135, 11, 852}, + dictWord{135, 10, 1529}, + dictWord{136, 0, 577}, + dictWord{138, 11, 568}, + dictWord{134, 0, 1037}, + dictWord{8, 11, 67}, + dictWord{ + 138, + 11, + 419, + }, + dictWord{4, 0, 413}, + dictWord{5, 0, 677}, + dictWord{8, 0, 432}, + dictWord{140, 0, 280}, + dictWord{10, 0, 600}, + dictWord{6, 10, 1667}, + dictWord{ + 7, + 11, + 967, + }, + dictWord{7, 10, 2036}, + dictWord{141, 11, 11}, + dictWord{6, 10, 511}, + dictWord{140, 10, 132}, + dictWord{6, 0, 799}, + dictWord{5, 10, 568}, + dictWord{ + 6, + 10, + 138, + }, + dictWord{135, 10, 1293}, + dictWord{8, 0, 159}, + dictWord{4, 10, 565}, + dictWord{136, 10, 827}, + dictWord{7, 0, 646}, + dictWord{7, 0, 1730}, + dictWord{ + 11, + 0, + 446, + }, + dictWord{141, 0, 178}, + dictWord{4, 10, 922}, + dictWord{133, 10, 1023}, + dictWord{135, 11, 11}, + dictWord{132, 0, 395}, + dictWord{11, 0, 145}, + dictWord{135, 10, 1002}, + dictWord{9, 0, 174}, + dictWord{10, 0, 164}, + dictWord{11, 0, 440}, + dictWord{11, 0, 514}, + dictWord{11, 0, 841}, + dictWord{15, 0, 98}, + dictWord{149, 0, 20}, + dictWord{134, 0, 426}, + dictWord{10, 0, 608}, + dictWord{139, 0, 1002}, + dictWord{7, 11, 320}, + dictWord{8, 11, 51}, + dictWord{12, 11, 481}, + dictWord{12, 11, 570}, + dictWord{148, 11, 106}, + dictWord{9, 0, 977}, + dictWord{9, 0, 983}, + dictWord{132, 11, 445}, + dictWord{138, 0, 250}, + dictWord{139, 0, 100}, + dictWord{6, 0, 1982}, + dictWord{136, 10, 402}, + dictWord{133, 11, 239}, + dictWord{4, 10, 716}, + dictWord{141, 10, 31}, + dictWord{5, 0, 476}, + dictWord{7, 11, 83}, + dictWord{7, 11, 1990}, + dictWord{8, 11, 130}, + dictWord{139, 11, 720}, + dictWord{8, 10, 691}, + dictWord{136, 10, 731}, + dictWord{5, 11, 123}, + dictWord{ + 6, + 11, + 530, + }, + dictWord{7, 11, 348}, + dictWord{135, 11, 1419}, + dictWord{5, 0, 76}, + dictWord{6, 0, 458}, + dictWord{6, 0, 497}, + dictWord{7, 0, 868}, + dictWord{9, 0, 658}, + dictWord{10, 0, 594}, + dictWord{11, 0, 173}, + dictWord{11, 0, 566}, + dictWord{12, 0, 20}, + dictWord{12, 0, 338}, + dictWord{141, 0, 200}, + dictWord{9, 11, 139}, + dictWord{ + 10, + 11, + 399, + }, + dictWord{11, 11, 469}, + dictWord{12, 11, 634}, + dictWord{141, 11, 223}, + dictWord{9, 10, 840}, + dictWord{138, 10, 803}, + dictWord{133, 10, 847}, + dictWord{11, 11, 223}, + dictWord{140, 11, 168}, + dictWord{132, 11, 210}, + dictWord{8, 0, 447}, + dictWord{9, 10, 53}, + dictWord{9, 10, 268}, + dictWord{9, 10, 901}, + dictWord{10, 10, 518}, + dictWord{10, 10, 829}, + dictWord{11, 10, 188}, + dictWord{13, 10, 74}, + dictWord{14, 10, 46}, + dictWord{15, 10, 17}, + dictWord{15, 10, 33}, + dictWord{17, 10, 40}, + dictWord{18, 10, 36}, + dictWord{19, 10, 20}, + dictWord{22, 10, 1}, + dictWord{152, 10, 2}, + dictWord{4, 0, 526}, + dictWord{7, 0, 1029}, + dictWord{135, 0, 1054}, + dictWord{19, 11, 59}, + dictWord{150, 11, 2}, + dictWord{4, 0, 636}, + dictWord{6, 0, 1875}, + dictWord{6, 0, 1920}, + dictWord{9, 0, 999}, + dictWord{ + 12, + 0, + 807, + }, + dictWord{12, 0, 825}, + dictWord{15, 0, 179}, + dictWord{15, 0, 190}, + dictWord{18, 0, 182}, + dictWord{136, 10, 532}, + dictWord{6, 0, 1699}, + dictWord{ + 7, + 0, + 660, + }, + dictWord{7, 0, 1124}, + dictWord{17, 0, 31}, + dictWord{19, 0, 22}, + dictWord{151, 0, 14}, + dictWord{135, 10, 681}, + dictWord{132, 11, 430}, + dictWord{ + 140, + 10, + 677, + }, + dictWord{4, 10, 684}, + dictWord{136, 10, 384}, + dictWord{132, 11, 756}, + dictWord{133, 11, 213}, + dictWord{7, 0, 188}, + dictWord{7, 10, 110}, + dictWord{ + 8, + 10, + 290, + }, + dictWord{8, 10, 591}, + dictWord{9, 10, 382}, + dictWord{9, 10, 649}, + dictWord{11, 10, 71}, + dictWord{11, 10, 155}, + dictWord{11, 10, 313}, + dictWord{ + 12, + 10, + 5, + }, + dictWord{13, 10, 325}, + dictWord{142, 10, 287}, + dictWord{7, 10, 360}, + dictWord{7, 10, 425}, + dictWord{9, 10, 66}, + dictWord{9, 10, 278}, + dictWord{ + 138, + 10, + 644, + }, + dictWord{142, 11, 164}, + dictWord{4, 0, 279}, + dictWord{7, 0, 301}, + dictWord{137, 0, 362}, + dictWord{134, 11, 586}, + dictWord{135, 0, 1743}, + dictWord{4, 0, 178}, + dictWord{133, 0, 399}, + dictWord{4, 10, 900}, + dictWord{133, 10, 861}, + dictWord{5, 10, 254}, + dictWord{7, 10, 985}, + dictWord{136, 10, 73}, + dictWord{133, 11, 108}, + dictWord{7, 10, 1959}, + dictWord{136, 10, 683}, + dictWord{133, 11, 219}, + dictWord{4, 11, 193}, + dictWord{5, 11, 916}, + dictWord{ + 7, + 11, + 364, + }, + dictWord{10, 11, 398}, + dictWord{10, 11, 726}, + dictWord{11, 11, 317}, + dictWord{11, 11, 626}, + dictWord{12, 11, 142}, + dictWord{12, 11, 288}, + dictWord{ + 12, + 11, + 678, + }, + dictWord{13, 11, 313}, + dictWord{15, 11, 113}, + dictWord{18, 11, 114}, + dictWord{21, 11, 30}, + dictWord{150, 11, 53}, + dictWord{6, 11, 241}, + dictWord{7, 11, 907}, + dictWord{8, 11, 832}, + dictWord{9, 11, 342}, + dictWord{10, 11, 729}, + dictWord{11, 11, 284}, + dictWord{11, 11, 445}, + dictWord{11, 11, 651}, + dictWord{11, 11, 863}, + dictWord{13, 11, 398}, + dictWord{146, 11, 99}, + dictWord{132, 0, 872}, + dictWord{134, 0, 831}, + dictWord{134, 0, 1692}, + dictWord{ + 6, + 0, + 202, + }, + dictWord{6, 0, 1006}, + dictWord{9, 0, 832}, + dictWord{10, 0, 636}, + dictWord{11, 0, 208}, + dictWord{12, 0, 360}, + dictWord{17, 0, 118}, + dictWord{18, 0, 27}, + dictWord{20, 0, 67}, + dictWord{137, 11, 734}, + dictWord{132, 10, 725}, + dictWord{7, 11, 993}, + dictWord{138, 11, 666}, + dictWord{134, 0, 1954}, + dictWord{ + 134, + 10, + 196, + }, + dictWord{7, 0, 872}, + dictWord{10, 0, 516}, + dictWord{139, 0, 167}, + dictWord{133, 10, 831}, + dictWord{4, 11, 562}, + dictWord{9, 11, 254}, + dictWord{ + 139, + 11, + 879, + }, + dictWord{137, 0, 313}, + dictWord{4, 0, 224}, + dictWord{132, 11, 786}, + dictWord{11, 0, 24}, + dictWord{12, 0, 170}, + dictWord{136, 10, 723}, + dictWord{ + 5, + 0, + 546, + }, + dictWord{7, 0, 35}, + dictWord{8, 0, 11}, + dictWord{8, 0, 12}, + dictWord{9, 0, 315}, + dictWord{9, 0, 533}, + dictWord{10, 0, 802}, + dictWord{11, 0, 166}, + dictWord{ + 12, + 0, + 525, + }, + dictWord{142, 0, 243}, + dictWord{7, 0, 1937}, + dictWord{13, 10, 80}, + dictWord{13, 10, 437}, + dictWord{145, 10, 74}, + dictWord{5, 0, 241}, + dictWord{ + 8, + 0, + 242, + }, + dictWord{9, 0, 451}, + dictWord{10, 0, 667}, + dictWord{11, 0, 598}, + dictWord{140, 0, 429}, + dictWord{150, 0, 46}, + dictWord{6, 0, 1273}, + dictWord{ + 137, + 0, + 830, + }, + dictWord{5, 10, 848}, + dictWord{6, 10, 66}, + dictWord{136, 10, 764}, + dictWord{6, 0, 825}, + dictWord{134, 0, 993}, + dictWord{4, 0, 1006}, + dictWord{ + 10, + 0, + 327, + }, + dictWord{13, 0, 271}, + dictWord{4, 10, 36}, + dictWord{7, 10, 1387}, + dictWord{139, 10, 755}, + dictWord{134, 0, 1023}, + dictWord{135, 0, 1580}, + dictWord{ + 4, + 0, + 366, + }, + dictWord{137, 0, 516}, + dictWord{132, 10, 887}, + dictWord{6, 0, 1736}, + dictWord{135, 0, 1891}, + dictWord{6, 11, 216}, + dictWord{7, 11, 901}, + dictWord{ + 7, + 11, + 1343, + }, + dictWord{136, 11, 493}, + dictWord{6, 10, 165}, + dictWord{138, 10, 388}, + dictWord{7, 11, 341}, + dictWord{139, 11, 219}, + dictWord{4, 10, 719}, + dictWord{135, 10, 155}, + dictWord{134, 0, 1935}, + dictWord{132, 0, 826}, + dictWord{6, 0, 331}, + dictWord{6, 0, 1605}, + dictWord{8, 0, 623}, + dictWord{11, 0, 139}, + dictWord{139, 0, 171}, + dictWord{135, 11, 1734}, + dictWord{10, 11, 115}, + dictWord{11, 11, 420}, + dictWord{12, 11, 154}, + dictWord{13, 11, 404}, + dictWord{ + 14, + 11, + 346, + }, + dictWord{15, 11, 54}, + dictWord{143, 11, 112}, + dictWord{7, 0, 288}, + dictWord{4, 10, 353}, + dictWord{6, 10, 146}, + dictWord{6, 10, 1789}, + dictWord{ + 7, + 10, + 990, + }, + dictWord{7, 10, 1348}, + dictWord{9, 10, 665}, + dictWord{9, 10, 898}, + dictWord{11, 10, 893}, + dictWord{142, 10, 212}, + dictWord{6, 0, 916}, + dictWord{134, 0, 1592}, + dictWord{7, 0, 1888}, + dictWord{4, 10, 45}, + dictWord{135, 10, 1257}, + dictWord{5, 11, 1011}, + dictWord{136, 11, 701}, + dictWord{ + 139, + 11, + 596, + }, + dictWord{4, 11, 54}, + dictWord{5, 11, 666}, + dictWord{7, 11, 1039}, + dictWord{7, 11, 1130}, + dictWord{9, 11, 195}, + dictWord{138, 11, 302}, + dictWord{ + 134, + 0, + 1471, + }, + dictWord{134, 0, 1570}, + dictWord{132, 0, 394}, + dictWord{140, 10, 65}, + dictWord{136, 10, 816}, + dictWord{135, 0, 1931}, + dictWord{7, 0, 574}, + dictWord{135, 0, 1719}, + dictWord{134, 11, 467}, + dictWord{132, 0, 658}, + dictWord{9, 0, 781}, + dictWord{10, 0, 144}, + dictWord{11, 0, 385}, + dictWord{13, 0, 161}, + dictWord{13, 0, 228}, + dictWord{13, 0, 268}, + dictWord{20, 0, 107}, + dictWord{134, 11, 1669}, + dictWord{136, 0, 374}, + dictWord{135, 0, 735}, + dictWord{4, 0, 344}, + dictWord{6, 0, 498}, + dictWord{139, 0, 323}, + dictWord{7, 0, 586}, + dictWord{7, 0, 1063}, + dictWord{6, 10, 559}, + dictWord{134, 10, 1691}, + dictWord{137, 0, 155}, + dictWord{133, 0, 906}, + dictWord{7, 11, 122}, + dictWord{9, 11, 259}, + dictWord{10, 11, 84}, + dictWord{11, 11, 470}, + dictWord{12, 11, 541}, + dictWord{ + 141, + 11, + 379, + }, + dictWord{134, 0, 1139}, + dictWord{10, 0, 108}, + dictWord{139, 0, 116}, + dictWord{134, 10, 456}, + dictWord{133, 10, 925}, + dictWord{5, 11, 82}, + dictWord{ + 5, + 11, + 131, + }, + dictWord{7, 11, 1755}, + dictWord{8, 11, 31}, + dictWord{9, 11, 168}, + dictWord{9, 11, 764}, + dictWord{139, 11, 869}, + dictWord{134, 11, 605}, + dictWord{ + 5, + 11, + 278, + }, + dictWord{137, 11, 68}, + dictWord{4, 11, 163}, + dictWord{5, 11, 201}, + dictWord{5, 11, 307}, + dictWord{5, 11, 310}, + dictWord{6, 11, 335}, + dictWord{ + 7, + 11, + 284, + }, + dictWord{136, 11, 165}, + dictWord{135, 11, 1660}, + dictWord{6, 11, 33}, + dictWord{135, 11, 1244}, + dictWord{4, 0, 616}, + dictWord{136, 11, 483}, + dictWord{8, 0, 857}, + dictWord{8, 0, 902}, + dictWord{8, 0, 910}, + dictWord{10, 0, 879}, + dictWord{12, 0, 726}, + dictWord{4, 11, 199}, + dictWord{139, 11, 34}, + dictWord{136, 0, 692}, + dictWord{6, 10, 193}, + dictWord{7, 10, 240}, + dictWord{7, 10, 1682}, + dictWord{10, 10, 51}, + dictWord{10, 10, 640}, + dictWord{11, 10, 410}, + dictWord{13, 10, 82}, + dictWord{14, 10, 247}, + dictWord{14, 10, 331}, + dictWord{142, 10, 377}, + dictWord{6, 0, 823}, + dictWord{134, 0, 983}, + dictWord{ + 139, + 10, + 411, + }, + dictWord{132, 0, 305}, + dictWord{136, 10, 633}, + dictWord{138, 11, 203}, + dictWord{134, 0, 681}, + dictWord{6, 11, 326}, + dictWord{7, 11, 677}, + dictWord{137, 11, 425}, + dictWord{5, 0, 214}, + dictWord{7, 0, 603}, + dictWord{8, 0, 611}, + dictWord{9, 0, 686}, + dictWord{10, 0, 88}, + dictWord{11, 0, 459}, + dictWord{ + 11, + 0, + 496, + }, + dictWord{12, 0, 463}, + dictWord{12, 0, 590}, + dictWord{141, 0, 0}, + dictWord{136, 0, 1004}, + dictWord{142, 0, 23}, + dictWord{134, 0, 1703}, + dictWord{ + 147, + 11, + 8, + }, + dictWord{145, 11, 56}, + dictWord{135, 0, 1443}, + dictWord{4, 10, 237}, + dictWord{135, 10, 514}, + dictWord{6, 0, 714}, + dictWord{145, 0, 19}, + dictWord{ + 5, + 11, + 358, + }, + dictWord{7, 11, 473}, + dictWord{7, 11, 1184}, + dictWord{10, 11, 662}, + dictWord{13, 11, 212}, + dictWord{13, 11, 304}, + dictWord{13, 11, 333}, + dictWord{145, 11, 98}, + dictWord{4, 0, 737}, + dictWord{10, 0, 98}, + dictWord{11, 0, 294}, + dictWord{12, 0, 60}, + dictWord{12, 0, 437}, + dictWord{13, 0, 64}, + dictWord{ + 13, + 0, + 380, + }, + dictWord{142, 0, 430}, + dictWord{6, 10, 392}, + dictWord{7, 10, 65}, + dictWord{135, 10, 2019}, + dictWord{6, 0, 1758}, + dictWord{8, 0, 520}, + dictWord{ + 9, + 0, + 345, + }, + dictWord{9, 0, 403}, + dictWord{142, 0, 350}, + dictWord{5, 0, 47}, + dictWord{10, 0, 242}, + dictWord{138, 0, 579}, + dictWord{5, 0, 139}, + dictWord{7, 0, 1168}, + dictWord{138, 0, 539}, + dictWord{134, 0, 1459}, + dictWord{13, 0, 388}, + dictWord{141, 11, 388}, + dictWord{134, 0, 253}, + dictWord{7, 10, 1260}, + dictWord{ + 135, + 10, + 1790, + }, + dictWord{10, 0, 252}, + dictWord{9, 10, 222}, + dictWord{139, 10, 900}, + dictWord{140, 0, 745}, + dictWord{133, 11, 946}, + dictWord{4, 0, 107}, + dictWord{ + 7, + 0, + 613, + }, + dictWord{8, 0, 439}, + dictWord{8, 0, 504}, + dictWord{9, 0, 501}, + dictWord{10, 0, 383}, + dictWord{139, 0, 477}, + dictWord{135, 11, 1485}, + dictWord{ + 132, + 0, + 871, + }, + dictWord{7, 11, 411}, + dictWord{7, 11, 590}, + dictWord{8, 11, 631}, + dictWord{9, 11, 323}, + dictWord{10, 11, 355}, + dictWord{11, 11, 491}, + dictWord{ + 12, + 11, + 143, + }, + dictWord{12, 11, 402}, + dictWord{13, 11, 73}, + dictWord{14, 11, 408}, + dictWord{15, 11, 107}, + dictWord{146, 11, 71}, + dictWord{132, 0, 229}, + dictWord{132, 0, 903}, + dictWord{140, 0, 71}, + dictWord{133, 0, 549}, + dictWord{4, 0, 47}, + dictWord{6, 0, 373}, + dictWord{7, 0, 452}, + dictWord{7, 0, 543}, + dictWord{ + 7, + 0, + 1828, + }, + dictWord{7, 0, 1856}, + dictWord{9, 0, 6}, + dictWord{11, 0, 257}, + dictWord{139, 0, 391}, + dictWord{7, 11, 1467}, + dictWord{8, 11, 328}, + dictWord{ + 10, + 11, + 544, + }, + dictWord{11, 11, 955}, + dictWord{13, 11, 320}, + dictWord{145, 11, 83}, + dictWord{5, 0, 980}, + dictWord{134, 0, 1754}, + dictWord{136, 0, 865}, + dictWord{ + 5, + 0, + 705, + }, + dictWord{137, 0, 606}, + dictWord{7, 0, 161}, + dictWord{8, 10, 201}, + dictWord{136, 10, 605}, + dictWord{143, 11, 35}, + dictWord{5, 11, 835}, + dictWord{ + 6, + 11, + 483, + }, + dictWord{140, 10, 224}, + dictWord{7, 0, 536}, + dictWord{7, 0, 1331}, + dictWord{136, 0, 143}, + dictWord{134, 0, 1388}, + dictWord{5, 0, 724}, + dictWord{ + 10, + 0, + 305, + }, + dictWord{11, 0, 151}, + dictWord{12, 0, 33}, + dictWord{12, 0, 121}, + dictWord{12, 0, 381}, + dictWord{17, 0, 3}, + dictWord{17, 0, 27}, + dictWord{17, 0, 78}, + dictWord{18, 0, 18}, + dictWord{19, 0, 54}, + dictWord{149, 0, 5}, + dictWord{4, 10, 523}, + dictWord{133, 10, 638}, + dictWord{5, 0, 19}, + dictWord{134, 0, 533}, + dictWord{ + 5, + 0, + 395, + }, + dictWord{5, 0, 951}, + dictWord{134, 0, 1776}, + dictWord{135, 0, 1908}, + dictWord{132, 0, 846}, + dictWord{10, 0, 74}, + dictWord{11, 0, 663}, + dictWord{ + 12, + 0, + 210, + }, + dictWord{13, 0, 166}, + dictWord{13, 0, 310}, + dictWord{14, 0, 373}, + dictWord{18, 0, 95}, + dictWord{19, 0, 43}, + dictWord{6, 10, 242}, + dictWord{7, 10, 227}, + dictWord{7, 10, 1581}, + dictWord{8, 10, 104}, + dictWord{9, 10, 113}, + dictWord{9, 10, 220}, + dictWord{9, 10, 427}, + dictWord{10, 10, 239}, + dictWord{11, 10, 579}, + dictWord{11, 10, 1023}, + dictWord{13, 10, 4}, + dictWord{13, 10, 204}, + dictWord{13, 10, 316}, + dictWord{148, 10, 86}, + dictWord{9, 11, 716}, + dictWord{11, 11, 108}, + dictWord{13, 11, 123}, + dictWord{14, 11, 252}, + dictWord{19, 11, 38}, + dictWord{21, 11, 3}, + dictWord{151, 11, 11}, + dictWord{8, 0, 372}, + dictWord{9, 0, 122}, + dictWord{138, 0, 175}, + dictWord{132, 11, 677}, + dictWord{7, 11, 1374}, + dictWord{136, 11, 540}, + dictWord{135, 10, 861}, + dictWord{132, 0, 695}, + dictWord{ + 7, + 0, + 497, + }, + dictWord{9, 0, 387}, + dictWord{147, 0, 81}, + dictWord{136, 0, 937}, + dictWord{134, 0, 718}, + dictWord{7, 0, 1328}, + dictWord{136, 10, 494}, + dictWord{ + 132, + 11, + 331, + }, + dictWord{6, 0, 1581}, + dictWord{133, 11, 747}, + dictWord{5, 0, 284}, + dictWord{6, 0, 49}, + dictWord{6, 0, 350}, + dictWord{7, 0, 1}, + dictWord{7, 0, 377}, + dictWord{7, 0, 1693}, + dictWord{8, 0, 18}, + dictWord{8, 0, 678}, + dictWord{9, 0, 161}, + dictWord{9, 0, 585}, + dictWord{9, 0, 671}, + dictWord{9, 0, 839}, + dictWord{11, 0, 912}, + dictWord{141, 0, 427}, + dictWord{7, 10, 1306}, + dictWord{8, 10, 505}, + dictWord{9, 10, 482}, + dictWord{10, 10, 126}, + dictWord{11, 10, 225}, + dictWord{12, 10, 347}, + dictWord{12, 10, 449}, + dictWord{13, 10, 19}, + dictWord{14, 10, 218}, + dictWord{142, 10, 435}, + dictWord{10, 10, 764}, + dictWord{12, 10, 120}, + dictWord{ + 13, + 10, + 39, + }, + dictWord{145, 10, 127}, + dictWord{4, 0, 597}, + dictWord{133, 10, 268}, + dictWord{134, 0, 1094}, + dictWord{4, 0, 1008}, + dictWord{134, 0, 1973}, + dictWord{132, 0, 811}, + dictWord{139, 0, 908}, + dictWord{135, 0, 1471}, + dictWord{133, 11, 326}, + dictWord{4, 10, 384}, + dictWord{135, 10, 1022}, + dictWord{ + 7, + 0, + 1935, + }, + dictWord{8, 0, 324}, + dictWord{12, 0, 42}, + dictWord{4, 11, 691}, + dictWord{7, 11, 1935}, + dictWord{8, 11, 324}, + dictWord{9, 11, 35}, + dictWord{10, 11, 680}, + dictWord{11, 11, 364}, + dictWord{12, 11, 42}, + dictWord{13, 11, 357}, + dictWord{146, 11, 16}, + dictWord{135, 0, 2014}, + dictWord{7, 0, 2007}, + dictWord{ + 9, + 0, + 101, + }, + dictWord{9, 0, 450}, + dictWord{10, 0, 66}, + dictWord{10, 0, 842}, + dictWord{11, 0, 536}, + dictWord{12, 0, 587}, + dictWord{6, 11, 32}, + dictWord{7, 11, 385}, + dictWord{7, 11, 757}, + dictWord{7, 11, 1916}, + dictWord{8, 11, 37}, + dictWord{8, 11, 94}, + dictWord{8, 11, 711}, + dictWord{9, 11, 541}, + dictWord{10, 11, 162}, + dictWord{ + 10, + 11, + 795, + }, + dictWord{11, 11, 989}, + dictWord{11, 11, 1010}, + dictWord{12, 11, 14}, + dictWord{142, 11, 308}, + dictWord{139, 0, 586}, + dictWord{ + 135, + 10, + 1703, + }, + dictWord{7, 0, 1077}, + dictWord{11, 0, 28}, + dictWord{9, 10, 159}, + dictWord{140, 10, 603}, + dictWord{6, 0, 1221}, + dictWord{136, 10, 583}, + dictWord{ + 6, + 11, + 152, + }, + dictWord{6, 11, 349}, + dictWord{6, 11, 1682}, + dictWord{7, 11, 1252}, + dictWord{8, 11, 112}, + dictWord{9, 11, 435}, + dictWord{9, 11, 668}, + dictWord{ + 10, + 11, + 290, + }, + dictWord{10, 11, 319}, + dictWord{10, 11, 815}, + dictWord{11, 11, 180}, + dictWord{11, 11, 837}, + dictWord{12, 11, 240}, + dictWord{13, 11, 152}, + dictWord{13, 11, 219}, + dictWord{142, 11, 158}, + dictWord{139, 0, 62}, + dictWord{132, 10, 515}, + dictWord{8, 10, 632}, + dictWord{8, 10, 697}, + dictWord{ + 137, + 10, + 854, + }, + dictWord{134, 0, 1766}, + dictWord{132, 11, 581}, + dictWord{6, 11, 126}, + dictWord{7, 11, 573}, + dictWord{8, 11, 397}, + dictWord{142, 11, 44}, + dictWord{ + 150, + 0, + 28, + }, + dictWord{11, 0, 670}, + dictWord{22, 0, 25}, + dictWord{4, 10, 136}, + dictWord{133, 10, 551}, + dictWord{6, 0, 1665}, + dictWord{7, 0, 256}, + dictWord{ + 7, + 0, + 1388, + }, + dictWord{138, 0, 499}, + dictWord{4, 0, 22}, + dictWord{5, 0, 10}, + dictWord{7, 0, 1576}, + dictWord{136, 0, 97}, + dictWord{134, 10, 1782}, + dictWord{5, 0, 481}, + dictWord{7, 10, 1287}, + dictWord{9, 10, 44}, + dictWord{10, 10, 552}, + dictWord{10, 10, 642}, + dictWord{11, 10, 839}, + dictWord{12, 10, 274}, + dictWord{ + 12, + 10, + 275, + }, + dictWord{12, 10, 372}, + dictWord{13, 10, 91}, + dictWord{142, 10, 125}, + dictWord{133, 11, 926}, + dictWord{7, 11, 1232}, + dictWord{137, 11, 531}, + dictWord{6, 0, 134}, + dictWord{7, 0, 437}, + dictWord{7, 0, 1824}, + dictWord{9, 0, 37}, + dictWord{14, 0, 285}, + dictWord{142, 0, 371}, + dictWord{7, 0, 486}, + dictWord{8, 0, 155}, + dictWord{11, 0, 93}, + dictWord{140, 0, 164}, + dictWord{6, 0, 1391}, + dictWord{134, 0, 1442}, + dictWord{133, 11, 670}, + dictWord{133, 0, 591}, + dictWord{ + 6, + 10, + 147, + }, + dictWord{7, 10, 886}, + dictWord{7, 11, 1957}, + dictWord{9, 10, 753}, + dictWord{138, 10, 268}, + dictWord{5, 0, 380}, + dictWord{5, 0, 650}, + dictWord{ + 7, + 0, + 1173, + }, + dictWord{136, 0, 310}, + dictWord{4, 0, 364}, + dictWord{7, 0, 1156}, + dictWord{7, 0, 1187}, + dictWord{137, 0, 409}, + dictWord{135, 11, 1621}, + dictWord{ + 134, + 0, + 482, + }, + dictWord{133, 11, 506}, + dictWord{4, 0, 781}, + dictWord{6, 0, 487}, + dictWord{7, 0, 926}, + dictWord{8, 0, 263}, + dictWord{139, 0, 500}, + dictWord{ + 138, + 10, + 137, + }, + dictWord{135, 11, 242}, + dictWord{139, 11, 96}, + dictWord{133, 10, 414}, + dictWord{135, 10, 1762}, + dictWord{134, 0, 804}, + dictWord{5, 11, 834}, + dictWord{7, 11, 1202}, + dictWord{8, 11, 14}, + dictWord{9, 11, 481}, + dictWord{137, 11, 880}, + dictWord{134, 10, 599}, + dictWord{4, 0, 94}, + dictWord{135, 0, 1265}, + dictWord{4, 0, 415}, + dictWord{132, 0, 417}, + dictWord{5, 0, 348}, + dictWord{6, 0, 522}, + dictWord{6, 10, 1749}, + dictWord{7, 11, 1526}, + dictWord{138, 11, 465}, + dictWord{134, 10, 1627}, + dictWord{132, 0, 1012}, + dictWord{132, 10, 488}, + dictWord{4, 11, 357}, + dictWord{6, 11, 172}, + dictWord{7, 11, 143}, + dictWord{ + 137, + 11, + 413, + }, + dictWord{4, 10, 83}, + dictWord{4, 11, 590}, + dictWord{146, 11, 76}, + dictWord{140, 10, 676}, + dictWord{7, 11, 287}, + dictWord{8, 11, 355}, + dictWord{ + 9, + 11, + 293, + }, + dictWord{137, 11, 743}, + dictWord{134, 10, 278}, + dictWord{6, 0, 1803}, + dictWord{18, 0, 165}, + dictWord{24, 0, 21}, + dictWord{5, 11, 169}, + dictWord{ + 7, + 11, + 333, + }, + dictWord{136, 11, 45}, + dictWord{12, 10, 97}, + dictWord{140, 11, 97}, + dictWord{4, 0, 408}, + dictWord{4, 0, 741}, + dictWord{135, 0, 500}, + dictWord{ + 132, + 11, + 198, + }, + dictWord{7, 10, 388}, + dictWord{7, 10, 644}, + dictWord{139, 10, 781}, + dictWord{4, 11, 24}, + dictWord{5, 11, 140}, + dictWord{5, 11, 185}, + dictWord{ + 7, + 11, + 1500, + }, + dictWord{11, 11, 565}, + dictWord{139, 11, 838}, + dictWord{6, 0, 1321}, + dictWord{9, 0, 257}, + dictWord{7, 10, 229}, + dictWord{8, 10, 59}, + dictWord{ + 9, + 10, + 190, + }, + dictWord{10, 10, 378}, + dictWord{140, 10, 191}, + dictWord{4, 11, 334}, + dictWord{133, 11, 593}, + dictWord{135, 11, 1885}, + dictWord{134, 0, 1138}, + dictWord{4, 0, 249}, + dictWord{6, 0, 73}, + dictWord{135, 0, 177}, + dictWord{133, 0, 576}, + dictWord{142, 0, 231}, + dictWord{137, 0, 288}, + dictWord{132, 10, 660}, + dictWord{7, 10, 1035}, + dictWord{138, 10, 737}, + dictWord{135, 0, 1487}, + dictWord{6, 0, 989}, + dictWord{9, 0, 433}, + dictWord{7, 10, 690}, + dictWord{9, 10, 587}, + dictWord{140, 10, 521}, + dictWord{7, 0, 1264}, + dictWord{7, 0, 1678}, + dictWord{11, 0, 945}, + dictWord{12, 0, 341}, + dictWord{12, 0, 471}, + dictWord{140, 0, 569}, + dictWord{132, 11, 709}, + dictWord{133, 11, 897}, + dictWord{5, 11, 224}, + dictWord{13, 11, 174}, + dictWord{146, 11, 52}, + dictWord{135, 11, 1840}, + dictWord{ + 134, + 10, + 1744, + }, + dictWord{12, 0, 87}, + dictWord{16, 0, 74}, + dictWord{4, 10, 733}, + dictWord{9, 10, 194}, + dictWord{10, 10, 92}, + dictWord{11, 10, 198}, + dictWord{ + 12, + 10, + 84, + }, + dictWord{141, 10, 128}, + dictWord{140, 0, 779}, + dictWord{135, 0, 538}, + dictWord{4, 11, 608}, + dictWord{133, 11, 497}, + dictWord{133, 0, 413}, + dictWord{7, 11, 1375}, + dictWord{7, 11, 1466}, + dictWord{138, 11, 331}, + dictWord{136, 0, 495}, + dictWord{6, 11, 540}, + dictWord{136, 11, 136}, + dictWord{7, 0, 54}, + dictWord{8, 0, 312}, + dictWord{10, 0, 191}, + dictWord{10, 0, 614}, + dictWord{140, 0, 567}, + dictWord{6, 0, 468}, + dictWord{7, 0, 567}, + dictWord{7, 0, 1478}, + dictWord{ + 8, + 0, + 530, + }, + dictWord{14, 0, 290}, + dictWord{133, 11, 999}, + dictWord{4, 11, 299}, + dictWord{7, 10, 306}, + dictWord{135, 11, 1004}, + dictWord{142, 11, 296}, + dictWord{134, 0, 1484}, + dictWord{133, 10, 979}, + dictWord{6, 0, 609}, + dictWord{9, 0, 815}, + dictWord{12, 11, 137}, + dictWord{14, 11, 9}, + dictWord{14, 11, 24}, + dictWord{142, 11, 64}, + dictWord{133, 11, 456}, + dictWord{6, 0, 484}, + dictWord{135, 0, 822}, + dictWord{133, 10, 178}, + dictWord{136, 11, 180}, + dictWord{ + 132, + 11, + 755, + }, + dictWord{137, 0, 900}, + dictWord{135, 0, 1335}, + dictWord{6, 0, 1724}, + dictWord{135, 0, 2022}, + dictWord{135, 11, 1139}, + dictWord{5, 0, 640}, + dictWord{132, 10, 390}, + dictWord{6, 0, 1831}, + dictWord{138, 11, 633}, + dictWord{135, 11, 566}, + dictWord{4, 11, 890}, + dictWord{5, 11, 805}, + dictWord{5, 11, 819}, + dictWord{5, 11, 961}, + dictWord{6, 11, 396}, + dictWord{6, 11, 1631}, + dictWord{6, 11, 1678}, + dictWord{7, 11, 1967}, + dictWord{7, 11, 2041}, + dictWord{ + 9, + 11, + 630, + }, + dictWord{11, 11, 8}, + dictWord{11, 11, 1019}, + dictWord{12, 11, 176}, + dictWord{13, 11, 225}, + dictWord{14, 11, 292}, + dictWord{149, 11, 24}, + dictWord{ + 132, + 0, + 474, + }, + dictWord{134, 0, 1103}, + dictWord{135, 0, 1504}, + dictWord{134, 0, 1576}, + dictWord{6, 0, 961}, + dictWord{6, 0, 1034}, + dictWord{140, 0, 655}, + dictWord{11, 11, 514}, + dictWord{149, 11, 20}, + dictWord{5, 0, 305}, + dictWord{135, 11, 1815}, + dictWord{7, 11, 1505}, + dictWord{10, 11, 190}, + dictWord{ + 10, + 11, + 634, + }, + dictWord{11, 11, 792}, + dictWord{12, 11, 358}, + dictWord{140, 11, 447}, + dictWord{5, 11, 0}, + dictWord{6, 11, 536}, + dictWord{7, 11, 604}, + dictWord{ + 13, + 11, + 445, + }, + dictWord{145, 11, 126}, + dictWord{7, 0, 1236}, + dictWord{133, 10, 105}, + dictWord{4, 0, 480}, + dictWord{6, 0, 217}, + dictWord{6, 0, 302}, + dictWord{ + 6, + 0, + 1642, + }, + dictWord{7, 0, 130}, + dictWord{7, 0, 837}, + dictWord{7, 0, 1321}, + dictWord{7, 0, 1547}, + dictWord{7, 0, 1657}, + dictWord{8, 0, 429}, + dictWord{9, 0, 228}, + dictWord{13, 0, 289}, + dictWord{13, 0, 343}, + dictWord{19, 0, 101}, + dictWord{6, 11, 232}, + dictWord{6, 11, 412}, + dictWord{7, 11, 1074}, + dictWord{8, 11, 9}, + dictWord{ + 8, + 11, + 157, + }, + dictWord{8, 11, 786}, + dictWord{9, 11, 196}, + dictWord{9, 11, 352}, + dictWord{9, 11, 457}, + dictWord{10, 11, 337}, + dictWord{11, 11, 232}, + dictWord{ + 11, + 11, + 877, + }, + dictWord{12, 11, 480}, + dictWord{140, 11, 546}, + dictWord{5, 10, 438}, + dictWord{7, 11, 958}, + dictWord{9, 10, 694}, + dictWord{12, 10, 627}, + dictWord{ + 13, + 11, + 38, + }, + dictWord{141, 10, 210}, + dictWord{4, 11, 382}, + dictWord{136, 11, 579}, + dictWord{7, 0, 278}, + dictWord{10, 0, 739}, + dictWord{11, 0, 708}, + dictWord{ + 141, + 0, + 348, + }, + dictWord{4, 11, 212}, + dictWord{135, 11, 1206}, + dictWord{135, 11, 1898}, + dictWord{6, 0, 708}, + dictWord{6, 0, 1344}, + dictWord{152, 10, 11}, + dictWord{137, 11, 768}, + dictWord{134, 0, 1840}, + dictWord{140, 0, 233}, + dictWord{8, 10, 25}, + dictWord{138, 10, 826}, + dictWord{6, 0, 2017}, + dictWord{ + 133, + 11, + 655, + }, + dictWord{6, 0, 1488}, + dictWord{139, 11, 290}, + dictWord{132, 10, 308}, + dictWord{134, 0, 1590}, + dictWord{134, 0, 1800}, + dictWord{134, 0, 1259}, + dictWord{16, 0, 28}, + dictWord{6, 11, 231}, + dictWord{7, 11, 95}, + dictWord{136, 11, 423}, + dictWord{133, 11, 300}, + dictWord{135, 10, 150}, + dictWord{ + 136, + 10, + 649, + }, + dictWord{7, 11, 1874}, + dictWord{137, 11, 641}, + dictWord{6, 11, 237}, + dictWord{7, 11, 611}, + dictWord{8, 11, 100}, + dictWord{9, 11, 416}, + dictWord{ + 11, + 11, + 335, + }, + dictWord{12, 11, 173}, + dictWord{146, 11, 101}, + dictWord{137, 0, 45}, + dictWord{134, 10, 521}, + dictWord{17, 0, 36}, + dictWord{14, 11, 26}, + dictWord{ + 146, + 11, + 150, + }, + dictWord{7, 0, 1442}, + dictWord{14, 0, 22}, + dictWord{5, 10, 339}, + dictWord{15, 10, 41}, + dictWord{15, 10, 166}, + dictWord{147, 10, 66}, + dictWord{ + 8, + 0, + 378, + }, + dictWord{6, 11, 581}, + dictWord{135, 11, 1119}, + dictWord{134, 0, 1507}, + dictWord{147, 11, 117}, + dictWord{139, 0, 39}, + dictWord{134, 0, 1054}, + dictWord{6, 0, 363}, + dictWord{7, 0, 1955}, + dictWord{136, 0, 725}, + dictWord{134, 0, 2036}, + dictWord{133, 11, 199}, + dictWord{6, 0, 1871}, + dictWord{9, 0, 935}, + dictWord{9, 0, 961}, + dictWord{9, 0, 1004}, + dictWord{9, 0, 1016}, + dictWord{12, 0, 805}, + dictWord{12, 0, 852}, + dictWord{12, 0, 853}, + dictWord{12, 0, 869}, + dictWord{ + 12, + 0, + 882, + }, + dictWord{12, 0, 896}, + dictWord{12, 0, 906}, + dictWord{12, 0, 917}, + dictWord{12, 0, 940}, + dictWord{15, 0, 170}, + dictWord{15, 0, 176}, + dictWord{ + 15, + 0, + 188, + }, + dictWord{15, 0, 201}, + dictWord{15, 0, 205}, + dictWord{15, 0, 212}, + dictWord{15, 0, 234}, + dictWord{15, 0, 244}, + dictWord{18, 0, 181}, + dictWord{18, 0, 193}, + dictWord{18, 0, 196}, + dictWord{18, 0, 201}, + dictWord{18, 0, 202}, + dictWord{18, 0, 210}, + dictWord{18, 0, 217}, + dictWord{18, 0, 235}, + dictWord{18, 0, 236}, + dictWord{18, 0, 237}, + dictWord{21, 0, 54}, + dictWord{21, 0, 55}, + dictWord{21, 0, 58}, + dictWord{21, 0, 59}, + dictWord{152, 0, 22}, + dictWord{134, 10, 1628}, + dictWord{ + 137, + 0, + 805, + }, + dictWord{5, 0, 813}, + dictWord{135, 0, 2046}, + dictWord{142, 11, 42}, + dictWord{5, 0, 712}, + dictWord{6, 0, 1240}, + dictWord{11, 0, 17}, + dictWord{ + 13, + 0, + 321, + }, + dictWord{144, 0, 67}, + dictWord{132, 0, 617}, + dictWord{135, 10, 829}, + dictWord{6, 0, 320}, + dictWord{7, 0, 781}, + dictWord{7, 0, 1921}, + dictWord{9, 0, 55}, + dictWord{10, 0, 186}, + dictWord{10, 0, 273}, + dictWord{10, 0, 664}, + dictWord{10, 0, 801}, + dictWord{11, 0, 996}, + dictWord{11, 0, 997}, + dictWord{13, 0, 157}, + dictWord{142, 0, 170}, + dictWord{136, 0, 271}, + dictWord{5, 10, 486}, + dictWord{135, 10, 1349}, + dictWord{18, 11, 91}, + dictWord{147, 11, 70}, + dictWord{10, 0, 445}, + dictWord{7, 10, 1635}, + dictWord{8, 10, 17}, + dictWord{138, 10, 295}, + dictWord{136, 11, 404}, + dictWord{7, 0, 103}, + dictWord{7, 0, 863}, + dictWord{11, 0, 184}, + dictWord{145, 0, 62}, + dictWord{138, 10, 558}, + dictWord{137, 0, 659}, + dictWord{6, 11, 312}, + dictWord{6, 11, 1715}, + dictWord{10, 11, 584}, + dictWord{ + 11, + 11, + 546, + }, + dictWord{11, 11, 692}, + dictWord{12, 11, 259}, + dictWord{12, 11, 295}, + dictWord{13, 11, 46}, + dictWord{141, 11, 154}, + dictWord{134, 0, 676}, + dictWord{132, 11, 588}, + dictWord{4, 11, 231}, + dictWord{5, 11, 61}, + dictWord{6, 11, 104}, + dictWord{7, 11, 729}, + dictWord{7, 11, 964}, + dictWord{7, 11, 1658}, + dictWord{140, 11, 414}, + dictWord{6, 11, 263}, + dictWord{138, 11, 757}, + dictWord{11, 0, 337}, + dictWord{142, 0, 303}, + dictWord{135, 11, 1363}, + dictWord{ + 132, + 11, + 320, + }, + dictWord{140, 0, 506}, + dictWord{134, 10, 447}, + dictWord{5, 0, 77}, + dictWord{7, 0, 1455}, + dictWord{10, 0, 843}, + dictWord{147, 0, 73}, + dictWord{ + 7, + 10, + 577, + }, + dictWord{7, 10, 1432}, + dictWord{9, 10, 475}, + dictWord{9, 10, 505}, + dictWord{9, 10, 526}, + dictWord{9, 10, 609}, + dictWord{9, 10, 689}, + dictWord{ + 9, + 10, + 726, + }, + dictWord{9, 10, 735}, + dictWord{9, 10, 738}, + dictWord{10, 10, 556}, + dictWord{10, 10, 674}, + dictWord{10, 10, 684}, + dictWord{11, 10, 89}, + dictWord{ + 11, + 10, + 202, + }, + dictWord{11, 10, 272}, + dictWord{11, 10, 380}, + dictWord{11, 10, 415}, + dictWord{11, 10, 505}, + dictWord{11, 10, 537}, + dictWord{11, 10, 550}, + dictWord{11, 10, 562}, + dictWord{11, 10, 640}, + dictWord{11, 10, 667}, + dictWord{11, 10, 688}, + dictWord{11, 10, 847}, + dictWord{11, 10, 927}, + dictWord{ + 11, + 10, + 930, + }, + dictWord{11, 10, 940}, + dictWord{12, 10, 144}, + dictWord{12, 10, 325}, + dictWord{12, 10, 329}, + dictWord{12, 10, 389}, + dictWord{12, 10, 403}, + dictWord{ + 12, + 10, + 451, + }, + dictWord{12, 10, 515}, + dictWord{12, 10, 604}, + dictWord{12, 10, 616}, + dictWord{12, 10, 626}, + dictWord{13, 10, 66}, + dictWord{13, 10, 131}, + dictWord{13, 10, 167}, + dictWord{13, 10, 236}, + dictWord{13, 10, 368}, + dictWord{13, 10, 411}, + dictWord{13, 10, 434}, + dictWord{13, 10, 453}, + dictWord{ + 13, + 10, + 461, + }, + dictWord{13, 10, 474}, + dictWord{14, 10, 59}, + dictWord{14, 10, 60}, + dictWord{14, 10, 139}, + dictWord{14, 10, 152}, + dictWord{14, 10, 276}, + dictWord{ + 14, + 10, + 353, + }, + dictWord{14, 10, 402}, + dictWord{15, 10, 28}, + dictWord{15, 10, 81}, + dictWord{15, 10, 123}, + dictWord{15, 10, 152}, + dictWord{18, 10, 136}, + dictWord{148, 10, 88}, + dictWord{132, 0, 458}, + dictWord{135, 0, 1420}, + dictWord{6, 0, 109}, + dictWord{10, 0, 382}, + dictWord{4, 11, 405}, + dictWord{4, 10, 609}, + dictWord{7, 10, 756}, + dictWord{7, 11, 817}, + dictWord{9, 10, 544}, + dictWord{11, 10, 413}, + dictWord{14, 11, 58}, + dictWord{14, 10, 307}, + dictWord{16, 10, 25}, + dictWord{17, 11, 37}, + dictWord{146, 11, 124}, + dictWord{6, 0, 330}, + dictWord{7, 0, 1084}, + dictWord{11, 0, 142}, + dictWord{133, 11, 974}, + dictWord{4, 10, 930}, + dictWord{133, 10, 947}, + dictWord{5, 10, 939}, + dictWord{142, 11, 394}, + dictWord{16, 0, 91}, + dictWord{145, 0, 87}, + dictWord{5, 11, 235}, + dictWord{5, 10, 962}, + dictWord{7, 11, 1239}, + dictWord{11, 11, 131}, + dictWord{140, 11, 370}, + dictWord{11, 0, 492}, + dictWord{5, 10, 651}, + dictWord{8, 10, 170}, + dictWord{9, 10, 61}, + dictWord{9, 10, 63}, + dictWord{10, 10, 23}, + dictWord{10, 10, 37}, + dictWord{10, 10, 834}, + dictWord{11, 10, 4}, + dictWord{11, 10, 281}, + dictWord{11, 10, 503}, + dictWord{ + 11, + 10, + 677, + }, + dictWord{12, 10, 96}, + dictWord{12, 10, 130}, + dictWord{12, 10, 244}, + dictWord{14, 10, 5}, + dictWord{14, 10, 40}, + dictWord{14, 10, 162}, + dictWord{ + 14, + 10, + 202, + }, + dictWord{146, 10, 133}, + dictWord{4, 10, 406}, + dictWord{5, 10, 579}, + dictWord{12, 10, 492}, + dictWord{150, 10, 15}, + dictWord{9, 11, 137}, + dictWord{138, 11, 221}, + dictWord{134, 0, 1239}, + dictWord{11, 0, 211}, + dictWord{140, 0, 145}, + dictWord{7, 11, 390}, + dictWord{138, 11, 140}, + dictWord{ + 135, + 11, + 1418, + }, + dictWord{135, 11, 1144}, + dictWord{134, 0, 1049}, + dictWord{7, 0, 321}, + dictWord{6, 10, 17}, + dictWord{7, 10, 1001}, + dictWord{7, 10, 1982}, + dictWord{ + 9, + 10, + 886, + }, + dictWord{10, 10, 489}, + dictWord{10, 10, 800}, + dictWord{11, 10, 782}, + dictWord{12, 10, 320}, + dictWord{13, 10, 467}, + dictWord{14, 10, 145}, + dictWord{14, 10, 387}, + dictWord{143, 10, 119}, + dictWord{145, 10, 17}, + dictWord{5, 11, 407}, + dictWord{11, 11, 489}, + dictWord{19, 11, 37}, + dictWord{20, 11, 73}, + dictWord{150, 11, 38}, + dictWord{133, 10, 458}, + dictWord{135, 0, 1985}, + dictWord{7, 10, 1983}, + dictWord{8, 10, 0}, + dictWord{8, 10, 171}, + dictWord{ + 9, + 10, + 120, + }, + dictWord{9, 10, 732}, + dictWord{10, 10, 473}, + dictWord{11, 10, 656}, + dictWord{11, 10, 998}, + dictWord{18, 10, 0}, + dictWord{18, 10, 2}, + dictWord{ + 147, + 10, + 21, + }, + dictWord{5, 11, 325}, + dictWord{7, 11, 1483}, + dictWord{8, 11, 5}, + dictWord{8, 11, 227}, + dictWord{9, 11, 105}, + dictWord{10, 11, 585}, + dictWord{ + 140, + 11, + 614, + }, + dictWord{136, 0, 122}, + dictWord{132, 0, 234}, + dictWord{135, 11, 1196}, + dictWord{6, 0, 976}, + dictWord{6, 0, 1098}, + dictWord{134, 0, 1441}, + dictWord{ + 7, + 0, + 253, + }, + dictWord{136, 0, 549}, + dictWord{6, 11, 621}, + dictWord{13, 11, 504}, + dictWord{144, 11, 19}, + dictWord{132, 10, 519}, + dictWord{5, 0, 430}, + dictWord{ + 5, + 0, + 932, + }, + dictWord{6, 0, 131}, + dictWord{7, 0, 417}, + dictWord{9, 0, 522}, + dictWord{11, 0, 314}, + dictWord{141, 0, 390}, + dictWord{14, 0, 149}, + dictWord{14, 0, 399}, + dictWord{143, 0, 57}, + dictWord{5, 10, 907}, + dictWord{6, 10, 31}, + dictWord{6, 11, 218}, + dictWord{7, 10, 491}, + dictWord{7, 10, 530}, + dictWord{8, 10, 592}, + dictWord{11, 10, 53}, + dictWord{11, 10, 779}, + dictWord{12, 10, 167}, + dictWord{12, 10, 411}, + dictWord{14, 10, 14}, + dictWord{14, 10, 136}, + dictWord{15, 10, 72}, + dictWord{16, 10, 17}, + dictWord{144, 10, 72}, + dictWord{140, 11, 330}, + dictWord{7, 11, 454}, + dictWord{7, 11, 782}, + dictWord{136, 11, 768}, + dictWord{ + 132, + 0, + 507, + }, + dictWord{10, 11, 676}, + dictWord{140, 11, 462}, + dictWord{6, 0, 630}, + dictWord{9, 0, 811}, + dictWord{4, 10, 208}, + dictWord{5, 10, 106}, + dictWord{ + 6, + 10, + 531, + }, + dictWord{8, 10, 408}, + dictWord{9, 10, 188}, + dictWord{138, 10, 572}, + dictWord{4, 0, 343}, + dictWord{5, 0, 511}, + dictWord{134, 10, 1693}, + dictWord{ + 134, + 11, + 164, + }, + dictWord{132, 0, 448}, + dictWord{7, 0, 455}, + dictWord{138, 0, 591}, + dictWord{135, 0, 1381}, + dictWord{12, 10, 441}, + dictWord{150, 11, 50}, + dictWord{9, 10, 449}, + dictWord{10, 10, 192}, + dictWord{138, 10, 740}, + dictWord{6, 0, 575}, + dictWord{132, 10, 241}, + dictWord{134, 0, 1175}, + dictWord{ + 134, + 0, + 653, + }, + dictWord{134, 0, 1761}, + dictWord{134, 0, 1198}, + dictWord{132, 10, 259}, + dictWord{6, 11, 343}, + dictWord{7, 11, 195}, + dictWord{9, 11, 226}, + dictWord{ + 10, + 11, + 197, + }, + dictWord{10, 11, 575}, + dictWord{11, 11, 502}, + dictWord{139, 11, 899}, + dictWord{7, 0, 1127}, + dictWord{7, 0, 1572}, + dictWord{10, 0, 297}, + dictWord{10, 0, 422}, + dictWord{11, 0, 764}, + dictWord{11, 0, 810}, + dictWord{12, 0, 264}, + dictWord{13, 0, 102}, + dictWord{13, 0, 300}, + dictWord{13, 0, 484}, + dictWord{ + 14, + 0, + 147, + }, + dictWord{14, 0, 229}, + dictWord{17, 0, 71}, + dictWord{18, 0, 118}, + dictWord{147, 0, 120}, + dictWord{135, 11, 666}, + dictWord{132, 0, 678}, + dictWord{ + 4, + 10, + 173, + }, + dictWord{5, 10, 312}, + dictWord{5, 10, 512}, + dictWord{135, 10, 1285}, + dictWord{7, 10, 1603}, + dictWord{7, 10, 1691}, + dictWord{9, 10, 464}, + dictWord{11, 10, 195}, + dictWord{12, 10, 279}, + dictWord{12, 10, 448}, + dictWord{14, 10, 11}, + dictWord{147, 10, 102}, + dictWord{16, 0, 99}, + dictWord{146, 0, 164}, + dictWord{7, 11, 1125}, + dictWord{9, 11, 143}, + dictWord{11, 11, 61}, + dictWord{14, 11, 405}, + dictWord{150, 11, 21}, + dictWord{137, 11, 260}, + dictWord{ + 4, + 10, + 452, + }, + dictWord{5, 10, 583}, + dictWord{5, 10, 817}, + dictWord{6, 10, 433}, + dictWord{7, 10, 593}, + dictWord{7, 10, 720}, + dictWord{7, 10, 1378}, + dictWord{ + 8, + 10, + 161, + }, + dictWord{9, 10, 284}, + dictWord{10, 10, 313}, + dictWord{139, 10, 886}, + dictWord{132, 10, 547}, + dictWord{136, 10, 722}, + dictWord{14, 0, 35}, + dictWord{142, 0, 191}, + dictWord{141, 0, 45}, + dictWord{138, 0, 121}, + dictWord{132, 0, 125}, + dictWord{134, 0, 1622}, + dictWord{133, 11, 959}, + dictWord{ + 8, + 10, + 420, + }, + dictWord{139, 10, 193}, + dictWord{132, 0, 721}, + dictWord{135, 10, 409}, + dictWord{136, 0, 145}, + dictWord{7, 0, 792}, + dictWord{8, 0, 147}, + dictWord{ + 10, + 0, + 821, + }, + dictWord{11, 0, 970}, + dictWord{11, 0, 1021}, + dictWord{136, 11, 173}, + dictWord{134, 11, 266}, + dictWord{132, 0, 715}, + dictWord{7, 0, 1999}, + dictWord{138, 10, 308}, + dictWord{133, 0, 531}, + dictWord{5, 0, 168}, + dictWord{5, 0, 930}, + dictWord{8, 0, 74}, + dictWord{9, 0, 623}, + dictWord{12, 0, 500}, + dictWord{ + 140, + 0, + 579, + }, + dictWord{144, 0, 65}, + dictWord{138, 11, 246}, + dictWord{6, 0, 220}, + dictWord{7, 0, 1101}, + dictWord{13, 0, 105}, + dictWord{142, 11, 314}, + dictWord{ + 5, + 10, + 1002, + }, + dictWord{136, 10, 745}, + dictWord{134, 0, 960}, + dictWord{20, 0, 0}, + dictWord{148, 11, 0}, + dictWord{4, 0, 1005}, + dictWord{4, 10, 239}, + dictWord{ + 6, + 10, + 477, + }, + dictWord{7, 10, 1607}, + dictWord{11, 10, 68}, + dictWord{139, 10, 617}, + dictWord{6, 0, 19}, + dictWord{7, 0, 1413}, + dictWord{139, 0, 428}, + dictWord{ + 149, + 10, + 13, + }, + dictWord{7, 0, 96}, + dictWord{8, 0, 401}, + dictWord{8, 0, 703}, + dictWord{9, 0, 896}, + dictWord{136, 11, 300}, + dictWord{134, 0, 1595}, + dictWord{145, 0, 116}, + dictWord{136, 0, 1021}, + dictWord{7, 0, 1961}, + dictWord{7, 0, 1965}, + dictWord{7, 0, 2030}, + dictWord{8, 0, 150}, + dictWord{8, 0, 702}, + dictWord{8, 0, 737}, + dictWord{ + 8, + 0, + 750, + }, + dictWord{140, 0, 366}, + dictWord{11, 11, 75}, + dictWord{142, 11, 267}, + dictWord{132, 10, 367}, + dictWord{8, 0, 800}, + dictWord{9, 0, 148}, + dictWord{ + 9, + 0, + 872, + }, + dictWord{9, 0, 890}, + dictWord{11, 0, 309}, + dictWord{11, 0, 1001}, + dictWord{13, 0, 267}, + dictWord{13, 0, 323}, + dictWord{5, 11, 427}, + dictWord{ + 5, + 11, + 734, + }, + dictWord{7, 11, 478}, + dictWord{136, 11, 52}, + dictWord{7, 11, 239}, + dictWord{11, 11, 217}, + dictWord{142, 11, 165}, + dictWord{132, 11, 323}, + dictWord{140, 11, 419}, + dictWord{13, 0, 299}, + dictWord{142, 0, 75}, + dictWord{6, 11, 87}, + dictWord{6, 11, 1734}, + dictWord{7, 11, 20}, + dictWord{7, 11, 1056}, + dictWord{ + 8, + 11, + 732, + }, + dictWord{9, 11, 406}, + dictWord{9, 11, 911}, + dictWord{138, 11, 694}, + dictWord{134, 0, 1383}, + dictWord{132, 10, 694}, + dictWord{ + 133, + 11, + 613, + }, + dictWord{137, 0, 779}, + dictWord{4, 0, 598}, + dictWord{140, 10, 687}, + dictWord{6, 0, 970}, + dictWord{135, 0, 424}, + dictWord{133, 0, 547}, + dictWord{ + 7, + 11, + 32, + }, + dictWord{7, 11, 984}, + dictWord{8, 11, 85}, + dictWord{8, 11, 709}, + dictWord{9, 11, 579}, + dictWord{9, 11, 847}, + dictWord{9, 11, 856}, + dictWord{10, 11, 799}, + dictWord{11, 11, 258}, + dictWord{11, 11, 1007}, + dictWord{12, 11, 331}, + dictWord{12, 11, 615}, + dictWord{13, 11, 188}, + dictWord{13, 11, 435}, + dictWord{ + 14, + 11, + 8, + }, + dictWord{15, 11, 165}, + dictWord{16, 11, 27}, + dictWord{148, 11, 40}, + dictWord{6, 0, 1222}, + dictWord{134, 0, 1385}, + dictWord{132, 0, 876}, + dictWord{ + 138, + 11, + 151, + }, + dictWord{135, 10, 213}, + dictWord{4, 11, 167}, + dictWord{135, 11, 82}, + dictWord{133, 0, 133}, + dictWord{6, 11, 24}, + dictWord{7, 11, 74}, + dictWord{ + 7, + 11, + 678, + }, + dictWord{137, 11, 258}, + dictWord{5, 11, 62}, + dictWord{6, 11, 534}, + dictWord{7, 11, 684}, + dictWord{7, 11, 1043}, + dictWord{7, 11, 1072}, + dictWord{ + 8, + 11, + 280, + }, + dictWord{8, 11, 541}, + dictWord{8, 11, 686}, + dictWord{10, 11, 519}, + dictWord{11, 11, 252}, + dictWord{140, 11, 282}, + dictWord{136, 0, 187}, + dictWord{8, 0, 8}, + dictWord{10, 0, 0}, + dictWord{10, 0, 818}, + dictWord{139, 0, 988}, + dictWord{132, 11, 359}, + dictWord{11, 0, 429}, + dictWord{15, 0, 51}, + dictWord{ + 135, + 10, + 1672, + }, + dictWord{136, 0, 685}, + dictWord{5, 11, 211}, + dictWord{7, 11, 88}, + dictWord{136, 11, 627}, + dictWord{134, 0, 472}, + dictWord{136, 0, 132}, + dictWord{ + 6, + 11, + 145, + }, + dictWord{141, 11, 336}, + dictWord{4, 10, 751}, + dictWord{11, 10, 390}, + dictWord{140, 10, 32}, + dictWord{6, 0, 938}, + dictWord{6, 0, 1060}, + dictWord{ + 4, + 11, + 263, + }, + dictWord{4, 10, 409}, + dictWord{133, 10, 78}, + dictWord{137, 0, 874}, + dictWord{8, 0, 774}, + dictWord{10, 0, 670}, + dictWord{12, 0, 51}, + dictWord{ + 4, + 11, + 916, + }, + dictWord{6, 10, 473}, + dictWord{7, 10, 1602}, + dictWord{10, 10, 698}, + dictWord{12, 10, 212}, + dictWord{13, 10, 307}, + dictWord{145, 10, 105}, + dictWord{146, 0, 92}, + dictWord{143, 10, 156}, + dictWord{132, 0, 830}, + dictWord{137, 0, 701}, + dictWord{4, 11, 599}, + dictWord{6, 11, 1634}, + dictWord{7, 11, 5}, + dictWord{7, 11, 55}, + dictWord{7, 11, 67}, + dictWord{7, 11, 97}, + dictWord{7, 11, 691}, + dictWord{7, 11, 979}, + dictWord{7, 11, 1697}, + dictWord{8, 11, 207}, + dictWord{ + 8, + 11, + 214, + }, + dictWord{8, 11, 231}, + dictWord{8, 11, 294}, + dictWord{8, 11, 336}, + dictWord{8, 11, 428}, + dictWord{8, 11, 451}, + dictWord{8, 11, 460}, + dictWord{8, 11, 471}, + dictWord{8, 11, 622}, + dictWord{8, 11, 626}, + dictWord{8, 11, 679}, + dictWord{8, 11, 759}, + dictWord{8, 11, 829}, + dictWord{9, 11, 11}, + dictWord{9, 11, 246}, + dictWord{ + 9, + 11, + 484, + }, + dictWord{9, 11, 573}, + dictWord{9, 11, 706}, + dictWord{9, 11, 762}, + dictWord{9, 11, 798}, + dictWord{9, 11, 855}, + dictWord{9, 11, 870}, + dictWord{ + 9, + 11, + 912, + }, + dictWord{10, 11, 303}, + dictWord{10, 11, 335}, + dictWord{10, 11, 424}, + dictWord{10, 11, 461}, + dictWord{10, 11, 543}, + dictWord{10, 11, 759}, + dictWord{10, 11, 814}, + dictWord{11, 11, 59}, + dictWord{11, 11, 199}, + dictWord{11, 11, 235}, + dictWord{11, 11, 475}, + dictWord{11, 11, 590}, + dictWord{11, 11, 929}, + dictWord{11, 11, 963}, + dictWord{12, 11, 114}, + dictWord{12, 11, 182}, + dictWord{12, 11, 226}, + dictWord{12, 11, 332}, + dictWord{12, 11, 439}, + dictWord{ + 12, + 11, + 575, + }, + dictWord{12, 11, 598}, + dictWord{13, 11, 8}, + dictWord{13, 11, 125}, + dictWord{13, 11, 194}, + dictWord{13, 11, 287}, + dictWord{14, 11, 197}, + dictWord{ + 14, + 11, + 383, + }, + dictWord{15, 11, 53}, + dictWord{17, 11, 63}, + dictWord{19, 11, 46}, + dictWord{19, 11, 98}, + dictWord{19, 11, 106}, + dictWord{148, 11, 85}, + dictWord{ + 4, + 0, + 127, + }, + dictWord{5, 0, 350}, + dictWord{6, 0, 356}, + dictWord{8, 0, 426}, + dictWord{9, 0, 572}, + dictWord{10, 0, 247}, + dictWord{139, 0, 312}, + dictWord{134, 0, 1215}, + dictWord{6, 0, 59}, + dictWord{9, 0, 603}, + dictWord{13, 0, 397}, + dictWord{7, 11, 1853}, + dictWord{138, 11, 437}, + dictWord{134, 0, 1762}, + dictWord{ + 147, + 11, + 126, + }, + dictWord{135, 10, 883}, + dictWord{13, 0, 293}, + dictWord{142, 0, 56}, + dictWord{133, 10, 617}, + dictWord{139, 10, 50}, + dictWord{5, 11, 187}, + dictWord{ + 7, + 10, + 1518, + }, + dictWord{139, 10, 694}, + dictWord{135, 0, 441}, + dictWord{6, 0, 111}, + dictWord{7, 0, 4}, + dictWord{8, 0, 163}, + dictWord{8, 0, 776}, + dictWord{ + 138, + 0, + 566, + }, + dictWord{132, 0, 806}, + dictWord{4, 11, 215}, + dictWord{9, 11, 38}, + dictWord{10, 11, 3}, + dictWord{11, 11, 23}, + dictWord{11, 11, 127}, + dictWord{ + 139, + 11, + 796, + }, + dictWord{14, 0, 233}, + dictWord{4, 10, 546}, + dictWord{135, 10, 2042}, + dictWord{135, 0, 1994}, + dictWord{134, 0, 1739}, + dictWord{135, 11, 1530}, + dictWord{136, 0, 393}, + dictWord{5, 0, 297}, + dictWord{7, 0, 1038}, + dictWord{14, 0, 359}, + dictWord{19, 0, 52}, + dictWord{148, 0, 47}, + dictWord{135, 0, 309}, + dictWord{ + 4, + 10, + 313, + }, + dictWord{133, 10, 577}, + dictWord{8, 10, 184}, + dictWord{141, 10, 433}, + dictWord{135, 10, 935}, + dictWord{12, 10, 186}, + dictWord{ + 12, + 10, + 292, + }, + dictWord{14, 10, 100}, + dictWord{146, 10, 70}, + dictWord{136, 0, 363}, + dictWord{14, 0, 175}, + dictWord{11, 10, 402}, + dictWord{12, 10, 109}, + dictWord{ + 12, + 10, + 431, + }, + dictWord{13, 10, 179}, + dictWord{13, 10, 206}, + dictWord{14, 10, 217}, + dictWord{16, 10, 3}, + dictWord{148, 10, 53}, + dictWord{5, 10, 886}, + dictWord{ + 6, + 10, + 46, + }, + dictWord{6, 10, 1790}, + dictWord{7, 10, 14}, + dictWord{7, 10, 732}, + dictWord{7, 10, 1654}, + dictWord{8, 10, 95}, + dictWord{8, 10, 327}, + dictWord{ + 8, + 10, + 616, + }, + dictWord{9, 10, 892}, + dictWord{10, 10, 598}, + dictWord{10, 10, 769}, + dictWord{11, 10, 134}, + dictWord{11, 10, 747}, + dictWord{12, 10, 378}, + dictWord{ + 142, + 10, + 97, + }, + dictWord{136, 0, 666}, + dictWord{135, 0, 1675}, + dictWord{6, 0, 655}, + dictWord{134, 0, 1600}, + dictWord{135, 0, 808}, + dictWord{133, 10, 1021}, + dictWord{4, 11, 28}, + dictWord{5, 11, 440}, + dictWord{7, 11, 248}, + dictWord{11, 11, 833}, + dictWord{140, 11, 344}, + dictWord{134, 11, 1654}, + dictWord{ + 132, + 0, + 280, + }, + dictWord{140, 0, 54}, + dictWord{4, 0, 421}, + dictWord{133, 0, 548}, + dictWord{132, 10, 153}, + dictWord{6, 11, 339}, + dictWord{135, 11, 923}, + dictWord{ + 133, + 11, + 853, + }, + dictWord{133, 10, 798}, + dictWord{132, 10, 587}, + dictWord{6, 11, 249}, + dictWord{7, 11, 1234}, + dictWord{139, 11, 573}, + dictWord{6, 10, 598}, + dictWord{7, 10, 42}, + dictWord{8, 10, 695}, + dictWord{10, 10, 212}, + dictWord{11, 10, 158}, + dictWord{14, 10, 196}, + dictWord{145, 10, 85}, + dictWord{7, 0, 249}, + dictWord{5, 10, 957}, + dictWord{133, 10, 1008}, + dictWord{4, 10, 129}, + dictWord{135, 10, 465}, + dictWord{6, 0, 254}, + dictWord{7, 0, 842}, + dictWord{7, 0, 1659}, + dictWord{9, 0, 109}, + dictWord{10, 0, 103}, + dictWord{7, 10, 908}, + dictWord{7, 10, 1201}, + dictWord{9, 10, 755}, + dictWord{11, 10, 906}, + dictWord{12, 10, 527}, + dictWord{146, 10, 7}, + dictWord{5, 0, 262}, + dictWord{136, 10, 450}, + dictWord{144, 0, 1}, + dictWord{10, 11, 201}, + dictWord{142, 11, 319}, + dictWord{7, 11, 49}, + dictWord{ + 7, + 11, + 392, + }, + dictWord{8, 11, 20}, + dictWord{8, 11, 172}, + dictWord{8, 11, 690}, + dictWord{9, 11, 383}, + dictWord{9, 11, 845}, + dictWord{10, 11, 48}, + dictWord{ + 11, + 11, + 293, + }, + dictWord{11, 11, 832}, + dictWord{11, 11, 920}, + dictWord{141, 11, 221}, + dictWord{5, 11, 858}, + dictWord{133, 11, 992}, + dictWord{134, 0, 805}, + dictWord{139, 10, 1003}, + dictWord{6, 0, 1630}, + dictWord{134, 11, 307}, + dictWord{7, 11, 1512}, + dictWord{135, 11, 1794}, + dictWord{6, 11, 268}, + dictWord{ + 137, + 11, + 62, + }, + dictWord{135, 10, 1868}, + dictWord{133, 0, 671}, + dictWord{4, 0, 989}, + dictWord{8, 0, 972}, + dictWord{136, 0, 998}, + dictWord{132, 11, 423}, + dictWord{132, 0, 889}, + dictWord{135, 0, 1382}, + dictWord{135, 0, 1910}, + dictWord{7, 10, 965}, + dictWord{7, 10, 1460}, + dictWord{135, 10, 1604}, + dictWord{ + 4, + 0, + 627, + }, + dictWord{5, 0, 775}, + dictWord{138, 11, 106}, + dictWord{134, 11, 348}, + dictWord{7, 0, 202}, + dictWord{11, 0, 362}, + dictWord{11, 0, 948}, + dictWord{ + 140, + 0, + 388, + }, + dictWord{138, 11, 771}, + dictWord{6, 11, 613}, + dictWord{136, 11, 223}, + dictWord{6, 0, 560}, + dictWord{7, 0, 451}, + dictWord{8, 0, 389}, + dictWord{ + 12, + 0, + 490, + }, + dictWord{13, 0, 16}, + dictWord{13, 0, 215}, + dictWord{13, 0, 351}, + dictWord{18, 0, 132}, + dictWord{147, 0, 125}, + dictWord{135, 0, 841}, + dictWord{ + 136, + 0, + 566, + }, + dictWord{136, 0, 938}, + dictWord{132, 11, 670}, + dictWord{5, 0, 912}, + dictWord{6, 0, 1695}, + dictWord{140, 11, 55}, + dictWord{9, 11, 40}, + dictWord{ + 139, + 11, + 136, + }, + dictWord{7, 0, 1361}, + dictWord{7, 10, 982}, + dictWord{10, 10, 32}, + dictWord{143, 10, 56}, + dictWord{11, 11, 259}, + dictWord{140, 11, 270}, + dictWord{ + 5, + 0, + 236, + }, + dictWord{6, 0, 572}, + dictWord{8, 0, 492}, + dictWord{11, 0, 618}, + dictWord{144, 0, 56}, + dictWord{8, 11, 572}, + dictWord{9, 11, 310}, + dictWord{9, 11, 682}, + dictWord{137, 11, 698}, + dictWord{134, 0, 1854}, + dictWord{5, 0, 190}, + dictWord{136, 0, 318}, + dictWord{133, 10, 435}, + dictWord{135, 0, 1376}, + dictWord{ + 4, + 11, + 296, + }, + dictWord{6, 11, 352}, + dictWord{7, 11, 401}, + dictWord{7, 11, 1410}, + dictWord{7, 11, 1594}, + dictWord{7, 11, 1674}, + dictWord{8, 11, 63}, + dictWord{ + 8, + 11, + 660, + }, + dictWord{137, 11, 74}, + dictWord{7, 0, 349}, + dictWord{5, 10, 85}, + dictWord{6, 10, 419}, + dictWord{7, 10, 305}, + dictWord{7, 10, 361}, + dictWord{7, 10, 1337}, + dictWord{8, 10, 71}, + dictWord{140, 10, 519}, + dictWord{4, 11, 139}, + dictWord{4, 11, 388}, + dictWord{140, 11, 188}, + dictWord{6, 0, 1972}, + dictWord{6, 0, 2013}, + dictWord{8, 0, 951}, + dictWord{10, 0, 947}, + dictWord{10, 0, 974}, + dictWord{10, 0, 1018}, + dictWord{142, 0, 476}, + dictWord{140, 10, 688}, + dictWord{ + 135, + 10, + 740, + }, + dictWord{5, 10, 691}, + dictWord{7, 10, 345}, + dictWord{9, 10, 94}, + dictWord{140, 10, 169}, + dictWord{9, 0, 344}, + dictWord{5, 10, 183}, + dictWord{6, 10, 582}, + dictWord{10, 10, 679}, + dictWord{140, 10, 435}, + dictWord{135, 10, 511}, + dictWord{132, 0, 850}, + dictWord{8, 11, 441}, + dictWord{10, 11, 314}, + dictWord{ + 143, + 11, + 3, + }, + dictWord{7, 10, 1993}, + dictWord{136, 10, 684}, + dictWord{4, 11, 747}, + dictWord{6, 11, 290}, + dictWord{6, 10, 583}, + dictWord{7, 11, 649}, + dictWord{ + 7, + 11, + 1479, + }, + dictWord{135, 11, 1583}, + dictWord{133, 11, 232}, + dictWord{133, 10, 704}, + dictWord{134, 0, 910}, + dictWord{4, 10, 179}, + dictWord{5, 10, 198}, + dictWord{133, 10, 697}, + dictWord{7, 10, 347}, + dictWord{7, 10, 971}, + dictWord{8, 10, 181}, + dictWord{138, 10, 711}, + dictWord{136, 11, 525}, + dictWord{ + 14, + 0, + 19, + }, + dictWord{14, 0, 28}, + dictWord{144, 0, 29}, + dictWord{7, 0, 85}, + dictWord{7, 0, 247}, + dictWord{8, 0, 585}, + dictWord{138, 0, 163}, + dictWord{4, 0, 487}, + dictWord{ + 7, + 11, + 472, + }, + dictWord{7, 11, 1801}, + dictWord{10, 11, 748}, + dictWord{141, 11, 458}, + dictWord{4, 10, 243}, + dictWord{5, 10, 203}, + dictWord{7, 10, 19}, + dictWord{ + 7, + 10, + 71, + }, + dictWord{7, 10, 113}, + dictWord{10, 10, 405}, + dictWord{11, 10, 357}, + dictWord{142, 10, 240}, + dictWord{7, 10, 1450}, + dictWord{139, 10, 99}, + dictWord{132, 11, 425}, + dictWord{138, 0, 145}, + dictWord{147, 0, 83}, + dictWord{6, 10, 492}, + dictWord{137, 11, 247}, + dictWord{4, 0, 1013}, + dictWord{ + 134, + 0, + 2033, + }, + dictWord{5, 10, 134}, + dictWord{6, 10, 408}, + dictWord{6, 10, 495}, + dictWord{135, 10, 1593}, + dictWord{135, 0, 1922}, + dictWord{134, 11, 1768}, + dictWord{4, 0, 124}, + dictWord{10, 0, 457}, + dictWord{11, 0, 121}, + dictWord{11, 0, 169}, + dictWord{11, 0, 870}, + dictWord{11, 0, 874}, + dictWord{12, 0, 214}, + dictWord{ + 14, + 0, + 187, + }, + dictWord{143, 0, 77}, + dictWord{5, 0, 557}, + dictWord{135, 0, 1457}, + dictWord{139, 0, 66}, + dictWord{5, 11, 943}, + dictWord{6, 11, 1779}, + dictWord{ + 142, + 10, + 4, + }, + dictWord{4, 10, 248}, + dictWord{4, 10, 665}, + dictWord{7, 10, 137}, + dictWord{137, 10, 349}, + dictWord{7, 0, 1193}, + dictWord{5, 11, 245}, + dictWord{ + 6, + 11, + 576, + }, + dictWord{7, 11, 582}, + dictWord{136, 11, 225}, + dictWord{144, 0, 82}, + dictWord{7, 10, 1270}, + dictWord{139, 10, 612}, + dictWord{5, 0, 454}, + dictWord{ + 10, + 0, + 352, + }, + dictWord{138, 11, 352}, + dictWord{18, 0, 57}, + dictWord{5, 10, 371}, + dictWord{135, 10, 563}, + dictWord{135, 0, 1333}, + dictWord{6, 0, 107}, + dictWord{ + 7, + 0, + 638, + }, + dictWord{7, 0, 1632}, + dictWord{9, 0, 396}, + dictWord{134, 11, 610}, + dictWord{5, 0, 370}, + dictWord{134, 0, 1756}, + dictWord{4, 10, 374}, + dictWord{ + 7, + 10, + 547, + }, + dictWord{7, 10, 1700}, + dictWord{7, 10, 1833}, + dictWord{139, 10, 858}, + dictWord{133, 0, 204}, + dictWord{6, 0, 1305}, + dictWord{9, 10, 311}, + dictWord{ + 141, + 10, + 42, + }, + dictWord{5, 0, 970}, + dictWord{134, 0, 1706}, + dictWord{6, 10, 1647}, + dictWord{7, 10, 1552}, + dictWord{7, 10, 2010}, + dictWord{9, 10, 494}, + dictWord{137, 10, 509}, + dictWord{13, 11, 455}, + dictWord{15, 11, 99}, + dictWord{15, 11, 129}, + dictWord{144, 11, 68}, + dictWord{135, 0, 3}, + dictWord{4, 0, 35}, + dictWord{ + 5, + 0, + 121, + }, + dictWord{5, 0, 483}, + dictWord{5, 0, 685}, + dictWord{6, 0, 489}, + dictWord{6, 0, 782}, + dictWord{6, 0, 1032}, + dictWord{7, 0, 1204}, + dictWord{136, 0, 394}, + dictWord{4, 0, 921}, + dictWord{133, 0, 1007}, + dictWord{8, 11, 360}, + dictWord{138, 11, 63}, + dictWord{135, 0, 1696}, + dictWord{134, 0, 1519}, + dictWord{ + 132, + 11, + 443, + }, + dictWord{135, 11, 944}, + dictWord{6, 10, 123}, + dictWord{7, 10, 214}, + dictWord{9, 10, 728}, + dictWord{10, 10, 157}, + dictWord{11, 10, 346}, + dictWord{11, 10, 662}, + dictWord{143, 10, 106}, + dictWord{137, 0, 981}, + dictWord{135, 10, 1435}, + dictWord{134, 0, 1072}, + dictWord{132, 0, 712}, + dictWord{ + 134, + 0, + 1629, + }, + dictWord{134, 0, 728}, + dictWord{4, 11, 298}, + dictWord{137, 11, 483}, + dictWord{6, 0, 1177}, + dictWord{6, 0, 1271}, + dictWord{5, 11, 164}, + dictWord{ + 7, + 11, + 121, + }, + dictWord{142, 11, 189}, + dictWord{7, 0, 1608}, + dictWord{4, 10, 707}, + dictWord{5, 10, 588}, + dictWord{6, 10, 393}, + dictWord{13, 10, 106}, + dictWord{ + 18, + 10, + 49, + }, + dictWord{147, 10, 41}, + dictWord{23, 0, 16}, + dictWord{151, 11, 16}, + dictWord{6, 10, 211}, + dictWord{7, 10, 1690}, + dictWord{11, 10, 486}, + dictWord{140, 10, 369}, + dictWord{133, 0, 485}, + dictWord{19, 11, 15}, + dictWord{149, 11, 27}, + dictWord{4, 11, 172}, + dictWord{9, 11, 611}, + dictWord{10, 11, 436}, + dictWord{12, 11, 673}, + dictWord{141, 11, 255}, + dictWord{5, 11, 844}, + dictWord{10, 11, 484}, + dictWord{11, 11, 754}, + dictWord{12, 11, 457}, + dictWord{ + 14, + 11, + 171, + }, + dictWord{14, 11, 389}, + dictWord{146, 11, 153}, + dictWord{4, 0, 285}, + dictWord{5, 0, 27}, + dictWord{5, 0, 317}, + dictWord{6, 0, 301}, + dictWord{7, 0, 7}, + dictWord{ + 8, + 0, + 153, + }, + dictWord{10, 0, 766}, + dictWord{11, 0, 468}, + dictWord{12, 0, 467}, + dictWord{141, 0, 143}, + dictWord{134, 0, 1462}, + dictWord{9, 11, 263}, + dictWord{ + 10, + 11, + 147, + }, + dictWord{138, 11, 492}, + dictWord{133, 11, 537}, + dictWord{6, 0, 1945}, + dictWord{6, 0, 1986}, + dictWord{6, 0, 1991}, + dictWord{134, 0, 2038}, + dictWord{134, 10, 219}, + dictWord{137, 11, 842}, + dictWord{14, 0, 52}, + dictWord{17, 0, 50}, + dictWord{5, 10, 582}, + dictWord{6, 10, 1646}, + dictWord{7, 10, 99}, + dictWord{7, 10, 1962}, + dictWord{7, 10, 1986}, + dictWord{8, 10, 515}, + dictWord{8, 10, 773}, + dictWord{9, 10, 23}, + dictWord{9, 10, 491}, + dictWord{12, 10, 620}, + dictWord{142, 10, 93}, + dictWord{138, 11, 97}, + dictWord{20, 0, 21}, + dictWord{20, 0, 44}, + dictWord{133, 10, 851}, + dictWord{136, 0, 819}, + dictWord{139, 0, 917}, + dictWord{5, 11, 230}, + dictWord{5, 11, 392}, + dictWord{6, 11, 420}, + dictWord{8, 10, 762}, + dictWord{8, 10, 812}, + dictWord{9, 11, 568}, + dictWord{9, 10, 910}, + dictWord{140, 11, 612}, + dictWord{135, 0, 784}, + dictWord{15, 0, 135}, + dictWord{143, 11, 135}, + dictWord{10, 0, 454}, + dictWord{140, 0, 324}, + dictWord{4, 11, 0}, + dictWord{5, 11, 41}, + dictWord{7, 11, 1459}, + dictWord{7, 11, 1469}, + dictWord{7, 11, 1618}, + dictWord{7, 11, 1859}, + dictWord{9, 11, 549}, + dictWord{139, 11, 905}, + dictWord{4, 10, 98}, + dictWord{7, 10, 1365}, + dictWord{9, 10, 422}, + dictWord{9, 10, 670}, + dictWord{10, 10, 775}, + dictWord{11, 10, 210}, + dictWord{13, 10, 26}, + dictWord{13, 10, 457}, + dictWord{141, 10, 476}, + dictWord{6, 0, 1719}, + dictWord{6, 0, 1735}, + dictWord{7, 0, 2016}, + dictWord{7, 0, 2020}, + dictWord{8, 0, 837}, + dictWord{137, 0, 852}, + dictWord{133, 11, 696}, + dictWord{135, 0, 852}, + dictWord{132, 0, 952}, + dictWord{134, 10, 1730}, + dictWord{132, 11, 771}, + dictWord{ + 138, + 0, + 568, + }, + dictWord{137, 0, 448}, + dictWord{139, 0, 146}, + dictWord{8, 0, 67}, + dictWord{138, 0, 419}, + dictWord{133, 11, 921}, + dictWord{137, 10, 147}, + dictWord{134, 0, 1826}, + dictWord{10, 0, 657}, + dictWord{14, 0, 297}, + dictWord{142, 0, 361}, + dictWord{6, 0, 666}, + dictWord{6, 0, 767}, + dictWord{134, 0, 1542}, + dictWord{139, 0, 729}, + dictWord{6, 11, 180}, + dictWord{7, 11, 1137}, + dictWord{8, 11, 751}, + dictWord{139, 11, 805}, + dictWord{4, 11, 183}, + dictWord{7, 11, 271}, + dictWord{11, 11, 824}, + dictWord{11, 11, 952}, + dictWord{13, 11, 278}, + dictWord{13, 11, 339}, + dictWord{13, 11, 482}, + dictWord{14, 11, 424}, + dictWord{ + 148, + 11, + 99, + }, + dictWord{4, 0, 669}, + dictWord{5, 11, 477}, + dictWord{5, 11, 596}, + dictWord{6, 11, 505}, + dictWord{7, 11, 1221}, + dictWord{11, 11, 907}, + dictWord{ + 12, + 11, + 209, + }, + dictWord{141, 11, 214}, + dictWord{135, 11, 1215}, + dictWord{5, 0, 402}, + dictWord{6, 10, 30}, + dictWord{11, 10, 56}, + dictWord{139, 10, 305}, + dictWord{ + 7, + 11, + 564, + }, + dictWord{142, 11, 168}, + dictWord{139, 0, 152}, + dictWord{7, 0, 912}, + dictWord{135, 10, 1614}, + dictWord{4, 10, 150}, + dictWord{5, 10, 303}, + dictWord{134, 10, 327}, + dictWord{7, 0, 320}, + dictWord{8, 0, 51}, + dictWord{9, 0, 868}, + dictWord{10, 0, 833}, + dictWord{12, 0, 481}, + dictWord{12, 0, 570}, + dictWord{ + 148, + 0, + 106, + }, + dictWord{132, 0, 445}, + dictWord{7, 11, 274}, + dictWord{11, 11, 263}, + dictWord{11, 11, 479}, + dictWord{11, 11, 507}, + dictWord{140, 11, 277}, + dictWord{10, 0, 555}, + dictWord{11, 0, 308}, + dictWord{19, 0, 95}, + dictWord{6, 11, 1645}, + dictWord{8, 10, 192}, + dictWord{10, 10, 78}, + dictWord{141, 10, 359}, + dictWord{135, 10, 786}, + dictWord{6, 11, 92}, + dictWord{6, 11, 188}, + dictWord{7, 11, 1269}, + dictWord{7, 11, 1524}, + dictWord{7, 11, 1876}, + dictWord{10, 11, 228}, + dictWord{139, 11, 1020}, + dictWord{4, 11, 459}, + dictWord{133, 11, 966}, + dictWord{11, 0, 386}, + dictWord{6, 10, 1638}, + dictWord{7, 10, 79}, + dictWord{ + 7, + 10, + 496, + }, + dictWord{9, 10, 138}, + dictWord{10, 10, 336}, + dictWord{12, 10, 412}, + dictWord{12, 10, 440}, + dictWord{142, 10, 305}, + dictWord{133, 0, 239}, + dictWord{ + 7, + 0, + 83, + }, + dictWord{7, 0, 1990}, + dictWord{8, 0, 130}, + dictWord{139, 0, 720}, + dictWord{138, 11, 709}, + dictWord{4, 0, 143}, + dictWord{5, 0, 550}, + dictWord{ + 133, + 0, + 752, + }, + dictWord{5, 0, 123}, + dictWord{6, 0, 530}, + dictWord{7, 0, 348}, + dictWord{135, 0, 1419}, + dictWord{135, 0, 2024}, + dictWord{6, 11, 18}, + dictWord{7, 11, 179}, + dictWord{7, 11, 721}, + dictWord{7, 11, 932}, + dictWord{8, 11, 548}, + dictWord{8, 11, 757}, + dictWord{9, 11, 54}, + dictWord{9, 11, 65}, + dictWord{9, 11, 532}, + dictWord{ + 9, + 11, + 844, + }, + dictWord{10, 11, 113}, + dictWord{10, 11, 117}, + dictWord{10, 11, 236}, + dictWord{10, 11, 315}, + dictWord{10, 11, 430}, + dictWord{10, 11, 798}, + dictWord{11, 11, 153}, + dictWord{11, 11, 351}, + dictWord{11, 11, 375}, + dictWord{12, 11, 78}, + dictWord{12, 11, 151}, + dictWord{12, 11, 392}, + dictWord{ + 14, + 11, + 248, + }, + dictWord{143, 11, 23}, + dictWord{7, 10, 204}, + dictWord{7, 10, 415}, + dictWord{8, 10, 42}, + dictWord{10, 10, 85}, + dictWord{139, 10, 564}, + dictWord{ + 134, + 0, + 958, + }, + dictWord{133, 11, 965}, + dictWord{132, 0, 210}, + dictWord{135, 11, 1429}, + dictWord{138, 11, 480}, + dictWord{134, 11, 182}, + dictWord{ + 139, + 11, + 345, + }, + dictWord{10, 11, 65}, + dictWord{10, 11, 488}, + dictWord{138, 11, 497}, + dictWord{4, 10, 3}, + dictWord{5, 10, 247}, + dictWord{5, 10, 644}, + dictWord{ + 7, + 10, + 744, + }, + dictWord{7, 10, 1207}, + dictWord{7, 10, 1225}, + dictWord{7, 10, 1909}, + dictWord{146, 10, 147}, + dictWord{132, 0, 430}, + dictWord{5, 10, 285}, + dictWord{ + 9, + 10, + 67, + }, + dictWord{13, 10, 473}, + dictWord{143, 10, 82}, + dictWord{144, 11, 16}, + dictWord{7, 11, 1162}, + dictWord{9, 11, 588}, + dictWord{10, 11, 260}, + dictWord{151, 10, 8}, + dictWord{133, 0, 213}, + dictWord{138, 0, 7}, + dictWord{135, 0, 801}, + dictWord{134, 11, 1786}, + dictWord{135, 11, 308}, + dictWord{6, 0, 936}, + dictWord{134, 0, 1289}, + dictWord{133, 0, 108}, + dictWord{132, 0, 885}, + dictWord{133, 0, 219}, + dictWord{139, 0, 587}, + dictWord{4, 0, 193}, + dictWord{5, 0, 916}, + dictWord{6, 0, 1041}, + dictWord{7, 0, 364}, + dictWord{10, 0, 398}, + dictWord{10, 0, 726}, + dictWord{11, 0, 317}, + dictWord{11, 0, 626}, + dictWord{12, 0, 142}, + dictWord{12, 0, 288}, + dictWord{12, 0, 678}, + dictWord{13, 0, 313}, + dictWord{15, 0, 113}, + dictWord{146, 0, 114}, + dictWord{135, 0, 1165}, + dictWord{6, 0, 241}, + dictWord{ + 9, + 0, + 342, + }, + dictWord{10, 0, 729}, + dictWord{11, 0, 284}, + dictWord{11, 0, 445}, + dictWord{11, 0, 651}, + dictWord{11, 0, 863}, + dictWord{13, 0, 398}, + dictWord{ + 146, + 0, + 99, + }, + dictWord{7, 0, 907}, + dictWord{136, 0, 832}, + dictWord{9, 0, 303}, + dictWord{4, 10, 29}, + dictWord{6, 10, 532}, + dictWord{7, 10, 1628}, + dictWord{7, 10, 1648}, + dictWord{9, 10, 350}, + dictWord{10, 10, 433}, + dictWord{11, 10, 97}, + dictWord{11, 10, 557}, + dictWord{11, 10, 745}, + dictWord{12, 10, 289}, + dictWord{ + 12, + 10, + 335, + }, + dictWord{12, 10, 348}, + dictWord{12, 10, 606}, + dictWord{13, 10, 116}, + dictWord{13, 10, 233}, + dictWord{13, 10, 466}, + dictWord{14, 10, 181}, + dictWord{ + 14, + 10, + 209, + }, + dictWord{14, 10, 232}, + dictWord{14, 10, 236}, + dictWord{14, 10, 300}, + dictWord{16, 10, 41}, + dictWord{148, 10, 97}, + dictWord{7, 11, 423}, + dictWord{7, 10, 1692}, + dictWord{136, 11, 588}, + dictWord{6, 0, 931}, + dictWord{134, 0, 1454}, + dictWord{5, 10, 501}, + dictWord{7, 10, 1704}, + dictWord{9, 10, 553}, + dictWord{11, 10, 520}, + dictWord{12, 10, 557}, + dictWord{141, 10, 249}, + dictWord{136, 11, 287}, + dictWord{4, 0, 562}, + dictWord{9, 0, 254}, + dictWord{ + 139, + 0, + 879, + }, + dictWord{132, 0, 786}, + dictWord{14, 11, 32}, + dictWord{18, 11, 85}, + dictWord{20, 11, 2}, + dictWord{152, 11, 16}, + dictWord{135, 0, 1294}, + dictWord{ + 7, + 11, + 723, + }, + dictWord{135, 11, 1135}, + dictWord{6, 0, 216}, + dictWord{7, 0, 901}, + dictWord{7, 0, 1343}, + dictWord{8, 0, 493}, + dictWord{134, 11, 403}, + dictWord{ + 7, + 11, + 719, + }, + dictWord{8, 11, 809}, + dictWord{136, 11, 834}, + dictWord{5, 11, 210}, + dictWord{6, 11, 213}, + dictWord{7, 11, 60}, + dictWord{10, 11, 364}, + dictWord{ + 139, + 11, + 135, + }, + dictWord{7, 0, 341}, + dictWord{11, 0, 219}, + dictWord{5, 11, 607}, + dictWord{8, 11, 326}, + dictWord{136, 11, 490}, + dictWord{4, 11, 701}, + dictWord{ + 5, + 11, + 472, + }, + dictWord{5, 11, 639}, + dictWord{7, 11, 1249}, + dictWord{9, 11, 758}, + dictWord{139, 11, 896}, + dictWord{135, 11, 380}, + dictWord{135, 11, 1947}, + dictWord{139, 0, 130}, + dictWord{135, 0, 1734}, + dictWord{10, 0, 115}, + dictWord{11, 0, 420}, + dictWord{12, 0, 154}, + dictWord{13, 0, 404}, + dictWord{14, 0, 346}, + dictWord{143, 0, 54}, + dictWord{134, 10, 129}, + dictWord{4, 11, 386}, + dictWord{7, 11, 41}, + dictWord{8, 11, 405}, + dictWord{9, 11, 497}, + dictWord{11, 11, 110}, + dictWord{11, 11, 360}, + dictWord{15, 11, 37}, + dictWord{144, 11, 84}, + dictWord{141, 11, 282}, + dictWord{5, 11, 46}, + dictWord{7, 11, 1452}, + dictWord{7, 11, 1480}, + dictWord{8, 11, 634}, + dictWord{140, 11, 472}, + dictWord{4, 11, 524}, + dictWord{136, 11, 810}, + dictWord{10, 11, 238}, + dictWord{141, 11, 33}, + dictWord{ + 133, + 0, + 604, + }, + dictWord{5, 0, 1011}, + dictWord{136, 0, 701}, + dictWord{8, 0, 856}, + dictWord{8, 0, 858}, + dictWord{8, 0, 879}, + dictWord{12, 0, 702}, + dictWord{142, 0, 447}, + dictWord{4, 0, 54}, + dictWord{5, 0, 666}, + dictWord{7, 0, 1039}, + dictWord{7, 0, 1130}, + dictWord{9, 0, 195}, + dictWord{138, 0, 302}, + dictWord{4, 10, 25}, + dictWord{ + 5, + 10, + 60, + }, + dictWord{6, 10, 504}, + dictWord{7, 10, 614}, + dictWord{7, 10, 1155}, + dictWord{140, 10, 0}, + dictWord{7, 10, 1248}, + dictWord{11, 10, 621}, + dictWord{ + 139, + 10, + 702, + }, + dictWord{133, 11, 997}, + dictWord{137, 10, 321}, + dictWord{134, 0, 1669}, + dictWord{134, 0, 1791}, + dictWord{4, 10, 379}, + dictWord{ + 135, + 10, + 1397, + }, + dictWord{138, 11, 372}, + dictWord{5, 11, 782}, + dictWord{5, 11, 829}, + dictWord{134, 11, 1738}, + dictWord{135, 0, 1228}, + dictWord{4, 10, 118}, + dictWord{6, 10, 274}, + dictWord{6, 10, 361}, + dictWord{7, 10, 75}, + dictWord{141, 10, 441}, + dictWord{132, 0, 623}, + dictWord{9, 11, 279}, + dictWord{10, 11, 407}, + dictWord{14, 11, 84}, + dictWord{150, 11, 18}, + dictWord{137, 10, 841}, + dictWord{135, 0, 798}, + dictWord{140, 10, 693}, + dictWord{5, 10, 314}, + dictWord{6, 10, 221}, + dictWord{7, 10, 419}, + dictWord{10, 10, 650}, + dictWord{11, 10, 396}, + dictWord{12, 10, 156}, + dictWord{13, 10, 369}, + dictWord{14, 10, 333}, + dictWord{ + 145, + 10, + 47, + }, + dictWord{135, 11, 1372}, + dictWord{7, 0, 122}, + dictWord{9, 0, 259}, + dictWord{10, 0, 84}, + dictWord{11, 0, 470}, + dictWord{12, 0, 541}, + dictWord{ + 141, + 0, + 379, + }, + dictWord{134, 0, 837}, + dictWord{8, 0, 1013}, + dictWord{4, 11, 78}, + dictWord{5, 11, 96}, + dictWord{5, 11, 182}, + dictWord{7, 11, 1724}, + dictWord{ + 7, + 11, + 1825, + }, + dictWord{10, 11, 394}, + dictWord{10, 11, 471}, + dictWord{11, 11, 532}, + dictWord{14, 11, 340}, + dictWord{145, 11, 88}, + dictWord{134, 0, 577}, + dictWord{135, 11, 1964}, + dictWord{132, 10, 913}, + dictWord{134, 0, 460}, + dictWord{8, 0, 891}, + dictWord{10, 0, 901}, + dictWord{10, 0, 919}, + dictWord{10, 0, 932}, + dictWord{12, 0, 715}, + dictWord{12, 0, 728}, + dictWord{12, 0, 777}, + dictWord{14, 0, 457}, + dictWord{144, 0, 103}, + dictWord{5, 0, 82}, + dictWord{5, 0, 131}, + dictWord{ + 7, + 0, + 1755, + }, + dictWord{8, 0, 31}, + dictWord{9, 0, 168}, + dictWord{9, 0, 764}, + dictWord{139, 0, 869}, + dictWord{136, 10, 475}, + dictWord{6, 0, 605}, + dictWord{ + 5, + 10, + 1016, + }, + dictWord{9, 11, 601}, + dictWord{9, 11, 619}, + dictWord{10, 11, 505}, + dictWord{10, 11, 732}, + dictWord{11, 11, 355}, + dictWord{140, 11, 139}, + dictWord{ + 7, + 10, + 602, + }, + dictWord{8, 10, 179}, + dictWord{10, 10, 781}, + dictWord{140, 10, 126}, + dictWord{134, 0, 1246}, + dictWord{6, 10, 329}, + dictWord{138, 10, 111}, + dictWord{6, 11, 215}, + dictWord{7, 11, 1028}, + dictWord{7, 11, 1473}, + dictWord{7, 11, 1721}, + dictWord{9, 11, 424}, + dictWord{138, 11, 779}, + dictWord{5, 0, 278}, + dictWord{137, 0, 68}, + dictWord{6, 0, 932}, + dictWord{6, 0, 1084}, + dictWord{144, 0, 86}, + dictWord{4, 0, 163}, + dictWord{5, 0, 201}, + dictWord{5, 0, 307}, + dictWord{ + 5, + 0, + 310, + }, + dictWord{6, 0, 335}, + dictWord{7, 0, 284}, + dictWord{7, 0, 1660}, + dictWord{136, 0, 165}, + dictWord{136, 0, 781}, + dictWord{134, 0, 707}, + dictWord{6, 0, 33}, + dictWord{135, 0, 1244}, + dictWord{5, 10, 821}, + dictWord{6, 11, 67}, + dictWord{6, 10, 1687}, + dictWord{7, 11, 258}, + dictWord{7, 11, 1630}, + dictWord{9, 11, 354}, + dictWord{9, 11, 675}, + dictWord{10, 11, 830}, + dictWord{14, 11, 80}, + dictWord{145, 11, 80}, + dictWord{6, 11, 141}, + dictWord{7, 11, 225}, + dictWord{9, 11, 59}, + dictWord{9, 11, 607}, + dictWord{10, 11, 312}, + dictWord{11, 11, 687}, + dictWord{12, 11, 555}, + dictWord{13, 11, 373}, + dictWord{13, 11, 494}, + dictWord{148, 11, 58}, + dictWord{134, 0, 1113}, + dictWord{9, 0, 388}, + dictWord{5, 10, 71}, + dictWord{7, 10, 1407}, + dictWord{9, 10, 704}, + dictWord{10, 10, 261}, + dictWord{10, 10, 619}, + dictWord{11, 10, 547}, + dictWord{11, 10, 619}, + dictWord{143, 10, 157}, + dictWord{7, 0, 1953}, + dictWord{136, 0, 720}, + dictWord{138, 0, 203}, + dictWord{ + 7, + 10, + 2008, + }, + dictWord{9, 10, 337}, + dictWord{138, 10, 517}, + dictWord{6, 0, 326}, + dictWord{7, 0, 677}, + dictWord{137, 0, 425}, + dictWord{139, 11, 81}, + dictWord{ + 7, + 0, + 1316, + }, + dictWord{7, 0, 1412}, + dictWord{7, 0, 1839}, + dictWord{9, 0, 589}, + dictWord{11, 0, 241}, + dictWord{11, 0, 676}, + dictWord{11, 0, 811}, + dictWord{11, 0, 891}, + dictWord{12, 0, 140}, + dictWord{12, 0, 346}, + dictWord{12, 0, 479}, + dictWord{13, 0, 140}, + dictWord{13, 0, 381}, + dictWord{14, 0, 188}, + dictWord{18, 0, 30}, + dictWord{148, 0, 108}, + dictWord{5, 0, 416}, + dictWord{6, 10, 86}, + dictWord{6, 10, 603}, + dictWord{7, 10, 292}, + dictWord{7, 10, 561}, + dictWord{8, 10, 257}, + dictWord{ + 8, + 10, + 382, + }, + dictWord{9, 10, 721}, + dictWord{9, 10, 778}, + dictWord{11, 10, 581}, + dictWord{140, 10, 466}, + dictWord{4, 10, 486}, + dictWord{133, 10, 491}, + dictWord{134, 0, 1300}, + dictWord{132, 10, 72}, + dictWord{7, 0, 847}, + dictWord{6, 10, 265}, + dictWord{7, 11, 430}, + dictWord{139, 11, 46}, + dictWord{5, 11, 602}, + dictWord{6, 11, 106}, + dictWord{7, 11, 1786}, + dictWord{7, 11, 1821}, + dictWord{7, 11, 2018}, + dictWord{9, 11, 418}, + dictWord{137, 11, 763}, + dictWord{5, 0, 358}, + dictWord{7, 0, 535}, + dictWord{7, 0, 1184}, + dictWord{10, 0, 662}, + dictWord{13, 0, 212}, + dictWord{13, 0, 304}, + dictWord{13, 0, 333}, + dictWord{145, 0, 98}, + dictWord{ + 5, + 11, + 65, + }, + dictWord{6, 11, 416}, + dictWord{7, 11, 1720}, + dictWord{7, 11, 1924}, + dictWord{8, 11, 677}, + dictWord{10, 11, 109}, + dictWord{11, 11, 14}, + dictWord{ + 11, + 11, + 70, + }, + dictWord{11, 11, 569}, + dictWord{11, 11, 735}, + dictWord{15, 11, 153}, + dictWord{148, 11, 80}, + dictWord{6, 0, 1823}, + dictWord{8, 0, 839}, + dictWord{ + 8, + 0, + 852, + }, + dictWord{8, 0, 903}, + dictWord{10, 0, 940}, + dictWord{12, 0, 707}, + dictWord{140, 0, 775}, + dictWord{135, 11, 1229}, + dictWord{6, 0, 1522}, + dictWord{ + 140, + 0, + 654, + }, + dictWord{136, 11, 595}, + dictWord{139, 0, 163}, + dictWord{141, 0, 314}, + dictWord{132, 0, 978}, + dictWord{4, 0, 601}, + dictWord{6, 0, 2035}, + dictWord{137, 10, 234}, + dictWord{5, 10, 815}, + dictWord{6, 10, 1688}, + dictWord{134, 10, 1755}, + dictWord{133, 0, 946}, + dictWord{136, 0, 434}, + dictWord{ + 6, + 10, + 197, + }, + dictWord{136, 10, 205}, + dictWord{7, 0, 411}, + dictWord{7, 0, 590}, + dictWord{8, 0, 631}, + dictWord{9, 0, 323}, + dictWord{10, 0, 355}, + dictWord{11, 0, 491}, + dictWord{12, 0, 143}, + dictWord{12, 0, 402}, + dictWord{13, 0, 73}, + dictWord{14, 0, 408}, + dictWord{15, 0, 107}, + dictWord{146, 0, 71}, + dictWord{7, 0, 1467}, + dictWord{ + 8, + 0, + 328, + }, + dictWord{10, 0, 544}, + dictWord{11, 0, 955}, + dictWord{12, 0, 13}, + dictWord{13, 0, 320}, + dictWord{145, 0, 83}, + dictWord{142, 0, 410}, + dictWord{ + 11, + 0, + 511, + }, + dictWord{13, 0, 394}, + dictWord{14, 0, 298}, + dictWord{14, 0, 318}, + dictWord{146, 0, 103}, + dictWord{6, 10, 452}, + dictWord{7, 10, 312}, + dictWord{ + 138, + 10, + 219, + }, + dictWord{138, 10, 589}, + dictWord{4, 10, 333}, + dictWord{9, 10, 176}, + dictWord{12, 10, 353}, + dictWord{141, 10, 187}, + dictWord{135, 11, 329}, + dictWord{132, 11, 469}, + dictWord{5, 0, 835}, + dictWord{134, 0, 483}, + dictWord{134, 11, 1743}, + dictWord{5, 11, 929}, + dictWord{6, 11, 340}, + dictWord{8, 11, 376}, + dictWord{136, 11, 807}, + dictWord{134, 10, 1685}, + dictWord{132, 0, 677}, + dictWord{5, 11, 218}, + dictWord{7, 11, 1610}, + dictWord{138, 11, 83}, + dictWord{ + 5, + 11, + 571, + }, + dictWord{135, 11, 1842}, + dictWord{132, 11, 455}, + dictWord{137, 0, 70}, + dictWord{135, 0, 1405}, + dictWord{7, 10, 135}, + dictWord{8, 10, 7}, + dictWord{ + 8, + 10, + 62, + }, + dictWord{9, 10, 243}, + dictWord{10, 10, 658}, + dictWord{10, 10, 697}, + dictWord{11, 10, 456}, + dictWord{139, 10, 756}, + dictWord{9, 10, 395}, + dictWord{138, 10, 79}, + dictWord{137, 0, 108}, + dictWord{6, 11, 161}, + dictWord{7, 11, 372}, + dictWord{137, 11, 597}, + dictWord{132, 11, 349}, + dictWord{ + 132, + 0, + 777, + }, + dictWord{132, 0, 331}, + dictWord{135, 10, 631}, + dictWord{133, 0, 747}, + dictWord{6, 11, 432}, + dictWord{6, 11, 608}, + dictWord{139, 11, 322}, + dictWord{138, 10, 835}, + dictWord{5, 11, 468}, + dictWord{7, 11, 1809}, + dictWord{10, 11, 325}, + dictWord{11, 11, 856}, + dictWord{12, 11, 345}, + dictWord{ + 143, + 11, + 104, + }, + dictWord{133, 11, 223}, + dictWord{7, 10, 406}, + dictWord{7, 10, 459}, + dictWord{8, 10, 606}, + dictWord{139, 10, 726}, + dictWord{132, 11, 566}, + dictWord{142, 0, 68}, + dictWord{4, 11, 59}, + dictWord{135, 11, 1394}, + dictWord{6, 11, 436}, + dictWord{139, 11, 481}, + dictWord{4, 11, 48}, + dictWord{5, 11, 271}, + dictWord{135, 11, 953}, + dictWord{139, 11, 170}, + dictWord{5, 11, 610}, + dictWord{136, 11, 457}, + dictWord{133, 11, 755}, + dictWord{135, 11, 1217}, + dictWord{ + 133, + 10, + 612, + }, + dictWord{132, 11, 197}, + dictWord{132, 0, 505}, + dictWord{4, 10, 372}, + dictWord{7, 10, 482}, + dictWord{8, 10, 158}, + dictWord{9, 10, 602}, + dictWord{ + 9, + 10, + 615, + }, + dictWord{10, 10, 245}, + dictWord{10, 10, 678}, + dictWord{10, 10, 744}, + dictWord{11, 10, 248}, + dictWord{139, 10, 806}, + dictWord{133, 0, 326}, + dictWord{5, 10, 854}, + dictWord{135, 10, 1991}, + dictWord{4, 0, 691}, + dictWord{146, 0, 16}, + dictWord{6, 0, 628}, + dictWord{9, 0, 35}, + dictWord{10, 0, 680}, + dictWord{10, 0, 793}, + dictWord{11, 0, 364}, + dictWord{13, 0, 357}, + dictWord{143, 0, 164}, + dictWord{138, 0, 654}, + dictWord{6, 0, 32}, + dictWord{7, 0, 385}, + dictWord{ + 7, + 0, + 757, + }, + dictWord{7, 0, 1916}, + dictWord{8, 0, 37}, + dictWord{8, 0, 94}, + dictWord{8, 0, 711}, + dictWord{9, 0, 541}, + dictWord{10, 0, 162}, + dictWord{10, 0, 795}, + dictWord{ + 11, + 0, + 989, + }, + dictWord{11, 0, 1010}, + dictWord{12, 0, 14}, + dictWord{142, 0, 308}, + dictWord{133, 11, 217}, + dictWord{6, 0, 152}, + dictWord{6, 0, 349}, + dictWord{ + 6, + 0, + 1682, + }, + dictWord{7, 0, 1252}, + dictWord{8, 0, 112}, + dictWord{9, 0, 435}, + dictWord{9, 0, 668}, + dictWord{10, 0, 290}, + dictWord{10, 0, 319}, + dictWord{10, 0, 815}, + dictWord{11, 0, 180}, + dictWord{11, 0, 837}, + dictWord{12, 0, 240}, + dictWord{13, 0, 152}, + dictWord{13, 0, 219}, + dictWord{142, 0, 158}, + dictWord{4, 0, 581}, + dictWord{134, 0, 726}, + dictWord{5, 10, 195}, + dictWord{135, 10, 1685}, + dictWord{6, 0, 126}, + dictWord{7, 0, 573}, + dictWord{8, 0, 397}, + dictWord{142, 0, 44}, + dictWord{138, 0, 89}, + dictWord{7, 10, 1997}, + dictWord{8, 10, 730}, + dictWord{139, 10, 1006}, + dictWord{134, 0, 1531}, + dictWord{134, 0, 1167}, + dictWord{ + 5, + 0, + 926, + }, + dictWord{12, 0, 203}, + dictWord{133, 10, 751}, + dictWord{4, 11, 165}, + dictWord{7, 11, 1398}, + dictWord{135, 11, 1829}, + dictWord{7, 0, 1232}, + dictWord{137, 0, 531}, + dictWord{135, 10, 821}, + dictWord{134, 0, 943}, + dictWord{133, 0, 670}, + dictWord{4, 0, 880}, + dictWord{139, 0, 231}, + dictWord{ + 134, + 0, + 1617, + }, + dictWord{135, 0, 1957}, + dictWord{5, 11, 9}, + dictWord{7, 11, 297}, + dictWord{7, 11, 966}, + dictWord{140, 11, 306}, + dictWord{6, 0, 975}, + dictWord{ + 134, + 0, + 985, + }, + dictWord{5, 10, 950}, + dictWord{5, 10, 994}, + dictWord{134, 10, 351}, + dictWord{12, 11, 21}, + dictWord{151, 11, 7}, + dictWord{5, 11, 146}, + dictWord{ + 6, + 11, + 411, + }, + dictWord{138, 11, 721}, + dictWord{7, 0, 242}, + dictWord{135, 0, 1942}, + dictWord{6, 11, 177}, + dictWord{135, 11, 467}, + dictWord{5, 0, 421}, + dictWord{ + 7, + 10, + 47, + }, + dictWord{137, 10, 684}, + dictWord{5, 0, 834}, + dictWord{7, 0, 1202}, + dictWord{8, 0, 14}, + dictWord{9, 0, 481}, + dictWord{137, 0, 880}, + dictWord{138, 0, 465}, + dictWord{6, 0, 688}, + dictWord{9, 0, 834}, + dictWord{132, 10, 350}, + dictWord{132, 0, 855}, + dictWord{4, 0, 357}, + dictWord{6, 0, 172}, + dictWord{7, 0, 143}, + dictWord{137, 0, 413}, + dictWord{133, 11, 200}, + dictWord{132, 0, 590}, + dictWord{7, 10, 1812}, + dictWord{13, 10, 259}, + dictWord{13, 10, 356}, + dictWord{ + 14, + 10, + 242, + }, + dictWord{147, 10, 114}, + dictWord{133, 10, 967}, + dictWord{11, 0, 114}, + dictWord{4, 10, 473}, + dictWord{7, 10, 623}, + dictWord{8, 10, 808}, + dictWord{ + 9, + 10, + 871, + }, + dictWord{9, 10, 893}, + dictWord{11, 10, 431}, + dictWord{12, 10, 112}, + dictWord{12, 10, 217}, + dictWord{12, 10, 243}, + dictWord{12, 10, 562}, + dictWord{ + 12, + 10, + 663, + }, + dictWord{12, 10, 683}, + dictWord{13, 10, 141}, + dictWord{13, 10, 197}, + dictWord{13, 10, 227}, + dictWord{13, 10, 406}, + dictWord{13, 10, 487}, + dictWord{14, 10, 156}, + dictWord{14, 10, 203}, + dictWord{14, 10, 224}, + dictWord{14, 10, 256}, + dictWord{18, 10, 58}, + dictWord{150, 10, 0}, + dictWord{ + 138, + 10, + 286, + }, + dictWord{4, 10, 222}, + dictWord{7, 10, 286}, + dictWord{136, 10, 629}, + dictWord{5, 0, 169}, + dictWord{7, 0, 333}, + dictWord{136, 0, 45}, + dictWord{ + 134, + 11, + 481, + }, + dictWord{132, 0, 198}, + dictWord{4, 0, 24}, + dictWord{5, 0, 140}, + dictWord{5, 0, 185}, + dictWord{7, 0, 1500}, + dictWord{11, 0, 565}, + dictWord{11, 0, 838}, + dictWord{4, 11, 84}, + dictWord{7, 11, 1482}, + dictWord{10, 11, 76}, + dictWord{138, 11, 142}, + dictWord{133, 0, 585}, + dictWord{141, 10, 306}, + dictWord{ + 133, + 11, + 1015, + }, + dictWord{4, 11, 315}, + dictWord{5, 11, 507}, + dictWord{135, 11, 1370}, + dictWord{136, 10, 146}, + dictWord{6, 0, 691}, + dictWord{134, 0, 1503}, + dictWord{ + 4, + 0, + 334, + }, + dictWord{133, 0, 593}, + dictWord{4, 10, 465}, + dictWord{135, 10, 1663}, + dictWord{142, 11, 173}, + dictWord{135, 0, 913}, + dictWord{12, 0, 116}, + dictWord{134, 11, 1722}, + dictWord{134, 0, 1360}, + dictWord{132, 0, 802}, + dictWord{8, 11, 222}, + dictWord{8, 11, 476}, + dictWord{9, 11, 238}, + dictWord{ + 11, + 11, + 516, + }, + dictWord{11, 11, 575}, + dictWord{15, 11, 109}, + dictWord{146, 11, 100}, + dictWord{6, 0, 308}, + dictWord{9, 0, 673}, + dictWord{7, 10, 138}, + dictWord{ + 7, + 10, + 517, + }, + dictWord{139, 10, 238}, + dictWord{132, 0, 709}, + dictWord{6, 0, 1876}, + dictWord{6, 0, 1895}, + dictWord{9, 0, 994}, + dictWord{9, 0, 1006}, + dictWord{ + 12, + 0, + 829, + }, + dictWord{12, 0, 888}, + dictWord{12, 0, 891}, + dictWord{146, 0, 185}, + dictWord{148, 10, 94}, + dictWord{4, 0, 228}, + dictWord{133, 0, 897}, + dictWord{ + 7, + 0, + 1840, + }, + dictWord{5, 10, 495}, + dictWord{7, 10, 834}, + dictWord{9, 10, 733}, + dictWord{139, 10, 378}, + dictWord{133, 10, 559}, + dictWord{6, 10, 21}, + dictWord{ + 6, + 10, + 1737, + }, + dictWord{7, 10, 1444}, + dictWord{136, 10, 224}, + dictWord{4, 0, 608}, + dictWord{133, 0, 497}, + dictWord{6, 11, 40}, + dictWord{135, 11, 1781}, + dictWord{134, 0, 1573}, + dictWord{135, 0, 2039}, + dictWord{6, 0, 540}, + dictWord{136, 0, 136}, + dictWord{4, 0, 897}, + dictWord{5, 0, 786}, + dictWord{133, 10, 519}, + dictWord{6, 0, 1878}, + dictWord{6, 0, 1884}, + dictWord{9, 0, 938}, + dictWord{9, 0, 948}, + dictWord{9, 0, 955}, + dictWord{9, 0, 973}, + dictWord{9, 0, 1012}, + dictWord{ + 12, + 0, + 895, + }, + dictWord{12, 0, 927}, + dictWord{143, 0, 254}, + dictWord{134, 0, 1469}, + dictWord{133, 0, 999}, + dictWord{4, 0, 299}, + dictWord{135, 0, 1004}, + dictWord{ + 4, + 0, + 745, + }, + dictWord{133, 0, 578}, + dictWord{136, 11, 574}, + dictWord{133, 0, 456}, + dictWord{134, 0, 1457}, + dictWord{7, 0, 1679}, + dictWord{132, 10, 402}, + dictWord{7, 0, 693}, + dictWord{8, 0, 180}, + dictWord{12, 0, 163}, + dictWord{8, 10, 323}, + dictWord{136, 10, 479}, + dictWord{11, 10, 580}, + dictWord{142, 10, 201}, + dictWord{5, 10, 59}, + dictWord{135, 10, 672}, + dictWord{132, 11, 354}, + dictWord{146, 10, 34}, + dictWord{4, 0, 755}, + dictWord{135, 11, 1558}, + dictWord{ + 7, + 0, + 1740, + }, + dictWord{146, 0, 48}, + dictWord{4, 10, 85}, + dictWord{135, 10, 549}, + dictWord{139, 0, 338}, + dictWord{133, 10, 94}, + dictWord{134, 0, 1091}, + dictWord{135, 11, 469}, + dictWord{12, 0, 695}, + dictWord{12, 0, 704}, + dictWord{20, 0, 113}, + dictWord{5, 11, 830}, + dictWord{14, 11, 338}, + dictWord{148, 11, 81}, + dictWord{135, 0, 1464}, + dictWord{6, 10, 11}, + dictWord{135, 10, 187}, + dictWord{135, 0, 975}, + dictWord{13, 0, 335}, + dictWord{132, 10, 522}, + dictWord{ + 134, + 0, + 1979, + }, + dictWord{5, 11, 496}, + dictWord{135, 11, 203}, + dictWord{4, 10, 52}, + dictWord{135, 10, 661}, + dictWord{7, 0, 1566}, + dictWord{8, 0, 269}, + dictWord{ + 9, + 0, + 212, + }, + dictWord{9, 0, 718}, + dictWord{14, 0, 15}, + dictWord{14, 0, 132}, + dictWord{142, 0, 227}, + dictWord{4, 0, 890}, + dictWord{5, 0, 805}, + dictWord{5, 0, 819}, + dictWord{ + 5, + 0, + 961, + }, + dictWord{6, 0, 396}, + dictWord{6, 0, 1631}, + dictWord{6, 0, 1678}, + dictWord{7, 0, 1967}, + dictWord{7, 0, 2041}, + dictWord{9, 0, 630}, + dictWord{11, 0, 8}, + dictWord{11, 0, 1019}, + dictWord{12, 0, 176}, + dictWord{13, 0, 225}, + dictWord{14, 0, 292}, + dictWord{21, 0, 24}, + dictWord{4, 10, 383}, + dictWord{133, 10, 520}, + dictWord{134, 11, 547}, + dictWord{135, 11, 1748}, + dictWord{5, 11, 88}, + dictWord{137, 11, 239}, + dictWord{146, 11, 128}, + dictWord{7, 11, 650}, + dictWord{ + 135, + 11, + 1310, + }, + dictWord{4, 10, 281}, + dictWord{5, 10, 38}, + dictWord{7, 10, 194}, + dictWord{7, 10, 668}, + dictWord{7, 10, 1893}, + dictWord{137, 10, 397}, + dictWord{135, 0, 1815}, + dictWord{9, 10, 635}, + dictWord{139, 10, 559}, + dictWord{7, 0, 1505}, + dictWord{10, 0, 190}, + dictWord{10, 0, 634}, + dictWord{11, 0, 792}, + dictWord{12, 0, 358}, + dictWord{140, 0, 447}, + dictWord{5, 0, 0}, + dictWord{6, 0, 536}, + dictWord{7, 0, 604}, + dictWord{13, 0, 445}, + dictWord{145, 0, 126}, + dictWord{ + 7, + 11, + 1076, + }, + dictWord{9, 11, 80}, + dictWord{11, 11, 78}, + dictWord{11, 11, 421}, + dictWord{11, 11, 534}, + dictWord{140, 11, 545}, + dictWord{8, 0, 966}, + dictWord{ + 10, + 0, + 1023, + }, + dictWord{14, 11, 369}, + dictWord{146, 11, 72}, + dictWord{135, 11, 1641}, + dictWord{6, 0, 232}, + dictWord{6, 0, 412}, + dictWord{7, 0, 1074}, + dictWord{ + 8, + 0, + 9, + }, + dictWord{8, 0, 157}, + dictWord{8, 0, 786}, + dictWord{9, 0, 196}, + dictWord{9, 0, 352}, + dictWord{9, 0, 457}, + dictWord{10, 0, 337}, + dictWord{11, 0, 232}, + dictWord{ + 11, + 0, + 877, + }, + dictWord{12, 0, 480}, + dictWord{140, 0, 546}, + dictWord{135, 0, 958}, + dictWord{4, 0, 382}, + dictWord{136, 0, 579}, + dictWord{4, 0, 212}, + dictWord{ + 135, + 0, + 1206, + }, + dictWord{4, 11, 497}, + dictWord{5, 11, 657}, + dictWord{135, 11, 1584}, + dictWord{132, 0, 681}, + dictWord{8, 0, 971}, + dictWord{138, 0, 965}, + dictWord{ + 5, + 10, + 448, + }, + dictWord{136, 10, 535}, + dictWord{14, 0, 16}, + dictWord{146, 0, 44}, + dictWord{11, 0, 584}, + dictWord{11, 0, 616}, + dictWord{14, 0, 275}, + dictWord{ + 11, + 11, + 584, + }, + dictWord{11, 11, 616}, + dictWord{142, 11, 275}, + dictWord{136, 11, 13}, + dictWord{7, 10, 610}, + dictWord{135, 10, 1501}, + dictWord{7, 11, 642}, + dictWord{8, 11, 250}, + dictWord{11, 11, 123}, + dictWord{11, 11, 137}, + dictWord{13, 11, 48}, + dictWord{142, 11, 95}, + dictWord{133, 0, 655}, + dictWord{17, 0, 67}, + dictWord{147, 0, 74}, + dictWord{134, 0, 751}, + dictWord{134, 0, 1967}, + dictWord{6, 0, 231}, + dictWord{136, 0, 423}, + dictWord{5, 0, 300}, + dictWord{138, 0, 1016}, + dictWord{4, 10, 319}, + dictWord{5, 10, 699}, + dictWord{138, 10, 673}, + dictWord{6, 0, 237}, + dictWord{7, 0, 611}, + dictWord{8, 0, 100}, + dictWord{9, 0, 416}, + dictWord{ + 11, + 0, + 335, + }, + dictWord{12, 0, 173}, + dictWord{18, 0, 101}, + dictWord{6, 10, 336}, + dictWord{8, 10, 552}, + dictWord{9, 10, 285}, + dictWord{10, 10, 99}, + dictWord{ + 139, + 10, + 568, + }, + dictWord{134, 0, 1370}, + dictWord{7, 10, 1406}, + dictWord{9, 10, 218}, + dictWord{141, 10, 222}, + dictWord{133, 10, 256}, + dictWord{ + 135, + 0, + 1208, + }, + dictWord{14, 11, 213}, + dictWord{148, 11, 38}, + dictWord{6, 0, 1219}, + dictWord{135, 11, 1642}, + dictWord{13, 0, 417}, + dictWord{14, 0, 129}, + dictWord{143, 0, 15}, + dictWord{10, 11, 545}, + dictWord{140, 11, 301}, + dictWord{17, 10, 39}, + dictWord{148, 10, 36}, + dictWord{133, 0, 199}, + dictWord{4, 11, 904}, + dictWord{133, 11, 794}, + dictWord{12, 0, 427}, + dictWord{146, 0, 38}, + dictWord{134, 0, 949}, + dictWord{8, 0, 665}, + dictWord{135, 10, 634}, + dictWord{ + 132, + 10, + 618, + }, + dictWord{135, 10, 259}, + dictWord{132, 10, 339}, + dictWord{133, 11, 761}, + dictWord{141, 10, 169}, + dictWord{132, 10, 759}, + dictWord{5, 0, 688}, + dictWord{7, 0, 539}, + dictWord{135, 0, 712}, + dictWord{7, 11, 386}, + dictWord{138, 11, 713}, + dictWord{134, 0, 1186}, + dictWord{6, 11, 7}, + dictWord{6, 11, 35}, + dictWord{ + 7, + 11, + 147, + }, + dictWord{7, 11, 1069}, + dictWord{7, 11, 1568}, + dictWord{7, 11, 1575}, + dictWord{7, 11, 1917}, + dictWord{8, 11, 43}, + dictWord{8, 11, 208}, + dictWord{ + 9, + 11, + 128, + }, + dictWord{9, 11, 866}, + dictWord{10, 11, 20}, + dictWord{11, 11, 981}, + dictWord{147, 11, 33}, + dictWord{7, 11, 893}, + dictWord{8, 10, 482}, + dictWord{141, 11, 424}, + dictWord{6, 0, 312}, + dictWord{6, 0, 1715}, + dictWord{10, 0, 584}, + dictWord{11, 0, 546}, + dictWord{11, 0, 692}, + dictWord{12, 0, 259}, + dictWord{ + 12, + 0, + 295, + }, + dictWord{13, 0, 46}, + dictWord{141, 0, 154}, + dictWord{5, 10, 336}, + dictWord{6, 10, 341}, + dictWord{6, 10, 478}, + dictWord{6, 10, 1763}, + dictWord{ + 136, + 10, + 386, + }, + dictWord{137, 0, 151}, + dictWord{132, 0, 588}, + dictWord{152, 0, 4}, + dictWord{6, 11, 322}, + dictWord{9, 11, 552}, + dictWord{11, 11, 274}, + dictWord{ + 13, + 11, + 209, + }, + dictWord{13, 11, 499}, + dictWord{14, 11, 85}, + dictWord{15, 11, 126}, + dictWord{145, 11, 70}, + dictWord{135, 10, 73}, + dictWord{4, 0, 231}, + dictWord{ + 5, + 0, + 61, + }, + dictWord{6, 0, 104}, + dictWord{7, 0, 729}, + dictWord{7, 0, 964}, + dictWord{7, 0, 1658}, + dictWord{140, 0, 414}, + dictWord{6, 0, 263}, + dictWord{138, 0, 757}, + dictWord{135, 10, 1971}, + dictWord{4, 0, 612}, + dictWord{133, 0, 561}, + dictWord{132, 0, 320}, + dictWord{135, 10, 1344}, + dictWord{8, 11, 83}, + dictWord{ + 8, + 11, + 817, + }, + dictWord{9, 11, 28}, + dictWord{9, 11, 29}, + dictWord{9, 11, 885}, + dictWord{10, 11, 387}, + dictWord{11, 11, 633}, + dictWord{11, 11, 740}, + dictWord{ + 13, + 11, + 235, + }, + dictWord{13, 11, 254}, + dictWord{15, 11, 143}, + dictWord{143, 11, 146}, + dictWord{5, 10, 396}, + dictWord{134, 10, 501}, + dictWord{140, 11, 49}, + dictWord{132, 0, 225}, + dictWord{4, 10, 929}, + dictWord{5, 10, 799}, + dictWord{8, 10, 46}, + dictWord{136, 10, 740}, + dictWord{4, 0, 405}, + dictWord{7, 0, 817}, + dictWord{ + 14, + 0, + 58, + }, + dictWord{17, 0, 37}, + dictWord{146, 0, 124}, + dictWord{133, 0, 974}, + dictWord{4, 11, 412}, + dictWord{133, 11, 581}, + dictWord{4, 10, 892}, + dictWord{ + 133, + 10, + 770, + }, + dictWord{4, 0, 996}, + dictWord{134, 0, 2026}, + dictWord{4, 0, 527}, + dictWord{5, 0, 235}, + dictWord{7, 0, 1239}, + dictWord{11, 0, 131}, + dictWord{ + 140, + 0, + 370, + }, + dictWord{9, 0, 16}, + dictWord{13, 0, 386}, + dictWord{135, 11, 421}, + dictWord{7, 0, 956}, + dictWord{7, 0, 1157}, + dictWord{7, 0, 1506}, + dictWord{7, 0, 1606}, + dictWord{7, 0, 1615}, + dictWord{7, 0, 1619}, + dictWord{7, 0, 1736}, + dictWord{7, 0, 1775}, + dictWord{8, 0, 590}, + dictWord{9, 0, 324}, + dictWord{9, 0, 736}, + dictWord{ + 9, + 0, + 774, + }, + dictWord{9, 0, 776}, + dictWord{9, 0, 784}, + dictWord{10, 0, 567}, + dictWord{10, 0, 708}, + dictWord{11, 0, 518}, + dictWord{11, 0, 613}, + dictWord{11, 0, 695}, + dictWord{11, 0, 716}, + dictWord{11, 0, 739}, + dictWord{11, 0, 770}, + dictWord{11, 0, 771}, + dictWord{11, 0, 848}, + dictWord{11, 0, 857}, + dictWord{11, 0, 931}, + dictWord{ + 11, + 0, + 947, + }, + dictWord{12, 0, 326}, + dictWord{12, 0, 387}, + dictWord{12, 0, 484}, + dictWord{12, 0, 528}, + dictWord{12, 0, 552}, + dictWord{12, 0, 613}, + dictWord{ + 13, + 0, + 189, + }, + dictWord{13, 0, 256}, + dictWord{13, 0, 340}, + dictWord{13, 0, 432}, + dictWord{13, 0, 436}, + dictWord{13, 0, 440}, + dictWord{13, 0, 454}, + dictWord{14, 0, 174}, + dictWord{14, 0, 220}, + dictWord{14, 0, 284}, + dictWord{14, 0, 390}, + dictWord{145, 0, 121}, + dictWord{135, 10, 158}, + dictWord{9, 0, 137}, + dictWord{138, 0, 221}, + dictWord{4, 11, 110}, + dictWord{10, 11, 415}, + dictWord{10, 11, 597}, + dictWord{142, 11, 206}, + dictWord{141, 11, 496}, + dictWord{135, 11, 205}, + dictWord{ + 151, + 10, + 25, + }, + dictWord{135, 11, 778}, + dictWord{7, 11, 1656}, + dictWord{7, 10, 2001}, + dictWord{9, 11, 369}, + dictWord{10, 11, 338}, + dictWord{10, 11, 490}, + dictWord{11, 11, 154}, + dictWord{11, 11, 545}, + dictWord{11, 11, 775}, + dictWord{13, 11, 77}, + dictWord{141, 11, 274}, + dictWord{4, 11, 444}, + dictWord{ + 10, + 11, + 146, + }, + dictWord{140, 11, 9}, + dictWord{7, 0, 390}, + dictWord{138, 0, 140}, + dictWord{135, 0, 1144}, + dictWord{134, 0, 464}, + dictWord{7, 10, 1461}, + dictWord{ + 140, + 10, + 91, + }, + dictWord{132, 10, 602}, + dictWord{4, 11, 283}, + dictWord{135, 11, 1194}, + dictWord{5, 0, 407}, + dictWord{11, 0, 204}, + dictWord{11, 0, 243}, + dictWord{ + 11, + 0, + 489, + }, + dictWord{12, 0, 293}, + dictWord{19, 0, 37}, + dictWord{20, 0, 73}, + dictWord{150, 0, 38}, + dictWord{7, 0, 1218}, + dictWord{136, 0, 303}, + dictWord{ + 5, + 0, + 325, + }, + dictWord{8, 0, 5}, + dictWord{8, 0, 227}, + dictWord{9, 0, 105}, + dictWord{10, 0, 585}, + dictWord{12, 0, 614}, + dictWord{4, 10, 13}, + dictWord{5, 10, 567}, + dictWord{ + 7, + 10, + 1498, + }, + dictWord{9, 10, 124}, + dictWord{11, 10, 521}, + dictWord{140, 10, 405}, + dictWord{135, 10, 1006}, + dictWord{7, 0, 800}, + dictWord{10, 0, 12}, + dictWord{134, 11, 1720}, + dictWord{135, 0, 1783}, + dictWord{132, 10, 735}, + dictWord{138, 10, 812}, + dictWord{4, 10, 170}, + dictWord{135, 10, 323}, + dictWord{ + 6, + 0, + 621, + }, + dictWord{13, 0, 504}, + dictWord{144, 0, 89}, + dictWord{5, 10, 304}, + dictWord{135, 10, 1403}, + dictWord{137, 11, 216}, + dictWord{6, 0, 920}, + dictWord{ + 6, + 0, + 1104, + }, + dictWord{9, 11, 183}, + dictWord{139, 11, 286}, + dictWord{4, 0, 376}, + dictWord{133, 10, 742}, + dictWord{134, 0, 218}, + dictWord{8, 0, 641}, + dictWord{ + 11, + 0, + 388, + }, + dictWord{140, 0, 580}, + dictWord{7, 0, 454}, + dictWord{7, 0, 782}, + dictWord{8, 0, 768}, + dictWord{140, 0, 686}, + dictWord{137, 11, 33}, + dictWord{ + 133, + 10, + 111, + }, + dictWord{144, 0, 0}, + dictWord{10, 0, 676}, + dictWord{140, 0, 462}, + dictWord{6, 0, 164}, + dictWord{136, 11, 735}, + dictWord{133, 10, 444}, + dictWord{ + 150, + 0, + 50, + }, + dictWord{7, 11, 1862}, + dictWord{12, 11, 491}, + dictWord{12, 11, 520}, + dictWord{13, 11, 383}, + dictWord{14, 11, 244}, + dictWord{146, 11, 12}, + dictWord{ + 5, + 11, + 132, + }, + dictWord{9, 11, 486}, + dictWord{9, 11, 715}, + dictWord{10, 11, 458}, + dictWord{11, 11, 373}, + dictWord{11, 11, 668}, + dictWord{11, 11, 795}, + dictWord{11, 11, 897}, + dictWord{12, 11, 272}, + dictWord{12, 11, 424}, + dictWord{12, 11, 539}, + dictWord{12, 11, 558}, + dictWord{14, 11, 245}, + dictWord{ + 14, + 11, + 263, + }, + dictWord{14, 11, 264}, + dictWord{14, 11, 393}, + dictWord{142, 11, 403}, + dictWord{8, 10, 123}, + dictWord{15, 10, 6}, + dictWord{144, 10, 7}, + dictWord{ + 6, + 0, + 285, + }, + dictWord{8, 0, 654}, + dictWord{11, 0, 749}, + dictWord{12, 0, 190}, + dictWord{12, 0, 327}, + dictWord{13, 0, 120}, + dictWord{13, 0, 121}, + dictWord{13, 0, 327}, + dictWord{15, 0, 47}, + dictWord{146, 0, 40}, + dictWord{5, 11, 8}, + dictWord{6, 11, 89}, + dictWord{6, 11, 400}, + dictWord{7, 11, 1569}, + dictWord{7, 11, 1623}, + dictWord{ + 7, + 11, + 1850, + }, + dictWord{8, 11, 218}, + dictWord{8, 11, 422}, + dictWord{9, 11, 570}, + dictWord{138, 11, 626}, + dictWord{6, 11, 387}, + dictWord{7, 11, 882}, + dictWord{141, 11, 111}, + dictWord{6, 0, 343}, + dictWord{7, 0, 195}, + dictWord{9, 0, 226}, + dictWord{10, 0, 197}, + dictWord{10, 0, 575}, + dictWord{11, 0, 502}, + dictWord{ + 11, + 0, + 899, + }, + dictWord{6, 11, 224}, + dictWord{7, 11, 877}, + dictWord{137, 11, 647}, + dictWord{5, 10, 937}, + dictWord{135, 10, 100}, + dictWord{135, 11, 790}, + dictWord{150, 0, 29}, + dictWord{147, 0, 8}, + dictWord{134, 0, 1812}, + dictWord{149, 0, 8}, + dictWord{135, 11, 394}, + dictWord{7, 0, 1125}, + dictWord{9, 0, 143}, + dictWord{ + 11, + 0, + 61, + }, + dictWord{14, 0, 405}, + dictWord{150, 0, 21}, + dictWord{10, 11, 755}, + dictWord{147, 11, 29}, + dictWord{9, 11, 378}, + dictWord{141, 11, 162}, + dictWord{135, 10, 922}, + dictWord{5, 10, 619}, + dictWord{133, 10, 698}, + dictWord{134, 0, 1327}, + dictWord{6, 0, 1598}, + dictWord{137, 0, 575}, + dictWord{ + 9, + 11, + 569, + }, + dictWord{12, 11, 12}, + dictWord{12, 11, 81}, + dictWord{12, 11, 319}, + dictWord{13, 11, 69}, + dictWord{14, 11, 259}, + dictWord{16, 11, 87}, + dictWord{ + 17, + 11, + 1, + }, + dictWord{17, 11, 21}, + dictWord{17, 11, 24}, + dictWord{18, 11, 15}, + dictWord{18, 11, 56}, + dictWord{18, 11, 59}, + dictWord{18, 11, 127}, + dictWord{18, 11, 154}, + dictWord{19, 11, 19}, + dictWord{148, 11, 31}, + dictWord{6, 0, 895}, + dictWord{135, 11, 1231}, + dictWord{5, 0, 959}, + dictWord{7, 11, 124}, + dictWord{136, 11, 38}, + dictWord{5, 11, 261}, + dictWord{7, 11, 78}, + dictWord{7, 11, 199}, + dictWord{8, 11, 815}, + dictWord{9, 11, 126}, + dictWord{138, 11, 342}, + dictWord{5, 10, 917}, + dictWord{134, 10, 1659}, + dictWord{7, 0, 1759}, + dictWord{5, 11, 595}, + dictWord{135, 11, 1863}, + dictWord{136, 0, 173}, + dictWord{134, 0, 266}, + dictWord{ + 142, + 0, + 261, + }, + dictWord{132, 11, 628}, + dictWord{5, 10, 251}, + dictWord{5, 10, 956}, + dictWord{8, 10, 268}, + dictWord{9, 10, 214}, + dictWord{146, 10, 142}, + dictWord{ + 7, + 11, + 266, + }, + dictWord{136, 11, 804}, + dictWord{135, 11, 208}, + dictWord{6, 11, 79}, + dictWord{7, 11, 1021}, + dictWord{135, 11, 1519}, + dictWord{11, 11, 704}, + dictWord{141, 11, 396}, + dictWord{5, 10, 346}, + dictWord{5, 10, 711}, + dictWord{136, 10, 390}, + dictWord{136, 11, 741}, + dictWord{134, 11, 376}, + dictWord{ + 134, + 0, + 1427, + }, + dictWord{6, 0, 1033}, + dictWord{6, 0, 1217}, + dictWord{136, 0, 300}, + dictWord{133, 10, 624}, + dictWord{6, 11, 100}, + dictWord{7, 11, 244}, + dictWord{ + 7, + 11, + 632, + }, + dictWord{7, 11, 1609}, + dictWord{8, 11, 178}, + dictWord{8, 11, 638}, + dictWord{141, 11, 58}, + dictWord{6, 0, 584}, + dictWord{5, 10, 783}, + dictWord{ + 7, + 10, + 1998, + }, + dictWord{135, 10, 2047}, + dictWord{5, 0, 427}, + dictWord{5, 0, 734}, + dictWord{7, 0, 478}, + dictWord{136, 0, 52}, + dictWord{7, 0, 239}, + dictWord{ + 11, + 0, + 217, + }, + dictWord{142, 0, 165}, + dictWord{134, 0, 1129}, + dictWord{6, 0, 168}, + dictWord{6, 0, 1734}, + dictWord{7, 0, 20}, + dictWord{7, 0, 1056}, + dictWord{8, 0, 732}, + dictWord{9, 0, 406}, + dictWord{9, 0, 911}, + dictWord{138, 0, 694}, + dictWord{132, 10, 594}, + dictWord{133, 11, 791}, + dictWord{7, 11, 686}, + dictWord{8, 11, 33}, + dictWord{8, 11, 238}, + dictWord{10, 11, 616}, + dictWord{11, 11, 467}, + dictWord{11, 11, 881}, + dictWord{13, 11, 217}, + dictWord{13, 11, 253}, + dictWord{ + 142, + 11, + 268, + }, + dictWord{137, 11, 476}, + dictWord{134, 0, 418}, + dictWord{133, 0, 613}, + dictWord{132, 0, 632}, + dictWord{132, 11, 447}, + dictWord{7, 0, 32}, + dictWord{ + 7, + 0, + 984, + }, + dictWord{8, 0, 85}, + dictWord{8, 0, 709}, + dictWord{9, 0, 579}, + dictWord{9, 0, 847}, + dictWord{9, 0, 856}, + dictWord{10, 0, 799}, + dictWord{11, 0, 258}, + dictWord{ + 11, + 0, + 1007, + }, + dictWord{12, 0, 331}, + dictWord{12, 0, 615}, + dictWord{13, 0, 188}, + dictWord{13, 0, 435}, + dictWord{14, 0, 8}, + dictWord{15, 0, 165}, + dictWord{ + 16, + 0, + 27, + }, + dictWord{20, 0, 40}, + dictWord{144, 11, 35}, + dictWord{4, 11, 128}, + dictWord{5, 11, 415}, + dictWord{6, 11, 462}, + dictWord{7, 11, 294}, + dictWord{7, 11, 578}, + dictWord{10, 11, 710}, + dictWord{139, 11, 86}, + dictWord{5, 0, 694}, + dictWord{136, 0, 909}, + dictWord{7, 0, 1109}, + dictWord{11, 0, 7}, + dictWord{5, 10, 37}, + dictWord{ + 6, + 10, + 39, + }, + dictWord{6, 10, 451}, + dictWord{7, 10, 218}, + dictWord{7, 10, 1166}, + dictWord{7, 10, 1687}, + dictWord{8, 10, 662}, + dictWord{144, 10, 2}, + dictWord{ + 136, + 11, + 587, + }, + dictWord{6, 11, 427}, + dictWord{7, 11, 1018}, + dictWord{138, 11, 692}, + dictWord{4, 11, 195}, + dictWord{6, 10, 508}, + dictWord{135, 11, 802}, + dictWord{4, 0, 167}, + dictWord{135, 0, 82}, + dictWord{5, 0, 62}, + dictWord{6, 0, 24}, + dictWord{6, 0, 534}, + dictWord{7, 0, 74}, + dictWord{7, 0, 678}, + dictWord{7, 0, 684}, + dictWord{ + 7, + 0, + 1043, + }, + dictWord{7, 0, 1072}, + dictWord{8, 0, 280}, + dictWord{8, 0, 541}, + dictWord{8, 0, 686}, + dictWord{9, 0, 258}, + dictWord{10, 0, 519}, + dictWord{11, 0, 252}, + dictWord{140, 0, 282}, + dictWord{138, 0, 33}, + dictWord{4, 0, 359}, + dictWord{133, 11, 738}, + dictWord{7, 0, 980}, + dictWord{9, 0, 328}, + dictWord{13, 0, 186}, + dictWord{13, 0, 364}, + dictWord{7, 10, 635}, + dictWord{7, 10, 796}, + dictWord{8, 10, 331}, + dictWord{9, 10, 330}, + dictWord{9, 10, 865}, + dictWord{10, 10, 119}, + dictWord{ + 10, + 10, + 235, + }, + dictWord{11, 10, 111}, + dictWord{11, 10, 129}, + dictWord{11, 10, 240}, + dictWord{12, 10, 31}, + dictWord{12, 10, 66}, + dictWord{12, 10, 222}, + dictWord{12, 10, 269}, + dictWord{12, 10, 599}, + dictWord{12, 10, 684}, + dictWord{12, 10, 689}, + dictWord{12, 10, 691}, + dictWord{142, 10, 345}, + dictWord{ + 137, + 10, + 527, + }, + dictWord{6, 0, 596}, + dictWord{7, 0, 585}, + dictWord{135, 10, 702}, + dictWord{134, 11, 1683}, + dictWord{133, 0, 211}, + dictWord{6, 0, 145}, + dictWord{ + 141, + 0, + 336, + }, + dictWord{134, 0, 1130}, + dictWord{7, 0, 873}, + dictWord{6, 10, 37}, + dictWord{7, 10, 1666}, + dictWord{8, 10, 195}, + dictWord{8, 10, 316}, + dictWord{ + 9, + 10, + 178, + }, + dictWord{9, 10, 276}, + dictWord{9, 10, 339}, + dictWord{9, 10, 536}, + dictWord{10, 10, 102}, + dictWord{10, 10, 362}, + dictWord{10, 10, 785}, + dictWord{ + 11, + 10, + 55, + }, + dictWord{11, 10, 149}, + dictWord{11, 10, 773}, + dictWord{13, 10, 416}, + dictWord{13, 10, 419}, + dictWord{14, 10, 38}, + dictWord{14, 10, 41}, + dictWord{ + 142, + 10, + 210, + }, + dictWord{8, 0, 840}, + dictWord{136, 0, 841}, + dictWord{132, 0, 263}, + dictWord{5, 11, 3}, + dictWord{8, 11, 578}, + dictWord{9, 11, 118}, + dictWord{ + 10, + 11, + 705, + }, + dictWord{12, 11, 383}, + dictWord{141, 11, 279}, + dictWord{132, 0, 916}, + dictWord{133, 11, 229}, + dictWord{133, 10, 645}, + dictWord{15, 0, 155}, + dictWord{16, 0, 79}, + dictWord{8, 11, 102}, + dictWord{10, 11, 578}, + dictWord{10, 11, 672}, + dictWord{12, 11, 496}, + dictWord{13, 11, 408}, + dictWord{14, 11, 121}, + dictWord{145, 11, 106}, + dictWord{4, 0, 599}, + dictWord{5, 0, 592}, + dictWord{6, 0, 1634}, + dictWord{7, 0, 5}, + dictWord{7, 0, 55}, + dictWord{7, 0, 67}, + dictWord{7, 0, 97}, + dictWord{7, 0, 691}, + dictWord{7, 0, 979}, + dictWord{7, 0, 1600}, + dictWord{7, 0, 1697}, + dictWord{8, 0, 207}, + dictWord{8, 0, 214}, + dictWord{8, 0, 231}, + dictWord{8, 0, 294}, + dictWord{8, 0, 336}, + dictWord{8, 0, 428}, + dictWord{8, 0, 471}, + dictWord{8, 0, 622}, + dictWord{8, 0, 626}, + dictWord{8, 0, 679}, + dictWord{8, 0, 759}, + dictWord{8, 0, 829}, + dictWord{9, 0, 11}, + dictWord{9, 0, 246}, + dictWord{9, 0, 484}, + dictWord{9, 0, 573}, + dictWord{9, 0, 706}, + dictWord{9, 0, 762}, + dictWord{9, 0, 798}, + dictWord{9, 0, 855}, + dictWord{9, 0, 870}, + dictWord{9, 0, 912}, + dictWord{10, 0, 303}, + dictWord{10, 0, 335}, + dictWord{10, 0, 424}, + dictWord{10, 0, 461}, + dictWord{10, 0, 543}, + dictWord{ + 10, + 0, + 759, + }, + dictWord{10, 0, 814}, + dictWord{11, 0, 59}, + dictWord{11, 0, 199}, + dictWord{11, 0, 235}, + dictWord{11, 0, 590}, + dictWord{11, 0, 631}, + dictWord{11, 0, 929}, + dictWord{11, 0, 963}, + dictWord{11, 0, 987}, + dictWord{12, 0, 114}, + dictWord{12, 0, 182}, + dictWord{12, 0, 226}, + dictWord{12, 0, 332}, + dictWord{12, 0, 439}, + dictWord{12, 0, 575}, + dictWord{12, 0, 598}, + dictWord{12, 0, 675}, + dictWord{13, 0, 8}, + dictWord{13, 0, 125}, + dictWord{13, 0, 194}, + dictWord{13, 0, 287}, + dictWord{ + 14, + 0, + 197, + }, + dictWord{14, 0, 383}, + dictWord{15, 0, 53}, + dictWord{17, 0, 63}, + dictWord{19, 0, 46}, + dictWord{19, 0, 98}, + dictWord{19, 0, 106}, + dictWord{148, 0, 85}, + dictWord{ + 7, + 0, + 1356, + }, + dictWord{132, 10, 290}, + dictWord{6, 10, 70}, + dictWord{7, 10, 1292}, + dictWord{10, 10, 762}, + dictWord{139, 10, 288}, + dictWord{150, 11, 55}, + dictWord{4, 0, 593}, + dictWord{8, 11, 115}, + dictWord{8, 11, 350}, + dictWord{9, 11, 489}, + dictWord{10, 11, 128}, + dictWord{11, 11, 306}, + dictWord{12, 11, 373}, + dictWord{14, 11, 30}, + dictWord{17, 11, 79}, + dictWord{147, 11, 80}, + dictWord{135, 11, 1235}, + dictWord{134, 0, 1392}, + dictWord{4, 11, 230}, + dictWord{ + 133, + 11, + 702, + }, + dictWord{147, 0, 126}, + dictWord{7, 10, 131}, + dictWord{7, 10, 422}, + dictWord{8, 10, 210}, + dictWord{140, 10, 573}, + dictWord{134, 0, 1179}, + dictWord{ + 139, + 11, + 435, + }, + dictWord{139, 10, 797}, + dictWord{134, 11, 1728}, + dictWord{4, 0, 162}, + dictWord{18, 11, 26}, + dictWord{19, 11, 42}, + dictWord{20, 11, 43}, + dictWord{21, 11, 0}, + dictWord{23, 11, 27}, + dictWord{152, 11, 14}, + dictWord{132, 10, 936}, + dictWord{6, 0, 765}, + dictWord{5, 10, 453}, + dictWord{134, 10, 441}, + dictWord{133, 0, 187}, + dictWord{135, 0, 1286}, + dictWord{6, 0, 635}, + dictWord{6, 0, 904}, + dictWord{6, 0, 1210}, + dictWord{134, 0, 1489}, + dictWord{4, 0, 215}, + dictWord{ + 8, + 0, + 890, + }, + dictWord{9, 0, 38}, + dictWord{10, 0, 923}, + dictWord{11, 0, 23}, + dictWord{11, 0, 127}, + dictWord{139, 0, 796}, + dictWord{6, 0, 1165}, + dictWord{ + 134, + 0, + 1306, + }, + dictWord{7, 0, 716}, + dictWord{13, 0, 97}, + dictWord{141, 0, 251}, + dictWord{132, 10, 653}, + dictWord{136, 0, 657}, + dictWord{146, 10, 80}, + dictWord{ + 5, + 11, + 622, + }, + dictWord{7, 11, 1032}, + dictWord{11, 11, 26}, + dictWord{11, 11, 213}, + dictWord{11, 11, 707}, + dictWord{12, 11, 380}, + dictWord{13, 11, 226}, + dictWord{141, 11, 355}, + dictWord{6, 0, 299}, + dictWord{5, 11, 70}, + dictWord{6, 11, 334}, + dictWord{9, 11, 171}, + dictWord{11, 11, 637}, + dictWord{12, 11, 202}, + dictWord{14, 11, 222}, + dictWord{145, 11, 42}, + dictWord{142, 0, 134}, + dictWord{4, 11, 23}, + dictWord{5, 11, 313}, + dictWord{5, 11, 1014}, + dictWord{6, 11, 50}, + dictWord{ + 6, + 11, + 51, + }, + dictWord{7, 11, 142}, + dictWord{7, 11, 384}, + dictWord{9, 11, 783}, + dictWord{139, 11, 741}, + dictWord{4, 11, 141}, + dictWord{7, 11, 559}, + dictWord{ + 8, + 11, + 640, + }, + dictWord{9, 11, 460}, + dictWord{12, 11, 183}, + dictWord{141, 11, 488}, + dictWord{136, 11, 614}, + dictWord{7, 10, 1368}, + dictWord{8, 10, 232}, + dictWord{8, 10, 361}, + dictWord{10, 10, 682}, + dictWord{138, 10, 742}, + dictWord{137, 10, 534}, + dictWord{6, 0, 1082}, + dictWord{140, 0, 658}, + dictWord{ + 137, + 10, + 27, + }, + dictWord{135, 0, 2002}, + dictWord{142, 10, 12}, + dictWord{4, 0, 28}, + dictWord{5, 0, 440}, + dictWord{7, 0, 248}, + dictWord{11, 0, 833}, + dictWord{140, 0, 344}, + dictWord{7, 10, 736}, + dictWord{139, 10, 264}, + dictWord{134, 10, 1657}, + dictWord{134, 0, 1654}, + dictWord{138, 0, 531}, + dictWord{5, 11, 222}, + dictWord{ + 9, + 11, + 140, + }, + dictWord{138, 11, 534}, + dictWord{6, 0, 634}, + dictWord{6, 0, 798}, + dictWord{134, 0, 840}, + dictWord{138, 11, 503}, + dictWord{135, 10, 127}, + dictWord{133, 0, 853}, + dictWord{5, 11, 154}, + dictWord{7, 11, 1491}, + dictWord{10, 11, 379}, + dictWord{138, 11, 485}, + dictWord{6, 0, 249}, + dictWord{7, 0, 1234}, + dictWord{139, 0, 573}, + dictWord{133, 11, 716}, + dictWord{7, 11, 1570}, + dictWord{140, 11, 542}, + dictWord{136, 10, 364}, + dictWord{138, 0, 527}, + dictWord{ + 4, + 11, + 91, + }, + dictWord{5, 11, 388}, + dictWord{5, 11, 845}, + dictWord{6, 11, 206}, + dictWord{6, 11, 252}, + dictWord{6, 11, 365}, + dictWord{7, 11, 136}, + dictWord{7, 11, 531}, + dictWord{8, 11, 264}, + dictWord{136, 11, 621}, + dictWord{134, 0, 1419}, + dictWord{135, 11, 1441}, + dictWord{7, 0, 49}, + dictWord{7, 0, 392}, + dictWord{8, 0, 20}, + dictWord{8, 0, 172}, + dictWord{8, 0, 690}, + dictWord{9, 0, 383}, + dictWord{9, 0, 845}, + dictWord{10, 0, 48}, + dictWord{11, 0, 293}, + dictWord{11, 0, 832}, + dictWord{ + 11, + 0, + 920, + }, + dictWord{11, 0, 984}, + dictWord{141, 0, 221}, + dictWord{5, 0, 858}, + dictWord{133, 0, 992}, + dictWord{5, 0, 728}, + dictWord{137, 10, 792}, + dictWord{ + 5, + 10, + 909, + }, + dictWord{9, 10, 849}, + dictWord{138, 10, 805}, + dictWord{7, 0, 525}, + dictWord{7, 0, 1579}, + dictWord{8, 0, 497}, + dictWord{136, 0, 573}, + dictWord{6, 0, 268}, + dictWord{137, 0, 62}, + dictWord{135, 11, 576}, + dictWord{134, 0, 1201}, + dictWord{5, 11, 771}, + dictWord{5, 11, 863}, + dictWord{5, 11, 898}, + dictWord{ + 6, + 11, + 1632, + }, + dictWord{6, 11, 1644}, + dictWord{134, 11, 1780}, + dictWord{133, 11, 331}, + dictWord{7, 0, 193}, + dictWord{7, 0, 1105}, + dictWord{10, 0, 495}, + dictWord{ + 7, + 10, + 397, + }, + dictWord{8, 10, 124}, + dictWord{8, 10, 619}, + dictWord{9, 10, 305}, + dictWord{11, 10, 40}, + dictWord{12, 10, 349}, + dictWord{13, 10, 134}, + dictWord{ + 13, + 10, + 295, + }, + dictWord{14, 10, 155}, + dictWord{15, 10, 120}, + dictWord{146, 10, 105}, + dictWord{138, 0, 106}, + dictWord{6, 0, 859}, + dictWord{5, 11, 107}, + dictWord{ + 7, + 11, + 201, + }, + dictWord{136, 11, 518}, + dictWord{6, 11, 446}, + dictWord{135, 11, 1817}, + dictWord{13, 0, 23}, + dictWord{4, 10, 262}, + dictWord{135, 10, 342}, + dictWord{133, 10, 641}, + dictWord{137, 11, 851}, + dictWord{6, 0, 925}, + dictWord{137, 0, 813}, + dictWord{132, 11, 504}, + dictWord{6, 0, 613}, + dictWord{ + 136, + 0, + 223, + }, + dictWord{4, 10, 99}, + dictWord{6, 10, 250}, + dictWord{6, 10, 346}, + dictWord{8, 10, 127}, + dictWord{138, 10, 81}, + dictWord{136, 0, 953}, + dictWord{ + 132, + 10, + 915, + }, + dictWord{139, 11, 892}, + dictWord{5, 10, 75}, + dictWord{9, 10, 517}, + dictWord{10, 10, 470}, + dictWord{12, 10, 155}, + dictWord{141, 10, 224}, + dictWord{ + 4, + 0, + 666, + }, + dictWord{7, 0, 1017}, + dictWord{7, 11, 996}, + dictWord{138, 11, 390}, + dictWord{5, 11, 883}, + dictWord{133, 11, 975}, + dictWord{14, 10, 83}, + dictWord{ + 142, + 11, + 83, + }, + dictWord{4, 0, 670}, + dictWord{5, 11, 922}, + dictWord{134, 11, 1707}, + dictWord{135, 0, 216}, + dictWord{9, 0, 40}, + dictWord{11, 0, 136}, + dictWord{ + 135, + 11, + 787, + }, + dictWord{5, 10, 954}, + dictWord{5, 11, 993}, + dictWord{7, 11, 515}, + dictWord{137, 11, 91}, + dictWord{139, 0, 259}, + dictWord{7, 0, 1114}, + dictWord{ + 9, + 0, + 310, + }, + dictWord{9, 0, 682}, + dictWord{10, 0, 440}, + dictWord{13, 0, 40}, + dictWord{6, 10, 304}, + dictWord{8, 10, 418}, + dictWord{11, 10, 341}, + dictWord{ + 139, + 10, + 675, + }, + dictWord{14, 0, 296}, + dictWord{9, 10, 410}, + dictWord{139, 10, 425}, + dictWord{10, 11, 377}, + dictWord{12, 11, 363}, + dictWord{13, 11, 68}, + dictWord{ + 13, + 11, + 94, + }, + dictWord{14, 11, 108}, + dictWord{142, 11, 306}, + dictWord{7, 0, 1401}, + dictWord{135, 0, 1476}, + dictWord{4, 0, 296}, + dictWord{6, 0, 475}, + dictWord{ + 7, + 0, + 401, + }, + dictWord{7, 0, 1410}, + dictWord{7, 0, 1594}, + dictWord{7, 0, 1674}, + dictWord{8, 0, 63}, + dictWord{8, 0, 660}, + dictWord{137, 0, 74}, + dictWord{4, 0, 139}, + dictWord{4, 0, 388}, + dictWord{140, 0, 188}, + dictWord{132, 0, 797}, + dictWord{132, 11, 766}, + dictWord{5, 11, 103}, + dictWord{7, 11, 921}, + dictWord{8, 11, 580}, + dictWord{8, 11, 593}, + dictWord{8, 11, 630}, + dictWord{138, 11, 28}, + dictWord{4, 11, 911}, + dictWord{5, 11, 867}, + dictWord{133, 11, 1013}, + dictWord{134, 10, 14}, + dictWord{134, 0, 1572}, + dictWord{134, 10, 1708}, + dictWord{21, 0, 39}, + dictWord{5, 10, 113}, + dictWord{6, 10, 243}, + dictWord{7, 10, 1865}, + dictWord{ + 11, + 10, + 161, + }, + dictWord{16, 10, 37}, + dictWord{145, 10, 99}, + dictWord{7, 11, 1563}, + dictWord{141, 11, 182}, + dictWord{5, 11, 135}, + dictWord{6, 11, 519}, + dictWord{ + 7, + 11, + 1722, + }, + dictWord{10, 11, 271}, + dictWord{11, 11, 261}, + dictWord{145, 11, 54}, + dictWord{132, 10, 274}, + dictWord{134, 0, 1594}, + dictWord{4, 11, 300}, + dictWord{5, 11, 436}, + dictWord{135, 11, 484}, + dictWord{4, 0, 747}, + dictWord{6, 0, 290}, + dictWord{7, 0, 649}, + dictWord{7, 0, 1479}, + dictWord{135, 0, 1583}, + dictWord{133, 11, 535}, + dictWord{147, 11, 82}, + dictWord{133, 0, 232}, + dictWord{137, 0, 887}, + dictWord{135, 10, 166}, + dictWord{136, 0, 521}, + dictWord{4, 0, 14}, + dictWord{7, 0, 472}, + dictWord{7, 0, 1801}, + dictWord{10, 0, 748}, + dictWord{141, 0, 458}, + dictWord{134, 0, 741}, + dictWord{134, 0, 992}, + dictWord{16, 0, 111}, + dictWord{137, 10, 304}, + dictWord{4, 0, 425}, + dictWord{5, 11, 387}, + dictWord{7, 11, 557}, + dictWord{12, 11, 547}, + dictWord{142, 11, 86}, + dictWord{ + 135, + 11, + 1747, + }, + dictWord{5, 10, 654}, + dictWord{135, 11, 1489}, + dictWord{7, 0, 789}, + dictWord{4, 11, 6}, + dictWord{5, 11, 708}, + dictWord{136, 11, 75}, + dictWord{ + 6, + 10, + 273, + }, + dictWord{10, 10, 188}, + dictWord{13, 10, 377}, + dictWord{146, 10, 77}, + dictWord{6, 0, 1593}, + dictWord{4, 11, 303}, + dictWord{7, 11, 619}, + dictWord{ + 10, + 11, + 547, + }, + dictWord{10, 11, 687}, + dictWord{11, 11, 122}, + dictWord{140, 11, 601}, + dictWord{134, 0, 1768}, + dictWord{135, 10, 410}, + dictWord{138, 11, 772}, + dictWord{11, 0, 233}, + dictWord{139, 10, 524}, + dictWord{5, 0, 943}, + dictWord{134, 0, 1779}, + dictWord{134, 10, 1785}, + dictWord{136, 11, 529}, + dictWord{ + 132, + 0, + 955, + }, + dictWord{5, 0, 245}, + dictWord{6, 0, 576}, + dictWord{7, 0, 582}, + dictWord{136, 0, 225}, + dictWord{132, 10, 780}, + dictWord{142, 0, 241}, + dictWord{ + 134, + 0, + 1943, + }, + dictWord{4, 11, 106}, + dictWord{7, 11, 310}, + dictWord{7, 11, 1785}, + dictWord{10, 11, 690}, + dictWord{139, 11, 717}, + dictWord{134, 0, 1284}, + dictWord{5, 11, 890}, + dictWord{133, 11, 988}, + dictWord{6, 11, 626}, + dictWord{142, 11, 431}, + dictWord{10, 11, 706}, + dictWord{145, 11, 32}, + dictWord{ + 137, + 11, + 332, + }, + dictWord{132, 11, 698}, + dictWord{135, 0, 709}, + dictWord{5, 10, 948}, + dictWord{138, 11, 17}, + dictWord{136, 0, 554}, + dictWord{134, 0, 1564}, + dictWord{139, 10, 941}, + dictWord{132, 0, 443}, + dictWord{134, 0, 909}, + dictWord{134, 11, 84}, + dictWord{142, 0, 280}, + dictWord{4, 10, 532}, + dictWord{5, 10, 706}, + dictWord{135, 10, 662}, + dictWord{132, 0, 729}, + dictWord{5, 10, 837}, + dictWord{6, 10, 1651}, + dictWord{139, 10, 985}, + dictWord{135, 10, 1861}, + dictWord{ + 4, + 0, + 348, + }, + dictWord{152, 11, 3}, + dictWord{5, 11, 986}, + dictWord{6, 11, 130}, + dictWord{7, 11, 1582}, + dictWord{8, 11, 458}, + dictWord{10, 11, 101}, + dictWord{ + 10, + 11, + 318, + }, + dictWord{138, 11, 823}, + dictWord{134, 0, 758}, + dictWord{4, 0, 298}, + dictWord{137, 0, 848}, + dictWord{4, 10, 330}, + dictWord{7, 10, 933}, + dictWord{ + 7, + 10, + 2012, + }, + dictWord{136, 10, 292}, + dictWord{7, 11, 1644}, + dictWord{137, 11, 129}, + dictWord{6, 0, 1422}, + dictWord{9, 0, 829}, + dictWord{135, 10, 767}, + dictWord{5, 0, 164}, + dictWord{7, 0, 121}, + dictWord{142, 0, 189}, + dictWord{7, 0, 812}, + dictWord{7, 0, 1261}, + dictWord{7, 0, 1360}, + dictWord{9, 0, 632}, + dictWord{ + 140, + 0, + 352, + }, + dictWord{135, 11, 1788}, + dictWord{139, 0, 556}, + dictWord{135, 11, 997}, + dictWord{145, 10, 114}, + dictWord{4, 0, 172}, + dictWord{9, 0, 611}, + dictWord{10, 0, 436}, + dictWord{12, 0, 673}, + dictWord{13, 0, 255}, + dictWord{137, 10, 883}, + dictWord{11, 0, 530}, + dictWord{138, 10, 274}, + dictWord{133, 0, 844}, + dictWord{134, 0, 984}, + dictWord{13, 0, 232}, + dictWord{18, 0, 35}, + dictWord{4, 10, 703}, + dictWord{135, 10, 207}, + dictWord{132, 10, 571}, + dictWord{9, 0, 263}, + dictWord{10, 0, 147}, + dictWord{138, 0, 492}, + dictWord{7, 11, 1756}, + dictWord{137, 11, 98}, + dictWord{5, 10, 873}, + dictWord{5, 10, 960}, + dictWord{8, 10, 823}, + dictWord{137, 10, 881}, + dictWord{133, 0, 537}, + dictWord{132, 0, 859}, + dictWord{7, 11, 1046}, + dictWord{139, 11, 160}, + dictWord{137, 0, 842}, + dictWord{ + 139, + 10, + 283, + }, + dictWord{5, 10, 33}, + dictWord{6, 10, 470}, + dictWord{139, 10, 424}, + dictWord{6, 11, 45}, + dictWord{7, 11, 433}, + dictWord{8, 11, 129}, + dictWord{ + 9, + 11, + 21, + }, + dictWord{10, 11, 392}, + dictWord{11, 11, 79}, + dictWord{12, 11, 499}, + dictWord{13, 11, 199}, + dictWord{141, 11, 451}, + dictWord{135, 0, 1291}, + dictWord{135, 10, 1882}, + dictWord{7, 11, 558}, + dictWord{136, 11, 353}, + dictWord{134, 0, 1482}, + dictWord{5, 0, 230}, + dictWord{5, 0, 392}, + dictWord{6, 0, 420}, + dictWord{9, 0, 568}, + dictWord{140, 0, 612}, + dictWord{6, 0, 262}, + dictWord{7, 10, 90}, + dictWord{7, 10, 664}, + dictWord{7, 10, 830}, + dictWord{7, 10, 1380}, + dictWord{ + 7, + 10, + 2025, + }, + dictWord{8, 11, 81}, + dictWord{8, 10, 448}, + dictWord{8, 10, 828}, + dictWord{9, 11, 189}, + dictWord{9, 11, 201}, + dictWord{11, 11, 478}, + dictWord{ + 11, + 11, + 712, + }, + dictWord{141, 11, 338}, + dictWord{142, 0, 31}, + dictWord{5, 11, 353}, + dictWord{151, 11, 26}, + dictWord{132, 0, 753}, + dictWord{4, 0, 0}, + dictWord{ + 5, + 0, + 41, + }, + dictWord{7, 0, 1459}, + dictWord{7, 0, 1469}, + dictWord{7, 0, 1859}, + dictWord{9, 0, 549}, + dictWord{139, 0, 905}, + dictWord{9, 10, 417}, + dictWord{ + 137, + 10, + 493, + }, + dictWord{135, 11, 1113}, + dictWord{133, 0, 696}, + dictWord{141, 11, 448}, + dictWord{134, 10, 295}, + dictWord{132, 0, 834}, + dictWord{4, 0, 771}, + dictWord{5, 10, 1019}, + dictWord{6, 11, 25}, + dictWord{7, 11, 855}, + dictWord{7, 11, 1258}, + dictWord{144, 11, 32}, + dictWord{134, 0, 1076}, + dictWord{133, 0, 921}, + dictWord{133, 0, 674}, + dictWord{4, 11, 4}, + dictWord{7, 11, 1118}, + dictWord{7, 11, 1320}, + dictWord{7, 11, 1706}, + dictWord{8, 11, 277}, + dictWord{9, 11, 622}, + dictWord{10, 11, 9}, + dictWord{11, 11, 724}, + dictWord{12, 11, 350}, + dictWord{12, 11, 397}, + dictWord{13, 11, 28}, + dictWord{13, 11, 159}, + dictWord{15, 11, 89}, + dictWord{18, 11, 5}, + dictWord{19, 11, 9}, + dictWord{20, 11, 34}, + dictWord{150, 11, 47}, + dictWord{134, 10, 208}, + dictWord{6, 0, 444}, + dictWord{136, 0, 308}, + dictWord{ + 6, + 0, + 180, + }, + dictWord{7, 0, 1137}, + dictWord{8, 0, 751}, + dictWord{139, 0, 805}, + dictWord{4, 0, 183}, + dictWord{7, 0, 271}, + dictWord{11, 0, 824}, + dictWord{ + 11, + 0, + 952, + }, + dictWord{13, 0, 278}, + dictWord{13, 0, 339}, + dictWord{13, 0, 482}, + dictWord{14, 0, 424}, + dictWord{148, 0, 99}, + dictWord{7, 11, 317}, + dictWord{ + 135, + 11, + 569, + }, + dictWord{4, 0, 19}, + dictWord{5, 0, 477}, + dictWord{5, 0, 596}, + dictWord{6, 0, 505}, + dictWord{7, 0, 1221}, + dictWord{11, 0, 907}, + dictWord{12, 0, 209}, + dictWord{141, 0, 214}, + dictWord{135, 0, 1215}, + dictWord{6, 0, 271}, + dictWord{7, 0, 398}, + dictWord{8, 0, 387}, + dictWord{10, 0, 344}, + dictWord{7, 10, 448}, + dictWord{ + 7, + 10, + 1629, + }, + dictWord{7, 10, 1813}, + dictWord{8, 10, 442}, + dictWord{9, 10, 710}, + dictWord{10, 10, 282}, + dictWord{138, 10, 722}, + dictWord{11, 10, 844}, + dictWord{12, 10, 104}, + dictWord{140, 10, 625}, + dictWord{134, 11, 255}, + dictWord{133, 10, 787}, + dictWord{134, 0, 1645}, + dictWord{11, 11, 956}, + dictWord{ + 151, + 11, + 3, + }, + dictWord{6, 0, 92}, + dictWord{6, 0, 188}, + dictWord{7, 0, 209}, + dictWord{7, 0, 1269}, + dictWord{7, 0, 1524}, + dictWord{7, 0, 1876}, + dictWord{8, 0, 661}, + dictWord{10, 0, 42}, + dictWord{10, 0, 228}, + dictWord{11, 0, 58}, + dictWord{11, 0, 1020}, + dictWord{12, 0, 58}, + dictWord{12, 0, 118}, + dictWord{141, 0, 32}, + dictWord{ + 4, + 0, + 459, + }, + dictWord{133, 0, 966}, + dictWord{4, 11, 536}, + dictWord{7, 11, 1141}, + dictWord{10, 11, 723}, + dictWord{139, 11, 371}, + dictWord{140, 0, 330}, + dictWord{134, 0, 1557}, + dictWord{7, 11, 285}, + dictWord{135, 11, 876}, + dictWord{136, 10, 491}, + dictWord{135, 11, 560}, + dictWord{6, 0, 18}, + dictWord{7, 0, 179}, + dictWord{7, 0, 932}, + dictWord{8, 0, 548}, + dictWord{8, 0, 757}, + dictWord{9, 0, 54}, + dictWord{9, 0, 65}, + dictWord{9, 0, 532}, + dictWord{9, 0, 844}, + dictWord{10, 0, 113}, + dictWord{10, 0, 117}, + dictWord{10, 0, 315}, + dictWord{10, 0, 560}, + dictWord{10, 0, 622}, + dictWord{10, 0, 798}, + dictWord{11, 0, 153}, + dictWord{11, 0, 351}, + dictWord{ + 11, + 0, + 375, + }, + dictWord{12, 0, 78}, + dictWord{12, 0, 151}, + dictWord{12, 0, 392}, + dictWord{12, 0, 666}, + dictWord{14, 0, 248}, + dictWord{143, 0, 23}, + dictWord{ + 6, + 0, + 1742, + }, + dictWord{132, 11, 690}, + dictWord{4, 10, 403}, + dictWord{5, 10, 441}, + dictWord{7, 10, 450}, + dictWord{10, 10, 840}, + dictWord{11, 10, 101}, + dictWord{ + 12, + 10, + 193, + }, + dictWord{141, 10, 430}, + dictWord{133, 0, 965}, + dictWord{134, 0, 182}, + dictWord{10, 0, 65}, + dictWord{10, 0, 488}, + dictWord{138, 0, 497}, + dictWord{135, 11, 1346}, + dictWord{6, 0, 973}, + dictWord{6, 0, 1158}, + dictWord{10, 11, 200}, + dictWord{19, 11, 2}, + dictWord{151, 11, 22}, + dictWord{4, 11, 190}, + dictWord{133, 11, 554}, + dictWord{133, 10, 679}, + dictWord{7, 0, 328}, + dictWord{137, 10, 326}, + dictWord{133, 11, 1001}, + dictWord{9, 0, 588}, + dictWord{ + 138, + 0, + 260, + }, + dictWord{133, 11, 446}, + dictWord{135, 10, 1128}, + dictWord{135, 10, 1796}, + dictWord{147, 11, 119}, + dictWord{134, 0, 1786}, + dictWord{ + 6, + 0, + 1328, + }, + dictWord{6, 0, 1985}, + dictWord{8, 0, 962}, + dictWord{138, 0, 1017}, + dictWord{135, 0, 308}, + dictWord{11, 0, 508}, + dictWord{4, 10, 574}, + dictWord{ + 7, + 10, + 350, + }, + dictWord{7, 10, 1024}, + dictWord{8, 10, 338}, + dictWord{9, 10, 677}, + dictWord{138, 10, 808}, + dictWord{138, 11, 752}, + dictWord{135, 10, 1081}, + dictWord{137, 11, 96}, + dictWord{7, 10, 1676}, + dictWord{135, 10, 2037}, + dictWord{136, 0, 588}, + dictWord{132, 11, 304}, + dictWord{133, 0, 614}, + dictWord{ + 140, + 0, + 793, + }, + dictWord{136, 0, 287}, + dictWord{137, 10, 297}, + dictWord{141, 10, 37}, + dictWord{6, 11, 53}, + dictWord{6, 11, 199}, + dictWord{7, 11, 1408}, + dictWord{ + 8, + 11, + 32, + }, + dictWord{8, 11, 93}, + dictWord{9, 11, 437}, + dictWord{10, 11, 397}, + dictWord{10, 11, 629}, + dictWord{11, 11, 593}, + dictWord{11, 11, 763}, + dictWord{ + 13, + 11, + 326, + }, + dictWord{145, 11, 35}, + dictWord{134, 11, 105}, + dictWord{9, 11, 320}, + dictWord{10, 11, 506}, + dictWord{138, 11, 794}, + dictWord{5, 11, 114}, + dictWord{5, 11, 255}, + dictWord{141, 11, 285}, + dictWord{140, 0, 290}, + dictWord{7, 11, 2035}, + dictWord{8, 11, 19}, + dictWord{9, 11, 89}, + dictWord{138, 11, 831}, + dictWord{134, 0, 1136}, + dictWord{7, 0, 719}, + dictWord{8, 0, 796}, + dictWord{8, 0, 809}, + dictWord{8, 0, 834}, + dictWord{6, 10, 306}, + dictWord{7, 10, 1140}, + dictWord{ + 7, + 10, + 1340, + }, + dictWord{8, 10, 133}, + dictWord{138, 10, 449}, + dictWord{139, 10, 1011}, + dictWord{5, 0, 210}, + dictWord{6, 0, 213}, + dictWord{7, 0, 60}, + dictWord{ + 10, + 0, + 364, + }, + dictWord{139, 0, 135}, + dictWord{5, 0, 607}, + dictWord{8, 0, 326}, + dictWord{136, 0, 490}, + dictWord{138, 11, 176}, + dictWord{132, 0, 701}, + dictWord{ + 5, + 0, + 472, + }, + dictWord{7, 0, 380}, + dictWord{137, 0, 758}, + dictWord{135, 0, 1947}, + dictWord{6, 0, 1079}, + dictWord{138, 0, 278}, + dictWord{138, 11, 391}, + dictWord{ + 5, + 10, + 329, + }, + dictWord{8, 10, 260}, + dictWord{139, 11, 156}, + dictWord{4, 0, 386}, + dictWord{7, 0, 41}, + dictWord{8, 0, 405}, + dictWord{8, 0, 728}, + dictWord{9, 0, 497}, + dictWord{11, 0, 110}, + dictWord{11, 0, 360}, + dictWord{15, 0, 37}, + dictWord{144, 0, 84}, + dictWord{5, 0, 46}, + dictWord{7, 0, 1452}, + dictWord{7, 0, 1480}, + dictWord{ + 8, + 0, + 634, + }, + dictWord{140, 0, 472}, + dictWord{136, 0, 961}, + dictWord{4, 0, 524}, + dictWord{136, 0, 810}, + dictWord{10, 0, 238}, + dictWord{141, 0, 33}, + dictWord{ + 132, + 10, + 657, + }, + dictWord{152, 10, 7}, + dictWord{133, 0, 532}, + dictWord{5, 0, 997}, + dictWord{135, 10, 1665}, + dictWord{7, 11, 594}, + dictWord{7, 11, 851}, + dictWord{ + 7, + 11, + 1858, + }, + dictWord{9, 11, 411}, + dictWord{9, 11, 574}, + dictWord{9, 11, 666}, + dictWord{9, 11, 737}, + dictWord{10, 11, 346}, + dictWord{10, 11, 712}, + dictWord{ + 11, + 11, + 246, + }, + dictWord{11, 11, 432}, + dictWord{11, 11, 517}, + dictWord{11, 11, 647}, + dictWord{11, 11, 679}, + dictWord{11, 11, 727}, + dictWord{12, 11, 304}, + dictWord{12, 11, 305}, + dictWord{12, 11, 323}, + dictWord{12, 11, 483}, + dictWord{12, 11, 572}, + dictWord{12, 11, 593}, + dictWord{12, 11, 602}, + dictWord{ + 13, + 11, + 95, + }, + dictWord{13, 11, 101}, + dictWord{13, 11, 171}, + dictWord{13, 11, 315}, + dictWord{13, 11, 378}, + dictWord{13, 11, 425}, + dictWord{13, 11, 475}, + dictWord{ + 14, + 11, + 63, + }, + dictWord{14, 11, 380}, + dictWord{14, 11, 384}, + dictWord{15, 11, 133}, + dictWord{18, 11, 112}, + dictWord{148, 11, 72}, + dictWord{5, 11, 955}, + dictWord{136, 11, 814}, + dictWord{134, 0, 1301}, + dictWord{5, 10, 66}, + dictWord{7, 10, 1896}, + dictWord{136, 10, 288}, + dictWord{133, 11, 56}, + dictWord{ + 134, + 10, + 1643, + }, + dictWord{6, 0, 1298}, + dictWord{148, 11, 100}, + dictWord{5, 0, 782}, + dictWord{5, 0, 829}, + dictWord{6, 0, 671}, + dictWord{6, 0, 1156}, + dictWord{6, 0, 1738}, + dictWord{137, 11, 621}, + dictWord{4, 0, 306}, + dictWord{5, 0, 570}, + dictWord{7, 0, 1347}, + dictWord{5, 10, 91}, + dictWord{5, 10, 648}, + dictWord{5, 10, 750}, + dictWord{ + 5, + 10, + 781, + }, + dictWord{6, 10, 54}, + dictWord{6, 10, 112}, + dictWord{6, 10, 402}, + dictWord{6, 10, 1732}, + dictWord{7, 10, 315}, + dictWord{7, 10, 749}, + dictWord{ + 7, + 10, + 1900, + }, + dictWord{9, 10, 78}, + dictWord{9, 10, 508}, + dictWord{10, 10, 611}, + dictWord{10, 10, 811}, + dictWord{11, 10, 510}, + dictWord{11, 10, 728}, + dictWord{ + 13, + 10, + 36, + }, + dictWord{14, 10, 39}, + dictWord{16, 10, 83}, + dictWord{17, 10, 124}, + dictWord{148, 10, 30}, + dictWord{8, 10, 570}, + dictWord{9, 11, 477}, + dictWord{ + 141, + 11, + 78, + }, + dictWord{4, 11, 639}, + dictWord{10, 11, 4}, + dictWord{10, 10, 322}, + dictWord{10, 10, 719}, + dictWord{11, 10, 407}, + dictWord{11, 11, 638}, + dictWord{ + 12, + 11, + 177, + }, + dictWord{148, 11, 57}, + dictWord{7, 0, 1823}, + dictWord{139, 0, 693}, + dictWord{7, 0, 759}, + dictWord{5, 11, 758}, + dictWord{8, 10, 125}, + dictWord{ + 8, + 10, + 369, + }, + dictWord{8, 10, 524}, + dictWord{10, 10, 486}, + dictWord{11, 10, 13}, + dictWord{11, 10, 381}, + dictWord{11, 10, 736}, + dictWord{11, 10, 766}, + dictWord{ + 11, + 10, + 845, + }, + dictWord{13, 10, 114}, + dictWord{13, 10, 292}, + dictWord{142, 10, 47}, + dictWord{7, 0, 1932}, + dictWord{6, 10, 1684}, + dictWord{6, 10, 1731}, + dictWord{7, 10, 356}, + dictWord{8, 10, 54}, + dictWord{8, 10, 221}, + dictWord{9, 10, 225}, + dictWord{9, 10, 356}, + dictWord{10, 10, 77}, + dictWord{10, 10, 446}, + dictWord{ + 10, + 10, + 731, + }, + dictWord{12, 10, 404}, + dictWord{141, 10, 491}, + dictWord{135, 11, 552}, + dictWord{135, 11, 1112}, + dictWord{4, 0, 78}, + dictWord{5, 0, 96}, + dictWord{ + 5, + 0, + 182, + }, + dictWord{6, 0, 1257}, + dictWord{7, 0, 1724}, + dictWord{7, 0, 1825}, + dictWord{10, 0, 394}, + dictWord{10, 0, 471}, + dictWord{11, 0, 532}, + dictWord{ + 14, + 0, + 340, + }, + dictWord{145, 0, 88}, + dictWord{139, 11, 328}, + dictWord{135, 0, 1964}, + dictWord{132, 10, 411}, + dictWord{4, 10, 80}, + dictWord{5, 10, 44}, + dictWord{ + 137, + 11, + 133, + }, + dictWord{5, 11, 110}, + dictWord{6, 11, 169}, + dictWord{6, 11, 1702}, + dictWord{7, 11, 400}, + dictWord{8, 11, 538}, + dictWord{9, 11, 184}, + dictWord{ + 9, + 11, + 524, + }, + dictWord{140, 11, 218}, + dictWord{4, 0, 521}, + dictWord{5, 10, 299}, + dictWord{7, 10, 1083}, + dictWord{140, 11, 554}, + dictWord{6, 11, 133}, + dictWord{ + 9, + 11, + 353, + }, + dictWord{12, 11, 628}, + dictWord{146, 11, 79}, + dictWord{6, 0, 215}, + dictWord{7, 0, 584}, + dictWord{7, 0, 1028}, + dictWord{7, 0, 1473}, + dictWord{ + 7, + 0, + 1721, + }, + dictWord{9, 0, 424}, + dictWord{138, 0, 779}, + dictWord{7, 0, 857}, + dictWord{7, 0, 1209}, + dictWord{7, 10, 1713}, + dictWord{9, 10, 537}, + dictWord{ + 10, + 10, + 165, + }, + dictWord{12, 10, 219}, + dictWord{140, 10, 561}, + dictWord{4, 10, 219}, + dictWord{6, 11, 93}, + dictWord{7, 11, 1422}, + dictWord{7, 10, 1761}, + dictWord{ + 7, + 11, + 1851, + }, + dictWord{8, 11, 673}, + dictWord{9, 10, 86}, + dictWord{9, 11, 529}, + dictWord{140, 11, 43}, + dictWord{137, 11, 371}, + dictWord{136, 0, 671}, + dictWord{ + 5, + 0, + 328, + }, + dictWord{135, 0, 918}, + dictWord{132, 0, 529}, + dictWord{9, 11, 25}, + dictWord{10, 11, 467}, + dictWord{138, 11, 559}, + dictWord{4, 11, 335}, + dictWord{ + 135, + 11, + 942, + }, + dictWord{134, 0, 716}, + dictWord{134, 0, 1509}, + dictWord{6, 0, 67}, + dictWord{7, 0, 258}, + dictWord{7, 0, 1630}, + dictWord{9, 0, 354}, + dictWord{ + 9, + 0, + 675, + }, + dictWord{10, 0, 830}, + dictWord{14, 0, 80}, + dictWord{17, 0, 80}, + dictWord{140, 10, 428}, + dictWord{134, 0, 1112}, + dictWord{6, 0, 141}, + dictWord{7, 0, 225}, + dictWord{9, 0, 59}, + dictWord{9, 0, 607}, + dictWord{10, 0, 312}, + dictWord{11, 0, 687}, + dictWord{12, 0, 555}, + dictWord{13, 0, 373}, + dictWord{13, 0, 494}, + dictWord{ + 148, + 0, + 58, + }, + dictWord{133, 10, 514}, + dictWord{8, 11, 39}, + dictWord{10, 11, 773}, + dictWord{11, 11, 84}, + dictWord{12, 11, 205}, + dictWord{142, 11, 1}, + dictWord{ + 8, + 0, + 783, + }, + dictWord{5, 11, 601}, + dictWord{133, 11, 870}, + dictWord{136, 11, 594}, + dictWord{4, 10, 55}, + dictWord{5, 10, 301}, + dictWord{6, 10, 571}, + dictWord{ + 14, + 10, + 49, + }, + dictWord{146, 10, 102}, + dictWord{132, 11, 181}, + dictWord{134, 11, 1652}, + dictWord{133, 10, 364}, + dictWord{4, 11, 97}, + dictWord{5, 11, 147}, + dictWord{6, 11, 286}, + dictWord{7, 11, 1362}, + dictWord{141, 11, 176}, + dictWord{4, 10, 76}, + dictWord{7, 10, 1550}, + dictWord{9, 10, 306}, + dictWord{9, 10, 430}, + dictWord{9, 10, 663}, + dictWord{10, 10, 683}, + dictWord{11, 10, 427}, + dictWord{11, 10, 753}, + dictWord{12, 10, 334}, + dictWord{12, 10, 442}, + dictWord{ + 14, + 10, + 258, + }, + dictWord{14, 10, 366}, + dictWord{143, 10, 131}, + dictWord{137, 10, 52}, + dictWord{6, 0, 955}, + dictWord{134, 0, 1498}, + dictWord{6, 11, 375}, + dictWord{ + 7, + 11, + 169, + }, + dictWord{7, 11, 254}, + dictWord{136, 11, 780}, + dictWord{7, 0, 430}, + dictWord{11, 0, 46}, + dictWord{14, 0, 343}, + dictWord{142, 11, 343}, + dictWord{ + 135, + 0, + 1183, + }, + dictWord{5, 0, 602}, + dictWord{7, 0, 2018}, + dictWord{9, 0, 418}, + dictWord{9, 0, 803}, + dictWord{135, 11, 1447}, + dictWord{8, 0, 677}, + dictWord{ + 135, + 11, + 1044, + }, + dictWord{139, 11, 285}, + dictWord{4, 10, 656}, + dictWord{135, 10, 779}, + dictWord{135, 10, 144}, + dictWord{5, 11, 629}, + dictWord{ + 135, + 11, + 1549, + }, + dictWord{135, 10, 1373}, + dictWord{138, 11, 209}, + dictWord{7, 10, 554}, + dictWord{7, 10, 605}, + dictWord{141, 10, 10}, + dictWord{5, 10, 838}, + dictWord{ + 5, + 10, + 841, + }, + dictWord{134, 10, 1649}, + dictWord{133, 10, 1012}, + dictWord{6, 0, 1357}, + dictWord{134, 0, 1380}, + dictWord{144, 0, 53}, + dictWord{6, 0, 590}, + dictWord{7, 10, 365}, + dictWord{7, 10, 1357}, + dictWord{7, 10, 1497}, + dictWord{8, 10, 154}, + dictWord{141, 10, 281}, + dictWord{133, 10, 340}, + dictWord{ + 132, + 11, + 420, + }, + dictWord{135, 0, 329}, + dictWord{147, 11, 32}, + dictWord{4, 0, 469}, + dictWord{10, 11, 429}, + dictWord{139, 10, 495}, + dictWord{8, 10, 261}, + dictWord{ + 9, + 10, + 144, + }, + dictWord{9, 10, 466}, + dictWord{10, 10, 370}, + dictWord{12, 10, 470}, + dictWord{13, 10, 144}, + dictWord{142, 10, 348}, + dictWord{142, 0, 460}, + dictWord{4, 11, 325}, + dictWord{9, 10, 897}, + dictWord{138, 11, 125}, + dictWord{6, 0, 1743}, + dictWord{6, 10, 248}, + dictWord{9, 10, 546}, + dictWord{10, 10, 535}, + dictWord{11, 10, 681}, + dictWord{141, 10, 135}, + dictWord{4, 0, 990}, + dictWord{5, 0, 929}, + dictWord{6, 0, 340}, + dictWord{8, 0, 376}, + dictWord{8, 0, 807}, + dictWord{ + 8, + 0, + 963, + }, + dictWord{8, 0, 980}, + dictWord{138, 0, 1007}, + dictWord{134, 0, 1603}, + dictWord{140, 0, 250}, + dictWord{4, 11, 714}, + dictWord{133, 11, 469}, + dictWord{134, 10, 567}, + dictWord{136, 10, 445}, + dictWord{5, 0, 218}, + dictWord{7, 0, 1610}, + dictWord{8, 0, 646}, + dictWord{10, 0, 83}, + dictWord{11, 11, 138}, + dictWord{140, 11, 40}, + dictWord{7, 0, 1512}, + dictWord{135, 0, 1794}, + dictWord{135, 11, 1216}, + dictWord{11, 0, 0}, + dictWord{16, 0, 78}, + dictWord{132, 11, 718}, + dictWord{133, 0, 571}, + dictWord{132, 0, 455}, + dictWord{134, 0, 1012}, + dictWord{5, 11, 124}, + dictWord{5, 11, 144}, + dictWord{6, 11, 548}, + dictWord{7, 11, 15}, + dictWord{7, 11, 153}, + dictWord{137, 11, 629}, + dictWord{142, 11, 10}, + dictWord{6, 11, 75}, + dictWord{7, 11, 1531}, + dictWord{8, 11, 416}, + dictWord{9, 11, 240}, + dictWord{9, 11, 275}, + dictWord{10, 11, 100}, + dictWord{11, 11, 658}, + dictWord{11, 11, 979}, + dictWord{12, 11, 86}, + dictWord{13, 11, 468}, + dictWord{14, 11, 66}, + dictWord{14, 11, 207}, + dictWord{15, 11, 20}, + dictWord{15, 11, 25}, + dictWord{144, 11, 58}, + dictWord{132, 10, 577}, + dictWord{5, 11, 141}, + dictWord{ + 5, + 11, + 915, + }, + dictWord{6, 11, 1783}, + dictWord{7, 11, 211}, + dictWord{7, 11, 698}, + dictWord{7, 11, 1353}, + dictWord{9, 11, 83}, + dictWord{9, 11, 281}, + dictWord{ + 10, + 11, + 376, + }, + dictWord{10, 11, 431}, + dictWord{11, 11, 543}, + dictWord{12, 11, 664}, + dictWord{13, 11, 280}, + dictWord{13, 11, 428}, + dictWord{14, 11, 61}, + dictWord{ + 14, + 11, + 128, + }, + dictWord{17, 11, 52}, + dictWord{145, 11, 81}, + dictWord{6, 0, 161}, + dictWord{7, 0, 372}, + dictWord{137, 0, 597}, + dictWord{132, 0, 349}, + dictWord{ + 10, + 11, + 702, + }, + dictWord{139, 11, 245}, + dictWord{134, 0, 524}, + dictWord{134, 10, 174}, + dictWord{6, 0, 432}, + dictWord{9, 0, 751}, + dictWord{139, 0, 322}, + dictWord{147, 11, 94}, + dictWord{4, 11, 338}, + dictWord{133, 11, 400}, + dictWord{5, 0, 468}, + dictWord{10, 0, 325}, + dictWord{11, 0, 856}, + dictWord{12, 0, 345}, + dictWord{143, 0, 104}, + dictWord{133, 0, 223}, + dictWord{132, 0, 566}, + dictWord{4, 11, 221}, + dictWord{5, 11, 659}, + dictWord{5, 11, 989}, + dictWord{7, 11, 697}, + dictWord{7, 11, 1211}, + dictWord{138, 11, 284}, + dictWord{135, 11, 1070}, + dictWord{4, 0, 59}, + dictWord{135, 0, 1394}, + dictWord{6, 0, 436}, + dictWord{11, 0, 481}, + dictWord{5, 10, 878}, + dictWord{133, 10, 972}, + dictWord{4, 0, 48}, + dictWord{5, 0, 271}, + dictWord{135, 0, 953}, + dictWord{5, 0, 610}, + dictWord{136, 0, 457}, + dictWord{ + 4, + 0, + 773, + }, + dictWord{5, 0, 618}, + dictWord{137, 0, 756}, + dictWord{133, 0, 755}, + dictWord{135, 0, 1217}, + dictWord{138, 11, 507}, + dictWord{132, 10, 351}, + dictWord{132, 0, 197}, + dictWord{143, 11, 78}, + dictWord{4, 11, 188}, + dictWord{7, 11, 805}, + dictWord{11, 11, 276}, + dictWord{142, 11, 293}, + dictWord{ + 5, + 11, + 884, + }, + dictWord{139, 11, 991}, + dictWord{132, 10, 286}, + dictWord{10, 0, 259}, + dictWord{10, 0, 428}, + dictWord{7, 10, 438}, + dictWord{7, 10, 627}, + dictWord{ + 7, + 10, + 1516, + }, + dictWord{8, 10, 40}, + dictWord{9, 10, 56}, + dictWord{9, 10, 294}, + dictWord{11, 10, 969}, + dictWord{11, 10, 995}, + dictWord{146, 10, 148}, + dictWord{ + 4, + 0, + 356, + }, + dictWord{5, 0, 217}, + dictWord{5, 0, 492}, + dictWord{5, 0, 656}, + dictWord{8, 0, 544}, + dictWord{136, 11, 544}, + dictWord{5, 0, 259}, + dictWord{6, 0, 1230}, + dictWord{7, 0, 414}, + dictWord{7, 0, 854}, + dictWord{142, 0, 107}, + dictWord{132, 0, 1007}, + dictWord{15, 0, 14}, + dictWord{144, 0, 5}, + dictWord{6, 0, 1580}, + dictWord{ + 132, + 10, + 738, + }, + dictWord{132, 11, 596}, + dictWord{132, 0, 673}, + dictWord{133, 10, 866}, + dictWord{6, 0, 1843}, + dictWord{135, 11, 1847}, + dictWord{4, 0, 165}, + dictWord{7, 0, 1398}, + dictWord{135, 0, 1829}, + dictWord{135, 11, 1634}, + dictWord{147, 11, 65}, + dictWord{6, 0, 885}, + dictWord{6, 0, 1009}, + dictWord{ + 137, + 0, + 809, + }, + dictWord{133, 10, 116}, + dictWord{132, 10, 457}, + dictWord{136, 11, 770}, + dictWord{9, 0, 498}, + dictWord{12, 0, 181}, + dictWord{10, 11, 361}, + dictWord{142, 11, 316}, + dictWord{134, 11, 595}, + dictWord{5, 0, 9}, + dictWord{7, 0, 297}, + dictWord{7, 0, 966}, + dictWord{140, 0, 306}, + dictWord{4, 11, 89}, + dictWord{ + 5, + 11, + 489, + }, + dictWord{6, 11, 315}, + dictWord{7, 11, 553}, + dictWord{7, 11, 1745}, + dictWord{138, 11, 243}, + dictWord{134, 0, 1487}, + dictWord{132, 0, 437}, + dictWord{ + 5, + 0, + 146, + }, + dictWord{6, 0, 411}, + dictWord{138, 0, 721}, + dictWord{5, 10, 527}, + dictWord{6, 10, 189}, + dictWord{135, 10, 859}, + dictWord{11, 10, 104}, + dictWord{ + 11, + 10, + 554, + }, + dictWord{15, 10, 60}, + dictWord{143, 10, 125}, + dictWord{6, 11, 1658}, + dictWord{9, 11, 3}, + dictWord{10, 11, 154}, + dictWord{11, 11, 641}, + dictWord{13, 11, 85}, + dictWord{13, 11, 201}, + dictWord{141, 11, 346}, + dictWord{6, 0, 177}, + dictWord{135, 0, 467}, + dictWord{134, 0, 1377}, + dictWord{ + 134, + 10, + 116, + }, + dictWord{136, 11, 645}, + dictWord{4, 11, 166}, + dictWord{5, 11, 505}, + dictWord{6, 11, 1670}, + dictWord{137, 11, 110}, + dictWord{133, 10, 487}, + dictWord{ + 4, + 10, + 86, + }, + dictWord{5, 10, 667}, + dictWord{5, 10, 753}, + dictWord{6, 10, 316}, + dictWord{6, 10, 455}, + dictWord{135, 10, 946}, + dictWord{133, 0, 200}, + dictWord{132, 0, 959}, + dictWord{6, 0, 1928}, + dictWord{134, 0, 1957}, + dictWord{139, 11, 203}, + dictWord{150, 10, 45}, + dictWord{4, 10, 79}, + dictWord{7, 10, 1773}, + dictWord{10, 10, 450}, + dictWord{11, 10, 589}, + dictWord{13, 10, 332}, + dictWord{13, 10, 493}, + dictWord{14, 10, 183}, + dictWord{14, 10, 334}, + dictWord{ + 14, + 10, + 362, + }, + dictWord{14, 10, 368}, + dictWord{14, 10, 376}, + dictWord{14, 10, 379}, + dictWord{19, 10, 90}, + dictWord{19, 10, 103}, + dictWord{19, 10, 127}, + dictWord{148, 10, 90}, + dictWord{6, 0, 1435}, + dictWord{135, 11, 1275}, + dictWord{134, 0, 481}, + dictWord{7, 11, 445}, + dictWord{8, 11, 307}, + dictWord{8, 11, 704}, + dictWord{10, 11, 41}, + dictWord{10, 11, 439}, + dictWord{11, 11, 237}, + dictWord{11, 11, 622}, + dictWord{140, 11, 201}, + dictWord{135, 11, 869}, + dictWord{ + 4, + 0, + 84, + }, + dictWord{7, 0, 1482}, + dictWord{10, 0, 76}, + dictWord{138, 0, 142}, + dictWord{11, 11, 277}, + dictWord{144, 11, 14}, + dictWord{135, 11, 1977}, + dictWord{ + 4, + 11, + 189, + }, + dictWord{5, 11, 713}, + dictWord{136, 11, 57}, + dictWord{133, 0, 1015}, + dictWord{138, 11, 371}, + dictWord{4, 0, 315}, + dictWord{5, 0, 507}, + dictWord{ + 135, + 0, + 1370, + }, + dictWord{4, 11, 552}, + dictWord{142, 10, 381}, + dictWord{9, 0, 759}, + dictWord{16, 0, 31}, + dictWord{16, 0, 39}, + dictWord{16, 0, 75}, + dictWord{18, 0, 24}, + dictWord{20, 0, 42}, + dictWord{152, 0, 1}, + dictWord{134, 0, 712}, + dictWord{134, 0, 1722}, + dictWord{133, 10, 663}, + dictWord{133, 10, 846}, + dictWord{ + 8, + 0, + 222, + }, + dictWord{8, 0, 476}, + dictWord{9, 0, 238}, + dictWord{11, 0, 516}, + dictWord{11, 0, 575}, + dictWord{15, 0, 109}, + dictWord{146, 0, 100}, + dictWord{7, 0, 1402}, + dictWord{7, 0, 1414}, + dictWord{12, 0, 456}, + dictWord{5, 10, 378}, + dictWord{8, 10, 465}, + dictWord{9, 10, 286}, + dictWord{10, 10, 185}, + dictWord{10, 10, 562}, + dictWord{10, 10, 635}, + dictWord{11, 10, 31}, + dictWord{11, 10, 393}, + dictWord{13, 10, 312}, + dictWord{18, 10, 65}, + dictWord{18, 10, 96}, + dictWord{147, 10, 89}, + dictWord{4, 0, 986}, + dictWord{6, 0, 1958}, + dictWord{6, 0, 2032}, + dictWord{8, 0, 934}, + dictWord{138, 0, 985}, + dictWord{7, 10, 1880}, + dictWord{9, 10, 680}, + dictWord{139, 10, 798}, + dictWord{134, 10, 1770}, + dictWord{145, 11, 49}, + dictWord{132, 11, 614}, + dictWord{132, 10, 648}, + dictWord{5, 10, 945}, + dictWord{ + 6, + 10, + 1656, + }, + dictWord{6, 10, 1787}, + dictWord{7, 10, 167}, + dictWord{8, 10, 824}, + dictWord{9, 10, 391}, + dictWord{10, 10, 375}, + dictWord{139, 10, 185}, + dictWord{138, 11, 661}, + dictWord{7, 0, 1273}, + dictWord{135, 11, 1945}, + dictWord{7, 0, 706}, + dictWord{7, 0, 1058}, + dictWord{138, 0, 538}, + dictWord{7, 10, 1645}, + dictWord{8, 10, 352}, + dictWord{137, 10, 249}, + dictWord{132, 10, 152}, + dictWord{11, 0, 92}, + dictWord{11, 0, 196}, + dictWord{11, 0, 409}, + dictWord{11, 0, 450}, + dictWord{11, 0, 666}, + dictWord{11, 0, 777}, + dictWord{12, 0, 262}, + dictWord{13, 0, 385}, + dictWord{13, 0, 393}, + dictWord{15, 0, 115}, + dictWord{16, 0, 45}, + dictWord{145, 0, 82}, + dictWord{133, 10, 1006}, + dictWord{6, 0, 40}, + dictWord{135, 0, 1781}, + dictWord{9, 11, 614}, + dictWord{139, 11, 327}, + dictWord{5, 10, 420}, + dictWord{135, 10, 1449}, + dictWord{135, 0, 431}, + dictWord{10, 0, 97}, + dictWord{135, 10, 832}, + dictWord{6, 0, 423}, + dictWord{7, 0, 665}, + dictWord{ + 135, + 0, + 1210, + }, + dictWord{7, 0, 237}, + dictWord{8, 0, 664}, + dictWord{9, 0, 42}, + dictWord{9, 0, 266}, + dictWord{9, 0, 380}, + dictWord{9, 0, 645}, + dictWord{10, 0, 177}, + dictWord{ + 138, + 0, + 276, + }, + dictWord{7, 0, 264}, + dictWord{133, 10, 351}, + dictWord{8, 0, 213}, + dictWord{5, 10, 40}, + dictWord{7, 10, 598}, + dictWord{7, 10, 1638}, + dictWord{ + 9, + 10, + 166, + }, + dictWord{9, 10, 640}, + dictWord{9, 10, 685}, + dictWord{9, 10, 773}, + dictWord{11, 10, 215}, + dictWord{13, 10, 65}, + dictWord{14, 10, 172}, + dictWord{ + 14, + 10, + 317, + }, + dictWord{145, 10, 6}, + dictWord{5, 11, 84}, + dictWord{134, 11, 163}, + dictWord{8, 10, 60}, + dictWord{9, 10, 343}, + dictWord{139, 10, 769}, + dictWord{ + 137, + 0, + 455, + }, + dictWord{133, 11, 410}, + dictWord{8, 0, 906}, + dictWord{12, 0, 700}, + dictWord{12, 0, 706}, + dictWord{140, 0, 729}, + dictWord{21, 11, 33}, + dictWord{ + 150, + 11, + 40, + }, + dictWord{7, 10, 1951}, + dictWord{8, 10, 765}, + dictWord{8, 10, 772}, + dictWord{140, 10, 671}, + dictWord{7, 10, 108}, + dictWord{8, 10, 219}, + dictWord{ + 8, + 10, + 388, + }, + dictWord{9, 10, 639}, + dictWord{9, 10, 775}, + dictWord{11, 10, 275}, + dictWord{140, 10, 464}, + dictWord{5, 11, 322}, + dictWord{7, 11, 1941}, + dictWord{ + 8, + 11, + 186, + }, + dictWord{9, 11, 262}, + dictWord{10, 11, 187}, + dictWord{14, 11, 208}, + dictWord{146, 11, 130}, + dictWord{139, 0, 624}, + dictWord{8, 0, 574}, + dictWord{ + 5, + 11, + 227, + }, + dictWord{140, 11, 29}, + dictWord{7, 11, 1546}, + dictWord{11, 11, 299}, + dictWord{142, 11, 407}, + dictWord{5, 10, 15}, + dictWord{6, 10, 56}, + dictWord{ + 7, + 10, + 1758, + }, + dictWord{8, 10, 500}, + dictWord{9, 10, 730}, + dictWord{11, 10, 331}, + dictWord{13, 10, 150}, + dictWord{142, 10, 282}, + dictWord{7, 11, 1395}, + dictWord{8, 11, 486}, + dictWord{9, 11, 236}, + dictWord{9, 11, 878}, + dictWord{10, 11, 218}, + dictWord{11, 11, 95}, + dictWord{19, 11, 17}, + dictWord{147, 11, 31}, + dictWord{135, 11, 2043}, + dictWord{4, 0, 354}, + dictWord{146, 11, 4}, + dictWord{140, 11, 80}, + dictWord{135, 0, 1558}, + dictWord{134, 10, 1886}, + dictWord{ + 5, + 10, + 205, + }, + dictWord{6, 10, 438}, + dictWord{137, 10, 711}, + dictWord{133, 11, 522}, + dictWord{133, 10, 534}, + dictWord{7, 0, 235}, + dictWord{7, 0, 1475}, + dictWord{ + 15, + 0, + 68, + }, + dictWord{146, 0, 120}, + dictWord{137, 10, 691}, + dictWord{4, 0, 942}, + dictWord{6, 0, 1813}, + dictWord{8, 0, 917}, + dictWord{10, 0, 884}, + dictWord{ + 12, + 0, + 696, + }, + dictWord{12, 0, 717}, + dictWord{12, 0, 723}, + dictWord{12, 0, 738}, + dictWord{12, 0, 749}, + dictWord{12, 0, 780}, + dictWord{16, 0, 97}, + dictWord{146, 0, 169}, + dictWord{6, 10, 443}, + dictWord{8, 11, 562}, + dictWord{9, 10, 237}, + dictWord{9, 10, 571}, + dictWord{9, 10, 695}, + dictWord{10, 10, 139}, + dictWord{11, 10, 715}, + dictWord{12, 10, 417}, + dictWord{141, 10, 421}, + dictWord{135, 0, 957}, + dictWord{133, 0, 830}, + dictWord{134, 11, 1771}, + dictWord{146, 0, 23}, + dictWord{ + 5, + 0, + 496, + }, + dictWord{6, 0, 694}, + dictWord{7, 0, 203}, + dictWord{7, 11, 1190}, + dictWord{137, 11, 620}, + dictWord{137, 11, 132}, + dictWord{6, 0, 547}, + dictWord{ + 134, + 0, + 1549, + }, + dictWord{8, 11, 258}, + dictWord{9, 11, 208}, + dictWord{137, 11, 359}, + dictWord{4, 0, 864}, + dictWord{5, 0, 88}, + dictWord{137, 0, 239}, + dictWord{ + 135, + 11, + 493, + }, + dictWord{4, 11, 317}, + dictWord{135, 11, 1279}, + dictWord{132, 11, 477}, + dictWord{4, 10, 578}, + dictWord{5, 11, 63}, + dictWord{133, 11, 509}, + dictWord{ + 7, + 0, + 650, + }, + dictWord{135, 0, 1310}, + dictWord{7, 0, 1076}, + dictWord{9, 0, 80}, + dictWord{11, 0, 78}, + dictWord{11, 0, 421}, + dictWord{11, 0, 534}, + dictWord{ + 140, + 0, + 545, + }, + dictWord{132, 11, 288}, + dictWord{12, 0, 553}, + dictWord{14, 0, 118}, + dictWord{133, 10, 923}, + dictWord{7, 0, 274}, + dictWord{11, 0, 479}, + dictWord{ + 139, + 0, + 507, + }, + dictWord{8, 11, 89}, + dictWord{8, 11, 620}, + dictWord{9, 11, 49}, + dictWord{10, 11, 774}, + dictWord{11, 11, 628}, + dictWord{12, 11, 322}, + dictWord{ + 143, + 11, + 124, + }, + dictWord{4, 0, 497}, + dictWord{135, 0, 1584}, + dictWord{7, 0, 261}, + dictWord{7, 0, 1115}, + dictWord{7, 0, 1354}, + dictWord{7, 0, 1404}, + dictWord{ + 7, + 0, + 1588, + }, + dictWord{7, 0, 1705}, + dictWord{7, 0, 1902}, + dictWord{9, 0, 465}, + dictWord{10, 0, 248}, + dictWord{10, 0, 349}, + dictWord{10, 0, 647}, + dictWord{11, 0, 527}, + dictWord{11, 0, 660}, + dictWord{11, 0, 669}, + dictWord{12, 0, 529}, + dictWord{13, 0, 305}, + dictWord{132, 10, 924}, + dictWord{133, 10, 665}, + dictWord{ + 136, + 0, + 13, + }, + dictWord{6, 0, 791}, + dictWord{138, 11, 120}, + dictWord{7, 0, 642}, + dictWord{8, 0, 250}, + dictWord{11, 0, 123}, + dictWord{11, 0, 137}, + dictWord{13, 0, 48}, + dictWord{142, 0, 95}, + dictWord{4, 10, 265}, + dictWord{7, 10, 807}, + dictWord{135, 10, 950}, + dictWord{5, 10, 93}, + dictWord{140, 10, 267}, + dictWord{135, 0, 1429}, + dictWord{4, 0, 949}, + dictWord{10, 0, 885}, + dictWord{10, 0, 891}, + dictWord{10, 0, 900}, + dictWord{10, 0, 939}, + dictWord{12, 0, 760}, + dictWord{142, 0, 449}, + dictWord{139, 11, 366}, + dictWord{132, 0, 818}, + dictWord{134, 11, 85}, + dictWord{135, 10, 994}, + dictWord{7, 0, 330}, + dictWord{5, 10, 233}, + dictWord{5, 10, 320}, + dictWord{6, 10, 140}, + dictWord{136, 10, 295}, + dictWord{4, 0, 1004}, + dictWord{8, 0, 982}, + dictWord{136, 0, 993}, + dictWord{133, 10, 978}, + dictWord{4, 10, 905}, + dictWord{6, 10, 1701}, + dictWord{137, 10, 843}, + dictWord{10, 0, 545}, + dictWord{140, 0, 301}, + dictWord{6, 0, 947}, + dictWord{134, 0, 1062}, + dictWord{ + 134, + 0, + 1188, + }, + dictWord{4, 0, 904}, + dictWord{5, 0, 794}, + dictWord{152, 10, 6}, + dictWord{134, 0, 1372}, + dictWord{135, 11, 608}, + dictWord{5, 11, 279}, + dictWord{ + 6, + 11, + 235, + }, + dictWord{7, 11, 468}, + dictWord{8, 11, 446}, + dictWord{9, 11, 637}, + dictWord{10, 11, 717}, + dictWord{11, 11, 738}, + dictWord{140, 11, 514}, + dictWord{ + 132, + 10, + 509, + }, + dictWord{5, 11, 17}, + dictWord{6, 11, 371}, + dictWord{137, 11, 528}, + dictWord{132, 0, 693}, + dictWord{4, 11, 115}, + dictWord{5, 11, 669}, + dictWord{ + 6, + 11, + 407, + }, + dictWord{8, 11, 311}, + dictWord{11, 11, 10}, + dictWord{141, 11, 5}, + dictWord{11, 0, 377}, + dictWord{7, 10, 273}, + dictWord{137, 11, 381}, + dictWord{ + 135, + 0, + 695, + }, + dictWord{7, 0, 386}, + dictWord{138, 0, 713}, + dictWord{135, 10, 1041}, + dictWord{134, 0, 1291}, + dictWord{6, 0, 7}, + dictWord{6, 0, 35}, + dictWord{ + 7, + 0, + 147, + }, + dictWord{7, 0, 1069}, + dictWord{7, 0, 1568}, + dictWord{7, 0, 1575}, + dictWord{7, 0, 1917}, + dictWord{8, 0, 43}, + dictWord{8, 0, 208}, + dictWord{9, 0, 128}, + dictWord{ + 9, + 0, + 866, + }, + dictWord{10, 0, 20}, + dictWord{11, 0, 981}, + dictWord{147, 0, 33}, + dictWord{7, 0, 893}, + dictWord{141, 0, 424}, + dictWord{139, 10, 234}, + dictWord{ + 150, + 11, + 56, + }, + dictWord{5, 11, 779}, + dictWord{5, 11, 807}, + dictWord{6, 11, 1655}, + dictWord{134, 11, 1676}, + dictWord{5, 10, 802}, + dictWord{7, 10, 2021}, + dictWord{136, 10, 805}, + dictWord{4, 11, 196}, + dictWord{5, 10, 167}, + dictWord{5, 11, 558}, + dictWord{5, 10, 899}, + dictWord{5, 11, 949}, + dictWord{6, 10, 410}, + dictWord{137, 10, 777}, + dictWord{137, 10, 789}, + dictWord{134, 10, 1705}, + dictWord{8, 0, 904}, + dictWord{140, 0, 787}, + dictWord{6, 0, 322}, + dictWord{9, 0, 552}, + dictWord{11, 0, 274}, + dictWord{13, 0, 209}, + dictWord{13, 0, 499}, + dictWord{14, 0, 85}, + dictWord{15, 0, 126}, + dictWord{145, 0, 70}, + dictWord{135, 10, 10}, + dictWord{ + 5, + 10, + 11, + }, + dictWord{6, 10, 117}, + dictWord{6, 10, 485}, + dictWord{7, 10, 1133}, + dictWord{9, 10, 582}, + dictWord{9, 10, 594}, + dictWord{11, 10, 21}, + dictWord{ + 11, + 10, + 818, + }, + dictWord{12, 10, 535}, + dictWord{141, 10, 86}, + dictWord{4, 10, 264}, + dictWord{7, 10, 1067}, + dictWord{8, 10, 204}, + dictWord{8, 10, 385}, + dictWord{139, 10, 953}, + dictWord{132, 11, 752}, + dictWord{138, 10, 56}, + dictWord{133, 10, 470}, + dictWord{6, 0, 1808}, + dictWord{8, 0, 83}, + dictWord{8, 0, 742}, + dictWord{8, 0, 817}, + dictWord{9, 0, 28}, + dictWord{9, 0, 29}, + dictWord{9, 0, 885}, + dictWord{10, 0, 387}, + dictWord{11, 0, 633}, + dictWord{11, 0, 740}, + dictWord{13, 0, 235}, + dictWord{13, 0, 254}, + dictWord{15, 0, 143}, + dictWord{143, 0, 146}, + dictWord{140, 0, 49}, + dictWord{134, 0, 1832}, + dictWord{4, 11, 227}, + dictWord{5, 11, 159}, + dictWord{5, 11, 409}, + dictWord{7, 11, 80}, + dictWord{10, 11, 294}, + dictWord{10, 11, 479}, + dictWord{12, 11, 418}, + dictWord{14, 11, 50}, + dictWord{14, 11, 249}, + dictWord{142, 11, 295}, + dictWord{7, 11, 1470}, + dictWord{8, 11, 66}, + dictWord{8, 11, 137}, + dictWord{8, 11, 761}, + dictWord{9, 11, 638}, + dictWord{11, 11, 80}, + dictWord{11, 11, 212}, + dictWord{11, 11, 368}, + dictWord{11, 11, 418}, + dictWord{12, 11, 8}, + dictWord{13, 11, 15}, + dictWord{16, 11, 61}, + dictWord{17, 11, 59}, + dictWord{19, 11, 28}, + dictWord{148, 11, 84}, + dictWord{139, 10, 1015}, + dictWord{138, 11, 468}, + dictWord{135, 0, 421}, + dictWord{6, 0, 415}, + dictWord{ + 7, + 0, + 1049, + }, + dictWord{137, 0, 442}, + dictWord{6, 11, 38}, + dictWord{7, 11, 1220}, + dictWord{8, 11, 185}, + dictWord{8, 11, 256}, + dictWord{9, 11, 22}, + dictWord{ + 9, + 11, + 331, + }, + dictWord{10, 11, 738}, + dictWord{11, 11, 205}, + dictWord{11, 11, 540}, + dictWord{11, 11, 746}, + dictWord{13, 11, 399}, + dictWord{13, 11, 465}, + dictWord{ + 14, + 11, + 88, + }, + dictWord{142, 11, 194}, + dictWord{139, 0, 289}, + dictWord{133, 10, 715}, + dictWord{4, 0, 110}, + dictWord{10, 0, 415}, + dictWord{10, 0, 597}, + dictWord{142, 0, 206}, + dictWord{4, 11, 159}, + dictWord{6, 11, 115}, + dictWord{7, 11, 252}, + dictWord{7, 11, 257}, + dictWord{7, 11, 1928}, + dictWord{8, 11, 69}, + dictWord{ + 9, + 11, + 384, + }, + dictWord{10, 11, 91}, + dictWord{10, 11, 615}, + dictWord{12, 11, 375}, + dictWord{14, 11, 235}, + dictWord{18, 11, 117}, + dictWord{147, 11, 123}, + dictWord{5, 11, 911}, + dictWord{136, 11, 278}, + dictWord{7, 0, 205}, + dictWord{7, 0, 2000}, + dictWord{8, 10, 794}, + dictWord{9, 10, 400}, + dictWord{10, 10, 298}, + dictWord{142, 10, 228}, + dictWord{135, 11, 1774}, + dictWord{4, 11, 151}, + dictWord{7, 11, 1567}, + dictWord{8, 11, 351}, + dictWord{137, 11, 322}, + dictWord{ + 136, + 10, + 724, + }, + dictWord{133, 11, 990}, + dictWord{7, 0, 1539}, + dictWord{11, 0, 512}, + dictWord{13, 0, 205}, + dictWord{19, 0, 30}, + dictWord{22, 0, 36}, + dictWord{23, 0, 19}, + dictWord{135, 11, 1539}, + dictWord{5, 11, 194}, + dictWord{7, 11, 1662}, + dictWord{9, 11, 90}, + dictWord{140, 11, 180}, + dictWord{6, 10, 190}, + dictWord{ + 7, + 10, + 768, + }, + dictWord{135, 10, 1170}, + dictWord{134, 0, 1340}, + dictWord{4, 0, 283}, + dictWord{135, 0, 1194}, + dictWord{133, 11, 425}, + dictWord{133, 11, 971}, + dictWord{12, 0, 549}, + dictWord{14, 10, 67}, + dictWord{147, 10, 60}, + dictWord{135, 10, 1023}, + dictWord{134, 0, 1720}, + dictWord{138, 11, 587}, + dictWord{ + 5, + 11, + 72, + }, + dictWord{6, 11, 264}, + dictWord{7, 11, 21}, + dictWord{7, 11, 46}, + dictWord{7, 11, 2013}, + dictWord{8, 11, 215}, + dictWord{8, 11, 513}, + dictWord{10, 11, 266}, + dictWord{139, 11, 22}, + dictWord{5, 0, 319}, + dictWord{135, 0, 534}, + dictWord{6, 10, 137}, + dictWord{9, 10, 75}, + dictWord{9, 10, 253}, + dictWord{10, 10, 194}, + dictWord{138, 10, 444}, + dictWord{7, 0, 1180}, + dictWord{20, 0, 112}, + dictWord{6, 11, 239}, + dictWord{7, 11, 118}, + dictWord{10, 11, 95}, + dictWord{11, 11, 603}, + dictWord{13, 11, 443}, + dictWord{14, 11, 160}, + dictWord{143, 11, 4}, + dictWord{134, 11, 431}, + dictWord{5, 11, 874}, + dictWord{6, 11, 1677}, + dictWord{ + 11, + 10, + 643, + }, + dictWord{12, 10, 115}, + dictWord{143, 11, 0}, + dictWord{134, 0, 967}, + dictWord{6, 11, 65}, + dictWord{7, 11, 939}, + dictWord{7, 11, 1172}, + dictWord{ + 7, + 11, + 1671, + }, + dictWord{9, 11, 540}, + dictWord{10, 11, 696}, + dictWord{11, 11, 265}, + dictWord{11, 11, 732}, + dictWord{11, 11, 928}, + dictWord{11, 11, 937}, + dictWord{ + 12, + 11, + 399, + }, + dictWord{13, 11, 438}, + dictWord{149, 11, 19}, + dictWord{137, 11, 200}, + dictWord{135, 0, 1940}, + dictWord{5, 10, 760}, + dictWord{7, 10, 542}, + dictWord{8, 10, 135}, + dictWord{136, 10, 496}, + dictWord{140, 11, 44}, + dictWord{7, 11, 1655}, + dictWord{136, 11, 305}, + dictWord{7, 10, 319}, + dictWord{ + 7, + 10, + 355, + }, + dictWord{7, 10, 763}, + dictWord{10, 10, 389}, + dictWord{145, 10, 43}, + dictWord{136, 0, 735}, + dictWord{138, 10, 786}, + dictWord{137, 11, 19}, + dictWord{132, 11, 696}, + dictWord{5, 0, 132}, + dictWord{9, 0, 486}, + dictWord{9, 0, 715}, + dictWord{10, 0, 458}, + dictWord{11, 0, 373}, + dictWord{11, 0, 668}, + dictWord{ + 11, + 0, + 795, + }, + dictWord{11, 0, 897}, + dictWord{12, 0, 272}, + dictWord{12, 0, 424}, + dictWord{12, 0, 539}, + dictWord{12, 0, 558}, + dictWord{14, 0, 245}, + dictWord{ + 14, + 0, + 263, + }, + dictWord{14, 0, 264}, + dictWord{14, 0, 393}, + dictWord{142, 0, 403}, + dictWord{10, 0, 38}, + dictWord{139, 0, 784}, + dictWord{132, 0, 838}, + dictWord{ + 4, + 11, + 302, + }, + dictWord{135, 11, 1766}, + dictWord{133, 0, 379}, + dictWord{5, 0, 8}, + dictWord{6, 0, 89}, + dictWord{6, 0, 400}, + dictWord{7, 0, 1569}, + dictWord{7, 0, 1623}, + dictWord{7, 0, 1850}, + dictWord{8, 0, 218}, + dictWord{8, 0, 422}, + dictWord{9, 0, 570}, + dictWord{10, 0, 626}, + dictWord{4, 11, 726}, + dictWord{133, 11, 630}, + dictWord{ + 4, + 0, + 1017, + }, + dictWord{138, 0, 660}, + dictWord{6, 0, 387}, + dictWord{7, 0, 882}, + dictWord{141, 0, 111}, + dictWord{6, 0, 224}, + dictWord{7, 0, 877}, + dictWord{ + 137, + 0, + 647, + }, + dictWord{4, 10, 58}, + dictWord{5, 10, 286}, + dictWord{6, 10, 319}, + dictWord{7, 10, 402}, + dictWord{7, 10, 1254}, + dictWord{7, 10, 1903}, + dictWord{ + 8, + 10, + 356, + }, + dictWord{140, 10, 408}, + dictWord{135, 0, 790}, + dictWord{9, 0, 510}, + dictWord{10, 0, 53}, + dictWord{4, 10, 389}, + dictWord{9, 10, 181}, + dictWord{ + 10, + 10, + 29, + }, + dictWord{10, 10, 816}, + dictWord{11, 10, 311}, + dictWord{11, 10, 561}, + dictWord{12, 10, 67}, + dictWord{141, 10, 181}, + dictWord{142, 0, 458}, + dictWord{ + 6, + 11, + 118, + }, + dictWord{7, 11, 215}, + dictWord{7, 11, 1521}, + dictWord{140, 11, 11}, + dictWord{134, 0, 954}, + dictWord{135, 0, 394}, + dictWord{134, 0, 1367}, + dictWord{5, 11, 225}, + dictWord{133, 10, 373}, + dictWord{132, 0, 882}, + dictWord{7, 0, 1409}, + dictWord{135, 10, 1972}, + dictWord{135, 10, 1793}, + dictWord{ + 4, + 11, + 370, + }, + dictWord{5, 11, 756}, + dictWord{135, 11, 1326}, + dictWord{150, 11, 13}, + dictWord{7, 11, 354}, + dictWord{10, 11, 410}, + dictWord{139, 11, 815}, + dictWord{6, 11, 1662}, + dictWord{7, 11, 48}, + dictWord{8, 11, 771}, + dictWord{10, 11, 116}, + dictWord{13, 11, 104}, + dictWord{14, 11, 105}, + dictWord{14, 11, 184}, + dictWord{15, 11, 168}, + dictWord{19, 11, 92}, + dictWord{148, 11, 68}, + dictWord{7, 0, 124}, + dictWord{136, 0, 38}, + dictWord{5, 0, 261}, + dictWord{7, 0, 78}, + dictWord{ + 7, + 0, + 199, + }, + dictWord{8, 0, 815}, + dictWord{9, 0, 126}, + dictWord{10, 0, 342}, + dictWord{140, 0, 647}, + dictWord{4, 0, 628}, + dictWord{140, 0, 724}, + dictWord{7, 0, 266}, + dictWord{8, 0, 804}, + dictWord{7, 10, 1651}, + dictWord{145, 10, 89}, + dictWord{135, 0, 208}, + dictWord{134, 0, 1178}, + dictWord{6, 0, 79}, + dictWord{135, 0, 1519}, + dictWord{132, 10, 672}, + dictWord{133, 10, 737}, + dictWord{136, 0, 741}, + dictWord{132, 11, 120}, + dictWord{4, 0, 710}, + dictWord{6, 0, 376}, + dictWord{ + 134, + 0, + 606, + }, + dictWord{134, 0, 1347}, + dictWord{134, 0, 1494}, + dictWord{6, 0, 850}, + dictWord{6, 0, 1553}, + dictWord{137, 0, 821}, + dictWord{5, 10, 145}, + dictWord{ + 134, + 11, + 593, + }, + dictWord{7, 0, 1311}, + dictWord{140, 0, 135}, + dictWord{4, 0, 467}, + dictWord{5, 0, 405}, + dictWord{134, 0, 544}, + dictWord{5, 11, 820}, + dictWord{ + 135, + 11, + 931, + }, + dictWord{6, 0, 100}, + dictWord{7, 0, 244}, + dictWord{7, 0, 632}, + dictWord{7, 0, 1609}, + dictWord{8, 0, 178}, + dictWord{8, 0, 638}, + dictWord{141, 0, 58}, + dictWord{4, 10, 387}, + dictWord{135, 10, 1288}, + dictWord{6, 11, 151}, + dictWord{6, 11, 1675}, + dictWord{7, 11, 383}, + dictWord{151, 11, 10}, + dictWord{ + 132, + 0, + 481, + }, + dictWord{135, 10, 550}, + dictWord{134, 0, 1378}, + dictWord{6, 11, 1624}, + dictWord{11, 11, 11}, + dictWord{12, 11, 422}, + dictWord{13, 11, 262}, + dictWord{142, 11, 360}, + dictWord{133, 0, 791}, + dictWord{4, 11, 43}, + dictWord{5, 11, 344}, + dictWord{133, 11, 357}, + dictWord{7, 0, 1227}, + dictWord{140, 0, 978}, + dictWord{7, 0, 686}, + dictWord{8, 0, 33}, + dictWord{8, 0, 238}, + dictWord{10, 0, 616}, + dictWord{11, 0, 467}, + dictWord{11, 0, 881}, + dictWord{13, 0, 217}, + dictWord{ + 13, + 0, + 253, + }, + dictWord{142, 0, 268}, + dictWord{137, 0, 857}, + dictWord{8, 0, 467}, + dictWord{8, 0, 1006}, + dictWord{7, 11, 148}, + dictWord{8, 11, 284}, + dictWord{ + 141, + 11, + 63, + }, + dictWord{4, 10, 576}, + dictWord{135, 10, 1263}, + dictWord{133, 11, 888}, + dictWord{5, 10, 919}, + dictWord{134, 10, 1673}, + dictWord{20, 10, 37}, + dictWord{148, 11, 37}, + dictWord{132, 0, 447}, + dictWord{132, 11, 711}, + dictWord{4, 0, 128}, + dictWord{5, 0, 415}, + dictWord{6, 0, 462}, + dictWord{7, 0, 294}, + dictWord{ + 7, + 0, + 578, + }, + dictWord{10, 0, 710}, + dictWord{139, 0, 86}, + dictWord{4, 10, 82}, + dictWord{5, 10, 333}, + dictWord{5, 10, 904}, + dictWord{6, 10, 207}, + dictWord{7, 10, 325}, + dictWord{7, 10, 1726}, + dictWord{8, 10, 101}, + dictWord{10, 10, 778}, + dictWord{139, 10, 220}, + dictWord{136, 0, 587}, + dictWord{137, 11, 440}, + dictWord{ + 133, + 10, + 903, + }, + dictWord{6, 0, 427}, + dictWord{7, 0, 1018}, + dictWord{138, 0, 692}, + dictWord{4, 0, 195}, + dictWord{135, 0, 802}, + dictWord{140, 10, 147}, + dictWord{ + 134, + 0, + 1546, + }, + dictWord{134, 0, 684}, + dictWord{132, 10, 705}, + dictWord{136, 0, 345}, + dictWord{11, 11, 678}, + dictWord{140, 11, 307}, + dictWord{ + 133, + 0, + 365, + }, + dictWord{134, 0, 1683}, + dictWord{4, 11, 65}, + dictWord{5, 11, 479}, + dictWord{5, 11, 1004}, + dictWord{7, 11, 1913}, + dictWord{8, 11, 317}, + dictWord{ + 9, + 11, + 302, + }, + dictWord{10, 11, 612}, + dictWord{141, 11, 22}, + dictWord{138, 0, 472}, + dictWord{4, 11, 261}, + dictWord{135, 11, 510}, + dictWord{134, 10, 90}, + dictWord{142, 0, 433}, + dictWord{151, 0, 28}, + dictWord{4, 11, 291}, + dictWord{7, 11, 101}, + dictWord{9, 11, 515}, + dictWord{12, 11, 152}, + dictWord{12, 11, 443}, + dictWord{13, 11, 392}, + dictWord{142, 11, 357}, + dictWord{140, 0, 997}, + dictWord{5, 0, 3}, + dictWord{8, 0, 578}, + dictWord{9, 0, 118}, + dictWord{10, 0, 705}, + dictWord{ + 141, + 0, + 279, + }, + dictWord{135, 11, 1266}, + dictWord{7, 10, 813}, + dictWord{12, 10, 497}, + dictWord{141, 10, 56}, + dictWord{133, 0, 229}, + dictWord{6, 10, 125}, + dictWord{135, 10, 1277}, + dictWord{8, 0, 102}, + dictWord{10, 0, 578}, + dictWord{10, 0, 672}, + dictWord{12, 0, 496}, + dictWord{13, 0, 408}, + dictWord{14, 0, 121}, + dictWord{17, 0, 106}, + dictWord{151, 10, 12}, + dictWord{6, 0, 866}, + dictWord{134, 0, 1080}, + dictWord{136, 0, 1022}, + dictWord{4, 11, 130}, + dictWord{135, 11, 843}, + dictWord{5, 11, 42}, + dictWord{5, 11, 879}, + dictWord{7, 11, 245}, + dictWord{7, 11, 324}, + dictWord{7, 11, 1532}, + dictWord{11, 11, 463}, + dictWord{11, 11, 472}, + dictWord{13, 11, 363}, + dictWord{144, 11, 52}, + dictWord{150, 0, 55}, + dictWord{8, 0, 115}, + dictWord{8, 0, 350}, + dictWord{9, 0, 489}, + dictWord{10, 0, 128}, + dictWord{ + 11, + 0, + 306, + }, + dictWord{12, 0, 373}, + dictWord{14, 0, 30}, + dictWord{17, 0, 79}, + dictWord{19, 0, 80}, + dictWord{4, 11, 134}, + dictWord{133, 11, 372}, + dictWord{ + 134, + 0, + 657, + }, + dictWord{134, 0, 933}, + dictWord{135, 11, 1147}, + dictWord{4, 0, 230}, + dictWord{133, 0, 702}, + dictWord{134, 0, 1728}, + dictWord{4, 0, 484}, + dictWord{ + 18, + 0, + 26, + }, + dictWord{19, 0, 42}, + dictWord{20, 0, 43}, + dictWord{21, 0, 0}, + dictWord{23, 0, 27}, + dictWord{152, 0, 14}, + dictWord{7, 0, 185}, + dictWord{135, 0, 703}, + dictWord{ + 6, + 0, + 417, + }, + dictWord{10, 0, 618}, + dictWord{7, 10, 1106}, + dictWord{9, 10, 770}, + dictWord{11, 10, 112}, + dictWord{140, 10, 413}, + dictWord{134, 0, 803}, + dictWord{132, 11, 644}, + dictWord{134, 0, 1262}, + dictWord{7, 11, 540}, + dictWord{12, 10, 271}, + dictWord{145, 10, 109}, + dictWord{135, 11, 123}, + dictWord{ + 132, + 0, + 633, + }, + dictWord{134, 11, 623}, + dictWord{4, 11, 908}, + dictWord{5, 11, 359}, + dictWord{5, 11, 508}, + dictWord{6, 11, 1723}, + dictWord{7, 11, 343}, + dictWord{ + 7, + 11, + 1996, + }, + dictWord{135, 11, 2026}, + dictWord{135, 0, 479}, + dictWord{10, 0, 262}, + dictWord{7, 10, 304}, + dictWord{9, 10, 646}, + dictWord{9, 10, 862}, + dictWord{ + 11, + 10, + 696, + }, + dictWord{12, 10, 208}, + dictWord{15, 10, 79}, + dictWord{147, 10, 108}, + dictWord{4, 11, 341}, + dictWord{135, 11, 480}, + dictWord{134, 0, 830}, + dictWord{5, 0, 70}, + dictWord{5, 0, 622}, + dictWord{6, 0, 334}, + dictWord{7, 0, 1032}, + dictWord{9, 0, 171}, + dictWord{11, 0, 26}, + dictWord{11, 0, 213}, + dictWord{ + 11, + 0, + 637, + }, + dictWord{11, 0, 707}, + dictWord{12, 0, 202}, + dictWord{12, 0, 380}, + dictWord{13, 0, 226}, + dictWord{13, 0, 355}, + dictWord{14, 0, 222}, + dictWord{145, 0, 42}, + dictWord{135, 10, 981}, + dictWord{143, 0, 217}, + dictWord{137, 11, 114}, + dictWord{4, 0, 23}, + dictWord{4, 0, 141}, + dictWord{5, 0, 313}, + dictWord{5, 0, 1014}, + dictWord{6, 0, 50}, + dictWord{6, 0, 51}, + dictWord{7, 0, 142}, + dictWord{7, 0, 384}, + dictWord{7, 0, 559}, + dictWord{8, 0, 640}, + dictWord{9, 0, 460}, + dictWord{9, 0, 783}, + dictWord{11, 0, 741}, + dictWord{12, 0, 183}, + dictWord{141, 0, 488}, + dictWord{141, 0, 360}, + dictWord{7, 0, 1586}, + dictWord{7, 11, 1995}, + dictWord{8, 11, 299}, + dictWord{11, 11, 890}, + dictWord{140, 11, 674}, + dictWord{132, 10, 434}, + dictWord{7, 0, 652}, + dictWord{134, 10, 550}, + dictWord{7, 0, 766}, + dictWord{5, 10, 553}, + dictWord{138, 10, 824}, + dictWord{7, 0, 737}, + dictWord{8, 0, 298}, + dictWord{136, 10, 452}, + dictWord{4, 11, 238}, + dictWord{5, 11, 503}, + dictWord{6, 11, 179}, + dictWord{7, 11, 2003}, + dictWord{8, 11, 381}, + dictWord{8, 11, 473}, + dictWord{9, 11, 149}, + dictWord{10, 11, 183}, + dictWord{15, 11, 45}, + dictWord{143, 11, 86}, + dictWord{133, 10, 292}, + dictWord{5, 0, 222}, + dictWord{9, 0, 655}, + dictWord{138, 0, 534}, + dictWord{138, 10, 135}, + dictWord{4, 11, 121}, + dictWord{5, 11, 156}, + dictWord{5, 11, 349}, + dictWord{9, 11, 136}, + dictWord{10, 11, 605}, + dictWord{14, 11, 342}, + dictWord{147, 11, 107}, + dictWord{137, 0, 906}, + dictWord{6, 0, 1013}, + dictWord{134, 0, 1250}, + dictWord{6, 0, 1956}, + dictWord{6, 0, 2009}, + dictWord{8, 0, 991}, + dictWord{144, 0, 120}, + dictWord{135, 11, 1192}, + dictWord{ + 138, + 0, + 503, + }, + dictWord{5, 0, 154}, + dictWord{7, 0, 1491}, + dictWord{10, 0, 379}, + dictWord{138, 0, 485}, + dictWord{6, 0, 1867}, + dictWord{6, 0, 1914}, + dictWord{6, 0, 1925}, + dictWord{9, 0, 917}, + dictWord{9, 0, 925}, + dictWord{9, 0, 932}, + dictWord{9, 0, 951}, + dictWord{9, 0, 1007}, + dictWord{9, 0, 1013}, + dictWord{12, 0, 806}, + dictWord{ + 12, + 0, + 810, + }, + dictWord{12, 0, 814}, + dictWord{12, 0, 816}, + dictWord{12, 0, 824}, + dictWord{12, 0, 832}, + dictWord{12, 0, 837}, + dictWord{12, 0, 863}, + dictWord{ + 12, + 0, + 868, + }, + dictWord{12, 0, 870}, + dictWord{12, 0, 889}, + dictWord{12, 0, 892}, + dictWord{12, 0, 900}, + dictWord{12, 0, 902}, + dictWord{12, 0, 908}, + dictWord{12, 0, 933}, + dictWord{12, 0, 942}, + dictWord{12, 0, 949}, + dictWord{12, 0, 954}, + dictWord{15, 0, 175}, + dictWord{15, 0, 203}, + dictWord{15, 0, 213}, + dictWord{15, 0, 218}, + dictWord{15, 0, 225}, + dictWord{15, 0, 231}, + dictWord{15, 0, 239}, + dictWord{15, 0, 248}, + dictWord{15, 0, 252}, + dictWord{18, 0, 190}, + dictWord{18, 0, 204}, + dictWord{ + 18, + 0, + 215, + }, + dictWord{18, 0, 216}, + dictWord{18, 0, 222}, + dictWord{18, 0, 225}, + dictWord{18, 0, 230}, + dictWord{18, 0, 239}, + dictWord{18, 0, 241}, + dictWord{ + 21, + 0, + 42, + }, + dictWord{21, 0, 43}, + dictWord{21, 0, 44}, + dictWord{21, 0, 45}, + dictWord{21, 0, 46}, + dictWord{21, 0, 53}, + dictWord{24, 0, 27}, + dictWord{152, 0, 31}, + dictWord{ + 133, + 0, + 716, + }, + dictWord{135, 0, 844}, + dictWord{4, 0, 91}, + dictWord{5, 0, 388}, + dictWord{5, 0, 845}, + dictWord{6, 0, 206}, + dictWord{6, 0, 252}, + dictWord{6, 0, 365}, + dictWord{ + 7, + 0, + 136, + }, + dictWord{7, 0, 531}, + dictWord{136, 0, 621}, + dictWord{7, 10, 393}, + dictWord{10, 10, 603}, + dictWord{139, 10, 206}, + dictWord{6, 11, 80}, + dictWord{ + 6, + 11, + 1694, + }, + dictWord{7, 11, 173}, + dictWord{7, 11, 1974}, + dictWord{9, 11, 547}, + dictWord{10, 11, 730}, + dictWord{14, 11, 18}, + dictWord{150, 11, 39}, + dictWord{137, 0, 748}, + dictWord{4, 11, 923}, + dictWord{134, 11, 1711}, + dictWord{4, 10, 912}, + dictWord{137, 10, 232}, + dictWord{7, 10, 98}, + dictWord{7, 10, 1973}, + dictWord{136, 10, 716}, + dictWord{14, 0, 103}, + dictWord{133, 10, 733}, + dictWord{132, 11, 595}, + dictWord{12, 0, 158}, + dictWord{18, 0, 8}, + dictWord{19, 0, 62}, + dictWord{20, 0, 6}, + dictWord{22, 0, 4}, + dictWord{23, 0, 2}, + dictWord{23, 0, 9}, + dictWord{5, 11, 240}, + dictWord{6, 11, 459}, + dictWord{7, 11, 12}, + dictWord{7, 11, 114}, + dictWord{7, 11, 502}, + dictWord{7, 11, 1751}, + dictWord{7, 11, 1753}, + dictWord{7, 11, 1805}, + dictWord{8, 11, 658}, + dictWord{9, 11, 1}, + dictWord{11, 11, 959}, + dictWord{13, 11, 446}, + dictWord{142, 11, 211}, + dictWord{135, 0, 576}, + dictWord{5, 0, 771}, + dictWord{5, 0, 863}, + dictWord{5, 0, 898}, + dictWord{6, 0, 648}, + dictWord{ + 6, + 0, + 1632, + }, + dictWord{6, 0, 1644}, + dictWord{134, 0, 1780}, + dictWord{133, 0, 331}, + dictWord{7, 11, 633}, + dictWord{7, 11, 905}, + dictWord{7, 11, 909}, + dictWord{ + 7, + 11, + 1538, + }, + dictWord{9, 11, 767}, + dictWord{140, 11, 636}, + dictWord{140, 0, 632}, + dictWord{5, 0, 107}, + dictWord{7, 0, 201}, + dictWord{136, 0, 518}, + dictWord{ + 6, + 0, + 446, + }, + dictWord{7, 0, 1817}, + dictWord{134, 11, 490}, + dictWord{9, 0, 851}, + dictWord{141, 0, 510}, + dictWord{7, 11, 250}, + dictWord{8, 11, 506}, + dictWord{ + 136, + 11, + 507, + }, + dictWord{4, 0, 504}, + dictWord{137, 10, 72}, + dictWord{132, 11, 158}, + dictWord{4, 11, 140}, + dictWord{7, 11, 362}, + dictWord{8, 11, 209}, + dictWord{ + 9, + 11, + 10, + }, + dictWord{9, 11, 160}, + dictWord{9, 11, 503}, + dictWord{10, 11, 689}, + dictWord{11, 11, 350}, + dictWord{11, 11, 553}, + dictWord{11, 11, 725}, + dictWord{ + 12, + 11, + 252, + }, + dictWord{12, 11, 583}, + dictWord{13, 11, 192}, + dictWord{13, 11, 352}, + dictWord{14, 11, 269}, + dictWord{14, 11, 356}, + dictWord{148, 11, 50}, + dictWord{6, 11, 597}, + dictWord{135, 11, 1318}, + dictWord{135, 10, 1454}, + dictWord{5, 0, 883}, + dictWord{5, 0, 975}, + dictWord{8, 0, 392}, + dictWord{148, 0, 7}, + dictWord{6, 11, 228}, + dictWord{7, 11, 1341}, + dictWord{9, 11, 408}, + dictWord{138, 11, 343}, + dictWord{11, 11, 348}, + dictWord{11, 10, 600}, + dictWord{12, 11, 99}, + dictWord{13, 10, 245}, + dictWord{18, 11, 1}, + dictWord{18, 11, 11}, + dictWord{147, 11, 4}, + dictWord{134, 11, 296}, + dictWord{5, 0, 922}, + dictWord{134, 0, 1707}, + dictWord{132, 11, 557}, + dictWord{4, 11, 548}, + dictWord{7, 10, 164}, + dictWord{7, 10, 1571}, + dictWord{9, 10, 107}, + dictWord{140, 10, 225}, + dictWord{ + 7, + 11, + 197, + }, + dictWord{8, 11, 142}, + dictWord{8, 11, 325}, + dictWord{9, 11, 150}, + dictWord{9, 11, 596}, + dictWord{10, 11, 350}, + dictWord{10, 11, 353}, + dictWord{ + 11, + 11, + 74, + }, + dictWord{11, 11, 315}, + dictWord{14, 11, 423}, + dictWord{143, 11, 141}, + dictWord{5, 0, 993}, + dictWord{7, 0, 515}, + dictWord{137, 0, 91}, + dictWord{4, 0, 131}, + dictWord{8, 0, 200}, + dictWord{5, 10, 484}, + dictWord{5, 10, 510}, + dictWord{6, 10, 434}, + dictWord{7, 10, 1000}, + dictWord{7, 10, 1098}, + dictWord{136, 10, 2}, + dictWord{152, 0, 10}, + dictWord{4, 11, 62}, + dictWord{5, 11, 83}, + dictWord{6, 11, 399}, + dictWord{6, 11, 579}, + dictWord{7, 11, 692}, + dictWord{7, 11, 846}, + dictWord{ + 7, + 11, + 1015, + }, + dictWord{7, 11, 1799}, + dictWord{8, 11, 403}, + dictWord{9, 11, 394}, + dictWord{10, 11, 133}, + dictWord{12, 11, 4}, + dictWord{12, 11, 297}, + dictWord{ + 12, + 11, + 452, + }, + dictWord{16, 11, 81}, + dictWord{18, 11, 19}, + dictWord{18, 11, 25}, + dictWord{21, 11, 14}, + dictWord{22, 11, 12}, + dictWord{151, 11, 18}, + dictWord{ + 140, + 11, + 459, + }, + dictWord{132, 11, 177}, + dictWord{7, 0, 1433}, + dictWord{9, 0, 365}, + dictWord{137, 11, 365}, + dictWord{132, 10, 460}, + dictWord{5, 0, 103}, + dictWord{ + 6, + 0, + 2004, + }, + dictWord{7, 0, 921}, + dictWord{8, 0, 580}, + dictWord{8, 0, 593}, + dictWord{8, 0, 630}, + dictWord{10, 0, 28}, + dictWord{5, 11, 411}, + dictWord{ + 135, + 11, + 653, + }, + dictWord{4, 10, 932}, + dictWord{133, 10, 891}, + dictWord{4, 0, 911}, + dictWord{5, 0, 867}, + dictWord{5, 0, 1013}, + dictWord{7, 0, 2034}, + dictWord{8, 0, 798}, + dictWord{136, 0, 813}, + dictWord{7, 11, 439}, + dictWord{10, 11, 727}, + dictWord{11, 11, 260}, + dictWord{139, 11, 684}, + dictWord{136, 10, 625}, + dictWord{ + 5, + 11, + 208, + }, + dictWord{7, 11, 753}, + dictWord{135, 11, 1528}, + dictWord{5, 0, 461}, + dictWord{7, 0, 1925}, + dictWord{12, 0, 39}, + dictWord{13, 0, 265}, + dictWord{ + 13, + 0, + 439, + }, + dictWord{134, 10, 76}, + dictWord{6, 0, 853}, + dictWord{8, 10, 92}, + dictWord{137, 10, 221}, + dictWord{5, 0, 135}, + dictWord{6, 0, 519}, + dictWord{7, 0, 1722}, + dictWord{10, 0, 271}, + dictWord{11, 0, 261}, + dictWord{145, 0, 54}, + dictWord{139, 11, 814}, + dictWord{14, 0, 338}, + dictWord{148, 0, 81}, + dictWord{4, 0, 300}, + dictWord{133, 0, 436}, + dictWord{5, 0, 419}, + dictWord{5, 0, 687}, + dictWord{7, 0, 864}, + dictWord{9, 0, 470}, + dictWord{135, 11, 864}, + dictWord{9, 0, 836}, + dictWord{ + 133, + 11, + 242, + }, + dictWord{134, 0, 1937}, + dictWord{4, 10, 763}, + dictWord{133, 11, 953}, + dictWord{132, 10, 622}, + dictWord{132, 0, 393}, + dictWord{ + 133, + 10, + 253, + }, + dictWord{8, 0, 357}, + dictWord{10, 0, 745}, + dictWord{14, 0, 426}, + dictWord{17, 0, 94}, + dictWord{19, 0, 57}, + dictWord{135, 10, 546}, + dictWord{5, 11, 615}, + dictWord{146, 11, 37}, + dictWord{9, 10, 73}, + dictWord{10, 10, 110}, + dictWord{14, 10, 185}, + dictWord{145, 10, 119}, + dictWord{11, 0, 703}, + dictWord{7, 10, 624}, + dictWord{7, 10, 916}, + dictWord{10, 10, 256}, + dictWord{139, 10, 87}, + dictWord{133, 11, 290}, + dictWord{5, 10, 212}, + dictWord{12, 10, 35}, + dictWord{ + 141, + 10, + 382, + }, + dictWord{132, 11, 380}, + dictWord{5, 11, 52}, + dictWord{7, 11, 277}, + dictWord{9, 11, 368}, + dictWord{139, 11, 791}, + dictWord{133, 0, 387}, + dictWord{ + 10, + 11, + 138, + }, + dictWord{139, 11, 476}, + dictWord{4, 0, 6}, + dictWord{5, 0, 708}, + dictWord{136, 0, 75}, + dictWord{7, 0, 1351}, + dictWord{9, 0, 581}, + dictWord{10, 0, 639}, + dictWord{11, 0, 453}, + dictWord{140, 0, 584}, + dictWord{132, 0, 303}, + dictWord{138, 0, 772}, + dictWord{135, 10, 1175}, + dictWord{4, 0, 749}, + dictWord{ + 5, + 10, + 816, + }, + dictWord{6, 11, 256}, + dictWord{7, 11, 307}, + dictWord{7, 11, 999}, + dictWord{7, 11, 1481}, + dictWord{7, 11, 1732}, + dictWord{7, 11, 1738}, + dictWord{ + 8, + 11, + 265, + }, + dictWord{9, 11, 414}, + dictWord{11, 11, 316}, + dictWord{12, 11, 52}, + dictWord{13, 11, 420}, + dictWord{147, 11, 100}, + dictWord{135, 11, 1296}, + dictWord{ + 6, + 0, + 1065, + }, + dictWord{5, 10, 869}, + dictWord{5, 10, 968}, + dictWord{6, 10, 1626}, + dictWord{8, 10, 734}, + dictWord{136, 10, 784}, + dictWord{4, 10, 542}, + dictWord{ + 6, + 10, + 1716, + }, + dictWord{6, 10, 1727}, + dictWord{7, 10, 1082}, + dictWord{7, 10, 1545}, + dictWord{8, 10, 56}, + dictWord{8, 10, 118}, + dictWord{8, 10, 412}, + dictWord{ + 8, + 10, + 564, + }, + dictWord{9, 10, 888}, + dictWord{9, 10, 908}, + dictWord{10, 10, 50}, + dictWord{10, 10, 423}, + dictWord{11, 10, 685}, + dictWord{11, 10, 697}, + dictWord{11, 10, 933}, + dictWord{12, 10, 299}, + dictWord{13, 10, 126}, + dictWord{13, 10, 136}, + dictWord{13, 10, 170}, + dictWord{141, 10, 190}, + dictWord{ + 134, + 0, + 226, + }, + dictWord{4, 0, 106}, + dictWord{7, 0, 310}, + dictWord{11, 0, 717}, + dictWord{133, 11, 723}, + dictWord{5, 0, 890}, + dictWord{5, 0, 988}, + dictWord{4, 10, 232}, + dictWord{9, 10, 202}, + dictWord{10, 10, 474}, + dictWord{140, 10, 433}, + dictWord{6, 0, 626}, + dictWord{142, 0, 431}, + dictWord{10, 0, 706}, + dictWord{150, 0, 44}, + dictWord{13, 0, 51}, + dictWord{6, 10, 108}, + dictWord{7, 10, 1003}, + dictWord{7, 10, 1181}, + dictWord{8, 10, 111}, + dictWord{136, 10, 343}, + dictWord{132, 0, 698}, + dictWord{5, 11, 109}, + dictWord{6, 11, 1784}, + dictWord{7, 11, 1895}, + dictWord{12, 11, 296}, + dictWord{140, 11, 302}, + dictWord{134, 0, 828}, + dictWord{ + 134, + 10, + 1712, + }, + dictWord{138, 0, 17}, + dictWord{7, 0, 1929}, + dictWord{4, 10, 133}, + dictWord{5, 11, 216}, + dictWord{7, 10, 711}, + dictWord{7, 10, 1298}, + dictWord{ + 7, + 10, + 1585, + }, + dictWord{7, 11, 1879}, + dictWord{9, 11, 141}, + dictWord{9, 11, 270}, + dictWord{9, 11, 679}, + dictWord{10, 11, 159}, + dictWord{10, 11, 553}, + dictWord{ + 11, + 11, + 197, + }, + dictWord{11, 11, 438}, + dictWord{12, 11, 538}, + dictWord{12, 11, 559}, + dictWord{13, 11, 193}, + dictWord{13, 11, 423}, + dictWord{14, 11, 144}, + dictWord{14, 11, 166}, + dictWord{14, 11, 167}, + dictWord{15, 11, 67}, + dictWord{147, 11, 84}, + dictWord{141, 11, 127}, + dictWord{7, 11, 1872}, + dictWord{ + 137, + 11, + 81, + }, + dictWord{6, 10, 99}, + dictWord{7, 10, 1808}, + dictWord{145, 10, 57}, + dictWord{134, 11, 391}, + dictWord{5, 0, 689}, + dictWord{6, 0, 84}, + dictWord{7, 0, 1250}, + dictWord{6, 10, 574}, + dictWord{7, 10, 428}, + dictWord{10, 10, 669}, + dictWord{11, 10, 485}, + dictWord{11, 10, 840}, + dictWord{12, 10, 300}, + dictWord{ + 142, + 10, + 250, + }, + dictWord{7, 11, 322}, + dictWord{136, 11, 249}, + dictWord{7, 11, 432}, + dictWord{135, 11, 1649}, + dictWord{135, 10, 1871}, + dictWord{137, 10, 252}, + dictWord{6, 11, 155}, + dictWord{140, 11, 234}, + dictWord{7, 0, 871}, + dictWord{19, 0, 27}, + dictWord{147, 11, 27}, + dictWord{140, 0, 498}, + dictWord{5, 0, 986}, + dictWord{6, 0, 130}, + dictWord{138, 0, 823}, + dictWord{6, 0, 1793}, + dictWord{7, 0, 1582}, + dictWord{8, 0, 458}, + dictWord{10, 0, 101}, + dictWord{10, 0, 318}, + dictWord{ + 10, + 0, + 945, + }, + dictWord{12, 0, 734}, + dictWord{16, 0, 104}, + dictWord{18, 0, 177}, + dictWord{6, 10, 323}, + dictWord{135, 10, 1564}, + dictWord{5, 11, 632}, + dictWord{ + 138, + 11, + 526, + }, + dictWord{10, 0, 435}, + dictWord{7, 10, 461}, + dictWord{136, 10, 775}, + dictWord{6, 11, 144}, + dictWord{7, 11, 948}, + dictWord{7, 11, 1042}, + dictWord{ + 7, + 11, + 1857, + }, + dictWord{8, 11, 235}, + dictWord{8, 11, 461}, + dictWord{9, 11, 453}, + dictWord{9, 11, 530}, + dictWord{10, 11, 354}, + dictWord{17, 11, 77}, + dictWord{ + 19, + 11, + 99, + }, + dictWord{148, 11, 79}, + dictWord{138, 0, 966}, + dictWord{7, 0, 1644}, + dictWord{137, 0, 129}, + dictWord{135, 0, 997}, + dictWord{136, 0, 502}, + dictWord{ + 5, + 11, + 196, + }, + dictWord{6, 11, 486}, + dictWord{7, 11, 212}, + dictWord{8, 11, 309}, + dictWord{136, 11, 346}, + dictWord{7, 10, 727}, + dictWord{146, 10, 73}, + dictWord{132, 0, 823}, + dictWord{132, 11, 686}, + dictWord{135, 0, 1927}, + dictWord{4, 0, 762}, + dictWord{7, 0, 1756}, + dictWord{137, 0, 98}, + dictWord{136, 10, 577}, + dictWord{24, 0, 8}, + dictWord{4, 11, 30}, + dictWord{5, 11, 43}, + dictWord{152, 11, 8}, + dictWord{7, 0, 1046}, + dictWord{139, 0, 160}, + dictWord{7, 0, 492}, + dictWord{ + 4, + 10, + 413, + }, + dictWord{5, 10, 677}, + dictWord{7, 11, 492}, + dictWord{8, 10, 432}, + dictWord{140, 10, 280}, + dictWord{6, 0, 45}, + dictWord{7, 0, 433}, + dictWord{8, 0, 129}, + dictWord{9, 0, 21}, + dictWord{10, 0, 392}, + dictWord{11, 0, 79}, + dictWord{12, 0, 499}, + dictWord{13, 0, 199}, + dictWord{141, 0, 451}, + dictWord{7, 0, 558}, + dictWord{ + 136, + 0, + 353, + }, + dictWord{4, 11, 220}, + dictWord{7, 11, 1535}, + dictWord{9, 11, 93}, + dictWord{139, 11, 474}, + dictWord{7, 10, 646}, + dictWord{7, 10, 1730}, + dictWord{ + 11, + 10, + 446, + }, + dictWord{141, 10, 178}, + dictWord{133, 0, 785}, + dictWord{134, 0, 1145}, + dictWord{8, 0, 81}, + dictWord{9, 0, 189}, + dictWord{9, 0, 201}, + dictWord{ + 11, + 0, + 478, + }, + dictWord{11, 0, 712}, + dictWord{141, 0, 338}, + dictWord{5, 0, 353}, + dictWord{151, 0, 26}, + dictWord{11, 0, 762}, + dictWord{132, 10, 395}, + dictWord{ + 134, + 0, + 2024, + }, + dictWord{4, 0, 611}, + dictWord{133, 0, 606}, + dictWord{9, 10, 174}, + dictWord{10, 10, 164}, + dictWord{11, 10, 440}, + dictWord{11, 10, 841}, + dictWord{ + 143, + 10, + 98, + }, + dictWord{134, 10, 426}, + dictWord{10, 10, 608}, + dictWord{139, 10, 1002}, + dictWord{138, 10, 250}, + dictWord{6, 0, 25}, + dictWord{7, 0, 855}, + dictWord{7, 0, 1258}, + dictWord{144, 0, 32}, + dictWord{7, 11, 1725}, + dictWord{138, 11, 393}, + dictWord{5, 11, 263}, + dictWord{134, 11, 414}, + dictWord{6, 0, 2011}, + dictWord{133, 10, 476}, + dictWord{4, 0, 4}, + dictWord{7, 0, 1118}, + dictWord{7, 0, 1320}, + dictWord{7, 0, 1706}, + dictWord{8, 0, 277}, + dictWord{9, 0, 622}, + dictWord{ + 10, + 0, + 9, + }, + dictWord{11, 0, 724}, + dictWord{12, 0, 350}, + dictWord{12, 0, 397}, + dictWord{13, 0, 28}, + dictWord{13, 0, 159}, + dictWord{15, 0, 89}, + dictWord{18, 0, 5}, + dictWord{ + 19, + 0, + 9, + }, + dictWord{20, 0, 34}, + dictWord{22, 0, 47}, + dictWord{6, 11, 178}, + dictWord{6, 11, 1750}, + dictWord{8, 11, 251}, + dictWord{9, 11, 690}, + dictWord{ + 10, + 11, + 155, + }, + dictWord{10, 11, 196}, + dictWord{10, 11, 373}, + dictWord{11, 11, 698}, + dictWord{13, 11, 155}, + dictWord{148, 11, 93}, + dictWord{5, 11, 97}, + dictWord{ + 137, + 11, + 393, + }, + dictWord{7, 0, 764}, + dictWord{11, 0, 461}, + dictWord{12, 0, 172}, + dictWord{5, 10, 76}, + dictWord{6, 10, 458}, + dictWord{6, 10, 497}, + dictWord{ + 7, + 10, + 868, + }, + dictWord{9, 10, 658}, + dictWord{10, 10, 594}, + dictWord{11, 10, 566}, + dictWord{12, 10, 338}, + dictWord{141, 10, 200}, + dictWord{134, 0, 1449}, + dictWord{138, 11, 40}, + dictWord{134, 11, 1639}, + dictWord{134, 0, 1445}, + dictWord{6, 0, 1168}, + dictWord{4, 10, 526}, + dictWord{7, 10, 1029}, + dictWord{ + 135, + 10, + 1054, + }, + dictWord{4, 11, 191}, + dictWord{7, 11, 934}, + dictWord{8, 11, 647}, + dictWord{145, 11, 97}, + dictWord{132, 10, 636}, + dictWord{6, 0, 233}, + dictWord{ + 7, + 10, + 660, + }, + dictWord{7, 10, 1124}, + dictWord{17, 10, 31}, + dictWord{19, 10, 22}, + dictWord{151, 10, 14}, + dictWord{6, 10, 1699}, + dictWord{136, 11, 110}, + dictWord{ + 12, + 11, + 246, + }, + dictWord{15, 11, 162}, + dictWord{19, 11, 64}, + dictWord{20, 11, 8}, + dictWord{20, 11, 95}, + dictWord{22, 11, 24}, + dictWord{152, 11, 17}, + dictWord{ + 5, + 11, + 165, + }, + dictWord{9, 11, 346}, + dictWord{138, 11, 655}, + dictWord{5, 11, 319}, + dictWord{135, 11, 534}, + dictWord{134, 0, 255}, + dictWord{9, 0, 216}, + dictWord{ + 8, + 11, + 128, + }, + dictWord{139, 11, 179}, + dictWord{9, 0, 183}, + dictWord{139, 0, 286}, + dictWord{11, 0, 956}, + dictWord{151, 0, 3}, + dictWord{4, 0, 536}, + dictWord{ + 7, + 0, + 1141, + }, + dictWord{10, 0, 723}, + dictWord{139, 0, 371}, + dictWord{4, 10, 279}, + dictWord{7, 10, 301}, + dictWord{137, 10, 362}, + dictWord{7, 0, 285}, + dictWord{ + 5, + 11, + 57, + }, + dictWord{6, 11, 101}, + dictWord{6, 11, 1663}, + dictWord{7, 11, 132}, + dictWord{7, 11, 1048}, + dictWord{7, 11, 1154}, + dictWord{7, 11, 1415}, + dictWord{ + 7, + 11, + 1507, + }, + dictWord{12, 11, 493}, + dictWord{15, 11, 105}, + dictWord{151, 11, 15}, + dictWord{5, 11, 459}, + dictWord{7, 11, 1073}, + dictWord{7, 10, 1743}, + dictWord{ + 8, + 11, + 241, + }, + dictWord{136, 11, 334}, + dictWord{4, 10, 178}, + dictWord{133, 10, 399}, + dictWord{135, 0, 560}, + dictWord{132, 0, 690}, + dictWord{135, 0, 1246}, + dictWord{18, 0, 157}, + dictWord{147, 0, 63}, + dictWord{10, 0, 599}, + dictWord{11, 0, 33}, + dictWord{12, 0, 571}, + dictWord{149, 0, 1}, + dictWord{6, 11, 324}, + dictWord{ + 6, + 11, + 520, + }, + dictWord{7, 11, 338}, + dictWord{7, 11, 1616}, + dictWord{7, 11, 1729}, + dictWord{8, 11, 228}, + dictWord{9, 11, 69}, + dictWord{139, 11, 750}, + dictWord{ + 7, + 0, + 1862, + }, + dictWord{12, 0, 491}, + dictWord{12, 0, 520}, + dictWord{13, 0, 383}, + dictWord{142, 0, 244}, + dictWord{135, 11, 734}, + dictWord{134, 10, 1692}, + dictWord{10, 0, 448}, + dictWord{11, 0, 630}, + dictWord{17, 0, 117}, + dictWord{6, 10, 202}, + dictWord{7, 11, 705}, + dictWord{12, 10, 360}, + dictWord{17, 10, 118}, + dictWord{18, 10, 27}, + dictWord{148, 10, 67}, + dictWord{4, 11, 73}, + dictWord{6, 11, 612}, + dictWord{7, 11, 927}, + dictWord{7, 11, 1822}, + dictWord{8, 11, 217}, + dictWord{ + 9, + 11, + 472, + }, + dictWord{9, 11, 765}, + dictWord{9, 11, 766}, + dictWord{10, 11, 408}, + dictWord{11, 11, 51}, + dictWord{11, 11, 793}, + dictWord{12, 11, 266}, + dictWord{ + 15, + 11, + 158, + }, + dictWord{20, 11, 89}, + dictWord{150, 11, 32}, + dictWord{4, 0, 190}, + dictWord{133, 0, 554}, + dictWord{133, 0, 1001}, + dictWord{5, 11, 389}, + dictWord{ + 8, + 11, + 636, + }, + dictWord{137, 11, 229}, + dictWord{5, 0, 446}, + dictWord{7, 10, 872}, + dictWord{10, 10, 516}, + dictWord{139, 10, 167}, + dictWord{137, 10, 313}, + dictWord{132, 10, 224}, + dictWord{134, 0, 1313}, + dictWord{5, 10, 546}, + dictWord{7, 10, 35}, + dictWord{8, 10, 11}, + dictWord{8, 10, 12}, + dictWord{9, 10, 315}, + dictWord{9, 10, 533}, + dictWord{10, 10, 802}, + dictWord{11, 10, 166}, + dictWord{12, 10, 525}, + dictWord{142, 10, 243}, + dictWord{6, 0, 636}, + dictWord{137, 0, 837}, + dictWord{5, 10, 241}, + dictWord{8, 10, 242}, + dictWord{9, 10, 451}, + dictWord{10, 10, 667}, + dictWord{11, 10, 598}, + dictWord{140, 10, 429}, + dictWord{22, 10, 46}, + dictWord{150, 11, 46}, + dictWord{136, 11, 472}, + dictWord{11, 0, 278}, + dictWord{142, 0, 73}, + dictWord{141, 11, 185}, + dictWord{132, 0, 868}, + dictWord{ + 134, + 0, + 972, + }, + dictWord{4, 10, 366}, + dictWord{137, 10, 516}, + dictWord{138, 0, 1010}, + dictWord{5, 11, 189}, + dictWord{6, 10, 1736}, + dictWord{7, 11, 442}, + dictWord{ + 7, + 11, + 443, + }, + dictWord{8, 11, 281}, + dictWord{12, 11, 174}, + dictWord{13, 11, 83}, + dictWord{141, 11, 261}, + dictWord{139, 11, 384}, + dictWord{6, 11, 2}, + dictWord{ + 7, + 11, + 191, + }, + dictWord{7, 11, 446}, + dictWord{7, 11, 758}, + dictWord{7, 11, 1262}, + dictWord{7, 11, 1737}, + dictWord{8, 11, 22}, + dictWord{8, 11, 270}, + dictWord{ + 8, + 11, + 612, + }, + dictWord{9, 11, 4}, + dictWord{9, 11, 167}, + dictWord{9, 11, 312}, + dictWord{9, 11, 436}, + dictWord{10, 11, 156}, + dictWord{10, 11, 216}, + dictWord{ + 10, + 11, + 311, + }, + dictWord{10, 11, 623}, + dictWord{11, 11, 72}, + dictWord{11, 11, 330}, + dictWord{11, 11, 455}, + dictWord{12, 11, 101}, + dictWord{12, 11, 321}, + dictWord{ + 12, + 11, + 504, + }, + dictWord{12, 11, 530}, + dictWord{12, 11, 543}, + dictWord{13, 11, 17}, + dictWord{13, 11, 156}, + dictWord{13, 11, 334}, + dictWord{14, 11, 48}, + dictWord{15, 11, 70}, + dictWord{17, 11, 60}, + dictWord{148, 11, 64}, + dictWord{6, 10, 331}, + dictWord{136, 10, 623}, + dictWord{135, 0, 1231}, + dictWord{132, 0, 304}, + dictWord{6, 11, 60}, + dictWord{7, 11, 670}, + dictWord{7, 11, 1327}, + dictWord{8, 11, 411}, + dictWord{8, 11, 435}, + dictWord{9, 11, 653}, + dictWord{9, 11, 740}, + dictWord{10, 11, 385}, + dictWord{11, 11, 222}, + dictWord{11, 11, 324}, + dictWord{11, 11, 829}, + dictWord{140, 11, 611}, + dictWord{7, 0, 506}, + dictWord{6, 11, 166}, + dictWord{7, 11, 374}, + dictWord{135, 11, 1174}, + dictWord{14, 11, 43}, + dictWord{146, 11, 21}, + dictWord{135, 11, 1694}, + dictWord{135, 10, 1888}, + dictWord{ + 5, + 11, + 206, + }, + dictWord{134, 11, 398}, + dictWord{135, 11, 50}, + dictWord{150, 0, 26}, + dictWord{6, 0, 53}, + dictWord{6, 0, 199}, + dictWord{7, 0, 1408}, + dictWord{ + 8, + 0, + 32, + }, + dictWord{8, 0, 93}, + dictWord{10, 0, 397}, + dictWord{10, 0, 629}, + dictWord{11, 0, 593}, + dictWord{11, 0, 763}, + dictWord{13, 0, 326}, + dictWord{145, 0, 35}, + dictWord{134, 0, 105}, + dictWord{132, 10, 394}, + dictWord{4, 0, 843}, + dictWord{138, 0, 794}, + dictWord{11, 0, 704}, + dictWord{141, 0, 396}, + dictWord{5, 0, 114}, + dictWord{5, 0, 255}, + dictWord{141, 0, 285}, + dictWord{6, 0, 619}, + dictWord{7, 0, 898}, + dictWord{7, 0, 1092}, + dictWord{8, 0, 485}, + dictWord{18, 0, 28}, + dictWord{ + 19, + 0, + 116, + }, + dictWord{135, 10, 1931}, + dictWord{9, 0, 145}, + dictWord{7, 10, 574}, + dictWord{135, 10, 1719}, + dictWord{7, 0, 2035}, + dictWord{8, 0, 19}, + dictWord{ + 9, + 0, + 89, + }, + dictWord{138, 0, 831}, + dictWord{132, 10, 658}, + dictWord{6, 11, 517}, + dictWord{7, 11, 1159}, + dictWord{10, 11, 621}, + dictWord{139, 11, 192}, + dictWord{ + 7, + 0, + 1933, + }, + dictWord{7, 11, 1933}, + dictWord{9, 10, 781}, + dictWord{10, 10, 144}, + dictWord{11, 10, 385}, + dictWord{13, 10, 161}, + dictWord{13, 10, 228}, + dictWord{13, 10, 268}, + dictWord{148, 10, 107}, + dictWord{136, 10, 374}, + dictWord{10, 11, 223}, + dictWord{139, 11, 645}, + dictWord{135, 0, 1728}, + dictWord{ + 7, + 11, + 64, + }, + dictWord{7, 11, 289}, + dictWord{136, 11, 245}, + dictWord{4, 10, 344}, + dictWord{6, 10, 498}, + dictWord{139, 10, 323}, + dictWord{136, 0, 746}, + dictWord{ + 135, + 10, + 1063, + }, + dictWord{137, 10, 155}, + dictWord{4, 0, 987}, + dictWord{6, 0, 1964}, + dictWord{6, 0, 1974}, + dictWord{6, 0, 1990}, + dictWord{136, 0, 995}, + dictWord{133, 11, 609}, + dictWord{133, 10, 906}, + dictWord{134, 0, 1550}, + dictWord{134, 0, 874}, + dictWord{5, 11, 129}, + dictWord{6, 11, 61}, + dictWord{ + 135, + 11, + 947, + }, + dictWord{4, 0, 1018}, + dictWord{6, 0, 1938}, + dictWord{6, 0, 2021}, + dictWord{134, 0, 2039}, + dictWord{132, 0, 814}, + dictWord{11, 0, 126}, + dictWord{ + 139, + 0, + 287, + }, + dictWord{134, 0, 1264}, + dictWord{5, 0, 955}, + dictWord{136, 0, 814}, + dictWord{141, 11, 506}, + dictWord{132, 11, 314}, + dictWord{6, 0, 981}, + dictWord{139, 11, 1000}, + dictWord{5, 0, 56}, + dictWord{8, 0, 892}, + dictWord{8, 0, 915}, + dictWord{140, 0, 776}, + dictWord{148, 0, 100}, + dictWord{10, 0, 4}, + dictWord{ + 10, + 0, + 13, + }, + dictWord{11, 0, 638}, + dictWord{148, 0, 57}, + dictWord{148, 11, 74}, + dictWord{5, 0, 738}, + dictWord{132, 10, 616}, + dictWord{133, 11, 637}, + dictWord{ + 136, + 10, + 692, + }, + dictWord{133, 0, 758}, + dictWord{132, 10, 305}, + dictWord{137, 11, 590}, + dictWord{5, 11, 280}, + dictWord{135, 11, 1226}, + dictWord{ + 134, + 11, + 494, + }, + dictWord{135, 0, 1112}, + dictWord{133, 11, 281}, + dictWord{13, 0, 44}, + dictWord{14, 0, 214}, + dictWord{5, 10, 214}, + dictWord{7, 10, 603}, + dictWord{ + 8, + 10, + 611, + }, + dictWord{9, 10, 686}, + dictWord{10, 10, 88}, + dictWord{11, 10, 459}, + dictWord{11, 10, 496}, + dictWord{12, 10, 463}, + dictWord{140, 10, 590}, + dictWord{ + 139, + 0, + 328, + }, + dictWord{135, 11, 1064}, + dictWord{137, 0, 133}, + dictWord{7, 0, 168}, + dictWord{13, 0, 196}, + dictWord{141, 0, 237}, + dictWord{134, 10, 1703}, + dictWord{134, 0, 1152}, + dictWord{135, 0, 1245}, + dictWord{5, 0, 110}, + dictWord{6, 0, 169}, + dictWord{6, 0, 1702}, + dictWord{7, 0, 400}, + dictWord{8, 0, 538}, + dictWord{ + 9, + 0, + 184, + }, + dictWord{9, 0, 524}, + dictWord{140, 0, 218}, + dictWord{6, 0, 1816}, + dictWord{10, 0, 871}, + dictWord{12, 0, 769}, + dictWord{140, 0, 785}, + dictWord{ + 132, + 11, + 630, + }, + dictWord{7, 11, 33}, + dictWord{7, 11, 120}, + dictWord{8, 11, 489}, + dictWord{9, 11, 319}, + dictWord{10, 11, 820}, + dictWord{11, 11, 1004}, + dictWord{ + 12, + 11, + 379, + }, + dictWord{13, 11, 117}, + dictWord{13, 11, 412}, + dictWord{14, 11, 25}, + dictWord{15, 11, 52}, + dictWord{15, 11, 161}, + dictWord{16, 11, 47}, + dictWord{149, 11, 2}, + dictWord{6, 0, 133}, + dictWord{8, 0, 413}, + dictWord{9, 0, 353}, + dictWord{139, 0, 993}, + dictWord{145, 10, 19}, + dictWord{4, 11, 937}, + dictWord{ + 133, + 11, + 801, + }, + dictWord{134, 0, 978}, + dictWord{6, 0, 93}, + dictWord{6, 0, 1508}, + dictWord{7, 0, 1422}, + dictWord{7, 0, 1851}, + dictWord{8, 0, 673}, + dictWord{9, 0, 529}, + dictWord{140, 0, 43}, + dictWord{6, 0, 317}, + dictWord{10, 0, 512}, + dictWord{4, 10, 737}, + dictWord{11, 10, 294}, + dictWord{12, 10, 60}, + dictWord{12, 10, 437}, + dictWord{13, 10, 64}, + dictWord{13, 10, 380}, + dictWord{142, 10, 430}, + dictWord{9, 0, 371}, + dictWord{7, 11, 1591}, + dictWord{144, 11, 43}, + dictWord{6, 10, 1758}, + dictWord{8, 10, 520}, + dictWord{9, 10, 345}, + dictWord{9, 10, 403}, + dictWord{142, 10, 350}, + dictWord{5, 0, 526}, + dictWord{10, 10, 242}, + dictWord{ + 138, + 10, + 579, + }, + dictWord{9, 0, 25}, + dictWord{10, 0, 467}, + dictWord{138, 0, 559}, + dictWord{5, 10, 139}, + dictWord{7, 10, 1168}, + dictWord{138, 10, 539}, + dictWord{ + 4, + 0, + 335, + }, + dictWord{135, 0, 942}, + dictWord{140, 0, 754}, + dictWord{132, 11, 365}, + dictWord{11, 0, 182}, + dictWord{142, 0, 195}, + dictWord{142, 11, 29}, + dictWord{ + 5, + 11, + 7, + }, + dictWord{139, 11, 774}, + dictWord{4, 11, 746}, + dictWord{135, 11, 1090}, + dictWord{8, 0, 39}, + dictWord{10, 0, 773}, + dictWord{11, 0, 84}, + dictWord{ + 12, + 0, + 205, + }, + dictWord{142, 0, 1}, + dictWord{5, 0, 601}, + dictWord{5, 0, 870}, + dictWord{5, 11, 360}, + dictWord{136, 11, 237}, + dictWord{132, 0, 181}, + dictWord{ + 136, + 0, + 370, + }, + dictWord{134, 0, 1652}, + dictWord{8, 0, 358}, + dictWord{4, 10, 107}, + dictWord{7, 10, 613}, + dictWord{8, 10, 439}, + dictWord{8, 10, 504}, + dictWord{ + 9, + 10, + 501, + }, + dictWord{10, 10, 383}, + dictWord{139, 10, 477}, + dictWord{132, 10, 229}, + dictWord{137, 11, 785}, + dictWord{4, 0, 97}, + dictWord{5, 0, 147}, + dictWord{ + 6, + 0, + 286, + }, + dictWord{7, 0, 1362}, + dictWord{141, 0, 176}, + dictWord{6, 0, 537}, + dictWord{7, 0, 788}, + dictWord{7, 0, 1816}, + dictWord{132, 10, 903}, + dictWord{ + 140, + 10, + 71, + }, + dictWord{6, 0, 743}, + dictWord{134, 0, 1223}, + dictWord{6, 0, 375}, + dictWord{7, 0, 169}, + dictWord{7, 0, 254}, + dictWord{8, 0, 780}, + dictWord{135, 11, 1493}, + dictWord{7, 0, 1714}, + dictWord{4, 10, 47}, + dictWord{6, 10, 373}, + dictWord{7, 10, 452}, + dictWord{7, 10, 543}, + dictWord{7, 10, 1856}, + dictWord{9, 10, 6}, + dictWord{ + 11, + 10, + 257, + }, + dictWord{139, 10, 391}, + dictWord{6, 0, 896}, + dictWord{136, 0, 1003}, + dictWord{135, 0, 1447}, + dictWord{137, 11, 341}, + dictWord{5, 10, 980}, + dictWord{134, 10, 1754}, + dictWord{145, 11, 22}, + dictWord{4, 11, 277}, + dictWord{5, 11, 608}, + dictWord{6, 11, 493}, + dictWord{7, 11, 457}, + dictWord{ + 140, + 11, + 384, + }, + dictWord{7, 10, 536}, + dictWord{7, 10, 1331}, + dictWord{136, 10, 143}, + dictWord{140, 0, 744}, + dictWord{7, 11, 27}, + dictWord{135, 11, 316}, + dictWord{ + 18, + 0, + 126, + }, + dictWord{5, 10, 19}, + dictWord{134, 10, 533}, + dictWord{4, 0, 788}, + dictWord{11, 0, 41}, + dictWord{5, 11, 552}, + dictWord{5, 11, 586}, + dictWord{ + 5, + 11, + 676, + }, + dictWord{6, 11, 448}, + dictWord{8, 11, 244}, + dictWord{11, 11, 1}, + dictWord{11, 11, 41}, + dictWord{13, 11, 3}, + dictWord{16, 11, 54}, + dictWord{17, 11, 4}, + dictWord{146, 11, 13}, + dictWord{4, 0, 985}, + dictWord{6, 0, 1801}, + dictWord{4, 11, 401}, + dictWord{137, 11, 264}, + dictWord{5, 10, 395}, + dictWord{5, 10, 951}, + dictWord{134, 10, 1776}, + dictWord{5, 0, 629}, + dictWord{135, 0, 1549}, + dictWord{11, 10, 663}, + dictWord{12, 10, 210}, + dictWord{13, 10, 166}, + dictWord{ + 13, + 10, + 310, + }, + dictWord{14, 10, 373}, + dictWord{147, 10, 43}, + dictWord{9, 11, 543}, + dictWord{10, 11, 524}, + dictWord{11, 11, 30}, + dictWord{12, 11, 524}, + dictWord{ + 14, + 11, + 315, + }, + dictWord{16, 11, 18}, + dictWord{20, 11, 26}, + dictWord{148, 11, 65}, + dictWord{4, 11, 205}, + dictWord{5, 11, 623}, + dictWord{7, 11, 104}, + dictWord{ + 136, + 11, + 519, + }, + dictWord{5, 0, 293}, + dictWord{134, 0, 601}, + dictWord{7, 11, 579}, + dictWord{9, 11, 41}, + dictWord{9, 11, 244}, + dictWord{9, 11, 669}, + dictWord{ + 10, + 11, + 5, + }, + dictWord{11, 11, 861}, + dictWord{11, 11, 951}, + dictWord{139, 11, 980}, + dictWord{132, 11, 717}, + dictWord{132, 10, 695}, + dictWord{7, 10, 497}, + dictWord{ + 9, + 10, + 387, + }, + dictWord{147, 10, 81}, + dictWord{132, 0, 420}, + dictWord{142, 0, 37}, + dictWord{6, 0, 1134}, + dictWord{6, 0, 1900}, + dictWord{12, 0, 830}, + dictWord{ + 12, + 0, + 878, + }, + dictWord{12, 0, 894}, + dictWord{15, 0, 221}, + dictWord{143, 0, 245}, + dictWord{132, 11, 489}, + dictWord{7, 0, 1570}, + dictWord{140, 0, 542}, + dictWord{ + 8, + 0, + 933, + }, + dictWord{136, 0, 957}, + dictWord{6, 0, 1371}, + dictWord{7, 0, 31}, + dictWord{8, 0, 373}, + dictWord{5, 10, 284}, + dictWord{6, 10, 49}, + dictWord{6, 10, 350}, + dictWord{7, 10, 377}, + dictWord{7, 10, 1693}, + dictWord{8, 10, 678}, + dictWord{9, 10, 161}, + dictWord{9, 10, 585}, + dictWord{9, 10, 671}, + dictWord{9, 10, 839}, + dictWord{11, 10, 912}, + dictWord{141, 10, 427}, + dictWord{135, 11, 892}, + dictWord{4, 0, 325}, + dictWord{138, 0, 125}, + dictWord{139, 11, 47}, + dictWord{ + 132, + 10, + 597, + }, + dictWord{138, 0, 323}, + dictWord{6, 0, 1547}, + dictWord{7, 11, 1605}, + dictWord{9, 11, 473}, + dictWord{11, 11, 962}, + dictWord{146, 11, 139}, + dictWord{ + 139, + 10, + 908, + }, + dictWord{7, 11, 819}, + dictWord{9, 11, 26}, + dictWord{9, 11, 392}, + dictWord{10, 11, 152}, + dictWord{10, 11, 226}, + dictWord{11, 11, 19}, + dictWord{ + 12, + 11, + 276, + }, + dictWord{12, 11, 426}, + dictWord{12, 11, 589}, + dictWord{13, 11, 460}, + dictWord{15, 11, 97}, + dictWord{19, 11, 48}, + dictWord{148, 11, 104}, + dictWord{135, 11, 51}, + dictWord{4, 0, 718}, + dictWord{135, 0, 1216}, + dictWord{6, 0, 1896}, + dictWord{6, 0, 1905}, + dictWord{6, 0, 1912}, + dictWord{9, 0, 947}, + dictWord{ + 9, + 0, + 974, + }, + dictWord{12, 0, 809}, + dictWord{12, 0, 850}, + dictWord{12, 0, 858}, + dictWord{12, 0, 874}, + dictWord{12, 0, 887}, + dictWord{12, 0, 904}, + dictWord{ + 12, + 0, + 929, + }, + dictWord{12, 0, 948}, + dictWord{12, 0, 952}, + dictWord{15, 0, 198}, + dictWord{15, 0, 206}, + dictWord{15, 0, 220}, + dictWord{15, 0, 227}, + dictWord{15, 0, 247}, + dictWord{18, 0, 188}, + dictWord{21, 0, 48}, + dictWord{21, 0, 50}, + dictWord{24, 0, 25}, + dictWord{24, 0, 29}, + dictWord{7, 11, 761}, + dictWord{7, 11, 1051}, + dictWord{ + 137, + 11, + 545, + }, + dictWord{5, 0, 124}, + dictWord{5, 0, 144}, + dictWord{6, 0, 548}, + dictWord{7, 0, 15}, + dictWord{7, 0, 153}, + dictWord{137, 0, 629}, + dictWord{ + 135, + 11, + 606, + }, + dictWord{135, 10, 2014}, + dictWord{7, 10, 2007}, + dictWord{9, 11, 46}, + dictWord{9, 10, 101}, + dictWord{9, 10, 450}, + dictWord{10, 10, 66}, + dictWord{ + 10, + 10, + 842, + }, + dictWord{11, 10, 536}, + dictWord{140, 10, 587}, + dictWord{6, 0, 75}, + dictWord{7, 0, 1531}, + dictWord{8, 0, 416}, + dictWord{9, 0, 240}, + dictWord{9, 0, 275}, + dictWord{10, 0, 100}, + dictWord{11, 0, 658}, + dictWord{11, 0, 979}, + dictWord{12, 0, 86}, + dictWord{14, 0, 207}, + dictWord{15, 0, 20}, + dictWord{143, 0, 25}, + dictWord{ + 5, + 0, + 141, + }, + dictWord{5, 0, 915}, + dictWord{6, 0, 1783}, + dictWord{7, 0, 211}, + dictWord{7, 0, 698}, + dictWord{7, 0, 1353}, + dictWord{9, 0, 83}, + dictWord{9, 0, 281}, + dictWord{ + 10, + 0, + 376, + }, + dictWord{10, 0, 431}, + dictWord{11, 0, 543}, + dictWord{12, 0, 664}, + dictWord{13, 0, 280}, + dictWord{13, 0, 428}, + dictWord{14, 0, 61}, + dictWord{ + 14, + 0, + 128, + }, + dictWord{17, 0, 52}, + dictWord{145, 0, 81}, + dictWord{132, 11, 674}, + dictWord{135, 0, 533}, + dictWord{149, 0, 6}, + dictWord{132, 11, 770}, + dictWord{ + 133, + 0, + 538, + }, + dictWord{5, 11, 79}, + dictWord{7, 11, 1027}, + dictWord{7, 11, 1477}, + dictWord{139, 11, 52}, + dictWord{139, 10, 62}, + dictWord{4, 0, 338}, + dictWord{ + 133, + 0, + 400, + }, + dictWord{5, 11, 789}, + dictWord{134, 11, 195}, + dictWord{4, 11, 251}, + dictWord{4, 11, 688}, + dictWord{7, 11, 513}, + dictWord{7, 11, 1284}, + dictWord{ + 9, + 11, + 87, + }, + dictWord{138, 11, 365}, + dictWord{134, 10, 1766}, + dictWord{6, 0, 0}, + dictWord{7, 0, 84}, + dictWord{11, 0, 895}, + dictWord{145, 0, 11}, + dictWord{ + 139, + 0, + 892, + }, + dictWord{4, 0, 221}, + dictWord{5, 0, 659}, + dictWord{7, 0, 697}, + dictWord{7, 0, 1211}, + dictWord{138, 0, 284}, + dictWord{133, 0, 989}, + dictWord{ + 133, + 11, + 889, + }, + dictWord{4, 11, 160}, + dictWord{5, 11, 330}, + dictWord{7, 11, 1434}, + dictWord{136, 11, 174}, + dictWord{6, 10, 1665}, + dictWord{7, 10, 256}, + dictWord{ + 7, + 10, + 1388, + }, + dictWord{10, 10, 499}, + dictWord{139, 10, 670}, + dictWord{7, 0, 848}, + dictWord{4, 10, 22}, + dictWord{5, 10, 10}, + dictWord{136, 10, 97}, + dictWord{ + 138, + 0, + 507, + }, + dictWord{133, 10, 481}, + dictWord{4, 0, 188}, + dictWord{135, 0, 805}, + dictWord{5, 0, 884}, + dictWord{6, 0, 732}, + dictWord{139, 0, 991}, + dictWord{ + 135, + 11, + 968, + }, + dictWord{11, 11, 636}, + dictWord{15, 11, 145}, + dictWord{17, 11, 34}, + dictWord{19, 11, 50}, + dictWord{151, 11, 20}, + dictWord{7, 0, 959}, + dictWord{ + 16, + 0, + 60, + }, + dictWord{6, 10, 134}, + dictWord{7, 10, 437}, + dictWord{9, 10, 37}, + dictWord{14, 10, 285}, + dictWord{142, 10, 371}, + dictWord{7, 10, 486}, + dictWord{ + 8, + 10, + 155, + }, + dictWord{11, 10, 93}, + dictWord{140, 10, 164}, + dictWord{134, 0, 1653}, + dictWord{7, 0, 337}, + dictWord{133, 10, 591}, + dictWord{6, 0, 1989}, + dictWord{ + 8, + 0, + 922, + }, + dictWord{8, 0, 978}, + dictWord{133, 11, 374}, + dictWord{132, 0, 638}, + dictWord{138, 0, 500}, + dictWord{133, 11, 731}, + dictWord{5, 10, 380}, + dictWord{ + 5, + 10, + 650, + }, + dictWord{136, 10, 310}, + dictWord{138, 11, 381}, + dictWord{4, 10, 364}, + dictWord{7, 10, 1156}, + dictWord{7, 10, 1187}, + dictWord{137, 10, 409}, + dictWord{137, 11, 224}, + dictWord{140, 0, 166}, + dictWord{134, 10, 482}, + dictWord{4, 11, 626}, + dictWord{5, 11, 642}, + dictWord{6, 11, 425}, + dictWord{ + 10, + 11, + 202, + }, + dictWord{139, 11, 141}, + dictWord{4, 10, 781}, + dictWord{6, 10, 487}, + dictWord{7, 10, 926}, + dictWord{8, 10, 263}, + dictWord{139, 10, 500}, + dictWord{ + 135, + 0, + 418, + }, + dictWord{4, 10, 94}, + dictWord{135, 10, 1265}, + dictWord{136, 0, 760}, + dictWord{132, 10, 417}, + dictWord{136, 11, 835}, + dictWord{5, 10, 348}, + dictWord{134, 10, 522}, + dictWord{6, 0, 1277}, + dictWord{134, 0, 1538}, + dictWord{139, 11, 541}, + dictWord{135, 11, 1597}, + dictWord{5, 11, 384}, + dictWord{ + 8, + 11, + 455, + }, + dictWord{140, 11, 48}, + dictWord{136, 0, 770}, + dictWord{5, 11, 264}, + dictWord{134, 11, 184}, + dictWord{4, 0, 89}, + dictWord{5, 0, 489}, + dictWord{ + 6, + 0, + 315, + }, + dictWord{7, 0, 553}, + dictWord{7, 0, 1745}, + dictWord{138, 0, 243}, + dictWord{4, 10, 408}, + dictWord{4, 10, 741}, + dictWord{135, 10, 500}, + dictWord{ + 134, + 0, + 1396, + }, + dictWord{133, 0, 560}, + dictWord{6, 0, 1658}, + dictWord{9, 0, 3}, + dictWord{10, 0, 154}, + dictWord{11, 0, 641}, + dictWord{13, 0, 85}, + dictWord{13, 0, 201}, + dictWord{141, 0, 346}, + dictWord{135, 11, 1595}, + dictWord{5, 11, 633}, + dictWord{6, 11, 28}, + dictWord{7, 11, 219}, + dictWord{135, 11, 1323}, + dictWord{ + 9, + 11, + 769, + }, + dictWord{140, 11, 185}, + dictWord{135, 11, 785}, + dictWord{7, 11, 359}, + dictWord{8, 11, 243}, + dictWord{140, 11, 175}, + dictWord{138, 0, 586}, + dictWord{ + 7, + 0, + 1271, + }, + dictWord{134, 10, 73}, + dictWord{132, 11, 105}, + dictWord{4, 0, 166}, + dictWord{5, 0, 505}, + dictWord{134, 0, 1670}, + dictWord{133, 10, 576}, + dictWord{4, 11, 324}, + dictWord{138, 11, 104}, + dictWord{142, 10, 231}, + dictWord{6, 0, 637}, + dictWord{7, 10, 1264}, + dictWord{7, 10, 1678}, + dictWord{ + 11, + 10, + 945, + }, + dictWord{12, 10, 341}, + dictWord{12, 10, 471}, + dictWord{12, 10, 569}, + dictWord{23, 11, 21}, + dictWord{151, 11, 23}, + dictWord{8, 11, 559}, + dictWord{ + 141, + 11, + 109, + }, + dictWord{134, 0, 1947}, + dictWord{7, 0, 445}, + dictWord{8, 0, 307}, + dictWord{8, 0, 704}, + dictWord{10, 0, 41}, + dictWord{10, 0, 439}, + dictWord{ + 11, + 0, + 237, + }, + dictWord{11, 0, 622}, + dictWord{140, 0, 201}, + dictWord{135, 11, 963}, + dictWord{135, 0, 1977}, + dictWord{4, 0, 189}, + dictWord{5, 0, 713}, + dictWord{ + 136, + 0, + 57, + }, + dictWord{138, 0, 371}, + dictWord{135, 10, 538}, + dictWord{132, 0, 552}, + dictWord{6, 0, 883}, + dictWord{133, 10, 413}, + dictWord{6, 0, 923}, + dictWord{ + 132, + 11, + 758, + }, + dictWord{138, 11, 215}, + dictWord{136, 10, 495}, + dictWord{7, 10, 54}, + dictWord{8, 10, 312}, + dictWord{10, 10, 191}, + dictWord{10, 10, 614}, + dictWord{140, 10, 567}, + dictWord{7, 11, 351}, + dictWord{139, 11, 128}, + dictWord{7, 0, 875}, + dictWord{6, 10, 468}, + dictWord{7, 10, 1478}, + dictWord{8, 10, 530}, + dictWord{142, 10, 290}, + dictWord{135, 0, 1788}, + dictWord{17, 0, 49}, + dictWord{133, 11, 918}, + dictWord{12, 11, 398}, + dictWord{20, 11, 39}, + dictWord{ + 21, + 11, + 11, + }, + dictWord{150, 11, 41}, + dictWord{10, 0, 661}, + dictWord{6, 10, 484}, + dictWord{135, 10, 822}, + dictWord{135, 0, 1945}, + dictWord{134, 0, 794}, + dictWord{ + 137, + 10, + 900, + }, + dictWord{135, 10, 1335}, + dictWord{6, 10, 1724}, + dictWord{135, 10, 2022}, + dictWord{132, 11, 340}, + dictWord{134, 0, 1135}, + dictWord{ + 4, + 0, + 784, + }, + dictWord{133, 0, 745}, + dictWord{5, 0, 84}, + dictWord{134, 0, 163}, + dictWord{133, 0, 410}, + dictWord{4, 0, 976}, + dictWord{5, 11, 985}, + dictWord{7, 11, 509}, + dictWord{7, 11, 529}, + dictWord{145, 11, 96}, + dictWord{132, 10, 474}, + dictWord{134, 0, 703}, + dictWord{135, 11, 1919}, + dictWord{5, 0, 322}, + dictWord{ + 8, + 0, + 186, + }, + dictWord{9, 0, 262}, + dictWord{10, 0, 187}, + dictWord{142, 0, 208}, + dictWord{135, 10, 1504}, + dictWord{133, 0, 227}, + dictWord{9, 0, 560}, + dictWord{ + 13, + 0, + 208, + }, + dictWord{133, 10, 305}, + dictWord{132, 11, 247}, + dictWord{7, 0, 1395}, + dictWord{8, 0, 486}, + dictWord{9, 0, 236}, + dictWord{9, 0, 878}, + dictWord{ + 10, + 0, + 218, + }, + dictWord{11, 0, 95}, + dictWord{19, 0, 17}, + dictWord{147, 0, 31}, + dictWord{7, 0, 2043}, + dictWord{8, 0, 672}, + dictWord{141, 0, 448}, + dictWord{4, 11, 184}, + dictWord{5, 11, 390}, + dictWord{6, 11, 337}, + dictWord{7, 11, 23}, + dictWord{7, 11, 494}, + dictWord{7, 11, 618}, + dictWord{7, 11, 1456}, + dictWord{8, 11, 27}, + dictWord{ + 8, + 11, + 599, + }, + dictWord{10, 11, 153}, + dictWord{139, 11, 710}, + dictWord{135, 0, 466}, + dictWord{135, 10, 1236}, + dictWord{6, 0, 167}, + dictWord{7, 0, 186}, + dictWord{7, 0, 656}, + dictWord{10, 0, 643}, + dictWord{4, 10, 480}, + dictWord{6, 10, 302}, + dictWord{6, 10, 1642}, + dictWord{7, 10, 837}, + dictWord{7, 10, 1547}, + dictWord{ + 7, + 10, + 1657, + }, + dictWord{8, 10, 429}, + dictWord{9, 10, 228}, + dictWord{13, 10, 289}, + dictWord{13, 10, 343}, + dictWord{147, 10, 101}, + dictWord{134, 0, 1428}, + dictWord{134, 0, 1440}, + dictWord{5, 0, 412}, + dictWord{7, 10, 278}, + dictWord{10, 10, 739}, + dictWord{11, 10, 708}, + dictWord{141, 10, 348}, + dictWord{ + 134, + 0, + 1118, + }, + dictWord{136, 0, 562}, + dictWord{148, 11, 46}, + dictWord{9, 0, 316}, + dictWord{139, 0, 256}, + dictWord{134, 0, 1771}, + dictWord{135, 0, 1190}, + dictWord{137, 0, 132}, + dictWord{10, 11, 227}, + dictWord{11, 11, 497}, + dictWord{11, 11, 709}, + dictWord{140, 11, 415}, + dictWord{143, 0, 66}, + dictWord{6, 11, 360}, + dictWord{7, 11, 1664}, + dictWord{136, 11, 478}, + dictWord{144, 10, 28}, + dictWord{4, 0, 317}, + dictWord{135, 0, 1279}, + dictWord{5, 0, 63}, + dictWord{ + 133, + 0, + 509, + }, + dictWord{136, 11, 699}, + dictWord{145, 10, 36}, + dictWord{134, 0, 1475}, + dictWord{11, 11, 343}, + dictWord{142, 11, 127}, + dictWord{132, 11, 739}, + dictWord{132, 0, 288}, + dictWord{135, 11, 1757}, + dictWord{8, 0, 89}, + dictWord{8, 0, 620}, + dictWord{9, 0, 608}, + dictWord{11, 0, 628}, + dictWord{12, 0, 322}, + dictWord{143, 0, 124}, + dictWord{134, 0, 1225}, + dictWord{7, 0, 1189}, + dictWord{4, 11, 67}, + dictWord{5, 11, 422}, + dictWord{6, 10, 363}, + dictWord{7, 11, 1037}, + dictWord{7, 11, 1289}, + dictWord{7, 11, 1555}, + dictWord{7, 10, 1955}, + dictWord{8, 10, 725}, + dictWord{9, 11, 741}, + dictWord{145, 11, 108}, + dictWord{ + 134, + 0, + 1468, + }, + dictWord{6, 0, 689}, + dictWord{134, 0, 1451}, + dictWord{138, 0, 120}, + dictWord{151, 0, 1}, + dictWord{137, 10, 805}, + dictWord{142, 0, 329}, + dictWord{ + 5, + 10, + 813, + }, + dictWord{135, 10, 2046}, + dictWord{135, 0, 226}, + dictWord{138, 11, 96}, + dictWord{7, 0, 1855}, + dictWord{5, 10, 712}, + dictWord{11, 10, 17}, + dictWord{13, 10, 321}, + dictWord{144, 10, 67}, + dictWord{9, 0, 461}, + dictWord{6, 10, 320}, + dictWord{7, 10, 781}, + dictWord{7, 10, 1921}, + dictWord{9, 10, 55}, + dictWord{ + 10, + 10, + 186, + }, + dictWord{10, 10, 273}, + dictWord{10, 10, 664}, + dictWord{10, 10, 801}, + dictWord{11, 10, 996}, + dictWord{11, 10, 997}, + dictWord{13, 10, 157}, + dictWord{142, 10, 170}, + dictWord{8, 11, 203}, + dictWord{8, 10, 271}, + dictWord{11, 11, 823}, + dictWord{11, 11, 846}, + dictWord{12, 11, 482}, + dictWord{ + 13, + 11, + 133, + }, + dictWord{13, 11, 277}, + dictWord{13, 11, 302}, + dictWord{13, 11, 464}, + dictWord{14, 11, 205}, + dictWord{142, 11, 221}, + dictWord{135, 0, 1346}, + dictWord{4, 11, 449}, + dictWord{133, 11, 718}, + dictWord{134, 0, 85}, + dictWord{14, 0, 299}, + dictWord{7, 10, 103}, + dictWord{7, 10, 863}, + dictWord{11, 10, 184}, + dictWord{145, 10, 62}, + dictWord{4, 11, 355}, + dictWord{6, 11, 311}, + dictWord{9, 11, 256}, + dictWord{138, 11, 404}, + dictWord{137, 10, 659}, + dictWord{ + 138, + 11, + 758, + }, + dictWord{133, 11, 827}, + dictWord{5, 11, 64}, + dictWord{140, 11, 581}, + dictWord{134, 0, 1171}, + dictWord{4, 11, 442}, + dictWord{7, 11, 1047}, + dictWord{ + 7, + 11, + 1352, + }, + dictWord{135, 11, 1643}, + dictWord{132, 0, 980}, + dictWord{5, 11, 977}, + dictWord{6, 11, 288}, + dictWord{7, 11, 528}, + dictWord{135, 11, 1065}, + dictWord{5, 0, 279}, + dictWord{6, 0, 235}, + dictWord{7, 0, 468}, + dictWord{8, 0, 446}, + dictWord{9, 0, 637}, + dictWord{10, 0, 717}, + dictWord{11, 0, 738}, + dictWord{ + 140, + 0, + 514, + }, + dictWord{132, 0, 293}, + dictWord{11, 10, 337}, + dictWord{142, 10, 303}, + dictWord{136, 11, 285}, + dictWord{5, 0, 17}, + dictWord{6, 0, 371}, + dictWord{ + 9, + 0, + 528, + }, + dictWord{12, 0, 364}, + dictWord{132, 11, 254}, + dictWord{5, 10, 77}, + dictWord{7, 10, 1455}, + dictWord{10, 10, 843}, + dictWord{147, 10, 73}, + dictWord{ + 150, + 0, + 5, + }, + dictWord{132, 10, 458}, + dictWord{6, 11, 12}, + dictWord{7, 11, 1219}, + dictWord{145, 11, 73}, + dictWord{135, 10, 1420}, + dictWord{6, 10, 109}, + dictWord{138, 10, 382}, + dictWord{135, 11, 125}, + dictWord{6, 10, 330}, + dictWord{7, 10, 1084}, + dictWord{139, 10, 142}, + dictWord{6, 11, 369}, + dictWord{ + 6, + 11, + 502, + }, + dictWord{7, 11, 1036}, + dictWord{8, 11, 348}, + dictWord{9, 11, 452}, + dictWord{10, 11, 26}, + dictWord{11, 11, 224}, + dictWord{11, 11, 387}, + dictWord{ + 11, + 11, + 772, + }, + dictWord{12, 11, 95}, + dictWord{12, 11, 629}, + dictWord{13, 11, 195}, + dictWord{13, 11, 207}, + dictWord{13, 11, 241}, + dictWord{14, 11, 260}, + dictWord{ + 14, + 11, + 270, + }, + dictWord{143, 11, 140}, + dictWord{132, 11, 269}, + dictWord{5, 11, 480}, + dictWord{7, 11, 532}, + dictWord{7, 11, 1197}, + dictWord{7, 11, 1358}, + dictWord{8, 11, 291}, + dictWord{11, 11, 349}, + dictWord{142, 11, 396}, + dictWord{150, 0, 48}, + dictWord{10, 0, 601}, + dictWord{13, 0, 353}, + dictWord{141, 0, 376}, + dictWord{5, 0, 779}, + dictWord{5, 0, 807}, + dictWord{6, 0, 1655}, + dictWord{134, 0, 1676}, + dictWord{142, 11, 223}, + dictWord{4, 0, 196}, + dictWord{5, 0, 558}, + dictWord{133, 0, 949}, + dictWord{148, 11, 15}, + dictWord{135, 11, 1764}, + dictWord{134, 0, 1322}, + dictWord{132, 0, 752}, + dictWord{139, 0, 737}, + dictWord{ + 135, + 11, + 657, + }, + dictWord{136, 11, 533}, + dictWord{135, 0, 412}, + dictWord{4, 0, 227}, + dictWord{5, 0, 159}, + dictWord{5, 0, 409}, + dictWord{7, 0, 80}, + dictWord{8, 0, 556}, + dictWord{10, 0, 479}, + dictWord{12, 0, 418}, + dictWord{14, 0, 50}, + dictWord{14, 0, 123}, + dictWord{14, 0, 192}, + dictWord{14, 0, 249}, + dictWord{14, 0, 295}, + dictWord{143, 0, 27}, + dictWord{7, 0, 1470}, + dictWord{8, 0, 66}, + dictWord{8, 0, 137}, + dictWord{8, 0, 761}, + dictWord{9, 0, 638}, + dictWord{11, 0, 80}, + dictWord{11, 0, 212}, + dictWord{11, 0, 368}, + dictWord{11, 0, 418}, + dictWord{12, 0, 8}, + dictWord{13, 0, 15}, + dictWord{16, 0, 61}, + dictWord{17, 0, 59}, + dictWord{19, 0, 28}, + dictWord{ + 148, + 0, + 84, + }, + dictWord{135, 10, 1985}, + dictWord{4, 11, 211}, + dictWord{4, 11, 332}, + dictWord{5, 11, 335}, + dictWord{6, 11, 238}, + dictWord{7, 11, 269}, + dictWord{ + 7, + 11, + 811, + }, + dictWord{7, 11, 1797}, + dictWord{8, 10, 122}, + dictWord{8, 11, 836}, + dictWord{9, 11, 507}, + dictWord{141, 11, 242}, + dictWord{6, 0, 683}, + dictWord{ + 134, + 0, + 1252, + }, + dictWord{4, 0, 873}, + dictWord{132, 10, 234}, + dictWord{134, 0, 835}, + dictWord{6, 0, 38}, + dictWord{7, 0, 1220}, + dictWord{8, 0, 185}, + dictWord{8, 0, 256}, + dictWord{9, 0, 22}, + dictWord{9, 0, 331}, + dictWord{10, 0, 738}, + dictWord{11, 0, 205}, + dictWord{11, 0, 540}, + dictWord{11, 0, 746}, + dictWord{13, 0, 465}, + dictWord{ + 14, + 0, + 88, + }, + dictWord{142, 0, 194}, + dictWord{138, 0, 986}, + dictWord{5, 11, 1009}, + dictWord{12, 11, 582}, + dictWord{146, 11, 131}, + dictWord{4, 0, 159}, + dictWord{ + 6, + 0, + 115, + }, + dictWord{7, 0, 252}, + dictWord{7, 0, 257}, + dictWord{7, 0, 1928}, + dictWord{8, 0, 69}, + dictWord{9, 0, 384}, + dictWord{10, 0, 91}, + dictWord{10, 0, 615}, + dictWord{ + 12, + 0, + 375, + }, + dictWord{14, 0, 235}, + dictWord{18, 0, 117}, + dictWord{147, 0, 123}, + dictWord{133, 0, 911}, + dictWord{136, 0, 278}, + dictWord{5, 10, 430}, + dictWord{ + 5, + 10, + 932, + }, + dictWord{6, 10, 131}, + dictWord{7, 10, 417}, + dictWord{9, 10, 522}, + dictWord{11, 10, 314}, + dictWord{141, 10, 390}, + dictWord{14, 10, 149}, + dictWord{14, 10, 399}, + dictWord{143, 10, 57}, + dictWord{4, 0, 151}, + dictWord{7, 0, 1567}, + dictWord{136, 0, 749}, + dictWord{5, 11, 228}, + dictWord{6, 11, 203}, + dictWord{ + 7, + 11, + 156, + }, + dictWord{8, 11, 347}, + dictWord{137, 11, 265}, + dictWord{132, 10, 507}, + dictWord{10, 0, 989}, + dictWord{140, 0, 956}, + dictWord{133, 0, 990}, + dictWord{5, 0, 194}, + dictWord{6, 0, 927}, + dictWord{7, 0, 1662}, + dictWord{9, 0, 90}, + dictWord{140, 0, 564}, + dictWord{4, 10, 343}, + dictWord{133, 10, 511}, + dictWord{133, 0, 425}, + dictWord{7, 10, 455}, + dictWord{138, 10, 591}, + dictWord{4, 0, 774}, + dictWord{7, 11, 476}, + dictWord{7, 11, 1592}, + dictWord{138, 11, 87}, + dictWord{5, 0, 971}, + dictWord{135, 10, 1381}, + dictWord{5, 11, 318}, + dictWord{147, 11, 121}, + dictWord{5, 11, 291}, + dictWord{7, 11, 765}, + dictWord{9, 11, 389}, + dictWord{140, 11, 548}, + dictWord{134, 10, 575}, + dictWord{4, 0, 827}, + dictWord{12, 0, 646}, + dictWord{12, 0, 705}, + dictWord{12, 0, 712}, + dictWord{140, 0, 714}, + dictWord{139, 0, 752}, + dictWord{137, 0, 662}, + dictWord{5, 0, 72}, + dictWord{6, 0, 264}, + dictWord{7, 0, 21}, + dictWord{7, 0, 46}, + dictWord{7, 0, 2013}, + dictWord{ + 8, + 0, + 215, + }, + dictWord{8, 0, 513}, + dictWord{10, 0, 266}, + dictWord{139, 0, 22}, + dictWord{139, 11, 522}, + dictWord{6, 0, 239}, + dictWord{7, 0, 118}, + dictWord{10, 0, 95}, + dictWord{11, 0, 603}, + dictWord{13, 0, 443}, + dictWord{14, 0, 160}, + dictWord{143, 0, 4}, + dictWord{6, 0, 431}, + dictWord{134, 0, 669}, + dictWord{7, 10, 1127}, + dictWord{ + 7, + 10, + 1572, + }, + dictWord{10, 10, 297}, + dictWord{10, 10, 422}, + dictWord{11, 10, 764}, + dictWord{11, 10, 810}, + dictWord{12, 10, 264}, + dictWord{13, 10, 102}, + dictWord{13, 10, 300}, + dictWord{13, 10, 484}, + dictWord{14, 10, 147}, + dictWord{14, 10, 229}, + dictWord{17, 10, 71}, + dictWord{18, 10, 118}, + dictWord{ + 147, + 10, + 120, + }, + dictWord{5, 0, 874}, + dictWord{6, 0, 1677}, + dictWord{15, 0, 0}, + dictWord{10, 11, 525}, + dictWord{139, 11, 82}, + dictWord{6, 0, 65}, + dictWord{7, 0, 939}, + dictWord{ + 7, + 0, + 1172, + }, + dictWord{7, 0, 1671}, + dictWord{9, 0, 540}, + dictWord{10, 0, 696}, + dictWord{11, 0, 265}, + dictWord{11, 0, 732}, + dictWord{11, 0, 928}, + dictWord{ + 11, + 0, + 937, + }, + dictWord{141, 0, 438}, + dictWord{134, 0, 1350}, + dictWord{136, 11, 547}, + dictWord{132, 11, 422}, + dictWord{5, 11, 355}, + dictWord{145, 11, 0}, + dictWord{137, 11, 905}, + dictWord{5, 0, 682}, + dictWord{135, 0, 1887}, + dictWord{132, 0, 809}, + dictWord{4, 0, 696}, + dictWord{133, 11, 865}, + dictWord{6, 0, 1074}, + dictWord{6, 0, 1472}, + dictWord{14, 10, 35}, + dictWord{142, 10, 191}, + dictWord{5, 11, 914}, + dictWord{134, 11, 1625}, + dictWord{133, 11, 234}, + dictWord{ + 135, + 11, + 1383, + }, + dictWord{137, 11, 780}, + dictWord{132, 10, 125}, + dictWord{4, 0, 726}, + dictWord{133, 0, 630}, + dictWord{8, 0, 802}, + dictWord{136, 0, 838}, + dictWord{132, 10, 721}, + dictWord{6, 0, 1337}, + dictWord{7, 0, 776}, + dictWord{19, 0, 56}, + dictWord{136, 10, 145}, + dictWord{132, 0, 970}, + dictWord{7, 10, 792}, + dictWord{8, 10, 147}, + dictWord{10, 10, 821}, + dictWord{139, 10, 1021}, + dictWord{139, 10, 970}, + dictWord{8, 0, 940}, + dictWord{137, 0, 797}, + dictWord{ + 135, + 11, + 1312, + }, + dictWord{9, 0, 248}, + dictWord{10, 0, 400}, + dictWord{7, 11, 816}, + dictWord{7, 11, 1241}, + dictWord{7, 10, 1999}, + dictWord{9, 11, 283}, + dictWord{ + 9, + 11, + 520, + }, + dictWord{10, 11, 213}, + dictWord{10, 11, 307}, + dictWord{10, 11, 463}, + dictWord{10, 11, 671}, + dictWord{10, 11, 746}, + dictWord{11, 11, 401}, + dictWord{ + 11, + 11, + 794, + }, + dictWord{12, 11, 517}, + dictWord{18, 11, 107}, + dictWord{147, 11, 115}, + dictWord{6, 0, 1951}, + dictWord{134, 0, 2040}, + dictWord{ + 135, + 11, + 339, + }, + dictWord{13, 0, 41}, + dictWord{15, 0, 93}, + dictWord{5, 10, 168}, + dictWord{5, 10, 930}, + dictWord{8, 10, 74}, + dictWord{9, 10, 623}, + dictWord{12, 10, 500}, + dictWord{140, 10, 579}, + dictWord{6, 0, 118}, + dictWord{7, 0, 215}, + dictWord{7, 0, 1521}, + dictWord{140, 0, 11}, + dictWord{6, 10, 220}, + dictWord{7, 10, 1101}, + dictWord{141, 10, 105}, + dictWord{6, 11, 421}, + dictWord{7, 11, 61}, + dictWord{7, 11, 1540}, + dictWord{10, 11, 11}, + dictWord{138, 11, 501}, + dictWord{7, 0, 615}, + dictWord{138, 0, 251}, + dictWord{140, 11, 631}, + dictWord{135, 0, 1044}, + dictWord{6, 10, 19}, + dictWord{7, 10, 1413}, + dictWord{139, 10, 428}, + dictWord{ + 133, + 0, + 225, + }, + dictWord{7, 10, 96}, + dictWord{8, 10, 401}, + dictWord{8, 10, 703}, + dictWord{137, 10, 896}, + dictWord{145, 10, 116}, + dictWord{6, 11, 102}, + dictWord{ + 7, + 11, + 72, + }, + dictWord{15, 11, 142}, + dictWord{147, 11, 67}, + dictWord{7, 10, 1961}, + dictWord{7, 10, 1965}, + dictWord{8, 10, 702}, + dictWord{136, 10, 750}, + dictWord{ + 7, + 10, + 2030, + }, + dictWord{8, 10, 150}, + dictWord{8, 10, 737}, + dictWord{12, 10, 366}, + dictWord{151, 11, 30}, + dictWord{4, 0, 370}, + dictWord{5, 0, 756}, + dictWord{ + 7, + 0, + 1326, + }, + dictWord{135, 11, 823}, + dictWord{8, 10, 800}, + dictWord{9, 10, 148}, + dictWord{9, 10, 872}, + dictWord{9, 10, 890}, + dictWord{11, 10, 309}, + dictWord{ + 11, + 10, + 1001, + }, + dictWord{13, 10, 267}, + dictWord{141, 10, 323}, + dictWord{6, 0, 1662}, + dictWord{7, 0, 48}, + dictWord{8, 0, 771}, + dictWord{10, 0, 116}, + dictWord{ + 13, + 0, + 104, + }, + dictWord{14, 0, 105}, + dictWord{14, 0, 184}, + dictWord{15, 0, 168}, + dictWord{19, 0, 92}, + dictWord{148, 0, 68}, + dictWord{10, 0, 209}, + dictWord{ + 135, + 11, + 1870, + }, + dictWord{7, 11, 68}, + dictWord{8, 11, 48}, + dictWord{8, 11, 88}, + dictWord{8, 11, 582}, + dictWord{8, 11, 681}, + dictWord{9, 11, 373}, + dictWord{9, 11, 864}, + dictWord{11, 11, 157}, + dictWord{11, 11, 336}, + dictWord{11, 11, 843}, + dictWord{148, 11, 27}, + dictWord{134, 0, 930}, + dictWord{4, 11, 88}, + dictWord{5, 11, 137}, + dictWord{5, 11, 174}, + dictWord{5, 11, 777}, + dictWord{6, 11, 1664}, + dictWord{6, 11, 1725}, + dictWord{7, 11, 77}, + dictWord{7, 11, 426}, + dictWord{7, 11, 1317}, + dictWord{7, 11, 1355}, + dictWord{8, 11, 126}, + dictWord{8, 11, 563}, + dictWord{9, 11, 523}, + dictWord{9, 11, 750}, + dictWord{10, 11, 310}, + dictWord{10, 11, 836}, + dictWord{11, 11, 42}, + dictWord{11, 11, 318}, + dictWord{11, 11, 731}, + dictWord{12, 11, 68}, + dictWord{12, 11, 92}, + dictWord{12, 11, 507}, + dictWord{12, 11, 692}, + dictWord{13, 11, 81}, + dictWord{13, 11, 238}, + dictWord{13, 11, 374}, + dictWord{18, 11, 138}, + dictWord{19, 11, 78}, + dictWord{19, 11, 111}, + dictWord{20, 11, 55}, + dictWord{20, 11, 77}, + dictWord{148, 11, 92}, + dictWord{4, 11, 938}, + dictWord{135, 11, 1831}, + dictWord{5, 10, 547}, + dictWord{7, 10, 424}, + dictWord{ + 8, + 11, + 617, + }, + dictWord{138, 11, 351}, + dictWord{6, 0, 1286}, + dictWord{6, 11, 1668}, + dictWord{7, 11, 1499}, + dictWord{8, 11, 117}, + dictWord{9, 11, 314}, + dictWord{ + 138, + 11, + 174, + }, + dictWord{6, 0, 759}, + dictWord{6, 0, 894}, + dictWord{7, 11, 707}, + dictWord{139, 11, 563}, + dictWord{4, 0, 120}, + dictWord{135, 0, 1894}, + dictWord{ + 9, + 0, + 385, + }, + dictWord{149, 0, 17}, + dictWord{138, 0, 429}, + dictWord{133, 11, 403}, + dictWord{5, 0, 820}, + dictWord{135, 0, 931}, + dictWord{10, 0, 199}, + dictWord{ + 133, + 10, + 133, + }, + dictWord{6, 0, 151}, + dictWord{6, 0, 1675}, + dictWord{7, 0, 383}, + dictWord{151, 0, 10}, + dictWord{6, 0, 761}, + dictWord{136, 10, 187}, + dictWord{ + 8, + 0, + 365, + }, + dictWord{10, 10, 0}, + dictWord{10, 10, 818}, + dictWord{139, 10, 988}, + dictWord{4, 11, 44}, + dictWord{5, 11, 311}, + dictWord{6, 11, 156}, + dictWord{ + 7, + 11, + 639, + }, + dictWord{7, 11, 762}, + dictWord{7, 11, 1827}, + dictWord{9, 11, 8}, + dictWord{9, 11, 462}, + dictWord{148, 11, 83}, + dictWord{4, 11, 346}, + dictWord{7, 11, 115}, + dictWord{9, 11, 180}, + dictWord{9, 11, 456}, + dictWord{138, 11, 363}, + dictWord{136, 10, 685}, + dictWord{7, 0, 1086}, + dictWord{145, 0, 46}, + dictWord{ + 6, + 0, + 1624, + }, + dictWord{11, 0, 11}, + dictWord{12, 0, 422}, + dictWord{13, 0, 444}, + dictWord{142, 0, 360}, + dictWord{6, 0, 1020}, + dictWord{6, 0, 1260}, + dictWord{ + 134, + 0, + 1589, + }, + dictWord{4, 0, 43}, + dictWord{5, 0, 344}, + dictWord{5, 0, 357}, + dictWord{14, 0, 472}, + dictWord{150, 0, 58}, + dictWord{6, 0, 1864}, + dictWord{6, 0, 1866}, + dictWord{6, 0, 1868}, + dictWord{6, 0, 1869}, + dictWord{6, 0, 1874}, + dictWord{6, 0, 1877}, + dictWord{6, 0, 1903}, + dictWord{6, 0, 1911}, + dictWord{9, 0, 920}, + dictWord{ + 9, + 0, + 921, + }, + dictWord{9, 0, 924}, + dictWord{9, 0, 946}, + dictWord{9, 0, 959}, + dictWord{9, 0, 963}, + dictWord{9, 0, 970}, + dictWord{9, 0, 997}, + dictWord{9, 0, 1008}, + dictWord{ + 9, + 0, + 1017, + }, + dictWord{12, 0, 795}, + dictWord{12, 0, 797}, + dictWord{12, 0, 798}, + dictWord{12, 0, 800}, + dictWord{12, 0, 803}, + dictWord{12, 0, 811}, + dictWord{ + 12, + 0, + 820, + }, + dictWord{12, 0, 821}, + dictWord{12, 0, 839}, + dictWord{12, 0, 841}, + dictWord{12, 0, 848}, + dictWord{12, 0, 911}, + dictWord{12, 0, 921}, + dictWord{12, 0, 922}, + dictWord{12, 0, 925}, + dictWord{12, 0, 937}, + dictWord{12, 0, 944}, + dictWord{12, 0, 945}, + dictWord{12, 0, 953}, + dictWord{15, 0, 184}, + dictWord{15, 0, 191}, + dictWord{15, 0, 199}, + dictWord{15, 0, 237}, + dictWord{15, 0, 240}, + dictWord{15, 0, 243}, + dictWord{15, 0, 246}, + dictWord{18, 0, 203}, + dictWord{21, 0, 40}, + dictWord{ + 21, + 0, + 52, + }, + dictWord{21, 0, 57}, + dictWord{24, 0, 23}, + dictWord{24, 0, 28}, + dictWord{152, 0, 30}, + dictWord{134, 0, 725}, + dictWord{145, 11, 58}, + dictWord{133, 0, 888}, + dictWord{137, 10, 874}, + dictWord{4, 0, 711}, + dictWord{8, 10, 774}, + dictWord{10, 10, 670}, + dictWord{140, 10, 51}, + dictWord{144, 11, 40}, + dictWord{ + 6, + 11, + 185, + }, + dictWord{7, 11, 1899}, + dictWord{139, 11, 673}, + dictWord{137, 10, 701}, + dictWord{137, 0, 440}, + dictWord{4, 11, 327}, + dictWord{5, 11, 478}, + dictWord{ + 7, + 11, + 1332, + }, + dictWord{8, 11, 753}, + dictWord{140, 11, 227}, + dictWord{4, 10, 127}, + dictWord{5, 10, 350}, + dictWord{6, 10, 356}, + dictWord{8, 10, 426}, + dictWord{ + 9, + 10, + 572, + }, + dictWord{10, 10, 247}, + dictWord{139, 10, 312}, + dictWord{5, 11, 1020}, + dictWord{133, 11, 1022}, + dictWord{4, 11, 103}, + dictWord{ + 133, + 11, + 401, + }, + dictWord{6, 0, 1913}, + dictWord{6, 0, 1926}, + dictWord{6, 0, 1959}, + dictWord{9, 0, 914}, + dictWord{9, 0, 939}, + dictWord{9, 0, 952}, + dictWord{9, 0, 979}, + dictWord{ + 9, + 0, + 990, + }, + dictWord{9, 0, 998}, + dictWord{9, 0, 1003}, + dictWord{9, 0, 1023}, + dictWord{12, 0, 827}, + dictWord{12, 0, 834}, + dictWord{12, 0, 845}, + dictWord{ + 12, + 0, + 912, + }, + dictWord{12, 0, 935}, + dictWord{12, 0, 951}, + dictWord{15, 0, 172}, + dictWord{15, 0, 174}, + dictWord{18, 0, 198}, + dictWord{149, 0, 63}, + dictWord{5, 0, 958}, + dictWord{5, 0, 987}, + dictWord{4, 11, 499}, + dictWord{135, 11, 1421}, + dictWord{7, 0, 885}, + dictWord{6, 10, 59}, + dictWord{6, 10, 1762}, + dictWord{9, 10, 603}, + dictWord{141, 10, 397}, + dictWord{10, 11, 62}, + dictWord{141, 11, 164}, + dictWord{4, 0, 847}, + dictWord{135, 0, 326}, + dictWord{11, 0, 276}, + dictWord{142, 0, 293}, + dictWord{4, 0, 65}, + dictWord{5, 0, 479}, + dictWord{5, 0, 1004}, + dictWord{7, 0, 1913}, + dictWord{8, 0, 317}, + dictWord{9, 0, 302}, + dictWord{10, 0, 612}, + dictWord{ + 13, + 0, + 22, + }, + dictWord{132, 11, 96}, + dictWord{4, 0, 261}, + dictWord{135, 0, 510}, + dictWord{135, 0, 1514}, + dictWord{6, 10, 111}, + dictWord{7, 10, 4}, + dictWord{8, 10, 163}, + dictWord{8, 10, 776}, + dictWord{138, 10, 566}, + dictWord{4, 0, 291}, + dictWord{9, 0, 515}, + dictWord{12, 0, 152}, + dictWord{12, 0, 443}, + dictWord{13, 0, 392}, + dictWord{142, 0, 357}, + dictWord{7, 11, 399}, + dictWord{135, 11, 1492}, + dictWord{4, 0, 589}, + dictWord{139, 0, 282}, + dictWord{6, 11, 563}, + dictWord{ + 135, + 10, + 1994, + }, + dictWord{5, 10, 297}, + dictWord{135, 10, 1038}, + dictWord{4, 0, 130}, + dictWord{7, 0, 843}, + dictWord{135, 0, 1562}, + dictWord{5, 0, 42}, + dictWord{ + 5, + 0, + 879, + }, + dictWord{7, 0, 245}, + dictWord{7, 0, 324}, + dictWord{7, 0, 1532}, + dictWord{11, 0, 463}, + dictWord{11, 0, 472}, + dictWord{13, 0, 363}, + dictWord{144, 0, 52}, + dictWord{4, 0, 134}, + dictWord{133, 0, 372}, + dictWord{133, 0, 680}, + dictWord{136, 10, 363}, + dictWord{6, 0, 1997}, + dictWord{8, 0, 935}, + dictWord{136, 0, 977}, + dictWord{4, 0, 810}, + dictWord{135, 0, 1634}, + dictWord{135, 10, 1675}, + dictWord{7, 0, 1390}, + dictWord{4, 11, 910}, + dictWord{133, 11, 832}, + dictWord{ + 7, + 10, + 808, + }, + dictWord{8, 11, 266}, + dictWord{139, 11, 578}, + dictWord{132, 0, 644}, + dictWord{4, 0, 982}, + dictWord{138, 0, 867}, + dictWord{132, 10, 280}, + dictWord{ + 135, + 0, + 540, + }, + dictWord{140, 10, 54}, + dictWord{135, 0, 123}, + dictWord{134, 0, 1978}, + dictWord{4, 10, 421}, + dictWord{133, 10, 548}, + dictWord{6, 0, 623}, + dictWord{136, 0, 789}, + dictWord{4, 0, 908}, + dictWord{5, 0, 359}, + dictWord{5, 0, 508}, + dictWord{6, 0, 1723}, + dictWord{7, 0, 343}, + dictWord{7, 0, 1996}, + dictWord{ + 135, + 0, + 2026, + }, + dictWord{134, 0, 1220}, + dictWord{4, 0, 341}, + dictWord{135, 0, 480}, + dictWord{6, 10, 254}, + dictWord{9, 10, 109}, + dictWord{138, 10, 103}, + dictWord{ + 134, + 0, + 888, + }, + dictWord{8, 11, 528}, + dictWord{137, 11, 348}, + dictWord{7, 0, 1995}, + dictWord{8, 0, 299}, + dictWord{11, 0, 890}, + dictWord{12, 0, 674}, + dictWord{ + 4, + 11, + 20, + }, + dictWord{133, 11, 616}, + dictWord{135, 11, 1094}, + dictWord{134, 10, 1630}, + dictWord{4, 0, 238}, + dictWord{5, 0, 503}, + dictWord{6, 0, 179}, + dictWord{ + 7, + 0, + 2003, + }, + dictWord{8, 0, 381}, + dictWord{8, 0, 473}, + dictWord{9, 0, 149}, + dictWord{10, 0, 788}, + dictWord{15, 0, 45}, + dictWord{15, 0, 86}, + dictWord{20, 0, 110}, + dictWord{150, 0, 57}, + dictWord{133, 10, 671}, + dictWord{4, 11, 26}, + dictWord{5, 11, 429}, + dictWord{6, 11, 245}, + dictWord{7, 11, 704}, + dictWord{7, 11, 1379}, + dictWord{135, 11, 1474}, + dictWord{4, 0, 121}, + dictWord{5, 0, 156}, + dictWord{5, 0, 349}, + dictWord{9, 0, 431}, + dictWord{10, 0, 605}, + dictWord{142, 0, 342}, + dictWord{ + 7, + 11, + 943, + }, + dictWord{139, 11, 614}, + dictWord{132, 10, 889}, + dictWord{132, 11, 621}, + dictWord{7, 10, 1382}, + dictWord{7, 11, 1382}, + dictWord{ + 135, + 10, + 1910, + }, + dictWord{132, 10, 627}, + dictWord{133, 10, 775}, + dictWord{133, 11, 542}, + dictWord{133, 11, 868}, + dictWord{136, 11, 433}, + dictWord{6, 0, 1373}, + dictWord{7, 0, 1011}, + dictWord{11, 10, 362}, + dictWord{11, 10, 948}, + dictWord{140, 10, 388}, + dictWord{6, 0, 80}, + dictWord{7, 0, 173}, + dictWord{9, 0, 547}, + dictWord{10, 0, 730}, + dictWord{14, 0, 18}, + dictWord{22, 0, 39}, + dictWord{135, 11, 1495}, + dictWord{6, 0, 1694}, + dictWord{135, 0, 1974}, + dictWord{140, 0, 196}, + dictWord{4, 0, 923}, + dictWord{6, 0, 507}, + dictWord{6, 0, 1711}, + dictWord{7, 10, 451}, + dictWord{8, 10, 389}, + dictWord{12, 10, 490}, + dictWord{13, 10, 16}, + dictWord{ + 13, + 10, + 215, + }, + dictWord{13, 10, 351}, + dictWord{18, 10, 132}, + dictWord{147, 10, 125}, + dictWord{6, 0, 646}, + dictWord{134, 0, 1047}, + dictWord{135, 10, 841}, + dictWord{136, 10, 566}, + dictWord{6, 0, 1611}, + dictWord{135, 0, 1214}, + dictWord{139, 0, 926}, + dictWord{132, 11, 525}, + dictWord{132, 0, 595}, + dictWord{ + 5, + 0, + 240, + }, + dictWord{6, 0, 459}, + dictWord{7, 0, 12}, + dictWord{7, 0, 114}, + dictWord{7, 0, 949}, + dictWord{7, 0, 1753}, + dictWord{7, 0, 1805}, + dictWord{8, 0, 658}, + dictWord{ + 9, + 0, + 1, + }, + dictWord{11, 0, 959}, + dictWord{141, 0, 446}, + dictWord{5, 10, 912}, + dictWord{134, 10, 1695}, + dictWord{132, 0, 446}, + dictWord{7, 11, 62}, + dictWord{ + 12, + 11, + 45, + }, + dictWord{147, 11, 112}, + dictWord{5, 10, 236}, + dictWord{6, 10, 572}, + dictWord{8, 10, 492}, + dictWord{11, 10, 618}, + dictWord{144, 10, 56}, + dictWord{ + 5, + 10, + 190, + }, + dictWord{136, 10, 318}, + dictWord{135, 10, 1376}, + dictWord{4, 11, 223}, + dictWord{6, 11, 359}, + dictWord{11, 11, 3}, + dictWord{13, 11, 108}, + dictWord{ + 14, + 11, + 89, + }, + dictWord{144, 11, 22}, + dictWord{132, 11, 647}, + dictWord{134, 0, 490}, + dictWord{134, 0, 491}, + dictWord{134, 0, 1584}, + dictWord{ + 135, + 11, + 685, + }, + dictWord{138, 11, 220}, + dictWord{7, 0, 250}, + dictWord{136, 0, 507}, + dictWord{132, 0, 158}, + dictWord{4, 0, 140}, + dictWord{7, 0, 362}, + dictWord{8, 0, 209}, + dictWord{9, 0, 10}, + dictWord{9, 0, 160}, + dictWord{9, 0, 503}, + dictWord{9, 0, 614}, + dictWord{10, 0, 689}, + dictWord{11, 0, 327}, + dictWord{11, 0, 553}, + dictWord{ + 11, + 0, + 725, + }, + dictWord{11, 0, 767}, + dictWord{12, 0, 252}, + dictWord{12, 0, 583}, + dictWord{13, 0, 192}, + dictWord{14, 0, 269}, + dictWord{14, 0, 356}, + dictWord{148, 0, 50}, + dictWord{19, 0, 1}, + dictWord{19, 0, 26}, + dictWord{150, 0, 9}, + dictWord{132, 11, 109}, + dictWord{6, 0, 228}, + dictWord{7, 0, 1341}, + dictWord{9, 0, 408}, + dictWord{ + 138, + 0, + 343, + }, + dictWord{4, 0, 373}, + dictWord{5, 0, 283}, + dictWord{6, 0, 480}, + dictWord{7, 0, 609}, + dictWord{10, 0, 860}, + dictWord{138, 0, 878}, + dictWord{6, 0, 779}, + dictWord{134, 0, 1209}, + dictWord{4, 0, 557}, + dictWord{7, 11, 263}, + dictWord{7, 11, 628}, + dictWord{136, 11, 349}, + dictWord{132, 0, 548}, + dictWord{7, 0, 197}, + dictWord{8, 0, 142}, + dictWord{8, 0, 325}, + dictWord{9, 0, 150}, + dictWord{9, 0, 596}, + dictWord{10, 0, 350}, + dictWord{10, 0, 353}, + dictWord{11, 0, 74}, + dictWord{ + 11, + 0, + 315, + }, + dictWord{12, 0, 662}, + dictWord{12, 0, 681}, + dictWord{14, 0, 423}, + dictWord{143, 0, 141}, + dictWord{4, 11, 40}, + dictWord{10, 11, 67}, + dictWord{ + 11, + 11, + 117, + }, + dictWord{11, 11, 768}, + dictWord{139, 11, 935}, + dictWord{7, 11, 992}, + dictWord{8, 11, 301}, + dictWord{9, 11, 722}, + dictWord{12, 11, 63}, + dictWord{ + 13, + 11, + 29, + }, + dictWord{14, 11, 161}, + dictWord{143, 11, 18}, + dictWord{6, 0, 1490}, + dictWord{138, 11, 532}, + dictWord{5, 0, 580}, + dictWord{7, 0, 378}, + dictWord{ + 7, + 0, + 674, + }, + dictWord{7, 0, 1424}, + dictWord{15, 0, 83}, + dictWord{16, 0, 11}, + dictWord{15, 11, 83}, + dictWord{144, 11, 11}, + dictWord{6, 0, 1057}, + dictWord{6, 0, 1335}, + dictWord{10, 0, 316}, + dictWord{7, 10, 85}, + dictWord{7, 10, 247}, + dictWord{8, 10, 585}, + dictWord{138, 10, 163}, + dictWord{4, 0, 169}, + dictWord{5, 0, 83}, + dictWord{ + 6, + 0, + 399, + }, + dictWord{6, 0, 579}, + dictWord{6, 0, 1513}, + dictWord{7, 0, 692}, + dictWord{7, 0, 846}, + dictWord{7, 0, 1015}, + dictWord{7, 0, 1799}, + dictWord{8, 0, 403}, + dictWord{9, 0, 394}, + dictWord{10, 0, 133}, + dictWord{12, 0, 4}, + dictWord{12, 0, 297}, + dictWord{12, 0, 452}, + dictWord{16, 0, 81}, + dictWord{18, 0, 25}, + dictWord{21, 0, 14}, + dictWord{22, 0, 12}, + dictWord{151, 0, 18}, + dictWord{134, 0, 1106}, + dictWord{7, 0, 1546}, + dictWord{11, 0, 299}, + dictWord{142, 0, 407}, + dictWord{134, 0, 1192}, + dictWord{132, 0, 177}, + dictWord{5, 0, 411}, + dictWord{135, 0, 653}, + dictWord{7, 0, 439}, + dictWord{10, 0, 727}, + dictWord{11, 0, 260}, + dictWord{139, 0, 684}, + dictWord{138, 10, 145}, + dictWord{147, 10, 83}, + dictWord{5, 0, 208}, + dictWord{7, 0, 753}, + dictWord{135, 0, 1528}, + dictWord{137, 11, 617}, + dictWord{ + 135, + 10, + 1922, + }, + dictWord{135, 11, 825}, + dictWord{11, 0, 422}, + dictWord{13, 0, 389}, + dictWord{4, 10, 124}, + dictWord{10, 10, 457}, + dictWord{11, 10, 121}, + dictWord{ + 11, + 10, + 169, + }, + dictWord{11, 10, 870}, + dictWord{12, 10, 214}, + dictWord{14, 10, 187}, + dictWord{143, 10, 77}, + dictWord{11, 0, 615}, + dictWord{15, 0, 58}, + dictWord{ + 11, + 11, + 615, + }, + dictWord{143, 11, 58}, + dictWord{9, 0, 618}, + dictWord{138, 0, 482}, + dictWord{6, 0, 1952}, + dictWord{6, 0, 1970}, + dictWord{142, 0, 505}, + dictWord{ + 7, + 10, + 1193, + }, + dictWord{135, 11, 1838}, + dictWord{133, 0, 242}, + dictWord{135, 10, 1333}, + dictWord{6, 10, 107}, + dictWord{7, 10, 638}, + dictWord{ + 7, + 10, + 1632, + }, + dictWord{137, 10, 396}, + dictWord{133, 0, 953}, + dictWord{5, 10, 370}, + dictWord{134, 10, 1756}, + dictWord{5, 11, 28}, + dictWord{6, 11, 204}, + dictWord{ + 10, + 11, + 320, + }, + dictWord{10, 11, 583}, + dictWord{13, 11, 502}, + dictWord{14, 11, 72}, + dictWord{14, 11, 274}, + dictWord{14, 11, 312}, + dictWord{14, 11, 344}, + dictWord{15, 11, 159}, + dictWord{16, 11, 62}, + dictWord{16, 11, 69}, + dictWord{17, 11, 30}, + dictWord{18, 11, 42}, + dictWord{18, 11, 53}, + dictWord{18, 11, 84}, + dictWord{18, 11, 140}, + dictWord{19, 11, 68}, + dictWord{19, 11, 85}, + dictWord{20, 11, 5}, + dictWord{20, 11, 45}, + dictWord{20, 11, 101}, + dictWord{22, 11, 7}, + dictWord{ + 150, + 11, + 20, + }, + dictWord{4, 11, 558}, + dictWord{6, 11, 390}, + dictWord{7, 11, 162}, + dictWord{7, 11, 689}, + dictWord{9, 11, 360}, + dictWord{138, 11, 653}, + dictWord{ + 11, + 0, + 802, + }, + dictWord{141, 0, 67}, + dictWord{133, 10, 204}, + dictWord{133, 0, 290}, + dictWord{5, 10, 970}, + dictWord{134, 10, 1706}, + dictWord{132, 0, 380}, + dictWord{5, 0, 52}, + dictWord{7, 0, 277}, + dictWord{9, 0, 368}, + dictWord{139, 0, 791}, + dictWord{5, 11, 856}, + dictWord{6, 11, 1672}, + dictWord{6, 11, 1757}, + dictWord{ + 6, + 11, + 1781, + }, + dictWord{7, 11, 1150}, + dictWord{7, 11, 1425}, + dictWord{7, 11, 1453}, + dictWord{140, 11, 513}, + dictWord{5, 11, 92}, + dictWord{7, 10, 3}, + dictWord{ + 10, + 11, + 736, + }, + dictWord{140, 11, 102}, + dictWord{4, 0, 112}, + dictWord{5, 0, 653}, + dictWord{5, 10, 483}, + dictWord{5, 10, 685}, + dictWord{6, 10, 489}, + dictWord{ + 7, + 10, + 1204, + }, + dictWord{136, 10, 394}, + dictWord{132, 10, 921}, + dictWord{6, 0, 1028}, + dictWord{133, 10, 1007}, + dictWord{5, 11, 590}, + dictWord{9, 11, 213}, + dictWord{145, 11, 91}, + dictWord{135, 10, 1696}, + dictWord{10, 0, 138}, + dictWord{139, 0, 476}, + dictWord{5, 0, 725}, + dictWord{5, 0, 727}, + dictWord{135, 0, 1811}, + dictWord{4, 0, 979}, + dictWord{6, 0, 1821}, + dictWord{6, 0, 1838}, + dictWord{8, 0, 876}, + dictWord{8, 0, 883}, + dictWord{8, 0, 889}, + dictWord{8, 0, 893}, + dictWord{ + 8, + 0, + 895, + }, + dictWord{10, 0, 934}, + dictWord{12, 0, 720}, + dictWord{14, 0, 459}, + dictWord{148, 0, 123}, + dictWord{135, 11, 551}, + dictWord{4, 0, 38}, + dictWord{6, 0, 435}, + dictWord{7, 0, 307}, + dictWord{7, 0, 999}, + dictWord{7, 0, 1481}, + dictWord{7, 0, 1732}, + dictWord{7, 0, 1738}, + dictWord{8, 0, 371}, + dictWord{9, 0, 414}, + dictWord{ + 11, + 0, + 316, + }, + dictWord{12, 0, 52}, + dictWord{13, 0, 420}, + dictWord{147, 0, 100}, + dictWord{135, 0, 1296}, + dictWord{132, 10, 712}, + dictWord{134, 10, 1629}, + dictWord{133, 0, 723}, + dictWord{134, 0, 651}, + dictWord{136, 11, 191}, + dictWord{9, 11, 791}, + dictWord{10, 11, 93}, + dictWord{11, 11, 301}, + dictWord{16, 11, 13}, + dictWord{17, 11, 23}, + dictWord{18, 11, 135}, + dictWord{19, 11, 12}, + dictWord{20, 11, 1}, + dictWord{20, 11, 12}, + dictWord{148, 11, 14}, + dictWord{136, 11, 503}, + dictWord{6, 11, 466}, + dictWord{135, 11, 671}, + dictWord{6, 0, 1200}, + dictWord{134, 0, 1330}, + dictWord{135, 0, 1255}, + dictWord{134, 0, 986}, + dictWord{ + 5, + 0, + 109, + }, + dictWord{6, 0, 1784}, + dictWord{7, 0, 1895}, + dictWord{12, 0, 296}, + dictWord{140, 0, 302}, + dictWord{135, 11, 983}, + dictWord{133, 10, 485}, + dictWord{ + 134, + 0, + 660, + }, + dictWord{134, 0, 800}, + dictWord{5, 0, 216}, + dictWord{5, 0, 294}, + dictWord{6, 0, 591}, + dictWord{7, 0, 1879}, + dictWord{9, 0, 141}, + dictWord{9, 0, 270}, + dictWord{9, 0, 679}, + dictWord{10, 0, 159}, + dictWord{11, 0, 197}, + dictWord{11, 0, 438}, + dictWord{12, 0, 538}, + dictWord{12, 0, 559}, + dictWord{14, 0, 144}, + dictWord{ + 14, + 0, + 167, + }, + dictWord{15, 0, 67}, + dictWord{4, 10, 285}, + dictWord{5, 10, 317}, + dictWord{6, 10, 301}, + dictWord{7, 10, 7}, + dictWord{8, 10, 153}, + dictWord{ + 10, + 10, + 766, + }, + dictWord{11, 10, 468}, + dictWord{12, 10, 467}, + dictWord{141, 10, 143}, + dictWord{136, 0, 945}, + dictWord{134, 0, 1090}, + dictWord{137, 0, 81}, + dictWord{12, 11, 468}, + dictWord{19, 11, 96}, + dictWord{148, 11, 24}, + dictWord{134, 0, 391}, + dictWord{138, 11, 241}, + dictWord{7, 0, 322}, + dictWord{136, 0, 249}, + dictWord{134, 0, 1412}, + dictWord{135, 11, 795}, + dictWord{5, 0, 632}, + dictWord{138, 0, 526}, + dictWord{136, 10, 819}, + dictWord{6, 0, 144}, + dictWord{7, 0, 948}, + dictWord{7, 0, 1042}, + dictWord{8, 0, 235}, + dictWord{8, 0, 461}, + dictWord{9, 0, 453}, + dictWord{9, 0, 796}, + dictWord{10, 0, 354}, + dictWord{17, 0, 77}, + dictWord{ + 135, + 11, + 954, + }, + dictWord{139, 10, 917}, + dictWord{6, 0, 940}, + dictWord{134, 0, 1228}, + dictWord{4, 0, 362}, + dictWord{7, 0, 52}, + dictWord{135, 0, 303}, + dictWord{ + 6, + 11, + 549, + }, + dictWord{8, 11, 34}, + dictWord{8, 11, 283}, + dictWord{9, 11, 165}, + dictWord{138, 11, 475}, + dictWord{7, 11, 370}, + dictWord{7, 11, 1007}, + dictWord{ + 7, + 11, + 1177, + }, + dictWord{135, 11, 1565}, + dictWord{5, 11, 652}, + dictWord{5, 11, 701}, + dictWord{135, 11, 449}, + dictWord{5, 0, 196}, + dictWord{6, 0, 486}, + dictWord{ + 7, + 0, + 212, + }, + dictWord{8, 0, 309}, + dictWord{136, 0, 346}, + dictWord{6, 10, 1719}, + dictWord{6, 10, 1735}, + dictWord{7, 10, 2016}, + dictWord{7, 10, 2020}, + dictWord{ + 8, + 10, + 837, + }, + dictWord{137, 10, 852}, + dictWord{6, 11, 159}, + dictWord{6, 11, 364}, + dictWord{7, 11, 516}, + dictWord{7, 11, 1439}, + dictWord{137, 11, 518}, + dictWord{135, 0, 1912}, + dictWord{135, 0, 1290}, + dictWord{132, 0, 686}, + dictWord{141, 11, 151}, + dictWord{138, 0, 625}, + dictWord{136, 0, 706}, + dictWord{ + 138, + 10, + 568, + }, + dictWord{139, 0, 412}, + dictWord{4, 0, 30}, + dictWord{133, 0, 43}, + dictWord{8, 10, 67}, + dictWord{138, 10, 419}, + dictWord{7, 0, 967}, + dictWord{ + 141, + 0, + 11, + }, + dictWord{12, 0, 758}, + dictWord{14, 0, 441}, + dictWord{142, 0, 462}, + dictWord{10, 10, 657}, + dictWord{14, 10, 297}, + dictWord{142, 10, 361}, + dictWord{ + 139, + 10, + 729, + }, + dictWord{4, 0, 220}, + dictWord{135, 0, 1535}, + dictWord{7, 11, 501}, + dictWord{9, 11, 111}, + dictWord{10, 11, 141}, + dictWord{11, 11, 332}, + dictWord{ + 13, + 11, + 43, + }, + dictWord{13, 11, 429}, + dictWord{14, 11, 130}, + dictWord{14, 11, 415}, + dictWord{145, 11, 102}, + dictWord{4, 0, 950}, + dictWord{6, 0, 1859}, + dictWord{ + 7, + 0, + 11, + }, + dictWord{8, 0, 873}, + dictWord{12, 0, 710}, + dictWord{12, 0, 718}, + dictWord{12, 0, 748}, + dictWord{12, 0, 765}, + dictWord{148, 0, 124}, + dictWord{ + 5, + 11, + 149, + }, + dictWord{5, 11, 935}, + dictWord{136, 11, 233}, + dictWord{142, 11, 291}, + dictWord{134, 0, 1579}, + dictWord{7, 0, 890}, + dictWord{8, 10, 51}, + dictWord{ + 9, + 10, + 868, + }, + dictWord{10, 10, 833}, + dictWord{12, 10, 481}, + dictWord{12, 10, 570}, + dictWord{148, 10, 106}, + dictWord{141, 0, 2}, + dictWord{132, 10, 445}, + dictWord{136, 11, 801}, + dictWord{135, 0, 1774}, + dictWord{7, 0, 1725}, + dictWord{138, 0, 393}, + dictWord{5, 0, 263}, + dictWord{134, 0, 414}, + dictWord{ + 132, + 11, + 322, + }, + dictWord{133, 10, 239}, + dictWord{7, 0, 456}, + dictWord{7, 10, 1990}, + dictWord{8, 10, 130}, + dictWord{139, 10, 720}, + dictWord{137, 0, 818}, + dictWord{ + 5, + 10, + 123, + }, + dictWord{6, 10, 530}, + dictWord{7, 10, 348}, + dictWord{135, 10, 1419}, + dictWord{135, 10, 2024}, + dictWord{6, 0, 178}, + dictWord{6, 0, 1750}, + dictWord{8, 0, 251}, + dictWord{9, 0, 690}, + dictWord{10, 0, 155}, + dictWord{10, 0, 196}, + dictWord{10, 0, 373}, + dictWord{11, 0, 698}, + dictWord{13, 0, 155}, + dictWord{ + 148, + 0, + 93, + }, + dictWord{5, 0, 97}, + dictWord{137, 0, 393}, + dictWord{134, 0, 674}, + dictWord{11, 0, 223}, + dictWord{140, 0, 168}, + dictWord{132, 10, 210}, + dictWord{ + 139, + 11, + 464, + }, + dictWord{6, 0, 1639}, + dictWord{146, 0, 159}, + dictWord{139, 11, 2}, + dictWord{7, 0, 934}, + dictWord{8, 0, 647}, + dictWord{17, 0, 97}, + dictWord{19, 0, 59}, + dictWord{150, 0, 2}, + dictWord{132, 0, 191}, + dictWord{5, 0, 165}, + dictWord{9, 0, 346}, + dictWord{10, 0, 655}, + dictWord{11, 0, 885}, + dictWord{4, 10, 430}, + dictWord{135, 11, 357}, + dictWord{133, 0, 877}, + dictWord{5, 10, 213}, + dictWord{133, 11, 406}, + dictWord{8, 0, 128}, + dictWord{139, 0, 179}, + dictWord{6, 11, 69}, + dictWord{135, 11, 117}, + dictWord{135, 0, 1297}, + dictWord{11, 11, 43}, + dictWord{13, 11, 72}, + dictWord{141, 11, 142}, + dictWord{135, 11, 1830}, + dictWord{ + 142, + 0, + 164, + }, + dictWord{5, 0, 57}, + dictWord{6, 0, 101}, + dictWord{6, 0, 586}, + dictWord{6, 0, 1663}, + dictWord{7, 0, 132}, + dictWord{7, 0, 1154}, + dictWord{7, 0, 1415}, + dictWord{7, 0, 1507}, + dictWord{12, 0, 493}, + dictWord{15, 0, 105}, + dictWord{151, 0, 15}, + dictWord{5, 0, 459}, + dictWord{7, 0, 1073}, + dictWord{8, 0, 241}, + dictWord{ + 136, + 0, + 334, + }, + dictWord{133, 11, 826}, + dictWord{133, 10, 108}, + dictWord{5, 10, 219}, + dictWord{10, 11, 132}, + dictWord{11, 11, 191}, + dictWord{11, 11, 358}, + dictWord{139, 11, 460}, + dictWord{6, 0, 324}, + dictWord{6, 0, 520}, + dictWord{7, 0, 338}, + dictWord{7, 0, 1729}, + dictWord{8, 0, 228}, + dictWord{139, 0, 750}, + dictWord{ + 21, + 0, + 30, + }, + dictWord{22, 0, 53}, + dictWord{4, 10, 193}, + dictWord{5, 10, 916}, + dictWord{7, 10, 364}, + dictWord{10, 10, 398}, + dictWord{10, 10, 726}, + dictWord{ + 11, + 10, + 317, + }, + dictWord{11, 10, 626}, + dictWord{12, 10, 142}, + dictWord{12, 10, 288}, + dictWord{12, 10, 678}, + dictWord{13, 10, 313}, + dictWord{15, 10, 113}, + dictWord{146, 10, 114}, + dictWord{6, 11, 110}, + dictWord{135, 11, 1681}, + dictWord{135, 0, 910}, + dictWord{6, 10, 241}, + dictWord{7, 10, 907}, + dictWord{8, 10, 832}, + dictWord{9, 10, 342}, + dictWord{10, 10, 729}, + dictWord{11, 10, 284}, + dictWord{11, 10, 445}, + dictWord{11, 10, 651}, + dictWord{11, 10, 863}, + dictWord{ + 13, + 10, + 398, + }, + dictWord{146, 10, 99}, + dictWord{7, 0, 705}, + dictWord{9, 0, 734}, + dictWord{5, 11, 1000}, + dictWord{7, 11, 733}, + dictWord{137, 11, 583}, + dictWord{4, 0, 73}, + dictWord{6, 0, 612}, + dictWord{7, 0, 927}, + dictWord{7, 0, 1822}, + dictWord{8, 0, 217}, + dictWord{9, 0, 765}, + dictWord{9, 0, 766}, + dictWord{10, 0, 408}, + dictWord{ + 11, + 0, + 51, + }, + dictWord{11, 0, 793}, + dictWord{12, 0, 266}, + dictWord{15, 0, 158}, + dictWord{20, 0, 89}, + dictWord{150, 0, 32}, + dictWord{7, 0, 1330}, + dictWord{4, 11, 297}, + dictWord{6, 11, 529}, + dictWord{7, 11, 152}, + dictWord{7, 11, 713}, + dictWord{7, 11, 1845}, + dictWord{8, 11, 710}, + dictWord{8, 11, 717}, + dictWord{140, 11, 639}, + dictWord{5, 0, 389}, + dictWord{136, 0, 636}, + dictWord{134, 0, 1409}, + dictWord{4, 10, 562}, + dictWord{9, 10, 254}, + dictWord{139, 10, 879}, + dictWord{134, 0, 893}, + dictWord{132, 10, 786}, + dictWord{4, 11, 520}, + dictWord{135, 11, 575}, + dictWord{136, 0, 21}, + dictWord{140, 0, 721}, + dictWord{136, 0, 959}, + dictWord{ + 7, + 11, + 1428, + }, + dictWord{7, 11, 1640}, + dictWord{9, 11, 169}, + dictWord{9, 11, 182}, + dictWord{9, 11, 367}, + dictWord{9, 11, 478}, + dictWord{9, 11, 506}, + dictWord{ + 9, + 11, + 551, + }, + dictWord{9, 11, 648}, + dictWord{9, 11, 651}, + dictWord{9, 11, 697}, + dictWord{9, 11, 705}, + dictWord{9, 11, 725}, + dictWord{9, 11, 787}, + dictWord{9, 11, 794}, + dictWord{10, 11, 198}, + dictWord{10, 11, 214}, + dictWord{10, 11, 267}, + dictWord{10, 11, 275}, + dictWord{10, 11, 456}, + dictWord{10, 11, 551}, + dictWord{ + 10, + 11, + 561, + }, + dictWord{10, 11, 613}, + dictWord{10, 11, 627}, + dictWord{10, 11, 668}, + dictWord{10, 11, 675}, + dictWord{10, 11, 691}, + dictWord{10, 11, 695}, + dictWord{10, 11, 707}, + dictWord{10, 11, 715}, + dictWord{11, 11, 183}, + dictWord{11, 11, 201}, + dictWord{11, 11, 244}, + dictWord{11, 11, 262}, + dictWord{ + 11, + 11, + 352, + }, + dictWord{11, 11, 439}, + dictWord{11, 11, 493}, + dictWord{11, 11, 572}, + dictWord{11, 11, 591}, + dictWord{11, 11, 608}, + dictWord{11, 11, 611}, + dictWord{ + 11, + 11, + 646, + }, + dictWord{11, 11, 674}, + dictWord{11, 11, 711}, + dictWord{11, 11, 751}, + dictWord{11, 11, 761}, + dictWord{11, 11, 776}, + dictWord{11, 11, 785}, + dictWord{11, 11, 850}, + dictWord{11, 11, 853}, + dictWord{11, 11, 862}, + dictWord{11, 11, 865}, + dictWord{11, 11, 868}, + dictWord{11, 11, 898}, + dictWord{ + 11, + 11, + 902, + }, + dictWord{11, 11, 903}, + dictWord{11, 11, 910}, + dictWord{11, 11, 932}, + dictWord{11, 11, 942}, + dictWord{11, 11, 957}, + dictWord{11, 11, 967}, + dictWord{ + 11, + 11, + 972, + }, + dictWord{12, 11, 148}, + dictWord{12, 11, 195}, + dictWord{12, 11, 220}, + dictWord{12, 11, 237}, + dictWord{12, 11, 318}, + dictWord{12, 11, 339}, + dictWord{12, 11, 393}, + dictWord{12, 11, 445}, + dictWord{12, 11, 450}, + dictWord{12, 11, 474}, + dictWord{12, 11, 509}, + dictWord{12, 11, 533}, + dictWord{ + 12, + 11, + 591, + }, + dictWord{12, 11, 594}, + dictWord{12, 11, 597}, + dictWord{12, 11, 621}, + dictWord{12, 11, 633}, + dictWord{12, 11, 642}, + dictWord{13, 11, 59}, + dictWord{ + 13, + 11, + 60, + }, + dictWord{13, 11, 145}, + dictWord{13, 11, 239}, + dictWord{13, 11, 250}, + dictWord{13, 11, 273}, + dictWord{13, 11, 329}, + dictWord{13, 11, 344}, + dictWord{13, 11, 365}, + dictWord{13, 11, 372}, + dictWord{13, 11, 387}, + dictWord{13, 11, 403}, + dictWord{13, 11, 414}, + dictWord{13, 11, 456}, + dictWord{ + 13, + 11, + 478, + }, + dictWord{13, 11, 483}, + dictWord{13, 11, 489}, + dictWord{14, 11, 55}, + dictWord{14, 11, 57}, + dictWord{14, 11, 81}, + dictWord{14, 11, 90}, + dictWord{ + 14, + 11, + 148, + }, + dictWord{14, 11, 239}, + dictWord{14, 11, 266}, + dictWord{14, 11, 321}, + dictWord{14, 11, 326}, + dictWord{14, 11, 327}, + dictWord{14, 11, 330}, + dictWord{ + 14, + 11, + 347, + }, + dictWord{14, 11, 355}, + dictWord{14, 11, 401}, + dictWord{14, 11, 411}, + dictWord{14, 11, 414}, + dictWord{14, 11, 416}, + dictWord{14, 11, 420}, + dictWord{15, 11, 61}, + dictWord{15, 11, 74}, + dictWord{15, 11, 87}, + dictWord{15, 11, 88}, + dictWord{15, 11, 94}, + dictWord{15, 11, 96}, + dictWord{15, 11, 116}, + dictWord{15, 11, 149}, + dictWord{15, 11, 154}, + dictWord{16, 11, 50}, + dictWord{16, 11, 63}, + dictWord{16, 11, 73}, + dictWord{17, 11, 2}, + dictWord{17, 11, 66}, + dictWord{ + 17, + 11, + 92, + }, + dictWord{17, 11, 103}, + dictWord{17, 11, 112}, + dictWord{18, 11, 50}, + dictWord{18, 11, 54}, + dictWord{18, 11, 82}, + dictWord{18, 11, 86}, + dictWord{ + 18, + 11, + 90, + }, + dictWord{18, 11, 111}, + dictWord{18, 11, 115}, + dictWord{18, 11, 156}, + dictWord{19, 11, 40}, + dictWord{19, 11, 79}, + dictWord{20, 11, 78}, + dictWord{ + 149, + 11, + 22, + }, + dictWord{137, 11, 170}, + dictWord{134, 0, 1433}, + dictWord{135, 11, 1307}, + dictWord{139, 11, 411}, + dictWord{5, 0, 189}, + dictWord{7, 0, 442}, + dictWord{7, 0, 443}, + dictWord{8, 0, 281}, + dictWord{12, 0, 174}, + dictWord{141, 0, 261}, + dictWord{6, 10, 216}, + dictWord{7, 10, 901}, + dictWord{7, 10, 1343}, + dictWord{136, 10, 493}, + dictWord{5, 11, 397}, + dictWord{6, 11, 154}, + dictWord{7, 10, 341}, + dictWord{7, 11, 676}, + dictWord{8, 11, 443}, + dictWord{8, 11, 609}, + dictWord{ + 9, + 11, + 24, + }, + dictWord{9, 11, 325}, + dictWord{10, 11, 35}, + dictWord{11, 10, 219}, + dictWord{11, 11, 535}, + dictWord{11, 11, 672}, + dictWord{11, 11, 1018}, + dictWord{12, 11, 637}, + dictWord{144, 11, 30}, + dictWord{6, 0, 2}, + dictWord{7, 0, 191}, + dictWord{7, 0, 446}, + dictWord{7, 0, 1262}, + dictWord{7, 0, 1737}, + dictWord{8, 0, 22}, + dictWord{8, 0, 270}, + dictWord{8, 0, 612}, + dictWord{9, 0, 4}, + dictWord{9, 0, 312}, + dictWord{9, 0, 436}, + dictWord{9, 0, 626}, + dictWord{10, 0, 216}, + dictWord{10, 0, 311}, + dictWord{10, 0, 521}, + dictWord{10, 0, 623}, + dictWord{11, 0, 72}, + dictWord{11, 0, 330}, + dictWord{11, 0, 455}, + dictWord{12, 0, 321}, + dictWord{12, 0, 504}, + dictWord{12, 0, 530}, + dictWord{12, 0, 543}, + dictWord{13, 0, 17}, + dictWord{13, 0, 156}, + dictWord{13, 0, 334}, + dictWord{14, 0, 131}, + dictWord{17, 0, 60}, + dictWord{ + 148, + 0, + 64, + }, + dictWord{7, 0, 354}, + dictWord{10, 0, 410}, + dictWord{139, 0, 815}, + dictWord{139, 10, 130}, + dictWord{7, 10, 1734}, + dictWord{137, 11, 631}, + dictWord{ + 12, + 0, + 425, + }, + dictWord{15, 0, 112}, + dictWord{10, 10, 115}, + dictWord{11, 10, 420}, + dictWord{13, 10, 404}, + dictWord{14, 10, 346}, + dictWord{143, 10, 54}, + dictWord{ + 6, + 0, + 60, + }, + dictWord{6, 0, 166}, + dictWord{7, 0, 374}, + dictWord{7, 0, 670}, + dictWord{7, 0, 1327}, + dictWord{8, 0, 411}, + dictWord{8, 0, 435}, + dictWord{9, 0, 653}, + dictWord{ + 9, + 0, + 740, + }, + dictWord{10, 0, 385}, + dictWord{11, 0, 222}, + dictWord{11, 0, 324}, + dictWord{11, 0, 829}, + dictWord{140, 0, 611}, + dictWord{7, 0, 1611}, + dictWord{ + 13, + 0, + 14, + }, + dictWord{15, 0, 44}, + dictWord{19, 0, 13}, + dictWord{148, 0, 76}, + dictWord{133, 11, 981}, + dictWord{4, 11, 56}, + dictWord{7, 11, 1791}, + dictWord{8, 11, 607}, + dictWord{8, 11, 651}, + dictWord{11, 11, 465}, + dictWord{11, 11, 835}, + dictWord{12, 11, 337}, + dictWord{141, 11, 480}, + dictWord{6, 0, 1478}, + dictWord{ + 5, + 10, + 1011, + }, + dictWord{136, 10, 701}, + dictWord{139, 0, 596}, + dictWord{5, 0, 206}, + dictWord{134, 0, 398}, + dictWord{4, 10, 54}, + dictWord{5, 10, 666}, + dictWord{ + 7, + 10, + 1039, + }, + dictWord{7, 10, 1130}, + dictWord{9, 10, 195}, + dictWord{138, 10, 302}, + dictWord{7, 0, 50}, + dictWord{9, 11, 158}, + dictWord{138, 11, 411}, + dictWord{ + 135, + 11, + 1120, + }, + dictWord{6, 0, 517}, + dictWord{7, 0, 1159}, + dictWord{10, 0, 621}, + dictWord{11, 0, 192}, + dictWord{134, 10, 1669}, + dictWord{4, 0, 592}, + dictWord{ + 6, + 0, + 600, + }, + dictWord{135, 0, 1653}, + dictWord{10, 0, 223}, + dictWord{139, 0, 645}, + dictWord{136, 11, 139}, + dictWord{7, 0, 64}, + dictWord{136, 0, 245}, + dictWord{ + 142, + 0, + 278, + }, + dictWord{6, 11, 622}, + dictWord{135, 11, 1030}, + dictWord{136, 0, 604}, + dictWord{134, 0, 1502}, + dictWord{138, 0, 265}, + dictWord{ + 141, + 11, + 168, + }, + dictWord{7, 0, 1763}, + dictWord{140, 0, 310}, + dictWord{7, 10, 798}, + dictWord{139, 11, 719}, + dictWord{7, 11, 160}, + dictWord{10, 11, 624}, + dictWord{ + 142, + 11, + 279, + }, + dictWord{132, 11, 363}, + dictWord{7, 10, 122}, + dictWord{9, 10, 259}, + dictWord{10, 10, 84}, + dictWord{11, 10, 470}, + dictWord{12, 10, 541}, + dictWord{141, 10, 379}, + dictWord{5, 0, 129}, + dictWord{6, 0, 61}, + dictWord{135, 0, 947}, + dictWord{134, 0, 1356}, + dictWord{135, 11, 1191}, + dictWord{13, 0, 505}, + dictWord{141, 0, 506}, + dictWord{11, 0, 1000}, + dictWord{5, 10, 82}, + dictWord{5, 10, 131}, + dictWord{7, 10, 1755}, + dictWord{8, 10, 31}, + dictWord{9, 10, 168}, + dictWord{9, 10, 764}, + dictWord{139, 10, 869}, + dictWord{134, 0, 966}, + dictWord{134, 10, 605}, + dictWord{134, 11, 292}, + dictWord{5, 11, 177}, + dictWord{ + 6, + 11, + 616, + }, + dictWord{7, 11, 827}, + dictWord{9, 11, 525}, + dictWord{138, 11, 656}, + dictWord{135, 11, 1486}, + dictWord{138, 11, 31}, + dictWord{5, 10, 278}, + dictWord{137, 10, 68}, + dictWord{4, 10, 163}, + dictWord{5, 10, 201}, + dictWord{5, 10, 307}, + dictWord{5, 10, 310}, + dictWord{6, 10, 335}, + dictWord{7, 10, 284}, + dictWord{136, 10, 165}, + dictWord{6, 0, 839}, + dictWord{135, 10, 1660}, + dictWord{136, 10, 781}, + dictWord{6, 10, 33}, + dictWord{135, 10, 1244}, + dictWord{ + 133, + 0, + 637, + }, + dictWord{4, 11, 161}, + dictWord{133, 11, 631}, + dictWord{137, 0, 590}, + dictWord{7, 10, 1953}, + dictWord{136, 10, 720}, + dictWord{5, 0, 280}, + dictWord{ + 7, + 0, + 1226, + }, + dictWord{138, 10, 203}, + dictWord{134, 0, 1386}, + dictWord{5, 0, 281}, + dictWord{6, 0, 1026}, + dictWord{6, 10, 326}, + dictWord{7, 10, 677}, + dictWord{ + 137, + 10, + 425, + }, + dictWord{7, 11, 1557}, + dictWord{135, 11, 1684}, + dictWord{135, 0, 1064}, + dictWord{9, 11, 469}, + dictWord{9, 11, 709}, + dictWord{12, 11, 512}, + dictWord{14, 11, 65}, + dictWord{145, 11, 12}, + dictWord{134, 0, 917}, + dictWord{10, 11, 229}, + dictWord{11, 11, 73}, + dictWord{11, 11, 376}, + dictWord{ + 139, + 11, + 433, + }, + dictWord{7, 0, 555}, + dictWord{9, 0, 192}, + dictWord{13, 0, 30}, + dictWord{13, 0, 49}, + dictWord{15, 0, 150}, + dictWord{16, 0, 76}, + dictWord{20, 0, 52}, + dictWord{ + 7, + 10, + 1316, + }, + dictWord{7, 10, 1412}, + dictWord{7, 10, 1839}, + dictWord{9, 10, 589}, + dictWord{11, 10, 241}, + dictWord{11, 10, 676}, + dictWord{11, 10, 811}, + dictWord{11, 10, 891}, + dictWord{12, 10, 140}, + dictWord{12, 10, 346}, + dictWord{12, 10, 479}, + dictWord{13, 10, 381}, + dictWord{14, 10, 188}, + dictWord{ + 146, + 10, + 30, + }, + dictWord{149, 0, 15}, + dictWord{6, 0, 1882}, + dictWord{6, 0, 1883}, + dictWord{6, 0, 1897}, + dictWord{9, 0, 945}, + dictWord{9, 0, 1014}, + dictWord{9, 0, 1020}, + dictWord{12, 0, 823}, + dictWord{12, 0, 842}, + dictWord{12, 0, 866}, + dictWord{12, 0, 934}, + dictWord{15, 0, 242}, + dictWord{146, 0, 208}, + dictWord{6, 0, 965}, + dictWord{134, 0, 1499}, + dictWord{7, 0, 33}, + dictWord{7, 0, 120}, + dictWord{8, 0, 489}, + dictWord{9, 0, 319}, + dictWord{10, 0, 820}, + dictWord{11, 0, 1004}, + dictWord{ + 12, + 0, + 379, + }, + dictWord{12, 0, 679}, + dictWord{13, 0, 117}, + dictWord{13, 0, 412}, + dictWord{14, 0, 25}, + dictWord{15, 0, 52}, + dictWord{15, 0, 161}, + dictWord{16, 0, 47}, + dictWord{149, 0, 2}, + dictWord{6, 11, 558}, + dictWord{7, 11, 651}, + dictWord{8, 11, 421}, + dictWord{9, 11, 0}, + dictWord{138, 11, 34}, + dictWord{4, 0, 937}, + dictWord{ + 5, + 0, + 801, + }, + dictWord{7, 0, 473}, + dictWord{5, 10, 358}, + dictWord{7, 10, 1184}, + dictWord{10, 10, 662}, + dictWord{13, 10, 212}, + dictWord{13, 10, 304}, + dictWord{ + 13, + 10, + 333, + }, + dictWord{145, 10, 98}, + dictWord{132, 0, 877}, + dictWord{6, 0, 693}, + dictWord{134, 0, 824}, + dictWord{132, 0, 365}, + dictWord{7, 11, 1832}, + dictWord{ + 138, + 11, + 374, + }, + dictWord{5, 0, 7}, + dictWord{139, 0, 774}, + dictWord{4, 0, 734}, + dictWord{5, 0, 662}, + dictWord{134, 0, 430}, + dictWord{4, 0, 746}, + dictWord{ + 135, + 0, + 1090, + }, + dictWord{5, 0, 360}, + dictWord{8, 0, 237}, + dictWord{10, 0, 231}, + dictWord{147, 0, 124}, + dictWord{138, 11, 348}, + dictWord{6, 11, 6}, + dictWord{7, 11, 81}, + dictWord{7, 11, 771}, + dictWord{7, 11, 1731}, + dictWord{9, 11, 405}, + dictWord{138, 11, 421}, + dictWord{6, 0, 740}, + dictWord{137, 0, 822}, + dictWord{ + 133, + 10, + 946, + }, + dictWord{7, 0, 1485}, + dictWord{136, 0, 929}, + dictWord{7, 10, 411}, + dictWord{8, 10, 631}, + dictWord{9, 10, 323}, + dictWord{10, 10, 355}, + dictWord{ + 11, + 10, + 491, + }, + dictWord{12, 10, 143}, + dictWord{12, 10, 402}, + dictWord{13, 10, 73}, + dictWord{14, 10, 408}, + dictWord{15, 10, 107}, + dictWord{146, 10, 71}, + dictWord{ + 135, + 10, + 590, + }, + dictWord{5, 11, 881}, + dictWord{133, 11, 885}, + dictWord{150, 11, 25}, + dictWord{4, 0, 852}, + dictWord{5, 11, 142}, + dictWord{134, 11, 546}, + dictWord{7, 10, 1467}, + dictWord{8, 10, 328}, + dictWord{10, 10, 544}, + dictWord{11, 10, 955}, + dictWord{13, 10, 320}, + dictWord{145, 10, 83}, + dictWord{9, 0, 17}, + dictWord{10, 0, 291}, + dictWord{11, 10, 511}, + dictWord{13, 10, 394}, + dictWord{14, 10, 298}, + dictWord{14, 10, 318}, + dictWord{146, 10, 103}, + dictWord{5, 11, 466}, + dictWord{11, 11, 571}, + dictWord{12, 11, 198}, + dictWord{13, 11, 283}, + dictWord{14, 11, 186}, + dictWord{15, 11, 21}, + dictWord{143, 11, 103}, + dictWord{ + 134, + 0, + 1001, + }, + dictWord{4, 11, 185}, + dictWord{5, 11, 257}, + dictWord{5, 11, 839}, + dictWord{5, 11, 936}, + dictWord{7, 11, 171}, + dictWord{9, 11, 399}, + dictWord{ + 10, + 11, + 258, + }, + dictWord{10, 11, 395}, + dictWord{10, 11, 734}, + dictWord{11, 11, 1014}, + dictWord{12, 11, 23}, + dictWord{13, 11, 350}, + dictWord{14, 11, 150}, + dictWord{147, 11, 6}, + dictWord{143, 0, 35}, + dictWord{132, 0, 831}, + dictWord{5, 10, 835}, + dictWord{134, 10, 483}, + dictWord{4, 0, 277}, + dictWord{5, 0, 608}, + dictWord{ + 6, + 0, + 493, + }, + dictWord{7, 0, 457}, + dictWord{12, 0, 384}, + dictWord{7, 11, 404}, + dictWord{7, 11, 1377}, + dictWord{7, 11, 1430}, + dictWord{7, 11, 2017}, + dictWord{ + 8, + 11, + 149, + }, + dictWord{8, 11, 239}, + dictWord{8, 11, 512}, + dictWord{8, 11, 793}, + dictWord{8, 11, 818}, + dictWord{9, 11, 474}, + dictWord{9, 11, 595}, + dictWord{ + 10, + 11, + 122, + }, + dictWord{10, 11, 565}, + dictWord{10, 11, 649}, + dictWord{10, 11, 783}, + dictWord{11, 11, 239}, + dictWord{11, 11, 295}, + dictWord{11, 11, 447}, + dictWord{ + 11, + 11, + 528, + }, + dictWord{11, 11, 639}, + dictWord{11, 11, 800}, + dictWord{11, 11, 936}, + dictWord{12, 11, 25}, + dictWord{12, 11, 73}, + dictWord{12, 11, 77}, + dictWord{12, 11, 157}, + dictWord{12, 11, 316}, + dictWord{12, 11, 390}, + dictWord{12, 11, 391}, + dictWord{12, 11, 394}, + dictWord{12, 11, 395}, + dictWord{ + 12, + 11, + 478, + }, + dictWord{12, 11, 503}, + dictWord{12, 11, 592}, + dictWord{12, 11, 680}, + dictWord{13, 11, 50}, + dictWord{13, 11, 53}, + dictWord{13, 11, 132}, + dictWord{ + 13, + 11, + 198, + }, + dictWord{13, 11, 275}, + dictWord{13, 11, 322}, + dictWord{13, 11, 415}, + dictWord{14, 11, 71}, + dictWord{14, 11, 257}, + dictWord{14, 11, 395}, + dictWord{15, 11, 71}, + dictWord{15, 11, 136}, + dictWord{17, 11, 123}, + dictWord{18, 11, 93}, + dictWord{147, 11, 58}, + dictWord{134, 0, 1351}, + dictWord{7, 0, 27}, + dictWord{135, 0, 316}, + dictWord{136, 11, 712}, + dictWord{136, 0, 984}, + dictWord{133, 0, 552}, + dictWord{137, 0, 264}, + dictWord{132, 0, 401}, + dictWord{6, 0, 710}, + dictWord{6, 0, 1111}, + dictWord{134, 0, 1343}, + dictWord{134, 0, 1211}, + dictWord{9, 0, 543}, + dictWord{10, 0, 524}, + dictWord{11, 0, 108}, + dictWord{11, 0, 653}, + dictWord{12, 0, 524}, + dictWord{13, 0, 123}, + dictWord{14, 0, 252}, + dictWord{16, 0, 18}, + dictWord{19, 0, 38}, + dictWord{20, 0, 26}, + dictWord{20, 0, 65}, + dictWord{ + 21, + 0, + 3, + }, + dictWord{151, 0, 11}, + dictWord{4, 0, 205}, + dictWord{5, 0, 623}, + dictWord{7, 0, 104}, + dictWord{8, 0, 519}, + dictWord{137, 0, 716}, + dictWord{132, 10, 677}, + dictWord{4, 11, 377}, + dictWord{152, 11, 13}, + dictWord{135, 11, 1673}, + dictWord{7, 0, 579}, + dictWord{9, 0, 41}, + dictWord{9, 0, 244}, + dictWord{9, 0, 669}, + dictWord{ + 10, + 0, + 5, + }, + dictWord{11, 0, 861}, + dictWord{11, 0, 951}, + dictWord{139, 0, 980}, + dictWord{132, 0, 717}, + dictWord{136, 0, 1011}, + dictWord{132, 0, 805}, + dictWord{ + 4, + 11, + 180, + }, + dictWord{135, 11, 1906}, + dictWord{132, 10, 777}, + dictWord{132, 10, 331}, + dictWord{132, 0, 489}, + dictWord{6, 0, 1024}, + dictWord{4, 11, 491}, + dictWord{133, 10, 747}, + dictWord{135, 11, 1182}, + dictWord{4, 11, 171}, + dictWord{138, 11, 234}, + dictWord{4, 11, 586}, + dictWord{7, 11, 1186}, + dictWord{ + 138, + 11, + 631, + }, + dictWord{135, 0, 892}, + dictWord{135, 11, 336}, + dictWord{9, 11, 931}, + dictWord{10, 11, 334}, + dictWord{148, 11, 71}, + dictWord{137, 0, 473}, + dictWord{6, 0, 864}, + dictWord{12, 0, 659}, + dictWord{139, 11, 926}, + dictWord{7, 0, 819}, + dictWord{9, 0, 26}, + dictWord{9, 0, 392}, + dictWord{10, 0, 152}, + dictWord{ + 10, + 0, + 226, + }, + dictWord{11, 0, 19}, + dictWord{12, 0, 276}, + dictWord{12, 0, 426}, + dictWord{12, 0, 589}, + dictWord{13, 0, 460}, + dictWord{15, 0, 97}, + dictWord{19, 0, 48}, + dictWord{148, 0, 104}, + dictWord{135, 0, 51}, + dictWord{133, 10, 326}, + dictWord{4, 10, 691}, + dictWord{146, 10, 16}, + dictWord{9, 0, 130}, + dictWord{11, 0, 765}, + dictWord{10, 10, 680}, + dictWord{10, 10, 793}, + dictWord{141, 10, 357}, + dictWord{133, 11, 765}, + dictWord{8, 0, 229}, + dictWord{6, 10, 32}, + dictWord{7, 10, 385}, + dictWord{7, 10, 757}, + dictWord{7, 10, 1916}, + dictWord{8, 10, 94}, + dictWord{8, 10, 711}, + dictWord{9, 10, 541}, + dictWord{10, 10, 162}, + dictWord{10, 10, 795}, + dictWord{11, 10, 989}, + dictWord{11, 10, 1010}, + dictWord{12, 10, 14}, + dictWord{142, 10, 308}, + dictWord{7, 11, 474}, + dictWord{137, 11, 578}, + dictWord{ + 132, + 0, + 674, + }, + dictWord{132, 0, 770}, + dictWord{5, 0, 79}, + dictWord{7, 0, 1027}, + dictWord{7, 0, 1477}, + dictWord{139, 0, 52}, + dictWord{133, 11, 424}, + dictWord{ + 134, + 0, + 1666, + }, + dictWord{6, 0, 409}, + dictWord{6, 10, 349}, + dictWord{6, 10, 1682}, + dictWord{7, 10, 1252}, + dictWord{8, 10, 112}, + dictWord{8, 11, 714}, + dictWord{ + 9, + 10, + 435, + }, + dictWord{9, 10, 668}, + dictWord{10, 10, 290}, + dictWord{10, 10, 319}, + dictWord{10, 10, 815}, + dictWord{11, 10, 180}, + dictWord{11, 10, 837}, + dictWord{ + 12, + 10, + 240, + }, + dictWord{13, 10, 152}, + dictWord{13, 10, 219}, + dictWord{142, 10, 158}, + dictWord{5, 0, 789}, + dictWord{134, 0, 195}, + dictWord{4, 0, 251}, + dictWord{ + 4, + 0, + 688, + }, + dictWord{7, 0, 513}, + dictWord{135, 0, 1284}, + dictWord{132, 10, 581}, + dictWord{9, 11, 420}, + dictWord{10, 11, 269}, + dictWord{10, 11, 285}, + dictWord{10, 11, 576}, + dictWord{11, 11, 397}, + dictWord{13, 11, 175}, + dictWord{145, 11, 90}, + dictWord{6, 10, 126}, + dictWord{7, 10, 573}, + dictWord{8, 10, 397}, + dictWord{142, 10, 44}, + dictWord{132, 11, 429}, + dictWord{133, 0, 889}, + dictWord{4, 0, 160}, + dictWord{5, 0, 330}, + dictWord{7, 0, 1434}, + dictWord{136, 0, 174}, + dictWord{7, 11, 18}, + dictWord{7, 11, 699}, + dictWord{7, 11, 1966}, + dictWord{8, 11, 752}, + dictWord{9, 11, 273}, + dictWord{9, 11, 412}, + dictWord{9, 11, 703}, + dictWord{ + 10, + 11, + 71, + }, + dictWord{10, 11, 427}, + dictWord{10, 11, 508}, + dictWord{146, 11, 97}, + dictWord{6, 0, 872}, + dictWord{134, 0, 899}, + dictWord{133, 10, 926}, + dictWord{134, 0, 1126}, + dictWord{134, 0, 918}, + dictWord{4, 11, 53}, + dictWord{5, 11, 186}, + dictWord{135, 11, 752}, + dictWord{7, 0, 268}, + dictWord{136, 0, 569}, + dictWord{134, 0, 1224}, + dictWord{6, 0, 1361}, + dictWord{7, 10, 1232}, + dictWord{137, 10, 531}, + dictWord{8, 11, 575}, + dictWord{10, 11, 289}, + dictWord{ + 139, + 11, + 319, + }, + dictWord{133, 10, 670}, + dictWord{132, 11, 675}, + dictWord{133, 0, 374}, + dictWord{135, 10, 1957}, + dictWord{133, 0, 731}, + dictWord{11, 0, 190}, + dictWord{15, 0, 49}, + dictWord{11, 11, 190}, + dictWord{143, 11, 49}, + dictWord{4, 0, 626}, + dictWord{5, 0, 506}, + dictWord{5, 0, 642}, + dictWord{6, 0, 425}, + dictWord{ + 10, + 0, + 202, + }, + dictWord{139, 0, 141}, + dictWord{137, 0, 444}, + dictWord{7, 10, 242}, + dictWord{135, 10, 1942}, + dictWord{6, 11, 209}, + dictWord{8, 11, 468}, + dictWord{ + 9, + 11, + 210, + }, + dictWord{11, 11, 36}, + dictWord{12, 11, 28}, + dictWord{12, 11, 630}, + dictWord{13, 11, 21}, + dictWord{13, 11, 349}, + dictWord{14, 11, 7}, + dictWord{ + 145, + 11, + 13, + }, + dictWord{4, 11, 342}, + dictWord{135, 11, 1179}, + dictWord{5, 10, 834}, + dictWord{7, 10, 1202}, + dictWord{8, 10, 14}, + dictWord{9, 10, 481}, + dictWord{ + 137, + 10, + 880, + }, + dictWord{4, 11, 928}, + dictWord{133, 11, 910}, + dictWord{4, 11, 318}, + dictWord{4, 11, 496}, + dictWord{7, 11, 856}, + dictWord{139, 11, 654}, + dictWord{136, 0, 835}, + dictWord{7, 0, 1526}, + dictWord{138, 10, 465}, + dictWord{151, 0, 17}, + dictWord{135, 0, 477}, + dictWord{4, 10, 357}, + dictWord{6, 10, 172}, + dictWord{7, 10, 143}, + dictWord{137, 10, 413}, + dictWord{6, 0, 1374}, + dictWord{138, 0, 994}, + dictWord{18, 0, 76}, + dictWord{132, 10, 590}, + dictWord{7, 0, 287}, + dictWord{8, 0, 355}, + dictWord{9, 0, 293}, + dictWord{137, 0, 743}, + dictWord{134, 0, 1389}, + dictWord{7, 11, 915}, + dictWord{8, 11, 247}, + dictWord{147, 11, 0}, + dictWord{ + 4, + 11, + 202, + }, + dictWord{5, 11, 382}, + dictWord{6, 11, 454}, + dictWord{7, 11, 936}, + dictWord{7, 11, 1803}, + dictWord{8, 11, 758}, + dictWord{9, 11, 375}, + dictWord{ + 9, + 11, + 895, + }, + dictWord{10, 11, 743}, + dictWord{10, 11, 792}, + dictWord{11, 11, 978}, + dictWord{11, 11, 1012}, + dictWord{142, 11, 109}, + dictWord{5, 0, 384}, + dictWord{8, 0, 455}, + dictWord{140, 0, 48}, + dictWord{132, 11, 390}, + dictWord{5, 10, 169}, + dictWord{7, 10, 333}, + dictWord{136, 10, 45}, + dictWord{5, 0, 264}, + dictWord{134, 0, 184}, + dictWord{138, 11, 791}, + dictWord{133, 11, 717}, + dictWord{132, 10, 198}, + dictWord{6, 11, 445}, + dictWord{7, 11, 332}, + dictWord{ + 137, + 11, + 909, + }, + dictWord{136, 0, 1001}, + dictWord{4, 10, 24}, + dictWord{5, 10, 140}, + dictWord{5, 10, 185}, + dictWord{7, 10, 1500}, + dictWord{11, 10, 565}, + dictWord{ + 139, + 10, + 838, + }, + dictWord{134, 11, 578}, + dictWord{5, 0, 633}, + dictWord{6, 0, 28}, + dictWord{135, 0, 1323}, + dictWord{132, 0, 851}, + dictWord{136, 11, 267}, + dictWord{ + 7, + 0, + 359, + }, + dictWord{8, 0, 243}, + dictWord{140, 0, 175}, + dictWord{4, 10, 334}, + dictWord{133, 10, 593}, + dictWord{141, 11, 87}, + dictWord{136, 11, 766}, + dictWord{10, 0, 287}, + dictWord{12, 0, 138}, + dictWord{10, 11, 287}, + dictWord{140, 11, 138}, + dictWord{4, 0, 105}, + dictWord{132, 0, 740}, + dictWord{140, 10, 116}, + dictWord{134, 0, 857}, + dictWord{135, 11, 1841}, + dictWord{6, 0, 1402}, + dictWord{137, 0, 819}, + dictWord{132, 11, 584}, + dictWord{132, 10, 709}, + dictWord{ + 133, + 10, + 897, + }, + dictWord{5, 0, 224}, + dictWord{13, 0, 174}, + dictWord{146, 0, 52}, + dictWord{135, 10, 1840}, + dictWord{4, 10, 608}, + dictWord{133, 10, 497}, + dictWord{139, 11, 60}, + dictWord{4, 0, 758}, + dictWord{135, 0, 1649}, + dictWord{4, 11, 226}, + dictWord{4, 11, 326}, + dictWord{135, 11, 1770}, + dictWord{5, 11, 426}, + dictWord{8, 11, 30}, + dictWord{9, 11, 2}, + dictWord{11, 11, 549}, + dictWord{147, 11, 122}, + dictWord{135, 10, 2039}, + dictWord{6, 10, 540}, + dictWord{ + 136, + 10, + 136, + }, + dictWord{4, 0, 573}, + dictWord{8, 0, 655}, + dictWord{4, 10, 897}, + dictWord{133, 10, 786}, + dictWord{7, 0, 351}, + dictWord{139, 0, 128}, + dictWord{ + 133, + 10, + 999, + }, + dictWord{4, 10, 299}, + dictWord{135, 10, 1004}, + dictWord{133, 0, 918}, + dictWord{132, 11, 345}, + dictWord{4, 11, 385}, + dictWord{7, 11, 265}, + dictWord{135, 11, 587}, + dictWord{133, 10, 456}, + dictWord{136, 10, 180}, + dictWord{6, 0, 687}, + dictWord{134, 0, 1537}, + dictWord{4, 11, 347}, + dictWord{ + 5, + 11, + 423, + }, + dictWord{5, 11, 996}, + dictWord{135, 11, 1329}, + dictWord{132, 10, 755}, + dictWord{7, 11, 1259}, + dictWord{9, 11, 125}, + dictWord{11, 11, 65}, + dictWord{140, 11, 285}, + dictWord{5, 11, 136}, + dictWord{6, 11, 136}, + dictWord{136, 11, 644}, + dictWord{134, 0, 1525}, + dictWord{4, 0, 1009}, + dictWord{ + 135, + 0, + 1139, + }, + dictWord{139, 10, 338}, + dictWord{132, 0, 340}, + dictWord{135, 10, 1464}, + dictWord{8, 0, 847}, + dictWord{10, 0, 861}, + dictWord{10, 0, 876}, + dictWord{ + 10, + 0, + 889, + }, + dictWord{10, 0, 922}, + dictWord{10, 0, 929}, + dictWord{10, 0, 933}, + dictWord{12, 0, 784}, + dictWord{140, 0, 791}, + dictWord{139, 0, 176}, + dictWord{ + 9, + 11, + 134, + }, + dictWord{10, 11, 2}, + dictWord{10, 11, 27}, + dictWord{10, 11, 333}, + dictWord{11, 11, 722}, + dictWord{143, 11, 1}, + dictWord{4, 11, 433}, + dictWord{ + 133, + 11, + 719, + }, + dictWord{5, 0, 985}, + dictWord{7, 0, 509}, + dictWord{7, 0, 529}, + dictWord{145, 0, 96}, + dictWord{132, 0, 615}, + dictWord{4, 10, 890}, + dictWord{ + 5, + 10, + 805, + }, + dictWord{5, 10, 819}, + dictWord{5, 10, 961}, + dictWord{6, 10, 396}, + dictWord{6, 10, 1631}, + dictWord{6, 10, 1678}, + dictWord{7, 10, 1967}, + dictWord{ + 7, + 10, + 2041, + }, + dictWord{9, 10, 630}, + dictWord{11, 10, 8}, + dictWord{11, 10, 1019}, + dictWord{12, 10, 176}, + dictWord{13, 10, 225}, + dictWord{14, 10, 292}, + dictWord{ + 149, + 10, + 24, + }, + dictWord{135, 0, 1919}, + dictWord{134, 0, 1131}, + dictWord{144, 11, 21}, + dictWord{144, 11, 51}, + dictWord{135, 10, 1815}, + dictWord{4, 0, 247}, + dictWord{7, 10, 1505}, + dictWord{10, 10, 190}, + dictWord{10, 10, 634}, + dictWord{11, 10, 792}, + dictWord{12, 10, 358}, + dictWord{140, 10, 447}, + dictWord{ + 5, + 10, + 0, + }, + dictWord{6, 10, 536}, + dictWord{7, 10, 604}, + dictWord{13, 10, 445}, + dictWord{145, 10, 126}, + dictWord{4, 0, 184}, + dictWord{5, 0, 390}, + dictWord{6, 0, 337}, + dictWord{7, 0, 23}, + dictWord{7, 0, 494}, + dictWord{7, 0, 618}, + dictWord{7, 0, 1456}, + dictWord{8, 0, 27}, + dictWord{8, 0, 599}, + dictWord{10, 0, 153}, + dictWord{ + 139, + 0, + 710, + }, + dictWord{6, 10, 232}, + dictWord{6, 10, 412}, + dictWord{7, 10, 1074}, + dictWord{8, 10, 9}, + dictWord{8, 10, 157}, + dictWord{8, 10, 786}, + dictWord{9, 10, 196}, + dictWord{9, 10, 352}, + dictWord{9, 10, 457}, + dictWord{10, 10, 337}, + dictWord{11, 10, 232}, + dictWord{11, 10, 877}, + dictWord{12, 10, 480}, + dictWord{ + 140, + 10, + 546, + }, + dictWord{13, 0, 38}, + dictWord{135, 10, 958}, + dictWord{4, 10, 382}, + dictWord{136, 10, 579}, + dictWord{4, 10, 212}, + dictWord{135, 10, 1206}, + dictWord{ + 4, + 11, + 555, + }, + dictWord{8, 11, 536}, + dictWord{138, 11, 288}, + dictWord{11, 11, 139}, + dictWord{139, 11, 171}, + dictWord{9, 11, 370}, + dictWord{138, 11, 90}, + dictWord{132, 0, 1015}, + dictWord{134, 0, 1088}, + dictWord{5, 10, 655}, + dictWord{135, 11, 977}, + dictWord{134, 0, 1585}, + dictWord{17, 10, 67}, + dictWord{ + 147, + 10, + 74, + }, + dictWord{10, 0, 227}, + dictWord{11, 0, 497}, + dictWord{11, 0, 709}, + dictWord{140, 0, 415}, + dictWord{6, 0, 360}, + dictWord{7, 0, 1664}, + dictWord{ + 136, + 0, + 478, + }, + dictWord{7, 0, 95}, + dictWord{6, 10, 231}, + dictWord{136, 10, 423}, + dictWord{140, 11, 65}, + dictWord{4, 11, 257}, + dictWord{135, 11, 2031}, + dictWord{ + 135, + 11, + 1768, + }, + dictWord{133, 10, 300}, + dictWord{139, 11, 211}, + dictWord{136, 0, 699}, + dictWord{6, 10, 237}, + dictWord{7, 10, 611}, + dictWord{8, 10, 100}, + dictWord{9, 10, 416}, + dictWord{11, 10, 335}, + dictWord{12, 10, 173}, + dictWord{146, 10, 101}, + dictWord{14, 0, 26}, + dictWord{146, 0, 150}, + dictWord{6, 0, 581}, + dictWord{135, 0, 1119}, + dictWord{135, 10, 1208}, + dictWord{132, 0, 739}, + dictWord{6, 11, 83}, + dictWord{6, 11, 1733}, + dictWord{135, 11, 1389}, + dictWord{ + 137, + 0, + 869, + }, + dictWord{4, 0, 67}, + dictWord{5, 0, 422}, + dictWord{7, 0, 1037}, + dictWord{7, 0, 1289}, + dictWord{7, 0, 1555}, + dictWord{9, 0, 741}, + dictWord{145, 0, 108}, + dictWord{133, 10, 199}, + dictWord{12, 10, 427}, + dictWord{146, 10, 38}, + dictWord{136, 0, 464}, + dictWord{142, 0, 42}, + dictWord{10, 0, 96}, + dictWord{8, 11, 501}, + dictWord{137, 11, 696}, + dictWord{134, 11, 592}, + dictWord{4, 0, 512}, + dictWord{4, 0, 966}, + dictWord{5, 0, 342}, + dictWord{6, 0, 1855}, + dictWord{8, 0, 869}, + dictWord{8, 0, 875}, + dictWord{8, 0, 901}, + dictWord{144, 0, 26}, + dictWord{8, 0, 203}, + dictWord{11, 0, 823}, + dictWord{11, 0, 846}, + dictWord{12, 0, 482}, + dictWord{ + 13, + 0, + 277, + }, + dictWord{13, 0, 302}, + dictWord{13, 0, 464}, + dictWord{14, 0, 205}, + dictWord{142, 0, 221}, + dictWord{4, 0, 449}, + dictWord{133, 0, 718}, + dictWord{ + 7, + 11, + 1718, + }, + dictWord{9, 11, 95}, + dictWord{9, 11, 274}, + dictWord{10, 11, 279}, + dictWord{10, 11, 317}, + dictWord{10, 11, 420}, + dictWord{11, 11, 303}, + dictWord{ + 11, + 11, + 808, + }, + dictWord{12, 11, 134}, + dictWord{12, 11, 367}, + dictWord{13, 11, 149}, + dictWord{13, 11, 347}, + dictWord{14, 11, 349}, + dictWord{14, 11, 406}, + dictWord{18, 11, 22}, + dictWord{18, 11, 89}, + dictWord{18, 11, 122}, + dictWord{147, 11, 47}, + dictWord{133, 11, 26}, + dictWord{4, 0, 355}, + dictWord{6, 0, 311}, + dictWord{ + 9, + 0, + 256, + }, + dictWord{138, 0, 404}, + dictWord{132, 11, 550}, + dictWord{10, 0, 758}, + dictWord{6, 10, 312}, + dictWord{6, 10, 1715}, + dictWord{10, 10, 584}, + dictWord{11, 10, 546}, + dictWord{11, 10, 692}, + dictWord{12, 10, 259}, + dictWord{12, 10, 295}, + dictWord{13, 10, 46}, + dictWord{141, 10, 154}, + dictWord{ + 136, + 11, + 822, + }, + dictWord{5, 0, 827}, + dictWord{4, 11, 902}, + dictWord{5, 11, 809}, + dictWord{6, 11, 122}, + dictWord{135, 11, 896}, + dictWord{5, 0, 64}, + dictWord{140, 0, 581}, + dictWord{4, 0, 442}, + dictWord{6, 0, 739}, + dictWord{7, 0, 1047}, + dictWord{7, 0, 1352}, + dictWord{7, 0, 1643}, + dictWord{7, 11, 1911}, + dictWord{9, 11, 449}, + dictWord{10, 11, 192}, + dictWord{138, 11, 740}, + dictWord{135, 11, 262}, + dictWord{132, 10, 588}, + dictWord{133, 11, 620}, + dictWord{5, 0, 977}, + dictWord{ + 6, + 0, + 288, + }, + dictWord{7, 0, 528}, + dictWord{4, 11, 34}, + dictWord{5, 11, 574}, + dictWord{7, 11, 279}, + dictWord{7, 11, 1624}, + dictWord{136, 11, 601}, + dictWord{ + 6, + 0, + 1375, + }, + dictWord{4, 10, 231}, + dictWord{5, 10, 61}, + dictWord{6, 10, 104}, + dictWord{7, 10, 729}, + dictWord{7, 10, 964}, + dictWord{7, 10, 1658}, + dictWord{ + 140, + 10, + 414, + }, + dictWord{6, 10, 263}, + dictWord{138, 10, 757}, + dictWord{132, 10, 320}, + dictWord{4, 0, 254}, + dictWord{7, 0, 1309}, + dictWord{5, 11, 332}, + dictWord{ + 135, + 11, + 1309, + }, + dictWord{6, 11, 261}, + dictWord{8, 11, 182}, + dictWord{139, 11, 943}, + dictWord{132, 10, 225}, + dictWord{6, 0, 12}, + dictWord{135, 0, 1219}, + dictWord{4, 0, 275}, + dictWord{12, 0, 376}, + dictWord{6, 11, 1721}, + dictWord{141, 11, 490}, + dictWord{4, 11, 933}, + dictWord{133, 11, 880}, + dictWord{6, 0, 951}, + dictWord{6, 0, 1109}, + dictWord{6, 0, 1181}, + dictWord{7, 0, 154}, + dictWord{4, 10, 405}, + dictWord{7, 10, 817}, + dictWord{14, 10, 58}, + dictWord{17, 10, 37}, + dictWord{ + 146, + 10, + 124, + }, + dictWord{6, 0, 1520}, + dictWord{133, 10, 974}, + dictWord{134, 0, 1753}, + dictWord{6, 0, 369}, + dictWord{6, 0, 502}, + dictWord{7, 0, 1036}, + dictWord{ + 8, + 0, + 348, + }, + dictWord{9, 0, 452}, + dictWord{10, 0, 26}, + dictWord{11, 0, 224}, + dictWord{11, 0, 387}, + dictWord{11, 0, 772}, + dictWord{12, 0, 95}, + dictWord{12, 0, 629}, + dictWord{13, 0, 195}, + dictWord{13, 0, 207}, + dictWord{13, 0, 241}, + dictWord{14, 0, 260}, + dictWord{14, 0, 270}, + dictWord{143, 0, 140}, + dictWord{132, 0, 269}, + dictWord{5, 0, 480}, + dictWord{7, 0, 532}, + dictWord{7, 0, 1197}, + dictWord{7, 0, 1358}, + dictWord{8, 0, 291}, + dictWord{11, 0, 349}, + dictWord{142, 0, 396}, + dictWord{ + 5, + 10, + 235, + }, + dictWord{7, 10, 1239}, + dictWord{11, 10, 131}, + dictWord{140, 10, 370}, + dictWord{7, 10, 956}, + dictWord{7, 10, 1157}, + dictWord{7, 10, 1506}, + dictWord{ + 7, + 10, + 1606, + }, + dictWord{7, 10, 1615}, + dictWord{7, 10, 1619}, + dictWord{7, 10, 1736}, + dictWord{7, 10, 1775}, + dictWord{8, 10, 590}, + dictWord{9, 10, 324}, + dictWord{9, 10, 736}, + dictWord{9, 10, 774}, + dictWord{9, 10, 776}, + dictWord{9, 10, 784}, + dictWord{10, 10, 567}, + dictWord{10, 10, 708}, + dictWord{11, 10, 518}, + dictWord{11, 10, 613}, + dictWord{11, 10, 695}, + dictWord{11, 10, 716}, + dictWord{11, 10, 739}, + dictWord{11, 10, 770}, + dictWord{11, 10, 771}, + dictWord{ + 11, + 10, + 848, + }, + dictWord{11, 10, 857}, + dictWord{11, 10, 931}, + dictWord{11, 10, 947}, + dictWord{12, 10, 326}, + dictWord{12, 10, 387}, + dictWord{12, 10, 484}, + dictWord{ + 12, + 10, + 528, + }, + dictWord{12, 10, 552}, + dictWord{12, 10, 613}, + dictWord{13, 10, 189}, + dictWord{13, 10, 256}, + dictWord{13, 10, 340}, + dictWord{13, 10, 432}, + dictWord{13, 10, 436}, + dictWord{13, 10, 440}, + dictWord{13, 10, 454}, + dictWord{14, 10, 174}, + dictWord{14, 10, 220}, + dictWord{14, 10, 284}, + dictWord{ + 14, + 10, + 390, + }, + dictWord{145, 10, 121}, + dictWord{8, 11, 598}, + dictWord{9, 11, 664}, + dictWord{138, 11, 441}, + dictWord{9, 10, 137}, + dictWord{138, 10, 221}, + dictWord{133, 11, 812}, + dictWord{148, 0, 15}, + dictWord{134, 0, 1341}, + dictWord{6, 0, 1017}, + dictWord{4, 11, 137}, + dictWord{7, 11, 1178}, + dictWord{ + 135, + 11, + 1520, + }, + dictWord{7, 10, 390}, + dictWord{138, 10, 140}, + dictWord{7, 11, 1260}, + dictWord{135, 11, 1790}, + dictWord{137, 11, 191}, + dictWord{ + 135, + 10, + 1144, + }, + dictWord{6, 0, 1810}, + dictWord{7, 0, 657}, + dictWord{8, 0, 886}, + dictWord{10, 0, 857}, + dictWord{14, 0, 440}, + dictWord{144, 0, 96}, + dictWord{8, 0, 533}, + dictWord{6, 11, 1661}, + dictWord{7, 11, 1975}, + dictWord{7, 11, 2009}, + dictWord{135, 11, 2011}, + dictWord{6, 0, 1453}, + dictWord{134, 10, 464}, + dictWord{ + 132, + 11, + 715, + }, + dictWord{5, 10, 407}, + dictWord{11, 10, 204}, + dictWord{11, 10, 243}, + dictWord{11, 10, 489}, + dictWord{12, 10, 293}, + dictWord{19, 10, 37}, + dictWord{20, 10, 73}, + dictWord{150, 10, 38}, + dictWord{133, 11, 703}, + dictWord{4, 0, 211}, + dictWord{7, 0, 1483}, + dictWord{5, 10, 325}, + dictWord{8, 10, 5}, + dictWord{ + 8, + 10, + 227, + }, + dictWord{9, 10, 105}, + dictWord{10, 10, 585}, + dictWord{140, 10, 614}, + dictWord{4, 0, 332}, + dictWord{5, 0, 335}, + dictWord{6, 0, 238}, + dictWord{ + 7, + 0, + 269, + }, + dictWord{7, 0, 811}, + dictWord{7, 0, 1797}, + dictWord{8, 0, 836}, + dictWord{9, 0, 507}, + dictWord{141, 0, 242}, + dictWord{5, 11, 89}, + dictWord{7, 11, 1915}, + dictWord{9, 11, 185}, + dictWord{9, 11, 235}, + dictWord{9, 11, 496}, + dictWord{10, 11, 64}, + dictWord{10, 11, 270}, + dictWord{10, 11, 403}, + dictWord{10, 11, 469}, + dictWord{10, 11, 529}, + dictWord{10, 11, 590}, + dictWord{11, 11, 140}, + dictWord{11, 11, 860}, + dictWord{13, 11, 1}, + dictWord{13, 11, 422}, + dictWord{14, 11, 341}, + dictWord{14, 11, 364}, + dictWord{17, 11, 93}, + dictWord{18, 11, 113}, + dictWord{19, 11, 97}, + dictWord{147, 11, 113}, + dictWord{133, 11, 695}, + dictWord{ + 16, + 0, + 19, + }, + dictWord{5, 11, 6}, + dictWord{6, 11, 183}, + dictWord{6, 10, 621}, + dictWord{7, 11, 680}, + dictWord{7, 11, 978}, + dictWord{7, 11, 1013}, + dictWord{7, 11, 1055}, + dictWord{12, 11, 230}, + dictWord{13, 11, 172}, + dictWord{13, 10, 504}, + dictWord{146, 11, 29}, + dictWord{136, 0, 156}, + dictWord{133, 0, 1009}, + dictWord{ + 6, + 11, + 29, + }, + dictWord{139, 11, 63}, + dictWord{134, 0, 820}, + dictWord{134, 10, 218}, + dictWord{7, 10, 454}, + dictWord{7, 10, 782}, + dictWord{8, 10, 768}, + dictWord{ + 140, + 10, + 686, + }, + dictWord{5, 0, 228}, + dictWord{6, 0, 203}, + dictWord{7, 0, 156}, + dictWord{8, 0, 347}, + dictWord{9, 0, 265}, + dictWord{18, 0, 39}, + dictWord{20, 0, 54}, + dictWord{21, 0, 31}, + dictWord{22, 0, 3}, + dictWord{23, 0, 0}, + dictWord{15, 11, 8}, + dictWord{18, 11, 39}, + dictWord{20, 11, 54}, + dictWord{21, 11, 31}, + dictWord{22, 11, 3}, + dictWord{151, 11, 0}, + dictWord{7, 0, 1131}, + dictWord{135, 0, 1468}, + dictWord{144, 10, 0}, + dictWord{134, 0, 1276}, + dictWord{10, 10, 676}, + dictWord{ + 140, + 10, + 462, + }, + dictWord{132, 11, 311}, + dictWord{134, 11, 1740}, + dictWord{7, 11, 170}, + dictWord{8, 11, 90}, + dictWord{8, 11, 177}, + dictWord{8, 11, 415}, + dictWord{ + 11, + 11, + 714, + }, + dictWord{142, 11, 281}, + dictWord{134, 10, 164}, + dictWord{6, 0, 1792}, + dictWord{138, 0, 849}, + dictWord{150, 10, 50}, + dictWord{5, 0, 291}, + dictWord{5, 0, 318}, + dictWord{7, 0, 765}, + dictWord{9, 0, 389}, + dictWord{12, 0, 548}, + dictWord{8, 11, 522}, + dictWord{142, 11, 328}, + dictWord{11, 11, 91}, + dictWord{ + 13, + 11, + 129, + }, + dictWord{15, 11, 101}, + dictWord{145, 11, 125}, + dictWord{4, 11, 494}, + dictWord{6, 11, 74}, + dictWord{7, 11, 44}, + dictWord{7, 11, 407}, + dictWord{ + 8, + 11, + 551, + }, + dictWord{12, 11, 17}, + dictWord{15, 11, 5}, + dictWord{148, 11, 11}, + dictWord{4, 11, 276}, + dictWord{133, 11, 296}, + dictWord{6, 10, 343}, + dictWord{ + 7, + 10, + 195, + }, + dictWord{7, 11, 1777}, + dictWord{9, 10, 226}, + dictWord{10, 10, 197}, + dictWord{10, 10, 575}, + dictWord{11, 10, 502}, + dictWord{139, 10, 899}, + dictWord{ + 10, + 0, + 525, + }, + dictWord{139, 0, 82}, + dictWord{14, 0, 453}, + dictWord{4, 11, 7}, + dictWord{5, 11, 90}, + dictWord{5, 11, 158}, + dictWord{6, 11, 542}, + dictWord{7, 11, 221}, + dictWord{7, 11, 1574}, + dictWord{9, 11, 490}, + dictWord{10, 11, 540}, + dictWord{11, 11, 443}, + dictWord{139, 11, 757}, + dictWord{135, 0, 666}, + dictWord{ + 22, + 10, + 29, + }, + dictWord{150, 11, 29}, + dictWord{4, 0, 422}, + dictWord{147, 10, 8}, + dictWord{5, 0, 355}, + dictWord{145, 0, 0}, + dictWord{6, 0, 1873}, + dictWord{9, 0, 918}, + dictWord{7, 11, 588}, + dictWord{9, 11, 175}, + dictWord{138, 11, 530}, + dictWord{143, 11, 31}, + dictWord{11, 0, 165}, + dictWord{7, 10, 1125}, + dictWord{9, 10, 143}, + dictWord{14, 10, 405}, + dictWord{150, 10, 21}, + dictWord{9, 0, 260}, + dictWord{137, 0, 905}, + dictWord{5, 11, 872}, + dictWord{6, 11, 57}, + dictWord{6, 11, 479}, + dictWord{ + 6, + 11, + 562, + }, + dictWord{7, 11, 471}, + dictWord{7, 11, 1060}, + dictWord{9, 11, 447}, + dictWord{9, 11, 454}, + dictWord{141, 11, 6}, + dictWord{138, 11, 704}, + dictWord{133, 0, 865}, + dictWord{5, 0, 914}, + dictWord{134, 0, 1625}, + dictWord{133, 0, 234}, + dictWord{7, 0, 1383}, + dictWord{5, 11, 31}, + dictWord{6, 11, 614}, + dictWord{145, 11, 61}, + dictWord{7, 11, 1200}, + dictWord{138, 11, 460}, + dictWord{6, 11, 424}, + dictWord{135, 11, 1866}, + dictWord{136, 0, 306}, + dictWord{ + 5, + 10, + 959, + }, + dictWord{12, 11, 30}, + dictWord{13, 11, 148}, + dictWord{14, 11, 87}, + dictWord{14, 11, 182}, + dictWord{16, 11, 42}, + dictWord{18, 11, 92}, + dictWord{ + 148, + 11, + 70, + }, + dictWord{6, 0, 1919}, + dictWord{6, 0, 1921}, + dictWord{9, 0, 923}, + dictWord{9, 0, 930}, + dictWord{9, 0, 941}, + dictWord{9, 0, 949}, + dictWord{9, 0, 987}, + dictWord{ + 9, + 0, + 988, + }, + dictWord{9, 0, 992}, + dictWord{12, 0, 802}, + dictWord{12, 0, 815}, + dictWord{12, 0, 856}, + dictWord{12, 0, 885}, + dictWord{12, 0, 893}, + dictWord{ + 12, + 0, + 898, + }, + dictWord{12, 0, 919}, + dictWord{12, 0, 920}, + dictWord{12, 0, 941}, + dictWord{12, 0, 947}, + dictWord{15, 0, 183}, + dictWord{15, 0, 185}, + dictWord{15, 0, 189}, + dictWord{15, 0, 197}, + dictWord{15, 0, 202}, + dictWord{15, 0, 233}, + dictWord{18, 0, 218}, + dictWord{18, 0, 219}, + dictWord{18, 0, 233}, + dictWord{143, 11, 156}, + dictWord{135, 10, 1759}, + dictWord{136, 10, 173}, + dictWord{13, 0, 163}, + dictWord{13, 0, 180}, + dictWord{18, 0, 78}, + dictWord{20, 0, 35}, + dictWord{5, 11, 13}, + dictWord{134, 11, 142}, + dictWord{134, 10, 266}, + dictWord{6, 11, 97}, + dictWord{7, 11, 116}, + dictWord{8, 11, 322}, + dictWord{8, 11, 755}, + dictWord{9, 11, 548}, + dictWord{10, 11, 714}, + dictWord{11, 11, 884}, + dictWord{141, 11, 324}, + dictWord{135, 0, 1312}, + dictWord{9, 0, 814}, + dictWord{137, 11, 676}, + dictWord{ + 133, + 0, + 707, + }, + dictWord{135, 0, 1493}, + dictWord{6, 0, 421}, + dictWord{7, 0, 61}, + dictWord{7, 0, 1540}, + dictWord{10, 0, 11}, + dictWord{138, 0, 501}, + dictWord{12, 0, 733}, + dictWord{12, 0, 766}, + dictWord{7, 11, 866}, + dictWord{135, 11, 1163}, + dictWord{137, 0, 341}, + dictWord{142, 0, 98}, + dictWord{145, 11, 115}, + dictWord{ + 135, + 11, + 1111, + }, + dictWord{136, 10, 300}, + dictWord{136, 0, 1014}, + dictWord{8, 11, 1}, + dictWord{9, 11, 112}, + dictWord{138, 11, 326}, + dictWord{132, 11, 730}, + dictWord{5, 11, 488}, + dictWord{6, 11, 527}, + dictWord{7, 11, 489}, + dictWord{7, 11, 1636}, + dictWord{8, 11, 121}, + dictWord{8, 11, 144}, + dictWord{8, 11, 359}, + dictWord{ + 9, + 11, + 193, + }, + dictWord{9, 11, 241}, + dictWord{9, 11, 336}, + dictWord{9, 11, 882}, + dictWord{11, 11, 266}, + dictWord{11, 11, 372}, + dictWord{11, 11, 944}, + dictWord{ + 12, + 11, + 401, + }, + dictWord{140, 11, 641}, + dictWord{6, 0, 971}, + dictWord{134, 0, 1121}, + dictWord{6, 0, 102}, + dictWord{7, 0, 72}, + dictWord{15, 0, 142}, + dictWord{ + 147, + 0, + 67, + }, + dictWord{151, 0, 30}, + dictWord{135, 0, 823}, + dictWord{134, 0, 1045}, + dictWord{5, 10, 427}, + dictWord{5, 10, 734}, + dictWord{7, 10, 478}, + dictWord{ + 136, + 10, + 52, + }, + dictWord{7, 0, 1930}, + dictWord{11, 10, 217}, + dictWord{142, 10, 165}, + dictWord{6, 0, 1512}, + dictWord{135, 0, 1870}, + dictWord{9, 11, 31}, + dictWord{ + 10, + 11, + 244, + }, + dictWord{10, 11, 699}, + dictWord{12, 11, 149}, + dictWord{141, 11, 497}, + dictWord{133, 11, 377}, + dictWord{145, 11, 101}, + dictWord{ + 10, + 11, + 158, + }, + dictWord{13, 11, 13}, + dictWord{13, 11, 137}, + dictWord{13, 11, 258}, + dictWord{14, 11, 111}, + dictWord{14, 11, 225}, + dictWord{14, 11, 253}, + dictWord{ + 14, + 11, + 304, + }, + dictWord{14, 11, 339}, + dictWord{14, 11, 417}, + dictWord{146, 11, 33}, + dictWord{6, 0, 87}, + dictWord{6, 10, 1734}, + dictWord{7, 10, 20}, + dictWord{ + 7, + 10, + 1056, + }, + dictWord{8, 10, 732}, + dictWord{9, 10, 406}, + dictWord{9, 10, 911}, + dictWord{138, 10, 694}, + dictWord{134, 0, 1243}, + dictWord{137, 0, 245}, + dictWord{ + 7, + 0, + 68, + }, + dictWord{8, 0, 48}, + dictWord{8, 0, 88}, + dictWord{8, 0, 582}, + dictWord{8, 0, 681}, + dictWord{9, 0, 373}, + dictWord{9, 0, 864}, + dictWord{11, 0, 157}, + dictWord{ + 11, + 0, + 336, + }, + dictWord{11, 0, 843}, + dictWord{148, 0, 27}, + dictWord{8, 11, 663}, + dictWord{144, 11, 8}, + dictWord{133, 10, 613}, + dictWord{4, 0, 88}, + dictWord{ + 5, + 0, + 137, + }, + dictWord{5, 0, 174}, + dictWord{5, 0, 777}, + dictWord{6, 0, 1664}, + dictWord{6, 0, 1725}, + dictWord{7, 0, 77}, + dictWord{7, 0, 426}, + dictWord{7, 0, 1317}, + dictWord{ + 7, + 0, + 1355, + }, + dictWord{8, 0, 126}, + dictWord{8, 0, 563}, + dictWord{9, 0, 523}, + dictWord{9, 0, 750}, + dictWord{10, 0, 310}, + dictWord{10, 0, 836}, + dictWord{11, 0, 42}, + dictWord{11, 0, 318}, + dictWord{11, 0, 731}, + dictWord{12, 0, 68}, + dictWord{12, 0, 92}, + dictWord{12, 0, 507}, + dictWord{12, 0, 692}, + dictWord{13, 0, 81}, + dictWord{ + 13, + 0, + 238, + }, + dictWord{13, 0, 374}, + dictWord{14, 0, 436}, + dictWord{18, 0, 138}, + dictWord{19, 0, 78}, + dictWord{19, 0, 111}, + dictWord{20, 0, 55}, + dictWord{20, 0, 77}, + dictWord{148, 0, 92}, + dictWord{141, 0, 418}, + dictWord{4, 0, 938}, + dictWord{137, 0, 625}, + dictWord{138, 0, 351}, + dictWord{5, 11, 843}, + dictWord{7, 10, 32}, + dictWord{ + 7, + 10, + 984, + }, + dictWord{8, 10, 85}, + dictWord{8, 10, 709}, + dictWord{9, 10, 579}, + dictWord{9, 10, 847}, + dictWord{9, 10, 856}, + dictWord{10, 10, 799}, + dictWord{ + 11, + 10, + 258, + }, + dictWord{11, 10, 1007}, + dictWord{12, 10, 331}, + dictWord{12, 10, 615}, + dictWord{13, 10, 188}, + dictWord{13, 10, 435}, + dictWord{14, 10, 8}, + dictWord{ + 15, + 10, + 165, + }, + dictWord{16, 10, 27}, + dictWord{148, 10, 40}, + dictWord{6, 0, 1668}, + dictWord{7, 0, 1499}, + dictWord{8, 0, 117}, + dictWord{9, 0, 314}, + dictWord{ + 138, + 0, + 174, + }, + dictWord{135, 0, 707}, + dictWord{132, 11, 554}, + dictWord{133, 11, 536}, + dictWord{5, 0, 403}, + dictWord{5, 11, 207}, + dictWord{9, 11, 79}, + dictWord{ + 11, + 11, + 625, + }, + dictWord{145, 11, 7}, + dictWord{132, 11, 424}, + dictWord{136, 11, 785}, + dictWord{4, 10, 167}, + dictWord{135, 10, 82}, + dictWord{9, 0, 7}, + dictWord{ + 23, + 0, + 6, + }, + dictWord{9, 11, 7}, + dictWord{151, 11, 6}, + dictWord{6, 0, 282}, + dictWord{5, 10, 62}, + dictWord{6, 10, 534}, + dictWord{7, 10, 74}, + dictWord{7, 10, 678}, + dictWord{ + 7, + 10, + 684, + }, + dictWord{7, 10, 1043}, + dictWord{7, 10, 1072}, + dictWord{8, 10, 280}, + dictWord{8, 10, 541}, + dictWord{8, 10, 686}, + dictWord{9, 10, 258}, + dictWord{ + 10, + 10, + 519, + }, + dictWord{11, 10, 252}, + dictWord{140, 10, 282}, + dictWord{138, 10, 33}, + dictWord{132, 10, 359}, + dictWord{4, 0, 44}, + dictWord{5, 0, 311}, + dictWord{ + 6, + 0, + 156, + }, + dictWord{7, 0, 639}, + dictWord{7, 0, 762}, + dictWord{7, 0, 1827}, + dictWord{9, 0, 8}, + dictWord{9, 0, 462}, + dictWord{148, 0, 83}, + dictWord{7, 11, 769}, + dictWord{ + 9, + 11, + 18, + }, + dictWord{138, 11, 358}, + dictWord{4, 0, 346}, + dictWord{7, 0, 115}, + dictWord{9, 0, 180}, + dictWord{9, 0, 456}, + dictWord{10, 0, 363}, + dictWord{ + 4, + 11, + 896, + }, + dictWord{134, 11, 1777}, + dictWord{133, 10, 211}, + dictWord{7, 0, 761}, + dictWord{7, 0, 1051}, + dictWord{137, 0, 545}, + dictWord{6, 10, 145}, + dictWord{ + 141, + 10, + 336, + }, + dictWord{7, 11, 750}, + dictWord{9, 11, 223}, + dictWord{11, 11, 27}, + dictWord{11, 11, 466}, + dictWord{12, 11, 624}, + dictWord{14, 11, 265}, + dictWord{146, 11, 61}, + dictWord{6, 0, 752}, + dictWord{6, 0, 768}, + dictWord{6, 0, 1195}, + dictWord{6, 0, 1254}, + dictWord{6, 0, 1619}, + dictWord{137, 0, 835}, + dictWord{ + 6, + 0, + 1936, + }, + dictWord{8, 0, 930}, + dictWord{136, 0, 960}, + dictWord{132, 10, 263}, + dictWord{132, 11, 249}, + dictWord{12, 0, 653}, + dictWord{132, 10, 916}, + dictWord{4, 11, 603}, + dictWord{133, 11, 661}, + dictWord{8, 0, 344}, + dictWord{4, 11, 11}, + dictWord{6, 11, 128}, + dictWord{7, 11, 231}, + dictWord{7, 11, 1533}, + dictWord{138, 11, 725}, + dictWord{134, 0, 1483}, + dictWord{134, 0, 875}, + dictWord{6, 0, 185}, + dictWord{7, 0, 1899}, + dictWord{9, 0, 875}, + dictWord{139, 0, 673}, + dictWord{15, 10, 155}, + dictWord{144, 10, 79}, + dictWord{7, 0, 93}, + dictWord{7, 0, 210}, + dictWord{7, 0, 1223}, + dictWord{8, 0, 451}, + dictWord{8, 0, 460}, + dictWord{ + 11, + 0, + 353, + }, + dictWord{11, 0, 475}, + dictWord{4, 10, 599}, + dictWord{6, 10, 1634}, + dictWord{7, 10, 67}, + dictWord{7, 10, 691}, + dictWord{7, 10, 979}, + dictWord{ + 7, + 10, + 1697, + }, + dictWord{8, 10, 207}, + dictWord{8, 10, 214}, + dictWord{8, 10, 231}, + dictWord{8, 10, 294}, + dictWord{8, 10, 336}, + dictWord{8, 10, 428}, + dictWord{ + 8, + 10, + 471, + }, + dictWord{8, 10, 622}, + dictWord{8, 10, 626}, + dictWord{8, 10, 679}, + dictWord{8, 10, 759}, + dictWord{8, 10, 829}, + dictWord{9, 10, 11}, + dictWord{9, 10, 246}, + dictWord{9, 10, 484}, + dictWord{9, 10, 573}, + dictWord{9, 10, 706}, + dictWord{9, 10, 762}, + dictWord{9, 10, 798}, + dictWord{9, 10, 855}, + dictWord{9, 10, 870}, + dictWord{ + 9, + 10, + 912, + }, + dictWord{10, 10, 303}, + dictWord{10, 10, 335}, + dictWord{10, 10, 424}, + dictWord{10, 10, 461}, + dictWord{10, 10, 543}, + dictWord{10, 10, 759}, + dictWord{10, 10, 814}, + dictWord{11, 10, 59}, + dictWord{11, 10, 235}, + dictWord{11, 10, 590}, + dictWord{11, 10, 929}, + dictWord{11, 10, 963}, + dictWord{ + 11, + 10, + 987, + }, + dictWord{12, 10, 114}, + dictWord{12, 10, 182}, + dictWord{12, 10, 226}, + dictWord{12, 10, 332}, + dictWord{12, 10, 439}, + dictWord{12, 10, 575}, + dictWord{ + 12, + 10, + 598, + }, + dictWord{12, 10, 675}, + dictWord{13, 10, 8}, + dictWord{13, 10, 125}, + dictWord{13, 10, 194}, + dictWord{13, 10, 287}, + dictWord{14, 10, 197}, + dictWord{14, 10, 383}, + dictWord{15, 10, 53}, + dictWord{17, 10, 63}, + dictWord{19, 10, 46}, + dictWord{19, 10, 98}, + dictWord{19, 10, 106}, + dictWord{148, 10, 85}, + dictWord{132, 11, 476}, + dictWord{4, 0, 327}, + dictWord{5, 0, 478}, + dictWord{7, 0, 1332}, + dictWord{136, 0, 753}, + dictWord{5, 0, 1020}, + dictWord{133, 0, 1022}, + dictWord{135, 11, 1807}, + dictWord{4, 0, 103}, + dictWord{133, 0, 401}, + dictWord{4, 0, 499}, + dictWord{135, 0, 1421}, + dictWord{10, 0, 207}, + dictWord{13, 0, 164}, + dictWord{147, 10, 126}, + dictWord{9, 11, 20}, + dictWord{10, 11, 324}, + dictWord{139, 11, 488}, + dictWord{132, 0, 96}, + dictWord{9, 11, 280}, + dictWord{ + 138, + 11, + 134, + }, + dictWord{135, 0, 968}, + dictWord{133, 10, 187}, + dictWord{135, 10, 1286}, + dictWord{5, 11, 112}, + dictWord{6, 11, 103}, + dictWord{134, 11, 150}, + dictWord{8, 0, 914}, + dictWord{10, 0, 3}, + dictWord{4, 10, 215}, + dictWord{9, 10, 38}, + dictWord{11, 10, 23}, + dictWord{11, 10, 127}, + dictWord{139, 10, 796}, + dictWord{ + 135, + 0, + 399, + }, + dictWord{6, 0, 563}, + dictWord{137, 0, 224}, + dictWord{6, 0, 704}, + dictWord{134, 0, 1214}, + dictWord{4, 11, 708}, + dictWord{8, 11, 15}, + dictWord{ + 9, + 11, + 50, + }, + dictWord{9, 11, 386}, + dictWord{11, 11, 18}, + dictWord{11, 11, 529}, + dictWord{140, 11, 228}, + dictWord{4, 11, 563}, + dictWord{7, 11, 109}, + dictWord{ + 7, + 11, + 592, + }, + dictWord{7, 11, 637}, + dictWord{7, 11, 770}, + dictWord{7, 11, 1701}, + dictWord{8, 11, 436}, + dictWord{8, 11, 463}, + dictWord{9, 11, 60}, + dictWord{9, 11, 335}, + dictWord{9, 11, 904}, + dictWord{10, 11, 73}, + dictWord{11, 11, 434}, + dictWord{12, 11, 585}, + dictWord{13, 11, 331}, + dictWord{18, 11, 110}, + dictWord{ + 148, + 11, + 60, + }, + dictWord{134, 0, 1559}, + dictWord{132, 11, 502}, + dictWord{6, 11, 347}, + dictWord{138, 11, 161}, + dictWord{4, 11, 33}, + dictWord{5, 11, 102}, + dictWord{ + 5, + 11, + 500, + }, + dictWord{6, 11, 284}, + dictWord{7, 11, 1079}, + dictWord{7, 11, 1423}, + dictWord{7, 11, 1702}, + dictWord{8, 11, 470}, + dictWord{9, 11, 554}, + dictWord{ + 9, + 11, + 723, + }, + dictWord{139, 11, 333}, + dictWord{7, 11, 246}, + dictWord{135, 11, 840}, + dictWord{6, 11, 10}, + dictWord{8, 11, 571}, + dictWord{9, 11, 739}, + dictWord{ + 143, + 11, + 91, + }, + dictWord{8, 0, 861}, + dictWord{10, 0, 905}, + dictWord{12, 0, 730}, + dictWord{12, 0, 789}, + dictWord{133, 11, 626}, + dictWord{134, 0, 946}, + dictWord{ + 5, + 0, + 746, + }, + dictWord{12, 0, 333}, + dictWord{14, 0, 332}, + dictWord{12, 11, 333}, + dictWord{142, 11, 332}, + dictWord{5, 11, 18}, + dictWord{6, 11, 526}, + dictWord{ + 13, + 11, + 24, + }, + dictWord{13, 11, 110}, + dictWord{19, 11, 5}, + dictWord{147, 11, 44}, + dictWord{4, 0, 910}, + dictWord{5, 0, 832}, + dictWord{135, 10, 2002}, + dictWord{ + 10, + 11, + 768, + }, + dictWord{139, 11, 787}, + dictWord{4, 11, 309}, + dictWord{5, 11, 462}, + dictWord{7, 11, 970}, + dictWord{135, 11, 1097}, + dictWord{4, 10, 28}, + dictWord{ + 5, + 10, + 440, + }, + dictWord{7, 10, 248}, + dictWord{11, 10, 833}, + dictWord{140, 10, 344}, + dictWord{134, 10, 1654}, + dictWord{6, 0, 632}, + dictWord{6, 0, 652}, + dictWord{ + 6, + 0, + 1272, + }, + dictWord{6, 0, 1384}, + dictWord{134, 0, 1560}, + dictWord{134, 11, 1704}, + dictWord{6, 0, 1393}, + dictWord{133, 10, 853}, + dictWord{6, 10, 249}, + dictWord{7, 10, 1234}, + dictWord{139, 10, 573}, + dictWord{5, 11, 86}, + dictWord{7, 11, 743}, + dictWord{9, 11, 85}, + dictWord{10, 11, 281}, + dictWord{10, 11, 432}, + dictWord{11, 11, 490}, + dictWord{12, 11, 251}, + dictWord{13, 11, 118}, + dictWord{14, 11, 378}, + dictWord{146, 11, 143}, + dictWord{5, 11, 524}, + dictWord{ + 133, + 11, + 744, + }, + dictWord{134, 0, 1514}, + dictWord{10, 0, 201}, + dictWord{142, 0, 319}, + dictWord{7, 0, 717}, + dictWord{10, 0, 510}, + dictWord{7, 10, 392}, + dictWord{ + 8, + 10, + 20, + }, + dictWord{8, 10, 172}, + dictWord{8, 10, 690}, + dictWord{9, 10, 383}, + dictWord{9, 10, 845}, + dictWord{11, 10, 293}, + dictWord{11, 10, 832}, + dictWord{ + 11, + 10, + 920, + }, + dictWord{11, 10, 984}, + dictWord{141, 10, 221}, + dictWord{134, 0, 1381}, + dictWord{5, 10, 858}, + dictWord{133, 10, 992}, + dictWord{8, 0, 528}, + dictWord{137, 0, 348}, + dictWord{10, 11, 107}, + dictWord{140, 11, 436}, + dictWord{4, 0, 20}, + dictWord{133, 0, 616}, + dictWord{134, 0, 1251}, + dictWord{ + 132, + 11, + 927, + }, + dictWord{10, 11, 123}, + dictWord{12, 11, 670}, + dictWord{13, 11, 371}, + dictWord{14, 11, 142}, + dictWord{146, 11, 94}, + dictWord{134, 0, 1163}, + dictWord{ + 7, + 11, + 1149, + }, + dictWord{137, 11, 156}, + dictWord{134, 0, 307}, + dictWord{133, 11, 778}, + dictWord{7, 0, 1091}, + dictWord{135, 0, 1765}, + dictWord{ + 5, + 11, + 502, + }, + dictWord{6, 10, 268}, + dictWord{137, 10, 62}, + dictWord{8, 11, 196}, + dictWord{10, 11, 283}, + dictWord{139, 11, 406}, + dictWord{4, 0, 26}, + dictWord{ + 5, + 0, + 429, + }, + dictWord{6, 0, 245}, + dictWord{7, 0, 704}, + dictWord{7, 0, 1379}, + dictWord{135, 0, 1474}, + dictWord{133, 11, 855}, + dictWord{132, 0, 881}, + dictWord{ + 4, + 0, + 621, + }, + dictWord{135, 11, 1596}, + dictWord{7, 11, 1400}, + dictWord{9, 11, 446}, + dictWord{138, 11, 45}, + dictWord{6, 0, 736}, + dictWord{138, 10, 106}, + dictWord{133, 0, 542}, + dictWord{134, 0, 348}, + dictWord{133, 0, 868}, + dictWord{136, 0, 433}, + dictWord{135, 0, 1495}, + dictWord{138, 0, 771}, + dictWord{ + 6, + 10, + 613, + }, + dictWord{136, 10, 223}, + dictWord{138, 0, 215}, + dictWord{141, 0, 124}, + dictWord{136, 11, 391}, + dictWord{135, 11, 172}, + dictWord{132, 10, 670}, + dictWord{140, 0, 55}, + dictWord{9, 10, 40}, + dictWord{139, 10, 136}, + dictWord{7, 0, 62}, + dictWord{147, 0, 112}, + dictWord{132, 0, 856}, + dictWord{132, 11, 568}, + dictWord{12, 0, 270}, + dictWord{139, 10, 259}, + dictWord{8, 0, 572}, + dictWord{137, 0, 698}, + dictWord{4, 11, 732}, + dictWord{9, 10, 310}, + dictWord{137, 10, 682}, + dictWord{142, 10, 296}, + dictWord{134, 0, 939}, + dictWord{136, 11, 733}, + dictWord{135, 11, 1435}, + dictWord{7, 10, 1401}, + dictWord{135, 10, 1476}, + dictWord{6, 0, 352}, + dictWord{4, 10, 296}, + dictWord{7, 10, 401}, + dictWord{7, 10, 1410}, + dictWord{7, 10, 1594}, + dictWord{7, 10, 1674}, + dictWord{8, 10, 63}, + dictWord{ + 8, + 10, + 660, + }, + dictWord{137, 10, 74}, + dictWord{4, 11, 428}, + dictWord{133, 11, 668}, + dictWord{4, 10, 139}, + dictWord{4, 10, 388}, + dictWord{140, 10, 188}, + dictWord{7, 11, 2015}, + dictWord{140, 11, 665}, + dictWord{132, 0, 647}, + dictWord{146, 0, 10}, + dictWord{138, 0, 220}, + dictWord{142, 0, 464}, + dictWord{ + 132, + 0, + 109, + }, + dictWord{134, 0, 1746}, + dictWord{6, 0, 515}, + dictWord{4, 10, 747}, + dictWord{6, 11, 1623}, + dictWord{6, 11, 1681}, + dictWord{7, 10, 649}, + dictWord{ + 7, + 10, + 1479, + }, + dictWord{135, 10, 1583}, + dictWord{133, 10, 232}, + dictWord{135, 0, 566}, + dictWord{137, 10, 887}, + dictWord{4, 0, 40}, + dictWord{10, 0, 67}, + dictWord{ + 11, + 0, + 117, + }, + dictWord{11, 0, 768}, + dictWord{139, 0, 935}, + dictWord{132, 0, 801}, + dictWord{7, 0, 992}, + dictWord{8, 0, 301}, + dictWord{9, 0, 722}, + dictWord{ + 12, + 0, + 63, + }, + dictWord{13, 0, 29}, + dictWord{14, 0, 161}, + dictWord{143, 0, 18}, + dictWord{139, 0, 923}, + dictWord{6, 11, 1748}, + dictWord{8, 11, 715}, + dictWord{9, 11, 802}, + dictWord{10, 11, 46}, + dictWord{10, 11, 819}, + dictWord{13, 11, 308}, + dictWord{14, 11, 351}, + dictWord{14, 11, 363}, + dictWord{146, 11, 67}, + dictWord{ + 137, + 11, + 745, + }, + dictWord{7, 0, 1145}, + dictWord{4, 10, 14}, + dictWord{7, 10, 1801}, + dictWord{10, 10, 748}, + dictWord{141, 10, 458}, + dictWord{4, 11, 63}, + dictWord{ + 5, + 11, + 347, + }, + dictWord{134, 11, 474}, + dictWord{135, 0, 568}, + dictWord{4, 10, 425}, + dictWord{7, 11, 577}, + dictWord{7, 11, 1432}, + dictWord{9, 11, 475}, + dictWord{ + 9, + 11, + 505, + }, + dictWord{9, 11, 526}, + dictWord{9, 11, 609}, + dictWord{9, 11, 689}, + dictWord{9, 11, 726}, + dictWord{9, 11, 735}, + dictWord{9, 11, 738}, + dictWord{ + 10, + 11, + 556, + }, + dictWord{10, 11, 674}, + dictWord{10, 11, 684}, + dictWord{11, 11, 89}, + dictWord{11, 11, 202}, + dictWord{11, 11, 272}, + dictWord{11, 11, 380}, + dictWord{ + 11, + 11, + 415, + }, + dictWord{11, 11, 505}, + dictWord{11, 11, 537}, + dictWord{11, 11, 550}, + dictWord{11, 11, 562}, + dictWord{11, 11, 640}, + dictWord{11, 11, 667}, + dictWord{11, 11, 688}, + dictWord{11, 11, 847}, + dictWord{11, 11, 927}, + dictWord{11, 11, 930}, + dictWord{11, 11, 940}, + dictWord{12, 11, 144}, + dictWord{ + 12, + 11, + 325, + }, + dictWord{12, 11, 329}, + dictWord{12, 11, 389}, + dictWord{12, 11, 403}, + dictWord{12, 11, 451}, + dictWord{12, 11, 515}, + dictWord{12, 11, 604}, + dictWord{ + 12, + 11, + 616, + }, + dictWord{12, 11, 626}, + dictWord{13, 11, 66}, + dictWord{13, 11, 131}, + dictWord{13, 11, 167}, + dictWord{13, 11, 236}, + dictWord{13, 11, 368}, + dictWord{13, 11, 411}, + dictWord{13, 11, 434}, + dictWord{13, 11, 453}, + dictWord{13, 11, 461}, + dictWord{13, 11, 474}, + dictWord{14, 11, 59}, + dictWord{14, 11, 60}, + dictWord{14, 11, 139}, + dictWord{14, 11, 152}, + dictWord{14, 11, 276}, + dictWord{14, 11, 353}, + dictWord{14, 11, 402}, + dictWord{15, 11, 28}, + dictWord{ + 15, + 11, + 81, + }, + dictWord{15, 11, 123}, + dictWord{15, 11, 152}, + dictWord{18, 11, 136}, + dictWord{148, 11, 88}, + dictWord{137, 0, 247}, + dictWord{135, 11, 1622}, + dictWord{ + 9, + 11, + 544, + }, + dictWord{11, 11, 413}, + dictWord{144, 11, 25}, + dictWord{4, 0, 645}, + dictWord{7, 0, 825}, + dictWord{6, 10, 1768}, + dictWord{135, 11, 89}, + dictWord{140, 0, 328}, + dictWord{5, 10, 943}, + dictWord{134, 10, 1779}, + dictWord{134, 0, 1363}, + dictWord{5, 10, 245}, + dictWord{6, 10, 576}, + dictWord{7, 10, 582}, + dictWord{136, 10, 225}, + dictWord{134, 0, 1280}, + dictWord{5, 11, 824}, + dictWord{133, 11, 941}, + dictWord{7, 11, 440}, + dictWord{8, 11, 230}, + dictWord{ + 139, + 11, + 106, + }, + dictWord{5, 0, 28}, + dictWord{6, 0, 204}, + dictWord{10, 0, 320}, + dictWord{10, 0, 583}, + dictWord{13, 0, 502}, + dictWord{14, 0, 72}, + dictWord{14, 0, 274}, + dictWord{14, 0, 312}, + dictWord{14, 0, 344}, + dictWord{15, 0, 159}, + dictWord{16, 0, 62}, + dictWord{16, 0, 69}, + dictWord{17, 0, 30}, + dictWord{18, 0, 42}, + dictWord{ + 18, + 0, + 53, + }, + dictWord{18, 0, 84}, + dictWord{18, 0, 140}, + dictWord{19, 0, 68}, + dictWord{19, 0, 85}, + dictWord{20, 0, 5}, + dictWord{20, 0, 45}, + dictWord{20, 0, 101}, + dictWord{ + 22, + 0, + 7, + }, + dictWord{150, 0, 20}, + dictWord{4, 0, 558}, + dictWord{6, 0, 390}, + dictWord{7, 0, 162}, + dictWord{7, 0, 689}, + dictWord{9, 0, 360}, + dictWord{138, 0, 653}, + dictWord{134, 0, 764}, + dictWord{6, 0, 862}, + dictWord{137, 0, 833}, + dictWord{5, 0, 856}, + dictWord{6, 0, 1672}, + dictWord{6, 0, 1757}, + dictWord{134, 0, 1781}, + dictWord{ + 5, + 0, + 92, + }, + dictWord{10, 0, 736}, + dictWord{140, 0, 102}, + dictWord{6, 0, 1927}, + dictWord{6, 0, 1944}, + dictWord{8, 0, 924}, + dictWord{8, 0, 948}, + dictWord{ + 10, + 0, + 967, + }, + dictWord{138, 0, 978}, + dictWord{134, 0, 1479}, + dictWord{5, 0, 590}, + dictWord{8, 0, 360}, + dictWord{9, 0, 213}, + dictWord{138, 0, 63}, + dictWord{ + 134, + 0, + 1521, + }, + dictWord{6, 0, 709}, + dictWord{134, 0, 891}, + dictWord{132, 10, 443}, + dictWord{13, 0, 477}, + dictWord{14, 0, 120}, + dictWord{148, 0, 61}, + dictWord{ + 4, + 11, + 914, + }, + dictWord{5, 11, 800}, + dictWord{133, 11, 852}, + dictWord{10, 11, 54}, + dictWord{141, 11, 115}, + dictWord{4, 11, 918}, + dictWord{133, 11, 876}, + dictWord{139, 11, 152}, + dictWord{4, 11, 92}, + dictWord{133, 11, 274}, + dictWord{135, 11, 1901}, + dictWord{9, 11, 800}, + dictWord{10, 11, 693}, + dictWord{ + 11, + 11, + 482, + }, + dictWord{11, 11, 734}, + dictWord{139, 11, 789}, + dictWord{9, 0, 483}, + dictWord{132, 10, 298}, + dictWord{6, 0, 1213}, + dictWord{141, 11, 498}, + dictWord{135, 11, 1451}, + dictWord{133, 11, 743}, + dictWord{4, 0, 1022}, + dictWord{10, 0, 1000}, + dictWord{12, 0, 957}, + dictWord{12, 0, 980}, + dictWord{ + 12, + 0, + 1013, + }, + dictWord{14, 0, 481}, + dictWord{144, 0, 116}, + dictWord{8, 0, 503}, + dictWord{17, 0, 29}, + dictWord{4, 11, 49}, + dictWord{7, 11, 280}, + dictWord{ + 135, + 11, + 1633, + }, + dictWord{135, 0, 1712}, + dictWord{134, 0, 466}, + dictWord{136, 11, 47}, + dictWord{5, 10, 164}, + dictWord{7, 10, 121}, + dictWord{142, 10, 189}, + dictWord{ + 7, + 10, + 812, + }, + dictWord{7, 10, 1261}, + dictWord{7, 10, 1360}, + dictWord{9, 10, 632}, + dictWord{140, 10, 352}, + dictWord{139, 10, 556}, + dictWord{132, 0, 731}, + dictWord{5, 11, 272}, + dictWord{5, 11, 908}, + dictWord{5, 11, 942}, + dictWord{7, 11, 1008}, + dictWord{7, 11, 1560}, + dictWord{8, 11, 197}, + dictWord{9, 11, 47}, + dictWord{11, 11, 538}, + dictWord{139, 11, 742}, + dictWord{4, 10, 172}, + dictWord{9, 10, 611}, + dictWord{10, 10, 436}, + dictWord{12, 10, 673}, + dictWord{ + 141, + 10, + 255, + }, + dictWord{133, 10, 844}, + dictWord{10, 0, 484}, + dictWord{11, 0, 754}, + dictWord{12, 0, 457}, + dictWord{14, 0, 171}, + dictWord{14, 0, 389}, + dictWord{ + 146, + 0, + 153, + }, + dictWord{9, 10, 263}, + dictWord{10, 10, 147}, + dictWord{138, 10, 492}, + dictWord{137, 11, 891}, + dictWord{138, 0, 241}, + dictWord{133, 10, 537}, + dictWord{6, 0, 2005}, + dictWord{136, 0, 964}, + dictWord{137, 10, 842}, + dictWord{151, 11, 8}, + dictWord{4, 11, 407}, + dictWord{132, 11, 560}, + dictWord{ + 135, + 11, + 1884, + }, + dictWord{6, 0, 1100}, + dictWord{134, 0, 1242}, + dictWord{135, 0, 954}, + dictWord{5, 10, 230}, + dictWord{5, 10, 392}, + dictWord{6, 10, 420}, + dictWord{ + 9, + 10, + 568, + }, + dictWord{140, 10, 612}, + dictWord{4, 11, 475}, + dictWord{11, 11, 35}, + dictWord{11, 11, 90}, + dictWord{13, 11, 7}, + dictWord{13, 11, 71}, + dictWord{ + 13, + 11, + 177, + }, + dictWord{142, 11, 422}, + dictWord{136, 11, 332}, + dictWord{135, 0, 1958}, + dictWord{6, 0, 549}, + dictWord{8, 0, 34}, + dictWord{8, 0, 283}, + dictWord{ + 9, + 0, + 165, + }, + dictWord{138, 0, 475}, + dictWord{10, 0, 952}, + dictWord{12, 0, 966}, + dictWord{140, 0, 994}, + dictWord{5, 0, 652}, + dictWord{5, 0, 701}, + dictWord{ + 135, + 0, + 449, + }, + dictWord{4, 0, 655}, + dictWord{7, 0, 850}, + dictWord{17, 0, 75}, + dictWord{146, 0, 137}, + dictWord{4, 0, 146}, + dictWord{7, 0, 1618}, + dictWord{8, 0, 670}, + dictWord{ + 5, + 10, + 41, + }, + dictWord{7, 10, 1459}, + dictWord{7, 10, 1469}, + dictWord{7, 10, 1859}, + dictWord{9, 10, 549}, + dictWord{139, 10, 905}, + dictWord{133, 10, 696}, + dictWord{6, 0, 159}, + dictWord{6, 0, 364}, + dictWord{7, 0, 516}, + dictWord{137, 0, 518}, + dictWord{135, 0, 1439}, + dictWord{6, 11, 222}, + dictWord{7, 11, 636}, + dictWord{ + 7, + 11, + 1620, + }, + dictWord{8, 11, 409}, + dictWord{9, 11, 693}, + dictWord{139, 11, 77}, + dictWord{13, 0, 151}, + dictWord{141, 11, 45}, + dictWord{6, 0, 1027}, + dictWord{ + 4, + 11, + 336, + }, + dictWord{132, 10, 771}, + dictWord{139, 11, 392}, + dictWord{10, 11, 121}, + dictWord{11, 11, 175}, + dictWord{149, 11, 16}, + dictWord{8, 0, 950}, + dictWord{138, 0, 983}, + dictWord{133, 10, 921}, + dictWord{135, 0, 993}, + dictWord{6, 10, 180}, + dictWord{7, 10, 1137}, + dictWord{8, 10, 751}, + dictWord{ + 139, + 10, + 805, + }, + dictWord{7, 0, 501}, + dictWord{9, 0, 111}, + dictWord{10, 0, 141}, + dictWord{11, 0, 332}, + dictWord{13, 0, 43}, + dictWord{13, 0, 429}, + dictWord{14, 0, 130}, + dictWord{14, 0, 415}, + dictWord{145, 0, 102}, + dictWord{4, 10, 183}, + dictWord{5, 11, 882}, + dictWord{7, 10, 271}, + dictWord{11, 10, 824}, + dictWord{11, 10, 952}, + dictWord{13, 10, 278}, + dictWord{13, 10, 339}, + dictWord{13, 10, 482}, + dictWord{14, 10, 424}, + dictWord{148, 10, 99}, + dictWord{4, 10, 19}, + dictWord{5, 10, 477}, + dictWord{5, 10, 596}, + dictWord{6, 10, 505}, + dictWord{7, 10, 1221}, + dictWord{11, 10, 907}, + dictWord{12, 10, 209}, + dictWord{141, 10, 214}, + dictWord{ + 135, + 10, + 1215, + }, + dictWord{133, 0, 452}, + dictWord{132, 11, 426}, + dictWord{5, 0, 149}, + dictWord{136, 0, 233}, + dictWord{133, 0, 935}, + dictWord{6, 11, 58}, + dictWord{ + 7, + 11, + 654, + }, + dictWord{7, 11, 745}, + dictWord{7, 11, 1969}, + dictWord{8, 11, 240}, + dictWord{8, 11, 675}, + dictWord{9, 11, 479}, + dictWord{9, 11, 731}, + dictWord{ + 10, + 11, + 330, + }, + dictWord{10, 11, 593}, + dictWord{10, 11, 817}, + dictWord{11, 11, 32}, + dictWord{11, 11, 133}, + dictWord{11, 11, 221}, + dictWord{145, 11, 68}, + dictWord{ + 12, + 0, + 582, + }, + dictWord{18, 0, 131}, + dictWord{7, 11, 102}, + dictWord{137, 11, 538}, + dictWord{136, 0, 801}, + dictWord{134, 10, 1645}, + dictWord{132, 0, 70}, + dictWord{6, 10, 92}, + dictWord{6, 10, 188}, + dictWord{7, 10, 1269}, + dictWord{7, 10, 1524}, + dictWord{7, 10, 1876}, + dictWord{10, 10, 228}, + dictWord{139, 10, 1020}, + dictWord{4, 10, 459}, + dictWord{133, 10, 966}, + dictWord{138, 0, 369}, + dictWord{16, 0, 36}, + dictWord{140, 10, 330}, + dictWord{141, 11, 366}, + dictWord{ + 7, + 0, + 721, + }, + dictWord{10, 0, 236}, + dictWord{12, 0, 204}, + dictWord{6, 10, 18}, + dictWord{7, 10, 932}, + dictWord{8, 10, 757}, + dictWord{9, 10, 54}, + dictWord{9, 10, 65}, + dictWord{9, 10, 844}, + dictWord{10, 10, 113}, + dictWord{10, 10, 315}, + dictWord{10, 10, 798}, + dictWord{11, 10, 153}, + dictWord{12, 10, 151}, + dictWord{12, 10, 392}, + dictWord{12, 10, 666}, + dictWord{142, 10, 248}, + dictWord{7, 0, 241}, + dictWord{10, 0, 430}, + dictWord{8, 10, 548}, + dictWord{9, 10, 532}, + dictWord{10, 10, 117}, + dictWord{11, 10, 351}, + dictWord{11, 10, 375}, + dictWord{143, 10, 23}, + dictWord{134, 10, 1742}, + dictWord{133, 10, 965}, + dictWord{133, 11, 566}, + dictWord{ + 6, + 11, + 48, + }, + dictWord{135, 11, 63}, + dictWord{134, 10, 182}, + dictWord{10, 10, 65}, + dictWord{10, 10, 488}, + dictWord{138, 10, 497}, + dictWord{6, 11, 114}, + dictWord{7, 11, 1224}, + dictWord{7, 11, 1556}, + dictWord{136, 11, 3}, + dictWord{134, 0, 1817}, + dictWord{8, 11, 576}, + dictWord{137, 11, 267}, + dictWord{ + 6, + 0, + 1078, + }, + dictWord{144, 0, 16}, + dictWord{9, 10, 588}, + dictWord{138, 10, 260}, + dictWord{138, 0, 1021}, + dictWord{5, 0, 406}, + dictWord{134, 0, 2022}, + dictWord{133, 11, 933}, + dictWord{6, 0, 69}, + dictWord{135, 0, 117}, + dictWord{7, 0, 1830}, + dictWord{136, 11, 427}, + dictWord{4, 0, 432}, + dictWord{135, 0, 824}, + dictWord{134, 10, 1786}, + dictWord{133, 0, 826}, + dictWord{139, 11, 67}, + dictWord{133, 11, 759}, + dictWord{135, 10, 308}, + dictWord{137, 0, 816}, + dictWord{ + 133, + 0, + 1000, + }, + dictWord{4, 0, 297}, + dictWord{6, 0, 529}, + dictWord{7, 0, 152}, + dictWord{7, 0, 713}, + dictWord{7, 0, 1845}, + dictWord{8, 0, 710}, + dictWord{8, 0, 717}, + dictWord{12, 0, 639}, + dictWord{140, 0, 685}, + dictWord{7, 0, 423}, + dictWord{136, 10, 588}, + dictWord{136, 10, 287}, + dictWord{136, 0, 510}, + dictWord{ + 134, + 0, + 1048, + }, + dictWord{6, 0, 618}, + dictWord{7, 11, 56}, + dictWord{7, 11, 1989}, + dictWord{8, 11, 337}, + dictWord{8, 11, 738}, + dictWord{9, 11, 600}, + dictWord{ + 10, + 11, + 483, + }, + dictWord{12, 11, 37}, + dictWord{13, 11, 447}, + dictWord{142, 11, 92}, + dictWord{4, 0, 520}, + dictWord{135, 0, 575}, + dictWord{8, 0, 990}, + dictWord{ + 138, + 0, + 977, + }, + dictWord{135, 11, 774}, + dictWord{9, 11, 347}, + dictWord{11, 11, 24}, + dictWord{140, 11, 170}, + dictWord{136, 11, 379}, + dictWord{140, 10, 290}, + dictWord{132, 11, 328}, + dictWord{4, 0, 321}, + dictWord{134, 0, 569}, + dictWord{4, 11, 101}, + dictWord{135, 11, 1171}, + dictWord{7, 0, 723}, + dictWord{7, 0, 1135}, + dictWord{5, 11, 833}, + dictWord{136, 11, 744}, + dictWord{7, 10, 719}, + dictWord{8, 10, 809}, + dictWord{136, 10, 834}, + dictWord{8, 0, 921}, + dictWord{136, 10, 796}, + dictWord{5, 10, 210}, + dictWord{6, 10, 213}, + dictWord{7, 10, 60}, + dictWord{10, 10, 364}, + dictWord{139, 10, 135}, + dictWord{5, 0, 397}, + dictWord{6, 0, 154}, + dictWord{7, 0, 676}, + dictWord{8, 0, 443}, + dictWord{8, 0, 609}, + dictWord{9, 0, 24}, + dictWord{9, 0, 325}, + dictWord{10, 0, 35}, + dictWord{11, 0, 535}, + dictWord{11, 0, 672}, + dictWord{11, 0, 1018}, + dictWord{12, 0, 637}, + dictWord{16, 0, 30}, + dictWord{5, 10, 607}, + dictWord{8, 10, 326}, + dictWord{136, 10, 490}, + dictWord{4, 10, 701}, + dictWord{5, 10, 472}, + dictWord{6, 11, 9}, + dictWord{6, 11, 397}, + dictWord{7, 11, 53}, + dictWord{7, 11, 1742}, + dictWord{9, 10, 758}, + dictWord{10, 11, 632}, + dictWord{ + 11, + 11, + 828, + }, + dictWord{140, 11, 146}, + dictWord{135, 10, 380}, + dictWord{135, 10, 1947}, + dictWord{148, 11, 109}, + dictWord{10, 10, 278}, + dictWord{ + 138, + 11, + 278, + }, + dictWord{134, 0, 856}, + dictWord{7, 0, 139}, + dictWord{4, 10, 386}, + dictWord{8, 10, 405}, + dictWord{8, 10, 728}, + dictWord{9, 10, 497}, + dictWord{ + 11, + 10, + 110, + }, + dictWord{11, 10, 360}, + dictWord{15, 10, 37}, + dictWord{144, 10, 84}, + dictWord{141, 0, 282}, + dictWord{133, 0, 981}, + dictWord{5, 0, 288}, + dictWord{ + 7, + 10, + 1452, + }, + dictWord{7, 10, 1480}, + dictWord{8, 10, 634}, + dictWord{140, 10, 472}, + dictWord{7, 0, 1890}, + dictWord{8, 11, 367}, + dictWord{10, 11, 760}, + dictWord{ + 14, + 11, + 79, + }, + dictWord{20, 11, 17}, + dictWord{152, 11, 0}, + dictWord{4, 10, 524}, + dictWord{136, 10, 810}, + dictWord{4, 0, 56}, + dictWord{7, 0, 1791}, + dictWord{ + 8, + 0, + 607, + }, + dictWord{8, 0, 651}, + dictWord{11, 0, 465}, + dictWord{11, 0, 835}, + dictWord{12, 0, 337}, + dictWord{141, 0, 480}, + dictWord{10, 10, 238}, + dictWord{ + 141, + 10, + 33, + }, + dictWord{11, 11, 417}, + dictWord{12, 11, 223}, + dictWord{140, 11, 265}, + dictWord{9, 0, 158}, + dictWord{10, 0, 411}, + dictWord{140, 0, 261}, + dictWord{ + 133, + 10, + 532, + }, + dictWord{133, 10, 997}, + dictWord{12, 11, 186}, + dictWord{12, 11, 292}, + dictWord{14, 11, 100}, + dictWord{146, 11, 70}, + dictWord{6, 0, 1403}, + dictWord{136, 0, 617}, + dictWord{134, 0, 1205}, + dictWord{139, 0, 563}, + dictWord{4, 0, 242}, + dictWord{134, 0, 333}, + dictWord{4, 11, 186}, + dictWord{5, 11, 157}, + dictWord{8, 11, 168}, + dictWord{138, 11, 6}, + dictWord{132, 0, 369}, + dictWord{133, 11, 875}, + dictWord{5, 10, 782}, + dictWord{5, 10, 829}, + dictWord{ + 134, + 10, + 1738, + }, + dictWord{134, 0, 622}, + dictWord{135, 11, 1272}, + dictWord{6, 0, 1407}, + dictWord{7, 11, 111}, + dictWord{136, 11, 581}, + dictWord{7, 10, 1823}, + dictWord{139, 10, 693}, + dictWord{7, 0, 160}, + dictWord{10, 0, 624}, + dictWord{142, 0, 279}, + dictWord{132, 0, 363}, + dictWord{10, 11, 589}, + dictWord{12, 11, 111}, + dictWord{13, 11, 260}, + dictWord{14, 11, 82}, + dictWord{18, 11, 63}, + dictWord{147, 11, 45}, + dictWord{7, 11, 1364}, + dictWord{7, 11, 1907}, + dictWord{ + 141, + 11, + 158, + }, + dictWord{4, 11, 404}, + dictWord{4, 11, 659}, + dictWord{135, 11, 675}, + dictWord{13, 11, 211}, + dictWord{14, 11, 133}, + dictWord{14, 11, 204}, + dictWord{ + 15, + 11, + 64, + }, + dictWord{15, 11, 69}, + dictWord{15, 11, 114}, + dictWord{16, 11, 10}, + dictWord{19, 11, 23}, + dictWord{19, 11, 35}, + dictWord{19, 11, 39}, + dictWord{ + 19, + 11, + 51, + }, + dictWord{19, 11, 71}, + dictWord{19, 11, 75}, + dictWord{152, 11, 15}, + dictWord{4, 10, 78}, + dictWord{5, 10, 96}, + dictWord{5, 10, 182}, + dictWord{7, 10, 1724}, + dictWord{7, 10, 1825}, + dictWord{10, 10, 394}, + dictWord{10, 10, 471}, + dictWord{11, 10, 532}, + dictWord{14, 10, 340}, + dictWord{145, 10, 88}, + dictWord{ + 135, + 10, + 1964, + }, + dictWord{133, 11, 391}, + dictWord{11, 11, 887}, + dictWord{14, 11, 365}, + dictWord{142, 11, 375}, + dictWord{5, 11, 540}, + dictWord{6, 11, 1697}, + dictWord{7, 11, 222}, + dictWord{136, 11, 341}, + dictWord{134, 11, 78}, + dictWord{9, 0, 601}, + dictWord{9, 0, 619}, + dictWord{10, 0, 505}, + dictWord{10, 0, 732}, + dictWord{11, 0, 355}, + dictWord{140, 0, 139}, + dictWord{134, 0, 292}, + dictWord{139, 0, 174}, + dictWord{5, 0, 177}, + dictWord{6, 0, 616}, + dictWord{7, 0, 827}, + dictWord{ + 9, + 0, + 525, + }, + dictWord{138, 0, 656}, + dictWord{10, 0, 31}, + dictWord{6, 10, 215}, + dictWord{7, 10, 1028}, + dictWord{7, 10, 1473}, + dictWord{7, 10, 1721}, + dictWord{ + 9, + 10, + 424, + }, + dictWord{138, 10, 779}, + dictWord{135, 10, 584}, + dictWord{136, 11, 293}, + dictWord{134, 0, 685}, + dictWord{135, 11, 1868}, + dictWord{ + 133, + 11, + 460, + }, + dictWord{7, 0, 647}, + dictWord{6, 10, 67}, + dictWord{7, 10, 1630}, + dictWord{9, 10, 354}, + dictWord{9, 10, 675}, + dictWord{10, 10, 830}, + dictWord{ + 14, + 10, + 80, + }, + dictWord{145, 10, 80}, + dictWord{4, 0, 161}, + dictWord{133, 0, 631}, + dictWord{6, 10, 141}, + dictWord{7, 10, 225}, + dictWord{9, 10, 59}, + dictWord{9, 10, 607}, + dictWord{10, 10, 312}, + dictWord{11, 10, 687}, + dictWord{12, 10, 555}, + dictWord{13, 10, 373}, + dictWord{13, 10, 494}, + dictWord{148, 10, 58}, + dictWord{ + 7, + 11, + 965, + }, + dictWord{7, 11, 1460}, + dictWord{135, 11, 1604}, + dictWord{136, 10, 783}, + dictWord{134, 11, 388}, + dictWord{6, 0, 722}, + dictWord{6, 0, 1267}, + dictWord{ + 4, + 11, + 511, + }, + dictWord{9, 11, 333}, + dictWord{9, 11, 379}, + dictWord{10, 11, 602}, + dictWord{11, 11, 441}, + dictWord{11, 11, 723}, + dictWord{11, 11, 976}, + dictWord{140, 11, 357}, + dictWord{134, 0, 1797}, + dictWord{135, 0, 1684}, + dictWord{9, 0, 469}, + dictWord{9, 0, 709}, + dictWord{12, 0, 512}, + dictWord{14, 0, 65}, + dictWord{17, 0, 12}, + dictWord{5, 11, 938}, + dictWord{136, 11, 707}, + dictWord{7, 0, 1230}, + dictWord{136, 0, 531}, + dictWord{10, 0, 229}, + dictWord{11, 0, 73}, + dictWord{ + 11, + 0, + 376, + }, + dictWord{139, 0, 433}, + dictWord{12, 0, 268}, + dictWord{12, 0, 640}, + dictWord{142, 0, 119}, + dictWord{7, 10, 430}, + dictWord{139, 10, 46}, + dictWord{ + 6, + 0, + 558, + }, + dictWord{7, 0, 651}, + dictWord{8, 0, 421}, + dictWord{9, 0, 0}, + dictWord{10, 0, 34}, + dictWord{139, 0, 1008}, + dictWord{6, 0, 106}, + dictWord{7, 0, 1786}, + dictWord{7, 0, 1821}, + dictWord{9, 0, 102}, + dictWord{9, 0, 763}, + dictWord{5, 10, 602}, + dictWord{7, 10, 2018}, + dictWord{137, 10, 418}, + dictWord{5, 0, 65}, + dictWord{ + 6, + 0, + 416, + }, + dictWord{7, 0, 1720}, + dictWord{7, 0, 1924}, + dictWord{10, 0, 109}, + dictWord{11, 0, 14}, + dictWord{11, 0, 70}, + dictWord{11, 0, 569}, + dictWord{11, 0, 735}, + dictWord{15, 0, 153}, + dictWord{20, 0, 80}, + dictWord{136, 10, 677}, + dictWord{135, 11, 1625}, + dictWord{137, 11, 772}, + dictWord{136, 0, 595}, + dictWord{ + 6, + 11, + 469, + }, + dictWord{7, 11, 1709}, + dictWord{138, 11, 515}, + dictWord{7, 0, 1832}, + dictWord{138, 0, 374}, + dictWord{9, 0, 106}, + dictWord{9, 0, 163}, + dictWord{ + 9, + 0, + 296, + }, + dictWord{10, 0, 167}, + dictWord{10, 0, 172}, + dictWord{10, 0, 777}, + dictWord{139, 0, 16}, + dictWord{6, 0, 6}, + dictWord{7, 0, 81}, + dictWord{7, 0, 771}, + dictWord{ + 7, + 0, + 1731, + }, + dictWord{9, 0, 405}, + dictWord{138, 0, 421}, + dictWord{4, 11, 500}, + dictWord{135, 11, 938}, + dictWord{5, 11, 68}, + dictWord{134, 11, 383}, + dictWord{ + 5, + 0, + 881, + }, + dictWord{133, 0, 885}, + dictWord{6, 0, 854}, + dictWord{6, 0, 1132}, + dictWord{6, 0, 1495}, + dictWord{6, 0, 1526}, + dictWord{6, 0, 1533}, + dictWord{ + 134, + 0, + 1577, + }, + dictWord{4, 11, 337}, + dictWord{6, 11, 353}, + dictWord{7, 11, 1934}, + dictWord{8, 11, 488}, + dictWord{137, 11, 429}, + dictWord{7, 11, 236}, + dictWord{ + 7, + 11, + 1795, + }, + dictWord{8, 11, 259}, + dictWord{9, 11, 135}, + dictWord{9, 11, 177}, + dictWord{10, 11, 825}, + dictWord{11, 11, 115}, + dictWord{11, 11, 370}, + dictWord{ + 11, + 11, + 405, + }, + dictWord{11, 11, 604}, + dictWord{12, 11, 10}, + dictWord{12, 11, 667}, + dictWord{12, 11, 669}, + dictWord{13, 11, 76}, + dictWord{14, 11, 310}, + dictWord{15, 11, 76}, + dictWord{15, 11, 147}, + dictWord{148, 11, 23}, + dictWord{5, 0, 142}, + dictWord{134, 0, 546}, + dictWord{4, 11, 15}, + dictWord{5, 11, 22}, + dictWord{ + 6, + 11, + 244, + }, + dictWord{7, 11, 40}, + dictWord{7, 11, 200}, + dictWord{7, 11, 906}, + dictWord{7, 11, 1199}, + dictWord{9, 11, 616}, + dictWord{10, 11, 716}, + dictWord{ + 11, + 11, + 635, + }, + dictWord{11, 11, 801}, + dictWord{140, 11, 458}, + dictWord{5, 0, 466}, + dictWord{11, 0, 571}, + dictWord{12, 0, 198}, + dictWord{13, 0, 283}, + dictWord{ + 14, + 0, + 186, + }, + dictWord{15, 0, 21}, + dictWord{15, 0, 103}, + dictWord{135, 10, 329}, + dictWord{4, 0, 185}, + dictWord{5, 0, 257}, + dictWord{5, 0, 839}, + dictWord{5, 0, 936}, + dictWord{9, 0, 399}, + dictWord{10, 0, 258}, + dictWord{10, 0, 395}, + dictWord{10, 0, 734}, + dictWord{11, 0, 1014}, + dictWord{12, 0, 23}, + dictWord{13, 0, 350}, + dictWord{ + 14, + 0, + 150, + }, + dictWord{19, 0, 6}, + dictWord{135, 11, 1735}, + dictWord{12, 11, 36}, + dictWord{141, 11, 337}, + dictWord{5, 11, 598}, + dictWord{7, 11, 791}, + dictWord{ + 8, + 11, + 108, + }, + dictWord{137, 11, 123}, + dictWord{132, 10, 469}, + dictWord{7, 0, 404}, + dictWord{7, 0, 1377}, + dictWord{7, 0, 1430}, + dictWord{7, 0, 2017}, + dictWord{ + 8, + 0, + 149, + }, + dictWord{8, 0, 239}, + dictWord{8, 0, 512}, + dictWord{8, 0, 793}, + dictWord{8, 0, 818}, + dictWord{9, 0, 474}, + dictWord{9, 0, 595}, + dictWord{10, 0, 122}, + dictWord{10, 0, 565}, + dictWord{10, 0, 649}, + dictWord{10, 0, 783}, + dictWord{11, 0, 239}, + dictWord{11, 0, 295}, + dictWord{11, 0, 447}, + dictWord{11, 0, 528}, + dictWord{ + 11, + 0, + 639, + }, + dictWord{11, 0, 800}, + dictWord{12, 0, 25}, + dictWord{12, 0, 77}, + dictWord{12, 0, 157}, + dictWord{12, 0, 256}, + dictWord{12, 0, 316}, + dictWord{12, 0, 390}, + dictWord{12, 0, 391}, + dictWord{12, 0, 395}, + dictWord{12, 0, 478}, + dictWord{12, 0, 503}, + dictWord{12, 0, 592}, + dictWord{12, 0, 680}, + dictWord{13, 0, 50}, + dictWord{13, 0, 53}, + dictWord{13, 0, 132}, + dictWord{13, 0, 198}, + dictWord{13, 0, 322}, + dictWord{13, 0, 415}, + dictWord{13, 0, 511}, + dictWord{14, 0, 71}, + dictWord{ + 14, + 0, + 395, + }, + dictWord{15, 0, 71}, + dictWord{15, 0, 136}, + dictWord{17, 0, 123}, + dictWord{18, 0, 93}, + dictWord{147, 0, 58}, + dictWord{136, 0, 712}, + dictWord{ + 134, + 10, + 1743, + }, + dictWord{5, 10, 929}, + dictWord{6, 10, 340}, + dictWord{8, 10, 376}, + dictWord{136, 10, 807}, + dictWord{6, 0, 1848}, + dictWord{8, 0, 860}, + dictWord{ + 10, + 0, + 856, + }, + dictWord{10, 0, 859}, + dictWord{10, 0, 925}, + dictWord{10, 0, 941}, + dictWord{140, 0, 762}, + dictWord{6, 0, 629}, + dictWord{6, 0, 906}, + dictWord{9, 0, 810}, + dictWord{140, 0, 652}, + dictWord{5, 10, 218}, + dictWord{7, 10, 1610}, + dictWord{138, 10, 83}, + dictWord{7, 10, 1512}, + dictWord{135, 10, 1794}, + dictWord{ + 4, + 0, + 377, + }, + dictWord{24, 0, 13}, + dictWord{4, 11, 155}, + dictWord{7, 11, 1689}, + dictWord{11, 10, 0}, + dictWord{144, 10, 78}, + dictWord{4, 11, 164}, + dictWord{5, 11, 151}, + dictWord{5, 11, 730}, + dictWord{5, 11, 741}, + dictWord{7, 11, 498}, + dictWord{7, 11, 870}, + dictWord{7, 11, 1542}, + dictWord{12, 11, 213}, + dictWord{14, 11, 36}, + dictWord{14, 11, 391}, + dictWord{17, 11, 111}, + dictWord{18, 11, 6}, + dictWord{18, 11, 46}, + dictWord{18, 11, 151}, + dictWord{19, 11, 36}, + dictWord{20, 11, 32}, + dictWord{20, 11, 56}, + dictWord{20, 11, 69}, + dictWord{20, 11, 102}, + dictWord{21, 11, 4}, + dictWord{22, 11, 8}, + dictWord{22, 11, 10}, + dictWord{22, 11, 14}, + dictWord{ + 150, + 11, + 31, + }, + dictWord{7, 0, 1842}, + dictWord{133, 10, 571}, + dictWord{4, 10, 455}, + dictWord{4, 11, 624}, + dictWord{135, 11, 1752}, + dictWord{134, 0, 1501}, + dictWord{4, 11, 492}, + dictWord{5, 11, 451}, + dictWord{6, 10, 161}, + dictWord{7, 10, 372}, + dictWord{137, 10, 597}, + dictWord{132, 10, 349}, + dictWord{4, 0, 180}, + dictWord{135, 0, 1906}, + dictWord{135, 11, 835}, + dictWord{141, 11, 70}, + dictWord{132, 0, 491}, + dictWord{137, 10, 751}, + dictWord{6, 10, 432}, + dictWord{ + 139, + 10, + 322, + }, + dictWord{4, 0, 171}, + dictWord{138, 0, 234}, + dictWord{6, 11, 113}, + dictWord{135, 11, 436}, + dictWord{4, 0, 586}, + dictWord{7, 0, 1186}, + dictWord{ + 138, + 0, + 631, + }, + dictWord{5, 10, 468}, + dictWord{10, 10, 325}, + dictWord{11, 10, 856}, + dictWord{12, 10, 345}, + dictWord{143, 10, 104}, + dictWord{5, 10, 223}, + dictWord{10, 11, 592}, + dictWord{10, 11, 753}, + dictWord{12, 11, 317}, + dictWord{12, 11, 355}, + dictWord{12, 11, 465}, + dictWord{12, 11, 469}, + dictWord{ + 12, + 11, + 560, + }, + dictWord{12, 11, 578}, + dictWord{141, 11, 243}, + dictWord{132, 10, 566}, + dictWord{135, 11, 520}, + dictWord{4, 10, 59}, + dictWord{135, 10, 1394}, + dictWord{6, 10, 436}, + dictWord{139, 10, 481}, + dictWord{9, 0, 931}, + dictWord{10, 0, 334}, + dictWord{20, 0, 71}, + dictWord{4, 10, 48}, + dictWord{5, 10, 271}, + dictWord{ + 7, + 10, + 953, + }, + dictWord{135, 11, 1878}, + dictWord{11, 0, 170}, + dictWord{5, 10, 610}, + dictWord{136, 10, 457}, + dictWord{133, 10, 755}, + dictWord{6, 0, 1587}, + dictWord{135, 10, 1217}, + dictWord{4, 10, 197}, + dictWord{149, 11, 26}, + dictWord{133, 11, 585}, + dictWord{137, 11, 521}, + dictWord{133, 0, 765}, + dictWord{ + 133, + 10, + 217, + }, + dictWord{139, 11, 586}, + dictWord{133, 0, 424}, + dictWord{9, 11, 752}, + dictWord{12, 11, 610}, + dictWord{13, 11, 431}, + dictWord{16, 11, 59}, + dictWord{146, 11, 109}, + dictWord{136, 0, 714}, + dictWord{7, 0, 685}, + dictWord{132, 11, 307}, + dictWord{9, 0, 420}, + dictWord{10, 0, 269}, + dictWord{10, 0, 285}, + dictWord{10, 0, 576}, + dictWord{11, 0, 397}, + dictWord{13, 0, 175}, + dictWord{145, 0, 90}, + dictWord{132, 0, 429}, + dictWord{133, 11, 964}, + dictWord{9, 11, 463}, + dictWord{138, 11, 595}, + dictWord{7, 0, 18}, + dictWord{7, 0, 699}, + dictWord{7, 0, 1966}, + dictWord{8, 0, 752}, + dictWord{9, 0, 273}, + dictWord{9, 0, 412}, + dictWord{ + 9, + 0, + 703, + }, + dictWord{10, 0, 71}, + dictWord{10, 0, 427}, + dictWord{138, 0, 508}, + dictWord{4, 10, 165}, + dictWord{7, 10, 1398}, + dictWord{135, 10, 1829}, + dictWord{ + 4, + 0, + 53, + }, + dictWord{5, 0, 186}, + dictWord{7, 0, 752}, + dictWord{7, 0, 828}, + dictWord{142, 0, 116}, + dictWord{8, 0, 575}, + dictWord{10, 0, 289}, + dictWord{139, 0, 319}, + dictWord{132, 0, 675}, + dictWord{134, 0, 1424}, + dictWord{4, 11, 75}, + dictWord{5, 11, 180}, + dictWord{6, 11, 500}, + dictWord{7, 11, 58}, + dictWord{7, 11, 710}, + dictWord{138, 11, 645}, + dictWord{133, 11, 649}, + dictWord{6, 11, 276}, + dictWord{7, 11, 282}, + dictWord{7, 11, 879}, + dictWord{7, 11, 924}, + dictWord{8, 11, 459}, + dictWord{9, 11, 599}, + dictWord{9, 11, 754}, + dictWord{11, 11, 574}, + dictWord{12, 11, 128}, + dictWord{12, 11, 494}, + dictWord{13, 11, 52}, + dictWord{13, 11, 301}, + dictWord{15, 11, 30}, + dictWord{143, 11, 132}, + dictWord{6, 0, 647}, + dictWord{134, 0, 1095}, + dictWord{5, 10, 9}, + dictWord{7, 10, 297}, + dictWord{7, 10, 966}, + dictWord{140, 10, 306}, + dictWord{132, 11, 200}, + dictWord{134, 0, 1334}, + dictWord{5, 10, 146}, + dictWord{6, 10, 411}, + dictWord{138, 10, 721}, + dictWord{ + 6, + 0, + 209, + }, + dictWord{6, 0, 1141}, + dictWord{6, 0, 1288}, + dictWord{8, 0, 468}, + dictWord{9, 0, 210}, + dictWord{11, 0, 36}, + dictWord{12, 0, 28}, + dictWord{12, 0, 630}, + dictWord{13, 0, 21}, + dictWord{13, 0, 349}, + dictWord{14, 0, 7}, + dictWord{145, 0, 13}, + dictWord{6, 10, 177}, + dictWord{135, 10, 467}, + dictWord{4, 0, 342}, + dictWord{ + 135, + 0, + 1179, + }, + dictWord{10, 11, 454}, + dictWord{140, 11, 324}, + dictWord{4, 0, 928}, + dictWord{133, 0, 910}, + dictWord{7, 0, 1838}, + dictWord{6, 11, 225}, + dictWord{ + 137, + 11, + 211, + }, + dictWord{16, 0, 101}, + dictWord{20, 0, 115}, + dictWord{20, 0, 118}, + dictWord{148, 0, 122}, + dictWord{4, 0, 496}, + dictWord{135, 0, 856}, + dictWord{ + 4, + 0, + 318, + }, + dictWord{11, 0, 654}, + dictWord{7, 11, 718}, + dictWord{139, 11, 102}, + dictWord{8, 11, 58}, + dictWord{9, 11, 724}, + dictWord{11, 11, 809}, + dictWord{ + 13, + 11, + 113, + }, + dictWord{145, 11, 72}, + dictWord{5, 10, 200}, + dictWord{6, 11, 345}, + dictWord{135, 11, 1247}, + dictWord{8, 11, 767}, + dictWord{8, 11, 803}, + dictWord{ + 9, + 11, + 301, + }, + dictWord{137, 11, 903}, + dictWord{7, 0, 915}, + dictWord{8, 0, 247}, + dictWord{19, 0, 0}, + dictWord{7, 11, 1949}, + dictWord{136, 11, 674}, + dictWord{ + 4, + 0, + 202, + }, + dictWord{5, 0, 382}, + dictWord{6, 0, 454}, + dictWord{7, 0, 936}, + dictWord{7, 0, 1803}, + dictWord{8, 0, 758}, + dictWord{9, 0, 375}, + dictWord{9, 0, 895}, + dictWord{ + 10, + 0, + 743, + }, + dictWord{10, 0, 792}, + dictWord{11, 0, 978}, + dictWord{11, 0, 1012}, + dictWord{142, 0, 109}, + dictWord{7, 0, 1150}, + dictWord{7, 0, 1425}, + dictWord{ + 7, + 0, + 1453, + }, + dictWord{140, 0, 513}, + dictWord{134, 11, 259}, + dictWord{138, 0, 791}, + dictWord{11, 0, 821}, + dictWord{12, 0, 110}, + dictWord{12, 0, 153}, + dictWord{ + 18, + 0, + 41, + }, + dictWord{150, 0, 19}, + dictWord{134, 10, 481}, + dictWord{132, 0, 796}, + dictWord{6, 0, 445}, + dictWord{9, 0, 909}, + dictWord{136, 11, 254}, + dictWord{ + 10, + 0, + 776, + }, + dictWord{13, 0, 345}, + dictWord{142, 0, 425}, + dictWord{4, 10, 84}, + dictWord{7, 10, 1482}, + dictWord{10, 10, 76}, + dictWord{138, 10, 142}, + dictWord{ + 135, + 11, + 742, + }, + dictWord{6, 0, 578}, + dictWord{133, 10, 1015}, + dictWord{6, 0, 1387}, + dictWord{4, 10, 315}, + dictWord{5, 10, 507}, + dictWord{135, 10, 1370}, + dictWord{4, 0, 438}, + dictWord{133, 0, 555}, + dictWord{136, 0, 766}, + dictWord{133, 11, 248}, + dictWord{134, 10, 1722}, + dictWord{4, 11, 116}, + dictWord{5, 11, 95}, + dictWord{5, 11, 445}, + dictWord{7, 11, 1688}, + dictWord{8, 11, 29}, + dictWord{9, 11, 272}, + dictWord{11, 11, 509}, + dictWord{139, 11, 915}, + dictWord{135, 0, 541}, + dictWord{133, 11, 543}, + dictWord{8, 10, 222}, + dictWord{8, 10, 476}, + dictWord{9, 10, 238}, + dictWord{11, 10, 516}, + dictWord{11, 10, 575}, + dictWord{ + 15, + 10, + 109, + }, + dictWord{146, 10, 100}, + dictWord{6, 0, 880}, + dictWord{134, 0, 1191}, + dictWord{5, 11, 181}, + dictWord{136, 11, 41}, + dictWord{134, 0, 1506}, + dictWord{132, 11, 681}, + dictWord{7, 11, 25}, + dictWord{8, 11, 202}, + dictWord{138, 11, 536}, + dictWord{139, 0, 983}, + dictWord{137, 0, 768}, + dictWord{132, 0, 584}, + dictWord{9, 11, 423}, + dictWord{140, 11, 89}, + dictWord{8, 11, 113}, + dictWord{9, 11, 877}, + dictWord{10, 11, 554}, + dictWord{11, 11, 83}, + dictWord{12, 11, 136}, + dictWord{147, 11, 109}, + dictWord{7, 10, 706}, + dictWord{7, 10, 1058}, + dictWord{138, 10, 538}, + dictWord{133, 11, 976}, + dictWord{4, 11, 206}, + dictWord{ + 135, + 11, + 746, + }, + dictWord{136, 11, 526}, + dictWord{140, 0, 737}, + dictWord{11, 10, 92}, + dictWord{11, 10, 196}, + dictWord{11, 10, 409}, + dictWord{11, 10, 450}, + dictWord{11, 10, 666}, + dictWord{11, 10, 777}, + dictWord{12, 10, 262}, + dictWord{13, 10, 385}, + dictWord{13, 10, 393}, + dictWord{15, 10, 115}, + dictWord{ + 16, + 10, + 45, + }, + dictWord{145, 10, 82}, + dictWord{4, 0, 226}, + dictWord{4, 0, 326}, + dictWord{7, 0, 1770}, + dictWord{4, 11, 319}, + dictWord{5, 11, 699}, + dictWord{138, 11, 673}, + dictWord{6, 10, 40}, + dictWord{135, 10, 1781}, + dictWord{5, 0, 426}, + dictWord{8, 0, 30}, + dictWord{9, 0, 2}, + dictWord{11, 0, 549}, + dictWord{147, 0, 122}, + dictWord{ + 6, + 0, + 1161, + }, + dictWord{134, 0, 1329}, + dictWord{138, 10, 97}, + dictWord{6, 10, 423}, + dictWord{7, 10, 665}, + dictWord{135, 10, 1210}, + dictWord{7, 11, 13}, + dictWord{ + 8, + 11, + 226, + }, + dictWord{10, 11, 537}, + dictWord{11, 11, 570}, + dictWord{11, 11, 605}, + dictWord{11, 11, 799}, + dictWord{11, 11, 804}, + dictWord{12, 11, 85}, + dictWord{12, 11, 516}, + dictWord{12, 11, 623}, + dictWord{13, 11, 112}, + dictWord{13, 11, 361}, + dictWord{14, 11, 77}, + dictWord{14, 11, 78}, + dictWord{17, 11, 28}, + dictWord{147, 11, 110}, + dictWord{132, 11, 769}, + dictWord{132, 11, 551}, + dictWord{132, 11, 728}, + dictWord{147, 0, 117}, + dictWord{9, 11, 57}, + dictWord{ + 9, + 11, + 459, + }, + dictWord{10, 11, 425}, + dictWord{11, 11, 119}, + dictWord{12, 11, 184}, + dictWord{12, 11, 371}, + dictWord{13, 11, 358}, + dictWord{145, 11, 51}, + dictWord{ + 5, + 11, + 188, + }, + dictWord{5, 11, 814}, + dictWord{8, 11, 10}, + dictWord{9, 11, 421}, + dictWord{9, 11, 729}, + dictWord{10, 11, 609}, + dictWord{139, 11, 689}, + dictWord{134, 11, 624}, + dictWord{135, 11, 298}, + dictWord{135, 0, 462}, + dictWord{4, 0, 345}, + dictWord{139, 10, 624}, + dictWord{136, 10, 574}, + dictWord{ + 4, + 0, + 385, + }, + dictWord{7, 0, 265}, + dictWord{135, 0, 587}, + dictWord{6, 0, 808}, + dictWord{132, 11, 528}, + dictWord{133, 0, 398}, + dictWord{132, 10, 354}, + dictWord{ + 4, + 0, + 347, + }, + dictWord{5, 0, 423}, + dictWord{5, 0, 996}, + dictWord{135, 0, 1329}, + dictWord{135, 10, 1558}, + dictWord{7, 0, 1259}, + dictWord{9, 0, 125}, + dictWord{ + 139, + 0, + 65, + }, + dictWord{5, 0, 136}, + dictWord{6, 0, 136}, + dictWord{136, 0, 644}, + dictWord{5, 11, 104}, + dictWord{6, 11, 173}, + dictWord{135, 11, 1631}, + dictWord{ + 135, + 0, + 469, + }, + dictWord{133, 10, 830}, + dictWord{4, 0, 278}, + dictWord{5, 0, 465}, + dictWord{135, 0, 1367}, + dictWord{7, 11, 810}, + dictWord{8, 11, 138}, + dictWord{ + 8, + 11, + 342, + }, + dictWord{9, 11, 84}, + dictWord{10, 11, 193}, + dictWord{11, 11, 883}, + dictWord{140, 11, 359}, + dictWord{5, 10, 496}, + dictWord{135, 10, 203}, + dictWord{ + 4, + 0, + 433, + }, + dictWord{133, 0, 719}, + dictWord{6, 11, 95}, + dictWord{134, 10, 547}, + dictWord{5, 10, 88}, + dictWord{137, 10, 239}, + dictWord{6, 11, 406}, + dictWord{ + 10, + 11, + 409, + }, + dictWord{10, 11, 447}, + dictWord{11, 11, 44}, + dictWord{140, 11, 100}, + dictWord{134, 0, 1423}, + dictWord{7, 10, 650}, + dictWord{135, 10, 1310}, + dictWord{134, 0, 749}, + dictWord{135, 11, 1243}, + dictWord{135, 0, 1363}, + dictWord{6, 0, 381}, + dictWord{7, 0, 645}, + dictWord{7, 0, 694}, + dictWord{8, 0, 546}, + dictWord{7, 10, 1076}, + dictWord{9, 10, 80}, + dictWord{11, 10, 78}, + dictWord{11, 10, 421}, + dictWord{11, 10, 534}, + dictWord{140, 10, 545}, + dictWord{ + 134, + 11, + 1636, + }, + dictWord{135, 11, 1344}, + dictWord{12, 0, 277}, + dictWord{7, 10, 274}, + dictWord{11, 10, 479}, + dictWord{139, 10, 507}, + dictWord{6, 0, 705}, + dictWord{ + 6, + 0, + 783, + }, + dictWord{6, 0, 1275}, + dictWord{6, 0, 1481}, + dictWord{4, 11, 282}, + dictWord{7, 11, 1034}, + dictWord{11, 11, 398}, + dictWord{11, 11, 634}, + dictWord{ + 12, + 11, + 1, + }, + dictWord{12, 11, 79}, + dictWord{12, 11, 544}, + dictWord{14, 11, 237}, + dictWord{17, 11, 10}, + dictWord{146, 11, 20}, + dictWord{134, 0, 453}, + dictWord{ + 4, + 0, + 555, + }, + dictWord{8, 0, 536}, + dictWord{10, 0, 288}, + dictWord{11, 0, 1005}, + dictWord{4, 10, 497}, + dictWord{135, 10, 1584}, + dictWord{5, 11, 118}, + dictWord{ + 5, + 11, + 499, + }, + dictWord{6, 11, 476}, + dictWord{7, 11, 600}, + dictWord{7, 11, 888}, + dictWord{135, 11, 1096}, + dictWord{138, 0, 987}, + dictWord{7, 0, 1107}, + dictWord{ + 7, + 10, + 261, + }, + dictWord{7, 10, 1115}, + dictWord{7, 10, 1354}, + dictWord{7, 10, 1588}, + dictWord{7, 10, 1705}, + dictWord{7, 10, 1902}, + dictWord{9, 10, 465}, + dictWord{10, 10, 248}, + dictWord{10, 10, 349}, + dictWord{10, 10, 647}, + dictWord{11, 10, 527}, + dictWord{11, 10, 660}, + dictWord{11, 10, 669}, + dictWord{ + 12, + 10, + 529, + }, + dictWord{141, 10, 305}, + dictWord{7, 11, 296}, + dictWord{7, 11, 596}, + dictWord{8, 11, 560}, + dictWord{8, 11, 586}, + dictWord{9, 11, 612}, + dictWord{ + 11, + 11, + 100, + }, + dictWord{11, 11, 304}, + dictWord{12, 11, 46}, + dictWord{13, 11, 89}, + dictWord{14, 11, 112}, + dictWord{145, 11, 122}, + dictWord{9, 0, 370}, + dictWord{ + 138, + 0, + 90, + }, + dictWord{136, 10, 13}, + dictWord{132, 0, 860}, + dictWord{7, 10, 642}, + dictWord{8, 10, 250}, + dictWord{11, 10, 123}, + dictWord{11, 10, 137}, + dictWord{ + 13, + 10, + 48, + }, + dictWord{142, 10, 95}, + dictWord{135, 10, 1429}, + dictWord{137, 11, 321}, + dictWord{132, 0, 257}, + dictWord{135, 0, 2031}, + dictWord{7, 0, 1768}, + dictWord{7, 11, 1599}, + dictWord{7, 11, 1723}, + dictWord{8, 11, 79}, + dictWord{8, 11, 106}, + dictWord{8, 11, 190}, + dictWord{8, 11, 302}, + dictWord{8, 11, 383}, + dictWord{9, 11, 119}, + dictWord{9, 11, 233}, + dictWord{9, 11, 298}, + dictWord{9, 11, 419}, + dictWord{9, 11, 471}, + dictWord{10, 11, 181}, + dictWord{10, 11, 406}, + dictWord{11, 11, 57}, + dictWord{11, 11, 85}, + dictWord{11, 11, 120}, + dictWord{11, 11, 177}, + dictWord{11, 11, 296}, + dictWord{11, 11, 382}, + dictWord{11, 11, 454}, + dictWord{11, 11, 758}, + dictWord{11, 11, 999}, + dictWord{12, 11, 27}, + dictWord{12, 11, 98}, + dictWord{12, 11, 131}, + dictWord{12, 11, 245}, + dictWord{ + 12, + 11, + 312, + }, + dictWord{12, 11, 446}, + dictWord{12, 11, 454}, + dictWord{13, 11, 25}, + dictWord{13, 11, 98}, + dictWord{13, 11, 426}, + dictWord{13, 11, 508}, + dictWord{ + 14, + 11, + 6, + }, + dictWord{14, 11, 163}, + dictWord{14, 11, 272}, + dictWord{14, 11, 277}, + dictWord{14, 11, 370}, + dictWord{15, 11, 95}, + dictWord{15, 11, 138}, + dictWord{ + 15, + 11, + 167, + }, + dictWord{17, 11, 18}, + dictWord{17, 11, 38}, + dictWord{20, 11, 96}, + dictWord{149, 11, 32}, + dictWord{5, 11, 722}, + dictWord{134, 11, 1759}, + dictWord{145, 11, 16}, + dictWord{6, 0, 1071}, + dictWord{134, 0, 1561}, + dictWord{10, 10, 545}, + dictWord{140, 10, 301}, + dictWord{6, 0, 83}, + dictWord{6, 0, 1733}, + dictWord{135, 0, 1389}, + dictWord{4, 0, 835}, + dictWord{135, 0, 1818}, + dictWord{133, 11, 258}, + dictWord{4, 10, 904}, + dictWord{133, 10, 794}, + dictWord{ + 134, + 0, + 2006, + }, + dictWord{5, 11, 30}, + dictWord{7, 11, 495}, + dictWord{8, 11, 134}, + dictWord{9, 11, 788}, + dictWord{140, 11, 438}, + dictWord{135, 11, 2004}, + dictWord{ + 137, + 0, + 696, + }, + dictWord{5, 11, 50}, + dictWord{6, 11, 439}, + dictWord{7, 11, 780}, + dictWord{135, 11, 1040}, + dictWord{7, 11, 772}, + dictWord{7, 11, 1104}, + dictWord{ + 7, + 11, + 1647, + }, + dictWord{11, 11, 269}, + dictWord{11, 11, 539}, + dictWord{11, 11, 607}, + dictWord{11, 11, 627}, + dictWord{11, 11, 706}, + dictWord{11, 11, 975}, + dictWord{12, 11, 248}, + dictWord{12, 11, 311}, + dictWord{12, 11, 434}, + dictWord{12, 11, 600}, + dictWord{12, 11, 622}, + dictWord{13, 11, 297}, + dictWord{ + 13, + 11, + 367, + }, + dictWord{13, 11, 485}, + dictWord{14, 11, 69}, + dictWord{14, 11, 409}, + dictWord{143, 11, 108}, + dictWord{5, 11, 1}, + dictWord{6, 11, 81}, + dictWord{ + 138, + 11, + 520, + }, + dictWord{7, 0, 1718}, + dictWord{9, 0, 95}, + dictWord{9, 0, 274}, + dictWord{10, 0, 279}, + dictWord{10, 0, 317}, + dictWord{10, 0, 420}, + dictWord{11, 0, 303}, + dictWord{11, 0, 808}, + dictWord{12, 0, 134}, + dictWord{12, 0, 367}, + dictWord{13, 0, 149}, + dictWord{13, 0, 347}, + dictWord{14, 0, 349}, + dictWord{14, 0, 406}, + dictWord{ + 18, + 0, + 22, + }, + dictWord{18, 0, 89}, + dictWord{18, 0, 122}, + dictWord{147, 0, 47}, + dictWord{5, 11, 482}, + dictWord{8, 11, 98}, + dictWord{9, 11, 172}, + dictWord{10, 11, 222}, + dictWord{10, 11, 700}, + dictWord{10, 11, 822}, + dictWord{11, 11, 302}, + dictWord{11, 11, 778}, + dictWord{12, 11, 50}, + dictWord{12, 11, 127}, + dictWord{ + 12, + 11, + 396, + }, + dictWord{13, 11, 62}, + dictWord{13, 11, 328}, + dictWord{14, 11, 122}, + dictWord{147, 11, 72}, + dictWord{7, 10, 386}, + dictWord{138, 10, 713}, + dictWord{ + 6, + 10, + 7, + }, + dictWord{6, 10, 35}, + dictWord{7, 10, 147}, + dictWord{7, 10, 1069}, + dictWord{7, 10, 1568}, + dictWord{7, 10, 1575}, + dictWord{7, 10, 1917}, + dictWord{ + 8, + 10, + 43, + }, + dictWord{8, 10, 208}, + dictWord{9, 10, 128}, + dictWord{9, 10, 866}, + dictWord{10, 10, 20}, + dictWord{11, 10, 981}, + dictWord{147, 10, 33}, + dictWord{ + 133, + 0, + 26, + }, + dictWord{132, 0, 550}, + dictWord{5, 11, 2}, + dictWord{7, 11, 1494}, + dictWord{136, 11, 589}, + dictWord{6, 11, 512}, + dictWord{7, 11, 797}, + dictWord{ + 8, + 11, + 253, + }, + dictWord{9, 11, 77}, + dictWord{10, 11, 1}, + dictWord{10, 11, 129}, + dictWord{10, 11, 225}, + dictWord{11, 11, 118}, + dictWord{11, 11, 226}, + dictWord{ + 11, + 11, + 251, + }, + dictWord{11, 11, 430}, + dictWord{11, 11, 701}, + dictWord{11, 11, 974}, + dictWord{11, 11, 982}, + dictWord{12, 11, 64}, + dictWord{12, 11, 260}, + dictWord{ + 12, + 11, + 488, + }, + dictWord{140, 11, 690}, + dictWord{7, 10, 893}, + dictWord{141, 10, 424}, + dictWord{134, 0, 901}, + dictWord{136, 0, 822}, + dictWord{4, 0, 902}, + dictWord{5, 0, 809}, + dictWord{134, 0, 122}, + dictWord{6, 0, 807}, + dictWord{134, 0, 1366}, + dictWord{7, 0, 262}, + dictWord{5, 11, 748}, + dictWord{134, 11, 553}, + dictWord{133, 0, 620}, + dictWord{4, 0, 34}, + dictWord{5, 0, 574}, + dictWord{7, 0, 279}, + dictWord{7, 0, 1624}, + dictWord{136, 0, 601}, + dictWord{9, 0, 170}, + dictWord{ + 6, + 10, + 322, + }, + dictWord{9, 10, 552}, + dictWord{11, 10, 274}, + dictWord{13, 10, 209}, + dictWord{13, 10, 499}, + dictWord{14, 10, 85}, + dictWord{15, 10, 126}, + dictWord{ + 145, + 10, + 70, + }, + dictWord{132, 0, 537}, + dictWord{4, 11, 12}, + dictWord{7, 11, 420}, + dictWord{7, 11, 522}, + dictWord{7, 11, 809}, + dictWord{8, 11, 797}, + dictWord{ + 141, + 11, + 88, + }, + dictWord{133, 0, 332}, + dictWord{8, 10, 83}, + dictWord{8, 10, 742}, + dictWord{8, 10, 817}, + dictWord{9, 10, 28}, + dictWord{9, 10, 29}, + dictWord{9, 10, 885}, + dictWord{10, 10, 387}, + dictWord{11, 10, 633}, + dictWord{11, 10, 740}, + dictWord{13, 10, 235}, + dictWord{13, 10, 254}, + dictWord{15, 10, 143}, + dictWord{ + 143, + 10, + 146, + }, + dictWord{6, 0, 1909}, + dictWord{9, 0, 964}, + dictWord{12, 0, 822}, + dictWord{12, 0, 854}, + dictWord{12, 0, 865}, + dictWord{12, 0, 910}, + dictWord{12, 0, 938}, + dictWord{15, 0, 169}, + dictWord{15, 0, 208}, + dictWord{15, 0, 211}, + dictWord{18, 0, 205}, + dictWord{18, 0, 206}, + dictWord{18, 0, 220}, + dictWord{18, 0, 223}, + dictWord{152, 0, 24}, + dictWord{140, 10, 49}, + dictWord{5, 11, 528}, + dictWord{135, 11, 1580}, + dictWord{6, 0, 261}, + dictWord{8, 0, 182}, + dictWord{139, 0, 943}, + dictWord{134, 0, 1721}, + dictWord{4, 0, 933}, + dictWord{133, 0, 880}, + dictWord{136, 11, 321}, + dictWord{5, 11, 266}, + dictWord{9, 11, 290}, + dictWord{9, 11, 364}, + dictWord{10, 11, 293}, + dictWord{11, 11, 606}, + dictWord{142, 11, 45}, + dictWord{6, 0, 1609}, + dictWord{4, 11, 50}, + dictWord{6, 11, 510}, + dictWord{6, 11, 594}, + dictWord{9, 11, 121}, + dictWord{10, 11, 49}, + dictWord{10, 11, 412}, + dictWord{139, 11, 834}, + dictWord{7, 0, 895}, + dictWord{136, 11, 748}, + dictWord{132, 11, 466}, + dictWord{4, 10, 110}, + dictWord{10, 10, 415}, + dictWord{10, 10, 597}, + dictWord{142, 10, 206}, + dictWord{133, 0, 812}, + dictWord{135, 11, 281}, + dictWord{ + 6, + 0, + 1890, + }, + dictWord{6, 0, 1902}, + dictWord{6, 0, 1916}, + dictWord{9, 0, 929}, + dictWord{9, 0, 942}, + dictWord{9, 0, 975}, + dictWord{9, 0, 984}, + dictWord{9, 0, 986}, + dictWord{ + 9, + 0, + 1011, + }, + dictWord{9, 0, 1019}, + dictWord{12, 0, 804}, + dictWord{12, 0, 851}, + dictWord{12, 0, 867}, + dictWord{12, 0, 916}, + dictWord{12, 0, 923}, + dictWord{ + 15, + 0, + 194, + }, + dictWord{15, 0, 204}, + dictWord{15, 0, 210}, + dictWord{15, 0, 222}, + dictWord{15, 0, 223}, + dictWord{15, 0, 229}, + dictWord{15, 0, 250}, + dictWord{ + 18, + 0, + 179, + }, + dictWord{18, 0, 186}, + dictWord{18, 0, 192}, + dictWord{7, 10, 205}, + dictWord{135, 10, 2000}, + dictWord{132, 11, 667}, + dictWord{135, 0, 778}, + dictWord{ + 4, + 0, + 137, + }, + dictWord{7, 0, 1178}, + dictWord{135, 0, 1520}, + dictWord{134, 0, 1314}, + dictWord{4, 11, 242}, + dictWord{134, 11, 333}, + dictWord{6, 0, 1661}, + dictWord{7, 0, 1975}, + dictWord{7, 0, 2009}, + dictWord{135, 0, 2011}, + dictWord{134, 0, 1591}, + dictWord{4, 10, 283}, + dictWord{135, 10, 1194}, + dictWord{ + 11, + 0, + 820, + }, + dictWord{150, 0, 51}, + dictWord{4, 11, 39}, + dictWord{5, 11, 36}, + dictWord{7, 11, 1843}, + dictWord{8, 11, 407}, + dictWord{11, 11, 144}, + dictWord{ + 140, + 11, + 523, + }, + dictWord{134, 10, 1720}, + dictWord{4, 11, 510}, + dictWord{7, 11, 29}, + dictWord{7, 11, 66}, + dictWord{7, 11, 1980}, + dictWord{10, 11, 487}, + dictWord{ + 10, + 11, + 809, + }, + dictWord{146, 11, 9}, + dictWord{5, 0, 89}, + dictWord{7, 0, 1915}, + dictWord{9, 0, 185}, + dictWord{9, 0, 235}, + dictWord{10, 0, 64}, + dictWord{10, 0, 270}, + dictWord{10, 0, 403}, + dictWord{10, 0, 469}, + dictWord{10, 0, 529}, + dictWord{10, 0, 590}, + dictWord{11, 0, 140}, + dictWord{11, 0, 860}, + dictWord{13, 0, 1}, + dictWord{ + 13, + 0, + 422, + }, + dictWord{14, 0, 341}, + dictWord{14, 0, 364}, + dictWord{17, 0, 93}, + dictWord{18, 0, 113}, + dictWord{19, 0, 97}, + dictWord{147, 0, 113}, + dictWord{133, 0, 695}, + dictWord{6, 0, 987}, + dictWord{134, 0, 1160}, + dictWord{5, 0, 6}, + dictWord{6, 0, 183}, + dictWord{7, 0, 680}, + dictWord{7, 0, 978}, + dictWord{7, 0, 1013}, + dictWord{ + 7, + 0, + 1055, + }, + dictWord{12, 0, 230}, + dictWord{13, 0, 172}, + dictWord{146, 0, 29}, + dictWord{134, 11, 570}, + dictWord{132, 11, 787}, + dictWord{134, 11, 518}, + dictWord{ + 6, + 0, + 29, + }, + dictWord{139, 0, 63}, + dictWord{132, 11, 516}, + dictWord{136, 11, 821}, + dictWord{132, 0, 311}, + dictWord{134, 0, 1740}, + dictWord{7, 0, 170}, + dictWord{8, 0, 90}, + dictWord{8, 0, 177}, + dictWord{8, 0, 415}, + dictWord{11, 0, 714}, + dictWord{14, 0, 281}, + dictWord{136, 10, 735}, + dictWord{134, 0, 1961}, + dictWord{ + 135, + 11, + 1405, + }, + dictWord{4, 11, 10}, + dictWord{7, 11, 917}, + dictWord{139, 11, 786}, + dictWord{5, 10, 132}, + dictWord{9, 10, 486}, + dictWord{9, 10, 715}, + dictWord{ + 10, + 10, + 458, + }, + dictWord{11, 10, 373}, + dictWord{11, 10, 668}, + dictWord{11, 10, 795}, + dictWord{11, 10, 897}, + dictWord{12, 10, 272}, + dictWord{12, 10, 424}, + dictWord{12, 10, 539}, + dictWord{12, 10, 558}, + dictWord{14, 10, 245}, + dictWord{14, 10, 263}, + dictWord{14, 10, 264}, + dictWord{14, 10, 393}, + dictWord{ + 142, + 10, + 403, + }, + dictWord{11, 0, 91}, + dictWord{13, 0, 129}, + dictWord{15, 0, 101}, + dictWord{145, 0, 125}, + dictWord{135, 0, 1132}, + dictWord{4, 0, 494}, + dictWord{6, 0, 74}, + dictWord{7, 0, 44}, + dictWord{7, 0, 407}, + dictWord{12, 0, 17}, + dictWord{15, 0, 5}, + dictWord{148, 0, 11}, + dictWord{133, 10, 379}, + dictWord{5, 0, 270}, + dictWord{ + 5, + 11, + 684, + }, + dictWord{6, 10, 89}, + dictWord{6, 10, 400}, + dictWord{7, 10, 1569}, + dictWord{7, 10, 1623}, + dictWord{7, 10, 1850}, + dictWord{8, 10, 218}, + dictWord{ + 8, + 10, + 422, + }, + dictWord{9, 10, 570}, + dictWord{138, 10, 626}, + dictWord{4, 0, 276}, + dictWord{133, 0, 296}, + dictWord{6, 0, 1523}, + dictWord{134, 11, 27}, + dictWord{ + 6, + 10, + 387, + }, + dictWord{7, 10, 882}, + dictWord{141, 10, 111}, + dictWord{6, 10, 224}, + dictWord{7, 10, 877}, + dictWord{137, 10, 647}, + dictWord{135, 10, 790}, + dictWord{ + 4, + 0, + 7, + }, + dictWord{5, 0, 90}, + dictWord{5, 0, 158}, + dictWord{6, 0, 542}, + dictWord{7, 0, 221}, + dictWord{7, 0, 1574}, + dictWord{9, 0, 490}, + dictWord{10, 0, 540}, + dictWord{ + 11, + 0, + 443, + }, + dictWord{139, 0, 757}, + dictWord{7, 0, 588}, + dictWord{9, 0, 175}, + dictWord{138, 0, 530}, + dictWord{135, 10, 394}, + dictWord{142, 11, 23}, + dictWord{ + 134, + 0, + 786, + }, + dictWord{135, 0, 580}, + dictWord{7, 0, 88}, + dictWord{136, 0, 627}, + dictWord{5, 0, 872}, + dictWord{6, 0, 57}, + dictWord{7, 0, 471}, + dictWord{9, 0, 447}, + dictWord{137, 0, 454}, + dictWord{6, 11, 342}, + dictWord{6, 11, 496}, + dictWord{8, 11, 275}, + dictWord{137, 11, 206}, + dictWord{4, 11, 909}, + dictWord{133, 11, 940}, + dictWord{6, 0, 735}, + dictWord{132, 11, 891}, + dictWord{8, 0, 845}, + dictWord{8, 0, 916}, + dictWord{135, 10, 1409}, + dictWord{5, 0, 31}, + dictWord{134, 0, 614}, + dictWord{11, 0, 458}, + dictWord{12, 0, 15}, + dictWord{140, 0, 432}, + dictWord{8, 0, 330}, + dictWord{140, 0, 477}, + dictWord{4, 0, 530}, + dictWord{5, 0, 521}, + dictWord{ + 7, + 0, + 1200, + }, + dictWord{10, 0, 460}, + dictWord{132, 11, 687}, + dictWord{6, 0, 424}, + dictWord{135, 0, 1866}, + dictWord{9, 0, 569}, + dictWord{12, 0, 12}, + dictWord{ + 12, + 0, + 81, + }, + dictWord{12, 0, 319}, + dictWord{13, 0, 69}, + dictWord{14, 0, 259}, + dictWord{16, 0, 87}, + dictWord{17, 0, 1}, + dictWord{17, 0, 21}, + dictWord{17, 0, 24}, + dictWord{ + 18, + 0, + 15, + }, + dictWord{18, 0, 56}, + dictWord{18, 0, 59}, + dictWord{18, 0, 127}, + dictWord{18, 0, 154}, + dictWord{19, 0, 19}, + dictWord{148, 0, 31}, + dictWord{7, 0, 1302}, + dictWord{136, 10, 38}, + dictWord{134, 11, 253}, + dictWord{5, 10, 261}, + dictWord{7, 10, 78}, + dictWord{7, 10, 199}, + dictWord{8, 10, 815}, + dictWord{9, 10, 126}, + dictWord{138, 10, 342}, + dictWord{5, 0, 595}, + dictWord{135, 0, 1863}, + dictWord{6, 11, 41}, + dictWord{141, 11, 160}, + dictWord{5, 0, 13}, + dictWord{134, 0, 142}, + dictWord{6, 0, 97}, + dictWord{7, 0, 116}, + dictWord{8, 0, 322}, + dictWord{8, 0, 755}, + dictWord{9, 0, 548}, + dictWord{10, 0, 714}, + dictWord{11, 0, 884}, + dictWord{13, 0, 324}, + dictWord{7, 11, 1304}, + dictWord{138, 11, 477}, + dictWord{132, 10, 628}, + dictWord{134, 11, 1718}, + dictWord{7, 10, 266}, + dictWord{136, 10, 804}, + dictWord{135, 10, 208}, + dictWord{7, 0, 1021}, + dictWord{6, 10, 79}, + dictWord{135, 10, 1519}, + dictWord{7, 0, 1472}, + dictWord{135, 0, 1554}, + dictWord{6, 11, 362}, + dictWord{146, 11, 51}, + dictWord{7, 0, 1071}, + dictWord{7, 0, 1541}, + dictWord{7, 0, 1767}, + dictWord{7, 0, 1806}, + dictWord{11, 0, 162}, + dictWord{11, 0, 242}, + dictWord{11, 0, 452}, + dictWord{12, 0, 605}, + dictWord{15, 0, 26}, + dictWord{144, 0, 44}, + dictWord{136, 10, 741}, + dictWord{133, 11, 115}, + dictWord{145, 0, 115}, + dictWord{134, 10, 376}, + dictWord{6, 0, 1406}, + dictWord{134, 0, 1543}, + dictWord{5, 11, 193}, + dictWord{12, 11, 178}, + dictWord{13, 11, 130}, + dictWord{ + 145, + 11, + 84, + }, + dictWord{135, 0, 1111}, + dictWord{8, 0, 1}, + dictWord{9, 0, 650}, + dictWord{10, 0, 326}, + dictWord{5, 11, 705}, + dictWord{137, 11, 606}, + dictWord{5, 0, 488}, + dictWord{6, 0, 527}, + dictWord{7, 0, 489}, + dictWord{7, 0, 1636}, + dictWord{8, 0, 121}, + dictWord{8, 0, 144}, + dictWord{8, 0, 359}, + dictWord{9, 0, 193}, + dictWord{9, 0, 241}, + dictWord{9, 0, 336}, + dictWord{9, 0, 882}, + dictWord{11, 0, 266}, + dictWord{11, 0, 372}, + dictWord{11, 0, 944}, + dictWord{12, 0, 401}, + dictWord{140, 0, 641}, + dictWord{135, 11, 174}, + dictWord{6, 0, 267}, + dictWord{7, 10, 244}, + dictWord{7, 10, 632}, + dictWord{7, 10, 1609}, + dictWord{8, 10, 178}, + dictWord{8, 10, 638}, + dictWord{141, 10, 58}, + dictWord{134, 0, 1983}, + dictWord{134, 0, 1155}, + dictWord{134, 0, 1575}, + dictWord{134, 0, 1438}, + dictWord{9, 0, 31}, + dictWord{ + 10, + 0, + 244, + }, + dictWord{10, 0, 699}, + dictWord{12, 0, 149}, + dictWord{141, 0, 497}, + dictWord{133, 0, 377}, + dictWord{4, 11, 122}, + dictWord{5, 11, 796}, + dictWord{ + 5, + 11, + 952, + }, + dictWord{6, 11, 1660}, + dictWord{6, 11, 1671}, + dictWord{8, 11, 567}, + dictWord{9, 11, 687}, + dictWord{9, 11, 742}, + dictWord{10, 11, 686}, + dictWord{ + 11, + 11, + 356, + }, + dictWord{11, 11, 682}, + dictWord{140, 11, 281}, + dictWord{145, 0, 101}, + dictWord{11, 11, 0}, + dictWord{144, 11, 78}, + dictWord{5, 11, 179}, + dictWord{ + 5, + 10, + 791, + }, + dictWord{7, 11, 1095}, + dictWord{135, 11, 1213}, + dictWord{8, 11, 372}, + dictWord{9, 11, 122}, + dictWord{138, 11, 175}, + dictWord{7, 10, 686}, + dictWord{8, 10, 33}, + dictWord{8, 10, 238}, + dictWord{10, 10, 616}, + dictWord{11, 10, 467}, + dictWord{11, 10, 881}, + dictWord{13, 10, 217}, + dictWord{13, 10, 253}, + dictWord{142, 10, 268}, + dictWord{9, 0, 476}, + dictWord{4, 11, 66}, + dictWord{7, 11, 722}, + dictWord{135, 11, 904}, + dictWord{7, 11, 352}, + dictWord{137, 11, 684}, + dictWord{135, 0, 2023}, + dictWord{135, 0, 1836}, + dictWord{132, 10, 447}, + dictWord{5, 0, 843}, + dictWord{144, 0, 35}, + dictWord{137, 11, 779}, + dictWord{ + 141, + 11, + 35, + }, + dictWord{4, 10, 128}, + dictWord{5, 10, 415}, + dictWord{6, 10, 462}, + dictWord{7, 10, 294}, + dictWord{7, 10, 578}, + dictWord{10, 10, 710}, + dictWord{ + 139, + 10, + 86, + }, + dictWord{132, 0, 554}, + dictWord{133, 0, 536}, + dictWord{136, 10, 587}, + dictWord{5, 0, 207}, + dictWord{9, 0, 79}, + dictWord{11, 0, 625}, + dictWord{ + 145, + 0, + 7, + }, + dictWord{7, 0, 1371}, + dictWord{6, 10, 427}, + dictWord{138, 10, 692}, + dictWord{4, 0, 424}, + dictWord{4, 10, 195}, + dictWord{135, 10, 802}, + dictWord{ + 8, + 0, + 785, + }, + dictWord{133, 11, 564}, + dictWord{135, 0, 336}, + dictWord{4, 0, 896}, + dictWord{6, 0, 1777}, + dictWord{134, 11, 556}, + dictWord{137, 11, 103}, + dictWord{134, 10, 1683}, + dictWord{7, 11, 544}, + dictWord{8, 11, 719}, + dictWord{138, 11, 61}, + dictWord{138, 10, 472}, + dictWord{4, 11, 5}, + dictWord{5, 11, 498}, + dictWord{136, 11, 637}, + dictWord{7, 0, 750}, + dictWord{9, 0, 223}, + dictWord{11, 0, 27}, + dictWord{11, 0, 466}, + dictWord{12, 0, 624}, + dictWord{14, 0, 265}, + dictWord{ + 146, + 0, + 61, + }, + dictWord{12, 0, 238}, + dictWord{18, 0, 155}, + dictWord{12, 11, 238}, + dictWord{146, 11, 155}, + dictWord{151, 10, 28}, + dictWord{133, 11, 927}, + dictWord{12, 0, 383}, + dictWord{5, 10, 3}, + dictWord{8, 10, 578}, + dictWord{9, 10, 118}, + dictWord{10, 10, 705}, + dictWord{141, 10, 279}, + dictWord{4, 11, 893}, + dictWord{ + 5, + 11, + 780, + }, + dictWord{133, 11, 893}, + dictWord{4, 0, 603}, + dictWord{133, 0, 661}, + dictWord{4, 0, 11}, + dictWord{6, 0, 128}, + dictWord{7, 0, 231}, + dictWord{ + 7, + 0, + 1533, + }, + dictWord{10, 0, 725}, + dictWord{5, 10, 229}, + dictWord{5, 11, 238}, + dictWord{135, 11, 1350}, + dictWord{8, 10, 102}, + dictWord{10, 10, 578}, + dictWord{ + 10, + 10, + 672, + }, + dictWord{12, 10, 496}, + dictWord{13, 10, 408}, + dictWord{14, 10, 121}, + dictWord{145, 10, 106}, + dictWord{132, 0, 476}, + dictWord{134, 0, 1552}, + dictWord{134, 11, 1729}, + dictWord{8, 10, 115}, + dictWord{8, 10, 350}, + dictWord{9, 10, 489}, + dictWord{10, 10, 128}, + dictWord{11, 10, 306}, + dictWord{ + 12, + 10, + 373, + }, + dictWord{14, 10, 30}, + dictWord{17, 10, 79}, + dictWord{19, 10, 80}, + dictWord{150, 10, 55}, + dictWord{135, 0, 1807}, + dictWord{4, 0, 680}, + dictWord{ + 4, + 11, + 60, + }, + dictWord{7, 11, 760}, + dictWord{7, 11, 1800}, + dictWord{8, 11, 314}, + dictWord{9, 11, 700}, + dictWord{139, 11, 487}, + dictWord{4, 10, 230}, + dictWord{ + 5, + 10, + 702, + }, + dictWord{148, 11, 94}, + dictWord{132, 11, 228}, + dictWord{139, 0, 435}, + dictWord{9, 0, 20}, + dictWord{10, 0, 324}, + dictWord{10, 0, 807}, + dictWord{ + 139, + 0, + 488, + }, + dictWord{6, 10, 1728}, + dictWord{136, 11, 419}, + dictWord{4, 10, 484}, + dictWord{18, 10, 26}, + dictWord{19, 10, 42}, + dictWord{20, 10, 43}, + dictWord{ + 21, + 10, + 0, + }, + dictWord{23, 10, 27}, + dictWord{152, 10, 14}, + dictWord{135, 0, 1431}, + dictWord{133, 11, 828}, + dictWord{5, 0, 112}, + dictWord{6, 0, 103}, + dictWord{ + 6, + 0, + 150, + }, + dictWord{7, 0, 1303}, + dictWord{9, 0, 292}, + dictWord{10, 0, 481}, + dictWord{20, 0, 13}, + dictWord{7, 11, 176}, + dictWord{7, 11, 178}, + dictWord{7, 11, 1110}, + dictWord{10, 11, 481}, + dictWord{148, 11, 13}, + dictWord{138, 0, 356}, + dictWord{4, 11, 51}, + dictWord{5, 11, 39}, + dictWord{6, 11, 4}, + dictWord{7, 11, 591}, + dictWord{ + 7, + 11, + 849, + }, + dictWord{7, 11, 951}, + dictWord{7, 11, 1129}, + dictWord{7, 11, 1613}, + dictWord{7, 11, 1760}, + dictWord{7, 11, 1988}, + dictWord{9, 11, 434}, + dictWord{10, 11, 754}, + dictWord{11, 11, 25}, + dictWord{11, 11, 37}, + dictWord{139, 11, 414}, + dictWord{6, 0, 1963}, + dictWord{134, 0, 2000}, + dictWord{ + 132, + 10, + 633, + }, + dictWord{6, 0, 1244}, + dictWord{133, 11, 902}, + dictWord{135, 11, 928}, + dictWord{140, 0, 18}, + dictWord{138, 0, 204}, + dictWord{135, 11, 1173}, + dictWord{134, 0, 867}, + dictWord{4, 0, 708}, + dictWord{8, 0, 15}, + dictWord{9, 0, 50}, + dictWord{9, 0, 386}, + dictWord{11, 0, 18}, + dictWord{11, 0, 529}, + dictWord{140, 0, 228}, + dictWord{134, 11, 270}, + dictWord{4, 0, 563}, + dictWord{7, 0, 109}, + dictWord{7, 0, 592}, + dictWord{7, 0, 637}, + dictWord{7, 0, 770}, + dictWord{8, 0, 463}, + dictWord{ + 9, + 0, + 60, + }, + dictWord{9, 0, 335}, + dictWord{9, 0, 904}, + dictWord{10, 0, 73}, + dictWord{11, 0, 434}, + dictWord{12, 0, 585}, + dictWord{13, 0, 331}, + dictWord{18, 0, 110}, + dictWord{148, 0, 60}, + dictWord{132, 0, 502}, + dictWord{14, 11, 359}, + dictWord{19, 11, 52}, + dictWord{148, 11, 47}, + dictWord{6, 11, 377}, + dictWord{7, 11, 1025}, + dictWord{9, 11, 613}, + dictWord{145, 11, 104}, + dictWord{6, 0, 347}, + dictWord{10, 0, 161}, + dictWord{5, 10, 70}, + dictWord{5, 10, 622}, + dictWord{6, 10, 334}, + dictWord{ + 7, + 10, + 1032, + }, + dictWord{9, 10, 171}, + dictWord{11, 10, 26}, + dictWord{11, 10, 213}, + dictWord{11, 10, 637}, + dictWord{11, 10, 707}, + dictWord{12, 10, 202}, + dictWord{12, 10, 380}, + dictWord{13, 10, 226}, + dictWord{13, 10, 355}, + dictWord{14, 10, 222}, + dictWord{145, 10, 42}, + dictWord{132, 11, 416}, + dictWord{4, 0, 33}, + dictWord{5, 0, 102}, + dictWord{6, 0, 284}, + dictWord{7, 0, 1079}, + dictWord{7, 0, 1423}, + dictWord{7, 0, 1702}, + dictWord{8, 0, 470}, + dictWord{9, 0, 554}, + dictWord{ + 9, + 0, + 723, + }, + dictWord{11, 0, 333}, + dictWord{142, 11, 372}, + dictWord{5, 11, 152}, + dictWord{5, 11, 197}, + dictWord{7, 11, 340}, + dictWord{7, 11, 867}, + dictWord{ + 10, + 11, + 548, + }, + dictWord{10, 11, 581}, + dictWord{11, 11, 6}, + dictWord{12, 11, 3}, + dictWord{12, 11, 19}, + dictWord{14, 11, 110}, + dictWord{142, 11, 289}, + dictWord{ + 7, + 0, + 246, + }, + dictWord{135, 0, 840}, + dictWord{6, 0, 10}, + dictWord{8, 0, 571}, + dictWord{9, 0, 739}, + dictWord{143, 0, 91}, + dictWord{6, 0, 465}, + dictWord{7, 0, 1465}, + dictWord{ + 4, + 10, + 23, + }, + dictWord{4, 10, 141}, + dictWord{5, 10, 313}, + dictWord{5, 10, 1014}, + dictWord{6, 10, 50}, + dictWord{7, 10, 142}, + dictWord{7, 10, 559}, + dictWord{ + 8, + 10, + 640, + }, + dictWord{9, 10, 460}, + dictWord{9, 10, 783}, + dictWord{11, 10, 741}, + dictWord{12, 10, 183}, + dictWord{141, 10, 488}, + dictWord{133, 0, 626}, + dictWord{ + 136, + 0, + 614, + }, + dictWord{138, 0, 237}, + dictWord{7, 11, 34}, + dictWord{7, 11, 190}, + dictWord{8, 11, 28}, + dictWord{8, 11, 141}, + dictWord{8, 11, 444}, + dictWord{ + 8, + 11, + 811, + }, + dictWord{9, 11, 468}, + dictWord{11, 11, 334}, + dictWord{12, 11, 24}, + dictWord{12, 11, 386}, + dictWord{140, 11, 576}, + dictWord{133, 11, 757}, + dictWord{ + 5, + 0, + 18, + }, + dictWord{6, 0, 526}, + dictWord{13, 0, 24}, + dictWord{13, 0, 110}, + dictWord{19, 0, 5}, + dictWord{147, 0, 44}, + dictWord{6, 0, 506}, + dictWord{134, 11, 506}, + dictWord{135, 11, 1553}, + dictWord{4, 0, 309}, + dictWord{5, 0, 462}, + dictWord{7, 0, 970}, + dictWord{7, 0, 1097}, + dictWord{22, 0, 30}, + dictWord{22, 0, 33}, + dictWord{ + 7, + 11, + 1385, + }, + dictWord{11, 11, 582}, + dictWord{11, 11, 650}, + dictWord{11, 11, 901}, + dictWord{11, 11, 949}, + dictWord{12, 11, 232}, + dictWord{12, 11, 236}, + dictWord{13, 11, 413}, + dictWord{13, 11, 501}, + dictWord{146, 11, 116}, + dictWord{9, 0, 140}, + dictWord{5, 10, 222}, + dictWord{138, 10, 534}, + dictWord{6, 0, 1056}, + dictWord{137, 10, 906}, + dictWord{134, 0, 1704}, + dictWord{138, 10, 503}, + dictWord{134, 0, 1036}, + dictWord{5, 10, 154}, + dictWord{7, 10, 1491}, + dictWord{ + 10, + 10, + 379, + }, + dictWord{138, 10, 485}, + dictWord{4, 11, 383}, + dictWord{133, 10, 716}, + dictWord{134, 0, 1315}, + dictWord{5, 0, 86}, + dictWord{7, 0, 743}, + dictWord{ + 9, + 0, + 85, + }, + dictWord{10, 0, 281}, + dictWord{10, 0, 432}, + dictWord{11, 0, 825}, + dictWord{12, 0, 251}, + dictWord{13, 0, 118}, + dictWord{142, 0, 378}, + dictWord{ + 8, + 0, + 264, + }, + dictWord{4, 10, 91}, + dictWord{5, 10, 388}, + dictWord{5, 10, 845}, + dictWord{6, 10, 206}, + dictWord{6, 10, 252}, + dictWord{6, 10, 365}, + dictWord{7, 10, 136}, + dictWord{7, 10, 531}, + dictWord{136, 10, 621}, + dictWord{5, 0, 524}, + dictWord{133, 0, 744}, + dictWord{5, 11, 277}, + dictWord{141, 11, 247}, + dictWord{ + 132, + 11, + 435, + }, + dictWord{10, 0, 107}, + dictWord{140, 0, 436}, + dictWord{132, 0, 927}, + dictWord{10, 0, 123}, + dictWord{12, 0, 670}, + dictWord{146, 0, 94}, + dictWord{ + 7, + 0, + 1149, + }, + dictWord{9, 0, 156}, + dictWord{138, 0, 957}, + dictWord{5, 11, 265}, + dictWord{6, 11, 212}, + dictWord{135, 11, 28}, + dictWord{133, 0, 778}, + dictWord{ + 133, + 0, + 502, + }, + dictWord{8, 0, 196}, + dictWord{10, 0, 283}, + dictWord{139, 0, 406}, + dictWord{135, 10, 576}, + dictWord{136, 11, 535}, + dictWord{134, 0, 1312}, + dictWord{ + 5, + 10, + 771, + }, + dictWord{5, 10, 863}, + dictWord{5, 10, 898}, + dictWord{6, 10, 1632}, + dictWord{6, 10, 1644}, + dictWord{134, 10, 1780}, + dictWord{5, 0, 855}, + dictWord{5, 10, 331}, + dictWord{135, 11, 1487}, + dictWord{132, 11, 702}, + dictWord{5, 11, 808}, + dictWord{135, 11, 2045}, + dictWord{7, 0, 1400}, + dictWord{ + 9, + 0, + 446, + }, + dictWord{138, 0, 45}, + dictWord{140, 10, 632}, + dictWord{132, 0, 1003}, + dictWord{5, 11, 166}, + dictWord{8, 11, 739}, + dictWord{140, 11, 511}, + dictWord{ + 5, + 10, + 107, + }, + dictWord{7, 10, 201}, + dictWord{136, 10, 518}, + dictWord{6, 10, 446}, + dictWord{135, 10, 1817}, + dictWord{134, 0, 1532}, + dictWord{ + 134, + 0, + 1097, + }, + dictWord{4, 11, 119}, + dictWord{5, 11, 170}, + dictWord{5, 11, 447}, + dictWord{7, 11, 1708}, + dictWord{7, 11, 1889}, + dictWord{9, 11, 357}, + dictWord{ + 9, + 11, + 719, + }, + dictWord{12, 11, 486}, + dictWord{140, 11, 596}, + dictWord{9, 10, 851}, + dictWord{141, 10, 510}, + dictWord{7, 0, 612}, + dictWord{8, 0, 545}, + dictWord{ + 8, + 0, + 568, + }, + dictWord{8, 0, 642}, + dictWord{9, 0, 717}, + dictWord{10, 0, 541}, + dictWord{10, 0, 763}, + dictWord{11, 0, 449}, + dictWord{12, 0, 489}, + dictWord{13, 0, 153}, + dictWord{13, 0, 296}, + dictWord{14, 0, 138}, + dictWord{14, 0, 392}, + dictWord{15, 0, 50}, + dictWord{16, 0, 6}, + dictWord{16, 0, 12}, + dictWord{20, 0, 9}, + dictWord{ + 132, + 10, + 504, + }, + dictWord{4, 11, 450}, + dictWord{135, 11, 1158}, + dictWord{11, 0, 54}, + dictWord{13, 0, 173}, + dictWord{13, 0, 294}, + dictWord{5, 10, 883}, + dictWord{ + 5, + 10, + 975, + }, + dictWord{8, 10, 392}, + dictWord{148, 10, 7}, + dictWord{13, 0, 455}, + dictWord{15, 0, 99}, + dictWord{15, 0, 129}, + dictWord{144, 0, 68}, + dictWord{135, 0, 172}, + dictWord{132, 11, 754}, + dictWord{5, 10, 922}, + dictWord{134, 10, 1707}, + dictWord{134, 0, 1029}, + dictWord{17, 11, 39}, + dictWord{148, 11, 36}, + dictWord{ + 4, + 0, + 568, + }, + dictWord{5, 10, 993}, + dictWord{7, 10, 515}, + dictWord{137, 10, 91}, + dictWord{132, 0, 732}, + dictWord{10, 0, 617}, + dictWord{138, 11, 617}, + dictWord{ + 134, + 0, + 974, + }, + dictWord{7, 0, 989}, + dictWord{10, 0, 377}, + dictWord{12, 0, 363}, + dictWord{13, 0, 68}, + dictWord{13, 0, 94}, + dictWord{14, 0, 108}, + dictWord{ + 142, + 0, + 306, + }, + dictWord{136, 0, 733}, + dictWord{132, 0, 428}, + dictWord{7, 0, 1789}, + dictWord{135, 11, 1062}, + dictWord{7, 0, 2015}, + dictWord{140, 0, 665}, + dictWord{135, 10, 1433}, + dictWord{5, 0, 287}, + dictWord{7, 10, 921}, + dictWord{8, 10, 580}, + dictWord{8, 10, 593}, + dictWord{8, 10, 630}, + dictWord{138, 10, 28}, + dictWord{138, 0, 806}, + dictWord{4, 10, 911}, + dictWord{5, 10, 867}, + dictWord{5, 10, 1013}, + dictWord{7, 10, 2034}, + dictWord{8, 10, 798}, + dictWord{136, 10, 813}, + dictWord{134, 0, 1539}, + dictWord{8, 11, 523}, + dictWord{150, 11, 34}, + dictWord{135, 11, 740}, + dictWord{7, 11, 238}, + dictWord{7, 11, 2033}, + dictWord{ + 8, + 11, + 120, + }, + dictWord{8, 11, 188}, + dictWord{8, 11, 659}, + dictWord{9, 11, 598}, + dictWord{10, 11, 466}, + dictWord{12, 11, 342}, + dictWord{12, 11, 588}, + dictWord{ + 13, + 11, + 503, + }, + dictWord{14, 11, 246}, + dictWord{143, 11, 92}, + dictWord{7, 0, 1563}, + dictWord{141, 0, 182}, + dictWord{5, 10, 135}, + dictWord{6, 10, 519}, + dictWord{ + 7, + 10, + 1722, + }, + dictWord{10, 10, 271}, + dictWord{11, 10, 261}, + dictWord{145, 10, 54}, + dictWord{14, 10, 338}, + dictWord{148, 10, 81}, + dictWord{7, 0, 484}, + dictWord{ + 4, + 10, + 300, + }, + dictWord{133, 10, 436}, + dictWord{145, 11, 114}, + dictWord{6, 0, 1623}, + dictWord{134, 0, 1681}, + dictWord{133, 11, 640}, + dictWord{4, 11, 201}, + dictWord{7, 11, 1744}, + dictWord{8, 11, 602}, + dictWord{11, 11, 247}, + dictWord{11, 11, 826}, + dictWord{145, 11, 65}, + dictWord{8, 11, 164}, + dictWord{ + 146, + 11, + 62, + }, + dictWord{6, 0, 1833}, + dictWord{6, 0, 1861}, + dictWord{136, 0, 878}, + dictWord{134, 0, 1569}, + dictWord{8, 10, 357}, + dictWord{10, 10, 745}, + dictWord{ + 14, + 10, + 426, + }, + dictWord{17, 10, 94}, + dictWord{147, 10, 57}, + dictWord{12, 0, 93}, + dictWord{12, 0, 501}, + dictWord{13, 0, 362}, + dictWord{14, 0, 151}, + dictWord{15, 0, 40}, + dictWord{15, 0, 59}, + dictWord{16, 0, 46}, + dictWord{17, 0, 25}, + dictWord{18, 0, 14}, + dictWord{18, 0, 134}, + dictWord{19, 0, 25}, + dictWord{19, 0, 69}, + dictWord{ + 20, + 0, + 16, + }, + dictWord{20, 0, 19}, + dictWord{20, 0, 66}, + dictWord{21, 0, 23}, + dictWord{21, 0, 25}, + dictWord{150, 0, 42}, + dictWord{6, 0, 1748}, + dictWord{8, 0, 715}, + dictWord{ + 9, + 0, + 802, + }, + dictWord{10, 0, 46}, + dictWord{10, 0, 819}, + dictWord{13, 0, 308}, + dictWord{14, 0, 351}, + dictWord{14, 0, 363}, + dictWord{146, 0, 67}, + dictWord{ + 132, + 0, + 994, + }, + dictWord{4, 0, 63}, + dictWord{133, 0, 347}, + dictWord{132, 0, 591}, + dictWord{133, 0, 749}, + dictWord{7, 11, 1577}, + dictWord{10, 11, 304}, + dictWord{ + 10, + 11, + 549, + }, + dictWord{11, 11, 424}, + dictWord{12, 11, 365}, + dictWord{13, 11, 220}, + dictWord{13, 11, 240}, + dictWord{142, 11, 33}, + dictWord{133, 0, 366}, + dictWord{ + 7, + 0, + 557, + }, + dictWord{12, 0, 547}, + dictWord{14, 0, 86}, + dictWord{133, 10, 387}, + dictWord{135, 0, 1747}, + dictWord{132, 11, 907}, + dictWord{5, 11, 100}, + dictWord{10, 11, 329}, + dictWord{12, 11, 416}, + dictWord{149, 11, 29}, + dictWord{4, 10, 6}, + dictWord{5, 10, 708}, + dictWord{136, 10, 75}, + dictWord{7, 10, 1351}, + dictWord{9, 10, 581}, + dictWord{10, 10, 639}, + dictWord{11, 10, 453}, + dictWord{140, 10, 584}, + dictWord{7, 0, 89}, + dictWord{132, 10, 303}, + dictWord{138, 10, 772}, + dictWord{132, 11, 176}, + dictWord{5, 11, 636}, + dictWord{5, 11, 998}, + dictWord{8, 11, 26}, + dictWord{137, 11, 358}, + dictWord{7, 11, 9}, + dictWord{7, 11, 1508}, + dictWord{9, 11, 317}, + dictWord{10, 11, 210}, + dictWord{10, 11, 292}, + dictWord{10, 11, 533}, + dictWord{11, 11, 555}, + dictWord{12, 11, 526}, + dictWord{ + 12, + 11, + 607, + }, + dictWord{13, 11, 263}, + dictWord{13, 11, 459}, + dictWord{142, 11, 271}, + dictWord{134, 0, 1463}, + dictWord{6, 0, 772}, + dictWord{6, 0, 1137}, + dictWord{ + 139, + 11, + 595, + }, + dictWord{7, 0, 977}, + dictWord{139, 11, 66}, + dictWord{138, 0, 893}, + dictWord{20, 0, 48}, + dictWord{148, 11, 48}, + dictWord{5, 0, 824}, + dictWord{ + 133, + 0, + 941, + }, + dictWord{134, 11, 295}, + dictWord{7, 0, 1543}, + dictWord{7, 0, 1785}, + dictWord{10, 0, 690}, + dictWord{4, 10, 106}, + dictWord{139, 10, 717}, + dictWord{ + 7, + 0, + 440, + }, + dictWord{8, 0, 230}, + dictWord{139, 0, 106}, + dictWord{5, 10, 890}, + dictWord{133, 10, 988}, + dictWord{6, 10, 626}, + dictWord{142, 10, 431}, + dictWord{ + 10, + 11, + 127, + }, + dictWord{141, 11, 27}, + dictWord{17, 0, 32}, + dictWord{10, 10, 706}, + dictWord{150, 10, 44}, + dictWord{132, 0, 216}, + dictWord{137, 0, 332}, + dictWord{4, 10, 698}, + dictWord{136, 11, 119}, + dictWord{139, 11, 267}, + dictWord{138, 10, 17}, + dictWord{11, 11, 526}, + dictWord{11, 11, 939}, + dictWord{ + 141, + 11, + 290, + }, + dictWord{7, 11, 1167}, + dictWord{11, 11, 934}, + dictWord{13, 11, 391}, + dictWord{145, 11, 76}, + dictWord{139, 11, 39}, + dictWord{134, 10, 84}, + dictWord{ + 4, + 0, + 914, + }, + dictWord{5, 0, 800}, + dictWord{133, 0, 852}, + dictWord{10, 0, 416}, + dictWord{141, 0, 115}, + dictWord{7, 0, 564}, + dictWord{142, 0, 168}, + dictWord{ + 4, + 0, + 918, + }, + dictWord{133, 0, 876}, + dictWord{134, 0, 1764}, + dictWord{152, 0, 3}, + dictWord{4, 0, 92}, + dictWord{5, 0, 274}, + dictWord{7, 11, 126}, + dictWord{136, 11, 84}, + dictWord{140, 10, 498}, + dictWord{136, 11, 790}, + dictWord{8, 0, 501}, + dictWord{5, 10, 986}, + dictWord{6, 10, 130}, + dictWord{7, 10, 1582}, + dictWord{ + 8, + 10, + 458, + }, + dictWord{10, 10, 101}, + dictWord{10, 10, 318}, + dictWord{138, 10, 823}, + dictWord{6, 11, 64}, + dictWord{12, 11, 377}, + dictWord{141, 11, 309}, + dictWord{ + 5, + 0, + 743, + }, + dictWord{138, 0, 851}, + dictWord{4, 0, 49}, + dictWord{7, 0, 280}, + dictWord{135, 0, 1633}, + dictWord{134, 0, 879}, + dictWord{136, 0, 47}, + dictWord{ + 7, + 10, + 1644, + }, + dictWord{137, 10, 129}, + dictWord{132, 0, 865}, + dictWord{134, 0, 1202}, + dictWord{9, 11, 34}, + dictWord{139, 11, 484}, + dictWord{135, 10, 997}, + dictWord{5, 0, 272}, + dictWord{5, 0, 908}, + dictWord{5, 0, 942}, + dictWord{8, 0, 197}, + dictWord{9, 0, 47}, + dictWord{11, 0, 538}, + dictWord{139, 0, 742}, + dictWord{ + 6, + 11, + 1700, + }, + dictWord{7, 11, 26}, + dictWord{7, 11, 293}, + dictWord{7, 11, 382}, + dictWord{7, 11, 1026}, + dictWord{7, 11, 1087}, + dictWord{7, 11, 2027}, + dictWord{ + 8, + 11, + 24, + }, + dictWord{8, 11, 114}, + dictWord{8, 11, 252}, + dictWord{8, 11, 727}, + dictWord{8, 11, 729}, + dictWord{9, 11, 30}, + dictWord{9, 11, 199}, + dictWord{9, 11, 231}, + dictWord{9, 11, 251}, + dictWord{9, 11, 334}, + dictWord{9, 11, 361}, + dictWord{9, 11, 488}, + dictWord{9, 11, 712}, + dictWord{10, 11, 55}, + dictWord{10, 11, 60}, + dictWord{ + 10, + 11, + 232, + }, + dictWord{10, 11, 332}, + dictWord{10, 11, 384}, + dictWord{10, 11, 396}, + dictWord{10, 11, 504}, + dictWord{10, 11, 542}, + dictWord{10, 11, 652}, + dictWord{11, 11, 20}, + dictWord{11, 11, 48}, + dictWord{11, 11, 207}, + dictWord{11, 11, 291}, + dictWord{11, 11, 298}, + dictWord{11, 11, 342}, + dictWord{ + 11, + 11, + 365, + }, + dictWord{11, 11, 394}, + dictWord{11, 11, 620}, + dictWord{11, 11, 705}, + dictWord{11, 11, 1017}, + dictWord{12, 11, 123}, + dictWord{12, 11, 340}, + dictWord{12, 11, 406}, + dictWord{12, 11, 643}, + dictWord{13, 11, 61}, + dictWord{13, 11, 269}, + dictWord{13, 11, 311}, + dictWord{13, 11, 319}, + dictWord{13, 11, 486}, + dictWord{14, 11, 234}, + dictWord{15, 11, 62}, + dictWord{15, 11, 85}, + dictWord{16, 11, 71}, + dictWord{18, 11, 119}, + dictWord{148, 11, 105}, + dictWord{ + 6, + 0, + 1455, + }, + dictWord{150, 11, 37}, + dictWord{135, 10, 1927}, + dictWord{135, 0, 1911}, + dictWord{137, 0, 891}, + dictWord{7, 10, 1756}, + dictWord{137, 10, 98}, + dictWord{7, 10, 1046}, + dictWord{139, 10, 160}, + dictWord{132, 0, 761}, + dictWord{6, 11, 379}, + dictWord{7, 11, 270}, + dictWord{7, 11, 1116}, + dictWord{ + 8, + 11, + 176, + }, + dictWord{8, 11, 183}, + dictWord{9, 11, 432}, + dictWord{9, 11, 661}, + dictWord{12, 11, 247}, + dictWord{12, 11, 617}, + dictWord{146, 11, 125}, + dictWord{ + 6, + 10, + 45, + }, + dictWord{7, 10, 433}, + dictWord{8, 10, 129}, + dictWord{9, 10, 21}, + dictWord{10, 10, 392}, + dictWord{11, 10, 79}, + dictWord{12, 10, 499}, + dictWord{ + 13, + 10, + 199, + }, + dictWord{141, 10, 451}, + dictWord{4, 0, 407}, + dictWord{5, 11, 792}, + dictWord{133, 11, 900}, + dictWord{132, 0, 560}, + dictWord{135, 0, 183}, + dictWord{ + 13, + 0, + 490, + }, + dictWord{7, 10, 558}, + dictWord{136, 10, 353}, + dictWord{4, 0, 475}, + dictWord{6, 0, 731}, + dictWord{11, 0, 35}, + dictWord{13, 0, 71}, + dictWord{13, 0, 177}, + dictWord{14, 0, 422}, + dictWord{133, 10, 785}, + dictWord{8, 10, 81}, + dictWord{9, 10, 189}, + dictWord{9, 10, 201}, + dictWord{11, 10, 478}, + dictWord{11, 10, 712}, + dictWord{141, 10, 338}, + dictWord{4, 0, 418}, + dictWord{4, 0, 819}, + dictWord{133, 10, 353}, + dictWord{151, 10, 26}, + dictWord{4, 11, 901}, + dictWord{ + 133, + 11, + 776, + }, + dictWord{132, 0, 575}, + dictWord{7, 0, 818}, + dictWord{16, 0, 92}, + dictWord{17, 0, 14}, + dictWord{17, 0, 45}, + dictWord{18, 0, 75}, + dictWord{148, 0, 18}, + dictWord{ + 6, + 0, + 222, + }, + dictWord{7, 0, 636}, + dictWord{7, 0, 1620}, + dictWord{8, 0, 409}, + dictWord{9, 0, 693}, + dictWord{139, 0, 77}, + dictWord{6, 10, 25}, + dictWord{7, 10, 855}, + dictWord{7, 10, 1258}, + dictWord{144, 10, 32}, + dictWord{6, 0, 1880}, + dictWord{6, 0, 1887}, + dictWord{6, 0, 1918}, + dictWord{6, 0, 1924}, + dictWord{9, 0, 967}, + dictWord{9, 0, 995}, + dictWord{9, 0, 1015}, + dictWord{12, 0, 826}, + dictWord{12, 0, 849}, + dictWord{12, 0, 857}, + dictWord{12, 0, 860}, + dictWord{12, 0, 886}, + dictWord{ + 12, + 0, + 932, + }, + dictWord{18, 0, 228}, + dictWord{18, 0, 231}, + dictWord{146, 0, 240}, + dictWord{134, 0, 633}, + dictWord{134, 0, 1308}, + dictWord{4, 11, 37}, + dictWord{ + 5, + 11, + 334, + }, + dictWord{135, 11, 1253}, + dictWord{10, 0, 86}, + dictWord{4, 10, 4}, + dictWord{7, 10, 1118}, + dictWord{7, 10, 1320}, + dictWord{7, 10, 1706}, + dictWord{ + 8, + 10, + 277, + }, + dictWord{9, 10, 622}, + dictWord{11, 10, 724}, + dictWord{12, 10, 350}, + dictWord{12, 10, 397}, + dictWord{13, 10, 28}, + dictWord{13, 10, 159}, + dictWord{ + 15, + 10, + 89, + }, + dictWord{18, 10, 5}, + dictWord{19, 10, 9}, + dictWord{20, 10, 34}, + dictWord{150, 10, 47}, + dictWord{132, 11, 508}, + dictWord{137, 11, 448}, + dictWord{ + 12, + 11, + 107, + }, + dictWord{146, 11, 31}, + dictWord{132, 0, 817}, + dictWord{134, 0, 663}, + dictWord{133, 0, 882}, + dictWord{134, 0, 914}, + dictWord{132, 11, 540}, + dictWord{132, 11, 533}, + dictWord{136, 11, 608}, + dictWord{8, 0, 885}, + dictWord{138, 0, 865}, + dictWord{132, 0, 426}, + dictWord{6, 0, 58}, + dictWord{7, 0, 745}, + dictWord{7, 0, 1969}, + dictWord{8, 0, 399}, + dictWord{8, 0, 675}, + dictWord{9, 0, 479}, + dictWord{9, 0, 731}, + dictWord{10, 0, 330}, + dictWord{10, 0, 593}, + dictWord{ + 10, + 0, + 817, + }, + dictWord{11, 0, 32}, + dictWord{11, 0, 133}, + dictWord{11, 0, 221}, + dictWord{145, 0, 68}, + dictWord{134, 10, 255}, + dictWord{7, 0, 102}, + dictWord{ + 137, + 0, + 538, + }, + dictWord{137, 10, 216}, + dictWord{7, 11, 253}, + dictWord{136, 11, 549}, + dictWord{135, 11, 912}, + dictWord{9, 10, 183}, + dictWord{139, 10, 286}, + dictWord{11, 10, 956}, + dictWord{151, 10, 3}, + dictWord{8, 11, 527}, + dictWord{18, 11, 60}, + dictWord{147, 11, 24}, + dictWord{4, 10, 536}, + dictWord{7, 10, 1141}, + dictWord{10, 10, 723}, + dictWord{139, 10, 371}, + dictWord{133, 11, 920}, + dictWord{7, 0, 876}, + dictWord{135, 10, 285}, + dictWord{135, 10, 560}, + dictWord{ + 132, + 10, + 690, + }, + dictWord{142, 11, 126}, + dictWord{11, 10, 33}, + dictWord{12, 10, 571}, + dictWord{149, 10, 1}, + dictWord{133, 0, 566}, + dictWord{9, 0, 139}, + dictWord{ + 10, + 0, + 399, + }, + dictWord{11, 0, 469}, + dictWord{12, 0, 634}, + dictWord{13, 0, 223}, + dictWord{132, 11, 483}, + dictWord{6, 0, 48}, + dictWord{135, 0, 63}, + dictWord{18, 0, 12}, + dictWord{7, 10, 1862}, + dictWord{12, 10, 491}, + dictWord{12, 10, 520}, + dictWord{13, 10, 383}, + dictWord{142, 10, 244}, + dictWord{135, 11, 1665}, + dictWord{132, 11, 448}, + dictWord{9, 11, 495}, + dictWord{146, 11, 104}, + dictWord{6, 0, 114}, + dictWord{7, 0, 1224}, + dictWord{7, 0, 1556}, + dictWord{136, 0, 3}, + dictWord{ + 4, + 10, + 190, + }, + dictWord{133, 10, 554}, + dictWord{8, 0, 576}, + dictWord{9, 0, 267}, + dictWord{133, 10, 1001}, + dictWord{133, 10, 446}, + dictWord{133, 0, 933}, + dictWord{139, 11, 1009}, + dictWord{8, 11, 653}, + dictWord{13, 11, 93}, + dictWord{147, 11, 14}, + dictWord{6, 0, 692}, + dictWord{6, 0, 821}, + dictWord{134, 0, 1077}, + dictWord{5, 11, 172}, + dictWord{135, 11, 801}, + dictWord{138, 0, 752}, + dictWord{4, 0, 375}, + dictWord{134, 0, 638}, + dictWord{134, 0, 1011}, + dictWord{ + 140, + 11, + 540, + }, + dictWord{9, 0, 96}, + dictWord{133, 11, 260}, + dictWord{139, 11, 587}, + dictWord{135, 10, 1231}, + dictWord{12, 0, 30}, + dictWord{13, 0, 148}, + dictWord{ + 14, + 0, + 87, + }, + dictWord{14, 0, 182}, + dictWord{16, 0, 42}, + dictWord{20, 0, 70}, + dictWord{132, 10, 304}, + dictWord{6, 0, 1398}, + dictWord{7, 0, 56}, + dictWord{7, 0, 1989}, + dictWord{8, 0, 337}, + dictWord{8, 0, 738}, + dictWord{9, 0, 600}, + dictWord{12, 0, 37}, + dictWord{13, 0, 447}, + dictWord{142, 0, 92}, + dictWord{138, 0, 666}, + dictWord{ + 5, + 0, + 394, + }, + dictWord{7, 0, 487}, + dictWord{136, 0, 246}, + dictWord{9, 0, 437}, + dictWord{6, 10, 53}, + dictWord{6, 10, 199}, + dictWord{7, 10, 1408}, + dictWord{8, 10, 32}, + dictWord{8, 10, 93}, + dictWord{10, 10, 397}, + dictWord{10, 10, 629}, + dictWord{11, 10, 593}, + dictWord{11, 10, 763}, + dictWord{13, 10, 326}, + dictWord{145, 10, 35}, + dictWord{134, 10, 105}, + dictWord{9, 0, 320}, + dictWord{10, 0, 506}, + dictWord{138, 10, 794}, + dictWord{7, 11, 57}, + dictWord{8, 11, 167}, + dictWord{8, 11, 375}, + dictWord{9, 11, 82}, + dictWord{9, 11, 561}, + dictWord{10, 11, 620}, + dictWord{10, 11, 770}, + dictWord{11, 10, 704}, + dictWord{141, 10, 396}, + dictWord{6, 0, 1003}, + dictWord{5, 10, 114}, + dictWord{5, 10, 255}, + dictWord{141, 10, 285}, + dictWord{7, 0, 866}, + dictWord{135, 0, 1163}, + dictWord{133, 11, 531}, + dictWord{ + 132, + 0, + 328, + }, + dictWord{7, 10, 2035}, + dictWord{8, 10, 19}, + dictWord{9, 10, 89}, + dictWord{138, 10, 831}, + dictWord{8, 11, 194}, + dictWord{136, 11, 756}, + dictWord{ + 136, + 0, + 1000, + }, + dictWord{5, 11, 453}, + dictWord{134, 11, 441}, + dictWord{4, 0, 101}, + dictWord{5, 0, 833}, + dictWord{7, 0, 1171}, + dictWord{136, 0, 744}, + dictWord{ + 133, + 0, + 726, + }, + dictWord{136, 10, 746}, + dictWord{138, 0, 176}, + dictWord{6, 0, 9}, + dictWord{6, 0, 397}, + dictWord{7, 0, 53}, + dictWord{7, 0, 1742}, + dictWord{10, 0, 632}, + dictWord{11, 0, 828}, + dictWord{140, 0, 146}, + dictWord{135, 11, 22}, + dictWord{145, 11, 64}, + dictWord{132, 0, 839}, + dictWord{11, 0, 417}, + dictWord{12, 0, 223}, + dictWord{140, 0, 265}, + dictWord{4, 11, 102}, + dictWord{7, 11, 815}, + dictWord{7, 11, 1699}, + dictWord{139, 11, 964}, + dictWord{5, 10, 955}, + dictWord{ + 136, + 10, + 814, + }, + dictWord{6, 0, 1931}, + dictWord{6, 0, 2007}, + dictWord{18, 0, 246}, + dictWord{146, 0, 247}, + dictWord{8, 0, 198}, + dictWord{11, 0, 29}, + dictWord{140, 0, 534}, + dictWord{135, 0, 1771}, + dictWord{6, 0, 846}, + dictWord{7, 11, 1010}, + dictWord{11, 11, 733}, + dictWord{11, 11, 759}, + dictWord{12, 11, 563}, + dictWord{ + 13, + 11, + 34, + }, + dictWord{14, 11, 101}, + dictWord{18, 11, 45}, + dictWord{146, 11, 129}, + dictWord{4, 0, 186}, + dictWord{5, 0, 157}, + dictWord{8, 0, 168}, + dictWord{138, 0, 6}, + dictWord{132, 11, 899}, + dictWord{133, 10, 56}, + dictWord{148, 10, 100}, + dictWord{133, 0, 875}, + dictWord{5, 0, 773}, + dictWord{5, 0, 991}, + dictWord{6, 0, 1635}, + dictWord{134, 0, 1788}, + dictWord{6, 0, 1274}, + dictWord{9, 0, 477}, + dictWord{141, 0, 78}, + dictWord{4, 0, 639}, + dictWord{7, 0, 111}, + dictWord{8, 0, 581}, + dictWord{ + 12, + 0, + 177, + }, + dictWord{6, 11, 52}, + dictWord{9, 11, 104}, + dictWord{9, 11, 559}, + dictWord{10, 10, 4}, + dictWord{10, 10, 13}, + dictWord{11, 10, 638}, + dictWord{ + 12, + 11, + 308, + }, + dictWord{19, 11, 87}, + dictWord{148, 10, 57}, + dictWord{132, 11, 604}, + dictWord{4, 11, 301}, + dictWord{133, 10, 738}, + dictWord{133, 10, 758}, + dictWord{134, 0, 1747}, + dictWord{7, 11, 1440}, + dictWord{11, 11, 854}, + dictWord{11, 11, 872}, + dictWord{11, 11, 921}, + dictWord{12, 11, 551}, + dictWord{ + 13, + 11, + 472, + }, + dictWord{142, 11, 367}, + dictWord{7, 0, 1364}, + dictWord{7, 0, 1907}, + dictWord{141, 0, 158}, + dictWord{134, 0, 873}, + dictWord{4, 0, 404}, + dictWord{ + 4, + 0, + 659, + }, + dictWord{7, 0, 552}, + dictWord{135, 0, 675}, + dictWord{135, 10, 1112}, + dictWord{139, 10, 328}, + dictWord{7, 11, 508}, + dictWord{137, 10, 133}, + dictWord{133, 0, 391}, + dictWord{5, 10, 110}, + dictWord{6, 10, 169}, + dictWord{6, 10, 1702}, + dictWord{7, 10, 400}, + dictWord{8, 10, 538}, + dictWord{9, 10, 184}, + dictWord{ + 9, + 10, + 524, + }, + dictWord{140, 10, 218}, + dictWord{6, 11, 310}, + dictWord{7, 11, 1849}, + dictWord{8, 11, 72}, + dictWord{8, 11, 272}, + dictWord{8, 11, 431}, + dictWord{ + 9, + 11, + 12, + }, + dictWord{9, 11, 351}, + dictWord{10, 11, 563}, + dictWord{10, 11, 630}, + dictWord{10, 11, 810}, + dictWord{11, 11, 367}, + dictWord{11, 11, 599}, + dictWord{11, 11, 686}, + dictWord{140, 11, 672}, + dictWord{5, 0, 540}, + dictWord{6, 0, 1697}, + dictWord{136, 0, 668}, + dictWord{132, 0, 883}, + dictWord{134, 0, 78}, + dictWord{12, 0, 628}, + dictWord{18, 0, 79}, + dictWord{6, 10, 133}, + dictWord{9, 10, 353}, + dictWord{139, 10, 993}, + dictWord{6, 11, 181}, + dictWord{7, 11, 537}, + dictWord{ + 8, + 11, + 64, + }, + dictWord{9, 11, 127}, + dictWord{10, 11, 496}, + dictWord{12, 11, 510}, + dictWord{141, 11, 384}, + dictWord{6, 10, 93}, + dictWord{7, 10, 1422}, + dictWord{ + 7, + 10, + 1851, + }, + dictWord{8, 10, 673}, + dictWord{9, 10, 529}, + dictWord{140, 10, 43}, + dictWord{137, 10, 371}, + dictWord{134, 0, 1460}, + dictWord{134, 0, 962}, + dictWord{4, 11, 244}, + dictWord{135, 11, 233}, + dictWord{9, 10, 25}, + dictWord{10, 10, 467}, + dictWord{138, 10, 559}, + dictWord{4, 10, 335}, + dictWord{ + 135, + 10, + 942, + }, + dictWord{133, 0, 460}, + dictWord{135, 11, 334}, + dictWord{134, 11, 1650}, + dictWord{4, 0, 199}, + dictWord{139, 0, 34}, + dictWord{5, 10, 601}, + dictWord{ + 8, + 10, + 39, + }, + dictWord{10, 10, 773}, + dictWord{11, 10, 84}, + dictWord{12, 10, 205}, + dictWord{142, 10, 1}, + dictWord{133, 10, 870}, + dictWord{134, 0, 388}, + dictWord{14, 0, 474}, + dictWord{148, 0, 120}, + dictWord{133, 11, 369}, + dictWord{139, 0, 271}, + dictWord{4, 0, 511}, + dictWord{9, 0, 333}, + dictWord{9, 0, 379}, + dictWord{ + 10, + 0, + 602, + }, + dictWord{11, 0, 441}, + dictWord{11, 0, 723}, + dictWord{11, 0, 976}, + dictWord{12, 0, 357}, + dictWord{132, 10, 181}, + dictWord{134, 0, 608}, + dictWord{134, 10, 1652}, + dictWord{22, 0, 49}, + dictWord{137, 11, 338}, + dictWord{140, 0, 988}, + dictWord{134, 0, 617}, + dictWord{5, 0, 938}, + dictWord{136, 0, 707}, + dictWord{132, 10, 97}, + dictWord{5, 10, 147}, + dictWord{6, 10, 286}, + dictWord{7, 10, 1362}, + dictWord{141, 10, 176}, + dictWord{6, 0, 756}, + dictWord{ + 134, + 0, + 1149, + }, + dictWord{133, 11, 896}, + dictWord{6, 10, 375}, + dictWord{7, 10, 169}, + dictWord{7, 10, 254}, + dictWord{136, 10, 780}, + dictWord{134, 0, 1583}, + dictWord{135, 10, 1447}, + dictWord{139, 0, 285}, + dictWord{7, 11, 1117}, + dictWord{8, 11, 393}, + dictWord{136, 11, 539}, + dictWord{135, 0, 344}, + dictWord{ + 6, + 0, + 469, + }, + dictWord{7, 0, 1709}, + dictWord{138, 0, 515}, + dictWord{5, 10, 629}, + dictWord{135, 10, 1549}, + dictWord{5, 11, 4}, + dictWord{5, 11, 810}, + dictWord{ + 6, + 11, + 13, + }, + dictWord{6, 11, 538}, + dictWord{6, 11, 1690}, + dictWord{6, 11, 1726}, + dictWord{7, 11, 499}, + dictWord{7, 11, 1819}, + dictWord{8, 11, 148}, + dictWord{ + 8, + 11, + 696, + }, + dictWord{8, 11, 791}, + dictWord{12, 11, 125}, + dictWord{13, 11, 54}, + dictWord{143, 11, 9}, + dictWord{135, 11, 1268}, + dictWord{137, 0, 404}, + dictWord{ + 132, + 0, + 500, + }, + dictWord{5, 0, 68}, + dictWord{134, 0, 383}, + dictWord{11, 0, 216}, + dictWord{139, 0, 340}, + dictWord{4, 11, 925}, + dictWord{5, 11, 803}, + dictWord{ + 8, + 11, + 698, + }, + dictWord{138, 11, 828}, + dictWord{4, 0, 337}, + dictWord{6, 0, 353}, + dictWord{7, 0, 1934}, + dictWord{8, 0, 488}, + dictWord{137, 0, 429}, + dictWord{7, 0, 236}, + dictWord{7, 0, 1795}, + dictWord{8, 0, 259}, + dictWord{9, 0, 135}, + dictWord{9, 0, 177}, + dictWord{9, 0, 860}, + dictWord{10, 0, 825}, + dictWord{11, 0, 115}, + dictWord{ + 11, + 0, + 370, + }, + dictWord{11, 0, 405}, + dictWord{11, 0, 604}, + dictWord{12, 0, 10}, + dictWord{12, 0, 667}, + dictWord{12, 0, 669}, + dictWord{13, 0, 76}, + dictWord{14, 0, 310}, + dictWord{15, 0, 76}, + dictWord{15, 0, 147}, + dictWord{148, 0, 23}, + dictWord{4, 0, 15}, + dictWord{4, 0, 490}, + dictWord{5, 0, 22}, + dictWord{6, 0, 244}, + dictWord{7, 0, 40}, + dictWord{7, 0, 200}, + dictWord{7, 0, 906}, + dictWord{7, 0, 1199}, + dictWord{9, 0, 616}, + dictWord{10, 0, 716}, + dictWord{11, 0, 635}, + dictWord{11, 0, 801}, + dictWord{ + 140, + 0, + 458, + }, + dictWord{12, 0, 756}, + dictWord{132, 10, 420}, + dictWord{134, 0, 1504}, + dictWord{6, 0, 757}, + dictWord{133, 11, 383}, + dictWord{6, 0, 1266}, + dictWord{ + 135, + 0, + 1735, + }, + dictWord{5, 0, 598}, + dictWord{7, 0, 791}, + dictWord{8, 0, 108}, + dictWord{9, 0, 123}, + dictWord{7, 10, 1570}, + dictWord{140, 10, 542}, + dictWord{ + 142, + 11, + 410, + }, + dictWord{9, 11, 660}, + dictWord{138, 11, 347}, +} diff --git a/vendor/github.com/andybalholm/brotli/symbol_list.go b/vendor/github.com/andybalholm/brotli/symbol_list.go new file mode 100644 index 000000000..c5cb49e5a --- /dev/null +++ b/vendor/github.com/andybalholm/brotli/symbol_list.go @@ -0,0 +1,22 @@ +package brotli + +/* Copyright 2013 Google Inc. All Rights Reserved. + + Distributed under MIT license. + See file LICENSE for detail or copy at https://opensource.org/licenses/MIT +*/ + +/* Utilities for building Huffman decoding tables. */ + +type symbolList struct { + storage []uint16 + offset int +} + +func symbolListGet(sl symbolList, i int) uint16 { + return sl.storage[i+sl.offset] +} + +func symbolListPut(sl symbolList, i int, val uint16) { + sl.storage[i+sl.offset] = val +} diff --git a/vendor/github.com/andybalholm/brotli/transform.go b/vendor/github.com/andybalholm/brotli/transform.go new file mode 100644 index 000000000..d2c043a62 --- /dev/null +++ b/vendor/github.com/andybalholm/brotli/transform.go @@ -0,0 +1,641 @@ +package brotli + +const ( + transformIdentity = 0 + transformOmitLast1 = 1 + transformOmitLast2 = 2 + transformOmitLast3 = 3 + transformOmitLast4 = 4 + transformOmitLast5 = 5 + transformOmitLast6 = 6 + transformOmitLast7 = 7 + transformOmitLast8 = 8 + transformOmitLast9 = 9 + transformUppercaseFirst = 10 + transformUppercaseAll = 11 + transformOmitFirst1 = 12 + transformOmitFirst2 = 13 + transformOmitFirst3 = 14 + transformOmitFirst4 = 15 + transformOmitFirst5 = 16 + transformOmitFirst6 = 17 + transformOmitFirst7 = 18 + transformOmitFirst8 = 19 + transformOmitFirst9 = 20 + transformShiftFirst = 21 + transformShiftAll = 22 + iota - 22 + numTransformTypes +) + +const transformsMaxCutOff = transformOmitLast9 + +type transforms struct { + prefix_suffix_size uint16 + prefix_suffix []byte + prefix_suffix_map []uint16 + num_transforms uint32 + transforms []byte + params []byte + cutOffTransforms [transformsMaxCutOff + 1]int16 +} + +func transformPrefixId(t *transforms, I int) byte { + return t.transforms[(I*3)+0] +} + +func transformType(t *transforms, I int) byte { + return t.transforms[(I*3)+1] +} + +func transformSuffixId(t *transforms, I int) byte { + return t.transforms[(I*3)+2] +} + +func transformPrefix(t *transforms, I int) []byte { + return t.prefix_suffix[t.prefix_suffix_map[transformPrefixId(t, I)]:] +} + +func transformSuffix(t *transforms, I int) []byte { + return t.prefix_suffix[t.prefix_suffix_map[transformSuffixId(t, I)]:] +} + +/* RFC 7932 transforms string data */ +const kPrefixSuffix string = "\001 \002, \010 of the \004 of \002s \001.\005 and \004 " + "in \001\"\004 to \002\">\001\n\002. \001]\005 for \003 a \006 " + "that \001'\006 with \006 from \004 by \001(\006. T" + "he \004 on \004 as \004 is \004ing \002\n\t\001:\003ed " + "\002=\"\004 at \003ly \001,\002='\005.com/\007. This \005" + " not \003er \003al \004ful \004ive \005less \004es" + "t \004ize \002\xc2\xa0\004ous \005 the \002e \000" + +var kPrefixSuffixMap = [50]uint16{ + 0x00, + 0x02, + 0x05, + 0x0E, + 0x13, + 0x16, + 0x18, + 0x1E, + 0x23, + 0x25, + 0x2A, + 0x2D, + 0x2F, + 0x32, + 0x34, + 0x3A, + 0x3E, + 0x45, + 0x47, + 0x4E, + 0x55, + 0x5A, + 0x5C, + 0x63, + 0x68, + 0x6D, + 0x72, + 0x77, + 0x7A, + 0x7C, + 0x80, + 0x83, + 0x88, + 0x8C, + 0x8E, + 0x91, + 0x97, + 0x9F, + 0xA5, + 0xA9, + 0xAD, + 0xB2, + 0xB7, + 0xBD, + 0xC2, + 0xC7, + 0xCA, + 0xCF, + 0xD5, + 0xD8, +} + +/* RFC 7932 transforms */ +var kTransformsData = []byte{ + 49, + transformIdentity, + 49, + 49, + transformIdentity, + 0, + 0, + transformIdentity, + 0, + 49, + transformOmitFirst1, + 49, + 49, + transformUppercaseFirst, + 0, + 49, + transformIdentity, + 47, + 0, + transformIdentity, + 49, + 4, + transformIdentity, + 0, + 49, + transformIdentity, + 3, + 49, + transformUppercaseFirst, + 49, + 49, + transformIdentity, + 6, + 49, + transformOmitFirst2, + 49, + 49, + transformOmitLast1, + 49, + 1, + transformIdentity, + 0, + 49, + transformIdentity, + 1, + 0, + transformUppercaseFirst, + 0, + 49, + transformIdentity, + 7, + 49, + transformIdentity, + 9, + 48, + transformIdentity, + 0, + 49, + transformIdentity, + 8, + 49, + transformIdentity, + 5, + 49, + transformIdentity, + 10, + 49, + transformIdentity, + 11, + 49, + transformOmitLast3, + 49, + 49, + transformIdentity, + 13, + 49, + transformIdentity, + 14, + 49, + transformOmitFirst3, + 49, + 49, + transformOmitLast2, + 49, + 49, + transformIdentity, + 15, + 49, + transformIdentity, + 16, + 0, + transformUppercaseFirst, + 49, + 49, + transformIdentity, + 12, + 5, + transformIdentity, + 49, + 0, + transformIdentity, + 1, + 49, + transformOmitFirst4, + 49, + 49, + transformIdentity, + 18, + 49, + transformIdentity, + 17, + 49, + transformIdentity, + 19, + 49, + transformIdentity, + 20, + 49, + transformOmitFirst5, + 49, + 49, + transformOmitFirst6, + 49, + 47, + transformIdentity, + 49, + 49, + transformOmitLast4, + 49, + 49, + transformIdentity, + 22, + 49, + transformUppercaseAll, + 49, + 49, + transformIdentity, + 23, + 49, + transformIdentity, + 24, + 49, + transformIdentity, + 25, + 49, + transformOmitLast7, + 49, + 49, + transformOmitLast1, + 26, + 49, + transformIdentity, + 27, + 49, + transformIdentity, + 28, + 0, + transformIdentity, + 12, + 49, + transformIdentity, + 29, + 49, + transformOmitFirst9, + 49, + 49, + transformOmitFirst7, + 49, + 49, + transformOmitLast6, + 49, + 49, + transformIdentity, + 21, + 49, + transformUppercaseFirst, + 1, + 49, + transformOmitLast8, + 49, + 49, + transformIdentity, + 31, + 49, + transformIdentity, + 32, + 47, + transformIdentity, + 3, + 49, + transformOmitLast5, + 49, + 49, + transformOmitLast9, + 49, + 0, + transformUppercaseFirst, + 1, + 49, + transformUppercaseFirst, + 8, + 5, + transformIdentity, + 21, + 49, + transformUppercaseAll, + 0, + 49, + transformUppercaseFirst, + 10, + 49, + transformIdentity, + 30, + 0, + transformIdentity, + 5, + 35, + transformIdentity, + 49, + 47, + transformIdentity, + 2, + 49, + transformUppercaseFirst, + 17, + 49, + transformIdentity, + 36, + 49, + transformIdentity, + 33, + 5, + transformIdentity, + 0, + 49, + transformUppercaseFirst, + 21, + 49, + transformUppercaseFirst, + 5, + 49, + transformIdentity, + 37, + 0, + transformIdentity, + 30, + 49, + transformIdentity, + 38, + 0, + transformUppercaseAll, + 0, + 49, + transformIdentity, + 39, + 0, + transformUppercaseAll, + 49, + 49, + transformIdentity, + 34, + 49, + transformUppercaseAll, + 8, + 49, + transformUppercaseFirst, + 12, + 0, + transformIdentity, + 21, + 49, + transformIdentity, + 40, + 0, + transformUppercaseFirst, + 12, + 49, + transformIdentity, + 41, + 49, + transformIdentity, + 42, + 49, + transformUppercaseAll, + 17, + 49, + transformIdentity, + 43, + 0, + transformUppercaseFirst, + 5, + 49, + transformUppercaseAll, + 10, + 0, + transformIdentity, + 34, + 49, + transformUppercaseFirst, + 33, + 49, + transformIdentity, + 44, + 49, + transformUppercaseAll, + 5, + 45, + transformIdentity, + 49, + 0, + transformIdentity, + 33, + 49, + transformUppercaseFirst, + 30, + 49, + transformUppercaseAll, + 30, + 49, + transformIdentity, + 46, + 49, + transformUppercaseAll, + 1, + 49, + transformUppercaseFirst, + 34, + 0, + transformUppercaseFirst, + 33, + 0, + transformUppercaseAll, + 30, + 0, + transformUppercaseAll, + 1, + 49, + transformUppercaseAll, + 33, + 49, + transformUppercaseAll, + 21, + 49, + transformUppercaseAll, + 12, + 0, + transformUppercaseAll, + 5, + 49, + transformUppercaseAll, + 34, + 0, + transformUppercaseAll, + 12, + 0, + transformUppercaseFirst, + 30, + 0, + transformUppercaseAll, + 34, + 0, + transformUppercaseFirst, + 34, +} + +var kBrotliTransforms = transforms{ + 217, + []byte(kPrefixSuffix), + kPrefixSuffixMap[:], + 121, + kTransformsData, + nil, /* no extra parameters */ + [transformsMaxCutOff + 1]int16{0, 12, 27, 23, 42, 63, 56, 48, 59, 64}, +} + +func getTransforms() *transforms { + return &kBrotliTransforms +} + +func toUpperCase(p []byte) int { + if p[0] < 0xC0 { + if p[0] >= 'a' && p[0] <= 'z' { + p[0] ^= 32 + } + + return 1 + } + + /* An overly simplified uppercasing model for UTF-8. */ + if p[0] < 0xE0 { + p[1] ^= 32 + return 2 + } + + /* An arbitrary transform for three byte characters. */ + p[2] ^= 5 + + return 3 +} + +func shiftTransform(word []byte, word_len int, parameter uint16) int { + /* Limited sign extension: scalar < (1 << 24). */ + var scalar uint32 = (uint32(parameter) & 0x7FFF) + (0x1000000 - (uint32(parameter) & 0x8000)) + if word[0] < 0x80 { + /* 1-byte rune / 0sssssss / 7 bit scalar (ASCII). */ + scalar += uint32(word[0]) + + word[0] = byte(scalar & 0x7F) + return 1 + } else if word[0] < 0xC0 { + /* Continuation / 10AAAAAA. */ + return 1 + } else if word[0] < 0xE0 { + /* 2-byte rune / 110sssss AAssssss / 11 bit scalar. */ + if word_len < 2 { + return 1 + } + scalar += uint32(word[1]&0x3F | (word[0]&0x1F)<<6) + word[0] = byte(0xC0 | (scalar>>6)&0x1F) + word[1] = byte(uint32(word[1]&0xC0) | scalar&0x3F) + return 2 + } else if word[0] < 0xF0 { + /* 3-byte rune / 1110ssss AAssssss BBssssss / 16 bit scalar. */ + if word_len < 3 { + return word_len + } + scalar += uint32(word[2])&0x3F | uint32(word[1]&0x3F)<<6 | uint32(word[0]&0x0F)<<12 + word[0] = byte(0xE0 | (scalar>>12)&0x0F) + word[1] = byte(uint32(word[1]&0xC0) | (scalar>>6)&0x3F) + word[2] = byte(uint32(word[2]&0xC0) | scalar&0x3F) + return 3 + } else if word[0] < 0xF8 { + /* 4-byte rune / 11110sss AAssssss BBssssss CCssssss / 21 bit scalar. */ + if word_len < 4 { + return word_len + } + scalar += uint32(word[3])&0x3F | uint32(word[2]&0x3F)<<6 | uint32(word[1]&0x3F)<<12 | uint32(word[0]&0x07)<<18 + word[0] = byte(0xF0 | (scalar>>18)&0x07) + word[1] = byte(uint32(word[1]&0xC0) | (scalar>>12)&0x3F) + word[2] = byte(uint32(word[2]&0xC0) | (scalar>>6)&0x3F) + word[3] = byte(uint32(word[3]&0xC0) | scalar&0x3F) + return 4 + } + + return 1 +} + +func transformDictionaryWord(dst []byte, word []byte, len int, trans *transforms, transform_idx int) int { + var idx int = 0 + var prefix []byte = transformPrefix(trans, transform_idx) + var type_ byte = transformType(trans, transform_idx) + var suffix []byte = transformSuffix(trans, transform_idx) + { + var prefix_len int = int(prefix[0]) + prefix = prefix[1:] + for { + tmp1 := prefix_len + prefix_len-- + if tmp1 == 0 { + break + } + dst[idx] = prefix[0] + idx++ + prefix = prefix[1:] + } + } + { + var t int = int(type_) + var i int = 0 + if t <= transformOmitLast9 { + len -= t + } else if t >= transformOmitFirst1 && t <= transformOmitFirst9 { + var skip int = t - (transformOmitFirst1 - 1) + word = word[skip:] + len -= skip + } + + for i < len { + dst[idx] = word[i] + idx++ + i++ + } + if t == transformUppercaseFirst { + toUpperCase(dst[idx-len:]) + } else if t == transformUppercaseAll { + var uppercase []byte = dst + uppercase = uppercase[idx-len:] + for len > 0 { + var step int = toUpperCase(uppercase) + uppercase = uppercase[step:] + len -= step + } + } else if t == transformShiftFirst { + var param uint16 = uint16(trans.params[transform_idx*2]) + uint16(trans.params[transform_idx*2+1])<<8 + shiftTransform(dst[idx-len:], int(len), param) + } else if t == transformShiftAll { + var param uint16 = uint16(trans.params[transform_idx*2]) + uint16(trans.params[transform_idx*2+1])<<8 + var shift []byte = dst + shift = shift[idx-len:] + for len > 0 { + var step int = shiftTransform(shift, int(len), param) + shift = shift[step:] + len -= step + } + } + } + { + var suffix_len int = int(suffix[0]) + suffix = suffix[1:] + for { + tmp2 := suffix_len + suffix_len-- + if tmp2 == 0 { + break + } + dst[idx] = suffix[0] + idx++ + suffix = suffix[1:] + } + return idx + } +} diff --git a/vendor/github.com/andybalholm/brotli/utf8_util.go b/vendor/github.com/andybalholm/brotli/utf8_util.go new file mode 100644 index 000000000..3244247ee --- /dev/null +++ b/vendor/github.com/andybalholm/brotli/utf8_util.go @@ -0,0 +1,70 @@ +package brotli + +/* Copyright 2013 Google Inc. All Rights Reserved. + + Distributed under MIT license. + See file LICENSE for detail or copy at https://opensource.org/licenses/MIT +*/ + +/* Heuristics for deciding about the UTF8-ness of strings. */ + +const kMinUTF8Ratio float64 = 0.75 + +/* Returns 1 if at least min_fraction of the bytes between pos and + pos + length in the (data, mask) ring-buffer is UTF8-encoded, otherwise + returns 0. */ +func parseAsUTF8(symbol *int, input []byte, size uint) uint { + /* ASCII */ + if input[0]&0x80 == 0 { + *symbol = int(input[0]) + if *symbol > 0 { + return 1 + } + } + + /* 2-byte UTF8 */ + if size > 1 && input[0]&0xE0 == 0xC0 && input[1]&0xC0 == 0x80 { + *symbol = (int(input[0])&0x1F)<<6 | int(input[1])&0x3F + if *symbol > 0x7F { + return 2 + } + } + + /* 3-byte UFT8 */ + if size > 2 && input[0]&0xF0 == 0xE0 && input[1]&0xC0 == 0x80 && input[2]&0xC0 == 0x80 { + *symbol = (int(input[0])&0x0F)<<12 | (int(input[1])&0x3F)<<6 | int(input[2])&0x3F + if *symbol > 0x7FF { + return 3 + } + } + + /* 4-byte UFT8 */ + if size > 3 && input[0]&0xF8 == 0xF0 && input[1]&0xC0 == 0x80 && input[2]&0xC0 == 0x80 && input[3]&0xC0 == 0x80 { + *symbol = (int(input[0])&0x07)<<18 | (int(input[1])&0x3F)<<12 | (int(input[2])&0x3F)<<6 | int(input[3])&0x3F + if *symbol > 0xFFFF && *symbol <= 0x10FFFF { + return 4 + } + } + + /* Not UTF8, emit a special symbol above the UTF8-code space */ + *symbol = 0x110000 | int(input[0]) + + return 1 +} + +/* Returns 1 if at least min_fraction of the data is UTF8-encoded.*/ +func isMostlyUTF8(data []byte, pos uint, mask uint, length uint, min_fraction float64) bool { + var size_utf8 uint = 0 + var i uint = 0 + for i < length { + var symbol int + current_data := data[(pos+i)&mask:] + var bytes_read uint = parseAsUTF8(&symbol, current_data, length-i) + i += bytes_read + if symbol < 0x110000 { + size_utf8 += bytes_read + } + } + + return float64(size_utf8) > min_fraction*float64(length) +} diff --git a/vendor/github.com/andybalholm/brotli/util.go b/vendor/github.com/andybalholm/brotli/util.go new file mode 100644 index 000000000..a84553a63 --- /dev/null +++ b/vendor/github.com/andybalholm/brotli/util.go @@ -0,0 +1,7 @@ +package brotli + +func assert(cond bool) { + if !cond { + panic("assertion failure") + } +} diff --git a/vendor/github.com/andybalholm/brotli/write_bits.go b/vendor/github.com/andybalholm/brotli/write_bits.go new file mode 100644 index 000000000..872990119 --- /dev/null +++ b/vendor/github.com/andybalholm/brotli/write_bits.go @@ -0,0 +1,52 @@ +package brotli + +import "encoding/binary" + +/* Copyright 2010 Google Inc. All Rights Reserved. + + Distributed under MIT license. + See file LICENSE for detail or copy at https://opensource.org/licenses/MIT +*/ + +/* Write bits into a byte array. */ + +/* This function writes bits into bytes in increasing addresses, and within + a byte least-significant-bit first. + + The function can write up to 56 bits in one go with WriteBits + Example: let's assume that 3 bits (Rs below) have been written already: + + BYTE-0 BYTE+1 BYTE+2 + + 0000 0RRR 0000 0000 0000 0000 + + Now, we could write 5 or less bits in MSB by just sifting by 3 + and OR'ing to BYTE-0. + + For n bits, we take the last 5 bits, OR that with high bits in BYTE-0, + and locate the rest in BYTE+1, BYTE+2, etc. */ +func writeBits(n_bits uint, bits uint64, pos *uint, array []byte) { + /* This branch of the code can write up to 56 bits at a time, + 7 bits are lost by being perhaps already in *p and at least + 1 bit is needed to initialize the bit-stream ahead (i.e. if 7 + bits are in *p and we write 57 bits, then the next write will + access a byte that was never initialized). */ + p := array[*pos>>3:] + v := uint64(p[0]) + v |= bits << (*pos & 7) + binary.LittleEndian.PutUint64(p, v) + *pos += n_bits +} + +func writeSingleBit(bit bool, pos *uint, array []byte) { + if bit { + writeBits(1, 1, pos, array) + } else { + writeBits(1, 0, pos, array) + } +} + +func writeBitsPrepareStorage(pos uint, array []byte) { + assert(pos&7 == 0) + array[pos>>3] = 0 +} diff --git a/vendor/github.com/andybalholm/brotli/writer.go b/vendor/github.com/andybalholm/brotli/writer.go new file mode 100644 index 000000000..8a688117d --- /dev/null +++ b/vendor/github.com/andybalholm/brotli/writer.go @@ -0,0 +1,162 @@ +package brotli + +import ( + "errors" + "io" + + "github.com/andybalholm/brotli/matchfinder" +) + +const ( + BestSpeed = 0 + BestCompression = 11 + DefaultCompression = 6 +) + +// WriterOptions configures Writer. +type WriterOptions struct { + // Quality controls the compression-speed vs compression-density trade-offs. + // The higher the quality, the slower the compression. Range is 0 to 11. + Quality int + // LGWin is the base 2 logarithm of the sliding window size. + // Range is 10 to 24. 0 indicates automatic configuration based on Quality. + LGWin int +} + +var ( + errEncode = errors.New("brotli: encode error") + errWriterClosed = errors.New("brotli: Writer is closed") +) + +// Writes to the returned writer are compressed and written to dst. +// It is the caller's responsibility to call Close on the Writer when done. +// Writes may be buffered and not flushed until Close. +func NewWriter(dst io.Writer) *Writer { + return NewWriterLevel(dst, DefaultCompression) +} + +// NewWriterLevel is like NewWriter but specifies the compression level instead +// of assuming DefaultCompression. +// The compression level can be DefaultCompression or any integer value between +// BestSpeed and BestCompression inclusive. +func NewWriterLevel(dst io.Writer, level int) *Writer { + return NewWriterOptions(dst, WriterOptions{ + Quality: level, + }) +} + +// NewWriterOptions is like NewWriter but specifies WriterOptions +func NewWriterOptions(dst io.Writer, options WriterOptions) *Writer { + w := new(Writer) + w.options = options + w.Reset(dst) + return w +} + +// Reset discards the Writer's state and makes it equivalent to the result of +// its original state from NewWriter or NewWriterLevel, but writing to dst +// instead. This permits reusing a Writer rather than allocating a new one. +func (w *Writer) Reset(dst io.Writer) { + encoderInitState(w) + w.params.quality = w.options.Quality + if w.options.LGWin > 0 { + w.params.lgwin = uint(w.options.LGWin) + } + w.dst = dst + w.err = nil +} + +func (w *Writer) writeChunk(p []byte, op int) (n int, err error) { + if w.dst == nil { + return 0, errWriterClosed + } + if w.err != nil { + return 0, w.err + } + + for { + availableIn := uint(len(p)) + nextIn := p + success := encoderCompressStream(w, op, &availableIn, &nextIn) + bytesConsumed := len(p) - int(availableIn) + p = p[bytesConsumed:] + n += bytesConsumed + if !success { + return n, errEncode + } + + if len(p) == 0 || w.err != nil { + return n, w.err + } + } +} + +// Flush outputs encoded data for all input provided to Write. The resulting +// output can be decoded to match all input before Flush, but the stream is +// not yet complete until after Close. +// Flush has a negative impact on compression. +func (w *Writer) Flush() error { + _, err := w.writeChunk(nil, operationFlush) + return err +} + +// Close flushes remaining data to the decorated writer. +func (w *Writer) Close() error { + // If stream is already closed, it is reported by `writeChunk`. + _, err := w.writeChunk(nil, operationFinish) + w.dst = nil + return err +} + +// Write implements io.Writer. Flush or Close must be called to ensure that the +// encoded bytes are actually flushed to the underlying Writer. +func (w *Writer) Write(p []byte) (n int, err error) { + return w.writeChunk(p, operationProcess) +} + +type nopCloser struct { + io.Writer +} + +func (nopCloser) Close() error { return nil } + +// NewWriterV2 is like NewWriterLevel, but it uses the new implementation +// based on the matchfinder package. It currently supports up to level 7; +// if a higher level is specified, level 7 will be used. +func NewWriterV2(dst io.Writer, level int) *matchfinder.Writer { + var mf matchfinder.MatchFinder + if level < 2 { + mf = matchfinder.M0{Lazy: level == 1} + } else { + hashLen := 6 + if level >= 6 { + hashLen = 5 + } + chainLen := 64 + switch level { + case 2: + chainLen = 0 + case 3: + chainLen = 1 + case 4: + chainLen = 2 + case 5: + chainLen = 4 + case 6: + chainLen = 8 + } + mf = &matchfinder.M4{ + MaxDistance: 1 << 20, + ChainLength: chainLen, + HashLen: hashLen, + DistanceBitCost: 57, + } + } + + return &matchfinder.Writer{ + Dest: dst, + MatchFinder: mf, + Encoder: &Encoder{}, + BlockSize: 1 << 16, + } +} diff --git a/vendor/github.com/klauspost/compress/.gitattributes b/vendor/github.com/klauspost/compress/.gitattributes new file mode 100644 index 000000000..57aa6487c --- /dev/null +++ b/vendor/github.com/klauspost/compress/.gitattributes @@ -0,0 +1,3 @@ +* -text +*.bin -text -diff +*.md text eol=lf diff --git a/vendor/github.com/klauspost/compress/.gitignore b/vendor/github.com/klauspost/compress/.gitignore new file mode 100644 index 000000000..d31b37815 --- /dev/null +++ b/vendor/github.com/klauspost/compress/.gitignore @@ -0,0 +1,32 @@ +# Compiled Object files, Static and Dynamic libs (Shared Objects) +*.o +*.a +*.so + +# Folders +_obj +_test + +# Architecture specific extensions/prefixes +*.[568vq] +[568vq].out + +*.cgo1.go +*.cgo2.c +_cgo_defun.c +_cgo_gotypes.go +_cgo_export.* + +_testmain.go + +*.exe +*.test +*.prof +/s2/cmd/_s2sx/sfx-exe + +# Linux perf files +perf.data +perf.data.old + +# gdb history +.gdb_history diff --git a/vendor/github.com/klauspost/compress/.goreleaser.yml b/vendor/github.com/klauspost/compress/.goreleaser.yml new file mode 100644 index 000000000..804a20181 --- /dev/null +++ b/vendor/github.com/klauspost/compress/.goreleaser.yml @@ -0,0 +1,132 @@ +version: 2 + +before: + hooks: + - ./gen.sh + +builds: + - + id: "s2c" + binary: s2c + main: ./s2/cmd/s2c/main.go + flags: + - -trimpath + env: + - CGO_ENABLED=0 + goos: + - aix + - linux + - freebsd + - netbsd + - windows + - darwin + goarch: + - 386 + - amd64 + - arm + - arm64 + - ppc64 + - ppc64le + - mips64 + - mips64le + goarm: + - 7 + ignore: + - goos: windows + goarch: arm + - + id: "s2d" + binary: s2d + main: ./s2/cmd/s2d/main.go + flags: + - -trimpath + env: + - CGO_ENABLED=0 + goos: + - aix + - linux + - freebsd + - netbsd + - windows + - darwin + goarch: + - 386 + - amd64 + - arm + - arm64 + - ppc64 + - ppc64le + - mips64 + - mips64le + goarm: + - 7 + ignore: + - goos: windows + goarch: arm + - + id: "s2sx" + binary: s2sx + main: ./s2/cmd/_s2sx/main.go + flags: + - -modfile=s2sx.mod + - -trimpath + env: + - CGO_ENABLED=0 + goos: + - aix + - linux + - freebsd + - netbsd + - windows + - darwin + goarch: + - 386 + - amd64 + - arm + - arm64 + - ppc64 + - ppc64le + - mips64 + - mips64le + goarm: + - 7 + ignore: + - goos: windows + goarch: arm + +archives: + - + id: s2-binaries + name_template: "s2-{{ .Os }}_{{ .Arch }}{{ if .Arm }}v{{ .Arm }}{{ end }}" + format_overrides: + - goos: windows + formats: ['zip'] + files: + - unpack/* + - s2/LICENSE + - s2/README.md +checksum: + name_template: 'checksums.txt' +snapshot: + version_template: "{{ .Tag }}-next" +changelog: + sort: asc + filters: + exclude: + - '^doc:' + - '^docs:' + - '^test:' + - '^tests:' + - '^Update\sREADME.md' + +nfpms: + - + file_name_template: "s2_package__{{ .Os }}_{{ .Arch }}{{ if .Arm }}v{{ .Arm }}{{ end }}" + vendor: Klaus Post + homepage: https://github.com/klauspost/compress + maintainer: Klaus Post + description: S2 Compression Tool + license: BSD 3-Clause + formats: + - deb + - rpm diff --git a/vendor/github.com/klauspost/compress/LICENSE b/vendor/github.com/klauspost/compress/LICENSE new file mode 100644 index 000000000..87d557477 --- /dev/null +++ b/vendor/github.com/klauspost/compress/LICENSE @@ -0,0 +1,304 @@ +Copyright (c) 2012 The Go Authors. All rights reserved. +Copyright (c) 2019 Klaus Post. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +------------------ + +Files: gzhttp/* + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2016-2017 The New York Times Company + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +------------------ + +Files: s2/cmd/internal/readahead/* + +The MIT License (MIT) + +Copyright (c) 2015 Klaus Post + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +--------------------- +Files: snappy/* +Files: internal/snapref/* + +Copyright (c) 2011 The Snappy-Go Authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +----------------- + +Files: s2/cmd/internal/filepathx/* + +Copyright 2016 The filepathx Authors + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/vendor/github.com/klauspost/compress/README.md b/vendor/github.com/klauspost/compress/README.md new file mode 100644 index 000000000..fb023f2cf --- /dev/null +++ b/vendor/github.com/klauspost/compress/README.md @@ -0,0 +1,700 @@ +# compress + +This package provides various compression algorithms. + +* [zstandard](https://github.com/klauspost/compress/tree/master/zstd#zstd) compression and decompression in pure Go. +* [S2](https://github.com/klauspost/compress/tree/master/s2#s2-compression) is a high performance replacement for Snappy. +* Optimized [deflate](https://godoc.org/github.com/klauspost/compress/flate) packages which can be used as a dropin replacement for [gzip](https://godoc.org/github.com/klauspost/compress/gzip), [zip](https://godoc.org/github.com/klauspost/compress/zip) and [zlib](https://godoc.org/github.com/klauspost/compress/zlib). +* [snappy](https://github.com/klauspost/compress/tree/master/snappy) is a drop-in replacement for `github.com/golang/snappy` offering better compression and concurrent streams. +* [huff0](https://github.com/klauspost/compress/tree/master/huff0) and [FSE](https://github.com/klauspost/compress/tree/master/fse) implementations for raw entropy encoding. +* [gzhttp](https://github.com/klauspost/compress/tree/master/gzhttp) Provides client and server wrappers for handling gzipped/zstd HTTP requests efficiently. +* [pgzip](https://github.com/klauspost/pgzip) is a separate package that provides a very fast parallel gzip implementation. + +[![Go Reference](https://pkg.go.dev/badge/klauspost/compress.svg)](https://pkg.go.dev/github.com/klauspost/compress?tab=subdirectories) +[![Go](https://github.com/klauspost/compress/actions/workflows/go.yml/badge.svg)](https://github.com/klauspost/compress/actions/workflows/go.yml) +[![Sourcegraph Badge](https://sourcegraph.com/github.com/klauspost/compress/-/badge.svg)](https://sourcegraph.com/github.com/klauspost/compress?badge) + +# package usage + +Use `go get github.com/klauspost/compress@latest` to add it to your project. + +This package will support the current Go version and 2 versions back. + +* Use the `nounsafe` tag to disable all use of the "unsafe" package. +* Use the `noasm` tag to disable all assembly across packages. + +Use the links above for more information on each. + +# changelog + +* Feb 9th, 2026 [1.18.4](https://github.com/klauspost/compress/releases/tag/v1.18.4) + * gzhttp: Add zstandard to server handler wrapper https://github.com/klauspost/compress/pull/1121 + * zstd: Add ResetWithOptions to encoder/decoder https://github.com/klauspost/compress/pull/1122 + * gzhttp: preserve qvalue when extra parameters follow in Accept-Encoding by @analytically in https://github.com/klauspost/compress/pull/1116 + +* Jan 16th, 2026 [1.18.3](https://github.com/klauspost/compress/releases/tag/v1.18.3) + * Downstream CVE-2025-61728. See [golang/go#77102](https://github.com/golang/go/issues/77102). + +* Dec 1st, 2025 - [1.18.2](https://github.com/klauspost/compress/releases/tag/v1.18.2) + * flate: Fix invalid encoding on level 9 with single value input in https://github.com/klauspost/compress/pull/1115 + * flate: reduce stateless allocations by @RXamzin in https://github.com/klauspost/compress/pull/1106 + +* Oct 20, 2025 - [1.18.1](https://github.com/klauspost/compress/releases/tag/v1.18.1) - RETRACTED + * zstd: Add simple zstd EncodeTo/DecodeTo functions https://github.com/klauspost/compress/pull/1079 + * zstd: Fix incorrect buffer size in dictionary encodes https://github.com/klauspost/compress/pull/1059 + * s2: check for cap, not len of buffer in EncodeBetter/Best by @vdarulis in https://github.com/klauspost/compress/pull/1080 + * zlib: Avoiding extra allocation in zlib.reader.Reset by @travelpolicy in https://github.com/klauspost/compress/pull/1086 + * gzhttp: remove redundant err check in zstdReader by @ryanfowler in https://github.com/klauspost/compress/pull/1090 + * flate: Faster load+store https://github.com/klauspost/compress/pull/1104 + * flate: Simplify matchlen https://github.com/klauspost/compress/pull/1101 + * flate: Use exact sizes for huffman tables https://github.com/klauspost/compress/pull/1103 + +* Feb 19th, 2025 - [1.18.0](https://github.com/klauspost/compress/releases/tag/v1.18.0) + * Add unsafe little endian loaders https://github.com/klauspost/compress/pull/1036 + * fix: check `r.err != nil` but return a nil value error `err` by @alingse in https://github.com/klauspost/compress/pull/1028 + * flate: Simplify L4-6 loading https://github.com/klauspost/compress/pull/1043 + * flate: Simplify matchlen (remove asm) https://github.com/klauspost/compress/pull/1045 + * s2: Improve small block compression speed w/o asm https://github.com/klauspost/compress/pull/1048 + * flate: Fix matchlen L5+L6 https://github.com/klauspost/compress/pull/1049 + * flate: Cleanup & reduce casts https://github.com/klauspost/compress/pull/1050 + +
+ See changes to v1.17.x + +* Oct 11th, 2024 - [1.17.11](https://github.com/klauspost/compress/releases/tag/v1.17.11) + * zstd: Fix extra CRC written with multiple Close calls https://github.com/klauspost/compress/pull/1017 + * s2: Don't use stack for index tables https://github.com/klauspost/compress/pull/1014 + * gzhttp: No content-type on no body response code by @juliens in https://github.com/klauspost/compress/pull/1011 + * gzhttp: Do not set the content-type when response has no body by @kevinpollet in https://github.com/klauspost/compress/pull/1013 + +* Sep 23rd, 2024 - [1.17.10](https://github.com/klauspost/compress/releases/tag/v1.17.10) + * gzhttp: Add TransportAlwaysDecompress option. https://github.com/klauspost/compress/pull/978 + * gzhttp: Add supported decompress request body by @mirecl in https://github.com/klauspost/compress/pull/1002 + * s2: Add EncodeBuffer buffer recycling callback https://github.com/klauspost/compress/pull/982 + * zstd: Improve memory usage on small streaming encodes https://github.com/klauspost/compress/pull/1007 + * flate: read data written with partial flush by @vajexal in https://github.com/klauspost/compress/pull/996 + +* Jun 12th, 2024 - [1.17.9](https://github.com/klauspost/compress/releases/tag/v1.17.9) + * s2: Reduce ReadFrom temporary allocations https://github.com/klauspost/compress/pull/949 + * flate, zstd: Shave some bytes off amd64 matchLen by @greatroar in https://github.com/klauspost/compress/pull/963 + * Upgrade zip/zlib to 1.22.4 upstream https://github.com/klauspost/compress/pull/970 https://github.com/klauspost/compress/pull/971 + * zstd: BuildDict fails with RLE table https://github.com/klauspost/compress/pull/951 + +* Apr 9th, 2024 - [1.17.8](https://github.com/klauspost/compress/releases/tag/v1.17.8) + * zstd: Reject blocks where reserved values are not 0 https://github.com/klauspost/compress/pull/885 + * zstd: Add RLE detection+encoding https://github.com/klauspost/compress/pull/938 + +* Feb 21st, 2024 - [1.17.7](https://github.com/klauspost/compress/releases/tag/v1.17.7) + * s2: Add AsyncFlush method: Complete the block without flushing by @Jille in https://github.com/klauspost/compress/pull/927 + * s2: Fix literal+repeat exceeds dst crash https://github.com/klauspost/compress/pull/930 + +* Feb 5th, 2024 - [1.17.6](https://github.com/klauspost/compress/releases/tag/v1.17.6) + * zstd: Fix incorrect repeat coding in best mode https://github.com/klauspost/compress/pull/923 + * s2: Fix DecodeConcurrent deadlock on errors https://github.com/klauspost/compress/pull/925 + +* Jan 26th, 2024 - [v1.17.5](https://github.com/klauspost/compress/releases/tag/v1.17.5) + * flate: Fix reset with dictionary on custom window encodes https://github.com/klauspost/compress/pull/912 + * zstd: Add Frame header encoding and stripping https://github.com/klauspost/compress/pull/908 + * zstd: Limit better/best default window to 8MB https://github.com/klauspost/compress/pull/913 + * zstd: Speed improvements by @greatroar in https://github.com/klauspost/compress/pull/896 https://github.com/klauspost/compress/pull/910 + * s2: Fix callbacks for skippable blocks and disallow 0xfe (Padding) by @Jille in https://github.com/klauspost/compress/pull/916 https://github.com/klauspost/compress/pull/917 +https://github.com/klauspost/compress/pull/919 https://github.com/klauspost/compress/pull/918 + +* Dec 1st, 2023 - [v1.17.4](https://github.com/klauspost/compress/releases/tag/v1.17.4) + * huff0: Speed up symbol counting by @greatroar in https://github.com/klauspost/compress/pull/887 + * huff0: Remove byteReader by @greatroar in https://github.com/klauspost/compress/pull/886 + * gzhttp: Allow overriding decompression on transport https://github.com/klauspost/compress/pull/892 + * gzhttp: Clamp compression level https://github.com/klauspost/compress/pull/890 + * gzip: Error out if reserved bits are set https://github.com/klauspost/compress/pull/891 + +* Nov 15th, 2023 - [v1.17.3](https://github.com/klauspost/compress/releases/tag/v1.17.3) + * fse: Fix max header size https://github.com/klauspost/compress/pull/881 + * zstd: Improve better/best compression https://github.com/klauspost/compress/pull/877 + * gzhttp: Fix missing content type on Close https://github.com/klauspost/compress/pull/883 + +* Oct 22nd, 2023 - [v1.17.2](https://github.com/klauspost/compress/releases/tag/v1.17.2) + * zstd: Fix rare *CORRUPTION* output in "best" mode. See https://github.com/klauspost/compress/pull/876 + +* Oct 14th, 2023 - [v1.17.1](https://github.com/klauspost/compress/releases/tag/v1.17.1) + * s2: Fix S2 "best" dictionary wrong encoding https://github.com/klauspost/compress/pull/871 + * flate: Reduce allocations in decompressor and minor code improvements by @fakefloordiv in https://github.com/klauspost/compress/pull/869 + * s2: Fix EstimateBlockSize on 6&7 length input https://github.com/klauspost/compress/pull/867 + +* Sept 19th, 2023 - [v1.17.0](https://github.com/klauspost/compress/releases/tag/v1.17.0) + * Add experimental dictionary builder https://github.com/klauspost/compress/pull/853 + * Add xerial snappy read/writer https://github.com/klauspost/compress/pull/838 + * flate: Add limited window compression https://github.com/klauspost/compress/pull/843 + * s2: Do 2 overlapping match checks https://github.com/klauspost/compress/pull/839 + * flate: Add amd64 assembly matchlen https://github.com/klauspost/compress/pull/837 + * gzip: Copy bufio.Reader on Reset by @thatguystone in https://github.com/klauspost/compress/pull/860 + +
+
+ See changes to v1.16.x + + +* July 1st, 2023 - [v1.16.7](https://github.com/klauspost/compress/releases/tag/v1.16.7) + * zstd: Fix default level first dictionary encode https://github.com/klauspost/compress/pull/829 + * s2: add GetBufferCapacity() method by @GiedriusS in https://github.com/klauspost/compress/pull/832 + +* June 13, 2023 - [v1.16.6](https://github.com/klauspost/compress/releases/tag/v1.16.6) + * zstd: correctly ignore WithEncoderPadding(1) by @ianlancetaylor in https://github.com/klauspost/compress/pull/806 + * zstd: Add amd64 match length assembly https://github.com/klauspost/compress/pull/824 + * gzhttp: Handle informational headers by @rtribotte in https://github.com/klauspost/compress/pull/815 + * s2: Improve Better compression slightly https://github.com/klauspost/compress/pull/663 + +* Apr 16, 2023 - [v1.16.5](https://github.com/klauspost/compress/releases/tag/v1.16.5) + * zstd: readByte needs to use io.ReadFull by @jnoxon in https://github.com/klauspost/compress/pull/802 + * gzip: Fix WriterTo after initial read https://github.com/klauspost/compress/pull/804 + +* Apr 5, 2023 - [v1.16.4](https://github.com/klauspost/compress/releases/tag/v1.16.4) + * zstd: Improve zstd best efficiency by @greatroar and @klauspost in https://github.com/klauspost/compress/pull/784 + * zstd: Respect WithAllLitEntropyCompression https://github.com/klauspost/compress/pull/792 + * zstd: Fix amd64 not always detecting corrupt data https://github.com/klauspost/compress/pull/785 + * zstd: Various minor improvements by @greatroar in https://github.com/klauspost/compress/pull/788 https://github.com/klauspost/compress/pull/794 https://github.com/klauspost/compress/pull/795 + * s2: Fix huge block overflow https://github.com/klauspost/compress/pull/779 + * s2: Allow CustomEncoder fallback https://github.com/klauspost/compress/pull/780 + * gzhttp: Support ResponseWriter Unwrap() in gzhttp handler by @jgimenez in https://github.com/klauspost/compress/pull/799 + +* Mar 13, 2023 - [v1.16.1](https://github.com/klauspost/compress/releases/tag/v1.16.1) + * zstd: Speed up + improve best encoder by @greatroar in https://github.com/klauspost/compress/pull/776 + * gzhttp: Add optional [BREACH mitigation](https://github.com/klauspost/compress/tree/master/gzhttp#breach-mitigation). https://github.com/klauspost/compress/pull/762 https://github.com/klauspost/compress/pull/768 https://github.com/klauspost/compress/pull/769 https://github.com/klauspost/compress/pull/770 https://github.com/klauspost/compress/pull/767 + * s2: Add Intel LZ4s converter https://github.com/klauspost/compress/pull/766 + * zstd: Minor bug fixes https://github.com/klauspost/compress/pull/771 https://github.com/klauspost/compress/pull/772 https://github.com/klauspost/compress/pull/773 + * huff0: Speed up compress1xDo by @greatroar in https://github.com/klauspost/compress/pull/774 + +* Feb 26, 2023 - [v1.16.0](https://github.com/klauspost/compress/releases/tag/v1.16.0) + * s2: Add [Dictionary](https://github.com/klauspost/compress/tree/master/s2#dictionaries) support. https://github.com/klauspost/compress/pull/685 + * s2: Add Compression Size Estimate. https://github.com/klauspost/compress/pull/752 + * s2: Add support for custom stream encoder. https://github.com/klauspost/compress/pull/755 + * s2: Add LZ4 block converter. https://github.com/klauspost/compress/pull/748 + * s2: Support io.ReaderAt in ReadSeeker. https://github.com/klauspost/compress/pull/747 + * s2c/s2sx: Use concurrent decoding. https://github.com/klauspost/compress/pull/746 +
+ +
+ See changes to v1.15.x + +* Jan 21st, 2023 (v1.15.15) + * deflate: Improve level 7-9 https://github.com/klauspost/compress/pull/739 + * zstd: Add delta encoding support by @greatroar in https://github.com/klauspost/compress/pull/728 + * zstd: Various speed improvements by @greatroar https://github.com/klauspost/compress/pull/741 https://github.com/klauspost/compress/pull/734 https://github.com/klauspost/compress/pull/736 https://github.com/klauspost/compress/pull/744 https://github.com/klauspost/compress/pull/743 https://github.com/klauspost/compress/pull/745 + * gzhttp: Add SuffixETag() and DropETag() options to prevent ETag collisions on compressed responses by @willbicks in https://github.com/klauspost/compress/pull/740 + +* Jan 3rd, 2023 (v1.15.14) + + * flate: Improve speed in big stateless blocks https://github.com/klauspost/compress/pull/718 + * zstd: Minor speed tweaks by @greatroar in https://github.com/klauspost/compress/pull/716 https://github.com/klauspost/compress/pull/720 + * export NoGzipResponseWriter for custom ResponseWriter wrappers by @harshavardhana in https://github.com/klauspost/compress/pull/722 + * s2: Add example for indexing and existing stream https://github.com/klauspost/compress/pull/723 + +* Dec 11, 2022 (v1.15.13) + * zstd: Add [MaxEncodedSize](https://pkg.go.dev/github.com/klauspost/compress@v1.15.13/zstd#Encoder.MaxEncodedSize) to encoder https://github.com/klauspost/compress/pull/691 + * zstd: Various tweaks and improvements https://github.com/klauspost/compress/pull/693 https://github.com/klauspost/compress/pull/695 https://github.com/klauspost/compress/pull/696 https://github.com/klauspost/compress/pull/701 https://github.com/klauspost/compress/pull/702 https://github.com/klauspost/compress/pull/703 https://github.com/klauspost/compress/pull/704 https://github.com/klauspost/compress/pull/705 https://github.com/klauspost/compress/pull/706 https://github.com/klauspost/compress/pull/707 https://github.com/klauspost/compress/pull/708 + +* Oct 26, 2022 (v1.15.12) + + * zstd: Tweak decoder allocs. https://github.com/klauspost/compress/pull/680 + * gzhttp: Always delete `HeaderNoCompression` https://github.com/klauspost/compress/pull/683 + +* Sept 26, 2022 (v1.15.11) + + * flate: Improve level 1-3 compression https://github.com/klauspost/compress/pull/678 + * zstd: Improve "best" compression by @nightwolfz in https://github.com/klauspost/compress/pull/677 + * zstd: Fix+reduce decompression allocations https://github.com/klauspost/compress/pull/668 + * zstd: Fix non-effective noescape tag https://github.com/klauspost/compress/pull/667 + +* Sept 16, 2022 (v1.15.10) + + * zstd: Add [WithDecodeAllCapLimit](https://pkg.go.dev/github.com/klauspost/compress@v1.15.10/zstd#WithDecodeAllCapLimit) https://github.com/klauspost/compress/pull/649 + * Add Go 1.19 - deprecate Go 1.16 https://github.com/klauspost/compress/pull/651 + * flate: Improve level 5+6 compression https://github.com/klauspost/compress/pull/656 + * zstd: Improve "better" compression https://github.com/klauspost/compress/pull/657 + * s2: Improve "best" compression https://github.com/klauspost/compress/pull/658 + * s2: Improve "better" compression. https://github.com/klauspost/compress/pull/635 + * s2: Slightly faster non-assembly decompression https://github.com/klauspost/compress/pull/646 + * Use arrays for constant size copies https://github.com/klauspost/compress/pull/659 + +* July 21, 2022 (v1.15.9) + + * zstd: Fix decoder crash on amd64 (no BMI) on invalid input https://github.com/klauspost/compress/pull/645 + * zstd: Disable decoder extended memory copies (amd64) due to possible crashes https://github.com/klauspost/compress/pull/644 + * zstd: Allow single segments up to "max decoded size" https://github.com/klauspost/compress/pull/643 + +* July 13, 2022 (v1.15.8) + + * gzip: fix stack exhaustion bug in Reader.Read https://github.com/klauspost/compress/pull/641 + * s2: Add Index header trim/restore https://github.com/klauspost/compress/pull/638 + * zstd: Optimize seqdeq amd64 asm by @greatroar in https://github.com/klauspost/compress/pull/636 + * zstd: Improve decoder memcopy https://github.com/klauspost/compress/pull/637 + * huff0: Pass a single bitReader pointer to asm by @greatroar in https://github.com/klauspost/compress/pull/634 + * zstd: Branchless getBits for amd64 w/o BMI2 by @greatroar in https://github.com/klauspost/compress/pull/640 + * gzhttp: Remove header before writing https://github.com/klauspost/compress/pull/639 + +* June 29, 2022 (v1.15.7) + + * s2: Fix absolute forward seeks https://github.com/klauspost/compress/pull/633 + * zip: Merge upstream https://github.com/klauspost/compress/pull/631 + * zip: Re-add zip64 fix https://github.com/klauspost/compress/pull/624 + * zstd: translate fseDecoder.buildDtable into asm by @WojciechMula in https://github.com/klauspost/compress/pull/598 + * flate: Faster histograms https://github.com/klauspost/compress/pull/620 + * deflate: Use compound hcode https://github.com/klauspost/compress/pull/622 + +* June 3, 2022 (v1.15.6) + * s2: Improve coding for long, close matches https://github.com/klauspost/compress/pull/613 + * s2c: Add Snappy/S2 stream recompression https://github.com/klauspost/compress/pull/611 + * zstd: Always use configured block size https://github.com/klauspost/compress/pull/605 + * zstd: Fix incorrect hash table placement for dict encoding in default https://github.com/klauspost/compress/pull/606 + * zstd: Apply default config to ZipDecompressor without options https://github.com/klauspost/compress/pull/608 + * gzhttp: Exclude more common archive formats https://github.com/klauspost/compress/pull/612 + * s2: Add ReaderIgnoreCRC https://github.com/klauspost/compress/pull/609 + * s2: Remove sanity load on index creation https://github.com/klauspost/compress/pull/607 + * snappy: Use dedicated function for scoring https://github.com/klauspost/compress/pull/614 + * s2c+s2d: Use official snappy framed extension https://github.com/klauspost/compress/pull/610 + +* May 25, 2022 (v1.15.5) + * s2: Add concurrent stream decompression https://github.com/klauspost/compress/pull/602 + * s2: Fix final emit oob read crash on amd64 https://github.com/klauspost/compress/pull/601 + * huff0: asm implementation of Decompress1X by @WojciechMula https://github.com/klauspost/compress/pull/596 + * zstd: Use 1 less goroutine for stream decoding https://github.com/klauspost/compress/pull/588 + * zstd: Copy literal in 16 byte blocks when possible https://github.com/klauspost/compress/pull/592 + * zstd: Speed up when WithDecoderLowmem(false) https://github.com/klauspost/compress/pull/599 + * zstd: faster next state update in BMI2 version of decode by @WojciechMula in https://github.com/klauspost/compress/pull/593 + * huff0: Do not check max size when reading table. https://github.com/klauspost/compress/pull/586 + * flate: Inplace hashing for level 7-9 https://github.com/klauspost/compress/pull/590 + + +* May 11, 2022 (v1.15.4) + * huff0: decompress directly into output by @WojciechMula in [#577](https://github.com/klauspost/compress/pull/577) + * inflate: Keep dict on stack [#581](https://github.com/klauspost/compress/pull/581) + * zstd: Faster decoding memcopy in asm [#583](https://github.com/klauspost/compress/pull/583) + * zstd: Fix ignored crc [#580](https://github.com/klauspost/compress/pull/580) + +* May 5, 2022 (v1.15.3) + * zstd: Allow to ignore checksum checking by @WojciechMula [#572](https://github.com/klauspost/compress/pull/572) + * s2: Fix incorrect seek for io.SeekEnd in [#575](https://github.com/klauspost/compress/pull/575) + +* Apr 26, 2022 (v1.15.2) + * zstd: Add x86-64 assembly for decompression on streams and blocks. Contributed by [@WojciechMula](https://github.com/WojciechMula). Typically 2x faster. [#528](https://github.com/klauspost/compress/pull/528) [#531](https://github.com/klauspost/compress/pull/531) [#545](https://github.com/klauspost/compress/pull/545) [#537](https://github.com/klauspost/compress/pull/537) + * zstd: Add options to ZipDecompressor and fixes [#539](https://github.com/klauspost/compress/pull/539) + * s2: Use sorted search for index [#555](https://github.com/klauspost/compress/pull/555) + * Minimum version is Go 1.16, added CI test on 1.18. + +* Mar 11, 2022 (v1.15.1) + * huff0: Add x86 assembly of Decode4X by @WojciechMula in [#512](https://github.com/klauspost/compress/pull/512) + * zstd: Reuse zip decoders in [#514](https://github.com/klauspost/compress/pull/514) + * zstd: Detect extra block data and report as corrupted in [#520](https://github.com/klauspost/compress/pull/520) + * zstd: Handle zero sized frame content size stricter in [#521](https://github.com/klauspost/compress/pull/521) + * zstd: Add stricter block size checks in [#523](https://github.com/klauspost/compress/pull/523) + +* Mar 3, 2022 (v1.15.0) + * zstd: Refactor decoder [#498](https://github.com/klauspost/compress/pull/498) + * zstd: Add stream encoding without goroutines [#505](https://github.com/klauspost/compress/pull/505) + * huff0: Prevent single blocks exceeding 16 bits by @klauspost in[#507](https://github.com/klauspost/compress/pull/507) + * flate: Inline literal emission [#509](https://github.com/klauspost/compress/pull/509) + * gzhttp: Add zstd to transport [#400](https://github.com/klauspost/compress/pull/400) + * gzhttp: Make content-type optional [#510](https://github.com/klauspost/compress/pull/510) + +Both compression and decompression now supports "synchronous" stream operations. This means that whenever "concurrency" is set to 1, they will operate without spawning goroutines. + +Stream decompression is now faster on asynchronous, since the goroutine allocation much more effectively splits the workload. On typical streams this will typically use 2 cores fully for decompression. When a stream has finished decoding no goroutines will be left over, so decoders can now safely be pooled and still be garbage collected. + +While the release has been extensively tested, it is recommended to testing when upgrading. + +
+ +
+ See changes to v1.14.x + +* Feb 22, 2022 (v1.14.4) + * flate: Fix rare huffman only (-2) corruption. [#503](https://github.com/klauspost/compress/pull/503) + * zip: Update deprecated CreateHeaderRaw to correctly call CreateRaw by @saracen in [#502](https://github.com/klauspost/compress/pull/502) + * zip: don't read data descriptor early by @saracen in [#501](https://github.com/klauspost/compress/pull/501) #501 + * huff0: Use static decompression buffer up to 30% faster [#499](https://github.com/klauspost/compress/pull/499) [#500](https://github.com/klauspost/compress/pull/500) + +* Feb 17, 2022 (v1.14.3) + * flate: Improve fastest levels compression speed ~10% more throughput. [#482](https://github.com/klauspost/compress/pull/482) [#489](https://github.com/klauspost/compress/pull/489) [#490](https://github.com/klauspost/compress/pull/490) [#491](https://github.com/klauspost/compress/pull/491) [#494](https://github.com/klauspost/compress/pull/494) [#478](https://github.com/klauspost/compress/pull/478) + * flate: Faster decompression speed, ~5-10%. [#483](https://github.com/klauspost/compress/pull/483) + * s2: Faster compression with Go v1.18 and amd64 microarch level 3+. [#484](https://github.com/klauspost/compress/pull/484) [#486](https://github.com/klauspost/compress/pull/486) + +* Jan 25, 2022 (v1.14.2) + * zstd: improve header decoder by @dsnet [#476](https://github.com/klauspost/compress/pull/476) + * zstd: Add bigger default blocks [#469](https://github.com/klauspost/compress/pull/469) + * zstd: Remove unused decompression buffer [#470](https://github.com/klauspost/compress/pull/470) + * zstd: Fix logically dead code by @ningmingxiao [#472](https://github.com/klauspost/compress/pull/472) + * flate: Improve level 7-9 [#471](https://github.com/klauspost/compress/pull/471) [#473](https://github.com/klauspost/compress/pull/473) + * zstd: Add noasm tag for xxhash [#475](https://github.com/klauspost/compress/pull/475) + +* Jan 11, 2022 (v1.14.1) + * s2: Add stream index in [#462](https://github.com/klauspost/compress/pull/462) + * flate: Speed and efficiency improvements in [#439](https://github.com/klauspost/compress/pull/439) [#461](https://github.com/klauspost/compress/pull/461) [#455](https://github.com/klauspost/compress/pull/455) [#452](https://github.com/klauspost/compress/pull/452) [#458](https://github.com/klauspost/compress/pull/458) + * zstd: Performance improvement in [#420]( https://github.com/klauspost/compress/pull/420) [#456](https://github.com/klauspost/compress/pull/456) [#437](https://github.com/klauspost/compress/pull/437) [#467](https://github.com/klauspost/compress/pull/467) [#468](https://github.com/klauspost/compress/pull/468) + * zstd: add arm64 xxhash assembly in [#464](https://github.com/klauspost/compress/pull/464) + * Add garbled for binaries for s2 in [#445](https://github.com/klauspost/compress/pull/445) +
+ +
+ See changes to v1.13.x + +* Aug 30, 2021 (v1.13.5) + * gz/zlib/flate: Alias stdlib errors [#425](https://github.com/klauspost/compress/pull/425) + * s2: Add block support to commandline tools [#413](https://github.com/klauspost/compress/pull/413) + * zstd: pooledZipWriter should return Writers to the same pool [#426](https://github.com/klauspost/compress/pull/426) + * Removed golang/snappy as external dependency for tests [#421](https://github.com/klauspost/compress/pull/421) + +* Aug 12, 2021 (v1.13.4) + * Add [snappy replacement package](https://github.com/klauspost/compress/tree/master/snappy). + * zstd: Fix incorrect encoding in "best" mode [#415](https://github.com/klauspost/compress/pull/415) + +* Aug 3, 2021 (v1.13.3) + * zstd: Improve Best compression [#404](https://github.com/klauspost/compress/pull/404) + * zstd: Fix WriteTo error forwarding [#411](https://github.com/klauspost/compress/pull/411) + * gzhttp: Return http.HandlerFunc instead of http.Handler. Unlikely breaking change. [#406](https://github.com/klauspost/compress/pull/406) + * s2sx: Fix max size error [#399](https://github.com/klauspost/compress/pull/399) + * zstd: Add optional stream content size on reset [#401](https://github.com/klauspost/compress/pull/401) + * zstd: use SpeedBestCompression for level >= 10 [#410](https://github.com/klauspost/compress/pull/410) + +* Jun 14, 2021 (v1.13.1) + * s2: Add full Snappy output support [#396](https://github.com/klauspost/compress/pull/396) + * zstd: Add configurable [Decoder window](https://pkg.go.dev/github.com/klauspost/compress/zstd#WithDecoderMaxWindow) size [#394](https://github.com/klauspost/compress/pull/394) + * gzhttp: Add header to skip compression [#389](https://github.com/klauspost/compress/pull/389) + * s2: Improve speed with bigger output margin [#395](https://github.com/klauspost/compress/pull/395) + +* Jun 3, 2021 (v1.13.0) + * Added [gzhttp](https://github.com/klauspost/compress/tree/master/gzhttp#gzip-handler) which allows wrapping HTTP servers and clients with GZIP compressors. + * zstd: Detect short invalid signatures [#382](https://github.com/klauspost/compress/pull/382) + * zstd: Spawn decoder goroutine only if needed. [#380](https://github.com/klauspost/compress/pull/380) +
+ + +
+ See changes to v1.12.x + +* May 25, 2021 (v1.12.3) + * deflate: Better/faster Huffman encoding [#374](https://github.com/klauspost/compress/pull/374) + * deflate: Allocate less for history. [#375](https://github.com/klauspost/compress/pull/375) + * zstd: Forward read errors [#373](https://github.com/klauspost/compress/pull/373) + +* Apr 27, 2021 (v1.12.2) + * zstd: Improve better/best compression [#360](https://github.com/klauspost/compress/pull/360) [#364](https://github.com/klauspost/compress/pull/364) [#365](https://github.com/klauspost/compress/pull/365) + * zstd: Add helpers to compress/decompress zstd inside zip files [#363](https://github.com/klauspost/compress/pull/363) + * deflate: Improve level 5+6 compression [#367](https://github.com/klauspost/compress/pull/367) + * s2: Improve better/best compression [#358](https://github.com/klauspost/compress/pull/358) [#359](https://github.com/klauspost/compress/pull/358) + * s2: Load after checking src limit on amd64. [#362](https://github.com/klauspost/compress/pull/362) + * s2sx: Limit max executable size [#368](https://github.com/klauspost/compress/pull/368) + +* Apr 14, 2021 (v1.12.1) + * snappy package removed. Upstream added as dependency. + * s2: Better compression in "best" mode [#353](https://github.com/klauspost/compress/pull/353) + * s2sx: Add stdin input and detect pre-compressed from signature [#352](https://github.com/klauspost/compress/pull/352) + * s2c/s2d: Add http as possible input [#348](https://github.com/klauspost/compress/pull/348) + * s2c/s2d/s2sx: Always truncate when writing files [#352](https://github.com/klauspost/compress/pull/352) + * zstd: Reduce memory usage further when using [WithLowerEncoderMem](https://pkg.go.dev/github.com/klauspost/compress/zstd#WithLowerEncoderMem) [#346](https://github.com/klauspost/compress/pull/346) + * s2: Fix potential problem with amd64 assembly and profilers [#349](https://github.com/klauspost/compress/pull/349) +
+ +
+ See changes to v1.11.x + +* Mar 26, 2021 (v1.11.13) + * zstd: Big speedup on small dictionary encodes [#344](https://github.com/klauspost/compress/pull/344) [#345](https://github.com/klauspost/compress/pull/345) + * zstd: Add [WithLowerEncoderMem](https://pkg.go.dev/github.com/klauspost/compress/zstd#WithLowerEncoderMem) encoder option [#336](https://github.com/klauspost/compress/pull/336) + * deflate: Improve entropy compression [#338](https://github.com/klauspost/compress/pull/338) + * s2: Clean up and minor performance improvement in best [#341](https://github.com/klauspost/compress/pull/341) + +* Mar 5, 2021 (v1.11.12) + * s2: Add `s2sx` binary that creates [self extracting archives](https://github.com/klauspost/compress/tree/master/s2#s2sx-self-extracting-archives). + * s2: Speed up decompression on non-assembly platforms [#328](https://github.com/klauspost/compress/pull/328) + +* Mar 1, 2021 (v1.11.9) + * s2: Add ARM64 decompression assembly. Around 2x output speed. [#324](https://github.com/klauspost/compress/pull/324) + * s2: Improve "better" speed and efficiency. [#325](https://github.com/klauspost/compress/pull/325) + * s2: Fix binaries. + +* Feb 25, 2021 (v1.11.8) + * s2: Fixed occasional out-of-bounds write on amd64. Upgrade recommended. + * s2: Add AMD64 assembly for better mode. 25-50% faster. [#315](https://github.com/klauspost/compress/pull/315) + * s2: Less upfront decoder allocation. [#322](https://github.com/klauspost/compress/pull/322) + * zstd: Faster "compression" of incompressible data. [#314](https://github.com/klauspost/compress/pull/314) + * zip: Fix zip64 headers. [#313](https://github.com/klauspost/compress/pull/313) + +* Jan 14, 2021 (v1.11.7) + * Use Bytes() interface to get bytes across packages. [#309](https://github.com/klauspost/compress/pull/309) + * s2: Add 'best' compression option. [#310](https://github.com/klauspost/compress/pull/310) + * s2: Add ReaderMaxBlockSize, changes `s2.NewReader` signature to include varargs. [#311](https://github.com/klauspost/compress/pull/311) + * s2: Fix crash on small better buffers. [#308](https://github.com/klauspost/compress/pull/308) + * s2: Clean up decoder. [#312](https://github.com/klauspost/compress/pull/312) + +* Jan 7, 2021 (v1.11.6) + * zstd: Make decoder allocations smaller [#306](https://github.com/klauspost/compress/pull/306) + * zstd: Free Decoder resources when Reset is called with a nil io.Reader [#305](https://github.com/klauspost/compress/pull/305) + +* Dec 20, 2020 (v1.11.4) + * zstd: Add Best compression mode [#304](https://github.com/klauspost/compress/pull/304) + * Add header decoder [#299](https://github.com/klauspost/compress/pull/299) + * s2: Add uncompressed stream option [#297](https://github.com/klauspost/compress/pull/297) + * Simplify/speed up small blocks with known max size. [#300](https://github.com/klauspost/compress/pull/300) + * zstd: Always reset literal dict encoder [#303](https://github.com/klauspost/compress/pull/303) + +* Nov 15, 2020 (v1.11.3) + * inflate: 10-15% faster decompression [#293](https://github.com/klauspost/compress/pull/293) + * zstd: Tweak DecodeAll default allocation [#295](https://github.com/klauspost/compress/pull/295) + +* Oct 11, 2020 (v1.11.2) + * s2: Fix out of bounds read in "better" block compression [#291](https://github.com/klauspost/compress/pull/291) + +* Oct 1, 2020 (v1.11.1) + * zstd: Set allLitEntropy true in default configuration [#286](https://github.com/klauspost/compress/pull/286) + +* Sept 8, 2020 (v1.11.0) + * zstd: Add experimental compression [dictionaries](https://github.com/klauspost/compress/tree/master/zstd#dictionaries) [#281](https://github.com/klauspost/compress/pull/281) + * zstd: Fix mixed Write and ReadFrom calls [#282](https://github.com/klauspost/compress/pull/282) + * inflate/gz: Limit variable shifts, ~5% faster decompression [#274](https://github.com/klauspost/compress/pull/274) +
+ +
+ See changes to v1.10.x + +* July 8, 2020 (v1.10.11) + * zstd: Fix extra block when compressing with ReadFrom. [#278](https://github.com/klauspost/compress/pull/278) + * huff0: Also populate compression table when reading decoding table. [#275](https://github.com/klauspost/compress/pull/275) + +* June 23, 2020 (v1.10.10) + * zstd: Skip entropy compression in fastest mode when no matches. [#270](https://github.com/klauspost/compress/pull/270) + +* June 16, 2020 (v1.10.9): + * zstd: API change for specifying dictionaries. See [#268](https://github.com/klauspost/compress/pull/268) + * zip: update CreateHeaderRaw to handle zip64 fields. [#266](https://github.com/klauspost/compress/pull/266) + * Fuzzit tests removed. The service has been purchased and is no longer available. + +* June 5, 2020 (v1.10.8): + * 1.15x faster zstd block decompression. [#265](https://github.com/klauspost/compress/pull/265) + +* June 1, 2020 (v1.10.7): + * Added zstd decompression [dictionary support](https://github.com/klauspost/compress/tree/master/zstd#dictionaries) + * Increase zstd decompression speed up to 1.19x. [#259](https://github.com/klauspost/compress/pull/259) + * Remove internal reset call in zstd compression and reduce allocations. [#263](https://github.com/klauspost/compress/pull/263) + +* May 21, 2020: (v1.10.6) + * zstd: Reduce allocations while decoding. [#258](https://github.com/klauspost/compress/pull/258), [#252](https://github.com/klauspost/compress/pull/252) + * zstd: Stricter decompression checks. + +* April 12, 2020: (v1.10.5) + * s2-commands: Flush output when receiving SIGINT. [#239](https://github.com/klauspost/compress/pull/239) + +* Apr 8, 2020: (v1.10.4) + * zstd: Minor/special case optimizations. [#251](https://github.com/klauspost/compress/pull/251), [#250](https://github.com/klauspost/compress/pull/250), [#249](https://github.com/klauspost/compress/pull/249), [#247](https://github.com/klauspost/compress/pull/247) +* Mar 11, 2020: (v1.10.3) + * s2: Use S2 encoder in pure Go mode for Snappy output as well. [#245](https://github.com/klauspost/compress/pull/245) + * s2: Fix pure Go block encoder. [#244](https://github.com/klauspost/compress/pull/244) + * zstd: Added "better compression" mode. [#240](https://github.com/klauspost/compress/pull/240) + * zstd: Improve speed of fastest compression mode by 5-10% [#241](https://github.com/klauspost/compress/pull/241) + * zstd: Skip creating encoders when not needed. [#238](https://github.com/klauspost/compress/pull/238) + +* Feb 27, 2020: (v1.10.2) + * Close to 50% speedup in inflate (gzip/zip decompression). [#236](https://github.com/klauspost/compress/pull/236) [#234](https://github.com/klauspost/compress/pull/234) [#232](https://github.com/klauspost/compress/pull/232) + * Reduce deflate level 1-6 memory usage up to 59%. [#227](https://github.com/klauspost/compress/pull/227) + +* Feb 18, 2020: (v1.10.1) + * Fix zstd crash when resetting multiple times without sending data. [#226](https://github.com/klauspost/compress/pull/226) + * deflate: Fix dictionary use on level 1-6. [#224](https://github.com/klauspost/compress/pull/224) + * Remove deflate writer reference when closing. [#224](https://github.com/klauspost/compress/pull/224) + +* Feb 4, 2020: (v1.10.0) + * Add optional dictionary to [stateless deflate](https://pkg.go.dev/github.com/klauspost/compress/flate?tab=doc#StatelessDeflate). Breaking change, send `nil` for previous behaviour. [#216](https://github.com/klauspost/compress/pull/216) + * Fix buffer overflow on repeated small block deflate. [#218](https://github.com/klauspost/compress/pull/218) + * Allow copying content from an existing ZIP file without decompressing+compressing. [#214](https://github.com/klauspost/compress/pull/214) + * Added [S2](https://github.com/klauspost/compress/tree/master/s2#s2-compression) AMD64 assembler and various optimizations. Stream speed >10GB/s. [#186](https://github.com/klauspost/compress/pull/186) + +
+ +
+ See changes prior to v1.10.0 + +* Jan 20,2020 (v1.9.8) Optimize gzip/deflate with better size estimates and faster table generation. [#207](https://github.com/klauspost/compress/pull/207) by [luyu6056](https://github.com/luyu6056), [#206](https://github.com/klauspost/compress/pull/206). +* Jan 11, 2020: S2 Encode/Decode will use provided buffer if capacity is big enough. [#204](https://github.com/klauspost/compress/pull/204) +* Jan 5, 2020: (v1.9.7) Fix another zstd regression in v1.9.5 - v1.9.6 removed. +* Jan 4, 2020: (v1.9.6) Regression in v1.9.5 fixed causing corrupt zstd encodes in rare cases. +* Jan 4, 2020: Faster IO in [s2c + s2d commandline tools](https://github.com/klauspost/compress/tree/master/s2#commandline-tools) compression/decompression. [#192](https://github.com/klauspost/compress/pull/192) +* Dec 29, 2019: Removed v1.9.5 since fuzz tests showed a compatibility problem with the reference zstandard decoder. +* Dec 29, 2019: (v1.9.5) zstd: 10-20% faster block compression. [#199](https://github.com/klauspost/compress/pull/199) +* Dec 29, 2019: [zip](https://godoc.org/github.com/klauspost/compress/zip) package updated with latest Go features +* Dec 29, 2019: zstd: Single segment flag condintions tweaked. [#197](https://github.com/klauspost/compress/pull/197) +* Dec 18, 2019: s2: Faster compression when ReadFrom is used. [#198](https://github.com/klauspost/compress/pull/198) +* Dec 10, 2019: s2: Fix repeat length output when just above at 16MB limit. +* Dec 10, 2019: zstd: Add function to get decoder as io.ReadCloser. [#191](https://github.com/klauspost/compress/pull/191) +* Dec 3, 2019: (v1.9.4) S2: limit max repeat length. [#188](https://github.com/klauspost/compress/pull/188) +* Dec 3, 2019: Add [WithNoEntropyCompression](https://godoc.org/github.com/klauspost/compress/zstd#WithNoEntropyCompression) to zstd [#187](https://github.com/klauspost/compress/pull/187) +* Dec 3, 2019: Reduce memory use for tests. Check for leaked goroutines. +* Nov 28, 2019 (v1.9.3) Less allocations in stateless deflate. +* Nov 28, 2019: 5-20% Faster huff0 decode. Impacts zstd as well. [#184](https://github.com/klauspost/compress/pull/184) +* Nov 12, 2019 (v1.9.2) Added [Stateless Compression](#stateless-compression) for gzip/deflate. +* Nov 12, 2019: Fixed zstd decompression of large single blocks. [#180](https://github.com/klauspost/compress/pull/180) +* Nov 11, 2019: Set default [s2c](https://github.com/klauspost/compress/tree/master/s2#commandline-tools) block size to 4MB. +* Nov 11, 2019: Reduce inflate memory use by 1KB. +* Nov 10, 2019: Less allocations in deflate bit writer. +* Nov 10, 2019: Fix inconsistent error returned by zstd decoder. +* Oct 28, 2019 (v1.9.1) ztsd: Fix crash when compressing blocks. [#174](https://github.com/klauspost/compress/pull/174) +* Oct 24, 2019 (v1.9.0) zstd: Fix rare data corruption [#173](https://github.com/klauspost/compress/pull/173) +* Oct 24, 2019 zstd: Fix huff0 out of buffer write [#171](https://github.com/klauspost/compress/pull/171) and always return errors [#172](https://github.com/klauspost/compress/pull/172) +* Oct 10, 2019: Big deflate rewrite, 30-40% faster with better compression [#105](https://github.com/klauspost/compress/pull/105) + +
+ +
+ See changes prior to v1.9.0 + +* Oct 10, 2019: (v1.8.6) zstd: Allow partial reads to get flushed data. [#169](https://github.com/klauspost/compress/pull/169) +* Oct 3, 2019: Fix inconsistent results on broken zstd streams. +* Sep 25, 2019: Added `-rm` (remove source files) and `-q` (no output except errors) to `s2c` and `s2d` [commands](https://github.com/klauspost/compress/tree/master/s2#commandline-tools) +* Sep 16, 2019: (v1.8.4) Add `s2c` and `s2d` [commandline tools](https://github.com/klauspost/compress/tree/master/s2#commandline-tools). +* Sep 10, 2019: (v1.8.3) Fix s2 decoder [Skip](https://godoc.org/github.com/klauspost/compress/s2#Reader.Skip). +* Sep 7, 2019: zstd: Added [WithWindowSize](https://godoc.org/github.com/klauspost/compress/zstd#WithWindowSize), contributed by [ianwilkes](https://github.com/ianwilkes). +* Sep 5, 2019: (v1.8.2) Add [WithZeroFrames](https://godoc.org/github.com/klauspost/compress/zstd#WithZeroFrames) which adds full zero payload block encoding option. +* Sep 5, 2019: Lazy initialization of zstandard predefined en/decoder tables. +* Aug 26, 2019: (v1.8.1) S2: 1-2% compression increase in "better" compression mode. +* Aug 26, 2019: zstd: Check maximum size of Huffman 1X compressed literals while decoding. +* Aug 24, 2019: (v1.8.0) Added [S2 compression](https://github.com/klauspost/compress/tree/master/s2#s2-compression), a high performance replacement for Snappy. +* Aug 21, 2019: (v1.7.6) Fixed minor issues found by fuzzer. One could lead to zstd not decompressing. +* Aug 18, 2019: Add [fuzzit](https://fuzzit.dev/) continuous fuzzing. +* Aug 14, 2019: zstd: Skip incompressible data 2x faster. [#147](https://github.com/klauspost/compress/pull/147) +* Aug 4, 2019 (v1.7.5): Better literal compression. [#146](https://github.com/klauspost/compress/pull/146) +* Aug 4, 2019: Faster zstd compression. [#143](https://github.com/klauspost/compress/pull/143) [#144](https://github.com/klauspost/compress/pull/144) +* Aug 4, 2019: Faster zstd decompression. [#145](https://github.com/klauspost/compress/pull/145) [#143](https://github.com/klauspost/compress/pull/143) [#142](https://github.com/klauspost/compress/pull/142) +* July 15, 2019 (v1.7.4): Fix double EOF block in rare cases on zstd encoder. +* July 15, 2019 (v1.7.3): Minor speedup/compression increase in default zstd encoder. +* July 14, 2019: zstd decoder: Fix decompression error on multiple uses with mixed content. +* July 7, 2019 (v1.7.2): Snappy update, zstd decoder potential race fix. +* June 17, 2019: zstd decompression bugfix. +* June 17, 2019: fix 32 bit builds. +* June 17, 2019: Easier use in modules (less dependencies). +* June 9, 2019: New stronger "default" [zstd](https://github.com/klauspost/compress/tree/master/zstd#zstd) compression mode. Matches zstd default compression ratio. +* June 5, 2019: 20-40% throughput in [zstandard](https://github.com/klauspost/compress/tree/master/zstd#zstd) compression and better compression. +* June 5, 2019: deflate/gzip compression: Reduce memory usage of lower compression levels. +* June 2, 2019: Added [zstandard](https://github.com/klauspost/compress/tree/master/zstd#zstd) compression! +* May 25, 2019: deflate/gzip: 10% faster bit writer, mostly visible in lower levels. +* Apr 22, 2019: [zstd](https://github.com/klauspost/compress/tree/master/zstd#zstd) decompression added. +* Aug 1, 2018: Added [huff0 README](https://github.com/klauspost/compress/tree/master/huff0#huff0-entropy-compression). +* Jul 8, 2018: Added [Performance Update 2018](#performance-update-2018) below. +* Jun 23, 2018: Merged [Go 1.11 inflate optimizations](https://go-review.googlesource.com/c/go/+/102235). Go 1.9 is now required. Backwards compatible version tagged with [v1.3.0](https://github.com/klauspost/compress/releases/tag/v1.3.0). +* Apr 2, 2018: Added [huff0](https://godoc.org/github.com/klauspost/compress/huff0) en/decoder. Experimental for now, API may change. +* Mar 4, 2018: Added [FSE Entropy](https://godoc.org/github.com/klauspost/compress/fse) en/decoder. Experimental for now, API may change. +* Nov 3, 2017: Add compression [Estimate](https://godoc.org/github.com/klauspost/compress#Estimate) function. +* May 28, 2017: Reduce allocations when resetting decoder. +* Apr 02, 2017: Change back to official crc32, since changes were merged in Go 1.7. +* Jan 14, 2017: Reduce stack pressure due to array copies. See [Issue #18625](https://github.com/golang/go/issues/18625). +* Oct 25, 2016: Level 2-4 have been rewritten and now offers significantly better performance than before. +* Oct 20, 2016: Port zlib changes from Go 1.7 to fix zlib writer issue. Please update. +* Oct 16, 2016: Go 1.7 changes merged. Apples to apples this package is a few percent faster, but has a significantly better balance between speed and compression per level. +* Mar 24, 2016: Always attempt Huffman encoding on level 4-7. This improves base 64 encoded data compression. +* Mar 24, 2016: Small speedup for level 1-3. +* Feb 19, 2016: Faster bit writer, level -2 is 15% faster, level 1 is 4% faster. +* Feb 19, 2016: Handle small payloads faster in level 1-3. +* Feb 19, 2016: Added faster level 2 + 3 compression modes. +* Feb 19, 2016: [Rebalanced compression levels](https://blog.klauspost.com/rebalancing-deflate-compression-levels/), so there is a more even progression in terms of compression. New default level is 5. +* Feb 14, 2016: Snappy: Merge upstream changes. +* Feb 14, 2016: Snappy: Fix aggressive skipping. +* Feb 14, 2016: Snappy: Update benchmark. +* Feb 13, 2016: Deflate: Fixed assembler problem that could lead to sub-optimal compression. +* Feb 12, 2016: Snappy: Added AMD64 SSE 4.2 optimizations to matching, which makes easy to compress material run faster. Typical speedup is around 25%. +* Feb 9, 2016: Added Snappy package fork. This version is 5-7% faster, much more on hard to compress content. +* Jan 30, 2016: Optimize level 1 to 3 by not considering static dictionary or storing uncompressed. ~4-5% speedup. +* Jan 16, 2016: Optimization on deflate level 1,2,3 compression. +* Jan 8 2016: Merge [CL 18317](https://go-review.googlesource.com/#/c/18317): fix reading, writing of zip64 archives. +* Dec 8 2015: Make level 1 and -2 deterministic even if write size differs. +* Dec 8 2015: Split encoding functions, so hashing and matching can potentially be inlined. 1-3% faster on AMD64. 5% faster on other platforms. +* Dec 8 2015: Fixed rare [one byte out-of bounds read](https://github.com/klauspost/compress/issues/20). Please update! +* Nov 23 2015: Optimization on token writer. ~2-4% faster. Contributed by [@dsnet](https://github.com/dsnet). +* Nov 20 2015: Small optimization to bit writer on 64 bit systems. +* Nov 17 2015: Fixed out-of-bound errors if the underlying Writer returned an error. See [#15](https://github.com/klauspost/compress/issues/15). +* Nov 12 2015: Added [io.WriterTo](https://golang.org/pkg/io/#WriterTo) support to gzip/inflate. +* Nov 11 2015: Merged [CL 16669](https://go-review.googlesource.com/#/c/16669/4): archive/zip: enable overriding (de)compressors per file +* Oct 15 2015: Added skipping on uncompressible data. Random data speed up >5x. + +
+ +# deflate usage + +The packages are drop-in replacements for standard library [deflate](https://godoc.org/github.com/klauspost/compress/flate), [gzip](https://godoc.org/github.com/klauspost/compress/gzip), [zip](https://godoc.org/github.com/klauspost/compress/zip), and [zlib](https://godoc.org/github.com/klauspost/compress/zlib). Simply replace the import path to use them: + +Typical speed is about 2x of the standard library packages. + +| old import | new import | Documentation | +|------------------|---------------------------------------|-------------------------------------------------------------------------| +| `compress/gzip` | `github.com/klauspost/compress/gzip` | [gzip](https://pkg.go.dev/github.com/klauspost/compress/gzip?tab=doc) | +| `compress/zlib` | `github.com/klauspost/compress/zlib` | [zlib](https://pkg.go.dev/github.com/klauspost/compress/zlib?tab=doc) | +| `archive/zip` | `github.com/klauspost/compress/zip` | [zip](https://pkg.go.dev/github.com/klauspost/compress/zip?tab=doc) | +| `compress/flate` | `github.com/klauspost/compress/flate` | [flate](https://pkg.go.dev/github.com/klauspost/compress/flate?tab=doc) | + +You may also be interested in [pgzip](https://github.com/klauspost/pgzip), which is a drop-in replacement for gzip, which support multithreaded compression on big files and the optimized [crc32](https://github.com/klauspost/crc32) package used by these packages. + +The packages implement the same API as the standard library, so you can use the original godoc documentation: [gzip](http://golang.org/pkg/compress/gzip/), [zip](http://golang.org/pkg/archive/zip/), [zlib](http://golang.org/pkg/compress/zlib/), [flate](http://golang.org/pkg/compress/flate/). + +Currently there is only minor speedup on decompression (mostly CRC32 calculation). + +Memory usage is typically 1MB for a Writer. stdlib is in the same range. +If you expect to have a lot of concurrently allocated Writers consider using +the stateless compression described below. + +For compression performance, see: [this spreadsheet](https://docs.google.com/spreadsheets/d/1nuNE2nPfuINCZJRMt6wFWhKpToF95I47XjSsc-1rbPQ/edit?usp=sharing). + +To disable all assembly add `-tags=noasm`. This works across all packages. + +# Stateless compression + +This package offers stateless compression as a special option for gzip/deflate. +It will do compression but without maintaining any state between Write calls. + +This means there will be no memory kept between Write calls, but compression and speed will be suboptimal. + +This is only relevant in cases where you expect to run many thousands of compressors concurrently, +but with very little activity. This is *not* intended for regular web servers serving individual requests. + +Because of this, the size of actual Write calls will affect output size. + +In gzip, specify level `-3` / `gzip.StatelessCompression` to enable. + +For direct deflate use, NewStatelessWriter and StatelessDeflate are available. See [documentation](https://godoc.org/github.com/klauspost/compress/flate#NewStatelessWriter) + +A `bufio.Writer` can of course be used to control write sizes. For example, to use a 4KB buffer: + +```go + // replace 'ioutil.Discard' with your output. + gzw, err := gzip.NewWriterLevel(ioutil.Discard, gzip.StatelessCompression) + if err != nil { + return err + } + defer gzw.Close() + + w := bufio.NewWriterSize(gzw, 4096) + defer w.Flush() + + // Write to 'w' +``` + +This will only use up to 4KB in memory when the writer is idle. + +Compression is almost always worse than the fastest compression level +and each write will allocate (a little) memory. + + +# Other packages + +Here are other packages of good quality and pure Go (no cgo wrappers or autoconverted code): + +* [github.com/pierrec/lz4](https://github.com/pierrec/lz4) - strong multithreaded LZ4 compression. +* [github.com/cosnicolaou/pbzip2](https://github.com/cosnicolaou/pbzip2) - multithreaded bzip2 decompression. +* [github.com/dsnet/compress](https://github.com/dsnet/compress) - brotli decompression, bzip2 writer. +* [github.com/ronanh/intcomp](https://github.com/ronanh/intcomp) - Integer compression. +* [github.com/spenczar/fpc](https://github.com/spenczar/fpc) - Float compression. +* [github.com/minio/zipindex](https://github.com/minio/zipindex) - External ZIP directory index. +* [github.com/ybirader/pzip](https://github.com/ybirader/pzip) - Fast concurrent zip archiver and extractor. + +# license + +This code is licensed under the same conditions as the original Go code. See LICENSE file. + + + + + diff --git a/vendor/github.com/klauspost/compress/SECURITY.md b/vendor/github.com/klauspost/compress/SECURITY.md new file mode 100644 index 000000000..ca6685e2b --- /dev/null +++ b/vendor/github.com/klauspost/compress/SECURITY.md @@ -0,0 +1,25 @@ +# Security Policy + +## Supported Versions + +Security updates are applied only to the latest release. + +## Vulnerability Definition + +A security vulnerability is a bug that with certain input triggers a crash or an infinite loop. Most calls will have varying execution time and only in rare cases will slow operation be considered a security vulnerability. + +Corrupted output generally is not considered a security vulnerability, unless independent operations are able to affect each other. Note that not all functionality is re-entrant and safe to use concurrently. + +Out-of-memory crashes only applies if the en/decoder uses an abnormal amount of memory, with appropriate options applied, to limit maximum window size, concurrency, etc. However, if you are in doubt you are welcome to file a security issue. + +It is assumed that all callers are trusted, meaning internal data exposed through reflection or inspection of returned data structures is not considered a vulnerability. + +Vulnerabilities resulting from compiler/assembler errors should be reported upstream. Depending on the severity this package may or may not implement a workaround. + +## Reporting a Vulnerability + +If you have discovered a security vulnerability in this project, please report it privately. **Do not disclose it as a public issue.** This gives us time to work with you to fix the issue before public exposure, reducing the chance that the exploit will be used before a patch is released. + +Please disclose it at [security advisory](https://github.com/klauspost/compress/security/advisories/new). If possible please provide a minimal reproducer. If the issue only applies to a single platform, it would be helpful to provide access to that. + +This project is maintained by a team of volunteers on a reasonable-effort basis. As such, vulnerabilities will be disclosed in a best effort base. diff --git a/vendor/github.com/klauspost/compress/compressible.go b/vendor/github.com/klauspost/compress/compressible.go new file mode 100644 index 000000000..ea5a692d5 --- /dev/null +++ b/vendor/github.com/klauspost/compress/compressible.go @@ -0,0 +1,85 @@ +package compress + +import "math" + +// Estimate returns a normalized compressibility estimate of block b. +// Values close to zero are likely uncompressible. +// Values above 0.1 are likely to be compressible. +// Values above 0.5 are very compressible. +// Very small lengths will return 0. +func Estimate(b []byte) float64 { + if len(b) < 16 { + return 0 + } + + // Correctly predicted order 1 + hits := 0 + lastMatch := false + var o1 [256]byte + var hist [256]int + c1 := byte(0) + for _, c := range b { + if c == o1[c1] { + // We only count a hit if there was two correct predictions in a row. + if lastMatch { + hits++ + } + lastMatch = true + } else { + lastMatch = false + } + o1[c1] = c + c1 = c + hist[c]++ + } + + // Use x^0.6 to give better spread + prediction := math.Pow(float64(hits)/float64(len(b)), 0.6) + + // Calculate histogram distribution + variance := float64(0) + avg := float64(len(b)) / 256 + + for _, v := range hist { + Δ := float64(v) - avg + variance += Δ * Δ + } + + stddev := math.Sqrt(float64(variance)) / float64(len(b)) + exp := math.Sqrt(1 / float64(len(b))) + + // Subtract expected stddev + stddev -= exp + if stddev < 0 { + stddev = 0 + } + stddev *= 1 + exp + + // Use x^0.4 to give better spread + entropy := math.Pow(stddev, 0.4) + + // 50/50 weight between prediction and histogram distribution + return math.Pow((prediction+entropy)/2, 0.9) +} + +// ShannonEntropyBits returns the number of bits minimum required to represent +// an entropy encoding of the input bytes. +// https://en.wiktionary.org/wiki/Shannon_entropy +func ShannonEntropyBits(b []byte) int { + if len(b) == 0 { + return 0 + } + var hist [256]int + for _, c := range b { + hist[c]++ + } + shannon := float64(0) + invTotal := 1.0 / float64(len(b)) + for _, v := range hist[:] { + if v > 0 { + n := float64(v) + shannon += math.Ceil(-math.Log2(n*invTotal) * n) + } + } + return int(math.Ceil(shannon)) +} diff --git a/vendor/github.com/klauspost/compress/flate/deflate.go b/vendor/github.com/klauspost/compress/flate/deflate.go new file mode 100644 index 000000000..57d17eeab --- /dev/null +++ b/vendor/github.com/klauspost/compress/flate/deflate.go @@ -0,0 +1,996 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Copyright (c) 2015 Klaus Post +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package flate + +import ( + "errors" + "fmt" + "io" + "math" + + "github.com/klauspost/compress/internal/le" +) + +const ( + NoCompression = 0 + BestSpeed = 1 + BestCompression = 9 + DefaultCompression = -1 + + // HuffmanOnly disables Lempel-Ziv match searching and only performs Huffman + // entropy encoding. This mode is useful in compressing data that has + // already been compressed with an LZ style algorithm (e.g. Snappy or LZ4) + // that lacks an entropy encoder. Compression gains are achieved when + // certain bytes in the input stream occur more frequently than others. + // + // Note that HuffmanOnly produces a compressed output that is + // RFC 1951 compliant. That is, any valid DEFLATE decompressor will + // continue to be able to decompress this output. + HuffmanOnly = -2 + ConstantCompression = HuffmanOnly // compatibility alias. + + logWindowSize = 15 + windowSize = 1 << logWindowSize + windowMask = windowSize - 1 + logMaxOffsetSize = 15 // Standard DEFLATE + minMatchLength = 4 // The smallest match that the compressor looks for + maxMatchLength = 258 // The longest match for the compressor + minOffsetSize = 1 // The shortest offset that makes any sense + + // The maximum number of tokens we will encode at the time. + // Smaller sizes usually creates less optimal blocks. + // Bigger can make context switching slow. + // We use this for levels 7-9, so we make it big. + maxFlateBlockTokens = 1 << 15 + maxStoreBlockSize = 65535 + hashBits = 17 // After 17 performance degrades + hashSize = 1 << hashBits + hashMask = (1 << hashBits) - 1 + hashShift = (hashBits + minMatchLength - 1) / minMatchLength + maxHashOffset = 1 << 28 + + skipNever = math.MaxInt32 + + debugDeflate = false +) + +type compressionLevel struct { + good, lazy, nice, chain, fastSkipHashing, level int +} + +// Compression levels have been rebalanced from zlib deflate defaults +// to give a bigger spread in speed and compression. +// See https://blog.klauspost.com/rebalancing-deflate-compression-levels/ +var levels = []compressionLevel{ + {}, // 0 + // Level 1-6 uses specialized algorithm - values not used + {0, 0, 0, 0, 0, 1}, + {0, 0, 0, 0, 0, 2}, + {0, 0, 0, 0, 0, 3}, + {0, 0, 0, 0, 0, 4}, + {0, 0, 0, 0, 0, 5}, + {0, 0, 0, 0, 0, 6}, + // Levels 7-9 use increasingly more lazy matching + // and increasingly stringent conditions for "good enough". + {8, 12, 16, 24, skipNever, 7}, + {16, 30, 40, 64, skipNever, 8}, + {32, 258, 258, 1024, skipNever, 9}, +} + +// advancedState contains state for the advanced levels, with bigger hash tables, etc. +type advancedState struct { + // deflate state + length int + offset int + maxInsertIndex int + chainHead int + hashOffset int + + ii uint16 // position of last match, intended to overflow to reset. + + // input window: unprocessed data is window[index:windowEnd] + index int + hashMatch [maxMatchLength + minMatchLength]uint32 + + // Input hash chains + // hashHead[hashValue] contains the largest inputIndex with the specified hash value + // If hashHead[hashValue] is within the current window, then + // hashPrev[hashHead[hashValue] & windowMask] contains the previous index + // with the same hash value. + hashHead [hashSize]uint32 + hashPrev [windowSize]uint32 +} + +type compressor struct { + compressionLevel + + h *huffmanEncoder + w *huffmanBitWriter + + // compression algorithm + fill func(*compressor, []byte) int // copy data to window + step func(*compressor) // process window + + window []byte + windowEnd int + blockStart int // window index where current tokens start + err error + + // queued output tokens + tokens tokens + fast fastEnc + state *advancedState + + sync bool // requesting flush + byteAvailable bool // if true, still need to process window[index-1]. +} + +func (d *compressor) fillDeflate(b []byte) int { + s := d.state + if s.index >= 2*windowSize-(minMatchLength+maxMatchLength) { + // shift the window by windowSize + //copy(d.window[:], d.window[windowSize:2*windowSize]) + *(*[windowSize]byte)(d.window) = *(*[windowSize]byte)(d.window[windowSize:]) + s.index -= windowSize + d.windowEnd -= windowSize + if d.blockStart >= windowSize { + d.blockStart -= windowSize + } else { + d.blockStart = math.MaxInt32 + } + s.hashOffset += windowSize + if s.hashOffset > maxHashOffset { + delta := s.hashOffset - 1 + s.hashOffset -= delta + s.chainHead -= delta + // Iterate over slices instead of arrays to avoid copying + // the entire table onto the stack (Issue #18625). + for i, v := range s.hashPrev[:] { + if int(v) > delta { + s.hashPrev[i] = uint32(int(v) - delta) + } else { + s.hashPrev[i] = 0 + } + } + for i, v := range s.hashHead[:] { + if int(v) > delta { + s.hashHead[i] = uint32(int(v) - delta) + } else { + s.hashHead[i] = 0 + } + } + } + } + n := copy(d.window[d.windowEnd:], b) + d.windowEnd += n + return n +} + +func (d *compressor) writeBlock(tok *tokens, index int, eof bool) error { + if index > 0 || eof { + var window []byte + if d.blockStart <= index { + window = d.window[d.blockStart:index] + } + d.blockStart = index + //d.w.writeBlock(tok, eof, window) + d.w.writeBlockDynamic(tok, eof, window, d.sync) + return d.w.err + } + return nil +} + +// writeBlockSkip writes the current block and uses the number of tokens +// to determine if the block should be stored on no matches, or +// only huffman encoded. +func (d *compressor) writeBlockSkip(tok *tokens, index int, eof bool) error { + if index > 0 || eof { + if d.blockStart <= index { + window := d.window[d.blockStart:index] + // If we removed less than a 64th of all literals + // we huffman compress the block. + if int(tok.n) > len(window)-int(tok.n>>6) { + d.w.writeBlockHuff(eof, window, d.sync) + } else { + // Write a dynamic huffman block. + d.w.writeBlockDynamic(tok, eof, window, d.sync) + } + } else { + d.w.writeBlock(tok, eof, nil) + } + d.blockStart = index + return d.w.err + } + return nil +} + +// fillWindow will fill the current window with the supplied +// dictionary and calculate all hashes. +// This is much faster than doing a full encode. +// Should only be used after a start/reset. +func (d *compressor) fillWindow(b []byte) { + // Do not fill window if we are in store-only or huffman mode. + if d.level <= 0 && d.level > -MinCustomWindowSize { + return + } + if d.fast != nil { + // encode the last data, but discard the result + if len(b) > maxMatchOffset { + b = b[len(b)-maxMatchOffset:] + } + d.fast.Encode(&d.tokens, b) + d.tokens.Reset() + return + } + s := d.state + // If we are given too much, cut it. + if len(b) > windowSize { + b = b[len(b)-windowSize:] + } + // Add all to window. + n := copy(d.window[d.windowEnd:], b) + + // Calculate 256 hashes at the time (more L1 cache hits) + loops := (n + 256 - minMatchLength) / 256 + for j := range loops { + startindex := j * 256 + end := min(startindex+256+minMatchLength-1, n) + tocheck := d.window[startindex:end] + dstSize := len(tocheck) - minMatchLength + 1 + + if dstSize <= 0 { + continue + } + + dst := s.hashMatch[:dstSize] + bulkHash4(tocheck, dst) + var newH uint32 + for i, val := range dst { + di := i + startindex + newH = val & hashMask + // Get previous value with the same hash. + // Our chain should point to the previous value. + s.hashPrev[di&windowMask] = s.hashHead[newH] + // Set the head of the hash chain to us. + s.hashHead[newH] = uint32(di + s.hashOffset) + } + } + // Update window information. + d.windowEnd += n + s.index = n +} + +// Try to find a match starting at index whose length is greater than prevSize. +// We only look at chainCount possibilities before giving up. +// pos = s.index, prevHead = s.chainHead-s.hashOffset, prevLength=minMatchLength-1, lookahead +func (d *compressor) findMatch(pos int, prevHead int, lookahead int) (length, offset int, ok bool) { + minMatchLook := min(lookahead, maxMatchLength) + + win := d.window[0 : pos+minMatchLook] + + // We quit when we get a match that's at least nice long + nice := min(d.nice, len(win)-pos) + + // If we've got a match that's good enough, only look in 1/4 the chain. + tries := d.chain + length = minMatchLength - 1 + + wEnd := win[pos+length] + wPos := win[pos:] + minIndex := max(pos-windowSize, 0) + offset = 0 + + if d.chain < 100 { + for i := prevHead; tries > 0; tries-- { + if wEnd == win[i+length] { + n := matchLen(win[i:i+minMatchLook], wPos) + if n > length { + length = n + offset = pos - i + ok = true + if n >= nice { + // The match is good enough that we don't try to find a better one. + break + } + wEnd = win[pos+n] + } + } + if i <= minIndex { + // hashPrev[i & windowMask] has already been overwritten, so stop now. + break + } + i = int(d.state.hashPrev[i&windowMask]) - d.state.hashOffset + if i < minIndex { + break + } + } + return + } + + // Minimum gain to accept a match. + cGain := 4 + + // Some like it higher (CSV), some like it lower (JSON) + const baseCost = 3 + // Base is 4 bytes at with an additional cost. + // Matches must be better than this. + + for i := prevHead; tries > 0; tries-- { + if wEnd == win[i+length] { + n := matchLen(win[i:i+minMatchLook], wPos) + if n > length { + // Calculate gain. Estimate + newGain := d.h.bitLengthRaw(wPos[:n]) - int(offsetExtraBits[offsetCode(uint32(pos-i))]) - baseCost - int(lengthExtraBits[lengthCodes[(n-3)&255]]) + + //fmt.Println("gain:", newGain, "prev:", cGain, "raw:", d.h.bitLengthRaw(wPos[:n]), "this-len:", n, "prev-len:", length) + if newGain > cGain { + length = n + offset = pos - i + cGain = newGain + ok = true + if n >= nice { + // The match is good enough that we don't try to find a better one. + break + } + wEnd = win[pos+n] + } + } + } + if i <= minIndex { + // hashPrev[i & windowMask] has already been overwritten, so stop now. + break + } + i = int(d.state.hashPrev[i&windowMask]) - d.state.hashOffset + if i < minIndex { + break + } + } + return +} + +func (d *compressor) writeStoredBlock(buf []byte) error { + if d.w.writeStoredHeader(len(buf), false); d.w.err != nil { + return d.w.err + } + d.w.writeBytes(buf) + return d.w.err +} + +// hash4 returns a hash representation of the first 4 bytes +// of the supplied slice. +// The caller must ensure that len(b) >= 4. +func hash4(b []byte) uint32 { + return hash4u(le.Load32(b, 0), hashBits) +} + +// hash4 returns the hash of u to fit in a hash table with h bits. +// Preferably h should be a constant and should always be <32. +func hash4u(u uint32, h uint8) uint32 { + return (u * prime4bytes) >> (32 - h) +} + +// bulkHash4 will compute hashes using the same +// algorithm as hash4 +func bulkHash4(b []byte, dst []uint32) { + if len(b) < 4 { + return + } + hb := le.Load32(b, 0) + + dst[0] = hash4u(hb, hashBits) + end := len(b) - 4 + 1 + for i := 1; i < end; i++ { + hb = (hb >> 8) | uint32(b[i+3])<<24 + dst[i] = hash4u(hb, hashBits) + } +} + +func (d *compressor) initDeflate() { + d.window = make([]byte, 2*windowSize) + d.byteAvailable = false + d.err = nil + if d.state == nil { + return + } + s := d.state + s.index = 0 + s.hashOffset = 1 + s.length = minMatchLength - 1 + s.offset = 0 + s.chainHead = -1 +} + +// deflateLazy is the same as deflate, but with d.fastSkipHashing == skipNever, +// meaning it always has lazy matching on. +func (d *compressor) deflateLazy() { + s := d.state + // Sanity enables additional runtime tests. + // It's intended to be used during development + // to supplement the currently ad-hoc unit tests. + const sanity = debugDeflate + + if d.windowEnd-s.index < minMatchLength+maxMatchLength && !d.sync { + return + } + if d.windowEnd != s.index && d.chain > 100 { + // Get literal huffman coder. + if d.h == nil { + d.h = newHuffmanEncoder(maxFlateBlockTokens) + } + var tmp [256]uint16 + toIndex := d.window[s.index:d.windowEnd] + toIndex = toIndex[:min(len(toIndex), maxFlateBlockTokens)] + for _, v := range toIndex { + tmp[v]++ + } + d.h.generate(tmp[:], 15) + } + + s.maxInsertIndex = d.windowEnd - (minMatchLength - 1) + + for { + if sanity && s.index > d.windowEnd { + panic("index > windowEnd") + } + lookahead := d.windowEnd - s.index + if lookahead < minMatchLength+maxMatchLength { + if !d.sync { + return + } + if sanity && s.index > d.windowEnd { + panic("index > windowEnd") + } + if lookahead == 0 { + // Flush current output block if any. + if d.byteAvailable { + // There is still one pending token that needs to be flushed + d.tokens.AddLiteral(d.window[s.index-1]) + d.byteAvailable = false + } + if d.tokens.n > 0 { + if d.err = d.writeBlock(&d.tokens, s.index, false); d.err != nil { + return + } + d.tokens.Reset() + } + return + } + } + if s.index < s.maxInsertIndex { + // Update the hash + hash := hash4(d.window[s.index:]) + ch := s.hashHead[hash] + s.chainHead = int(ch) + s.hashPrev[s.index&windowMask] = ch + s.hashHead[hash] = uint32(s.index + s.hashOffset) + } + prevLength := s.length + prevOffset := s.offset + s.length = minMatchLength - 1 + s.offset = 0 + minIndex := max(s.index-windowSize, 0) + + if s.chainHead-s.hashOffset >= minIndex && lookahead > prevLength && prevLength < d.lazy { + if newLength, newOffset, ok := d.findMatch(s.index, s.chainHead-s.hashOffset, lookahead); ok { + s.length = newLength + s.offset = newOffset + } + } + + if prevLength >= minMatchLength && s.length <= prevLength { + // No better match, but check for better match at end... + // + // Skip forward a number of bytes. + // Offset of 2 seems to yield best results. 3 is sometimes better. + const checkOff = 2 + + // Check all, except full length + if prevLength < maxMatchLength-checkOff { + prevIndex := s.index - 1 + if prevIndex+prevLength < s.maxInsertIndex { + end := min(lookahead, maxMatchLength+checkOff) + end += prevIndex + + // Hash at match end. + h := hash4(d.window[prevIndex+prevLength:]) + ch2 := int(s.hashHead[h]) - s.hashOffset - prevLength + if prevIndex-ch2 != prevOffset && ch2 > minIndex+checkOff { + length := matchLen(d.window[prevIndex+checkOff:end], d.window[ch2+checkOff:]) + // It seems like a pure length metric is best. + if length > prevLength { + prevLength = length + prevOffset = prevIndex - ch2 + + // Extend back... + for i := checkOff - 1; i >= 0; i-- { + if prevLength >= maxMatchLength || d.window[prevIndex+i] != d.window[ch2+i] { + // Emit tokens we "owe" + for j := 0; j <= i; j++ { + d.tokens.AddLiteral(d.window[prevIndex+j]) + if d.tokens.n == maxFlateBlockTokens { + // The block includes the current character + if d.err = d.writeBlock(&d.tokens, s.index, false); d.err != nil { + return + } + d.tokens.Reset() + } + s.index++ + if s.index < s.maxInsertIndex { + h := hash4(d.window[s.index:]) + ch := s.hashHead[h] + s.chainHead = int(ch) + s.hashPrev[s.index&windowMask] = ch + s.hashHead[h] = uint32(s.index + s.hashOffset) + } + } + break + } else { + prevLength++ + } + } + } else if false { + // Check one further ahead. + // Only rarely better, disabled for now. + prevIndex++ + h := hash4(d.window[prevIndex+prevLength:]) + ch2 := int(s.hashHead[h]) - s.hashOffset - prevLength + if prevIndex-ch2 != prevOffset && ch2 > minIndex+checkOff { + length := matchLen(d.window[prevIndex+checkOff:end], d.window[ch2+checkOff:]) + // It seems like a pure length metric is best. + if length > prevLength+checkOff { + prevLength = length + prevOffset = prevIndex - ch2 + prevIndex-- + + // Extend back... + for i := checkOff; i >= 0; i-- { + if prevLength >= maxMatchLength || d.window[prevIndex+i] != d.window[ch2+i-1] { + // Emit tokens we "owe" + for j := 0; j <= i; j++ { + d.tokens.AddLiteral(d.window[prevIndex+j]) + if d.tokens.n == maxFlateBlockTokens { + // The block includes the current character + if d.err = d.writeBlock(&d.tokens, s.index, false); d.err != nil { + return + } + d.tokens.Reset() + } + s.index++ + if s.index < s.maxInsertIndex { + h := hash4(d.window[s.index:]) + ch := s.hashHead[h] + s.chainHead = int(ch) + s.hashPrev[s.index&windowMask] = ch + s.hashHead[h] = uint32(s.index + s.hashOffset) + } + } + break + } else { + prevLength++ + } + } + } + } + } + } + } + } + // There was a match at the previous step, and the current match is + // not better. Output the previous match. + d.tokens.AddMatch(uint32(prevLength-3), uint32(prevOffset-minOffsetSize)) + + // Insert in the hash table all strings up to the end of the match. + // index and index-1 are already inserted. If there is not enough + // lookahead, the last two strings are not inserted into the hash + // table. + newIndex := s.index + prevLength - 1 + // Calculate missing hashes + end := min(newIndex, s.maxInsertIndex) + end += minMatchLength - 1 + startindex := min(s.index+1, s.maxInsertIndex) + tocheck := d.window[startindex:end] + dstSize := len(tocheck) - minMatchLength + 1 + if dstSize > 0 { + dst := s.hashMatch[:dstSize] + bulkHash4(tocheck, dst) + var newH uint32 + for i, val := range dst { + di := i + startindex + newH = val & hashMask + // Get previous value with the same hash. + // Our chain should point to the previous value. + s.hashPrev[di&windowMask] = s.hashHead[newH] + // Set the head of the hash chain to us. + s.hashHead[newH] = uint32(di + s.hashOffset) + } + } + + s.index = newIndex + d.byteAvailable = false + s.length = minMatchLength - 1 + if d.tokens.n == maxFlateBlockTokens { + // The block includes the current character + if d.err = d.writeBlock(&d.tokens, s.index, false); d.err != nil { + return + } + d.tokens.Reset() + } + s.ii = 0 + } else { + // Reset, if we got a match this run. + if s.length >= minMatchLength { + s.ii = 0 + } + // We have a byte waiting. Emit it. + if d.byteAvailable { + s.ii++ + d.tokens.AddLiteral(d.window[s.index-1]) + if d.tokens.n == maxFlateBlockTokens { + if d.err = d.writeBlock(&d.tokens, s.index, false); d.err != nil { + return + } + d.tokens.Reset() + } + s.index++ + + // If we have a long run of no matches, skip additional bytes + // Resets when s.ii overflows after 64KB. + if n := int(s.ii) - d.chain; n > 0 { + n = 1 + int(n>>6) + for j := 0; j < n; j++ { + if s.index >= d.windowEnd-1 { + break + } + d.tokens.AddLiteral(d.window[s.index-1]) + if d.tokens.n == maxFlateBlockTokens { + if d.err = d.writeBlock(&d.tokens, s.index, false); d.err != nil { + return + } + d.tokens.Reset() + } + // Index... + if s.index < s.maxInsertIndex { + h := hash4(d.window[s.index:]) + ch := s.hashHead[h] + s.chainHead = int(ch) + s.hashPrev[s.index&windowMask] = ch + s.hashHead[h] = uint32(s.index + s.hashOffset) + } + s.index++ + } + // Flush last byte + d.tokens.AddLiteral(d.window[s.index-1]) + d.byteAvailable = false + // s.length = minMatchLength - 1 // not needed, since s.ii is reset above, so it should never be > minMatchLength + if d.tokens.n == maxFlateBlockTokens { + if d.err = d.writeBlock(&d.tokens, s.index, false); d.err != nil { + return + } + d.tokens.Reset() + } + } + } else { + s.index++ + d.byteAvailable = true + } + } + } +} + +func (d *compressor) store() { + if d.windowEnd > 0 && (d.windowEnd == maxStoreBlockSize || d.sync) { + d.err = d.writeStoredBlock(d.window[:d.windowEnd]) + d.windowEnd = 0 + } +} + +// fillWindow will fill the buffer with data for huffman-only compression. +// The number of bytes copied is returned. +func (d *compressor) fillBlock(b []byte) int { + n := copy(d.window[d.windowEnd:], b) + d.windowEnd += n + return n +} + +// storeHuff will compress and store the currently added data, +// if enough has been accumulated or we at the end of the stream. +// Any error that occurred will be in d.err +func (d *compressor) storeHuff() { + if d.windowEnd < len(d.window) && !d.sync || d.windowEnd == 0 { + return + } + d.w.writeBlockHuff(false, d.window[:d.windowEnd], d.sync) + d.err = d.w.err + d.windowEnd = 0 +} + +// storeFast will compress and store the currently added data, +// if enough has been accumulated or we at the end of the stream. +// Any error that occurred will be in d.err +func (d *compressor) storeFast() { + // We only compress if we have maxStoreBlockSize. + if d.windowEnd < len(d.window) { + if !d.sync { + return + } + // Handle extremely small sizes. + if d.windowEnd < 128 { + if d.windowEnd == 0 { + return + } + if d.windowEnd <= 32 { + d.err = d.writeStoredBlock(d.window[:d.windowEnd]) + } else { + d.w.writeBlockHuff(false, d.window[:d.windowEnd], true) + d.err = d.w.err + } + d.tokens.Reset() + d.windowEnd = 0 + d.fast.Reset() + return + } + } + + d.fast.Encode(&d.tokens, d.window[:d.windowEnd]) + // If we made zero matches, store the block as is. + if d.tokens.n == 0 { + d.err = d.writeStoredBlock(d.window[:d.windowEnd]) + // If we removed less than 1/16th, huffman compress the block. + } else if int(d.tokens.n) > d.windowEnd-(d.windowEnd>>4) { + d.w.writeBlockHuff(false, d.window[:d.windowEnd], d.sync) + d.err = d.w.err + } else { + d.w.writeBlockDynamic(&d.tokens, false, d.window[:d.windowEnd], d.sync) + d.err = d.w.err + } + d.tokens.Reset() + d.windowEnd = 0 +} + +// write will add input byte to the stream. +// Unless an error occurs all bytes will be consumed. +func (d *compressor) write(b []byte) (n int, err error) { + if d.err != nil { + return 0, d.err + } + n = len(b) + for len(b) > 0 { + if d.windowEnd == len(d.window) || d.sync { + d.step(d) + } + b = b[d.fill(d, b):] + if d.err != nil { + return 0, d.err + } + } + return n, d.err +} + +func (d *compressor) syncFlush() error { + d.sync = true + if d.err != nil { + return d.err + } + d.step(d) + if d.err == nil { + d.w.writeStoredHeader(0, false) + d.w.flush() + d.err = d.w.err + } + d.sync = false + return d.err +} + +func (d *compressor) init(w io.Writer, level int) (err error) { + d.w = newHuffmanBitWriter(w) + + switch { + case level == NoCompression: + d.window = make([]byte, maxStoreBlockSize) + d.fill = (*compressor).fillBlock + d.step = (*compressor).store + case level == ConstantCompression: + d.w.logNewTablePenalty = 10 + d.window = make([]byte, 32<<10) + d.fill = (*compressor).fillBlock + d.step = (*compressor).storeHuff + case level == DefaultCompression: + level = 5 + fallthrough + case level >= 1 && level <= 6: + d.w.logNewTablePenalty = 7 + d.fast = newFastEnc(level) + d.window = make([]byte, maxStoreBlockSize) + d.fill = (*compressor).fillBlock + d.step = (*compressor).storeFast + case 7 <= level && level <= 9: + d.w.logNewTablePenalty = 8 + d.state = &advancedState{} + d.compressionLevel = levels[level] + d.initDeflate() + d.fill = (*compressor).fillDeflate + d.step = (*compressor).deflateLazy + case -level >= MinCustomWindowSize && -level <= MaxCustomWindowSize: + d.w.logNewTablePenalty = 7 + d.fast = &fastEncL5Window{maxOffset: int32(-level), cur: maxStoreBlockSize} + d.window = make([]byte, maxStoreBlockSize) + d.fill = (*compressor).fillBlock + d.step = (*compressor).storeFast + default: + return fmt.Errorf("flate: invalid compression level %d: want value in range [-2, 9]", level) + } + d.level = level + return nil +} + +// reset the state of the compressor. +func (d *compressor) reset(w io.Writer) { + d.w.reset(w) + d.sync = false + d.err = nil + // We only need to reset a few things for Snappy. + if d.fast != nil { + d.fast.Reset() + d.windowEnd = 0 + d.tokens.Reset() + return + } + switch d.compressionLevel.chain { + case 0: + // level was NoCompression or ConstantCompression. + d.windowEnd = 0 + default: + s := d.state + s.chainHead = -1 + for i := range s.hashHead { + s.hashHead[i] = 0 + } + for i := range s.hashPrev { + s.hashPrev[i] = 0 + } + s.hashOffset = 1 + s.index, d.windowEnd = 0, 0 + d.blockStart, d.byteAvailable = 0, false + d.tokens.Reset() + s.length = minMatchLength - 1 + s.offset = 0 + s.ii = 0 + s.maxInsertIndex = 0 + } +} + +func (d *compressor) close() error { + if d.err != nil { + return d.err + } + d.sync = true + d.step(d) + if d.err != nil { + return d.err + } + if d.w.writeStoredHeader(0, true); d.w.err != nil { + return d.w.err + } + d.w.flush() + d.w.reset(nil) + return d.w.err +} + +// NewWriter returns a new Writer compressing data at the given level. +// Following zlib, levels range from 1 (BestSpeed) to 9 (BestCompression); +// higher levels typically run slower but compress more. +// Level 0 (NoCompression) does not attempt any compression; it only adds the +// necessary DEFLATE framing. +// Level -1 (DefaultCompression) uses the default compression level. +// Level -2 (ConstantCompression) will use Huffman compression only, giving +// a very fast compression for all types of input, but sacrificing considerable +// compression efficiency. +// +// If level is in the range [-2, 9] then the error returned will be nil. +// Otherwise the error returned will be non-nil. +func NewWriter(w io.Writer, level int) (*Writer, error) { + var dw Writer + if err := dw.d.init(w, level); err != nil { + return nil, err + } + return &dw, nil +} + +// NewWriterDict is like NewWriter but initializes the new +// Writer with a preset dictionary. The returned Writer behaves +// as if the dictionary had been written to it without producing +// any compressed output. The compressed data written to w +// can only be decompressed by a Reader initialized with the +// same dictionary. +func NewWriterDict(w io.Writer, level int, dict []byte) (*Writer, error) { + zw, err := NewWriter(w, level) + if err != nil { + return nil, err + } + zw.d.fillWindow(dict) + zw.dict = append(zw.dict, dict...) // duplicate dictionary for Reset method. + return zw, err +} + +// MinCustomWindowSize is the minimum window size that can be sent to NewWriterWindow. +const MinCustomWindowSize = 32 + +// MaxCustomWindowSize is the maximum custom window that can be sent to NewWriterWindow. +const MaxCustomWindowSize = windowSize + +// NewWriterWindow returns a new Writer compressing data with a custom window size. +// windowSize must be from MinCustomWindowSize to MaxCustomWindowSize. +func NewWriterWindow(w io.Writer, windowSize int) (*Writer, error) { + if windowSize < MinCustomWindowSize { + return nil, errors.New("flate: requested window size less than MinWindowSize") + } + if windowSize > MaxCustomWindowSize { + return nil, errors.New("flate: requested window size bigger than MaxCustomWindowSize") + } + var dw Writer + if err := dw.d.init(w, -windowSize); err != nil { + return nil, err + } + return &dw, nil +} + +// A Writer takes data written to it and writes the compressed +// form of that data to an underlying writer (see NewWriter). +type Writer struct { + d compressor + dict []byte +} + +// Write writes data to w, which will eventually write the +// compressed form of data to its underlying writer. +func (w *Writer) Write(data []byte) (n int, err error) { + return w.d.write(data) +} + +// Flush flushes any pending data to the underlying writer. +// It is useful mainly in compressed network protocols, to ensure that +// a remote reader has enough data to reconstruct a packet. +// Flush does not return until the data has been written. +// Calling Flush when there is no pending data still causes the Writer +// to emit a sync marker of at least 4 bytes. +// If the underlying writer returns an error, Flush returns that error. +// +// In the terminology of the zlib library, Flush is equivalent to Z_SYNC_FLUSH. +func (w *Writer) Flush() error { + // For more about flushing: + // http://www.bolet.org/~pornin/deflate-flush.html + return w.d.syncFlush() +} + +// Close flushes and closes the writer. +func (w *Writer) Close() error { + return w.d.close() +} + +// Reset discards the writer's state and makes it equivalent to +// the result of NewWriter or NewWriterDict called with dst +// and w's level and dictionary. +func (w *Writer) Reset(dst io.Writer) { + if len(w.dict) > 0 { + // w was created with NewWriterDict + w.d.reset(dst) + if dst != nil { + w.d.fillWindow(w.dict) + } + } else { + // w was created with NewWriter + w.d.reset(dst) + } +} + +// ResetDict discards the writer's state and makes it equivalent to +// the result of NewWriter or NewWriterDict called with dst +// and w's level, but sets a specific dictionary. +func (w *Writer) ResetDict(dst io.Writer, dict []byte) { + w.dict = dict + w.d.reset(dst) + w.d.fillWindow(w.dict) +} diff --git a/vendor/github.com/klauspost/compress/flate/dict_decoder.go b/vendor/github.com/klauspost/compress/flate/dict_decoder.go new file mode 100644 index 000000000..cb855abc4 --- /dev/null +++ b/vendor/github.com/klauspost/compress/flate/dict_decoder.go @@ -0,0 +1,181 @@ +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package flate + +// dictDecoder implements the LZ77 sliding dictionary as used in decompression. +// LZ77 decompresses data through sequences of two forms of commands: +// +// - Literal insertions: Runs of one or more symbols are inserted into the data +// stream as is. This is accomplished through the writeByte method for a +// single symbol, or combinations of writeSlice/writeMark for multiple symbols. +// Any valid stream must start with a literal insertion if no preset dictionary +// is used. +// +// - Backward copies: Runs of one or more symbols are copied from previously +// emitted data. Backward copies come as the tuple (dist, length) where dist +// determines how far back in the stream to copy from and length determines how +// many bytes to copy. Note that it is valid for the length to be greater than +// the distance. Since LZ77 uses forward copies, that situation is used to +// perform a form of run-length encoding on repeated runs of symbols. +// The writeCopy and tryWriteCopy are used to implement this command. +// +// For performance reasons, this implementation performs little to no sanity +// checks about the arguments. As such, the invariants documented for each +// method call must be respected. +type dictDecoder struct { + hist []byte // Sliding window history + + // Invariant: 0 <= rdPos <= wrPos <= len(hist) + wrPos int // Current output position in buffer + rdPos int // Have emitted hist[:rdPos] already + full bool // Has a full window length been written yet? +} + +// init initializes dictDecoder to have a sliding window dictionary of the given +// size. If a preset dict is provided, it will initialize the dictionary with +// the contents of dict. +func (dd *dictDecoder) init(size int, dict []byte) { + *dd = dictDecoder{hist: dd.hist} + + if cap(dd.hist) < size { + dd.hist = make([]byte, size) + } + dd.hist = dd.hist[:size] + + if len(dict) > len(dd.hist) { + dict = dict[len(dict)-len(dd.hist):] + } + dd.wrPos = copy(dd.hist, dict) + if dd.wrPos == len(dd.hist) { + dd.wrPos = 0 + dd.full = true + } + dd.rdPos = dd.wrPos +} + +// histSize reports the total amount of historical data in the dictionary. +func (dd *dictDecoder) histSize() int { + if dd.full { + return len(dd.hist) + } + return dd.wrPos +} + +// availRead reports the number of bytes that can be flushed by readFlush. +func (dd *dictDecoder) availRead() int { + return dd.wrPos - dd.rdPos +} + +// availWrite reports the available amount of output buffer space. +func (dd *dictDecoder) availWrite() int { + return len(dd.hist) - dd.wrPos +} + +// writeSlice returns a slice of the available buffer to write data to. +// +// This invariant will be kept: len(s) <= availWrite() +func (dd *dictDecoder) writeSlice() []byte { + return dd.hist[dd.wrPos:] +} + +// writeMark advances the writer pointer by cnt. +// +// This invariant must be kept: 0 <= cnt <= availWrite() +func (dd *dictDecoder) writeMark(cnt int) { + dd.wrPos += cnt +} + +// writeByte writes a single byte to the dictionary. +// +// This invariant must be kept: 0 < availWrite() +func (dd *dictDecoder) writeByte(c byte) { + dd.hist[dd.wrPos] = c + dd.wrPos++ +} + +// writeCopy copies a string at a given (dist, length) to the output. +// This returns the number of bytes copied and may be less than the requested +// length if the available space in the output buffer is too small. +// +// This invariant must be kept: 0 < dist <= histSize() +func (dd *dictDecoder) writeCopy(dist, length int) int { + dstBase := dd.wrPos + dstPos := dstBase + srcPos := dstPos - dist + endPos := min(dstPos+length, len(dd.hist)) + + // Copy non-overlapping section after destination position. + // + // This section is non-overlapping in that the copy length for this section + // is always less than or equal to the backwards distance. This can occur + // if a distance refers to data that wraps-around in the buffer. + // Thus, a backwards copy is performed here; that is, the exact bytes in + // the source prior to the copy is placed in the destination. + if srcPos < 0 { + srcPos += len(dd.hist) + dstPos += copy(dd.hist[dstPos:endPos], dd.hist[srcPos:]) + srcPos = 0 + } + + // Copy possibly overlapping section before destination position. + // + // This section can overlap if the copy length for this section is larger + // than the backwards distance. This is allowed by LZ77 so that repeated + // strings can be succinctly represented using (dist, length) pairs. + // Thus, a forwards copy is performed here; that is, the bytes copied is + // possibly dependent on the resulting bytes in the destination as the copy + // progresses along. This is functionally equivalent to the following: + // + // for i := 0; i < endPos-dstPos; i++ { + // dd.hist[dstPos+i] = dd.hist[srcPos+i] + // } + // dstPos = endPos + // + for dstPos < endPos { + dstPos += copy(dd.hist[dstPos:endPos], dd.hist[srcPos:dstPos]) + } + + dd.wrPos = dstPos + return dstPos - dstBase +} + +// tryWriteCopy tries to copy a string at a given (distance, length) to the +// output. This specialized version is optimized for short distances. +// +// This method is designed to be inlined for performance reasons. +// +// This invariant must be kept: 0 < dist <= histSize() +func (dd *dictDecoder) tryWriteCopy(dist, length int) int { + dstPos := dd.wrPos + endPos := dstPos + length + if dstPos < dist || endPos > len(dd.hist) { + return 0 + } + dstBase := dstPos + srcPos := dstPos - dist + + // Copy possibly overlapping section before destination position. +loop: + dstPos += copy(dd.hist[dstPos:endPos], dd.hist[srcPos:dstPos]) + if dstPos < endPos { + goto loop // Avoid for-loop so that this function can be inlined + } + + dd.wrPos = dstPos + return dstPos - dstBase +} + +// readFlush returns a slice of the historical buffer that is ready to be +// emitted to the user. The data returned by readFlush must be fully consumed +// before calling any other dictDecoder methods. +func (dd *dictDecoder) readFlush() []byte { + toRead := dd.hist[dd.rdPos:dd.wrPos] + dd.rdPos = dd.wrPos + if dd.wrPos == len(dd.hist) { + dd.wrPos, dd.rdPos = 0, 0 + dd.full = true + } + return toRead +} diff --git a/vendor/github.com/klauspost/compress/flate/fast_encoder.go b/vendor/github.com/klauspost/compress/flate/fast_encoder.go new file mode 100644 index 000000000..791c9dcbf --- /dev/null +++ b/vendor/github.com/klauspost/compress/flate/fast_encoder.go @@ -0,0 +1,189 @@ +// Copyright 2011 The Snappy-Go Authors. All rights reserved. +// Modified for deflate by Klaus Post (c) 2015. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package flate + +import ( + "fmt" + + "github.com/klauspost/compress/internal/le" +) + +type fastEnc interface { + Encode(dst *tokens, src []byte) + Reset() +} + +func newFastEnc(level int) fastEnc { + switch level { + case 1: + return &fastEncL1{fastGen: fastGen{cur: maxStoreBlockSize}} + case 2: + return &fastEncL2{fastGen: fastGen{cur: maxStoreBlockSize}} + case 3: + return &fastEncL3{fastGen: fastGen{cur: maxStoreBlockSize}} + case 4: + return &fastEncL4{fastGen: fastGen{cur: maxStoreBlockSize}} + case 5: + return &fastEncL5{fastGen: fastGen{cur: maxStoreBlockSize}} + case 6: + return &fastEncL6{fastGen: fastGen{cur: maxStoreBlockSize}} + default: + panic("invalid level specified") + } +} + +const ( + tableBits = 15 // Bits used in the table + tableSize = 1 << tableBits // Size of the table + tableShift = 32 - tableBits // Right-shift to get the tableBits most significant bits of a uint32. + baseMatchOffset = 1 // The smallest match offset + baseMatchLength = 3 // The smallest match length per the RFC section 3.2.5 + maxMatchOffset = 1 << 15 // The largest match offset + + bTableBits = 17 // Bits used in the big tables + bTableSize = 1 << bTableBits // Size of the table + allocHistory = maxStoreBlockSize * 5 // Size to preallocate for history. + bufferReset = (1 << 31) - allocHistory - maxStoreBlockSize - 1 // Reset the buffer offset when reaching this. +) + +const ( + prime3bytes = 506832829 + prime4bytes = 2654435761 + prime5bytes = 889523592379 + prime6bytes = 227718039650203 + prime7bytes = 58295818150454627 + prime8bytes = 0xcf1bbcdcb7a56463 +) + +func load3232(b []byte, i int32) uint32 { + return le.Load32(b, i) +} + +func load6432(b []byte, i int32) uint64 { + return le.Load64(b, i) +} + +type tableEntry struct { + offset int32 +} + +// fastGen maintains the table for matches, +// and the previous byte block for level 2. +// This is the generic implementation. +type fastGen struct { + hist []byte + cur int32 +} + +func (e *fastGen) addBlock(src []byte) int32 { + // check if we have space already + if len(e.hist)+len(src) > cap(e.hist) { + if cap(e.hist) == 0 { + e.hist = make([]byte, 0, allocHistory) + } else { + if cap(e.hist) < maxMatchOffset*2 { + panic("unexpected buffer size") + } + // Move down + offset := int32(len(e.hist)) - maxMatchOffset + // copy(e.hist[0:maxMatchOffset], e.hist[offset:]) + *(*[maxMatchOffset]byte)(e.hist) = *(*[maxMatchOffset]byte)(e.hist[offset:]) + e.cur += offset + e.hist = e.hist[:maxMatchOffset] + } + } + s := int32(len(e.hist)) + e.hist = append(e.hist, src...) + return s +} + +type tableEntryPrev struct { + Cur tableEntry + Prev tableEntry +} + +// hash7 returns the hash of the lowest 7 bytes of u to fit in a hash table with h bits. +// Preferably h should be a constant and should always be <64. +func hash7(u uint64, h uint8) uint32 { + return uint32(((u << (64 - 56)) * prime7bytes) >> ((64 - h) & reg8SizeMask64)) +} + +// hashLen returns a hash of the lowest mls bytes of with length output bits. +// mls must be >=3 and <=8. Any other value will return hash for 4 bytes. +// length should always be < 32. +// Preferably length and mls should be a constant for inlining. +func hashLen(u uint64, length, mls uint8) uint32 { + switch mls { + case 3: + return (uint32(u<<8) * prime3bytes) >> (32 - length) + case 5: + return uint32(((u << (64 - 40)) * prime5bytes) >> (64 - length)) + case 6: + return uint32(((u << (64 - 48)) * prime6bytes) >> (64 - length)) + case 7: + return uint32(((u << (64 - 56)) * prime7bytes) >> (64 - length)) + case 8: + return uint32((u * prime8bytes) >> (64 - length)) + default: + return (uint32(u) * prime4bytes) >> (32 - length) + } +} + +// matchlen will return the match length between offsets and t in src. +// The maximum length returned is maxMatchLength - 4. +// It is assumed that s > t, that t >=0 and s < len(src). +func (e *fastGen) matchlen(s, t int, src []byte) int32 { + if debugDeflate { + if t >= s { + panic(fmt.Sprint("t >=s:", t, s)) + } + if int(s) >= len(src) { + panic(fmt.Sprint("s >= len(src):", s, len(src))) + } + if t < 0 { + panic(fmt.Sprint("t < 0:", t)) + } + if s-t > maxMatchOffset { + panic(fmt.Sprint(s, "-", t, "(", s-t, ") > maxMatchLength (", maxMatchOffset, ")")) + } + } + a := src[s:min(s+maxMatchLength-4, len(src))] + b := src[t:] + return int32(matchLen(a, b)) +} + +// matchlenLong will return the match length between offsets and t in src. +// It is assumed that s > t, that t >=0 and s < len(src). +func (e *fastGen) matchlenLong(s, t int, src []byte) int32 { + if debugDeflate { + if t >= s { + panic(fmt.Sprint("t >=s:", t, s)) + } + if int(s) >= len(src) { + panic(fmt.Sprint("s >= len(src):", s, len(src))) + } + if t < 0 { + panic(fmt.Sprint("t < 0:", t)) + } + if s-t > maxMatchOffset { + panic(fmt.Sprint(s, "-", t, "(", s-t, ") > maxMatchLength (", maxMatchOffset, ")")) + } + } + return int32(matchLen(src[s:], src[t:])) +} + +// Reset the encoding table. +func (e *fastGen) Reset() { + if cap(e.hist) < allocHistory { + e.hist = make([]byte, 0, allocHistory) + } + // We offset current position so everything will be out of reach. + // If we are above the buffer reset it will be cleared anyway since len(hist) == 0. + if e.cur <= bufferReset { + e.cur += maxMatchOffset + int32(len(e.hist)) + } + e.hist = e.hist[:0] +} diff --git a/vendor/github.com/klauspost/compress/flate/huffman_bit_writer.go b/vendor/github.com/klauspost/compress/flate/huffman_bit_writer.go new file mode 100644 index 000000000..7151140cc --- /dev/null +++ b/vendor/github.com/klauspost/compress/flate/huffman_bit_writer.go @@ -0,0 +1,1174 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package flate + +import ( + "fmt" + "io" + "math" + + "github.com/klauspost/compress/internal/le" +) + +const ( + // The largest offset code. + offsetCodeCount = 30 + + // The special code used to mark the end of a block. + endBlockMarker = 256 + + // The first length code. + lengthCodesStart = 257 + + // The number of codegen codes. + codegenCodeCount = 19 + badCode = 255 + + // maxPredefinedTokens is the maximum number of tokens + // where we check if fixed size is smaller. + maxPredefinedTokens = 250 + + // bufferFlushSize indicates the buffer size + // after which bytes are flushed to the writer. + // Should preferably be a multiple of 6, since + // we accumulate 6 bytes between writes to the buffer. + bufferFlushSize = 246 +) + +// Minimum length code that emits bits. +const lengthExtraBitsMinCode = 8 + +// The number of extra bits needed by length code X - LENGTH_CODES_START. +var lengthExtraBits = [32]uint8{ + /* 257 */ 0, 0, 0, + /* 260 */ 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, + /* 270 */ 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, + /* 280 */ 4, 5, 5, 5, 5, 0, +} + +// The length indicated by length code X - LENGTH_CODES_START. +var lengthBase = [32]uint8{ + 0, 1, 2, 3, 4, 5, 6, 7, 8, 10, + 12, 14, 16, 20, 24, 28, 32, 40, 48, 56, + 64, 80, 96, 112, 128, 160, 192, 224, 255, +} + +// Minimum offset code that emits bits. +const offsetExtraBitsMinCode = 4 + +// offset code word extra bits. +var offsetExtraBits = [32]int8{ + 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, + 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, + 9, 9, 10, 10, 11, 11, 12, 12, 13, 13, + /* extended window */ + 14, 14, +} + +var offsetCombined = [32]uint32{} + +func init() { + var offsetBase = [32]uint32{ + /* normal deflate */ + 0x000000, 0x000001, 0x000002, 0x000003, 0x000004, + 0x000006, 0x000008, 0x00000c, 0x000010, 0x000018, + 0x000020, 0x000030, 0x000040, 0x000060, 0x000080, + 0x0000c0, 0x000100, 0x000180, 0x000200, 0x000300, + 0x000400, 0x000600, 0x000800, 0x000c00, 0x001000, + 0x001800, 0x002000, 0x003000, 0x004000, 0x006000, + + /* extended window */ + 0x008000, 0x00c000, + } + + for i := range offsetCombined[:] { + // Don't use extended window values... + if offsetExtraBits[i] == 0 || offsetBase[i] > 0x006000 { + continue + } + offsetCombined[i] = uint32(offsetExtraBits[i]) | (offsetBase[i] << 8) + } +} + +// The odd order in which the codegen code sizes are written. +var codegenOrder = []uint32{16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15} + +type huffmanBitWriter struct { + // writer is the underlying writer. + // Do not use it directly; use the write method, which ensures + // that Write errors are sticky. + writer io.Writer + + // Data waiting to be written is bytes[0:nbytes] + // and then the low nbits of bits. + bits uint64 + nbits uint8 + nbytes uint8 + lastHuffMan bool + literalEncoding *huffmanEncoder + tmpLitEncoding *huffmanEncoder + offsetEncoding *huffmanEncoder + codegenEncoding *huffmanEncoder + err error + lastHeader int + // Set between 0 (reused block can be up to 2x the size) + logNewTablePenalty uint + bytes [256 + 8]byte + literalFreq [lengthCodesStart + 32]uint16 + offsetFreq [32]uint16 + codegenFreq [codegenCodeCount]uint16 + + // codegen must have an extra space for the final symbol. + codegen [literalCount + offsetCodeCount + 1]uint8 +} + +// Huffman reuse. +// +// The huffmanBitWriter supports reusing huffman tables and thereby combining block sections. +// +// This is controlled by several variables: +// +// If lastHeader is non-zero the Huffman table can be reused. +// This also indicates that a Huffman table has been generated that can output all +// possible symbols. +// It also indicates that an EOB has not yet been emitted, so if a new tabel is generated +// an EOB with the previous table must be written. +// +// If lastHuffMan is set, a table for outputting literals has been generated and offsets are invalid. +// +// An incoming block estimates the output size of a new table using a 'fresh' by calculating the +// optimal size and adding a penalty in 'logNewTablePenalty'. +// A Huffman table is not optimal, which is why we add a penalty, and generating a new table +// is slower both for compression and decompression. + +func newHuffmanBitWriter(w io.Writer) *huffmanBitWriter { + return &huffmanBitWriter{ + writer: w, + literalEncoding: newHuffmanEncoder(literalCount), + tmpLitEncoding: newHuffmanEncoder(literalCount), + codegenEncoding: newHuffmanEncoder(codegenCodeCount), + offsetEncoding: newHuffmanEncoder(offsetCodeCount), + } +} + +func (w *huffmanBitWriter) reset(writer io.Writer) { + w.writer = writer + w.bits, w.nbits, w.nbytes, w.err = 0, 0, 0, nil + w.lastHeader = 0 + w.lastHuffMan = false +} + +func (w *huffmanBitWriter) canReuse(t *tokens) (ok bool) { + a := t.offHist[:offsetCodeCount] + b := w.offsetEncoding.codes + b = b[:len(a)] + for i, v := range a { + if v != 0 && b[i].zero() { + return false + } + } + + a = t.extraHist[:literalCount-256] + b = w.literalEncoding.codes[256:literalCount] + b = b[:len(a)] + for i, v := range a { + if v != 0 && b[i].zero() { + return false + } + } + + a = t.litHist[:256] + b = w.literalEncoding.codes[:len(a)] + for i, v := range a { + if v != 0 && b[i].zero() { + return false + } + } + return true +} + +func (w *huffmanBitWriter) flush() { + if w.err != nil { + w.nbits = 0 + return + } + if w.lastHeader > 0 { + // We owe an EOB + w.writeCode(w.literalEncoding.codes[endBlockMarker]) + w.lastHeader = 0 + } + n := w.nbytes + for w.nbits != 0 { + w.bytes[n] = byte(w.bits) + w.bits >>= 8 + if w.nbits > 8 { // Avoid underflow + w.nbits -= 8 + } else { + w.nbits = 0 + } + n++ + } + w.bits = 0 + if n > 0 { + w.write(w.bytes[:n]) + } + w.nbytes = 0 +} + +func (w *huffmanBitWriter) write(b []byte) { + if w.err != nil { + return + } + _, w.err = w.writer.Write(b) +} + +func (w *huffmanBitWriter) writeBits(b int32, nb uint8) { + w.bits |= uint64(b) << (w.nbits & 63) + w.nbits += nb + if w.nbits >= 48 { + w.writeOutBits() + } +} + +func (w *huffmanBitWriter) writeBytes(bytes []byte) { + if w.err != nil { + return + } + n := w.nbytes + if w.nbits&7 != 0 { + w.err = InternalError("writeBytes with unfinished bits") + return + } + for w.nbits != 0 { + w.bytes[n] = byte(w.bits) + w.bits >>= 8 + w.nbits -= 8 + n++ + } + if n != 0 { + w.write(w.bytes[:n]) + } + w.nbytes = 0 + w.write(bytes) +} + +// RFC 1951 3.2.7 specifies a special run-length encoding for specifying +// the literal and offset lengths arrays (which are concatenated into a single +// array). This method generates that run-length encoding. +// +// The result is written into the codegen array, and the frequencies +// of each code is written into the codegenFreq array. +// Codes 0-15 are single byte codes. Codes 16-18 are followed by additional +// information. Code badCode is an end marker +// +// numLiterals The number of literals in literalEncoding +// numOffsets The number of offsets in offsetEncoding +// litenc, offenc The literal and offset encoder to use +func (w *huffmanBitWriter) generateCodegen(numLiterals int, numOffsets int, litEnc, offEnc *huffmanEncoder) { + for i := range w.codegenFreq { + w.codegenFreq[i] = 0 + } + // Note that we are using codegen both as a temporary variable for holding + // a copy of the frequencies, and as the place where we put the result. + // This is fine because the output is always shorter than the input used + // so far. + codegen := w.codegen[:] // cache + // Copy the concatenated code sizes to codegen. Put a marker at the end. + cgnl := codegen[:numLiterals] + for i := range cgnl { + cgnl[i] = litEnc.codes[i].len() + } + + cgnl = codegen[numLiterals : numLiterals+numOffsets] + for i := range cgnl { + cgnl[i] = offEnc.codes[i].len() + } + codegen[numLiterals+numOffsets] = badCode + + size := codegen[0] + count := 1 + outIndex := 0 + for inIndex := 1; size != badCode; inIndex++ { + // INVARIANT: We have seen "count" copies of size that have not yet + // had output generated for them. + nextSize := codegen[inIndex] + if nextSize == size { + count++ + continue + } + // We need to generate codegen indicating "count" of size. + if size != 0 { + codegen[outIndex] = size + outIndex++ + w.codegenFreq[size]++ + count-- + for count >= 3 { + n := min(6, count) + codegen[outIndex] = 16 + outIndex++ + codegen[outIndex] = uint8(n - 3) + outIndex++ + w.codegenFreq[16]++ + count -= n + } + } else { + for count >= 11 { + n := min(138, count) + codegen[outIndex] = 18 + outIndex++ + codegen[outIndex] = uint8(n - 11) + outIndex++ + w.codegenFreq[18]++ + count -= n + } + if count >= 3 { + // count >= 3 && count <= 10 + codegen[outIndex] = 17 + outIndex++ + codegen[outIndex] = uint8(count - 3) + outIndex++ + w.codegenFreq[17]++ + count = 0 + } + } + count-- + for ; count >= 0; count-- { + codegen[outIndex] = size + outIndex++ + w.codegenFreq[size]++ + } + // Set up invariant for next time through the loop. + size = nextSize + count = 1 + } + // Marker indicating the end of the codegen. + codegen[outIndex] = badCode +} + +func (w *huffmanBitWriter) codegens() int { + numCodegens := len(w.codegenFreq) + for numCodegens > 4 && w.codegenFreq[codegenOrder[numCodegens-1]] == 0 { + numCodegens-- + } + return numCodegens +} + +func (w *huffmanBitWriter) headerSize() (size, numCodegens int) { + numCodegens = len(w.codegenFreq) + for numCodegens > 4 && w.codegenFreq[codegenOrder[numCodegens-1]] == 0 { + numCodegens-- + } + return 3 + 5 + 5 + 4 + (3 * numCodegens) + + w.codegenEncoding.bitLength(w.codegenFreq[:]) + + int(w.codegenFreq[16])*2 + + int(w.codegenFreq[17])*3 + + int(w.codegenFreq[18])*7, numCodegens +} + +// dynamicSize returns the size of dynamically encoded data in bits. +func (w *huffmanBitWriter) dynamicReuseSize(litEnc, offEnc *huffmanEncoder) (size int) { + size = litEnc.bitLength(w.literalFreq[:]) + + offEnc.bitLength(w.offsetFreq[:]) + return size +} + +// dynamicSize returns the size of dynamically encoded data in bits. +func (w *huffmanBitWriter) dynamicSize(litEnc, offEnc *huffmanEncoder, extraBits int) (size, numCodegens int) { + header, numCodegens := w.headerSize() + size = header + + litEnc.bitLength(w.literalFreq[:]) + + offEnc.bitLength(w.offsetFreq[:]) + + extraBits + return size, numCodegens +} + +// extraBitSize will return the number of bits that will be written +// as "extra" bits on matches. +func (w *huffmanBitWriter) extraBitSize() int { + total := 0 + for i, n := range w.literalFreq[257:literalCount] { + total += int(n) * int(lengthExtraBits[i&31]) + } + for i, n := range w.offsetFreq[:offsetCodeCount] { + total += int(n) * int(offsetExtraBits[i&31]) + } + return total +} + +// fixedSize returns the size of dynamically encoded data in bits. +func (w *huffmanBitWriter) fixedSize(extraBits int) int { + return 3 + + fixedLiteralEncoding.bitLength(w.literalFreq[:]) + + fixedOffsetEncoding.bitLength(w.offsetFreq[:]) + + extraBits +} + +// storedSize calculates the stored size, including header. +// The function returns the size in bits and whether the block +// fits inside a single block. +func (w *huffmanBitWriter) storedSize(in []byte) (int, bool) { + if in == nil { + return 0, false + } + if len(in) <= maxStoreBlockSize { + return (len(in) + 5) * 8, true + } + return 0, false +} + +func (w *huffmanBitWriter) writeCode(c hcode) { + // The function does not get inlined if we "& 63" the shift. + w.bits |= c.code64() << (w.nbits & 63) + w.nbits += c.len() + if w.nbits >= 48 { + w.writeOutBits() + } +} + +// writeOutBits will write bits to the buffer. +func (w *huffmanBitWriter) writeOutBits() { + bits := w.bits + w.bits >>= 48 + w.nbits -= 48 + n := w.nbytes + + // We overwrite, but faster... + le.Store64(w.bytes[:], n, bits) + n += 6 + + if n >= bufferFlushSize { + if w.err != nil { + n = 0 + return + } + w.write(w.bytes[:n]) + n = 0 + } + + w.nbytes = n +} + +// Write the header of a dynamic Huffman block to the output stream. +// +// numLiterals The number of literals specified in codegen +// numOffsets The number of offsets specified in codegen +// numCodegens The number of codegens used in codegen +func (w *huffmanBitWriter) writeDynamicHeader(numLiterals int, numOffsets int, numCodegens int, isEof bool) { + if w.err != nil { + return + } + var firstBits int32 = 4 + if isEof { + firstBits = 5 + } + w.writeBits(firstBits, 3) + w.writeBits(int32(numLiterals-257), 5) + w.writeBits(int32(numOffsets-1), 5) + w.writeBits(int32(numCodegens-4), 4) + + for i := range numCodegens { + value := uint(w.codegenEncoding.codes[codegenOrder[i]].len()) + w.writeBits(int32(value), 3) + } + + i := 0 + for { + var codeWord = uint32(w.codegen[i]) + i++ + if codeWord == badCode { + break + } + w.writeCode(w.codegenEncoding.codes[codeWord]) + + switch codeWord { + case 16: + w.writeBits(int32(w.codegen[i]), 2) + i++ + case 17: + w.writeBits(int32(w.codegen[i]), 3) + i++ + case 18: + w.writeBits(int32(w.codegen[i]), 7) + i++ + } + } +} + +// writeStoredHeader will write a stored header. +// If the stored block is only used for EOF, +// it is replaced with a fixed huffman block. +func (w *huffmanBitWriter) writeStoredHeader(length int, isEof bool) { + if w.err != nil { + return + } + if w.lastHeader > 0 { + // We owe an EOB + w.writeCode(w.literalEncoding.codes[endBlockMarker]) + w.lastHeader = 0 + } + + // To write EOF, use a fixed encoding block. 10 bits instead of 5 bytes. + if length == 0 && isEof { + w.writeFixedHeader(isEof) + // EOB: 7 bits, value: 0 + w.writeBits(0, 7) + w.flush() + return + } + + var flag int32 + if isEof { + flag = 1 + } + w.writeBits(flag, 3) + w.flush() + w.writeBits(int32(length), 16) + w.writeBits(int32(^uint16(length)), 16) +} + +func (w *huffmanBitWriter) writeFixedHeader(isEof bool) { + if w.err != nil { + return + } + if w.lastHeader > 0 { + // We owe an EOB + w.writeCode(w.literalEncoding.codes[endBlockMarker]) + w.lastHeader = 0 + } + + // Indicate that we are a fixed Huffman block + var value int32 = 2 + if isEof { + value = 3 + } + w.writeBits(value, 3) +} + +// writeBlock will write a block of tokens with the smallest encoding. +// The original input can be supplied, and if the huffman encoded data +// is larger than the original bytes, the data will be written as a +// stored block. +// If the input is nil, the tokens will always be Huffman encoded. +func (w *huffmanBitWriter) writeBlock(tokens *tokens, eof bool, input []byte) { + if w.err != nil { + return + } + + tokens.AddEOB() + if w.lastHeader > 0 { + // We owe an EOB + w.writeCode(w.literalEncoding.codes[endBlockMarker]) + w.lastHeader = 0 + } + numLiterals, numOffsets := w.indexTokens(tokens, false) + w.generate() + var extraBits int + storedSize, storable := w.storedSize(input) + if storable { + extraBits = w.extraBitSize() + } + + // Figure out smallest code. + // Fixed Huffman baseline. + var literalEncoding = fixedLiteralEncoding + var offsetEncoding = fixedOffsetEncoding + var size = math.MaxInt32 + if tokens.n < maxPredefinedTokens { + size = w.fixedSize(extraBits) + } + + // Dynamic Huffman? + var numCodegens int + + // Generate codegen and codegenFrequencies, which indicates how to encode + // the literalEncoding and the offsetEncoding. + w.generateCodegen(numLiterals, numOffsets, w.literalEncoding, w.offsetEncoding) + w.codegenEncoding.generate(w.codegenFreq[:], 7) + dynamicSize, numCodegens := w.dynamicSize(w.literalEncoding, w.offsetEncoding, extraBits) + + if dynamicSize < size { + size = dynamicSize + literalEncoding = w.literalEncoding + offsetEncoding = w.offsetEncoding + } + + // Stored bytes? + if storable && storedSize <= size { + w.writeStoredHeader(len(input), eof) + w.writeBytes(input) + return + } + + // Huffman. + if literalEncoding == fixedLiteralEncoding { + w.writeFixedHeader(eof) + } else { + w.writeDynamicHeader(numLiterals, numOffsets, numCodegens, eof) + } + + // Write the tokens. + w.writeTokens(tokens.Slice(), literalEncoding.codes, offsetEncoding.codes) +} + +// writeBlockDynamic encodes a block using a dynamic Huffman table. +// This should be used if the symbols used have a disproportionate +// histogram distribution. +// If input is supplied and the compression savings are below 1/16th of the +// input size the block is stored. +func (w *huffmanBitWriter) writeBlockDynamic(tokens *tokens, eof bool, input []byte, sync bool) { + if w.err != nil { + return + } + + sync = sync || eof + if sync { + tokens.AddEOB() + } + + // We cannot reuse pure huffman table, and must mark as EOF. + if (w.lastHuffMan || eof) && w.lastHeader > 0 { + // We will not try to reuse. + w.writeCode(w.literalEncoding.codes[endBlockMarker]) + w.lastHeader = 0 + w.lastHuffMan = false + } + + // fillReuse enables filling of empty values. + // This will make encodings always reusable without testing. + // However, this does not appear to benefit on most cases. + const fillReuse = false + + // Check if we can reuse... + if !fillReuse && w.lastHeader > 0 && !w.canReuse(tokens) { + w.writeCode(w.literalEncoding.codes[endBlockMarker]) + w.lastHeader = 0 + } + + numLiterals, numOffsets := w.indexTokens(tokens, true) + extraBits := 0 + ssize, storable := w.storedSize(input) + + const usePrefs = true + if storable || w.lastHeader > 0 { + extraBits = w.extraBitSize() + } + + var size int + + // Check if we should reuse. + if w.lastHeader > 0 { + // Estimate size for using a new table. + // Use the previous header size as the best estimate. + newSize := w.lastHeader + tokens.EstimatedBits() + newSize += int(w.literalEncoding.codes[endBlockMarker].len()) + newSize>>w.logNewTablePenalty + + // The estimated size is calculated as an optimal table. + // We add a penalty to make it more realistic and re-use a bit more. + reuseSize := w.dynamicReuseSize(w.literalEncoding, w.offsetEncoding) + extraBits + + // Check if a new table is better. + if newSize < reuseSize { + // Write the EOB we owe. + w.writeCode(w.literalEncoding.codes[endBlockMarker]) + size = newSize + w.lastHeader = 0 + } else { + size = reuseSize + } + + if tokens.n < maxPredefinedTokens { + if preSize := w.fixedSize(extraBits) + 7; usePrefs && preSize < size { + // Check if we get a reasonable size decrease. + if storable && ssize <= size { + w.writeStoredHeader(len(input), eof) + w.writeBytes(input) + return + } + w.writeFixedHeader(eof) + if !sync { + tokens.AddEOB() + } + w.writeTokens(tokens.Slice(), fixedLiteralEncoding.codes, fixedOffsetEncoding.codes) + return + } + } + // Check if we get a reasonable size decrease. + if storable && ssize <= size { + w.writeStoredHeader(len(input), eof) + w.writeBytes(input) + return + } + } + + // We want a new block/table + if w.lastHeader == 0 { + if fillReuse && !sync { + w.fillTokens() + numLiterals, numOffsets = maxNumLit, maxNumDist + } else { + w.literalFreq[endBlockMarker] = 1 + } + + w.generate() + // Generate codegen and codegenFrequencies, which indicates how to encode + // the literalEncoding and the offsetEncoding. + w.generateCodegen(numLiterals, numOffsets, w.literalEncoding, w.offsetEncoding) + w.codegenEncoding.generate(w.codegenFreq[:], 7) + + var numCodegens int + if fillReuse && !sync { + // Reindex for accurate size... + w.indexTokens(tokens, true) + } + size, numCodegens = w.dynamicSize(w.literalEncoding, w.offsetEncoding, extraBits) + + // Store predefined, if we don't get a reasonable improvement. + if tokens.n < maxPredefinedTokens { + if preSize := w.fixedSize(extraBits); usePrefs && preSize <= size { + // Store bytes, if we don't get an improvement. + if storable && ssize <= preSize { + w.writeStoredHeader(len(input), eof) + w.writeBytes(input) + return + } + w.writeFixedHeader(eof) + if !sync { + tokens.AddEOB() + } + w.writeTokens(tokens.Slice(), fixedLiteralEncoding.codes, fixedOffsetEncoding.codes) + return + } + } + + if storable && ssize <= size { + // Store bytes, if we don't get an improvement. + w.writeStoredHeader(len(input), eof) + w.writeBytes(input) + return + } + + // Write Huffman table. + w.writeDynamicHeader(numLiterals, numOffsets, numCodegens, eof) + if !sync { + w.lastHeader, _ = w.headerSize() + } + w.lastHuffMan = false + } + + if sync { + w.lastHeader = 0 + } + // Write the tokens. + w.writeTokens(tokens.Slice(), w.literalEncoding.codes, w.offsetEncoding.codes) +} + +func (w *huffmanBitWriter) fillTokens() { + for i, v := range w.literalFreq[:literalCount] { + if v == 0 { + w.literalFreq[i] = 1 + } + } + for i, v := range w.offsetFreq[:offsetCodeCount] { + if v == 0 { + w.offsetFreq[i] = 1 + } + } +} + +// indexTokens indexes a slice of tokens, and updates +// literalFreq and offsetFreq, and generates literalEncoding +// and offsetEncoding. +// The number of literal and offset tokens is returned. +func (w *huffmanBitWriter) indexTokens(t *tokens, alwaysEOB bool) (numLiterals, numOffsets int) { + //copy(w.literalFreq[:], t.litHist[:]) + *(*[256]uint16)(w.literalFreq[:]) = t.litHist + //copy(w.literalFreq[256:], t.extraHist[:]) + *(*[32]uint16)(w.literalFreq[256:]) = t.extraHist + w.offsetFreq = t.offHist + + if t.n == 0 { + return + } + if alwaysEOB { + w.literalFreq[endBlockMarker] = 1 + } + + // get the number of literals + numLiterals = len(w.literalFreq) + for w.literalFreq[numLiterals-1] == 0 { + numLiterals-- + } + // get the number of offsets + numOffsets = len(w.offsetFreq) + for numOffsets > 0 && w.offsetFreq[numOffsets-1] == 0 { + numOffsets-- + } + if numOffsets == 0 { + // We haven't found a single match. If we want to go with the dynamic encoding, + // we should count at least one offset to be sure that the offset huffman tree could be encoded. + w.offsetFreq[0] = 1 + numOffsets = 1 + } + return +} + +func (w *huffmanBitWriter) generate() { + w.literalEncoding.generate(w.literalFreq[:literalCount], 15) + w.offsetEncoding.generate(w.offsetFreq[:offsetCodeCount], 15) +} + +// writeTokens writes a slice of tokens to the output. +// codes for literal and offset encoding must be supplied. +func (w *huffmanBitWriter) writeTokens(tokens []token, leCodes, oeCodes []hcode) { + if w.err != nil { + return + } + if len(tokens) == 0 { + return + } + + // Only last token should be endBlockMarker. + var deferEOB bool + if tokens[len(tokens)-1] == endBlockMarker { + tokens = tokens[:len(tokens)-1] + deferEOB = true + } + + // Create slices up to the next power of two to avoid bounds checks. + lits := leCodes[:256] + offs := oeCodes[:32] + lengths := leCodes[lengthCodesStart:] + lengths = lengths[:32] + + // Go 1.16 LOVES having these on stack. + bits, nbits, nbytes := w.bits, w.nbits, w.nbytes + + for _, t := range tokens { + if t < 256 { + //w.writeCode(lits[t.literal()]) + c := lits[t] + bits |= c.code64() << (nbits & 63) + nbits += c.len() + if nbits >= 48 { + le.Store64(w.bytes[:], nbytes, bits) + bits >>= 48 + nbits -= 48 + nbytes += 6 + if nbytes >= bufferFlushSize { + if w.err != nil { + nbytes = 0 + return + } + _, w.err = w.writer.Write(w.bytes[:nbytes]) + nbytes = 0 + } + } + continue + } + + // Write the length + length := t.length() + lengthCode := lengthCode(length) & 31 + if false { + w.writeCode(lengths[lengthCode]) + } else { + // inlined + c := lengths[lengthCode] + bits |= c.code64() << (nbits & 63) + nbits += c.len() + if nbits >= 48 { + le.Store64(w.bytes[:], nbytes, bits) + bits >>= 48 + nbits -= 48 + nbytes += 6 + if nbytes >= bufferFlushSize { + if w.err != nil { + nbytes = 0 + return + } + _, w.err = w.writer.Write(w.bytes[:nbytes]) + nbytes = 0 + } + } + } + + if lengthCode >= lengthExtraBitsMinCode { + extraLengthBits := lengthExtraBits[lengthCode] + //w.writeBits(extraLength, extraLengthBits) + extraLength := int32(length - lengthBase[lengthCode]) + bits |= uint64(extraLength) << (nbits & 63) + nbits += extraLengthBits + if nbits >= 48 { + le.Store64(w.bytes[:], nbytes, bits) + bits >>= 48 + nbits -= 48 + nbytes += 6 + if nbytes >= bufferFlushSize { + if w.err != nil { + nbytes = 0 + return + } + _, w.err = w.writer.Write(w.bytes[:nbytes]) + nbytes = 0 + } + } + } + // Write the offset + offset := t.offset() + offsetCode := (offset >> 16) & 31 + if false { + w.writeCode(offs[offsetCode]) + } else { + // inlined + c := offs[offsetCode] + bits |= c.code64() << (nbits & 63) + nbits += c.len() + if nbits >= 48 { + le.Store64(w.bytes[:], nbytes, bits) + bits >>= 48 + nbits -= 48 + nbytes += 6 + if nbytes >= bufferFlushSize { + if w.err != nil { + nbytes = 0 + return + } + _, w.err = w.writer.Write(w.bytes[:nbytes]) + nbytes = 0 + } + } + } + + if offsetCode >= offsetExtraBitsMinCode { + offsetComb := offsetCombined[offsetCode] + //w.writeBits(extraOffset, extraOffsetBits) + bits |= uint64((offset-(offsetComb>>8))&matchOffsetOnlyMask) << (nbits & 63) + nbits += uint8(offsetComb) + if nbits >= 48 { + le.Store64(w.bytes[:], nbytes, bits) + bits >>= 48 + nbits -= 48 + nbytes += 6 + if nbytes >= bufferFlushSize { + if w.err != nil { + nbytes = 0 + return + } + _, w.err = w.writer.Write(w.bytes[:nbytes]) + nbytes = 0 + } + } + } + } + // Restore... + w.bits, w.nbits, w.nbytes = bits, nbits, nbytes + + if deferEOB { + w.writeCode(leCodes[endBlockMarker]) + } +} + +// huffOffset is a static offset encoder used for huffman only encoding. +// It can be reused since we will not be encoding offset values. +var huffOffset *huffmanEncoder + +func init() { + w := newHuffmanBitWriter(nil) + w.offsetFreq[0] = 1 + huffOffset = newHuffmanEncoder(offsetCodeCount) + huffOffset.generate(w.offsetFreq[:offsetCodeCount], 15) +} + +// writeBlockHuff encodes a block of bytes as either +// Huffman encoded literals or uncompressed bytes if the +// results only gains very little from compression. +func (w *huffmanBitWriter) writeBlockHuff(eof bool, input []byte, sync bool) { + if w.err != nil { + return + } + + // Clear histogram + for i := range w.literalFreq[:] { + w.literalFreq[i] = 0 + } + if !w.lastHuffMan { + for i := range w.offsetFreq[:] { + w.offsetFreq[i] = 0 + } + } + + const numLiterals = endBlockMarker + 1 + const numOffsets = 1 + + // Add everything as literals + // We have to estimate the header size. + // Assume header is around 70 bytes: + // https://stackoverflow.com/a/25454430 + const guessHeaderSizeBits = 70 * 8 + histogram(input, w.literalFreq[:numLiterals]) + ssize, storable := w.storedSize(input) + if storable && len(input) > 1024 { + // Quick check for incompressible content. + abs := float64(0) + avg := float64(len(input)) / 256 + max := float64(len(input) * 2) + for _, v := range w.literalFreq[:256] { + diff := float64(v) - avg + abs += diff * diff + if abs > max { + break + } + } + if abs < max { + if debugDeflate { + fmt.Println("stored", abs, "<", max) + } + // No chance we can compress this... + w.writeStoredHeader(len(input), eof) + w.writeBytes(input) + return + } + } + w.literalFreq[endBlockMarker] = 1 + w.tmpLitEncoding.generate(w.literalFreq[:numLiterals], 15) + estBits := w.tmpLitEncoding.canReuseBits(w.literalFreq[:numLiterals]) + if estBits < math.MaxInt32 { + estBits += w.lastHeader + if w.lastHeader == 0 { + estBits += guessHeaderSizeBits + } + estBits += estBits >> w.logNewTablePenalty + } + + // Store bytes, if we don't get a reasonable improvement. + if storable && ssize <= estBits { + if debugDeflate { + fmt.Println("stored,", ssize, "<=", estBits) + } + w.writeStoredHeader(len(input), eof) + w.writeBytes(input) + return + } + + if w.lastHeader > 0 { + reuseSize := w.literalEncoding.canReuseBits(w.literalFreq[:256]) + + if estBits < reuseSize { + if debugDeflate { + fmt.Println("NOT reusing, reuse:", reuseSize/8, "> new:", estBits/8, "header est:", w.lastHeader/8, "bytes") + } + // We owe an EOB + w.writeCode(w.literalEncoding.codes[endBlockMarker]) + w.lastHeader = 0 + } else if debugDeflate { + fmt.Println("reusing, reuse:", reuseSize/8, "> new:", estBits/8, "- header est:", w.lastHeader/8) + } + } + + count := 0 + if w.lastHeader == 0 { + // Use the temp encoding, so swap. + w.literalEncoding, w.tmpLitEncoding = w.tmpLitEncoding, w.literalEncoding + // Generate codegen and codegenFrequencies, which indicates how to encode + // the literalEncoding and the offsetEncoding. + w.generateCodegen(numLiterals, numOffsets, w.literalEncoding, huffOffset) + w.codegenEncoding.generate(w.codegenFreq[:], 7) + numCodegens := w.codegens() + + // Huffman. + w.writeDynamicHeader(numLiterals, numOffsets, numCodegens, eof) + w.lastHuffMan = true + w.lastHeader, _ = w.headerSize() + if debugDeflate { + count += w.lastHeader + fmt.Println("header:", count/8) + } + } + + encoding := w.literalEncoding.codes[:256] + // Go 1.16 LOVES having these on stack. At least 1.5x the speed. + bits, nbits, nbytes := w.bits, w.nbits, w.nbytes + + if debugDeflate { + count -= int(nbytes)*8 + int(nbits) + } + // Unroll, write 3 codes/loop. + // Fastest number of unrolls. + for len(input) > 3 { + // We must have at least 48 bits free. + if nbits >= 8 { + n := nbits >> 3 + le.Store64(w.bytes[:], nbytes, bits) + bits >>= (n * 8) & 63 + nbits -= n * 8 + nbytes += n + } + if nbytes >= bufferFlushSize { + if w.err != nil { + nbytes = 0 + return + } + if debugDeflate { + count += int(nbytes) * 8 + } + _, w.err = w.writer.Write(w.bytes[:nbytes]) + nbytes = 0 + } + a, b := encoding[input[0]], encoding[input[1]] + bits |= a.code64() << (nbits & 63) + bits |= b.code64() << ((nbits + a.len()) & 63) + c := encoding[input[2]] + nbits += b.len() + a.len() + bits |= c.code64() << (nbits & 63) + nbits += c.len() + input = input[3:] + } + + // Remaining... + for _, t := range input { + if nbits >= 48 { + le.Store64(w.bytes[:], nbytes, bits) + bits >>= 48 + nbits -= 48 + nbytes += 6 + if nbytes >= bufferFlushSize { + if w.err != nil { + nbytes = 0 + return + } + if debugDeflate { + count += int(nbytes) * 8 + } + _, w.err = w.writer.Write(w.bytes[:nbytes]) + nbytes = 0 + } + } + // Bitwriting inlined, ~30% speedup + c := encoding[t] + bits |= c.code64() << (nbits & 63) + + nbits += c.len() + if debugDeflate { + count += int(c.len()) + } + } + // Restore... + w.bits, w.nbits, w.nbytes = bits, nbits, nbytes + + if debugDeflate { + nb := count + int(nbytes)*8 + int(nbits) + fmt.Println("wrote", nb, "bits,", nb/8, "bytes.") + } + // Flush if needed to have space. + if w.nbits >= 48 { + w.writeOutBits() + } + + if eof || sync { + w.writeCode(w.literalEncoding.codes[endBlockMarker]) + w.lastHeader = 0 + w.lastHuffMan = false + } +} diff --git a/vendor/github.com/klauspost/compress/flate/huffman_code.go b/vendor/github.com/klauspost/compress/flate/huffman_code.go new file mode 100644 index 000000000..4b312dea3 --- /dev/null +++ b/vendor/github.com/klauspost/compress/flate/huffman_code.go @@ -0,0 +1,417 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package flate + +import ( + "math" + "math/bits" +) + +const ( + maxBitsLimit = 16 + // number of valid literals + literalCount = 286 +) + +// hcode is a huffman code with a bit code and bit length. +type hcode uint32 + +func (h hcode) len() uint8 { + return uint8(h) +} + +func (h hcode) code64() uint64 { + return uint64(h >> 8) +} + +func (h hcode) zero() bool { + return h == 0 +} + +type huffmanEncoder struct { + codes []hcode + bitCount [17]int32 + + // Allocate a reusable buffer with the longest possible frequency table. + // Possible lengths are codegenCodeCount, offsetCodeCount and literalCount. + // The largest of these is literalCount, so we allocate for that case. + freqcache [literalCount + 1]literalNode +} + +type literalNode struct { + literal uint16 + freq uint16 +} + +// A levelInfo describes the state of the constructed tree for a given depth. +type levelInfo struct { + // Our level. for better printing + level int32 + + // The frequency of the last node at this level + lastFreq int32 + + // The frequency of the next character to add to this level + nextCharFreq int32 + + // The frequency of the next pair (from level below) to add to this level. + // Only valid if the "needed" value of the next lower level is 0. + nextPairFreq int32 + + // The number of chains remaining to generate for this level before moving + // up to the next level + needed int32 +} + +// set sets the code and length of an hcode. +func (h *hcode) set(code uint16, length uint8) { + *h = hcode(length) | (hcode(code) << 8) +} + +func newhcode(code uint16, length uint8) hcode { + return hcode(length) | (hcode(code) << 8) +} + +func reverseBits(number uint16, bitLength byte) uint16 { + return bits.Reverse16(number << ((16 - bitLength) & 15)) +} + +func maxNode() literalNode { return literalNode{math.MaxUint16, math.MaxUint16} } + +func newHuffmanEncoder(size int) *huffmanEncoder { + // Make capacity to next power of two. + c := uint(bits.Len32(uint32(size - 1))) + return &huffmanEncoder{codes: make([]hcode, size, 1<= 3 +// The cases of 0, 1, and 2 literals are handled by special case code. +// +// list An array of the literals with non-zero frequencies +// +// and their associated frequencies. The array is in order of increasing +// frequency, and has as its last element a special element with frequency +// MaxInt32 +// +// maxBits The maximum number of bits that should be used to encode any literal. +// +// Must be less than 16. +// +// return An integer array in which array[i] indicates the number of literals +// +// that should be encoded in i bits. +func (h *huffmanEncoder) bitCounts(list []literalNode, maxBits int32) []int32 { + if maxBits >= maxBitsLimit { + panic("flate: maxBits too large") + } + n := int32(len(list)) + list = list[0 : n+1] + list[n] = maxNode() + + // The tree can't have greater depth than n - 1, no matter what. This + // saves a little bit of work in some small cases + if maxBits > n-1 { + maxBits = n - 1 + } + + // Create information about each of the levels. + // A bogus "Level 0" whose sole purpose is so that + // level1.prev.needed==0. This makes level1.nextPairFreq + // be a legitimate value that never gets chosen. + var levels [maxBitsLimit]levelInfo + // leafCounts[i] counts the number of literals at the left + // of ancestors of the rightmost node at level i. + // leafCounts[i][j] is the number of literals at the left + // of the level j ancestor. + var leafCounts [maxBitsLimit][maxBitsLimit]int32 + + // Descending to only have 1 bounds check. + l2f := int32(list[2].freq) + l1f := int32(list[1].freq) + l0f := int32(list[0].freq) + int32(list[1].freq) + + for level := int32(1); level <= maxBits; level++ { + // For every level, the first two items are the first two characters. + // We initialize the levels as if we had already figured this out. + levels[level] = levelInfo{ + level: level, + lastFreq: l1f, + nextCharFreq: l2f, + nextPairFreq: l0f, + } + leafCounts[level][level] = 2 + if level == 1 { + levels[level].nextPairFreq = math.MaxInt32 + } + } + + // We need a total of 2*n - 2 items at top level and have already generated 2. + levels[maxBits].needed = 2*n - 4 + + level := uint32(maxBits) + for level < 16 { + l := &levels[level] + if l.nextPairFreq == math.MaxInt32 && l.nextCharFreq == math.MaxInt32 { + // We've run out of both leafs and pairs. + // End all calculations for this level. + // To make sure we never come back to this level or any lower level, + // set nextPairFreq impossibly large. + l.needed = 0 + levels[level+1].nextPairFreq = math.MaxInt32 + level++ + continue + } + + prevFreq := l.lastFreq + if l.nextCharFreq < l.nextPairFreq { + // The next item on this row is a leaf node. + n := leafCounts[level][level] + 1 + l.lastFreq = l.nextCharFreq + // Lower leafCounts are the same of the previous node. + leafCounts[level][level] = n + e := list[n] + if e.literal < math.MaxUint16 { + l.nextCharFreq = int32(e.freq) + } else { + l.nextCharFreq = math.MaxInt32 + } + } else { + // The next item on this row is a pair from the previous row. + // nextPairFreq isn't valid until we generate two + // more values in the level below + l.lastFreq = l.nextPairFreq + // Take leaf counts from the lower level, except counts[level] remains the same. + if true { + save := leafCounts[level][level] + leafCounts[level] = leafCounts[level-1] + leafCounts[level][level] = save + } else { + copy(leafCounts[level][:level], leafCounts[level-1][:level]) + } + levels[l.level-1].needed = 2 + } + + if l.needed--; l.needed == 0 { + // We've done everything we need to do for this level. + // Continue calculating one level up. Fill in nextPairFreq + // of that level with the sum of the two nodes we've just calculated on + // this level. + if l.level == maxBits { + // All done! + break + } + levels[l.level+1].nextPairFreq = prevFreq + l.lastFreq + level++ + } else { + // If we stole from below, move down temporarily to replenish it. + for levels[level-1].needed > 0 { + level-- + } + } + } + + // Somethings is wrong if at the end, the top level is null or hasn't used + // all of the leaves. + if leafCounts[maxBits][maxBits] != n { + panic("leafCounts[maxBits][maxBits] != n") + } + + bitCount := h.bitCount[:maxBits+1] + bits := 1 + counts := &leafCounts[maxBits] + for level := maxBits; level > 0; level-- { + // chain.leafCount gives the number of literals requiring at least "bits" + // bits to encode. + bitCount[bits] = counts[level] - counts[level-1] + bits++ + } + return bitCount +} + +// Look at the leaves and assign them a bit count and an encoding as specified +// in RFC 1951 3.2.2 +func (h *huffmanEncoder) assignEncodingAndSize(bitCount []int32, list []literalNode) { + code := uint16(0) + for n, bits := range bitCount { + code <<= 1 + if n == 0 || bits == 0 { + continue + } + // The literals list[len(list)-bits] .. list[len(list)-bits] + // are encoded using "bits" bits, and get the values + // code, code + 1, .... The code values are + // assigned in literal order (not frequency order). + chunk := list[len(list)-int(bits):] + + sortByLiteral(chunk) + for _, node := range chunk { + h.codes[node.literal] = newhcode(reverseBits(code, uint8(n)), uint8(n)) + code++ + } + list = list[0 : len(list)-int(bits)] + } +} + +// Update this Huffman Code object to be the minimum code for the specified frequency count. +// +// freq An array of frequencies, in which frequency[i] gives the frequency of literal i. +// maxBits The maximum number of bits to use for any literal. +func (h *huffmanEncoder) generate(freq []uint16, maxBits int32) { + list := h.freqcache[:len(freq)+1] + codes := h.codes[:len(freq)] + // Number of non-zero literals + count := 0 + // Set list to be the set of all non-zero literals and their frequencies + for i, f := range freq { + if f != 0 { + list[count] = literalNode{uint16(i), f} + count++ + } else { + codes[i] = 0 + } + } + list[count] = literalNode{} + + list = list[:count] + if count <= 2 { + // Handle the small cases here, because they are awkward for the general case code. With + // two or fewer literals, everything has bit length 1. + for i, node := range list { + // "list" is in order of increasing literal value. + h.codes[node.literal].set(uint16(i), 1) + } + return + } + sortByFreq(list) + + // Get the number of literals for each bit count + bitCount := h.bitCounts(list, maxBits) + // And do the assignment + h.assignEncodingAndSize(bitCount, list) +} + +// atLeastOne clamps the result between 1 and 15. +func atLeastOne(v float32) float32 { + if v < 1 { + return 1 + } + if v > 15 { + return 15 + } + return v +} + +func histogram(b []byte, h []uint16) { + if true && len(b) >= 8<<10 { + // Split for bigger inputs + histogramSplit(b, h) + } else { + h = h[:256] + for _, t := range b { + h[t]++ + } + } +} + +func histogramSplit(b []byte, h []uint16) { + // Tested, and slightly faster than 2-way. + // Writing to separate arrays and combining is also slightly slower. + h = h[:256] + for len(b)&3 != 0 { + h[b[0]]++ + b = b[1:] + } + n := len(b) / 4 + x, y, z, w := b[:n], b[n:], b[n+n:], b[n+n+n:] + y, z, w = y[:len(x)], z[:len(x)], w[:len(x)] + for i, t := range x { + v0 := &h[t] + v1 := &h[y[i]] + v2 := &h[z[i]] + v3 := &h[w[i]] + *v0++ + *v1++ + *v2++ + *v3++ + } +} diff --git a/vendor/github.com/klauspost/compress/flate/huffman_sortByFreq.go b/vendor/github.com/klauspost/compress/flate/huffman_sortByFreq.go new file mode 100644 index 000000000..6c05ba8c1 --- /dev/null +++ b/vendor/github.com/klauspost/compress/flate/huffman_sortByFreq.go @@ -0,0 +1,159 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package flate + +// Sort sorts data. +// It makes one call to data.Len to determine n, and O(n*log(n)) calls to +// data.Less and data.Swap. The sort is not guaranteed to be stable. +func sortByFreq(data []literalNode) { + n := len(data) + quickSortByFreq(data, 0, n, maxDepth(n)) +} + +func quickSortByFreq(data []literalNode, a, b, maxDepth int) { + for b-a > 12 { // Use ShellSort for slices <= 12 elements + if maxDepth == 0 { + heapSort(data, a, b) + return + } + maxDepth-- + mlo, mhi := doPivotByFreq(data, a, b) + // Avoiding recursion on the larger subproblem guarantees + // a stack depth of at most lg(b-a). + if mlo-a < b-mhi { + quickSortByFreq(data, a, mlo, maxDepth) + a = mhi // i.e., quickSortByFreq(data, mhi, b) + } else { + quickSortByFreq(data, mhi, b, maxDepth) + b = mlo // i.e., quickSortByFreq(data, a, mlo) + } + } + if b-a > 1 { + // Do ShellSort pass with gap 6 + // It could be written in this simplified form cause b-a <= 12 + for i := a + 6; i < b; i++ { + if data[i].freq == data[i-6].freq && data[i].literal < data[i-6].literal || data[i].freq < data[i-6].freq { + data[i], data[i-6] = data[i-6], data[i] + } + } + insertionSortByFreq(data, a, b) + } +} + +func doPivotByFreq(data []literalNode, lo, hi int) (midlo, midhi int) { + m := int(uint(lo+hi) >> 1) // Written like this to avoid integer overflow. + if hi-lo > 40 { + // Tukey's ``Ninther,'' median of three medians of three. + s := (hi - lo) / 8 + medianOfThreeSortByFreq(data, lo, lo+s, lo+2*s) + medianOfThreeSortByFreq(data, m, m-s, m+s) + medianOfThreeSortByFreq(data, hi-1, hi-1-s, hi-1-2*s) + } + medianOfThreeSortByFreq(data, lo, m, hi-1) + + // Invariants are: + // data[lo] = pivot (set up by ChoosePivot) + // data[lo < i < a] < pivot + // data[a <= i < b] <= pivot + // data[b <= i < c] unexamined + // data[c <= i < hi-1] > pivot + // data[hi-1] >= pivot + pivot := lo + a, c := lo+1, hi-1 + + for ; a < c && (data[a].freq == data[pivot].freq && data[a].literal < data[pivot].literal || data[a].freq < data[pivot].freq); a++ { + } + b := a + for { + for ; b < c && (data[pivot].freq == data[b].freq && data[pivot].literal > data[b].literal || data[pivot].freq > data[b].freq); b++ { // data[b] <= pivot + } + for ; b < c && (data[pivot].freq == data[c-1].freq && data[pivot].literal < data[c-1].literal || data[pivot].freq < data[c-1].freq); c-- { // data[c-1] > pivot + } + if b >= c { + break + } + // data[b] > pivot; data[c-1] <= pivot + data[b], data[c-1] = data[c-1], data[b] + b++ + c-- + } + // If hi-c<3 then there are duplicates (by property of median of nine). + // Let's be a bit more conservative, and set border to 5. + protect := hi-c < 5 + if !protect && hi-c < (hi-lo)/4 { + // Lets test some points for equality to pivot + dups := 0 + if data[pivot].freq == data[hi-1].freq && data[pivot].literal > data[hi-1].literal || data[pivot].freq > data[hi-1].freq { // data[hi-1] = pivot + data[c], data[hi-1] = data[hi-1], data[c] + c++ + dups++ + } + if data[b-1].freq == data[pivot].freq && data[b-1].literal > data[pivot].literal || data[b-1].freq > data[pivot].freq { // data[b-1] = pivot + b-- + dups++ + } + // m-lo = (hi-lo)/2 > 6 + // b-lo > (hi-lo)*3/4-1 > 8 + // ==> m < b ==> data[m] <= pivot + if data[m].freq == data[pivot].freq && data[m].literal > data[pivot].literal || data[m].freq > data[pivot].freq { // data[m] = pivot + data[m], data[b-1] = data[b-1], data[m] + b-- + dups++ + } + // if at least 2 points are equal to pivot, assume skewed distribution + protect = dups > 1 + } + if protect { + // Protect against a lot of duplicates + // Add invariant: + // data[a <= i < b] unexamined + // data[b <= i < c] = pivot + for { + for ; a < b && (data[b-1].freq == data[pivot].freq && data[b-1].literal > data[pivot].literal || data[b-1].freq > data[pivot].freq); b-- { // data[b] == pivot + } + for ; a < b && (data[a].freq == data[pivot].freq && data[a].literal < data[pivot].literal || data[a].freq < data[pivot].freq); a++ { // data[a] < pivot + } + if a >= b { + break + } + // data[a] == pivot; data[b-1] < pivot + data[a], data[b-1] = data[b-1], data[a] + a++ + b-- + } + } + // Swap pivot into middle + data[pivot], data[b-1] = data[b-1], data[pivot] + return b - 1, c +} + +// Insertion sort +func insertionSortByFreq(data []literalNode, a, b int) { + for i := a + 1; i < b; i++ { + for j := i; j > a && (data[j].freq == data[j-1].freq && data[j].literal < data[j-1].literal || data[j].freq < data[j-1].freq); j-- { + data[j], data[j-1] = data[j-1], data[j] + } + } +} + +// quickSortByFreq, loosely following Bentley and McIlroy, +// ``Engineering a Sort Function,'' SP&E November 1993. + +// medianOfThreeSortByFreq moves the median of the three values data[m0], data[m1], data[m2] into data[m1]. +func medianOfThreeSortByFreq(data []literalNode, m1, m0, m2 int) { + // sort 3 elements + if data[m1].freq == data[m0].freq && data[m1].literal < data[m0].literal || data[m1].freq < data[m0].freq { + data[m1], data[m0] = data[m0], data[m1] + } + // data[m0] <= data[m1] + if data[m2].freq == data[m1].freq && data[m2].literal < data[m1].literal || data[m2].freq < data[m1].freq { + data[m2], data[m1] = data[m1], data[m2] + // data[m0] <= data[m2] && data[m1] < data[m2] + if data[m1].freq == data[m0].freq && data[m1].literal < data[m0].literal || data[m1].freq < data[m0].freq { + data[m1], data[m0] = data[m0], data[m1] + } + } + // now data[m0] <= data[m1] <= data[m2] +} diff --git a/vendor/github.com/klauspost/compress/flate/huffman_sortByLiteral.go b/vendor/github.com/klauspost/compress/flate/huffman_sortByLiteral.go new file mode 100644 index 000000000..93f1aea10 --- /dev/null +++ b/vendor/github.com/klauspost/compress/flate/huffman_sortByLiteral.go @@ -0,0 +1,201 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package flate + +// Sort sorts data. +// It makes one call to data.Len to determine n, and O(n*log(n)) calls to +// data.Less and data.Swap. The sort is not guaranteed to be stable. +func sortByLiteral(data []literalNode) { + n := len(data) + quickSort(data, 0, n, maxDepth(n)) +} + +func quickSort(data []literalNode, a, b, maxDepth int) { + for b-a > 12 { // Use ShellSort for slices <= 12 elements + if maxDepth == 0 { + heapSort(data, a, b) + return + } + maxDepth-- + mlo, mhi := doPivot(data, a, b) + // Avoiding recursion on the larger subproblem guarantees + // a stack depth of at most lg(b-a). + if mlo-a < b-mhi { + quickSort(data, a, mlo, maxDepth) + a = mhi // i.e., quickSort(data, mhi, b) + } else { + quickSort(data, mhi, b, maxDepth) + b = mlo // i.e., quickSort(data, a, mlo) + } + } + if b-a > 1 { + // Do ShellSort pass with gap 6 + // It could be written in this simplified form cause b-a <= 12 + for i := a + 6; i < b; i++ { + if data[i].literal < data[i-6].literal { + data[i], data[i-6] = data[i-6], data[i] + } + } + insertionSort(data, a, b) + } +} +func heapSort(data []literalNode, a, b int) { + first := a + lo := 0 + hi := b - a + + // Build heap with greatest element at top. + for i := (hi - 1) / 2; i >= 0; i-- { + siftDown(data, i, hi, first) + } + + // Pop elements, largest first, into end of data. + for i := hi - 1; i >= 0; i-- { + data[first], data[first+i] = data[first+i], data[first] + siftDown(data, lo, i, first) + } +} + +// siftDown implements the heap property on data[lo, hi). +// first is an offset into the array where the root of the heap lies. +func siftDown(data []literalNode, lo, hi, first int) { + root := lo + for { + child := 2*root + 1 + if child >= hi { + break + } + if child+1 < hi && data[first+child].literal < data[first+child+1].literal { + child++ + } + if data[first+root].literal > data[first+child].literal { + return + } + data[first+root], data[first+child] = data[first+child], data[first+root] + root = child + } +} +func doPivot(data []literalNode, lo, hi int) (midlo, midhi int) { + m := int(uint(lo+hi) >> 1) // Written like this to avoid integer overflow. + if hi-lo > 40 { + // Tukey's ``Ninther,'' median of three medians of three. + s := (hi - lo) / 8 + medianOfThree(data, lo, lo+s, lo+2*s) + medianOfThree(data, m, m-s, m+s) + medianOfThree(data, hi-1, hi-1-s, hi-1-2*s) + } + medianOfThree(data, lo, m, hi-1) + + // Invariants are: + // data[lo] = pivot (set up by ChoosePivot) + // data[lo < i < a] < pivot + // data[a <= i < b] <= pivot + // data[b <= i < c] unexamined + // data[c <= i < hi-1] > pivot + // data[hi-1] >= pivot + pivot := lo + a, c := lo+1, hi-1 + + for ; a < c && data[a].literal < data[pivot].literal; a++ { + } + b := a + for { + for ; b < c && data[pivot].literal > data[b].literal; b++ { // data[b] <= pivot + } + for ; b < c && data[pivot].literal < data[c-1].literal; c-- { // data[c-1] > pivot + } + if b >= c { + break + } + // data[b] > pivot; data[c-1] <= pivot + data[b], data[c-1] = data[c-1], data[b] + b++ + c-- + } + // If hi-c<3 then there are duplicates (by property of median of nine). + // Let's be a bit more conservative, and set border to 5. + protect := hi-c < 5 + if !protect && hi-c < (hi-lo)/4 { + // Lets test some points for equality to pivot + dups := 0 + if data[pivot].literal > data[hi-1].literal { // data[hi-1] = pivot + data[c], data[hi-1] = data[hi-1], data[c] + c++ + dups++ + } + if data[b-1].literal > data[pivot].literal { // data[b-1] = pivot + b-- + dups++ + } + // m-lo = (hi-lo)/2 > 6 + // b-lo > (hi-lo)*3/4-1 > 8 + // ==> m < b ==> data[m] <= pivot + if data[m].literal > data[pivot].literal { // data[m] = pivot + data[m], data[b-1] = data[b-1], data[m] + b-- + dups++ + } + // if at least 2 points are equal to pivot, assume skewed distribution + protect = dups > 1 + } + if protect { + // Protect against a lot of duplicates + // Add invariant: + // data[a <= i < b] unexamined + // data[b <= i < c] = pivot + for { + for ; a < b && data[b-1].literal > data[pivot].literal; b-- { // data[b] == pivot + } + for ; a < b && data[a].literal < data[pivot].literal; a++ { // data[a] < pivot + } + if a >= b { + break + } + // data[a] == pivot; data[b-1] < pivot + data[a], data[b-1] = data[b-1], data[a] + a++ + b-- + } + } + // Swap pivot into middle + data[pivot], data[b-1] = data[b-1], data[pivot] + return b - 1, c +} + +// Insertion sort +func insertionSort(data []literalNode, a, b int) { + for i := a + 1; i < b; i++ { + for j := i; j > a && data[j].literal < data[j-1].literal; j-- { + data[j], data[j-1] = data[j-1], data[j] + } + } +} + +// maxDepth returns a threshold at which quicksort should switch +// to heapsort. It returns 2*ceil(lg(n+1)). +func maxDepth(n int) int { + var depth int + for i := n; i > 0; i >>= 1 { + depth++ + } + return depth * 2 +} + +// medianOfThree moves the median of the three values data[m0], data[m1], data[m2] into data[m1]. +func medianOfThree(data []literalNode, m1, m0, m2 int) { + // sort 3 elements + if data[m1].literal < data[m0].literal { + data[m1], data[m0] = data[m0], data[m1] + } + // data[m0] <= data[m1] + if data[m2].literal < data[m1].literal { + data[m2], data[m1] = data[m1], data[m2] + // data[m0] <= data[m2] && data[m1] < data[m2] + if data[m1].literal < data[m0].literal { + data[m1], data[m0] = data[m0], data[m1] + } + } + // now data[m0] <= data[m1] <= data[m2] +} diff --git a/vendor/github.com/klauspost/compress/flate/inflate.go b/vendor/github.com/klauspost/compress/flate/inflate.go new file mode 100644 index 000000000..6e90126db --- /dev/null +++ b/vendor/github.com/klauspost/compress/flate/inflate.go @@ -0,0 +1,865 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package flate implements the DEFLATE compressed data format, described in +// RFC 1951. The gzip and zlib packages implement access to DEFLATE-based file +// formats. +package flate + +import ( + "bufio" + "compress/flate" + "fmt" + "io" + "math/bits" + "sync" +) + +const ( + maxCodeLen = 16 // max length of Huffman code + maxCodeLenMask = 15 // mask for max length of Huffman code + // The next three numbers come from the RFC section 3.2.7, with the + // additional proviso in section 3.2.5 which implies that distance codes + // 30 and 31 should never occur in compressed data. + maxNumLit = 286 + maxNumDist = 30 + numCodes = 19 // number of codes in Huffman meta-code + + debugDecode = false +) + +// Value of length - 3 and extra bits. +type lengthExtra struct { + length, extra uint8 +} + +var decCodeToLen = [32]lengthExtra{{length: 0x0, extra: 0x0}, {length: 0x1, extra: 0x0}, {length: 0x2, extra: 0x0}, {length: 0x3, extra: 0x0}, {length: 0x4, extra: 0x0}, {length: 0x5, extra: 0x0}, {length: 0x6, extra: 0x0}, {length: 0x7, extra: 0x0}, {length: 0x8, extra: 0x1}, {length: 0xa, extra: 0x1}, {length: 0xc, extra: 0x1}, {length: 0xe, extra: 0x1}, {length: 0x10, extra: 0x2}, {length: 0x14, extra: 0x2}, {length: 0x18, extra: 0x2}, {length: 0x1c, extra: 0x2}, {length: 0x20, extra: 0x3}, {length: 0x28, extra: 0x3}, {length: 0x30, extra: 0x3}, {length: 0x38, extra: 0x3}, {length: 0x40, extra: 0x4}, {length: 0x50, extra: 0x4}, {length: 0x60, extra: 0x4}, {length: 0x70, extra: 0x4}, {length: 0x80, extra: 0x5}, {length: 0xa0, extra: 0x5}, {length: 0xc0, extra: 0x5}, {length: 0xe0, extra: 0x5}, {length: 0xff, extra: 0x0}, {length: 0x0, extra: 0x0}, {length: 0x0, extra: 0x0}, {length: 0x0, extra: 0x0}} + +var bitMask32 = [32]uint32{ + 0, 1, 3, 7, 0xF, 0x1F, 0x3F, 0x7F, 0xFF, + 0x1FF, 0x3FF, 0x7FF, 0xFFF, 0x1FFF, 0x3FFF, 0x7FFF, 0xFFFF, + 0x1ffff, 0x3ffff, 0x7FFFF, 0xfFFFF, 0x1fFFFF, 0x3fFFFF, 0x7fFFFF, 0xffFFFF, + 0x1ffFFFF, 0x3ffFFFF, 0x7ffFFFF, 0xfffFFFF, 0x1fffFFFF, 0x3fffFFFF, 0x7fffFFFF, +} // up to 32 bits + +// Initialize the fixedHuffmanDecoder only once upon first use. +var fixedOnce sync.Once +var fixedHuffmanDecoder huffmanDecoder + +// A CorruptInputError reports the presence of corrupt input at a given offset. +type CorruptInputError = flate.CorruptInputError + +// An InternalError reports an error in the flate code itself. +type InternalError string + +func (e InternalError) Error() string { return "flate: internal error: " + string(e) } + +// A ReadError reports an error encountered while reading input. +// +// Deprecated: No longer returned. +type ReadError = flate.ReadError + +// A WriteError reports an error encountered while writing output. +// +// Deprecated: No longer returned. +type WriteError = flate.WriteError + +// Resetter resets a ReadCloser returned by NewReader or NewReaderDict to +// to switch to a new underlying Reader. This permits reusing a ReadCloser +// instead of allocating a new one. +type Resetter interface { + // Reset discards any buffered data and resets the Resetter as if it was + // newly initialized with the given reader. + Reset(r io.Reader, dict []byte) error +} + +// The data structure for decoding Huffman tables is based on that of +// zlib. There is a lookup table of a fixed bit width (huffmanChunkBits), +// For codes smaller than the table width, there are multiple entries +// (each combination of trailing bits has the same value). For codes +// larger than the table width, the table contains a link to an overflow +// table. The width of each entry in the link table is the maximum code +// size minus the chunk width. +// +// Note that you can do a lookup in the table even without all bits +// filled. Since the extra bits are zero, and the DEFLATE Huffman codes +// have the property that shorter codes come before longer ones, the +// bit length estimate in the result is a lower bound on the actual +// number of bits. +// +// See the following: +// http://www.gzip.org/algorithm.txt + +// chunk & 15 is number of bits +// chunk >> 4 is value, including table link + +const ( + huffmanChunkBits = 9 + huffmanNumChunks = 1 << huffmanChunkBits + huffmanCountMask = 15 + huffmanValueShift = 4 +) + +type huffmanDecoder struct { + maxRead int // the maximum number of bits we can read and not overread + chunks *[huffmanNumChunks]uint16 // chunks as described above + links [][]uint16 // overflow links + linkMask uint32 // mask the width of the link table +} + +// Initialize Huffman decoding tables from array of code lengths. +// Following this function, h is guaranteed to be initialized into a complete +// tree (i.e., neither over-subscribed nor under-subscribed). The exception is a +// degenerate case where the tree has only a single symbol with length 1. Empty +// trees are permitted. +func (h *huffmanDecoder) init(lengths []int) bool { + // Sanity enables additional runtime tests during Huffman + // table construction. It's intended to be used during + // development to supplement the currently ad-hoc unit tests. + const sanity = false + + if h.chunks == nil { + h.chunks = new([huffmanNumChunks]uint16) + } + + if h.maxRead != 0 { + *h = huffmanDecoder{chunks: h.chunks, links: h.links} + } + + // Count number of codes of each length, + // compute maxRead and max length. + var count [maxCodeLen]int + var min, max int + for _, n := range lengths { + if n == 0 { + continue + } + if min == 0 || n < min { + min = n + } + if n > max { + max = n + } + count[n&maxCodeLenMask]++ + } + + // Empty tree. The decompressor.huffSym function will fail later if the tree + // is used. Technically, an empty tree is only valid for the HDIST tree and + // not the HCLEN and HLIT tree. However, a stream with an empty HCLEN tree + // is guaranteed to fail since it will attempt to use the tree to decode the + // codes for the HLIT and HDIST trees. Similarly, an empty HLIT tree is + // guaranteed to fail later since the compressed data section must be + // composed of at least one symbol (the end-of-block marker). + if max == 0 { + return true + } + + code := 0 + var nextcode [maxCodeLen]int + for i := min; i <= max; i++ { + code <<= 1 + nextcode[i&maxCodeLenMask] = code + code += count[i&maxCodeLenMask] + } + + // Check that the coding is complete (i.e., that we've + // assigned all 2-to-the-max possible bit sequences). + // Exception: To be compatible with zlib, we also need to + // accept degenerate single-code codings. See also + // TestDegenerateHuffmanCoding. + if code != 1< huffmanChunkBits { + numLinks := 1 << (uint(max) - huffmanChunkBits) + h.linkMask = uint32(numLinks - 1) + + // create link tables + link := nextcode[huffmanChunkBits+1] >> 1 + if cap(h.links) < huffmanNumChunks-link { + h.links = make([][]uint16, huffmanNumChunks-link) + } else { + h.links = h.links[:huffmanNumChunks-link] + } + for j := uint(link); j < huffmanNumChunks; j++ { + reverse := int(bits.Reverse16(uint16(j))) + reverse >>= uint(16 - huffmanChunkBits) + off := j - uint(link) + if sanity && h.chunks[reverse] != 0 { + panic("impossible: overwriting existing chunk") + } + h.chunks[reverse] = uint16(off<>= uint(16 - n) + if n <= huffmanChunkBits { + for off := reverse; off < len(h.chunks); off += 1 << uint(n) { + // We should never need to overwrite + // an existing chunk. Also, 0 is + // never a valid chunk, because the + // lower 4 "count" bits should be + // between 1 and 15. + if sanity && h.chunks[off] != 0 { + panic("impossible: overwriting existing chunk") + } + h.chunks[off] = chunk + } + } else { + j := reverse & (huffmanNumChunks - 1) + if sanity && h.chunks[j]&huffmanCountMask != huffmanChunkBits+1 { + // Longer codes should have been + // associated with a link table above. + panic("impossible: not an indirect chunk") + } + value := h.chunks[j] >> huffmanValueShift + linktab := h.links[value] + reverse >>= huffmanChunkBits + for off := reverse; off < len(linktab); off += 1 << uint(n-huffmanChunkBits) { + if sanity && linktab[off] != 0 { + panic("impossible: overwriting existing chunk") + } + linktab[off] = chunk + } + } + } + + if sanity { + // Above we've sanity checked that we never overwrote + // an existing entry. Here we additionally check that + // we filled the tables completely. + for i, chunk := range h.chunks { + if chunk == 0 { + // As an exception, in the degenerate + // single-code case, we allow odd + // chunks to be missing. + if code == 1 && i%2 == 1 { + continue + } + panic("impossible: missing chunk") + } + } + for _, linktab := range h.links { + for _, chunk := range linktab { + if chunk == 0 { + panic("impossible: missing chunk") + } + } + } + } + + return true +} + +// Reader is the actual read interface needed by NewReader. +// If the passed in io.Reader does not also have ReadByte, +// the NewReader will introduce its own buffering. +type Reader interface { + io.Reader + io.ByteReader +} + +type step uint8 + +const ( + copyData step = iota + 1 + nextBlock + huffmanBytesBuffer + huffmanBytesReader + huffmanBufioReader + huffmanStringsReader + huffmanGenericReader +) + +// flushMode tells decompressor when to return data +type flushMode uint8 + +const ( + syncFlush flushMode = iota // return data after sync flush block + partialFlush // return data after each block +) + +// Decompress state. +type decompressor struct { + // Input source. + r Reader + roffset int64 + + // Huffman decoders for literal/length, distance. + h1, h2 huffmanDecoder + + // Length arrays used to define Huffman codes. + bits *[maxNumLit + maxNumDist]int + codebits *[numCodes]int + + // Output history, buffer. + dict dictDecoder + + // Next step in the decompression, + // and decompression state. + step step + stepState int + err error + toRead []byte + hl, hd *huffmanDecoder + copyLen int + copyDist int + + // Temporary buffer (avoids repeated allocation). + buf [4]byte + + // Input bits, in top of b. + b uint32 + + nb uint + final bool + + flushMode flushMode +} + +func (f *decompressor) nextBlock() { + for f.nb < 1+2 { + if f.err = f.moreBits(); f.err != nil { + return + } + } + f.final = f.b&1 == 1 + f.b >>= 1 + typ := f.b & 3 + f.b >>= 2 + f.nb -= 1 + 2 + switch typ { + case 0: + f.dataBlock() + if debugDecode { + fmt.Println("stored block") + } + case 1: + // compressed, fixed Huffman tables + f.hl = &fixedHuffmanDecoder + f.hd = nil + f.huffmanBlockDecoder() + if debugDecode { + fmt.Println("predefinied huffman block") + } + case 2: + // compressed, dynamic Huffman tables + if f.err = f.readHuffman(); f.err != nil { + break + } + f.hl = &f.h1 + f.hd = &f.h2 + f.huffmanBlockDecoder() + if debugDecode { + fmt.Println("dynamic huffman block") + } + default: + // 3 is reserved. + if debugDecode { + fmt.Println("reserved data block encountered") + } + f.err = CorruptInputError(f.roffset) + } +} + +func (f *decompressor) Read(b []byte) (int, error) { + for { + if len(f.toRead) > 0 { + n := copy(b, f.toRead) + f.toRead = f.toRead[n:] + if len(f.toRead) == 0 { + return n, f.err + } + return n, nil + } + if f.err != nil { + return 0, f.err + } + + f.doStep() + + if f.err != nil && len(f.toRead) == 0 { + f.toRead = f.dict.readFlush() // Flush what's left in case of error + } + } +} + +// WriteTo implements the io.WriteTo interface for io.Copy and friends. +func (f *decompressor) WriteTo(w io.Writer) (int64, error) { + total := int64(0) + flushed := false + for { + if len(f.toRead) > 0 { + n, err := w.Write(f.toRead) + total += int64(n) + if err != nil { + f.err = err + return total, err + } + if n != len(f.toRead) { + return total, io.ErrShortWrite + } + f.toRead = f.toRead[:0] + } + if f.err != nil && flushed { + if f.err == io.EOF { + return total, nil + } + return total, f.err + } + if f.err == nil { + f.doStep() + } + if len(f.toRead) == 0 && f.err != nil && !flushed { + f.toRead = f.dict.readFlush() // Flush what's left in case of error + flushed = true + } + } +} + +func (f *decompressor) Close() error { + if f.err == io.EOF { + return nil + } + return f.err +} + +// RFC 1951 section 3.2.7. +// Compression with dynamic Huffman codes + +var codeOrder = [...]int{16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15} + +func (f *decompressor) readHuffman() error { + // HLIT[5], HDIST[5], HCLEN[4]. + for f.nb < 5+5+4 { + if err := f.moreBits(); err != nil { + return err + } + } + nlit := int(f.b&0x1F) + 257 + if nlit > maxNumLit { + if debugDecode { + fmt.Println("nlit > maxNumLit", nlit) + } + return CorruptInputError(f.roffset) + } + f.b >>= 5 + ndist := int(f.b&0x1F) + 1 + if ndist > maxNumDist { + if debugDecode { + fmt.Println("ndist > maxNumDist", ndist) + } + return CorruptInputError(f.roffset) + } + f.b >>= 5 + nclen := int(f.b&0xF) + 4 + // numCodes is 19, so nclen is always valid. + f.b >>= 4 + f.nb -= 5 + 5 + 4 + + // (HCLEN+4)*3 bits: code lengths in the magic codeOrder order. + for i := range nclen { + for f.nb < 3 { + if err := f.moreBits(); err != nil { + return err + } + } + f.codebits[codeOrder[i]] = int(f.b & 0x7) + f.b >>= 3 + f.nb -= 3 + } + for i := nclen; i < len(codeOrder); i++ { + f.codebits[codeOrder[i]] = 0 + } + if !f.h1.init(f.codebits[0:]) { + if debugDecode { + fmt.Println("init codebits failed") + } + return CorruptInputError(f.roffset) + } + + // HLIT + 257 code lengths, HDIST + 1 code lengths, + // using the code length Huffman code. + for i, n := 0, nlit+ndist; i < n; { + x, err := f.huffSym(&f.h1) + if err != nil { + return err + } + if x < 16 { + // Actual length. + f.bits[i] = x + i++ + continue + } + // Repeat previous length or zero. + var rep int + var nb uint + var b int + switch x { + default: + return InternalError("unexpected length code") + case 16: + rep = 3 + nb = 2 + if i == 0 { + if debugDecode { + fmt.Println("i==0") + } + return CorruptInputError(f.roffset) + } + b = f.bits[i-1] + case 17: + rep = 3 + nb = 3 + b = 0 + case 18: + rep = 11 + nb = 7 + b = 0 + } + for f.nb < nb { + if err := f.moreBits(); err != nil { + if debugDecode { + fmt.Println("morebits:", err) + } + return err + } + } + rep += int(f.b & uint32(1<<(nb®SizeMaskUint32)-1)) + f.b >>= nb & regSizeMaskUint32 + f.nb -= nb + if i+rep > n { + if debugDecode { + fmt.Println("i+rep > n", i, rep, n) + } + return CorruptInputError(f.roffset) + } + for j := 0; j < rep; j++ { + f.bits[i] = b + i++ + } + } + + if !f.h1.init(f.bits[0:nlit]) || !f.h2.init(f.bits[nlit:nlit+ndist]) { + if debugDecode { + fmt.Println("init2 failed") + } + return CorruptInputError(f.roffset) + } + + // As an optimization, we can initialize the maxRead bits to read at a time + // for the HLIT tree to the length of the EOB marker since we know that + // every block must terminate with one. This preserves the property that + // we never read any extra bytes after the end of the DEFLATE stream. + if f.h1.maxRead < f.bits[endBlockMarker] { + f.h1.maxRead = f.bits[endBlockMarker] + } + if !f.final { + // If not the final block, the smallest block possible is + // a predefined table, BTYPE=01, with a single EOB marker. + // This will take up 3 + 7 bits. + f.h1.maxRead += 10 + } + + return nil +} + +// Copy a single uncompressed data block from input to output. +func (f *decompressor) dataBlock() { + // Uncompressed. + // Discard current half-byte. + left := (f.nb) & 7 + f.nb -= left + f.b >>= left + + offBytes := f.nb >> 3 + // Unfilled values will be overwritten. + f.buf[0] = uint8(f.b) + f.buf[1] = uint8(f.b >> 8) + f.buf[2] = uint8(f.b >> 16) + f.buf[3] = uint8(f.b >> 24) + + f.roffset += int64(offBytes) + f.nb, f.b = 0, 0 + + // Length then ones-complement of length. + nr, err := io.ReadFull(f.r, f.buf[offBytes:4]) + f.roffset += int64(nr) + if err != nil { + f.err = noEOF(err) + return + } + n := uint16(f.buf[0]) | uint16(f.buf[1])<<8 + nn := uint16(f.buf[2]) | uint16(f.buf[3])<<8 + if nn != ^n { + if debugDecode { + ncomp := ^n + fmt.Println("uint16(nn) != uint16(^n)", nn, ncomp) + } + f.err = CorruptInputError(f.roffset) + return + } + + if n == 0 { + if f.flushMode == syncFlush { + f.toRead = f.dict.readFlush() + } + + f.finishBlock() + return + } + + f.copyLen = int(n) + f.copyData() +} + +// copyData copies f.copyLen bytes from the underlying reader into f.hist. +// It pauses for reads when f.hist is full. +func (f *decompressor) copyData() { + buf := f.dict.writeSlice() + if len(buf) > f.copyLen { + buf = buf[:f.copyLen] + } + + cnt, err := io.ReadFull(f.r, buf) + f.roffset += int64(cnt) + f.copyLen -= cnt + f.dict.writeMark(cnt) + if err != nil { + f.err = noEOF(err) + return + } + + if f.dict.availWrite() == 0 || f.copyLen > 0 { + f.toRead = f.dict.readFlush() + f.step = copyData + return + } + f.finishBlock() +} + +func (f *decompressor) finishBlock() { + if f.final { + if f.dict.availRead() > 0 { + f.toRead = f.dict.readFlush() + } + + f.err = io.EOF + } else if f.flushMode == partialFlush && f.dict.availRead() > 0 { + f.toRead = f.dict.readFlush() + } + + f.step = nextBlock +} + +func (f *decompressor) doStep() { + switch f.step { + case copyData: + f.copyData() + case nextBlock: + f.nextBlock() + case huffmanBytesBuffer: + f.huffmanBytesBuffer() + case huffmanBytesReader: + f.huffmanBytesReader() + case huffmanBufioReader: + f.huffmanBufioReader() + case huffmanStringsReader: + f.huffmanStringsReader() + case huffmanGenericReader: + f.huffmanGenericReader() + default: + panic("BUG: unexpected step state") + } +} + +// noEOF returns err, unless err == io.EOF, in which case it returns io.ErrUnexpectedEOF. +func noEOF(e error) error { + if e == io.EOF { + return io.ErrUnexpectedEOF + } + return e +} + +func (f *decompressor) moreBits() error { + c, err := f.r.ReadByte() + if err != nil { + return noEOF(err) + } + f.roffset++ + f.b |= uint32(c) << (f.nb & regSizeMaskUint32) + f.nb += 8 + return nil +} + +// Read the next Huffman-encoded symbol from f according to h. +func (f *decompressor) huffSym(h *huffmanDecoder) (int, error) { + // Since a huffmanDecoder can be empty or be composed of a degenerate tree + // with single element, huffSym must error on these two edge cases. In both + // cases, the chunks slice will be 0 for the invalid sequence, leading it + // satisfy the n == 0 check below. + n := uint(h.maxRead) + // Optimization. Compiler isn't smart enough to keep f.b,f.nb in registers, + // but is smart enough to keep local variables in registers, so use nb and b, + // inline call to moreBits and reassign b,nb back to f on return. + nb, b := f.nb, f.b + for { + for nb < n { + c, err := f.r.ReadByte() + if err != nil { + f.b = b + f.nb = nb + return 0, noEOF(err) + } + f.roffset++ + b |= uint32(c) << (nb & regSizeMaskUint32) + nb += 8 + } + chunk := h.chunks[b&(huffmanNumChunks-1)] + n = uint(chunk & huffmanCountMask) + if n > huffmanChunkBits { + chunk = h.links[chunk>>huffmanValueShift][(b>>huffmanChunkBits)&h.linkMask] + n = uint(chunk & huffmanCountMask) + } + if n <= nb { + if n == 0 { + f.b = b + f.nb = nb + if debugDecode { + fmt.Println("huffsym: n==0") + } + f.err = CorruptInputError(f.roffset) + return 0, f.err + } + f.b = b >> (n & regSizeMaskUint32) + f.nb = nb - n + return int(chunk >> huffmanValueShift), nil + } + } +} + +func makeReader(r io.Reader) Reader { + if rr, ok := r.(Reader); ok { + return rr + } + return bufio.NewReader(r) +} + +func fixedHuffmanDecoderInit() { + fixedOnce.Do(func() { + // These come from the RFC section 3.2.6. + var bits [288]int + for i := range 144 { + bits[i] = 8 + } + for i := 144; i < 256; i++ { + bits[i] = 9 + } + for i := 256; i < 280; i++ { + bits[i] = 7 + } + for i := 280; i < 288; i++ { + bits[i] = 8 + } + fixedHuffmanDecoder.init(bits[:]) + }) +} + +func (f *decompressor) Reset(r io.Reader, dict []byte) error { + *f = decompressor{ + r: makeReader(r), + bits: f.bits, + codebits: f.codebits, + h1: f.h1, + h2: f.h2, + dict: f.dict, + step: nextBlock, + } + f.dict.init(maxMatchOffset, dict) + return nil +} + +type ReaderOpt func(*decompressor) + +// WithPartialBlock tells decompressor to return after each block, +// so it can read data written with partial flush +func WithPartialBlock() ReaderOpt { + return func(f *decompressor) { + f.flushMode = partialFlush + } +} + +// WithDict initializes the reader with a preset dictionary +func WithDict(dict []byte) ReaderOpt { + return func(f *decompressor) { + f.dict.init(maxMatchOffset, dict) + } +} + +// NewReaderOpts returns new reader with provided options +func NewReaderOpts(r io.Reader, opts ...ReaderOpt) io.ReadCloser { + fixedHuffmanDecoderInit() + + var f decompressor + f.r = makeReader(r) + f.bits = new([maxNumLit + maxNumDist]int) + f.codebits = new([numCodes]int) + f.step = nextBlock + f.dict.init(maxMatchOffset, nil) + + for _, opt := range opts { + opt(&f) + } + + return &f +} + +// NewReader returns a new ReadCloser that can be used +// to read the uncompressed version of r. +// If r does not also implement io.ByteReader, +// the decompressor may read more data than necessary from r. +// It is the caller's responsibility to call Close on the ReadCloser +// when finished reading. +// +// The ReadCloser returned by NewReader also implements Resetter. +func NewReader(r io.Reader) io.ReadCloser { + return NewReaderOpts(r) +} + +// NewReaderDict is like NewReader but initializes the reader +// with a preset dictionary. The returned Reader behaves as if +// the uncompressed data stream started with the given dictionary, +// which has already been read. NewReaderDict is typically used +// to read data compressed by NewWriterDict. +// +// The ReadCloser returned by NewReader also implements Resetter. +func NewReaderDict(r io.Reader, dict []byte) io.ReadCloser { + return NewReaderOpts(r, WithDict(dict)) +} diff --git a/vendor/github.com/klauspost/compress/flate/inflate_gen.go b/vendor/github.com/klauspost/compress/flate/inflate_gen.go new file mode 100644 index 000000000..2b2f993f7 --- /dev/null +++ b/vendor/github.com/klauspost/compress/flate/inflate_gen.go @@ -0,0 +1,1283 @@ +// Code generated by go generate gen_inflate.go. DO NOT EDIT. + +package flate + +import ( + "bufio" + "bytes" + "fmt" + "math/bits" + "strings" +) + +// Decode a single Huffman block from f. +// hl and hd are the Huffman states for the lit/length values +// and the distance values, respectively. If hd == nil, using the +// fixed distance encoding associated with fixed Huffman blocks. +func (f *decompressor) huffmanBytesBuffer() { + const ( + stateInit = iota // Zero value must be stateInit + stateDict + ) + fr := f.r.(*bytes.Buffer) + + // Optimization. Compiler isn't smart enough to keep f.b,f.nb in registers, + // but is smart enough to keep local variables in registers, so use nb and b, + // inline call to moreBits and reassign b,nb back to f on return. + fnb, fb, dict := f.nb, f.b, &f.dict + + switch f.stepState { + case stateInit: + goto readLiteral + case stateDict: + goto copyHistory + } + +readLiteral: + // Read literal and/or (length, distance) according to RFC section 3.2.3. + { + var v int + { + // Inlined v, err := f.huffSym(f.hl) + // Since a huffmanDecoder can be empty or be composed of a degenerate tree + // with single element, huffSym must error on these two edge cases. In both + // cases, the chunks slice will be 0 for the invalid sequence, leading it + // satisfy the n == 0 check below. + n := uint(f.hl.maxRead) + for { + for fnb < n { + c, err := fr.ReadByte() + if err != nil { + f.b, f.nb = fb, fnb + f.err = noEOF(err) + return + } + f.roffset++ + fb |= uint32(c) << (fnb & regSizeMaskUint32) + fnb += 8 + } + chunk := f.hl.chunks[fb&(huffmanNumChunks-1)] + n = uint(chunk & huffmanCountMask) + if n > huffmanChunkBits { + chunk = f.hl.links[chunk>>huffmanValueShift][(fb>>huffmanChunkBits)&f.hl.linkMask] + n = uint(chunk & huffmanCountMask) + } + if n <= fnb { + if n == 0 { + f.b, f.nb = fb, fnb + if debugDecode { + fmt.Println("huffsym: n==0") + } + f.err = CorruptInputError(f.roffset) + return + } + fb = fb >> (n & regSizeMaskUint32) + fnb = fnb - n + v = int(chunk >> huffmanValueShift) + break + } + } + } + + var length int + switch { + case v < 256: + dict.writeByte(byte(v)) + if dict.availWrite() == 0 { + f.toRead = dict.readFlush() + f.step = huffmanBytesBuffer + f.stepState = stateInit + f.b, f.nb = fb, fnb + return + } + goto readLiteral + case v == 256: + f.b, f.nb = fb, fnb + f.finishBlock() + return + // otherwise, reference to older data + case v < 265: + length = v - (257 - 3) + case v < maxNumLit: + val := decCodeToLen[(v - 257)] + length = int(val.length) + 3 + n := uint(val.extra) + for fnb < n { + c, err := fr.ReadByte() + if err != nil { + f.b, f.nb = fb, fnb + if debugDecode { + fmt.Println("morebits n>0:", err) + } + f.err = err + return + } + f.roffset++ + fb |= uint32(c) << (fnb & regSizeMaskUint32) + fnb += 8 + } + length += int(fb & bitMask32[n]) + fb >>= n & regSizeMaskUint32 + fnb -= n + default: + if debugDecode { + fmt.Println(v, ">= maxNumLit") + } + f.err = CorruptInputError(f.roffset) + f.b, f.nb = fb, fnb + return + } + + var dist uint32 + if f.hd == nil { + for fnb < 5 { + c, err := fr.ReadByte() + if err != nil { + f.b, f.nb = fb, fnb + if debugDecode { + fmt.Println("morebits f.nb<5:", err) + } + f.err = err + return + } + f.roffset++ + fb |= uint32(c) << (fnb & regSizeMaskUint32) + fnb += 8 + } + dist = uint32(bits.Reverse8(uint8(fb & 0x1F << 3))) + fb >>= 5 + fnb -= 5 + } else { + // Since a huffmanDecoder can be empty or be composed of a degenerate tree + // with single element, huffSym must error on these two edge cases. In both + // cases, the chunks slice will be 0 for the invalid sequence, leading it + // satisfy the n == 0 check below. + n := uint(f.hd.maxRead) + // Optimization. Compiler isn't smart enough to keep f.b,f.nb in registers, + // but is smart enough to keep local variables in registers, so use nb and b, + // inline call to moreBits and reassign b,nb back to f on return. + for { + for fnb < n { + c, err := fr.ReadByte() + if err != nil { + f.b, f.nb = fb, fnb + f.err = noEOF(err) + return + } + f.roffset++ + fb |= uint32(c) << (fnb & regSizeMaskUint32) + fnb += 8 + } + chunk := f.hd.chunks[fb&(huffmanNumChunks-1)] + n = uint(chunk & huffmanCountMask) + if n > huffmanChunkBits { + chunk = f.hd.links[chunk>>huffmanValueShift][(fb>>huffmanChunkBits)&f.hd.linkMask] + n = uint(chunk & huffmanCountMask) + } + if n <= fnb { + if n == 0 { + f.b, f.nb = fb, fnb + if debugDecode { + fmt.Println("huffsym: n==0") + } + f.err = CorruptInputError(f.roffset) + return + } + fb = fb >> (n & regSizeMaskUint32) + fnb = fnb - n + dist = uint32(chunk >> huffmanValueShift) + break + } + } + } + + switch { + case dist < 4: + dist++ + case dist < maxNumDist: + nb := uint(dist-2) >> 1 + // have 1 bit in bottom of dist, need nb more. + extra := (dist & 1) << (nb & regSizeMaskUint32) + for fnb < nb { + c, err := fr.ReadByte() + if err != nil { + f.b, f.nb = fb, fnb + if debugDecode { + fmt.Println("morebits f.nb>= nb & regSizeMaskUint32 + fnb -= nb + dist = 1<<((nb+1)®SizeMaskUint32) + 1 + extra + // slower: dist = bitMask32[nb+1] + 2 + extra + default: + f.b, f.nb = fb, fnb + if debugDecode { + fmt.Println("dist too big:", dist, maxNumDist) + } + f.err = CorruptInputError(f.roffset) + return + } + + // No check on length; encoding can be prescient. + if dist > uint32(dict.histSize()) { + f.b, f.nb = fb, fnb + if debugDecode { + fmt.Println("dist > dict.histSize():", dist, dict.histSize()) + } + f.err = CorruptInputError(f.roffset) + return + } + + f.copyLen, f.copyDist = length, int(dist) + goto copyHistory + } + +copyHistory: + // Perform a backwards copy according to RFC section 3.2.3. + { + cnt := dict.tryWriteCopy(f.copyDist, f.copyLen) + if cnt == 0 { + cnt = dict.writeCopy(f.copyDist, f.copyLen) + } + f.copyLen -= cnt + + if dict.availWrite() == 0 || f.copyLen > 0 { + f.toRead = dict.readFlush() + f.step = huffmanBytesBuffer // We need to continue this work + f.stepState = stateDict + f.b, f.nb = fb, fnb + return + } + goto readLiteral + } + // Not reached +} + +// Decode a single Huffman block from f. +// hl and hd are the Huffman states for the lit/length values +// and the distance values, respectively. If hd == nil, using the +// fixed distance encoding associated with fixed Huffman blocks. +func (f *decompressor) huffmanBytesReader() { + const ( + stateInit = iota // Zero value must be stateInit + stateDict + ) + fr := f.r.(*bytes.Reader) + + // Optimization. Compiler isn't smart enough to keep f.b,f.nb in registers, + // but is smart enough to keep local variables in registers, so use nb and b, + // inline call to moreBits and reassign b,nb back to f on return. + fnb, fb, dict := f.nb, f.b, &f.dict + + switch f.stepState { + case stateInit: + goto readLiteral + case stateDict: + goto copyHistory + } + +readLiteral: + // Read literal and/or (length, distance) according to RFC section 3.2.3. + { + var v int + { + // Inlined v, err := f.huffSym(f.hl) + // Since a huffmanDecoder can be empty or be composed of a degenerate tree + // with single element, huffSym must error on these two edge cases. In both + // cases, the chunks slice will be 0 for the invalid sequence, leading it + // satisfy the n == 0 check below. + n := uint(f.hl.maxRead) + for { + for fnb < n { + c, err := fr.ReadByte() + if err != nil { + f.b, f.nb = fb, fnb + f.err = noEOF(err) + return + } + f.roffset++ + fb |= uint32(c) << (fnb & regSizeMaskUint32) + fnb += 8 + } + chunk := f.hl.chunks[fb&(huffmanNumChunks-1)] + n = uint(chunk & huffmanCountMask) + if n > huffmanChunkBits { + chunk = f.hl.links[chunk>>huffmanValueShift][(fb>>huffmanChunkBits)&f.hl.linkMask] + n = uint(chunk & huffmanCountMask) + } + if n <= fnb { + if n == 0 { + f.b, f.nb = fb, fnb + if debugDecode { + fmt.Println("huffsym: n==0") + } + f.err = CorruptInputError(f.roffset) + return + } + fb = fb >> (n & regSizeMaskUint32) + fnb = fnb - n + v = int(chunk >> huffmanValueShift) + break + } + } + } + + var length int + switch { + case v < 256: + dict.writeByte(byte(v)) + if dict.availWrite() == 0 { + f.toRead = dict.readFlush() + f.step = huffmanBytesReader + f.stepState = stateInit + f.b, f.nb = fb, fnb + return + } + goto readLiteral + case v == 256: + f.b, f.nb = fb, fnb + f.finishBlock() + return + // otherwise, reference to older data + case v < 265: + length = v - (257 - 3) + case v < maxNumLit: + val := decCodeToLen[(v - 257)] + length = int(val.length) + 3 + n := uint(val.extra) + for fnb < n { + c, err := fr.ReadByte() + if err != nil { + f.b, f.nb = fb, fnb + if debugDecode { + fmt.Println("morebits n>0:", err) + } + f.err = err + return + } + f.roffset++ + fb |= uint32(c) << (fnb & regSizeMaskUint32) + fnb += 8 + } + length += int(fb & bitMask32[n]) + fb >>= n & regSizeMaskUint32 + fnb -= n + default: + if debugDecode { + fmt.Println(v, ">= maxNumLit") + } + f.err = CorruptInputError(f.roffset) + f.b, f.nb = fb, fnb + return + } + + var dist uint32 + if f.hd == nil { + for fnb < 5 { + c, err := fr.ReadByte() + if err != nil { + f.b, f.nb = fb, fnb + if debugDecode { + fmt.Println("morebits f.nb<5:", err) + } + f.err = err + return + } + f.roffset++ + fb |= uint32(c) << (fnb & regSizeMaskUint32) + fnb += 8 + } + dist = uint32(bits.Reverse8(uint8(fb & 0x1F << 3))) + fb >>= 5 + fnb -= 5 + } else { + // Since a huffmanDecoder can be empty or be composed of a degenerate tree + // with single element, huffSym must error on these two edge cases. In both + // cases, the chunks slice will be 0 for the invalid sequence, leading it + // satisfy the n == 0 check below. + n := uint(f.hd.maxRead) + // Optimization. Compiler isn't smart enough to keep f.b,f.nb in registers, + // but is smart enough to keep local variables in registers, so use nb and b, + // inline call to moreBits and reassign b,nb back to f on return. + for { + for fnb < n { + c, err := fr.ReadByte() + if err != nil { + f.b, f.nb = fb, fnb + f.err = noEOF(err) + return + } + f.roffset++ + fb |= uint32(c) << (fnb & regSizeMaskUint32) + fnb += 8 + } + chunk := f.hd.chunks[fb&(huffmanNumChunks-1)] + n = uint(chunk & huffmanCountMask) + if n > huffmanChunkBits { + chunk = f.hd.links[chunk>>huffmanValueShift][(fb>>huffmanChunkBits)&f.hd.linkMask] + n = uint(chunk & huffmanCountMask) + } + if n <= fnb { + if n == 0 { + f.b, f.nb = fb, fnb + if debugDecode { + fmt.Println("huffsym: n==0") + } + f.err = CorruptInputError(f.roffset) + return + } + fb = fb >> (n & regSizeMaskUint32) + fnb = fnb - n + dist = uint32(chunk >> huffmanValueShift) + break + } + } + } + + switch { + case dist < 4: + dist++ + case dist < maxNumDist: + nb := uint(dist-2) >> 1 + // have 1 bit in bottom of dist, need nb more. + extra := (dist & 1) << (nb & regSizeMaskUint32) + for fnb < nb { + c, err := fr.ReadByte() + if err != nil { + f.b, f.nb = fb, fnb + if debugDecode { + fmt.Println("morebits f.nb>= nb & regSizeMaskUint32 + fnb -= nb + dist = 1<<((nb+1)®SizeMaskUint32) + 1 + extra + // slower: dist = bitMask32[nb+1] + 2 + extra + default: + f.b, f.nb = fb, fnb + if debugDecode { + fmt.Println("dist too big:", dist, maxNumDist) + } + f.err = CorruptInputError(f.roffset) + return + } + + // No check on length; encoding can be prescient. + if dist > uint32(dict.histSize()) { + f.b, f.nb = fb, fnb + if debugDecode { + fmt.Println("dist > dict.histSize():", dist, dict.histSize()) + } + f.err = CorruptInputError(f.roffset) + return + } + + f.copyLen, f.copyDist = length, int(dist) + goto copyHistory + } + +copyHistory: + // Perform a backwards copy according to RFC section 3.2.3. + { + cnt := dict.tryWriteCopy(f.copyDist, f.copyLen) + if cnt == 0 { + cnt = dict.writeCopy(f.copyDist, f.copyLen) + } + f.copyLen -= cnt + + if dict.availWrite() == 0 || f.copyLen > 0 { + f.toRead = dict.readFlush() + f.step = huffmanBytesReader // We need to continue this work + f.stepState = stateDict + f.b, f.nb = fb, fnb + return + } + goto readLiteral + } + // Not reached +} + +// Decode a single Huffman block from f. +// hl and hd are the Huffman states for the lit/length values +// and the distance values, respectively. If hd == nil, using the +// fixed distance encoding associated with fixed Huffman blocks. +func (f *decompressor) huffmanBufioReader() { + const ( + stateInit = iota // Zero value must be stateInit + stateDict + ) + fr := f.r.(*bufio.Reader) + + // Optimization. Compiler isn't smart enough to keep f.b,f.nb in registers, + // but is smart enough to keep local variables in registers, so use nb and b, + // inline call to moreBits and reassign b,nb back to f on return. + fnb, fb, dict := f.nb, f.b, &f.dict + + switch f.stepState { + case stateInit: + goto readLiteral + case stateDict: + goto copyHistory + } + +readLiteral: + // Read literal and/or (length, distance) according to RFC section 3.2.3. + { + var v int + { + // Inlined v, err := f.huffSym(f.hl) + // Since a huffmanDecoder can be empty or be composed of a degenerate tree + // with single element, huffSym must error on these two edge cases. In both + // cases, the chunks slice will be 0 for the invalid sequence, leading it + // satisfy the n == 0 check below. + n := uint(f.hl.maxRead) + for { + for fnb < n { + c, err := fr.ReadByte() + if err != nil { + f.b, f.nb = fb, fnb + f.err = noEOF(err) + return + } + f.roffset++ + fb |= uint32(c) << (fnb & regSizeMaskUint32) + fnb += 8 + } + chunk := f.hl.chunks[fb&(huffmanNumChunks-1)] + n = uint(chunk & huffmanCountMask) + if n > huffmanChunkBits { + chunk = f.hl.links[chunk>>huffmanValueShift][(fb>>huffmanChunkBits)&f.hl.linkMask] + n = uint(chunk & huffmanCountMask) + } + if n <= fnb { + if n == 0 { + f.b, f.nb = fb, fnb + if debugDecode { + fmt.Println("huffsym: n==0") + } + f.err = CorruptInputError(f.roffset) + return + } + fb = fb >> (n & regSizeMaskUint32) + fnb = fnb - n + v = int(chunk >> huffmanValueShift) + break + } + } + } + + var length int + switch { + case v < 256: + dict.writeByte(byte(v)) + if dict.availWrite() == 0 { + f.toRead = dict.readFlush() + f.step = huffmanBufioReader + f.stepState = stateInit + f.b, f.nb = fb, fnb + return + } + goto readLiteral + case v == 256: + f.b, f.nb = fb, fnb + f.finishBlock() + return + // otherwise, reference to older data + case v < 265: + length = v - (257 - 3) + case v < maxNumLit: + val := decCodeToLen[(v - 257)] + length = int(val.length) + 3 + n := uint(val.extra) + for fnb < n { + c, err := fr.ReadByte() + if err != nil { + f.b, f.nb = fb, fnb + if debugDecode { + fmt.Println("morebits n>0:", err) + } + f.err = err + return + } + f.roffset++ + fb |= uint32(c) << (fnb & regSizeMaskUint32) + fnb += 8 + } + length += int(fb & bitMask32[n]) + fb >>= n & regSizeMaskUint32 + fnb -= n + default: + if debugDecode { + fmt.Println(v, ">= maxNumLit") + } + f.err = CorruptInputError(f.roffset) + f.b, f.nb = fb, fnb + return + } + + var dist uint32 + if f.hd == nil { + for fnb < 5 { + c, err := fr.ReadByte() + if err != nil { + f.b, f.nb = fb, fnb + if debugDecode { + fmt.Println("morebits f.nb<5:", err) + } + f.err = err + return + } + f.roffset++ + fb |= uint32(c) << (fnb & regSizeMaskUint32) + fnb += 8 + } + dist = uint32(bits.Reverse8(uint8(fb & 0x1F << 3))) + fb >>= 5 + fnb -= 5 + } else { + // Since a huffmanDecoder can be empty or be composed of a degenerate tree + // with single element, huffSym must error on these two edge cases. In both + // cases, the chunks slice will be 0 for the invalid sequence, leading it + // satisfy the n == 0 check below. + n := uint(f.hd.maxRead) + // Optimization. Compiler isn't smart enough to keep f.b,f.nb in registers, + // but is smart enough to keep local variables in registers, so use nb and b, + // inline call to moreBits and reassign b,nb back to f on return. + for { + for fnb < n { + c, err := fr.ReadByte() + if err != nil { + f.b, f.nb = fb, fnb + f.err = noEOF(err) + return + } + f.roffset++ + fb |= uint32(c) << (fnb & regSizeMaskUint32) + fnb += 8 + } + chunk := f.hd.chunks[fb&(huffmanNumChunks-1)] + n = uint(chunk & huffmanCountMask) + if n > huffmanChunkBits { + chunk = f.hd.links[chunk>>huffmanValueShift][(fb>>huffmanChunkBits)&f.hd.linkMask] + n = uint(chunk & huffmanCountMask) + } + if n <= fnb { + if n == 0 { + f.b, f.nb = fb, fnb + if debugDecode { + fmt.Println("huffsym: n==0") + } + f.err = CorruptInputError(f.roffset) + return + } + fb = fb >> (n & regSizeMaskUint32) + fnb = fnb - n + dist = uint32(chunk >> huffmanValueShift) + break + } + } + } + + switch { + case dist < 4: + dist++ + case dist < maxNumDist: + nb := uint(dist-2) >> 1 + // have 1 bit in bottom of dist, need nb more. + extra := (dist & 1) << (nb & regSizeMaskUint32) + for fnb < nb { + c, err := fr.ReadByte() + if err != nil { + f.b, f.nb = fb, fnb + if debugDecode { + fmt.Println("morebits f.nb>= nb & regSizeMaskUint32 + fnb -= nb + dist = 1<<((nb+1)®SizeMaskUint32) + 1 + extra + // slower: dist = bitMask32[nb+1] + 2 + extra + default: + f.b, f.nb = fb, fnb + if debugDecode { + fmt.Println("dist too big:", dist, maxNumDist) + } + f.err = CorruptInputError(f.roffset) + return + } + + // No check on length; encoding can be prescient. + if dist > uint32(dict.histSize()) { + f.b, f.nb = fb, fnb + if debugDecode { + fmt.Println("dist > dict.histSize():", dist, dict.histSize()) + } + f.err = CorruptInputError(f.roffset) + return + } + + f.copyLen, f.copyDist = length, int(dist) + goto copyHistory + } + +copyHistory: + // Perform a backwards copy according to RFC section 3.2.3. + { + cnt := dict.tryWriteCopy(f.copyDist, f.copyLen) + if cnt == 0 { + cnt = dict.writeCopy(f.copyDist, f.copyLen) + } + f.copyLen -= cnt + + if dict.availWrite() == 0 || f.copyLen > 0 { + f.toRead = dict.readFlush() + f.step = huffmanBufioReader // We need to continue this work + f.stepState = stateDict + f.b, f.nb = fb, fnb + return + } + goto readLiteral + } + // Not reached +} + +// Decode a single Huffman block from f. +// hl and hd are the Huffman states for the lit/length values +// and the distance values, respectively. If hd == nil, using the +// fixed distance encoding associated with fixed Huffman blocks. +func (f *decompressor) huffmanStringsReader() { + const ( + stateInit = iota // Zero value must be stateInit + stateDict + ) + fr := f.r.(*strings.Reader) + + // Optimization. Compiler isn't smart enough to keep f.b,f.nb in registers, + // but is smart enough to keep local variables in registers, so use nb and b, + // inline call to moreBits and reassign b,nb back to f on return. + fnb, fb, dict := f.nb, f.b, &f.dict + + switch f.stepState { + case stateInit: + goto readLiteral + case stateDict: + goto copyHistory + } + +readLiteral: + // Read literal and/or (length, distance) according to RFC section 3.2.3. + { + var v int + { + // Inlined v, err := f.huffSym(f.hl) + // Since a huffmanDecoder can be empty or be composed of a degenerate tree + // with single element, huffSym must error on these two edge cases. In both + // cases, the chunks slice will be 0 for the invalid sequence, leading it + // satisfy the n == 0 check below. + n := uint(f.hl.maxRead) + for { + for fnb < n { + c, err := fr.ReadByte() + if err != nil { + f.b, f.nb = fb, fnb + f.err = noEOF(err) + return + } + f.roffset++ + fb |= uint32(c) << (fnb & regSizeMaskUint32) + fnb += 8 + } + chunk := f.hl.chunks[fb&(huffmanNumChunks-1)] + n = uint(chunk & huffmanCountMask) + if n > huffmanChunkBits { + chunk = f.hl.links[chunk>>huffmanValueShift][(fb>>huffmanChunkBits)&f.hl.linkMask] + n = uint(chunk & huffmanCountMask) + } + if n <= fnb { + if n == 0 { + f.b, f.nb = fb, fnb + if debugDecode { + fmt.Println("huffsym: n==0") + } + f.err = CorruptInputError(f.roffset) + return + } + fb = fb >> (n & regSizeMaskUint32) + fnb = fnb - n + v = int(chunk >> huffmanValueShift) + break + } + } + } + + var length int + switch { + case v < 256: + dict.writeByte(byte(v)) + if dict.availWrite() == 0 { + f.toRead = dict.readFlush() + f.step = huffmanStringsReader + f.stepState = stateInit + f.b, f.nb = fb, fnb + return + } + goto readLiteral + case v == 256: + f.b, f.nb = fb, fnb + f.finishBlock() + return + // otherwise, reference to older data + case v < 265: + length = v - (257 - 3) + case v < maxNumLit: + val := decCodeToLen[(v - 257)] + length = int(val.length) + 3 + n := uint(val.extra) + for fnb < n { + c, err := fr.ReadByte() + if err != nil { + f.b, f.nb = fb, fnb + if debugDecode { + fmt.Println("morebits n>0:", err) + } + f.err = err + return + } + f.roffset++ + fb |= uint32(c) << (fnb & regSizeMaskUint32) + fnb += 8 + } + length += int(fb & bitMask32[n]) + fb >>= n & regSizeMaskUint32 + fnb -= n + default: + if debugDecode { + fmt.Println(v, ">= maxNumLit") + } + f.err = CorruptInputError(f.roffset) + f.b, f.nb = fb, fnb + return + } + + var dist uint32 + if f.hd == nil { + for fnb < 5 { + c, err := fr.ReadByte() + if err != nil { + f.b, f.nb = fb, fnb + if debugDecode { + fmt.Println("morebits f.nb<5:", err) + } + f.err = err + return + } + f.roffset++ + fb |= uint32(c) << (fnb & regSizeMaskUint32) + fnb += 8 + } + dist = uint32(bits.Reverse8(uint8(fb & 0x1F << 3))) + fb >>= 5 + fnb -= 5 + } else { + // Since a huffmanDecoder can be empty or be composed of a degenerate tree + // with single element, huffSym must error on these two edge cases. In both + // cases, the chunks slice will be 0 for the invalid sequence, leading it + // satisfy the n == 0 check below. + n := uint(f.hd.maxRead) + // Optimization. Compiler isn't smart enough to keep f.b,f.nb in registers, + // but is smart enough to keep local variables in registers, so use nb and b, + // inline call to moreBits and reassign b,nb back to f on return. + for { + for fnb < n { + c, err := fr.ReadByte() + if err != nil { + f.b, f.nb = fb, fnb + f.err = noEOF(err) + return + } + f.roffset++ + fb |= uint32(c) << (fnb & regSizeMaskUint32) + fnb += 8 + } + chunk := f.hd.chunks[fb&(huffmanNumChunks-1)] + n = uint(chunk & huffmanCountMask) + if n > huffmanChunkBits { + chunk = f.hd.links[chunk>>huffmanValueShift][(fb>>huffmanChunkBits)&f.hd.linkMask] + n = uint(chunk & huffmanCountMask) + } + if n <= fnb { + if n == 0 { + f.b, f.nb = fb, fnb + if debugDecode { + fmt.Println("huffsym: n==0") + } + f.err = CorruptInputError(f.roffset) + return + } + fb = fb >> (n & regSizeMaskUint32) + fnb = fnb - n + dist = uint32(chunk >> huffmanValueShift) + break + } + } + } + + switch { + case dist < 4: + dist++ + case dist < maxNumDist: + nb := uint(dist-2) >> 1 + // have 1 bit in bottom of dist, need nb more. + extra := (dist & 1) << (nb & regSizeMaskUint32) + for fnb < nb { + c, err := fr.ReadByte() + if err != nil { + f.b, f.nb = fb, fnb + if debugDecode { + fmt.Println("morebits f.nb>= nb & regSizeMaskUint32 + fnb -= nb + dist = 1<<((nb+1)®SizeMaskUint32) + 1 + extra + // slower: dist = bitMask32[nb+1] + 2 + extra + default: + f.b, f.nb = fb, fnb + if debugDecode { + fmt.Println("dist too big:", dist, maxNumDist) + } + f.err = CorruptInputError(f.roffset) + return + } + + // No check on length; encoding can be prescient. + if dist > uint32(dict.histSize()) { + f.b, f.nb = fb, fnb + if debugDecode { + fmt.Println("dist > dict.histSize():", dist, dict.histSize()) + } + f.err = CorruptInputError(f.roffset) + return + } + + f.copyLen, f.copyDist = length, int(dist) + goto copyHistory + } + +copyHistory: + // Perform a backwards copy according to RFC section 3.2.3. + { + cnt := dict.tryWriteCopy(f.copyDist, f.copyLen) + if cnt == 0 { + cnt = dict.writeCopy(f.copyDist, f.copyLen) + } + f.copyLen -= cnt + + if dict.availWrite() == 0 || f.copyLen > 0 { + f.toRead = dict.readFlush() + f.step = huffmanStringsReader // We need to continue this work + f.stepState = stateDict + f.b, f.nb = fb, fnb + return + } + goto readLiteral + } + // Not reached +} + +// Decode a single Huffman block from f. +// hl and hd are the Huffman states for the lit/length values +// and the distance values, respectively. If hd == nil, using the +// fixed distance encoding associated with fixed Huffman blocks. +func (f *decompressor) huffmanGenericReader() { + const ( + stateInit = iota // Zero value must be stateInit + stateDict + ) + fr := f.r.(Reader) + + // Optimization. Compiler isn't smart enough to keep f.b,f.nb in registers, + // but is smart enough to keep local variables in registers, so use nb and b, + // inline call to moreBits and reassign b,nb back to f on return. + fnb, fb, dict := f.nb, f.b, &f.dict + + switch f.stepState { + case stateInit: + goto readLiteral + case stateDict: + goto copyHistory + } + +readLiteral: + // Read literal and/or (length, distance) according to RFC section 3.2.3. + { + var v int + { + // Inlined v, err := f.huffSym(f.hl) + // Since a huffmanDecoder can be empty or be composed of a degenerate tree + // with single element, huffSym must error on these two edge cases. In both + // cases, the chunks slice will be 0 for the invalid sequence, leading it + // satisfy the n == 0 check below. + n := uint(f.hl.maxRead) + for { + for fnb < n { + c, err := fr.ReadByte() + if err != nil { + f.b, f.nb = fb, fnb + f.err = noEOF(err) + return + } + f.roffset++ + fb |= uint32(c) << (fnb & regSizeMaskUint32) + fnb += 8 + } + chunk := f.hl.chunks[fb&(huffmanNumChunks-1)] + n = uint(chunk & huffmanCountMask) + if n > huffmanChunkBits { + chunk = f.hl.links[chunk>>huffmanValueShift][(fb>>huffmanChunkBits)&f.hl.linkMask] + n = uint(chunk & huffmanCountMask) + } + if n <= fnb { + if n == 0 { + f.b, f.nb = fb, fnb + if debugDecode { + fmt.Println("huffsym: n==0") + } + f.err = CorruptInputError(f.roffset) + return + } + fb = fb >> (n & regSizeMaskUint32) + fnb = fnb - n + v = int(chunk >> huffmanValueShift) + break + } + } + } + + var length int + switch { + case v < 256: + dict.writeByte(byte(v)) + if dict.availWrite() == 0 { + f.toRead = dict.readFlush() + f.step = huffmanGenericReader + f.stepState = stateInit + f.b, f.nb = fb, fnb + return + } + goto readLiteral + case v == 256: + f.b, f.nb = fb, fnb + f.finishBlock() + return + // otherwise, reference to older data + case v < 265: + length = v - (257 - 3) + case v < maxNumLit: + val := decCodeToLen[(v - 257)] + length = int(val.length) + 3 + n := uint(val.extra) + for fnb < n { + c, err := fr.ReadByte() + if err != nil { + f.b, f.nb = fb, fnb + if debugDecode { + fmt.Println("morebits n>0:", err) + } + f.err = err + return + } + f.roffset++ + fb |= uint32(c) << (fnb & regSizeMaskUint32) + fnb += 8 + } + length += int(fb & bitMask32[n]) + fb >>= n & regSizeMaskUint32 + fnb -= n + default: + if debugDecode { + fmt.Println(v, ">= maxNumLit") + } + f.err = CorruptInputError(f.roffset) + f.b, f.nb = fb, fnb + return + } + + var dist uint32 + if f.hd == nil { + for fnb < 5 { + c, err := fr.ReadByte() + if err != nil { + f.b, f.nb = fb, fnb + if debugDecode { + fmt.Println("morebits f.nb<5:", err) + } + f.err = err + return + } + f.roffset++ + fb |= uint32(c) << (fnb & regSizeMaskUint32) + fnb += 8 + } + dist = uint32(bits.Reverse8(uint8(fb & 0x1F << 3))) + fb >>= 5 + fnb -= 5 + } else { + // Since a huffmanDecoder can be empty or be composed of a degenerate tree + // with single element, huffSym must error on these two edge cases. In both + // cases, the chunks slice will be 0 for the invalid sequence, leading it + // satisfy the n == 0 check below. + n := uint(f.hd.maxRead) + // Optimization. Compiler isn't smart enough to keep f.b,f.nb in registers, + // but is smart enough to keep local variables in registers, so use nb and b, + // inline call to moreBits and reassign b,nb back to f on return. + for { + for fnb < n { + c, err := fr.ReadByte() + if err != nil { + f.b, f.nb = fb, fnb + f.err = noEOF(err) + return + } + f.roffset++ + fb |= uint32(c) << (fnb & regSizeMaskUint32) + fnb += 8 + } + chunk := f.hd.chunks[fb&(huffmanNumChunks-1)] + n = uint(chunk & huffmanCountMask) + if n > huffmanChunkBits { + chunk = f.hd.links[chunk>>huffmanValueShift][(fb>>huffmanChunkBits)&f.hd.linkMask] + n = uint(chunk & huffmanCountMask) + } + if n <= fnb { + if n == 0 { + f.b, f.nb = fb, fnb + if debugDecode { + fmt.Println("huffsym: n==0") + } + f.err = CorruptInputError(f.roffset) + return + } + fb = fb >> (n & regSizeMaskUint32) + fnb = fnb - n + dist = uint32(chunk >> huffmanValueShift) + break + } + } + } + + switch { + case dist < 4: + dist++ + case dist < maxNumDist: + nb := uint(dist-2) >> 1 + // have 1 bit in bottom of dist, need nb more. + extra := (dist & 1) << (nb & regSizeMaskUint32) + for fnb < nb { + c, err := fr.ReadByte() + if err != nil { + f.b, f.nb = fb, fnb + if debugDecode { + fmt.Println("morebits f.nb>= nb & regSizeMaskUint32 + fnb -= nb + dist = 1<<((nb+1)®SizeMaskUint32) + 1 + extra + // slower: dist = bitMask32[nb+1] + 2 + extra + default: + f.b, f.nb = fb, fnb + if debugDecode { + fmt.Println("dist too big:", dist, maxNumDist) + } + f.err = CorruptInputError(f.roffset) + return + } + + // No check on length; encoding can be prescient. + if dist > uint32(dict.histSize()) { + f.b, f.nb = fb, fnb + if debugDecode { + fmt.Println("dist > dict.histSize():", dist, dict.histSize()) + } + f.err = CorruptInputError(f.roffset) + return + } + + f.copyLen, f.copyDist = length, int(dist) + goto copyHistory + } + +copyHistory: + // Perform a backwards copy according to RFC section 3.2.3. + { + cnt := dict.tryWriteCopy(f.copyDist, f.copyLen) + if cnt == 0 { + cnt = dict.writeCopy(f.copyDist, f.copyLen) + } + f.copyLen -= cnt + + if dict.availWrite() == 0 || f.copyLen > 0 { + f.toRead = dict.readFlush() + f.step = huffmanGenericReader // We need to continue this work + f.stepState = stateDict + f.b, f.nb = fb, fnb + return + } + goto readLiteral + } + // Not reached +} + +func (f *decompressor) huffmanBlockDecoder() { + switch f.r.(type) { + case *bytes.Buffer: + f.huffmanBytesBuffer() + case *bytes.Reader: + f.huffmanBytesReader() + case *bufio.Reader: + f.huffmanBufioReader() + case *strings.Reader: + f.huffmanStringsReader() + case Reader: + f.huffmanGenericReader() + default: + f.huffmanGenericReader() + } +} diff --git a/vendor/github.com/klauspost/compress/flate/level1.go b/vendor/github.com/klauspost/compress/flate/level1.go new file mode 100644 index 000000000..c3581a342 --- /dev/null +++ b/vendor/github.com/klauspost/compress/flate/level1.go @@ -0,0 +1,215 @@ +package flate + +import ( + "fmt" + + "github.com/klauspost/compress/internal/le" +) + +// fastGen maintains the table for matches, +// and the previous byte block for level 2. +// This is the generic implementation. +type fastEncL1 struct { + fastGen + table [tableSize]tableEntry +} + +// EncodeL1 uses a similar algorithm to level 1 +func (e *fastEncL1) Encode(dst *tokens, src []byte) { + const ( + inputMargin = 12 - 1 + minNonLiteralBlockSize = 1 + 1 + inputMargin + hashBytes = 5 + ) + if debugDeflate && e.cur < 0 { + panic(fmt.Sprint("e.cur < 0: ", e.cur)) + } + + // Protect against e.cur wraparound. + for e.cur >= bufferReset { + if len(e.hist) == 0 { + for i := range e.table[:] { + e.table[i] = tableEntry{} + } + e.cur = maxMatchOffset + break + } + // Shift down everything in the table that isn't already too far away. + minOff := e.cur + int32(len(e.hist)) - maxMatchOffset + for i := range e.table[:] { + v := e.table[i].offset + if v <= minOff { + v = 0 + } else { + v = v - e.cur + maxMatchOffset + } + e.table[i].offset = v + } + e.cur = maxMatchOffset + } + + s := e.addBlock(src) + + // This check isn't in the Snappy implementation, but there, the caller + // instead of the callee handles this case. + if len(src) < minNonLiteralBlockSize { + // We do not fill the token table. + // This will be picked up by caller. + dst.n = uint16(len(src)) + return + } + + // Override src + src = e.hist + nextEmit := s + + // sLimit is when to stop looking for offset/length copies. The inputMargin + // lets us use a fast path for emitLiteral in the main loop, while we are + // looking for copies. + sLimit := int32(len(src) - inputMargin) + + // nextEmit is where in src the next emitLiteral should start from. + cv := load6432(src, s) + + for { + const skipLog = 5 + const doEvery = 2 + + nextS := s + var candidate tableEntry + var t int32 + for { + nextHash := hashLen(cv, tableBits, hashBytes) + candidate = e.table[nextHash] + nextS = s + doEvery + (s-nextEmit)>>skipLog + if nextS > sLimit { + goto emitRemainder + } + + now := load6432(src, nextS) + e.table[nextHash] = tableEntry{offset: s + e.cur} + nextHash = hashLen(now, tableBits, hashBytes) + t = candidate.offset - e.cur + if s-t < maxMatchOffset && uint32(cv) == load3232(src, t) { + e.table[nextHash] = tableEntry{offset: nextS + e.cur} + break + } + + // Do one right away... + cv = now + s = nextS + nextS++ + candidate = e.table[nextHash] + now >>= 8 + e.table[nextHash] = tableEntry{offset: s + e.cur} + + t = candidate.offset - e.cur + if s-t < maxMatchOffset && uint32(cv) == load3232(src, t) { + e.table[nextHash] = tableEntry{offset: nextS + e.cur} + break + } + cv = now + s = nextS + } + + // A 4-byte match has been found. We'll later see if more than 4 bytes + // match. But, prior to the match, src[nextEmit:s] are unmatched. Emit + // them as literal bytes. + for { + // Invariant: we have a 4-byte match at s, and no need to emit any + // literal bytes prior to s. + + // Extend the 4-byte match as long as possible. + l := e.matchlenLong(int(s+4), int(t+4), src) + 4 + + // Extend backwards + for t > 0 && s > nextEmit && le.Load8(src, t-1) == le.Load8(src, s-1) { + s-- + t-- + l++ + } + if nextEmit < s { + if false { + emitLiteral(dst, src[nextEmit:s]) + } else { + for _, v := range src[nextEmit:s] { + dst.tokens[dst.n] = token(v) + dst.litHist[v]++ + dst.n++ + } + } + } + + // Save the match found + if false { + dst.AddMatchLong(l, uint32(s-t-baseMatchOffset)) + } else { + // Inlined... + xoffset := uint32(s - t - baseMatchOffset) + xlength := l + oc := offsetCode(xoffset) + xoffset |= oc << 16 + for xlength > 0 { + xl := xlength + if xl > 258 { + if xl > 258+baseMatchLength { + xl = 258 + } else { + xl = 258 - baseMatchLength + } + } + xlength -= xl + xl -= baseMatchLength + dst.extraHist[lengthCodes1[uint8(xl)]]++ + dst.offHist[oc]++ + dst.tokens[dst.n] = token(matchType | uint32(xl)<= s { + s = nextS + 1 + } + if s >= sLimit { + // Index first pair after match end. + if int(s+l+8) < len(src) { + cv := load6432(src, s) + e.table[hashLen(cv, tableBits, hashBytes)] = tableEntry{offset: s + e.cur} + } + goto emitRemainder + } + + // We could immediately start working at s now, but to improve + // compression we first update the hash table at s-2 and at s. If + // another emitCopy is not our next move, also calculate nextHash + // at s+1. At least on GOARCH=amd64, these three hash calculations + // are faster as one load64 call (with some shifts) instead of + // three load32 calls. + x := load6432(src, s-2) + o := e.cur + s - 2 + prevHash := hashLen(x, tableBits, hashBytes) + e.table[prevHash] = tableEntry{offset: o} + x >>= 16 + currHash := hashLen(x, tableBits, hashBytes) + candidate = e.table[currHash] + e.table[currHash] = tableEntry{offset: o + 2} + + t = candidate.offset - e.cur + if s-t > maxMatchOffset || uint32(x) != load3232(src, t) { + cv = x >> 8 + s++ + break + } + } + } + +emitRemainder: + if int(nextEmit) < len(src) { + // If nothing was added, don't encode literals. + if dst.n == 0 { + return + } + emitLiteral(dst, src[nextEmit:]) + } +} diff --git a/vendor/github.com/klauspost/compress/flate/level2.go b/vendor/github.com/klauspost/compress/flate/level2.go new file mode 100644 index 000000000..c8d047f2d --- /dev/null +++ b/vendor/github.com/klauspost/compress/flate/level2.go @@ -0,0 +1,214 @@ +package flate + +import "fmt" + +// fastGen maintains the table for matches, +// and the previous byte block for level 2. +// This is the generic implementation. +type fastEncL2 struct { + fastGen + table [bTableSize]tableEntry +} + +// EncodeL2 uses a similar algorithm to level 1, but is capable +// of matching across blocks giving better compression at a small slowdown. +func (e *fastEncL2) Encode(dst *tokens, src []byte) { + const ( + inputMargin = 12 - 1 + minNonLiteralBlockSize = 1 + 1 + inputMargin + hashBytes = 5 + ) + + if debugDeflate && e.cur < 0 { + panic(fmt.Sprint("e.cur < 0: ", e.cur)) + } + + // Protect against e.cur wraparound. + for e.cur >= bufferReset { + if len(e.hist) == 0 { + for i := range e.table[:] { + e.table[i] = tableEntry{} + } + e.cur = maxMatchOffset + break + } + // Shift down everything in the table that isn't already too far away. + minOff := e.cur + int32(len(e.hist)) - maxMatchOffset + for i := range e.table[:] { + v := e.table[i].offset + if v <= minOff { + v = 0 + } else { + v = v - e.cur + maxMatchOffset + } + e.table[i].offset = v + } + e.cur = maxMatchOffset + } + + s := e.addBlock(src) + + // This check isn't in the Snappy implementation, but there, the caller + // instead of the callee handles this case. + if len(src) < minNonLiteralBlockSize { + // We do not fill the token table. + // This will be picked up by caller. + dst.n = uint16(len(src)) + return + } + + // Override src + src = e.hist + nextEmit := s + + // sLimit is when to stop looking for offset/length copies. The inputMargin + // lets us use a fast path for emitLiteral in the main loop, while we are + // looking for copies. + sLimit := int32(len(src) - inputMargin) + + // nextEmit is where in src the next emitLiteral should start from. + cv := load6432(src, s) + for { + // When should we start skipping if we haven't found matches in a long while. + const skipLog = 5 + const doEvery = 2 + + nextS := s + var candidate tableEntry + for { + nextHash := hashLen(cv, bTableBits, hashBytes) + s = nextS + nextS = s + doEvery + (s-nextEmit)>>skipLog + if nextS > sLimit { + goto emitRemainder + } + candidate = e.table[nextHash] + now := load6432(src, nextS) + e.table[nextHash] = tableEntry{offset: s + e.cur} + nextHash = hashLen(now, bTableBits, hashBytes) + + offset := s - (candidate.offset - e.cur) + if offset < maxMatchOffset && uint32(cv) == load3232(src, candidate.offset-e.cur) { + e.table[nextHash] = tableEntry{offset: nextS + e.cur} + break + } + + // Do one right away... + cv = now + s = nextS + nextS++ + candidate = e.table[nextHash] + now >>= 8 + e.table[nextHash] = tableEntry{offset: s + e.cur} + + offset = s - (candidate.offset - e.cur) + if offset < maxMatchOffset && uint32(cv) == load3232(src, candidate.offset-e.cur) { + break + } + cv = now + } + + // A 4-byte match has been found. We'll later see if more than 4 bytes + // match. But, prior to the match, src[nextEmit:s] are unmatched. Emit + // them as literal bytes. + + // Call emitCopy, and then see if another emitCopy could be our next + // move. Repeat until we find no match for the input immediately after + // what was consumed by the last emitCopy call. + // + // If we exit this loop normally then we need to call emitLiteral next, + // though we don't yet know how big the literal will be. We handle that + // by proceeding to the next iteration of the main loop. We also can + // exit this loop via goto if we get close to exhausting the input. + for { + // Invariant: we have a 4-byte match at s, and no need to emit any + // literal bytes prior to s. + + // Extend the 4-byte match as long as possible. + t := candidate.offset - e.cur + l := e.matchlenLong(int(s+4), int(t+4), src) + 4 + + // Extend backwards + for t > 0 && s > nextEmit && src[t-1] == src[s-1] { + s-- + t-- + l++ + } + if nextEmit < s { + if false { + emitLiteral(dst, src[nextEmit:s]) + } else { + for _, v := range src[nextEmit:s] { + dst.tokens[dst.n] = token(v) + dst.litHist[v]++ + dst.n++ + } + } + } + + dst.AddMatchLong(l, uint32(s-t-baseMatchOffset)) + s += l + nextEmit = s + if nextS >= s { + s = nextS + 1 + } + + if s >= sLimit { + // Index first pair after match end. + if int(s+l+8) < len(src) { + cv := load6432(src, s) + e.table[hashLen(cv, bTableBits, hashBytes)] = tableEntry{offset: s + e.cur} + } + goto emitRemainder + } + + // Store every second hash in-between, but offset by 1. + for i := s - l + 2; i < s-5; i += 7 { + x := load6432(src, i) + nextHash := hashLen(x, bTableBits, hashBytes) + e.table[nextHash] = tableEntry{offset: e.cur + i} + // Skip one + x >>= 16 + nextHash = hashLen(x, bTableBits, hashBytes) + e.table[nextHash] = tableEntry{offset: e.cur + i + 2} + // Skip one + x >>= 16 + nextHash = hashLen(x, bTableBits, hashBytes) + e.table[nextHash] = tableEntry{offset: e.cur + i + 4} + } + + // We could immediately start working at s now, but to improve + // compression we first update the hash table at s-2 to s. If + // another emitCopy is not our next move, also calculate nextHash + // at s+1. At least on GOARCH=amd64, these three hash calculations + // are faster as one load64 call (with some shifts) instead of + // three load32 calls. + x := load6432(src, s-2) + o := e.cur + s - 2 + prevHash := hashLen(x, bTableBits, hashBytes) + prevHash2 := hashLen(x>>8, bTableBits, hashBytes) + e.table[prevHash] = tableEntry{offset: o} + e.table[prevHash2] = tableEntry{offset: o + 1} + currHash := hashLen(x>>16, bTableBits, hashBytes) + candidate = e.table[currHash] + e.table[currHash] = tableEntry{offset: o + 2} + + offset := s - (candidate.offset - e.cur) + if offset > maxMatchOffset || uint32(x>>16) != load3232(src, candidate.offset-e.cur) { + cv = x >> 24 + s++ + break + } + } + } + +emitRemainder: + if int(nextEmit) < len(src) { + // If nothing was added, don't encode literals. + if dst.n == 0 { + return + } + + emitLiteral(dst, src[nextEmit:]) + } +} diff --git a/vendor/github.com/klauspost/compress/flate/level3.go b/vendor/github.com/klauspost/compress/flate/level3.go new file mode 100644 index 000000000..33f9fb152 --- /dev/null +++ b/vendor/github.com/klauspost/compress/flate/level3.go @@ -0,0 +1,241 @@ +package flate + +import "fmt" + +// fastEncL3 +type fastEncL3 struct { + fastGen + table [1 << 16]tableEntryPrev +} + +// Encode uses a similar algorithm to level 2, will check up to two candidates. +func (e *fastEncL3) Encode(dst *tokens, src []byte) { + const ( + inputMargin = 12 - 1 + minNonLiteralBlockSize = 1 + 1 + inputMargin + tableBits = 16 + tableSize = 1 << tableBits + hashBytes = 5 + ) + + if debugDeflate && e.cur < 0 { + panic(fmt.Sprint("e.cur < 0: ", e.cur)) + } + + // Protect against e.cur wraparound. + for e.cur >= bufferReset { + if len(e.hist) == 0 { + for i := range e.table[:] { + e.table[i] = tableEntryPrev{} + } + e.cur = maxMatchOffset + break + } + // Shift down everything in the table that isn't already too far away. + minOff := e.cur + int32(len(e.hist)) - maxMatchOffset + for i := range e.table[:] { + v := e.table[i] + if v.Cur.offset <= minOff { + v.Cur.offset = 0 + } else { + v.Cur.offset = v.Cur.offset - e.cur + maxMatchOffset + } + if v.Prev.offset <= minOff { + v.Prev.offset = 0 + } else { + v.Prev.offset = v.Prev.offset - e.cur + maxMatchOffset + } + e.table[i] = v + } + e.cur = maxMatchOffset + } + + s := e.addBlock(src) + + // Skip if too small. + if len(src) < minNonLiteralBlockSize { + // We do not fill the token table. + // This will be picked up by caller. + dst.n = uint16(len(src)) + return + } + + // Override src + src = e.hist + nextEmit := s + + // sLimit is when to stop looking for offset/length copies. The inputMargin + // lets us use a fast path for emitLiteral in the main loop, while we are + // looking for copies. + sLimit := int32(len(src) - inputMargin) + + // nextEmit is where in src the next emitLiteral should start from. + cv := load6432(src, s) + for { + const skipLog = 7 + nextS := s + var candidate tableEntry + for { + nextHash := hashLen(cv, tableBits, hashBytes) + s = nextS + nextS = s + 1 + (s-nextEmit)>>skipLog + if nextS > sLimit { + goto emitRemainder + } + candidates := e.table[nextHash] + now := load6432(src, nextS) + + // Safe offset distance until s + 4... + minOffset := e.cur + s - (maxMatchOffset - 4) + e.table[nextHash] = tableEntryPrev{Prev: candidates.Cur, Cur: tableEntry{offset: s + e.cur}} + + // Check both candidates + candidate = candidates.Cur + if candidate.offset < minOffset { + cv = now + // Previous will also be invalid, we have nothing. + continue + } + + if uint32(cv) == load3232(src, candidate.offset-e.cur) { + if candidates.Prev.offset < minOffset || uint32(cv) != load3232(src, candidates.Prev.offset-e.cur) { + break + } + // Both match and are valid, pick longest. + offset := s - (candidate.offset - e.cur) + o2 := s - (candidates.Prev.offset - e.cur) + l1, l2 := matchLen(src[s+4:], src[s-offset+4:]), matchLen(src[s+4:], src[s-o2+4:]) + if l2 > l1 { + candidate = candidates.Prev + } + break + } else { + // We only check if value mismatches. + // Offset will always be invalid in other cases. + candidate = candidates.Prev + if candidate.offset > minOffset && uint32(cv) == load3232(src, candidate.offset-e.cur) { + break + } + } + cv = now + } + + // Call emitCopy, and then see if another emitCopy could be our next + // move. Repeat until we find no match for the input immediately after + // what was consumed by the last emitCopy call. + // + // If we exit this loop normally then we need to call emitLiteral next, + // though we don't yet know how big the literal will be. We handle that + // by proceeding to the next iteration of the main loop. We also can + // exit this loop via goto if we get close to exhausting the input. + for { + // Invariant: we have a 4-byte match at s, and no need to emit any + // literal bytes prior to s. + + // Extend the 4-byte match as long as possible. + // + t := candidate.offset - e.cur + l := e.matchlenLong(int(s+4), int(t+4), src) + 4 + + // Extend backwards + for t > 0 && s > nextEmit && src[t-1] == src[s-1] { + s-- + t-- + l++ + } + if nextEmit < s { + if false { + emitLiteral(dst, src[nextEmit:s]) + } else { + for _, v := range src[nextEmit:s] { + dst.tokens[dst.n] = token(v) + dst.litHist[v]++ + dst.n++ + } + } + } + + dst.AddMatchLong(l, uint32(s-t-baseMatchOffset)) + s += l + nextEmit = s + if nextS >= s { + s = nextS + 1 + } + + if s >= sLimit { + t += l + // Index first pair after match end. + if int(t+8) < len(src) && t > 0 { + cv = load6432(src, t) + nextHash := hashLen(cv, tableBits, hashBytes) + e.table[nextHash] = tableEntryPrev{ + Prev: e.table[nextHash].Cur, + Cur: tableEntry{offset: e.cur + t}, + } + } + goto emitRemainder + } + + // Store every 5th hash in-between. + for i := s - l + 2; i < s-5; i += 6 { + nextHash := hashLen(load6432(src, i), tableBits, hashBytes) + e.table[nextHash] = tableEntryPrev{ + Prev: e.table[nextHash].Cur, + Cur: tableEntry{offset: e.cur + i}} + } + // We could immediately start working at s now, but to improve + // compression we first update the hash table at s-2 to s. + x := load6432(src, s-2) + prevHash := hashLen(x, tableBits, hashBytes) + + e.table[prevHash] = tableEntryPrev{ + Prev: e.table[prevHash].Cur, + Cur: tableEntry{offset: e.cur + s - 2}, + } + x >>= 8 + prevHash = hashLen(x, tableBits, hashBytes) + + e.table[prevHash] = tableEntryPrev{ + Prev: e.table[prevHash].Cur, + Cur: tableEntry{offset: e.cur + s - 1}, + } + x >>= 8 + currHash := hashLen(x, tableBits, hashBytes) + candidates := e.table[currHash] + cv = x + e.table[currHash] = tableEntryPrev{ + Prev: candidates.Cur, + Cur: tableEntry{offset: s + e.cur}, + } + + // Check both candidates + candidate = candidates.Cur + minOffset := e.cur + s - (maxMatchOffset - 4) + + if candidate.offset > minOffset { + if uint32(cv) == load3232(src, candidate.offset-e.cur) { + // Found a match... + continue + } + candidate = candidates.Prev + if candidate.offset > minOffset && uint32(cv) == load3232(src, candidate.offset-e.cur) { + // Match at prev... + continue + } + } + cv = x >> 8 + s++ + break + } + } + +emitRemainder: + if int(nextEmit) < len(src) { + // If nothing was added, don't encode literals. + if dst.n == 0 { + return + } + + emitLiteral(dst, src[nextEmit:]) + } +} diff --git a/vendor/github.com/klauspost/compress/flate/level4.go b/vendor/github.com/klauspost/compress/flate/level4.go new file mode 100644 index 000000000..88509e197 --- /dev/null +++ b/vendor/github.com/klauspost/compress/flate/level4.go @@ -0,0 +1,221 @@ +package flate + +import "fmt" + +type fastEncL4 struct { + fastGen + table [tableSize]tableEntry + bTable [tableSize]tableEntry +} + +func (e *fastEncL4) Encode(dst *tokens, src []byte) { + const ( + inputMargin = 12 - 1 + minNonLiteralBlockSize = 1 + 1 + inputMargin + hashShortBytes = 4 + ) + if debugDeflate && e.cur < 0 { + panic(fmt.Sprint("e.cur < 0: ", e.cur)) + } + // Protect against e.cur wraparound. + for e.cur >= bufferReset { + if len(e.hist) == 0 { + for i := range e.table[:] { + e.table[i] = tableEntry{} + } + for i := range e.bTable[:] { + e.bTable[i] = tableEntry{} + } + e.cur = maxMatchOffset + break + } + // Shift down everything in the table that isn't already too far away. + minOff := e.cur + int32(len(e.hist)) - maxMatchOffset + for i := range e.table[:] { + v := e.table[i].offset + if v <= minOff { + v = 0 + } else { + v = v - e.cur + maxMatchOffset + } + e.table[i].offset = v + } + for i := range e.bTable[:] { + v := e.bTable[i].offset + if v <= minOff { + v = 0 + } else { + v = v - e.cur + maxMatchOffset + } + e.bTable[i].offset = v + } + e.cur = maxMatchOffset + } + + s := e.addBlock(src) + + // This check isn't in the Snappy implementation, but there, the caller + // instead of the callee handles this case. + if len(src) < minNonLiteralBlockSize { + // We do not fill the token table. + // This will be picked up by caller. + dst.n = uint16(len(src)) + return + } + + // Override src + src = e.hist + nextEmit := s + + // sLimit is when to stop looking for offset/length copies. The inputMargin + // lets us use a fast path for emitLiteral in the main loop, while we are + // looking for copies. + sLimit := int32(len(src) - inputMargin) + + // nextEmit is where in src the next emitLiteral should start from. + cv := load6432(src, s) + for { + const skipLog = 6 + const doEvery = 1 + + nextS := s + var t int32 + for { + nextHashS := hashLen(cv, tableBits, hashShortBytes) + nextHashL := hash7(cv, tableBits) + + s = nextS + nextS = s + doEvery + (s-nextEmit)>>skipLog + if nextS > sLimit { + goto emitRemainder + } + // Fetch a short+long candidate + sCandidate := e.table[nextHashS] + lCandidate := e.bTable[nextHashL] + next := load6432(src, nextS) + entry := tableEntry{offset: s + e.cur} + e.table[nextHashS] = entry + e.bTable[nextHashL] = entry + + t = lCandidate.offset - e.cur + if s-t < maxMatchOffset && uint32(cv) == load3232(src, t) { + // We got a long match. Use that. + break + } + + t = sCandidate.offset - e.cur + if s-t < maxMatchOffset && uint32(cv) == load3232(src, t) { + // Found a 4 match... + lCandidate = e.bTable[hash7(next, tableBits)] + + // If the next long is a candidate, check if we should use that instead... + lOff := lCandidate.offset - e.cur + if nextS-lOff < maxMatchOffset && load3232(src, lOff) == uint32(next) { + l1, l2 := matchLen(src[s+4:], src[t+4:]), matchLen(src[nextS+4:], src[nextS-lOff+4:]) + if l2 > l1 { + s = nextS + t = lCandidate.offset - e.cur + } + } + break + } + cv = next + } + + // A 4-byte match has been found. We'll later see if more than 4 bytes + // match. But, prior to the match, src[nextEmit:s] are unmatched. Emit + // them as literal bytes. + + // Extend the 4-byte match as long as possible. + l := e.matchlenLong(int(s+4), int(t+4), src) + 4 + + // Extend backwards + for t > 0 && s > nextEmit && src[t-1] == src[s-1] { + s-- + t-- + l++ + } + if nextEmit < s { + if false { + emitLiteral(dst, src[nextEmit:s]) + } else { + for _, v := range src[nextEmit:s] { + dst.tokens[dst.n] = token(v) + dst.litHist[v]++ + dst.n++ + } + } + } + if debugDeflate { + if t >= s { + panic("s-t") + } + if (s - t) > maxMatchOffset { + panic(fmt.Sprintln("mmo", t)) + } + if l < baseMatchLength { + panic("bml") + } + } + + dst.AddMatchLong(l, uint32(s-t-baseMatchOffset)) + s += l + nextEmit = s + if nextS >= s { + s = nextS + 1 + } + + if s >= sLimit { + // Index first pair after match end. + if int(s+8) < len(src) { + cv := load6432(src, s) + e.table[hashLen(cv, tableBits, hashShortBytes)] = tableEntry{offset: s + e.cur} + e.bTable[hash7(cv, tableBits)] = tableEntry{offset: s + e.cur} + } + goto emitRemainder + } + + // Store every 3rd hash in-between + if true { + i := nextS + if i < s-1 { + cv := load6432(src, i) + t := tableEntry{offset: i + e.cur} + t2 := tableEntry{offset: t.offset + 1} + e.bTable[hash7(cv, tableBits)] = t + e.bTable[hash7(cv>>8, tableBits)] = t2 + e.table[hashLen(cv>>8, tableBits, hashShortBytes)] = t2 + + i += 3 + for ; i < s-1; i += 3 { + cv := load6432(src, i) + t := tableEntry{offset: i + e.cur} + t2 := tableEntry{offset: t.offset + 1} + e.bTable[hash7(cv, tableBits)] = t + e.bTable[hash7(cv>>8, tableBits)] = t2 + e.table[hashLen(cv>>8, tableBits, hashShortBytes)] = t2 + } + } + } + + // We could immediately start working at s now, but to improve + // compression we first update the hash table at s-1 and at s. + x := load6432(src, s-1) + o := e.cur + s - 1 + prevHashS := hashLen(x, tableBits, hashShortBytes) + prevHashL := hash7(x, tableBits) + e.table[prevHashS] = tableEntry{offset: o} + e.bTable[prevHashL] = tableEntry{offset: o} + cv = x >> 8 + } + +emitRemainder: + if int(nextEmit) < len(src) { + // If nothing was added, don't encode literals. + if dst.n == 0 { + return + } + + emitLiteral(dst, src[nextEmit:]) + } +} diff --git a/vendor/github.com/klauspost/compress/flate/level5.go b/vendor/github.com/klauspost/compress/flate/level5.go new file mode 100644 index 000000000..a22ad7d12 --- /dev/null +++ b/vendor/github.com/klauspost/compress/flate/level5.go @@ -0,0 +1,705 @@ +package flate + +import "fmt" + +type fastEncL5 struct { + fastGen + table [tableSize]tableEntry + bTable [tableSize]tableEntryPrev +} + +func (e *fastEncL5) Encode(dst *tokens, src []byte) { + const ( + inputMargin = 12 - 1 + minNonLiteralBlockSize = 1 + 1 + inputMargin + hashShortBytes = 4 + ) + if debugDeflate && e.cur < 0 { + panic(fmt.Sprint("e.cur < 0: ", e.cur)) + } + + // Protect against e.cur wraparound. + for e.cur >= bufferReset { + if len(e.hist) == 0 { + for i := range e.table[:] { + e.table[i] = tableEntry{} + } + for i := range e.bTable[:] { + e.bTable[i] = tableEntryPrev{} + } + e.cur = maxMatchOffset + break + } + // Shift down everything in the table that isn't already too far away. + minOff := e.cur + int32(len(e.hist)) - maxMatchOffset + for i := range e.table[:] { + v := e.table[i].offset + if v <= minOff { + v = 0 + } else { + v = v - e.cur + maxMatchOffset + } + e.table[i].offset = v + } + for i := range e.bTable[:] { + v := e.bTable[i] + if v.Cur.offset <= minOff { + v.Cur.offset = 0 + v.Prev.offset = 0 + } else { + v.Cur.offset = v.Cur.offset - e.cur + maxMatchOffset + if v.Prev.offset <= minOff { + v.Prev.offset = 0 + } else { + v.Prev.offset = v.Prev.offset - e.cur + maxMatchOffset + } + } + e.bTable[i] = v + } + e.cur = maxMatchOffset + } + + s := e.addBlock(src) + + // This check isn't in the Snappy implementation, but there, the caller + // instead of the callee handles this case. + if len(src) < minNonLiteralBlockSize { + // We do not fill the token table. + // This will be picked up by caller. + dst.n = uint16(len(src)) + return + } + + // Override src + src = e.hist + nextEmit := s + + // sLimit is when to stop looking for offset/length copies. The inputMargin + // lets us use a fast path for emitLiteral in the main loop, while we are + // looking for copies. + sLimit := int32(len(src) - inputMargin) + + // nextEmit is where in src the next emitLiteral should start from. + cv := load6432(src, s) + for { + const skipLog = 6 + const doEvery = 1 + + nextS := s + var l int32 + var t int32 + for { + nextHashS := hashLen(cv, tableBits, hashShortBytes) + nextHashL := hash7(cv, tableBits) + + s = nextS + nextS = s + doEvery + (s-nextEmit)>>skipLog + if nextS > sLimit { + goto emitRemainder + } + // Fetch a short+long candidate + sCandidate := e.table[nextHashS] + lCandidate := e.bTable[nextHashL] + next := load6432(src, nextS) + entry := tableEntry{offset: s + e.cur} + e.table[nextHashS] = entry + eLong := &e.bTable[nextHashL] + eLong.Cur, eLong.Prev = entry, eLong.Cur + + nextHashS = hashLen(next, tableBits, hashShortBytes) + nextHashL = hash7(next, tableBits) + + t = lCandidate.Cur.offset - e.cur + if s-t < maxMatchOffset { + if uint32(cv) == load3232(src, t) { + // Store the next match + e.table[nextHashS] = tableEntry{offset: nextS + e.cur} + eLong := &e.bTable[nextHashL] + eLong.Cur, eLong.Prev = tableEntry{offset: nextS + e.cur}, eLong.Cur + + t2 := lCandidate.Prev.offset - e.cur + if s-t2 < maxMatchOffset && uint32(cv) == load3232(src, t2) { + l = e.matchlen(int(s+4), int(t+4), src) + 4 + ml1 := e.matchlen(int(s+4), int(t2+4), src) + 4 + if ml1 > l { + t = t2 + l = ml1 + break + } + } + break + } + t = lCandidate.Prev.offset - e.cur + if s-t < maxMatchOffset && uint32(cv) == load3232(src, t) { + // Store the next match + e.table[nextHashS] = tableEntry{offset: nextS + e.cur} + eLong := &e.bTable[nextHashL] + eLong.Cur, eLong.Prev = tableEntry{offset: nextS + e.cur}, eLong.Cur + break + } + } + + t = sCandidate.offset - e.cur + if s-t < maxMatchOffset && uint32(cv) == load3232(src, t) { + // Found a 4 match... + l = e.matchlen(int(s+4), int(t+4), src) + 4 + lCandidate = e.bTable[nextHashL] + // Store the next match + + e.table[nextHashS] = tableEntry{offset: nextS + e.cur} + eLong := &e.bTable[nextHashL] + eLong.Cur, eLong.Prev = tableEntry{offset: nextS + e.cur}, eLong.Cur + + // If the next long is a candidate, use that... + t2 := lCandidate.Cur.offset - e.cur + if nextS-t2 < maxMatchOffset { + if load3232(src, t2) == uint32(next) { + ml := e.matchlen(int(nextS+4), int(t2+4), src) + 4 + if ml > l { + t = t2 + s = nextS + l = ml + break + } + } + // If the previous long is a candidate, use that... + t2 = lCandidate.Prev.offset - e.cur + if nextS-t2 < maxMatchOffset && load3232(src, t2) == uint32(next) { + ml := e.matchlen(int(nextS+4), int(t2+4), src) + 4 + if ml > l { + t = t2 + s = nextS + l = ml + break + } + } + } + break + } + cv = next + } + + // A 4-byte match has been found. We'll later see if more than 4 bytes + // match. But, prior to the match, src[nextEmit:s] are unmatched. Emit + // them as literal bytes. + + if l == 0 { + // Extend the 4-byte match as long as possible. + l = e.matchlenLong(int(s+4), int(t+4), src) + 4 + } else if l == maxMatchLength { + l += e.matchlenLong(int(s+l), int(t+l), src) + } + + // Try to locate a better match by checking the end of best match... + if sAt := s + l; l < 30 && sAt < sLimit { + // Allow some bytes at the beginning to mismatch. + // Sweet spot is 2/3 bytes depending on input. + // 3 is only a little better when it is but sometimes a lot worse. + // The skipped bytes are tested in Extend backwards, + // and still picked up as part of the match if they do. + const skipBeginning = 2 + eLong := e.bTable[hash7(load6432(src, sAt), tableBits)].Cur.offset + t2 := eLong - e.cur - l + skipBeginning + s2 := s + skipBeginning + off := s2 - t2 + if t2 >= 0 && off < maxMatchOffset && off > 0 { + if l2 := e.matchlenLong(int(s2), int(t2), src); l2 > l { + t = t2 + l = l2 + s = s2 + } + } + } + + // Extend backwards + for t > 0 && s > nextEmit && src[t-1] == src[s-1] { + s-- + t-- + l++ + } + if nextEmit < s { + if false { + emitLiteral(dst, src[nextEmit:s]) + } else { + for _, v := range src[nextEmit:s] { + dst.tokens[dst.n] = token(v) + dst.litHist[v]++ + dst.n++ + } + } + } + if debugDeflate { + if t >= s { + panic(fmt.Sprintln("s-t", s, t)) + } + if (s - t) > maxMatchOffset { + panic(fmt.Sprintln("mmo", s-t)) + } + if l < baseMatchLength { + panic("bml") + } + } + + dst.AddMatchLong(l, uint32(s-t-baseMatchOffset)) + s += l + nextEmit = s + if nextS >= s { + s = nextS + 1 + } + + if s >= sLimit { + goto emitRemainder + } + + // Store every 3rd hash in-between. + if true { + const hashEvery = 3 + i := s - l + 1 + if i < s-1 { + cv := load6432(src, i) + t := tableEntry{offset: i + e.cur} + e.table[hashLen(cv, tableBits, hashShortBytes)] = t + eLong := &e.bTable[hash7(cv, tableBits)] + eLong.Cur, eLong.Prev = t, eLong.Cur + + // Do an long at i+1 + cv >>= 8 + t = tableEntry{offset: t.offset + 1} + eLong = &e.bTable[hash7(cv, tableBits)] + eLong.Cur, eLong.Prev = t, eLong.Cur + + // We only have enough bits for a short entry at i+2 + cv >>= 8 + t = tableEntry{offset: t.offset + 1} + e.table[hashLen(cv, tableBits, hashShortBytes)] = t + + // Skip one - otherwise we risk hitting 's' + i += 4 + for ; i < s-1; i += hashEvery { + cv := load6432(src, i) + t := tableEntry{offset: i + e.cur} + t2 := tableEntry{offset: t.offset + 1} + eLong := &e.bTable[hash7(cv, tableBits)] + eLong.Cur, eLong.Prev = t, eLong.Cur + e.table[hashLen(cv>>8, tableBits, hashShortBytes)] = t2 + } + } + } + + // We could immediately start working at s now, but to improve + // compression we first update the hash table at s-1 and at s. + x := load6432(src, s-1) + o := e.cur + s - 1 + prevHashS := hashLen(x, tableBits, hashShortBytes) + prevHashL := hash7(x, tableBits) + e.table[prevHashS] = tableEntry{offset: o} + eLong := &e.bTable[prevHashL] + eLong.Cur, eLong.Prev = tableEntry{offset: o}, eLong.Cur + cv = x >> 8 + } + +emitRemainder: + if int(nextEmit) < len(src) { + // If nothing was added, don't encode literals. + if dst.n == 0 { + return + } + + emitLiteral(dst, src[nextEmit:]) + } +} + +// fastEncL5Window is a level 5 encoder, +// but with a custom window size. +type fastEncL5Window struct { + hist []byte + cur int32 + maxOffset int32 + table [tableSize]tableEntry + bTable [tableSize]tableEntryPrev +} + +func (e *fastEncL5Window) Encode(dst *tokens, src []byte) { + const ( + inputMargin = 12 - 1 + minNonLiteralBlockSize = 1 + 1 + inputMargin + hashShortBytes = 4 + ) + maxMatchOffset := e.maxOffset + if debugDeflate && e.cur < 0 { + panic(fmt.Sprint("e.cur < 0: ", e.cur)) + } + + // Protect against e.cur wraparound. + for e.cur >= bufferReset { + if len(e.hist) == 0 { + for i := range e.table[:] { + e.table[i] = tableEntry{} + } + for i := range e.bTable[:] { + e.bTable[i] = tableEntryPrev{} + } + e.cur = maxMatchOffset + break + } + // Shift down everything in the table that isn't already too far away. + minOff := e.cur + int32(len(e.hist)) - maxMatchOffset + for i := range e.table[:] { + v := e.table[i].offset + if v <= minOff { + v = 0 + } else { + v = v - e.cur + maxMatchOffset + } + e.table[i].offset = v + } + for i := range e.bTable[:] { + v := e.bTable[i] + if v.Cur.offset <= minOff { + v.Cur.offset = 0 + v.Prev.offset = 0 + } else { + v.Cur.offset = v.Cur.offset - e.cur + maxMatchOffset + if v.Prev.offset <= minOff { + v.Prev.offset = 0 + } else { + v.Prev.offset = v.Prev.offset - e.cur + maxMatchOffset + } + } + e.bTable[i] = v + } + e.cur = maxMatchOffset + } + + s := e.addBlock(src) + + // This check isn't in the Snappy implementation, but there, the caller + // instead of the callee handles this case. + if len(src) < minNonLiteralBlockSize { + // We do not fill the token table. + // This will be picked up by caller. + dst.n = uint16(len(src)) + return + } + + // Override src + src = e.hist + nextEmit := s + + // sLimit is when to stop looking for offset/length copies. The inputMargin + // lets us use a fast path for emitLiteral in the main loop, while we are + // looking for copies. + sLimit := int32(len(src) - inputMargin) + + // nextEmit is where in src the next emitLiteral should start from. + cv := load6432(src, s) + for { + const skipLog = 6 + const doEvery = 1 + + nextS := s + var l int32 + var t int32 + for { + nextHashS := hashLen(cv, tableBits, hashShortBytes) + nextHashL := hash7(cv, tableBits) + + s = nextS + nextS = s + doEvery + (s-nextEmit)>>skipLog + if nextS > sLimit { + goto emitRemainder + } + // Fetch a short+long candidate + sCandidate := e.table[nextHashS] + lCandidate := e.bTable[nextHashL] + next := load6432(src, nextS) + entry := tableEntry{offset: s + e.cur} + e.table[nextHashS] = entry + eLong := &e.bTable[nextHashL] + eLong.Cur, eLong.Prev = entry, eLong.Cur + + nextHashS = hashLen(next, tableBits, hashShortBytes) + nextHashL = hash7(next, tableBits) + + t = lCandidate.Cur.offset - e.cur + if s-t < maxMatchOffset { + if uint32(cv) == load3232(src, t) { + // Store the next match + e.table[nextHashS] = tableEntry{offset: nextS + e.cur} + eLong := &e.bTable[nextHashL] + eLong.Cur, eLong.Prev = tableEntry{offset: nextS + e.cur}, eLong.Cur + + t2 := lCandidate.Prev.offset - e.cur + if s-t2 < maxMatchOffset && uint32(cv) == load3232(src, t2) { + l = e.matchlen(s+4, t+4, src) + 4 + ml1 := e.matchlen(s+4, t2+4, src) + 4 + if ml1 > l { + t = t2 + l = ml1 + break + } + } + break + } + t = lCandidate.Prev.offset - e.cur + if s-t < maxMatchOffset && uint32(cv) == load3232(src, t) { + // Store the next match + e.table[nextHashS] = tableEntry{offset: nextS + e.cur} + eLong := &e.bTable[nextHashL] + eLong.Cur, eLong.Prev = tableEntry{offset: nextS + e.cur}, eLong.Cur + break + } + } + + t = sCandidate.offset - e.cur + if s-t < maxMatchOffset && uint32(cv) == load3232(src, t) { + // Found a 4 match... + l = e.matchlen(s+4, t+4, src) + 4 + lCandidate = e.bTable[nextHashL] + // Store the next match + + e.table[nextHashS] = tableEntry{offset: nextS + e.cur} + eLong := &e.bTable[nextHashL] + eLong.Cur, eLong.Prev = tableEntry{offset: nextS + e.cur}, eLong.Cur + + // If the next long is a candidate, use that... + t2 := lCandidate.Cur.offset - e.cur + if nextS-t2 < maxMatchOffset { + if load3232(src, t2) == uint32(next) { + ml := e.matchlen(nextS+4, t2+4, src) + 4 + if ml > l { + t = t2 + s = nextS + l = ml + break + } + } + // If the previous long is a candidate, use that... + t2 = lCandidate.Prev.offset - e.cur + if nextS-t2 < maxMatchOffset && load3232(src, t2) == uint32(next) { + ml := e.matchlen(nextS+4, t2+4, src) + 4 + if ml > l { + t = t2 + s = nextS + l = ml + break + } + } + } + break + } + cv = next + } + + // A 4-byte match has been found. We'll later see if more than 4 bytes + // match. But, prior to the match, src[nextEmit:s] are unmatched. Emit + // them as literal bytes. + + if l == 0 { + // Extend the 4-byte match as long as possible. + l = e.matchlenLong(s+4, t+4, src) + 4 + } else if l == maxMatchLength { + l += e.matchlenLong(s+l, t+l, src) + } + + // Try to locate a better match by checking the end of best match... + if sAt := s + l; l < 30 && sAt < sLimit { + // Allow some bytes at the beginning to mismatch. + // Sweet spot is 2/3 bytes depending on input. + // 3 is only a little better when it is but sometimes a lot worse. + // The skipped bytes are tested in Extend backwards, + // and still picked up as part of the match if they do. + const skipBeginning = 2 + eLong := e.bTable[hash7(load6432(src, sAt), tableBits)].Cur.offset + t2 := eLong - e.cur - l + skipBeginning + s2 := s + skipBeginning + off := s2 - t2 + if t2 >= 0 && off < maxMatchOffset && off > 0 { + if l2 := e.matchlenLong(s2, t2, src); l2 > l { + t = t2 + l = l2 + s = s2 + } + } + } + + // Extend backwards + for t > 0 && s > nextEmit && src[t-1] == src[s-1] { + s-- + t-- + l++ + } + if nextEmit < s { + if false { + emitLiteral(dst, src[nextEmit:s]) + } else { + for _, v := range src[nextEmit:s] { + dst.tokens[dst.n] = token(v) + dst.litHist[v]++ + dst.n++ + } + } + } + if debugDeflate { + if t >= s { + panic(fmt.Sprintln("s-t", s, t)) + } + if (s - t) > maxMatchOffset { + panic(fmt.Sprintln("mmo", s-t)) + } + if l < baseMatchLength { + panic("bml") + } + } + + dst.AddMatchLong(l, uint32(s-t-baseMatchOffset)) + s += l + nextEmit = s + if nextS >= s { + s = nextS + 1 + } + + if s >= sLimit { + goto emitRemainder + } + + // Store every 3rd hash in-between. + if true { + const hashEvery = 3 + i := s - l + 1 + if i < s-1 { + cv := load6432(src, i) + t := tableEntry{offset: i + e.cur} + e.table[hashLen(cv, tableBits, hashShortBytes)] = t + eLong := &e.bTable[hash7(cv, tableBits)] + eLong.Cur, eLong.Prev = t, eLong.Cur + + // Do an long at i+1 + cv >>= 8 + t = tableEntry{offset: t.offset + 1} + eLong = &e.bTable[hash7(cv, tableBits)] + eLong.Cur, eLong.Prev = t, eLong.Cur + + // We only have enough bits for a short entry at i+2 + cv >>= 8 + t = tableEntry{offset: t.offset + 1} + e.table[hashLen(cv, tableBits, hashShortBytes)] = t + + // Skip one - otherwise we risk hitting 's' + i += 4 + for ; i < s-1; i += hashEvery { + cv := load6432(src, i) + t := tableEntry{offset: i + e.cur} + t2 := tableEntry{offset: t.offset + 1} + eLong := &e.bTable[hash7(cv, tableBits)] + eLong.Cur, eLong.Prev = t, eLong.Cur + e.table[hashLen(cv>>8, tableBits, hashShortBytes)] = t2 + } + } + } + + // We could immediately start working at s now, but to improve + // compression we first update the hash table at s-1 and at s. + x := load6432(src, s-1) + o := e.cur + s - 1 + prevHashS := hashLen(x, tableBits, hashShortBytes) + prevHashL := hash7(x, tableBits) + e.table[prevHashS] = tableEntry{offset: o} + eLong := &e.bTable[prevHashL] + eLong.Cur, eLong.Prev = tableEntry{offset: o}, eLong.Cur + cv = x >> 8 + } + +emitRemainder: + if int(nextEmit) < len(src) { + // If nothing was added, don't encode literals. + if dst.n == 0 { + return + } + + emitLiteral(dst, src[nextEmit:]) + } +} + +// Reset the encoding table. +func (e *fastEncL5Window) Reset() { + // We keep the same allocs, since we are compressing the same block sizes. + if cap(e.hist) < allocHistory { + e.hist = make([]byte, 0, allocHistory) + } + + // We offset current position so everything will be out of reach. + // If we are above the buffer reset it will be cleared anyway since len(hist) == 0. + if e.cur <= int32(bufferReset) { + e.cur += e.maxOffset + int32(len(e.hist)) + } + e.hist = e.hist[:0] +} + +func (e *fastEncL5Window) addBlock(src []byte) int32 { + // check if we have space already + maxMatchOffset := e.maxOffset + + if len(e.hist)+len(src) > cap(e.hist) { + if cap(e.hist) == 0 { + e.hist = make([]byte, 0, allocHistory) + } else { + if cap(e.hist) < int(maxMatchOffset*2) { + panic("unexpected buffer size") + } + // Move down + offset := int32(len(e.hist)) - maxMatchOffset + copy(e.hist[0:maxMatchOffset], e.hist[offset:]) + e.cur += offset + e.hist = e.hist[:maxMatchOffset] + } + } + s := int32(len(e.hist)) + e.hist = append(e.hist, src...) + return s +} + +// matchlen will return the match length between offsets and t in src. +// The maximum length returned is maxMatchLength - 4. +// It is assumed that s > t, that t >=0 and s < len(src). +func (e *fastEncL5Window) matchlen(s, t int32, src []byte) int32 { + if debugDecode { + if t >= s { + panic(fmt.Sprint("t >=s:", t, s)) + } + if int(s) >= len(src) { + panic(fmt.Sprint("s >= len(src):", s, len(src))) + } + if t < 0 { + panic(fmt.Sprint("t < 0:", t)) + } + if s-t > e.maxOffset { + panic(fmt.Sprint(s, "-", t, "(", s-t, ") > maxMatchLength (", maxMatchOffset, ")")) + } + } + s1 := min(int(s)+maxMatchLength-4, len(src)) + + // Extend the match to be as long as possible. + return int32(matchLen(src[s:s1], src[t:])) +} + +// matchlenLong will return the match length between offsets and t in src. +// It is assumed that s > t, that t >=0 and s < len(src). +func (e *fastEncL5Window) matchlenLong(s, t int32, src []byte) int32 { + if debugDeflate { + if t >= s { + panic(fmt.Sprint("t >=s:", t, s)) + } + if int(s) >= len(src) { + panic(fmt.Sprint("s >= len(src):", s, len(src))) + } + if t < 0 { + panic(fmt.Sprint("t < 0:", t)) + } + if s-t > e.maxOffset { + panic(fmt.Sprint(s, "-", t, "(", s-t, ") > maxMatchLength (", maxMatchOffset, ")")) + } + } + // Extend the match to be as long as possible. + return int32(matchLen(src[s:], src[t:])) +} diff --git a/vendor/github.com/klauspost/compress/flate/level6.go b/vendor/github.com/klauspost/compress/flate/level6.go new file mode 100644 index 000000000..96f5bb430 --- /dev/null +++ b/vendor/github.com/klauspost/compress/flate/level6.go @@ -0,0 +1,325 @@ +package flate + +import "fmt" + +type fastEncL6 struct { + fastGen + table [tableSize]tableEntry + bTable [tableSize]tableEntryPrev +} + +func (e *fastEncL6) Encode(dst *tokens, src []byte) { + const ( + inputMargin = 12 - 1 + minNonLiteralBlockSize = 1 + 1 + inputMargin + hashShortBytes = 4 + ) + if debugDeflate && e.cur < 0 { + panic(fmt.Sprint("e.cur < 0: ", e.cur)) + } + + // Protect against e.cur wraparound. + for e.cur >= bufferReset { + if len(e.hist) == 0 { + for i := range e.table[:] { + e.table[i] = tableEntry{} + } + for i := range e.bTable[:] { + e.bTable[i] = tableEntryPrev{} + } + e.cur = maxMatchOffset + break + } + // Shift down everything in the table that isn't already too far away. + minOff := e.cur + int32(len(e.hist)) - maxMatchOffset + for i := range e.table[:] { + v := e.table[i].offset + if v <= minOff { + v = 0 + } else { + v = v - e.cur + maxMatchOffset + } + e.table[i].offset = v + } + for i := range e.bTable[:] { + v := e.bTable[i] + if v.Cur.offset <= minOff { + v.Cur.offset = 0 + v.Prev.offset = 0 + } else { + v.Cur.offset = v.Cur.offset - e.cur + maxMatchOffset + if v.Prev.offset <= minOff { + v.Prev.offset = 0 + } else { + v.Prev.offset = v.Prev.offset - e.cur + maxMatchOffset + } + } + e.bTable[i] = v + } + e.cur = maxMatchOffset + } + + s := e.addBlock(src) + + // This check isn't in the Snappy implementation, but there, the caller + // instead of the callee handles this case. + if len(src) < minNonLiteralBlockSize { + // We do not fill the token table. + // This will be picked up by caller. + dst.n = uint16(len(src)) + return + } + + // Override src + src = e.hist + nextEmit := s + + // sLimit is when to stop looking for offset/length copies. The inputMargin + // lets us use a fast path for emitLiteral in the main loop, while we are + // looking for copies. + sLimit := int32(len(src) - inputMargin) + + // nextEmit is where in src the next emitLiteral should start from. + cv := load6432(src, s) + // Repeat MUST be > 1 and within range + repeat := int32(1) + for { + const skipLog = 7 + const doEvery = 1 + + nextS := s + var l int32 + var t int32 + for { + nextHashS := hashLen(cv, tableBits, hashShortBytes) + nextHashL := hash7(cv, tableBits) + s = nextS + nextS = s + doEvery + (s-nextEmit)>>skipLog + if nextS > sLimit { + goto emitRemainder + } + // Fetch a short+long candidate + sCandidate := e.table[nextHashS] + lCandidate := e.bTable[nextHashL] + next := load6432(src, nextS) + entry := tableEntry{offset: s + e.cur} + e.table[nextHashS] = entry + eLong := &e.bTable[nextHashL] + eLong.Cur, eLong.Prev = entry, eLong.Cur + + // Calculate hashes of 'next' + nextHashS = hashLen(next, tableBits, hashShortBytes) + nextHashL = hash7(next, tableBits) + + t = lCandidate.Cur.offset - e.cur + if s-t < maxMatchOffset { + if uint32(cv) == load3232(src, t) { + // Long candidate matches at least 4 bytes. + + // Store the next match + e.table[nextHashS] = tableEntry{offset: nextS + e.cur} + eLong := &e.bTable[nextHashL] + eLong.Cur, eLong.Prev = tableEntry{offset: nextS + e.cur}, eLong.Cur + + // Check the previous long candidate as well. + t2 := lCandidate.Prev.offset - e.cur + if s-t2 < maxMatchOffset && uint32(cv) == load3232(src, t2) { + l = e.matchlen(int(s+4), int(t+4), src) + 4 + ml1 := e.matchlen(int(s+4), int(t2+4), src) + 4 + if ml1 > l { + t = t2 + l = ml1 + break + } + } + break + } + // Current value did not match, but check if previous long value does. + t = lCandidate.Prev.offset - e.cur + if s-t < maxMatchOffset && uint32(cv) == load3232(src, t) { + // Store the next match + e.table[nextHashS] = tableEntry{offset: nextS + e.cur} + eLong := &e.bTable[nextHashL] + eLong.Cur, eLong.Prev = tableEntry{offset: nextS + e.cur}, eLong.Cur + break + } + } + + t = sCandidate.offset - e.cur + if s-t < maxMatchOffset && uint32(cv) == load3232(src, t) { + // Found a 4 match... + l = e.matchlen(int(s+4), int(t+4), src) + 4 + + // Look up next long candidate (at nextS) + lCandidate = e.bTable[nextHashL] + + // Store the next match + e.table[nextHashS] = tableEntry{offset: nextS + e.cur} + eLong := &e.bTable[nextHashL] + eLong.Cur, eLong.Prev = tableEntry{offset: nextS + e.cur}, eLong.Cur + + // Check repeat at s + repOff + const repOff = 1 + t2 := s - repeat + repOff + if load3232(src, t2) == uint32(cv>>(8*repOff)) { + ml := e.matchlen(int(s+4+repOff), int(t2+4), src) + 4 + if ml > l { + t = t2 + l = ml + s += repOff + // Not worth checking more. + break + } + } + + // If the next long is a candidate, use that... + t2 = lCandidate.Cur.offset - e.cur + if nextS-t2 < maxMatchOffset { + if load3232(src, t2) == uint32(next) { + ml := e.matchlen(int(nextS+4), int(t2+4), src) + 4 + if ml > l { + t = t2 + s = nextS + l = ml + // This is ok, but check previous as well. + } + } + // If the previous long is a candidate, use that... + t2 = lCandidate.Prev.offset - e.cur + if nextS-t2 < maxMatchOffset && load3232(src, t2) == uint32(next) { + ml := e.matchlen(int(nextS+4), int(t2+4), src) + 4 + if ml > l { + t = t2 + s = nextS + l = ml + break + } + } + } + break + } + cv = next + } + + // A 4-byte match has been found. We'll later see if more than 4 bytes + // match. But, prior to the match, src[nextEmit:s] are unmatched. Emit + // them as literal bytes. + + // Extend the 4-byte match as long as possible. + if l == 0 { + l = e.matchlenLong(int(s+4), int(t+4), src) + 4 + } else if l == maxMatchLength { + l += e.matchlenLong(int(s+l), int(t+l), src) + } + + // Try to locate a better match by checking the end-of-match... + if sAt := s + l; sAt < sLimit { + // Allow some bytes at the beginning to mismatch. + // Sweet spot is 2/3 bytes depending on input. + // 3 is only a little better when it is but sometimes a lot worse. + // The skipped bytes are tested in Extend backwards, + // and still picked up as part of the match if they do. + const skipBeginning = 2 + eLong := &e.bTable[hash7(load6432(src, sAt), tableBits)] + // Test current + t2 := eLong.Cur.offset - e.cur - l + skipBeginning + s2 := s + skipBeginning + off := s2 - t2 + if off < maxMatchOffset { + if off > 0 && t2 >= 0 { + if l2 := e.matchlenLong(int(s2), int(t2), src); l2 > l { + t = t2 + l = l2 + s = s2 + } + } + // Test next: + t2 = eLong.Prev.offset - e.cur - l + skipBeginning + off := s2 - t2 + if off > 0 && off < maxMatchOffset && t2 >= 0 { + if l2 := e.matchlenLong(int(s2), int(t2), src); l2 > l { + t = t2 + l = l2 + s = s2 + } + } + } + } + + // Extend backwards + for t > 0 && s > nextEmit && src[t-1] == src[s-1] { + s-- + t-- + l++ + } + if nextEmit < s { + if false { + emitLiteral(dst, src[nextEmit:s]) + } else { + for _, v := range src[nextEmit:s] { + dst.tokens[dst.n] = token(v) + dst.litHist[v]++ + dst.n++ + } + } + } + if false { + if t >= s { + panic(fmt.Sprintln("s-t", s, t)) + } + if (s - t) > maxMatchOffset { + panic(fmt.Sprintln("mmo", s-t)) + } + if l < baseMatchLength { + panic("bml") + } + } + + dst.AddMatchLong(l, uint32(s-t-baseMatchOffset)) + repeat = s - t + s += l + nextEmit = s + if nextS >= s { + s = nextS + 1 + } + + if s >= sLimit { + // Index after match end. + for i := nextS + 1; i < int32(len(src))-8; i += 2 { + cv := load6432(src, i) + e.table[hashLen(cv, tableBits, hashShortBytes)] = tableEntry{offset: i + e.cur} + eLong := &e.bTable[hash7(cv, tableBits)] + eLong.Cur, eLong.Prev = tableEntry{offset: i + e.cur}, eLong.Cur + } + goto emitRemainder + } + + // Store every long hash in-between and every second short. + if true { + for i := nextS + 1; i < s-1; i += 2 { + cv := load6432(src, i) + t := tableEntry{offset: i + e.cur} + t2 := tableEntry{offset: t.offset + 1} + eLong := &e.bTable[hash7(cv, tableBits)] + eLong2 := &e.bTable[hash7(cv>>8, tableBits)] + e.table[hashLen(cv, tableBits, hashShortBytes)] = t + eLong.Cur, eLong.Prev = t, eLong.Cur + eLong2.Cur, eLong2.Prev = t2, eLong2.Cur + } + } + + // We could immediately start working at s now, but to improve + // compression we first update the hash table at s-1 and at s. + cv = load6432(src, s) + } + +emitRemainder: + if int(nextEmit) < len(src) { + // If nothing was added, don't encode literals. + if dst.n == 0 { + return + } + + emitLiteral(dst, src[nextEmit:]) + } +} diff --git a/vendor/github.com/klauspost/compress/flate/matchlen_generic.go b/vendor/github.com/klauspost/compress/flate/matchlen_generic.go new file mode 100644 index 000000000..6149384aa --- /dev/null +++ b/vendor/github.com/klauspost/compress/flate/matchlen_generic.go @@ -0,0 +1,34 @@ +// Copyright 2019+ Klaus Post. All rights reserved. +// License information can be found in the LICENSE file. + +package flate + +import ( + "math/bits" + + "github.com/klauspost/compress/internal/le" +) + +// matchLen returns the maximum common prefix length of a and b. +// a must be the shortest of the two. +func matchLen(a, b []byte) (n int) { + left := len(a) + for left >= 8 { + diff := le.Load64(a, n) ^ le.Load64(b, n) + if diff != 0 { + return n + bits.TrailingZeros64(diff)>>3 + } + n += 8 + left -= 8 + } + + a = a[n:] + b = b[n:] + for i := range a { + if a[i] != b[i] { + break + } + n++ + } + return n +} diff --git a/vendor/github.com/klauspost/compress/flate/regmask_amd64.go b/vendor/github.com/klauspost/compress/flate/regmask_amd64.go new file mode 100644 index 000000000..6ed28061b --- /dev/null +++ b/vendor/github.com/klauspost/compress/flate/regmask_amd64.go @@ -0,0 +1,37 @@ +package flate + +const ( + // Masks for shifts with register sizes of the shift value. + // This can be used to work around the x86 design of shifting by mod register size. + // It can be used when a variable shift is always smaller than the register size. + + // reg8SizeMaskX - shift value is 8 bits, shifted is X + reg8SizeMask8 = 7 + reg8SizeMask16 = 15 + reg8SizeMask32 = 31 + reg8SizeMask64 = 63 + + // reg16SizeMaskX - shift value is 16 bits, shifted is X + reg16SizeMask8 = reg8SizeMask8 + reg16SizeMask16 = reg8SizeMask16 + reg16SizeMask32 = reg8SizeMask32 + reg16SizeMask64 = reg8SizeMask64 + + // reg32SizeMaskX - shift value is 32 bits, shifted is X + reg32SizeMask8 = reg8SizeMask8 + reg32SizeMask16 = reg8SizeMask16 + reg32SizeMask32 = reg8SizeMask32 + reg32SizeMask64 = reg8SizeMask64 + + // reg64SizeMaskX - shift value is 64 bits, shifted is X + reg64SizeMask8 = reg8SizeMask8 + reg64SizeMask16 = reg8SizeMask16 + reg64SizeMask32 = reg8SizeMask32 + reg64SizeMask64 = reg8SizeMask64 + + // regSizeMaskUintX - shift value is uint, shifted is X + regSizeMaskUint8 = reg8SizeMask8 + regSizeMaskUint16 = reg8SizeMask16 + regSizeMaskUint32 = reg8SizeMask32 + regSizeMaskUint64 = reg8SizeMask64 +) diff --git a/vendor/github.com/klauspost/compress/flate/regmask_other.go b/vendor/github.com/klauspost/compress/flate/regmask_other.go new file mode 100644 index 000000000..e62caf711 --- /dev/null +++ b/vendor/github.com/klauspost/compress/flate/regmask_other.go @@ -0,0 +1,39 @@ +//go:build !amd64 + +package flate + +const ( + // Masks for shifts with register sizes of the shift value. + // This can be used to work around the x86 design of shifting by mod register size. + // It can be used when a variable shift is always smaller than the register size. + + // reg8SizeMaskX - shift value is 8 bits, shifted is X + reg8SizeMask8 = 0xff + reg8SizeMask16 = 0xff + reg8SizeMask32 = 0xff + reg8SizeMask64 = 0xff + + // reg16SizeMaskX - shift value is 16 bits, shifted is X + reg16SizeMask8 = 0xffff + reg16SizeMask16 = 0xffff + reg16SizeMask32 = 0xffff + reg16SizeMask64 = 0xffff + + // reg32SizeMaskX - shift value is 32 bits, shifted is X + reg32SizeMask8 = 0xffffffff + reg32SizeMask16 = 0xffffffff + reg32SizeMask32 = 0xffffffff + reg32SizeMask64 = 0xffffffff + + // reg64SizeMaskX - shift value is 64 bits, shifted is X + reg64SizeMask8 = 0xffffffffffffffff + reg64SizeMask16 = 0xffffffffffffffff + reg64SizeMask32 = 0xffffffffffffffff + reg64SizeMask64 = 0xffffffffffffffff + + // regSizeMaskUintX - shift value is uint, shifted is X + regSizeMaskUint8 = ^uint(0) + regSizeMaskUint16 = ^uint(0) + regSizeMaskUint32 = ^uint(0) + regSizeMaskUint64 = ^uint(0) +) diff --git a/vendor/github.com/klauspost/compress/flate/stateless.go b/vendor/github.com/klauspost/compress/flate/stateless.go new file mode 100644 index 000000000..455ed3e2b --- /dev/null +++ b/vendor/github.com/klauspost/compress/flate/stateless.go @@ -0,0 +1,325 @@ +package flate + +import ( + "io" + "math" + "sync" + + "github.com/klauspost/compress/internal/le" +) + +const ( + maxStatelessBlock = math.MaxInt16 + // dictionary will be taken from maxStatelessBlock, so limit it. + maxStatelessDict = 8 << 10 + + slTableBits = 13 + slTableSize = 1 << slTableBits + slTableShift = 32 - slTableBits +) + +type statelessWriter struct { + dst io.Writer + closed bool +} + +func (s *statelessWriter) Close() error { + if s.closed { + return nil + } + s.closed = true + // Emit EOF block + return StatelessDeflate(s.dst, nil, true, nil) +} + +func (s *statelessWriter) Write(p []byte) (n int, err error) { + err = StatelessDeflate(s.dst, p, false, nil) + if err != nil { + return 0, err + } + return len(p), nil +} + +func (s *statelessWriter) Reset(w io.Writer) { + s.dst = w + s.closed = false +} + +// NewStatelessWriter will do compression but without maintaining any state +// between Write calls. +// There will be no memory kept between Write calls, +// but compression and speed will be suboptimal. +// Because of this, the size of actual Write calls will affect output size. +func NewStatelessWriter(dst io.Writer) io.WriteCloser { + return &statelessWriter{dst: dst} +} + +// bitWriterPool contains bit writers that can be reused. +var bitWriterPool = sync.Pool{ + New: func() any { + return newHuffmanBitWriter(nil) + }, +} + +// tokensPool contains tokens struct objects that can be reused +var tokensPool = sync.Pool{ + New: func() any { + return &tokens{} + }, +} + +// StatelessDeflate allows compressing directly to a Writer without retaining state. +// When returning everything will be flushed. +// Up to 8KB of an optional dictionary can be given which is presumed to precede the block. +// Longer dictionaries will be truncated and will still produce valid output. +// Sending nil dictionary is perfectly fine. +func StatelessDeflate(out io.Writer, in []byte, eof bool, dict []byte) error { + bw := bitWriterPool.Get().(*huffmanBitWriter) + bw.reset(out) + defer func() { + // don't keep a reference to our output + bw.reset(nil) + bitWriterPool.Put(bw) + }() + if eof && len(in) == 0 { + // Just write an EOF block. + // Could be faster... + bw.writeStoredHeader(0, true) + bw.flush() + return bw.err + } + + // Truncate dict + if len(dict) > maxStatelessDict { + dict = dict[len(dict)-maxStatelessDict:] + } + + // For subsequent loops, keep shallow dict reference to avoid alloc+copy. + var inDict []byte + + dst := tokensPool.Get().(*tokens) + dst.Reset() + defer func() { + tokensPool.Put(dst) + }() + + for len(in) > 0 { + todo := in + if len(inDict) > 0 { + if len(todo) > maxStatelessBlock-maxStatelessDict { + todo = todo[:maxStatelessBlock-maxStatelessDict] + } + } else if len(todo) > maxStatelessBlock-len(dict) { + todo = todo[:maxStatelessBlock-len(dict)] + } + inOrg := in + in = in[len(todo):] + uncompressed := todo + if len(dict) > 0 { + // combine dict and source + bufLen := len(todo) + len(dict) + combined := make([]byte, bufLen) + copy(combined, dict) + copy(combined[len(dict):], todo) + todo = combined + } + // Compress + if len(inDict) == 0 { + statelessEnc(dst, todo, int16(len(dict))) + } else { + statelessEnc(dst, inDict[:maxStatelessDict+len(todo)], maxStatelessDict) + } + isEof := eof && len(in) == 0 + + if dst.n == 0 { + bw.writeStoredHeader(len(uncompressed), isEof) + if bw.err != nil { + return bw.err + } + bw.writeBytes(uncompressed) + } else if int(dst.n) > len(uncompressed)-len(uncompressed)>>4 { + // If we removed less than 1/16th, huffman compress the block. + bw.writeBlockHuff(isEof, uncompressed, len(in) == 0) + } else { + bw.writeBlockDynamic(dst, isEof, uncompressed, len(in) == 0) + } + if len(in) > 0 { + // Retain a dict if we have more + inDict = inOrg[len(uncompressed)-maxStatelessDict:] + dict = nil + dst.Reset() + } + if bw.err != nil { + return bw.err + } + } + if !eof { + // Align, only a stored block can do that. + bw.writeStoredHeader(0, false) + } + bw.flush() + return bw.err +} + +func hashSL(u uint32) uint32 { + return (u * 0x1e35a7bd) >> slTableShift +} + +func load3216(b []byte, i int16) uint32 { + return le.Load32(b, i) +} + +func load6416(b []byte, i int16) uint64 { + return le.Load64(b, i) +} + +func statelessEnc(dst *tokens, src []byte, startAt int16) { + const ( + inputMargin = 12 - 1 + minNonLiteralBlockSize = 1 + 1 + inputMargin + ) + + type tableEntry struct { + offset int16 + } + + var table [slTableSize]tableEntry + + // This check isn't in the Snappy implementation, but there, the caller + // instead of the callee handles this case. + if len(src)-int(startAt) < minNonLiteralBlockSize { + // We do not fill the token table. + // This will be picked up by caller. + dst.n = 0 + return + } + // Index until startAt + if startAt > 0 { + cv := load3232(src, 0) + for i := range startAt { + table[hashSL(cv)] = tableEntry{offset: i} + cv = (cv >> 8) | (uint32(src[i+4]) << 24) + } + } + + s := startAt + 1 + nextEmit := startAt + // sLimit is when to stop looking for offset/length copies. The inputMargin + // lets us use a fast path for emitLiteral in the main loop, while we are + // looking for copies. + sLimit := int16(len(src) - inputMargin) + + // nextEmit is where in src the next emitLiteral should start from. + cv := load3216(src, s) + + for { + const skipLog = 5 + const doEvery = 2 + + nextS := s + var candidate tableEntry + for { + nextHash := hashSL(cv) + candidate = table[nextHash] + nextS = s + doEvery + (s-nextEmit)>>skipLog + if nextS > sLimit || nextS <= 0 { + goto emitRemainder + } + + now := load6416(src, nextS) + table[nextHash] = tableEntry{offset: s} + nextHash = hashSL(uint32(now)) + + if cv == load3216(src, candidate.offset) { + table[nextHash] = tableEntry{offset: nextS} + break + } + + // Do one right away... + cv = uint32(now) + s = nextS + nextS++ + candidate = table[nextHash] + now >>= 8 + table[nextHash] = tableEntry{offset: s} + + if cv == load3216(src, candidate.offset) { + table[nextHash] = tableEntry{offset: nextS} + break + } + cv = uint32(now) + s = nextS + } + + // A 4-byte match has been found. We'll later see if more than 4 bytes + // match. But, prior to the match, src[nextEmit:s] are unmatched. Emit + // them as literal bytes. + for { + // Invariant: we have a 4-byte match at s, and no need to emit any + // literal bytes prior to s. + + // Extend the 4-byte match as long as possible. + t := candidate.offset + l := int16(matchLen(src[s+4:], src[t+4:]) + 4) + + // Extend backwards + for t > 0 && s > nextEmit && src[t-1] == src[s-1] { + s-- + t-- + l++ + } + if nextEmit < s { + if false { + emitLiteral(dst, src[nextEmit:s]) + } else { + for _, v := range src[nextEmit:s] { + dst.tokens[dst.n] = token(v) + dst.litHist[v]++ + dst.n++ + } + } + } + + // Save the match found + dst.AddMatchLong(int32(l), uint32(s-t-baseMatchOffset)) + s += l + nextEmit = s + if nextS >= s { + s = nextS + 1 + } + if s >= sLimit { + goto emitRemainder + } + + // We could immediately start working at s now, but to improve + // compression we first update the hash table at s-2 and at s. If + // another emitCopy is not our next move, also calculate nextHash + // at s+1. At least on GOARCH=amd64, these three hash calculations + // are faster as one load64 call (with some shifts) instead of + // three load32 calls. + x := load6416(src, s-2) + o := s - 2 + prevHash := hashSL(uint32(x)) + table[prevHash] = tableEntry{offset: o} + x >>= 16 + currHash := hashSL(uint32(x)) + candidate = table[currHash] + table[currHash] = tableEntry{offset: o + 2} + + if uint32(x) != load3216(src, candidate.offset) { + cv = uint32(x >> 8) + s++ + break + } + } + } + +emitRemainder: + if int(nextEmit) < len(src) { + // If nothing was added, don't encode literals. + if dst.n == 0 { + return + } + emitLiteral(dst, src[nextEmit:]) + } +} diff --git a/vendor/github.com/klauspost/compress/flate/token.go b/vendor/github.com/klauspost/compress/flate/token.go new file mode 100644 index 000000000..d818790c1 --- /dev/null +++ b/vendor/github.com/klauspost/compress/flate/token.go @@ -0,0 +1,379 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package flate + +import ( + "bytes" + "encoding/binary" + "fmt" + "io" + "math" +) + +const ( + // bits 0-16 xoffset = offset - MIN_OFFSET_SIZE, or literal - 16 bits + // bits 16-22 offsetcode - 5 bits + // bits 22-30 xlength = length - MIN_MATCH_LENGTH - 8 bits + // bits 30-32 type 0 = literal 1=EOF 2=Match 3=Unused - 2 bits + lengthShift = 22 + offsetMask = 1<maxnumlit + offHist [32]uint16 // offset codes + litHist [256]uint16 // codes 0->255 + nFilled int + n uint16 // Must be able to contain maxStoreBlockSize + tokens [maxStoreBlockSize + 1]token +} + +func (t *tokens) Reset() { + if t.n == 0 { + return + } + t.n = 0 + t.nFilled = 0 + for i := range t.litHist[:] { + t.litHist[i] = 0 + } + for i := range t.extraHist[:] { + t.extraHist[i] = 0 + } + for i := range t.offHist[:] { + t.offHist[i] = 0 + } +} + +func (t *tokens) Fill() { + if t.n == 0 { + return + } + for i, v := range t.litHist[:] { + if v == 0 { + t.litHist[i] = 1 + t.nFilled++ + } + } + for i, v := range t.extraHist[:literalCount-256] { + if v == 0 { + t.nFilled++ + t.extraHist[i] = 1 + } + } + for i, v := range t.offHist[:offsetCodeCount] { + if v == 0 { + t.offHist[i] = 1 + } + } +} + +func indexTokens(in []token) tokens { + var t tokens + t.indexTokens(in) + return t +} + +func (t *tokens) indexTokens(in []token) { + t.Reset() + for _, tok := range in { + if tok < matchType { + t.AddLiteral(tok.literal()) + continue + } + t.AddMatch(uint32(tok.length()), tok.offset()&matchOffsetOnlyMask) + } +} + +// emitLiteral writes a literal chunk and returns the number of bytes written. +func emitLiteral(dst *tokens, lit []byte) { + for _, v := range lit { + dst.tokens[dst.n] = token(v) + dst.litHist[v]++ + dst.n++ + } +} + +func (t *tokens) AddLiteral(lit byte) { + t.tokens[t.n] = token(lit) + t.litHist[lit]++ + t.n++ +} + +// from https://stackoverflow.com/a/28730362 +func mFastLog2(val float32) float32 { + ux := int32(math.Float32bits(val)) + log2 := (float32)(((ux >> 23) & 255) - 128) + ux &= -0x7f800001 + ux += 127 << 23 + uval := math.Float32frombits(uint32(ux)) + log2 += ((-0.34484843)*uval+2.02466578)*uval - 0.67487759 + return log2 +} + +// EstimatedBits will return an minimum size estimated by an *optimal* +// compression of the block. +// The size of the block +func (t *tokens) EstimatedBits() int { + shannon := float32(0) + bits := int(0) + nMatches := 0 + total := int(t.n) + t.nFilled + if total > 0 { + invTotal := 1.0 / float32(total) + for _, v := range t.litHist[:] { + if v > 0 { + n := float32(v) + shannon += atLeastOne(-mFastLog2(n*invTotal)) * n + } + } + // Just add 15 for EOB + shannon += 15 + for i, v := range t.extraHist[1 : literalCount-256] { + if v > 0 { + n := float32(v) + shannon += atLeastOne(-mFastLog2(n*invTotal)) * n + bits += int(lengthExtraBits[i&31]) * int(v) + nMatches += int(v) + } + } + } + if nMatches > 0 { + invTotal := 1.0 / float32(nMatches) + for i, v := range t.offHist[:offsetCodeCount] { + if v > 0 { + n := float32(v) + shannon += atLeastOne(-mFastLog2(n*invTotal)) * n + bits += int(offsetExtraBits[i&31]) * int(v) + } + } + } + return int(shannon) + bits +} + +// AddMatch adds a match to the tokens. +// This function is very sensitive to inlining and right on the border. +func (t *tokens) AddMatch(xlength uint32, xoffset uint32) { + if debugDeflate { + if xlength >= maxMatchLength+baseMatchLength { + panic(fmt.Errorf("invalid length: %v", xlength)) + } + if xoffset >= maxMatchOffset+baseMatchOffset { + panic(fmt.Errorf("invalid offset: %v", xoffset)) + } + } + oCode := offsetCode(xoffset) + xoffset |= oCode << 16 + + t.extraHist[lengthCodes1[uint8(xlength)]]++ + t.offHist[oCode&31]++ + t.tokens[t.n] = token(matchType | xlength<= maxMatchOffset+baseMatchOffset { + panic(fmt.Errorf("invalid offset: %v", xoffset)) + } + } + oc := offsetCode(xoffset) + xoffset |= oc << 16 + for xlength > 0 { + xl := xlength + if xl > 258 { + // We need to have at least baseMatchLength left over for next loop. + if xl > 258+baseMatchLength { + xl = 258 + } else { + xl = 258 - baseMatchLength + } + } + xlength -= xl + xl -= baseMatchLength + t.extraHist[lengthCodes1[uint8(xl)]]++ + t.offHist[oc&31]++ + t.tokens[t.n] = token(matchType | uint32(xl)<> lengthShift) } + +// Convert length to code. +func lengthCode(len uint8) uint8 { return lengthCodes[len] } + +// Returns the offset code corresponding to a specific offset +func offsetCode(off uint32) uint32 { + if false { + if off < uint32(len(offsetCodes)) { + return offsetCodes[off&255] + } else if off>>7 < uint32(len(offsetCodes)) { + return offsetCodes[(off>>7)&255] + 14 + } else { + return offsetCodes[(off>>14)&255] + 28 + } + } + if off < uint32(len(offsetCodes)) { + return offsetCodes[uint8(off)] + } + return offsetCodes14[uint8(off>>7)] +} diff --git a/vendor/github.com/klauspost/compress/fse/README.md b/vendor/github.com/klauspost/compress/fse/README.md new file mode 100644 index 000000000..27d8ed56f --- /dev/null +++ b/vendor/github.com/klauspost/compress/fse/README.md @@ -0,0 +1,79 @@ +# Finite State Entropy + +This package provides Finite State Entropy encoding and decoding. + +Finite State Entropy (also referenced as [tANS](https://en.wikipedia.org/wiki/Asymmetric_numeral_systems#tANS)) +encoding provides a fast near-optimal symbol encoding/decoding +for byte blocks as implemented in [zstandard](https://github.com/facebook/zstd). + +This can be used for compressing input with a lot of similar input values to the smallest number of bytes. +This does not perform any multi-byte [dictionary coding](https://en.wikipedia.org/wiki/Dictionary_coder) as LZ coders, +but it can be used as a secondary step to compressors (like Snappy) that does not do entropy encoding. + +* [Godoc documentation](https://godoc.org/github.com/klauspost/compress/fse) + +## News + + * Feb 2018: First implementation released. Consider this beta software for now. + +# Usage + +This package provides a low level interface that allows to compress single independent blocks. + +Each block is separate, and there is no built in integrity checks. +This means that the caller should keep track of block sizes and also do checksums if needed. + +Compressing a block is done via the [`Compress`](https://godoc.org/github.com/klauspost/compress/fse#Compress) function. +You must provide input and will receive the output and maybe an error. + +These error values can be returned: + +| Error | Description | +|---------------------|-----------------------------------------------------------------------------| +| `` | Everything ok, output is returned | +| `ErrIncompressible` | Returned when input is judged to be too hard to compress | +| `ErrUseRLE` | Returned from the compressor when the input is a single byte value repeated | +| `(error)` | An internal error occurred. | + +As can be seen above there are errors that will be returned even under normal operation so it is important to handle these. + +To reduce allocations you can provide a [`Scratch`](https://godoc.org/github.com/klauspost/compress/fse#Scratch) object +that can be re-used for successive calls. Both compression and decompression accepts a `Scratch` object, and the same +object can be used for both. + +Be aware, that when re-using a `Scratch` object that the *output* buffer is also re-used, so if you are still using this +you must set the `Out` field in the scratch to nil. The same buffer is used for compression and decompression output. + +Decompressing is done by calling the [`Decompress`](https://godoc.org/github.com/klauspost/compress/fse#Decompress) function. +You must provide the output from the compression stage, at exactly the size you got back. If you receive an error back +your input was likely corrupted. + +It is important to note that a successful decoding does *not* mean your output matches your original input. +There are no integrity checks, so relying on errors from the decompressor does not assure your data is valid. + +For more detailed usage, see examples in the [godoc documentation](https://godoc.org/github.com/klauspost/compress/fse#pkg-examples). + +# Performance + +A lot of factors are affecting speed. Block sizes and compressibility of the material are primary factors. +All compression functions are currently only running on the calling goroutine so only one core will be used per block. + +The compressor is significantly faster if symbols are kept as small as possible. The highest byte value of the input +is used to reduce some of the processing, so if all your input is above byte value 64 for instance, it may be +beneficial to transpose all your input values down by 64. + +With moderate block sizes around 64k speed are typically 200MB/s per core for compression and +around 300MB/s decompression speed. + +The same hardware typically does Huffman (deflate) encoding at 125MB/s and decompression at 100MB/s. + +# Plans + +At one point, more internals will be exposed to facilitate more "expert" usage of the components. + +A streaming interface is also likely to be implemented. Likely compatible with [FSE stream format](https://github.com/Cyan4973/FiniteStateEntropy/blob/dev/programs/fileio.c#L261). + +# Contributing + +Contributions are always welcome. Be aware that adding public functions will require good justification and breaking +changes will likely not be accepted. If in doubt open an issue before writing the PR. \ No newline at end of file diff --git a/vendor/github.com/klauspost/compress/fse/bitreader.go b/vendor/github.com/klauspost/compress/fse/bitreader.go new file mode 100644 index 000000000..f65eb3909 --- /dev/null +++ b/vendor/github.com/klauspost/compress/fse/bitreader.go @@ -0,0 +1,122 @@ +// Copyright 2018 Klaus Post. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. +// Based on work Copyright (c) 2013, Yann Collet, released under BSD License. + +package fse + +import ( + "encoding/binary" + "errors" + "io" +) + +// bitReader reads a bitstream in reverse. +// The last set bit indicates the start of the stream and is used +// for aligning the input. +type bitReader struct { + in []byte + off uint // next byte to read is at in[off - 1] + value uint64 + bitsRead uint8 +} + +// init initializes and resets the bit reader. +func (b *bitReader) init(in []byte) error { + if len(in) < 1 { + return errors.New("corrupt stream: too short") + } + b.in = in + b.off = uint(len(in)) + // The highest bit of the last byte indicates where to start + v := in[len(in)-1] + if v == 0 { + return errors.New("corrupt stream, did not find end of stream") + } + b.bitsRead = 64 + b.value = 0 + if len(in) >= 8 { + b.fillFastStart() + } else { + b.fill() + b.fill() + } + b.bitsRead += 8 - uint8(highBits(uint32(v))) + return nil +} + +// getBits will return n bits. n can be 0. +func (b *bitReader) getBits(n uint8) uint16 { + if n == 0 || b.bitsRead >= 64 { + return 0 + } + return b.getBitsFast(n) +} + +// getBitsFast requires that at least one bit is requested every time. +// There are no checks if the buffer is filled. +func (b *bitReader) getBitsFast(n uint8) uint16 { + const regMask = 64 - 1 + v := uint16((b.value << (b.bitsRead & regMask)) >> ((regMask + 1 - n) & regMask)) + b.bitsRead += n + return v +} + +// fillFast() will make sure at least 32 bits are available. +// There must be at least 4 bytes available. +func (b *bitReader) fillFast() { + if b.bitsRead < 32 { + return + } + // 2 bounds checks. + v := b.in[b.off-4:] + v = v[:4] + low := (uint32(v[0])) | (uint32(v[1]) << 8) | (uint32(v[2]) << 16) | (uint32(v[3]) << 24) + b.value = (b.value << 32) | uint64(low) + b.bitsRead -= 32 + b.off -= 4 +} + +// fill() will make sure at least 32 bits are available. +func (b *bitReader) fill() { + if b.bitsRead < 32 { + return + } + if b.off > 4 { + v := b.in[b.off-4:] + v = v[:4] + low := (uint32(v[0])) | (uint32(v[1]) << 8) | (uint32(v[2]) << 16) | (uint32(v[3]) << 24) + b.value = (b.value << 32) | uint64(low) + b.bitsRead -= 32 + b.off -= 4 + return + } + for b.off > 0 { + b.value = (b.value << 8) | uint64(b.in[b.off-1]) + b.bitsRead -= 8 + b.off-- + } +} + +// fillFastStart() assumes the bitreader is empty and there is at least 8 bytes to read. +func (b *bitReader) fillFastStart() { + // Do single re-slice to avoid bounds checks. + b.value = binary.LittleEndian.Uint64(b.in[b.off-8:]) + b.bitsRead = 0 + b.off -= 8 +} + +// finished returns true if all bits have been read from the bit stream. +func (b *bitReader) finished() bool { + return b.bitsRead >= 64 && b.off == 0 +} + +// close the bitstream and returns an error if out-of-buffer reads occurred. +func (b *bitReader) close() error { + // Release reference. + b.in = nil + if b.bitsRead > 64 { + return io.ErrUnexpectedEOF + } + return nil +} diff --git a/vendor/github.com/klauspost/compress/fse/bitwriter.go b/vendor/github.com/klauspost/compress/fse/bitwriter.go new file mode 100644 index 000000000..d58b3fe42 --- /dev/null +++ b/vendor/github.com/klauspost/compress/fse/bitwriter.go @@ -0,0 +1,167 @@ +// Copyright 2018 Klaus Post. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. +// Based on work Copyright (c) 2013, Yann Collet, released under BSD License. + +package fse + +import "fmt" + +// bitWriter will write bits. +// First bit will be LSB of the first byte of output. +type bitWriter struct { + bitContainer uint64 + nBits uint8 + out []byte +} + +// bitMask16 is bitmasks. Has extra to avoid bounds check. +var bitMask16 = [32]uint16{ + 0, 1, 3, 7, 0xF, 0x1F, + 0x3F, 0x7F, 0xFF, 0x1FF, 0x3FF, 0x7FF, + 0xFFF, 0x1FFF, 0x3FFF, 0x7FFF, 0xFFFF, 0xFFFF, + 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, + 0xFFFF, 0xFFFF} /* up to 16 bits */ + +// addBits16NC will add up to 16 bits. +// It will not check if there is space for them, +// so the caller must ensure that it has flushed recently. +func (b *bitWriter) addBits16NC(value uint16, bits uint8) { + b.bitContainer |= uint64(value&bitMask16[bits&31]) << (b.nBits & 63) + b.nBits += bits +} + +// addBits16Clean will add up to 16 bits. value may not contain more set bits than indicated. +// It will not check if there is space for them, so the caller must ensure that it has flushed recently. +func (b *bitWriter) addBits16Clean(value uint16, bits uint8) { + b.bitContainer |= uint64(value) << (b.nBits & 63) + b.nBits += bits +} + +// addBits16ZeroNC will add up to 16 bits. +// It will not check if there is space for them, +// so the caller must ensure that it has flushed recently. +// This is fastest if bits can be zero. +func (b *bitWriter) addBits16ZeroNC(value uint16, bits uint8) { + if bits == 0 { + return + } + value <<= (16 - bits) & 15 + value >>= (16 - bits) & 15 + b.bitContainer |= uint64(value) << (b.nBits & 63) + b.nBits += bits +} + +// flush will flush all pending full bytes. +// There will be at least 56 bits available for writing when this has been called. +// Using flush32 is faster, but leaves less space for writing. +func (b *bitWriter) flush() { + v := b.nBits >> 3 + switch v { + case 0: + case 1: + b.out = append(b.out, + byte(b.bitContainer), + ) + case 2: + b.out = append(b.out, + byte(b.bitContainer), + byte(b.bitContainer>>8), + ) + case 3: + b.out = append(b.out, + byte(b.bitContainer), + byte(b.bitContainer>>8), + byte(b.bitContainer>>16), + ) + case 4: + b.out = append(b.out, + byte(b.bitContainer), + byte(b.bitContainer>>8), + byte(b.bitContainer>>16), + byte(b.bitContainer>>24), + ) + case 5: + b.out = append(b.out, + byte(b.bitContainer), + byte(b.bitContainer>>8), + byte(b.bitContainer>>16), + byte(b.bitContainer>>24), + byte(b.bitContainer>>32), + ) + case 6: + b.out = append(b.out, + byte(b.bitContainer), + byte(b.bitContainer>>8), + byte(b.bitContainer>>16), + byte(b.bitContainer>>24), + byte(b.bitContainer>>32), + byte(b.bitContainer>>40), + ) + case 7: + b.out = append(b.out, + byte(b.bitContainer), + byte(b.bitContainer>>8), + byte(b.bitContainer>>16), + byte(b.bitContainer>>24), + byte(b.bitContainer>>32), + byte(b.bitContainer>>40), + byte(b.bitContainer>>48), + ) + case 8: + b.out = append(b.out, + byte(b.bitContainer), + byte(b.bitContainer>>8), + byte(b.bitContainer>>16), + byte(b.bitContainer>>24), + byte(b.bitContainer>>32), + byte(b.bitContainer>>40), + byte(b.bitContainer>>48), + byte(b.bitContainer>>56), + ) + default: + panic(fmt.Errorf("bits (%d) > 64", b.nBits)) + } + b.bitContainer >>= v << 3 + b.nBits &= 7 +} + +// flush32 will flush out, so there are at least 32 bits available for writing. +func (b *bitWriter) flush32() { + if b.nBits < 32 { + return + } + b.out = append(b.out, + byte(b.bitContainer), + byte(b.bitContainer>>8), + byte(b.bitContainer>>16), + byte(b.bitContainer>>24)) + b.nBits -= 32 + b.bitContainer >>= 32 +} + +// flushAlign will flush remaining full bytes and align to next byte boundary. +func (b *bitWriter) flushAlign() { + nbBytes := (b.nBits + 7) >> 3 + for i := range nbBytes { + b.out = append(b.out, byte(b.bitContainer>>(i*8))) + } + b.nBits = 0 + b.bitContainer = 0 +} + +// close will write the alignment bit and write the final byte(s) +// to the output. +func (b *bitWriter) close() { + // End mark + b.addBits16Clean(1, 1) + // flush until next byte. + b.flushAlign() +} + +// reset and continue writing by appending to out. +func (b *bitWriter) reset(out []byte) { + b.bitContainer = 0 + b.nBits = 0 + b.out = out +} diff --git a/vendor/github.com/klauspost/compress/fse/bytereader.go b/vendor/github.com/klauspost/compress/fse/bytereader.go new file mode 100644 index 000000000..abade2d60 --- /dev/null +++ b/vendor/github.com/klauspost/compress/fse/bytereader.go @@ -0,0 +1,47 @@ +// Copyright 2018 Klaus Post. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. +// Based on work Copyright (c) 2013, Yann Collet, released under BSD License. + +package fse + +// byteReader provides a byte reader that reads +// little endian values from a byte stream. +// The input stream is manually advanced. +// The reader performs no bounds checks. +type byteReader struct { + b []byte + off int +} + +// init will initialize the reader and set the input. +func (b *byteReader) init(in []byte) { + b.b = in + b.off = 0 +} + +// advance the stream b n bytes. +func (b *byteReader) advance(n uint) { + b.off += int(n) +} + +// Uint32 returns a little endian uint32 starting at current offset. +func (b byteReader) Uint32() uint32 { + b2 := b.b[b.off:] + b2 = b2[:4] + v3 := uint32(b2[3]) + v2 := uint32(b2[2]) + v1 := uint32(b2[1]) + v0 := uint32(b2[0]) + return v0 | (v1 << 8) | (v2 << 16) | (v3 << 24) +} + +// unread returns the unread portion of the input. +func (b byteReader) unread() []byte { + return b.b[b.off:] +} + +// remain will return the number of bytes remaining. +func (b byteReader) remain() int { + return len(b.b) - b.off +} diff --git a/vendor/github.com/klauspost/compress/fse/compress.go b/vendor/github.com/klauspost/compress/fse/compress.go new file mode 100644 index 000000000..8c8baa4fc --- /dev/null +++ b/vendor/github.com/klauspost/compress/fse/compress.go @@ -0,0 +1,683 @@ +// Copyright 2018 Klaus Post. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. +// Based on work Copyright (c) 2013, Yann Collet, released under BSD License. + +package fse + +import ( + "errors" + "fmt" +) + +// Compress the input bytes. Input must be < 2GB. +// Provide a Scratch buffer to avoid memory allocations. +// Note that the output is also kept in the scratch buffer. +// If input is too hard to compress, ErrIncompressible is returned. +// If input is a single byte value repeated ErrUseRLE is returned. +func Compress(in []byte, s *Scratch) ([]byte, error) { + if len(in) <= 1 { + return nil, ErrIncompressible + } + if len(in) > (2<<30)-1 { + return nil, errors.New("input too big, must be < 2GB") + } + s, err := s.prepare(in) + if err != nil { + return nil, err + } + + // Create histogram, if none was provided. + maxCount := s.maxCount + if maxCount == 0 { + maxCount = s.countSimple(in) + } + // Reset for next run. + s.clearCount = true + s.maxCount = 0 + if maxCount == len(in) { + // One symbol, use RLE + return nil, ErrUseRLE + } + if maxCount == 1 || maxCount < (len(in)>>7) { + // Each symbol present maximum once or too well distributed. + return nil, ErrIncompressible + } + s.optimalTableLog() + err = s.normalizeCount() + if err != nil { + return nil, err + } + err = s.writeCount() + if err != nil { + return nil, err + } + + if false { + err = s.validateNorm() + if err != nil { + return nil, err + } + } + + err = s.buildCTable() + if err != nil { + return nil, err + } + err = s.compress(in) + if err != nil { + return nil, err + } + s.Out = s.bw.out + // Check if we compressed. + if len(s.Out) >= len(in) { + return nil, ErrIncompressible + } + return s.Out, nil +} + +// cState contains the compression state of a stream. +type cState struct { + bw *bitWriter + stateTable []uint16 + state uint16 +} + +// init will initialize the compression state to the first symbol of the stream. +func (c *cState) init(bw *bitWriter, ct *cTable, tableLog uint8, first symbolTransform) { + c.bw = bw + c.stateTable = ct.stateTable + + nbBitsOut := (first.deltaNbBits + (1 << 15)) >> 16 + im := int32((nbBitsOut << 16) - first.deltaNbBits) + lu := (im >> nbBitsOut) + first.deltaFindState + c.state = c.stateTable[lu] +} + +// encode the output symbol provided and write it to the bitstream. +func (c *cState) encode(symbolTT symbolTransform) { + nbBitsOut := (uint32(c.state) + symbolTT.deltaNbBits) >> 16 + dstState := int32(c.state>>(nbBitsOut&15)) + symbolTT.deltaFindState + c.bw.addBits16NC(c.state, uint8(nbBitsOut)) + c.state = c.stateTable[dstState] +} + +// encode the output symbol provided and write it to the bitstream. +func (c *cState) encodeZero(symbolTT symbolTransform) { + nbBitsOut := (uint32(c.state) + symbolTT.deltaNbBits) >> 16 + dstState := int32(c.state>>(nbBitsOut&15)) + symbolTT.deltaFindState + c.bw.addBits16ZeroNC(c.state, uint8(nbBitsOut)) + c.state = c.stateTable[dstState] +} + +// flush will write the tablelog to the output and flush the remaining full bytes. +func (c *cState) flush(tableLog uint8) { + c.bw.flush32() + c.bw.addBits16NC(c.state, tableLog) + c.bw.flush() +} + +// compress is the main compression loop that will encode the input from the last byte to the first. +func (s *Scratch) compress(src []byte) error { + if len(src) <= 2 { + return errors.New("compress: src too small") + } + tt := s.ct.symbolTT[:256] + s.bw.reset(s.Out) + + // Our two states each encodes every second byte. + // Last byte encoded (first byte decoded) will always be encoded by c1. + var c1, c2 cState + + // Encode so remaining size is divisible by 4. + ip := len(src) + if ip&1 == 1 { + c1.init(&s.bw, &s.ct, s.actualTableLog, tt[src[ip-1]]) + c2.init(&s.bw, &s.ct, s.actualTableLog, tt[src[ip-2]]) + c1.encodeZero(tt[src[ip-3]]) + ip -= 3 + } else { + c2.init(&s.bw, &s.ct, s.actualTableLog, tt[src[ip-1]]) + c1.init(&s.bw, &s.ct, s.actualTableLog, tt[src[ip-2]]) + ip -= 2 + } + if ip&2 != 0 { + c2.encodeZero(tt[src[ip-1]]) + c1.encodeZero(tt[src[ip-2]]) + ip -= 2 + } + src = src[:ip] + + // Main compression loop. + switch { + case !s.zeroBits && s.actualTableLog <= 8: + // We can encode 4 symbols without requiring a flush. + // We do not need to check if any output is 0 bits. + for ; len(src) >= 4; src = src[:len(src)-4] { + s.bw.flush32() + v3, v2, v1, v0 := src[len(src)-4], src[len(src)-3], src[len(src)-2], src[len(src)-1] + c2.encode(tt[v0]) + c1.encode(tt[v1]) + c2.encode(tt[v2]) + c1.encode(tt[v3]) + } + case !s.zeroBits: + // We do not need to check if any output is 0 bits. + for ; len(src) >= 4; src = src[:len(src)-4] { + s.bw.flush32() + v3, v2, v1, v0 := src[len(src)-4], src[len(src)-3], src[len(src)-2], src[len(src)-1] + c2.encode(tt[v0]) + c1.encode(tt[v1]) + s.bw.flush32() + c2.encode(tt[v2]) + c1.encode(tt[v3]) + } + case s.actualTableLog <= 8: + // We can encode 4 symbols without requiring a flush + for ; len(src) >= 4; src = src[:len(src)-4] { + s.bw.flush32() + v3, v2, v1, v0 := src[len(src)-4], src[len(src)-3], src[len(src)-2], src[len(src)-1] + c2.encodeZero(tt[v0]) + c1.encodeZero(tt[v1]) + c2.encodeZero(tt[v2]) + c1.encodeZero(tt[v3]) + } + default: + for ; len(src) >= 4; src = src[:len(src)-4] { + s.bw.flush32() + v3, v2, v1, v0 := src[len(src)-4], src[len(src)-3], src[len(src)-2], src[len(src)-1] + c2.encodeZero(tt[v0]) + c1.encodeZero(tt[v1]) + s.bw.flush32() + c2.encodeZero(tt[v2]) + c1.encodeZero(tt[v3]) + } + } + + // Flush final state. + // Used to initialize state when decoding. + c2.flush(s.actualTableLog) + c1.flush(s.actualTableLog) + + s.bw.close() + return nil +} + +// writeCount will write the normalized histogram count to header. +// This is read back by readNCount. +func (s *Scratch) writeCount() error { + var ( + tableLog = s.actualTableLog + tableSize = 1 << tableLog + previous0 bool + charnum uint16 + + maxHeaderSize = ((int(s.symbolLen)*int(tableLog) + 4 + 2) >> 3) + 3 + + // Write Table Size + bitStream = uint32(tableLog - minTablelog) + bitCount = uint(4) + remaining = int16(tableSize + 1) /* +1 for extra accuracy */ + threshold = int16(tableSize) + nbBits = uint(tableLog + 1) + ) + if cap(s.Out) < maxHeaderSize { + s.Out = make([]byte, 0, s.br.remain()+maxHeaderSize) + } + outP := uint(0) + out := s.Out[:maxHeaderSize] + + // stops at 1 + for remaining > 1 { + if previous0 { + start := charnum + for s.norm[charnum] == 0 { + charnum++ + } + for charnum >= start+24 { + start += 24 + bitStream += uint32(0xFFFF) << bitCount + out[outP] = byte(bitStream) + out[outP+1] = byte(bitStream >> 8) + outP += 2 + bitStream >>= 16 + } + for charnum >= start+3 { + start += 3 + bitStream += 3 << bitCount + bitCount += 2 + } + bitStream += uint32(charnum-start) << bitCount + bitCount += 2 + if bitCount > 16 { + out[outP] = byte(bitStream) + out[outP+1] = byte(bitStream >> 8) + outP += 2 + bitStream >>= 16 + bitCount -= 16 + } + } + + count := s.norm[charnum] + charnum++ + max := (2*threshold - 1) - remaining + if count < 0 { + remaining += count + } else { + remaining -= count + } + count++ // +1 for extra accuracy + if count >= threshold { + count += max // [0..max[ [max..threshold[ (...) [threshold+max 2*threshold[ + } + bitStream += uint32(count) << bitCount + bitCount += nbBits + if count < max { + bitCount-- + } + + previous0 = count == 1 + if remaining < 1 { + return errors.New("internal error: remaining<1") + } + for remaining < threshold { + nbBits-- + threshold >>= 1 + } + + if bitCount > 16 { + out[outP] = byte(bitStream) + out[outP+1] = byte(bitStream >> 8) + outP += 2 + bitStream >>= 16 + bitCount -= 16 + } + } + + out[outP] = byte(bitStream) + out[outP+1] = byte(bitStream >> 8) + outP += (bitCount + 7) / 8 + + if charnum > s.symbolLen { + return errors.New("internal error: charnum > s.symbolLen") + } + s.Out = out[:outP] + return nil +} + +// symbolTransform contains the state transform for a symbol. +type symbolTransform struct { + deltaFindState int32 + deltaNbBits uint32 +} + +// String prints values as a human readable string. +func (s symbolTransform) String() string { + return fmt.Sprintf("dnbits: %08x, fs:%d", s.deltaNbBits, s.deltaFindState) +} + +// cTable contains tables used for compression. +type cTable struct { + tableSymbol []byte + stateTable []uint16 + symbolTT []symbolTransform +} + +// allocCtable will allocate tables needed for compression. +// If existing tables a re big enough, they are simply re-used. +func (s *Scratch) allocCtable() { + tableSize := 1 << s.actualTableLog + // get tableSymbol that is big enough. + if cap(s.ct.tableSymbol) < tableSize { + s.ct.tableSymbol = make([]byte, tableSize) + } + s.ct.tableSymbol = s.ct.tableSymbol[:tableSize] + + ctSize := tableSize + if cap(s.ct.stateTable) < ctSize { + s.ct.stateTable = make([]uint16, ctSize) + } + s.ct.stateTable = s.ct.stateTable[:ctSize] + + if cap(s.ct.symbolTT) < 256 { + s.ct.symbolTT = make([]symbolTransform, 256) + } + s.ct.symbolTT = s.ct.symbolTT[:256] +} + +// buildCTable will populate the compression table so it is ready to be used. +func (s *Scratch) buildCTable() error { + tableSize := uint32(1 << s.actualTableLog) + highThreshold := tableSize - 1 + var cumul [maxSymbolValue + 2]int16 + + s.allocCtable() + tableSymbol := s.ct.tableSymbol[:tableSize] + // symbol start positions + { + cumul[0] = 0 + for ui, v := range s.norm[:s.symbolLen-1] { + u := byte(ui) // one less than reference + if v == -1 { + // Low proba symbol + cumul[u+1] = cumul[u] + 1 + tableSymbol[highThreshold] = u + highThreshold-- + } else { + cumul[u+1] = cumul[u] + v + } + } + // Encode last symbol separately to avoid overflowing u + u := int(s.symbolLen - 1) + v := s.norm[s.symbolLen-1] + if v == -1 { + // Low proba symbol + cumul[u+1] = cumul[u] + 1 + tableSymbol[highThreshold] = byte(u) + highThreshold-- + } else { + cumul[u+1] = cumul[u] + v + } + if uint32(cumul[s.symbolLen]) != tableSize { + return fmt.Errorf("internal error: expected cumul[s.symbolLen] (%d) == tableSize (%d)", cumul[s.symbolLen], tableSize) + } + cumul[s.symbolLen] = int16(tableSize) + 1 + } + // Spread symbols + s.zeroBits = false + { + step := tableStep(tableSize) + tableMask := tableSize - 1 + var position uint32 + // if any symbol > largeLimit, we may have 0 bits output. + largeLimit := int16(1 << (s.actualTableLog - 1)) + for ui, v := range s.norm[:s.symbolLen] { + symbol := byte(ui) + if v > largeLimit { + s.zeroBits = true + } + for range v { + tableSymbol[position] = symbol + position = (position + step) & tableMask + for position > highThreshold { + position = (position + step) & tableMask + } /* Low proba area */ + } + } + + // Check if we have gone through all positions + if position != 0 { + return errors.New("position!=0") + } + } + + // Build table + table := s.ct.stateTable + { + tsi := int(tableSize) + for u, v := range tableSymbol { + // TableU16 : sorted by symbol order; gives next state value + table[cumul[v]] = uint16(tsi + u) + cumul[v]++ + } + } + + // Build Symbol Transformation Table + { + total := int16(0) + symbolTT := s.ct.symbolTT[:s.symbolLen] + tableLog := s.actualTableLog + tl := (uint32(tableLog) << 16) - (1 << tableLog) + for i, v := range s.norm[:s.symbolLen] { + switch v { + case 0: + case -1, 1: + symbolTT[i].deltaNbBits = tl + symbolTT[i].deltaFindState = int32(total - 1) + total++ + default: + maxBitsOut := uint32(tableLog) - highBits(uint32(v-1)) + minStatePlus := uint32(v) << maxBitsOut + symbolTT[i].deltaNbBits = (maxBitsOut << 16) - minStatePlus + symbolTT[i].deltaFindState = int32(total - v) + total += v + } + } + if total != int16(tableSize) { + return fmt.Errorf("total mismatch %d (got) != %d (want)", total, tableSize) + } + } + return nil +} + +// countSimple will create a simple histogram in s.count. +// Returns the biggest count. +// Does not update s.clearCount. +func (s *Scratch) countSimple(in []byte) (max int) { + for _, v := range in { + s.count[v]++ + } + m, symlen := uint32(0), s.symbolLen + for i, v := range s.count[:] { + if v == 0 { + continue + } + if v > m { + m = v + } + symlen = uint16(i) + 1 + } + s.symbolLen = symlen + return int(m) +} + +// minTableLog provides the minimum logSize to safely represent a distribution. +func (s *Scratch) minTableLog() uint8 { + minBitsSrc := highBits(uint32(s.br.remain()-1)) + 1 + minBitsSymbols := highBits(uint32(s.symbolLen-1)) + 2 + if minBitsSrc < minBitsSymbols { + return uint8(minBitsSrc) + } + return uint8(minBitsSymbols) +} + +// optimalTableLog calculates and sets the optimal tableLog in s.actualTableLog +func (s *Scratch) optimalTableLog() { + tableLog := s.TableLog + minBits := s.minTableLog() + maxBitsSrc := uint8(highBits(uint32(s.br.remain()-1))) - 2 + if maxBitsSrc < tableLog { + // Accuracy can be reduced + tableLog = maxBitsSrc + } + if minBits > tableLog { + tableLog = minBits + } + // Need a minimum to safely represent all symbol values + if tableLog < minTablelog { + tableLog = minTablelog + } + if tableLog > maxTableLog { + tableLog = maxTableLog + } + s.actualTableLog = tableLog +} + +var rtbTable = [...]uint32{0, 473195, 504333, 520860, 550000, 700000, 750000, 830000} + +// normalizeCount will normalize the count of the symbols so +// the total is equal to the table size. +func (s *Scratch) normalizeCount() error { + var ( + tableLog = s.actualTableLog + scale = 62 - uint64(tableLog) + step = (1 << 62) / uint64(s.br.remain()) + vStep = uint64(1) << (scale - 20) + stillToDistribute = int16(1 << tableLog) + largest int + largestP int16 + lowThreshold = (uint32)(s.br.remain() >> tableLog) + ) + + for i, cnt := range s.count[:s.symbolLen] { + // already handled + // if (count[s] == s.length) return 0; /* rle special case */ + + if cnt == 0 { + s.norm[i] = 0 + continue + } + if cnt <= lowThreshold { + s.norm[i] = -1 + stillToDistribute-- + } else { + proba := (int16)((uint64(cnt) * step) >> scale) + if proba < 8 { + restToBeat := vStep * uint64(rtbTable[proba]) + v := uint64(cnt)*step - (uint64(proba) << scale) + if v > restToBeat { + proba++ + } + } + if proba > largestP { + largestP = proba + largest = i + } + s.norm[i] = proba + stillToDistribute -= proba + } + } + + if -stillToDistribute >= (s.norm[largest] >> 1) { + // corner case, need another normalization method + return s.normalizeCount2() + } + s.norm[largest] += stillToDistribute + return nil +} + +// Secondary normalization method. +// To be used when primary method fails. +func (s *Scratch) normalizeCount2() error { + const notYetAssigned = -2 + var ( + distributed uint32 + total = uint32(s.br.remain()) + tableLog = s.actualTableLog + lowThreshold = total >> tableLog + lowOne = (total * 3) >> (tableLog + 1) + ) + for i, cnt := range s.count[:s.symbolLen] { + if cnt == 0 { + s.norm[i] = 0 + continue + } + if cnt <= lowThreshold { + s.norm[i] = -1 + distributed++ + total -= cnt + continue + } + if cnt <= lowOne { + s.norm[i] = 1 + distributed++ + total -= cnt + continue + } + s.norm[i] = notYetAssigned + } + toDistribute := (1 << tableLog) - distributed + + if (total / toDistribute) > lowOne { + // risk of rounding to zero + lowOne = (total * 3) / (toDistribute * 2) + for i, cnt := range s.count[:s.symbolLen] { + if (s.norm[i] == notYetAssigned) && (cnt <= lowOne) { + s.norm[i] = 1 + distributed++ + total -= cnt + continue + } + } + toDistribute = (1 << tableLog) - distributed + } + if distributed == uint32(s.symbolLen)+1 { + // all values are pretty poor; + // probably incompressible data (should have already been detected); + // find max, then give all remaining points to max + var maxV int + var maxC uint32 + for i, cnt := range s.count[:s.symbolLen] { + if cnt > maxC { + maxV = i + maxC = cnt + } + } + s.norm[maxV] += int16(toDistribute) + return nil + } + + if total == 0 { + // all of the symbols were low enough for the lowOne or lowThreshold + for i := uint32(0); toDistribute > 0; i = (i + 1) % (uint32(s.symbolLen)) { + if s.norm[i] > 0 { + toDistribute-- + s.norm[i]++ + } + } + return nil + } + + var ( + vStepLog = 62 - uint64(tableLog) + mid = uint64((1 << (vStepLog - 1)) - 1) + rStep = (((1 << vStepLog) * uint64(toDistribute)) + mid) / uint64(total) // scale on remaining + tmpTotal = mid + ) + for i, cnt := range s.count[:s.symbolLen] { + if s.norm[i] == notYetAssigned { + var ( + end = tmpTotal + uint64(cnt)*rStep + sStart = uint32(tmpTotal >> vStepLog) + sEnd = uint32(end >> vStepLog) + weight = sEnd - sStart + ) + if weight < 1 { + return errors.New("weight < 1") + } + s.norm[i] = int16(weight) + tmpTotal = end + } + } + return nil +} + +// validateNorm validates the normalized histogram table. +func (s *Scratch) validateNorm() (err error) { + var total int + for _, v := range s.norm[:s.symbolLen] { + if v >= 0 { + total += int(v) + } else { + total -= int(v) + } + } + defer func() { + if err == nil { + return + } + fmt.Printf("selected TableLog: %d, Symbol length: %d\n", s.actualTableLog, s.symbolLen) + for i, v := range s.norm[:s.symbolLen] { + fmt.Printf("%3d: %5d -> %4d \n", i, s.count[i], v) + } + }() + if total != (1 << s.actualTableLog) { + return fmt.Errorf("warning: Total == %d != %d", total, 1< tablelogAbsoluteMax { + return errors.New("tableLog too large") + } + bitStream >>= 4 + bitCount := uint(4) + + s.actualTableLog = uint8(nbBits) + remaining := int32((1 << nbBits) + 1) + threshold := int32(1 << nbBits) + gotTotal := int32(0) + nbBits++ + + for remaining > 1 { + if previous0 { + n0 := charnum + for (bitStream & 0xFFFF) == 0xFFFF { + n0 += 24 + if b.off < iend-5 { + b.advance(2) + bitStream = b.Uint32() >> bitCount + } else { + bitStream >>= 16 + bitCount += 16 + } + } + for (bitStream & 3) == 3 { + n0 += 3 + bitStream >>= 2 + bitCount += 2 + } + n0 += uint16(bitStream & 3) + bitCount += 2 + if n0 > maxSymbolValue { + return errors.New("maxSymbolValue too small") + } + for charnum < n0 { + s.norm[charnum&0xff] = 0 + charnum++ + } + + if b.off <= iend-7 || b.off+int(bitCount>>3) <= iend-4 { + b.advance(bitCount >> 3) + bitCount &= 7 + bitStream = b.Uint32() >> bitCount + } else { + bitStream >>= 2 + } + } + + max := (2*(threshold) - 1) - (remaining) + var count int32 + + if (int32(bitStream) & (threshold - 1)) < max { + count = int32(bitStream) & (threshold - 1) + bitCount += nbBits - 1 + } else { + count = int32(bitStream) & (2*threshold - 1) + if count >= threshold { + count -= max + } + bitCount += nbBits + } + + count-- // extra accuracy + if count < 0 { + // -1 means +1 + remaining += count + gotTotal -= count + } else { + remaining -= count + gotTotal += count + } + s.norm[charnum&0xff] = int16(count) + charnum++ + previous0 = count == 0 + for remaining < threshold { + nbBits-- + threshold >>= 1 + } + if b.off <= iend-7 || b.off+int(bitCount>>3) <= iend-4 { + b.advance(bitCount >> 3) + bitCount &= 7 + } else { + bitCount -= (uint)(8 * (len(b.b) - 4 - b.off)) + b.off = len(b.b) - 4 + } + bitStream = b.Uint32() >> (bitCount & 31) + } + s.symbolLen = charnum + + if s.symbolLen <= 1 { + return fmt.Errorf("symbolLen (%d) too small", s.symbolLen) + } + if s.symbolLen > maxSymbolValue+1 { + return fmt.Errorf("symbolLen (%d) too big", s.symbolLen) + } + if remaining != 1 { + return fmt.Errorf("corruption detected (remaining %d != 1)", remaining) + } + if bitCount > 32 { + return fmt.Errorf("corruption detected (bitCount %d > 32)", bitCount) + } + if gotTotal != 1<> 3) + return nil +} + +// decSymbol contains information about a state entry, +// Including the state offset base, the output symbol and +// the number of bits to read for the low part of the destination state. +type decSymbol struct { + newState uint16 + symbol uint8 + nbBits uint8 +} + +// allocDtable will allocate decoding tables if they are not big enough. +func (s *Scratch) allocDtable() { + tableSize := 1 << s.actualTableLog + if cap(s.decTable) < tableSize { + s.decTable = make([]decSymbol, tableSize) + } + s.decTable = s.decTable[:tableSize] + + if cap(s.ct.tableSymbol) < 256 { + s.ct.tableSymbol = make([]byte, 256) + } + s.ct.tableSymbol = s.ct.tableSymbol[:256] + + if cap(s.ct.stateTable) < 256 { + s.ct.stateTable = make([]uint16, 256) + } + s.ct.stateTable = s.ct.stateTable[:256] +} + +// buildDtable will build the decoding table. +func (s *Scratch) buildDtable() error { + tableSize := uint32(1 << s.actualTableLog) + highThreshold := tableSize - 1 + s.allocDtable() + symbolNext := s.ct.stateTable[:256] + + // Init, lay down lowprob symbols + s.zeroBits = false + { + largeLimit := int16(1 << (s.actualTableLog - 1)) + for i, v := range s.norm[:s.symbolLen] { + if v == -1 { + s.decTable[highThreshold].symbol = uint8(i) + highThreshold-- + symbolNext[i] = 1 + } else { + if v >= largeLimit { + s.zeroBits = true + } + symbolNext[i] = uint16(v) + } + } + } + // Spread symbols + { + tableMask := tableSize - 1 + step := tableStep(tableSize) + position := uint32(0) + for ss, v := range s.norm[:s.symbolLen] { + for i := 0; i < int(v); i++ { + s.decTable[position].symbol = uint8(ss) + position = (position + step) & tableMask + for position > highThreshold { + // lowprob area + position = (position + step) & tableMask + } + } + } + if position != 0 { + // position must reach all cells once, otherwise normalizedCounter is incorrect + return errors.New("corrupted input (position != 0)") + } + } + + // Build Decoding table + { + tableSize := uint16(1 << s.actualTableLog) + for u, v := range s.decTable { + symbol := v.symbol + nextState := symbolNext[symbol] + symbolNext[symbol] = nextState + 1 + nBits := s.actualTableLog - byte(highBits(uint32(nextState))) + s.decTable[u].nbBits = nBits + newState := (nextState << nBits) - tableSize + if newState >= tableSize { + return fmt.Errorf("newState (%d) outside table size (%d)", newState, tableSize) + } + if newState == uint16(u) && nBits == 0 { + // Seems weird that this is possible with nbits > 0. + return fmt.Errorf("newState (%d) == oldState (%d) and no bits", newState, u) + } + s.decTable[u].newState = newState + } + } + return nil +} + +// decompress will decompress the bitstream. +// If the buffer is over-read an error is returned. +func (s *Scratch) decompress() error { + br := &s.bits + if err := br.init(s.br.unread()); err != nil { + return err + } + + var s1, s2 decoder + // Initialize and decode first state and symbol. + s1.init(br, s.decTable, s.actualTableLog) + s2.init(br, s.decTable, s.actualTableLog) + + // Use temp table to avoid bound checks/append penalty. + var tmp = s.ct.tableSymbol[:256] + var off uint8 + + // Main part + if !s.zeroBits { + for br.off >= 8 { + br.fillFast() + tmp[off+0] = s1.nextFast() + tmp[off+1] = s2.nextFast() + br.fillFast() + tmp[off+2] = s1.nextFast() + tmp[off+3] = s2.nextFast() + off += 4 + // When off is 0, we have overflowed and should write. + if off == 0 { + s.Out = append(s.Out, tmp...) + if len(s.Out) >= s.DecompressLimit { + return fmt.Errorf("output size (%d) > DecompressLimit (%d)", len(s.Out), s.DecompressLimit) + } + } + } + } else { + for br.off >= 8 { + br.fillFast() + tmp[off+0] = s1.next() + tmp[off+1] = s2.next() + br.fillFast() + tmp[off+2] = s1.next() + tmp[off+3] = s2.next() + off += 4 + if off == 0 { + s.Out = append(s.Out, tmp...) + // When off is 0, we have overflowed and should write. + if len(s.Out) >= s.DecompressLimit { + return fmt.Errorf("output size (%d) > DecompressLimit (%d)", len(s.Out), s.DecompressLimit) + } + } + } + } + s.Out = append(s.Out, tmp[:off]...) + + // Final bits, a bit more expensive check + for { + if s1.finished() { + s.Out = append(s.Out, s1.final(), s2.final()) + break + } + br.fill() + s.Out = append(s.Out, s1.next()) + if s2.finished() { + s.Out = append(s.Out, s2.final(), s1.final()) + break + } + s.Out = append(s.Out, s2.next()) + if len(s.Out) >= s.DecompressLimit { + return fmt.Errorf("output size (%d) > DecompressLimit (%d)", len(s.Out), s.DecompressLimit) + } + } + return br.close() +} + +// decoder keeps track of the current state and updates it from the bitstream. +type decoder struct { + state uint16 + br *bitReader + dt []decSymbol +} + +// init will initialize the decoder and read the first state from the stream. +func (d *decoder) init(in *bitReader, dt []decSymbol, tableLog uint8) { + d.dt = dt + d.br = in + d.state = in.getBits(tableLog) +} + +// next returns the next symbol and sets the next state. +// At least tablelog bits must be available in the bit reader. +func (d *decoder) next() uint8 { + n := &d.dt[d.state] + lowBits := d.br.getBits(n.nbBits) + d.state = n.newState + lowBits + return n.symbol +} + +// finished returns true if all bits have been read from the bitstream +// and the next state would require reading bits from the input. +func (d *decoder) finished() bool { + return d.br.finished() && d.dt[d.state].nbBits > 0 +} + +// final returns the current state symbol without decoding the next. +func (d *decoder) final() uint8 { + return d.dt[d.state].symbol +} + +// nextFast returns the next symbol and sets the next state. +// This can only be used if no symbols are 0 bits. +// At least tablelog bits must be available in the bit reader. +func (d *decoder) nextFast() uint8 { + n := d.dt[d.state] + lowBits := d.br.getBitsFast(n.nbBits) + d.state = n.newState + lowBits + return n.symbol +} diff --git a/vendor/github.com/klauspost/compress/fse/fse.go b/vendor/github.com/klauspost/compress/fse/fse.go new file mode 100644 index 000000000..535cbadfd --- /dev/null +++ b/vendor/github.com/klauspost/compress/fse/fse.go @@ -0,0 +1,144 @@ +// Copyright 2018 Klaus Post. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. +// Based on work Copyright (c) 2013, Yann Collet, released under BSD License. + +// Package fse provides Finite State Entropy encoding and decoding. +// +// Finite State Entropy encoding provides a fast near-optimal symbol encoding/decoding +// for byte blocks as implemented in zstd. +// +// See https://github.com/klauspost/compress/tree/master/fse for more information. +package fse + +import ( + "errors" + "fmt" + "math/bits" +) + +const ( + /*!MEMORY_USAGE : + * Memory usage formula : N->2^N Bytes (examples : 10 -> 1KB; 12 -> 4KB ; 16 -> 64KB; 20 -> 1MB; etc.) + * Increasing memory usage improves compression ratio + * Reduced memory usage can improve speed, due to cache effect + * Recommended max value is 14, for 16KB, which nicely fits into Intel x86 L1 cache */ + maxMemoryUsage = 14 + defaultMemoryUsage = 13 + + maxTableLog = maxMemoryUsage - 2 + maxTablesize = 1 << maxTableLog + defaultTablelog = defaultMemoryUsage - 2 + minTablelog = 5 + maxSymbolValue = 255 +) + +var ( + // ErrIncompressible is returned when input is judged to be too hard to compress. + ErrIncompressible = errors.New("input is not compressible") + + // ErrUseRLE is returned from the compressor when the input is a single byte value repeated. + ErrUseRLE = errors.New("input is single value repeated") +) + +// Scratch provides temporary storage for compression and decompression. +type Scratch struct { + // Private + count [maxSymbolValue + 1]uint32 + norm [maxSymbolValue + 1]int16 + br byteReader + bits bitReader + bw bitWriter + ct cTable // Compression tables. + decTable []decSymbol // Decompression table. + maxCount int // count of the most probable symbol + + // Per block parameters. + // These can be used to override compression parameters of the block. + // Do not touch, unless you know what you are doing. + + // Out is output buffer. + // If the scratch is re-used before the caller is done processing the output, + // set this field to nil. + // Otherwise the output buffer will be re-used for next Compression/Decompression step + // and allocation will be avoided. + Out []byte + + // DecompressLimit limits the maximum decoded size acceptable. + // If > 0 decompression will stop when approximately this many bytes + // has been decoded. + // If 0, maximum size will be 2GB. + DecompressLimit int + + symbolLen uint16 // Length of active part of the symbol table. + actualTableLog uint8 // Selected tablelog. + zeroBits bool // no bits has prob > 50%. + clearCount bool // clear count + + // MaxSymbolValue will override the maximum symbol value of the next block. + MaxSymbolValue uint8 + + // TableLog will attempt to override the tablelog for the next block. + TableLog uint8 +} + +// Histogram allows to populate the histogram and skip that step in the compression, +// It otherwise allows to inspect the histogram when compression is done. +// To indicate that you have populated the histogram call HistogramFinished +// with the value of the highest populated symbol, as well as the number of entries +// in the most populated entry. These are accepted at face value. +// The returned slice will always be length 256. +func (s *Scratch) Histogram() []uint32 { + return s.count[:] +} + +// HistogramFinished can be called to indicate that the histogram has been populated. +// maxSymbol is the index of the highest set symbol of the next data segment. +// maxCount is the number of entries in the most populated entry. +// These are accepted at face value. +func (s *Scratch) HistogramFinished(maxSymbol uint8, maxCount int) { + s.maxCount = maxCount + s.symbolLen = uint16(maxSymbol) + 1 + s.clearCount = maxCount != 0 +} + +// prepare will prepare and allocate scratch tables used for both compression and decompression. +func (s *Scratch) prepare(in []byte) (*Scratch, error) { + if s == nil { + s = &Scratch{} + } + if s.MaxSymbolValue == 0 { + s.MaxSymbolValue = 255 + } + if s.TableLog == 0 { + s.TableLog = defaultTablelog + } + if s.TableLog > maxTableLog { + return nil, fmt.Errorf("tableLog (%d) > maxTableLog (%d)", s.TableLog, maxTableLog) + } + if cap(s.Out) == 0 { + s.Out = make([]byte, 0, len(in)) + } + if s.clearCount && s.maxCount == 0 { + for i := range s.count { + s.count[i] = 0 + } + s.clearCount = false + } + s.br.init(in) + if s.DecompressLimit == 0 { + // Max size 2GB. + s.DecompressLimit = (2 << 30) - 1 + } + + return s, nil +} + +// tableStep returns the next table index. +func tableStep(tableSize uint32) uint32 { + return (tableSize >> 1) + (tableSize >> 3) + 3 +} + +func highBits(val uint32) (n uint32) { + return uint32(bits.Len32(val) - 1) +} diff --git a/vendor/github.com/klauspost/compress/gen.sh b/vendor/github.com/klauspost/compress/gen.sh new file mode 100644 index 000000000..aff942205 --- /dev/null +++ b/vendor/github.com/klauspost/compress/gen.sh @@ -0,0 +1,4 @@ +#!/bin/sh + +cd s2/cmd/_s2sx/ || exit 1 +go generate . diff --git a/vendor/github.com/klauspost/compress/gzip/gunzip.go b/vendor/github.com/klauspost/compress/gzip/gunzip.go new file mode 100644 index 000000000..00a0a2c38 --- /dev/null +++ b/vendor/github.com/klauspost/compress/gzip/gunzip.go @@ -0,0 +1,380 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package gzip implements reading and writing of gzip format compressed files, +// as specified in RFC 1952. +package gzip + +import ( + "bufio" + "compress/gzip" + "encoding/binary" + "hash/crc32" + "io" + "time" + + "github.com/klauspost/compress/flate" +) + +const ( + gzipID1 = 0x1f + gzipID2 = 0x8b + gzipDeflate = 8 + flagText = 1 << 0 + flagHdrCrc = 1 << 1 + flagExtra = 1 << 2 + flagName = 1 << 3 + flagComment = 1 << 4 +) + +var ( + // ErrChecksum is returned when reading GZIP data that has an invalid checksum. + ErrChecksum = gzip.ErrChecksum + // ErrHeader is returned when reading GZIP data that has an invalid header. + ErrHeader = gzip.ErrHeader +) + +var le = binary.LittleEndian + +// noEOF converts io.EOF to io.ErrUnexpectedEOF. +func noEOF(err error) error { + if err == io.EOF { + return io.ErrUnexpectedEOF + } + return err +} + +// The gzip file stores a header giving metadata about the compressed file. +// That header is exposed as the fields of the Writer and Reader structs. +// +// Strings must be UTF-8 encoded and may only contain Unicode code points +// U+0001 through U+00FF, due to limitations of the GZIP file format. +type Header struct { + Comment string // comment + Extra []byte // "extra data" + ModTime time.Time // modification time + Name string // file name + OS byte // operating system type +} + +// A Reader is an io.Reader that can be read to retrieve +// uncompressed data from a gzip-format compressed file. +// +// In general, a gzip file can be a concatenation of gzip files, +// each with its own header. Reads from the Reader +// return the concatenation of the uncompressed data of each. +// Only the first header is recorded in the Reader fields. +// +// Gzip files store a length and checksum of the uncompressed data. +// The Reader will return a ErrChecksum when Read +// reaches the end of the uncompressed data if it does not +// have the expected length or checksum. Clients should treat data +// returned by Read as tentative until they receive the io.EOF +// marking the end of the data. +type Reader struct { + Header // valid after NewReader or Reader.Reset + r flate.Reader + br *bufio.Reader + decompressor io.ReadCloser + digest uint32 // CRC-32, IEEE polynomial (section 8) + size uint32 // Uncompressed size (section 2.3.1) + buf [512]byte + err error + multistream bool +} + +// NewReader creates a new Reader reading the given reader. +// If r does not also implement io.ByteReader, +// the decompressor may read more data than necessary from r. +// +// It is the caller's responsibility to call Close on the Reader when done. +// +// The Reader.Header fields will be valid in the Reader returned. +func NewReader(r io.Reader) (*Reader, error) { + z := new(Reader) + if err := z.Reset(r); err != nil { + return nil, err + } + return z, nil +} + +// Reset discards the Reader z's state and makes it equivalent to the +// result of its original state from NewReader, but reading from r instead. +// This permits reusing a Reader rather than allocating a new one. +func (z *Reader) Reset(r io.Reader) error { + *z = Reader{ + decompressor: z.decompressor, + multistream: true, + br: z.br, + } + if rr, ok := r.(flate.Reader); ok { + z.r = rr + } else { + // Reuse if we can. + if z.br != nil { + z.br.Reset(r) + } else { + z.br = bufio.NewReader(r) + } + z.r = z.br + } + z.Header, z.err = z.readHeader() + return z.err +} + +// Multistream controls whether the reader supports multistream files. +// +// If enabled (the default), the Reader expects the input to be a sequence +// of individually gzipped data streams, each with its own header and +// trailer, ending at EOF. The effect is that the concatenation of a sequence +// of gzipped files is treated as equivalent to the gzip of the concatenation +// of the sequence. This is standard behavior for gzip readers. +// +// Calling Multistream(false) disables this behavior; disabling the behavior +// can be useful when reading file formats that distinguish individual gzip +// data streams or mix gzip data streams with other data streams. +// In this mode, when the Reader reaches the end of the data stream, +// Read returns io.EOF. If the underlying reader implements io.ByteReader, +// it will be left positioned just after the gzip stream. +// To start the next stream, call z.Reset(r) followed by z.Multistream(false). +// If there is no next stream, z.Reset(r) will return io.EOF. +func (z *Reader) Multistream(ok bool) { + z.multistream = ok +} + +// readString reads a NUL-terminated string from z.r. +// It treats the bytes read as being encoded as ISO 8859-1 (Latin-1) and +// will output a string encoded using UTF-8. +// This method always updates z.digest with the data read. +func (z *Reader) readString() (string, error) { + var err error + needConv := false + for i := 0; ; i++ { + if i >= len(z.buf) { + return "", ErrHeader + } + z.buf[i], err = z.r.ReadByte() + if err != nil { + return "", err + } + if z.buf[i] > 0x7f { + needConv = true + } + if z.buf[i] == 0 { + // Digest covers the NUL terminator. + z.digest = crc32.Update(z.digest, crc32.IEEETable, z.buf[:i+1]) + + // Strings are ISO 8859-1, Latin-1 (RFC 1952, section 2.3.1). + if needConv { + s := make([]rune, 0, i) + for _, v := range z.buf[:i] { + s = append(s, rune(v)) + } + return string(s), nil + } + return string(z.buf[:i]), nil + } + } +} + +// readHeader reads the GZIP header according to section 2.3.1. +// This method does not set z.err. +func (z *Reader) readHeader() (hdr Header, err error) { + if _, err = io.ReadFull(z.r, z.buf[:10]); err != nil { + // RFC 1952, section 2.2, says the following: + // A gzip file consists of a series of "members" (compressed data sets). + // + // Other than this, the specification does not clarify whether a + // "series" is defined as "one or more" or "zero or more". To err on the + // side of caution, Go interprets this to mean "zero or more". + // Thus, it is okay to return io.EOF here. + return hdr, err + } + if z.buf[0] != gzipID1 || z.buf[1] != gzipID2 || z.buf[2] != gzipDeflate { + return hdr, ErrHeader + } + flg := z.buf[3] + hdr.ModTime = time.Unix(int64(le.Uint32(z.buf[4:8])), 0) + // z.buf[8] is XFL and is currently ignored. + hdr.OS = z.buf[9] + z.digest = crc32.ChecksumIEEE(z.buf[:10]) + + if flg&flagExtra != 0 { + if _, err = io.ReadFull(z.r, z.buf[:2]); err != nil { + return hdr, noEOF(err) + } + z.digest = crc32.Update(z.digest, crc32.IEEETable, z.buf[:2]) + data := make([]byte, le.Uint16(z.buf[:2])) + if _, err = io.ReadFull(z.r, data); err != nil { + return hdr, noEOF(err) + } + z.digest = crc32.Update(z.digest, crc32.IEEETable, data) + hdr.Extra = data + } + + var s string + if flg&flagName != 0 { + if s, err = z.readString(); err != nil { + return hdr, err + } + hdr.Name = s + } + + if flg&flagComment != 0 { + if s, err = z.readString(); err != nil { + return hdr, err + } + hdr.Comment = s + } + + if flg&flagHdrCrc != 0 { + if _, err = io.ReadFull(z.r, z.buf[:2]); err != nil { + return hdr, noEOF(err) + } + digest := le.Uint16(z.buf[:2]) + if digest != uint16(z.digest) { + return hdr, ErrHeader + } + } + + // Reserved FLG bits must be zero. + if flg>>5 != 0 { + return hdr, ErrHeader + } + + z.digest = 0 + if z.decompressor == nil { + z.decompressor = flate.NewReader(z.r) + } else { + z.decompressor.(flate.Resetter).Reset(z.r, nil) + } + return hdr, nil +} + +// Read implements io.Reader, reading uncompressed bytes from its underlying Reader. +func (z *Reader) Read(p []byte) (n int, err error) { + if z.err != nil { + return 0, z.err + } + + for n == 0 { + n, z.err = z.decompressor.Read(p) + z.digest = crc32.Update(z.digest, crc32.IEEETable, p[:n]) + z.size += uint32(n) + if z.err != io.EOF { + // In the normal case we return here. + return n, z.err + } + + // Finished file; check checksum and size. + if _, err := io.ReadFull(z.r, z.buf[:8]); err != nil { + z.err = noEOF(err) + return n, z.err + } + digest := le.Uint32(z.buf[:4]) + size := le.Uint32(z.buf[4:8]) + if digest != z.digest || size != z.size { + z.err = ErrChecksum + return n, z.err + } + z.digest, z.size = 0, 0 + + // File is ok; check if there is another. + if !z.multistream { + return n, io.EOF + } + z.err = nil // Remove io.EOF + + if _, z.err = z.readHeader(); z.err != nil { + return n, z.err + } + } + + return n, nil +} + +type crcer interface { + io.Writer + Sum32() uint32 + Reset() +} +type crcUpdater struct { + z *Reader +} + +func (c *crcUpdater) Write(p []byte) (int, error) { + c.z.digest = crc32.Update(c.z.digest, crc32.IEEETable, p) + return len(p), nil +} + +func (c *crcUpdater) Sum32() uint32 { + return c.z.digest +} + +func (c *crcUpdater) Reset() { + c.z.digest = 0 +} + +// WriteTo support the io.WriteTo interface for io.Copy and friends. +func (z *Reader) WriteTo(w io.Writer) (int64, error) { + total := int64(0) + crcWriter := crcer(crc32.NewIEEE()) + if z.digest != 0 { + crcWriter = &crcUpdater{z: z} + } + for { + if z.err != nil { + if z.err == io.EOF { + return total, nil + } + return total, z.err + } + + // We write both to output and digest. + mw := io.MultiWriter(w, crcWriter) + n, err := z.decompressor.(io.WriterTo).WriteTo(mw) + total += n + z.size += uint32(n) + if err != nil { + z.err = err + return total, z.err + } + + // Finished file; check checksum + size. + if _, err := io.ReadFull(z.r, z.buf[0:8]); err != nil { + if err == io.EOF { + err = io.ErrUnexpectedEOF + } + z.err = err + return total, err + } + z.digest = crcWriter.Sum32() + digest := le.Uint32(z.buf[:4]) + size := le.Uint32(z.buf[4:8]) + if digest != z.digest || size != z.size { + z.err = ErrChecksum + return total, z.err + } + z.digest, z.size = 0, 0 + + // File is ok; check if there is another. + if !z.multistream { + return total, nil + } + crcWriter.Reset() + z.err = nil // Remove io.EOF + + if _, z.err = z.readHeader(); z.err != nil { + if z.err == io.EOF { + return total, nil + } + return total, z.err + } + } +} + +// Close closes the Reader. It does not close the underlying io.Reader. +// In order for the GZIP checksum to be verified, the reader must be +// fully consumed until the io.EOF. +func (z *Reader) Close() error { return z.decompressor.Close() } diff --git a/vendor/github.com/klauspost/compress/gzip/gzip.go b/vendor/github.com/klauspost/compress/gzip/gzip.go new file mode 100644 index 000000000..5bc720593 --- /dev/null +++ b/vendor/github.com/klauspost/compress/gzip/gzip.go @@ -0,0 +1,290 @@ +// Copyright 2010 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package gzip + +import ( + "errors" + "fmt" + "hash/crc32" + "io" + + "github.com/klauspost/compress/flate" +) + +// These constants are copied from the flate package, so that code that imports +// "compress/gzip" does not also have to import "compress/flate". +const ( + NoCompression = flate.NoCompression + BestSpeed = flate.BestSpeed + BestCompression = flate.BestCompression + DefaultCompression = flate.DefaultCompression + ConstantCompression = flate.ConstantCompression + HuffmanOnly = flate.HuffmanOnly + + // StatelessCompression will do compression but without maintaining any state + // between Write calls. + // There will be no memory kept between Write calls, + // but compression and speed will be suboptimal. + // Because of this, the size of actual Write calls will affect output size. + StatelessCompression = -3 +) + +// A Writer is an io.WriteCloser. +// Writes to a Writer are compressed and written to w. +type Writer struct { + Header // written at first call to Write, Flush, or Close + w io.Writer + level int + err error + compressor *flate.Writer + digest uint32 // CRC-32, IEEE polynomial (section 8) + size uint32 // Uncompressed size (section 2.3.1) + wroteHeader bool + closed bool + buf [10]byte +} + +// NewWriter returns a new Writer. +// Writes to the returned writer are compressed and written to w. +// +// It is the caller's responsibility to call Close on the WriteCloser when done. +// Writes may be buffered and not flushed until Close. +// +// Callers that wish to set the fields in Writer.Header must do so before +// the first call to Write, Flush, or Close. +func NewWriter(w io.Writer) *Writer { + z, _ := NewWriterLevel(w, DefaultCompression) + return z +} + +// NewWriterLevel is like NewWriter but specifies the compression level instead +// of assuming DefaultCompression. +// +// The compression level can be DefaultCompression, NoCompression, or any +// integer value between BestSpeed and BestCompression inclusive. The error +// returned will be nil if the level is valid. +func NewWriterLevel(w io.Writer, level int) (*Writer, error) { + if level < StatelessCompression || level > BestCompression { + return nil, fmt.Errorf("gzip: invalid compression level: %d", level) + } + z := new(Writer) + z.init(w, level) + return z, nil +} + +// MinCustomWindowSize is the minimum window size that can be sent to NewWriterWindow. +const MinCustomWindowSize = flate.MinCustomWindowSize + +// MaxCustomWindowSize is the maximum custom window that can be sent to NewWriterWindow. +const MaxCustomWindowSize = flate.MaxCustomWindowSize + +// NewWriterWindow returns a new Writer compressing data with a custom window size. +// windowSize must be from MinCustomWindowSize to MaxCustomWindowSize. +func NewWriterWindow(w io.Writer, windowSize int) (*Writer, error) { + if windowSize < MinCustomWindowSize { + return nil, errors.New("gzip: requested window size less than MinWindowSize") + } + if windowSize > MaxCustomWindowSize { + return nil, errors.New("gzip: requested window size bigger than MaxCustomWindowSize") + } + + z := new(Writer) + z.init(w, -windowSize) + return z, nil +} + +func (z *Writer) init(w io.Writer, level int) { + compressor := z.compressor + if level != StatelessCompression { + if compressor != nil { + compressor.Reset(w) + } + } + + *z = Writer{ + Header: Header{ + OS: 255, // unknown + }, + w: w, + level: level, + compressor: compressor, + } +} + +// Reset discards the Writer z's state and makes it equivalent to the +// result of its original state from NewWriter or NewWriterLevel, but +// writing to w instead. This permits reusing a Writer rather than +// allocating a new one. +func (z *Writer) Reset(w io.Writer) { + z.init(w, z.level) +} + +// writeBytes writes a length-prefixed byte slice to z.w. +func (z *Writer) writeBytes(b []byte) error { + if len(b) > 0xffff { + return errors.New("gzip.Write: Extra data is too large") + } + le.PutUint16(z.buf[:2], uint16(len(b))) + _, err := z.w.Write(z.buf[:2]) + if err != nil { + return err + } + _, err = z.w.Write(b) + return err +} + +// writeString writes a UTF-8 string s in GZIP's format to z.w. +// GZIP (RFC 1952) specifies that strings are NUL-terminated ISO 8859-1 (Latin-1). +func (z *Writer) writeString(s string) (err error) { + // GZIP stores Latin-1 strings; error if non-Latin-1; convert if non-ASCII. + needconv := false + for _, v := range s { + if v == 0 || v > 0xff { + return errors.New("gzip.Write: non-Latin-1 header string") + } + if v > 0x7f { + needconv = true + } + } + if needconv { + b := make([]byte, 0, len(s)) + for _, v := range s { + b = append(b, byte(v)) + } + _, err = z.w.Write(b) + } else { + _, err = io.WriteString(z.w, s) + } + if err != nil { + return err + } + // GZIP strings are NUL-terminated. + z.buf[0] = 0 + _, err = z.w.Write(z.buf[:1]) + return err +} + +// Write writes a compressed form of p to the underlying io.Writer. The +// compressed bytes are not necessarily flushed until the Writer is closed. +func (z *Writer) Write(p []byte) (int, error) { + if z.err != nil { + return 0, z.err + } + var n int + // Write the GZIP header lazily. + if !z.wroteHeader { + z.wroteHeader = true + z.buf[0] = gzipID1 + z.buf[1] = gzipID2 + z.buf[2] = gzipDeflate + z.buf[3] = 0 + if z.Extra != nil { + z.buf[3] |= 0x04 + } + if z.Name != "" { + z.buf[3] |= 0x08 + } + if z.Comment != "" { + z.buf[3] |= 0x10 + } + le.PutUint32(z.buf[4:8], uint32(z.ModTime.Unix())) + if z.level == BestCompression { + z.buf[8] = 2 + } else if z.level == BestSpeed { + z.buf[8] = 4 + } else { + z.buf[8] = 0 + } + z.buf[9] = z.OS + n, z.err = z.w.Write(z.buf[:10]) + if z.err != nil { + return n, z.err + } + if z.Extra != nil { + z.err = z.writeBytes(z.Extra) + if z.err != nil { + return n, z.err + } + } + if z.Name != "" { + z.err = z.writeString(z.Name) + if z.err != nil { + return n, z.err + } + } + if z.Comment != "" { + z.err = z.writeString(z.Comment) + if z.err != nil { + return n, z.err + } + } + + if z.compressor == nil && z.level != StatelessCompression { + z.compressor, _ = flate.NewWriter(z.w, z.level) + } + } + z.size += uint32(len(p)) + z.digest = crc32.Update(z.digest, crc32.IEEETable, p) + if z.level == StatelessCompression { + return len(p), flate.StatelessDeflate(z.w, p, false, nil) + } + n, z.err = z.compressor.Write(p) + return n, z.err +} + +// Flush flushes any pending compressed data to the underlying writer. +// +// It is useful mainly in compressed network protocols, to ensure that +// a remote reader has enough data to reconstruct a packet. Flush does +// not return until the data has been written. If the underlying +// writer returns an error, Flush returns that error. +// +// In the terminology of the zlib library, Flush is equivalent to Z_SYNC_FLUSH. +func (z *Writer) Flush() error { + if z.err != nil { + return z.err + } + if z.closed || z.level == StatelessCompression { + return nil + } + if !z.wroteHeader { + z.Write(nil) + if z.err != nil { + return z.err + } + } + z.err = z.compressor.Flush() + return z.err +} + +// Close closes the Writer, flushing any unwritten data to the underlying +// io.Writer, but does not close the underlying io.Writer. +func (z *Writer) Close() error { + if z.err != nil { + return z.err + } + if z.closed { + return nil + } + z.closed = true + if !z.wroteHeader { + z.Write(nil) + if z.err != nil { + return z.err + } + } + if z.level == StatelessCompression { + z.err = flate.StatelessDeflate(z.w, nil, true, nil) + } else { + z.err = z.compressor.Close() + } + if z.err != nil { + return z.err + } + le.PutUint32(z.buf[:4], z.digest) + le.PutUint32(z.buf[4:8], z.size) + _, z.err = z.w.Write(z.buf[:8]) + return z.err +} diff --git a/vendor/github.com/klauspost/compress/huff0/.gitignore b/vendor/github.com/klauspost/compress/huff0/.gitignore new file mode 100644 index 000000000..b3d262958 --- /dev/null +++ b/vendor/github.com/klauspost/compress/huff0/.gitignore @@ -0,0 +1 @@ +/huff0-fuzz.zip diff --git a/vendor/github.com/klauspost/compress/huff0/README.md b/vendor/github.com/klauspost/compress/huff0/README.md new file mode 100644 index 000000000..26d5101b3 --- /dev/null +++ b/vendor/github.com/klauspost/compress/huff0/README.md @@ -0,0 +1,89 @@ +# Huff0 entropy compression + +This package provides Huff0 encoding and decoding as used in zstd. + +[Huff0](https://github.com/Cyan4973/FiniteStateEntropy#new-generation-entropy-coders), +a Huffman codec designed for modern CPU, featuring OoO (Out of Order) operations on multiple ALU +(Arithmetic Logic Unit), achieving extremely fast compression and decompression speeds. + +This can be used for compressing input with a lot of similar input values to the smallest number of bytes. +This does not perform any multi-byte [dictionary coding](https://en.wikipedia.org/wiki/Dictionary_coder) as LZ coders, +but it can be used as a secondary step to compressors (like Snappy) that does not do entropy encoding. + +* [Godoc documentation](https://godoc.org/github.com/klauspost/compress/huff0) + +## News + +This is used as part of the [zstandard](https://github.com/klauspost/compress/tree/master/zstd#zstd) compression and decompression package. + +This ensures that most functionality is well tested. + +# Usage + +This package provides a low level interface that allows to compress single independent blocks. + +Each block is separate, and there is no built in integrity checks. +This means that the caller should keep track of block sizes and also do checksums if needed. + +Compressing a block is done via the [`Compress1X`](https://godoc.org/github.com/klauspost/compress/huff0#Compress1X) and +[`Compress4X`](https://godoc.org/github.com/klauspost/compress/huff0#Compress4X) functions. +You must provide input and will receive the output and maybe an error. + +These error values can be returned: + +| Error | Description | +|---------------------|-----------------------------------------------------------------------------| +| `` | Everything ok, output is returned | +| `ErrIncompressible` | Returned when input is judged to be too hard to compress | +| `ErrUseRLE` | Returned from the compressor when the input is a single byte value repeated | +| `ErrTooBig` | Returned if the input block exceeds the maximum allowed size (128 Kib) | +| `(error)` | An internal error occurred. | + + +As can be seen above some of there are errors that will be returned even under normal operation so it is important to handle these. + +To reduce allocations you can provide a [`Scratch`](https://godoc.org/github.com/klauspost/compress/huff0#Scratch) object +that can be re-used for successive calls. Both compression and decompression accepts a `Scratch` object, and the same +object can be used for both. + +Be aware, that when re-using a `Scratch` object that the *output* buffer is also re-used, so if you are still using this +you must set the `Out` field in the scratch to nil. The same buffer is used for compression and decompression output. + +The `Scratch` object will retain state that allows to re-use previous tables for encoding and decoding. + +## Tables and re-use + +Huff0 allows for reusing tables from the previous block to save space if that is expected to give better/faster results. + +The Scratch object allows you to set a [`ReusePolicy`](https://godoc.org/github.com/klauspost/compress/huff0#ReusePolicy) +that controls this behaviour. See the documentation for details. This can be altered between each block. + +Do however note that this information is *not* stored in the output block and it is up to the users of the package to +record whether [`ReadTable`](https://godoc.org/github.com/klauspost/compress/huff0#ReadTable) should be called, +based on the boolean reported back from the CompressXX call. + +If you want to store the table separate from the data, you can access them as `OutData` and `OutTable` on the +[`Scratch`](https://godoc.org/github.com/klauspost/compress/huff0#Scratch) object. + +## Decompressing + +The first part of decoding is to initialize the decoding table through [`ReadTable`](https://godoc.org/github.com/klauspost/compress/huff0#ReadTable). +This will initialize the decoding tables. +You can supply the complete block to `ReadTable` and it will return the data part of the block +which can be given to the decompressor. + +Decompressing is done by calling the [`Decompress1X`](https://godoc.org/github.com/klauspost/compress/huff0#Scratch.Decompress1X) +or [`Decompress4X`](https://godoc.org/github.com/klauspost/compress/huff0#Scratch.Decompress4X) function. + +For concurrently decompressing content with a fixed table a stateless [`Decoder`](https://godoc.org/github.com/klauspost/compress/huff0#Decoder) can be requested which will remain correct as long as the scratch is unchanged. The capacity of the provided slice indicates the expected output size. + +You must provide the output from the compression stage, at exactly the size you got back. If you receive an error back +your input was likely corrupted. + +It is important to note that a successful decoding does *not* mean your output matches your original input. +There are no integrity checks, so relying on errors from the decompressor does not assure your data is valid. + +# Contributing + +Contributions are always welcome. Be aware that adding public functions will require good justification and breaking +changes will likely not be accepted. If in doubt open an issue before writing the PR. diff --git a/vendor/github.com/klauspost/compress/huff0/bitreader.go b/vendor/github.com/klauspost/compress/huff0/bitreader.go new file mode 100644 index 000000000..bfc7a523d --- /dev/null +++ b/vendor/github.com/klauspost/compress/huff0/bitreader.go @@ -0,0 +1,224 @@ +// Copyright 2018 Klaus Post. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. +// Based on work Copyright (c) 2013, Yann Collet, released under BSD License. + +package huff0 + +import ( + "errors" + "fmt" + "io" + + "github.com/klauspost/compress/internal/le" +) + +// bitReader reads a bitstream in reverse. +// The last set bit indicates the start of the stream and is used +// for aligning the input. +type bitReaderBytes struct { + in []byte + off uint // next byte to read is at in[off - 1] + value uint64 + bitsRead uint8 +} + +// init initializes and resets the bit reader. +func (b *bitReaderBytes) init(in []byte) error { + if len(in) < 1 { + return errors.New("corrupt stream: too short") + } + b.in = in + b.off = uint(len(in)) + // The highest bit of the last byte indicates where to start + v := in[len(in)-1] + if v == 0 { + return errors.New("corrupt stream, did not find end of stream") + } + b.bitsRead = 64 + b.value = 0 + if len(in) >= 8 { + b.fillFastStart() + } else { + b.fill() + b.fill() + } + b.advance(8 - uint8(highBit32(uint32(v)))) + return nil +} + +// peekByteFast requires that at least one byte is requested every time. +// There are no checks if the buffer is filled. +func (b *bitReaderBytes) peekByteFast() uint8 { + got := uint8(b.value >> 56) + return got +} + +func (b *bitReaderBytes) advance(n uint8) { + b.bitsRead += n + b.value <<= n & 63 +} + +// fillFast() will make sure at least 32 bits are available. +// There must be at least 4 bytes available. +func (b *bitReaderBytes) fillFast() { + if b.bitsRead < 32 { + return + } + + // 2 bounds checks. + low := le.Load32(b.in, b.off-4) + b.value |= uint64(low) << (b.bitsRead - 32) + b.bitsRead -= 32 + b.off -= 4 +} + +// fillFastStart() assumes the bitReaderBytes is empty and there is at least 8 bytes to read. +func (b *bitReaderBytes) fillFastStart() { + // Do single re-slice to avoid bounds checks. + b.value = le.Load64(b.in, b.off-8) + b.bitsRead = 0 + b.off -= 8 +} + +// fill() will make sure at least 32 bits are available. +func (b *bitReaderBytes) fill() { + if b.bitsRead < 32 { + return + } + if b.off >= 4 { + low := le.Load32(b.in, b.off-4) + b.value |= uint64(low) << (b.bitsRead - 32) + b.bitsRead -= 32 + b.off -= 4 + return + } + for b.off > 0 { + b.value |= uint64(b.in[b.off-1]) << (b.bitsRead - 8) + b.bitsRead -= 8 + b.off-- + } +} + +// finished returns true if all bits have been read from the bit stream. +func (b *bitReaderBytes) finished() bool { + return b.off == 0 && b.bitsRead >= 64 +} + +func (b *bitReaderBytes) remaining() uint { + return b.off*8 + uint(64-b.bitsRead) +} + +// close the bitstream and returns an error if out-of-buffer reads occurred. +func (b *bitReaderBytes) close() error { + // Release reference. + b.in = nil + if b.remaining() > 0 { + return fmt.Errorf("corrupt input: %d bits remain on stream", b.remaining()) + } + if b.bitsRead > 64 { + return io.ErrUnexpectedEOF + } + return nil +} + +// bitReaderShifted reads a bitstream in reverse. +// The last set bit indicates the start of the stream and is used +// for aligning the input. +type bitReaderShifted struct { + in []byte + off uint // next byte to read is at in[off - 1] + value uint64 + bitsRead uint8 +} + +// init initializes and resets the bit reader. +func (b *bitReaderShifted) init(in []byte) error { + if len(in) < 1 { + return errors.New("corrupt stream: too short") + } + b.in = in + b.off = uint(len(in)) + // The highest bit of the last byte indicates where to start + v := in[len(in)-1] + if v == 0 { + return errors.New("corrupt stream, did not find end of stream") + } + b.bitsRead = 64 + b.value = 0 + if len(in) >= 8 { + b.fillFastStart() + } else { + b.fill() + b.fill() + } + b.advance(8 - uint8(highBit32(uint32(v)))) + return nil +} + +// peekBitsFast requires that at least one bit is requested every time. +// There are no checks if the buffer is filled. +func (b *bitReaderShifted) peekBitsFast(n uint8) uint16 { + return uint16(b.value >> ((64 - n) & 63)) +} + +func (b *bitReaderShifted) advance(n uint8) { + b.bitsRead += n + b.value <<= n & 63 +} + +// fillFast() will make sure at least 32 bits are available. +// There must be at least 4 bytes available. +func (b *bitReaderShifted) fillFast() { + if b.bitsRead < 32 { + return + } + + low := le.Load32(b.in, b.off-4) + b.value |= uint64(low) << ((b.bitsRead - 32) & 63) + b.bitsRead -= 32 + b.off -= 4 +} + +// fillFastStart() assumes the bitReaderShifted is empty and there is at least 8 bytes to read. +func (b *bitReaderShifted) fillFastStart() { + b.value = le.Load64(b.in, b.off-8) + b.bitsRead = 0 + b.off -= 8 +} + +// fill() will make sure at least 32 bits are available. +func (b *bitReaderShifted) fill() { + if b.bitsRead < 32 { + return + } + if b.off > 4 { + low := le.Load32(b.in, b.off-4) + b.value |= uint64(low) << ((b.bitsRead - 32) & 63) + b.bitsRead -= 32 + b.off -= 4 + return + } + for b.off > 0 { + b.value |= uint64(b.in[b.off-1]) << ((b.bitsRead - 8) & 63) + b.bitsRead -= 8 + b.off-- + } +} + +func (b *bitReaderShifted) remaining() uint { + return b.off*8 + uint(64-b.bitsRead) +} + +// close the bitstream and returns an error if out-of-buffer reads occurred. +func (b *bitReaderShifted) close() error { + // Release reference. + b.in = nil + if b.remaining() > 0 { + return fmt.Errorf("corrupt input: %d bits remain on stream", b.remaining()) + } + if b.bitsRead > 64 { + return io.ErrUnexpectedEOF + } + return nil +} diff --git a/vendor/github.com/klauspost/compress/huff0/bitwriter.go b/vendor/github.com/klauspost/compress/huff0/bitwriter.go new file mode 100644 index 000000000..41db94cde --- /dev/null +++ b/vendor/github.com/klauspost/compress/huff0/bitwriter.go @@ -0,0 +1,102 @@ +// Copyright 2018 Klaus Post. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. +// Based on work Copyright (c) 2013, Yann Collet, released under BSD License. + +package huff0 + +// bitWriter will write bits. +// First bit will be LSB of the first byte of output. +type bitWriter struct { + bitContainer uint64 + nBits uint8 + out []byte +} + +// addBits16Clean will add up to 16 bits. value may not contain more set bits than indicated. +// It will not check if there is space for them, so the caller must ensure that it has flushed recently. +func (b *bitWriter) addBits16Clean(value uint16, bits uint8) { + b.bitContainer |= uint64(value) << (b.nBits & 63) + b.nBits += bits +} + +// encSymbol will add up to 16 bits. value may not contain more set bits than indicated. +// It will not check if there is space for them, so the caller must ensure that it has flushed recently. +func (b *bitWriter) encSymbol(ct cTable, symbol byte) { + enc := ct[symbol] + b.bitContainer |= uint64(enc.val) << (b.nBits & 63) + if false { + if enc.nBits == 0 { + panic("nbits 0") + } + } + b.nBits += enc.nBits +} + +// encTwoSymbols will add up to 32 bits. value may not contain more set bits than indicated. +// It will not check if there is space for them, so the caller must ensure that it has flushed recently. +func (b *bitWriter) encTwoSymbols(ct cTable, av, bv byte) { + encA := ct[av] + encB := ct[bv] + sh := b.nBits & 63 + combined := uint64(encA.val) | (uint64(encB.val) << (encA.nBits & 63)) + b.bitContainer |= combined << sh + if false { + if encA.nBits == 0 { + panic("nbitsA 0") + } + if encB.nBits == 0 { + panic("nbitsB 0") + } + } + b.nBits += encA.nBits + encB.nBits +} + +// encFourSymbols adds up to 32 bits from four symbols. +// It will not check if there is space for them, +// so the caller must ensure that b has been flushed recently. +func (b *bitWriter) encFourSymbols(encA, encB, encC, encD cTableEntry) { + bitsA := encA.nBits + bitsB := bitsA + encB.nBits + bitsC := bitsB + encC.nBits + bitsD := bitsC + encD.nBits + combined := uint64(encA.val) | + (uint64(encB.val) << (bitsA & 63)) | + (uint64(encC.val) << (bitsB & 63)) | + (uint64(encD.val) << (bitsC & 63)) + b.bitContainer |= combined << (b.nBits & 63) + b.nBits += bitsD +} + +// flush32 will flush out, so there are at least 32 bits available for writing. +func (b *bitWriter) flush32() { + if b.nBits < 32 { + return + } + b.out = append(b.out, + byte(b.bitContainer), + byte(b.bitContainer>>8), + byte(b.bitContainer>>16), + byte(b.bitContainer>>24)) + b.nBits -= 32 + b.bitContainer >>= 32 +} + +// flushAlign will flush remaining full bytes and align to next byte boundary. +func (b *bitWriter) flushAlign() { + nbBytes := (b.nBits + 7) >> 3 + for i := range nbBytes { + b.out = append(b.out, byte(b.bitContainer>>(i*8))) + } + b.nBits = 0 + b.bitContainer = 0 +} + +// close will write the alignment bit and write the final byte(s) +// to the output. +func (b *bitWriter) close() { + // End mark + b.addBits16Clean(1, 1) + // flush until next byte. + b.flushAlign() +} diff --git a/vendor/github.com/klauspost/compress/huff0/compress.go b/vendor/github.com/klauspost/compress/huff0/compress.go new file mode 100644 index 000000000..a97cf1b5d --- /dev/null +++ b/vendor/github.com/klauspost/compress/huff0/compress.go @@ -0,0 +1,742 @@ +package huff0 + +import ( + "fmt" + "math" + "runtime" + "sync" +) + +// Compress1X will compress the input. +// The output can be decoded using Decompress1X. +// Supply a Scratch object. The scratch object contains state about re-use, +// So when sharing across independent encodes, be sure to set the re-use policy. +func Compress1X(in []byte, s *Scratch) (out []byte, reUsed bool, err error) { + s, err = s.prepare(in) + if err != nil { + return nil, false, err + } + return compress(in, s, s.compress1X) +} + +// Compress4X will compress the input. The input is split into 4 independent blocks +// and compressed similar to Compress1X. +// The output can be decoded using Decompress4X. +// Supply a Scratch object. The scratch object contains state about re-use, +// So when sharing across independent encodes, be sure to set the re-use policy. +func Compress4X(in []byte, s *Scratch) (out []byte, reUsed bool, err error) { + s, err = s.prepare(in) + if err != nil { + return nil, false, err + } + if false { + // TODO: compress4Xp only slightly faster. + const parallelThreshold = 8 << 10 + if len(in) < parallelThreshold || runtime.GOMAXPROCS(0) == 1 { + return compress(in, s, s.compress4X) + } + return compress(in, s, s.compress4Xp) + } + return compress(in, s, s.compress4X) +} + +func compress(in []byte, s *Scratch, compressor func(src []byte) ([]byte, error)) (out []byte, reUsed bool, err error) { + // Nuke previous table if we cannot reuse anyway. + if s.Reuse == ReusePolicyNone { + s.prevTable = s.prevTable[:0] + } + + // Create histogram, if none was provided. + maxCount := s.maxCount + var canReuse = false + if maxCount == 0 { + maxCount, canReuse = s.countSimple(in) + } else { + canReuse = s.canUseTable(s.prevTable) + } + + // We want the output size to be less than this: + wantSize := len(in) + if s.WantLogLess > 0 { + wantSize -= wantSize >> s.WantLogLess + } + + // Reset for next run. + s.clearCount = true + s.maxCount = 0 + if maxCount >= len(in) { + if maxCount > len(in) { + return nil, false, fmt.Errorf("maxCount (%d) > length (%d)", maxCount, len(in)) + } + if len(in) == 1 { + return nil, false, ErrIncompressible + } + // One symbol, use RLE + return nil, false, ErrUseRLE + } + if maxCount == 1 || maxCount < (len(in)>>7) { + // Each symbol present maximum once or too well distributed. + return nil, false, ErrIncompressible + } + if s.Reuse == ReusePolicyMust && !canReuse { + // We must reuse, but we can't. + return nil, false, ErrIncompressible + } + if (s.Reuse == ReusePolicyPrefer || s.Reuse == ReusePolicyMust) && canReuse { + keepTable := s.cTable + keepTL := s.actualTableLog + s.cTable = s.prevTable + s.actualTableLog = s.prevTableLog + s.Out, err = compressor(in) + s.cTable = keepTable + s.actualTableLog = keepTL + if err == nil && len(s.Out) < wantSize { + s.OutData = s.Out + return s.Out, true, nil + } + if s.Reuse == ReusePolicyMust { + return nil, false, ErrIncompressible + } + // Do not attempt to re-use later. + s.prevTable = s.prevTable[:0] + } + + // Calculate new table. + err = s.buildCTable() + if err != nil { + return nil, false, err + } + + if false && !s.canUseTable(s.cTable) { + panic("invalid table generated") + } + + if s.Reuse == ReusePolicyAllow && canReuse { + hSize := len(s.Out) + oldSize := s.prevTable.estimateSize(s.count[:s.symbolLen]) + newSize := s.cTable.estimateSize(s.count[:s.symbolLen]) + if oldSize <= hSize+newSize || hSize+12 >= wantSize { + // Retain cTable even if we re-use. + keepTable := s.cTable + keepTL := s.actualTableLog + + s.cTable = s.prevTable + s.actualTableLog = s.prevTableLog + s.Out, err = compressor(in) + + // Restore ctable. + s.cTable = keepTable + s.actualTableLog = keepTL + if err != nil { + return nil, false, err + } + if len(s.Out) >= wantSize { + return nil, false, ErrIncompressible + } + s.OutData = s.Out + return s.Out, true, nil + } + } + + // Use new table + err = s.cTable.write(s) + if err != nil { + s.OutTable = nil + return nil, false, err + } + s.OutTable = s.Out + + // Compress using new table + s.Out, err = compressor(in) + if err != nil { + s.OutTable = nil + return nil, false, err + } + if len(s.Out) >= wantSize { + s.OutTable = nil + return nil, false, ErrIncompressible + } + // Move current table into previous. + s.prevTable, s.prevTableLog, s.cTable = s.cTable, s.actualTableLog, s.prevTable[:0] + s.OutData = s.Out[len(s.OutTable):] + return s.Out, false, nil +} + +// EstimateSizes will estimate the data sizes +func EstimateSizes(in []byte, s *Scratch) (tableSz, dataSz, reuseSz int, err error) { + s, err = s.prepare(in) + if err != nil { + return 0, 0, 0, err + } + + // Create histogram, if none was provided. + tableSz, dataSz, reuseSz = -1, -1, -1 + maxCount := s.maxCount + var canReuse = false + if maxCount == 0 { + maxCount, canReuse = s.countSimple(in) + } else { + canReuse = s.canUseTable(s.prevTable) + } + + // We want the output size to be less than this: + wantSize := len(in) + if s.WantLogLess > 0 { + wantSize -= wantSize >> s.WantLogLess + } + + // Reset for next run. + s.clearCount = true + s.maxCount = 0 + if maxCount >= len(in) { + if maxCount > len(in) { + return 0, 0, 0, fmt.Errorf("maxCount (%d) > length (%d)", maxCount, len(in)) + } + if len(in) == 1 { + return 0, 0, 0, ErrIncompressible + } + // One symbol, use RLE + return 0, 0, 0, ErrUseRLE + } + if maxCount == 1 || maxCount < (len(in)>>7) { + // Each symbol present maximum once or too well distributed. + return 0, 0, 0, ErrIncompressible + } + + // Calculate new table. + err = s.buildCTable() + if err != nil { + return 0, 0, 0, err + } + + if false && !s.canUseTable(s.cTable) { + panic("invalid table generated") + } + + tableSz, err = s.cTable.estTableSize(s) + if err != nil { + return 0, 0, 0, err + } + if canReuse { + reuseSz = s.prevTable.estimateSize(s.count[:s.symbolLen]) + } + dataSz = s.cTable.estimateSize(s.count[:s.symbolLen]) + + // Restore + return tableSz, dataSz, reuseSz, nil +} + +func (s *Scratch) compress1X(src []byte) ([]byte, error) { + return s.compress1xDo(s.Out, src), nil +} + +func (s *Scratch) compress1xDo(dst, src []byte) []byte { + var bw = bitWriter{out: dst} + + // N is length divisible by 4. + n := len(src) + n -= n & 3 + cTable := s.cTable[:256] + + // Encode last bytes. + for i := len(src) & 3; i > 0; i-- { + bw.encSymbol(cTable, src[n+i-1]) + } + n -= 4 + if s.actualTableLog <= 8 { + for ; n >= 0; n -= 4 { + tmp := src[n : n+4] + // tmp should be len 4 + bw.flush32() + bw.encFourSymbols(cTable[tmp[3]], cTable[tmp[2]], cTable[tmp[1]], cTable[tmp[0]]) + } + } else { + for ; n >= 0; n -= 4 { + tmp := src[n : n+4] + // tmp should be len 4 + bw.flush32() + bw.encTwoSymbols(cTable, tmp[3], tmp[2]) + bw.flush32() + bw.encTwoSymbols(cTable, tmp[1], tmp[0]) + } + } + bw.close() + return bw.out +} + +var sixZeros [6]byte + +func (s *Scratch) compress4X(src []byte) ([]byte, error) { + if len(src) < 12 { + return nil, ErrIncompressible + } + segmentSize := (len(src) + 3) / 4 + + // Add placeholder for output length + offsetIdx := len(s.Out) + s.Out = append(s.Out, sixZeros[:]...) + + for i := range 4 { + toDo := src + if len(toDo) > segmentSize { + toDo = toDo[:segmentSize] + } + src = src[len(toDo):] + + idx := len(s.Out) + s.Out = s.compress1xDo(s.Out, toDo) + if len(s.Out)-idx > math.MaxUint16 { + // We cannot store the size in the jump table + return nil, ErrIncompressible + } + // Write compressed length as little endian before block. + if i < 3 { + // Last length is not written. + length := len(s.Out) - idx + s.Out[i*2+offsetIdx] = byte(length) + s.Out[i*2+offsetIdx+1] = byte(length >> 8) + } + } + + return s.Out, nil +} + +// compress4Xp will compress 4 streams using separate goroutines. +func (s *Scratch) compress4Xp(src []byte) ([]byte, error) { + if len(src) < 12 { + return nil, ErrIncompressible + } + // Add placeholder for output length + s.Out = s.Out[:6] + + segmentSize := (len(src) + 3) / 4 + var wg sync.WaitGroup + wg.Add(4) + for i := range 4 { + toDo := src + if len(toDo) > segmentSize { + toDo = toDo[:segmentSize] + } + src = src[len(toDo):] + + // Separate goroutine for each block. + go func(i int) { + s.tmpOut[i] = s.compress1xDo(s.tmpOut[i][:0], toDo) + wg.Done() + }(i) + } + wg.Wait() + for i := range 4 { + o := s.tmpOut[i] + if len(o) > math.MaxUint16 { + // We cannot store the size in the jump table + return nil, ErrIncompressible + } + // Write compressed length as little endian before block. + if i < 3 { + // Last length is not written. + s.Out[i*2] = byte(len(o)) + s.Out[i*2+1] = byte(len(o) >> 8) + } + + // Write output. + s.Out = append(s.Out, o...) + } + return s.Out, nil +} + +// countSimple will create a simple histogram in s.count. +// Returns the biggest count. +// Does not update s.clearCount. +func (s *Scratch) countSimple(in []byte) (max int, reuse bool) { + reuse = true + _ = s.count // Assert that s != nil to speed up the following loop. + for _, v := range in { + s.count[v]++ + } + m := uint32(0) + if len(s.prevTable) > 0 { + for i, v := range s.count[:] { + if v == 0 { + continue + } + if v > m { + m = v + } + s.symbolLen = uint16(i) + 1 + if i >= len(s.prevTable) { + reuse = false + } else if s.prevTable[i].nBits == 0 { + reuse = false + } + } + return int(m), reuse + } + for i, v := range s.count[:] { + if v == 0 { + continue + } + if v > m { + m = v + } + s.symbolLen = uint16(i) + 1 + } + return int(m), false +} + +func (s *Scratch) canUseTable(c cTable) bool { + if len(c) < int(s.symbolLen) { + return false + } + for i, v := range s.count[:s.symbolLen] { + if v != 0 && c[i].nBits == 0 { + return false + } + } + return true +} + +//lint:ignore U1000 used for debugging +func (s *Scratch) validateTable(c cTable) bool { + if len(c) < int(s.symbolLen) { + return false + } + for i, v := range s.count[:s.symbolLen] { + if v != 0 { + if c[i].nBits == 0 { + return false + } + if c[i].nBits > s.actualTableLog { + return false + } + } + } + return true +} + +// minTableLog provides the minimum logSize to safely represent a distribution. +func (s *Scratch) minTableLog() uint8 { + minBitsSrc := highBit32(uint32(s.srcLen)) + 1 + minBitsSymbols := highBit32(uint32(s.symbolLen-1)) + 2 + if minBitsSrc < minBitsSymbols { + return uint8(minBitsSrc) + } + return uint8(minBitsSymbols) +} + +// optimalTableLog calculates and sets the optimal tableLog in s.actualTableLog +func (s *Scratch) optimalTableLog() { + tableLog := s.TableLog + minBits := s.minTableLog() + maxBitsSrc := uint8(highBit32(uint32(s.srcLen-1))) - 1 + if maxBitsSrc < tableLog { + // Accuracy can be reduced + tableLog = maxBitsSrc + } + if minBits > tableLog { + tableLog = minBits + } + // Need a minimum to safely represent all symbol values + if tableLog < minTablelog { + tableLog = minTablelog + } + if tableLog > tableLogMax { + tableLog = tableLogMax + } + s.actualTableLog = tableLog +} + +type cTableEntry struct { + val uint16 + nBits uint8 + // We have 8 bits extra +} + +const huffNodesMask = huffNodesLen - 1 + +func (s *Scratch) buildCTable() error { + s.optimalTableLog() + s.huffSort() + if cap(s.cTable) < maxSymbolValue+1 { + s.cTable = make([]cTableEntry, s.symbolLen, maxSymbolValue+1) + } else { + s.cTable = s.cTable[:s.symbolLen] + for i := range s.cTable { + s.cTable[i] = cTableEntry{} + } + } + + var startNode = int16(s.symbolLen) + nonNullRank := s.symbolLen - 1 + + nodeNb := startNode + huffNode := s.nodes[1 : huffNodesLen+1] + + // This overlays the slice above, but allows "-1" index lookups. + // Different from reference implementation. + huffNode0 := s.nodes[0 : huffNodesLen+1] + + for huffNode[nonNullRank].count() == 0 { + nonNullRank-- + } + + lowS := int16(nonNullRank) + nodeRoot := nodeNb + lowS - 1 + lowN := nodeNb + huffNode[nodeNb].setCount(huffNode[lowS].count() + huffNode[lowS-1].count()) + huffNode[lowS].setParent(nodeNb) + huffNode[lowS-1].setParent(nodeNb) + nodeNb++ + lowS -= 2 + for n := nodeNb; n <= nodeRoot; n++ { + huffNode[n].setCount(1 << 30) + } + // fake entry, strong barrier + huffNode0[0].setCount(1 << 31) + + // create parents + for nodeNb <= nodeRoot { + var n1, n2 int16 + if huffNode0[lowS+1].count() < huffNode0[lowN+1].count() { + n1 = lowS + lowS-- + } else { + n1 = lowN + lowN++ + } + if huffNode0[lowS+1].count() < huffNode0[lowN+1].count() { + n2 = lowS + lowS-- + } else { + n2 = lowN + lowN++ + } + + huffNode[nodeNb].setCount(huffNode0[n1+1].count() + huffNode0[n2+1].count()) + huffNode0[n1+1].setParent(nodeNb) + huffNode0[n2+1].setParent(nodeNb) + nodeNb++ + } + + // distribute weights (unlimited tree height) + huffNode[nodeRoot].setNbBits(0) + for n := nodeRoot - 1; n >= startNode; n-- { + huffNode[n].setNbBits(huffNode[huffNode[n].parent()].nbBits() + 1) + } + for n := uint16(0); n <= nonNullRank; n++ { + huffNode[n].setNbBits(huffNode[huffNode[n].parent()].nbBits() + 1) + } + s.actualTableLog = s.setMaxHeight(int(nonNullRank)) + maxNbBits := s.actualTableLog + + // fill result into tree (val, nbBits) + if maxNbBits > tableLogMax { + return fmt.Errorf("internal error: maxNbBits (%d) > tableLogMax (%d)", maxNbBits, tableLogMax) + } + var nbPerRank [tableLogMax + 1]uint16 + var valPerRank [16]uint16 + for _, v := range huffNode[:nonNullRank+1] { + nbPerRank[v.nbBits()]++ + } + // determine stating value per rank + { + min := uint16(0) + for n := maxNbBits; n > 0; n-- { + // get starting value within each rank + valPerRank[n] = min + min += nbPerRank[n] + min >>= 1 + } + } + + // push nbBits per symbol, symbol order + for _, v := range huffNode[:nonNullRank+1] { + s.cTable[v.symbol()].nBits = v.nbBits() + } + + // assign value within rank, symbol order + t := s.cTable[:s.symbolLen] + for n, val := range t { + nbits := val.nBits & 15 + v := valPerRank[nbits] + t[n].val = v + valPerRank[nbits] = v + 1 + } + + return nil +} + +// huffSort will sort symbols, decreasing order. +func (s *Scratch) huffSort() { + type rankPos struct { + base uint32 + current uint32 + } + + // Clear nodes + nodes := s.nodes[:huffNodesLen+1] + s.nodes = nodes + nodes = nodes[1 : huffNodesLen+1] + + // Sort into buckets based on length of symbol count. + var rank [32]rankPos + for _, v := range s.count[:s.symbolLen] { + r := highBit32(v+1) & 31 + rank[r].base++ + } + // maxBitLength is log2(BlockSizeMax) + 1 + const maxBitLength = 18 + 1 + for n := maxBitLength; n > 0; n-- { + rank[n-1].base += rank[n].base + } + for n := range rank[:maxBitLength] { + rank[n].current = rank[n].base + } + for n, c := range s.count[:s.symbolLen] { + r := (highBit32(c+1) + 1) & 31 + pos := rank[r].current + rank[r].current++ + prev := nodes[(pos-1)&huffNodesMask] + for pos > rank[r].base && c > prev.count() { + nodes[pos&huffNodesMask] = prev + pos-- + prev = nodes[(pos-1)&huffNodesMask] + } + nodes[pos&huffNodesMask] = makeNodeElt(c, byte(n)) + } +} + +func (s *Scratch) setMaxHeight(lastNonNull int) uint8 { + maxNbBits := s.actualTableLog + huffNode := s.nodes[1 : huffNodesLen+1] + //huffNode = huffNode[: huffNodesLen] + + largestBits := huffNode[lastNonNull].nbBits() + + // early exit : no elt > maxNbBits + if largestBits <= maxNbBits { + return largestBits + } + totalCost := int(0) + baseCost := int(1) << (largestBits - maxNbBits) + n := uint32(lastNonNull) + + for huffNode[n].nbBits() > maxNbBits { + totalCost += baseCost - (1 << (largestBits - huffNode[n].nbBits())) + huffNode[n].setNbBits(maxNbBits) + n-- + } + // n stops at huffNode[n].nbBits <= maxNbBits + + for huffNode[n].nbBits() == maxNbBits { + n-- + } + // n end at index of smallest symbol using < maxNbBits + + // renorm totalCost + totalCost >>= largestBits - maxNbBits /* note : totalCost is necessarily a multiple of baseCost */ + + // repay normalized cost + { + const noSymbol = 0xF0F0F0F0 + var rankLast [tableLogMax + 2]uint32 + + for i := range rankLast[:] { + rankLast[i] = noSymbol + } + + // Get pos of last (smallest) symbol per rank + { + currentNbBits := maxNbBits + for pos := int(n); pos >= 0; pos-- { + if huffNode[pos].nbBits() >= currentNbBits { + continue + } + currentNbBits = huffNode[pos].nbBits() // < maxNbBits + rankLast[maxNbBits-currentNbBits] = uint32(pos) + } + } + + for totalCost > 0 { + nBitsToDecrease := uint8(highBit32(uint32(totalCost))) + 1 + + for ; nBitsToDecrease > 1; nBitsToDecrease-- { + highPos := rankLast[nBitsToDecrease] + lowPos := rankLast[nBitsToDecrease-1] + if highPos == noSymbol { + continue + } + if lowPos == noSymbol { + break + } + highTotal := huffNode[highPos].count() + lowTotal := 2 * huffNode[lowPos].count() + if highTotal <= lowTotal { + break + } + } + // only triggered when no more rank 1 symbol left => find closest one (note : there is necessarily at least one !) + // HUF_MAX_TABLELOG test just to please gcc 5+; but it should not be necessary + // FIXME: try to remove + for (nBitsToDecrease <= tableLogMax) && (rankLast[nBitsToDecrease] == noSymbol) { + nBitsToDecrease++ + } + totalCost -= 1 << (nBitsToDecrease - 1) + if rankLast[nBitsToDecrease-1] == noSymbol { + // this rank is no longer empty + rankLast[nBitsToDecrease-1] = rankLast[nBitsToDecrease] + } + huffNode[rankLast[nBitsToDecrease]].setNbBits(1 + + huffNode[rankLast[nBitsToDecrease]].nbBits()) + if rankLast[nBitsToDecrease] == 0 { + /* special case, reached largest symbol */ + rankLast[nBitsToDecrease] = noSymbol + } else { + rankLast[nBitsToDecrease]-- + if huffNode[rankLast[nBitsToDecrease]].nbBits() != maxNbBits-nBitsToDecrease { + rankLast[nBitsToDecrease] = noSymbol /* this rank is now empty */ + } + } + } + + for totalCost < 0 { /* Sometimes, cost correction overshoot */ + if rankLast[1] == noSymbol { /* special case : no rank 1 symbol (using maxNbBits-1); let's create one from largest rank 0 (using maxNbBits) */ + for huffNode[n].nbBits() == maxNbBits { + n-- + } + huffNode[n+1].setNbBits(huffNode[n+1].nbBits() - 1) + rankLast[1] = n + 1 + totalCost++ + continue + } + huffNode[rankLast[1]+1].setNbBits(huffNode[rankLast[1]+1].nbBits() - 1) + rankLast[1]++ + totalCost++ + } + } + return maxNbBits +} + +// A nodeElt is the fields +// +// count uint32 +// parent uint16 +// symbol byte +// nbBits uint8 +// +// in some order, all squashed into an integer so that the compiler +// always loads and stores entire nodeElts instead of separate fields. +type nodeElt uint64 + +func makeNodeElt(count uint32, symbol byte) nodeElt { + return nodeElt(count) | nodeElt(symbol)<<48 +} + +func (e *nodeElt) count() uint32 { return uint32(*e) } +func (e *nodeElt) parent() uint16 { return uint16(*e >> 32) } +func (e *nodeElt) symbol() byte { return byte(*e >> 48) } +func (e *nodeElt) nbBits() uint8 { return uint8(*e >> 56) } + +func (e *nodeElt) setCount(c uint32) { *e = (*e)&0xffffffff00000000 | nodeElt(c) } +func (e *nodeElt) setParent(p int16) { *e = (*e)&0xffff0000ffffffff | nodeElt(uint16(p))<<32 } +func (e *nodeElt) setNbBits(n uint8) { *e = (*e)&0x00ffffffffffffff | nodeElt(n)<<56 } diff --git a/vendor/github.com/klauspost/compress/huff0/decompress.go b/vendor/github.com/klauspost/compress/huff0/decompress.go new file mode 100644 index 000000000..7d0efa881 --- /dev/null +++ b/vendor/github.com/klauspost/compress/huff0/decompress.go @@ -0,0 +1,1161 @@ +package huff0 + +import ( + "errors" + "fmt" + "io" + "sync" + + "github.com/klauspost/compress/fse" +) + +type dTable struct { + single []dEntrySingle +} + +// single-symbols decoding +type dEntrySingle struct { + entry uint16 +} + +// Uses special code for all tables that are < 8 bits. +const use8BitTables = true + +// ReadTable will read a table from the input. +// The size of the input may be larger than the table definition. +// Any content remaining after the table definition will be returned. +// If no Scratch is provided a new one is allocated. +// The returned Scratch can be used for encoding or decoding input using this table. +func ReadTable(in []byte, s *Scratch) (s2 *Scratch, remain []byte, err error) { + s, err = s.prepare(nil) + if err != nil { + return s, nil, err + } + if len(in) <= 1 { + return s, nil, errors.New("input too small for table") + } + iSize := in[0] + in = in[1:] + if iSize >= 128 { + // Uncompressed + oSize := iSize - 127 + iSize = (oSize + 1) / 2 + if int(iSize) > len(in) { + return s, nil, errors.New("input too small for table") + } + for n := uint8(0); n < oSize; n += 2 { + v := in[n/2] + s.huffWeight[n] = v >> 4 + s.huffWeight[n+1] = v & 15 + } + s.symbolLen = uint16(oSize) + in = in[iSize:] + } else { + if len(in) < int(iSize) { + return s, nil, fmt.Errorf("input too small for table, want %d bytes, have %d", iSize, len(in)) + } + // FSE compressed weights + s.fse.DecompressLimit = 255 + hw := s.huffWeight[:] + s.fse.Out = hw + b, err := fse.Decompress(in[:iSize], s.fse) + s.fse.Out = nil + if err != nil { + return s, nil, fmt.Errorf("fse decompress returned: %w", err) + } + if len(b) > 255 { + return s, nil, errors.New("corrupt input: output table too large") + } + s.symbolLen = uint16(len(b)) + in = in[iSize:] + } + + // collect weight stats + var rankStats [16]uint32 + weightTotal := uint32(0) + for _, v := range s.huffWeight[:s.symbolLen] { + if v > tableLogMax { + return s, nil, errors.New("corrupt input: weight too large") + } + v2 := v & 15 + rankStats[v2]++ + // (1 << (v2-1)) is slower since the compiler cannot prove that v2 isn't 0. + weightTotal += (1 << v2) >> 1 + } + if weightTotal == 0 { + return s, nil, errors.New("corrupt input: weights zero") + } + + // get last non-null symbol weight (implied, total must be 2^n) + { + tableLog := highBit32(weightTotal) + 1 + if tableLog > tableLogMax { + return s, nil, errors.New("corrupt input: tableLog too big") + } + s.actualTableLog = uint8(tableLog) + // determine last weight + { + total := uint32(1) << tableLog + rest := total - weightTotal + verif := uint32(1) << highBit32(rest) + lastWeight := highBit32(rest) + 1 + if verif != rest { + // last value must be a clean power of 2 + return s, nil, errors.New("corrupt input: last value not power of two") + } + s.huffWeight[s.symbolLen] = uint8(lastWeight) + s.symbolLen++ + rankStats[lastWeight]++ + } + } + + if (rankStats[1] < 2) || (rankStats[1]&1 != 0) { + // by construction : at least 2 elts of rank 1, must be even + return s, nil, errors.New("corrupt input: min elt size, even check failed ") + } + + // TODO: Choose between single/double symbol decoding + + // Calculate starting value for each rank + { + var nextRankStart uint32 + for n := uint8(1); n < s.actualTableLog+1; n++ { + current := nextRankStart + nextRankStart += rankStats[n] << (n - 1) + rankStats[n] = current + } + } + + // fill DTable (always full size) + tSize := 1 << tableLogMax + if len(s.dt.single) != tSize { + s.dt.single = make([]dEntrySingle, tSize) + } + cTable := s.prevTable + if cap(cTable) < maxSymbolValue+1 { + cTable = make([]cTableEntry, 0, maxSymbolValue+1) + } + cTable = cTable[:maxSymbolValue+1] + s.prevTable = cTable[:s.symbolLen] + s.prevTableLog = s.actualTableLog + + for n, w := range s.huffWeight[:s.symbolLen] { + if w == 0 { + cTable[n] = cTableEntry{ + val: 0, + nBits: 0, + } + continue + } + length := (uint32(1) << w) >> 1 + d := dEntrySingle{ + entry: uint16(s.actualTableLog+1-w) | (uint16(n) << 8), + } + + rank := &rankStats[w] + cTable[n] = cTableEntry{ + val: uint16(*rank >> (w - 1)), + nBits: uint8(d.entry), + } + + single := s.dt.single[*rank : *rank+length] + for i := range single { + single[i] = d + } + *rank += length + } + + return s, in, nil +} + +// Decompress1X will decompress a 1X encoded stream. +// The length of the supplied input must match the end of a block exactly. +// Before this is called, the table must be initialized with ReadTable unless +// the encoder re-used the table. +// deprecated: Use the stateless Decoder() to get a concurrent version. +func (s *Scratch) Decompress1X(in []byte) (out []byte, err error) { + if cap(s.Out) < s.MaxDecodedSize { + s.Out = make([]byte, s.MaxDecodedSize) + } + s.Out = s.Out[:0:s.MaxDecodedSize] + s.Out, err = s.Decoder().Decompress1X(s.Out, in) + return s.Out, err +} + +// Decompress4X will decompress a 4X encoded stream. +// Before this is called, the table must be initialized with ReadTable unless +// the encoder re-used the table. +// The length of the supplied input must match the end of a block exactly. +// The destination size of the uncompressed data must be known and provided. +// deprecated: Use the stateless Decoder() to get a concurrent version. +func (s *Scratch) Decompress4X(in []byte, dstSize int) (out []byte, err error) { + if dstSize > s.MaxDecodedSize { + return nil, ErrMaxDecodedSizeExceeded + } + if cap(s.Out) < dstSize { + s.Out = make([]byte, s.MaxDecodedSize) + } + s.Out = s.Out[:0:dstSize] + s.Out, err = s.Decoder().Decompress4X(s.Out, in) + return s.Out, err +} + +// Decoder will return a stateless decoder that can be used by multiple +// decompressors concurrently. +// Before this is called, the table must be initialized with ReadTable. +// The Decoder is still linked to the scratch buffer so that cannot be reused. +// However, it is safe to discard the scratch. +func (s *Scratch) Decoder() *Decoder { + return &Decoder{ + dt: s.dt, + actualTableLog: s.actualTableLog, + bufs: &s.decPool, + } +} + +// Decoder provides stateless decoding. +type Decoder struct { + dt dTable + actualTableLog uint8 + bufs *sync.Pool +} + +func (d *Decoder) buffer() *[4][256]byte { + buf, ok := d.bufs.Get().(*[4][256]byte) + if ok { + return buf + } + return &[4][256]byte{} +} + +// decompress1X8Bit will decompress a 1X encoded stream with tablelog <= 8. +// The cap of the output buffer will be the maximum decompressed size. +// The length of the supplied input must match the end of a block exactly. +func (d *Decoder) decompress1X8Bit(dst, src []byte) ([]byte, error) { + if d.actualTableLog == 8 { + return d.decompress1X8BitExactly(dst, src) + } + var br bitReaderBytes + err := br.init(src) + if err != nil { + return dst, err + } + maxDecodedSize := cap(dst) + dst = dst[:0] + + // Avoid bounds check by always having full sized table. + dt := d.dt.single[:256] + + // Use temp table to avoid bound checks/append penalty. + bufs := d.buffer() + buf := &bufs[0] + var off uint8 + + switch d.actualTableLog { + case 8: + const shift = 0 + for br.off >= 4 { + br.fillFast() + v := dt[uint8(br.value>>(56+shift))] + br.advance(uint8(v.entry)) + buf[off+0] = uint8(v.entry >> 8) + + v = dt[uint8(br.value>>(56+shift))] + br.advance(uint8(v.entry)) + buf[off+1] = uint8(v.entry >> 8) + + v = dt[uint8(br.value>>(56+shift))] + br.advance(uint8(v.entry)) + buf[off+2] = uint8(v.entry >> 8) + + v = dt[uint8(br.value>>(56+shift))] + br.advance(uint8(v.entry)) + buf[off+3] = uint8(v.entry >> 8) + + off += 4 + if off == 0 { + if len(dst)+256 > maxDecodedSize { + br.close() + d.bufs.Put(bufs) + return nil, ErrMaxDecodedSizeExceeded + } + dst = append(dst, buf[:]...) + } + } + case 7: + const shift = 8 - 7 + for br.off >= 4 { + br.fillFast() + v := dt[uint8(br.value>>(56+shift))] + br.advance(uint8(v.entry)) + buf[off+0] = uint8(v.entry >> 8) + + v = dt[uint8(br.value>>(56+shift))] + br.advance(uint8(v.entry)) + buf[off+1] = uint8(v.entry >> 8) + + v = dt[uint8(br.value>>(56+shift))] + br.advance(uint8(v.entry)) + buf[off+2] = uint8(v.entry >> 8) + + v = dt[uint8(br.value>>(56+shift))] + br.advance(uint8(v.entry)) + buf[off+3] = uint8(v.entry >> 8) + + off += 4 + if off == 0 { + if len(dst)+256 > maxDecodedSize { + br.close() + d.bufs.Put(bufs) + return nil, ErrMaxDecodedSizeExceeded + } + dst = append(dst, buf[:]...) + } + } + case 6: + const shift = 8 - 6 + for br.off >= 4 { + br.fillFast() + v := dt[uint8(br.value>>(56+shift))] + br.advance(uint8(v.entry)) + buf[off+0] = uint8(v.entry >> 8) + + v = dt[uint8(br.value>>(56+shift))] + br.advance(uint8(v.entry)) + buf[off+1] = uint8(v.entry >> 8) + + v = dt[uint8(br.value>>(56+shift))] + br.advance(uint8(v.entry)) + buf[off+2] = uint8(v.entry >> 8) + + v = dt[uint8(br.value>>(56+shift))] + br.advance(uint8(v.entry)) + buf[off+3] = uint8(v.entry >> 8) + + off += 4 + if off == 0 { + if len(dst)+256 > maxDecodedSize { + d.bufs.Put(bufs) + br.close() + return nil, ErrMaxDecodedSizeExceeded + } + dst = append(dst, buf[:]...) + } + } + case 5: + const shift = 8 - 5 + for br.off >= 4 { + br.fillFast() + v := dt[uint8(br.value>>(56+shift))] + br.advance(uint8(v.entry)) + buf[off+0] = uint8(v.entry >> 8) + + v = dt[uint8(br.value>>(56+shift))] + br.advance(uint8(v.entry)) + buf[off+1] = uint8(v.entry >> 8) + + v = dt[uint8(br.value>>(56+shift))] + br.advance(uint8(v.entry)) + buf[off+2] = uint8(v.entry >> 8) + + v = dt[uint8(br.value>>(56+shift))] + br.advance(uint8(v.entry)) + buf[off+3] = uint8(v.entry >> 8) + + off += 4 + if off == 0 { + if len(dst)+256 > maxDecodedSize { + d.bufs.Put(bufs) + br.close() + return nil, ErrMaxDecodedSizeExceeded + } + dst = append(dst, buf[:]...) + } + } + case 4: + const shift = 8 - 4 + for br.off >= 4 { + br.fillFast() + v := dt[uint8(br.value>>(56+shift))] + br.advance(uint8(v.entry)) + buf[off+0] = uint8(v.entry >> 8) + + v = dt[uint8(br.value>>(56+shift))] + br.advance(uint8(v.entry)) + buf[off+1] = uint8(v.entry >> 8) + + v = dt[uint8(br.value>>(56+shift))] + br.advance(uint8(v.entry)) + buf[off+2] = uint8(v.entry >> 8) + + v = dt[uint8(br.value>>(56+shift))] + br.advance(uint8(v.entry)) + buf[off+3] = uint8(v.entry >> 8) + + off += 4 + if off == 0 { + if len(dst)+256 > maxDecodedSize { + d.bufs.Put(bufs) + br.close() + return nil, ErrMaxDecodedSizeExceeded + } + dst = append(dst, buf[:]...) + } + } + case 3: + const shift = 8 - 3 + for br.off >= 4 { + br.fillFast() + v := dt[uint8(br.value>>(56+shift))] + br.advance(uint8(v.entry)) + buf[off+0] = uint8(v.entry >> 8) + + v = dt[uint8(br.value>>(56+shift))] + br.advance(uint8(v.entry)) + buf[off+1] = uint8(v.entry >> 8) + + v = dt[uint8(br.value>>(56+shift))] + br.advance(uint8(v.entry)) + buf[off+2] = uint8(v.entry >> 8) + + v = dt[uint8(br.value>>(56+shift))] + br.advance(uint8(v.entry)) + buf[off+3] = uint8(v.entry >> 8) + + off += 4 + if off == 0 { + if len(dst)+256 > maxDecodedSize { + d.bufs.Put(bufs) + br.close() + return nil, ErrMaxDecodedSizeExceeded + } + dst = append(dst, buf[:]...) + } + } + case 2: + const shift = 8 - 2 + for br.off >= 4 { + br.fillFast() + v := dt[uint8(br.value>>(56+shift))] + br.advance(uint8(v.entry)) + buf[off+0] = uint8(v.entry >> 8) + + v = dt[uint8(br.value>>(56+shift))] + br.advance(uint8(v.entry)) + buf[off+1] = uint8(v.entry >> 8) + + v = dt[uint8(br.value>>(56+shift))] + br.advance(uint8(v.entry)) + buf[off+2] = uint8(v.entry >> 8) + + v = dt[uint8(br.value>>(56+shift))] + br.advance(uint8(v.entry)) + buf[off+3] = uint8(v.entry >> 8) + + off += 4 + if off == 0 { + if len(dst)+256 > maxDecodedSize { + d.bufs.Put(bufs) + br.close() + return nil, ErrMaxDecodedSizeExceeded + } + dst = append(dst, buf[:]...) + } + } + case 1: + const shift = 8 - 1 + for br.off >= 4 { + br.fillFast() + v := dt[uint8(br.value>>(56+shift))] + br.advance(uint8(v.entry)) + buf[off+0] = uint8(v.entry >> 8) + + v = dt[uint8(br.value>>(56+shift))] + br.advance(uint8(v.entry)) + buf[off+1] = uint8(v.entry >> 8) + + v = dt[uint8(br.value>>(56+shift))] + br.advance(uint8(v.entry)) + buf[off+2] = uint8(v.entry >> 8) + + v = dt[uint8(br.value>>(56+shift))] + br.advance(uint8(v.entry)) + buf[off+3] = uint8(v.entry >> 8) + + off += 4 + if off == 0 { + if len(dst)+256 > maxDecodedSize { + d.bufs.Put(bufs) + br.close() + return nil, ErrMaxDecodedSizeExceeded + } + dst = append(dst, buf[:]...) + } + } + default: + d.bufs.Put(bufs) + return nil, fmt.Errorf("invalid tablelog: %d", d.actualTableLog) + } + + if len(dst)+int(off) > maxDecodedSize { + d.bufs.Put(bufs) + br.close() + return nil, ErrMaxDecodedSizeExceeded + } + dst = append(dst, buf[:off]...) + + // br < 4, so uint8 is fine + bitsLeft := int8(uint8(br.off)*8 + (64 - br.bitsRead)) + shift := (8 - d.actualTableLog) & 7 + + for bitsLeft > 0 { + if br.bitsRead >= 64-8 { + for br.off > 0 { + br.value |= uint64(br.in[br.off-1]) << (br.bitsRead - 8) + br.bitsRead -= 8 + br.off-- + } + } + if len(dst) >= maxDecodedSize { + br.close() + d.bufs.Put(bufs) + return nil, ErrMaxDecodedSizeExceeded + } + v := dt[br.peekByteFast()>>shift] + nBits := uint8(v.entry) + br.advance(nBits) + bitsLeft -= int8(nBits) + dst = append(dst, uint8(v.entry>>8)) + } + d.bufs.Put(bufs) + return dst, br.close() +} + +// decompress1X8Bit will decompress a 1X encoded stream with tablelog <= 8. +// The cap of the output buffer will be the maximum decompressed size. +// The length of the supplied input must match the end of a block exactly. +func (d *Decoder) decompress1X8BitExactly(dst, src []byte) ([]byte, error) { + var br bitReaderBytes + err := br.init(src) + if err != nil { + return dst, err + } + maxDecodedSize := cap(dst) + dst = dst[:0] + + // Avoid bounds check by always having full sized table. + dt := d.dt.single[:256] + + // Use temp table to avoid bound checks/append penalty. + bufs := d.buffer() + buf := &bufs[0] + var off uint8 + + const shift = 56 + + //fmt.Printf("mask: %b, tl:%d\n", mask, d.actualTableLog) + for br.off >= 4 { + br.fillFast() + v := dt[uint8(br.value>>shift)] + br.advance(uint8(v.entry)) + buf[off+0] = uint8(v.entry >> 8) + + v = dt[uint8(br.value>>shift)] + br.advance(uint8(v.entry)) + buf[off+1] = uint8(v.entry >> 8) + + v = dt[uint8(br.value>>shift)] + br.advance(uint8(v.entry)) + buf[off+2] = uint8(v.entry >> 8) + + v = dt[uint8(br.value>>shift)] + br.advance(uint8(v.entry)) + buf[off+3] = uint8(v.entry >> 8) + + off += 4 + if off == 0 { + if len(dst)+256 > maxDecodedSize { + d.bufs.Put(bufs) + br.close() + return nil, ErrMaxDecodedSizeExceeded + } + dst = append(dst, buf[:]...) + } + } + + if len(dst)+int(off) > maxDecodedSize { + d.bufs.Put(bufs) + br.close() + return nil, ErrMaxDecodedSizeExceeded + } + dst = append(dst, buf[:off]...) + + // br < 4, so uint8 is fine + bitsLeft := int8(uint8(br.off)*8 + (64 - br.bitsRead)) + for bitsLeft > 0 { + if br.bitsRead >= 64-8 { + for br.off > 0 { + br.value |= uint64(br.in[br.off-1]) << (br.bitsRead - 8) + br.bitsRead -= 8 + br.off-- + } + } + if len(dst) >= maxDecodedSize { + d.bufs.Put(bufs) + br.close() + return nil, ErrMaxDecodedSizeExceeded + } + v := dt[br.peekByteFast()] + nBits := uint8(v.entry) + br.advance(nBits) + bitsLeft -= int8(nBits) + dst = append(dst, uint8(v.entry>>8)) + } + d.bufs.Put(bufs) + return dst, br.close() +} + +// Decompress4X will decompress a 4X encoded stream. +// The length of the supplied input must match the end of a block exactly. +// The *capacity* of the dst slice must match the destination size of +// the uncompressed data exactly. +func (d *Decoder) decompress4X8bit(dst, src []byte) ([]byte, error) { + if d.actualTableLog == 8 { + return d.decompress4X8bitExactly(dst, src) + } + + var br [4]bitReaderBytes + start := 6 + for i := range 3 { + length := int(src[i*2]) | (int(src[i*2+1]) << 8) + if start+length >= len(src) { + return nil, errors.New("truncated input (or invalid offset)") + } + err := br[i].init(src[start : start+length]) + if err != nil { + return nil, err + } + start += length + } + err := br[3].init(src[start:]) + if err != nil { + return nil, err + } + + // destination, offset to match first output + dstSize := cap(dst) + dst = dst[:dstSize] + out := dst + dstEvery := (dstSize + 3) / 4 + + shift := (56 + (8 - d.actualTableLog)) & 63 + + const tlSize = 1 << 8 + single := d.dt.single[:tlSize] + + // Use temp table to avoid bound checks/append penalty. + buf := d.buffer() + var off uint8 + var decoded int + + // Decode 4 values from each decoder/loop. + const bufoff = 256 + for { + if br[0].off < 4 || br[1].off < 4 || br[2].off < 4 || br[3].off < 4 { + break + } + + { + // Interleave 2 decodes. + const stream = 0 + const stream2 = 1 + br1 := &br[stream] + br2 := &br[stream2] + br1.fillFast() + br2.fillFast() + + v := single[uint8(br1.value>>shift)].entry + v2 := single[uint8(br2.value>>shift)].entry + br1.bitsRead += uint8(v) + br1.value <<= v & 63 + br2.bitsRead += uint8(v2) + br2.value <<= v2 & 63 + buf[stream][off] = uint8(v >> 8) + buf[stream2][off] = uint8(v2 >> 8) + + v = single[uint8(br1.value>>shift)].entry + v2 = single[uint8(br2.value>>shift)].entry + br1.bitsRead += uint8(v) + br1.value <<= v & 63 + br2.bitsRead += uint8(v2) + br2.value <<= v2 & 63 + buf[stream][off+1] = uint8(v >> 8) + buf[stream2][off+1] = uint8(v2 >> 8) + + v = single[uint8(br1.value>>shift)].entry + v2 = single[uint8(br2.value>>shift)].entry + br1.bitsRead += uint8(v) + br1.value <<= v & 63 + br2.bitsRead += uint8(v2) + br2.value <<= v2 & 63 + buf[stream][off+2] = uint8(v >> 8) + buf[stream2][off+2] = uint8(v2 >> 8) + + v = single[uint8(br1.value>>shift)].entry + v2 = single[uint8(br2.value>>shift)].entry + br1.bitsRead += uint8(v) + br1.value <<= v & 63 + br2.bitsRead += uint8(v2) + br2.value <<= v2 & 63 + buf[stream][off+3] = uint8(v >> 8) + buf[stream2][off+3] = uint8(v2 >> 8) + } + + { + const stream = 2 + const stream2 = 3 + br1 := &br[stream] + br2 := &br[stream2] + br1.fillFast() + br2.fillFast() + + v := single[uint8(br1.value>>shift)].entry + v2 := single[uint8(br2.value>>shift)].entry + br1.bitsRead += uint8(v) + br1.value <<= v & 63 + br2.bitsRead += uint8(v2) + br2.value <<= v2 & 63 + buf[stream][off] = uint8(v >> 8) + buf[stream2][off] = uint8(v2 >> 8) + + v = single[uint8(br1.value>>shift)].entry + v2 = single[uint8(br2.value>>shift)].entry + br1.bitsRead += uint8(v) + br1.value <<= v & 63 + br2.bitsRead += uint8(v2) + br2.value <<= v2 & 63 + buf[stream][off+1] = uint8(v >> 8) + buf[stream2][off+1] = uint8(v2 >> 8) + + v = single[uint8(br1.value>>shift)].entry + v2 = single[uint8(br2.value>>shift)].entry + br1.bitsRead += uint8(v) + br1.value <<= v & 63 + br2.bitsRead += uint8(v2) + br2.value <<= v2 & 63 + buf[stream][off+2] = uint8(v >> 8) + buf[stream2][off+2] = uint8(v2 >> 8) + + v = single[uint8(br1.value>>shift)].entry + v2 = single[uint8(br2.value>>shift)].entry + br1.bitsRead += uint8(v) + br1.value <<= v & 63 + br2.bitsRead += uint8(v2) + br2.value <<= v2 & 63 + buf[stream][off+3] = uint8(v >> 8) + buf[stream2][off+3] = uint8(v2 >> 8) + } + + off += 4 + + if off == 0 { + if bufoff > dstEvery { + d.bufs.Put(buf) + return nil, errors.New("corruption detected: stream overrun 1") + } + // There must at least be 3 buffers left. + if len(out)-bufoff < dstEvery*3 { + d.bufs.Put(buf) + return nil, errors.New("corruption detected: stream overrun 2") + } + //copy(out, buf[0][:]) + //copy(out[dstEvery:], buf[1][:]) + //copy(out[dstEvery*2:], buf[2][:]) + *(*[bufoff]byte)(out) = buf[0] + *(*[bufoff]byte)(out[dstEvery:]) = buf[1] + *(*[bufoff]byte)(out[dstEvery*2:]) = buf[2] + *(*[bufoff]byte)(out[dstEvery*3:]) = buf[3] + out = out[bufoff:] + decoded += bufoff * 4 + } + } + if off > 0 { + ioff := int(off) + if len(out) < dstEvery*3+ioff { + d.bufs.Put(buf) + return nil, errors.New("corruption detected: stream overrun 3") + } + copy(out, buf[0][:off]) + copy(out[dstEvery:], buf[1][:off]) + copy(out[dstEvery*2:], buf[2][:off]) + copy(out[dstEvery*3:], buf[3][:off]) + decoded += int(off) * 4 + out = out[off:] + } + + // Decode remaining. + // Decode remaining. + remainBytes := dstEvery - (decoded / 4) + for i := range br { + offset := dstEvery * i + endsAt := min(offset+remainBytes, len(out)) + br := &br[i] + bitsLeft := br.remaining() + for bitsLeft > 0 { + if br.finished() { + d.bufs.Put(buf) + return nil, io.ErrUnexpectedEOF + } + if br.bitsRead >= 56 { + if br.off >= 4 { + v := br.in[br.off-4:] + v = v[:4] + low := (uint32(v[0])) | (uint32(v[1]) << 8) | (uint32(v[2]) << 16) | (uint32(v[3]) << 24) + br.value |= uint64(low) << (br.bitsRead - 32) + br.bitsRead -= 32 + br.off -= 4 + } else { + for br.off > 0 { + br.value |= uint64(br.in[br.off-1]) << (br.bitsRead - 8) + br.bitsRead -= 8 + br.off-- + } + } + } + // end inline... + if offset >= endsAt { + d.bufs.Put(buf) + return nil, errors.New("corruption detected: stream overrun 4") + } + + // Read value and increment offset. + v := single[uint8(br.value>>shift)].entry + nBits := uint8(v) + br.advance(nBits) + bitsLeft -= uint(nBits) + out[offset] = uint8(v >> 8) + offset++ + } + if offset != endsAt { + d.bufs.Put(buf) + return nil, fmt.Errorf("corruption detected: short output block %d, end %d != %d", i, offset, endsAt) + } + decoded += offset - dstEvery*i + err = br.close() + if err != nil { + d.bufs.Put(buf) + return nil, err + } + } + d.bufs.Put(buf) + if dstSize != decoded { + return nil, errors.New("corruption detected: short output block") + } + return dst, nil +} + +// Decompress4X will decompress a 4X encoded stream. +// The length of the supplied input must match the end of a block exactly. +// The *capacity* of the dst slice must match the destination size of +// the uncompressed data exactly. +func (d *Decoder) decompress4X8bitExactly(dst, src []byte) ([]byte, error) { + var br [4]bitReaderBytes + start := 6 + for i := range 3 { + length := int(src[i*2]) | (int(src[i*2+1]) << 8) + if start+length >= len(src) { + return nil, errors.New("truncated input (or invalid offset)") + } + err := br[i].init(src[start : start+length]) + if err != nil { + return nil, err + } + start += length + } + err := br[3].init(src[start:]) + if err != nil { + return nil, err + } + + // destination, offset to match first output + dstSize := cap(dst) + dst = dst[:dstSize] + out := dst + dstEvery := (dstSize + 3) / 4 + + const shift = 56 + const tlSize = 1 << 8 + single := d.dt.single[:tlSize] + + // Use temp table to avoid bound checks/append penalty. + buf := d.buffer() + var off uint8 + var decoded int + + // Decode 4 values from each decoder/loop. + const bufoff = 256 + for { + if br[0].off < 4 || br[1].off < 4 || br[2].off < 4 || br[3].off < 4 { + break + } + + { + // Interleave 2 decodes. + const stream = 0 + const stream2 = 1 + br1 := &br[stream] + br2 := &br[stream2] + br1.fillFast() + br2.fillFast() + + v := single[uint8(br1.value>>shift)].entry + v2 := single[uint8(br2.value>>shift)].entry + br1.bitsRead += uint8(v) + br1.value <<= v & 63 + br2.bitsRead += uint8(v2) + br2.value <<= v2 & 63 + buf[stream][off] = uint8(v >> 8) + buf[stream2][off] = uint8(v2 >> 8) + + v = single[uint8(br1.value>>shift)].entry + v2 = single[uint8(br2.value>>shift)].entry + br1.bitsRead += uint8(v) + br1.value <<= v & 63 + br2.bitsRead += uint8(v2) + br2.value <<= v2 & 63 + buf[stream][off+1] = uint8(v >> 8) + buf[stream2][off+1] = uint8(v2 >> 8) + + v = single[uint8(br1.value>>shift)].entry + v2 = single[uint8(br2.value>>shift)].entry + br1.bitsRead += uint8(v) + br1.value <<= v & 63 + br2.bitsRead += uint8(v2) + br2.value <<= v2 & 63 + buf[stream][off+2] = uint8(v >> 8) + buf[stream2][off+2] = uint8(v2 >> 8) + + v = single[uint8(br1.value>>shift)].entry + v2 = single[uint8(br2.value>>shift)].entry + br1.bitsRead += uint8(v) + br1.value <<= v & 63 + br2.bitsRead += uint8(v2) + br2.value <<= v2 & 63 + buf[stream][off+3] = uint8(v >> 8) + buf[stream2][off+3] = uint8(v2 >> 8) + } + + { + const stream = 2 + const stream2 = 3 + br1 := &br[stream] + br2 := &br[stream2] + br1.fillFast() + br2.fillFast() + + v := single[uint8(br1.value>>shift)].entry + v2 := single[uint8(br2.value>>shift)].entry + br1.bitsRead += uint8(v) + br1.value <<= v & 63 + br2.bitsRead += uint8(v2) + br2.value <<= v2 & 63 + buf[stream][off] = uint8(v >> 8) + buf[stream2][off] = uint8(v2 >> 8) + + v = single[uint8(br1.value>>shift)].entry + v2 = single[uint8(br2.value>>shift)].entry + br1.bitsRead += uint8(v) + br1.value <<= v & 63 + br2.bitsRead += uint8(v2) + br2.value <<= v2 & 63 + buf[stream][off+1] = uint8(v >> 8) + buf[stream2][off+1] = uint8(v2 >> 8) + + v = single[uint8(br1.value>>shift)].entry + v2 = single[uint8(br2.value>>shift)].entry + br1.bitsRead += uint8(v) + br1.value <<= v & 63 + br2.bitsRead += uint8(v2) + br2.value <<= v2 & 63 + buf[stream][off+2] = uint8(v >> 8) + buf[stream2][off+2] = uint8(v2 >> 8) + + v = single[uint8(br1.value>>shift)].entry + v2 = single[uint8(br2.value>>shift)].entry + br1.bitsRead += uint8(v) + br1.value <<= v & 63 + br2.bitsRead += uint8(v2) + br2.value <<= v2 & 63 + buf[stream][off+3] = uint8(v >> 8) + buf[stream2][off+3] = uint8(v2 >> 8) + } + + off += 4 + + if off == 0 { + if bufoff > dstEvery { + d.bufs.Put(buf) + return nil, errors.New("corruption detected: stream overrun 1") + } + // There must at least be 3 buffers left. + if len(out)-bufoff < dstEvery*3 { + d.bufs.Put(buf) + return nil, errors.New("corruption detected: stream overrun 2") + } + + //copy(out, buf[0][:]) + //copy(out[dstEvery:], buf[1][:]) + //copy(out[dstEvery*2:], buf[2][:]) + // copy(out[dstEvery*3:], buf[3][:]) + *(*[bufoff]byte)(out) = buf[0] + *(*[bufoff]byte)(out[dstEvery:]) = buf[1] + *(*[bufoff]byte)(out[dstEvery*2:]) = buf[2] + *(*[bufoff]byte)(out[dstEvery*3:]) = buf[3] + out = out[bufoff:] + decoded += bufoff * 4 + } + } + if off > 0 { + ioff := int(off) + if len(out) < dstEvery*3+ioff { + return nil, errors.New("corruption detected: stream overrun 3") + } + copy(out, buf[0][:off]) + copy(out[dstEvery:], buf[1][:off]) + copy(out[dstEvery*2:], buf[2][:off]) + copy(out[dstEvery*3:], buf[3][:off]) + decoded += int(off) * 4 + out = out[off:] + } + + // Decode remaining. + remainBytes := dstEvery - (decoded / 4) + for i := range br { + offset := dstEvery * i + endsAt := min(offset+remainBytes, len(out)) + br := &br[i] + bitsLeft := br.remaining() + for bitsLeft > 0 { + if br.finished() { + d.bufs.Put(buf) + return nil, io.ErrUnexpectedEOF + } + if br.bitsRead >= 56 { + if br.off >= 4 { + v := br.in[br.off-4:] + v = v[:4] + low := (uint32(v[0])) | (uint32(v[1]) << 8) | (uint32(v[2]) << 16) | (uint32(v[3]) << 24) + br.value |= uint64(low) << (br.bitsRead - 32) + br.bitsRead -= 32 + br.off -= 4 + } else { + for br.off > 0 { + br.value |= uint64(br.in[br.off-1]) << (br.bitsRead - 8) + br.bitsRead -= 8 + br.off-- + } + } + } + // end inline... + if offset >= endsAt { + d.bufs.Put(buf) + return nil, errors.New("corruption detected: stream overrun 4") + } + + // Read value and increment offset. + v := single[br.peekByteFast()].entry + nBits := uint8(v) + br.advance(nBits) + bitsLeft -= uint(nBits) + out[offset] = uint8(v >> 8) + offset++ + } + if offset != endsAt { + d.bufs.Put(buf) + return nil, fmt.Errorf("corruption detected: short output block %d, end %d != %d", i, offset, endsAt) + } + + decoded += offset - dstEvery*i + err = br.close() + if err != nil { + d.bufs.Put(buf) + return nil, err + } + } + d.bufs.Put(buf) + if dstSize != decoded { + return nil, errors.New("corruption detected: short output block") + } + return dst, nil +} + +// matches will compare a decoding table to a coding table. +// Errors are written to the writer. +// Nothing will be written if table is ok. +func (s *Scratch) matches(ct cTable, w io.Writer) { + if s == nil || len(s.dt.single) == 0 { + return + } + dt := s.dt.single[:1<>8) == byte(sym) { + fmt.Fprintf(w, "symbol %x has decoder, but no encoder\n", sym) + errs++ + break + } + } + if errs == 0 { + broken-- + } + continue + } + // Unused bits in input + ub := tablelog - enc.nBits + top := enc.val << ub + // decoder looks at top bits. + dec := dt[top] + if uint8(dec.entry) != enc.nBits { + fmt.Fprintf(w, "symbol 0x%x bit size mismatch (enc: %d, dec:%d).\n", sym, enc.nBits, uint8(dec.entry)) + errs++ + } + if uint8(dec.entry>>8) != uint8(sym) { + fmt.Fprintf(w, "symbol 0x%x decoder output mismatch (enc: %d, dec:%d).\n", sym, sym, uint8(dec.entry>>8)) + errs++ + } + if errs > 0 { + fmt.Fprintf(w, "%d errors in base, stopping\n", errs) + continue + } + // Ensure that all combinations are covered. + for i := uint16(0); i < (1 << ub); i++ { + vval := top | i + dec := dt[vval] + if uint8(dec.entry) != enc.nBits { + fmt.Fprintf(w, "symbol 0x%x bit size mismatch (enc: %d, dec:%d).\n", vval, enc.nBits, uint8(dec.entry)) + errs++ + } + if uint8(dec.entry>>8) != uint8(sym) { + fmt.Fprintf(w, "symbol 0x%x decoder output mismatch (enc: %d, dec:%d).\n", vval, sym, uint8(dec.entry>>8)) + errs++ + } + if errs > 20 { + fmt.Fprintf(w, "%d errors, stopping\n", errs) + break + } + } + if errs == 0 { + ok++ + broken-- + } + } + if broken > 0 { + fmt.Fprintf(w, "%d broken, %d ok\n", broken, ok) + } +} diff --git a/vendor/github.com/klauspost/compress/huff0/decompress_amd64.go b/vendor/github.com/klauspost/compress/huff0/decompress_amd64.go new file mode 100644 index 000000000..2d6ef64be --- /dev/null +++ b/vendor/github.com/klauspost/compress/huff0/decompress_amd64.go @@ -0,0 +1,222 @@ +//go:build amd64 && !appengine && !noasm && gc + +// This file contains the specialisation of Decoder.Decompress4X +// and Decoder.Decompress1X that use an asm implementation of thir main loops. +package huff0 + +import ( + "errors" + "fmt" + + "github.com/klauspost/compress/internal/cpuinfo" +) + +// decompress4x_main_loop_x86 is an x86 assembler implementation +// of Decompress4X when tablelog > 8. +// +//go:noescape +func decompress4x_main_loop_amd64(ctx *decompress4xContext) + +// decompress4x_8b_loop_x86 is an x86 assembler implementation +// of Decompress4X when tablelog <= 8 which decodes 4 entries +// per loop. +// +//go:noescape +func decompress4x_8b_main_loop_amd64(ctx *decompress4xContext) + +// fallback8BitSize is the size where using Go version is faster. +const fallback8BitSize = 800 + +type decompress4xContext struct { + pbr *[4]bitReaderShifted + peekBits uint8 + out *byte + dstEvery int + tbl *dEntrySingle + decoded int + limit *byte +} + +// Decompress4X will decompress a 4X encoded stream. +// The length of the supplied input must match the end of a block exactly. +// The *capacity* of the dst slice must match the destination size of +// the uncompressed data exactly. +func (d *Decoder) Decompress4X(dst, src []byte) ([]byte, error) { + if len(d.dt.single) == 0 { + return nil, errors.New("no table loaded") + } + if len(src) < 6+(4*1) { + return nil, errors.New("input too small") + } + + use8BitTables := d.actualTableLog <= 8 + if cap(dst) < fallback8BitSize && use8BitTables { + return d.decompress4X8bit(dst, src) + } + + var br [4]bitReaderShifted + // Decode "jump table" + start := 6 + for i := range 3 { + length := int(src[i*2]) | (int(src[i*2+1]) << 8) + if start+length >= len(src) { + return nil, errors.New("truncated input (or invalid offset)") + } + err := br[i].init(src[start : start+length]) + if err != nil { + return nil, err + } + start += length + } + err := br[3].init(src[start:]) + if err != nil { + return nil, err + } + + // destination, offset to match first output + dstSize := cap(dst) + dst = dst[:dstSize] + out := dst + dstEvery := (dstSize + 3) / 4 + + const tlSize = 1 << tableLogMax + const tlMask = tlSize - 1 + single := d.dt.single[:tlSize] + + var decoded int + + if len(out) > 4*4 && !(br[0].off < 4 || br[1].off < 4 || br[2].off < 4 || br[3].off < 4) { + ctx := decompress4xContext{ + pbr: &br, + peekBits: uint8((64 - d.actualTableLog) & 63), // see: bitReaderShifted.peekBitsFast() + out: &out[0], + dstEvery: dstEvery, + tbl: &single[0], + limit: &out[dstEvery-4], // Always stop decoding when first buffer gets here to avoid writing OOB on last. + } + if use8BitTables { + decompress4x_8b_main_loop_amd64(&ctx) + } else { + decompress4x_main_loop_amd64(&ctx) + } + + decoded = ctx.decoded + out = out[decoded/4:] + } + + // Decode remaining. + remainBytes := dstEvery - (decoded / 4) + for i := range br { + offset := dstEvery * i + endsAt := min(offset+remainBytes, len(out)) + br := &br[i] + bitsLeft := br.remaining() + for bitsLeft > 0 { + br.fill() + if offset >= endsAt { + return nil, errors.New("corruption detected: stream overrun 4") + } + + // Read value and increment offset. + val := br.peekBitsFast(d.actualTableLog) + v := single[val&tlMask].entry + nBits := uint8(v) + br.advance(nBits) + bitsLeft -= uint(nBits) + out[offset] = uint8(v >> 8) + offset++ + } + if offset != endsAt { + return nil, fmt.Errorf("corruption detected: short output block %d, end %d != %d", i, offset, endsAt) + } + decoded += offset - dstEvery*i + err = br.close() + if err != nil { + return nil, err + } + } + if dstSize != decoded { + return nil, errors.New("corruption detected: short output block") + } + return dst, nil +} + +// decompress4x_main_loop_x86 is an x86 assembler implementation +// of Decompress1X when tablelog > 8. +// +//go:noescape +func decompress1x_main_loop_amd64(ctx *decompress1xContext) + +// decompress4x_main_loop_x86 is an x86 with BMI2 assembler implementation +// of Decompress1X when tablelog > 8. +// +//go:noescape +func decompress1x_main_loop_bmi2(ctx *decompress1xContext) + +type decompress1xContext struct { + pbr *bitReaderShifted + peekBits uint8 + out *byte + outCap int + tbl *dEntrySingle + decoded int +} + +// Error reported by asm implementations +const error_max_decoded_size_exeeded = -1 + +// Decompress1X will decompress a 1X encoded stream. +// The cap of the output buffer will be the maximum decompressed size. +// The length of the supplied input must match the end of a block exactly. +func (d *Decoder) Decompress1X(dst, src []byte) ([]byte, error) { + if len(d.dt.single) == 0 { + return nil, errors.New("no table loaded") + } + var br bitReaderShifted + err := br.init(src) + if err != nil { + return dst, err + } + maxDecodedSize := cap(dst) + dst = dst[:maxDecodedSize] + + const tlSize = 1 << tableLogMax + const tlMask = tlSize - 1 + + if maxDecodedSize >= 4 { + ctx := decompress1xContext{ + pbr: &br, + out: &dst[0], + outCap: maxDecodedSize, + peekBits: uint8((64 - d.actualTableLog) & 63), // see: bitReaderShifted.peekBitsFast() + tbl: &d.dt.single[0], + } + + if cpuinfo.HasBMI2() { + decompress1x_main_loop_bmi2(&ctx) + } else { + decompress1x_main_loop_amd64(&ctx) + } + if ctx.decoded == error_max_decoded_size_exeeded { + return nil, ErrMaxDecodedSizeExceeded + } + + dst = dst[:ctx.decoded] + } + + // br < 8, so uint8 is fine + bitsLeft := uint8(br.off)*8 + 64 - br.bitsRead + for bitsLeft > 0 { + br.fill() + if len(dst) >= maxDecodedSize { + br.close() + return nil, ErrMaxDecodedSizeExceeded + } + v := d.dt.single[br.peekBitsFast(d.actualTableLog)&tlMask] + nBits := uint8(v.entry) + br.advance(nBits) + bitsLeft -= nBits + dst = append(dst, uint8(v.entry>>8)) + } + return dst, br.close() +} diff --git a/vendor/github.com/klauspost/compress/huff0/decompress_amd64.s b/vendor/github.com/klauspost/compress/huff0/decompress_amd64.s new file mode 100644 index 000000000..c4c7ab2d1 --- /dev/null +++ b/vendor/github.com/klauspost/compress/huff0/decompress_amd64.s @@ -0,0 +1,830 @@ +// Code generated by command: go run gen.go -out ../decompress_amd64.s -pkg=huff0. DO NOT EDIT. + +//go:build amd64 && !appengine && !noasm && gc + +// func decompress4x_main_loop_amd64(ctx *decompress4xContext) +TEXT ·decompress4x_main_loop_amd64(SB), $0-8 + // Preload values + MOVQ ctx+0(FP), AX + MOVBQZX 8(AX), DI + MOVQ 16(AX), BX + MOVQ 48(AX), SI + MOVQ 24(AX), R8 + MOVQ 32(AX), R9 + MOVQ (AX), R10 + + // Main loop +main_loop: + XORL DX, DX + CMPQ BX, SI + SETGE DL + + // br0.fillFast32() + MOVQ 32(R10), R11 + MOVBQZX 40(R10), R12 + CMPQ R12, $0x20 + JBE skip_fill0 + MOVQ 24(R10), AX + SUBQ $0x20, R12 + SUBQ $0x04, AX + MOVQ (R10), R13 + + // b.value |= uint64(low) << (b.bitsRead & 63) + MOVL (AX)(R13*1), R13 + MOVQ R12, CX + SHLQ CL, R13 + MOVQ AX, 24(R10) + ORQ R13, R11 + + // exhausted += (br0.off < 4) + CMPQ AX, $0x04 + ADCB $+0, DL + +skip_fill0: + // val0 := br0.peekTopBits(peekBits) + MOVQ R11, R13 + MOVQ DI, CX + SHRQ CL, R13 + + // v0 := table[val0&mask] + MOVW (R9)(R13*2), CX + + // br0.advance(uint8(v0.entry) + MOVB CH, AL + SHLQ CL, R11 + ADDB CL, R12 + + // val1 := br0.peekTopBits(peekBits) + MOVQ DI, CX + MOVQ R11, R13 + SHRQ CL, R13 + + // v1 := table[val1&mask] + MOVW (R9)(R13*2), CX + + // br0.advance(uint8(v1.entry)) + MOVB CH, AH + SHLQ CL, R11 + ADDB CL, R12 + + // these two writes get coalesced + // out[id * dstEvery + 0] = uint8(v0.entry >> 8) + // out[id * dstEvery + 1] = uint8(v1.entry >> 8) + MOVW AX, (BX) + + // update the bitreader structure + MOVQ R11, 32(R10) + MOVB R12, 40(R10) + + // br1.fillFast32() + MOVQ 80(R10), R11 + MOVBQZX 88(R10), R12 + CMPQ R12, $0x20 + JBE skip_fill1 + MOVQ 72(R10), AX + SUBQ $0x20, R12 + SUBQ $0x04, AX + MOVQ 48(R10), R13 + + // b.value |= uint64(low) << (b.bitsRead & 63) + MOVL (AX)(R13*1), R13 + MOVQ R12, CX + SHLQ CL, R13 + MOVQ AX, 72(R10) + ORQ R13, R11 + + // exhausted += (br1.off < 4) + CMPQ AX, $0x04 + ADCB $+0, DL + +skip_fill1: + // val0 := br1.peekTopBits(peekBits) + MOVQ R11, R13 + MOVQ DI, CX + SHRQ CL, R13 + + // v0 := table[val0&mask] + MOVW (R9)(R13*2), CX + + // br1.advance(uint8(v0.entry) + MOVB CH, AL + SHLQ CL, R11 + ADDB CL, R12 + + // val1 := br1.peekTopBits(peekBits) + MOVQ DI, CX + MOVQ R11, R13 + SHRQ CL, R13 + + // v1 := table[val1&mask] + MOVW (R9)(R13*2), CX + + // br1.advance(uint8(v1.entry)) + MOVB CH, AH + SHLQ CL, R11 + ADDB CL, R12 + + // these two writes get coalesced + // out[id * dstEvery + 0] = uint8(v0.entry >> 8) + // out[id * dstEvery + 1] = uint8(v1.entry >> 8) + MOVW AX, (BX)(R8*1) + + // update the bitreader structure + MOVQ R11, 80(R10) + MOVB R12, 88(R10) + + // br2.fillFast32() + MOVQ 128(R10), R11 + MOVBQZX 136(R10), R12 + CMPQ R12, $0x20 + JBE skip_fill2 + MOVQ 120(R10), AX + SUBQ $0x20, R12 + SUBQ $0x04, AX + MOVQ 96(R10), R13 + + // b.value |= uint64(low) << (b.bitsRead & 63) + MOVL (AX)(R13*1), R13 + MOVQ R12, CX + SHLQ CL, R13 + MOVQ AX, 120(R10) + ORQ R13, R11 + + // exhausted += (br2.off < 4) + CMPQ AX, $0x04 + ADCB $+0, DL + +skip_fill2: + // val0 := br2.peekTopBits(peekBits) + MOVQ R11, R13 + MOVQ DI, CX + SHRQ CL, R13 + + // v0 := table[val0&mask] + MOVW (R9)(R13*2), CX + + // br2.advance(uint8(v0.entry) + MOVB CH, AL + SHLQ CL, R11 + ADDB CL, R12 + + // val1 := br2.peekTopBits(peekBits) + MOVQ DI, CX + MOVQ R11, R13 + SHRQ CL, R13 + + // v1 := table[val1&mask] + MOVW (R9)(R13*2), CX + + // br2.advance(uint8(v1.entry)) + MOVB CH, AH + SHLQ CL, R11 + ADDB CL, R12 + + // these two writes get coalesced + // out[id * dstEvery + 0] = uint8(v0.entry >> 8) + // out[id * dstEvery + 1] = uint8(v1.entry >> 8) + MOVW AX, (BX)(R8*2) + + // update the bitreader structure + MOVQ R11, 128(R10) + MOVB R12, 136(R10) + + // br3.fillFast32() + MOVQ 176(R10), R11 + MOVBQZX 184(R10), R12 + CMPQ R12, $0x20 + JBE skip_fill3 + MOVQ 168(R10), AX + SUBQ $0x20, R12 + SUBQ $0x04, AX + MOVQ 144(R10), R13 + + // b.value |= uint64(low) << (b.bitsRead & 63) + MOVL (AX)(R13*1), R13 + MOVQ R12, CX + SHLQ CL, R13 + MOVQ AX, 168(R10) + ORQ R13, R11 + + // exhausted += (br3.off < 4) + CMPQ AX, $0x04 + ADCB $+0, DL + +skip_fill3: + // val0 := br3.peekTopBits(peekBits) + MOVQ R11, R13 + MOVQ DI, CX + SHRQ CL, R13 + + // v0 := table[val0&mask] + MOVW (R9)(R13*2), CX + + // br3.advance(uint8(v0.entry) + MOVB CH, AL + SHLQ CL, R11 + ADDB CL, R12 + + // val1 := br3.peekTopBits(peekBits) + MOVQ DI, CX + MOVQ R11, R13 + SHRQ CL, R13 + + // v1 := table[val1&mask] + MOVW (R9)(R13*2), CX + + // br3.advance(uint8(v1.entry)) + MOVB CH, AH + SHLQ CL, R11 + ADDB CL, R12 + + // these two writes get coalesced + // out[id * dstEvery + 0] = uint8(v0.entry >> 8) + // out[id * dstEvery + 1] = uint8(v1.entry >> 8) + LEAQ (R8)(R8*2), CX + MOVW AX, (BX)(CX*1) + + // update the bitreader structure + MOVQ R11, 176(R10) + MOVB R12, 184(R10) + ADDQ $0x02, BX + TESTB DL, DL + JZ main_loop + MOVQ ctx+0(FP), AX + SUBQ 16(AX), BX + SHLQ $0x02, BX + MOVQ BX, 40(AX) + RET + +// func decompress4x_8b_main_loop_amd64(ctx *decompress4xContext) +TEXT ·decompress4x_8b_main_loop_amd64(SB), $0-8 + // Preload values + MOVQ ctx+0(FP), CX + MOVBQZX 8(CX), DI + MOVQ 16(CX), BX + MOVQ 48(CX), SI + MOVQ 24(CX), R8 + MOVQ 32(CX), R9 + MOVQ (CX), R10 + + // Main loop +main_loop: + XORL DX, DX + CMPQ BX, SI + SETGE DL + + // br0.fillFast32() + MOVQ 32(R10), R11 + MOVBQZX 40(R10), R12 + CMPQ R12, $0x20 + JBE skip_fill0 + MOVQ 24(R10), R13 + SUBQ $0x20, R12 + SUBQ $0x04, R13 + MOVQ (R10), R14 + + // b.value |= uint64(low) << (b.bitsRead & 63) + MOVL (R13)(R14*1), R14 + MOVQ R12, CX + SHLQ CL, R14 + MOVQ R13, 24(R10) + ORQ R14, R11 + + // exhausted += (br0.off < 4) + CMPQ R13, $0x04 + ADCB $+0, DL + +skip_fill0: + // val0 := br0.peekTopBits(peekBits) + MOVQ R11, R13 + MOVQ DI, CX + SHRQ CL, R13 + + // v0 := table[val0&mask] + MOVW (R9)(R13*2), CX + + // br0.advance(uint8(v0.entry) + MOVB CH, AL + SHLQ CL, R11 + ADDB CL, R12 + + // val1 := br0.peekTopBits(peekBits) + MOVQ R11, R13 + MOVQ DI, CX + SHRQ CL, R13 + + // v1 := table[val0&mask] + MOVW (R9)(R13*2), CX + + // br0.advance(uint8(v1.entry) + MOVB CH, AH + SHLQ CL, R11 + ADDB CL, R12 + BSWAPL AX + + // val2 := br0.peekTopBits(peekBits) + MOVQ R11, R13 + MOVQ DI, CX + SHRQ CL, R13 + + // v2 := table[val0&mask] + MOVW (R9)(R13*2), CX + + // br0.advance(uint8(v2.entry) + MOVB CH, AH + SHLQ CL, R11 + ADDB CL, R12 + + // val3 := br0.peekTopBits(peekBits) + MOVQ R11, R13 + MOVQ DI, CX + SHRQ CL, R13 + + // v3 := table[val0&mask] + MOVW (R9)(R13*2), CX + + // br0.advance(uint8(v3.entry) + MOVB CH, AL + SHLQ CL, R11 + ADDB CL, R12 + BSWAPL AX + + // these four writes get coalesced + // out[id * dstEvery + 0] = uint8(v0.entry >> 8) + // out[id * dstEvery + 1] = uint8(v1.entry >> 8) + // out[id * dstEvery + 3] = uint8(v2.entry >> 8) + // out[id * dstEvery + 4] = uint8(v3.entry >> 8) + MOVL AX, (BX) + + // update the bitreader structure + MOVQ R11, 32(R10) + MOVB R12, 40(R10) + + // br1.fillFast32() + MOVQ 80(R10), R11 + MOVBQZX 88(R10), R12 + CMPQ R12, $0x20 + JBE skip_fill1 + MOVQ 72(R10), R13 + SUBQ $0x20, R12 + SUBQ $0x04, R13 + MOVQ 48(R10), R14 + + // b.value |= uint64(low) << (b.bitsRead & 63) + MOVL (R13)(R14*1), R14 + MOVQ R12, CX + SHLQ CL, R14 + MOVQ R13, 72(R10) + ORQ R14, R11 + + // exhausted += (br1.off < 4) + CMPQ R13, $0x04 + ADCB $+0, DL + +skip_fill1: + // val0 := br1.peekTopBits(peekBits) + MOVQ R11, R13 + MOVQ DI, CX + SHRQ CL, R13 + + // v0 := table[val0&mask] + MOVW (R9)(R13*2), CX + + // br1.advance(uint8(v0.entry) + MOVB CH, AL + SHLQ CL, R11 + ADDB CL, R12 + + // val1 := br1.peekTopBits(peekBits) + MOVQ R11, R13 + MOVQ DI, CX + SHRQ CL, R13 + + // v1 := table[val0&mask] + MOVW (R9)(R13*2), CX + + // br1.advance(uint8(v1.entry) + MOVB CH, AH + SHLQ CL, R11 + ADDB CL, R12 + BSWAPL AX + + // val2 := br1.peekTopBits(peekBits) + MOVQ R11, R13 + MOVQ DI, CX + SHRQ CL, R13 + + // v2 := table[val0&mask] + MOVW (R9)(R13*2), CX + + // br1.advance(uint8(v2.entry) + MOVB CH, AH + SHLQ CL, R11 + ADDB CL, R12 + + // val3 := br1.peekTopBits(peekBits) + MOVQ R11, R13 + MOVQ DI, CX + SHRQ CL, R13 + + // v3 := table[val0&mask] + MOVW (R9)(R13*2), CX + + // br1.advance(uint8(v3.entry) + MOVB CH, AL + SHLQ CL, R11 + ADDB CL, R12 + BSWAPL AX + + // these four writes get coalesced + // out[id * dstEvery + 0] = uint8(v0.entry >> 8) + // out[id * dstEvery + 1] = uint8(v1.entry >> 8) + // out[id * dstEvery + 3] = uint8(v2.entry >> 8) + // out[id * dstEvery + 4] = uint8(v3.entry >> 8) + MOVL AX, (BX)(R8*1) + + // update the bitreader structure + MOVQ R11, 80(R10) + MOVB R12, 88(R10) + + // br2.fillFast32() + MOVQ 128(R10), R11 + MOVBQZX 136(R10), R12 + CMPQ R12, $0x20 + JBE skip_fill2 + MOVQ 120(R10), R13 + SUBQ $0x20, R12 + SUBQ $0x04, R13 + MOVQ 96(R10), R14 + + // b.value |= uint64(low) << (b.bitsRead & 63) + MOVL (R13)(R14*1), R14 + MOVQ R12, CX + SHLQ CL, R14 + MOVQ R13, 120(R10) + ORQ R14, R11 + + // exhausted += (br2.off < 4) + CMPQ R13, $0x04 + ADCB $+0, DL + +skip_fill2: + // val0 := br2.peekTopBits(peekBits) + MOVQ R11, R13 + MOVQ DI, CX + SHRQ CL, R13 + + // v0 := table[val0&mask] + MOVW (R9)(R13*2), CX + + // br2.advance(uint8(v0.entry) + MOVB CH, AL + SHLQ CL, R11 + ADDB CL, R12 + + // val1 := br2.peekTopBits(peekBits) + MOVQ R11, R13 + MOVQ DI, CX + SHRQ CL, R13 + + // v1 := table[val0&mask] + MOVW (R9)(R13*2), CX + + // br2.advance(uint8(v1.entry) + MOVB CH, AH + SHLQ CL, R11 + ADDB CL, R12 + BSWAPL AX + + // val2 := br2.peekTopBits(peekBits) + MOVQ R11, R13 + MOVQ DI, CX + SHRQ CL, R13 + + // v2 := table[val0&mask] + MOVW (R9)(R13*2), CX + + // br2.advance(uint8(v2.entry) + MOVB CH, AH + SHLQ CL, R11 + ADDB CL, R12 + + // val3 := br2.peekTopBits(peekBits) + MOVQ R11, R13 + MOVQ DI, CX + SHRQ CL, R13 + + // v3 := table[val0&mask] + MOVW (R9)(R13*2), CX + + // br2.advance(uint8(v3.entry) + MOVB CH, AL + SHLQ CL, R11 + ADDB CL, R12 + BSWAPL AX + + // these four writes get coalesced + // out[id * dstEvery + 0] = uint8(v0.entry >> 8) + // out[id * dstEvery + 1] = uint8(v1.entry >> 8) + // out[id * dstEvery + 3] = uint8(v2.entry >> 8) + // out[id * dstEvery + 4] = uint8(v3.entry >> 8) + MOVL AX, (BX)(R8*2) + + // update the bitreader structure + MOVQ R11, 128(R10) + MOVB R12, 136(R10) + + // br3.fillFast32() + MOVQ 176(R10), R11 + MOVBQZX 184(R10), R12 + CMPQ R12, $0x20 + JBE skip_fill3 + MOVQ 168(R10), R13 + SUBQ $0x20, R12 + SUBQ $0x04, R13 + MOVQ 144(R10), R14 + + // b.value |= uint64(low) << (b.bitsRead & 63) + MOVL (R13)(R14*1), R14 + MOVQ R12, CX + SHLQ CL, R14 + MOVQ R13, 168(R10) + ORQ R14, R11 + + // exhausted += (br3.off < 4) + CMPQ R13, $0x04 + ADCB $+0, DL + +skip_fill3: + // val0 := br3.peekTopBits(peekBits) + MOVQ R11, R13 + MOVQ DI, CX + SHRQ CL, R13 + + // v0 := table[val0&mask] + MOVW (R9)(R13*2), CX + + // br3.advance(uint8(v0.entry) + MOVB CH, AL + SHLQ CL, R11 + ADDB CL, R12 + + // val1 := br3.peekTopBits(peekBits) + MOVQ R11, R13 + MOVQ DI, CX + SHRQ CL, R13 + + // v1 := table[val0&mask] + MOVW (R9)(R13*2), CX + + // br3.advance(uint8(v1.entry) + MOVB CH, AH + SHLQ CL, R11 + ADDB CL, R12 + BSWAPL AX + + // val2 := br3.peekTopBits(peekBits) + MOVQ R11, R13 + MOVQ DI, CX + SHRQ CL, R13 + + // v2 := table[val0&mask] + MOVW (R9)(R13*2), CX + + // br3.advance(uint8(v2.entry) + MOVB CH, AH + SHLQ CL, R11 + ADDB CL, R12 + + // val3 := br3.peekTopBits(peekBits) + MOVQ R11, R13 + MOVQ DI, CX + SHRQ CL, R13 + + // v3 := table[val0&mask] + MOVW (R9)(R13*2), CX + + // br3.advance(uint8(v3.entry) + MOVB CH, AL + SHLQ CL, R11 + ADDB CL, R12 + BSWAPL AX + + // these four writes get coalesced + // out[id * dstEvery + 0] = uint8(v0.entry >> 8) + // out[id * dstEvery + 1] = uint8(v1.entry >> 8) + // out[id * dstEvery + 3] = uint8(v2.entry >> 8) + // out[id * dstEvery + 4] = uint8(v3.entry >> 8) + LEAQ (R8)(R8*2), CX + MOVL AX, (BX)(CX*1) + + // update the bitreader structure + MOVQ R11, 176(R10) + MOVB R12, 184(R10) + ADDQ $0x04, BX + TESTB DL, DL + JZ main_loop + MOVQ ctx+0(FP), AX + SUBQ 16(AX), BX + SHLQ $0x02, BX + MOVQ BX, 40(AX) + RET + +// func decompress1x_main_loop_amd64(ctx *decompress1xContext) +TEXT ·decompress1x_main_loop_amd64(SB), $0-8 + MOVQ ctx+0(FP), CX + MOVQ 16(CX), DX + MOVQ 24(CX), BX + CMPQ BX, $0x04 + JB error_max_decoded_size_exceeded + LEAQ (DX)(BX*1), BX + MOVQ (CX), SI + MOVQ (SI), R8 + MOVQ 24(SI), R9 + MOVQ 32(SI), R10 + MOVBQZX 40(SI), R11 + MOVQ 32(CX), SI + MOVBQZX 8(CX), DI + JMP loop_condition + +main_loop: + // Check if we have room for 4 bytes in the output buffer + LEAQ 4(DX), CX + CMPQ CX, BX + JGE error_max_decoded_size_exceeded + + // Decode 4 values + CMPQ R11, $0x20 + JL bitReader_fillFast_1_end + SUBQ $0x20, R11 + SUBQ $0x04, R9 + MOVL (R8)(R9*1), R12 + MOVQ R11, CX + SHLQ CL, R12 + ORQ R12, R10 + +bitReader_fillFast_1_end: + MOVQ DI, CX + MOVQ R10, R12 + SHRQ CL, R12 + MOVW (SI)(R12*2), CX + MOVB CH, AL + MOVBQZX CL, CX + ADDQ CX, R11 + SHLQ CL, R10 + MOVQ DI, CX + MOVQ R10, R12 + SHRQ CL, R12 + MOVW (SI)(R12*2), CX + MOVB CH, AH + MOVBQZX CL, CX + ADDQ CX, R11 + SHLQ CL, R10 + BSWAPL AX + CMPQ R11, $0x20 + JL bitReader_fillFast_2_end + SUBQ $0x20, R11 + SUBQ $0x04, R9 + MOVL (R8)(R9*1), R12 + MOVQ R11, CX + SHLQ CL, R12 + ORQ R12, R10 + +bitReader_fillFast_2_end: + MOVQ DI, CX + MOVQ R10, R12 + SHRQ CL, R12 + MOVW (SI)(R12*2), CX + MOVB CH, AH + MOVBQZX CL, CX + ADDQ CX, R11 + SHLQ CL, R10 + MOVQ DI, CX + MOVQ R10, R12 + SHRQ CL, R12 + MOVW (SI)(R12*2), CX + MOVB CH, AL + MOVBQZX CL, CX + ADDQ CX, R11 + SHLQ CL, R10 + BSWAPL AX + + // Store the decoded values + MOVL AX, (DX) + ADDQ $0x04, DX + +loop_condition: + CMPQ R9, $0x08 + JGE main_loop + + // Update ctx structure + MOVQ ctx+0(FP), AX + SUBQ 16(AX), DX + MOVQ DX, 40(AX) + MOVQ (AX), AX + MOVQ R9, 24(AX) + MOVQ R10, 32(AX) + MOVB R11, 40(AX) + RET + + // Report error +error_max_decoded_size_exceeded: + MOVQ ctx+0(FP), AX + MOVQ $-1, CX + MOVQ CX, 40(AX) + RET + +// func decompress1x_main_loop_bmi2(ctx *decompress1xContext) +// Requires: BMI2 +TEXT ·decompress1x_main_loop_bmi2(SB), $0-8 + MOVQ ctx+0(FP), CX + MOVQ 16(CX), DX + MOVQ 24(CX), BX + CMPQ BX, $0x04 + JB error_max_decoded_size_exceeded + LEAQ (DX)(BX*1), BX + MOVQ (CX), SI + MOVQ (SI), R8 + MOVQ 24(SI), R9 + MOVQ 32(SI), R10 + MOVBQZX 40(SI), R11 + MOVQ 32(CX), SI + MOVBQZX 8(CX), DI + JMP loop_condition + +main_loop: + // Check if we have room for 4 bytes in the output buffer + LEAQ 4(DX), CX + CMPQ CX, BX + JGE error_max_decoded_size_exceeded + + // Decode 4 values + CMPQ R11, $0x20 + JL bitReader_fillFast_1_end + SUBQ $0x20, R11 + SUBQ $0x04, R9 + MOVL (R8)(R9*1), CX + SHLXQ R11, CX, CX + ORQ CX, R10 + +bitReader_fillFast_1_end: + SHRXQ DI, R10, CX + MOVW (SI)(CX*2), CX + MOVB CH, AL + MOVBQZX CL, CX + ADDQ CX, R11 + SHLXQ CX, R10, R10 + SHRXQ DI, R10, CX + MOVW (SI)(CX*2), CX + MOVB CH, AH + MOVBQZX CL, CX + ADDQ CX, R11 + SHLXQ CX, R10, R10 + BSWAPL AX + CMPQ R11, $0x20 + JL bitReader_fillFast_2_end + SUBQ $0x20, R11 + SUBQ $0x04, R9 + MOVL (R8)(R9*1), CX + SHLXQ R11, CX, CX + ORQ CX, R10 + +bitReader_fillFast_2_end: + SHRXQ DI, R10, CX + MOVW (SI)(CX*2), CX + MOVB CH, AH + MOVBQZX CL, CX + ADDQ CX, R11 + SHLXQ CX, R10, R10 + SHRXQ DI, R10, CX + MOVW (SI)(CX*2), CX + MOVB CH, AL + MOVBQZX CL, CX + ADDQ CX, R11 + SHLXQ CX, R10, R10 + BSWAPL AX + + // Store the decoded values + MOVL AX, (DX) + ADDQ $0x04, DX + +loop_condition: + CMPQ R9, $0x08 + JGE main_loop + + // Update ctx structure + MOVQ ctx+0(FP), AX + SUBQ 16(AX), DX + MOVQ DX, 40(AX) + MOVQ (AX), AX + MOVQ R9, 24(AX) + MOVQ R10, 32(AX) + MOVB R11, 40(AX) + RET + + // Report error +error_max_decoded_size_exceeded: + MOVQ ctx+0(FP), AX + MOVQ $-1, CX + MOVQ CX, 40(AX) + RET diff --git a/vendor/github.com/klauspost/compress/huff0/decompress_generic.go b/vendor/github.com/klauspost/compress/huff0/decompress_generic.go new file mode 100644 index 000000000..610392322 --- /dev/null +++ b/vendor/github.com/klauspost/compress/huff0/decompress_generic.go @@ -0,0 +1,298 @@ +//go:build !amd64 || appengine || !gc || noasm + +// This file contains a generic implementation of Decoder.Decompress4X. +package huff0 + +import ( + "errors" + "fmt" +) + +// Decompress4X will decompress a 4X encoded stream. +// The length of the supplied input must match the end of a block exactly. +// The *capacity* of the dst slice must match the destination size of +// the uncompressed data exactly. +func (d *Decoder) Decompress4X(dst, src []byte) ([]byte, error) { + if len(d.dt.single) == 0 { + return nil, errors.New("no table loaded") + } + if len(src) < 6+(4*1) { + return nil, errors.New("input too small") + } + if use8BitTables && d.actualTableLog <= 8 { + return d.decompress4X8bit(dst, src) + } + + var br [4]bitReaderShifted + // Decode "jump table" + start := 6 + for i := 0; i < 3; i++ { + length := int(src[i*2]) | (int(src[i*2+1]) << 8) + if start+length >= len(src) { + return nil, errors.New("truncated input (or invalid offset)") + } + err := br[i].init(src[start : start+length]) + if err != nil { + return nil, err + } + start += length + } + err := br[3].init(src[start:]) + if err != nil { + return nil, err + } + + // destination, offset to match first output + dstSize := cap(dst) + dst = dst[:dstSize] + out := dst + dstEvery := (dstSize + 3) / 4 + + const tlSize = 1 << tableLogMax + const tlMask = tlSize - 1 + single := d.dt.single[:tlSize] + + // Use temp table to avoid bound checks/append penalty. + buf := d.buffer() + var off uint8 + var decoded int + + // Decode 2 values from each decoder/loop. + const bufoff = 256 + for { + if br[0].off < 4 || br[1].off < 4 || br[2].off < 4 || br[3].off < 4 { + break + } + + { + const stream = 0 + const stream2 = 1 + br[stream].fillFast() + br[stream2].fillFast() + + val := br[stream].peekBitsFast(d.actualTableLog) + val2 := br[stream2].peekBitsFast(d.actualTableLog) + v := single[val&tlMask] + v2 := single[val2&tlMask] + br[stream].advance(uint8(v.entry)) + br[stream2].advance(uint8(v2.entry)) + buf[stream][off] = uint8(v.entry >> 8) + buf[stream2][off] = uint8(v2.entry >> 8) + + val = br[stream].peekBitsFast(d.actualTableLog) + val2 = br[stream2].peekBitsFast(d.actualTableLog) + v = single[val&tlMask] + v2 = single[val2&tlMask] + br[stream].advance(uint8(v.entry)) + br[stream2].advance(uint8(v2.entry)) + buf[stream][off+1] = uint8(v.entry >> 8) + buf[stream2][off+1] = uint8(v2.entry >> 8) + } + + { + const stream = 2 + const stream2 = 3 + br[stream].fillFast() + br[stream2].fillFast() + + val := br[stream].peekBitsFast(d.actualTableLog) + val2 := br[stream2].peekBitsFast(d.actualTableLog) + v := single[val&tlMask] + v2 := single[val2&tlMask] + br[stream].advance(uint8(v.entry)) + br[stream2].advance(uint8(v2.entry)) + buf[stream][off] = uint8(v.entry >> 8) + buf[stream2][off] = uint8(v2.entry >> 8) + + val = br[stream].peekBitsFast(d.actualTableLog) + val2 = br[stream2].peekBitsFast(d.actualTableLog) + v = single[val&tlMask] + v2 = single[val2&tlMask] + br[stream].advance(uint8(v.entry)) + br[stream2].advance(uint8(v2.entry)) + buf[stream][off+1] = uint8(v.entry >> 8) + buf[stream2][off+1] = uint8(v2.entry >> 8) + } + + off += 2 + + if off == 0 { + if bufoff > dstEvery { + d.bufs.Put(buf) + return nil, errors.New("corruption detected: stream overrun 1") + } + // There must at least be 3 buffers left. + if len(out)-bufoff < dstEvery*3 { + d.bufs.Put(buf) + return nil, errors.New("corruption detected: stream overrun 2") + } + //copy(out, buf[0][:]) + //copy(out[dstEvery:], buf[1][:]) + //copy(out[dstEvery*2:], buf[2][:]) + //copy(out[dstEvery*3:], buf[3][:]) + *(*[bufoff]byte)(out) = buf[0] + *(*[bufoff]byte)(out[dstEvery:]) = buf[1] + *(*[bufoff]byte)(out[dstEvery*2:]) = buf[2] + *(*[bufoff]byte)(out[dstEvery*3:]) = buf[3] + out = out[bufoff:] + decoded += bufoff * 4 + } + } + if off > 0 { + ioff := int(off) + if len(out) < dstEvery*3+ioff { + d.bufs.Put(buf) + return nil, errors.New("corruption detected: stream overrun 3") + } + copy(out, buf[0][:off]) + copy(out[dstEvery:], buf[1][:off]) + copy(out[dstEvery*2:], buf[2][:off]) + copy(out[dstEvery*3:], buf[3][:off]) + decoded += int(off) * 4 + out = out[off:] + } + + // Decode remaining. + remainBytes := dstEvery - (decoded / 4) + for i := range br { + offset := dstEvery * i + endsAt := offset + remainBytes + if endsAt > len(out) { + endsAt = len(out) + } + br := &br[i] + bitsLeft := br.remaining() + for bitsLeft > 0 { + br.fill() + if offset >= endsAt { + d.bufs.Put(buf) + return nil, errors.New("corruption detected: stream overrun 4") + } + + // Read value and increment offset. + val := br.peekBitsFast(d.actualTableLog) + v := single[val&tlMask].entry + nBits := uint8(v) + br.advance(nBits) + bitsLeft -= uint(nBits) + out[offset] = uint8(v >> 8) + offset++ + } + if offset != endsAt { + d.bufs.Put(buf) + return nil, fmt.Errorf("corruption detected: short output block %d, end %d != %d", i, offset, endsAt) + } + decoded += offset - dstEvery*i + err = br.close() + if err != nil { + return nil, err + } + } + d.bufs.Put(buf) + if dstSize != decoded { + return nil, errors.New("corruption detected: short output block") + } + return dst, nil +} + +// Decompress1X will decompress a 1X encoded stream. +// The cap of the output buffer will be the maximum decompressed size. +// The length of the supplied input must match the end of a block exactly. +func (d *Decoder) Decompress1X(dst, src []byte) ([]byte, error) { + if len(d.dt.single) == 0 { + return nil, errors.New("no table loaded") + } + if use8BitTables && d.actualTableLog <= 8 { + return d.decompress1X8Bit(dst, src) + } + var br bitReaderShifted + err := br.init(src) + if err != nil { + return dst, err + } + maxDecodedSize := cap(dst) + dst = dst[:0] + + // Avoid bounds check by always having full sized table. + const tlSize = 1 << tableLogMax + const tlMask = tlSize - 1 + dt := d.dt.single[:tlSize] + + // Use temp table to avoid bound checks/append penalty. + bufs := d.buffer() + buf := &bufs[0] + var off uint8 + + for br.off >= 8 { + br.fillFast() + v := dt[br.peekBitsFast(d.actualTableLog)&tlMask] + br.advance(uint8(v.entry)) + buf[off+0] = uint8(v.entry >> 8) + + v = dt[br.peekBitsFast(d.actualTableLog)&tlMask] + br.advance(uint8(v.entry)) + buf[off+1] = uint8(v.entry >> 8) + + // Refill + br.fillFast() + + v = dt[br.peekBitsFast(d.actualTableLog)&tlMask] + br.advance(uint8(v.entry)) + buf[off+2] = uint8(v.entry >> 8) + + v = dt[br.peekBitsFast(d.actualTableLog)&tlMask] + br.advance(uint8(v.entry)) + buf[off+3] = uint8(v.entry >> 8) + + off += 4 + if off == 0 { + if len(dst)+256 > maxDecodedSize { + br.close() + d.bufs.Put(bufs) + return nil, ErrMaxDecodedSizeExceeded + } + dst = append(dst, buf[:]...) + } + } + + if len(dst)+int(off) > maxDecodedSize { + d.bufs.Put(bufs) + br.close() + return nil, ErrMaxDecodedSizeExceeded + } + dst = append(dst, buf[:off]...) + + // br < 8, so uint8 is fine + bitsLeft := uint8(br.off)*8 + 64 - br.bitsRead + for bitsLeft > 0 { + br.fill() + if false && br.bitsRead >= 32 { + if br.off >= 4 { + v := br.in[br.off-4:] + v = v[:4] + low := (uint32(v[0])) | (uint32(v[1]) << 8) | (uint32(v[2]) << 16) | (uint32(v[3]) << 24) + br.value = (br.value << 32) | uint64(low) + br.bitsRead -= 32 + br.off -= 4 + } else { + for br.off > 0 { + br.value = (br.value << 8) | uint64(br.in[br.off-1]) + br.bitsRead -= 8 + br.off-- + } + } + } + if len(dst) >= maxDecodedSize { + d.bufs.Put(bufs) + br.close() + return nil, ErrMaxDecodedSizeExceeded + } + v := d.dt.single[br.peekBitsFast(d.actualTableLog)&tlMask] + nBits := uint8(v.entry) + br.advance(nBits) + bitsLeft -= nBits + dst = append(dst, uint8(v.entry>>8)) + } + d.bufs.Put(bufs) + return dst, br.close() +} diff --git a/vendor/github.com/klauspost/compress/huff0/huff0.go b/vendor/github.com/klauspost/compress/huff0/huff0.go new file mode 100644 index 000000000..67d9e05b6 --- /dev/null +++ b/vendor/github.com/klauspost/compress/huff0/huff0.go @@ -0,0 +1,337 @@ +// Package huff0 provides fast huffman encoding as used in zstd. +// +// See README.md at https://github.com/klauspost/compress/tree/master/huff0 for details. +package huff0 + +import ( + "errors" + "fmt" + "math" + "math/bits" + "sync" + + "github.com/klauspost/compress/fse" +) + +const ( + maxSymbolValue = 255 + + // zstandard limits tablelog to 11, see: + // https://github.com/facebook/zstd/blob/dev/doc/zstd_compression_format.md#huffman-tree-description + tableLogMax = 11 + tableLogDefault = 11 + minTablelog = 5 + huffNodesLen = 512 + + // BlockSizeMax is maximum input size for a single block uncompressed. + BlockSizeMax = 1<<18 - 1 +) + +var ( + // ErrIncompressible is returned when input is judged to be too hard to compress. + ErrIncompressible = errors.New("input is not compressible") + + // ErrUseRLE is returned from the compressor when the input is a single byte value repeated. + ErrUseRLE = errors.New("input is single value repeated") + + // ErrTooBig is return if input is too large for a single block. + ErrTooBig = errors.New("input too big") + + // ErrMaxDecodedSizeExceeded is return if input is too large for a single block. + ErrMaxDecodedSizeExceeded = errors.New("maximum output size exceeded") +) + +type ReusePolicy uint8 + +const ( + // ReusePolicyAllow will allow reuse if it produces smaller output. + ReusePolicyAllow ReusePolicy = iota + + // ReusePolicyPrefer will re-use aggressively if possible. + // This will not check if a new table will produce smaller output, + // except if the current table is impossible to use or + // compressed output is bigger than input. + ReusePolicyPrefer + + // ReusePolicyNone will disable re-use of tables. + // This is slightly faster than ReusePolicyAllow but may produce larger output. + ReusePolicyNone + + // ReusePolicyMust must allow reuse and produce smaller output. + ReusePolicyMust +) + +type Scratch struct { + count [maxSymbolValue + 1]uint32 + + // Per block parameters. + // These can be used to override compression parameters of the block. + // Do not touch, unless you know what you are doing. + + // Out is output buffer. + // If the scratch is re-used before the caller is done processing the output, + // set this field to nil. + // Otherwise the output buffer will be re-used for next Compression/Decompression step + // and allocation will be avoided. + Out []byte + + // OutTable will contain the table data only, if a new table has been generated. + // Slice of the returned data. + OutTable []byte + + // OutData will contain the compressed data. + // Slice of the returned data. + OutData []byte + + // MaxDecodedSize will set the maximum allowed output size. + // This value will automatically be set to BlockSizeMax if not set. + // Decoders will return ErrMaxDecodedSizeExceeded is this limit is exceeded. + MaxDecodedSize int + + srcLen int + + // MaxSymbolValue will override the maximum symbol value of the next block. + MaxSymbolValue uint8 + + // TableLog will attempt to override the tablelog for the next block. + // Must be <= 11 and >= 5. + TableLog uint8 + + // Reuse will specify the reuse policy + Reuse ReusePolicy + + // WantLogLess allows to specify a log 2 reduction that should at least be achieved, + // otherwise the block will be returned as incompressible. + // The reduction should then at least be (input size >> WantLogLess) + // If WantLogLess == 0 any improvement will do. + WantLogLess uint8 + + symbolLen uint16 // Length of active part of the symbol table. + maxCount int // count of the most probable symbol + clearCount bool // clear count + actualTableLog uint8 // Selected tablelog. + prevTableLog uint8 // Tablelog for previous table + prevTable cTable // Table used for previous compression. + cTable cTable // compression table + dt dTable // decompression table + nodes []nodeElt + tmpOut [4][]byte + fse *fse.Scratch + decPool sync.Pool // *[4][256]byte buffers. + huffWeight [maxSymbolValue + 1]byte +} + +// TransferCTable will transfer the previously used compression table. +func (s *Scratch) TransferCTable(src *Scratch) { + if cap(s.prevTable) < len(src.prevTable) { + s.prevTable = make(cTable, 0, maxSymbolValue+1) + } + s.prevTable = s.prevTable[:len(src.prevTable)] + copy(s.prevTable, src.prevTable) + s.prevTableLog = src.prevTableLog +} + +func (s *Scratch) prepare(in []byte) (*Scratch, error) { + if len(in) > BlockSizeMax { + return nil, ErrTooBig + } + if s == nil { + s = &Scratch{} + } + if s.MaxSymbolValue == 0 { + s.MaxSymbolValue = maxSymbolValue + } + if s.TableLog == 0 { + s.TableLog = tableLogDefault + } + if s.TableLog > tableLogMax || s.TableLog < minTablelog { + return nil, fmt.Errorf(" invalid tableLog %d (%d -> %d)", s.TableLog, minTablelog, tableLogMax) + } + if s.MaxDecodedSize <= 0 || s.MaxDecodedSize > BlockSizeMax { + s.MaxDecodedSize = BlockSizeMax + } + if s.clearCount && s.maxCount == 0 { + for i := range s.count { + s.count[i] = 0 + } + s.clearCount = false + } + if cap(s.Out) == 0 { + s.Out = make([]byte, 0, len(in)) + } + s.Out = s.Out[:0] + + s.OutTable = nil + s.OutData = nil + if cap(s.nodes) < huffNodesLen+1 { + s.nodes = make([]nodeElt, 0, huffNodesLen+1) + } + s.nodes = s.nodes[:0] + if s.fse == nil { + s.fse = &fse.Scratch{} + } + s.srcLen = len(in) + + return s, nil +} + +type cTable []cTableEntry + +func (c cTable) write(s *Scratch) error { + var ( + // precomputed conversion table + bitsToWeight [tableLogMax + 1]byte + huffLog = s.actualTableLog + // last weight is not saved. + maxSymbolValue = uint8(s.symbolLen - 1) + huffWeight = s.huffWeight[:256] + ) + const ( + maxFSETableLog = 6 + ) + // convert to weight + bitsToWeight[0] = 0 + for n := uint8(1); n < huffLog+1; n++ { + bitsToWeight[n] = huffLog + 1 - n + } + + // Acquire histogram for FSE. + hist := s.fse.Histogram() + hist = hist[:256] + for i := range hist[:16] { + hist[i] = 0 + } + for n := range maxSymbolValue { + v := bitsToWeight[c[n].nBits] & 15 + huffWeight[n] = v + hist[v]++ + } + + // FSE compress if feasible. + if maxSymbolValue >= 2 { + huffMaxCnt := uint32(0) + huffMax := uint8(0) + for i, v := range hist[:16] { + if v == 0 { + continue + } + huffMax = byte(i) + if v > huffMaxCnt { + huffMaxCnt = v + } + } + s.fse.HistogramFinished(huffMax, int(huffMaxCnt)) + s.fse.TableLog = maxFSETableLog + b, err := fse.Compress(huffWeight[:maxSymbolValue], s.fse) + if err == nil && len(b) < int(s.symbolLen>>1) { + s.Out = append(s.Out, uint8(len(b))) + s.Out = append(s.Out, b...) + return nil + } + // Unable to compress (RLE/uncompressible) + } + // write raw values as 4-bits (max : 15) + if maxSymbolValue > (256 - 128) { + // should not happen : likely means source cannot be compressed + return ErrIncompressible + } + op := s.Out + // special case, pack weights 4 bits/weight. + op = append(op, 128|(maxSymbolValue-1)) + // be sure it doesn't cause msan issue in final combination + huffWeight[maxSymbolValue] = 0 + for n := uint16(0); n < uint16(maxSymbolValue); n += 2 { + op = append(op, (huffWeight[n]<<4)|huffWeight[n+1]) + } + s.Out = op + return nil +} + +func (c cTable) estTableSize(s *Scratch) (sz int, err error) { + var ( + // precomputed conversion table + bitsToWeight [tableLogMax + 1]byte + huffLog = s.actualTableLog + // last weight is not saved. + maxSymbolValue = uint8(s.symbolLen - 1) + huffWeight = s.huffWeight[:256] + ) + const ( + maxFSETableLog = 6 + ) + // convert to weight + bitsToWeight[0] = 0 + for n := uint8(1); n < huffLog+1; n++ { + bitsToWeight[n] = huffLog + 1 - n + } + + // Acquire histogram for FSE. + hist := s.fse.Histogram() + hist = hist[:256] + for i := range hist[:16] { + hist[i] = 0 + } + for n := range maxSymbolValue { + v := bitsToWeight[c[n].nBits] & 15 + huffWeight[n] = v + hist[v]++ + } + + // FSE compress if feasible. + if maxSymbolValue >= 2 { + huffMaxCnt := uint32(0) + huffMax := uint8(0) + for i, v := range hist[:16] { + if v == 0 { + continue + } + huffMax = byte(i) + if v > huffMaxCnt { + huffMaxCnt = v + } + } + s.fse.HistogramFinished(huffMax, int(huffMaxCnt)) + s.fse.TableLog = maxFSETableLog + b, err := fse.Compress(huffWeight[:maxSymbolValue], s.fse) + if err == nil && len(b) < int(s.symbolLen>>1) { + sz += 1 + len(b) + return sz, nil + } + // Unable to compress (RLE/uncompressible) + } + // write raw values as 4-bits (max : 15) + if maxSymbolValue > (256 - 128) { + // should not happen : likely means source cannot be compressed + return 0, ErrIncompressible + } + // special case, pack weights 4 bits/weight. + sz += 1 + int(maxSymbolValue/2) + return sz, nil +} + +// estimateSize returns the estimated size in bytes of the input represented in the +// histogram supplied. +func (c cTable) estimateSize(hist []uint32) int { + nbBits := uint32(7) + for i, v := range c[:len(hist)] { + nbBits += uint32(v.nBits) * hist[i] + } + return int(nbBits >> 3) +} + +// minSize returns the minimum possible size considering the shannon limit. +func (s *Scratch) minSize(total int) int { + nbBits := float64(7) + fTotal := float64(total) + for _, v := range s.count[:s.symbolLen] { + n := float64(v) + if n > 0 { + nbBits += math.Log2(fTotal/n) * n + } + } + return int(nbBits) >> 3 +} + +func highBit32(val uint32) (n uint32) { + return uint32(bits.Len32(val) - 1) +} diff --git a/vendor/github.com/klauspost/compress/internal/cpuinfo/cpuinfo.go b/vendor/github.com/klauspost/compress/internal/cpuinfo/cpuinfo.go new file mode 100644 index 000000000..3954c5121 --- /dev/null +++ b/vendor/github.com/klauspost/compress/internal/cpuinfo/cpuinfo.go @@ -0,0 +1,34 @@ +// Package cpuinfo gives runtime info about the current CPU. +// +// This is a very limited module meant for use internally +// in this project. For more versatile solution check +// https://github.com/klauspost/cpuid. +package cpuinfo + +// HasBMI1 checks whether an x86 CPU supports the BMI1 extension. +func HasBMI1() bool { + return hasBMI1 +} + +// HasBMI2 checks whether an x86 CPU supports the BMI2 extension. +func HasBMI2() bool { + return hasBMI2 +} + +// DisableBMI2 will disable BMI2, for testing purposes. +// Call returned function to restore previous state. +func DisableBMI2() func() { + old := hasBMI2 + hasBMI2 = false + return func() { + hasBMI2 = old + } +} + +// HasBMI checks whether an x86 CPU supports both BMI1 and BMI2 extensions. +func HasBMI() bool { + return HasBMI1() && HasBMI2() +} + +var hasBMI1 bool +var hasBMI2 bool diff --git a/vendor/github.com/klauspost/compress/internal/cpuinfo/cpuinfo_amd64.go b/vendor/github.com/klauspost/compress/internal/cpuinfo/cpuinfo_amd64.go new file mode 100644 index 000000000..b97f9056f --- /dev/null +++ b/vendor/github.com/klauspost/compress/internal/cpuinfo/cpuinfo_amd64.go @@ -0,0 +1,10 @@ +//go:build amd64 && !appengine && !noasm && gc + +package cpuinfo + +// go:noescape +func x86extensions() (bmi1, bmi2 bool) + +func init() { + hasBMI1, hasBMI2 = x86extensions() +} diff --git a/vendor/github.com/klauspost/compress/internal/cpuinfo/cpuinfo_amd64.s b/vendor/github.com/klauspost/compress/internal/cpuinfo/cpuinfo_amd64.s new file mode 100644 index 000000000..4465fbe9e --- /dev/null +++ b/vendor/github.com/klauspost/compress/internal/cpuinfo/cpuinfo_amd64.s @@ -0,0 +1,36 @@ +// +build !appengine +// +build gc +// +build !noasm + +#include "textflag.h" +#include "funcdata.h" +#include "go_asm.h" + +TEXT ·x86extensions(SB), NOSPLIT, $0 + // 1. determine max EAX value + XORQ AX, AX + CPUID + + CMPQ AX, $7 + JB unsupported + + // 2. EAX = 7, ECX = 0 --- see Table 3-8 "Information Returned by CPUID Instruction" + MOVQ $7, AX + MOVQ $0, CX + CPUID + + BTQ $3, BX // bit 3 = BMI1 + SETCS AL + + BTQ $8, BX // bit 8 = BMI2 + SETCS AH + + MOVB AL, bmi1+0(FP) + MOVB AH, bmi2+1(FP) + RET + +unsupported: + XORQ AX, AX + MOVB AL, bmi1+0(FP) + MOVB AL, bmi2+1(FP) + RET diff --git a/vendor/github.com/klauspost/compress/internal/le/le.go b/vendor/github.com/klauspost/compress/internal/le/le.go new file mode 100644 index 000000000..e54909e16 --- /dev/null +++ b/vendor/github.com/klauspost/compress/internal/le/le.go @@ -0,0 +1,5 @@ +package le + +type Indexer interface { + int | int8 | int16 | int32 | int64 | uint | uint8 | uint16 | uint32 | uint64 +} diff --git a/vendor/github.com/klauspost/compress/internal/le/unsafe_disabled.go b/vendor/github.com/klauspost/compress/internal/le/unsafe_disabled.go new file mode 100644 index 000000000..4f2a0d8c5 --- /dev/null +++ b/vendor/github.com/klauspost/compress/internal/le/unsafe_disabled.go @@ -0,0 +1,42 @@ +//go:build !(amd64 || arm64 || ppc64le || riscv64) || nounsafe || purego || appengine + +package le + +import ( + "encoding/binary" +) + +// Load8 will load from b at index i. +func Load8[I Indexer](b []byte, i I) byte { + return b[i] +} + +// Load16 will load from b at index i. +func Load16[I Indexer](b []byte, i I) uint16 { + return binary.LittleEndian.Uint16(b[i:]) +} + +// Load32 will load from b at index i. +func Load32[I Indexer](b []byte, i I) uint32 { + return binary.LittleEndian.Uint32(b[i:]) +} + +// Load64 will load from b at index i. +func Load64[I Indexer](b []byte, i I) uint64 { + return binary.LittleEndian.Uint64(b[i:]) +} + +// Store16 will store v at b. +func Store16(b []byte, v uint16) { + binary.LittleEndian.PutUint16(b, v) +} + +// Store32 will store v at b. +func Store32(b []byte, v uint32) { + binary.LittleEndian.PutUint32(b, v) +} + +// Store64 will store v at b. +func Store64[I Indexer](b []byte, i I, v uint64) { + binary.LittleEndian.PutUint64(b[i:], v) +} diff --git a/vendor/github.com/klauspost/compress/internal/le/unsafe_enabled.go b/vendor/github.com/klauspost/compress/internal/le/unsafe_enabled.go new file mode 100644 index 000000000..218a38bc4 --- /dev/null +++ b/vendor/github.com/klauspost/compress/internal/le/unsafe_enabled.go @@ -0,0 +1,52 @@ +// We enable 64 bit LE platforms: + +//go:build (amd64 || arm64 || ppc64le || riscv64) && !nounsafe && !purego && !appengine + +package le + +import ( + "unsafe" +) + +// Load8 will load from b at index i. +func Load8[I Indexer](b []byte, i I) byte { + //return binary.LittleEndian.Uint16(b[i:]) + //return *(*uint16)(unsafe.Pointer(&b[i])) + return *(*byte)(unsafe.Add(unsafe.Pointer(unsafe.SliceData(b)), i)) +} + +// Load16 will load from b at index i. +func Load16[I Indexer](b []byte, i I) uint16 { + //return binary.LittleEndian.Uint16(b[i:]) + //return *(*uint16)(unsafe.Pointer(&b[i])) + return *(*uint16)(unsafe.Add(unsafe.Pointer(unsafe.SliceData(b)), i)) +} + +// Load32 will load from b at index i. +func Load32[I Indexer](b []byte, i I) uint32 { + //return binary.LittleEndian.Uint32(b[i:]) + //return *(*uint32)(unsafe.Pointer(&b[i])) + return *(*uint32)(unsafe.Add(unsafe.Pointer(unsafe.SliceData(b)), i)) +} + +// Load64 will load from b at index i. +func Load64[I Indexer](b []byte, i I) uint64 { + //return binary.LittleEndian.Uint64(b[i:]) + //return *(*uint64)(unsafe.Pointer(&b[i])) + return *(*uint64)(unsafe.Add(unsafe.Pointer(unsafe.SliceData(b)), i)) +} + +// Store16 will store v at b. +func Store16(b []byte, v uint16) { + *(*uint16)(unsafe.Pointer(unsafe.SliceData(b))) = v +} + +// Store32 will store v at b. +func Store32(b []byte, v uint32) { + *(*uint32)(unsafe.Pointer(unsafe.SliceData(b))) = v +} + +// Store64 will store v at b[i:]. +func Store64[I Indexer](b []byte, i I, v uint64) { + *(*uint64)(unsafe.Add(unsafe.Pointer(unsafe.SliceData(b)), i)) = v +} diff --git a/vendor/github.com/klauspost/compress/internal/race/norace.go b/vendor/github.com/klauspost/compress/internal/race/norace.go new file mode 100644 index 000000000..affbbbb59 --- /dev/null +++ b/vendor/github.com/klauspost/compress/internal/race/norace.go @@ -0,0 +1,13 @@ +// Copyright 2015 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build !race + +package race + +func ReadSlice[T any](s []T) { +} + +func WriteSlice[T any](s []T) { +} diff --git a/vendor/github.com/klauspost/compress/internal/race/race.go b/vendor/github.com/klauspost/compress/internal/race/race.go new file mode 100644 index 000000000..f5e240dcd --- /dev/null +++ b/vendor/github.com/klauspost/compress/internal/race/race.go @@ -0,0 +1,26 @@ +// Copyright 2015 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build race + +package race + +import ( + "runtime" + "unsafe" +) + +func ReadSlice[T any](s []T) { + if len(s) == 0 { + return + } + runtime.RaceReadRange(unsafe.Pointer(&s[0]), len(s)*int(unsafe.Sizeof(s[0]))) +} + +func WriteSlice[T any](s []T) { + if len(s) == 0 { + return + } + runtime.RaceWriteRange(unsafe.Pointer(&s[0]), len(s)*int(unsafe.Sizeof(s[0]))) +} diff --git a/vendor/github.com/klauspost/compress/internal/snapref/LICENSE b/vendor/github.com/klauspost/compress/internal/snapref/LICENSE new file mode 100644 index 000000000..6050c10f4 --- /dev/null +++ b/vendor/github.com/klauspost/compress/internal/snapref/LICENSE @@ -0,0 +1,27 @@ +Copyright (c) 2011 The Snappy-Go Authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/vendor/github.com/klauspost/compress/internal/snapref/decode.go b/vendor/github.com/klauspost/compress/internal/snapref/decode.go new file mode 100644 index 000000000..a2c82fcd2 --- /dev/null +++ b/vendor/github.com/klauspost/compress/internal/snapref/decode.go @@ -0,0 +1,264 @@ +// Copyright 2011 The Snappy-Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package snapref + +import ( + "encoding/binary" + "errors" + "io" +) + +var ( + // ErrCorrupt reports that the input is invalid. + ErrCorrupt = errors.New("snappy: corrupt input") + // ErrTooLarge reports that the uncompressed length is too large. + ErrTooLarge = errors.New("snappy: decoded block is too large") + // ErrUnsupported reports that the input isn't supported. + ErrUnsupported = errors.New("snappy: unsupported input") + + errUnsupportedLiteralLength = errors.New("snappy: unsupported literal length") +) + +// DecodedLen returns the length of the decoded block. +func DecodedLen(src []byte) (int, error) { + v, _, err := decodedLen(src) + return v, err +} + +// decodedLen returns the length of the decoded block and the number of bytes +// that the length header occupied. +func decodedLen(src []byte) (blockLen, headerLen int, err error) { + v, n := binary.Uvarint(src) + if n <= 0 || v > 0xffffffff { + return 0, 0, ErrCorrupt + } + + const wordSize = 32 << (^uint(0) >> 32 & 1) + if wordSize == 32 && v > 0x7fffffff { + return 0, 0, ErrTooLarge + } + return int(v), n, nil +} + +const ( + decodeErrCodeCorrupt = 1 + decodeErrCodeUnsupportedLiteralLength = 2 +) + +// Decode returns the decoded form of src. The returned slice may be a sub- +// slice of dst if dst was large enough to hold the entire decoded block. +// Otherwise, a newly allocated slice will be returned. +// +// The dst and src must not overlap. It is valid to pass a nil dst. +// +// Decode handles the Snappy block format, not the Snappy stream format. +func Decode(dst, src []byte) ([]byte, error) { + dLen, s, err := decodedLen(src) + if err != nil { + return nil, err + } + if dLen <= len(dst) { + dst = dst[:dLen] + } else { + dst = make([]byte, dLen) + } + switch decode(dst, src[s:]) { + case 0: + return dst, nil + case decodeErrCodeUnsupportedLiteralLength: + return nil, errUnsupportedLiteralLength + } + return nil, ErrCorrupt +} + +// NewReader returns a new Reader that decompresses from r, using the framing +// format described at +// https://github.com/google/snappy/blob/master/framing_format.txt +func NewReader(r io.Reader) *Reader { + return &Reader{ + r: r, + decoded: make([]byte, maxBlockSize), + buf: make([]byte, maxEncodedLenOfMaxBlockSize+checksumSize), + } +} + +// Reader is an io.Reader that can read Snappy-compressed bytes. +// +// Reader handles the Snappy stream format, not the Snappy block format. +type Reader struct { + r io.Reader + err error + decoded []byte + buf []byte + // decoded[i:j] contains decoded bytes that have not yet been passed on. + i, j int + readHeader bool +} + +// Reset discards any buffered data, resets all state, and switches the Snappy +// reader to read from r. This permits reusing a Reader rather than allocating +// a new one. +func (r *Reader) Reset(reader io.Reader) { + r.r = reader + r.err = nil + r.i = 0 + r.j = 0 + r.readHeader = false +} + +func (r *Reader) readFull(p []byte, allowEOF bool) (ok bool) { + if _, r.err = io.ReadFull(r.r, p); r.err != nil { + if r.err == io.ErrUnexpectedEOF || (r.err == io.EOF && !allowEOF) { + r.err = ErrCorrupt + } + return false + } + return true +} + +func (r *Reader) fill() error { + for r.i >= r.j { + if !r.readFull(r.buf[:4], true) { + return r.err + } + chunkType := r.buf[0] + if !r.readHeader { + if chunkType != chunkTypeStreamIdentifier { + r.err = ErrCorrupt + return r.err + } + r.readHeader = true + } + chunkLen := int(r.buf[1]) | int(r.buf[2])<<8 | int(r.buf[3])<<16 + if chunkLen > len(r.buf) { + r.err = ErrUnsupported + return r.err + } + + // The chunk types are specified at + // https://github.com/google/snappy/blob/master/framing_format.txt + switch chunkType { + case chunkTypeCompressedData: + // Section 4.2. Compressed data (chunk type 0x00). + if chunkLen < checksumSize { + r.err = ErrCorrupt + return r.err + } + buf := r.buf[:chunkLen] + if !r.readFull(buf, false) { + return r.err + } + checksum := uint32(buf[0]) | uint32(buf[1])<<8 | uint32(buf[2])<<16 | uint32(buf[3])<<24 + buf = buf[checksumSize:] + + n, err := DecodedLen(buf) + if err != nil { + r.err = err + return r.err + } + if n > len(r.decoded) { + r.err = ErrCorrupt + return r.err + } + if _, err := Decode(r.decoded, buf); err != nil { + r.err = err + return r.err + } + if crc(r.decoded[:n]) != checksum { + r.err = ErrCorrupt + return r.err + } + r.i, r.j = 0, n + continue + + case chunkTypeUncompressedData: + // Section 4.3. Uncompressed data (chunk type 0x01). + if chunkLen < checksumSize { + r.err = ErrCorrupt + return r.err + } + buf := r.buf[:checksumSize] + if !r.readFull(buf, false) { + return r.err + } + checksum := uint32(buf[0]) | uint32(buf[1])<<8 | uint32(buf[2])<<16 | uint32(buf[3])<<24 + // Read directly into r.decoded instead of via r.buf. + n := chunkLen - checksumSize + if n > len(r.decoded) { + r.err = ErrCorrupt + return r.err + } + if !r.readFull(r.decoded[:n], false) { + return r.err + } + if crc(r.decoded[:n]) != checksum { + r.err = ErrCorrupt + return r.err + } + r.i, r.j = 0, n + continue + + case chunkTypeStreamIdentifier: + // Section 4.1. Stream identifier (chunk type 0xff). + if chunkLen != len(magicBody) { + r.err = ErrCorrupt + return r.err + } + if !r.readFull(r.buf[:len(magicBody)], false) { + return r.err + } + for i := range len(magicBody) { + if r.buf[i] != magicBody[i] { + r.err = ErrCorrupt + return r.err + } + } + continue + } + + if chunkType <= 0x7f { + // Section 4.5. Reserved unskippable chunks (chunk types 0x02-0x7f). + r.err = ErrUnsupported + return r.err + } + // Section 4.4 Padding (chunk type 0xfe). + // Section 4.6. Reserved skippable chunks (chunk types 0x80-0xfd). + if !r.readFull(r.buf[:chunkLen], false) { + return r.err + } + } + + return nil +} + +// Read satisfies the io.Reader interface. +func (r *Reader) Read(p []byte) (int, error) { + if r.err != nil { + return 0, r.err + } + + if err := r.fill(); err != nil { + return 0, err + } + + n := copy(p, r.decoded[r.i:r.j]) + r.i += n + return n, nil +} + +// ReadByte satisfies the io.ByteReader interface. +func (r *Reader) ReadByte() (byte, error) { + if r.err != nil { + return 0, r.err + } + + if err := r.fill(); err != nil { + return 0, err + } + + c := r.decoded[r.i] + r.i++ + return c, nil +} diff --git a/vendor/github.com/klauspost/compress/internal/snapref/decode_other.go b/vendor/github.com/klauspost/compress/internal/snapref/decode_other.go new file mode 100644 index 000000000..77395a6b8 --- /dev/null +++ b/vendor/github.com/klauspost/compress/internal/snapref/decode_other.go @@ -0,0 +1,113 @@ +// Copyright 2016 The Snappy-Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package snapref + +// decode writes the decoding of src to dst. It assumes that the varint-encoded +// length of the decompressed bytes has already been read, and that len(dst) +// equals that length. +// +// It returns 0 on success or a decodeErrCodeXxx error code on failure. +func decode(dst, src []byte) int { + var d, s, offset, length int + for s < len(src) { + switch src[s] & 0x03 { + case tagLiteral: + x := uint32(src[s] >> 2) + switch { + case x < 60: + s++ + case x == 60: + s += 2 + if uint(s) > uint(len(src)) { // The uint conversions catch overflow from the previous line. + return decodeErrCodeCorrupt + } + x = uint32(src[s-1]) + case x == 61: + s += 3 + if uint(s) > uint(len(src)) { // The uint conversions catch overflow from the previous line. + return decodeErrCodeCorrupt + } + x = uint32(src[s-2]) | uint32(src[s-1])<<8 + case x == 62: + s += 4 + if uint(s) > uint(len(src)) { // The uint conversions catch overflow from the previous line. + return decodeErrCodeCorrupt + } + x = uint32(src[s-3]) | uint32(src[s-2])<<8 | uint32(src[s-1])<<16 + case x == 63: + s += 5 + if uint(s) > uint(len(src)) { // The uint conversions catch overflow from the previous line. + return decodeErrCodeCorrupt + } + x = uint32(src[s-4]) | uint32(src[s-3])<<8 | uint32(src[s-2])<<16 | uint32(src[s-1])<<24 + } + length = int(x) + 1 + if length <= 0 { + return decodeErrCodeUnsupportedLiteralLength + } + if length > len(dst)-d || length > len(src)-s { + return decodeErrCodeCorrupt + } + copy(dst[d:], src[s:s+length]) + d += length + s += length + continue + + case tagCopy1: + s += 2 + if uint(s) > uint(len(src)) { // The uint conversions catch overflow from the previous line. + return decodeErrCodeCorrupt + } + length = 4 + int(src[s-2])>>2&0x7 + offset = int(uint32(src[s-2])&0xe0<<3 | uint32(src[s-1])) + + case tagCopy2: + s += 3 + if uint(s) > uint(len(src)) { // The uint conversions catch overflow from the previous line. + return decodeErrCodeCorrupt + } + length = 1 + int(src[s-3])>>2 + offset = int(uint32(src[s-2]) | uint32(src[s-1])<<8) + + case tagCopy4: + s += 5 + if uint(s) > uint(len(src)) { // The uint conversions catch overflow from the previous line. + return decodeErrCodeCorrupt + } + length = 1 + int(src[s-5])>>2 + offset = int(uint32(src[s-4]) | uint32(src[s-3])<<8 | uint32(src[s-2])<<16 | uint32(src[s-1])<<24) + } + + if offset <= 0 || d < offset || length > len(dst)-d { + return decodeErrCodeCorrupt + } + // Copy from an earlier sub-slice of dst to a later sub-slice. + // If no overlap, use the built-in copy: + if offset >= length { + copy(dst[d:d+length], dst[d-offset:]) + d += length + continue + } + + // Unlike the built-in copy function, this byte-by-byte copy always runs + // forwards, even if the slices overlap. Conceptually, this is: + // + // d += forwardCopy(dst[d:d+length], dst[d-offset:]) + // + // We align the slices into a and b and show the compiler they are the same size. + // This allows the loop to run without bounds checks. + a := dst[d : d+length] + b := dst[d-offset:] + b = b[:len(a)] + for i := range a { + a[i] = b[i] + } + d += length + } + if d != len(dst) { + return decodeErrCodeCorrupt + } + return 0 +} diff --git a/vendor/github.com/klauspost/compress/internal/snapref/encode.go b/vendor/github.com/klauspost/compress/internal/snapref/encode.go new file mode 100644 index 000000000..860a99416 --- /dev/null +++ b/vendor/github.com/klauspost/compress/internal/snapref/encode.go @@ -0,0 +1,291 @@ +// Copyright 2011 The Snappy-Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package snapref + +import ( + "encoding/binary" + "errors" + "io" +) + +// Encode returns the encoded form of src. The returned slice may be a sub- +// slice of dst if dst was large enough to hold the entire encoded block. +// Otherwise, a newly allocated slice will be returned. +// +// The dst and src must not overlap. It is valid to pass a nil dst. +// +// Encode handles the Snappy block format, not the Snappy stream format. +func Encode(dst, src []byte) []byte { + if n := MaxEncodedLen(len(src)); n < 0 { + panic(ErrTooLarge) + } else if cap(dst) < n { + dst = make([]byte, n) + } else { + dst = dst[:n] + } + + // The block starts with the varint-encoded length of the decompressed bytes. + d := binary.PutUvarint(dst, uint64(len(src))) + + for len(src) > 0 { + p := src + src = nil + if len(p) > maxBlockSize { + p, src = p[:maxBlockSize], p[maxBlockSize:] + } + if len(p) < minNonLiteralBlockSize { + d += emitLiteral(dst[d:], p) + } else { + d += encodeBlock(dst[d:], p) + } + } + return dst[:d] +} + +// inputMargin is the minimum number of extra input bytes to keep, inside +// encodeBlock's inner loop. On some architectures, this margin lets us +// implement a fast path for emitLiteral, where the copy of short (<= 16 byte) +// literals can be implemented as a single load to and store from a 16-byte +// register. That literal's actual length can be as short as 1 byte, so this +// can copy up to 15 bytes too much, but that's OK as subsequent iterations of +// the encoding loop will fix up the copy overrun, and this inputMargin ensures +// that we don't overrun the dst and src buffers. +const inputMargin = 16 - 1 + +// minNonLiteralBlockSize is the minimum size of the input to encodeBlock that +// could be encoded with a copy tag. This is the minimum with respect to the +// algorithm used by encodeBlock, not a minimum enforced by the file format. +// +// The encoded output must start with at least a 1 byte literal, as there are +// no previous bytes to copy. A minimal (1 byte) copy after that, generated +// from an emitCopy call in encodeBlock's main loop, would require at least +// another inputMargin bytes, for the reason above: we want any emitLiteral +// calls inside encodeBlock's main loop to use the fast path if possible, which +// requires being able to overrun by inputMargin bytes. Thus, +// minNonLiteralBlockSize equals 1 + 1 + inputMargin. +// +// The C++ code doesn't use this exact threshold, but it could, as discussed at +// https://groups.google.com/d/topic/snappy-compression/oGbhsdIJSJ8/discussion +// The difference between Go (2+inputMargin) and C++ (inputMargin) is purely an +// optimization. It should not affect the encoded form. This is tested by +// TestSameEncodingAsCppShortCopies. +const minNonLiteralBlockSize = 1 + 1 + inputMargin + +// MaxEncodedLen returns the maximum length of a snappy block, given its +// uncompressed length. +// +// It will return a negative value if srcLen is too large to encode. +func MaxEncodedLen(srcLen int) int { + n := uint64(srcLen) + if n > 0xffffffff { + return -1 + } + // Compressed data can be defined as: + // compressed := item* literal* + // item := literal* copy + // + // The trailing literal sequence has a space blowup of at most 62/60 + // since a literal of length 60 needs one tag byte + one extra byte + // for length information. + // + // Item blowup is trickier to measure. Suppose the "copy" op copies + // 4 bytes of data. Because of a special check in the encoding code, + // we produce a 4-byte copy only if the offset is < 65536. Therefore + // the copy op takes 3 bytes to encode, and this type of item leads + // to at most the 62/60 blowup for representing literals. + // + // Suppose the "copy" op copies 5 bytes of data. If the offset is big + // enough, it will take 5 bytes to encode the copy op. Therefore the + // worst case here is a one-byte literal followed by a five-byte copy. + // That is, 6 bytes of input turn into 7 bytes of "compressed" data. + // + // This last factor dominates the blowup, so the final estimate is: + n = 32 + n + n/6 + if n > 0xffffffff { + return -1 + } + return int(n) +} + +var errClosed = errors.New("snappy: Writer is closed") + +// NewWriter returns a new Writer that compresses to w. +// +// The Writer returned does not buffer writes. There is no need to Flush or +// Close such a Writer. +// +// Deprecated: the Writer returned is not suitable for many small writes, only +// for few large writes. Use NewBufferedWriter instead, which is efficient +// regardless of the frequency and shape of the writes, and remember to Close +// that Writer when done. +func NewWriter(w io.Writer) *Writer { + return &Writer{ + w: w, + obuf: make([]byte, obufLen), + } +} + +// NewBufferedWriter returns a new Writer that compresses to w, using the +// framing format described at +// https://github.com/google/snappy/blob/master/framing_format.txt +// +// The Writer returned buffers writes. Users must call Close to guarantee all +// data has been forwarded to the underlying io.Writer. They may also call +// Flush zero or more times before calling Close. +func NewBufferedWriter(w io.Writer) *Writer { + return &Writer{ + w: w, + ibuf: make([]byte, 0, maxBlockSize), + obuf: make([]byte, obufLen), + } +} + +// Writer is an io.Writer that can write Snappy-compressed bytes. +// +// Writer handles the Snappy stream format, not the Snappy block format. +type Writer struct { + w io.Writer + err error + + // ibuf is a buffer for the incoming (uncompressed) bytes. + // + // Its use is optional. For backwards compatibility, Writers created by the + // NewWriter function have ibuf == nil, do not buffer incoming bytes, and + // therefore do not need to be Flush'ed or Close'd. + ibuf []byte + + // obuf is a buffer for the outgoing (compressed) bytes. + obuf []byte + + // wroteStreamHeader is whether we have written the stream header. + wroteStreamHeader bool +} + +// Reset discards the writer's state and switches the Snappy writer to write to +// w. This permits reusing a Writer rather than allocating a new one. +func (w *Writer) Reset(writer io.Writer) { + w.w = writer + w.err = nil + if w.ibuf != nil { + w.ibuf = w.ibuf[:0] + } + w.wroteStreamHeader = false +} + +// Write satisfies the io.Writer interface. +func (w *Writer) Write(p []byte) (nRet int, errRet error) { + if w.ibuf == nil { + // Do not buffer incoming bytes. This does not perform or compress well + // if the caller of Writer.Write writes many small slices. This + // behavior is therefore deprecated, but still supported for backwards + // compatibility with code that doesn't explicitly Flush or Close. + return w.write(p) + } + + // The remainder of this method is based on bufio.Writer.Write from the + // standard library. + + for len(p) > (cap(w.ibuf)-len(w.ibuf)) && w.err == nil { + var n int + if len(w.ibuf) == 0 { + // Large write, empty buffer. + // Write directly from p to avoid copy. + n, _ = w.write(p) + } else { + n = copy(w.ibuf[len(w.ibuf):cap(w.ibuf)], p) + w.ibuf = w.ibuf[:len(w.ibuf)+n] + w.Flush() + } + nRet += n + p = p[n:] + } + if w.err != nil { + return nRet, w.err + } + n := copy(w.ibuf[len(w.ibuf):cap(w.ibuf)], p) + w.ibuf = w.ibuf[:len(w.ibuf)+n] + nRet += n + return nRet, nil +} + +func (w *Writer) write(p []byte) (nRet int, errRet error) { + if w.err != nil { + return 0, w.err + } + for len(p) > 0 { + obufStart := len(magicChunk) + if !w.wroteStreamHeader { + w.wroteStreamHeader = true + copy(w.obuf, magicChunk) + obufStart = 0 + } + + var uncompressed []byte + if len(p) > maxBlockSize { + uncompressed, p = p[:maxBlockSize], p[maxBlockSize:] + } else { + uncompressed, p = p, nil + } + checksum := crc(uncompressed) + + // Compress the buffer, discarding the result if the improvement + // isn't at least 12.5%. + compressed := Encode(w.obuf[obufHeaderLen:], uncompressed) + chunkType := uint8(chunkTypeCompressedData) + chunkLen := 4 + len(compressed) + obufEnd := obufHeaderLen + len(compressed) + if len(compressed) >= len(uncompressed)-len(uncompressed)/8 { + chunkType = chunkTypeUncompressedData + chunkLen = 4 + len(uncompressed) + obufEnd = obufHeaderLen + } + + // Fill in the per-chunk header that comes before the body. + w.obuf[len(magicChunk)+0] = chunkType + w.obuf[len(magicChunk)+1] = uint8(chunkLen >> 0) + w.obuf[len(magicChunk)+2] = uint8(chunkLen >> 8) + w.obuf[len(magicChunk)+3] = uint8(chunkLen >> 16) + w.obuf[len(magicChunk)+4] = uint8(checksum >> 0) + w.obuf[len(magicChunk)+5] = uint8(checksum >> 8) + w.obuf[len(magicChunk)+6] = uint8(checksum >> 16) + w.obuf[len(magicChunk)+7] = uint8(checksum >> 24) + + if _, err := w.w.Write(w.obuf[obufStart:obufEnd]); err != nil { + w.err = err + return nRet, err + } + if chunkType == chunkTypeUncompressedData { + if _, err := w.w.Write(uncompressed); err != nil { + w.err = err + return nRet, err + } + } + nRet += len(uncompressed) + } + return nRet, nil +} + +// Flush flushes the Writer to its underlying io.Writer. +func (w *Writer) Flush() error { + if w.err != nil { + return w.err + } + if len(w.ibuf) == 0 { + return nil + } + w.write(w.ibuf) + w.ibuf = w.ibuf[:0] + return w.err +} + +// Close calls Flush and then closes the Writer. +func (w *Writer) Close() error { + w.Flush() + ret := w.err + if w.err == nil { + w.err = errClosed + } + return ret +} diff --git a/vendor/github.com/klauspost/compress/internal/snapref/encode_other.go b/vendor/github.com/klauspost/compress/internal/snapref/encode_other.go new file mode 100644 index 000000000..2754bac6f --- /dev/null +++ b/vendor/github.com/klauspost/compress/internal/snapref/encode_other.go @@ -0,0 +1,250 @@ +// Copyright 2016 The Snappy-Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package snapref + +func load32(b []byte, i int) uint32 { + b = b[i : i+4 : len(b)] // Help the compiler eliminate bounds checks on the next line. + return uint32(b[0]) | uint32(b[1])<<8 | uint32(b[2])<<16 | uint32(b[3])<<24 +} + +func load64(b []byte, i int) uint64 { + b = b[i : i+8 : len(b)] // Help the compiler eliminate bounds checks on the next line. + return uint64(b[0]) | uint64(b[1])<<8 | uint64(b[2])<<16 | uint64(b[3])<<24 | + uint64(b[4])<<32 | uint64(b[5])<<40 | uint64(b[6])<<48 | uint64(b[7])<<56 +} + +// emitLiteral writes a literal chunk and returns the number of bytes written. +// +// It assumes that: +// +// dst is long enough to hold the encoded bytes +// 1 <= len(lit) && len(lit) <= 65536 +func emitLiteral(dst, lit []byte) int { + i, n := 0, uint(len(lit)-1) + switch { + case n < 60: + dst[0] = uint8(n)<<2 | tagLiteral + i = 1 + case n < 1<<8: + dst[0] = 60<<2 | tagLiteral + dst[1] = uint8(n) + i = 2 + default: + dst[0] = 61<<2 | tagLiteral + dst[1] = uint8(n) + dst[2] = uint8(n >> 8) + i = 3 + } + return i + copy(dst[i:], lit) +} + +// emitCopy writes a copy chunk and returns the number of bytes written. +// +// It assumes that: +// +// dst is long enough to hold the encoded bytes +// 1 <= offset && offset <= 65535 +// 4 <= length && length <= 65535 +func emitCopy(dst []byte, offset, length int) int { + i := 0 + // The maximum length for a single tagCopy1 or tagCopy2 op is 64 bytes. The + // threshold for this loop is a little higher (at 68 = 64 + 4), and the + // length emitted down below is a little lower (at 60 = 64 - 4), because + // it's shorter to encode a length 67 copy as a length 60 tagCopy2 followed + // by a length 7 tagCopy1 (which encodes as 3+2 bytes) than to encode it as + // a length 64 tagCopy2 followed by a length 3 tagCopy2 (which encodes as + // 3+3 bytes). The magic 4 in the 64±4 is because the minimum length for a + // tagCopy1 op is 4 bytes, which is why a length 3 copy has to be an + // encodes-as-3-bytes tagCopy2 instead of an encodes-as-2-bytes tagCopy1. + for length >= 68 { + // Emit a length 64 copy, encoded as 3 bytes. + dst[i+0] = 63<<2 | tagCopy2 + dst[i+1] = uint8(offset) + dst[i+2] = uint8(offset >> 8) + i += 3 + length -= 64 + } + if length > 64 { + // Emit a length 60 copy, encoded as 3 bytes. + dst[i+0] = 59<<2 | tagCopy2 + dst[i+1] = uint8(offset) + dst[i+2] = uint8(offset >> 8) + i += 3 + length -= 60 + } + if length >= 12 || offset >= 2048 { + // Emit the remaining copy, encoded as 3 bytes. + dst[i+0] = uint8(length-1)<<2 | tagCopy2 + dst[i+1] = uint8(offset) + dst[i+2] = uint8(offset >> 8) + return i + 3 + } + // Emit the remaining copy, encoded as 2 bytes. + dst[i+0] = uint8(offset>>8)<<5 | uint8(length-4)<<2 | tagCopy1 + dst[i+1] = uint8(offset) + return i + 2 +} + +func hash(u, shift uint32) uint32 { + return (u * 0x1e35a7bd) >> shift +} + +// EncodeBlockInto exposes encodeBlock but checks dst size. +func EncodeBlockInto(dst, src []byte) (d int) { + if MaxEncodedLen(len(src)) > len(dst) { + return 0 + } + + // encodeBlock breaks on too big blocks, so split. + for len(src) > 0 { + p := src + src = nil + if len(p) > maxBlockSize { + p, src = p[:maxBlockSize], p[maxBlockSize:] + } + if len(p) < minNonLiteralBlockSize { + d += emitLiteral(dst[d:], p) + } else { + d += encodeBlock(dst[d:], p) + } + } + return d +} + +// encodeBlock encodes a non-empty src to a guaranteed-large-enough dst. It +// assumes that the varint-encoded length of the decompressed bytes has already +// been written. +// +// It also assumes that: +// +// len(dst) >= MaxEncodedLen(len(src)) && +// minNonLiteralBlockSize <= len(src) && len(src) <= maxBlockSize +func encodeBlock(dst, src []byte) (d int) { + // Initialize the hash table. Its size ranges from 1<<8 to 1<<14 inclusive. + // The table element type is uint16, as s < sLimit and sLimit < len(src) + // and len(src) <= maxBlockSize and maxBlockSize == 65536. + const ( + maxTableSize = 1 << 14 + // tableMask is redundant, but helps the compiler eliminate bounds + // checks. + tableMask = maxTableSize - 1 + ) + shift := uint32(32 - 8) + for tableSize := 1 << 8; tableSize < maxTableSize && tableSize < len(src); tableSize *= 2 { + shift-- + } + // In Go, all array elements are zero-initialized, so there is no advantage + // to a smaller tableSize per se. However, it matches the C++ algorithm, + // and in the asm versions of this code, we can get away with zeroing only + // the first tableSize elements. + var table [maxTableSize]uint16 + + // sLimit is when to stop looking for offset/length copies. The inputMargin + // lets us use a fast path for emitLiteral in the main loop, while we are + // looking for copies. + sLimit := len(src) - inputMargin + + // nextEmit is where in src the next emitLiteral should start from. + nextEmit := 0 + + // The encoded form must start with a literal, as there are no previous + // bytes to copy, so we start looking for hash matches at s == 1. + s := 1 + nextHash := hash(load32(src, s), shift) + + for { + // Copied from the C++ snappy implementation: + // + // Heuristic match skipping: If 32 bytes are scanned with no matches + // found, start looking only at every other byte. If 32 more bytes are + // scanned (or skipped), look at every third byte, etc.. When a match + // is found, immediately go back to looking at every byte. This is a + // small loss (~5% performance, ~0.1% density) for compressible data + // due to more bookkeeping, but for non-compressible data (such as + // JPEG) it's a huge win since the compressor quickly "realizes" the + // data is incompressible and doesn't bother looking for matches + // everywhere. + // + // The "skip" variable keeps track of how many bytes there are since + // the last match; dividing it by 32 (ie. right-shifting by five) gives + // the number of bytes to move ahead for each iteration. + skip := 32 + + nextS := s + candidate := 0 + for { + s = nextS + bytesBetweenHashLookups := skip >> 5 + nextS = s + bytesBetweenHashLookups + skip += bytesBetweenHashLookups + if nextS > sLimit { + goto emitRemainder + } + candidate = int(table[nextHash&tableMask]) + table[nextHash&tableMask] = uint16(s) + nextHash = hash(load32(src, nextS), shift) + if load32(src, s) == load32(src, candidate) { + break + } + } + + // A 4-byte match has been found. We'll later see if more than 4 bytes + // match. But, prior to the match, src[nextEmit:s] are unmatched. Emit + // them as literal bytes. + d += emitLiteral(dst[d:], src[nextEmit:s]) + + // Call emitCopy, and then see if another emitCopy could be our next + // move. Repeat until we find no match for the input immediately after + // what was consumed by the last emitCopy call. + // + // If we exit this loop normally then we need to call emitLiteral next, + // though we don't yet know how big the literal will be. We handle that + // by proceeding to the next iteration of the main loop. We also can + // exit this loop via goto if we get close to exhausting the input. + for { + // Invariant: we have a 4-byte match at s, and no need to emit any + // literal bytes prior to s. + base := s + + // Extend the 4-byte match as long as possible. + // + // This is an inlined version of: + // s = extendMatch(src, candidate+4, s+4) + s += 4 + for i := candidate + 4; s < len(src) && src[i] == src[s]; i, s = i+1, s+1 { + } + + d += emitCopy(dst[d:], base-candidate, s-base) + nextEmit = s + if s >= sLimit { + goto emitRemainder + } + + // We could immediately start working at s now, but to improve + // compression we first update the hash table at s-1 and at s. If + // another emitCopy is not our next move, also calculate nextHash + // at s+1. At least on GOARCH=amd64, these three hash calculations + // are faster as one load64 call (with some shifts) instead of + // three load32 calls. + x := load64(src, s-1) + prevHash := hash(uint32(x>>0), shift) + table[prevHash&tableMask] = uint16(s - 1) + currHash := hash(uint32(x>>8), shift) + candidate = int(table[currHash&tableMask]) + table[currHash&tableMask] = uint16(s) + if uint32(x>>8) != load32(src, candidate) { + nextHash = hash(uint32(x>>16), shift) + s++ + break + } + } + } + +emitRemainder: + if nextEmit < len(src) { + d += emitLiteral(dst[d:], src[nextEmit:]) + } + return d +} diff --git a/vendor/github.com/klauspost/compress/internal/snapref/snappy.go b/vendor/github.com/klauspost/compress/internal/snapref/snappy.go new file mode 100644 index 000000000..34d01f4aa --- /dev/null +++ b/vendor/github.com/klauspost/compress/internal/snapref/snappy.go @@ -0,0 +1,98 @@ +// Copyright 2011 The Snappy-Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package snapref implements the Snappy compression format. It aims for very +// high speeds and reasonable compression. +// +// There are actually two Snappy formats: block and stream. They are related, +// but different: trying to decompress block-compressed data as a Snappy stream +// will fail, and vice versa. The block format is the Decode and Encode +// functions and the stream format is the Reader and Writer types. +// +// The block format, the more common case, is used when the complete size (the +// number of bytes) of the original data is known upfront, at the time +// compression starts. The stream format, also known as the framing format, is +// for when that isn't always true. +// +// The canonical, C++ implementation is at https://github.com/google/snappy and +// it only implements the block format. +package snapref + +import ( + "hash/crc32" +) + +/* +Each encoded block begins with the varint-encoded length of the decoded data, +followed by a sequence of chunks. Chunks begin and end on byte boundaries. The +first byte of each chunk is broken into its 2 least and 6 most significant bits +called l and m: l ranges in [0, 4) and m ranges in [0, 64). l is the chunk tag. +Zero means a literal tag. All other values mean a copy tag. + +For literal tags: + - If m < 60, the next 1 + m bytes are literal bytes. + - Otherwise, let n be the little-endian unsigned integer denoted by the next + m - 59 bytes. The next 1 + n bytes after that are literal bytes. + +For copy tags, length bytes are copied from offset bytes ago, in the style of +Lempel-Ziv compression algorithms. In particular: + - For l == 1, the offset ranges in [0, 1<<11) and the length in [4, 12). + The length is 4 + the low 3 bits of m. The high 3 bits of m form bits 8-10 + of the offset. The next byte is bits 0-7 of the offset. + - For l == 2, the offset ranges in [0, 1<<16) and the length in [1, 65). + The length is 1 + m. The offset is the little-endian unsigned integer + denoted by the next 2 bytes. + - For l == 3, this tag is a legacy format that is no longer issued by most + encoders. Nonetheless, the offset ranges in [0, 1<<32) and the length in + [1, 65). The length is 1 + m. The offset is the little-endian unsigned + integer denoted by the next 4 bytes. +*/ +const ( + tagLiteral = 0x00 + tagCopy1 = 0x01 + tagCopy2 = 0x02 + tagCopy4 = 0x03 +) + +const ( + checksumSize = 4 + chunkHeaderSize = 4 + magicChunk = "\xff\x06\x00\x00" + magicBody + magicBody = "sNaPpY" + + // maxBlockSize is the maximum size of the input to encodeBlock. It is not + // part of the wire format per se, but some parts of the encoder assume + // that an offset fits into a uint16. + // + // Also, for the framing format (Writer type instead of Encode function), + // https://github.com/google/snappy/blob/master/framing_format.txt says + // that "the uncompressed data in a chunk must be no longer than 65536 + // bytes". + maxBlockSize = 65536 + + // maxEncodedLenOfMaxBlockSize equals MaxEncodedLen(maxBlockSize), but is + // hard coded to be a const instead of a variable, so that obufLen can also + // be a const. Their equivalence is confirmed by + // TestMaxEncodedLenOfMaxBlockSize. + maxEncodedLenOfMaxBlockSize = 76490 + + obufHeaderLen = len(magicChunk) + checksumSize + chunkHeaderSize + obufLen = obufHeaderLen + maxEncodedLenOfMaxBlockSize +) + +const ( + chunkTypeCompressedData = 0x00 + chunkTypeUncompressedData = 0x01 + chunkTypePadding = 0xfe + chunkTypeStreamIdentifier = 0xff +) + +var crcTable = crc32.MakeTable(crc32.Castagnoli) + +// crc implements the checksum specified in section 3 of +// https://github.com/google/snappy/blob/master/framing_format.txt +func crc(b []byte) uint32 { + c := crc32.Update(0, crcTable, b) + return uint32(c>>15|c<<17) + 0xa282ead8 +} diff --git a/vendor/github.com/klauspost/compress/s2/.gitignore b/vendor/github.com/klauspost/compress/s2/.gitignore new file mode 100644 index 000000000..3a89c6e3e --- /dev/null +++ b/vendor/github.com/klauspost/compress/s2/.gitignore @@ -0,0 +1,15 @@ +testdata/bench + +# These explicitly listed benchmark data files are for an obsolete version of +# snappy_test.go. +testdata/alice29.txt +testdata/asyoulik.txt +testdata/fireworks.jpeg +testdata/geo.protodata +testdata/html +testdata/html_x_4 +testdata/kppkn.gtb +testdata/lcet10.txt +testdata/paper-100k.pdf +testdata/plrabn12.txt +testdata/urls.10K diff --git a/vendor/github.com/klauspost/compress/s2/LICENSE b/vendor/github.com/klauspost/compress/s2/LICENSE new file mode 100644 index 000000000..1d2d645bd --- /dev/null +++ b/vendor/github.com/klauspost/compress/s2/LICENSE @@ -0,0 +1,28 @@ +Copyright (c) 2011 The Snappy-Go Authors. All rights reserved. +Copyright (c) 2019 Klaus Post. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/vendor/github.com/klauspost/compress/s2/README.md b/vendor/github.com/klauspost/compress/s2/README.md new file mode 100644 index 000000000..b0bf59fbb --- /dev/null +++ b/vendor/github.com/klauspost/compress/s2/README.md @@ -0,0 +1,1134 @@ +# MinLZ + +I have taken the experiences from this library and created a backwards compatible compression package called MinLZ. + +That package will seamlessly decode S2 content, making the transition from this package fairly trivial. + +There are many improvements to pretty much all aspects of S2 since we have "broken free" of the Snappy format specification. +You can read a writeup on [Design and Improvements over S2](https://gist.github.com/klauspost/a25b66198cdbdf7b5b224f670c894ed5). + +The only aspect not covered is custom dictionary encoding. While I do intend to fix errors in this package, +I do not expect to make significant improvements, since I consider MinLZ a better basis for going forward. + +See https://github.com/minio/minlz for all details. + +# S2 Compression + +S2 is an extension of [Snappy](https://github.com/google/snappy). + +S2 is aimed for high throughput, which is why it features concurrent compression for bigger payloads. + +Decoding is compatible with Snappy compressed content, but content compressed with S2 cannot be decompressed by Snappy. +This means that S2 can seamlessly replace Snappy without converting compressed content. + +S2 can produce Snappy compatible output, faster and better than Snappy. +If you want full benefit of the changes you should use s2 without Snappy compatibility. + +S2 is designed to have high throughput on content that cannot be compressed. +This is important, so you don't have to worry about spending CPU cycles on already compressed data. + +## Benefits over Snappy + +* Better compression +* Adjustable compression (3 levels) +* Concurrent stream compression +* Faster decompression, even for Snappy compatible content +* Concurrent Snappy/S2 stream decompression +* Skip forward in compressed stream +* Random seeking with indexes +* Compatible with reading Snappy compressed content +* Smaller block size overhead on incompressible blocks +* Block concatenation +* Block Dictionary support +* Uncompressed stream mode +* Automatic stream size padding +* Snappy compatible block compression + +## Drawbacks over Snappy + +* Not optimized for 32 bit systems +* Streams use slightly more memory due to larger blocks and concurrency (configurable) + +# Usage + +Installation: `go get -u github.com/klauspost/compress/s2` + +Full package documentation: + +[![godoc][1]][2] + +[1]: https://godoc.org/github.com/klauspost/compress?status.svg +[2]: https://godoc.org/github.com/klauspost/compress/s2 + +## Compression + +```Go +func EncodeStream(src io.Reader, dst io.Writer) error { + enc := s2.NewWriter(dst) + _, err := io.Copy(enc, src) + if err != nil { + enc.Close() + return err + } + // Blocks until compression is done. + return enc.Close() +} +``` + +You should always call `enc.Close()`, otherwise you will leak resources and your encode will be incomplete. + +For the best throughput, you should attempt to reuse the `Writer` using the `Reset()` method. + +The Writer in S2 is always buffered, therefore `NewBufferedWriter` in Snappy can be replaced with `NewWriter` in S2. +It is possible to flush any buffered data using the `Flush()` method. +This will block until all data sent to the encoder has been written to the output. + +S2 also supports the `io.ReaderFrom` interface, which will consume all input from a reader. + +As a final method to compress data, if you have a single block of data you would like to have encoded as a stream, +a slightly more efficient method is to use the `EncodeBuffer` method. +This will take ownership of the buffer until the stream is closed. + +```Go +func EncodeStream(src []byte, dst io.Writer) error { + enc := s2.NewWriter(dst) + // The encoder owns the buffer until Flush or Close is called. + err := enc.EncodeBuffer(src) + if err != nil { + enc.Close() + return err + } + // Blocks until compression is done. + return enc.Close() +} +``` + +Each call to `EncodeBuffer` will result in discrete blocks being created without buffering, +so it should only be used a single time per stream. +If you need to write several blocks, you should use the regular io.Writer interface. + + +## Decompression + +```Go +func DecodeStream(src io.Reader, dst io.Writer) error { + dec := s2.NewReader(src) + _, err := io.Copy(dst, dec) + return err +} +``` + +Similar to the Writer, a Reader can be reused using the `Reset` method. + +For the best possible throughput, there is a `EncodeBuffer(buf []byte)` function available. +However, it requires that the provided buffer isn't used after it is handed over to S2 and until the stream is flushed or closed. + +For smaller data blocks, there is also a non-streaming interface: `Encode()`, `EncodeBetter()` and `Decode()`. +Do however note that these functions (similar to Snappy) does not provide validation of data, +so data corruption may be undetected. Stream encoding provides CRC checks of data. + +It is possible to efficiently skip forward in a compressed stream using the `Skip()` method. +For big skips the decompressor is able to skip blocks without decompressing them. + +## Single Blocks + +Similar to Snappy S2 offers single block compression. +Blocks do not offer the same flexibility and safety as streams, +but may be preferable for very small payloads, less than 100K. + +Using a simple `dst := s2.Encode(nil, src)` will compress `src` and return the compressed result. +It is possible to provide a destination buffer. +If the buffer has a capacity of `s2.MaxEncodedLen(len(src))` it will be used. +If not a new will be allocated. + +Alternatively `EncodeBetter`/`EncodeBest` can also be used for better, but slightly slower compression. + +Similarly to decompress a block you can use `dst, err := s2.Decode(nil, src)`. +Again an optional destination buffer can be supplied. +The `s2.DecodedLen(src)` can be used to get the minimum capacity needed. +If that is not satisfied a new buffer will be allocated. + +Block function always operate on a single goroutine since it should only be used for small payloads. + +# Commandline tools + +Some very simply commandline tools are provided; `s2c` for compression and `s2d` for decompression. + +Binaries can be downloaded on the [Releases Page](https://github.com/klauspost/compress/releases). + +Installing then requires Go to be installed. To install them, use: + +`go install github.com/klauspost/compress/s2/cmd/s2c@latest && go install github.com/klauspost/compress/s2/cmd/s2d@latest` + +To build binaries to the current folder use: + +`go build github.com/klauspost/compress/s2/cmd/s2c && go build github.com/klauspost/compress/s2/cmd/s2d` + + +## s2c + +``` +Usage: s2c [options] file1 file2 + +Compresses all files supplied as input separately. +Output files are written as 'filename.ext.s2' or 'filename.ext.snappy'. +By default output files will be overwritten. +Use - as the only file name to read from stdin and write to stdout. + +Wildcards are accepted: testdir/*.txt will compress all files in testdir ending with .txt +Directories can be wildcards as well. testdir/*/*.txt will match testdir/subdir/b.txt + +File names beginning with 'http://' and 'https://' will be downloaded and compressed. +Only http response code 200 is accepted. + +Options: + -bench int + Run benchmark n times. No output will be written + -blocksize string + Max block size. Examples: 64K, 256K, 1M, 4M. Must be power of two and <= 4MB (default "4M") + -c Write all output to stdout. Multiple input files will be concatenated + -cpu int + Compress using this amount of threads (default 32) + -faster + Compress faster, but with a minor compression loss + -help + Display help + -index + Add seek index (default true) + -o string + Write output to another file. Single input file only + -pad string + Pad size to a multiple of this value, Examples: 500, 64K, 256K, 1M, 4M, etc (default "1") + -q Don't write any output to terminal, except errors + -rm + Delete source file(s) after successful compression + -safe + Do not overwrite output files + -slower + Compress more, but a lot slower + -snappy + Generate Snappy compatible output stream + -verify + Verify written files + +``` + +## s2d + +``` +Usage: s2d [options] file1 file2 + +Decompresses all files supplied as input. Input files must end with '.s2' or '.snappy'. +Output file names have the extension removed. By default output files will be overwritten. +Use - as the only file name to read from stdin and write to stdout. + +Wildcards are accepted: testdir/*.txt will compress all files in testdir ending with .txt +Directories can be wildcards as well. testdir/*/*.txt will match testdir/subdir/b.txt + +File names beginning with 'http://' and 'https://' will be downloaded and decompressed. +Extensions on downloaded files are ignored. Only http response code 200 is accepted. + +Options: + -bench int + Run benchmark n times. No output will be written + -c Write all output to stdout. Multiple input files will be concatenated + -help + Display help + -o string + Write output to another file. Single input file only + -offset string + Start at offset. Examples: 92, 64K, 256K, 1M, 4M. Requires Index + -q Don't write any output to terminal, except errors + -rm + Delete source file(s) after successful decompression + -safe + Do not overwrite output files + -tail string + Return last of compressed file. Examples: 92, 64K, 256K, 1M, 4M. Requires Index + -verify + Verify files, but do not write output +``` + +## s2sx: self-extracting archives + +s2sx allows creating self-extracting archives with no dependencies. + +By default, executables are created for the same platforms as the host os, +but this can be overridden with `-os` and `-arch` parameters. + +Extracted files have 0666 permissions, except when untar option used. + +``` +Usage: s2sx [options] file1 file2 + +Compresses all files supplied as input separately. +If files have '.s2' extension they are assumed to be compressed already. +Output files are written as 'filename.s2sx' and with '.exe' for windows targets. +If output is big, an additional file with ".more" is written. This must be included as well. +By default output files will be overwritten. + +Wildcards are accepted: testdir/*.txt will compress all files in testdir ending with .txt +Directories can be wildcards as well. testdir/*/*.txt will match testdir/subdir/b.txt + +Options: + -arch string + Destination architecture (default "amd64") + -c Write all output to stdout. Multiple input files will be concatenated + -cpu int + Compress using this amount of threads (default 32) + -help + Display help + -max string + Maximum executable size. Rest will be written to another file. (default "1G") + -os string + Destination operating system (default "windows") + -q Don't write any output to terminal, except errors + -rm + Delete source file(s) after successful compression + -safe + Do not overwrite output files + -untar + Untar on destination +``` + +Available platforms are: + + * darwin-amd64 + * darwin-arm64 + * linux-amd64 + * linux-arm + * linux-arm64 + * linux-mips64 + * linux-ppc64le + * windows-386 + * windows-amd64 + +By default, there is a size limit of 1GB for the output executable. + +When this is exceeded the remaining file content is written to a file called +output+`.more`. This file must be included for a successful extraction and +placed alongside the executable for a successful extraction. + +This file *must* have the same name as the executable, so if the executable is renamed, +so must the `.more` file. + +This functionality is disabled with stdin/stdout. + +### Self-extracting TAR files + +If you wrap a TAR file you can specify `-untar` to make it untar on the destination host. + +Files are extracted to the current folder with the path specified in the tar file. + +Note that tar files are not validated before they are wrapped. + +For security reasons files that move below the root folder are not allowed. + +# Performance + +This section will focus on comparisons to Snappy. +This package is solely aimed at replacing Snappy as a high speed compression package. +If you are mainly looking for better compression [zstandard](https://github.com/klauspost/compress/tree/master/zstd#zstd) +gives better compression, but typically at speeds slightly below "better" mode in this package. + +Compression is increased compared to Snappy, mostly around 5-20% and the throughput is typically 25-40% increased (single threaded) compared to the Snappy Go implementation. + +Streams are concurrently compressed. The stream will be distributed among all available CPU cores for the best possible throughput. + +A "better" compression mode is also available. This allows to trade a bit of speed for a minor compression gain. +The content compressed in this mode is fully compatible with the standard decoder. + +Snappy vs S2 **compression** speed on 16 core (32 thread) computer, using all threads and a single thread (1 CPU): + +| File | S2 Speed | S2 Throughput | S2 % smaller | S2 "better" | "better" throughput | "better" % smaller | +|---------------------------------------------------------------------------------------------------------|----------|---------------|--------------|-------------|---------------------|--------------------| +| [rawstudio-mint14.tar](https://files.klauspost.com/compress/rawstudio-mint14.7z) | 16.33x | 10556 MB/s | 8.0% | 6.04x | 5252 MB/s | 14.7% | +| (1 CPU) | 1.08x | 940 MB/s | - | 0.46x | 400 MB/s | - | +| [github-june-2days-2019.json](https://files.klauspost.com/compress/github-june-2days-2019.json.zst) | 16.51x | 15224 MB/s | 31.70% | 9.47x | 8734 MB/s | 37.71% | +| (1 CPU) | 1.26x | 1157 MB/s | - | 0.60x | 556 MB/s | - | +| [github-ranks-backup.bin](https://files.klauspost.com/compress/github-ranks-backup.bin.zst) | 15.14x | 12598 MB/s | -5.76% | 6.23x | 5675 MB/s | 3.62% | +| (1 CPU) | 1.02x | 932 MB/s | - | 0.47x | 432 MB/s | - | +| [consensus.db.10gb](https://files.klauspost.com/compress/consensus.db.10gb.zst) | 11.21x | 12116 MB/s | 15.95% | 3.24x | 3500 MB/s | 18.00% | +| (1 CPU) | 1.05x | 1135 MB/s | - | 0.27x | 292 MB/s | - | +| [apache.log](https://files.klauspost.com/compress/apache.log.zst) | 8.55x | 16673 MB/s | 20.54% | 5.85x | 11420 MB/s | 24.97% | +| (1 CPU) | 1.91x | 1771 MB/s | - | 0.53x | 1041 MB/s | - | +| [gob-stream](https://files.klauspost.com/compress/gob-stream.7z) | 15.76x | 14357 MB/s | 24.01% | 8.67x | 7891 MB/s | 33.68% | +| (1 CPU) | 1.17x | 1064 MB/s | - | 0.65x | 595 MB/s | - | +| [10gb.tar](http://mattmahoney.net/dc/10gb.html) | 13.33x | 9835 MB/s | 2.34% | 6.85x | 4863 MB/s | 9.96% | +| (1 CPU) | 0.97x | 689 MB/s | - | 0.55x | 387 MB/s | - | +| sharnd.out.2gb | 9.11x | 13213 MB/s | 0.01% | 1.49x | 9184 MB/s | 0.01% | +| (1 CPU) | 0.88x | 5418 MB/s | - | 0.77x | 5417 MB/s | - | +| [sofia-air-quality-dataset csv](https://files.klauspost.com/compress/sofia-air-quality-dataset.tar.zst) | 22.00x | 11477 MB/s | 18.73% | 11.15x | 5817 MB/s | 27.88% | +| (1 CPU) | 1.23x | 642 MB/s | - | 0.71x | 642 MB/s | - | +| [silesia.tar](http://sun.aei.polsl.pl/~sdeor/corpus/silesia.zip) | 11.23x | 6520 MB/s | 5.9% | 5.35x | 3109 MB/s | 15.88% | +| (1 CPU) | 1.05x | 607 MB/s | - | 0.52x | 304 MB/s | - | +| [enwik9](https://files.klauspost.com/compress/enwik9.zst) | 19.28x | 8440 MB/s | 4.04% | 9.31x | 4076 MB/s | 18.04% | +| (1 CPU) | 1.12x | 488 MB/s | - | 0.57x | 250 MB/s | - | + +### Legend + +* `S2 Speed`: Speed of S2 compared to Snappy, using 16 cores and 1 core. +* `S2 Throughput`: Throughput of S2 in MB/s. +* `S2 % smaller`: How many percent of the Snappy output size is S2 better. +* `S2 "better"`: Speed when enabling "better" compression mode in S2 compared to Snappy. +* `"better" throughput`: Speed when enabling "better" compression mode in S2 compared to Snappy. +* `"better" % smaller`: How many percent of the Snappy output size is S2 better when using "better" compression. + +There is a good speedup across the board when using a single thread and a significant speedup when using multiple threads. + +Machine generated data gets by far the biggest compression boost, with size being reduced by up to 35% of Snappy size. + +The "better" compression mode sees a good improvement in all cases, but usually at a performance cost. + +Incompressible content (`sharnd.out.2gb`, 2GB random data) sees the smallest speedup. +This is likely dominated by synchronization overhead, which is confirmed by the fact that single threaded performance is higher (see above). + +## Decompression + +S2 attempts to create content that is also fast to decompress, except in "better" mode where the smallest representation is used. + +S2 vs Snappy **decompression** speed. Both operating on single core: + +| File | S2 Throughput | vs. Snappy | Better Throughput | vs. Snappy | +|-----------------------------------------------------------------------------------------------------|---------------|------------|-------------------|------------| +| [rawstudio-mint14.tar](https://files.klauspost.com/compress/rawstudio-mint14.7z) | 2117 MB/s | 1.14x | 1738 MB/s | 0.94x | +| [github-june-2days-2019.json](https://files.klauspost.com/compress/github-june-2days-2019.json.zst) | 2401 MB/s | 1.25x | 2307 MB/s | 1.20x | +| [github-ranks-backup.bin](https://files.klauspost.com/compress/github-ranks-backup.bin.zst) | 2075 MB/s | 0.98x | 1764 MB/s | 0.83x | +| [consensus.db.10gb](https://files.klauspost.com/compress/consensus.db.10gb.zst) | 2967 MB/s | 1.05x | 2885 MB/s | 1.02x | +| [adresser.json](https://files.klauspost.com/compress/adresser.json.zst) | 4141 MB/s | 1.07x | 4184 MB/s | 1.08x | +| [gob-stream](https://files.klauspost.com/compress/gob-stream.7z) | 2264 MB/s | 1.12x | 2185 MB/s | 1.08x | +| [10gb.tar](http://mattmahoney.net/dc/10gb.html) | 1525 MB/s | 1.03x | 1347 MB/s | 0.91x | +| sharnd.out.2gb | 3813 MB/s | 0.79x | 3900 MB/s | 0.81x | +| [enwik9](http://mattmahoney.net/dc/textdata.html) | 1246 MB/s | 1.29x | 967 MB/s | 1.00x | +| [silesia.tar](http://sun.aei.polsl.pl/~sdeor/corpus/silesia.zip) | 1433 MB/s | 1.12x | 1203 MB/s | 0.94x | +| [enwik10](https://encode.su/threads/3315-enwik10-benchmark-results) | 1284 MB/s | 1.32x | 1010 MB/s | 1.04x | + +### Legend + +* `S2 Throughput`: Decompression speed of S2 encoded content. +* `Better Throughput`: Decompression speed of S2 "better" encoded content. +* `vs Snappy`: Decompression speed of S2 "better" mode compared to Snappy and absolute speed. + + +While the decompression code hasn't changed, there is a significant speedup in decompression speed. +S2 prefers longer matches and will typically only find matches that are 6 bytes or longer. +While this reduces compression a bit, it improves decompression speed. + +The "better" compression mode will actively look for shorter matches, which is why it has a decompression speed quite similar to Snappy. + +Without assembly decompression is also very fast; single goroutine decompression speed. No assembly: + +| File | S2 Throughput | S2 throughput | +|--------------------------------|---------------|---------------| +| consensus.db.10gb.s2 | 1.84x | 2289.8 MB/s | +| 10gb.tar.s2 | 1.30x | 867.07 MB/s | +| rawstudio-mint14.tar.s2 | 1.66x | 1329.65 MB/s | +| github-june-2days-2019.json.s2 | 2.36x | 1831.59 MB/s | +| github-ranks-backup.bin.s2 | 1.73x | 1390.7 MB/s | +| enwik9.s2 | 1.67x | 681.53 MB/s | +| adresser.json.s2 | 3.41x | 4230.53 MB/s | +| silesia.tar.s2 | 1.52x | 811.58 | + +Even though S2 typically compresses better than Snappy, decompression speed is always better. + +### Concurrent Stream Decompression + +For full stream decompression S2 offers a [DecodeConcurrent](https://pkg.go.dev/github.com/klauspost/compress/s2#Reader.DecodeConcurrent) +that will decode a full stream using multiple goroutines. + +Example scaling, AMD Ryzen 3950X, 16 cores, decompression using `s2d -bench=3 `, best of 3: + +| Input | `-cpu=1` | `-cpu=2` | `-cpu=4` | `-cpu=8` | `-cpu=16` | +|-------------------------------------------|------------|------------|------------|------------|-------------| +| enwik10.snappy | 1098.6MB/s | 1819.8MB/s | 3625.6MB/s | 6910.6MB/s | 10818.2MB/s | +| enwik10.s2 | 1303.5MB/s | 2606.1MB/s | 4847.9MB/s | 8878.4MB/s | 9592.1MB/s | +| sofia-air-quality-dataset.tar.snappy | 1302.0MB/s | 2165.0MB/s | 4244.5MB/s | 8241.0MB/s | 12920.5MB/s | +| sofia-air-quality-dataset.tar.s2 | 1399.2MB/s | 2463.2MB/s | 5196.5MB/s | 9639.8MB/s | 11439.5MB/s | +| sofia-air-quality-dataset.tar.s2 (no asm) | 837.5MB/s | 1652.6MB/s | 3183.6MB/s | 5945.0MB/s | 9620.7MB/s | + +Scaling can be expected to be pretty linear until memory bandwidth is saturated. + +For now the DecodeConcurrent can only be used for full streams without seeking or combining with regular reads. + +## Block compression + + +When compressing blocks no concurrent compression is performed just as Snappy. +This is because blocks are for smaller payloads and generally will not benefit from concurrent compression. + +An important change is that incompressible blocks will not be more than at most 10 bytes bigger than the input. +In rare, worst case scenario Snappy blocks could be significantly bigger than the input. + +### Mixed content blocks + +The most reliable is a wide dataset. +For this we use [`webdevdata.org-2015-01-07-subset`](https://files.klauspost.com/compress/webdevdata.org-2015-01-07-4GB-subset.7z), +53927 files, total input size: 4,014,735,833 bytes. Single goroutine used. + +| * | Input | Output | Reduction | MB/s | +|-------------------|------------|------------|------------|------------| +| S2 | 4014735833 | 1059723369 | 73.60% | **936.73** | +| S2 Better | 4014735833 | 961580539 | 76.05% | 451.10 | +| S2 Best | 4014735833 | 899182886 | **77.60%** | 46.84 | +| Snappy | 4014735833 | 1128706759 | 71.89% | 790.15 | +| S2, Snappy Output | 4014735833 | 1093823291 | 72.75% | 936.60 | +| LZ4 | 4014735833 | 1063768713 | 73.50% | 452.02 | + +S2 delivers both the best single threaded throughput with regular mode and the best compression rate with "best". +"Better" mode provides the same compression speed as LZ4 with better compression ratio. + +When outputting Snappy compatible output it still delivers better throughput (150MB/s more) and better compression. + +As can be seen from the other benchmarks decompression should also be easier on the S2 generated output. + +Though they cannot be compared due to different decompression speeds here are the speed/size comparisons for +other Go compressors: + +| * | Input | Output | Reduction | MB/s | +|-------------------|------------|------------|-----------|--------| +| Zstd Fastest (Go) | 4014735833 | 794608518 | 80.21% | 236.04 | +| Zstd Best (Go) | 4014735833 | 704603356 | 82.45% | 35.63 | +| Deflate (Go) l1 | 4014735833 | 871294239 | 78.30% | 214.04 | +| Deflate (Go) l9 | 4014735833 | 730389060 | 81.81% | 41.17 | + +### Standard block compression + +Benchmarking single block performance is subject to a lot more variation since it only tests a limited number of file patterns. +So individual benchmarks should only be seen as a guideline and the overall picture is more important. + +These micro-benchmarks are with data in cache and trained branch predictors. For a more realistic benchmark see the mixed content above. + +Block compression. Parallel benchmark running on 16 cores, 16 goroutines. + +AMD64 assembly is use for both S2 and Snappy. + +| Absolute Perf | Snappy size | S2 Size | Snappy Speed | S2 Speed | Snappy dec | S2 dec | +|-----------------------|-------------|---------|--------------|-------------|-------------|-------------| +| html | 22843 | 20868 | 16246 MB/s | 18617 MB/s | 40972 MB/s | 49263 MB/s | +| urls.10K | 335492 | 286541 | 7943 MB/s | 10201 MB/s | 22523 MB/s | 26484 MB/s | +| fireworks.jpeg | 123034 | 123100 | 349544 MB/s | 303228 MB/s | 718321 MB/s | 827552 MB/s | +| fireworks.jpeg (200B) | 146 | 155 | 8869 MB/s | 20180 MB/s | 33691 MB/s | 52421 MB/s | +| paper-100k.pdf | 85304 | 84202 | 167546 MB/s | 112988 MB/s | 326905 MB/s | 291944 MB/s | +| html_x_4 | 92234 | 20870 | 15194 MB/s | 54457 MB/s | 30843 MB/s | 32217 MB/s | +| alice29.txt | 88034 | 85934 | 5936 MB/s | 6540 MB/s | 12882 MB/s | 20044 MB/s | +| asyoulik.txt | 77503 | 79575 | 5517 MB/s | 6657 MB/s | 12735 MB/s | 22806 MB/s | +| lcet10.txt | 234661 | 220383 | 6235 MB/s | 6303 MB/s | 14519 MB/s | 18697 MB/s | +| plrabn12.txt | 319267 | 318196 | 5159 MB/s | 6074 MB/s | 11923 MB/s | 19901 MB/s | +| geo.protodata | 23335 | 18606 | 21220 MB/s | 25432 MB/s | 56271 MB/s | 62540 MB/s | +| kppkn.gtb | 69526 | 65019 | 9732 MB/s | 8905 MB/s | 18491 MB/s | 18969 MB/s | +| alice29.txt (128B) | 80 | 82 | 6691 MB/s | 17179 MB/s | 31883 MB/s | 38874 MB/s | +| alice29.txt (1000B) | 774 | 774 | 12204 MB/s | 13273 MB/s | 48056 MB/s | 52341 MB/s | +| alice29.txt (10000B) | 6648 | 6933 | 10044 MB/s | 12824 MB/s | 32378 MB/s | 46322 MB/s | +| alice29.txt (20000B) | 12686 | 13516 | 7733 MB/s | 12160 MB/s | 30566 MB/s | 58969 MB/s | + + +Speed is generally at or above Snappy. Small blocks gets a significant speedup, although at the expense of size. + +Decompression speed is better than Snappy, except in one case. + +Since payloads are very small the variance in terms of size is rather big, so they should only be seen as a general guideline. + +Size is on average around Snappy, but varies on content type. +In cases where compression is worse, it usually is compensated by a speed boost. + + +### Better compression + +Benchmarking single block performance is subject to a lot more variation since it only tests a limited number of file patterns. +So individual benchmarks should only be seen as a guideline and the overall picture is more important. + +| Absolute Perf | Snappy size | Better Size | Snappy Speed | Better Speed | Snappy dec | Better dec | +|-----------------------|-------------|-------------|--------------|--------------|-------------|-------------| +| html | 22843 | 18972 | 16246 MB/s | 8621 MB/s | 40972 MB/s | 40292 MB/s | +| urls.10K | 335492 | 248079 | 7943 MB/s | 5104 MB/s | 22523 MB/s | 20981 MB/s | +| fireworks.jpeg | 123034 | 123100 | 349544 MB/s | 84429 MB/s | 718321 MB/s | 823698 MB/s | +| fireworks.jpeg (200B) | 146 | 149 | 8869 MB/s | 7125 MB/s | 33691 MB/s | 30101 MB/s | +| paper-100k.pdf | 85304 | 82887 | 167546 MB/s | 11087 MB/s | 326905 MB/s | 198869 MB/s | +| html_x_4 | 92234 | 18982 | 15194 MB/s | 29316 MB/s | 30843 MB/s | 30937 MB/s | +| alice29.txt | 88034 | 71611 | 5936 MB/s | 3709 MB/s | 12882 MB/s | 16611 MB/s | +| asyoulik.txt | 77503 | 65941 | 5517 MB/s | 3380 MB/s | 12735 MB/s | 14975 MB/s | +| lcet10.txt | 234661 | 184939 | 6235 MB/s | 3537 MB/s | 14519 MB/s | 16634 MB/s | +| plrabn12.txt | 319267 | 264990 | 5159 MB/s | 2960 MB/s | 11923 MB/s | 13382 MB/s | +| geo.protodata | 23335 | 17689 | 21220 MB/s | 10859 MB/s | 56271 MB/s | 57961 MB/s | +| kppkn.gtb | 69526 | 55398 | 9732 MB/s | 5206 MB/s | 18491 MB/s | 16524 MB/s | +| alice29.txt (128B) | 80 | 78 | 6691 MB/s | 7422 MB/s | 31883 MB/s | 34225 MB/s | +| alice29.txt (1000B) | 774 | 746 | 12204 MB/s | 5734 MB/s | 48056 MB/s | 42068 MB/s | +| alice29.txt (10000B) | 6648 | 6218 | 10044 MB/s | 6055 MB/s | 32378 MB/s | 28813 MB/s | +| alice29.txt (20000B) | 12686 | 11492 | 7733 MB/s | 3143 MB/s | 30566 MB/s | 27315 MB/s | + + +Except for the mostly incompressible JPEG image compression is better and usually in the +double digits in terms of percentage reduction over Snappy. + +The PDF sample shows a significant slowdown compared to Snappy, as this mode tries harder +to compress the data. Very small blocks are also not favorable for better compression, so throughput is way down. + +This mode aims to provide better compression at the expense of performance and achieves that +without a huge performance penalty, except on very small blocks. + +Decompression speed suffers a little compared to the regular S2 mode, +but still manages to be close to Snappy in spite of increased compression. + +# Best compression mode + +S2 offers a "best" compression mode. + +This will compress as much as possible with little regard to CPU usage. + +Mainly for offline compression, but where decompression speed should still +be high and compatible with other S2 compressed data. + +Some examples compared on 16 core CPU, amd64 assembly used: + +``` +* enwik10 +Default... 10000000000 -> 4759950115 [47.60%]; 1.03s, 9263.0MB/s +Better... 10000000000 -> 4084706676 [40.85%]; 2.16s, 4415.4MB/s +Best... 10000000000 -> 3615520079 [36.16%]; 42.259s, 225.7MB/s + +* github-june-2days-2019.json +Default... 6273951764 -> 1041700255 [16.60%]; 431ms, 13882.3MB/s +Better... 6273951764 -> 945841238 [15.08%]; 547ms, 10938.4MB/s +Best... 6273951764 -> 826392576 [13.17%]; 9.455s, 632.8MB/s + +* nyc-taxi-data-10M.csv +Default... 3325605752 -> 1093516949 [32.88%]; 324ms, 9788.7MB/s +Better... 3325605752 -> 885394158 [26.62%]; 491ms, 6459.4MB/s +Best... 3325605752 -> 773681257 [23.26%]; 8.29s, 412.0MB/s + +* 10gb.tar +Default... 10065157632 -> 5915541066 [58.77%]; 1.028s, 9337.4MB/s +Better... 10065157632 -> 5453844650 [54.19%]; 1.597s, 4862.7MB/s +Best... 10065157632 -> 5192495021 [51.59%]; 32.78s, 308.2MB/ + +* consensus.db.10gb +Default... 10737418240 -> 4549762344 [42.37%]; 882ms, 12118.4MB/s +Better... 10737418240 -> 4438535064 [41.34%]; 1.533s, 3500.9MB/s +Best... 10737418240 -> 4210602774 [39.21%]; 42.96s, 254.4MB/s +``` + +Decompression speed should be around the same as using the 'better' compression mode. + +## Dictionaries + +*Note: S2 dictionary compression is currently at an early implementation stage, with no assembly for +neither encoding nor decoding. Performance improvements can be expected in the future.* + +Adding dictionaries allow providing a custom dictionary that will serve as lookup in the beginning of blocks. + +The same dictionary *must* be used for both encoding and decoding. +S2 does not keep track of whether the same dictionary is used, +and using the wrong dictionary will most often not result in an error when decompressing. + +Blocks encoded *without* dictionaries can be decompressed seamlessly *with* a dictionary. +This means it is possible to switch from an encoding without dictionaries to an encoding with dictionaries +and treat the blocks similarly. + +Similar to [zStandard dictionaries](https://github.com/facebook/zstd#the-case-for-small-data-compression), +the same usage scenario applies to S2 dictionaries. + +> Training works if there is some correlation in a family of small data samples. The more data-specific a dictionary is, the more efficient it is (there is no universal dictionary). Hence, deploying one dictionary per type of data will provide the greatest benefits. Dictionary gains are mostly effective in the first few KB. Then, the compression algorithm will gradually use previously decoded content to better compress the rest of the file. + +S2 further limits the dictionary to only be enabled on the first 64KB of a block. +This will remove any negative (speed) impacts of the dictionaries on bigger blocks. + +### Compression + +Using the [github_users_sample_set](https://github.com/facebook/zstd/releases/download/v1.1.3/github_users_sample_set.tar.zst) +and a 64KB dictionary trained with zStandard the following sizes can be achieved. + +| | Default | Better | Best | +|--------------------|------------------|------------------|-----------------------| +| Without Dictionary | 3362023 (44.92%) | 3083163 (41.19%) | 3057944 (40.86%) | +| With Dictionary | 921524 (12.31%) | 873154 (11.67%) | 785503 bytes (10.49%) | + +So for highly repetitive content, this case provides an almost 3x reduction in size. + +For less uniform data we will use the Go source code tree. +Compressing First 64KB of all `.go` files in `go/src`, Go 1.19.5, 8912 files, 51253563 bytes input: + +| | Default | Better | Best | +|--------------------|-------------------|-------------------|-------------------| +| Without Dictionary | 22955767 (44.79%) | 20189613 (39.39% | 19482828 (38.01%) | +| With Dictionary | 19654568 (38.35%) | 16289357 (31.78%) | 15184589 (29.63%) | +| Saving/file | 362 bytes | 428 bytes | 472 bytes | + + +### Creating Dictionaries + +There are no tools to create dictionaries in S2. +However, there are multiple ways to create a useful dictionary: + +#### Using a Sample File + +If your input is very uniform, you can just use a sample file as the dictionary. + +For example in the `github_users_sample_set` above, the average compression only goes up from +10.49% to 11.48% by using the first file as dictionary compared to using a dedicated dictionary. + +```Go + // Read a sample + sample, err := os.ReadFile("sample.json") + + // Create a dictionary. + dict := s2.MakeDict(sample, nil) + + // b := dict.Bytes() will provide a dictionary that can be saved + // and reloaded with s2.NewDict(b). + + // To encode: + encoded := dict.Encode(nil, file) + + // To decode: + decoded, err := dict.Decode(nil, file) +``` + +#### Using Zstandard + +Zstandard dictionaries can easily be converted to S2 dictionaries. + +This can be helpful to generate dictionaries for files that don't have a fixed structure. + + +Example, with training set files placed in `./training-set`: + +`λ zstd -r --train-fastcover training-set/* --maxdict=65536 -o name.dict` + +This will create a dictionary of 64KB, that can be converted to a dictionary like this: + +```Go + // Decode the Zstandard dictionary. + insp, err := zstd.InspectDictionary(zdict) + if err != nil { + panic(err) + } + + // We are only interested in the contents. + // Assume that files start with "// Copyright (c) 2023". + // Search for the longest match for that. + // This may save a few bytes. + dict := s2.MakeDict(insp.Content(), []byte("// Copyright (c) 2023")) + + // b := dict.Bytes() will provide a dictionary that can be saved + // and reloaded with s2.NewDict(b). + + // We can now encode using this dictionary + encodedWithDict := dict.Encode(nil, payload) + + // To decode content: + decoded, err := dict.Decode(nil, encodedWithDict) +``` + +It is recommended to save the dictionary returned by ` b:= dict.Bytes()`, since that will contain only the S2 dictionary. + +This dictionary can later be loaded using `s2.NewDict(b)`. The dictionary then no longer requires `zstd` to be initialized. + +Also note how `s2.MakeDict` allows you to search for a common starting sequence of your files. +This can be omitted, at the expense of a few bytes. + +# Snappy Compatibility + +S2 now offers full compatibility with Snappy. + +This means that the efficient encoders of S2 can be used to generate fully Snappy compatible output. + +There is a [snappy](https://github.com/klauspost/compress/tree/master/snappy) package that can be used by +simply changing imports from `github.com/golang/snappy` to `github.com/klauspost/compress/snappy`. +This uses "better" mode for all operations. +If you would like more control, you can use the s2 package as described below: + +## Blocks + +Snappy compatible blocks can be generated with the S2 encoder. +Compression and speed is typically a bit better `MaxEncodedLen` is also smaller for smaller memory usage. Replace + +| Snappy | S2 replacement | +|---------------------------|-----------------------| +| snappy.Encode(...) | s2.EncodeSnappy(...) | +| snappy.MaxEncodedLen(...) | s2.MaxEncodedLen(...) | + +`s2.EncodeSnappy` can be replaced with `s2.EncodeSnappyBetter` or `s2.EncodeSnappyBest` to get more efficiently compressed snappy compatible output. + +`s2.ConcatBlocks` is compatible with snappy blocks. + +Comparison of [`webdevdata.org-2015-01-07-subset`](https://files.klauspost.com/compress/webdevdata.org-2015-01-07-4GB-subset.7z), +53927 files, total input size: 4,014,735,833 bytes. amd64, single goroutine used: + +| Encoder | Size | MB/s | Reduction | +|-----------------------|------------|------------|------------| +| snappy.Encode | 1128706759 | 725.59 | 71.89% | +| s2.EncodeSnappy | 1093823291 | **899.16** | 72.75% | +| s2.EncodeSnappyBetter | 1001158548 | 578.49 | 75.06% | +| s2.EncodeSnappyBest | 944507998 | 66.00 | **76.47%** | + +## Streams + +For streams, replace `enc = snappy.NewBufferedWriter(w)` with `enc = s2.NewWriter(w, s2.WriterSnappyCompat())`. +All other options are available, but note that block size limit is different for snappy. + +Comparison of different streams, AMD Ryzen 3950x, 16 cores. Size and throughput: + +| File | snappy.NewWriter | S2 Snappy | S2 Snappy, Better | S2 Snappy, Best | +|-----------------------------|--------------------------|---------------------------|--------------------------|-------------------------| +| nyc-taxi-data-10M.csv | 1316042016 - 539.47MB/s | 1307003093 - 10132.73MB/s | 1174534014 - 5002.44MB/s | 1115904679 - 177.97MB/s | +| enwik10 (xml) | 5088294643 - 451.13MB/s | 5175840939 - 9440.69MB/s | 4560784526 - 4487.21MB/s | 4340299103 - 158.92MB/s | +| 10gb.tar (mixed) | 6056946612 - 729.73MB/s | 6208571995 - 9978.05MB/s | 5741646126 - 4919.98MB/s | 5548973895 - 180.44MB/s | +| github-june-2days-2019.json | 1525176492 - 933.00MB/s | 1476519054 - 13150.12MB/s | 1400547532 - 5803.40MB/s | 1321887137 - 204.29MB/s | +| consensus.db.10gb (db) | 5412897703 - 1102.14MB/s | 5354073487 - 13562.91MB/s | 5335069899 - 5294.73MB/s | 5201000954 - 175.72MB/s | + +# Decompression + +All decompression functions map directly to equivalent s2 functions. + +| Snappy | S2 replacement | +|------------------------|--------------------| +| snappy.Decode(...) | s2.Decode(...) | +| snappy.DecodedLen(...) | s2.DecodedLen(...) | +| snappy.NewReader(...) | s2.NewReader(...) | + +Features like [quick forward skipping without decompression](https://pkg.go.dev/github.com/klauspost/compress/s2#Reader.Skip) +are also available for Snappy streams. + +If you know you are only decompressing snappy streams, setting [`ReaderMaxBlockSize(64<<10)`](https://pkg.go.dev/github.com/klauspost/compress/s2#ReaderMaxBlockSize) +on your Reader will reduce memory consumption. + +# Concatenating blocks and streams. + +Concatenating streams will concatenate the output of both without recompressing them. +While this is inefficient in terms of compression it might be usable in certain scenarios. +The 10 byte 'stream identifier' of the second stream can optionally be stripped, but it is not a requirement. + +Blocks can be concatenated using the `ConcatBlocks` function. + +Snappy blocks/streams can safely be concatenated with S2 blocks and streams. +Streams with indexes (see below) will currently not work on concatenated streams. + +# Stream Seek Index + +S2 and Snappy streams can have indexes. These indexes will allow random seeking within the compressed data. + +The index can either be appended to the stream as a skippable block or returned for separate storage. + +When the index is appended to a stream it will be skipped by regular decoders, +so the output remains compatible with other decoders. + +## Creating an Index + +To automatically add an index to a stream, add `WriterAddIndex()` option to your writer. +Then the index will be added to the stream when `Close()` is called. + +``` + // Add Index to stream... + enc := s2.NewWriter(w, s2.WriterAddIndex()) + io.Copy(enc, r) + enc.Close() +``` + +If you want to store the index separately, you can use `CloseIndex()` instead of the regular `Close()`. +This will return the index. Note that `CloseIndex()` should only be called once, and you shouldn't call `Close()`. + +``` + // Get index for separate storage... + enc := s2.NewWriter(w) + io.Copy(enc, r) + index, err := enc.CloseIndex() +``` + +The `index` can then be used needing to read from the stream. +This means the index can be used without needing to seek to the end of the stream +or for manually forwarding streams. See below. + +Finally, an existing S2/Snappy stream can be indexed using the `s2.IndexStream(r io.Reader)` function. + +## Using Indexes + +To use indexes there is a `ReadSeeker(random bool, index []byte) (*ReadSeeker, error)` function available. + +Calling ReadSeeker will return an [io.ReadSeeker](https://pkg.go.dev/io#ReadSeeker) compatible version of the reader. + +If 'random' is specified the returned io.Seeker can be used for random seeking, otherwise only forward seeking is supported. +Enabling random seeking requires the original input to support the [io.Seeker](https://pkg.go.dev/io#Seeker) interface. + +``` + dec := s2.NewReader(r) + rs, err := dec.ReadSeeker(false, nil) + rs.Seek(wantOffset, io.SeekStart) +``` + +Get a seeker to seek forward. Since no index is provided, the index is read from the stream. +This requires that an index was added and that `r` supports the [io.Seeker](https://pkg.go.dev/io#Seeker) interface. + +A custom index can be specified which will be used if supplied. +When using a custom index, it will not be read from the input stream. + +``` + dec := s2.NewReader(r) + rs, err := dec.ReadSeeker(false, index) + rs.Seek(wantOffset, io.SeekStart) +``` + +This will read the index from `index`. Since we specify non-random (forward only) seeking `r` does not have to be an io.Seeker + +``` + dec := s2.NewReader(r) + rs, err := dec.ReadSeeker(true, index) + rs.Seek(wantOffset, io.SeekStart) +``` + +Finally, since we specify that we want to do random seeking `r` must be an io.Seeker. + +The returned [ReadSeeker](https://pkg.go.dev/github.com/klauspost/compress/s2#ReadSeeker) contains a shallow reference to the existing Reader, +meaning changes performed to one is reflected in the other. + +To check if a stream contains an index at the end, the `(*Index).LoadStream(rs io.ReadSeeker) error` can be used. + +## Manually Forwarding Streams + +Indexes can also be read outside the decoder using the [Index](https://pkg.go.dev/github.com/klauspost/compress/s2#Index) type. +This can be used for parsing indexes, either separate or in streams. + +In some cases it may not be possible to serve a seekable stream. +This can for instance be an HTTP stream, where the Range request +is sent at the start of the stream. + +With a little bit of extra code it is still possible to use indexes +to forward to specific offset with a single forward skip. + +It is possible to load the index manually like this: +``` + var index s2.Index + _, err = index.Load(idxBytes) +``` + +This can be used to figure out how much to offset the compressed stream: + +``` + compressedOffset, uncompressedOffset, err := index.Find(wantOffset) +``` + +The `compressedOffset` is the number of bytes that should be skipped +from the beginning of the compressed file. + +The `uncompressedOffset` will then be offset of the uncompressed bytes returned +when decoding from that position. This will always be <= wantOffset. + +When creating a decoder it must be specified that it should *not* expect a stream identifier +at the beginning of the stream. Assuming the io.Reader `r` has been forwarded to `compressedOffset` +we create the decoder like this: + +``` + dec := s2.NewReader(r, s2.ReaderIgnoreStreamIdentifier()) +``` + +We are not completely done. We still need to forward the stream the uncompressed bytes we didn't want. +This is done using the regular "Skip" function: + +``` + err = dec.Skip(wantOffset - uncompressedOffset) +``` + +This will ensure that we are at exactly the offset we want, and reading from `dec` will start at the requested offset. + +# Compact storage + +For compact storage [RemoveIndexHeaders](https://pkg.go.dev/github.com/klauspost/compress/s2#RemoveIndexHeaders) can be used to remove any redundant info from +a serialized index. If you remove the header it must be restored before [Loading](https://pkg.go.dev/github.com/klauspost/compress/s2#Index.Load). + +This is expected to save 20 bytes. These can be restored using [RestoreIndexHeaders](https://pkg.go.dev/github.com/klauspost/compress/s2#RestoreIndexHeaders). This removes a layer of security, but is the most compact representation. Returns nil if headers contains errors. + +## Index Format: + +Each block is structured as a snappy skippable block, with the chunk ID 0x99. + +The block can be read from the front, but contains information so it can be read from the back as well. + +Numbers are stored as fixed size little endian values or [zigzag encoded](https://developers.google.com/protocol-buffers/docs/encoding#signed_integers) [base 128 varints](https://developers.google.com/protocol-buffers/docs/encoding), +with un-encoded value length of 64 bits, unless other limits are specified. + +| Content | Format | +|--------------------------------------|-------------------------------------------------------------------------------------------------------------------------------| +| ID, `[1]byte` | Always 0x99. | +| Data Length, `[3]byte` | 3 byte little-endian length of the chunk in bytes, following this. | +| Header `[6]byte` | Header, must be `[115, 50, 105, 100, 120, 0]` or in text: "s2idx\x00". | +| UncompressedSize, Varint | Total Uncompressed size. | +| CompressedSize, Varint | Total Compressed size if known. Should be -1 if unknown. | +| EstBlockSize, Varint | Block Size, used for guessing uncompressed offsets. Must be >= 0. | +| Entries, Varint | Number of Entries in index, must be < 65536 and >=0. | +| HasUncompressedOffsets `byte` | 0 if no uncompressed offsets are present, 1 if present. Other values are invalid. | +| UncompressedOffsets, [Entries]VarInt | Uncompressed offsets. See below how to decode. | +| CompressedOffsets, [Entries]VarInt | Compressed offsets. See below how to decode. | +| Block Size, `[4]byte` | Little Endian total encoded size (including header and trailer). Can be used for searching backwards to start of block. | +| Trailer `[6]byte` | Trailer, must be `[0, 120, 100, 105, 50, 115]` or in text: "\x00xdi2s". Can be used for identifying block from end of stream. | + +For regular streams the uncompressed offsets are fully predictable, +so `HasUncompressedOffsets` allows to specify that compressed blocks all have +exactly `EstBlockSize` bytes of uncompressed content. + +Entries *must* be in order, starting with the lowest offset, +and there *must* be no uncompressed offset duplicates. +Entries *may* point to the start of a skippable block, +but it is then not allowed to also have an entry for the next block since +that would give an uncompressed offset duplicate. + +There is no requirement for all blocks to be represented in the index. +In fact there is a maximum of 65536 block entries in an index. + +The writer can use any method to reduce the number of entries. +An implicit block start at 0,0 can be assumed. + +### Decoding entries: + +``` +// Read Uncompressed entries. +// Each assumes EstBlockSize delta from previous. +for each entry { + uOff = 0 + if HasUncompressedOffsets == 1 { + uOff = ReadVarInt // Read value from stream + } + + // Except for the first entry, use previous values. + if entryNum == 0 { + entry[entryNum].UncompressedOffset = uOff + continue + } + + // Uncompressed uses previous offset and adds EstBlockSize + entry[entryNum].UncompressedOffset = entry[entryNum-1].UncompressedOffset + EstBlockSize + uOff +} + + +// Guess that the first block will be 50% of uncompressed size. +// Integer truncating division must be used. +CompressGuess := EstBlockSize / 2 + +// Read Compressed entries. +// Each assumes CompressGuess delta from previous. +// CompressGuess is adjusted for each value. +for each entry { + cOff = ReadVarInt // Read value from stream + + // Except for the first entry, use previous values. + if entryNum == 0 { + entry[entryNum].CompressedOffset = cOff + continue + } + + // Compressed uses previous and our estimate. + entry[entryNum].CompressedOffset = entry[entryNum-1].CompressedOffset + CompressGuess + cOff + + // Adjust compressed offset for next loop, integer truncating division must be used. + CompressGuess += cOff/2 +} +``` + +To decode from any given uncompressed offset `(wantOffset)`: + +* Iterate entries until `entry[n].UncompressedOffset > wantOffset`. +* Start decoding from `entry[n-1].CompressedOffset`. +* Discard `entry[n-1].UncompressedOffset - wantOffset` bytes from the decoded stream. + +See [using indexes](https://github.com/klauspost/compress/tree/master/s2#using-indexes) for functions that perform the operations with a simpler interface. + + +# Format Extensions + +* Frame [Stream identifier](https://github.com/google/snappy/blob/master/framing_format.txt#L68) changed from `sNaPpY` to `S2sTwO`. +* [Framed compressed blocks](https://github.com/google/snappy/blob/master/format_description.txt) can be up to 4MB (up from 64KB). +* Compressed blocks can have an offset of `0`, which indicates to repeat the last seen offset. + +Repeat offsets must be encoded as a [2.2.1. Copy with 1-byte offset (01)](https://github.com/google/snappy/blob/master/format_description.txt#L89), where the offset is 0. + +The length is specified by reading the 3-bit length specified in the tag and decode using this table: + +| Length | Actual Length | +|--------|----------------------| +| 0 | 4 | +| 1 | 5 | +| 2 | 6 | +| 3 | 7 | +| 4 | 8 | +| 5 | 8 + read 1 byte | +| 6 | 260 + read 2 bytes | +| 7 | 65540 + read 3 bytes | + +This allows any repeat offset + length to be represented by 2 to 5 bytes. +It also allows to emit matches longer than 64 bytes with one copy + one repeat instead of several 64 byte copies. + +Lengths are stored as little endian values. + +The first copy of a block cannot be a repeat offset and the offset is reset on every block in streams. + +Default streaming block size is 1MB. + +# Dictionary Encoding + +Adding dictionaries allow providing a custom dictionary that will serve as lookup in the beginning of blocks. + +A dictionary provides an initial repeat value that can be used to point to a common header. + +Other than that the dictionary contains values that can be used as back-references. + +Often used data should be placed at the *end* of the dictionary since offsets < 2048 bytes will be smaller. + +## Format + +Dictionary *content* must at least 16 bytes and less or equal to 64KiB (65536 bytes). + +Encoding: `[repeat value (uvarint)][dictionary content...]` + +Before the dictionary content, an unsigned base-128 (uvarint) encoded value specifying the initial repeat offset. +This value is an offset into the dictionary content and not a back-reference offset, +so setting this to 0 will make the repeat value point to the first value of the dictionary. + +The value must be less than the dictionary length-8 + +## Encoding + +From the decoder point of view the dictionary content is seen as preceding the encoded content. + +`[dictionary content][decoded output]` + +Backreferences to the dictionary are encoded as ordinary backreferences that have an offset before the start of the decoded block. + +Matches copying from the dictionary are **not** allowed to cross from the dictionary into the decoded data. +However, if a copy ends at the end of the dictionary the next repeat will point to the start of the decoded buffer, which is allowed. + +The first match can be a repeat value, which will use the repeat offset stored in the dictionary. + +When 64KB (65536 bytes) has been en/decoded it is no longer allowed to reference the dictionary, +neither by a copy nor repeat operations. +If the boundary is crossed while copying from the dictionary, the operation should complete, +but the next instruction is not allowed to reference the dictionary. + +Valid blocks encoded *without* a dictionary can be decoded with any dictionary. +There are no checks whether the supplied dictionary is the correct for a block. +Because of this there is no overhead by using a dictionary. + +## Example + +This is the dictionary content. Elements are separated by `[]`. + +Dictionary: `[0x0a][Yesterday 25 bananas were added to Benjamins brown bag]`. + +Initial repeat offset is set at 10, which is the letter `2`. + +Encoded `[LIT "10"][REPEAT len=10][LIT "hich"][MATCH off=50 len=6][MATCH off=31 len=6][MATCH off=61 len=10]` + +Decoded: `[10][ bananas w][hich][ were ][brown ][were added]` + +Output: `10 bananas which were brown were added` + + +## Streams + +For streams each block can use the dictionary. + +The dictionary cannot not currently be provided on the stream. + + +# LICENSE + +This code is based on the [Snappy-Go](https://github.com/golang/snappy) implementation. + +Use of this source code is governed by a BSD-style license that can be found in the LICENSE file. diff --git a/vendor/github.com/klauspost/compress/s2/decode.go b/vendor/github.com/klauspost/compress/s2/decode.go new file mode 100644 index 000000000..264ffd0a9 --- /dev/null +++ b/vendor/github.com/klauspost/compress/s2/decode.go @@ -0,0 +1,443 @@ +// Copyright 2011 The Snappy-Go Authors. All rights reserved. +// Copyright (c) 2019 Klaus Post. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package s2 + +import ( + "encoding/binary" + "errors" + "fmt" + "strconv" + + "github.com/klauspost/compress/internal/race" +) + +var ( + // ErrCorrupt reports that the input is invalid. + ErrCorrupt = errors.New("s2: corrupt input") + // ErrCRC reports that the input failed CRC validation (streams only) + ErrCRC = errors.New("s2: corrupt input, crc mismatch") + // ErrTooLarge reports that the uncompressed length is too large. + ErrTooLarge = errors.New("s2: decoded block is too large") + // ErrUnsupported reports that the input isn't supported. + ErrUnsupported = errors.New("s2: unsupported input") +) + +// DecodedLen returns the length of the decoded block. +func DecodedLen(src []byte) (int, error) { + v, _, err := decodedLen(src) + return v, err +} + +// decodedLen returns the length of the decoded block and the number of bytes +// that the length header occupied. +func decodedLen(src []byte) (blockLen, headerLen int, err error) { + v, n := binary.Uvarint(src) + if n <= 0 || v > 0xffffffff { + return 0, 0, ErrCorrupt + } + + const wordSize = 32 << (^uint(0) >> 32 & 1) + if wordSize == 32 && v > 0x7fffffff { + return 0, 0, ErrTooLarge + } + return int(v), n, nil +} + +const ( + decodeErrCodeCorrupt = 1 +) + +// Decode returns the decoded form of src. The returned slice may be a sub- +// slice of dst if dst was large enough to hold the entire decoded block. +// Otherwise, a newly allocated slice will be returned. +// +// The dst and src must not overlap. It is valid to pass a nil dst. +func Decode(dst, src []byte) ([]byte, error) { + dLen, s, err := decodedLen(src) + if err != nil { + return nil, err + } + if dLen <= cap(dst) { + dst = dst[:dLen] + } else { + dst = make([]byte, dLen) + } + + race.WriteSlice(dst) + race.ReadSlice(src[s:]) + + if s2Decode(dst, src[s:]) != 0 { + return nil, ErrCorrupt + } + return dst, nil +} + +// s2DecodeDict writes the decoding of src to dst. It assumes that the varint-encoded +// length of the decompressed bytes has already been read, and that len(dst) +// equals that length. +// +// It returns 0 on success or a decodeErrCodeXxx error code on failure. +func s2DecodeDict(dst, src []byte, dict *Dict) int { + if dict == nil { + return s2Decode(dst, src) + } + const debug = false + const debugErrs = debug + + if debug { + fmt.Println("Starting decode, dst len:", len(dst)) + } + var d, s, length int + offset := len(dict.dict) - dict.repeat + + // As long as we can read at least 5 bytes... + for s < len(src)-5 { + // Removing bounds checks is SLOWER, when if doing + // in := src[s:s+5] + // Checked on Go 1.18 + switch src[s] & 0x03 { + case tagLiteral: + x := uint32(src[s] >> 2) + switch { + case x < 60: + s++ + case x == 60: + s += 2 + x = uint32(src[s-1]) + case x == 61: + in := src[s : s+3] + x = uint32(in[1]) | uint32(in[2])<<8 + s += 3 + case x == 62: + in := src[s : s+4] + // Load as 32 bit and shift down. + x = uint32(in[0]) | uint32(in[1])<<8 | uint32(in[2])<<16 | uint32(in[3])<<24 + x >>= 8 + s += 4 + case x == 63: + in := src[s : s+5] + x = uint32(in[1]) | uint32(in[2])<<8 | uint32(in[3])<<16 | uint32(in[4])<<24 + s += 5 + } + length = int(x) + 1 + if debug { + fmt.Println("literals, length:", length, "d-after:", d+length) + } + if length > len(dst)-d || length > len(src)-s || (strconv.IntSize == 32 && length <= 0) { + if debugErrs { + fmt.Println("corrupt literal: length:", length, "d-left:", len(dst)-d, "src-left:", len(src)-s) + } + return decodeErrCodeCorrupt + } + + copy(dst[d:], src[s:s+length]) + d += length + s += length + continue + + case tagCopy1: + s += 2 + toffset := int(uint32(src[s-2])&0xe0<<3 | uint32(src[s-1])) + length = int(src[s-2]) >> 2 & 0x7 + if toffset == 0 { + if debug { + fmt.Print("(repeat) ") + } + // keep last offset + switch length { + case 5: + length = int(src[s]) + 4 + s += 1 + case 6: + in := src[s : s+2] + length = int(uint32(in[0])|(uint32(in[1])<<8)) + (1 << 8) + s += 2 + case 7: + in := src[s : s+3] + length = int((uint32(in[2])<<16)|(uint32(in[1])<<8)|uint32(in[0])) + (1 << 16) + s += 3 + default: // 0-> 4 + } + } else { + offset = toffset + } + length += 4 + case tagCopy2: + in := src[s : s+3] + offset = int(uint32(in[1]) | uint32(in[2])<<8) + length = 1 + int(in[0])>>2 + s += 3 + + case tagCopy4: + in := src[s : s+5] + offset = int(uint32(in[1]) | uint32(in[2])<<8 | uint32(in[3])<<16 | uint32(in[4])<<24) + length = 1 + int(in[0])>>2 + s += 5 + } + + if offset <= 0 || length > len(dst)-d { + if debugErrs { + fmt.Println("match error; offset:", offset, "length:", length, "dst-left:", len(dst)-d) + } + return decodeErrCodeCorrupt + } + + // copy from dict + if d < offset { + if d > MaxDictSrcOffset { + if debugErrs { + fmt.Println("dict after", MaxDictSrcOffset, "d:", d, "offset:", offset, "length:", length) + } + return decodeErrCodeCorrupt + } + startOff := len(dict.dict) - offset + d + if startOff < 0 || startOff+length > len(dict.dict) { + if debugErrs { + fmt.Printf("offset (%d) + length (%d) bigger than dict (%d)\n", offset, length, len(dict.dict)) + } + return decodeErrCodeCorrupt + } + if debug { + fmt.Println("dict copy, length:", length, "offset:", offset, "d-after:", d+length, "dict start offset:", startOff) + } + copy(dst[d:d+length], dict.dict[startOff:]) + d += length + continue + } + + if debug { + fmt.Println("copy, length:", length, "offset:", offset, "d-after:", d+length) + } + + // Copy from an earlier sub-slice of dst to a later sub-slice. + // If no overlap, use the built-in copy: + if offset > length { + copy(dst[d:d+length], dst[d-offset:]) + d += length + continue + } + + // Unlike the built-in copy function, this byte-by-byte copy always runs + // forwards, even if the slices overlap. Conceptually, this is: + // + // d += forwardCopy(dst[d:d+length], dst[d-offset:]) + // + // We align the slices into a and b and show the compiler they are the same size. + // This allows the loop to run without bounds checks. + a := dst[d : d+length] + b := dst[d-offset:] + b = b[:len(a)] + for i := range a { + a[i] = b[i] + } + d += length + } + + // Remaining with extra checks... + for s < len(src) { + switch src[s] & 0x03 { + case tagLiteral: + x := uint32(src[s] >> 2) + switch { + case x < 60: + s++ + case x == 60: + s += 2 + if uint(s) > uint(len(src)) { // The uint conversions catch overflow from the previous line. + if debugErrs { + fmt.Println("src went oob") + } + return decodeErrCodeCorrupt + } + x = uint32(src[s-1]) + case x == 61: + s += 3 + if uint(s) > uint(len(src)) { // The uint conversions catch overflow from the previous line. + if debugErrs { + fmt.Println("src went oob") + } + return decodeErrCodeCorrupt + } + x = uint32(src[s-2]) | uint32(src[s-1])<<8 + case x == 62: + s += 4 + if uint(s) > uint(len(src)) { // The uint conversions catch overflow from the previous line. + if debugErrs { + fmt.Println("src went oob") + } + return decodeErrCodeCorrupt + } + x = uint32(src[s-3]) | uint32(src[s-2])<<8 | uint32(src[s-1])<<16 + case x == 63: + s += 5 + if uint(s) > uint(len(src)) { // The uint conversions catch overflow from the previous line. + if debugErrs { + fmt.Println("src went oob") + } + return decodeErrCodeCorrupt + } + x = uint32(src[s-4]) | uint32(src[s-3])<<8 | uint32(src[s-2])<<16 | uint32(src[s-1])<<24 + } + length = int(x) + 1 + if length > len(dst)-d || length > len(src)-s || (strconv.IntSize == 32 && length <= 0) { + if debugErrs { + fmt.Println("corrupt literal: length:", length, "d-left:", len(dst)-d, "src-left:", len(src)-s) + } + return decodeErrCodeCorrupt + } + if debug { + fmt.Println("literals, length:", length, "d-after:", d+length) + } + + copy(dst[d:], src[s:s+length]) + d += length + s += length + continue + + case tagCopy1: + s += 2 + if uint(s) > uint(len(src)) { // The uint conversions catch overflow from the previous line. + if debugErrs { + fmt.Println("src went oob") + } + return decodeErrCodeCorrupt + } + length = int(src[s-2]) >> 2 & 0x7 + toffset := int(uint32(src[s-2])&0xe0<<3 | uint32(src[s-1])) + if toffset == 0 { + if debug { + fmt.Print("(repeat) ") + } + // keep last offset + switch length { + case 5: + s += 1 + if uint(s) > uint(len(src)) { // The uint conversions catch overflow from the previous line. + if debugErrs { + fmt.Println("src went oob") + } + return decodeErrCodeCorrupt + } + length = int(uint32(src[s-1])) + 4 + case 6: + s += 2 + if uint(s) > uint(len(src)) { // The uint conversions catch overflow from the previous line. + if debugErrs { + fmt.Println("src went oob") + } + return decodeErrCodeCorrupt + } + length = int(uint32(src[s-2])|(uint32(src[s-1])<<8)) + (1 << 8) + case 7: + s += 3 + if uint(s) > uint(len(src)) { // The uint conversions catch overflow from the previous line. + if debugErrs { + fmt.Println("src went oob") + } + return decodeErrCodeCorrupt + } + length = int(uint32(src[s-3])|(uint32(src[s-2])<<8)|(uint32(src[s-1])<<16)) + (1 << 16) + default: // 0-> 4 + } + } else { + offset = toffset + } + length += 4 + case tagCopy2: + s += 3 + if uint(s) > uint(len(src)) { // The uint conversions catch overflow from the previous line. + if debugErrs { + fmt.Println("src went oob") + } + return decodeErrCodeCorrupt + } + length = 1 + int(src[s-3])>>2 + offset = int(uint32(src[s-2]) | uint32(src[s-1])<<8) + + case tagCopy4: + s += 5 + if uint(s) > uint(len(src)) { // The uint conversions catch overflow from the previous line. + if debugErrs { + fmt.Println("src went oob") + } + return decodeErrCodeCorrupt + } + length = 1 + int(src[s-5])>>2 + offset = int(uint32(src[s-4]) | uint32(src[s-3])<<8 | uint32(src[s-2])<<16 | uint32(src[s-1])<<24) + } + + if offset <= 0 || length > len(dst)-d { + if debugErrs { + fmt.Println("match error; offset:", offset, "length:", length, "dst-left:", len(dst)-d) + } + return decodeErrCodeCorrupt + } + + // copy from dict + if d < offset { + if d > MaxDictSrcOffset { + if debugErrs { + fmt.Println("dict after", MaxDictSrcOffset, "d:", d, "offset:", offset, "length:", length) + } + return decodeErrCodeCorrupt + } + rOff := len(dict.dict) - (offset - d) + if debug { + fmt.Println("starting dict entry from dict offset", len(dict.dict)-rOff) + } + if rOff+length > len(dict.dict) { + if debugErrs { + fmt.Println("err: END offset", rOff+length, "bigger than dict", len(dict.dict), "dict offset:", rOff, "length:", length) + } + return decodeErrCodeCorrupt + } + if rOff < 0 { + if debugErrs { + fmt.Println("err: START offset", rOff, "less than 0", len(dict.dict), "dict offset:", rOff, "length:", length) + } + return decodeErrCodeCorrupt + } + copy(dst[d:d+length], dict.dict[rOff:]) + d += length + continue + } + + if debug { + fmt.Println("copy, length:", length, "offset:", offset, "d-after:", d+length) + } + + // Copy from an earlier sub-slice of dst to a later sub-slice. + // If no overlap, use the built-in copy: + if offset > length { + copy(dst[d:d+length], dst[d-offset:]) + d += length + continue + } + + // Unlike the built-in copy function, this byte-by-byte copy always runs + // forwards, even if the slices overlap. Conceptually, this is: + // + // d += forwardCopy(dst[d:d+length], dst[d-offset:]) + // + // We align the slices into a and b and show the compiler they are the same size. + // This allows the loop to run without bounds checks. + a := dst[d : d+length] + b := dst[d-offset:] + b = b[:len(a)] + for i := range a { + a[i] = b[i] + } + d += length + } + + if d != len(dst) { + if debugErrs { + fmt.Println("wanted length", len(dst), "got", d) + } + return decodeErrCodeCorrupt + } + return 0 +} diff --git a/vendor/github.com/klauspost/compress/s2/decode_amd64.s b/vendor/github.com/klauspost/compress/s2/decode_amd64.s new file mode 100644 index 000000000..1216df78f --- /dev/null +++ b/vendor/github.com/klauspost/compress/s2/decode_amd64.s @@ -0,0 +1,568 @@ +// Copyright 2016 The Go Authors. All rights reserved. +// Copyright (c) 2019 Klaus Post. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build !appengine +// +build gc +// +build !noasm + +#include "textflag.h" + +#define R_TMP0 AX +#define R_TMP1 BX +#define R_LEN CX +#define R_OFF DX +#define R_SRC SI +#define R_DST DI +#define R_DBASE R8 +#define R_DLEN R9 +#define R_DEND R10 +#define R_SBASE R11 +#define R_SLEN R12 +#define R_SEND R13 +#define R_TMP2 R14 +#define R_TMP3 R15 + +// The asm code generally follows the pure Go code in decode_other.go, except +// where marked with a "!!!". + +// func decode(dst, src []byte) int +// +// All local variables fit into registers. The non-zero stack size is only to +// spill registers and push args when issuing a CALL. The register allocation: +// - R_TMP0 scratch +// - R_TMP1 scratch +// - R_LEN length or x (shared) +// - R_OFF offset +// - R_SRC &src[s] +// - R_DST &dst[d] +// + R_DBASE dst_base +// + R_DLEN dst_len +// + R_DEND dst_base + dst_len +// + R_SBASE src_base +// + R_SLEN src_len +// + R_SEND src_base + src_len +// - R_TMP2 used by doCopy +// - R_TMP3 used by doCopy +// +// The registers R_DBASE-R_SEND (marked with a "+") are set at the start of the +// function, and after a CALL returns, and are not otherwise modified. +// +// The d variable is implicitly R_DST - R_DBASE, and len(dst)-d is R_DEND - R_DST. +// The s variable is implicitly R_SRC - R_SBASE, and len(src)-s is R_SEND - R_SRC. +TEXT ·s2Decode(SB), NOSPLIT, $56-56 + // Initialize R_SRC, R_DST and R_DBASE-R_SEND. + MOVQ dst_base+0(FP), R_DBASE + MOVQ dst_len+8(FP), R_DLEN + MOVQ R_DBASE, R_DST + MOVQ R_DBASE, R_DEND + ADDQ R_DLEN, R_DEND + MOVQ src_base+24(FP), R_SBASE + MOVQ src_len+32(FP), R_SLEN + MOVQ R_SBASE, R_SRC + MOVQ R_SBASE, R_SEND + ADDQ R_SLEN, R_SEND + XORQ R_OFF, R_OFF + +loop: + // for s < len(src) + CMPQ R_SRC, R_SEND + JEQ end + + // R_LEN = uint32(src[s]) + // + // switch src[s] & 0x03 + MOVBLZX (R_SRC), R_LEN + MOVL R_LEN, R_TMP1 + ANDL $3, R_TMP1 + CMPL R_TMP1, $1 + JAE tagCopy + + // ---------------------------------------- + // The code below handles literal tags. + + // case tagLiteral: + // x := uint32(src[s] >> 2) + // switch + SHRL $2, R_LEN + CMPL R_LEN, $60 + JAE tagLit60Plus + + // case x < 60: + // s++ + INCQ R_SRC + +doLit: + // This is the end of the inner "switch", when we have a literal tag. + // + // We assume that R_LEN == x and x fits in a uint32, where x is the variable + // used in the pure Go decode_other.go code. + + // length = int(x) + 1 + // + // Unlike the pure Go code, we don't need to check if length <= 0 because + // R_LEN can hold 64 bits, so the increment cannot overflow. + INCQ R_LEN + + // Prepare to check if copying length bytes will run past the end of dst or + // src. + // + // R_TMP0 = len(dst) - d + // R_TMP1 = len(src) - s + MOVQ R_DEND, R_TMP0 + SUBQ R_DST, R_TMP0 + MOVQ R_SEND, R_TMP1 + SUBQ R_SRC, R_TMP1 + + // !!! Try a faster technique for short (16 or fewer bytes) copies. + // + // if length > 16 || len(dst)-d < 16 || len(src)-s < 16 { + // goto callMemmove // Fall back on calling runtime·memmove. + // } + // + // The C++ snappy code calls this TryFastAppend. It also checks len(src)-s + // against 21 instead of 16, because it cannot assume that all of its input + // is contiguous in memory and so it needs to leave enough source bytes to + // read the next tag without refilling buffers, but Go's Decode assumes + // contiguousness (the src argument is a []byte). + CMPQ R_LEN, $16 + JGT callMemmove + CMPQ R_TMP0, $16 + JLT callMemmove + CMPQ R_TMP1, $16 + JLT callMemmove + + // !!! Implement the copy from src to dst as a 16-byte load and store. + // (Decode's documentation says that dst and src must not overlap.) + // + // This always copies 16 bytes, instead of only length bytes, but that's + // OK. If the input is a valid Snappy encoding then subsequent iterations + // will fix up the overrun. Otherwise, Decode returns a nil []byte (and a + // non-nil error), so the overrun will be ignored. + // + // Note that on amd64, it is legal and cheap to issue unaligned 8-byte or + // 16-byte loads and stores. This technique probably wouldn't be as + // effective on architectures that are fussier about alignment. + MOVOU 0(R_SRC), X0 + MOVOU X0, 0(R_DST) + + // d += length + // s += length + ADDQ R_LEN, R_DST + ADDQ R_LEN, R_SRC + JMP loop + +callMemmove: + // if length > len(dst)-d || length > len(src)-s { etc } + CMPQ R_LEN, R_TMP0 + JGT errCorrupt + CMPQ R_LEN, R_TMP1 + JGT errCorrupt + + // copy(dst[d:], src[s:s+length]) + // + // This means calling runtime·memmove(&dst[d], &src[s], length), so we push + // R_DST, R_SRC and R_LEN as arguments. Coincidentally, we also need to spill those + // three registers to the stack, to save local variables across the CALL. + MOVQ R_DST, 0(SP) + MOVQ R_SRC, 8(SP) + MOVQ R_LEN, 16(SP) + MOVQ R_DST, 24(SP) + MOVQ R_SRC, 32(SP) + MOVQ R_LEN, 40(SP) + MOVQ R_OFF, 48(SP) + CALL runtime·memmove(SB) + + // Restore local variables: unspill registers from the stack and + // re-calculate R_DBASE-R_SEND. + MOVQ 24(SP), R_DST + MOVQ 32(SP), R_SRC + MOVQ 40(SP), R_LEN + MOVQ 48(SP), R_OFF + MOVQ dst_base+0(FP), R_DBASE + MOVQ dst_len+8(FP), R_DLEN + MOVQ R_DBASE, R_DEND + ADDQ R_DLEN, R_DEND + MOVQ src_base+24(FP), R_SBASE + MOVQ src_len+32(FP), R_SLEN + MOVQ R_SBASE, R_SEND + ADDQ R_SLEN, R_SEND + + // d += length + // s += length + ADDQ R_LEN, R_DST + ADDQ R_LEN, R_SRC + JMP loop + +tagLit60Plus: + // !!! This fragment does the + // + // s += x - 58; if uint(s) > uint(len(src)) { etc } + // + // checks. In the asm version, we code it once instead of once per switch case. + ADDQ R_LEN, R_SRC + SUBQ $58, R_SRC + CMPQ R_SRC, R_SEND + JA errCorrupt + + // case x == 60: + CMPL R_LEN, $61 + JEQ tagLit61 + JA tagLit62Plus + + // x = uint32(src[s-1]) + MOVBLZX -1(R_SRC), R_LEN + JMP doLit + +tagLit61: + // case x == 61: + // x = uint32(src[s-2]) | uint32(src[s-1])<<8 + MOVWLZX -2(R_SRC), R_LEN + JMP doLit + +tagLit62Plus: + CMPL R_LEN, $62 + JA tagLit63 + + // case x == 62: + // x = uint32(src[s-3]) | uint32(src[s-2])<<8 | uint32(src[s-1])<<16 + // We read one byte, safe to read one back, since we are just reading tag. + // x = binary.LittleEndian.Uint32(src[s-1:]) >> 8 + MOVL -4(R_SRC), R_LEN + SHRL $8, R_LEN + JMP doLit + +tagLit63: + // case x == 63: + // x = uint32(src[s-4]) | uint32(src[s-3])<<8 | uint32(src[s-2])<<16 | uint32(src[s-1])<<24 + MOVL -4(R_SRC), R_LEN + JMP doLit + +// The code above handles literal tags. +// ---------------------------------------- +// The code below handles copy tags. + +tagCopy4: + // case tagCopy4: + // s += 5 + ADDQ $5, R_SRC + + // if uint(s) > uint(len(src)) { etc } + CMPQ R_SRC, R_SEND + JA errCorrupt + + // length = 1 + int(src[s-5])>>2 + SHRQ $2, R_LEN + INCQ R_LEN + + // offset = int(uint32(src[s-4]) | uint32(src[s-3])<<8 | uint32(src[s-2])<<16 | uint32(src[s-1])<<24) + MOVLQZX -4(R_SRC), R_OFF + JMP doCopy + +tagCopy2: + // case tagCopy2: + // s += 3 + ADDQ $3, R_SRC + + // if uint(s) > uint(len(src)) { etc } + CMPQ R_SRC, R_SEND + JA errCorrupt + + // length = 1 + int(src[s-3])>>2 + SHRQ $2, R_LEN + INCQ R_LEN + + // offset = int(uint32(src[s-2]) | uint32(src[s-1])<<8) + MOVWQZX -2(R_SRC), R_OFF + JMP doCopy + +tagCopy: + // We have a copy tag. We assume that: + // - R_TMP1 == src[s] & 0x03 + // - R_LEN == src[s] + CMPQ R_TMP1, $2 + JEQ tagCopy2 + JA tagCopy4 + + // case tagCopy1: + // s += 2 + ADDQ $2, R_SRC + + // if uint(s) > uint(len(src)) { etc } + CMPQ R_SRC, R_SEND + JA errCorrupt + + // offset = int(uint32(src[s-2])&0xe0<<3 | uint32(src[s-1])) + // length = 4 + int(src[s-2])>>2&0x7 + MOVBQZX -1(R_SRC), R_TMP1 + MOVQ R_LEN, R_TMP0 + SHRQ $2, R_LEN + ANDQ $0xe0, R_TMP0 + ANDQ $7, R_LEN + SHLQ $3, R_TMP0 + ADDQ $4, R_LEN + ORQ R_TMP1, R_TMP0 + + // check if repeat code, ZF set by ORQ. + JZ repeatCode + + // This is a regular copy, transfer our temporary value to R_OFF (length) + MOVQ R_TMP0, R_OFF + JMP doCopy + +// This is a repeat code. +repeatCode: + // If length < 9, reuse last offset, with the length already calculated. + CMPQ R_LEN, $9 + JL doCopyRepeat + + // Read additional bytes for length. + JE repeatLen1 + + // Rare, so the extra branch shouldn't hurt too much. + CMPQ R_LEN, $10 + JE repeatLen2 + JMP repeatLen3 + +// Read repeat lengths. +repeatLen1: + // s ++ + ADDQ $1, R_SRC + + // if uint(s) > uint(len(src)) { etc } + CMPQ R_SRC, R_SEND + JA errCorrupt + + // length = src[s-1] + 8 + MOVBQZX -1(R_SRC), R_LEN + ADDL $8, R_LEN + JMP doCopyRepeat + +repeatLen2: + // s +=2 + ADDQ $2, R_SRC + + // if uint(s) > uint(len(src)) { etc } + CMPQ R_SRC, R_SEND + JA errCorrupt + + // length = uint32(src[s-2]) | (uint32(src[s-1])<<8) + (1 << 8) + MOVWQZX -2(R_SRC), R_LEN + ADDL $260, R_LEN + JMP doCopyRepeat + +repeatLen3: + // s +=3 + ADDQ $3, R_SRC + + // if uint(s) > uint(len(src)) { etc } + CMPQ R_SRC, R_SEND + JA errCorrupt + + // length = uint32(src[s-3]) | (uint32(src[s-2])<<8) | (uint32(src[s-1])<<16) + (1 << 16) + // Read one byte further back (just part of the tag, shifted out) + MOVL -4(R_SRC), R_LEN + SHRL $8, R_LEN + ADDL $65540, R_LEN + JMP doCopyRepeat + +doCopy: + // This is the end of the outer "switch", when we have a copy tag. + // + // We assume that: + // - R_LEN == length && R_LEN > 0 + // - R_OFF == offset + + // if d < offset { etc } + MOVQ R_DST, R_TMP1 + SUBQ R_DBASE, R_TMP1 + CMPQ R_TMP1, R_OFF + JLT errCorrupt + + // Repeat values can skip the test above, since any offset > 0 will be in dst. +doCopyRepeat: + // if offset <= 0 { etc } + CMPQ R_OFF, $0 + JLE errCorrupt + + // if length > len(dst)-d { etc } + MOVQ R_DEND, R_TMP1 + SUBQ R_DST, R_TMP1 + CMPQ R_LEN, R_TMP1 + JGT errCorrupt + + // forwardCopy(dst[d:d+length], dst[d-offset:]); d += length + // + // Set: + // - R_TMP2 = len(dst)-d + // - R_TMP3 = &dst[d-offset] + MOVQ R_DEND, R_TMP2 + SUBQ R_DST, R_TMP2 + MOVQ R_DST, R_TMP3 + SUBQ R_OFF, R_TMP3 + + // !!! Try a faster technique for short (16 or fewer bytes) forward copies. + // + // First, try using two 8-byte load/stores, similar to the doLit technique + // above. Even if dst[d:d+length] and dst[d-offset:] can overlap, this is + // still OK if offset >= 8. Note that this has to be two 8-byte load/stores + // and not one 16-byte load/store, and the first store has to be before the + // second load, due to the overlap if offset is in the range [8, 16). + // + // if length > 16 || offset < 8 || len(dst)-d < 16 { + // goto slowForwardCopy + // } + // copy 16 bytes + // d += length + CMPQ R_LEN, $16 + JGT slowForwardCopy + CMPQ R_OFF, $8 + JLT slowForwardCopy + CMPQ R_TMP2, $16 + JLT slowForwardCopy + MOVQ 0(R_TMP3), R_TMP0 + MOVQ R_TMP0, 0(R_DST) + MOVQ 8(R_TMP3), R_TMP1 + MOVQ R_TMP1, 8(R_DST) + ADDQ R_LEN, R_DST + JMP loop + +slowForwardCopy: + // !!! If the forward copy is longer than 16 bytes, or if offset < 8, we + // can still try 8-byte load stores, provided we can overrun up to 10 extra + // bytes. As above, the overrun will be fixed up by subsequent iterations + // of the outermost loop. + // + // The C++ snappy code calls this technique IncrementalCopyFastPath. Its + // commentary says: + // + // ---- + // + // The main part of this loop is a simple copy of eight bytes at a time + // until we've copied (at least) the requested amount of bytes. However, + // if d and d-offset are less than eight bytes apart (indicating a + // repeating pattern of length < 8), we first need to expand the pattern in + // order to get the correct results. For instance, if the buffer looks like + // this, with the eight-byte and patterns marked as + // intervals: + // + // abxxxxxxxxxxxx + // [------] d-offset + // [------] d + // + // a single eight-byte copy from to will repeat the pattern + // once, after which we can move two bytes without moving : + // + // ababxxxxxxxxxx + // [------] d-offset + // [------] d + // + // and repeat the exercise until the two no longer overlap. + // + // This allows us to do very well in the special case of one single byte + // repeated many times, without taking a big hit for more general cases. + // + // The worst case of extra writing past the end of the match occurs when + // offset == 1 and length == 1; the last copy will read from byte positions + // [0..7] and write to [4..11], whereas it was only supposed to write to + // position 1. Thus, ten excess bytes. + // + // ---- + // + // That "10 byte overrun" worst case is confirmed by Go's + // TestSlowForwardCopyOverrun, which also tests the fixUpSlowForwardCopy + // and finishSlowForwardCopy algorithm. + // + // if length > len(dst)-d-10 { + // goto verySlowForwardCopy + // } + SUBQ $10, R_TMP2 + CMPQ R_LEN, R_TMP2 + JGT verySlowForwardCopy + + // We want to keep the offset, so we use R_TMP2 from here. + MOVQ R_OFF, R_TMP2 + +makeOffsetAtLeast8: + // !!! As above, expand the pattern so that offset >= 8 and we can use + // 8-byte load/stores. + // + // for offset < 8 { + // copy 8 bytes from dst[d-offset:] to dst[d:] + // length -= offset + // d += offset + // offset += offset + // // The two previous lines together means that d-offset, and therefore + // // R_TMP3, is unchanged. + // } + CMPQ R_TMP2, $8 + JGE fixUpSlowForwardCopy + MOVQ (R_TMP3), R_TMP1 + MOVQ R_TMP1, (R_DST) + SUBQ R_TMP2, R_LEN + ADDQ R_TMP2, R_DST + ADDQ R_TMP2, R_TMP2 + JMP makeOffsetAtLeast8 + +fixUpSlowForwardCopy: + // !!! Add length (which might be negative now) to d (implied by R_DST being + // &dst[d]) so that d ends up at the right place when we jump back to the + // top of the loop. Before we do that, though, we save R_DST to R_TMP0 so that, if + // length is positive, copying the remaining length bytes will write to the + // right place. + MOVQ R_DST, R_TMP0 + ADDQ R_LEN, R_DST + +finishSlowForwardCopy: + // !!! Repeat 8-byte load/stores until length <= 0. Ending with a negative + // length means that we overrun, but as above, that will be fixed up by + // subsequent iterations of the outermost loop. + CMPQ R_LEN, $0 + JLE loop + MOVQ (R_TMP3), R_TMP1 + MOVQ R_TMP1, (R_TMP0) + ADDQ $8, R_TMP3 + ADDQ $8, R_TMP0 + SUBQ $8, R_LEN + JMP finishSlowForwardCopy + +verySlowForwardCopy: + // verySlowForwardCopy is a simple implementation of forward copy. In C + // parlance, this is a do/while loop instead of a while loop, since we know + // that length > 0. In Go syntax: + // + // for { + // dst[d] = dst[d - offset] + // d++ + // length-- + // if length == 0 { + // break + // } + // } + MOVB (R_TMP3), R_TMP1 + MOVB R_TMP1, (R_DST) + INCQ R_TMP3 + INCQ R_DST + DECQ R_LEN + JNZ verySlowForwardCopy + JMP loop + +// The code above handles copy tags. +// ---------------------------------------- + +end: + // This is the end of the "for s < len(src)". + // + // if d != len(dst) { etc } + CMPQ R_DST, R_DEND + JNE errCorrupt + + // return 0 + MOVQ $0, ret+48(FP) + RET + +errCorrupt: + // return decodeErrCodeCorrupt + MOVQ $1, ret+48(FP) + RET diff --git a/vendor/github.com/klauspost/compress/s2/decode_arm64.s b/vendor/github.com/klauspost/compress/s2/decode_arm64.s new file mode 100644 index 000000000..78e463f34 --- /dev/null +++ b/vendor/github.com/klauspost/compress/s2/decode_arm64.s @@ -0,0 +1,574 @@ +// Copyright 2020 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build !appengine +// +build gc +// +build !noasm + +#include "textflag.h" + +#define R_TMP0 R2 +#define R_TMP1 R3 +#define R_LEN R4 +#define R_OFF R5 +#define R_SRC R6 +#define R_DST R7 +#define R_DBASE R8 +#define R_DLEN R9 +#define R_DEND R10 +#define R_SBASE R11 +#define R_SLEN R12 +#define R_SEND R13 +#define R_TMP2 R14 +#define R_TMP3 R15 + +// TEST_SRC will check if R_SRC is <= SRC_END +#define TEST_SRC() \ + CMP R_SEND, R_SRC \ + BGT errCorrupt + +// MOVD R_SRC, R_TMP1 +// SUB R_SBASE, R_TMP1, R_TMP1 +// CMP R_SLEN, R_TMP1 +// BGT errCorrupt + +// The asm code generally follows the pure Go code in decode_other.go, except +// where marked with a "!!!". + +// func decode(dst, src []byte) int +// +// All local variables fit into registers. The non-zero stack size is only to +// spill registers and push args when issuing a CALL. The register allocation: +// - R_TMP0 scratch +// - R_TMP1 scratch +// - R_LEN length or x +// - R_OFF offset +// - R_SRC &src[s] +// - R_DST &dst[d] +// + R_DBASE dst_base +// + R_DLEN dst_len +// + R_DEND dst_base + dst_len +// + R_SBASE src_base +// + R_SLEN src_len +// + R_SEND src_base + src_len +// - R_TMP2 used by doCopy +// - R_TMP3 used by doCopy +// +// The registers R_DBASE-R_SEND (marked with a "+") are set at the start of the +// function, and after a CALL returns, and are not otherwise modified. +// +// The d variable is implicitly R_DST - R_DBASE, and len(dst)-d is R_DEND - R_DST. +// The s variable is implicitly R_SRC - R_SBASE, and len(src)-s is R_SEND - R_SRC. +TEXT ·s2Decode(SB), NOSPLIT, $56-56 + // Initialize R_SRC, R_DST and R_DBASE-R_SEND. + MOVD dst_base+0(FP), R_DBASE + MOVD dst_len+8(FP), R_DLEN + MOVD R_DBASE, R_DST + MOVD R_DBASE, R_DEND + ADD R_DLEN, R_DEND, R_DEND + MOVD src_base+24(FP), R_SBASE + MOVD src_len+32(FP), R_SLEN + MOVD R_SBASE, R_SRC + MOVD R_SBASE, R_SEND + ADD R_SLEN, R_SEND, R_SEND + MOVD $0, R_OFF + +loop: + // for s < len(src) + CMP R_SEND, R_SRC + BEQ end + + // R_LEN = uint32(src[s]) + // + // switch src[s] & 0x03 + MOVBU (R_SRC), R_LEN + MOVW R_LEN, R_TMP1 + ANDW $3, R_TMP1 + MOVW $1, R1 + CMPW R1, R_TMP1 + BGE tagCopy + + // ---------------------------------------- + // The code below handles literal tags. + + // case tagLiteral: + // x := uint32(src[s] >> 2) + // switch + MOVW $60, R1 + LSRW $2, R_LEN, R_LEN + CMPW R_LEN, R1 + BLS tagLit60Plus + + // case x < 60: + // s++ + ADD $1, R_SRC, R_SRC + +doLit: + // This is the end of the inner "switch", when we have a literal tag. + // + // We assume that R_LEN == x and x fits in a uint32, where x is the variable + // used in the pure Go decode_other.go code. + + // length = int(x) + 1 + // + // Unlike the pure Go code, we don't need to check if length <= 0 because + // R_LEN can hold 64 bits, so the increment cannot overflow. + ADD $1, R_LEN, R_LEN + + // Prepare to check if copying length bytes will run past the end of dst or + // src. + // + // R_TMP0 = len(dst) - d + // R_TMP1 = len(src) - s + MOVD R_DEND, R_TMP0 + SUB R_DST, R_TMP0, R_TMP0 + MOVD R_SEND, R_TMP1 + SUB R_SRC, R_TMP1, R_TMP1 + + // !!! Try a faster technique for short (16 or fewer bytes) copies. + // + // if length > 16 || len(dst)-d < 16 || len(src)-s < 16 { + // goto callMemmove // Fall back on calling runtime·memmove. + // } + // + // The C++ snappy code calls this TryFastAppend. It also checks len(src)-s + // against 21 instead of 16, because it cannot assume that all of its input + // is contiguous in memory and so it needs to leave enough source bytes to + // read the next tag without refilling buffers, but Go's Decode assumes + // contiguousness (the src argument is a []byte). + CMP $16, R_LEN + BGT callMemmove + CMP $16, R_TMP0 + BLT callMemmove + CMP $16, R_TMP1 + BLT callMemmove + + // !!! Implement the copy from src to dst as a 16-byte load and store. + // (Decode's documentation says that dst and src must not overlap.) + // + // This always copies 16 bytes, instead of only length bytes, but that's + // OK. If the input is a valid Snappy encoding then subsequent iterations + // will fix up the overrun. Otherwise, Decode returns a nil []byte (and a + // non-nil error), so the overrun will be ignored. + // + // Note that on arm64, it is legal and cheap to issue unaligned 8-byte or + // 16-byte loads and stores. This technique probably wouldn't be as + // effective on architectures that are fussier about alignment. + LDP 0(R_SRC), (R_TMP2, R_TMP3) + STP (R_TMP2, R_TMP3), 0(R_DST) + + // d += length + // s += length + ADD R_LEN, R_DST, R_DST + ADD R_LEN, R_SRC, R_SRC + B loop + +callMemmove: + // if length > len(dst)-d || length > len(src)-s { etc } + CMP R_TMP0, R_LEN + BGT errCorrupt + CMP R_TMP1, R_LEN + BGT errCorrupt + + // copy(dst[d:], src[s:s+length]) + // + // This means calling runtime·memmove(&dst[d], &src[s], length), so we push + // R_DST, R_SRC and R_LEN as arguments. Coincidentally, we also need to spill those + // three registers to the stack, to save local variables across the CALL. + MOVD R_DST, 8(RSP) + MOVD R_SRC, 16(RSP) + MOVD R_LEN, 24(RSP) + MOVD R_DST, 32(RSP) + MOVD R_SRC, 40(RSP) + MOVD R_LEN, 48(RSP) + MOVD R_OFF, 56(RSP) + CALL runtime·memmove(SB) + + // Restore local variables: unspill registers from the stack and + // re-calculate R_DBASE-R_SEND. + MOVD 32(RSP), R_DST + MOVD 40(RSP), R_SRC + MOVD 48(RSP), R_LEN + MOVD 56(RSP), R_OFF + MOVD dst_base+0(FP), R_DBASE + MOVD dst_len+8(FP), R_DLEN + MOVD R_DBASE, R_DEND + ADD R_DLEN, R_DEND, R_DEND + MOVD src_base+24(FP), R_SBASE + MOVD src_len+32(FP), R_SLEN + MOVD R_SBASE, R_SEND + ADD R_SLEN, R_SEND, R_SEND + + // d += length + // s += length + ADD R_LEN, R_DST, R_DST + ADD R_LEN, R_SRC, R_SRC + B loop + +tagLit60Plus: + // !!! This fragment does the + // + // s += x - 58; if uint(s) > uint(len(src)) { etc } + // + // checks. In the asm version, we code it once instead of once per switch case. + ADD R_LEN, R_SRC, R_SRC + SUB $58, R_SRC, R_SRC + TEST_SRC() + + // case x == 60: + MOVW $61, R1 + CMPW R1, R_LEN + BEQ tagLit61 + BGT tagLit62Plus + + // x = uint32(src[s-1]) + MOVBU -1(R_SRC), R_LEN + B doLit + +tagLit61: + // case x == 61: + // x = uint32(src[s-2]) | uint32(src[s-1])<<8 + MOVHU -2(R_SRC), R_LEN + B doLit + +tagLit62Plus: + CMPW $62, R_LEN + BHI tagLit63 + + // case x == 62: + // x = uint32(src[s-3]) | uint32(src[s-2])<<8 | uint32(src[s-1])<<16 + MOVHU -3(R_SRC), R_LEN + MOVBU -1(R_SRC), R_TMP1 + ORR R_TMP1<<16, R_LEN + B doLit + +tagLit63: + // case x == 63: + // x = uint32(src[s-4]) | uint32(src[s-3])<<8 | uint32(src[s-2])<<16 | uint32(src[s-1])<<24 + MOVWU -4(R_SRC), R_LEN + B doLit + + // The code above handles literal tags. + // ---------------------------------------- + // The code below handles copy tags. + +tagCopy4: + // case tagCopy4: + // s += 5 + ADD $5, R_SRC, R_SRC + + // if uint(s) > uint(len(src)) { etc } + MOVD R_SRC, R_TMP1 + SUB R_SBASE, R_TMP1, R_TMP1 + CMP R_SLEN, R_TMP1 + BGT errCorrupt + + // length = 1 + int(src[s-5])>>2 + MOVD $1, R1 + ADD R_LEN>>2, R1, R_LEN + + // offset = int(uint32(src[s-4]) | uint32(src[s-3])<<8 | uint32(src[s-2])<<16 | uint32(src[s-1])<<24) + MOVWU -4(R_SRC), R_OFF + B doCopy + +tagCopy2: + // case tagCopy2: + // s += 3 + ADD $3, R_SRC, R_SRC + + // if uint(s) > uint(len(src)) { etc } + TEST_SRC() + + // length = 1 + int(src[s-3])>>2 + MOVD $1, R1 + ADD R_LEN>>2, R1, R_LEN + + // offset = int(uint32(src[s-2]) | uint32(src[s-1])<<8) + MOVHU -2(R_SRC), R_OFF + B doCopy + +tagCopy: + // We have a copy tag. We assume that: + // - R_TMP1 == src[s] & 0x03 + // - R_LEN == src[s] + CMP $2, R_TMP1 + BEQ tagCopy2 + BGT tagCopy4 + + // case tagCopy1: + // s += 2 + ADD $2, R_SRC, R_SRC + + // if uint(s) > uint(len(src)) { etc } + TEST_SRC() + + // offset = int(uint32(src[s-2])&0xe0<<3 | uint32(src[s-1])) + // Calculate offset in R_TMP0 in case it is a repeat. + MOVD R_LEN, R_TMP0 + AND $0xe0, R_TMP0 + MOVBU -1(R_SRC), R_TMP1 + ORR R_TMP0<<3, R_TMP1, R_TMP0 + + // length = 4 + int(src[s-2])>>2&0x7 + MOVD $7, R1 + AND R_LEN>>2, R1, R_LEN + ADD $4, R_LEN, R_LEN + + // check if repeat code with offset 0. + CMP $0, R_TMP0 + BEQ repeatCode + + // This is a regular copy, transfer our temporary value to R_OFF (offset) + MOVD R_TMP0, R_OFF + B doCopy + + // This is a repeat code. +repeatCode: + // If length < 9, reuse last offset, with the length already calculated. + CMP $9, R_LEN + BLT doCopyRepeat + BEQ repeatLen1 + CMP $10, R_LEN + BEQ repeatLen2 + +repeatLen3: + // s +=3 + ADD $3, R_SRC, R_SRC + + // if uint(s) > uint(len(src)) { etc } + TEST_SRC() + + // length = uint32(src[s-3]) | (uint32(src[s-2])<<8) | (uint32(src[s-1])<<16) + 65540 + MOVBU -1(R_SRC), R_TMP0 + MOVHU -3(R_SRC), R_LEN + ORR R_TMP0<<16, R_LEN, R_LEN + ADD $65540, R_LEN, R_LEN + B doCopyRepeat + +repeatLen2: + // s +=2 + ADD $2, R_SRC, R_SRC + + // if uint(s) > uint(len(src)) { etc } + TEST_SRC() + + // length = uint32(src[s-2]) | (uint32(src[s-1])<<8) + 260 + MOVHU -2(R_SRC), R_LEN + ADD $260, R_LEN, R_LEN + B doCopyRepeat + +repeatLen1: + // s +=1 + ADD $1, R_SRC, R_SRC + + // if uint(s) > uint(len(src)) { etc } + TEST_SRC() + + // length = src[s-1] + 8 + MOVBU -1(R_SRC), R_LEN + ADD $8, R_LEN, R_LEN + B doCopyRepeat + +doCopy: + // This is the end of the outer "switch", when we have a copy tag. + // + // We assume that: + // - R_LEN == length && R_LEN > 0 + // - R_OFF == offset + + // if d < offset { etc } + MOVD R_DST, R_TMP1 + SUB R_DBASE, R_TMP1, R_TMP1 + CMP R_OFF, R_TMP1 + BLT errCorrupt + + // Repeat values can skip the test above, since any offset > 0 will be in dst. +doCopyRepeat: + + // if offset <= 0 { etc } + CMP $0, R_OFF + BLE errCorrupt + + // if length > len(dst)-d { etc } + MOVD R_DEND, R_TMP1 + SUB R_DST, R_TMP1, R_TMP1 + CMP R_TMP1, R_LEN + BGT errCorrupt + + // forwardCopy(dst[d:d+length], dst[d-offset:]); d += length + // + // Set: + // - R_TMP2 = len(dst)-d + // - R_TMP3 = &dst[d-offset] + MOVD R_DEND, R_TMP2 + SUB R_DST, R_TMP2, R_TMP2 + MOVD R_DST, R_TMP3 + SUB R_OFF, R_TMP3, R_TMP3 + + // !!! Try a faster technique for short (16 or fewer bytes) forward copies. + // + // First, try using two 8-byte load/stores, similar to the doLit technique + // above. Even if dst[d:d+length] and dst[d-offset:] can overlap, this is + // still OK if offset >= 8. Note that this has to be two 8-byte load/stores + // and not one 16-byte load/store, and the first store has to be before the + // second load, due to the overlap if offset is in the range [8, 16). + // + // if length > 16 || offset < 8 || len(dst)-d < 16 { + // goto slowForwardCopy + // } + // copy 16 bytes + // d += length + CMP $16, R_LEN + BGT slowForwardCopy + CMP $8, R_OFF + BLT slowForwardCopy + CMP $16, R_TMP2 + BLT slowForwardCopy + MOVD 0(R_TMP3), R_TMP0 + MOVD R_TMP0, 0(R_DST) + MOVD 8(R_TMP3), R_TMP1 + MOVD R_TMP1, 8(R_DST) + ADD R_LEN, R_DST, R_DST + B loop + +slowForwardCopy: + // !!! If the forward copy is longer than 16 bytes, or if offset < 8, we + // can still try 8-byte load stores, provided we can overrun up to 10 extra + // bytes. As above, the overrun will be fixed up by subsequent iterations + // of the outermost loop. + // + // The C++ snappy code calls this technique IncrementalCopyFastPath. Its + // commentary says: + // + // ---- + // + // The main part of this loop is a simple copy of eight bytes at a time + // until we've copied (at least) the requested amount of bytes. However, + // if d and d-offset are less than eight bytes apart (indicating a + // repeating pattern of length < 8), we first need to expand the pattern in + // order to get the correct results. For instance, if the buffer looks like + // this, with the eight-byte and patterns marked as + // intervals: + // + // abxxxxxxxxxxxx + // [------] d-offset + // [------] d + // + // a single eight-byte copy from to will repeat the pattern + // once, after which we can move two bytes without moving : + // + // ababxxxxxxxxxx + // [------] d-offset + // [------] d + // + // and repeat the exercise until the two no longer overlap. + // + // This allows us to do very well in the special case of one single byte + // repeated many times, without taking a big hit for more general cases. + // + // The worst case of extra writing past the end of the match occurs when + // offset == 1 and length == 1; the last copy will read from byte positions + // [0..7] and write to [4..11], whereas it was only supposed to write to + // position 1. Thus, ten excess bytes. + // + // ---- + // + // That "10 byte overrun" worst case is confirmed by Go's + // TestSlowForwardCopyOverrun, which also tests the fixUpSlowForwardCopy + // and finishSlowForwardCopy algorithm. + // + // if length > len(dst)-d-10 { + // goto verySlowForwardCopy + // } + SUB $10, R_TMP2, R_TMP2 + CMP R_TMP2, R_LEN + BGT verySlowForwardCopy + + // We want to keep the offset, so we use R_TMP2 from here. + MOVD R_OFF, R_TMP2 + +makeOffsetAtLeast8: + // !!! As above, expand the pattern so that offset >= 8 and we can use + // 8-byte load/stores. + // + // for offset < 8 { + // copy 8 bytes from dst[d-offset:] to dst[d:] + // length -= offset + // d += offset + // offset += offset + // // The two previous lines together means that d-offset, and therefore + // // R_TMP3, is unchanged. + // } + CMP $8, R_TMP2 + BGE fixUpSlowForwardCopy + MOVD (R_TMP3), R_TMP1 + MOVD R_TMP1, (R_DST) + SUB R_TMP2, R_LEN, R_LEN + ADD R_TMP2, R_DST, R_DST + ADD R_TMP2, R_TMP2, R_TMP2 + B makeOffsetAtLeast8 + +fixUpSlowForwardCopy: + // !!! Add length (which might be negative now) to d (implied by R_DST being + // &dst[d]) so that d ends up at the right place when we jump back to the + // top of the loop. Before we do that, though, we save R_DST to R_TMP0 so that, if + // length is positive, copying the remaining length bytes will write to the + // right place. + MOVD R_DST, R_TMP0 + ADD R_LEN, R_DST, R_DST + +finishSlowForwardCopy: + // !!! Repeat 8-byte load/stores until length <= 0. Ending with a negative + // length means that we overrun, but as above, that will be fixed up by + // subsequent iterations of the outermost loop. + MOVD $0, R1 + CMP R1, R_LEN + BLE loop + MOVD (R_TMP3), R_TMP1 + MOVD R_TMP1, (R_TMP0) + ADD $8, R_TMP3, R_TMP3 + ADD $8, R_TMP0, R_TMP0 + SUB $8, R_LEN, R_LEN + B finishSlowForwardCopy + +verySlowForwardCopy: + // verySlowForwardCopy is a simple implementation of forward copy. In C + // parlance, this is a do/while loop instead of a while loop, since we know + // that length > 0. In Go syntax: + // + // for { + // dst[d] = dst[d - offset] + // d++ + // length-- + // if length == 0 { + // break + // } + // } + MOVB (R_TMP3), R_TMP1 + MOVB R_TMP1, (R_DST) + ADD $1, R_TMP3, R_TMP3 + ADD $1, R_DST, R_DST + SUB $1, R_LEN, R_LEN + CBNZ R_LEN, verySlowForwardCopy + B loop + + // The code above handles copy tags. + // ---------------------------------------- + +end: + // This is the end of the "for s < len(src)". + // + // if d != len(dst) { etc } + CMP R_DEND, R_DST + BNE errCorrupt + + // return 0 + MOVD $0, ret+48(FP) + RET + +errCorrupt: + // return decodeErrCodeCorrupt + MOVD $1, R_TMP0 + MOVD R_TMP0, ret+48(FP) + RET diff --git a/vendor/github.com/klauspost/compress/s2/decode_asm.go b/vendor/github.com/klauspost/compress/s2/decode_asm.go new file mode 100644 index 000000000..cb3576edd --- /dev/null +++ b/vendor/github.com/klauspost/compress/s2/decode_asm.go @@ -0,0 +1,17 @@ +// Copyright 2016 The Snappy-Go Authors. All rights reserved. +// Copyright (c) 2019 Klaus Post. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build (amd64 || arm64) && !appengine && gc && !noasm +// +build amd64 arm64 +// +build !appengine +// +build gc +// +build !noasm + +package s2 + +// decode has the same semantics as in decode_other.go. +// +//go:noescape +func s2Decode(dst, src []byte) int diff --git a/vendor/github.com/klauspost/compress/s2/decode_other.go b/vendor/github.com/klauspost/compress/s2/decode_other.go new file mode 100644 index 000000000..2905ba277 --- /dev/null +++ b/vendor/github.com/klauspost/compress/s2/decode_other.go @@ -0,0 +1,287 @@ +// Copyright 2016 The Snappy-Go Authors. All rights reserved. +// Copyright (c) 2019 Klaus Post. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build (!amd64 && !arm64) || appengine || !gc || noasm + +package s2 + +import ( + "fmt" + "strconv" + + "github.com/klauspost/compress/internal/le" +) + +// decode writes the decoding of src to dst. It assumes that the varint-encoded +// length of the decompressed bytes has already been read, and that len(dst) +// equals that length. +// +// It returns 0 on success or a decodeErrCodeXxx error code on failure. +func s2Decode(dst, src []byte) int { + const debug = false + if debug { + fmt.Println("Starting decode, dst len:", len(dst)) + } + var d, s, length int + offset := 0 + + // As long as we can read at least 5 bytes... + for s < len(src)-5 { + // Removing bounds checks is SLOWER, when if doing + // in := src[s:s+5] + // Checked on Go 1.18 + switch src[s] & 0x03 { + case tagLiteral: + x := uint32(src[s] >> 2) + switch { + case x < 60: + s++ + case x == 60: + x = uint32(src[s+1]) + s += 2 + case x == 61: + x = uint32(le.Load16(src, s+1)) + s += 3 + case x == 62: + // Load as 32 bit and shift down. + x = le.Load32(src, s) + x >>= 8 + s += 4 + case x == 63: + x = le.Load32(src, s+1) + s += 5 + } + length = int(x) + 1 + if length > len(dst)-d || length > len(src)-s || (strconv.IntSize == 32 && length <= 0) { + if debug { + fmt.Println("corrupt: lit size", length) + } + return decodeErrCodeCorrupt + } + if debug { + fmt.Println("literals, length:", length, "d-after:", d+length) + } + + copy(dst[d:], src[s:s+length]) + d += length + s += length + continue + + case tagCopy1: + s += 2 + toffset := int(uint32(src[s-2])&0xe0<<3 | uint32(src[s-1])) + length = int(src[s-2]) >> 2 & 0x7 + if toffset == 0 { + if debug { + fmt.Print("(repeat) ") + } + // keep last offset + switch length { + case 5: + length = int(src[s]) + 4 + s += 1 + case 6: + length = int(le.Load16(src, s)) + 1<<8 + s += 2 + case 7: + in := src[s : s+3] + length = int((uint32(in[2])<<16)|(uint32(in[1])<<8)|uint32(in[0])) + (1 << 16) + s += 3 + default: // 0-> 4 + } + } else { + offset = toffset + } + length += 4 + case tagCopy2: + offset = int(le.Load16(src, s+1)) + length = 1 + int(src[s])>>2 + s += 3 + + case tagCopy4: + offset = int(le.Load32(src, s+1)) + length = 1 + int(src[s])>>2 + s += 5 + } + + if offset <= 0 || d < offset || length > len(dst)-d { + if debug { + fmt.Println("corrupt: match, length", length, "offset:", offset, "dst avail:", len(dst)-d, "dst pos:", d) + } + + return decodeErrCodeCorrupt + } + + if debug { + fmt.Println("copy, length:", length, "offset:", offset, "d-after:", d+length) + } + + // Copy from an earlier sub-slice of dst to a later sub-slice. + // If no overlap, use the built-in copy: + if offset > length { + copy(dst[d:d+length], dst[d-offset:]) + d += length + continue + } + + // Unlike the built-in copy function, this byte-by-byte copy always runs + // forwards, even if the slices overlap. Conceptually, this is: + // + // d += forwardCopy(dst[d:d+length], dst[d-offset:]) + // + // We align the slices into a and b and show the compiler they are the same size. + // This allows the loop to run without bounds checks. + a := dst[d : d+length] + b := dst[d-offset:] + b = b[:len(a)] + for i := range a { + a[i] = b[i] + } + d += length + } + + // Remaining with extra checks... + for s < len(src) { + switch src[s] & 0x03 { + case tagLiteral: + x := uint32(src[s] >> 2) + switch { + case x < 60: + s++ + case x == 60: + s += 2 + if uint(s) > uint(len(src)) { // The uint conversions catch overflow from the previous line. + return decodeErrCodeCorrupt + } + x = uint32(src[s-1]) + case x == 61: + s += 3 + if uint(s) > uint(len(src)) { // The uint conversions catch overflow from the previous line. + return decodeErrCodeCorrupt + } + x = uint32(src[s-2]) | uint32(src[s-1])<<8 + case x == 62: + s += 4 + if uint(s) > uint(len(src)) { // The uint conversions catch overflow from the previous line. + return decodeErrCodeCorrupt + } + x = uint32(src[s-3]) | uint32(src[s-2])<<8 | uint32(src[s-1])<<16 + case x == 63: + s += 5 + if uint(s) > uint(len(src)) { // The uint conversions catch overflow from the previous line. + return decodeErrCodeCorrupt + } + x = uint32(src[s-4]) | uint32(src[s-3])<<8 | uint32(src[s-2])<<16 | uint32(src[s-1])<<24 + } + length = int(x) + 1 + if length > len(dst)-d || length > len(src)-s || (strconv.IntSize == 32 && length <= 0) { + if debug { + fmt.Println("corrupt: lit size", length) + } + return decodeErrCodeCorrupt + } + if debug { + fmt.Println("literals, length:", length, "d-after:", d+length) + } + + copy(dst[d:], src[s:s+length]) + d += length + s += length + continue + + case tagCopy1: + s += 2 + if uint(s) > uint(len(src)) { // The uint conversions catch overflow from the previous line. + return decodeErrCodeCorrupt + } + length = int(src[s-2]) >> 2 & 0x7 + toffset := int(uint32(src[s-2])&0xe0<<3 | uint32(src[s-1])) + if toffset == 0 { + if debug { + fmt.Print("(repeat) ") + } + // keep last offset + switch length { + case 5: + s += 1 + if uint(s) > uint(len(src)) { // The uint conversions catch overflow from the previous line. + return decodeErrCodeCorrupt + } + length = int(uint32(src[s-1])) + 4 + case 6: + s += 2 + if uint(s) > uint(len(src)) { // The uint conversions catch overflow from the previous line. + return decodeErrCodeCorrupt + } + length = int(uint32(src[s-2])|(uint32(src[s-1])<<8)) + (1 << 8) + case 7: + s += 3 + if uint(s) > uint(len(src)) { // The uint conversions catch overflow from the previous line. + return decodeErrCodeCorrupt + } + length = int(uint32(src[s-3])|(uint32(src[s-2])<<8)|(uint32(src[s-1])<<16)) + (1 << 16) + default: // 0-> 4 + } + } else { + offset = toffset + } + length += 4 + case tagCopy2: + s += 3 + if uint(s) > uint(len(src)) { // The uint conversions catch overflow from the previous line. + return decodeErrCodeCorrupt + } + length = 1 + int(src[s-3])>>2 + offset = int(uint32(src[s-2]) | uint32(src[s-1])<<8) + + case tagCopy4: + s += 5 + if uint(s) > uint(len(src)) { // The uint conversions catch overflow from the previous line. + return decodeErrCodeCorrupt + } + length = 1 + int(src[s-5])>>2 + offset = int(uint32(src[s-4]) | uint32(src[s-3])<<8 | uint32(src[s-2])<<16 | uint32(src[s-1])<<24) + } + + if offset <= 0 || d < offset || length > len(dst)-d { + if debug { + fmt.Println("corrupt: match, length", length, "offset:", offset, "dst avail:", len(dst)-d, "dst pos:", d) + } + return decodeErrCodeCorrupt + } + + if debug { + fmt.Println("copy, length:", length, "offset:", offset, "d-after:", d+length) + } + + // Copy from an earlier sub-slice of dst to a later sub-slice. + // If no overlap, use the built-in copy: + if offset > length { + copy(dst[d:d+length], dst[d-offset:]) + d += length + continue + } + + // Unlike the built-in copy function, this byte-by-byte copy always runs + // forwards, even if the slices overlap. Conceptually, this is: + // + // d += forwardCopy(dst[d:d+length], dst[d-offset:]) + // + // We align the slices into a and b and show the compiler they are the same size. + // This allows the loop to run without bounds checks. + a := dst[d : d+length] + b := dst[d-offset:] + b = b[:len(a)] + for i := range a { + a[i] = b[i] + } + d += length + } + + if d != len(dst) { + return decodeErrCodeCorrupt + } + return 0 +} diff --git a/vendor/github.com/klauspost/compress/s2/dict.go b/vendor/github.com/klauspost/compress/s2/dict.go new file mode 100644 index 000000000..f125ad096 --- /dev/null +++ b/vendor/github.com/klauspost/compress/s2/dict.go @@ -0,0 +1,350 @@ +// Copyright (c) 2022+ Klaus Post. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package s2 + +import ( + "bytes" + "encoding/binary" + "sync" +) + +const ( + // MinDictSize is the minimum dictionary size when repeat has been read. + MinDictSize = 16 + + // MaxDictSize is the maximum dictionary size when repeat has been read. + MaxDictSize = 65536 + + // MaxDictSrcOffset is the maximum offset where a dictionary entry can start. + MaxDictSrcOffset = 65535 +) + +// Dict contains a dictionary that can be used for encoding and decoding s2 +type Dict struct { + dict []byte + repeat int // Repeat as index of dict + + fast, better, best sync.Once + fastTable *[1 << 14]uint16 + + betterTableShort *[1 << 14]uint16 + betterTableLong *[1 << 17]uint16 + + bestTableShort *[1 << 16]uint32 + bestTableLong *[1 << 19]uint32 +} + +// NewDict will read a dictionary. +// It will return nil if the dictionary is invalid. +func NewDict(dict []byte) *Dict { + if len(dict) == 0 { + return nil + } + var d Dict + // Repeat is the first value of the dict + r, n := binary.Uvarint(dict) + if n <= 0 { + return nil + } + dict = dict[n:] + d.dict = dict + if cap(d.dict) < len(d.dict)+16 { + d.dict = append(make([]byte, 0, len(d.dict)+16), d.dict...) + } + if len(dict) < MinDictSize || len(dict) > MaxDictSize { + return nil + } + d.repeat = int(r) + if d.repeat > len(dict) { + return nil + } + return &d +} + +// Bytes will return a serialized version of the dictionary. +// The output can be sent to NewDict. +func (d *Dict) Bytes() []byte { + dst := make([]byte, binary.MaxVarintLen16+len(d.dict)) + return append(dst[:binary.PutUvarint(dst, uint64(d.repeat))], d.dict...) +} + +// MakeDict will create a dictionary. +// 'data' must be at least MinDictSize. +// If data is longer than MaxDictSize only the last MaxDictSize bytes will be used. +// If searchStart is set the start repeat value will be set to the last +// match of this content. +// If no matches are found, it will attempt to find shorter matches. +// This content should match the typical start of a block. +// If at least 4 bytes cannot be matched, repeat is set to start of block. +func MakeDict(data []byte, searchStart []byte) *Dict { + if len(data) == 0 { + return nil + } + if len(data) > MaxDictSize { + data = data[len(data)-MaxDictSize:] + } + var d Dict + dict := data + d.dict = dict + if cap(d.dict) < len(d.dict)+16 { + d.dict = append(make([]byte, 0, len(d.dict)+16), d.dict...) + } + if len(dict) < MinDictSize { + return nil + } + + // Find the longest match possible, last entry if multiple. + for s := len(searchStart); s > 4; s-- { + if idx := bytes.LastIndex(data, searchStart[:s]); idx >= 0 && idx <= len(data)-8 { + d.repeat = idx + break + } + } + + return &d +} + +// MakeDictManual will create a dictionary. +// 'data' must be at least MinDictSize and less than or equal to MaxDictSize. +// A manual first repeat index into data must be provided. +// It must be less than len(data)-8. +func MakeDictManual(data []byte, firstIdx uint16) *Dict { + if len(data) < MinDictSize || int(firstIdx) >= len(data)-8 || len(data) > MaxDictSize { + return nil + } + var d Dict + dict := data + d.dict = dict + if cap(d.dict) < len(d.dict)+16 { + d.dict = append(make([]byte, 0, len(d.dict)+16), d.dict...) + } + + d.repeat = int(firstIdx) + return &d +} + +// Encode returns the encoded form of src. The returned slice may be a sub- +// slice of dst if dst was large enough to hold the entire encoded block. +// Otherwise, a newly allocated slice will be returned. +// +// The dst and src must not overlap. It is valid to pass a nil dst. +// +// The blocks will require the same amount of memory to decode as encoding, +// and does not make for concurrent decoding. +// Also note that blocks do not contain CRC information, so corruption may be undetected. +// +// If you need to encode larger amounts of data, consider using +// the streaming interface which gives all of these features. +func (d *Dict) Encode(dst, src []byte) []byte { + if n := MaxEncodedLen(len(src)); n < 0 { + panic(ErrTooLarge) + } else if cap(dst) < n { + dst = make([]byte, n) + } else { + dst = dst[:n] + } + + // The block starts with the varint-encoded length of the decompressed bytes. + dstP := binary.PutUvarint(dst, uint64(len(src))) + + if len(src) == 0 { + return dst[:dstP] + } + if len(src) < minNonLiteralBlockSize { + dstP += emitLiteral(dst[dstP:], src) + return dst[:dstP] + } + n := encodeBlockDictGo(dst[dstP:], src, d) + if n > 0 { + dstP += n + return dst[:dstP] + } + // Not compressible + dstP += emitLiteral(dst[dstP:], src) + return dst[:dstP] +} + +// EncodeBetter returns the encoded form of src. The returned slice may be a sub- +// slice of dst if dst was large enough to hold the entire encoded block. +// Otherwise, a newly allocated slice will be returned. +// +// EncodeBetter compresses better than Encode but typically with a +// 10-40% speed decrease on both compression and decompression. +// +// The dst and src must not overlap. It is valid to pass a nil dst. +// +// The blocks will require the same amount of memory to decode as encoding, +// and does not make for concurrent decoding. +// Also note that blocks do not contain CRC information, so corruption may be undetected. +// +// If you need to encode larger amounts of data, consider using +// the streaming interface which gives all of these features. +func (d *Dict) EncodeBetter(dst, src []byte) []byte { + if n := MaxEncodedLen(len(src)); n < 0 { + panic(ErrTooLarge) + } else if len(dst) < n { + dst = make([]byte, n) + } + + // The block starts with the varint-encoded length of the decompressed bytes. + dstP := binary.PutUvarint(dst, uint64(len(src))) + + if len(src) == 0 { + return dst[:dstP] + } + if len(src) < minNonLiteralBlockSize { + dstP += emitLiteral(dst[dstP:], src) + return dst[:dstP] + } + n := encodeBlockBetterDict(dst[dstP:], src, d) + if n > 0 { + dstP += n + return dst[:dstP] + } + // Not compressible + dstP += emitLiteral(dst[dstP:], src) + return dst[:dstP] +} + +// EncodeBest returns the encoded form of src. The returned slice may be a sub- +// slice of dst if dst was large enough to hold the entire encoded block. +// Otherwise, a newly allocated slice will be returned. +// +// EncodeBest compresses as good as reasonably possible but with a +// big speed decrease. +// +// The dst and src must not overlap. It is valid to pass a nil dst. +// +// The blocks will require the same amount of memory to decode as encoding, +// and does not make for concurrent decoding. +// Also note that blocks do not contain CRC information, so corruption may be undetected. +// +// If you need to encode larger amounts of data, consider using +// the streaming interface which gives all of these features. +func (d *Dict) EncodeBest(dst, src []byte) []byte { + if n := MaxEncodedLen(len(src)); n < 0 { + panic(ErrTooLarge) + } else if len(dst) < n { + dst = make([]byte, n) + } + + // The block starts with the varint-encoded length of the decompressed bytes. + dstP := binary.PutUvarint(dst, uint64(len(src))) + + if len(src) == 0 { + return dst[:dstP] + } + if len(src) < minNonLiteralBlockSize { + dstP += emitLiteral(dst[dstP:], src) + return dst[:dstP] + } + n := encodeBlockBest(dst[dstP:], src, d) + if n > 0 { + dstP += n + return dst[:dstP] + } + // Not compressible + dstP += emitLiteral(dst[dstP:], src) + return dst[:dstP] +} + +// Decode returns the decoded form of src. The returned slice may be a sub- +// slice of dst if dst was large enough to hold the entire decoded block. +// Otherwise, a newly allocated slice will be returned. +// +// The dst and src must not overlap. It is valid to pass a nil dst. +func (d *Dict) Decode(dst, src []byte) ([]byte, error) { + dLen, s, err := decodedLen(src) + if err != nil { + return nil, err + } + if dLen <= cap(dst) { + dst = dst[:dLen] + } else { + dst = make([]byte, dLen) + } + if s2DecodeDict(dst, src[s:], d) != 0 { + return nil, ErrCorrupt + } + return dst, nil +} + +func (d *Dict) initFast() { + d.fast.Do(func() { + const ( + tableBits = 14 + maxTableSize = 1 << tableBits + ) + + var table [maxTableSize]uint16 + // We stop so any entry of length 8 can always be read. + for i := 0; i < len(d.dict)-8-2; i += 3 { + x0 := load64(d.dict, i) + h0 := hash6(x0, tableBits) + h1 := hash6(x0>>8, tableBits) + h2 := hash6(x0>>16, tableBits) + table[h0] = uint16(i) + table[h1] = uint16(i + 1) + table[h2] = uint16(i + 2) + } + d.fastTable = &table + }) +} + +func (d *Dict) initBetter() { + d.better.Do(func() { + const ( + // Long hash matches. + lTableBits = 17 + maxLTableSize = 1 << lTableBits + + // Short hash matches. + sTableBits = 14 + maxSTableSize = 1 << sTableBits + ) + + var lTable [maxLTableSize]uint16 + var sTable [maxSTableSize]uint16 + + // We stop so any entry of length 8 can always be read. + for i := 0; i < len(d.dict)-8; i++ { + cv := load64(d.dict, i) + lTable[hash7(cv, lTableBits)] = uint16(i) + sTable[hash4(cv, sTableBits)] = uint16(i) + } + d.betterTableShort = &sTable + d.betterTableLong = &lTable + }) +} + +func (d *Dict) initBest() { + d.best.Do(func() { + const ( + // Long hash matches. + lTableBits = 19 + maxLTableSize = 1 << lTableBits + + // Short hash matches. + sTableBits = 16 + maxSTableSize = 1 << sTableBits + ) + + var lTable [maxLTableSize]uint32 + var sTable [maxSTableSize]uint32 + + // We stop so any entry of length 8 can always be read. + for i := 0; i < len(d.dict)-8; i++ { + cv := load64(d.dict, i) + hashL := hash8(cv, lTableBits) + hashS := hash4(cv, sTableBits) + candidateL := lTable[hashL] + candidateS := sTable[hashS] + lTable[hashL] = uint32(i) | candidateL<<16 + sTable[hashS] = uint32(i) | candidateS<<16 + } + d.bestTableShort = &sTable + d.bestTableLong = &lTable + }) +} diff --git a/vendor/github.com/klauspost/compress/s2/encode.go b/vendor/github.com/klauspost/compress/s2/encode.go new file mode 100644 index 000000000..330e75571 --- /dev/null +++ b/vendor/github.com/klauspost/compress/s2/encode.go @@ -0,0 +1,418 @@ +// Copyright 2011 The Snappy-Go Authors. All rights reserved. +// Copyright (c) 2019 Klaus Post. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package s2 + +import ( + "encoding/binary" + "math" + "math/bits" + "sync" + + "github.com/klauspost/compress/internal/race" +) + +// Encode returns the encoded form of src. The returned slice may be a sub- +// slice of dst if dst was large enough to hold the entire encoded block. +// Otherwise, a newly allocated slice will be returned. +// +// The dst and src must not overlap. It is valid to pass a nil dst. +// +// The blocks will require the same amount of memory to decode as encoding, +// and does not make for concurrent decoding. +// Also note that blocks do not contain CRC information, so corruption may be undetected. +// +// If you need to encode larger amounts of data, consider using +// the streaming interface which gives all of these features. +func Encode(dst, src []byte) []byte { + if n := MaxEncodedLen(len(src)); n < 0 { + panic(ErrTooLarge) + } else if cap(dst) < n { + dst = make([]byte, n) + } else { + dst = dst[:n] + } + + // The block starts with the varint-encoded length of the decompressed bytes. + d := binary.PutUvarint(dst, uint64(len(src))) + + if len(src) == 0 { + return dst[:d] + } + if len(src) < minNonLiteralBlockSize { + d += emitLiteral(dst[d:], src) + return dst[:d] + } + n := encodeBlock(dst[d:], src) + if n > 0 { + d += n + return dst[:d] + } + // Not compressible + d += emitLiteral(dst[d:], src) + return dst[:d] +} + +var estblockPool [2]sync.Pool + +// EstimateBlockSize will perform a very fast compression +// without outputting the result and return the compressed output size. +// The function returns -1 if no improvement could be achieved. +// Using actual compression will most often produce better compression than the estimate. +func EstimateBlockSize(src []byte) (d int) { + if len(src) <= inputMargin || int64(len(src)) > 0xffffffff { + return -1 + } + if len(src) <= 1024 { + const sz, pool = 2048, 0 + tmp, ok := estblockPool[pool].Get().(*[sz]byte) + if !ok { + tmp = &[sz]byte{} + } + race.WriteSlice(tmp[:]) + defer estblockPool[pool].Put(tmp) + + d = calcBlockSizeSmall(src, tmp) + } else { + const sz, pool = 32768, 1 + tmp, ok := estblockPool[pool].Get().(*[sz]byte) + if !ok { + tmp = &[sz]byte{} + } + race.WriteSlice(tmp[:]) + defer estblockPool[pool].Put(tmp) + + d = calcBlockSize(src, tmp) + } + + if d == 0 { + return -1 + } + // Size of the varint encoded block size. + d += (bits.Len64(uint64(len(src))) + 7) / 7 + + if d >= len(src) { + return -1 + } + return d +} + +// EncodeBetter returns the encoded form of src. The returned slice may be a sub- +// slice of dst if dst was large enough to hold the entire encoded block. +// Otherwise, a newly allocated slice will be returned. +// +// EncodeBetter compresses better than Encode but typically with a +// 10-40% speed decrease on both compression and decompression. +// +// The dst and src must not overlap. It is valid to pass a nil dst. +// +// The blocks will require the same amount of memory to decode as encoding, +// and does not make for concurrent decoding. +// Also note that blocks do not contain CRC information, so corruption may be undetected. +// +// If you need to encode larger amounts of data, consider using +// the streaming interface which gives all of these features. +func EncodeBetter(dst, src []byte) []byte { + if n := MaxEncodedLen(len(src)); n < 0 { + panic(ErrTooLarge) + } else if cap(dst) < n { + dst = make([]byte, n) + } else { + dst = dst[:n] + } + + // The block starts with the varint-encoded length of the decompressed bytes. + d := binary.PutUvarint(dst, uint64(len(src))) + + if len(src) == 0 { + return dst[:d] + } + if len(src) < minNonLiteralBlockSize { + d += emitLiteral(dst[d:], src) + return dst[:d] + } + n := encodeBlockBetter(dst[d:], src) + if n > 0 { + d += n + return dst[:d] + } + // Not compressible + d += emitLiteral(dst[d:], src) + return dst[:d] +} + +// EncodeBest returns the encoded form of src. The returned slice may be a sub- +// slice of dst if dst was large enough to hold the entire encoded block. +// Otherwise, a newly allocated slice will be returned. +// +// EncodeBest compresses as good as reasonably possible but with a +// big speed decrease. +// +// The dst and src must not overlap. It is valid to pass a nil dst. +// +// The blocks will require the same amount of memory to decode as encoding, +// and does not make for concurrent decoding. +// Also note that blocks do not contain CRC information, so corruption may be undetected. +// +// If you need to encode larger amounts of data, consider using +// the streaming interface which gives all of these features. +func EncodeBest(dst, src []byte) []byte { + if n := MaxEncodedLen(len(src)); n < 0 { + panic(ErrTooLarge) + } else if cap(dst) < n { + dst = make([]byte, n) + } else { + dst = dst[:n] + } + + // The block starts with the varint-encoded length of the decompressed bytes. + d := binary.PutUvarint(dst, uint64(len(src))) + + if len(src) == 0 { + return dst[:d] + } + if len(src) < minNonLiteralBlockSize { + d += emitLiteral(dst[d:], src) + return dst[:d] + } + n := encodeBlockBest(dst[d:], src, nil) + if n > 0 { + d += n + return dst[:d] + } + // Not compressible + d += emitLiteral(dst[d:], src) + return dst[:d] +} + +// EncodeSnappy returns the encoded form of src. The returned slice may be a sub- +// slice of dst if dst was large enough to hold the entire encoded block. +// Otherwise, a newly allocated slice will be returned. +// +// The output is Snappy compatible and will likely decompress faster. +// +// The dst and src must not overlap. It is valid to pass a nil dst. +// +// The blocks will require the same amount of memory to decode as encoding, +// and does not make for concurrent decoding. +// Also note that blocks do not contain CRC information, so corruption may be undetected. +// +// If you need to encode larger amounts of data, consider using +// the streaming interface which gives all of these features. +func EncodeSnappy(dst, src []byte) []byte { + if n := MaxEncodedLen(len(src)); n < 0 { + panic(ErrTooLarge) + } else if cap(dst) < n { + dst = make([]byte, n) + } else { + dst = dst[:n] + } + + // The block starts with the varint-encoded length of the decompressed bytes. + d := binary.PutUvarint(dst, uint64(len(src))) + + if len(src) == 0 { + return dst[:d] + } + if len(src) < minNonLiteralBlockSize { + d += emitLiteral(dst[d:], src) + return dst[:d] + } + + n := encodeBlockSnappy(dst[d:], src) + if n > 0 { + d += n + return dst[:d] + } + // Not compressible + d += emitLiteral(dst[d:], src) + return dst[:d] +} + +// EncodeSnappyBetter returns the encoded form of src. The returned slice may be a sub- +// slice of dst if dst was large enough to hold the entire encoded block. +// Otherwise, a newly allocated slice will be returned. +// +// The output is Snappy compatible and will likely decompress faster. +// +// The dst and src must not overlap. It is valid to pass a nil dst. +// +// The blocks will require the same amount of memory to decode as encoding, +// and does not make for concurrent decoding. +// Also note that blocks do not contain CRC information, so corruption may be undetected. +// +// If you need to encode larger amounts of data, consider using +// the streaming interface which gives all of these features. +func EncodeSnappyBetter(dst, src []byte) []byte { + if n := MaxEncodedLen(len(src)); n < 0 { + panic(ErrTooLarge) + } else if cap(dst) < n { + dst = make([]byte, n) + } else { + dst = dst[:n] + } + + // The block starts with the varint-encoded length of the decompressed bytes. + d := binary.PutUvarint(dst, uint64(len(src))) + + if len(src) == 0 { + return dst[:d] + } + if len(src) < minNonLiteralBlockSize { + d += emitLiteral(dst[d:], src) + return dst[:d] + } + + n := encodeBlockBetterSnappy(dst[d:], src) + if n > 0 { + d += n + return dst[:d] + } + // Not compressible + d += emitLiteral(dst[d:], src) + return dst[:d] +} + +// EncodeSnappyBest returns the encoded form of src. The returned slice may be a sub- +// slice of dst if dst was large enough to hold the entire encoded block. +// Otherwise, a newly allocated slice will be returned. +// +// The output is Snappy compatible and will likely decompress faster. +// +// The dst and src must not overlap. It is valid to pass a nil dst. +// +// The blocks will require the same amount of memory to decode as encoding, +// and does not make for concurrent decoding. +// Also note that blocks do not contain CRC information, so corruption may be undetected. +// +// If you need to encode larger amounts of data, consider using +// the streaming interface which gives all of these features. +func EncodeSnappyBest(dst, src []byte) []byte { + if n := MaxEncodedLen(len(src)); n < 0 { + panic(ErrTooLarge) + } else if cap(dst) < n { + dst = make([]byte, n) + } else { + dst = dst[:n] + } + + // The block starts with the varint-encoded length of the decompressed bytes. + d := binary.PutUvarint(dst, uint64(len(src))) + + if len(src) == 0 { + return dst[:d] + } + if len(src) < minNonLiteralBlockSize { + d += emitLiteral(dst[d:], src) + return dst[:d] + } + + n := encodeBlockBestSnappy(dst[d:], src) + if n > 0 { + d += n + return dst[:d] + } + // Not compressible + d += emitLiteral(dst[d:], src) + return dst[:d] +} + +// ConcatBlocks will concatenate the supplied blocks and append them to the supplied destination. +// If the destination is nil or too small, a new will be allocated. +// The blocks are not validated, so garbage in = garbage out. +// dst may not overlap block data. +// Any data in dst is preserved as is, so it will not be considered a block. +func ConcatBlocks(dst []byte, blocks ...[]byte) ([]byte, error) { + totalSize := uint64(0) + compSize := 0 + for _, b := range blocks { + l, hdr, err := decodedLen(b) + if err != nil { + return nil, err + } + totalSize += uint64(l) + compSize += len(b) - hdr + } + if totalSize == 0 { + dst = append(dst, 0) + return dst, nil + } + if totalSize > math.MaxUint32 { + return nil, ErrTooLarge + } + var tmp [binary.MaxVarintLen32]byte + hdrSize := binary.PutUvarint(tmp[:], totalSize) + wantSize := hdrSize + compSize + + if cap(dst)-len(dst) < wantSize { + dst = append(make([]byte, 0, wantSize+len(dst)), dst...) + } + dst = append(dst, tmp[:hdrSize]...) + for _, b := range blocks { + _, hdr, err := decodedLen(b) + if err != nil { + return nil, err + } + dst = append(dst, b[hdr:]...) + } + return dst, nil +} + +// inputMargin is the minimum number of extra input bytes to keep, inside +// encodeBlock's inner loop. On some architectures, this margin lets us +// implement a fast path for emitLiteral, where the copy of short (<= 16 byte) +// literals can be implemented as a single load to and store from a 16-byte +// register. That literal's actual length can be as short as 1 byte, so this +// can copy up to 15 bytes too much, but that's OK as subsequent iterations of +// the encoding loop will fix up the copy overrun, and this inputMargin ensures +// that we don't overrun the dst and src buffers. +const inputMargin = 8 + +// minNonLiteralBlockSize is the minimum size of the input to encodeBlock that +// will be accepted by the encoder. +const minNonLiteralBlockSize = 32 + +const intReduction = 2 - (1 << (^uint(0) >> 63)) // 1 (32 bits) or 0 (64 bits) + +// MaxBlockSize is the maximum value where MaxEncodedLen will return a valid block size. +// Blocks this big are highly discouraged, though. +// Half the size on 32 bit systems. +const MaxBlockSize = (1<<(32-intReduction) - 1) - binary.MaxVarintLen32 - 5 + +// MaxEncodedLen returns the maximum length of a snappy block, given its +// uncompressed length. +// +// It will return a negative value if srcLen is too large to encode. +// 32 bit platforms will have lower thresholds for rejecting big content. +func MaxEncodedLen(srcLen int) int { + n := uint64(srcLen) + if intReduction == 1 { + // 32 bits + if n > math.MaxInt32 { + // Also includes negative. + return -1 + } + } else if n > 0xffffffff { + // 64 bits + // Also includes negative. + return -1 + } + // Size of the varint encoded block size. + n = n + uint64((bits.Len64(n)+7)/7) + + // Add maximum size of encoding block as literals. + n += uint64(literalExtraSize(int64(srcLen))) + if intReduction == 1 { + // 32 bits + if n > math.MaxInt32 { + return -1 + } + } else if n > 0xffffffff { + // 64 bits + // Also includes negative. + return -1 + } + return int(n) +} diff --git a/vendor/github.com/klauspost/compress/s2/encode_all.go b/vendor/github.com/klauspost/compress/s2/encode_all.go new file mode 100644 index 000000000..9d12c44f3 --- /dev/null +++ b/vendor/github.com/klauspost/compress/s2/encode_all.go @@ -0,0 +1,1477 @@ +// Copyright 2016 The Snappy-Go Authors. All rights reserved. +// Copyright (c) 2019 Klaus Post. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package s2 + +import ( + "bytes" + "encoding/binary" + "fmt" + "math/bits" + + "github.com/klauspost/compress/internal/le" +) + +func load32(b []byte, i int) uint32 { + return le.Load32(b, i) +} + +func load64(b []byte, i int) uint64 { + return le.Load64(b, i) +} + +// hash6 returns the hash of the lowest 6 bytes of u to fit in a hash table with h bits. +// Preferably h should be a constant and should always be <64. +func hash6(u uint64, h uint8) uint32 { + const prime6bytes = 227718039650203 + return uint32(((u << (64 - 48)) * prime6bytes) >> ((64 - h) & 63)) +} + +func encodeGo(dst, src []byte) []byte { + if n := MaxEncodedLen(len(src)); n < 0 { + panic(ErrTooLarge) + } else if len(dst) < n { + dst = make([]byte, n) + } + + // The block starts with the varint-encoded length of the decompressed bytes. + d := binary.PutUvarint(dst, uint64(len(src))) + + if len(src) == 0 { + return dst[:d] + } + if len(src) < minNonLiteralBlockSize { + d += emitLiteral(dst[d:], src) + return dst[:d] + } + var n int + if len(src) < 64<<10 { + n = encodeBlockGo64K(dst[d:], src) + } else { + n = encodeBlockGo(dst[d:], src) + } + if n > 0 { + d += n + return dst[:d] + } + // Not compressible + d += emitLiteral(dst[d:], src) + return dst[:d] +} + +// encodeBlockGo encodes a non-empty src to a guaranteed-large-enough dst. It +// assumes that the varint-encoded length of the decompressed bytes has already +// been written. +// +// It also assumes that: +// +// len(dst) >= MaxEncodedLen(len(src)) && +// minNonLiteralBlockSize <= len(src) && len(src) <= maxBlockSize +func encodeBlockGo(dst, src []byte) (d int) { + // Initialize the hash table. + const ( + tableBits = 14 + maxTableSize = 1 << tableBits + + debug = false + ) + var table [maxTableSize]uint32 + + // sLimit is when to stop looking for offset/length copies. The inputMargin + // lets us use a fast path for emitLiteral in the main loop, while we are + // looking for copies. + sLimit := len(src) - inputMargin + + // Bail if we can't compress to at least this. + dstLimit := len(src) - len(src)>>5 - 5 + + // nextEmit is where in src the next emitLiteral should start from. + nextEmit := 0 + + // The encoded form must start with a literal, as there are no previous + // bytes to copy, so we start looking for hash matches at s == 1. + s := 1 + cv := load64(src, s) + + // We search for a repeat at -1, but don't output repeats when nextEmit == 0 + repeat := 1 + + for { + candidate := 0 + for { + // Next src position to check + nextS := s + (s-nextEmit)>>6 + 4 + if nextS > sLimit { + goto emitRemainder + } + hash0 := hash6(cv, tableBits) + hash1 := hash6(cv>>8, tableBits) + candidate = int(table[hash0]) + candidate2 := int(table[hash1]) + table[hash0] = uint32(s) + table[hash1] = uint32(s + 1) + hash2 := hash6(cv>>16, tableBits) + + // Check repeat at offset checkRep. + const checkRep = 1 + if uint32(cv>>(checkRep*8)) == load32(src, s-repeat+checkRep) { + base := s + checkRep + // Extend back + for i := base - repeat; base > nextEmit && i > 0 && src[i-1] == src[base-1]; { + i-- + base-- + } + + // Bail if we exceed the maximum size. + if d+(base-nextEmit) > dstLimit { + return 0 + } + + d += emitLiteral(dst[d:], src[nextEmit:base]) + + // Extend forward + candidate := s - repeat + 4 + checkRep + s += 4 + checkRep + for s <= sLimit { + if diff := load64(src, s) ^ load64(src, candidate); diff != 0 { + s += bits.TrailingZeros64(diff) >> 3 + break + } + s += 8 + candidate += 8 + } + if debug { + // Validate match. + if s <= candidate { + panic("s <= candidate") + } + a := src[base:s] + b := src[base-repeat : base-repeat+(s-base)] + if !bytes.Equal(a, b) { + panic("mismatch") + } + } + if nextEmit > 0 { + // same as `add := emitCopy(dst[d:], repeat, s-base)` but skips storing offset. + d += emitRepeat(dst[d:], repeat, s-base) + } else { + // First match, cannot be repeat. + d += emitCopy(dst[d:], repeat, s-base) + } + nextEmit = s + if s >= sLimit { + goto emitRemainder + } + cv = load64(src, s) + continue + } + + if uint32(cv) == load32(src, candidate) { + break + } + candidate = int(table[hash2]) + if uint32(cv>>8) == load32(src, candidate2) { + table[hash2] = uint32(s + 2) + candidate = candidate2 + s++ + break + } + table[hash2] = uint32(s + 2) + if uint32(cv>>16) == load32(src, candidate) { + s += 2 + break + } + + cv = load64(src, nextS) + s = nextS + } + + // Extend backwards. + // The top bytes will be rechecked to get the full match. + for candidate > 0 && s > nextEmit && src[candidate-1] == src[s-1] { + candidate-- + s-- + } + + // Bail if we exceed the maximum size. + if d+(s-nextEmit) > dstLimit { + return 0 + } + + // A 4-byte match has been found. We'll later see if more than 4 bytes + // match. But, prior to the match, src[nextEmit:s] are unmatched. Emit + // them as literal bytes. + + d += emitLiteral(dst[d:], src[nextEmit:s]) + + // Call emitCopy, and then see if another emitCopy could be our next + // move. Repeat until we find no match for the input immediately after + // what was consumed by the last emitCopy call. + // + // If we exit this loop normally then we need to call emitLiteral next, + // though we don't yet know how big the literal will be. We handle that + // by proceeding to the next iteration of the main loop. We also can + // exit this loop via goto if we get close to exhausting the input. + for { + // Invariant: we have a 4-byte match at s, and no need to emit any + // literal bytes prior to s. + base := s + repeat = base - candidate + + // Extend the 4-byte match as long as possible. + s += 4 + candidate += 4 + for s <= len(src)-8 { + if diff := load64(src, s) ^ load64(src, candidate); diff != 0 { + s += bits.TrailingZeros64(diff) >> 3 + break + } + s += 8 + candidate += 8 + } + + d += emitCopy(dst[d:], repeat, s-base) + if debug { + // Validate match. + if s <= candidate { + panic("s <= candidate") + } + a := src[base:s] + b := src[base-repeat : base-repeat+(s-base)] + if !bytes.Equal(a, b) { + panic("mismatch") + } + } + + nextEmit = s + if s >= sLimit { + goto emitRemainder + } + + if d > dstLimit { + // Do we have space for more, if not bail. + return 0 + } + // Check for an immediate match, otherwise start search at s+1 + x := load64(src, s-2) + m2Hash := hash6(x, tableBits) + currHash := hash6(x>>16, tableBits) + candidate = int(table[currHash]) + table[m2Hash] = uint32(s - 2) + table[currHash] = uint32(s) + if debug && s == candidate { + panic("s == candidate") + } + if uint32(x>>16) != load32(src, candidate) { + cv = load64(src, s+1) + s++ + break + } + } + } + +emitRemainder: + if nextEmit < len(src) { + // Bail if we exceed the maximum size. + if d+len(src)-nextEmit > dstLimit { + return 0 + } + d += emitLiteral(dst[d:], src[nextEmit:]) + } + return d +} + +// encodeBlockGo64K is a specialized version for compressing blocks <= 64KB +func encodeBlockGo64K(dst, src []byte) (d int) { + // Initialize the hash table. + const ( + tableBits = 14 + maxTableSize = 1 << tableBits + + debug = false + ) + + var table [maxTableSize]uint16 + + // sLimit is when to stop looking for offset/length copies. The inputMargin + // lets us use a fast path for emitLiteral in the main loop, while we are + // looking for copies. + sLimit := len(src) - inputMargin + + // Bail if we can't compress to at least this. + dstLimit := len(src) - len(src)>>5 - 5 + + // nextEmit is where in src the next emitLiteral should start from. + nextEmit := 0 + + // The encoded form must start with a literal, as there are no previous + // bytes to copy, so we start looking for hash matches at s == 1. + s := 1 + cv := load64(src, s) + + // We search for a repeat at -1, but don't output repeats when nextEmit == 0 + repeat := 1 + + for { + candidate := 0 + for { + // Next src position to check + nextS := s + (s-nextEmit)>>5 + 4 + if nextS > sLimit { + goto emitRemainder + } + hash0 := hash6(cv, tableBits) + hash1 := hash6(cv>>8, tableBits) + candidate = int(table[hash0]) + candidate2 := int(table[hash1]) + table[hash0] = uint16(s) + table[hash1] = uint16(s + 1) + hash2 := hash6(cv>>16, tableBits) + + // Check repeat at offset checkRep. + const checkRep = 1 + if uint32(cv>>(checkRep*8)) == load32(src, s-repeat+checkRep) { + base := s + checkRep + // Extend back + for i := base - repeat; base > nextEmit && i > 0 && src[i-1] == src[base-1]; { + i-- + base-- + } + + // Bail if we exceed the maximum size. + if d+(base-nextEmit) > dstLimit { + return 0 + } + + d += emitLiteral(dst[d:], src[nextEmit:base]) + + // Extend forward + candidate := s - repeat + 4 + checkRep + s += 4 + checkRep + for s <= sLimit { + if diff := load64(src, s) ^ load64(src, candidate); diff != 0 { + s += bits.TrailingZeros64(diff) >> 3 + break + } + s += 8 + candidate += 8 + } + if debug { + // Validate match. + if s <= candidate { + panic("s <= candidate") + } + a := src[base:s] + b := src[base-repeat : base-repeat+(s-base)] + if !bytes.Equal(a, b) { + panic("mismatch") + } + } + if nextEmit > 0 { + // same as `add := emitCopy(dst[d:], repeat, s-base)` but skips storing offset. + d += emitRepeat(dst[d:], repeat, s-base) + } else { + // First match, cannot be repeat. + d += emitCopy(dst[d:], repeat, s-base) + } + nextEmit = s + if s >= sLimit { + goto emitRemainder + } + cv = load64(src, s) + continue + } + + if uint32(cv) == load32(src, candidate) { + break + } + candidate = int(table[hash2]) + if uint32(cv>>8) == load32(src, candidate2) { + table[hash2] = uint16(s + 2) + candidate = candidate2 + s++ + break + } + table[hash2] = uint16(s + 2) + if uint32(cv>>16) == load32(src, candidate) { + s += 2 + break + } + + cv = load64(src, nextS) + s = nextS + } + + // Extend backwards. + // The top bytes will be rechecked to get the full match. + for candidate > 0 && s > nextEmit && src[candidate-1] == src[s-1] { + candidate-- + s-- + } + + // Bail if we exceed the maximum size. + if d+(s-nextEmit) > dstLimit { + return 0 + } + + // A 4-byte match has been found. We'll later see if more than 4 bytes + // match. But, prior to the match, src[nextEmit:s] are unmatched. Emit + // them as literal bytes. + + d += emitLiteral(dst[d:], src[nextEmit:s]) + + // Call emitCopy, and then see if another emitCopy could be our next + // move. Repeat until we find no match for the input immediately after + // what was consumed by the last emitCopy call. + // + // If we exit this loop normally then we need to call emitLiteral next, + // though we don't yet know how big the literal will be. We handle that + // by proceeding to the next iteration of the main loop. We also can + // exit this loop via goto if we get close to exhausting the input. + for { + // Invariant: we have a 4-byte match at s, and no need to emit any + // literal bytes prior to s. + base := s + repeat = base - candidate + + // Extend the 4-byte match as long as possible. + s += 4 + candidate += 4 + for s <= len(src)-8 { + if diff := load64(src, s) ^ load64(src, candidate); diff != 0 { + s += bits.TrailingZeros64(diff) >> 3 + break + } + s += 8 + candidate += 8 + } + + d += emitCopy(dst[d:], repeat, s-base) + if debug { + // Validate match. + if s <= candidate { + panic("s <= candidate") + } + a := src[base:s] + b := src[base-repeat : base-repeat+(s-base)] + if !bytes.Equal(a, b) { + panic("mismatch") + } + } + + nextEmit = s + if s >= sLimit { + goto emitRemainder + } + + if d > dstLimit { + // Do we have space for more, if not bail. + return 0 + } + // Check for an immediate match, otherwise start search at s+1 + x := load64(src, s-2) + m2Hash := hash6(x, tableBits) + currHash := hash6(x>>16, tableBits) + candidate = int(table[currHash]) + table[m2Hash] = uint16(s - 2) + table[currHash] = uint16(s) + if debug && s == candidate { + panic("s == candidate") + } + if uint32(x>>16) != load32(src, candidate) { + cv = load64(src, s+1) + s++ + break + } + } + } + +emitRemainder: + if nextEmit < len(src) { + // Bail if we exceed the maximum size. + if d+len(src)-nextEmit > dstLimit { + return 0 + } + d += emitLiteral(dst[d:], src[nextEmit:]) + } + return d +} + +func encodeBlockSnappyGo(dst, src []byte) (d int) { + // Initialize the hash table. + const ( + tableBits = 14 + maxTableSize = 1 << tableBits + ) + var table [maxTableSize]uint32 + + // sLimit is when to stop looking for offset/length copies. The inputMargin + // lets us use a fast path for emitLiteral in the main loop, while we are + // looking for copies. + sLimit := len(src) - inputMargin + + // Bail if we can't compress to at least this. + dstLimit := len(src) - len(src)>>5 - 5 + + // nextEmit is where in src the next emitLiteral should start from. + nextEmit := 0 + + // The encoded form must start with a literal, as there are no previous + // bytes to copy, so we start looking for hash matches at s == 1. + s := 1 + cv := load64(src, s) + + // We search for a repeat at -1, but don't output repeats when nextEmit == 0 + repeat := 1 + + for { + candidate := 0 + for { + // Next src position to check + nextS := s + (s-nextEmit)>>6 + 4 + if nextS > sLimit { + goto emitRemainder + } + hash0 := hash6(cv, tableBits) + hash1 := hash6(cv>>8, tableBits) + candidate = int(table[hash0]) + candidate2 := int(table[hash1]) + table[hash0] = uint32(s) + table[hash1] = uint32(s + 1) + hash2 := hash6(cv>>16, tableBits) + + // Check repeat at offset checkRep. + const checkRep = 1 + if uint32(cv>>(checkRep*8)) == load32(src, s-repeat+checkRep) { + base := s + checkRep + // Extend back + for i := base - repeat; base > nextEmit && i > 0 && src[i-1] == src[base-1]; { + i-- + base-- + } + // Bail if we exceed the maximum size. + if d+(base-nextEmit) > dstLimit { + return 0 + } + + d += emitLiteral(dst[d:], src[nextEmit:base]) + + // Extend forward + candidate := s - repeat + 4 + checkRep + s += 4 + checkRep + for s <= sLimit { + if diff := load64(src, s) ^ load64(src, candidate); diff != 0 { + s += bits.TrailingZeros64(diff) >> 3 + break + } + s += 8 + candidate += 8 + } + + d += emitCopyNoRepeat(dst[d:], repeat, s-base) + nextEmit = s + if s >= sLimit { + goto emitRemainder + } + + cv = load64(src, s) + continue + } + + if uint32(cv) == load32(src, candidate) { + break + } + candidate = int(table[hash2]) + if uint32(cv>>8) == load32(src, candidate2) { + table[hash2] = uint32(s + 2) + candidate = candidate2 + s++ + break + } + table[hash2] = uint32(s + 2) + if uint32(cv>>16) == load32(src, candidate) { + s += 2 + break + } + + cv = load64(src, nextS) + s = nextS + } + + // Extend backwards + for candidate > 0 && s > nextEmit && src[candidate-1] == src[s-1] { + candidate-- + s-- + } + + // Bail if we exceed the maximum size. + if d+(s-nextEmit) > dstLimit { + return 0 + } + + // A 4-byte match has been found. We'll later see if more than 4 bytes + // match. But, prior to the match, src[nextEmit:s] are unmatched. Emit + // them as literal bytes. + + d += emitLiteral(dst[d:], src[nextEmit:s]) + + // Call emitCopy, and then see if another emitCopy could be our next + // move. Repeat until we find no match for the input immediately after + // what was consumed by the last emitCopy call. + // + // If we exit this loop normally then we need to call emitLiteral next, + // though we don't yet know how big the literal will be. We handle that + // by proceeding to the next iteration of the main loop. We also can + // exit this loop via goto if we get close to exhausting the input. + for { + // Invariant: we have a 4-byte match at s, and no need to emit any + // literal bytes prior to s. + base := s + repeat = base - candidate + + // Extend the 4-byte match as long as possible. + s += 4 + candidate += 4 + for s <= len(src)-8 { + if diff := load64(src, s) ^ load64(src, candidate); diff != 0 { + s += bits.TrailingZeros64(diff) >> 3 + break + } + s += 8 + candidate += 8 + } + + d += emitCopyNoRepeat(dst[d:], repeat, s-base) + if false { + // Validate match. + a := src[base:s] + b := src[base-repeat : base-repeat+(s-base)] + if !bytes.Equal(a, b) { + panic("mismatch") + } + } + + nextEmit = s + if s >= sLimit { + goto emitRemainder + } + + if d > dstLimit { + // Do we have space for more, if not bail. + return 0 + } + // Check for an immediate match, otherwise start search at s+1 + x := load64(src, s-2) + m2Hash := hash6(x, tableBits) + currHash := hash6(x>>16, tableBits) + candidate = int(table[currHash]) + table[m2Hash] = uint32(s - 2) + table[currHash] = uint32(s) + if uint32(x>>16) != load32(src, candidate) { + cv = load64(src, s+1) + s++ + break + } + } + } + +emitRemainder: + if nextEmit < len(src) { + // Bail if we exceed the maximum size. + if d+len(src)-nextEmit > dstLimit { + return 0 + } + d += emitLiteral(dst[d:], src[nextEmit:]) + } + return d +} + +// encodeBlockSnappyGo64K is a special version of encodeBlockSnappyGo for sizes <64KB +func encodeBlockSnappyGo64K(dst, src []byte) (d int) { + // Initialize the hash table. + const ( + tableBits = 14 + maxTableSize = 1 << tableBits + ) + + var table [maxTableSize]uint16 + + // sLimit is when to stop looking for offset/length copies. The inputMargin + // lets us use a fast path for emitLiteral in the main loop, while we are + // looking for copies. + sLimit := len(src) - inputMargin + + // Bail if we can't compress to at least this. + dstLimit := len(src) - len(src)>>5 - 5 + + // nextEmit is where in src the next emitLiteral should start from. + nextEmit := 0 + + // The encoded form must start with a literal, as there are no previous + // bytes to copy, so we start looking for hash matches at s == 1. + s := 1 + cv := load64(src, s) + + // We search for a repeat at -1, but don't output repeats when nextEmit == 0 + repeat := 1 + + for { + candidate := 0 + for { + // Next src position to check + nextS := s + (s-nextEmit)>>5 + 4 + if nextS > sLimit { + goto emitRemainder + } + hash0 := hash6(cv, tableBits) + hash1 := hash6(cv>>8, tableBits) + candidate = int(table[hash0]) + candidate2 := int(table[hash1]) + table[hash0] = uint16(s) + table[hash1] = uint16(s + 1) + hash2 := hash6(cv>>16, tableBits) + + // Check repeat at offset checkRep. + const checkRep = 1 + if uint32(cv>>(checkRep*8)) == load32(src, s-repeat+checkRep) { + base := s + checkRep + // Extend back + for i := base - repeat; base > nextEmit && i > 0 && src[i-1] == src[base-1]; { + i-- + base-- + } + // Bail if we exceed the maximum size. + if d+(base-nextEmit) > dstLimit { + return 0 + } + + d += emitLiteral(dst[d:], src[nextEmit:base]) + + // Extend forward + candidate := s - repeat + 4 + checkRep + s += 4 + checkRep + for s <= sLimit { + if diff := load64(src, s) ^ load64(src, candidate); diff != 0 { + s += bits.TrailingZeros64(diff) >> 3 + break + } + s += 8 + candidate += 8 + } + + d += emitCopyNoRepeat(dst[d:], repeat, s-base) + nextEmit = s + if s >= sLimit { + goto emitRemainder + } + + cv = load64(src, s) + continue + } + + if uint32(cv) == load32(src, candidate) { + break + } + candidate = int(table[hash2]) + if uint32(cv>>8) == load32(src, candidate2) { + table[hash2] = uint16(s + 2) + candidate = candidate2 + s++ + break + } + table[hash2] = uint16(s + 2) + if uint32(cv>>16) == load32(src, candidate) { + s += 2 + break + } + + cv = load64(src, nextS) + s = nextS + } + + // Extend backwards + for candidate > 0 && s > nextEmit && src[candidate-1] == src[s-1] { + candidate-- + s-- + } + + // Bail if we exceed the maximum size. + if d+(s-nextEmit) > dstLimit { + return 0 + } + + // A 4-byte match has been found. We'll later see if more than 4 bytes + // match. But, prior to the match, src[nextEmit:s] are unmatched. Emit + // them as literal bytes. + + d += emitLiteral(dst[d:], src[nextEmit:s]) + + // Call emitCopy, and then see if another emitCopy could be our next + // move. Repeat until we find no match for the input immediately after + // what was consumed by the last emitCopy call. + // + // If we exit this loop normally then we need to call emitLiteral next, + // though we don't yet know how big the literal will be. We handle that + // by proceeding to the next iteration of the main loop. We also can + // exit this loop via goto if we get close to exhausting the input. + for { + // Invariant: we have a 4-byte match at s, and no need to emit any + // literal bytes prior to s. + base := s + repeat = base - candidate + + // Extend the 4-byte match as long as possible. + s += 4 + candidate += 4 + for s <= len(src)-8 { + if diff := load64(src, s) ^ load64(src, candidate); diff != 0 { + s += bits.TrailingZeros64(diff) >> 3 + break + } + s += 8 + candidate += 8 + } + + d += emitCopyNoRepeat(dst[d:], repeat, s-base) + if false { + // Validate match. + a := src[base:s] + b := src[base-repeat : base-repeat+(s-base)] + if !bytes.Equal(a, b) { + panic("mismatch") + } + } + + nextEmit = s + if s >= sLimit { + goto emitRemainder + } + + if d > dstLimit { + // Do we have space for more, if not bail. + return 0 + } + // Check for an immediate match, otherwise start search at s+1 + x := load64(src, s-2) + m2Hash := hash6(x, tableBits) + currHash := hash6(x>>16, tableBits) + candidate = int(table[currHash]) + table[m2Hash] = uint16(s - 2) + table[currHash] = uint16(s) + if uint32(x>>16) != load32(src, candidate) { + cv = load64(src, s+1) + s++ + break + } + } + } + +emitRemainder: + if nextEmit < len(src) { + // Bail if we exceed the maximum size. + if d+len(src)-nextEmit > dstLimit { + return 0 + } + d += emitLiteral(dst[d:], src[nextEmit:]) + } + return d +} + +// encodeBlockGo encodes a non-empty src to a guaranteed-large-enough dst. It +// assumes that the varint-encoded length of the decompressed bytes has already +// been written. +// +// It also assumes that: +// +// len(dst) >= MaxEncodedLen(len(src)) && +// minNonLiteralBlockSize <= len(src) && len(src) <= maxBlockSize +func encodeBlockDictGo(dst, src []byte, dict *Dict) (d int) { + // Initialize the hash table. + const ( + tableBits = 14 + maxTableSize = 1 << tableBits + maxAhead = 8 // maximum bytes ahead without checking sLimit + + debug = false + ) + dict.initFast() + + var table [maxTableSize]uint32 + + // sLimit is when to stop looking for offset/length copies. The inputMargin + // lets us use a fast path for emitLiteral in the main loop, while we are + // looking for copies. + sLimit := min(len(src)-inputMargin, MaxDictSrcOffset-maxAhead) + + // Bail if we can't compress to at least this. + dstLimit := len(src) - len(src)>>5 - 5 + + // nextEmit is where in src the next emitLiteral should start from. + nextEmit := 0 + + // The encoded form can start with a dict entry (copy or repeat). + s := 0 + + // Convert dict repeat to offset + repeat := len(dict.dict) - dict.repeat + cv := load64(src, 0) + + // While in dict +searchDict: + for { + // Next src position to check + nextS := s + (s-nextEmit)>>6 + 4 + hash0 := hash6(cv, tableBits) + hash1 := hash6(cv>>8, tableBits) + if nextS > sLimit { + if debug { + fmt.Println("slimit reached", s, nextS) + } + break searchDict + } + candidateDict := int(dict.fastTable[hash0]) + candidateDict2 := int(dict.fastTable[hash1]) + candidate2 := int(table[hash1]) + candidate := int(table[hash0]) + table[hash0] = uint32(s) + table[hash1] = uint32(s + 1) + hash2 := hash6(cv>>16, tableBits) + + // Check repeat at offset checkRep. + const checkRep = 1 + + if repeat > s { + candidate := len(dict.dict) - repeat + s + if repeat-s >= 4 && uint32(cv) == load32(dict.dict, candidate) { + // Extend back + base := s + for i := candidate; base > nextEmit && i > 0 && dict.dict[i-1] == src[base-1]; { + i-- + base-- + } + // Bail if we exceed the maximum size. + if d+(base-nextEmit) > dstLimit { + return 0 + } + + d += emitLiteral(dst[d:], src[nextEmit:base]) + if debug && nextEmit != base { + fmt.Println("emitted ", base-nextEmit, "literals") + } + s += 4 + candidate += 4 + for candidate < len(dict.dict)-8 && s <= len(src)-8 { + if diff := load64(src, s) ^ load64(dict.dict, candidate); diff != 0 { + s += bits.TrailingZeros64(diff) >> 3 + break + } + s += 8 + candidate += 8 + } + d += emitRepeat(dst[d:], repeat, s-base) + if debug { + fmt.Println("emitted dict repeat length", s-base, "offset:", repeat, "s:", s) + } + nextEmit = s + if s >= sLimit { + break searchDict + } + cv = load64(src, s) + continue + } + } else if uint32(cv>>(checkRep*8)) == load32(src, s-repeat+checkRep) { + base := s + checkRep + // Extend back + for i := base - repeat; base > nextEmit && i > 0 && src[i-1] == src[base-1]; { + i-- + base-- + } + d += emitLiteral(dst[d:], src[nextEmit:base]) + if debug && nextEmit != base { + fmt.Println("emitted ", base-nextEmit, "literals") + } + + // Extend forward + candidate := s - repeat + 4 + checkRep + s += 4 + checkRep + for s <= sLimit { + if diff := load64(src, s) ^ load64(src, candidate); diff != 0 { + s += bits.TrailingZeros64(diff) >> 3 + break + } + s += 8 + candidate += 8 + } + if debug { + // Validate match. + if s <= candidate { + panic("s <= candidate") + } + a := src[base:s] + b := src[base-repeat : base-repeat+(s-base)] + if !bytes.Equal(a, b) { + panic("mismatch") + } + } + + if nextEmit > 0 { + // same as `add := emitCopy(dst[d:], repeat, s-base)` but skips storing offset. + d += emitRepeat(dst[d:], repeat, s-base) + } else { + // First match, cannot be repeat. + d += emitCopy(dst[d:], repeat, s-base) + } + + nextEmit = s + if s >= sLimit { + break searchDict + } + if debug { + fmt.Println("emitted reg repeat", s-base, "s:", s) + } + cv = load64(src, s) + continue searchDict + } + if s == 0 { + cv = load64(src, nextS) + s = nextS + continue searchDict + } + // Start with table. These matches will always be closer. + if uint32(cv) == load32(src, candidate) { + goto emitMatch + } + candidate = int(table[hash2]) + if uint32(cv>>8) == load32(src, candidate2) { + table[hash2] = uint32(s + 2) + candidate = candidate2 + s++ + goto emitMatch + } + + // Check dict. Dicts have longer offsets, so we want longer matches. + if cv == load64(dict.dict, candidateDict) { + table[hash2] = uint32(s + 2) + goto emitDict + } + + candidateDict = int(dict.fastTable[hash2]) + // Check if upper 7 bytes match + if candidateDict2 >= 1 { + if cv^load64(dict.dict, candidateDict2-1) < (1 << 8) { + table[hash2] = uint32(s + 2) + candidateDict = candidateDict2 + s++ + goto emitDict + } + } + + table[hash2] = uint32(s + 2) + if uint32(cv>>16) == load32(src, candidate) { + s += 2 + goto emitMatch + } + if candidateDict >= 2 { + // Check if upper 6 bytes match + if cv^load64(dict.dict, candidateDict-2) < (1 << 16) { + s += 2 + goto emitDict + } + } + + cv = load64(src, nextS) + s = nextS + continue searchDict + + emitDict: + { + if debug { + if load32(dict.dict, candidateDict) != load32(src, s) { + panic("dict emit mismatch") + } + } + // Extend backwards. + // The top bytes will be rechecked to get the full match. + for candidateDict > 0 && s > nextEmit && dict.dict[candidateDict-1] == src[s-1] { + candidateDict-- + s-- + } + + // Bail if we exceed the maximum size. + if d+(s-nextEmit) > dstLimit { + return 0 + } + + // A 4-byte match has been found. We'll later see if more than 4 bytes + // match. But, prior to the match, src[nextEmit:s] are unmatched. Emit + // them as literal bytes. + + d += emitLiteral(dst[d:], src[nextEmit:s]) + if debug && nextEmit != s { + fmt.Println("emitted ", s-nextEmit, "literals") + } + { + // Invariant: we have a 4-byte match at s, and no need to emit any + // literal bytes prior to s. + base := s + repeat = s + (len(dict.dict)) - candidateDict + + // Extend the 4-byte match as long as possible. + s += 4 + candidateDict += 4 + for s <= len(src)-8 && len(dict.dict)-candidateDict >= 8 { + if diff := load64(src, s) ^ load64(dict.dict, candidateDict); diff != 0 { + s += bits.TrailingZeros64(diff) >> 3 + break + } + s += 8 + candidateDict += 8 + } + + // Matches longer than 64 are split. + if s <= sLimit || s-base < 8 { + d += emitCopy(dst[d:], repeat, s-base) + } else { + // Split to ensure we don't start a copy within next block + d += emitCopy(dst[d:], repeat, 4) + d += emitRepeat(dst[d:], repeat, s-base-4) + } + if false { + // Validate match. + if s <= candidate { + panic("s <= candidate") + } + a := src[base:s] + b := dict.dict[base-repeat : base-repeat+(s-base)] + if !bytes.Equal(a, b) { + panic("mismatch") + } + } + if debug { + fmt.Println("emitted dict copy, length", s-base, "offset:", repeat, "s:", s) + } + nextEmit = s + if s >= sLimit { + break searchDict + } + + if d > dstLimit { + // Do we have space for more, if not bail. + return 0 + } + + // Index and continue loop to try new candidate. + x := load64(src, s-2) + m2Hash := hash6(x, tableBits) + currHash := hash6(x>>8, tableBits) + table[m2Hash] = uint32(s - 2) + table[currHash] = uint32(s - 1) + cv = load64(src, s) + } + continue + } + emitMatch: + + // Extend backwards. + // The top bytes will be rechecked to get the full match. + for candidate > 0 && s > nextEmit && src[candidate-1] == src[s-1] { + candidate-- + s-- + } + + // Bail if we exceed the maximum size. + if d+(s-nextEmit) > dstLimit { + return 0 + } + + // A 4-byte match has been found. We'll later see if more than 4 bytes + // match. But, prior to the match, src[nextEmit:s] are unmatched. Emit + // them as literal bytes. + + d += emitLiteral(dst[d:], src[nextEmit:s]) + if debug && nextEmit != s { + fmt.Println("emitted ", s-nextEmit, "literals") + } + // Call emitCopy, and then see if another emitCopy could be our next + // move. Repeat until we find no match for the input immediately after + // what was consumed by the last emitCopy call. + // + // If we exit this loop normally then we need to call emitLiteral next, + // though we don't yet know how big the literal will be. We handle that + // by proceeding to the next iteration of the main loop. We also can + // exit this loop via goto if we get close to exhausting the input. + for { + // Invariant: we have a 4-byte match at s, and no need to emit any + // literal bytes prior to s. + base := s + repeat = base - candidate + + // Extend the 4-byte match as long as possible. + s += 4 + candidate += 4 + for s <= len(src)-8 { + if diff := load64(src, s) ^ load64(src, candidate); diff != 0 { + s += bits.TrailingZeros64(diff) >> 3 + break + } + s += 8 + candidate += 8 + } + + d += emitCopy(dst[d:], repeat, s-base) + if debug { + // Validate match. + if s <= candidate { + panic("s <= candidate") + } + a := src[base:s] + b := src[base-repeat : base-repeat+(s-base)] + if !bytes.Equal(a, b) { + panic("mismatch") + } + } + if debug { + fmt.Println("emitted src copy, length", s-base, "offset:", repeat, "s:", s) + } + nextEmit = s + if s >= sLimit { + break searchDict + } + + if d > dstLimit { + // Do we have space for more, if not bail. + return 0 + } + // Check for an immediate match, otherwise start search at s+1 + x := load64(src, s-2) + m2Hash := hash6(x, tableBits) + currHash := hash6(x>>16, tableBits) + candidate = int(table[currHash]) + table[m2Hash] = uint32(s - 2) + table[currHash] = uint32(s) + if debug && s == candidate { + panic("s == candidate") + } + if uint32(x>>16) != load32(src, candidate) { + cv = load64(src, s+1) + s++ + break + } + } + } + + // Search without dict: + if repeat > s { + repeat = 0 + } + + // No more dict + sLimit = len(src) - inputMargin + if s >= sLimit { + goto emitRemainder + } + if debug { + fmt.Println("non-dict matching at", s, "repeat:", repeat) + } + cv = load64(src, s) + if debug { + fmt.Println("now", s, "->", sLimit, "out:", d, "left:", len(src)-s, "nextemit:", nextEmit, "dstLimit:", dstLimit, "s:", s) + } + for { + candidate := 0 + for { + // Next src position to check + nextS := s + (s-nextEmit)>>6 + 4 + if nextS > sLimit { + goto emitRemainder + } + hash0 := hash6(cv, tableBits) + hash1 := hash6(cv>>8, tableBits) + candidate = int(table[hash0]) + candidate2 := int(table[hash1]) + table[hash0] = uint32(s) + table[hash1] = uint32(s + 1) + hash2 := hash6(cv>>16, tableBits) + + // Check repeat at offset checkRep. + const checkRep = 1 + if repeat > 0 && uint32(cv>>(checkRep*8)) == load32(src, s-repeat+checkRep) { + base := s + checkRep + // Extend back + for i := base - repeat; base > nextEmit && i > 0 && src[i-1] == src[base-1]; { + i-- + base-- + } + // Bail if we exceed the maximum size. + if d+(base-nextEmit) > dstLimit { + return 0 + } + + d += emitLiteral(dst[d:], src[nextEmit:base]) + if debug && nextEmit != base { + fmt.Println("emitted ", base-nextEmit, "literals") + } + // Extend forward + candidate := s - repeat + 4 + checkRep + s += 4 + checkRep + for s <= sLimit { + if diff := load64(src, s) ^ load64(src, candidate); diff != 0 { + s += bits.TrailingZeros64(diff) >> 3 + break + } + s += 8 + candidate += 8 + } + if debug { + // Validate match. + if s <= candidate { + panic("s <= candidate") + } + a := src[base:s] + b := src[base-repeat : base-repeat+(s-base)] + if !bytes.Equal(a, b) { + panic("mismatch") + } + } + if nextEmit > 0 { + // same as `add := emitCopy(dst[d:], repeat, s-base)` but skips storing offset. + d += emitRepeat(dst[d:], repeat, s-base) + } else { + // First match, cannot be repeat. + d += emitCopy(dst[d:], repeat, s-base) + } + if debug { + fmt.Println("emitted src repeat length", s-base, "offset:", repeat, "s:", s) + } + nextEmit = s + if s >= sLimit { + goto emitRemainder + } + + cv = load64(src, s) + continue + } + + if uint32(cv) == load32(src, candidate) { + break + } + candidate = int(table[hash2]) + if uint32(cv>>8) == load32(src, candidate2) { + table[hash2] = uint32(s + 2) + candidate = candidate2 + s++ + break + } + table[hash2] = uint32(s + 2) + if uint32(cv>>16) == load32(src, candidate) { + s += 2 + break + } + + cv = load64(src, nextS) + s = nextS + } + + // Extend backwards. + // The top bytes will be rechecked to get the full match. + for candidate > 0 && s > nextEmit && src[candidate-1] == src[s-1] { + candidate-- + s-- + } + + // Bail if we exceed the maximum size. + if d+(s-nextEmit) > dstLimit { + return 0 + } + + // A 4-byte match has been found. We'll later see if more than 4 bytes + // match. But, prior to the match, src[nextEmit:s] are unmatched. Emit + // them as literal bytes. + + d += emitLiteral(dst[d:], src[nextEmit:s]) + if debug && nextEmit != s { + fmt.Println("emitted ", s-nextEmit, "literals") + } + // Call emitCopy, and then see if another emitCopy could be our next + // move. Repeat until we find no match for the input immediately after + // what was consumed by the last emitCopy call. + // + // If we exit this loop normally then we need to call emitLiteral next, + // though we don't yet know how big the literal will be. We handle that + // by proceeding to the next iteration of the main loop. We also can + // exit this loop via goto if we get close to exhausting the input. + for { + // Invariant: we have a 4-byte match at s, and no need to emit any + // literal bytes prior to s. + base := s + repeat = base - candidate + + // Extend the 4-byte match as long as possible. + s += 4 + candidate += 4 + for s <= len(src)-8 { + if diff := load64(src, s) ^ load64(src, candidate); diff != 0 { + s += bits.TrailingZeros64(diff) >> 3 + break + } + s += 8 + candidate += 8 + } + + d += emitCopy(dst[d:], repeat, s-base) + if debug { + // Validate match. + if s <= candidate { + panic("s <= candidate") + } + a := src[base:s] + b := src[base-repeat : base-repeat+(s-base)] + if !bytes.Equal(a, b) { + panic("mismatch") + } + } + if debug { + fmt.Println("emitted src copy, length", s-base, "offset:", repeat, "s:", s) + } + nextEmit = s + if s >= sLimit { + goto emitRemainder + } + + if d > dstLimit { + // Do we have space for more, if not bail. + return 0 + } + // Check for an immediate match, otherwise start search at s+1 + x := load64(src, s-2) + m2Hash := hash6(x, tableBits) + currHash := hash6(x>>16, tableBits) + candidate = int(table[currHash]) + table[m2Hash] = uint32(s - 2) + table[currHash] = uint32(s) + if debug && s == candidate { + panic("s == candidate") + } + if uint32(x>>16) != load32(src, candidate) { + cv = load64(src, s+1) + s++ + break + } + } + } + +emitRemainder: + if nextEmit < len(src) { + // Bail if we exceed the maximum size. + if d+len(src)-nextEmit > dstLimit { + return 0 + } + d += emitLiteral(dst[d:], src[nextEmit:]) + if debug && nextEmit != s { + fmt.Println("emitted ", len(src)-nextEmit, "literals") + } + } + return d +} diff --git a/vendor/github.com/klauspost/compress/s2/encode_amd64.go b/vendor/github.com/klauspost/compress/s2/encode_amd64.go new file mode 100644 index 000000000..68d72a41d --- /dev/null +++ b/vendor/github.com/klauspost/compress/s2/encode_amd64.go @@ -0,0 +1,316 @@ +//go:build !appengine && !noasm && gc + +package s2 + +import ( + "sync" + + "github.com/klauspost/compress/internal/race" +) + +const hasAmd64Asm = true + +var encPools [4]sync.Pool + +// encodeBlock encodes a non-empty src to a guaranteed-large-enough dst. It +// assumes that the varint-encoded length of the decompressed bytes has already +// been written. +// +// It also assumes that: +// +// len(dst) >= MaxEncodedLen(len(src)) && +// minNonLiteralBlockSize <= len(src) && len(src) <= maxBlockSize +func encodeBlock(dst, src []byte) (d int) { + race.ReadSlice(src) + race.WriteSlice(dst) + + const ( + // Use 12 bit table when less than... + limit12B = 16 << 10 + // Use 10 bit table when less than... + limit10B = 4 << 10 + // Use 8 bit table when less than... + limit8B = 512 + ) + + if len(src) >= 4<<20 { + const sz, pool = 65536, 0 + tmp, ok := encPools[pool].Get().(*[sz]byte) + if !ok { + tmp = &[sz]byte{} + } + race.WriteSlice(tmp[:]) + defer encPools[pool].Put(tmp) + return encodeBlockAsm(dst, src, tmp) + } + if len(src) >= limit12B { + const sz, pool = 65536, 0 + tmp, ok := encPools[pool].Get().(*[sz]byte) + if !ok { + tmp = &[sz]byte{} + } + race.WriteSlice(tmp[:]) + defer encPools[pool].Put(tmp) + return encodeBlockAsm4MB(dst, src, tmp) + } + if len(src) >= limit10B { + const sz, pool = 16384, 1 + tmp, ok := encPools[pool].Get().(*[sz]byte) + if !ok { + tmp = &[sz]byte{} + } + race.WriteSlice(tmp[:]) + defer encPools[pool].Put(tmp) + return encodeBlockAsm12B(dst, src, tmp) + } + if len(src) >= limit8B { + const sz, pool = 4096, 2 + tmp, ok := encPools[pool].Get().(*[sz]byte) + if !ok { + tmp = &[sz]byte{} + } + race.WriteSlice(tmp[:]) + defer encPools[pool].Put(tmp) + return encodeBlockAsm10B(dst, src, tmp) + } + if len(src) < minNonLiteralBlockSize { + return 0 + } + const sz, pool = 1024, 3 + tmp, ok := encPools[pool].Get().(*[sz]byte) + if !ok { + tmp = &[sz]byte{} + } + race.WriteSlice(tmp[:]) + defer encPools[pool].Put(tmp) + return encodeBlockAsm8B(dst, src, tmp) +} + +var encBetterPools [5]sync.Pool + +// encodeBlockBetter encodes a non-empty src to a guaranteed-large-enough dst. It +// assumes that the varint-encoded length of the decompressed bytes has already +// been written. +// +// It also assumes that: +// +// len(dst) >= MaxEncodedLen(len(src)) && +// minNonLiteralBlockSize <= len(src) && len(src) <= maxBlockSize +func encodeBlockBetter(dst, src []byte) (d int) { + race.ReadSlice(src) + race.WriteSlice(dst) + + const ( + // Use 12 bit table when less than... + limit12B = 16 << 10 + // Use 10 bit table when less than... + limit10B = 4 << 10 + // Use 8 bit table when less than... + limit8B = 512 + ) + + if len(src) > 4<<20 { + const sz, pool = 589824, 0 + tmp, ok := encBetterPools[pool].Get().(*[sz]byte) + if !ok { + tmp = &[sz]byte{} + } + race.WriteSlice(tmp[:]) + defer encBetterPools[pool].Put(tmp) + return encodeBetterBlockAsm(dst, src, tmp) + } + if len(src) >= limit12B { + const sz, pool = 589824, 0 + tmp, ok := encBetterPools[pool].Get().(*[sz]byte) + if !ok { + tmp = &[sz]byte{} + } + race.WriteSlice(tmp[:]) + defer encBetterPools[pool].Put(tmp) + + return encodeBetterBlockAsm4MB(dst, src, tmp) + } + if len(src) >= limit10B { + const sz, pool = 81920, 0 + tmp, ok := encBetterPools[pool].Get().(*[sz]byte) + if !ok { + tmp = &[sz]byte{} + } + race.WriteSlice(tmp[:]) + defer encBetterPools[pool].Put(tmp) + + return encodeBetterBlockAsm12B(dst, src, tmp) + } + if len(src) >= limit8B { + const sz, pool = 20480, 1 + tmp, ok := encBetterPools[pool].Get().(*[sz]byte) + if !ok { + tmp = &[sz]byte{} + } + race.WriteSlice(tmp[:]) + defer encBetterPools[pool].Put(tmp) + return encodeBetterBlockAsm10B(dst, src, tmp) + } + if len(src) < minNonLiteralBlockSize { + return 0 + } + + const sz, pool = 5120, 2 + tmp, ok := encBetterPools[pool].Get().(*[sz]byte) + if !ok { + tmp = &[sz]byte{} + } + race.WriteSlice(tmp[:]) + defer encBetterPools[pool].Put(tmp) + return encodeBetterBlockAsm8B(dst, src, tmp) +} + +// encodeBlockSnappy encodes a non-empty src to a guaranteed-large-enough dst. It +// assumes that the varint-encoded length of the decompressed bytes has already +// been written. +// +// It also assumes that: +// +// len(dst) >= MaxEncodedLen(len(src)) && +// minNonLiteralBlockSize <= len(src) && len(src) <= maxBlockSize +func encodeBlockSnappy(dst, src []byte) (d int) { + race.ReadSlice(src) + race.WriteSlice(dst) + + const ( + // Use 12 bit table when less than... + limit12B = 16 << 10 + // Use 10 bit table when less than... + limit10B = 4 << 10 + // Use 8 bit table when less than... + limit8B = 512 + ) + if len(src) > 65536 { + const sz, pool = 65536, 0 + tmp, ok := encPools[pool].Get().(*[sz]byte) + if !ok { + tmp = &[sz]byte{} + } + race.WriteSlice(tmp[:]) + defer encPools[pool].Put(tmp) + return encodeSnappyBlockAsm(dst, src, tmp) + } + if len(src) >= limit12B { + const sz, pool = 65536, 0 + tmp, ok := encPools[pool].Get().(*[sz]byte) + if !ok { + tmp = &[sz]byte{} + } + race.WriteSlice(tmp[:]) + defer encPools[pool].Put(tmp) + return encodeSnappyBlockAsm64K(dst, src, tmp) + } + if len(src) >= limit10B { + const sz, pool = 16384, 1 + tmp, ok := encPools[pool].Get().(*[sz]byte) + if !ok { + tmp = &[sz]byte{} + } + race.WriteSlice(tmp[:]) + defer encPools[pool].Put(tmp) + return encodeSnappyBlockAsm12B(dst, src, tmp) + } + if len(src) >= limit8B { + const sz, pool = 4096, 2 + tmp, ok := encPools[pool].Get().(*[sz]byte) + if !ok { + tmp = &[sz]byte{} + } + race.WriteSlice(tmp[:]) + defer encPools[pool].Put(tmp) + return encodeSnappyBlockAsm10B(dst, src, tmp) + } + if len(src) < minNonLiteralBlockSize { + return 0 + } + const sz, pool = 1024, 3 + tmp, ok := encPools[pool].Get().(*[sz]byte) + if !ok { + tmp = &[sz]byte{} + } + race.WriteSlice(tmp[:]) + defer encPools[pool].Put(tmp) + return encodeSnappyBlockAsm8B(dst, src, tmp) +} + +// encodeBlockSnappy encodes a non-empty src to a guaranteed-large-enough dst. It +// assumes that the varint-encoded length of the decompressed bytes has already +// been written. +// +// It also assumes that: +// +// len(dst) >= MaxEncodedLen(len(src)) && +// minNonLiteralBlockSize <= len(src) && len(src) <= maxBlockSize +func encodeBlockBetterSnappy(dst, src []byte) (d int) { + race.ReadSlice(src) + race.WriteSlice(dst) + + const ( + // Use 12 bit table when less than... + limit12B = 16 << 10 + // Use 10 bit table when less than... + limit10B = 4 << 10 + // Use 8 bit table when less than... + limit8B = 512 + ) + if len(src) > 65536 { + const sz, pool = 589824, 0 + tmp, ok := encBetterPools[pool].Get().(*[sz]byte) + if !ok { + tmp = &[sz]byte{} + } + race.WriteSlice(tmp[:]) + defer encBetterPools[pool].Put(tmp) + return encodeSnappyBetterBlockAsm(dst, src, tmp) + } + + if len(src) >= limit12B { + const sz, pool = 294912, 4 + tmp, ok := encBetterPools[pool].Get().(*[sz]byte) + if !ok { + tmp = &[sz]byte{} + } + race.WriteSlice(tmp[:]) + defer encBetterPools[pool].Put(tmp) + + return encodeSnappyBetterBlockAsm64K(dst, src, tmp) + } + if len(src) >= limit10B { + const sz, pool = 81920, 0 + tmp, ok := encBetterPools[pool].Get().(*[sz]byte) + if !ok { + tmp = &[sz]byte{} + } + race.WriteSlice(tmp[:]) + defer encBetterPools[pool].Put(tmp) + + return encodeSnappyBetterBlockAsm12B(dst, src, tmp) + } + if len(src) >= limit8B { + const sz, pool = 20480, 1 + tmp, ok := encBetterPools[pool].Get().(*[sz]byte) + if !ok { + tmp = &[sz]byte{} + } + race.WriteSlice(tmp[:]) + defer encBetterPools[pool].Put(tmp) + return encodeSnappyBetterBlockAsm10B(dst, src, tmp) + } + if len(src) < minNonLiteralBlockSize { + return 0 + } + + const sz, pool = 5120, 2 + tmp, ok := encBetterPools[pool].Get().(*[sz]byte) + if !ok { + tmp = &[sz]byte{} + } + race.WriteSlice(tmp[:]) + defer encBetterPools[pool].Put(tmp) + return encodeSnappyBetterBlockAsm8B(dst, src, tmp) +} diff --git a/vendor/github.com/klauspost/compress/s2/encode_best.go b/vendor/github.com/klauspost/compress/s2/encode_best.go new file mode 100644 index 000000000..49f2166ec --- /dev/null +++ b/vendor/github.com/klauspost/compress/s2/encode_best.go @@ -0,0 +1,797 @@ +// Copyright 2016 The Snappy-Go Authors. All rights reserved. +// Copyright (c) 2019 Klaus Post. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package s2 + +import ( + "fmt" + "math" + "math/bits" +) + +// encodeBlockBest encodes a non-empty src to a guaranteed-large-enough dst. It +// assumes that the varint-encoded length of the decompressed bytes has already +// been written. +// +// It also assumes that: +// +// len(dst) >= MaxEncodedLen(len(src)) && +// minNonLiteralBlockSize <= len(src) && len(src) <= maxBlockSize +func encodeBlockBest(dst, src []byte, dict *Dict) (d int) { + // Initialize the hash tables. + const ( + // Long hash matches. + lTableBits = bestLongTableBits + maxLTableSize = bestLongTableSize + + // Short hash matches. + sTableBits = bestShortTableBits + maxSTableSize = bestShortTableSize + + inputMargin = 8 + 2 + + debug = false + ) + + // sLimit is when to stop looking for offset/length copies. The inputMargin + // lets us use a fast path for emitLiteral in the main loop, while we are + // looking for copies. + sLimit := len(src) - inputMargin + if len(src) < minNonLiteralBlockSize { + return 0 + } + sLimitDict := min(len(src)-inputMargin, MaxDictSrcOffset-inputMargin) + + tbl := getBestTables() + lTable := &tbl.lTable + sTable := &tbl.sTable + defer bestTablePool.Put(tbl) + + // Bail if we can't compress to at least this. + dstLimit := len(src) - 5 + + // nextEmit is where in src the next emitLiteral should start from. + nextEmit := 0 + + // The encoded form must start with a literal, as there are no previous + // bytes to copy, so we start looking for hash matches at s == 1. + s := 1 + repeat := 1 + if dict != nil { + dict.initBest() + s = 0 + repeat = len(dict.dict) - dict.repeat + } + cv := load64(src, s) + + // We search for a repeat at -1, but don't output repeats when nextEmit == 0 + const lowbitMask = 0xffffffff + getCur := func(x uint64) int { + return int(x & lowbitMask) + } + getPrev := func(x uint64) int { + return int(x >> 32) + } + const maxSkip = 64 + + for { + type match struct { + offset int + s int + length int + score int + rep, dict bool + } + var best match + for { + // Next src position to check + nextS := (s-nextEmit)>>8 + 1 + if nextS > maxSkip { + nextS = s + maxSkip + } else { + nextS += s + } + if nextS > sLimit { + goto emitRemainder + } + if dict != nil && s >= MaxDictSrcOffset { + dict = nil + if repeat > s { + repeat = math.MinInt32 + } + } + hashL := hash8(cv, lTableBits) + hashS := hash4(cv, sTableBits) + candidateL := lTable[hashL] + candidateS := sTable[hashS] + + score := func(m match) int { + // Matches that are longer forward are penalized since we must emit it as a literal. + score := m.length - m.s + if nextEmit == m.s { + // If we do not have to emit literals, we save 1 byte + score++ + } + offset := m.s - m.offset + if m.rep { + return score - emitRepeatSize(offset, m.length) + } + return score - emitCopySize(offset, m.length) + } + + matchAt := func(offset, s int, first uint32, rep bool) match { + if best.length != 0 && best.s-best.offset == s-offset { + // Don't retest if we have the same offset. + return match{offset: offset, s: s} + } + if load32(src, offset) != first { + return match{offset: offset, s: s} + } + m := match{offset: offset, s: s, length: 4 + offset, rep: rep} + s += 4 + for s < len(src) { + if len(src)-s < 8 { + if src[s] == src[m.length] { + m.length++ + s++ + continue + } + break + } + if diff := load64(src, s) ^ load64(src, m.length); diff != 0 { + m.length += bits.TrailingZeros64(diff) >> 3 + break + } + s += 8 + m.length += 8 + } + m.length -= offset + m.score = score(m) + if m.score <= -m.s { + // Eliminate if no savings, we might find a better one. + m.length = 0 + } + return m + } + matchDict := func(candidate, s int, first uint32, rep bool) match { + if s >= MaxDictSrcOffset { + return match{offset: candidate, s: s} + } + // Calculate offset as if in continuous array with s + offset := -len(dict.dict) + candidate + if best.length != 0 && best.s-best.offset == s-offset && !rep { + // Don't retest if we have the same offset. + return match{offset: offset, s: s} + } + + if load32(dict.dict, candidate) != first { + return match{offset: offset, s: s} + } + m := match{offset: offset, s: s, length: 4 + candidate, rep: rep, dict: true} + s += 4 + if !rep { + for s < sLimitDict && m.length < len(dict.dict) { + if len(src)-s < 8 || len(dict.dict)-m.length < 8 { + if src[s] == dict.dict[m.length] { + m.length++ + s++ + continue + } + break + } + if diff := load64(src, s) ^ load64(dict.dict, m.length); diff != 0 { + m.length += bits.TrailingZeros64(diff) >> 3 + break + } + s += 8 + m.length += 8 + } + } else { + for s < len(src) && m.length < len(dict.dict) { + if len(src)-s < 8 || len(dict.dict)-m.length < 8 { + if src[s] == dict.dict[m.length] { + m.length++ + s++ + continue + } + break + } + if diff := load64(src, s) ^ load64(dict.dict, m.length); diff != 0 { + m.length += bits.TrailingZeros64(diff) >> 3 + break + } + s += 8 + m.length += 8 + } + } + m.length -= candidate + m.score = score(m) + if m.score <= -m.s { + // Eliminate if no savings, we might find a better one. + m.length = 0 + } + return m + } + + bestOf := func(a, b match) match { + if b.length == 0 { + return a + } + if a.length == 0 { + return b + } + as := a.score + b.s + bs := b.score + a.s + if as >= bs { + return a + } + return b + } + + if s > 0 { + best = bestOf(matchAt(getCur(candidateL), s, uint32(cv), false), matchAt(getPrev(candidateL), s, uint32(cv), false)) + best = bestOf(best, matchAt(getCur(candidateS), s, uint32(cv), false)) + best = bestOf(best, matchAt(getPrev(candidateS), s, uint32(cv), false)) + } + if dict != nil { + candidateL := dict.bestTableLong[hashL] + candidateS := dict.bestTableShort[hashS] + best = bestOf(best, matchDict(int(candidateL&0xffff), s, uint32(cv), false)) + best = bestOf(best, matchDict(int(candidateL>>16), s, uint32(cv), false)) + best = bestOf(best, matchDict(int(candidateS&0xffff), s, uint32(cv), false)) + best = bestOf(best, matchDict(int(candidateS>>16), s, uint32(cv), false)) + } + { + if (dict == nil || repeat <= s) && repeat > 0 { + best = bestOf(best, matchAt(s-repeat+1, s+1, uint32(cv>>8), true)) + } else if s-repeat < -4 && dict != nil { + candidate := len(dict.dict) - (repeat - s) + best = bestOf(best, matchDict(candidate, s, uint32(cv), true)) + candidate++ + best = bestOf(best, matchDict(candidate, s+1, uint32(cv>>8), true)) + } + + if best.length > 0 { + hashS := hash4(cv>>8, sTableBits) + // s+1 + nextShort := sTable[hashS] + s := s + 1 + cv := load64(src, s) + hashL := hash8(cv, lTableBits) + nextLong := lTable[hashL] + best = bestOf(best, matchAt(getCur(nextShort), s, uint32(cv), false)) + best = bestOf(best, matchAt(getPrev(nextShort), s, uint32(cv), false)) + best = bestOf(best, matchAt(getCur(nextLong), s, uint32(cv), false)) + best = bestOf(best, matchAt(getPrev(nextLong), s, uint32(cv), false)) + + // Dict at + 1 + if dict != nil { + candidateL := dict.bestTableLong[hashL] + candidateS := dict.bestTableShort[hashS] + + best = bestOf(best, matchDict(int(candidateL&0xffff), s, uint32(cv), false)) + best = bestOf(best, matchDict(int(candidateS&0xffff), s, uint32(cv), false)) + } + + // s+2 + if true { + hashS := hash4(cv>>8, sTableBits) + + nextShort = sTable[hashS] + s++ + cv = load64(src, s) + hashL := hash8(cv, lTableBits) + nextLong = lTable[hashL] + + if (dict == nil || repeat <= s) && repeat > 0 { + // Repeat at + 2 + best = bestOf(best, matchAt(s-repeat, s, uint32(cv), true)) + } else if repeat-s > 4 && dict != nil { + candidate := len(dict.dict) - (repeat - s) + best = bestOf(best, matchDict(candidate, s, uint32(cv), true)) + } + best = bestOf(best, matchAt(getCur(nextShort), s, uint32(cv), false)) + best = bestOf(best, matchAt(getPrev(nextShort), s, uint32(cv), false)) + best = bestOf(best, matchAt(getCur(nextLong), s, uint32(cv), false)) + best = bestOf(best, matchAt(getPrev(nextLong), s, uint32(cv), false)) + + // Dict at +2 + // Very small gain + if dict != nil { + candidateL := dict.bestTableLong[hashL] + candidateS := dict.bestTableShort[hashS] + + best = bestOf(best, matchDict(int(candidateL&0xffff), s, uint32(cv), false)) + best = bestOf(best, matchDict(int(candidateS&0xffff), s, uint32(cv), false)) + } + } + // Search for a match at best match end, see if that is better. + // Allow some bytes at the beginning to mismatch. + // Sweet spot is around 1-2 bytes, but depends on input. + // The skipped bytes are tested in Extend backwards, + // and still picked up as part of the match if they do. + const skipBeginning = 2 + const skipEnd = 1 + if sAt := best.s + best.length - skipEnd; sAt < sLimit { + + sBack := best.s + skipBeginning - skipEnd + backL := best.length - skipBeginning + // Load initial values + cv = load64(src, sBack) + + // Grab candidates... + next := lTable[hash8(load64(src, sAt), lTableBits)] + + if checkAt := getCur(next) - backL; checkAt > 0 { + best = bestOf(best, matchAt(checkAt, sBack, uint32(cv), false)) + } + if checkAt := getPrev(next) - backL; checkAt > 0 { + best = bestOf(best, matchAt(checkAt, sBack, uint32(cv), false)) + } + // Disabled: Extremely small gain + if false { + next = sTable[hash4(load64(src, sAt), sTableBits)] + if checkAt := getCur(next) - backL; checkAt > 0 { + best = bestOf(best, matchAt(checkAt, sBack, uint32(cv), false)) + } + if checkAt := getPrev(next) - backL; checkAt > 0 { + best = bestOf(best, matchAt(checkAt, sBack, uint32(cv), false)) + } + } + } + } + } + + // Update table + lTable[hashL] = uint64(s) | candidateL<<32 + sTable[hashS] = uint64(s) | candidateS<<32 + + if best.length > 0 { + break + } + + cv = load64(src, nextS) + s = nextS + } + + // Extend backwards, not needed for repeats... + s = best.s + if !best.rep && !best.dict { + for best.offset > 0 && s > nextEmit && src[best.offset-1] == src[s-1] { + best.offset-- + best.length++ + s-- + } + } + if false && best.offset >= s { + panic(fmt.Errorf("t %d >= s %d", best.offset, s)) + } + // Bail if we exceed the maximum size. + if d+(s-nextEmit) > dstLimit { + return 0 + } + + base := s + offset := s - best.offset + s += best.length + + if offset > 65535 && s-base <= 5 && !best.rep { + // Bail if the match is equal or worse to the encoding. + s = best.s + 1 + if s >= sLimit { + goto emitRemainder + } + cv = load64(src, s) + continue + } + if debug && nextEmit != base { + fmt.Println("EMIT", base-nextEmit, "literals. base-after:", base) + } + d += emitLiteral(dst[d:], src[nextEmit:base]) + if best.rep { + if nextEmit > 0 || best.dict { + if debug { + fmt.Println("REPEAT, length", best.length, "offset:", offset, "s-after:", s, "dict:", best.dict, "best:", best) + } + // same as `add := emitCopy(dst[d:], repeat, s-base)` but skips storing offset. + d += emitRepeat(dst[d:], offset, best.length) + } else { + // First match without dict cannot be a repeat. + if debug { + fmt.Println("COPY, length", best.length, "offset:", offset, "s-after:", s, "dict:", best.dict, "best:", best) + } + d += emitCopy(dst[d:], offset, best.length) + } + } else { + if debug { + fmt.Println("COPY, length", best.length, "offset:", offset, "s-after:", s, "dict:", best.dict, "best:", best) + } + d += emitCopy(dst[d:], offset, best.length) + } + repeat = offset + + nextEmit = s + if s >= sLimit { + goto emitRemainder + } + + if d > dstLimit { + // Do we have space for more, if not bail. + return 0 + } + // Fill tables... + for i := best.s + 1; i < s; i++ { + cv0 := load64(src, i) + long0 := hash8(cv0, lTableBits) + short0 := hash4(cv0, sTableBits) + lTable[long0] = uint64(i) | lTable[long0]<<32 + sTable[short0] = uint64(i) | sTable[short0]<<32 + } + cv = load64(src, s) + } + +emitRemainder: + if nextEmit < len(src) { + // Bail if we exceed the maximum size. + if d+len(src)-nextEmit > dstLimit { + return 0 + } + if debug && nextEmit != s { + fmt.Println("emitted ", len(src)-nextEmit, "literals") + } + d += emitLiteral(dst[d:], src[nextEmit:]) + } + return d +} + +// encodeBlockBestSnappy encodes a non-empty src to a guaranteed-large-enough dst. It +// assumes that the varint-encoded length of the decompressed bytes has already +// been written. +// +// It also assumes that: +// +// len(dst) >= MaxEncodedLen(len(src)) && +// minNonLiteralBlockSize <= len(src) && len(src) <= maxBlockSize +func encodeBlockBestSnappy(dst, src []byte) (d int) { + // Initialize the hash tables. + const ( + // Long hash matches. + lTableBits = bestLongTableBits + maxLTableSize = bestLongTableSize + + // Short hash matches. + sTableBits = bestShortTableBits + maxSTableSize = bestShortTableSize + + inputMargin = 8 + 2 + ) + + // sLimit is when to stop looking for offset/length copies. The inputMargin + // lets us use a fast path for emitLiteral in the main loop, while we are + // looking for copies. + sLimit := len(src) - inputMargin + if len(src) < minNonLiteralBlockSize { + return 0 + } + + tbl := getBestTables() + lTable := &tbl.lTable + sTable := &tbl.sTable + defer bestTablePool.Put(tbl) + + // Bail if we can't compress to at least this. + dstLimit := len(src) - 5 + + // nextEmit is where in src the next emitLiteral should start from. + nextEmit := 0 + + // The encoded form must start with a literal, as there are no previous + // bytes to copy, so we start looking for hash matches at s == 1. + s := 1 + cv := load64(src, s) + + // We search for a repeat at -1, but don't output repeats when nextEmit == 0 + repeat := 1 + const lowbitMask = 0xffffffff + getCur := func(x uint64) int { + return int(x & lowbitMask) + } + getPrev := func(x uint64) int { + return int(x >> 32) + } + const maxSkip = 64 + + for { + type match struct { + offset int + s int + length int + score int + } + var best match + for { + // Next src position to check + nextS := (s-nextEmit)>>8 + 1 + if nextS > maxSkip { + nextS = s + maxSkip + } else { + nextS += s + } + if nextS > sLimit { + goto emitRemainder + } + hashL := hash8(cv, lTableBits) + hashS := hash4(cv, sTableBits) + candidateL := lTable[hashL] + candidateS := sTable[hashS] + + score := func(m match) int { + // Matches that are longer forward are penalized since we must emit it as a literal. + score := m.length - m.s + if nextEmit == m.s { + // If we do not have to emit literals, we save 1 byte + score++ + } + offset := m.s - m.offset + + return score - emitCopyNoRepeatSize(offset, m.length) + } + + matchAt := func(offset, s int, first uint32) match { + if best.length != 0 && best.s-best.offset == s-offset { + // Don't retest if we have the same offset. + return match{offset: offset, s: s} + } + if load32(src, offset) != first { + return match{offset: offset, s: s} + } + m := match{offset: offset, s: s, length: 4 + offset} + s += 4 + for s <= sLimit { + if diff := load64(src, s) ^ load64(src, m.length); diff != 0 { + m.length += bits.TrailingZeros64(diff) >> 3 + break + } + s += 8 + m.length += 8 + } + m.length -= offset + m.score = score(m) + if m.score <= -m.s { + // Eliminate if no savings, we might find a better one. + m.length = 0 + } + return m + } + + bestOf := func(a, b match) match { + if b.length == 0 { + return a + } + if a.length == 0 { + return b + } + as := a.score + b.s + bs := b.score + a.s + if as >= bs { + return a + } + return b + } + + best = bestOf(matchAt(getCur(candidateL), s, uint32(cv)), matchAt(getPrev(candidateL), s, uint32(cv))) + best = bestOf(best, matchAt(getCur(candidateS), s, uint32(cv))) + best = bestOf(best, matchAt(getPrev(candidateS), s, uint32(cv))) + + { + best = bestOf(best, matchAt(s-repeat+1, s+1, uint32(cv>>8))) + if best.length > 0 { + // s+1 + nextShort := sTable[hash4(cv>>8, sTableBits)] + s := s + 1 + cv := load64(src, s) + nextLong := lTable[hash8(cv, lTableBits)] + best = bestOf(best, matchAt(getCur(nextShort), s, uint32(cv))) + best = bestOf(best, matchAt(getPrev(nextShort), s, uint32(cv))) + best = bestOf(best, matchAt(getCur(nextLong), s, uint32(cv))) + best = bestOf(best, matchAt(getPrev(nextLong), s, uint32(cv))) + // Repeat at + 2 + best = bestOf(best, matchAt(s-repeat+1, s+1, uint32(cv>>8))) + + // s+2 + if true { + nextShort = sTable[hash4(cv>>8, sTableBits)] + s++ + cv = load64(src, s) + nextLong = lTable[hash8(cv, lTableBits)] + best = bestOf(best, matchAt(getCur(nextShort), s, uint32(cv))) + best = bestOf(best, matchAt(getPrev(nextShort), s, uint32(cv))) + best = bestOf(best, matchAt(getCur(nextLong), s, uint32(cv))) + best = bestOf(best, matchAt(getPrev(nextLong), s, uint32(cv))) + } + // Search for a match at best match end, see if that is better. + if sAt := best.s + best.length; sAt < sLimit { + sBack := best.s + backL := best.length + // Load initial values + cv = load64(src, sBack) + // Search for mismatch + next := lTable[hash8(load64(src, sAt), lTableBits)] + //next := sTable[hash4(load64(src, sAt), sTableBits)] + + if checkAt := getCur(next) - backL; checkAt > 0 { + best = bestOf(best, matchAt(checkAt, sBack, uint32(cv))) + } + if checkAt := getPrev(next) - backL; checkAt > 0 { + best = bestOf(best, matchAt(checkAt, sBack, uint32(cv))) + } + } + } + } + + // Update table + lTable[hashL] = uint64(s) | candidateL<<32 + sTable[hashS] = uint64(s) | candidateS<<32 + + if best.length > 0 { + break + } + + cv = load64(src, nextS) + s = nextS + } + + // Extend backwards, not needed for repeats... + s = best.s + if true { + for best.offset > 0 && s > nextEmit && src[best.offset-1] == src[s-1] { + best.offset-- + best.length++ + s-- + } + } + if false && best.offset >= s { + panic(fmt.Errorf("t %d >= s %d", best.offset, s)) + } + // Bail if we exceed the maximum size. + if d+(s-nextEmit) > dstLimit { + return 0 + } + + base := s + offset := s - best.offset + + s += best.length + + if offset > 65535 && s-base <= 5 { + // Bail if the match is equal or worse to the encoding. + s = best.s + 1 + if s >= sLimit { + goto emitRemainder + } + cv = load64(src, s) + continue + } + d += emitLiteral(dst[d:], src[nextEmit:base]) + d += emitCopyNoRepeat(dst[d:], offset, best.length) + repeat = offset + + nextEmit = s + if s >= sLimit { + goto emitRemainder + } + + if d > dstLimit { + // Do we have space for more, if not bail. + return 0 + } + // Fill tables... + for i := best.s + 1; i < s; i++ { + cv0 := load64(src, i) + long0 := hash8(cv0, lTableBits) + short0 := hash4(cv0, sTableBits) + lTable[long0] = uint64(i) | lTable[long0]<<32 + sTable[short0] = uint64(i) | sTable[short0]<<32 + } + cv = load64(src, s) + } + +emitRemainder: + if nextEmit < len(src) { + // Bail if we exceed the maximum size. + if d+len(src)-nextEmit > dstLimit { + return 0 + } + d += emitLiteral(dst[d:], src[nextEmit:]) + } + return d +} + +// emitCopySize returns the size to encode the offset+length +// +// It assumes that: +// +// 1 <= offset && offset <= math.MaxUint32 +// 4 <= length && length <= 1 << 24 +func emitCopySize(offset, length int) int { + if offset >= 65536 { + i := 0 + if length > 64 { + length -= 64 + if length >= 4 { + // Emit remaining as repeats + return 5 + emitRepeatSize(offset, length) + } + i = 5 + } + if length == 0 { + return i + } + return i + 5 + } + + // Offset no more than 2 bytes. + if length > 64 { + if offset < 2048 { + // Emit 8 bytes, then rest as repeats... + return 2 + emitRepeatSize(offset, length-8) + } + // Emit remaining as repeats, at least 4 bytes remain. + return 3 + emitRepeatSize(offset, length-60) + } + if length >= 12 || offset >= 2048 { + return 3 + } + // Emit the remaining copy, encoded as 2 bytes. + return 2 +} + +// emitCopyNoRepeatSize returns the size to encode the offset+length +// +// It assumes that: +// +// 1 <= offset && offset <= math.MaxUint32 +// 4 <= length && length <= 1 << 24 +func emitCopyNoRepeatSize(offset, length int) int { + if offset >= 65536 { + return 5 + 5*(length/64) + } + + // Offset no more than 2 bytes. + if length > 64 { + // Emit remaining as repeats, at least 4 bytes remain. + return 3 + 3*(length/60) + } + if length >= 12 || offset >= 2048 { + return 3 + } + // Emit the remaining copy, encoded as 2 bytes. + return 2 +} + +// emitRepeatSize returns the number of bytes required to encode a repeat. +// Length must be at least 4 and < 1<<24 +func emitRepeatSize(offset, length int) int { + // Repeat offset, make length cheaper + if length <= 4+4 || (length < 8+4 && offset < 2048) { + return 2 + } + if length < (1<<8)+4+4 { + return 3 + } + if length < (1<<16)+(1<<8)+4 { + return 4 + } + const maxRepeat = (1 << 24) - 1 + length -= (1 << 16) - 4 + left := 0 + if length > maxRepeat { + left = length - maxRepeat + 4 + } + if left > 0 { + return 5 + emitRepeatSize(offset, left) + } + return 5 +} diff --git a/vendor/github.com/klauspost/compress/s2/encode_better.go b/vendor/github.com/klauspost/compress/s2/encode_better.go new file mode 100644 index 000000000..adbc57c4d --- /dev/null +++ b/vendor/github.com/klauspost/compress/s2/encode_better.go @@ -0,0 +1,1513 @@ +// Copyright 2016 The Snappy-Go Authors. All rights reserved. +// Copyright (c) 2019 Klaus Post. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package s2 + +import ( + "bytes" + "fmt" + "math/bits" +) + +// hash4 returns the hash of the lowest 4 bytes of u to fit in a hash table with h bits. +// Preferably h should be a constant and should always be <32. +func hash4(u uint64, h uint8) uint32 { + const prime4bytes = 2654435761 + return (uint32(u) * prime4bytes) >> ((32 - h) & 31) +} + +// hash5 returns the hash of the lowest 5 bytes of u to fit in a hash table with h bits. +// Preferably h should be a constant and should always be <64. +func hash5(u uint64, h uint8) uint32 { + const prime5bytes = 889523592379 + return uint32(((u << (64 - 40)) * prime5bytes) >> ((64 - h) & 63)) +} + +// hash7 returns the hash of the lowest 7 bytes of u to fit in a hash table with h bits. +// Preferably h should be a constant and should always be <64. +func hash7(u uint64, h uint8) uint32 { + const prime7bytes = 58295818150454627 + return uint32(((u << (64 - 56)) * prime7bytes) >> ((64 - h) & 63)) +} + +// hash8 returns the hash of u to fit in a hash table with h bits. +// Preferably h should be a constant and should always be <64. +func hash8(u uint64, h uint8) uint32 { + const prime8bytes = 0xcf1bbcdcb7a56463 + return uint32((u * prime8bytes) >> ((64 - h) & 63)) +} + +// encodeBlockBetter encodes a non-empty src to a guaranteed-large-enough dst. It +// assumes that the varint-encoded length of the decompressed bytes has already +// been written. +// +// It also assumes that: +// +// len(dst) >= MaxEncodedLen(len(src)) && +// minNonLiteralBlockSize <= len(src) && len(src) <= maxBlockSize +func encodeBlockBetterGo(dst, src []byte) (d int) { + // sLimit is when to stop looking for offset/length copies. The inputMargin + // lets us use a fast path for emitLiteral in the main loop, while we are + // looking for copies. + sLimit := len(src) - inputMargin + if len(src) < minNonLiteralBlockSize { + return 0 + } + + // Initialize the hash tables. + const ( + // Long hash matches. + lTableBits = betterLongTableBits + maxLTableSize = betterLongTableSize + + // Short hash matches. + sTableBits = betterShortTableBits + maxSTableSize = betterShortTableSize + ) + + tbl := getBetterTables() + lTable := &tbl.lTable + sTable := &tbl.sTable + defer betterTablePool.Put(tbl) + + // Bail if we can't compress to at least this. + dstLimit := len(src) - len(src)>>5 - 6 + + // nextEmit is where in src the next emitLiteral should start from. + nextEmit := 0 + + // The encoded form must start with a literal, as there are no previous + // bytes to copy, so we start looking for hash matches at s == 1. + s := 1 + cv := load64(src, s) + + // We initialize repeat to 0, so we never match on first attempt + repeat := 0 + + for { + candidateL := 0 + nextS := 0 + for { + // Next src position to check + nextS = s + (s-nextEmit)>>7 + 1 + if nextS > sLimit { + goto emitRemainder + } + hashL := hash7(cv, lTableBits) + hashS := hash4(cv, sTableBits) + candidateL = int(lTable[hashL]) + candidateS := int(sTable[hashS]) + lTable[hashL] = uint32(s) + sTable[hashS] = uint32(s) + + valLong := load64(src, candidateL) + valShort := load64(src, candidateS) + + // If long matches at least 8 bytes, use that. + if cv == valLong { + break + } + if cv == valShort { + candidateL = candidateS + break + } + + // Check repeat at offset checkRep. + const checkRep = 1 + // Minimum length of a repeat. Tested with various values. + // While 4-5 offers improvements in some, 6 reduces + // regressions significantly. + const wantRepeatBytes = 6 + const repeatMask = ((1 << (wantRepeatBytes * 8)) - 1) << (8 * checkRep) + if false && repeat > 0 && cv&repeatMask == load64(src, s-repeat)&repeatMask { + base := s + checkRep + // Extend back + for i := base - repeat; base > nextEmit && i > 0 && src[i-1] == src[base-1]; { + i-- + base-- + } + d += emitLiteral(dst[d:], src[nextEmit:base]) + + // Extend forward + candidate := s - repeat + wantRepeatBytes + checkRep + s += wantRepeatBytes + checkRep + for s < len(src) { + if len(src)-s < 8 { + if src[s] == src[candidate] { + s++ + candidate++ + continue + } + break + } + if diff := load64(src, s) ^ load64(src, candidate); diff != 0 { + s += bits.TrailingZeros64(diff) >> 3 + break + } + s += 8 + candidate += 8 + } + // same as `add := emitCopy(dst[d:], repeat, s-base)` but skips storing offset. + d += emitRepeat(dst[d:], repeat, s-base) + nextEmit = s + if s >= sLimit { + goto emitRemainder + } + // Index in-between + index0 := base + 1 + index1 := s - 2 + + for index0 < index1 { + cv0 := load64(src, index0) + cv1 := load64(src, index1) + lTable[hash7(cv0, lTableBits)] = uint32(index0) + sTable[hash4(cv0>>8, sTableBits)] = uint32(index0 + 1) + + lTable[hash7(cv1, lTableBits)] = uint32(index1) + sTable[hash4(cv1>>8, sTableBits)] = uint32(index1 + 1) + index0 += 2 + index1 -= 2 + } + + cv = load64(src, s) + continue + } + + // Long likely matches 7, so take that. + if uint32(cv) == uint32(valLong) { + break + } + + // Check our short candidate + if uint32(cv) == uint32(valShort) { + // Try a long candidate at s+1 + hashL = hash7(cv>>8, lTableBits) + candidateL = int(lTable[hashL]) + lTable[hashL] = uint32(s + 1) + if uint32(cv>>8) == load32(src, candidateL) { + s++ + break + } + // Use our short candidate. + candidateL = candidateS + break + } + + cv = load64(src, nextS) + s = nextS + } + + // Extend backwards + for candidateL > 0 && s > nextEmit && src[candidateL-1] == src[s-1] { + candidateL-- + s-- + } + + // Bail if we exceed the maximum size. + if d+(s-nextEmit) > dstLimit { + return 0 + } + + base := s + offset := base - candidateL + + // Extend the 4-byte match as long as possible. + s += 4 + candidateL += 4 + for s < len(src) { + if len(src)-s < 8 { + if src[s] == src[candidateL] { + s++ + candidateL++ + continue + } + break + } + if diff := load64(src, s) ^ load64(src, candidateL); diff != 0 { + s += bits.TrailingZeros64(diff) >> 3 + break + } + s += 8 + candidateL += 8 + } + + if offset > 65535 && s-base <= 5 && repeat != offset { + // Bail if the match is equal or worse to the encoding. + s = nextS + 1 + if s >= sLimit { + goto emitRemainder + } + cv = load64(src, s) + continue + } + + d += emitLiteral(dst[d:], src[nextEmit:base]) + if repeat == offset { + d += emitRepeat(dst[d:], offset, s-base) + } else { + d += emitCopy(dst[d:], offset, s-base) + repeat = offset + } + + nextEmit = s + if s >= sLimit { + goto emitRemainder + } + + if d > dstLimit { + // Do we have space for more, if not bail. + return 0 + } + + // Index short & long + index0 := base + 1 + index1 := s - 2 + + cv0 := load64(src, index0) + cv1 := load64(src, index1) + lTable[hash7(cv0, lTableBits)] = uint32(index0) + sTable[hash4(cv0>>8, sTableBits)] = uint32(index0 + 1) + + // lTable could be postponed, but very minor difference. + lTable[hash7(cv1, lTableBits)] = uint32(index1) + sTable[hash4(cv1>>8, sTableBits)] = uint32(index1 + 1) + index0 += 1 + index1 -= 1 + cv = load64(src, s) + + // Index large values sparsely in between. + // We do two starting from different offsets for speed. + index2 := (index0 + index1 + 1) >> 1 + for index2 < index1 { + lTable[hash7(load64(src, index0), lTableBits)] = uint32(index0) + lTable[hash7(load64(src, index2), lTableBits)] = uint32(index2) + index0 += 2 + index2 += 2 + } + } + +emitRemainder: + if nextEmit < len(src) { + // Bail if we exceed the maximum size. + if d+len(src)-nextEmit > dstLimit { + return 0 + } + d += emitLiteral(dst[d:], src[nextEmit:]) + } + return d +} + +// encodeBlockBetterSnappyGo encodes a non-empty src to a guaranteed-large-enough dst. It +// assumes that the varint-encoded length of the decompressed bytes has already +// been written. +// +// It also assumes that: +// +// len(dst) >= MaxEncodedLen(len(src)) && +// minNonLiteralBlockSize <= len(src) && len(src) <= maxBlockSize +func encodeBlockBetterSnappyGo(dst, src []byte) (d int) { + // sLimit is when to stop looking for offset/length copies. The inputMargin + // lets us use a fast path for emitLiteral in the main loop, while we are + // looking for copies. + sLimit := len(src) - inputMargin + if len(src) < minNonLiteralBlockSize { + return 0 + } + + // Initialize the hash tables. + const ( + // Long hash matches. + lTableBits = betterSnappyLongTableBits + maxLTableSize = betterSnappyLongTableSize + + // Short hash matches. + sTableBits = betterShortTableBits + maxSTableSize = betterShortTableSize + ) + + tbl := getBetterSnappyTables() + lTable := &tbl.lTable + sTable := &tbl.sTable + defer betterSnappyTablePool.Put(tbl) + + // Bail if we can't compress to at least this. + dstLimit := len(src) - len(src)>>5 - 6 + + // nextEmit is where in src the next emitLiteral should start from. + nextEmit := 0 + + // The encoded form must start with a literal, as there are no previous + // bytes to copy, so we start looking for hash matches at s == 1. + s := 1 + cv := load64(src, s) + + // We initialize repeat to 0, so we never match on first attempt + repeat := 0 + const maxSkip = 100 + + for { + candidateL := 0 + nextS := 0 + for { + // Next src position to check + nextS = min(s+(s-nextEmit)>>7+1, s+maxSkip) + + if nextS > sLimit { + goto emitRemainder + } + hashL := hash7(cv, lTableBits) + hashS := hash4(cv, sTableBits) + candidateL = int(lTable[hashL]) + candidateS := int(sTable[hashS]) + lTable[hashL] = uint32(s) + sTable[hashS] = uint32(s) + + if uint32(cv) == load32(src, candidateL) { + break + } + + // Check our short candidate + if uint32(cv) == load32(src, candidateS) { + // Try a long candidate at s+1 + hashL = hash7(cv>>8, lTableBits) + candidateL = int(lTable[hashL]) + lTable[hashL] = uint32(s + 1) + if uint32(cv>>8) == load32(src, candidateL) { + s++ + break + } + // Use our short candidate. + candidateL = candidateS + break + } + + cv = load64(src, nextS) + s = nextS + } + + // Extend backwards + for candidateL > 0 && s > nextEmit && src[candidateL-1] == src[s-1] { + candidateL-- + s-- + } + + // Bail if we exceed the maximum size. + if d+(s-nextEmit) > dstLimit { + return 0 + } + + base := s + offset := base - candidateL + + // Extend the 4-byte match as long as possible. + s += 4 + candidateL += 4 + for s < len(src) { + if len(src)-s < 8 { + if src[s] == src[candidateL] { + s++ + candidateL++ + continue + } + break + } + if diff := load64(src, s) ^ load64(src, candidateL); diff != 0 { + s += bits.TrailingZeros64(diff) >> 3 + break + } + s += 8 + candidateL += 8 + } + + if offset > 65535 && s-base <= 5 && repeat != offset { + // Bail if the match is equal or worse to the encoding. + s = nextS + 1 + if s >= sLimit { + goto emitRemainder + } + cv = load64(src, s) + continue + } + + d += emitLiteral(dst[d:], src[nextEmit:base]) + d += emitCopyNoRepeat(dst[d:], offset, s-base) + repeat = offset + + nextEmit = s + if s >= sLimit { + goto emitRemainder + } + + if d > dstLimit { + // Do we have space for more, if not bail. + return 0 + } + + // Index short & long + index0 := base + 1 + index1 := s - 2 + + cv0 := load64(src, index0) + cv1 := load64(src, index1) + lTable[hash7(cv0, lTableBits)] = uint32(index0) + sTable[hash4(cv0>>8, sTableBits)] = uint32(index0 + 1) + + lTable[hash7(cv1, lTableBits)] = uint32(index1) + sTable[hash4(cv1>>8, sTableBits)] = uint32(index1 + 1) + index0 += 1 + index1 -= 1 + cv = load64(src, s) + + // Index large values sparsely in between. + // We do two starting from different offsets for speed. + index2 := (index0 + index1 + 1) >> 1 + for index2 < index1 { + lTable[hash7(load64(src, index0), lTableBits)] = uint32(index0) + lTable[hash7(load64(src, index2), lTableBits)] = uint32(index2) + index0 += 2 + index2 += 2 + } + } + +emitRemainder: + if nextEmit < len(src) { + // Bail if we exceed the maximum size. + if d+len(src)-nextEmit > dstLimit { + return 0 + } + d += emitLiteral(dst[d:], src[nextEmit:]) + } + return d +} + +func encodeBlockBetterGo64K(dst, src []byte) (d int) { + // sLimit is when to stop looking for offset/length copies. The inputMargin + // lets us use a fast path for emitLiteral in the main loop, while we are + // looking for copies. + sLimit := len(src) - inputMargin + if len(src) < minNonLiteralBlockSize { + return 0 + } + // Initialize the hash tables. + // Use smaller tables for smaller blocks + const ( + // Long hash matches. + lTableBits = 16 + maxLTableSize = 1 << lTableBits + + // Short hash matches. + sTableBits = 13 + maxSTableSize = 1 << sTableBits + ) + + var lTable [maxLTableSize]uint16 + var sTable [maxSTableSize]uint16 + + // Bail if we can't compress to at least this. + dstLimit := len(src) - len(src)>>5 - 6 + + // nextEmit is where in src the next emitLiteral should start from. + nextEmit := 0 + + // The encoded form must start with a literal, as there are no previous + // bytes to copy, so we start looking for hash matches at s == 1. + s := 1 + cv := load64(src, s) + + // We initialize repeat to 0, so we never match on first attempt + repeat := 0 + + for { + candidateL := 0 + nextS := 0 + for { + // Next src position to check + nextS = s + (s-nextEmit)>>6 + 1 + if nextS > sLimit { + goto emitRemainder + } + hashL := hash7(cv, lTableBits) + hashS := hash4(cv, sTableBits) + candidateL = int(lTable[hashL]) + candidateS := int(sTable[hashS]) + lTable[hashL] = uint16(s) + sTable[hashS] = uint16(s) + + valLong := load64(src, candidateL) + valShort := load64(src, candidateS) + + // If long matches at least 8 bytes, use that. + if cv == valLong { + break + } + if cv == valShort { + candidateL = candidateS + break + } + + // Check repeat at offset checkRep. + const checkRep = 1 + // Minimum length of a repeat. Tested with various values. + // While 4-5 offers improvements in some, 6 reduces + // regressions significantly. + const wantRepeatBytes = 6 + const repeatMask = ((1 << (wantRepeatBytes * 8)) - 1) << (8 * checkRep) + if false && repeat > 0 && cv&repeatMask == load64(src, s-repeat)&repeatMask { + base := s + checkRep + // Extend back + for i := base - repeat; base > nextEmit && i > 0 && src[i-1] == src[base-1]; { + i-- + base-- + } + d += emitLiteral(dst[d:], src[nextEmit:base]) + + // Extend forward + candidate := s - repeat + wantRepeatBytes + checkRep + s += wantRepeatBytes + checkRep + for s < len(src) { + if len(src)-s < 8 { + if src[s] == src[candidate] { + s++ + candidate++ + continue + } + break + } + if diff := load64(src, s) ^ load64(src, candidate); diff != 0 { + s += bits.TrailingZeros64(diff) >> 3 + break + } + s += 8 + candidate += 8 + } + // same as `add := emitCopy(dst[d:], repeat, s-base)` but skips storing offset. + d += emitRepeat(dst[d:], repeat, s-base) + nextEmit = s + if s >= sLimit { + goto emitRemainder + } + // Index in-between + index0 := base + 1 + index1 := s - 2 + + for index0 < index1 { + cv0 := load64(src, index0) + cv1 := load64(src, index1) + lTable[hash7(cv0, lTableBits)] = uint16(index0) + sTable[hash4(cv0>>8, sTableBits)] = uint16(index0 + 1) + + lTable[hash7(cv1, lTableBits)] = uint16(index1) + sTable[hash4(cv1>>8, sTableBits)] = uint16(index1 + 1) + index0 += 2 + index1 -= 2 + } + + cv = load64(src, s) + continue + } + + // Long likely matches 7, so take that. + if uint32(cv) == uint32(valLong) { + break + } + + // Check our short candidate + if uint32(cv) == uint32(valShort) { + // Try a long candidate at s+1 + hashL = hash7(cv>>8, lTableBits) + candidateL = int(lTable[hashL]) + lTable[hashL] = uint16(s + 1) + if uint32(cv>>8) == load32(src, candidateL) { + s++ + break + } + // Use our short candidate. + candidateL = candidateS + break + } + + cv = load64(src, nextS) + s = nextS + } + + // Extend backwards + for candidateL > 0 && s > nextEmit && src[candidateL-1] == src[s-1] { + candidateL-- + s-- + } + + // Bail if we exceed the maximum size. + if d+(s-nextEmit) > dstLimit { + return 0 + } + + base := s + offset := base - candidateL + + // Extend the 4-byte match as long as possible. + s += 4 + candidateL += 4 + for s < len(src) { + if len(src)-s < 8 { + if src[s] == src[candidateL] { + s++ + candidateL++ + continue + } + break + } + if diff := load64(src, s) ^ load64(src, candidateL); diff != 0 { + s += bits.TrailingZeros64(diff) >> 3 + break + } + s += 8 + candidateL += 8 + } + + d += emitLiteral(dst[d:], src[nextEmit:base]) + if repeat == offset { + d += emitRepeat(dst[d:], offset, s-base) + } else { + d += emitCopy(dst[d:], offset, s-base) + repeat = offset + } + + nextEmit = s + if s >= sLimit { + goto emitRemainder + } + + if d > dstLimit { + // Do we have space for more, if not bail. + return 0 + } + + // Index short & long + index0 := base + 1 + index1 := s - 2 + + cv0 := load64(src, index0) + cv1 := load64(src, index1) + lTable[hash7(cv0, lTableBits)] = uint16(index0) + sTable[hash4(cv0>>8, sTableBits)] = uint16(index0 + 1) + + // lTable could be postponed, but very minor difference. + lTable[hash7(cv1, lTableBits)] = uint16(index1) + sTable[hash4(cv1>>8, sTableBits)] = uint16(index1 + 1) + index0 += 1 + index1 -= 1 + cv = load64(src, s) + + // Index large values sparsely in between. + // We do two starting from different offsets for speed. + index2 := (index0 + index1 + 1) >> 1 + for index2 < index1 { + lTable[hash7(load64(src, index0), lTableBits)] = uint16(index0) + lTable[hash7(load64(src, index2), lTableBits)] = uint16(index2) + index0 += 2 + index2 += 2 + } + } + +emitRemainder: + if nextEmit < len(src) { + // Bail if we exceed the maximum size. + if d+len(src)-nextEmit > dstLimit { + return 0 + } + d += emitLiteral(dst[d:], src[nextEmit:]) + } + return d +} + +// encodeBlockBetterSnappyGo encodes a non-empty src to a guaranteed-large-enough dst. It +// assumes that the varint-encoded length of the decompressed bytes has already +// been written. +// +// It also assumes that: +// +// len(dst) >= MaxEncodedLen(len(src)) && +// minNonLiteralBlockSize <= len(src) && len(src) <= maxBlockSize +func encodeBlockBetterSnappyGo64K(dst, src []byte) (d int) { + // sLimit is when to stop looking for offset/length copies. The inputMargin + // lets us use a fast path for emitLiteral in the main loop, while we are + // looking for copies. + sLimit := len(src) - inputMargin + if len(src) < minNonLiteralBlockSize { + return 0 + } + + // Initialize the hash tables. + // Use smaller tables for smaller blocks + const ( + // Long hash matches. + lTableBits = 15 + maxLTableSize = 1 << lTableBits + + // Short hash matches. + sTableBits = 13 + maxSTableSize = 1 << sTableBits + ) + + var lTable [maxLTableSize]uint16 + var sTable [maxSTableSize]uint16 + + // Bail if we can't compress to at least this. + dstLimit := len(src) - len(src)>>5 - 6 + + // nextEmit is where in src the next emitLiteral should start from. + nextEmit := 0 + + // The encoded form must start with a literal, as there are no previous + // bytes to copy, so we start looking for hash matches at s == 1. + s := 1 + cv := load64(src, s) + + const maxSkip = 100 + + for { + candidateL := 0 + nextS := 0 + for { + // Next src position to check + nextS = min(s+(s-nextEmit)>>6+1, s+maxSkip) + + if nextS > sLimit { + goto emitRemainder + } + hashL := hash7(cv, lTableBits) + hashS := hash4(cv, sTableBits) + candidateL = int(lTable[hashL]) + candidateS := int(sTable[hashS]) + lTable[hashL] = uint16(s) + sTable[hashS] = uint16(s) + + if uint32(cv) == load32(src, candidateL) { + break + } + + // Check our short candidate + if uint32(cv) == load32(src, candidateS) { + // Try a long candidate at s+1 + hashL = hash7(cv>>8, lTableBits) + candidateL = int(lTable[hashL]) + lTable[hashL] = uint16(s + 1) + if uint32(cv>>8) == load32(src, candidateL) { + s++ + break + } + // Use our short candidate. + candidateL = candidateS + break + } + + cv = load64(src, nextS) + s = nextS + } + + // Extend backwards + for candidateL > 0 && s > nextEmit && src[candidateL-1] == src[s-1] { + candidateL-- + s-- + } + + // Bail if we exceed the maximum size. + if d+(s-nextEmit) > dstLimit { + return 0 + } + + base := s + offset := base - candidateL + + // Extend the 4-byte match as long as possible. + s += 4 + candidateL += 4 + for s < len(src) { + if len(src)-s < 8 { + if src[s] == src[candidateL] { + s++ + candidateL++ + continue + } + break + } + if diff := load64(src, s) ^ load64(src, candidateL); diff != 0 { + s += bits.TrailingZeros64(diff) >> 3 + break + } + s += 8 + candidateL += 8 + } + + d += emitLiteral(dst[d:], src[nextEmit:base]) + d += emitCopyNoRepeat(dst[d:], offset, s-base) + + nextEmit = s + if s >= sLimit { + goto emitRemainder + } + + if d > dstLimit { + // Do we have space for more, if not bail. + return 0 + } + + // Index short & long + index0 := base + 1 + index1 := s - 2 + + cv0 := load64(src, index0) + cv1 := load64(src, index1) + lTable[hash7(cv0, lTableBits)] = uint16(index0) + sTable[hash4(cv0>>8, sTableBits)] = uint16(index0 + 1) + + lTable[hash7(cv1, lTableBits)] = uint16(index1) + sTable[hash4(cv1>>8, sTableBits)] = uint16(index1 + 1) + index0 += 1 + index1 -= 1 + cv = load64(src, s) + + // Index large values sparsely in between. + // We do two starting from different offsets for speed. + index2 := (index0 + index1 + 1) >> 1 + for index2 < index1 { + lTable[hash7(load64(src, index0), lTableBits)] = uint16(index0) + lTable[hash7(load64(src, index2), lTableBits)] = uint16(index2) + index0 += 2 + index2 += 2 + } + } + +emitRemainder: + if nextEmit < len(src) { + // Bail if we exceed the maximum size. + if d+len(src)-nextEmit > dstLimit { + return 0 + } + d += emitLiteral(dst[d:], src[nextEmit:]) + } + return d +} + +// encodeBlockBetterDict encodes a non-empty src to a guaranteed-large-enough dst. It +// assumes that the varint-encoded length of the decompressed bytes has already +// been written. +// +// It also assumes that: +// +// len(dst) >= MaxEncodedLen(len(src)) && +// minNonLiteralBlockSize <= len(src) && len(src) <= maxBlockSize +func encodeBlockBetterDict(dst, src []byte, dict *Dict) (d int) { + // sLimit is when to stop looking for offset/length copies. The inputMargin + // lets us use a fast path for emitLiteral in the main loop, while we are + // looking for copies. + // Initialize the hash tables. + const ( + // Long hash matches. + lTableBits = betterLongTableBits + maxLTableSize = betterLongTableSize + + // Short hash matches. + sTableBits = betterShortTableBits + maxSTableSize = betterShortTableSize + + maxAhead = 8 // maximum bytes ahead without checking sLimit + + debug = false + ) + + sLimit := min(len(src)-inputMargin, MaxDictSrcOffset-maxAhead) + if len(src) < minNonLiteralBlockSize { + return 0 + } + + dict.initBetter() + + tbl := getBetterTables() + lTable := &tbl.lTable + sTable := &tbl.sTable + defer betterTablePool.Put(tbl) + + // Bail if we can't compress to at least this. + dstLimit := len(src) - len(src)>>5 - 6 + + // nextEmit is where in src the next emitLiteral should start from. + nextEmit := 0 + + // The encoded form must start with a literal, as there are no previous + // bytes to copy, so we start looking for hash matches at s == 1. + s := 0 + cv := load64(src, s) + + // We initialize repeat to 0, so we never match on first attempt + repeat := len(dict.dict) - dict.repeat + + // While in dict +searchDict: + for { + candidateL := 0 + nextS := 0 + for { + // Next src position to check + nextS = s + (s-nextEmit)>>7 + 1 + if nextS > sLimit { + break searchDict + } + hashL := hash7(cv, lTableBits) + hashS := hash4(cv, sTableBits) + candidateL = int(lTable[hashL]) + candidateS := int(sTable[hashS]) + dictL := int(dict.betterTableLong[hashL]) + dictS := int(dict.betterTableShort[hashS]) + lTable[hashL] = uint32(s) + sTable[hashS] = uint32(s) + + valLong := load64(src, candidateL) + valShort := load64(src, candidateS) + + // If long matches at least 8 bytes, use that. + if s != 0 { + if cv == valLong { + goto emitMatch + } + if cv == valShort { + candidateL = candidateS + goto emitMatch + } + } + + // Check dict repeat. + if repeat >= s+4 { + candidate := len(dict.dict) - repeat + s + if candidate > 0 && uint32(cv) == load32(dict.dict, candidate) { + // Extend back + base := s + for i := candidate; base > nextEmit && i > 0 && dict.dict[i-1] == src[base-1]; { + i-- + base-- + } + d += emitLiteral(dst[d:], src[nextEmit:base]) + if debug && nextEmit != base { + fmt.Println("emitted ", base-nextEmit, "literals") + } + s += 4 + candidate += 4 + for candidate < len(dict.dict)-8 && s <= len(src)-8 { + if diff := load64(src, s) ^ load64(dict.dict, candidate); diff != 0 { + s += bits.TrailingZeros64(diff) >> 3 + break + } + s += 8 + candidate += 8 + } + d += emitRepeat(dst[d:], repeat, s-base) + if debug { + fmt.Println("emitted dict repeat length", s-base, "offset:", repeat, "s:", s) + } + nextEmit = s + if s >= sLimit { + break searchDict + } + // Index in-between + index0 := base + 1 + index1 := s - 2 + + cv = load64(src, s) + for index0 < index1 { + cv0 := load64(src, index0) + cv1 := load64(src, index1) + lTable[hash7(cv0, lTableBits)] = uint32(index0) + sTable[hash4(cv0>>8, sTableBits)] = uint32(index0 + 1) + + lTable[hash7(cv1, lTableBits)] = uint32(index1) + sTable[hash4(cv1>>8, sTableBits)] = uint32(index1 + 1) + index0 += 2 + index1 -= 2 + } + continue + } + } + // Don't try to find match at s==0 + if s == 0 { + cv = load64(src, nextS) + s = nextS + continue + } + + // Long likely matches 7, so take that. + if uint32(cv) == uint32(valLong) { + goto emitMatch + } + + // Long dict... + if uint32(cv) == load32(dict.dict, dictL) { + candidateL = dictL + goto emitDict + } + + // Check our short candidate + if uint32(cv) == uint32(valShort) { + // Try a long candidate at s+1 + hashL = hash7(cv>>8, lTableBits) + candidateL = int(lTable[hashL]) + lTable[hashL] = uint32(s + 1) + if uint32(cv>>8) == load32(src, candidateL) { + s++ + goto emitMatch + } + // Use our short candidate. + candidateL = candidateS + goto emitMatch + } + if uint32(cv) == load32(dict.dict, dictS) { + // Try a long candidate at s+1 + hashL = hash7(cv>>8, lTableBits) + candidateL = int(lTable[hashL]) + lTable[hashL] = uint32(s + 1) + if uint32(cv>>8) == load32(src, candidateL) { + s++ + goto emitMatch + } + candidateL = dictS + goto emitDict + } + cv = load64(src, nextS) + s = nextS + } + emitDict: + { + if debug { + if load32(dict.dict, candidateL) != load32(src, s) { + panic("dict emit mismatch") + } + } + // Extend backwards. + // The top bytes will be rechecked to get the full match. + for candidateL > 0 && s > nextEmit && dict.dict[candidateL-1] == src[s-1] { + candidateL-- + s-- + } + + // Bail if we exceed the maximum size. + if d+(s-nextEmit) > dstLimit { + return 0 + } + + // A 4-byte match has been found. We'll later see if more than 4 bytes + // match. But, prior to the match, src[nextEmit:s] are unmatched. Emit + // them as literal bytes. + + d += emitLiteral(dst[d:], src[nextEmit:s]) + if debug && nextEmit != s { + fmt.Println("emitted ", s-nextEmit, "literals") + } + { + // Invariant: we have a 4-byte match at s, and no need to emit any + // literal bytes prior to s. + base := s + offset := s + (len(dict.dict)) - candidateL + + // Extend the 4-byte match as long as possible. + s += 4 + candidateL += 4 + for s <= len(src)-8 && len(dict.dict)-candidateL >= 8 { + if diff := load64(src, s) ^ load64(dict.dict, candidateL); diff != 0 { + s += bits.TrailingZeros64(diff) >> 3 + break + } + s += 8 + candidateL += 8 + } + + if repeat == offset { + if debug { + fmt.Println("emitted dict repeat, length", s-base, "offset:", offset, "s:", s, "dict offset:", candidateL) + } + d += emitRepeat(dst[d:], offset, s-base) + } else { + if debug { + fmt.Println("emitted dict copy, length", s-base, "offset:", offset, "s:", s, "dict offset:", candidateL) + } + // Matches longer than 64 are split. + if s <= sLimit || s-base < 8 { + d += emitCopy(dst[d:], offset, s-base) + } else { + // Split to ensure we don't start a copy within next block. + d += emitCopy(dst[d:], offset, 4) + d += emitRepeat(dst[d:], offset, s-base-4) + } + repeat = offset + } + if false { + // Validate match. + if s <= candidateL { + panic("s <= candidate") + } + a := src[base:s] + b := dict.dict[base-repeat : base-repeat+(s-base)] + if !bytes.Equal(a, b) { + panic("mismatch") + } + } + + nextEmit = s + if s >= sLimit { + break searchDict + } + + if d > dstLimit { + // Do we have space for more, if not bail. + return 0 + } + + // Index short & long + index0 := base + 1 + index1 := s - 2 + + cv0 := load64(src, index0) + cv1 := load64(src, index1) + lTable[hash7(cv0, lTableBits)] = uint32(index0) + sTable[hash4(cv0>>8, sTableBits)] = uint32(index0 + 1) + + lTable[hash7(cv1, lTableBits)] = uint32(index1) + sTable[hash4(cv1>>8, sTableBits)] = uint32(index1 + 1) + index0 += 1 + index1 -= 1 + cv = load64(src, s) + + // index every second long in between. + for index0 < index1 { + lTable[hash7(load64(src, index0), lTableBits)] = uint32(index0) + lTable[hash7(load64(src, index1), lTableBits)] = uint32(index1) + index0 += 2 + index1 -= 2 + } + } + continue + } + emitMatch: + + // Extend backwards + for candidateL > 0 && s > nextEmit && src[candidateL-1] == src[s-1] { + candidateL-- + s-- + } + + // Bail if we exceed the maximum size. + if d+(s-nextEmit) > dstLimit { + return 0 + } + + base := s + offset := base - candidateL + + // Extend the 4-byte match as long as possible. + s += 4 + candidateL += 4 + for s < len(src) { + if len(src)-s < 8 { + if src[s] == src[candidateL] { + s++ + candidateL++ + continue + } + break + } + if diff := load64(src, s) ^ load64(src, candidateL); diff != 0 { + s += bits.TrailingZeros64(diff) >> 3 + break + } + s += 8 + candidateL += 8 + } + + if offset > 65535 && s-base <= 5 && repeat != offset { + // Bail if the match is equal or worse to the encoding. + s = nextS + 1 + if s >= sLimit { + goto emitRemainder + } + cv = load64(src, s) + continue + } + + d += emitLiteral(dst[d:], src[nextEmit:base]) + if debug && nextEmit != s { + fmt.Println("emitted ", s-nextEmit, "literals") + } + if repeat == offset { + if debug { + fmt.Println("emitted match repeat, length", s-base, "offset:", offset, "s:", s) + } + d += emitRepeat(dst[d:], offset, s-base) + } else { + if debug { + fmt.Println("emitted match copy, length", s-base, "offset:", offset, "s:", s) + } + d += emitCopy(dst[d:], offset, s-base) + repeat = offset + } + + nextEmit = s + if s >= sLimit { + goto emitRemainder + } + + if d > dstLimit { + // Do we have space for more, if not bail. + return 0 + } + + // Index short & long + index0 := base + 1 + index1 := s - 2 + + cv0 := load64(src, index0) + cv1 := load64(src, index1) + lTable[hash7(cv0, lTableBits)] = uint32(index0) + sTable[hash4(cv0>>8, sTableBits)] = uint32(index0 + 1) + + lTable[hash7(cv1, lTableBits)] = uint32(index1) + sTable[hash4(cv1>>8, sTableBits)] = uint32(index1 + 1) + index0 += 1 + index1 -= 1 + cv = load64(src, s) + + // Index large values sparsely in between. + // We do two starting from different offsets for speed. + index2 := (index0 + index1 + 1) >> 1 + for index2 < index1 { + lTable[hash7(load64(src, index0), lTableBits)] = uint32(index0) + lTable[hash7(load64(src, index2), lTableBits)] = uint32(index2) + index0 += 2 + index2 += 2 + } + } + + // Search without dict: + if repeat > s { + repeat = 0 + } + + // No more dict + sLimit = len(src) - inputMargin + if s >= sLimit { + goto emitRemainder + } + cv = load64(src, s) + if debug { + fmt.Println("now", s, "->", sLimit, "out:", d, "left:", len(src)-s, "nextemit:", nextEmit, "dstLimit:", dstLimit, "s:", s) + } + for { + candidateL := 0 + nextS := 0 + for { + // Next src position to check + nextS = s + (s-nextEmit)>>7 + 1 + if nextS > sLimit { + goto emitRemainder + } + hashL := hash7(cv, lTableBits) + hashS := hash4(cv, sTableBits) + candidateL = int(lTable[hashL]) + candidateS := int(sTable[hashS]) + lTable[hashL] = uint32(s) + sTable[hashS] = uint32(s) + + valLong := load64(src, candidateL) + valShort := load64(src, candidateS) + + // If long matches at least 8 bytes, use that. + if cv == valLong { + break + } + if cv == valShort { + candidateL = candidateS + break + } + + // Check repeat at offset checkRep. + const checkRep = 1 + // Minimum length of a repeat. Tested with various values. + // While 4-5 offers improvements in some, 6 reduces + // regressions significantly. + const wantRepeatBytes = 6 + const repeatMask = ((1 << (wantRepeatBytes * 8)) - 1) << (8 * checkRep) + if false && repeat > 0 && cv&repeatMask == load64(src, s-repeat)&repeatMask { + base := s + checkRep + // Extend back + for i := base - repeat; base > nextEmit && i > 0 && src[i-1] == src[base-1]; { + i-- + base-- + } + d += emitLiteral(dst[d:], src[nextEmit:base]) + + // Extend forward + candidate := s - repeat + wantRepeatBytes + checkRep + s += wantRepeatBytes + checkRep + for s < len(src) { + if len(src)-s < 8 { + if src[s] == src[candidate] { + s++ + candidate++ + continue + } + break + } + if diff := load64(src, s) ^ load64(src, candidate); diff != 0 { + s += bits.TrailingZeros64(diff) >> 3 + break + } + s += 8 + candidate += 8 + } + // same as `add := emitCopy(dst[d:], repeat, s-base)` but skips storing offset. + d += emitRepeat(dst[d:], repeat, s-base) + nextEmit = s + if s >= sLimit { + goto emitRemainder + } + // Index in-between + index0 := base + 1 + index1 := s - 2 + + for index0 < index1 { + cv0 := load64(src, index0) + cv1 := load64(src, index1) + lTable[hash7(cv0, lTableBits)] = uint32(index0) + sTable[hash4(cv0>>8, sTableBits)] = uint32(index0 + 1) + + lTable[hash7(cv1, lTableBits)] = uint32(index1) + sTable[hash4(cv1>>8, sTableBits)] = uint32(index1 + 1) + index0 += 2 + index1 -= 2 + } + + cv = load64(src, s) + continue + } + + // Long likely matches 7, so take that. + if uint32(cv) == uint32(valLong) { + break + } + + // Check our short candidate + if uint32(cv) == uint32(valShort) { + // Try a long candidate at s+1 + hashL = hash7(cv>>8, lTableBits) + candidateL = int(lTable[hashL]) + lTable[hashL] = uint32(s + 1) + if uint32(cv>>8) == load32(src, candidateL) { + s++ + break + } + // Use our short candidate. + candidateL = candidateS + break + } + + cv = load64(src, nextS) + s = nextS + } + + // Extend backwards + for candidateL > 0 && s > nextEmit && src[candidateL-1] == src[s-1] { + candidateL-- + s-- + } + + // Bail if we exceed the maximum size. + if d+(s-nextEmit) > dstLimit { + return 0 + } + + base := s + offset := base - candidateL + + // Extend the 4-byte match as long as possible. + s += 4 + candidateL += 4 + for s < len(src) { + if len(src)-s < 8 { + if src[s] == src[candidateL] { + s++ + candidateL++ + continue + } + break + } + if diff := load64(src, s) ^ load64(src, candidateL); diff != 0 { + s += bits.TrailingZeros64(diff) >> 3 + break + } + s += 8 + candidateL += 8 + } + + if offset > 65535 && s-base <= 5 && repeat != offset { + // Bail if the match is equal or worse to the encoding. + s = nextS + 1 + if s >= sLimit { + goto emitRemainder + } + cv = load64(src, s) + continue + } + + d += emitLiteral(dst[d:], src[nextEmit:base]) + if repeat == offset { + d += emitRepeat(dst[d:], offset, s-base) + } else { + d += emitCopy(dst[d:], offset, s-base) + repeat = offset + } + + nextEmit = s + if s >= sLimit { + goto emitRemainder + } + + if d > dstLimit { + // Do we have space for more, if not bail. + return 0 + } + + // Index short & long + index0 := base + 1 + index1 := s - 2 + + cv0 := load64(src, index0) + cv1 := load64(src, index1) + lTable[hash7(cv0, lTableBits)] = uint32(index0) + sTable[hash4(cv0>>8, sTableBits)] = uint32(index0 + 1) + + lTable[hash7(cv1, lTableBits)] = uint32(index1) + sTable[hash4(cv1>>8, sTableBits)] = uint32(index1 + 1) + index0 += 1 + index1 -= 1 + cv = load64(src, s) + + // Index large values sparsely in between. + // We do two starting from different offsets for speed. + index2 := (index0 + index1 + 1) >> 1 + for index2 < index1 { + lTable[hash7(load64(src, index0), lTableBits)] = uint32(index0) + lTable[hash7(load64(src, index2), lTableBits)] = uint32(index2) + index0 += 2 + index2 += 2 + } + } + +emitRemainder: + if nextEmit < len(src) { + // Bail if we exceed the maximum size. + if d+len(src)-nextEmit > dstLimit { + return 0 + } + d += emitLiteral(dst[d:], src[nextEmit:]) + } + return d +} diff --git a/vendor/github.com/klauspost/compress/s2/encode_go.go b/vendor/github.com/klauspost/compress/s2/encode_go.go new file mode 100644 index 000000000..5597f3ef2 --- /dev/null +++ b/vendor/github.com/klauspost/compress/s2/encode_go.go @@ -0,0 +1,740 @@ +//go:build !amd64 || appengine || !gc || noasm + +package s2 + +import ( + "bytes" + "math/bits" +) + +const hasAmd64Asm = false + +// encodeBlock encodes a non-empty src to a guaranteed-large-enough dst. It +// assumes that the varint-encoded length of the decompressed bytes has already +// been written. +// +// It also assumes that: +// +// len(dst) >= MaxEncodedLen(len(src)) +func encodeBlock(dst, src []byte) (d int) { + if len(src) < minNonLiteralBlockSize { + return 0 + } + if len(src) <= 64<<10 { + return encodeBlockGo64K(dst, src) + } + return encodeBlockGo(dst, src) +} + +// encodeBlockBetter encodes a non-empty src to a guaranteed-large-enough dst. It +// assumes that the varint-encoded length of the decompressed bytes has already +// been written. +// +// It also assumes that: +// +// len(dst) >= MaxEncodedLen(len(src)) +func encodeBlockBetter(dst, src []byte) (d int) { + if len(src) <= 64<<10 { + return encodeBlockBetterGo64K(dst, src) + } + return encodeBlockBetterGo(dst, src) +} + +// encodeBlockBetter encodes a non-empty src to a guaranteed-large-enough dst. It +// assumes that the varint-encoded length of the decompressed bytes has already +// been written. +// +// It also assumes that: +// +// len(dst) >= MaxEncodedLen(len(src)) +func encodeBlockBetterSnappy(dst, src []byte) (d int) { + if len(src) <= 64<<10 { + return encodeBlockBetterSnappyGo64K(dst, src) + } + return encodeBlockBetterSnappyGo(dst, src) +} + +// encodeBlock encodes a non-empty src to a guaranteed-large-enough dst. It +// assumes that the varint-encoded length of the decompressed bytes has already +// been written. +// +// It also assumes that: +// +// len(dst) >= MaxEncodedLen(len(src)) +func encodeBlockSnappy(dst, src []byte) (d int) { + if len(src) < minNonLiteralBlockSize { + return 0 + } + if len(src) <= 64<<10 { + return encodeBlockSnappyGo64K(dst, src) + } + return encodeBlockSnappyGo(dst, src) +} + +// emitLiteral writes a literal chunk and returns the number of bytes written. +// +// It assumes that: +// +// dst is long enough to hold the encoded bytes +// 0 <= len(lit) && len(lit) <= math.MaxUint32 +func emitLiteral(dst, lit []byte) int { + if len(lit) == 0 { + return 0 + } + const num = 63<<2 | tagLiteral + i, n := 0, uint(len(lit)-1) + switch { + case n < 60: + dst[0] = uint8(n)<<2 | tagLiteral + i = 1 + case n < 1<<8: + dst[1] = uint8(n) + dst[0] = 60<<2 | tagLiteral + i = 2 + case n < 1<<16: + dst[2] = uint8(n >> 8) + dst[1] = uint8(n) + dst[0] = 61<<2 | tagLiteral + i = 3 + case n < 1<<24: + dst[3] = uint8(n >> 16) + dst[2] = uint8(n >> 8) + dst[1] = uint8(n) + dst[0] = 62<<2 | tagLiteral + i = 4 + default: + dst[4] = uint8(n >> 24) + dst[3] = uint8(n >> 16) + dst[2] = uint8(n >> 8) + dst[1] = uint8(n) + dst[0] = 63<<2 | tagLiteral + i = 5 + } + return i + copy(dst[i:], lit) +} + +// emitRepeat writes a repeat chunk and returns the number of bytes written. +// Length must be at least 4 and < 1<<24 +func emitRepeat(dst []byte, offset, length int) int { + // Repeat offset, make length cheaper + length -= 4 + if length <= 4 { + dst[0] = uint8(length)<<2 | tagCopy1 + dst[1] = 0 + return 2 + } + if length < 8 && offset < 2048 { + // Encode WITH offset + dst[1] = uint8(offset) + dst[0] = uint8(offset>>8)<<5 | uint8(length)<<2 | tagCopy1 + return 2 + } + if length < (1<<8)+4 { + length -= 4 + dst[2] = uint8(length) + dst[1] = 0 + dst[0] = 5<<2 | tagCopy1 + return 3 + } + if length < (1<<16)+(1<<8) { + length -= 1 << 8 + dst[3] = uint8(length >> 8) + dst[2] = uint8(length >> 0) + dst[1] = 0 + dst[0] = 6<<2 | tagCopy1 + return 4 + } + const maxRepeat = (1 << 24) - 1 + length -= 1 << 16 + left := 0 + if length > maxRepeat { + left = length - maxRepeat + 4 + length = maxRepeat - 4 + } + dst[4] = uint8(length >> 16) + dst[3] = uint8(length >> 8) + dst[2] = uint8(length >> 0) + dst[1] = 0 + dst[0] = 7<<2 | tagCopy1 + if left > 0 { + return 5 + emitRepeat(dst[5:], offset, left) + } + return 5 +} + +// emitCopy writes a copy chunk and returns the number of bytes written. +// +// It assumes that: +// +// dst is long enough to hold the encoded bytes +// 1 <= offset && offset <= math.MaxUint32 +// 4 <= length && length <= 1 << 24 +func emitCopy(dst []byte, offset, length int) int { + if offset >= 65536 { + i := 0 + if length > 64 { + // Emit a length 64 copy, encoded as 5 bytes. + dst[4] = uint8(offset >> 24) + dst[3] = uint8(offset >> 16) + dst[2] = uint8(offset >> 8) + dst[1] = uint8(offset) + dst[0] = 63<<2 | tagCopy4 + length -= 64 + if length >= 4 { + // Emit remaining as repeats + return 5 + emitRepeat(dst[5:], offset, length) + } + i = 5 + } + if length == 0 { + return i + } + // Emit a copy, offset encoded as 4 bytes. + dst[i+0] = uint8(length-1)<<2 | tagCopy4 + dst[i+1] = uint8(offset) + dst[i+2] = uint8(offset >> 8) + dst[i+3] = uint8(offset >> 16) + dst[i+4] = uint8(offset >> 24) + return i + 5 + } + + // Offset no more than 2 bytes. + if length > 64 { + off := 3 + if offset < 2048 { + // emit 8 bytes as tagCopy1, rest as repeats. + dst[1] = uint8(offset) + dst[0] = uint8(offset>>8)<<5 | uint8(8-4)<<2 | tagCopy1 + length -= 8 + off = 2 + } else { + // Emit a length 60 copy, encoded as 3 bytes. + // Emit remaining as repeat value (minimum 4 bytes). + dst[2] = uint8(offset >> 8) + dst[1] = uint8(offset) + dst[0] = 59<<2 | tagCopy2 + length -= 60 + } + // Emit remaining as repeats, at least 4 bytes remain. + return off + emitRepeat(dst[off:], offset, length) + } + if length >= 12 || offset >= 2048 { + // Emit the remaining copy, encoded as 3 bytes. + dst[2] = uint8(offset >> 8) + dst[1] = uint8(offset) + dst[0] = uint8(length-1)<<2 | tagCopy2 + return 3 + } + // Emit the remaining copy, encoded as 2 bytes. + dst[1] = uint8(offset) + dst[0] = uint8(offset>>8)<<5 | uint8(length-4)<<2 | tagCopy1 + return 2 +} + +// emitCopyNoRepeat writes a copy chunk and returns the number of bytes written. +// +// It assumes that: +// +// dst is long enough to hold the encoded bytes +// 1 <= offset && offset <= math.MaxUint32 +// 4 <= length && length <= 1 << 24 +func emitCopyNoRepeat(dst []byte, offset, length int) int { + if offset >= 65536 { + i := 0 + if length > 64 { + // Emit a length 64 copy, encoded as 5 bytes. + dst[4] = uint8(offset >> 24) + dst[3] = uint8(offset >> 16) + dst[2] = uint8(offset >> 8) + dst[1] = uint8(offset) + dst[0] = 63<<2 | tagCopy4 + length -= 64 + if length >= 4 { + // Emit remaining as repeats + return 5 + emitCopyNoRepeat(dst[5:], offset, length) + } + i = 5 + } + if length == 0 { + return i + } + // Emit a copy, offset encoded as 4 bytes. + dst[i+0] = uint8(length-1)<<2 | tagCopy4 + dst[i+1] = uint8(offset) + dst[i+2] = uint8(offset >> 8) + dst[i+3] = uint8(offset >> 16) + dst[i+4] = uint8(offset >> 24) + return i + 5 + } + + // Offset no more than 2 bytes. + if length > 64 { + // Emit a length 60 copy, encoded as 3 bytes. + // Emit remaining as repeat value (minimum 4 bytes). + dst[2] = uint8(offset >> 8) + dst[1] = uint8(offset) + dst[0] = 59<<2 | tagCopy2 + length -= 60 + // Emit remaining as repeats, at least 4 bytes remain. + return 3 + emitCopyNoRepeat(dst[3:], offset, length) + } + if length >= 12 || offset >= 2048 { + // Emit the remaining copy, encoded as 3 bytes. + dst[2] = uint8(offset >> 8) + dst[1] = uint8(offset) + dst[0] = uint8(length-1)<<2 | tagCopy2 + return 3 + } + // Emit the remaining copy, encoded as 2 bytes. + dst[1] = uint8(offset) + dst[0] = uint8(offset>>8)<<5 | uint8(length-4)<<2 | tagCopy1 + return 2 +} + +// matchLen returns how many bytes match in a and b +// +// It assumes that: +// +// len(a) <= len(b) +func matchLen(a []byte, b []byte) int { + b = b[:len(a)] + var checked int + if len(a) > 4 { + // Try 4 bytes first + if diff := load32(a, 0) ^ load32(b, 0); diff != 0 { + return bits.TrailingZeros32(diff) >> 3 + } + // Switch to 8 byte matching. + checked = 4 + a = a[4:] + b = b[4:] + for len(a) >= 8 { + b = b[:len(a)] + if diff := load64(a, 0) ^ load64(b, 0); diff != 0 { + return checked + (bits.TrailingZeros64(diff) >> 3) + } + checked += 8 + a = a[8:] + b = b[8:] + } + } + b = b[:len(a)] + for i := range a { + if a[i] != b[i] { + return int(i) + checked + } + } + return len(a) + checked +} + +// input must be > inputMargin +func calcBlockSize(src []byte, _ *[32768]byte) (d int) { + // Initialize the hash table. + const ( + tableBits = 13 + maxTableSize = 1 << tableBits + ) + + var table [maxTableSize]uint32 + + // sLimit is when to stop looking for offset/length copies. The inputMargin + // lets us use a fast path for emitLiteral in the main loop, while we are + // looking for copies. + sLimit := len(src) - inputMargin + + // Bail if we can't compress to at least this. + dstLimit := len(src) - len(src)>>5 - 5 + + // nextEmit is where in src the next emitLiteral should start from. + nextEmit := 0 + + // The encoded form must start with a literal, as there are no previous + // bytes to copy, so we start looking for hash matches at s == 1. + s := 1 + cv := load64(src, s) + + // We search for a repeat at -1, but don't output repeats when nextEmit == 0 + repeat := 1 + + for { + candidate := 0 + for { + // Next src position to check + nextS := s + (s-nextEmit)>>6 + 4 + if nextS > sLimit { + goto emitRemainder + } + hash0 := hash6(cv, tableBits) + hash1 := hash6(cv>>8, tableBits) + candidate = int(table[hash0]) + candidate2 := int(table[hash1]) + table[hash0] = uint32(s) + table[hash1] = uint32(s + 1) + hash2 := hash6(cv>>16, tableBits) + + // Check repeat at offset checkRep. + const checkRep = 1 + if uint32(cv>>(checkRep*8)) == load32(src, s-repeat+checkRep) { + base := s + checkRep + // Extend back + for i := base - repeat; base > nextEmit && i > 0 && src[i-1] == src[base-1]; { + i-- + base-- + } + d += emitLiteralSize(src[nextEmit:base]) + + // Extend forward + candidate := s - repeat + 4 + checkRep + s += 4 + checkRep + for s <= sLimit { + if diff := load64(src, s) ^ load64(src, candidate); diff != 0 { + s += bits.TrailingZeros64(diff) >> 3 + break + } + s += 8 + candidate += 8 + } + + d += emitCopyNoRepeatSize(repeat, s-base) + nextEmit = s + if s >= sLimit { + goto emitRemainder + } + + cv = load64(src, s) + continue + } + + if uint32(cv) == load32(src, candidate) { + break + } + candidate = int(table[hash2]) + if uint32(cv>>8) == load32(src, candidate2) { + table[hash2] = uint32(s + 2) + candidate = candidate2 + s++ + break + } + table[hash2] = uint32(s + 2) + if uint32(cv>>16) == load32(src, candidate) { + s += 2 + break + } + + cv = load64(src, nextS) + s = nextS + } + + // Extend backwards + for candidate > 0 && s > nextEmit && src[candidate-1] == src[s-1] { + candidate-- + s-- + } + + // Bail if we exceed the maximum size. + if d+(s-nextEmit) > dstLimit { + return 0 + } + + // A 4-byte match has been found. We'll later see if more than 4 bytes + // match. But, prior to the match, src[nextEmit:s] are unmatched. Emit + // them as literal bytes. + + d += emitLiteralSize(src[nextEmit:s]) + + // Call emitCopy, and then see if another emitCopy could be our next + // move. Repeat until we find no match for the input immediately after + // what was consumed by the last emitCopy call. + // + // If we exit this loop normally then we need to call emitLiteral next, + // though we don't yet know how big the literal will be. We handle that + // by proceeding to the next iteration of the main loop. We also can + // exit this loop via goto if we get close to exhausting the input. + for { + // Invariant: we have a 4-byte match at s, and no need to emit any + // literal bytes prior to s. + base := s + repeat = base - candidate + + // Extend the 4-byte match as long as possible. + s += 4 + candidate += 4 + for s <= len(src)-8 { + if diff := load64(src, s) ^ load64(src, candidate); diff != 0 { + s += bits.TrailingZeros64(diff) >> 3 + break + } + s += 8 + candidate += 8 + } + + d += emitCopyNoRepeatSize(repeat, s-base) + if false { + // Validate match. + a := src[base:s] + b := src[base-repeat : base-repeat+(s-base)] + if !bytes.Equal(a, b) { + panic("mismatch") + } + } + + nextEmit = s + if s >= sLimit { + goto emitRemainder + } + + if d > dstLimit { + // Do we have space for more, if not bail. + return 0 + } + // Check for an immediate match, otherwise start search at s+1 + x := load64(src, s-2) + m2Hash := hash6(x, tableBits) + currHash := hash6(x>>16, tableBits) + candidate = int(table[currHash]) + table[m2Hash] = uint32(s - 2) + table[currHash] = uint32(s) + if uint32(x>>16) != load32(src, candidate) { + cv = load64(src, s+1) + s++ + break + } + } + } + +emitRemainder: + if nextEmit < len(src) { + // Bail if we exceed the maximum size. + if d+len(src)-nextEmit > dstLimit { + return 0 + } + d += emitLiteralSize(src[nextEmit:]) + } + return d +} + +// length must be > inputMargin. +func calcBlockSizeSmall(src []byte, _ *[2048]byte) (d int) { + // Initialize the hash table. + const ( + tableBits = 9 + maxTableSize = 1 << tableBits + ) + + var table [maxTableSize]uint32 + + // sLimit is when to stop looking for offset/length copies. The inputMargin + // lets us use a fast path for emitLiteral in the main loop, while we are + // looking for copies. + sLimit := len(src) - inputMargin + + // Bail if we can't compress to at least this. + dstLimit := len(src) - len(src)>>5 - 5 + + // nextEmit is where in src the next emitLiteral should start from. + nextEmit := 0 + + // The encoded form must start with a literal, as there are no previous + // bytes to copy, so we start looking for hash matches at s == 1. + s := 1 + cv := load64(src, s) + + // We search for a repeat at -1, but don't output repeats when nextEmit == 0 + repeat := 1 + + for { + candidate := 0 + for { + // Next src position to check + nextS := s + (s-nextEmit)>>6 + 4 + if nextS > sLimit { + goto emitRemainder + } + hash0 := hash6(cv, tableBits) + hash1 := hash6(cv>>8, tableBits) + candidate = int(table[hash0]) + candidate2 := int(table[hash1]) + table[hash0] = uint32(s) + table[hash1] = uint32(s + 1) + hash2 := hash6(cv>>16, tableBits) + + // Check repeat at offset checkRep. + const checkRep = 1 + if uint32(cv>>(checkRep*8)) == load32(src, s-repeat+checkRep) { + base := s + checkRep + // Extend back + for i := base - repeat; base > nextEmit && i > 0 && src[i-1] == src[base-1]; { + i-- + base-- + } + d += emitLiteralSize(src[nextEmit:base]) + + // Extend forward + candidate := s - repeat + 4 + checkRep + s += 4 + checkRep + for s <= sLimit { + if diff := load64(src, s) ^ load64(src, candidate); diff != 0 { + s += bits.TrailingZeros64(diff) >> 3 + break + } + s += 8 + candidate += 8 + } + + d += emitCopyNoRepeatSize(repeat, s-base) + nextEmit = s + if s >= sLimit { + goto emitRemainder + } + + cv = load64(src, s) + continue + } + + if uint32(cv) == load32(src, candidate) { + break + } + candidate = int(table[hash2]) + if uint32(cv>>8) == load32(src, candidate2) { + table[hash2] = uint32(s + 2) + candidate = candidate2 + s++ + break + } + table[hash2] = uint32(s + 2) + if uint32(cv>>16) == load32(src, candidate) { + s += 2 + break + } + + cv = load64(src, nextS) + s = nextS + } + + // Extend backwards + for candidate > 0 && s > nextEmit && src[candidate-1] == src[s-1] { + candidate-- + s-- + } + + // Bail if we exceed the maximum size. + if d+(s-nextEmit) > dstLimit { + return 0 + } + + // A 4-byte match has been found. We'll later see if more than 4 bytes + // match. But, prior to the match, src[nextEmit:s] are unmatched. Emit + // them as literal bytes. + + d += emitLiteralSize(src[nextEmit:s]) + + // Call emitCopy, and then see if another emitCopy could be our next + // move. Repeat until we find no match for the input immediately after + // what was consumed by the last emitCopy call. + // + // If we exit this loop normally then we need to call emitLiteral next, + // though we don't yet know how big the literal will be. We handle that + // by proceeding to the next iteration of the main loop. We also can + // exit this loop via goto if we get close to exhausting the input. + for { + // Invariant: we have a 4-byte match at s, and no need to emit any + // literal bytes prior to s. + base := s + repeat = base - candidate + + // Extend the 4-byte match as long as possible. + s += 4 + candidate += 4 + for s <= len(src)-8 { + if diff := load64(src, s) ^ load64(src, candidate); diff != 0 { + s += bits.TrailingZeros64(diff) >> 3 + break + } + s += 8 + candidate += 8 + } + + d += emitCopyNoRepeatSize(repeat, s-base) + if false { + // Validate match. + a := src[base:s] + b := src[base-repeat : base-repeat+(s-base)] + if !bytes.Equal(a, b) { + panic("mismatch") + } + } + + nextEmit = s + if s >= sLimit { + goto emitRemainder + } + + if d > dstLimit { + // Do we have space for more, if not bail. + return 0 + } + // Check for an immediate match, otherwise start search at s+1 + x := load64(src, s-2) + m2Hash := hash6(x, tableBits) + currHash := hash6(x>>16, tableBits) + candidate = int(table[currHash]) + table[m2Hash] = uint32(s - 2) + table[currHash] = uint32(s) + if uint32(x>>16) != load32(src, candidate) { + cv = load64(src, s+1) + s++ + break + } + } + } + +emitRemainder: + if nextEmit < len(src) { + // Bail if we exceed the maximum size. + if d+len(src)-nextEmit > dstLimit { + return 0 + } + d += emitLiteralSize(src[nextEmit:]) + } + return d +} + +// emitLiteral writes a literal chunk and returns the number of bytes written. +// +// It assumes that: +// +// dst is long enough to hold the encoded bytes +// 0 <= len(lit) && len(lit) <= math.MaxUint32 +func emitLiteralSize(lit []byte) int { + if len(lit) == 0 { + return 0 + } + switch { + case len(lit) <= 60: + return len(lit) + 1 + case len(lit) <= 1<<8: + return len(lit) + 2 + case len(lit) <= 1<<16: + return len(lit) + 3 + case len(lit) <= 1<<24: + return len(lit) + 4 + default: + return len(lit) + 5 + } +} + +func cvtLZ4BlockAsm(dst []byte, src []byte) (uncompressed int, dstUsed int) { + panic("cvtLZ4BlockAsm should be unreachable") +} + +func cvtLZ4BlockSnappyAsm(dst []byte, src []byte) (uncompressed int, dstUsed int) { + panic("cvtLZ4BlockSnappyAsm should be unreachable") +} + +func cvtLZ4sBlockAsm(dst []byte, src []byte) (uncompressed int, dstUsed int) { + panic("cvtLZ4sBlockAsm should be unreachable") +} + +func cvtLZ4sBlockSnappyAsm(dst []byte, src []byte) (uncompressed int, dstUsed int) { + panic("cvtLZ4sBlockSnappyAsm should be unreachable") +} diff --git a/vendor/github.com/klauspost/compress/s2/encodeblock_amd64.go b/vendor/github.com/klauspost/compress/s2/encodeblock_amd64.go new file mode 100644 index 000000000..f43aa8154 --- /dev/null +++ b/vendor/github.com/klauspost/compress/s2/encodeblock_amd64.go @@ -0,0 +1,228 @@ +// Code generated by command: go run gen.go -out ../encodeblock_amd64.s -stubs ../encodeblock_amd64.go -pkg=s2. DO NOT EDIT. + +//go:build !appengine && !noasm && gc && !noasm + +package s2 + +func _dummy_() + +// encodeBlockAsm encodes a non-empty src to a guaranteed-large-enough dst. +// Maximum input 4294967295 bytes. +// It assumes that the varint-encoded length of the decompressed bytes has already been written. +// +//go:noescape +func encodeBlockAsm(dst []byte, src []byte, tmp *[65536]byte) int + +// encodeBlockAsm4MB encodes a non-empty src to a guaranteed-large-enough dst. +// Maximum input 4194304 bytes. +// It assumes that the varint-encoded length of the decompressed bytes has already been written. +// +//go:noescape +func encodeBlockAsm4MB(dst []byte, src []byte, tmp *[65536]byte) int + +// encodeBlockAsm12B encodes a non-empty src to a guaranteed-large-enough dst. +// Maximum input 16383 bytes. +// It assumes that the varint-encoded length of the decompressed bytes has already been written. +// +//go:noescape +func encodeBlockAsm12B(dst []byte, src []byte, tmp *[16384]byte) int + +// encodeBlockAsm10B encodes a non-empty src to a guaranteed-large-enough dst. +// Maximum input 4095 bytes. +// It assumes that the varint-encoded length of the decompressed bytes has already been written. +// +//go:noescape +func encodeBlockAsm10B(dst []byte, src []byte, tmp *[4096]byte) int + +// encodeBlockAsm8B encodes a non-empty src to a guaranteed-large-enough dst. +// Maximum input 511 bytes. +// It assumes that the varint-encoded length of the decompressed bytes has already been written. +// +//go:noescape +func encodeBlockAsm8B(dst []byte, src []byte, tmp *[1024]byte) int + +// encodeBetterBlockAsm encodes a non-empty src to a guaranteed-large-enough dst. +// Maximum input 4294967295 bytes. +// It assumes that the varint-encoded length of the decompressed bytes has already been written. +// +//go:noescape +func encodeBetterBlockAsm(dst []byte, src []byte, tmp *[589824]byte) int + +// encodeBetterBlockAsm4MB encodes a non-empty src to a guaranteed-large-enough dst. +// Maximum input 4194304 bytes. +// It assumes that the varint-encoded length of the decompressed bytes has already been written. +// +//go:noescape +func encodeBetterBlockAsm4MB(dst []byte, src []byte, tmp *[589824]byte) int + +// encodeBetterBlockAsm12B encodes a non-empty src to a guaranteed-large-enough dst. +// Maximum input 16383 bytes. +// It assumes that the varint-encoded length of the decompressed bytes has already been written. +// +//go:noescape +func encodeBetterBlockAsm12B(dst []byte, src []byte, tmp *[81920]byte) int + +// encodeBetterBlockAsm10B encodes a non-empty src to a guaranteed-large-enough dst. +// Maximum input 4095 bytes. +// It assumes that the varint-encoded length of the decompressed bytes has already been written. +// +//go:noescape +func encodeBetterBlockAsm10B(dst []byte, src []byte, tmp *[20480]byte) int + +// encodeBetterBlockAsm8B encodes a non-empty src to a guaranteed-large-enough dst. +// Maximum input 511 bytes. +// It assumes that the varint-encoded length of the decompressed bytes has already been written. +// +//go:noescape +func encodeBetterBlockAsm8B(dst []byte, src []byte, tmp *[5120]byte) int + +// encodeSnappyBlockAsm encodes a non-empty src to a guaranteed-large-enough dst. +// Maximum input 4294967295 bytes. +// It assumes that the varint-encoded length of the decompressed bytes has already been written. +// +//go:noescape +func encodeSnappyBlockAsm(dst []byte, src []byte, tmp *[65536]byte) int + +// encodeSnappyBlockAsm64K encodes a non-empty src to a guaranteed-large-enough dst. +// Maximum input 65535 bytes. +// It assumes that the varint-encoded length of the decompressed bytes has already been written. +// +//go:noescape +func encodeSnappyBlockAsm64K(dst []byte, src []byte, tmp *[65536]byte) int + +// encodeSnappyBlockAsm12B encodes a non-empty src to a guaranteed-large-enough dst. +// Maximum input 16383 bytes. +// It assumes that the varint-encoded length of the decompressed bytes has already been written. +// +//go:noescape +func encodeSnappyBlockAsm12B(dst []byte, src []byte, tmp *[16384]byte) int + +// encodeSnappyBlockAsm10B encodes a non-empty src to a guaranteed-large-enough dst. +// Maximum input 4095 bytes. +// It assumes that the varint-encoded length of the decompressed bytes has already been written. +// +//go:noescape +func encodeSnappyBlockAsm10B(dst []byte, src []byte, tmp *[4096]byte) int + +// encodeSnappyBlockAsm8B encodes a non-empty src to a guaranteed-large-enough dst. +// Maximum input 511 bytes. +// It assumes that the varint-encoded length of the decompressed bytes has already been written. +// +//go:noescape +func encodeSnappyBlockAsm8B(dst []byte, src []byte, tmp *[1024]byte) int + +// encodeSnappyBetterBlockAsm encodes a non-empty src to a guaranteed-large-enough dst. +// Maximum input 4294967295 bytes. +// It assumes that the varint-encoded length of the decompressed bytes has already been written. +// +//go:noescape +func encodeSnappyBetterBlockAsm(dst []byte, src []byte, tmp *[589824]byte) int + +// encodeSnappyBetterBlockAsm64K encodes a non-empty src to a guaranteed-large-enough dst. +// Maximum input 65535 bytes. +// It assumes that the varint-encoded length of the decompressed bytes has already been written. +// +//go:noescape +func encodeSnappyBetterBlockAsm64K(dst []byte, src []byte, tmp *[294912]byte) int + +// encodeSnappyBetterBlockAsm12B encodes a non-empty src to a guaranteed-large-enough dst. +// Maximum input 16383 bytes. +// It assumes that the varint-encoded length of the decompressed bytes has already been written. +// +//go:noescape +func encodeSnappyBetterBlockAsm12B(dst []byte, src []byte, tmp *[81920]byte) int + +// encodeSnappyBetterBlockAsm10B encodes a non-empty src to a guaranteed-large-enough dst. +// Maximum input 4095 bytes. +// It assumes that the varint-encoded length of the decompressed bytes has already been written. +// +//go:noescape +func encodeSnappyBetterBlockAsm10B(dst []byte, src []byte, tmp *[20480]byte) int + +// encodeSnappyBetterBlockAsm8B encodes a non-empty src to a guaranteed-large-enough dst. +// Maximum input 511 bytes. +// It assumes that the varint-encoded length of the decompressed bytes has already been written. +// +//go:noescape +func encodeSnappyBetterBlockAsm8B(dst []byte, src []byte, tmp *[5120]byte) int + +// calcBlockSize encodes a non-empty src to a guaranteed-large-enough dst. +// Maximum input 4294967295 bytes. +// It assumes that the varint-encoded length of the decompressed bytes has already been written. +// +//go:noescape +func calcBlockSize(src []byte, tmp *[32768]byte) int + +// calcBlockSizeSmall encodes a non-empty src to a guaranteed-large-enough dst. +// Maximum input 1024 bytes. +// It assumes that the varint-encoded length of the decompressed bytes has already been written. +// +//go:noescape +func calcBlockSizeSmall(src []byte, tmp *[2048]byte) int + +// emitLiteral writes a literal chunk and returns the number of bytes written. +// +// It assumes that: +// +// dst is long enough to hold the encoded bytes with margin of 0 bytes +// 0 <= len(lit) && len(lit) <= math.MaxUint32 +// +//go:noescape +func emitLiteral(dst []byte, lit []byte) int + +// emitRepeat writes a repeat chunk and returns the number of bytes written. +// Length must be at least 4 and < 1<<32 +// +//go:noescape +func emitRepeat(dst []byte, offset int, length int) int + +// emitCopy writes a copy chunk and returns the number of bytes written. +// +// It assumes that: +// +// dst is long enough to hold the encoded bytes +// 1 <= offset && offset <= math.MaxUint32 +// 4 <= length && length <= 1 << 24 +// +//go:noescape +func emitCopy(dst []byte, offset int, length int) int + +// emitCopyNoRepeat writes a copy chunk and returns the number of bytes written. +// +// It assumes that: +// +// dst is long enough to hold the encoded bytes +// 1 <= offset && offset <= math.MaxUint32 +// 4 <= length && length <= 1 << 24 +// +//go:noescape +func emitCopyNoRepeat(dst []byte, offset int, length int) int + +// matchLen returns how many bytes match in a and b +// +// It assumes that: +// +// len(a) <= len(b) +// +//go:noescape +func matchLen(a []byte, b []byte) int + +// cvtLZ4Block converts an LZ4 block to S2 +// +//go:noescape +func cvtLZ4BlockAsm(dst []byte, src []byte) (uncompressed int, dstUsed int) + +// cvtLZ4sBlock converts an LZ4s block to S2 +// +//go:noescape +func cvtLZ4sBlockAsm(dst []byte, src []byte) (uncompressed int, dstUsed int) + +// cvtLZ4Block converts an LZ4 block to Snappy +// +//go:noescape +func cvtLZ4BlockSnappyAsm(dst []byte, src []byte) (uncompressed int, dstUsed int) + +// cvtLZ4sBlock converts an LZ4s block to Snappy +// +//go:noescape +func cvtLZ4sBlockSnappyAsm(dst []byte, src []byte) (uncompressed int, dstUsed int) diff --git a/vendor/github.com/klauspost/compress/s2/encodeblock_amd64.s b/vendor/github.com/klauspost/compress/s2/encodeblock_amd64.s new file mode 100644 index 000000000..df9be687b --- /dev/null +++ b/vendor/github.com/klauspost/compress/s2/encodeblock_amd64.s @@ -0,0 +1,21303 @@ +// Code generated by command: go run gen.go -out ../encodeblock_amd64.s -stubs ../encodeblock_amd64.go -pkg=s2. DO NOT EDIT. + +//go:build !appengine && !noasm && gc && !noasm + +#include "textflag.h" + +// func _dummy_() +TEXT ·_dummy_(SB), $0 +#ifdef GOAMD64_v4 +#ifndef GOAMD64_v3 +#define GOAMD64_v3 +#endif +#endif + RET + +// func encodeBlockAsm(dst []byte, src []byte, tmp *[65536]byte) int +// Requires: BMI, SSE2 +TEXT ·encodeBlockAsm(SB), $24-64 + MOVQ tmp+48(FP), AX + MOVQ dst_base+0(FP), CX + MOVQ $0x00000200, DX + MOVQ AX, BX + PXOR X0, X0 + +zero_loop_encodeBlockAsm: + MOVOU X0, (BX) + MOVOU X0, 16(BX) + MOVOU X0, 32(BX) + MOVOU X0, 48(BX) + MOVOU X0, 64(BX) + MOVOU X0, 80(BX) + MOVOU X0, 96(BX) + MOVOU X0, 112(BX) + ADDQ $0x80, BX + DECQ DX + JNZ zero_loop_encodeBlockAsm + MOVL $0x00000000, 12(SP) + MOVQ src_len+32(FP), DX + LEAQ -9(DX), BX + LEAQ -8(DX), SI + MOVL SI, 8(SP) + SHRQ $0x05, DX + SUBL DX, BX + LEAQ (CX)(BX*1), BX + MOVQ BX, (SP) + MOVL $0x00000001, DX + MOVL DX, 16(SP) + MOVQ src_base+24(FP), BX + +search_loop_encodeBlockAsm: + MOVL DX, SI + SUBL 12(SP), SI + SHRL $0x06, SI + LEAL 4(DX)(SI*1), SI + CMPL SI, 8(SP) + JAE emit_remainder_encodeBlockAsm + MOVQ (BX)(DX*1), DI + MOVL SI, 20(SP) + MOVQ $0x0000cf1bbcdcbf9b, R9 + MOVQ DI, R10 + MOVQ DI, R11 + SHRQ $0x08, R11 + SHLQ $0x10, R10 + IMULQ R9, R10 + SHRQ $0x32, R10 + SHLQ $0x10, R11 + IMULQ R9, R11 + SHRQ $0x32, R11 + MOVL (AX)(R10*4), SI + MOVL (AX)(R11*4), R8 + MOVL DX, (AX)(R10*4) + LEAL 1(DX), R10 + MOVL R10, (AX)(R11*4) + MOVQ DI, R10 + SHRQ $0x10, R10 + SHLQ $0x10, R10 + IMULQ R9, R10 + SHRQ $0x32, R10 + MOVL DX, R9 + SUBL 16(SP), R9 + MOVL 1(BX)(R9*1), R11 + MOVQ DI, R9 + SHRQ $0x08, R9 + CMPL R9, R11 + JNE no_repeat_found_encodeBlockAsm + LEAL 1(DX), DI + MOVL 12(SP), R8 + MOVL DI, SI + SUBL 16(SP), SI + JZ repeat_extend_back_end_encodeBlockAsm + +repeat_extend_back_loop_encodeBlockAsm: + CMPL DI, R8 + JBE repeat_extend_back_end_encodeBlockAsm + MOVB -1(BX)(SI*1), R9 + MOVB -1(BX)(DI*1), R10 + CMPB R9, R10 + JNE repeat_extend_back_end_encodeBlockAsm + LEAL -1(DI), DI + DECL SI + JNZ repeat_extend_back_loop_encodeBlockAsm + +repeat_extend_back_end_encodeBlockAsm: + MOVL DI, SI + SUBL 12(SP), SI + LEAQ 5(CX)(SI*1), SI + CMPQ SI, (SP) + JB repeat_dst_size_check_encodeBlockAsm + MOVQ $0x00000000, ret+56(FP) + RET + +repeat_dst_size_check_encodeBlockAsm: + MOVL 12(SP), SI + CMPL SI, DI + JEQ emit_literal_done_repeat_emit_encodeBlockAsm + MOVL DI, R9 + MOVL DI, 12(SP) + LEAQ (BX)(SI*1), R10 + SUBL SI, R9 + LEAL -1(R9), SI + CMPL SI, $0x3c + JB one_byte_repeat_emit_encodeBlockAsm + CMPL SI, $0x00000100 + JB two_bytes_repeat_emit_encodeBlockAsm + CMPL SI, $0x00010000 + JB three_bytes_repeat_emit_encodeBlockAsm + CMPL SI, $0x01000000 + JB four_bytes_repeat_emit_encodeBlockAsm + MOVB $0xfc, (CX) + MOVL SI, 1(CX) + ADDQ $0x05, CX + JMP memmove_long_repeat_emit_encodeBlockAsm + +four_bytes_repeat_emit_encodeBlockAsm: + MOVL SI, R11 + SHRL $0x10, R11 + MOVB $0xf8, (CX) + MOVW SI, 1(CX) + MOVB R11, 3(CX) + ADDQ $0x04, CX + JMP memmove_long_repeat_emit_encodeBlockAsm + +three_bytes_repeat_emit_encodeBlockAsm: + MOVB $0xf4, (CX) + MOVW SI, 1(CX) + ADDQ $0x03, CX + JMP memmove_long_repeat_emit_encodeBlockAsm + +two_bytes_repeat_emit_encodeBlockAsm: + MOVB $0xf0, (CX) + MOVB SI, 1(CX) + ADDQ $0x02, CX + CMPL SI, $0x40 + JB memmove_repeat_emit_encodeBlockAsm + JMP memmove_long_repeat_emit_encodeBlockAsm + +one_byte_repeat_emit_encodeBlockAsm: + SHLB $0x02, SI + MOVB SI, (CX) + ADDQ $0x01, CX + +memmove_repeat_emit_encodeBlockAsm: + LEAQ (CX)(R9*1), SI + + // genMemMoveShort + CMPQ R9, $0x08 + JBE emit_lit_memmove_repeat_emit_encodeBlockAsm_memmove_move_8 + CMPQ R9, $0x10 + JBE emit_lit_memmove_repeat_emit_encodeBlockAsm_memmove_move_8through16 + CMPQ R9, $0x20 + JBE emit_lit_memmove_repeat_emit_encodeBlockAsm_memmove_move_17through32 + JMP emit_lit_memmove_repeat_emit_encodeBlockAsm_memmove_move_33through64 + +emit_lit_memmove_repeat_emit_encodeBlockAsm_memmove_move_8: + MOVQ (R10), R11 + MOVQ R11, (CX) + JMP memmove_end_copy_repeat_emit_encodeBlockAsm + +emit_lit_memmove_repeat_emit_encodeBlockAsm_memmove_move_8through16: + MOVQ (R10), R11 + MOVQ -8(R10)(R9*1), R10 + MOVQ R11, (CX) + MOVQ R10, -8(CX)(R9*1) + JMP memmove_end_copy_repeat_emit_encodeBlockAsm + +emit_lit_memmove_repeat_emit_encodeBlockAsm_memmove_move_17through32: + MOVOU (R10), X0 + MOVOU -16(R10)(R9*1), X1 + MOVOU X0, (CX) + MOVOU X1, -16(CX)(R9*1) + JMP memmove_end_copy_repeat_emit_encodeBlockAsm + +emit_lit_memmove_repeat_emit_encodeBlockAsm_memmove_move_33through64: + MOVOU (R10), X0 + MOVOU 16(R10), X1 + MOVOU -32(R10)(R9*1), X2 + MOVOU -16(R10)(R9*1), X3 + MOVOU X0, (CX) + MOVOU X1, 16(CX) + MOVOU X2, -32(CX)(R9*1) + MOVOU X3, -16(CX)(R9*1) + +memmove_end_copy_repeat_emit_encodeBlockAsm: + MOVQ SI, CX + JMP emit_literal_done_repeat_emit_encodeBlockAsm + +memmove_long_repeat_emit_encodeBlockAsm: + LEAQ (CX)(R9*1), SI + + // genMemMoveLong + MOVOU (R10), X0 + MOVOU 16(R10), X1 + MOVOU -32(R10)(R9*1), X2 + MOVOU -16(R10)(R9*1), X3 + MOVQ R9, R12 + SHRQ $0x05, R12 + MOVQ CX, R11 + ANDL $0x0000001f, R11 + MOVQ $0x00000040, R13 + SUBQ R11, R13 + DECQ R12 + JA emit_lit_memmove_long_repeat_emit_encodeBlockAsmlarge_forward_sse_loop_32 + LEAQ -32(R10)(R13*1), R11 + LEAQ -32(CX)(R13*1), R14 + +emit_lit_memmove_long_repeat_emit_encodeBlockAsmlarge_big_loop_back: + MOVOU (R11), X4 + MOVOU 16(R11), X5 + MOVOA X4, (R14) + MOVOA X5, 16(R14) + ADDQ $0x20, R14 + ADDQ $0x20, R11 + ADDQ $0x20, R13 + DECQ R12 + JNA emit_lit_memmove_long_repeat_emit_encodeBlockAsmlarge_big_loop_back + +emit_lit_memmove_long_repeat_emit_encodeBlockAsmlarge_forward_sse_loop_32: + MOVOU -32(R10)(R13*1), X4 + MOVOU -16(R10)(R13*1), X5 + MOVOA X4, -32(CX)(R13*1) + MOVOA X5, -16(CX)(R13*1) + ADDQ $0x20, R13 + CMPQ R9, R13 + JAE emit_lit_memmove_long_repeat_emit_encodeBlockAsmlarge_forward_sse_loop_32 + MOVOU X0, (CX) + MOVOU X1, 16(CX) + MOVOU X2, -32(CX)(R9*1) + MOVOU X3, -16(CX)(R9*1) + MOVQ SI, CX + +emit_literal_done_repeat_emit_encodeBlockAsm: + ADDL $0x05, DX + MOVL DX, SI + SUBL 16(SP), SI + MOVQ src_len+32(FP), R9 + SUBL DX, R9 + LEAQ (BX)(DX*1), R10 + LEAQ (BX)(SI*1), SI + + // matchLen + XORL R12, R12 + +matchlen_loopback_16_repeat_extend_encodeBlockAsm: + CMPL R9, $0x10 + JB matchlen_match8_repeat_extend_encodeBlockAsm + MOVQ (R10)(R12*1), R11 + MOVQ 8(R10)(R12*1), R13 + XORQ (SI)(R12*1), R11 + JNZ matchlen_bsf_8_repeat_extend_encodeBlockAsm + XORQ 8(SI)(R12*1), R13 + JNZ matchlen_bsf_16repeat_extend_encodeBlockAsm + LEAL -16(R9), R9 + LEAL 16(R12), R12 + JMP matchlen_loopback_16_repeat_extend_encodeBlockAsm + +matchlen_bsf_16repeat_extend_encodeBlockAsm: +#ifdef GOAMD64_v3 + TZCNTQ R13, R13 + +#else + BSFQ R13, R13 + +#endif + SARQ $0x03, R13 + LEAL 8(R12)(R13*1), R12 + JMP repeat_extend_forward_end_encodeBlockAsm + +matchlen_match8_repeat_extend_encodeBlockAsm: + CMPL R9, $0x08 + JB matchlen_match4_repeat_extend_encodeBlockAsm + MOVQ (R10)(R12*1), R11 + XORQ (SI)(R12*1), R11 + JNZ matchlen_bsf_8_repeat_extend_encodeBlockAsm + LEAL -8(R9), R9 + LEAL 8(R12), R12 + JMP matchlen_match4_repeat_extend_encodeBlockAsm + +matchlen_bsf_8_repeat_extend_encodeBlockAsm: +#ifdef GOAMD64_v3 + TZCNTQ R11, R11 + +#else + BSFQ R11, R11 + +#endif + SARQ $0x03, R11 + LEAL (R12)(R11*1), R12 + JMP repeat_extend_forward_end_encodeBlockAsm + +matchlen_match4_repeat_extend_encodeBlockAsm: + CMPL R9, $0x04 + JB matchlen_match2_repeat_extend_encodeBlockAsm + MOVL (R10)(R12*1), R11 + CMPL (SI)(R12*1), R11 + JNE matchlen_match2_repeat_extend_encodeBlockAsm + LEAL -4(R9), R9 + LEAL 4(R12), R12 + +matchlen_match2_repeat_extend_encodeBlockAsm: + CMPL R9, $0x01 + JE matchlen_match1_repeat_extend_encodeBlockAsm + JB repeat_extend_forward_end_encodeBlockAsm + MOVW (R10)(R12*1), R11 + CMPW (SI)(R12*1), R11 + JNE matchlen_match1_repeat_extend_encodeBlockAsm + LEAL 2(R12), R12 + SUBL $0x02, R9 + JZ repeat_extend_forward_end_encodeBlockAsm + +matchlen_match1_repeat_extend_encodeBlockAsm: + MOVB (R10)(R12*1), R11 + CMPB (SI)(R12*1), R11 + JNE repeat_extend_forward_end_encodeBlockAsm + LEAL 1(R12), R12 + +repeat_extend_forward_end_encodeBlockAsm: + ADDL R12, DX + MOVL DX, SI + SUBL DI, SI + MOVL 16(SP), DI + TESTL R8, R8 + JZ repeat_as_copy_encodeBlockAsm + + // emitRepeat +emit_repeat_again_match_repeat_encodeBlockAsm: + MOVL SI, R8 + LEAL -4(SI), SI + CMPL R8, $0x08 + JBE repeat_two_match_repeat_encodeBlockAsm + CMPL R8, $0x0c + JAE cant_repeat_two_offset_match_repeat_encodeBlockAsm + CMPL DI, $0x00000800 + JB repeat_two_offset_match_repeat_encodeBlockAsm + +cant_repeat_two_offset_match_repeat_encodeBlockAsm: + CMPL SI, $0x00000104 + JB repeat_three_match_repeat_encodeBlockAsm + CMPL SI, $0x00010100 + JB repeat_four_match_repeat_encodeBlockAsm + CMPL SI, $0x0100ffff + JB repeat_five_match_repeat_encodeBlockAsm + LEAL -16842747(SI), SI + MOVL $0xfffb001d, (CX) + MOVB $0xff, 4(CX) + ADDQ $0x05, CX + JMP emit_repeat_again_match_repeat_encodeBlockAsm + +repeat_five_match_repeat_encodeBlockAsm: + LEAL -65536(SI), SI + MOVL SI, DI + MOVW $0x001d, (CX) + MOVW SI, 2(CX) + SARL $0x10, DI + MOVB DI, 4(CX) + ADDQ $0x05, CX + JMP repeat_end_emit_encodeBlockAsm + +repeat_four_match_repeat_encodeBlockAsm: + LEAL -256(SI), SI + MOVW $0x0019, (CX) + MOVW SI, 2(CX) + ADDQ $0x04, CX + JMP repeat_end_emit_encodeBlockAsm + +repeat_three_match_repeat_encodeBlockAsm: + LEAL -4(SI), SI + MOVW $0x0015, (CX) + MOVB SI, 2(CX) + ADDQ $0x03, CX + JMP repeat_end_emit_encodeBlockAsm + +repeat_two_match_repeat_encodeBlockAsm: + SHLL $0x02, SI + ORL $0x01, SI + MOVW SI, (CX) + ADDQ $0x02, CX + JMP repeat_end_emit_encodeBlockAsm + +repeat_two_offset_match_repeat_encodeBlockAsm: + XORQ R8, R8 + LEAL 1(R8)(SI*4), SI + MOVB DI, 1(CX) + SARL $0x08, DI + SHLL $0x05, DI + ORL DI, SI + MOVB SI, (CX) + ADDQ $0x02, CX + JMP repeat_end_emit_encodeBlockAsm + +repeat_as_copy_encodeBlockAsm: + // emitCopy + CMPL DI, $0x00010000 + JB two_byte_offset_repeat_as_copy_encodeBlockAsm + CMPL SI, $0x40 + JBE four_bytes_remain_repeat_as_copy_encodeBlockAsm + MOVB $0xff, (CX) + MOVL DI, 1(CX) + LEAL -64(SI), SI + ADDQ $0x05, CX + CMPL SI, $0x04 + JB four_bytes_remain_repeat_as_copy_encodeBlockAsm + + // emitRepeat +emit_repeat_again_repeat_as_copy_encodeBlockAsm_emit_copy: + MOVL SI, R8 + LEAL -4(SI), SI + CMPL R8, $0x08 + JBE repeat_two_repeat_as_copy_encodeBlockAsm_emit_copy + CMPL R8, $0x0c + JAE cant_repeat_two_offset_repeat_as_copy_encodeBlockAsm_emit_copy + CMPL DI, $0x00000800 + JB repeat_two_offset_repeat_as_copy_encodeBlockAsm_emit_copy + +cant_repeat_two_offset_repeat_as_copy_encodeBlockAsm_emit_copy: + CMPL SI, $0x00000104 + JB repeat_three_repeat_as_copy_encodeBlockAsm_emit_copy + CMPL SI, $0x00010100 + JB repeat_four_repeat_as_copy_encodeBlockAsm_emit_copy + CMPL SI, $0x0100ffff + JB repeat_five_repeat_as_copy_encodeBlockAsm_emit_copy + LEAL -16842747(SI), SI + MOVL $0xfffb001d, (CX) + MOVB $0xff, 4(CX) + ADDQ $0x05, CX + JMP emit_repeat_again_repeat_as_copy_encodeBlockAsm_emit_copy + +repeat_five_repeat_as_copy_encodeBlockAsm_emit_copy: + LEAL -65536(SI), SI + MOVL SI, DI + MOVW $0x001d, (CX) + MOVW SI, 2(CX) + SARL $0x10, DI + MOVB DI, 4(CX) + ADDQ $0x05, CX + JMP repeat_end_emit_encodeBlockAsm + +repeat_four_repeat_as_copy_encodeBlockAsm_emit_copy: + LEAL -256(SI), SI + MOVW $0x0019, (CX) + MOVW SI, 2(CX) + ADDQ $0x04, CX + JMP repeat_end_emit_encodeBlockAsm + +repeat_three_repeat_as_copy_encodeBlockAsm_emit_copy: + LEAL -4(SI), SI + MOVW $0x0015, (CX) + MOVB SI, 2(CX) + ADDQ $0x03, CX + JMP repeat_end_emit_encodeBlockAsm + +repeat_two_repeat_as_copy_encodeBlockAsm_emit_copy: + SHLL $0x02, SI + ORL $0x01, SI + MOVW SI, (CX) + ADDQ $0x02, CX + JMP repeat_end_emit_encodeBlockAsm + +repeat_two_offset_repeat_as_copy_encodeBlockAsm_emit_copy: + XORQ R8, R8 + LEAL 1(R8)(SI*4), SI + MOVB DI, 1(CX) + SARL $0x08, DI + SHLL $0x05, DI + ORL DI, SI + MOVB SI, (CX) + ADDQ $0x02, CX + JMP repeat_end_emit_encodeBlockAsm + +four_bytes_remain_repeat_as_copy_encodeBlockAsm: + TESTL SI, SI + JZ repeat_end_emit_encodeBlockAsm + XORL R8, R8 + LEAL -1(R8)(SI*4), SI + MOVB SI, (CX) + MOVL DI, 1(CX) + ADDQ $0x05, CX + JMP repeat_end_emit_encodeBlockAsm + +two_byte_offset_repeat_as_copy_encodeBlockAsm: + CMPL SI, $0x40 + JBE two_byte_offset_short_repeat_as_copy_encodeBlockAsm + CMPL DI, $0x00000800 + JAE long_offset_short_repeat_as_copy_encodeBlockAsm + MOVL $0x00000001, R8 + LEAL 16(R8), R8 + MOVB DI, 1(CX) + MOVL DI, R9 + SHRL $0x08, R9 + SHLL $0x05, R9 + ORL R9, R8 + MOVB R8, (CX) + ADDQ $0x02, CX + SUBL $0x08, SI + + // emitRepeat + LEAL -4(SI), SI + JMP cant_repeat_two_offset_repeat_as_copy_encodeBlockAsm_emit_copy_short_2b + +emit_repeat_again_repeat_as_copy_encodeBlockAsm_emit_copy_short_2b: + MOVL SI, R8 + LEAL -4(SI), SI + CMPL R8, $0x08 + JBE repeat_two_repeat_as_copy_encodeBlockAsm_emit_copy_short_2b + CMPL R8, $0x0c + JAE cant_repeat_two_offset_repeat_as_copy_encodeBlockAsm_emit_copy_short_2b + CMPL DI, $0x00000800 + JB repeat_two_offset_repeat_as_copy_encodeBlockAsm_emit_copy_short_2b + +cant_repeat_two_offset_repeat_as_copy_encodeBlockAsm_emit_copy_short_2b: + CMPL SI, $0x00000104 + JB repeat_three_repeat_as_copy_encodeBlockAsm_emit_copy_short_2b + CMPL SI, $0x00010100 + JB repeat_four_repeat_as_copy_encodeBlockAsm_emit_copy_short_2b + CMPL SI, $0x0100ffff + JB repeat_five_repeat_as_copy_encodeBlockAsm_emit_copy_short_2b + LEAL -16842747(SI), SI + MOVL $0xfffb001d, (CX) + MOVB $0xff, 4(CX) + ADDQ $0x05, CX + JMP emit_repeat_again_repeat_as_copy_encodeBlockAsm_emit_copy_short_2b + +repeat_five_repeat_as_copy_encodeBlockAsm_emit_copy_short_2b: + LEAL -65536(SI), SI + MOVL SI, DI + MOVW $0x001d, (CX) + MOVW SI, 2(CX) + SARL $0x10, DI + MOVB DI, 4(CX) + ADDQ $0x05, CX + JMP repeat_end_emit_encodeBlockAsm + +repeat_four_repeat_as_copy_encodeBlockAsm_emit_copy_short_2b: + LEAL -256(SI), SI + MOVW $0x0019, (CX) + MOVW SI, 2(CX) + ADDQ $0x04, CX + JMP repeat_end_emit_encodeBlockAsm + +repeat_three_repeat_as_copy_encodeBlockAsm_emit_copy_short_2b: + LEAL -4(SI), SI + MOVW $0x0015, (CX) + MOVB SI, 2(CX) + ADDQ $0x03, CX + JMP repeat_end_emit_encodeBlockAsm + +repeat_two_repeat_as_copy_encodeBlockAsm_emit_copy_short_2b: + SHLL $0x02, SI + ORL $0x01, SI + MOVW SI, (CX) + ADDQ $0x02, CX + JMP repeat_end_emit_encodeBlockAsm + +repeat_two_offset_repeat_as_copy_encodeBlockAsm_emit_copy_short_2b: + XORQ R8, R8 + LEAL 1(R8)(SI*4), SI + MOVB DI, 1(CX) + SARL $0x08, DI + SHLL $0x05, DI + ORL DI, SI + MOVB SI, (CX) + ADDQ $0x02, CX + JMP repeat_end_emit_encodeBlockAsm + +long_offset_short_repeat_as_copy_encodeBlockAsm: + MOVB $0xee, (CX) + MOVW DI, 1(CX) + LEAL -60(SI), SI + ADDQ $0x03, CX + + // emitRepeat +emit_repeat_again_repeat_as_copy_encodeBlockAsm_emit_copy_short: + MOVL SI, R8 + LEAL -4(SI), SI + CMPL R8, $0x08 + JBE repeat_two_repeat_as_copy_encodeBlockAsm_emit_copy_short + CMPL R8, $0x0c + JAE cant_repeat_two_offset_repeat_as_copy_encodeBlockAsm_emit_copy_short + CMPL DI, $0x00000800 + JB repeat_two_offset_repeat_as_copy_encodeBlockAsm_emit_copy_short + +cant_repeat_two_offset_repeat_as_copy_encodeBlockAsm_emit_copy_short: + CMPL SI, $0x00000104 + JB repeat_three_repeat_as_copy_encodeBlockAsm_emit_copy_short + CMPL SI, $0x00010100 + JB repeat_four_repeat_as_copy_encodeBlockAsm_emit_copy_short + CMPL SI, $0x0100ffff + JB repeat_five_repeat_as_copy_encodeBlockAsm_emit_copy_short + LEAL -16842747(SI), SI + MOVL $0xfffb001d, (CX) + MOVB $0xff, 4(CX) + ADDQ $0x05, CX + JMP emit_repeat_again_repeat_as_copy_encodeBlockAsm_emit_copy_short + +repeat_five_repeat_as_copy_encodeBlockAsm_emit_copy_short: + LEAL -65536(SI), SI + MOVL SI, DI + MOVW $0x001d, (CX) + MOVW SI, 2(CX) + SARL $0x10, DI + MOVB DI, 4(CX) + ADDQ $0x05, CX + JMP repeat_end_emit_encodeBlockAsm + +repeat_four_repeat_as_copy_encodeBlockAsm_emit_copy_short: + LEAL -256(SI), SI + MOVW $0x0019, (CX) + MOVW SI, 2(CX) + ADDQ $0x04, CX + JMP repeat_end_emit_encodeBlockAsm + +repeat_three_repeat_as_copy_encodeBlockAsm_emit_copy_short: + LEAL -4(SI), SI + MOVW $0x0015, (CX) + MOVB SI, 2(CX) + ADDQ $0x03, CX + JMP repeat_end_emit_encodeBlockAsm + +repeat_two_repeat_as_copy_encodeBlockAsm_emit_copy_short: + SHLL $0x02, SI + ORL $0x01, SI + MOVW SI, (CX) + ADDQ $0x02, CX + JMP repeat_end_emit_encodeBlockAsm + +repeat_two_offset_repeat_as_copy_encodeBlockAsm_emit_copy_short: + XORQ R8, R8 + LEAL 1(R8)(SI*4), SI + MOVB DI, 1(CX) + SARL $0x08, DI + SHLL $0x05, DI + ORL DI, SI + MOVB SI, (CX) + ADDQ $0x02, CX + JMP repeat_end_emit_encodeBlockAsm + +two_byte_offset_short_repeat_as_copy_encodeBlockAsm: + MOVL SI, R8 + SHLL $0x02, R8 + CMPL SI, $0x0c + JAE emit_copy_three_repeat_as_copy_encodeBlockAsm + CMPL DI, $0x00000800 + JAE emit_copy_three_repeat_as_copy_encodeBlockAsm + LEAL -15(R8), R8 + MOVB DI, 1(CX) + SHRL $0x08, DI + SHLL $0x05, DI + ORL DI, R8 + MOVB R8, (CX) + ADDQ $0x02, CX + JMP repeat_end_emit_encodeBlockAsm + +emit_copy_three_repeat_as_copy_encodeBlockAsm: + LEAL -2(R8), R8 + MOVB R8, (CX) + MOVW DI, 1(CX) + ADDQ $0x03, CX + +repeat_end_emit_encodeBlockAsm: + MOVL DX, 12(SP) + JMP search_loop_encodeBlockAsm + +no_repeat_found_encodeBlockAsm: + CMPL (BX)(SI*1), DI + JEQ candidate_match_encodeBlockAsm + SHRQ $0x08, DI + MOVL (AX)(R10*4), SI + LEAL 2(DX), R9 + CMPL (BX)(R8*1), DI + JEQ candidate2_match_encodeBlockAsm + MOVL R9, (AX)(R10*4) + SHRQ $0x08, DI + CMPL (BX)(SI*1), DI + JEQ candidate3_match_encodeBlockAsm + MOVL 20(SP), DX + JMP search_loop_encodeBlockAsm + +candidate3_match_encodeBlockAsm: + ADDL $0x02, DX + JMP candidate_match_encodeBlockAsm + +candidate2_match_encodeBlockAsm: + MOVL R9, (AX)(R10*4) + INCL DX + MOVL R8, SI + +candidate_match_encodeBlockAsm: + MOVL 12(SP), DI + TESTL SI, SI + JZ match_extend_back_end_encodeBlockAsm + +match_extend_back_loop_encodeBlockAsm: + CMPL DX, DI + JBE match_extend_back_end_encodeBlockAsm + MOVB -1(BX)(SI*1), R8 + MOVB -1(BX)(DX*1), R9 + CMPB R8, R9 + JNE match_extend_back_end_encodeBlockAsm + LEAL -1(DX), DX + DECL SI + JZ match_extend_back_end_encodeBlockAsm + JMP match_extend_back_loop_encodeBlockAsm + +match_extend_back_end_encodeBlockAsm: + MOVL DX, DI + SUBL 12(SP), DI + LEAQ 5(CX)(DI*1), DI + CMPQ DI, (SP) + JB match_dst_size_check_encodeBlockAsm + MOVQ $0x00000000, ret+56(FP) + RET + +match_dst_size_check_encodeBlockAsm: + MOVL DX, DI + MOVL 12(SP), R8 + CMPL R8, DI + JEQ emit_literal_done_match_emit_encodeBlockAsm + MOVL DI, R9 + MOVL DI, 12(SP) + LEAQ (BX)(R8*1), DI + SUBL R8, R9 + LEAL -1(R9), R8 + CMPL R8, $0x3c + JB one_byte_match_emit_encodeBlockAsm + CMPL R8, $0x00000100 + JB two_bytes_match_emit_encodeBlockAsm + CMPL R8, $0x00010000 + JB three_bytes_match_emit_encodeBlockAsm + CMPL R8, $0x01000000 + JB four_bytes_match_emit_encodeBlockAsm + MOVB $0xfc, (CX) + MOVL R8, 1(CX) + ADDQ $0x05, CX + JMP memmove_long_match_emit_encodeBlockAsm + +four_bytes_match_emit_encodeBlockAsm: + MOVL R8, R10 + SHRL $0x10, R10 + MOVB $0xf8, (CX) + MOVW R8, 1(CX) + MOVB R10, 3(CX) + ADDQ $0x04, CX + JMP memmove_long_match_emit_encodeBlockAsm + +three_bytes_match_emit_encodeBlockAsm: + MOVB $0xf4, (CX) + MOVW R8, 1(CX) + ADDQ $0x03, CX + JMP memmove_long_match_emit_encodeBlockAsm + +two_bytes_match_emit_encodeBlockAsm: + MOVB $0xf0, (CX) + MOVB R8, 1(CX) + ADDQ $0x02, CX + CMPL R8, $0x40 + JB memmove_match_emit_encodeBlockAsm + JMP memmove_long_match_emit_encodeBlockAsm + +one_byte_match_emit_encodeBlockAsm: + SHLB $0x02, R8 + MOVB R8, (CX) + ADDQ $0x01, CX + +memmove_match_emit_encodeBlockAsm: + LEAQ (CX)(R9*1), R8 + + // genMemMoveShort + CMPQ R9, $0x08 + JBE emit_lit_memmove_match_emit_encodeBlockAsm_memmove_move_8 + CMPQ R9, $0x10 + JBE emit_lit_memmove_match_emit_encodeBlockAsm_memmove_move_8through16 + CMPQ R9, $0x20 + JBE emit_lit_memmove_match_emit_encodeBlockAsm_memmove_move_17through32 + JMP emit_lit_memmove_match_emit_encodeBlockAsm_memmove_move_33through64 + +emit_lit_memmove_match_emit_encodeBlockAsm_memmove_move_8: + MOVQ (DI), R10 + MOVQ R10, (CX) + JMP memmove_end_copy_match_emit_encodeBlockAsm + +emit_lit_memmove_match_emit_encodeBlockAsm_memmove_move_8through16: + MOVQ (DI), R10 + MOVQ -8(DI)(R9*1), DI + MOVQ R10, (CX) + MOVQ DI, -8(CX)(R9*1) + JMP memmove_end_copy_match_emit_encodeBlockAsm + +emit_lit_memmove_match_emit_encodeBlockAsm_memmove_move_17through32: + MOVOU (DI), X0 + MOVOU -16(DI)(R9*1), X1 + MOVOU X0, (CX) + MOVOU X1, -16(CX)(R9*1) + JMP memmove_end_copy_match_emit_encodeBlockAsm + +emit_lit_memmove_match_emit_encodeBlockAsm_memmove_move_33through64: + MOVOU (DI), X0 + MOVOU 16(DI), X1 + MOVOU -32(DI)(R9*1), X2 + MOVOU -16(DI)(R9*1), X3 + MOVOU X0, (CX) + MOVOU X1, 16(CX) + MOVOU X2, -32(CX)(R9*1) + MOVOU X3, -16(CX)(R9*1) + +memmove_end_copy_match_emit_encodeBlockAsm: + MOVQ R8, CX + JMP emit_literal_done_match_emit_encodeBlockAsm + +memmove_long_match_emit_encodeBlockAsm: + LEAQ (CX)(R9*1), R8 + + // genMemMoveLong + MOVOU (DI), X0 + MOVOU 16(DI), X1 + MOVOU -32(DI)(R9*1), X2 + MOVOU -16(DI)(R9*1), X3 + MOVQ R9, R11 + SHRQ $0x05, R11 + MOVQ CX, R10 + ANDL $0x0000001f, R10 + MOVQ $0x00000040, R12 + SUBQ R10, R12 + DECQ R11 + JA emit_lit_memmove_long_match_emit_encodeBlockAsmlarge_forward_sse_loop_32 + LEAQ -32(DI)(R12*1), R10 + LEAQ -32(CX)(R12*1), R13 + +emit_lit_memmove_long_match_emit_encodeBlockAsmlarge_big_loop_back: + MOVOU (R10), X4 + MOVOU 16(R10), X5 + MOVOA X4, (R13) + MOVOA X5, 16(R13) + ADDQ $0x20, R13 + ADDQ $0x20, R10 + ADDQ $0x20, R12 + DECQ R11 + JNA emit_lit_memmove_long_match_emit_encodeBlockAsmlarge_big_loop_back + +emit_lit_memmove_long_match_emit_encodeBlockAsmlarge_forward_sse_loop_32: + MOVOU -32(DI)(R12*1), X4 + MOVOU -16(DI)(R12*1), X5 + MOVOA X4, -32(CX)(R12*1) + MOVOA X5, -16(CX)(R12*1) + ADDQ $0x20, R12 + CMPQ R9, R12 + JAE emit_lit_memmove_long_match_emit_encodeBlockAsmlarge_forward_sse_loop_32 + MOVOU X0, (CX) + MOVOU X1, 16(CX) + MOVOU X2, -32(CX)(R9*1) + MOVOU X3, -16(CX)(R9*1) + MOVQ R8, CX + +emit_literal_done_match_emit_encodeBlockAsm: +match_nolit_loop_encodeBlockAsm: + MOVL DX, DI + SUBL SI, DI + MOVL DI, 16(SP) + ADDL $0x04, DX + ADDL $0x04, SI + MOVQ src_len+32(FP), DI + SUBL DX, DI + LEAQ (BX)(DX*1), R8 + LEAQ (BX)(SI*1), SI + + // matchLen + XORL R10, R10 + +matchlen_loopback_16_match_nolit_encodeBlockAsm: + CMPL DI, $0x10 + JB matchlen_match8_match_nolit_encodeBlockAsm + MOVQ (R8)(R10*1), R9 + MOVQ 8(R8)(R10*1), R11 + XORQ (SI)(R10*1), R9 + JNZ matchlen_bsf_8_match_nolit_encodeBlockAsm + XORQ 8(SI)(R10*1), R11 + JNZ matchlen_bsf_16match_nolit_encodeBlockAsm + LEAL -16(DI), DI + LEAL 16(R10), R10 + JMP matchlen_loopback_16_match_nolit_encodeBlockAsm + +matchlen_bsf_16match_nolit_encodeBlockAsm: +#ifdef GOAMD64_v3 + TZCNTQ R11, R11 + +#else + BSFQ R11, R11 + +#endif + SARQ $0x03, R11 + LEAL 8(R10)(R11*1), R10 + JMP match_nolit_end_encodeBlockAsm + +matchlen_match8_match_nolit_encodeBlockAsm: + CMPL DI, $0x08 + JB matchlen_match4_match_nolit_encodeBlockAsm + MOVQ (R8)(R10*1), R9 + XORQ (SI)(R10*1), R9 + JNZ matchlen_bsf_8_match_nolit_encodeBlockAsm + LEAL -8(DI), DI + LEAL 8(R10), R10 + JMP matchlen_match4_match_nolit_encodeBlockAsm + +matchlen_bsf_8_match_nolit_encodeBlockAsm: +#ifdef GOAMD64_v3 + TZCNTQ R9, R9 + +#else + BSFQ R9, R9 + +#endif + SARQ $0x03, R9 + LEAL (R10)(R9*1), R10 + JMP match_nolit_end_encodeBlockAsm + +matchlen_match4_match_nolit_encodeBlockAsm: + CMPL DI, $0x04 + JB matchlen_match2_match_nolit_encodeBlockAsm + MOVL (R8)(R10*1), R9 + CMPL (SI)(R10*1), R9 + JNE matchlen_match2_match_nolit_encodeBlockAsm + LEAL -4(DI), DI + LEAL 4(R10), R10 + +matchlen_match2_match_nolit_encodeBlockAsm: + CMPL DI, $0x01 + JE matchlen_match1_match_nolit_encodeBlockAsm + JB match_nolit_end_encodeBlockAsm + MOVW (R8)(R10*1), R9 + CMPW (SI)(R10*1), R9 + JNE matchlen_match1_match_nolit_encodeBlockAsm + LEAL 2(R10), R10 + SUBL $0x02, DI + JZ match_nolit_end_encodeBlockAsm + +matchlen_match1_match_nolit_encodeBlockAsm: + MOVB (R8)(R10*1), R9 + CMPB (SI)(R10*1), R9 + JNE match_nolit_end_encodeBlockAsm + LEAL 1(R10), R10 + +match_nolit_end_encodeBlockAsm: + ADDL R10, DX + MOVL 16(SP), SI + ADDL $0x04, R10 + MOVL DX, 12(SP) + + // emitCopy + CMPL SI, $0x00010000 + JB two_byte_offset_match_nolit_encodeBlockAsm + CMPL R10, $0x40 + JBE four_bytes_remain_match_nolit_encodeBlockAsm + MOVB $0xff, (CX) + MOVL SI, 1(CX) + LEAL -64(R10), R10 + ADDQ $0x05, CX + CMPL R10, $0x04 + JB four_bytes_remain_match_nolit_encodeBlockAsm + + // emitRepeat +emit_repeat_again_match_nolit_encodeBlockAsm_emit_copy: + MOVL R10, DI + LEAL -4(R10), R10 + CMPL DI, $0x08 + JBE repeat_two_match_nolit_encodeBlockAsm_emit_copy + CMPL DI, $0x0c + JAE cant_repeat_two_offset_match_nolit_encodeBlockAsm_emit_copy + CMPL SI, $0x00000800 + JB repeat_two_offset_match_nolit_encodeBlockAsm_emit_copy + +cant_repeat_two_offset_match_nolit_encodeBlockAsm_emit_copy: + CMPL R10, $0x00000104 + JB repeat_three_match_nolit_encodeBlockAsm_emit_copy + CMPL R10, $0x00010100 + JB repeat_four_match_nolit_encodeBlockAsm_emit_copy + CMPL R10, $0x0100ffff + JB repeat_five_match_nolit_encodeBlockAsm_emit_copy + LEAL -16842747(R10), R10 + MOVL $0xfffb001d, (CX) + MOVB $0xff, 4(CX) + ADDQ $0x05, CX + JMP emit_repeat_again_match_nolit_encodeBlockAsm_emit_copy + +repeat_five_match_nolit_encodeBlockAsm_emit_copy: + LEAL -65536(R10), R10 + MOVL R10, SI + MOVW $0x001d, (CX) + MOVW R10, 2(CX) + SARL $0x10, SI + MOVB SI, 4(CX) + ADDQ $0x05, CX + JMP match_nolit_emitcopy_end_encodeBlockAsm + +repeat_four_match_nolit_encodeBlockAsm_emit_copy: + LEAL -256(R10), R10 + MOVW $0x0019, (CX) + MOVW R10, 2(CX) + ADDQ $0x04, CX + JMP match_nolit_emitcopy_end_encodeBlockAsm + +repeat_three_match_nolit_encodeBlockAsm_emit_copy: + LEAL -4(R10), R10 + MOVW $0x0015, (CX) + MOVB R10, 2(CX) + ADDQ $0x03, CX + JMP match_nolit_emitcopy_end_encodeBlockAsm + +repeat_two_match_nolit_encodeBlockAsm_emit_copy: + SHLL $0x02, R10 + ORL $0x01, R10 + MOVW R10, (CX) + ADDQ $0x02, CX + JMP match_nolit_emitcopy_end_encodeBlockAsm + +repeat_two_offset_match_nolit_encodeBlockAsm_emit_copy: + XORQ DI, DI + LEAL 1(DI)(R10*4), R10 + MOVB SI, 1(CX) + SARL $0x08, SI + SHLL $0x05, SI + ORL SI, R10 + MOVB R10, (CX) + ADDQ $0x02, CX + JMP match_nolit_emitcopy_end_encodeBlockAsm + +four_bytes_remain_match_nolit_encodeBlockAsm: + TESTL R10, R10 + JZ match_nolit_emitcopy_end_encodeBlockAsm + XORL DI, DI + LEAL -1(DI)(R10*4), R10 + MOVB R10, (CX) + MOVL SI, 1(CX) + ADDQ $0x05, CX + JMP match_nolit_emitcopy_end_encodeBlockAsm + +two_byte_offset_match_nolit_encodeBlockAsm: + CMPL R10, $0x40 + JBE two_byte_offset_short_match_nolit_encodeBlockAsm + CMPL SI, $0x00000800 + JAE long_offset_short_match_nolit_encodeBlockAsm + MOVL $0x00000001, DI + LEAL 16(DI), DI + MOVB SI, 1(CX) + MOVL SI, R8 + SHRL $0x08, R8 + SHLL $0x05, R8 + ORL R8, DI + MOVB DI, (CX) + ADDQ $0x02, CX + SUBL $0x08, R10 + + // emitRepeat + LEAL -4(R10), R10 + JMP cant_repeat_two_offset_match_nolit_encodeBlockAsm_emit_copy_short_2b + +emit_repeat_again_match_nolit_encodeBlockAsm_emit_copy_short_2b: + MOVL R10, DI + LEAL -4(R10), R10 + CMPL DI, $0x08 + JBE repeat_two_match_nolit_encodeBlockAsm_emit_copy_short_2b + CMPL DI, $0x0c + JAE cant_repeat_two_offset_match_nolit_encodeBlockAsm_emit_copy_short_2b + CMPL SI, $0x00000800 + JB repeat_two_offset_match_nolit_encodeBlockAsm_emit_copy_short_2b + +cant_repeat_two_offset_match_nolit_encodeBlockAsm_emit_copy_short_2b: + CMPL R10, $0x00000104 + JB repeat_three_match_nolit_encodeBlockAsm_emit_copy_short_2b + CMPL R10, $0x00010100 + JB repeat_four_match_nolit_encodeBlockAsm_emit_copy_short_2b + CMPL R10, $0x0100ffff + JB repeat_five_match_nolit_encodeBlockAsm_emit_copy_short_2b + LEAL -16842747(R10), R10 + MOVL $0xfffb001d, (CX) + MOVB $0xff, 4(CX) + ADDQ $0x05, CX + JMP emit_repeat_again_match_nolit_encodeBlockAsm_emit_copy_short_2b + +repeat_five_match_nolit_encodeBlockAsm_emit_copy_short_2b: + LEAL -65536(R10), R10 + MOVL R10, SI + MOVW $0x001d, (CX) + MOVW R10, 2(CX) + SARL $0x10, SI + MOVB SI, 4(CX) + ADDQ $0x05, CX + JMP match_nolit_emitcopy_end_encodeBlockAsm + +repeat_four_match_nolit_encodeBlockAsm_emit_copy_short_2b: + LEAL -256(R10), R10 + MOVW $0x0019, (CX) + MOVW R10, 2(CX) + ADDQ $0x04, CX + JMP match_nolit_emitcopy_end_encodeBlockAsm + +repeat_three_match_nolit_encodeBlockAsm_emit_copy_short_2b: + LEAL -4(R10), R10 + MOVW $0x0015, (CX) + MOVB R10, 2(CX) + ADDQ $0x03, CX + JMP match_nolit_emitcopy_end_encodeBlockAsm + +repeat_two_match_nolit_encodeBlockAsm_emit_copy_short_2b: + SHLL $0x02, R10 + ORL $0x01, R10 + MOVW R10, (CX) + ADDQ $0x02, CX + JMP match_nolit_emitcopy_end_encodeBlockAsm + +repeat_two_offset_match_nolit_encodeBlockAsm_emit_copy_short_2b: + XORQ DI, DI + LEAL 1(DI)(R10*4), R10 + MOVB SI, 1(CX) + SARL $0x08, SI + SHLL $0x05, SI + ORL SI, R10 + MOVB R10, (CX) + ADDQ $0x02, CX + JMP match_nolit_emitcopy_end_encodeBlockAsm + +long_offset_short_match_nolit_encodeBlockAsm: + MOVB $0xee, (CX) + MOVW SI, 1(CX) + LEAL -60(R10), R10 + ADDQ $0x03, CX + + // emitRepeat +emit_repeat_again_match_nolit_encodeBlockAsm_emit_copy_short: + MOVL R10, DI + LEAL -4(R10), R10 + CMPL DI, $0x08 + JBE repeat_two_match_nolit_encodeBlockAsm_emit_copy_short + CMPL DI, $0x0c + JAE cant_repeat_two_offset_match_nolit_encodeBlockAsm_emit_copy_short + CMPL SI, $0x00000800 + JB repeat_two_offset_match_nolit_encodeBlockAsm_emit_copy_short + +cant_repeat_two_offset_match_nolit_encodeBlockAsm_emit_copy_short: + CMPL R10, $0x00000104 + JB repeat_three_match_nolit_encodeBlockAsm_emit_copy_short + CMPL R10, $0x00010100 + JB repeat_four_match_nolit_encodeBlockAsm_emit_copy_short + CMPL R10, $0x0100ffff + JB repeat_five_match_nolit_encodeBlockAsm_emit_copy_short + LEAL -16842747(R10), R10 + MOVL $0xfffb001d, (CX) + MOVB $0xff, 4(CX) + ADDQ $0x05, CX + JMP emit_repeat_again_match_nolit_encodeBlockAsm_emit_copy_short + +repeat_five_match_nolit_encodeBlockAsm_emit_copy_short: + LEAL -65536(R10), R10 + MOVL R10, SI + MOVW $0x001d, (CX) + MOVW R10, 2(CX) + SARL $0x10, SI + MOVB SI, 4(CX) + ADDQ $0x05, CX + JMP match_nolit_emitcopy_end_encodeBlockAsm + +repeat_four_match_nolit_encodeBlockAsm_emit_copy_short: + LEAL -256(R10), R10 + MOVW $0x0019, (CX) + MOVW R10, 2(CX) + ADDQ $0x04, CX + JMP match_nolit_emitcopy_end_encodeBlockAsm + +repeat_three_match_nolit_encodeBlockAsm_emit_copy_short: + LEAL -4(R10), R10 + MOVW $0x0015, (CX) + MOVB R10, 2(CX) + ADDQ $0x03, CX + JMP match_nolit_emitcopy_end_encodeBlockAsm + +repeat_two_match_nolit_encodeBlockAsm_emit_copy_short: + SHLL $0x02, R10 + ORL $0x01, R10 + MOVW R10, (CX) + ADDQ $0x02, CX + JMP match_nolit_emitcopy_end_encodeBlockAsm + +repeat_two_offset_match_nolit_encodeBlockAsm_emit_copy_short: + XORQ DI, DI + LEAL 1(DI)(R10*4), R10 + MOVB SI, 1(CX) + SARL $0x08, SI + SHLL $0x05, SI + ORL SI, R10 + MOVB R10, (CX) + ADDQ $0x02, CX + JMP match_nolit_emitcopy_end_encodeBlockAsm + +two_byte_offset_short_match_nolit_encodeBlockAsm: + MOVL R10, DI + SHLL $0x02, DI + CMPL R10, $0x0c + JAE emit_copy_three_match_nolit_encodeBlockAsm + CMPL SI, $0x00000800 + JAE emit_copy_three_match_nolit_encodeBlockAsm + LEAL -15(DI), DI + MOVB SI, 1(CX) + SHRL $0x08, SI + SHLL $0x05, SI + ORL SI, DI + MOVB DI, (CX) + ADDQ $0x02, CX + JMP match_nolit_emitcopy_end_encodeBlockAsm + +emit_copy_three_match_nolit_encodeBlockAsm: + LEAL -2(DI), DI + MOVB DI, (CX) + MOVW SI, 1(CX) + ADDQ $0x03, CX + +match_nolit_emitcopy_end_encodeBlockAsm: + CMPL DX, 8(SP) + JAE emit_remainder_encodeBlockAsm + MOVQ -2(BX)(DX*1), DI + CMPQ CX, (SP) + JB match_nolit_dst_ok_encodeBlockAsm + MOVQ $0x00000000, ret+56(FP) + RET + +match_nolit_dst_ok_encodeBlockAsm: + MOVQ $0x0000cf1bbcdcbf9b, R9 + MOVQ DI, R8 + SHRQ $0x10, DI + MOVQ DI, SI + SHLQ $0x10, R8 + IMULQ R9, R8 + SHRQ $0x32, R8 + SHLQ $0x10, SI + IMULQ R9, SI + SHRQ $0x32, SI + LEAL -2(DX), R9 + LEAQ (AX)(SI*4), R10 + MOVL (R10), SI + MOVL R9, (AX)(R8*4) + MOVL DX, (R10) + CMPL (BX)(SI*1), DI + JEQ match_nolit_loop_encodeBlockAsm + INCL DX + JMP search_loop_encodeBlockAsm + +emit_remainder_encodeBlockAsm: + MOVQ src_len+32(FP), AX + SUBL 12(SP), AX + LEAQ 5(CX)(AX*1), AX + CMPQ AX, (SP) + JB emit_remainder_ok_encodeBlockAsm + MOVQ $0x00000000, ret+56(FP) + RET + +emit_remainder_ok_encodeBlockAsm: + MOVQ src_len+32(FP), AX + MOVL 12(SP), DX + CMPL DX, AX + JEQ emit_literal_done_emit_remainder_encodeBlockAsm + MOVL AX, SI + MOVL AX, 12(SP) + LEAQ (BX)(DX*1), AX + SUBL DX, SI + LEAL -1(SI), DX + CMPL DX, $0x3c + JB one_byte_emit_remainder_encodeBlockAsm + CMPL DX, $0x00000100 + JB two_bytes_emit_remainder_encodeBlockAsm + CMPL DX, $0x00010000 + JB three_bytes_emit_remainder_encodeBlockAsm + CMPL DX, $0x01000000 + JB four_bytes_emit_remainder_encodeBlockAsm + MOVB $0xfc, (CX) + MOVL DX, 1(CX) + ADDQ $0x05, CX + JMP memmove_long_emit_remainder_encodeBlockAsm + +four_bytes_emit_remainder_encodeBlockAsm: + MOVL DX, BX + SHRL $0x10, BX + MOVB $0xf8, (CX) + MOVW DX, 1(CX) + MOVB BL, 3(CX) + ADDQ $0x04, CX + JMP memmove_long_emit_remainder_encodeBlockAsm + +three_bytes_emit_remainder_encodeBlockAsm: + MOVB $0xf4, (CX) + MOVW DX, 1(CX) + ADDQ $0x03, CX + JMP memmove_long_emit_remainder_encodeBlockAsm + +two_bytes_emit_remainder_encodeBlockAsm: + MOVB $0xf0, (CX) + MOVB DL, 1(CX) + ADDQ $0x02, CX + CMPL DX, $0x40 + JB memmove_emit_remainder_encodeBlockAsm + JMP memmove_long_emit_remainder_encodeBlockAsm + +one_byte_emit_remainder_encodeBlockAsm: + SHLB $0x02, DL + MOVB DL, (CX) + ADDQ $0x01, CX + +memmove_emit_remainder_encodeBlockAsm: + LEAQ (CX)(SI*1), DX + MOVL SI, BX + + // genMemMoveShort + CMPQ BX, $0x03 + JB emit_lit_memmove_emit_remainder_encodeBlockAsm_memmove_move_1or2 + JE emit_lit_memmove_emit_remainder_encodeBlockAsm_memmove_move_3 + CMPQ BX, $0x08 + JB emit_lit_memmove_emit_remainder_encodeBlockAsm_memmove_move_4through7 + CMPQ BX, $0x10 + JBE emit_lit_memmove_emit_remainder_encodeBlockAsm_memmove_move_8through16 + CMPQ BX, $0x20 + JBE emit_lit_memmove_emit_remainder_encodeBlockAsm_memmove_move_17through32 + JMP emit_lit_memmove_emit_remainder_encodeBlockAsm_memmove_move_33through64 + +emit_lit_memmove_emit_remainder_encodeBlockAsm_memmove_move_1or2: + MOVB (AX), SI + MOVB -1(AX)(BX*1), AL + MOVB SI, (CX) + MOVB AL, -1(CX)(BX*1) + JMP memmove_end_copy_emit_remainder_encodeBlockAsm + +emit_lit_memmove_emit_remainder_encodeBlockAsm_memmove_move_3: + MOVW (AX), SI + MOVB 2(AX), AL + MOVW SI, (CX) + MOVB AL, 2(CX) + JMP memmove_end_copy_emit_remainder_encodeBlockAsm + +emit_lit_memmove_emit_remainder_encodeBlockAsm_memmove_move_4through7: + MOVL (AX), SI + MOVL -4(AX)(BX*1), AX + MOVL SI, (CX) + MOVL AX, -4(CX)(BX*1) + JMP memmove_end_copy_emit_remainder_encodeBlockAsm + +emit_lit_memmove_emit_remainder_encodeBlockAsm_memmove_move_8through16: + MOVQ (AX), SI + MOVQ -8(AX)(BX*1), AX + MOVQ SI, (CX) + MOVQ AX, -8(CX)(BX*1) + JMP memmove_end_copy_emit_remainder_encodeBlockAsm + +emit_lit_memmove_emit_remainder_encodeBlockAsm_memmove_move_17through32: + MOVOU (AX), X0 + MOVOU -16(AX)(BX*1), X1 + MOVOU X0, (CX) + MOVOU X1, -16(CX)(BX*1) + JMP memmove_end_copy_emit_remainder_encodeBlockAsm + +emit_lit_memmove_emit_remainder_encodeBlockAsm_memmove_move_33through64: + MOVOU (AX), X0 + MOVOU 16(AX), X1 + MOVOU -32(AX)(BX*1), X2 + MOVOU -16(AX)(BX*1), X3 + MOVOU X0, (CX) + MOVOU X1, 16(CX) + MOVOU X2, -32(CX)(BX*1) + MOVOU X3, -16(CX)(BX*1) + +memmove_end_copy_emit_remainder_encodeBlockAsm: + MOVQ DX, CX + JMP emit_literal_done_emit_remainder_encodeBlockAsm + +memmove_long_emit_remainder_encodeBlockAsm: + LEAQ (CX)(SI*1), DX + MOVL SI, BX + + // genMemMoveLong + MOVOU (AX), X0 + MOVOU 16(AX), X1 + MOVOU -32(AX)(BX*1), X2 + MOVOU -16(AX)(BX*1), X3 + MOVQ BX, DI + SHRQ $0x05, DI + MOVQ CX, SI + ANDL $0x0000001f, SI + MOVQ $0x00000040, R8 + SUBQ SI, R8 + DECQ DI + JA emit_lit_memmove_long_emit_remainder_encodeBlockAsmlarge_forward_sse_loop_32 + LEAQ -32(AX)(R8*1), SI + LEAQ -32(CX)(R8*1), R9 + +emit_lit_memmove_long_emit_remainder_encodeBlockAsmlarge_big_loop_back: + MOVOU (SI), X4 + MOVOU 16(SI), X5 + MOVOA X4, (R9) + MOVOA X5, 16(R9) + ADDQ $0x20, R9 + ADDQ $0x20, SI + ADDQ $0x20, R8 + DECQ DI + JNA emit_lit_memmove_long_emit_remainder_encodeBlockAsmlarge_big_loop_back + +emit_lit_memmove_long_emit_remainder_encodeBlockAsmlarge_forward_sse_loop_32: + MOVOU -32(AX)(R8*1), X4 + MOVOU -16(AX)(R8*1), X5 + MOVOA X4, -32(CX)(R8*1) + MOVOA X5, -16(CX)(R8*1) + ADDQ $0x20, R8 + CMPQ BX, R8 + JAE emit_lit_memmove_long_emit_remainder_encodeBlockAsmlarge_forward_sse_loop_32 + MOVOU X0, (CX) + MOVOU X1, 16(CX) + MOVOU X2, -32(CX)(BX*1) + MOVOU X3, -16(CX)(BX*1) + MOVQ DX, CX + +emit_literal_done_emit_remainder_encodeBlockAsm: + MOVQ dst_base+0(FP), AX + SUBQ AX, CX + MOVQ CX, ret+56(FP) + RET + +// func encodeBlockAsm4MB(dst []byte, src []byte, tmp *[65536]byte) int +// Requires: BMI, SSE2 +TEXT ·encodeBlockAsm4MB(SB), $24-64 + MOVQ tmp+48(FP), AX + MOVQ dst_base+0(FP), CX + MOVQ $0x00000200, DX + MOVQ AX, BX + PXOR X0, X0 + +zero_loop_encodeBlockAsm4MB: + MOVOU X0, (BX) + MOVOU X0, 16(BX) + MOVOU X0, 32(BX) + MOVOU X0, 48(BX) + MOVOU X0, 64(BX) + MOVOU X0, 80(BX) + MOVOU X0, 96(BX) + MOVOU X0, 112(BX) + ADDQ $0x80, BX + DECQ DX + JNZ zero_loop_encodeBlockAsm4MB + MOVL $0x00000000, 12(SP) + MOVQ src_len+32(FP), DX + LEAQ -9(DX), BX + LEAQ -8(DX), SI + MOVL SI, 8(SP) + SHRQ $0x05, DX + SUBL DX, BX + LEAQ (CX)(BX*1), BX + MOVQ BX, (SP) + MOVL $0x00000001, DX + MOVL DX, 16(SP) + MOVQ src_base+24(FP), BX + +search_loop_encodeBlockAsm4MB: + MOVL DX, SI + SUBL 12(SP), SI + SHRL $0x06, SI + LEAL 4(DX)(SI*1), SI + CMPL SI, 8(SP) + JAE emit_remainder_encodeBlockAsm4MB + MOVQ (BX)(DX*1), DI + MOVL SI, 20(SP) + MOVQ $0x0000cf1bbcdcbf9b, R9 + MOVQ DI, R10 + MOVQ DI, R11 + SHRQ $0x08, R11 + SHLQ $0x10, R10 + IMULQ R9, R10 + SHRQ $0x32, R10 + SHLQ $0x10, R11 + IMULQ R9, R11 + SHRQ $0x32, R11 + MOVL (AX)(R10*4), SI + MOVL (AX)(R11*4), R8 + MOVL DX, (AX)(R10*4) + LEAL 1(DX), R10 + MOVL R10, (AX)(R11*4) + MOVQ DI, R10 + SHRQ $0x10, R10 + SHLQ $0x10, R10 + IMULQ R9, R10 + SHRQ $0x32, R10 + MOVL DX, R9 + SUBL 16(SP), R9 + MOVL 1(BX)(R9*1), R11 + MOVQ DI, R9 + SHRQ $0x08, R9 + CMPL R9, R11 + JNE no_repeat_found_encodeBlockAsm4MB + LEAL 1(DX), DI + MOVL 12(SP), R8 + MOVL DI, SI + SUBL 16(SP), SI + JZ repeat_extend_back_end_encodeBlockAsm4MB + +repeat_extend_back_loop_encodeBlockAsm4MB: + CMPL DI, R8 + JBE repeat_extend_back_end_encodeBlockAsm4MB + MOVB -1(BX)(SI*1), R9 + MOVB -1(BX)(DI*1), R10 + CMPB R9, R10 + JNE repeat_extend_back_end_encodeBlockAsm4MB + LEAL -1(DI), DI + DECL SI + JNZ repeat_extend_back_loop_encodeBlockAsm4MB + +repeat_extend_back_end_encodeBlockAsm4MB: + MOVL DI, SI + SUBL 12(SP), SI + LEAQ 4(CX)(SI*1), SI + CMPQ SI, (SP) + JB repeat_dst_size_check_encodeBlockAsm4MB + MOVQ $0x00000000, ret+56(FP) + RET + +repeat_dst_size_check_encodeBlockAsm4MB: + MOVL 12(SP), SI + CMPL SI, DI + JEQ emit_literal_done_repeat_emit_encodeBlockAsm4MB + MOVL DI, R9 + MOVL DI, 12(SP) + LEAQ (BX)(SI*1), R10 + SUBL SI, R9 + LEAL -1(R9), SI + CMPL SI, $0x3c + JB one_byte_repeat_emit_encodeBlockAsm4MB + CMPL SI, $0x00000100 + JB two_bytes_repeat_emit_encodeBlockAsm4MB + CMPL SI, $0x00010000 + JB three_bytes_repeat_emit_encodeBlockAsm4MB + MOVL SI, R11 + SHRL $0x10, R11 + MOVB $0xf8, (CX) + MOVW SI, 1(CX) + MOVB R11, 3(CX) + ADDQ $0x04, CX + JMP memmove_long_repeat_emit_encodeBlockAsm4MB + +three_bytes_repeat_emit_encodeBlockAsm4MB: + MOVB $0xf4, (CX) + MOVW SI, 1(CX) + ADDQ $0x03, CX + JMP memmove_long_repeat_emit_encodeBlockAsm4MB + +two_bytes_repeat_emit_encodeBlockAsm4MB: + MOVB $0xf0, (CX) + MOVB SI, 1(CX) + ADDQ $0x02, CX + CMPL SI, $0x40 + JB memmove_repeat_emit_encodeBlockAsm4MB + JMP memmove_long_repeat_emit_encodeBlockAsm4MB + +one_byte_repeat_emit_encodeBlockAsm4MB: + SHLB $0x02, SI + MOVB SI, (CX) + ADDQ $0x01, CX + +memmove_repeat_emit_encodeBlockAsm4MB: + LEAQ (CX)(R9*1), SI + + // genMemMoveShort + CMPQ R9, $0x08 + JBE emit_lit_memmove_repeat_emit_encodeBlockAsm4MB_memmove_move_8 + CMPQ R9, $0x10 + JBE emit_lit_memmove_repeat_emit_encodeBlockAsm4MB_memmove_move_8through16 + CMPQ R9, $0x20 + JBE emit_lit_memmove_repeat_emit_encodeBlockAsm4MB_memmove_move_17through32 + JMP emit_lit_memmove_repeat_emit_encodeBlockAsm4MB_memmove_move_33through64 + +emit_lit_memmove_repeat_emit_encodeBlockAsm4MB_memmove_move_8: + MOVQ (R10), R11 + MOVQ R11, (CX) + JMP memmove_end_copy_repeat_emit_encodeBlockAsm4MB + +emit_lit_memmove_repeat_emit_encodeBlockAsm4MB_memmove_move_8through16: + MOVQ (R10), R11 + MOVQ -8(R10)(R9*1), R10 + MOVQ R11, (CX) + MOVQ R10, -8(CX)(R9*1) + JMP memmove_end_copy_repeat_emit_encodeBlockAsm4MB + +emit_lit_memmove_repeat_emit_encodeBlockAsm4MB_memmove_move_17through32: + MOVOU (R10), X0 + MOVOU -16(R10)(R9*1), X1 + MOVOU X0, (CX) + MOVOU X1, -16(CX)(R9*1) + JMP memmove_end_copy_repeat_emit_encodeBlockAsm4MB + +emit_lit_memmove_repeat_emit_encodeBlockAsm4MB_memmove_move_33through64: + MOVOU (R10), X0 + MOVOU 16(R10), X1 + MOVOU -32(R10)(R9*1), X2 + MOVOU -16(R10)(R9*1), X3 + MOVOU X0, (CX) + MOVOU X1, 16(CX) + MOVOU X2, -32(CX)(R9*1) + MOVOU X3, -16(CX)(R9*1) + +memmove_end_copy_repeat_emit_encodeBlockAsm4MB: + MOVQ SI, CX + JMP emit_literal_done_repeat_emit_encodeBlockAsm4MB + +memmove_long_repeat_emit_encodeBlockAsm4MB: + LEAQ (CX)(R9*1), SI + + // genMemMoveLong + MOVOU (R10), X0 + MOVOU 16(R10), X1 + MOVOU -32(R10)(R9*1), X2 + MOVOU -16(R10)(R9*1), X3 + MOVQ R9, R12 + SHRQ $0x05, R12 + MOVQ CX, R11 + ANDL $0x0000001f, R11 + MOVQ $0x00000040, R13 + SUBQ R11, R13 + DECQ R12 + JA emit_lit_memmove_long_repeat_emit_encodeBlockAsm4MBlarge_forward_sse_loop_32 + LEAQ -32(R10)(R13*1), R11 + LEAQ -32(CX)(R13*1), R14 + +emit_lit_memmove_long_repeat_emit_encodeBlockAsm4MBlarge_big_loop_back: + MOVOU (R11), X4 + MOVOU 16(R11), X5 + MOVOA X4, (R14) + MOVOA X5, 16(R14) + ADDQ $0x20, R14 + ADDQ $0x20, R11 + ADDQ $0x20, R13 + DECQ R12 + JNA emit_lit_memmove_long_repeat_emit_encodeBlockAsm4MBlarge_big_loop_back + +emit_lit_memmove_long_repeat_emit_encodeBlockAsm4MBlarge_forward_sse_loop_32: + MOVOU -32(R10)(R13*1), X4 + MOVOU -16(R10)(R13*1), X5 + MOVOA X4, -32(CX)(R13*1) + MOVOA X5, -16(CX)(R13*1) + ADDQ $0x20, R13 + CMPQ R9, R13 + JAE emit_lit_memmove_long_repeat_emit_encodeBlockAsm4MBlarge_forward_sse_loop_32 + MOVOU X0, (CX) + MOVOU X1, 16(CX) + MOVOU X2, -32(CX)(R9*1) + MOVOU X3, -16(CX)(R9*1) + MOVQ SI, CX + +emit_literal_done_repeat_emit_encodeBlockAsm4MB: + ADDL $0x05, DX + MOVL DX, SI + SUBL 16(SP), SI + MOVQ src_len+32(FP), R9 + SUBL DX, R9 + LEAQ (BX)(DX*1), R10 + LEAQ (BX)(SI*1), SI + + // matchLen + XORL R12, R12 + +matchlen_loopback_16_repeat_extend_encodeBlockAsm4MB: + CMPL R9, $0x10 + JB matchlen_match8_repeat_extend_encodeBlockAsm4MB + MOVQ (R10)(R12*1), R11 + MOVQ 8(R10)(R12*1), R13 + XORQ (SI)(R12*1), R11 + JNZ matchlen_bsf_8_repeat_extend_encodeBlockAsm4MB + XORQ 8(SI)(R12*1), R13 + JNZ matchlen_bsf_16repeat_extend_encodeBlockAsm4MB + LEAL -16(R9), R9 + LEAL 16(R12), R12 + JMP matchlen_loopback_16_repeat_extend_encodeBlockAsm4MB + +matchlen_bsf_16repeat_extend_encodeBlockAsm4MB: +#ifdef GOAMD64_v3 + TZCNTQ R13, R13 + +#else + BSFQ R13, R13 + +#endif + SARQ $0x03, R13 + LEAL 8(R12)(R13*1), R12 + JMP repeat_extend_forward_end_encodeBlockAsm4MB + +matchlen_match8_repeat_extend_encodeBlockAsm4MB: + CMPL R9, $0x08 + JB matchlen_match4_repeat_extend_encodeBlockAsm4MB + MOVQ (R10)(R12*1), R11 + XORQ (SI)(R12*1), R11 + JNZ matchlen_bsf_8_repeat_extend_encodeBlockAsm4MB + LEAL -8(R9), R9 + LEAL 8(R12), R12 + JMP matchlen_match4_repeat_extend_encodeBlockAsm4MB + +matchlen_bsf_8_repeat_extend_encodeBlockAsm4MB: +#ifdef GOAMD64_v3 + TZCNTQ R11, R11 + +#else + BSFQ R11, R11 + +#endif + SARQ $0x03, R11 + LEAL (R12)(R11*1), R12 + JMP repeat_extend_forward_end_encodeBlockAsm4MB + +matchlen_match4_repeat_extend_encodeBlockAsm4MB: + CMPL R9, $0x04 + JB matchlen_match2_repeat_extend_encodeBlockAsm4MB + MOVL (R10)(R12*1), R11 + CMPL (SI)(R12*1), R11 + JNE matchlen_match2_repeat_extend_encodeBlockAsm4MB + LEAL -4(R9), R9 + LEAL 4(R12), R12 + +matchlen_match2_repeat_extend_encodeBlockAsm4MB: + CMPL R9, $0x01 + JE matchlen_match1_repeat_extend_encodeBlockAsm4MB + JB repeat_extend_forward_end_encodeBlockAsm4MB + MOVW (R10)(R12*1), R11 + CMPW (SI)(R12*1), R11 + JNE matchlen_match1_repeat_extend_encodeBlockAsm4MB + LEAL 2(R12), R12 + SUBL $0x02, R9 + JZ repeat_extend_forward_end_encodeBlockAsm4MB + +matchlen_match1_repeat_extend_encodeBlockAsm4MB: + MOVB (R10)(R12*1), R11 + CMPB (SI)(R12*1), R11 + JNE repeat_extend_forward_end_encodeBlockAsm4MB + LEAL 1(R12), R12 + +repeat_extend_forward_end_encodeBlockAsm4MB: + ADDL R12, DX + MOVL DX, SI + SUBL DI, SI + MOVL 16(SP), DI + TESTL R8, R8 + JZ repeat_as_copy_encodeBlockAsm4MB + + // emitRepeat + MOVL SI, R8 + LEAL -4(SI), SI + CMPL R8, $0x08 + JBE repeat_two_match_repeat_encodeBlockAsm4MB + CMPL R8, $0x0c + JAE cant_repeat_two_offset_match_repeat_encodeBlockAsm4MB + CMPL DI, $0x00000800 + JB repeat_two_offset_match_repeat_encodeBlockAsm4MB + +cant_repeat_two_offset_match_repeat_encodeBlockAsm4MB: + CMPL SI, $0x00000104 + JB repeat_three_match_repeat_encodeBlockAsm4MB + CMPL SI, $0x00010100 + JB repeat_four_match_repeat_encodeBlockAsm4MB + LEAL -65536(SI), SI + MOVL SI, DI + MOVW $0x001d, (CX) + MOVW SI, 2(CX) + SARL $0x10, DI + MOVB DI, 4(CX) + ADDQ $0x05, CX + JMP repeat_end_emit_encodeBlockAsm4MB + +repeat_four_match_repeat_encodeBlockAsm4MB: + LEAL -256(SI), SI + MOVW $0x0019, (CX) + MOVW SI, 2(CX) + ADDQ $0x04, CX + JMP repeat_end_emit_encodeBlockAsm4MB + +repeat_three_match_repeat_encodeBlockAsm4MB: + LEAL -4(SI), SI + MOVW $0x0015, (CX) + MOVB SI, 2(CX) + ADDQ $0x03, CX + JMP repeat_end_emit_encodeBlockAsm4MB + +repeat_two_match_repeat_encodeBlockAsm4MB: + SHLL $0x02, SI + ORL $0x01, SI + MOVW SI, (CX) + ADDQ $0x02, CX + JMP repeat_end_emit_encodeBlockAsm4MB + +repeat_two_offset_match_repeat_encodeBlockAsm4MB: + XORQ R8, R8 + LEAL 1(R8)(SI*4), SI + MOVB DI, 1(CX) + SARL $0x08, DI + SHLL $0x05, DI + ORL DI, SI + MOVB SI, (CX) + ADDQ $0x02, CX + JMP repeat_end_emit_encodeBlockAsm4MB + +repeat_as_copy_encodeBlockAsm4MB: + // emitCopy + CMPL DI, $0x00010000 + JB two_byte_offset_repeat_as_copy_encodeBlockAsm4MB + CMPL SI, $0x40 + JBE four_bytes_remain_repeat_as_copy_encodeBlockAsm4MB + MOVB $0xff, (CX) + MOVL DI, 1(CX) + LEAL -64(SI), SI + ADDQ $0x05, CX + CMPL SI, $0x04 + JB four_bytes_remain_repeat_as_copy_encodeBlockAsm4MB + + // emitRepeat + MOVL SI, R8 + LEAL -4(SI), SI + CMPL R8, $0x08 + JBE repeat_two_repeat_as_copy_encodeBlockAsm4MB_emit_copy + CMPL R8, $0x0c + JAE cant_repeat_two_offset_repeat_as_copy_encodeBlockAsm4MB_emit_copy + CMPL DI, $0x00000800 + JB repeat_two_offset_repeat_as_copy_encodeBlockAsm4MB_emit_copy + +cant_repeat_two_offset_repeat_as_copy_encodeBlockAsm4MB_emit_copy: + CMPL SI, $0x00000104 + JB repeat_three_repeat_as_copy_encodeBlockAsm4MB_emit_copy + CMPL SI, $0x00010100 + JB repeat_four_repeat_as_copy_encodeBlockAsm4MB_emit_copy + LEAL -65536(SI), SI + MOVL SI, DI + MOVW $0x001d, (CX) + MOVW SI, 2(CX) + SARL $0x10, DI + MOVB DI, 4(CX) + ADDQ $0x05, CX + JMP repeat_end_emit_encodeBlockAsm4MB + +repeat_four_repeat_as_copy_encodeBlockAsm4MB_emit_copy: + LEAL -256(SI), SI + MOVW $0x0019, (CX) + MOVW SI, 2(CX) + ADDQ $0x04, CX + JMP repeat_end_emit_encodeBlockAsm4MB + +repeat_three_repeat_as_copy_encodeBlockAsm4MB_emit_copy: + LEAL -4(SI), SI + MOVW $0x0015, (CX) + MOVB SI, 2(CX) + ADDQ $0x03, CX + JMP repeat_end_emit_encodeBlockAsm4MB + +repeat_two_repeat_as_copy_encodeBlockAsm4MB_emit_copy: + SHLL $0x02, SI + ORL $0x01, SI + MOVW SI, (CX) + ADDQ $0x02, CX + JMP repeat_end_emit_encodeBlockAsm4MB + +repeat_two_offset_repeat_as_copy_encodeBlockAsm4MB_emit_copy: + XORQ R8, R8 + LEAL 1(R8)(SI*4), SI + MOVB DI, 1(CX) + SARL $0x08, DI + SHLL $0x05, DI + ORL DI, SI + MOVB SI, (CX) + ADDQ $0x02, CX + JMP repeat_end_emit_encodeBlockAsm4MB + +four_bytes_remain_repeat_as_copy_encodeBlockAsm4MB: + TESTL SI, SI + JZ repeat_end_emit_encodeBlockAsm4MB + XORL R8, R8 + LEAL -1(R8)(SI*4), SI + MOVB SI, (CX) + MOVL DI, 1(CX) + ADDQ $0x05, CX + JMP repeat_end_emit_encodeBlockAsm4MB + +two_byte_offset_repeat_as_copy_encodeBlockAsm4MB: + CMPL SI, $0x40 + JBE two_byte_offset_short_repeat_as_copy_encodeBlockAsm4MB + CMPL DI, $0x00000800 + JAE long_offset_short_repeat_as_copy_encodeBlockAsm4MB + MOVL $0x00000001, R8 + LEAL 16(R8), R8 + MOVB DI, 1(CX) + SHRL $0x08, DI + SHLL $0x05, DI + ORL DI, R8 + MOVB R8, (CX) + ADDQ $0x02, CX + SUBL $0x08, SI + + // emitRepeat + LEAL -4(SI), SI + JMP cant_repeat_two_offset_repeat_as_copy_encodeBlockAsm4MB_emit_copy_short_2b + MOVL SI, R8 + LEAL -4(SI), SI + CMPL R8, $0x08 + JBE repeat_two_repeat_as_copy_encodeBlockAsm4MB_emit_copy_short_2b + CMPL R8, $0x0c + JAE cant_repeat_two_offset_repeat_as_copy_encodeBlockAsm4MB_emit_copy_short_2b + CMPL DI, $0x00000800 + JB repeat_two_offset_repeat_as_copy_encodeBlockAsm4MB_emit_copy_short_2b + +cant_repeat_two_offset_repeat_as_copy_encodeBlockAsm4MB_emit_copy_short_2b: + CMPL SI, $0x00000104 + JB repeat_three_repeat_as_copy_encodeBlockAsm4MB_emit_copy_short_2b + CMPL SI, $0x00010100 + JB repeat_four_repeat_as_copy_encodeBlockAsm4MB_emit_copy_short_2b + LEAL -65536(SI), SI + MOVL SI, DI + MOVW $0x001d, (CX) + MOVW SI, 2(CX) + SARL $0x10, DI + MOVB DI, 4(CX) + ADDQ $0x05, CX + JMP repeat_end_emit_encodeBlockAsm4MB + +repeat_four_repeat_as_copy_encodeBlockAsm4MB_emit_copy_short_2b: + LEAL -256(SI), SI + MOVW $0x0019, (CX) + MOVW SI, 2(CX) + ADDQ $0x04, CX + JMP repeat_end_emit_encodeBlockAsm4MB + +repeat_three_repeat_as_copy_encodeBlockAsm4MB_emit_copy_short_2b: + LEAL -4(SI), SI + MOVW $0x0015, (CX) + MOVB SI, 2(CX) + ADDQ $0x03, CX + JMP repeat_end_emit_encodeBlockAsm4MB + +repeat_two_repeat_as_copy_encodeBlockAsm4MB_emit_copy_short_2b: + SHLL $0x02, SI + ORL $0x01, SI + MOVW SI, (CX) + ADDQ $0x02, CX + JMP repeat_end_emit_encodeBlockAsm4MB + +repeat_two_offset_repeat_as_copy_encodeBlockAsm4MB_emit_copy_short_2b: + XORQ R8, R8 + LEAL 1(R8)(SI*4), SI + MOVB DI, 1(CX) + SARL $0x08, DI + SHLL $0x05, DI + ORL DI, SI + MOVB SI, (CX) + ADDQ $0x02, CX + JMP repeat_end_emit_encodeBlockAsm4MB + +long_offset_short_repeat_as_copy_encodeBlockAsm4MB: + MOVB $0xee, (CX) + MOVW DI, 1(CX) + LEAL -60(SI), SI + ADDQ $0x03, CX + + // emitRepeat + MOVL SI, R8 + LEAL -4(SI), SI + CMPL R8, $0x08 + JBE repeat_two_repeat_as_copy_encodeBlockAsm4MB_emit_copy_short + CMPL R8, $0x0c + JAE cant_repeat_two_offset_repeat_as_copy_encodeBlockAsm4MB_emit_copy_short + CMPL DI, $0x00000800 + JB repeat_two_offset_repeat_as_copy_encodeBlockAsm4MB_emit_copy_short + +cant_repeat_two_offset_repeat_as_copy_encodeBlockAsm4MB_emit_copy_short: + CMPL SI, $0x00000104 + JB repeat_three_repeat_as_copy_encodeBlockAsm4MB_emit_copy_short + CMPL SI, $0x00010100 + JB repeat_four_repeat_as_copy_encodeBlockAsm4MB_emit_copy_short + LEAL -65536(SI), SI + MOVL SI, DI + MOVW $0x001d, (CX) + MOVW SI, 2(CX) + SARL $0x10, DI + MOVB DI, 4(CX) + ADDQ $0x05, CX + JMP repeat_end_emit_encodeBlockAsm4MB + +repeat_four_repeat_as_copy_encodeBlockAsm4MB_emit_copy_short: + LEAL -256(SI), SI + MOVW $0x0019, (CX) + MOVW SI, 2(CX) + ADDQ $0x04, CX + JMP repeat_end_emit_encodeBlockAsm4MB + +repeat_three_repeat_as_copy_encodeBlockAsm4MB_emit_copy_short: + LEAL -4(SI), SI + MOVW $0x0015, (CX) + MOVB SI, 2(CX) + ADDQ $0x03, CX + JMP repeat_end_emit_encodeBlockAsm4MB + +repeat_two_repeat_as_copy_encodeBlockAsm4MB_emit_copy_short: + SHLL $0x02, SI + ORL $0x01, SI + MOVW SI, (CX) + ADDQ $0x02, CX + JMP repeat_end_emit_encodeBlockAsm4MB + +repeat_two_offset_repeat_as_copy_encodeBlockAsm4MB_emit_copy_short: + XORQ R8, R8 + LEAL 1(R8)(SI*4), SI + MOVB DI, 1(CX) + SARL $0x08, DI + SHLL $0x05, DI + ORL DI, SI + MOVB SI, (CX) + ADDQ $0x02, CX + JMP repeat_end_emit_encodeBlockAsm4MB + +two_byte_offset_short_repeat_as_copy_encodeBlockAsm4MB: + MOVL SI, R8 + SHLL $0x02, R8 + CMPL SI, $0x0c + JAE emit_copy_three_repeat_as_copy_encodeBlockAsm4MB + CMPL DI, $0x00000800 + JAE emit_copy_three_repeat_as_copy_encodeBlockAsm4MB + LEAL -15(R8), R8 + MOVB DI, 1(CX) + SHRL $0x08, DI + SHLL $0x05, DI + ORL DI, R8 + MOVB R8, (CX) + ADDQ $0x02, CX + JMP repeat_end_emit_encodeBlockAsm4MB + +emit_copy_three_repeat_as_copy_encodeBlockAsm4MB: + LEAL -2(R8), R8 + MOVB R8, (CX) + MOVW DI, 1(CX) + ADDQ $0x03, CX + +repeat_end_emit_encodeBlockAsm4MB: + MOVL DX, 12(SP) + JMP search_loop_encodeBlockAsm4MB + +no_repeat_found_encodeBlockAsm4MB: + CMPL (BX)(SI*1), DI + JEQ candidate_match_encodeBlockAsm4MB + SHRQ $0x08, DI + MOVL (AX)(R10*4), SI + LEAL 2(DX), R9 + CMPL (BX)(R8*1), DI + JEQ candidate2_match_encodeBlockAsm4MB + MOVL R9, (AX)(R10*4) + SHRQ $0x08, DI + CMPL (BX)(SI*1), DI + JEQ candidate3_match_encodeBlockAsm4MB + MOVL 20(SP), DX + JMP search_loop_encodeBlockAsm4MB + +candidate3_match_encodeBlockAsm4MB: + ADDL $0x02, DX + JMP candidate_match_encodeBlockAsm4MB + +candidate2_match_encodeBlockAsm4MB: + MOVL R9, (AX)(R10*4) + INCL DX + MOVL R8, SI + +candidate_match_encodeBlockAsm4MB: + MOVL 12(SP), DI + TESTL SI, SI + JZ match_extend_back_end_encodeBlockAsm4MB + +match_extend_back_loop_encodeBlockAsm4MB: + CMPL DX, DI + JBE match_extend_back_end_encodeBlockAsm4MB + MOVB -1(BX)(SI*1), R8 + MOVB -1(BX)(DX*1), R9 + CMPB R8, R9 + JNE match_extend_back_end_encodeBlockAsm4MB + LEAL -1(DX), DX + DECL SI + JZ match_extend_back_end_encodeBlockAsm4MB + JMP match_extend_back_loop_encodeBlockAsm4MB + +match_extend_back_end_encodeBlockAsm4MB: + MOVL DX, DI + SUBL 12(SP), DI + LEAQ 4(CX)(DI*1), DI + CMPQ DI, (SP) + JB match_dst_size_check_encodeBlockAsm4MB + MOVQ $0x00000000, ret+56(FP) + RET + +match_dst_size_check_encodeBlockAsm4MB: + MOVL DX, DI + MOVL 12(SP), R8 + CMPL R8, DI + JEQ emit_literal_done_match_emit_encodeBlockAsm4MB + MOVL DI, R9 + MOVL DI, 12(SP) + LEAQ (BX)(R8*1), DI + SUBL R8, R9 + LEAL -1(R9), R8 + CMPL R8, $0x3c + JB one_byte_match_emit_encodeBlockAsm4MB + CMPL R8, $0x00000100 + JB two_bytes_match_emit_encodeBlockAsm4MB + CMPL R8, $0x00010000 + JB three_bytes_match_emit_encodeBlockAsm4MB + MOVL R8, R10 + SHRL $0x10, R10 + MOVB $0xf8, (CX) + MOVW R8, 1(CX) + MOVB R10, 3(CX) + ADDQ $0x04, CX + JMP memmove_long_match_emit_encodeBlockAsm4MB + +three_bytes_match_emit_encodeBlockAsm4MB: + MOVB $0xf4, (CX) + MOVW R8, 1(CX) + ADDQ $0x03, CX + JMP memmove_long_match_emit_encodeBlockAsm4MB + +two_bytes_match_emit_encodeBlockAsm4MB: + MOVB $0xf0, (CX) + MOVB R8, 1(CX) + ADDQ $0x02, CX + CMPL R8, $0x40 + JB memmove_match_emit_encodeBlockAsm4MB + JMP memmove_long_match_emit_encodeBlockAsm4MB + +one_byte_match_emit_encodeBlockAsm4MB: + SHLB $0x02, R8 + MOVB R8, (CX) + ADDQ $0x01, CX + +memmove_match_emit_encodeBlockAsm4MB: + LEAQ (CX)(R9*1), R8 + + // genMemMoveShort + CMPQ R9, $0x08 + JBE emit_lit_memmove_match_emit_encodeBlockAsm4MB_memmove_move_8 + CMPQ R9, $0x10 + JBE emit_lit_memmove_match_emit_encodeBlockAsm4MB_memmove_move_8through16 + CMPQ R9, $0x20 + JBE emit_lit_memmove_match_emit_encodeBlockAsm4MB_memmove_move_17through32 + JMP emit_lit_memmove_match_emit_encodeBlockAsm4MB_memmove_move_33through64 + +emit_lit_memmove_match_emit_encodeBlockAsm4MB_memmove_move_8: + MOVQ (DI), R10 + MOVQ R10, (CX) + JMP memmove_end_copy_match_emit_encodeBlockAsm4MB + +emit_lit_memmove_match_emit_encodeBlockAsm4MB_memmove_move_8through16: + MOVQ (DI), R10 + MOVQ -8(DI)(R9*1), DI + MOVQ R10, (CX) + MOVQ DI, -8(CX)(R9*1) + JMP memmove_end_copy_match_emit_encodeBlockAsm4MB + +emit_lit_memmove_match_emit_encodeBlockAsm4MB_memmove_move_17through32: + MOVOU (DI), X0 + MOVOU -16(DI)(R9*1), X1 + MOVOU X0, (CX) + MOVOU X1, -16(CX)(R9*1) + JMP memmove_end_copy_match_emit_encodeBlockAsm4MB + +emit_lit_memmove_match_emit_encodeBlockAsm4MB_memmove_move_33through64: + MOVOU (DI), X0 + MOVOU 16(DI), X1 + MOVOU -32(DI)(R9*1), X2 + MOVOU -16(DI)(R9*1), X3 + MOVOU X0, (CX) + MOVOU X1, 16(CX) + MOVOU X2, -32(CX)(R9*1) + MOVOU X3, -16(CX)(R9*1) + +memmove_end_copy_match_emit_encodeBlockAsm4MB: + MOVQ R8, CX + JMP emit_literal_done_match_emit_encodeBlockAsm4MB + +memmove_long_match_emit_encodeBlockAsm4MB: + LEAQ (CX)(R9*1), R8 + + // genMemMoveLong + MOVOU (DI), X0 + MOVOU 16(DI), X1 + MOVOU -32(DI)(R9*1), X2 + MOVOU -16(DI)(R9*1), X3 + MOVQ R9, R11 + SHRQ $0x05, R11 + MOVQ CX, R10 + ANDL $0x0000001f, R10 + MOVQ $0x00000040, R12 + SUBQ R10, R12 + DECQ R11 + JA emit_lit_memmove_long_match_emit_encodeBlockAsm4MBlarge_forward_sse_loop_32 + LEAQ -32(DI)(R12*1), R10 + LEAQ -32(CX)(R12*1), R13 + +emit_lit_memmove_long_match_emit_encodeBlockAsm4MBlarge_big_loop_back: + MOVOU (R10), X4 + MOVOU 16(R10), X5 + MOVOA X4, (R13) + MOVOA X5, 16(R13) + ADDQ $0x20, R13 + ADDQ $0x20, R10 + ADDQ $0x20, R12 + DECQ R11 + JNA emit_lit_memmove_long_match_emit_encodeBlockAsm4MBlarge_big_loop_back + +emit_lit_memmove_long_match_emit_encodeBlockAsm4MBlarge_forward_sse_loop_32: + MOVOU -32(DI)(R12*1), X4 + MOVOU -16(DI)(R12*1), X5 + MOVOA X4, -32(CX)(R12*1) + MOVOA X5, -16(CX)(R12*1) + ADDQ $0x20, R12 + CMPQ R9, R12 + JAE emit_lit_memmove_long_match_emit_encodeBlockAsm4MBlarge_forward_sse_loop_32 + MOVOU X0, (CX) + MOVOU X1, 16(CX) + MOVOU X2, -32(CX)(R9*1) + MOVOU X3, -16(CX)(R9*1) + MOVQ R8, CX + +emit_literal_done_match_emit_encodeBlockAsm4MB: +match_nolit_loop_encodeBlockAsm4MB: + MOVL DX, DI + SUBL SI, DI + MOVL DI, 16(SP) + ADDL $0x04, DX + ADDL $0x04, SI + MOVQ src_len+32(FP), DI + SUBL DX, DI + LEAQ (BX)(DX*1), R8 + LEAQ (BX)(SI*1), SI + + // matchLen + XORL R10, R10 + +matchlen_loopback_16_match_nolit_encodeBlockAsm4MB: + CMPL DI, $0x10 + JB matchlen_match8_match_nolit_encodeBlockAsm4MB + MOVQ (R8)(R10*1), R9 + MOVQ 8(R8)(R10*1), R11 + XORQ (SI)(R10*1), R9 + JNZ matchlen_bsf_8_match_nolit_encodeBlockAsm4MB + XORQ 8(SI)(R10*1), R11 + JNZ matchlen_bsf_16match_nolit_encodeBlockAsm4MB + LEAL -16(DI), DI + LEAL 16(R10), R10 + JMP matchlen_loopback_16_match_nolit_encodeBlockAsm4MB + +matchlen_bsf_16match_nolit_encodeBlockAsm4MB: +#ifdef GOAMD64_v3 + TZCNTQ R11, R11 + +#else + BSFQ R11, R11 + +#endif + SARQ $0x03, R11 + LEAL 8(R10)(R11*1), R10 + JMP match_nolit_end_encodeBlockAsm4MB + +matchlen_match8_match_nolit_encodeBlockAsm4MB: + CMPL DI, $0x08 + JB matchlen_match4_match_nolit_encodeBlockAsm4MB + MOVQ (R8)(R10*1), R9 + XORQ (SI)(R10*1), R9 + JNZ matchlen_bsf_8_match_nolit_encodeBlockAsm4MB + LEAL -8(DI), DI + LEAL 8(R10), R10 + JMP matchlen_match4_match_nolit_encodeBlockAsm4MB + +matchlen_bsf_8_match_nolit_encodeBlockAsm4MB: +#ifdef GOAMD64_v3 + TZCNTQ R9, R9 + +#else + BSFQ R9, R9 + +#endif + SARQ $0x03, R9 + LEAL (R10)(R9*1), R10 + JMP match_nolit_end_encodeBlockAsm4MB + +matchlen_match4_match_nolit_encodeBlockAsm4MB: + CMPL DI, $0x04 + JB matchlen_match2_match_nolit_encodeBlockAsm4MB + MOVL (R8)(R10*1), R9 + CMPL (SI)(R10*1), R9 + JNE matchlen_match2_match_nolit_encodeBlockAsm4MB + LEAL -4(DI), DI + LEAL 4(R10), R10 + +matchlen_match2_match_nolit_encodeBlockAsm4MB: + CMPL DI, $0x01 + JE matchlen_match1_match_nolit_encodeBlockAsm4MB + JB match_nolit_end_encodeBlockAsm4MB + MOVW (R8)(R10*1), R9 + CMPW (SI)(R10*1), R9 + JNE matchlen_match1_match_nolit_encodeBlockAsm4MB + LEAL 2(R10), R10 + SUBL $0x02, DI + JZ match_nolit_end_encodeBlockAsm4MB + +matchlen_match1_match_nolit_encodeBlockAsm4MB: + MOVB (R8)(R10*1), R9 + CMPB (SI)(R10*1), R9 + JNE match_nolit_end_encodeBlockAsm4MB + LEAL 1(R10), R10 + +match_nolit_end_encodeBlockAsm4MB: + ADDL R10, DX + MOVL 16(SP), SI + ADDL $0x04, R10 + MOVL DX, 12(SP) + + // emitCopy + CMPL SI, $0x00010000 + JB two_byte_offset_match_nolit_encodeBlockAsm4MB + CMPL R10, $0x40 + JBE four_bytes_remain_match_nolit_encodeBlockAsm4MB + MOVB $0xff, (CX) + MOVL SI, 1(CX) + LEAL -64(R10), R10 + ADDQ $0x05, CX + CMPL R10, $0x04 + JB four_bytes_remain_match_nolit_encodeBlockAsm4MB + + // emitRepeat + MOVL R10, DI + LEAL -4(R10), R10 + CMPL DI, $0x08 + JBE repeat_two_match_nolit_encodeBlockAsm4MB_emit_copy + CMPL DI, $0x0c + JAE cant_repeat_two_offset_match_nolit_encodeBlockAsm4MB_emit_copy + CMPL SI, $0x00000800 + JB repeat_two_offset_match_nolit_encodeBlockAsm4MB_emit_copy + +cant_repeat_two_offset_match_nolit_encodeBlockAsm4MB_emit_copy: + CMPL R10, $0x00000104 + JB repeat_three_match_nolit_encodeBlockAsm4MB_emit_copy + CMPL R10, $0x00010100 + JB repeat_four_match_nolit_encodeBlockAsm4MB_emit_copy + LEAL -65536(R10), R10 + MOVL R10, SI + MOVW $0x001d, (CX) + MOVW R10, 2(CX) + SARL $0x10, SI + MOVB SI, 4(CX) + ADDQ $0x05, CX + JMP match_nolit_emitcopy_end_encodeBlockAsm4MB + +repeat_four_match_nolit_encodeBlockAsm4MB_emit_copy: + LEAL -256(R10), R10 + MOVW $0x0019, (CX) + MOVW R10, 2(CX) + ADDQ $0x04, CX + JMP match_nolit_emitcopy_end_encodeBlockAsm4MB + +repeat_three_match_nolit_encodeBlockAsm4MB_emit_copy: + LEAL -4(R10), R10 + MOVW $0x0015, (CX) + MOVB R10, 2(CX) + ADDQ $0x03, CX + JMP match_nolit_emitcopy_end_encodeBlockAsm4MB + +repeat_two_match_nolit_encodeBlockAsm4MB_emit_copy: + SHLL $0x02, R10 + ORL $0x01, R10 + MOVW R10, (CX) + ADDQ $0x02, CX + JMP match_nolit_emitcopy_end_encodeBlockAsm4MB + +repeat_two_offset_match_nolit_encodeBlockAsm4MB_emit_copy: + XORQ DI, DI + LEAL 1(DI)(R10*4), R10 + MOVB SI, 1(CX) + SARL $0x08, SI + SHLL $0x05, SI + ORL SI, R10 + MOVB R10, (CX) + ADDQ $0x02, CX + JMP match_nolit_emitcopy_end_encodeBlockAsm4MB + +four_bytes_remain_match_nolit_encodeBlockAsm4MB: + TESTL R10, R10 + JZ match_nolit_emitcopy_end_encodeBlockAsm4MB + XORL DI, DI + LEAL -1(DI)(R10*4), R10 + MOVB R10, (CX) + MOVL SI, 1(CX) + ADDQ $0x05, CX + JMP match_nolit_emitcopy_end_encodeBlockAsm4MB + +two_byte_offset_match_nolit_encodeBlockAsm4MB: + CMPL R10, $0x40 + JBE two_byte_offset_short_match_nolit_encodeBlockAsm4MB + CMPL SI, $0x00000800 + JAE long_offset_short_match_nolit_encodeBlockAsm4MB + MOVL $0x00000001, DI + LEAL 16(DI), DI + MOVB SI, 1(CX) + SHRL $0x08, SI + SHLL $0x05, SI + ORL SI, DI + MOVB DI, (CX) + ADDQ $0x02, CX + SUBL $0x08, R10 + + // emitRepeat + LEAL -4(R10), R10 + JMP cant_repeat_two_offset_match_nolit_encodeBlockAsm4MB_emit_copy_short_2b + MOVL R10, DI + LEAL -4(R10), R10 + CMPL DI, $0x08 + JBE repeat_two_match_nolit_encodeBlockAsm4MB_emit_copy_short_2b + CMPL DI, $0x0c + JAE cant_repeat_two_offset_match_nolit_encodeBlockAsm4MB_emit_copy_short_2b + CMPL SI, $0x00000800 + JB repeat_two_offset_match_nolit_encodeBlockAsm4MB_emit_copy_short_2b + +cant_repeat_two_offset_match_nolit_encodeBlockAsm4MB_emit_copy_short_2b: + CMPL R10, $0x00000104 + JB repeat_three_match_nolit_encodeBlockAsm4MB_emit_copy_short_2b + CMPL R10, $0x00010100 + JB repeat_four_match_nolit_encodeBlockAsm4MB_emit_copy_short_2b + LEAL -65536(R10), R10 + MOVL R10, SI + MOVW $0x001d, (CX) + MOVW R10, 2(CX) + SARL $0x10, SI + MOVB SI, 4(CX) + ADDQ $0x05, CX + JMP match_nolit_emitcopy_end_encodeBlockAsm4MB + +repeat_four_match_nolit_encodeBlockAsm4MB_emit_copy_short_2b: + LEAL -256(R10), R10 + MOVW $0x0019, (CX) + MOVW R10, 2(CX) + ADDQ $0x04, CX + JMP match_nolit_emitcopy_end_encodeBlockAsm4MB + +repeat_three_match_nolit_encodeBlockAsm4MB_emit_copy_short_2b: + LEAL -4(R10), R10 + MOVW $0x0015, (CX) + MOVB R10, 2(CX) + ADDQ $0x03, CX + JMP match_nolit_emitcopy_end_encodeBlockAsm4MB + +repeat_two_match_nolit_encodeBlockAsm4MB_emit_copy_short_2b: + SHLL $0x02, R10 + ORL $0x01, R10 + MOVW R10, (CX) + ADDQ $0x02, CX + JMP match_nolit_emitcopy_end_encodeBlockAsm4MB + +repeat_two_offset_match_nolit_encodeBlockAsm4MB_emit_copy_short_2b: + XORQ DI, DI + LEAL 1(DI)(R10*4), R10 + MOVB SI, 1(CX) + SARL $0x08, SI + SHLL $0x05, SI + ORL SI, R10 + MOVB R10, (CX) + ADDQ $0x02, CX + JMP match_nolit_emitcopy_end_encodeBlockAsm4MB + +long_offset_short_match_nolit_encodeBlockAsm4MB: + MOVB $0xee, (CX) + MOVW SI, 1(CX) + LEAL -60(R10), R10 + ADDQ $0x03, CX + + // emitRepeat + MOVL R10, DI + LEAL -4(R10), R10 + CMPL DI, $0x08 + JBE repeat_two_match_nolit_encodeBlockAsm4MB_emit_copy_short + CMPL DI, $0x0c + JAE cant_repeat_two_offset_match_nolit_encodeBlockAsm4MB_emit_copy_short + CMPL SI, $0x00000800 + JB repeat_two_offset_match_nolit_encodeBlockAsm4MB_emit_copy_short + +cant_repeat_two_offset_match_nolit_encodeBlockAsm4MB_emit_copy_short: + CMPL R10, $0x00000104 + JB repeat_three_match_nolit_encodeBlockAsm4MB_emit_copy_short + CMPL R10, $0x00010100 + JB repeat_four_match_nolit_encodeBlockAsm4MB_emit_copy_short + LEAL -65536(R10), R10 + MOVL R10, SI + MOVW $0x001d, (CX) + MOVW R10, 2(CX) + SARL $0x10, SI + MOVB SI, 4(CX) + ADDQ $0x05, CX + JMP match_nolit_emitcopy_end_encodeBlockAsm4MB + +repeat_four_match_nolit_encodeBlockAsm4MB_emit_copy_short: + LEAL -256(R10), R10 + MOVW $0x0019, (CX) + MOVW R10, 2(CX) + ADDQ $0x04, CX + JMP match_nolit_emitcopy_end_encodeBlockAsm4MB + +repeat_three_match_nolit_encodeBlockAsm4MB_emit_copy_short: + LEAL -4(R10), R10 + MOVW $0x0015, (CX) + MOVB R10, 2(CX) + ADDQ $0x03, CX + JMP match_nolit_emitcopy_end_encodeBlockAsm4MB + +repeat_two_match_nolit_encodeBlockAsm4MB_emit_copy_short: + SHLL $0x02, R10 + ORL $0x01, R10 + MOVW R10, (CX) + ADDQ $0x02, CX + JMP match_nolit_emitcopy_end_encodeBlockAsm4MB + +repeat_two_offset_match_nolit_encodeBlockAsm4MB_emit_copy_short: + XORQ DI, DI + LEAL 1(DI)(R10*4), R10 + MOVB SI, 1(CX) + SARL $0x08, SI + SHLL $0x05, SI + ORL SI, R10 + MOVB R10, (CX) + ADDQ $0x02, CX + JMP match_nolit_emitcopy_end_encodeBlockAsm4MB + +two_byte_offset_short_match_nolit_encodeBlockAsm4MB: + MOVL R10, DI + SHLL $0x02, DI + CMPL R10, $0x0c + JAE emit_copy_three_match_nolit_encodeBlockAsm4MB + CMPL SI, $0x00000800 + JAE emit_copy_three_match_nolit_encodeBlockAsm4MB + LEAL -15(DI), DI + MOVB SI, 1(CX) + SHRL $0x08, SI + SHLL $0x05, SI + ORL SI, DI + MOVB DI, (CX) + ADDQ $0x02, CX + JMP match_nolit_emitcopy_end_encodeBlockAsm4MB + +emit_copy_three_match_nolit_encodeBlockAsm4MB: + LEAL -2(DI), DI + MOVB DI, (CX) + MOVW SI, 1(CX) + ADDQ $0x03, CX + +match_nolit_emitcopy_end_encodeBlockAsm4MB: + CMPL DX, 8(SP) + JAE emit_remainder_encodeBlockAsm4MB + MOVQ -2(BX)(DX*1), DI + CMPQ CX, (SP) + JB match_nolit_dst_ok_encodeBlockAsm4MB + MOVQ $0x00000000, ret+56(FP) + RET + +match_nolit_dst_ok_encodeBlockAsm4MB: + MOVQ $0x0000cf1bbcdcbf9b, R9 + MOVQ DI, R8 + SHRQ $0x10, DI + MOVQ DI, SI + SHLQ $0x10, R8 + IMULQ R9, R8 + SHRQ $0x32, R8 + SHLQ $0x10, SI + IMULQ R9, SI + SHRQ $0x32, SI + LEAL -2(DX), R9 + LEAQ (AX)(SI*4), R10 + MOVL (R10), SI + MOVL R9, (AX)(R8*4) + MOVL DX, (R10) + CMPL (BX)(SI*1), DI + JEQ match_nolit_loop_encodeBlockAsm4MB + INCL DX + JMP search_loop_encodeBlockAsm4MB + +emit_remainder_encodeBlockAsm4MB: + MOVQ src_len+32(FP), AX + SUBL 12(SP), AX + LEAQ 4(CX)(AX*1), AX + CMPQ AX, (SP) + JB emit_remainder_ok_encodeBlockAsm4MB + MOVQ $0x00000000, ret+56(FP) + RET + +emit_remainder_ok_encodeBlockAsm4MB: + MOVQ src_len+32(FP), AX + MOVL 12(SP), DX + CMPL DX, AX + JEQ emit_literal_done_emit_remainder_encodeBlockAsm4MB + MOVL AX, SI + MOVL AX, 12(SP) + LEAQ (BX)(DX*1), AX + SUBL DX, SI + LEAL -1(SI), DX + CMPL DX, $0x3c + JB one_byte_emit_remainder_encodeBlockAsm4MB + CMPL DX, $0x00000100 + JB two_bytes_emit_remainder_encodeBlockAsm4MB + CMPL DX, $0x00010000 + JB three_bytes_emit_remainder_encodeBlockAsm4MB + MOVL DX, BX + SHRL $0x10, BX + MOVB $0xf8, (CX) + MOVW DX, 1(CX) + MOVB BL, 3(CX) + ADDQ $0x04, CX + JMP memmove_long_emit_remainder_encodeBlockAsm4MB + +three_bytes_emit_remainder_encodeBlockAsm4MB: + MOVB $0xf4, (CX) + MOVW DX, 1(CX) + ADDQ $0x03, CX + JMP memmove_long_emit_remainder_encodeBlockAsm4MB + +two_bytes_emit_remainder_encodeBlockAsm4MB: + MOVB $0xf0, (CX) + MOVB DL, 1(CX) + ADDQ $0x02, CX + CMPL DX, $0x40 + JB memmove_emit_remainder_encodeBlockAsm4MB + JMP memmove_long_emit_remainder_encodeBlockAsm4MB + +one_byte_emit_remainder_encodeBlockAsm4MB: + SHLB $0x02, DL + MOVB DL, (CX) + ADDQ $0x01, CX + +memmove_emit_remainder_encodeBlockAsm4MB: + LEAQ (CX)(SI*1), DX + MOVL SI, BX + + // genMemMoveShort + CMPQ BX, $0x03 + JB emit_lit_memmove_emit_remainder_encodeBlockAsm4MB_memmove_move_1or2 + JE emit_lit_memmove_emit_remainder_encodeBlockAsm4MB_memmove_move_3 + CMPQ BX, $0x08 + JB emit_lit_memmove_emit_remainder_encodeBlockAsm4MB_memmove_move_4through7 + CMPQ BX, $0x10 + JBE emit_lit_memmove_emit_remainder_encodeBlockAsm4MB_memmove_move_8through16 + CMPQ BX, $0x20 + JBE emit_lit_memmove_emit_remainder_encodeBlockAsm4MB_memmove_move_17through32 + JMP emit_lit_memmove_emit_remainder_encodeBlockAsm4MB_memmove_move_33through64 + +emit_lit_memmove_emit_remainder_encodeBlockAsm4MB_memmove_move_1or2: + MOVB (AX), SI + MOVB -1(AX)(BX*1), AL + MOVB SI, (CX) + MOVB AL, -1(CX)(BX*1) + JMP memmove_end_copy_emit_remainder_encodeBlockAsm4MB + +emit_lit_memmove_emit_remainder_encodeBlockAsm4MB_memmove_move_3: + MOVW (AX), SI + MOVB 2(AX), AL + MOVW SI, (CX) + MOVB AL, 2(CX) + JMP memmove_end_copy_emit_remainder_encodeBlockAsm4MB + +emit_lit_memmove_emit_remainder_encodeBlockAsm4MB_memmove_move_4through7: + MOVL (AX), SI + MOVL -4(AX)(BX*1), AX + MOVL SI, (CX) + MOVL AX, -4(CX)(BX*1) + JMP memmove_end_copy_emit_remainder_encodeBlockAsm4MB + +emit_lit_memmove_emit_remainder_encodeBlockAsm4MB_memmove_move_8through16: + MOVQ (AX), SI + MOVQ -8(AX)(BX*1), AX + MOVQ SI, (CX) + MOVQ AX, -8(CX)(BX*1) + JMP memmove_end_copy_emit_remainder_encodeBlockAsm4MB + +emit_lit_memmove_emit_remainder_encodeBlockAsm4MB_memmove_move_17through32: + MOVOU (AX), X0 + MOVOU -16(AX)(BX*1), X1 + MOVOU X0, (CX) + MOVOU X1, -16(CX)(BX*1) + JMP memmove_end_copy_emit_remainder_encodeBlockAsm4MB + +emit_lit_memmove_emit_remainder_encodeBlockAsm4MB_memmove_move_33through64: + MOVOU (AX), X0 + MOVOU 16(AX), X1 + MOVOU -32(AX)(BX*1), X2 + MOVOU -16(AX)(BX*1), X3 + MOVOU X0, (CX) + MOVOU X1, 16(CX) + MOVOU X2, -32(CX)(BX*1) + MOVOU X3, -16(CX)(BX*1) + +memmove_end_copy_emit_remainder_encodeBlockAsm4MB: + MOVQ DX, CX + JMP emit_literal_done_emit_remainder_encodeBlockAsm4MB + +memmove_long_emit_remainder_encodeBlockAsm4MB: + LEAQ (CX)(SI*1), DX + MOVL SI, BX + + // genMemMoveLong + MOVOU (AX), X0 + MOVOU 16(AX), X1 + MOVOU -32(AX)(BX*1), X2 + MOVOU -16(AX)(BX*1), X3 + MOVQ BX, DI + SHRQ $0x05, DI + MOVQ CX, SI + ANDL $0x0000001f, SI + MOVQ $0x00000040, R8 + SUBQ SI, R8 + DECQ DI + JA emit_lit_memmove_long_emit_remainder_encodeBlockAsm4MBlarge_forward_sse_loop_32 + LEAQ -32(AX)(R8*1), SI + LEAQ -32(CX)(R8*1), R9 + +emit_lit_memmove_long_emit_remainder_encodeBlockAsm4MBlarge_big_loop_back: + MOVOU (SI), X4 + MOVOU 16(SI), X5 + MOVOA X4, (R9) + MOVOA X5, 16(R9) + ADDQ $0x20, R9 + ADDQ $0x20, SI + ADDQ $0x20, R8 + DECQ DI + JNA emit_lit_memmove_long_emit_remainder_encodeBlockAsm4MBlarge_big_loop_back + +emit_lit_memmove_long_emit_remainder_encodeBlockAsm4MBlarge_forward_sse_loop_32: + MOVOU -32(AX)(R8*1), X4 + MOVOU -16(AX)(R8*1), X5 + MOVOA X4, -32(CX)(R8*1) + MOVOA X5, -16(CX)(R8*1) + ADDQ $0x20, R8 + CMPQ BX, R8 + JAE emit_lit_memmove_long_emit_remainder_encodeBlockAsm4MBlarge_forward_sse_loop_32 + MOVOU X0, (CX) + MOVOU X1, 16(CX) + MOVOU X2, -32(CX)(BX*1) + MOVOU X3, -16(CX)(BX*1) + MOVQ DX, CX + +emit_literal_done_emit_remainder_encodeBlockAsm4MB: + MOVQ dst_base+0(FP), AX + SUBQ AX, CX + MOVQ CX, ret+56(FP) + RET + +// func encodeBlockAsm12B(dst []byte, src []byte, tmp *[16384]byte) int +// Requires: BMI, SSE2 +TEXT ·encodeBlockAsm12B(SB), $24-64 + MOVQ tmp+48(FP), AX + MOVQ dst_base+0(FP), CX + MOVQ $0x00000080, DX + MOVQ AX, BX + PXOR X0, X0 + +zero_loop_encodeBlockAsm12B: + MOVOU X0, (BX) + MOVOU X0, 16(BX) + MOVOU X0, 32(BX) + MOVOU X0, 48(BX) + MOVOU X0, 64(BX) + MOVOU X0, 80(BX) + MOVOU X0, 96(BX) + MOVOU X0, 112(BX) + ADDQ $0x80, BX + DECQ DX + JNZ zero_loop_encodeBlockAsm12B + MOVL $0x00000000, 12(SP) + MOVQ src_len+32(FP), DX + LEAQ -9(DX), BX + LEAQ -8(DX), SI + MOVL SI, 8(SP) + SHRQ $0x05, DX + SUBL DX, BX + LEAQ (CX)(BX*1), BX + MOVQ BX, (SP) + MOVL $0x00000001, DX + MOVL DX, 16(SP) + MOVQ src_base+24(FP), BX + +search_loop_encodeBlockAsm12B: + MOVL DX, SI + SUBL 12(SP), SI + SHRL $0x05, SI + LEAL 4(DX)(SI*1), SI + CMPL SI, 8(SP) + JAE emit_remainder_encodeBlockAsm12B + MOVQ (BX)(DX*1), DI + MOVL SI, 20(SP) + MOVQ $0x000000cf1bbcdcbb, R9 + MOVQ DI, R10 + MOVQ DI, R11 + SHRQ $0x08, R11 + SHLQ $0x18, R10 + IMULQ R9, R10 + SHRQ $0x34, R10 + SHLQ $0x18, R11 + IMULQ R9, R11 + SHRQ $0x34, R11 + MOVL (AX)(R10*4), SI + MOVL (AX)(R11*4), R8 + MOVL DX, (AX)(R10*4) + LEAL 1(DX), R10 + MOVL R10, (AX)(R11*4) + MOVQ DI, R10 + SHRQ $0x10, R10 + SHLQ $0x18, R10 + IMULQ R9, R10 + SHRQ $0x34, R10 + MOVL DX, R9 + SUBL 16(SP), R9 + MOVL 1(BX)(R9*1), R11 + MOVQ DI, R9 + SHRQ $0x08, R9 + CMPL R9, R11 + JNE no_repeat_found_encodeBlockAsm12B + LEAL 1(DX), DI + MOVL 12(SP), R8 + MOVL DI, SI + SUBL 16(SP), SI + JZ repeat_extend_back_end_encodeBlockAsm12B + +repeat_extend_back_loop_encodeBlockAsm12B: + CMPL DI, R8 + JBE repeat_extend_back_end_encodeBlockAsm12B + MOVB -1(BX)(SI*1), R9 + MOVB -1(BX)(DI*1), R10 + CMPB R9, R10 + JNE repeat_extend_back_end_encodeBlockAsm12B + LEAL -1(DI), DI + DECL SI + JNZ repeat_extend_back_loop_encodeBlockAsm12B + +repeat_extend_back_end_encodeBlockAsm12B: + MOVL DI, SI + SUBL 12(SP), SI + LEAQ 3(CX)(SI*1), SI + CMPQ SI, (SP) + JB repeat_dst_size_check_encodeBlockAsm12B + MOVQ $0x00000000, ret+56(FP) + RET + +repeat_dst_size_check_encodeBlockAsm12B: + MOVL 12(SP), SI + CMPL SI, DI + JEQ emit_literal_done_repeat_emit_encodeBlockAsm12B + MOVL DI, R9 + MOVL DI, 12(SP) + LEAQ (BX)(SI*1), R10 + SUBL SI, R9 + LEAL -1(R9), SI + CMPL SI, $0x3c + JB one_byte_repeat_emit_encodeBlockAsm12B + CMPL SI, $0x00000100 + JB two_bytes_repeat_emit_encodeBlockAsm12B + JB three_bytes_repeat_emit_encodeBlockAsm12B + +three_bytes_repeat_emit_encodeBlockAsm12B: + MOVB $0xf4, (CX) + MOVW SI, 1(CX) + ADDQ $0x03, CX + JMP memmove_long_repeat_emit_encodeBlockAsm12B + +two_bytes_repeat_emit_encodeBlockAsm12B: + MOVB $0xf0, (CX) + MOVB SI, 1(CX) + ADDQ $0x02, CX + CMPL SI, $0x40 + JB memmove_repeat_emit_encodeBlockAsm12B + JMP memmove_long_repeat_emit_encodeBlockAsm12B + +one_byte_repeat_emit_encodeBlockAsm12B: + SHLB $0x02, SI + MOVB SI, (CX) + ADDQ $0x01, CX + +memmove_repeat_emit_encodeBlockAsm12B: + LEAQ (CX)(R9*1), SI + + // genMemMoveShort + CMPQ R9, $0x08 + JBE emit_lit_memmove_repeat_emit_encodeBlockAsm12B_memmove_move_8 + CMPQ R9, $0x10 + JBE emit_lit_memmove_repeat_emit_encodeBlockAsm12B_memmove_move_8through16 + CMPQ R9, $0x20 + JBE emit_lit_memmove_repeat_emit_encodeBlockAsm12B_memmove_move_17through32 + JMP emit_lit_memmove_repeat_emit_encodeBlockAsm12B_memmove_move_33through64 + +emit_lit_memmove_repeat_emit_encodeBlockAsm12B_memmove_move_8: + MOVQ (R10), R11 + MOVQ R11, (CX) + JMP memmove_end_copy_repeat_emit_encodeBlockAsm12B + +emit_lit_memmove_repeat_emit_encodeBlockAsm12B_memmove_move_8through16: + MOVQ (R10), R11 + MOVQ -8(R10)(R9*1), R10 + MOVQ R11, (CX) + MOVQ R10, -8(CX)(R9*1) + JMP memmove_end_copy_repeat_emit_encodeBlockAsm12B + +emit_lit_memmove_repeat_emit_encodeBlockAsm12B_memmove_move_17through32: + MOVOU (R10), X0 + MOVOU -16(R10)(R9*1), X1 + MOVOU X0, (CX) + MOVOU X1, -16(CX)(R9*1) + JMP memmove_end_copy_repeat_emit_encodeBlockAsm12B + +emit_lit_memmove_repeat_emit_encodeBlockAsm12B_memmove_move_33through64: + MOVOU (R10), X0 + MOVOU 16(R10), X1 + MOVOU -32(R10)(R9*1), X2 + MOVOU -16(R10)(R9*1), X3 + MOVOU X0, (CX) + MOVOU X1, 16(CX) + MOVOU X2, -32(CX)(R9*1) + MOVOU X3, -16(CX)(R9*1) + +memmove_end_copy_repeat_emit_encodeBlockAsm12B: + MOVQ SI, CX + JMP emit_literal_done_repeat_emit_encodeBlockAsm12B + +memmove_long_repeat_emit_encodeBlockAsm12B: + LEAQ (CX)(R9*1), SI + + // genMemMoveLong + MOVOU (R10), X0 + MOVOU 16(R10), X1 + MOVOU -32(R10)(R9*1), X2 + MOVOU -16(R10)(R9*1), X3 + MOVQ R9, R12 + SHRQ $0x05, R12 + MOVQ CX, R11 + ANDL $0x0000001f, R11 + MOVQ $0x00000040, R13 + SUBQ R11, R13 + DECQ R12 + JA emit_lit_memmove_long_repeat_emit_encodeBlockAsm12Blarge_forward_sse_loop_32 + LEAQ -32(R10)(R13*1), R11 + LEAQ -32(CX)(R13*1), R14 + +emit_lit_memmove_long_repeat_emit_encodeBlockAsm12Blarge_big_loop_back: + MOVOU (R11), X4 + MOVOU 16(R11), X5 + MOVOA X4, (R14) + MOVOA X5, 16(R14) + ADDQ $0x20, R14 + ADDQ $0x20, R11 + ADDQ $0x20, R13 + DECQ R12 + JNA emit_lit_memmove_long_repeat_emit_encodeBlockAsm12Blarge_big_loop_back + +emit_lit_memmove_long_repeat_emit_encodeBlockAsm12Blarge_forward_sse_loop_32: + MOVOU -32(R10)(R13*1), X4 + MOVOU -16(R10)(R13*1), X5 + MOVOA X4, -32(CX)(R13*1) + MOVOA X5, -16(CX)(R13*1) + ADDQ $0x20, R13 + CMPQ R9, R13 + JAE emit_lit_memmove_long_repeat_emit_encodeBlockAsm12Blarge_forward_sse_loop_32 + MOVOU X0, (CX) + MOVOU X1, 16(CX) + MOVOU X2, -32(CX)(R9*1) + MOVOU X3, -16(CX)(R9*1) + MOVQ SI, CX + +emit_literal_done_repeat_emit_encodeBlockAsm12B: + ADDL $0x05, DX + MOVL DX, SI + SUBL 16(SP), SI + MOVQ src_len+32(FP), R9 + SUBL DX, R9 + LEAQ (BX)(DX*1), R10 + LEAQ (BX)(SI*1), SI + + // matchLen + XORL R12, R12 + +matchlen_loopback_16_repeat_extend_encodeBlockAsm12B: + CMPL R9, $0x10 + JB matchlen_match8_repeat_extend_encodeBlockAsm12B + MOVQ (R10)(R12*1), R11 + MOVQ 8(R10)(R12*1), R13 + XORQ (SI)(R12*1), R11 + JNZ matchlen_bsf_8_repeat_extend_encodeBlockAsm12B + XORQ 8(SI)(R12*1), R13 + JNZ matchlen_bsf_16repeat_extend_encodeBlockAsm12B + LEAL -16(R9), R9 + LEAL 16(R12), R12 + JMP matchlen_loopback_16_repeat_extend_encodeBlockAsm12B + +matchlen_bsf_16repeat_extend_encodeBlockAsm12B: +#ifdef GOAMD64_v3 + TZCNTQ R13, R13 + +#else + BSFQ R13, R13 + +#endif + SARQ $0x03, R13 + LEAL 8(R12)(R13*1), R12 + JMP repeat_extend_forward_end_encodeBlockAsm12B + +matchlen_match8_repeat_extend_encodeBlockAsm12B: + CMPL R9, $0x08 + JB matchlen_match4_repeat_extend_encodeBlockAsm12B + MOVQ (R10)(R12*1), R11 + XORQ (SI)(R12*1), R11 + JNZ matchlen_bsf_8_repeat_extend_encodeBlockAsm12B + LEAL -8(R9), R9 + LEAL 8(R12), R12 + JMP matchlen_match4_repeat_extend_encodeBlockAsm12B + +matchlen_bsf_8_repeat_extend_encodeBlockAsm12B: +#ifdef GOAMD64_v3 + TZCNTQ R11, R11 + +#else + BSFQ R11, R11 + +#endif + SARQ $0x03, R11 + LEAL (R12)(R11*1), R12 + JMP repeat_extend_forward_end_encodeBlockAsm12B + +matchlen_match4_repeat_extend_encodeBlockAsm12B: + CMPL R9, $0x04 + JB matchlen_match2_repeat_extend_encodeBlockAsm12B + MOVL (R10)(R12*1), R11 + CMPL (SI)(R12*1), R11 + JNE matchlen_match2_repeat_extend_encodeBlockAsm12B + LEAL -4(R9), R9 + LEAL 4(R12), R12 + +matchlen_match2_repeat_extend_encodeBlockAsm12B: + CMPL R9, $0x01 + JE matchlen_match1_repeat_extend_encodeBlockAsm12B + JB repeat_extend_forward_end_encodeBlockAsm12B + MOVW (R10)(R12*1), R11 + CMPW (SI)(R12*1), R11 + JNE matchlen_match1_repeat_extend_encodeBlockAsm12B + LEAL 2(R12), R12 + SUBL $0x02, R9 + JZ repeat_extend_forward_end_encodeBlockAsm12B + +matchlen_match1_repeat_extend_encodeBlockAsm12B: + MOVB (R10)(R12*1), R11 + CMPB (SI)(R12*1), R11 + JNE repeat_extend_forward_end_encodeBlockAsm12B + LEAL 1(R12), R12 + +repeat_extend_forward_end_encodeBlockAsm12B: + ADDL R12, DX + MOVL DX, SI + SUBL DI, SI + MOVL 16(SP), DI + TESTL R8, R8 + JZ repeat_as_copy_encodeBlockAsm12B + + // emitRepeat + MOVL SI, R8 + LEAL -4(SI), SI + CMPL R8, $0x08 + JBE repeat_two_match_repeat_encodeBlockAsm12B + CMPL R8, $0x0c + JAE cant_repeat_two_offset_match_repeat_encodeBlockAsm12B + CMPL DI, $0x00000800 + JB repeat_two_offset_match_repeat_encodeBlockAsm12B + +cant_repeat_two_offset_match_repeat_encodeBlockAsm12B: + CMPL SI, $0x00000104 + JB repeat_three_match_repeat_encodeBlockAsm12B + LEAL -256(SI), SI + MOVW $0x0019, (CX) + MOVW SI, 2(CX) + ADDQ $0x04, CX + JMP repeat_end_emit_encodeBlockAsm12B + +repeat_three_match_repeat_encodeBlockAsm12B: + LEAL -4(SI), SI + MOVW $0x0015, (CX) + MOVB SI, 2(CX) + ADDQ $0x03, CX + JMP repeat_end_emit_encodeBlockAsm12B + +repeat_two_match_repeat_encodeBlockAsm12B: + SHLL $0x02, SI + ORL $0x01, SI + MOVW SI, (CX) + ADDQ $0x02, CX + JMP repeat_end_emit_encodeBlockAsm12B + +repeat_two_offset_match_repeat_encodeBlockAsm12B: + XORQ R8, R8 + LEAL 1(R8)(SI*4), SI + MOVB DI, 1(CX) + SARL $0x08, DI + SHLL $0x05, DI + ORL DI, SI + MOVB SI, (CX) + ADDQ $0x02, CX + JMP repeat_end_emit_encodeBlockAsm12B + +repeat_as_copy_encodeBlockAsm12B: + // emitCopy + CMPL SI, $0x40 + JBE two_byte_offset_short_repeat_as_copy_encodeBlockAsm12B + CMPL DI, $0x00000800 + JAE long_offset_short_repeat_as_copy_encodeBlockAsm12B + MOVL $0x00000001, R8 + LEAL 16(R8), R8 + MOVB DI, 1(CX) + SHRL $0x08, DI + SHLL $0x05, DI + ORL DI, R8 + MOVB R8, (CX) + ADDQ $0x02, CX + SUBL $0x08, SI + + // emitRepeat + LEAL -4(SI), SI + JMP cant_repeat_two_offset_repeat_as_copy_encodeBlockAsm12B_emit_copy_short_2b + MOVL SI, R8 + LEAL -4(SI), SI + CMPL R8, $0x08 + JBE repeat_two_repeat_as_copy_encodeBlockAsm12B_emit_copy_short_2b + CMPL R8, $0x0c + JAE cant_repeat_two_offset_repeat_as_copy_encodeBlockAsm12B_emit_copy_short_2b + CMPL DI, $0x00000800 + JB repeat_two_offset_repeat_as_copy_encodeBlockAsm12B_emit_copy_short_2b + +cant_repeat_two_offset_repeat_as_copy_encodeBlockAsm12B_emit_copy_short_2b: + CMPL SI, $0x00000104 + JB repeat_three_repeat_as_copy_encodeBlockAsm12B_emit_copy_short_2b + LEAL -256(SI), SI + MOVW $0x0019, (CX) + MOVW SI, 2(CX) + ADDQ $0x04, CX + JMP repeat_end_emit_encodeBlockAsm12B + +repeat_three_repeat_as_copy_encodeBlockAsm12B_emit_copy_short_2b: + LEAL -4(SI), SI + MOVW $0x0015, (CX) + MOVB SI, 2(CX) + ADDQ $0x03, CX + JMP repeat_end_emit_encodeBlockAsm12B + +repeat_two_repeat_as_copy_encodeBlockAsm12B_emit_copy_short_2b: + SHLL $0x02, SI + ORL $0x01, SI + MOVW SI, (CX) + ADDQ $0x02, CX + JMP repeat_end_emit_encodeBlockAsm12B + +repeat_two_offset_repeat_as_copy_encodeBlockAsm12B_emit_copy_short_2b: + XORQ R8, R8 + LEAL 1(R8)(SI*4), SI + MOVB DI, 1(CX) + SARL $0x08, DI + SHLL $0x05, DI + ORL DI, SI + MOVB SI, (CX) + ADDQ $0x02, CX + JMP repeat_end_emit_encodeBlockAsm12B + +long_offset_short_repeat_as_copy_encodeBlockAsm12B: + MOVB $0xee, (CX) + MOVW DI, 1(CX) + LEAL -60(SI), SI + ADDQ $0x03, CX + + // emitRepeat + MOVL SI, R8 + LEAL -4(SI), SI + CMPL R8, $0x08 + JBE repeat_two_repeat_as_copy_encodeBlockAsm12B_emit_copy_short + CMPL R8, $0x0c + JAE cant_repeat_two_offset_repeat_as_copy_encodeBlockAsm12B_emit_copy_short + CMPL DI, $0x00000800 + JB repeat_two_offset_repeat_as_copy_encodeBlockAsm12B_emit_copy_short + +cant_repeat_two_offset_repeat_as_copy_encodeBlockAsm12B_emit_copy_short: + CMPL SI, $0x00000104 + JB repeat_three_repeat_as_copy_encodeBlockAsm12B_emit_copy_short + LEAL -256(SI), SI + MOVW $0x0019, (CX) + MOVW SI, 2(CX) + ADDQ $0x04, CX + JMP repeat_end_emit_encodeBlockAsm12B + +repeat_three_repeat_as_copy_encodeBlockAsm12B_emit_copy_short: + LEAL -4(SI), SI + MOVW $0x0015, (CX) + MOVB SI, 2(CX) + ADDQ $0x03, CX + JMP repeat_end_emit_encodeBlockAsm12B + +repeat_two_repeat_as_copy_encodeBlockAsm12B_emit_copy_short: + SHLL $0x02, SI + ORL $0x01, SI + MOVW SI, (CX) + ADDQ $0x02, CX + JMP repeat_end_emit_encodeBlockAsm12B + +repeat_two_offset_repeat_as_copy_encodeBlockAsm12B_emit_copy_short: + XORQ R8, R8 + LEAL 1(R8)(SI*4), SI + MOVB DI, 1(CX) + SARL $0x08, DI + SHLL $0x05, DI + ORL DI, SI + MOVB SI, (CX) + ADDQ $0x02, CX + JMP repeat_end_emit_encodeBlockAsm12B + +two_byte_offset_short_repeat_as_copy_encodeBlockAsm12B: + MOVL SI, R8 + SHLL $0x02, R8 + CMPL SI, $0x0c + JAE emit_copy_three_repeat_as_copy_encodeBlockAsm12B + CMPL DI, $0x00000800 + JAE emit_copy_three_repeat_as_copy_encodeBlockAsm12B + LEAL -15(R8), R8 + MOVB DI, 1(CX) + SHRL $0x08, DI + SHLL $0x05, DI + ORL DI, R8 + MOVB R8, (CX) + ADDQ $0x02, CX + JMP repeat_end_emit_encodeBlockAsm12B + +emit_copy_three_repeat_as_copy_encodeBlockAsm12B: + LEAL -2(R8), R8 + MOVB R8, (CX) + MOVW DI, 1(CX) + ADDQ $0x03, CX + +repeat_end_emit_encodeBlockAsm12B: + MOVL DX, 12(SP) + JMP search_loop_encodeBlockAsm12B + +no_repeat_found_encodeBlockAsm12B: + CMPL (BX)(SI*1), DI + JEQ candidate_match_encodeBlockAsm12B + SHRQ $0x08, DI + MOVL (AX)(R10*4), SI + LEAL 2(DX), R9 + CMPL (BX)(R8*1), DI + JEQ candidate2_match_encodeBlockAsm12B + MOVL R9, (AX)(R10*4) + SHRQ $0x08, DI + CMPL (BX)(SI*1), DI + JEQ candidate3_match_encodeBlockAsm12B + MOVL 20(SP), DX + JMP search_loop_encodeBlockAsm12B + +candidate3_match_encodeBlockAsm12B: + ADDL $0x02, DX + JMP candidate_match_encodeBlockAsm12B + +candidate2_match_encodeBlockAsm12B: + MOVL R9, (AX)(R10*4) + INCL DX + MOVL R8, SI + +candidate_match_encodeBlockAsm12B: + MOVL 12(SP), DI + TESTL SI, SI + JZ match_extend_back_end_encodeBlockAsm12B + +match_extend_back_loop_encodeBlockAsm12B: + CMPL DX, DI + JBE match_extend_back_end_encodeBlockAsm12B + MOVB -1(BX)(SI*1), R8 + MOVB -1(BX)(DX*1), R9 + CMPB R8, R9 + JNE match_extend_back_end_encodeBlockAsm12B + LEAL -1(DX), DX + DECL SI + JZ match_extend_back_end_encodeBlockAsm12B + JMP match_extend_back_loop_encodeBlockAsm12B + +match_extend_back_end_encodeBlockAsm12B: + MOVL DX, DI + SUBL 12(SP), DI + LEAQ 3(CX)(DI*1), DI + CMPQ DI, (SP) + JB match_dst_size_check_encodeBlockAsm12B + MOVQ $0x00000000, ret+56(FP) + RET + +match_dst_size_check_encodeBlockAsm12B: + MOVL DX, DI + MOVL 12(SP), R8 + CMPL R8, DI + JEQ emit_literal_done_match_emit_encodeBlockAsm12B + MOVL DI, R9 + MOVL DI, 12(SP) + LEAQ (BX)(R8*1), DI + SUBL R8, R9 + LEAL -1(R9), R8 + CMPL R8, $0x3c + JB one_byte_match_emit_encodeBlockAsm12B + CMPL R8, $0x00000100 + JB two_bytes_match_emit_encodeBlockAsm12B + JB three_bytes_match_emit_encodeBlockAsm12B + +three_bytes_match_emit_encodeBlockAsm12B: + MOVB $0xf4, (CX) + MOVW R8, 1(CX) + ADDQ $0x03, CX + JMP memmove_long_match_emit_encodeBlockAsm12B + +two_bytes_match_emit_encodeBlockAsm12B: + MOVB $0xf0, (CX) + MOVB R8, 1(CX) + ADDQ $0x02, CX + CMPL R8, $0x40 + JB memmove_match_emit_encodeBlockAsm12B + JMP memmove_long_match_emit_encodeBlockAsm12B + +one_byte_match_emit_encodeBlockAsm12B: + SHLB $0x02, R8 + MOVB R8, (CX) + ADDQ $0x01, CX + +memmove_match_emit_encodeBlockAsm12B: + LEAQ (CX)(R9*1), R8 + + // genMemMoveShort + CMPQ R9, $0x08 + JBE emit_lit_memmove_match_emit_encodeBlockAsm12B_memmove_move_8 + CMPQ R9, $0x10 + JBE emit_lit_memmove_match_emit_encodeBlockAsm12B_memmove_move_8through16 + CMPQ R9, $0x20 + JBE emit_lit_memmove_match_emit_encodeBlockAsm12B_memmove_move_17through32 + JMP emit_lit_memmove_match_emit_encodeBlockAsm12B_memmove_move_33through64 + +emit_lit_memmove_match_emit_encodeBlockAsm12B_memmove_move_8: + MOVQ (DI), R10 + MOVQ R10, (CX) + JMP memmove_end_copy_match_emit_encodeBlockAsm12B + +emit_lit_memmove_match_emit_encodeBlockAsm12B_memmove_move_8through16: + MOVQ (DI), R10 + MOVQ -8(DI)(R9*1), DI + MOVQ R10, (CX) + MOVQ DI, -8(CX)(R9*1) + JMP memmove_end_copy_match_emit_encodeBlockAsm12B + +emit_lit_memmove_match_emit_encodeBlockAsm12B_memmove_move_17through32: + MOVOU (DI), X0 + MOVOU -16(DI)(R9*1), X1 + MOVOU X0, (CX) + MOVOU X1, -16(CX)(R9*1) + JMP memmove_end_copy_match_emit_encodeBlockAsm12B + +emit_lit_memmove_match_emit_encodeBlockAsm12B_memmove_move_33through64: + MOVOU (DI), X0 + MOVOU 16(DI), X1 + MOVOU -32(DI)(R9*1), X2 + MOVOU -16(DI)(R9*1), X3 + MOVOU X0, (CX) + MOVOU X1, 16(CX) + MOVOU X2, -32(CX)(R9*1) + MOVOU X3, -16(CX)(R9*1) + +memmove_end_copy_match_emit_encodeBlockAsm12B: + MOVQ R8, CX + JMP emit_literal_done_match_emit_encodeBlockAsm12B + +memmove_long_match_emit_encodeBlockAsm12B: + LEAQ (CX)(R9*1), R8 + + // genMemMoveLong + MOVOU (DI), X0 + MOVOU 16(DI), X1 + MOVOU -32(DI)(R9*1), X2 + MOVOU -16(DI)(R9*1), X3 + MOVQ R9, R11 + SHRQ $0x05, R11 + MOVQ CX, R10 + ANDL $0x0000001f, R10 + MOVQ $0x00000040, R12 + SUBQ R10, R12 + DECQ R11 + JA emit_lit_memmove_long_match_emit_encodeBlockAsm12Blarge_forward_sse_loop_32 + LEAQ -32(DI)(R12*1), R10 + LEAQ -32(CX)(R12*1), R13 + +emit_lit_memmove_long_match_emit_encodeBlockAsm12Blarge_big_loop_back: + MOVOU (R10), X4 + MOVOU 16(R10), X5 + MOVOA X4, (R13) + MOVOA X5, 16(R13) + ADDQ $0x20, R13 + ADDQ $0x20, R10 + ADDQ $0x20, R12 + DECQ R11 + JNA emit_lit_memmove_long_match_emit_encodeBlockAsm12Blarge_big_loop_back + +emit_lit_memmove_long_match_emit_encodeBlockAsm12Blarge_forward_sse_loop_32: + MOVOU -32(DI)(R12*1), X4 + MOVOU -16(DI)(R12*1), X5 + MOVOA X4, -32(CX)(R12*1) + MOVOA X5, -16(CX)(R12*1) + ADDQ $0x20, R12 + CMPQ R9, R12 + JAE emit_lit_memmove_long_match_emit_encodeBlockAsm12Blarge_forward_sse_loop_32 + MOVOU X0, (CX) + MOVOU X1, 16(CX) + MOVOU X2, -32(CX)(R9*1) + MOVOU X3, -16(CX)(R9*1) + MOVQ R8, CX + +emit_literal_done_match_emit_encodeBlockAsm12B: +match_nolit_loop_encodeBlockAsm12B: + MOVL DX, DI + SUBL SI, DI + MOVL DI, 16(SP) + ADDL $0x04, DX + ADDL $0x04, SI + MOVQ src_len+32(FP), DI + SUBL DX, DI + LEAQ (BX)(DX*1), R8 + LEAQ (BX)(SI*1), SI + + // matchLen + XORL R10, R10 + +matchlen_loopback_16_match_nolit_encodeBlockAsm12B: + CMPL DI, $0x10 + JB matchlen_match8_match_nolit_encodeBlockAsm12B + MOVQ (R8)(R10*1), R9 + MOVQ 8(R8)(R10*1), R11 + XORQ (SI)(R10*1), R9 + JNZ matchlen_bsf_8_match_nolit_encodeBlockAsm12B + XORQ 8(SI)(R10*1), R11 + JNZ matchlen_bsf_16match_nolit_encodeBlockAsm12B + LEAL -16(DI), DI + LEAL 16(R10), R10 + JMP matchlen_loopback_16_match_nolit_encodeBlockAsm12B + +matchlen_bsf_16match_nolit_encodeBlockAsm12B: +#ifdef GOAMD64_v3 + TZCNTQ R11, R11 + +#else + BSFQ R11, R11 + +#endif + SARQ $0x03, R11 + LEAL 8(R10)(R11*1), R10 + JMP match_nolit_end_encodeBlockAsm12B + +matchlen_match8_match_nolit_encodeBlockAsm12B: + CMPL DI, $0x08 + JB matchlen_match4_match_nolit_encodeBlockAsm12B + MOVQ (R8)(R10*1), R9 + XORQ (SI)(R10*1), R9 + JNZ matchlen_bsf_8_match_nolit_encodeBlockAsm12B + LEAL -8(DI), DI + LEAL 8(R10), R10 + JMP matchlen_match4_match_nolit_encodeBlockAsm12B + +matchlen_bsf_8_match_nolit_encodeBlockAsm12B: +#ifdef GOAMD64_v3 + TZCNTQ R9, R9 + +#else + BSFQ R9, R9 + +#endif + SARQ $0x03, R9 + LEAL (R10)(R9*1), R10 + JMP match_nolit_end_encodeBlockAsm12B + +matchlen_match4_match_nolit_encodeBlockAsm12B: + CMPL DI, $0x04 + JB matchlen_match2_match_nolit_encodeBlockAsm12B + MOVL (R8)(R10*1), R9 + CMPL (SI)(R10*1), R9 + JNE matchlen_match2_match_nolit_encodeBlockAsm12B + LEAL -4(DI), DI + LEAL 4(R10), R10 + +matchlen_match2_match_nolit_encodeBlockAsm12B: + CMPL DI, $0x01 + JE matchlen_match1_match_nolit_encodeBlockAsm12B + JB match_nolit_end_encodeBlockAsm12B + MOVW (R8)(R10*1), R9 + CMPW (SI)(R10*1), R9 + JNE matchlen_match1_match_nolit_encodeBlockAsm12B + LEAL 2(R10), R10 + SUBL $0x02, DI + JZ match_nolit_end_encodeBlockAsm12B + +matchlen_match1_match_nolit_encodeBlockAsm12B: + MOVB (R8)(R10*1), R9 + CMPB (SI)(R10*1), R9 + JNE match_nolit_end_encodeBlockAsm12B + LEAL 1(R10), R10 + +match_nolit_end_encodeBlockAsm12B: + ADDL R10, DX + MOVL 16(SP), SI + ADDL $0x04, R10 + MOVL DX, 12(SP) + + // emitCopy + CMPL R10, $0x40 + JBE two_byte_offset_short_match_nolit_encodeBlockAsm12B + CMPL SI, $0x00000800 + JAE long_offset_short_match_nolit_encodeBlockAsm12B + MOVL $0x00000001, DI + LEAL 16(DI), DI + MOVB SI, 1(CX) + SHRL $0x08, SI + SHLL $0x05, SI + ORL SI, DI + MOVB DI, (CX) + ADDQ $0x02, CX + SUBL $0x08, R10 + + // emitRepeat + LEAL -4(R10), R10 + JMP cant_repeat_two_offset_match_nolit_encodeBlockAsm12B_emit_copy_short_2b + MOVL R10, DI + LEAL -4(R10), R10 + CMPL DI, $0x08 + JBE repeat_two_match_nolit_encodeBlockAsm12B_emit_copy_short_2b + CMPL DI, $0x0c + JAE cant_repeat_two_offset_match_nolit_encodeBlockAsm12B_emit_copy_short_2b + CMPL SI, $0x00000800 + JB repeat_two_offset_match_nolit_encodeBlockAsm12B_emit_copy_short_2b + +cant_repeat_two_offset_match_nolit_encodeBlockAsm12B_emit_copy_short_2b: + CMPL R10, $0x00000104 + JB repeat_three_match_nolit_encodeBlockAsm12B_emit_copy_short_2b + LEAL -256(R10), R10 + MOVW $0x0019, (CX) + MOVW R10, 2(CX) + ADDQ $0x04, CX + JMP match_nolit_emitcopy_end_encodeBlockAsm12B + +repeat_three_match_nolit_encodeBlockAsm12B_emit_copy_short_2b: + LEAL -4(R10), R10 + MOVW $0x0015, (CX) + MOVB R10, 2(CX) + ADDQ $0x03, CX + JMP match_nolit_emitcopy_end_encodeBlockAsm12B + +repeat_two_match_nolit_encodeBlockAsm12B_emit_copy_short_2b: + SHLL $0x02, R10 + ORL $0x01, R10 + MOVW R10, (CX) + ADDQ $0x02, CX + JMP match_nolit_emitcopy_end_encodeBlockAsm12B + +repeat_two_offset_match_nolit_encodeBlockAsm12B_emit_copy_short_2b: + XORQ DI, DI + LEAL 1(DI)(R10*4), R10 + MOVB SI, 1(CX) + SARL $0x08, SI + SHLL $0x05, SI + ORL SI, R10 + MOVB R10, (CX) + ADDQ $0x02, CX + JMP match_nolit_emitcopy_end_encodeBlockAsm12B + +long_offset_short_match_nolit_encodeBlockAsm12B: + MOVB $0xee, (CX) + MOVW SI, 1(CX) + LEAL -60(R10), R10 + ADDQ $0x03, CX + + // emitRepeat + MOVL R10, DI + LEAL -4(R10), R10 + CMPL DI, $0x08 + JBE repeat_two_match_nolit_encodeBlockAsm12B_emit_copy_short + CMPL DI, $0x0c + JAE cant_repeat_two_offset_match_nolit_encodeBlockAsm12B_emit_copy_short + CMPL SI, $0x00000800 + JB repeat_two_offset_match_nolit_encodeBlockAsm12B_emit_copy_short + +cant_repeat_two_offset_match_nolit_encodeBlockAsm12B_emit_copy_short: + CMPL R10, $0x00000104 + JB repeat_three_match_nolit_encodeBlockAsm12B_emit_copy_short + LEAL -256(R10), R10 + MOVW $0x0019, (CX) + MOVW R10, 2(CX) + ADDQ $0x04, CX + JMP match_nolit_emitcopy_end_encodeBlockAsm12B + +repeat_three_match_nolit_encodeBlockAsm12B_emit_copy_short: + LEAL -4(R10), R10 + MOVW $0x0015, (CX) + MOVB R10, 2(CX) + ADDQ $0x03, CX + JMP match_nolit_emitcopy_end_encodeBlockAsm12B + +repeat_two_match_nolit_encodeBlockAsm12B_emit_copy_short: + SHLL $0x02, R10 + ORL $0x01, R10 + MOVW R10, (CX) + ADDQ $0x02, CX + JMP match_nolit_emitcopy_end_encodeBlockAsm12B + +repeat_two_offset_match_nolit_encodeBlockAsm12B_emit_copy_short: + XORQ DI, DI + LEAL 1(DI)(R10*4), R10 + MOVB SI, 1(CX) + SARL $0x08, SI + SHLL $0x05, SI + ORL SI, R10 + MOVB R10, (CX) + ADDQ $0x02, CX + JMP match_nolit_emitcopy_end_encodeBlockAsm12B + +two_byte_offset_short_match_nolit_encodeBlockAsm12B: + MOVL R10, DI + SHLL $0x02, DI + CMPL R10, $0x0c + JAE emit_copy_three_match_nolit_encodeBlockAsm12B + CMPL SI, $0x00000800 + JAE emit_copy_three_match_nolit_encodeBlockAsm12B + LEAL -15(DI), DI + MOVB SI, 1(CX) + SHRL $0x08, SI + SHLL $0x05, SI + ORL SI, DI + MOVB DI, (CX) + ADDQ $0x02, CX + JMP match_nolit_emitcopy_end_encodeBlockAsm12B + +emit_copy_three_match_nolit_encodeBlockAsm12B: + LEAL -2(DI), DI + MOVB DI, (CX) + MOVW SI, 1(CX) + ADDQ $0x03, CX + +match_nolit_emitcopy_end_encodeBlockAsm12B: + CMPL DX, 8(SP) + JAE emit_remainder_encodeBlockAsm12B + MOVQ -2(BX)(DX*1), DI + CMPQ CX, (SP) + JB match_nolit_dst_ok_encodeBlockAsm12B + MOVQ $0x00000000, ret+56(FP) + RET + +match_nolit_dst_ok_encodeBlockAsm12B: + MOVQ $0x000000cf1bbcdcbb, R9 + MOVQ DI, R8 + SHRQ $0x10, DI + MOVQ DI, SI + SHLQ $0x18, R8 + IMULQ R9, R8 + SHRQ $0x34, R8 + SHLQ $0x18, SI + IMULQ R9, SI + SHRQ $0x34, SI + LEAL -2(DX), R9 + LEAQ (AX)(SI*4), R10 + MOVL (R10), SI + MOVL R9, (AX)(R8*4) + MOVL DX, (R10) + CMPL (BX)(SI*1), DI + JEQ match_nolit_loop_encodeBlockAsm12B + INCL DX + JMP search_loop_encodeBlockAsm12B + +emit_remainder_encodeBlockAsm12B: + MOVQ src_len+32(FP), AX + SUBL 12(SP), AX + LEAQ 3(CX)(AX*1), AX + CMPQ AX, (SP) + JB emit_remainder_ok_encodeBlockAsm12B + MOVQ $0x00000000, ret+56(FP) + RET + +emit_remainder_ok_encodeBlockAsm12B: + MOVQ src_len+32(FP), AX + MOVL 12(SP), DX + CMPL DX, AX + JEQ emit_literal_done_emit_remainder_encodeBlockAsm12B + MOVL AX, SI + MOVL AX, 12(SP) + LEAQ (BX)(DX*1), AX + SUBL DX, SI + LEAL -1(SI), DX + CMPL DX, $0x3c + JB one_byte_emit_remainder_encodeBlockAsm12B + CMPL DX, $0x00000100 + JB two_bytes_emit_remainder_encodeBlockAsm12B + JB three_bytes_emit_remainder_encodeBlockAsm12B + +three_bytes_emit_remainder_encodeBlockAsm12B: + MOVB $0xf4, (CX) + MOVW DX, 1(CX) + ADDQ $0x03, CX + JMP memmove_long_emit_remainder_encodeBlockAsm12B + +two_bytes_emit_remainder_encodeBlockAsm12B: + MOVB $0xf0, (CX) + MOVB DL, 1(CX) + ADDQ $0x02, CX + CMPL DX, $0x40 + JB memmove_emit_remainder_encodeBlockAsm12B + JMP memmove_long_emit_remainder_encodeBlockAsm12B + +one_byte_emit_remainder_encodeBlockAsm12B: + SHLB $0x02, DL + MOVB DL, (CX) + ADDQ $0x01, CX + +memmove_emit_remainder_encodeBlockAsm12B: + LEAQ (CX)(SI*1), DX + MOVL SI, BX + + // genMemMoveShort + CMPQ BX, $0x03 + JB emit_lit_memmove_emit_remainder_encodeBlockAsm12B_memmove_move_1or2 + JE emit_lit_memmove_emit_remainder_encodeBlockAsm12B_memmove_move_3 + CMPQ BX, $0x08 + JB emit_lit_memmove_emit_remainder_encodeBlockAsm12B_memmove_move_4through7 + CMPQ BX, $0x10 + JBE emit_lit_memmove_emit_remainder_encodeBlockAsm12B_memmove_move_8through16 + CMPQ BX, $0x20 + JBE emit_lit_memmove_emit_remainder_encodeBlockAsm12B_memmove_move_17through32 + JMP emit_lit_memmove_emit_remainder_encodeBlockAsm12B_memmove_move_33through64 + +emit_lit_memmove_emit_remainder_encodeBlockAsm12B_memmove_move_1or2: + MOVB (AX), SI + MOVB -1(AX)(BX*1), AL + MOVB SI, (CX) + MOVB AL, -1(CX)(BX*1) + JMP memmove_end_copy_emit_remainder_encodeBlockAsm12B + +emit_lit_memmove_emit_remainder_encodeBlockAsm12B_memmove_move_3: + MOVW (AX), SI + MOVB 2(AX), AL + MOVW SI, (CX) + MOVB AL, 2(CX) + JMP memmove_end_copy_emit_remainder_encodeBlockAsm12B + +emit_lit_memmove_emit_remainder_encodeBlockAsm12B_memmove_move_4through7: + MOVL (AX), SI + MOVL -4(AX)(BX*1), AX + MOVL SI, (CX) + MOVL AX, -4(CX)(BX*1) + JMP memmove_end_copy_emit_remainder_encodeBlockAsm12B + +emit_lit_memmove_emit_remainder_encodeBlockAsm12B_memmove_move_8through16: + MOVQ (AX), SI + MOVQ -8(AX)(BX*1), AX + MOVQ SI, (CX) + MOVQ AX, -8(CX)(BX*1) + JMP memmove_end_copy_emit_remainder_encodeBlockAsm12B + +emit_lit_memmove_emit_remainder_encodeBlockAsm12B_memmove_move_17through32: + MOVOU (AX), X0 + MOVOU -16(AX)(BX*1), X1 + MOVOU X0, (CX) + MOVOU X1, -16(CX)(BX*1) + JMP memmove_end_copy_emit_remainder_encodeBlockAsm12B + +emit_lit_memmove_emit_remainder_encodeBlockAsm12B_memmove_move_33through64: + MOVOU (AX), X0 + MOVOU 16(AX), X1 + MOVOU -32(AX)(BX*1), X2 + MOVOU -16(AX)(BX*1), X3 + MOVOU X0, (CX) + MOVOU X1, 16(CX) + MOVOU X2, -32(CX)(BX*1) + MOVOU X3, -16(CX)(BX*1) + +memmove_end_copy_emit_remainder_encodeBlockAsm12B: + MOVQ DX, CX + JMP emit_literal_done_emit_remainder_encodeBlockAsm12B + +memmove_long_emit_remainder_encodeBlockAsm12B: + LEAQ (CX)(SI*1), DX + MOVL SI, BX + + // genMemMoveLong + MOVOU (AX), X0 + MOVOU 16(AX), X1 + MOVOU -32(AX)(BX*1), X2 + MOVOU -16(AX)(BX*1), X3 + MOVQ BX, DI + SHRQ $0x05, DI + MOVQ CX, SI + ANDL $0x0000001f, SI + MOVQ $0x00000040, R8 + SUBQ SI, R8 + DECQ DI + JA emit_lit_memmove_long_emit_remainder_encodeBlockAsm12Blarge_forward_sse_loop_32 + LEAQ -32(AX)(R8*1), SI + LEAQ -32(CX)(R8*1), R9 + +emit_lit_memmove_long_emit_remainder_encodeBlockAsm12Blarge_big_loop_back: + MOVOU (SI), X4 + MOVOU 16(SI), X5 + MOVOA X4, (R9) + MOVOA X5, 16(R9) + ADDQ $0x20, R9 + ADDQ $0x20, SI + ADDQ $0x20, R8 + DECQ DI + JNA emit_lit_memmove_long_emit_remainder_encodeBlockAsm12Blarge_big_loop_back + +emit_lit_memmove_long_emit_remainder_encodeBlockAsm12Blarge_forward_sse_loop_32: + MOVOU -32(AX)(R8*1), X4 + MOVOU -16(AX)(R8*1), X5 + MOVOA X4, -32(CX)(R8*1) + MOVOA X5, -16(CX)(R8*1) + ADDQ $0x20, R8 + CMPQ BX, R8 + JAE emit_lit_memmove_long_emit_remainder_encodeBlockAsm12Blarge_forward_sse_loop_32 + MOVOU X0, (CX) + MOVOU X1, 16(CX) + MOVOU X2, -32(CX)(BX*1) + MOVOU X3, -16(CX)(BX*1) + MOVQ DX, CX + +emit_literal_done_emit_remainder_encodeBlockAsm12B: + MOVQ dst_base+0(FP), AX + SUBQ AX, CX + MOVQ CX, ret+56(FP) + RET + +// func encodeBlockAsm10B(dst []byte, src []byte, tmp *[4096]byte) int +// Requires: BMI, SSE2 +TEXT ·encodeBlockAsm10B(SB), $24-64 + MOVQ tmp+48(FP), AX + MOVQ dst_base+0(FP), CX + MOVQ $0x00000020, DX + MOVQ AX, BX + PXOR X0, X0 + +zero_loop_encodeBlockAsm10B: + MOVOU X0, (BX) + MOVOU X0, 16(BX) + MOVOU X0, 32(BX) + MOVOU X0, 48(BX) + MOVOU X0, 64(BX) + MOVOU X0, 80(BX) + MOVOU X0, 96(BX) + MOVOU X0, 112(BX) + ADDQ $0x80, BX + DECQ DX + JNZ zero_loop_encodeBlockAsm10B + MOVL $0x00000000, 12(SP) + MOVQ src_len+32(FP), DX + LEAQ -9(DX), BX + LEAQ -8(DX), SI + MOVL SI, 8(SP) + SHRQ $0x05, DX + SUBL DX, BX + LEAQ (CX)(BX*1), BX + MOVQ BX, (SP) + MOVL $0x00000001, DX + MOVL DX, 16(SP) + MOVQ src_base+24(FP), BX + +search_loop_encodeBlockAsm10B: + MOVL DX, SI + SUBL 12(SP), SI + SHRL $0x05, SI + LEAL 4(DX)(SI*1), SI + CMPL SI, 8(SP) + JAE emit_remainder_encodeBlockAsm10B + MOVQ (BX)(DX*1), DI + MOVL SI, 20(SP) + MOVQ $0x9e3779b1, R9 + MOVQ DI, R10 + MOVQ DI, R11 + SHRQ $0x08, R11 + SHLQ $0x20, R10 + IMULQ R9, R10 + SHRQ $0x36, R10 + SHLQ $0x20, R11 + IMULQ R9, R11 + SHRQ $0x36, R11 + MOVL (AX)(R10*4), SI + MOVL (AX)(R11*4), R8 + MOVL DX, (AX)(R10*4) + LEAL 1(DX), R10 + MOVL R10, (AX)(R11*4) + MOVQ DI, R10 + SHRQ $0x10, R10 + SHLQ $0x20, R10 + IMULQ R9, R10 + SHRQ $0x36, R10 + MOVL DX, R9 + SUBL 16(SP), R9 + MOVL 1(BX)(R9*1), R11 + MOVQ DI, R9 + SHRQ $0x08, R9 + CMPL R9, R11 + JNE no_repeat_found_encodeBlockAsm10B + LEAL 1(DX), DI + MOVL 12(SP), R8 + MOVL DI, SI + SUBL 16(SP), SI + JZ repeat_extend_back_end_encodeBlockAsm10B + +repeat_extend_back_loop_encodeBlockAsm10B: + CMPL DI, R8 + JBE repeat_extend_back_end_encodeBlockAsm10B + MOVB -1(BX)(SI*1), R9 + MOVB -1(BX)(DI*1), R10 + CMPB R9, R10 + JNE repeat_extend_back_end_encodeBlockAsm10B + LEAL -1(DI), DI + DECL SI + JNZ repeat_extend_back_loop_encodeBlockAsm10B + +repeat_extend_back_end_encodeBlockAsm10B: + MOVL DI, SI + SUBL 12(SP), SI + LEAQ 3(CX)(SI*1), SI + CMPQ SI, (SP) + JB repeat_dst_size_check_encodeBlockAsm10B + MOVQ $0x00000000, ret+56(FP) + RET + +repeat_dst_size_check_encodeBlockAsm10B: + MOVL 12(SP), SI + CMPL SI, DI + JEQ emit_literal_done_repeat_emit_encodeBlockAsm10B + MOVL DI, R9 + MOVL DI, 12(SP) + LEAQ (BX)(SI*1), R10 + SUBL SI, R9 + LEAL -1(R9), SI + CMPL SI, $0x3c + JB one_byte_repeat_emit_encodeBlockAsm10B + CMPL SI, $0x00000100 + JB two_bytes_repeat_emit_encodeBlockAsm10B + JB three_bytes_repeat_emit_encodeBlockAsm10B + +three_bytes_repeat_emit_encodeBlockAsm10B: + MOVB $0xf4, (CX) + MOVW SI, 1(CX) + ADDQ $0x03, CX + JMP memmove_long_repeat_emit_encodeBlockAsm10B + +two_bytes_repeat_emit_encodeBlockAsm10B: + MOVB $0xf0, (CX) + MOVB SI, 1(CX) + ADDQ $0x02, CX + CMPL SI, $0x40 + JB memmove_repeat_emit_encodeBlockAsm10B + JMP memmove_long_repeat_emit_encodeBlockAsm10B + +one_byte_repeat_emit_encodeBlockAsm10B: + SHLB $0x02, SI + MOVB SI, (CX) + ADDQ $0x01, CX + +memmove_repeat_emit_encodeBlockAsm10B: + LEAQ (CX)(R9*1), SI + + // genMemMoveShort + CMPQ R9, $0x08 + JBE emit_lit_memmove_repeat_emit_encodeBlockAsm10B_memmove_move_8 + CMPQ R9, $0x10 + JBE emit_lit_memmove_repeat_emit_encodeBlockAsm10B_memmove_move_8through16 + CMPQ R9, $0x20 + JBE emit_lit_memmove_repeat_emit_encodeBlockAsm10B_memmove_move_17through32 + JMP emit_lit_memmove_repeat_emit_encodeBlockAsm10B_memmove_move_33through64 + +emit_lit_memmove_repeat_emit_encodeBlockAsm10B_memmove_move_8: + MOVQ (R10), R11 + MOVQ R11, (CX) + JMP memmove_end_copy_repeat_emit_encodeBlockAsm10B + +emit_lit_memmove_repeat_emit_encodeBlockAsm10B_memmove_move_8through16: + MOVQ (R10), R11 + MOVQ -8(R10)(R9*1), R10 + MOVQ R11, (CX) + MOVQ R10, -8(CX)(R9*1) + JMP memmove_end_copy_repeat_emit_encodeBlockAsm10B + +emit_lit_memmove_repeat_emit_encodeBlockAsm10B_memmove_move_17through32: + MOVOU (R10), X0 + MOVOU -16(R10)(R9*1), X1 + MOVOU X0, (CX) + MOVOU X1, -16(CX)(R9*1) + JMP memmove_end_copy_repeat_emit_encodeBlockAsm10B + +emit_lit_memmove_repeat_emit_encodeBlockAsm10B_memmove_move_33through64: + MOVOU (R10), X0 + MOVOU 16(R10), X1 + MOVOU -32(R10)(R9*1), X2 + MOVOU -16(R10)(R9*1), X3 + MOVOU X0, (CX) + MOVOU X1, 16(CX) + MOVOU X2, -32(CX)(R9*1) + MOVOU X3, -16(CX)(R9*1) + +memmove_end_copy_repeat_emit_encodeBlockAsm10B: + MOVQ SI, CX + JMP emit_literal_done_repeat_emit_encodeBlockAsm10B + +memmove_long_repeat_emit_encodeBlockAsm10B: + LEAQ (CX)(R9*1), SI + + // genMemMoveLong + MOVOU (R10), X0 + MOVOU 16(R10), X1 + MOVOU -32(R10)(R9*1), X2 + MOVOU -16(R10)(R9*1), X3 + MOVQ R9, R12 + SHRQ $0x05, R12 + MOVQ CX, R11 + ANDL $0x0000001f, R11 + MOVQ $0x00000040, R13 + SUBQ R11, R13 + DECQ R12 + JA emit_lit_memmove_long_repeat_emit_encodeBlockAsm10Blarge_forward_sse_loop_32 + LEAQ -32(R10)(R13*1), R11 + LEAQ -32(CX)(R13*1), R14 + +emit_lit_memmove_long_repeat_emit_encodeBlockAsm10Blarge_big_loop_back: + MOVOU (R11), X4 + MOVOU 16(R11), X5 + MOVOA X4, (R14) + MOVOA X5, 16(R14) + ADDQ $0x20, R14 + ADDQ $0x20, R11 + ADDQ $0x20, R13 + DECQ R12 + JNA emit_lit_memmove_long_repeat_emit_encodeBlockAsm10Blarge_big_loop_back + +emit_lit_memmove_long_repeat_emit_encodeBlockAsm10Blarge_forward_sse_loop_32: + MOVOU -32(R10)(R13*1), X4 + MOVOU -16(R10)(R13*1), X5 + MOVOA X4, -32(CX)(R13*1) + MOVOA X5, -16(CX)(R13*1) + ADDQ $0x20, R13 + CMPQ R9, R13 + JAE emit_lit_memmove_long_repeat_emit_encodeBlockAsm10Blarge_forward_sse_loop_32 + MOVOU X0, (CX) + MOVOU X1, 16(CX) + MOVOU X2, -32(CX)(R9*1) + MOVOU X3, -16(CX)(R9*1) + MOVQ SI, CX + +emit_literal_done_repeat_emit_encodeBlockAsm10B: + ADDL $0x05, DX + MOVL DX, SI + SUBL 16(SP), SI + MOVQ src_len+32(FP), R9 + SUBL DX, R9 + LEAQ (BX)(DX*1), R10 + LEAQ (BX)(SI*1), SI + + // matchLen + XORL R12, R12 + +matchlen_loopback_16_repeat_extend_encodeBlockAsm10B: + CMPL R9, $0x10 + JB matchlen_match8_repeat_extend_encodeBlockAsm10B + MOVQ (R10)(R12*1), R11 + MOVQ 8(R10)(R12*1), R13 + XORQ (SI)(R12*1), R11 + JNZ matchlen_bsf_8_repeat_extend_encodeBlockAsm10B + XORQ 8(SI)(R12*1), R13 + JNZ matchlen_bsf_16repeat_extend_encodeBlockAsm10B + LEAL -16(R9), R9 + LEAL 16(R12), R12 + JMP matchlen_loopback_16_repeat_extend_encodeBlockAsm10B + +matchlen_bsf_16repeat_extend_encodeBlockAsm10B: +#ifdef GOAMD64_v3 + TZCNTQ R13, R13 + +#else + BSFQ R13, R13 + +#endif + SARQ $0x03, R13 + LEAL 8(R12)(R13*1), R12 + JMP repeat_extend_forward_end_encodeBlockAsm10B + +matchlen_match8_repeat_extend_encodeBlockAsm10B: + CMPL R9, $0x08 + JB matchlen_match4_repeat_extend_encodeBlockAsm10B + MOVQ (R10)(R12*1), R11 + XORQ (SI)(R12*1), R11 + JNZ matchlen_bsf_8_repeat_extend_encodeBlockAsm10B + LEAL -8(R9), R9 + LEAL 8(R12), R12 + JMP matchlen_match4_repeat_extend_encodeBlockAsm10B + +matchlen_bsf_8_repeat_extend_encodeBlockAsm10B: +#ifdef GOAMD64_v3 + TZCNTQ R11, R11 + +#else + BSFQ R11, R11 + +#endif + SARQ $0x03, R11 + LEAL (R12)(R11*1), R12 + JMP repeat_extend_forward_end_encodeBlockAsm10B + +matchlen_match4_repeat_extend_encodeBlockAsm10B: + CMPL R9, $0x04 + JB matchlen_match2_repeat_extend_encodeBlockAsm10B + MOVL (R10)(R12*1), R11 + CMPL (SI)(R12*1), R11 + JNE matchlen_match2_repeat_extend_encodeBlockAsm10B + LEAL -4(R9), R9 + LEAL 4(R12), R12 + +matchlen_match2_repeat_extend_encodeBlockAsm10B: + CMPL R9, $0x01 + JE matchlen_match1_repeat_extend_encodeBlockAsm10B + JB repeat_extend_forward_end_encodeBlockAsm10B + MOVW (R10)(R12*1), R11 + CMPW (SI)(R12*1), R11 + JNE matchlen_match1_repeat_extend_encodeBlockAsm10B + LEAL 2(R12), R12 + SUBL $0x02, R9 + JZ repeat_extend_forward_end_encodeBlockAsm10B + +matchlen_match1_repeat_extend_encodeBlockAsm10B: + MOVB (R10)(R12*1), R11 + CMPB (SI)(R12*1), R11 + JNE repeat_extend_forward_end_encodeBlockAsm10B + LEAL 1(R12), R12 + +repeat_extend_forward_end_encodeBlockAsm10B: + ADDL R12, DX + MOVL DX, SI + SUBL DI, SI + MOVL 16(SP), DI + TESTL R8, R8 + JZ repeat_as_copy_encodeBlockAsm10B + + // emitRepeat + MOVL SI, R8 + LEAL -4(SI), SI + CMPL R8, $0x08 + JBE repeat_two_match_repeat_encodeBlockAsm10B + CMPL R8, $0x0c + JAE cant_repeat_two_offset_match_repeat_encodeBlockAsm10B + CMPL DI, $0x00000800 + JB repeat_two_offset_match_repeat_encodeBlockAsm10B + +cant_repeat_two_offset_match_repeat_encodeBlockAsm10B: + CMPL SI, $0x00000104 + JB repeat_three_match_repeat_encodeBlockAsm10B + LEAL -256(SI), SI + MOVW $0x0019, (CX) + MOVW SI, 2(CX) + ADDQ $0x04, CX + JMP repeat_end_emit_encodeBlockAsm10B + +repeat_three_match_repeat_encodeBlockAsm10B: + LEAL -4(SI), SI + MOVW $0x0015, (CX) + MOVB SI, 2(CX) + ADDQ $0x03, CX + JMP repeat_end_emit_encodeBlockAsm10B + +repeat_two_match_repeat_encodeBlockAsm10B: + SHLL $0x02, SI + ORL $0x01, SI + MOVW SI, (CX) + ADDQ $0x02, CX + JMP repeat_end_emit_encodeBlockAsm10B + +repeat_two_offset_match_repeat_encodeBlockAsm10B: + XORQ R8, R8 + LEAL 1(R8)(SI*4), SI + MOVB DI, 1(CX) + SARL $0x08, DI + SHLL $0x05, DI + ORL DI, SI + MOVB SI, (CX) + ADDQ $0x02, CX + JMP repeat_end_emit_encodeBlockAsm10B + +repeat_as_copy_encodeBlockAsm10B: + // emitCopy + CMPL SI, $0x40 + JBE two_byte_offset_short_repeat_as_copy_encodeBlockAsm10B + CMPL DI, $0x00000800 + JAE long_offset_short_repeat_as_copy_encodeBlockAsm10B + MOVL $0x00000001, R8 + LEAL 16(R8), R8 + MOVB DI, 1(CX) + SHRL $0x08, DI + SHLL $0x05, DI + ORL DI, R8 + MOVB R8, (CX) + ADDQ $0x02, CX + SUBL $0x08, SI + + // emitRepeat + LEAL -4(SI), SI + JMP cant_repeat_two_offset_repeat_as_copy_encodeBlockAsm10B_emit_copy_short_2b + MOVL SI, R8 + LEAL -4(SI), SI + CMPL R8, $0x08 + JBE repeat_two_repeat_as_copy_encodeBlockAsm10B_emit_copy_short_2b + CMPL R8, $0x0c + JAE cant_repeat_two_offset_repeat_as_copy_encodeBlockAsm10B_emit_copy_short_2b + CMPL DI, $0x00000800 + JB repeat_two_offset_repeat_as_copy_encodeBlockAsm10B_emit_copy_short_2b + +cant_repeat_two_offset_repeat_as_copy_encodeBlockAsm10B_emit_copy_short_2b: + CMPL SI, $0x00000104 + JB repeat_three_repeat_as_copy_encodeBlockAsm10B_emit_copy_short_2b + LEAL -256(SI), SI + MOVW $0x0019, (CX) + MOVW SI, 2(CX) + ADDQ $0x04, CX + JMP repeat_end_emit_encodeBlockAsm10B + +repeat_three_repeat_as_copy_encodeBlockAsm10B_emit_copy_short_2b: + LEAL -4(SI), SI + MOVW $0x0015, (CX) + MOVB SI, 2(CX) + ADDQ $0x03, CX + JMP repeat_end_emit_encodeBlockAsm10B + +repeat_two_repeat_as_copy_encodeBlockAsm10B_emit_copy_short_2b: + SHLL $0x02, SI + ORL $0x01, SI + MOVW SI, (CX) + ADDQ $0x02, CX + JMP repeat_end_emit_encodeBlockAsm10B + +repeat_two_offset_repeat_as_copy_encodeBlockAsm10B_emit_copy_short_2b: + XORQ R8, R8 + LEAL 1(R8)(SI*4), SI + MOVB DI, 1(CX) + SARL $0x08, DI + SHLL $0x05, DI + ORL DI, SI + MOVB SI, (CX) + ADDQ $0x02, CX + JMP repeat_end_emit_encodeBlockAsm10B + +long_offset_short_repeat_as_copy_encodeBlockAsm10B: + MOVB $0xee, (CX) + MOVW DI, 1(CX) + LEAL -60(SI), SI + ADDQ $0x03, CX + + // emitRepeat + MOVL SI, R8 + LEAL -4(SI), SI + CMPL R8, $0x08 + JBE repeat_two_repeat_as_copy_encodeBlockAsm10B_emit_copy_short + CMPL R8, $0x0c + JAE cant_repeat_two_offset_repeat_as_copy_encodeBlockAsm10B_emit_copy_short + CMPL DI, $0x00000800 + JB repeat_two_offset_repeat_as_copy_encodeBlockAsm10B_emit_copy_short + +cant_repeat_two_offset_repeat_as_copy_encodeBlockAsm10B_emit_copy_short: + CMPL SI, $0x00000104 + JB repeat_three_repeat_as_copy_encodeBlockAsm10B_emit_copy_short + LEAL -256(SI), SI + MOVW $0x0019, (CX) + MOVW SI, 2(CX) + ADDQ $0x04, CX + JMP repeat_end_emit_encodeBlockAsm10B + +repeat_three_repeat_as_copy_encodeBlockAsm10B_emit_copy_short: + LEAL -4(SI), SI + MOVW $0x0015, (CX) + MOVB SI, 2(CX) + ADDQ $0x03, CX + JMP repeat_end_emit_encodeBlockAsm10B + +repeat_two_repeat_as_copy_encodeBlockAsm10B_emit_copy_short: + SHLL $0x02, SI + ORL $0x01, SI + MOVW SI, (CX) + ADDQ $0x02, CX + JMP repeat_end_emit_encodeBlockAsm10B + +repeat_two_offset_repeat_as_copy_encodeBlockAsm10B_emit_copy_short: + XORQ R8, R8 + LEAL 1(R8)(SI*4), SI + MOVB DI, 1(CX) + SARL $0x08, DI + SHLL $0x05, DI + ORL DI, SI + MOVB SI, (CX) + ADDQ $0x02, CX + JMP repeat_end_emit_encodeBlockAsm10B + +two_byte_offset_short_repeat_as_copy_encodeBlockAsm10B: + MOVL SI, R8 + SHLL $0x02, R8 + CMPL SI, $0x0c + JAE emit_copy_three_repeat_as_copy_encodeBlockAsm10B + CMPL DI, $0x00000800 + JAE emit_copy_three_repeat_as_copy_encodeBlockAsm10B + LEAL -15(R8), R8 + MOVB DI, 1(CX) + SHRL $0x08, DI + SHLL $0x05, DI + ORL DI, R8 + MOVB R8, (CX) + ADDQ $0x02, CX + JMP repeat_end_emit_encodeBlockAsm10B + +emit_copy_three_repeat_as_copy_encodeBlockAsm10B: + LEAL -2(R8), R8 + MOVB R8, (CX) + MOVW DI, 1(CX) + ADDQ $0x03, CX + +repeat_end_emit_encodeBlockAsm10B: + MOVL DX, 12(SP) + JMP search_loop_encodeBlockAsm10B + +no_repeat_found_encodeBlockAsm10B: + CMPL (BX)(SI*1), DI + JEQ candidate_match_encodeBlockAsm10B + SHRQ $0x08, DI + MOVL (AX)(R10*4), SI + LEAL 2(DX), R9 + CMPL (BX)(R8*1), DI + JEQ candidate2_match_encodeBlockAsm10B + MOVL R9, (AX)(R10*4) + SHRQ $0x08, DI + CMPL (BX)(SI*1), DI + JEQ candidate3_match_encodeBlockAsm10B + MOVL 20(SP), DX + JMP search_loop_encodeBlockAsm10B + +candidate3_match_encodeBlockAsm10B: + ADDL $0x02, DX + JMP candidate_match_encodeBlockAsm10B + +candidate2_match_encodeBlockAsm10B: + MOVL R9, (AX)(R10*4) + INCL DX + MOVL R8, SI + +candidate_match_encodeBlockAsm10B: + MOVL 12(SP), DI + TESTL SI, SI + JZ match_extend_back_end_encodeBlockAsm10B + +match_extend_back_loop_encodeBlockAsm10B: + CMPL DX, DI + JBE match_extend_back_end_encodeBlockAsm10B + MOVB -1(BX)(SI*1), R8 + MOVB -1(BX)(DX*1), R9 + CMPB R8, R9 + JNE match_extend_back_end_encodeBlockAsm10B + LEAL -1(DX), DX + DECL SI + JZ match_extend_back_end_encodeBlockAsm10B + JMP match_extend_back_loop_encodeBlockAsm10B + +match_extend_back_end_encodeBlockAsm10B: + MOVL DX, DI + SUBL 12(SP), DI + LEAQ 3(CX)(DI*1), DI + CMPQ DI, (SP) + JB match_dst_size_check_encodeBlockAsm10B + MOVQ $0x00000000, ret+56(FP) + RET + +match_dst_size_check_encodeBlockAsm10B: + MOVL DX, DI + MOVL 12(SP), R8 + CMPL R8, DI + JEQ emit_literal_done_match_emit_encodeBlockAsm10B + MOVL DI, R9 + MOVL DI, 12(SP) + LEAQ (BX)(R8*1), DI + SUBL R8, R9 + LEAL -1(R9), R8 + CMPL R8, $0x3c + JB one_byte_match_emit_encodeBlockAsm10B + CMPL R8, $0x00000100 + JB two_bytes_match_emit_encodeBlockAsm10B + JB three_bytes_match_emit_encodeBlockAsm10B + +three_bytes_match_emit_encodeBlockAsm10B: + MOVB $0xf4, (CX) + MOVW R8, 1(CX) + ADDQ $0x03, CX + JMP memmove_long_match_emit_encodeBlockAsm10B + +two_bytes_match_emit_encodeBlockAsm10B: + MOVB $0xf0, (CX) + MOVB R8, 1(CX) + ADDQ $0x02, CX + CMPL R8, $0x40 + JB memmove_match_emit_encodeBlockAsm10B + JMP memmove_long_match_emit_encodeBlockAsm10B + +one_byte_match_emit_encodeBlockAsm10B: + SHLB $0x02, R8 + MOVB R8, (CX) + ADDQ $0x01, CX + +memmove_match_emit_encodeBlockAsm10B: + LEAQ (CX)(R9*1), R8 + + // genMemMoveShort + CMPQ R9, $0x08 + JBE emit_lit_memmove_match_emit_encodeBlockAsm10B_memmove_move_8 + CMPQ R9, $0x10 + JBE emit_lit_memmove_match_emit_encodeBlockAsm10B_memmove_move_8through16 + CMPQ R9, $0x20 + JBE emit_lit_memmove_match_emit_encodeBlockAsm10B_memmove_move_17through32 + JMP emit_lit_memmove_match_emit_encodeBlockAsm10B_memmove_move_33through64 + +emit_lit_memmove_match_emit_encodeBlockAsm10B_memmove_move_8: + MOVQ (DI), R10 + MOVQ R10, (CX) + JMP memmove_end_copy_match_emit_encodeBlockAsm10B + +emit_lit_memmove_match_emit_encodeBlockAsm10B_memmove_move_8through16: + MOVQ (DI), R10 + MOVQ -8(DI)(R9*1), DI + MOVQ R10, (CX) + MOVQ DI, -8(CX)(R9*1) + JMP memmove_end_copy_match_emit_encodeBlockAsm10B + +emit_lit_memmove_match_emit_encodeBlockAsm10B_memmove_move_17through32: + MOVOU (DI), X0 + MOVOU -16(DI)(R9*1), X1 + MOVOU X0, (CX) + MOVOU X1, -16(CX)(R9*1) + JMP memmove_end_copy_match_emit_encodeBlockAsm10B + +emit_lit_memmove_match_emit_encodeBlockAsm10B_memmove_move_33through64: + MOVOU (DI), X0 + MOVOU 16(DI), X1 + MOVOU -32(DI)(R9*1), X2 + MOVOU -16(DI)(R9*1), X3 + MOVOU X0, (CX) + MOVOU X1, 16(CX) + MOVOU X2, -32(CX)(R9*1) + MOVOU X3, -16(CX)(R9*1) + +memmove_end_copy_match_emit_encodeBlockAsm10B: + MOVQ R8, CX + JMP emit_literal_done_match_emit_encodeBlockAsm10B + +memmove_long_match_emit_encodeBlockAsm10B: + LEAQ (CX)(R9*1), R8 + + // genMemMoveLong + MOVOU (DI), X0 + MOVOU 16(DI), X1 + MOVOU -32(DI)(R9*1), X2 + MOVOU -16(DI)(R9*1), X3 + MOVQ R9, R11 + SHRQ $0x05, R11 + MOVQ CX, R10 + ANDL $0x0000001f, R10 + MOVQ $0x00000040, R12 + SUBQ R10, R12 + DECQ R11 + JA emit_lit_memmove_long_match_emit_encodeBlockAsm10Blarge_forward_sse_loop_32 + LEAQ -32(DI)(R12*1), R10 + LEAQ -32(CX)(R12*1), R13 + +emit_lit_memmove_long_match_emit_encodeBlockAsm10Blarge_big_loop_back: + MOVOU (R10), X4 + MOVOU 16(R10), X5 + MOVOA X4, (R13) + MOVOA X5, 16(R13) + ADDQ $0x20, R13 + ADDQ $0x20, R10 + ADDQ $0x20, R12 + DECQ R11 + JNA emit_lit_memmove_long_match_emit_encodeBlockAsm10Blarge_big_loop_back + +emit_lit_memmove_long_match_emit_encodeBlockAsm10Blarge_forward_sse_loop_32: + MOVOU -32(DI)(R12*1), X4 + MOVOU -16(DI)(R12*1), X5 + MOVOA X4, -32(CX)(R12*1) + MOVOA X5, -16(CX)(R12*1) + ADDQ $0x20, R12 + CMPQ R9, R12 + JAE emit_lit_memmove_long_match_emit_encodeBlockAsm10Blarge_forward_sse_loop_32 + MOVOU X0, (CX) + MOVOU X1, 16(CX) + MOVOU X2, -32(CX)(R9*1) + MOVOU X3, -16(CX)(R9*1) + MOVQ R8, CX + +emit_literal_done_match_emit_encodeBlockAsm10B: +match_nolit_loop_encodeBlockAsm10B: + MOVL DX, DI + SUBL SI, DI + MOVL DI, 16(SP) + ADDL $0x04, DX + ADDL $0x04, SI + MOVQ src_len+32(FP), DI + SUBL DX, DI + LEAQ (BX)(DX*1), R8 + LEAQ (BX)(SI*1), SI + + // matchLen + XORL R10, R10 + +matchlen_loopback_16_match_nolit_encodeBlockAsm10B: + CMPL DI, $0x10 + JB matchlen_match8_match_nolit_encodeBlockAsm10B + MOVQ (R8)(R10*1), R9 + MOVQ 8(R8)(R10*1), R11 + XORQ (SI)(R10*1), R9 + JNZ matchlen_bsf_8_match_nolit_encodeBlockAsm10B + XORQ 8(SI)(R10*1), R11 + JNZ matchlen_bsf_16match_nolit_encodeBlockAsm10B + LEAL -16(DI), DI + LEAL 16(R10), R10 + JMP matchlen_loopback_16_match_nolit_encodeBlockAsm10B + +matchlen_bsf_16match_nolit_encodeBlockAsm10B: +#ifdef GOAMD64_v3 + TZCNTQ R11, R11 + +#else + BSFQ R11, R11 + +#endif + SARQ $0x03, R11 + LEAL 8(R10)(R11*1), R10 + JMP match_nolit_end_encodeBlockAsm10B + +matchlen_match8_match_nolit_encodeBlockAsm10B: + CMPL DI, $0x08 + JB matchlen_match4_match_nolit_encodeBlockAsm10B + MOVQ (R8)(R10*1), R9 + XORQ (SI)(R10*1), R9 + JNZ matchlen_bsf_8_match_nolit_encodeBlockAsm10B + LEAL -8(DI), DI + LEAL 8(R10), R10 + JMP matchlen_match4_match_nolit_encodeBlockAsm10B + +matchlen_bsf_8_match_nolit_encodeBlockAsm10B: +#ifdef GOAMD64_v3 + TZCNTQ R9, R9 + +#else + BSFQ R9, R9 + +#endif + SARQ $0x03, R9 + LEAL (R10)(R9*1), R10 + JMP match_nolit_end_encodeBlockAsm10B + +matchlen_match4_match_nolit_encodeBlockAsm10B: + CMPL DI, $0x04 + JB matchlen_match2_match_nolit_encodeBlockAsm10B + MOVL (R8)(R10*1), R9 + CMPL (SI)(R10*1), R9 + JNE matchlen_match2_match_nolit_encodeBlockAsm10B + LEAL -4(DI), DI + LEAL 4(R10), R10 + +matchlen_match2_match_nolit_encodeBlockAsm10B: + CMPL DI, $0x01 + JE matchlen_match1_match_nolit_encodeBlockAsm10B + JB match_nolit_end_encodeBlockAsm10B + MOVW (R8)(R10*1), R9 + CMPW (SI)(R10*1), R9 + JNE matchlen_match1_match_nolit_encodeBlockAsm10B + LEAL 2(R10), R10 + SUBL $0x02, DI + JZ match_nolit_end_encodeBlockAsm10B + +matchlen_match1_match_nolit_encodeBlockAsm10B: + MOVB (R8)(R10*1), R9 + CMPB (SI)(R10*1), R9 + JNE match_nolit_end_encodeBlockAsm10B + LEAL 1(R10), R10 + +match_nolit_end_encodeBlockAsm10B: + ADDL R10, DX + MOVL 16(SP), SI + ADDL $0x04, R10 + MOVL DX, 12(SP) + + // emitCopy + CMPL R10, $0x40 + JBE two_byte_offset_short_match_nolit_encodeBlockAsm10B + CMPL SI, $0x00000800 + JAE long_offset_short_match_nolit_encodeBlockAsm10B + MOVL $0x00000001, DI + LEAL 16(DI), DI + MOVB SI, 1(CX) + SHRL $0x08, SI + SHLL $0x05, SI + ORL SI, DI + MOVB DI, (CX) + ADDQ $0x02, CX + SUBL $0x08, R10 + + // emitRepeat + LEAL -4(R10), R10 + JMP cant_repeat_two_offset_match_nolit_encodeBlockAsm10B_emit_copy_short_2b + MOVL R10, DI + LEAL -4(R10), R10 + CMPL DI, $0x08 + JBE repeat_two_match_nolit_encodeBlockAsm10B_emit_copy_short_2b + CMPL DI, $0x0c + JAE cant_repeat_two_offset_match_nolit_encodeBlockAsm10B_emit_copy_short_2b + CMPL SI, $0x00000800 + JB repeat_two_offset_match_nolit_encodeBlockAsm10B_emit_copy_short_2b + +cant_repeat_two_offset_match_nolit_encodeBlockAsm10B_emit_copy_short_2b: + CMPL R10, $0x00000104 + JB repeat_three_match_nolit_encodeBlockAsm10B_emit_copy_short_2b + LEAL -256(R10), R10 + MOVW $0x0019, (CX) + MOVW R10, 2(CX) + ADDQ $0x04, CX + JMP match_nolit_emitcopy_end_encodeBlockAsm10B + +repeat_three_match_nolit_encodeBlockAsm10B_emit_copy_short_2b: + LEAL -4(R10), R10 + MOVW $0x0015, (CX) + MOVB R10, 2(CX) + ADDQ $0x03, CX + JMP match_nolit_emitcopy_end_encodeBlockAsm10B + +repeat_two_match_nolit_encodeBlockAsm10B_emit_copy_short_2b: + SHLL $0x02, R10 + ORL $0x01, R10 + MOVW R10, (CX) + ADDQ $0x02, CX + JMP match_nolit_emitcopy_end_encodeBlockAsm10B + +repeat_two_offset_match_nolit_encodeBlockAsm10B_emit_copy_short_2b: + XORQ DI, DI + LEAL 1(DI)(R10*4), R10 + MOVB SI, 1(CX) + SARL $0x08, SI + SHLL $0x05, SI + ORL SI, R10 + MOVB R10, (CX) + ADDQ $0x02, CX + JMP match_nolit_emitcopy_end_encodeBlockAsm10B + +long_offset_short_match_nolit_encodeBlockAsm10B: + MOVB $0xee, (CX) + MOVW SI, 1(CX) + LEAL -60(R10), R10 + ADDQ $0x03, CX + + // emitRepeat + MOVL R10, DI + LEAL -4(R10), R10 + CMPL DI, $0x08 + JBE repeat_two_match_nolit_encodeBlockAsm10B_emit_copy_short + CMPL DI, $0x0c + JAE cant_repeat_two_offset_match_nolit_encodeBlockAsm10B_emit_copy_short + CMPL SI, $0x00000800 + JB repeat_two_offset_match_nolit_encodeBlockAsm10B_emit_copy_short + +cant_repeat_two_offset_match_nolit_encodeBlockAsm10B_emit_copy_short: + CMPL R10, $0x00000104 + JB repeat_three_match_nolit_encodeBlockAsm10B_emit_copy_short + LEAL -256(R10), R10 + MOVW $0x0019, (CX) + MOVW R10, 2(CX) + ADDQ $0x04, CX + JMP match_nolit_emitcopy_end_encodeBlockAsm10B + +repeat_three_match_nolit_encodeBlockAsm10B_emit_copy_short: + LEAL -4(R10), R10 + MOVW $0x0015, (CX) + MOVB R10, 2(CX) + ADDQ $0x03, CX + JMP match_nolit_emitcopy_end_encodeBlockAsm10B + +repeat_two_match_nolit_encodeBlockAsm10B_emit_copy_short: + SHLL $0x02, R10 + ORL $0x01, R10 + MOVW R10, (CX) + ADDQ $0x02, CX + JMP match_nolit_emitcopy_end_encodeBlockAsm10B + +repeat_two_offset_match_nolit_encodeBlockAsm10B_emit_copy_short: + XORQ DI, DI + LEAL 1(DI)(R10*4), R10 + MOVB SI, 1(CX) + SARL $0x08, SI + SHLL $0x05, SI + ORL SI, R10 + MOVB R10, (CX) + ADDQ $0x02, CX + JMP match_nolit_emitcopy_end_encodeBlockAsm10B + +two_byte_offset_short_match_nolit_encodeBlockAsm10B: + MOVL R10, DI + SHLL $0x02, DI + CMPL R10, $0x0c + JAE emit_copy_three_match_nolit_encodeBlockAsm10B + CMPL SI, $0x00000800 + JAE emit_copy_three_match_nolit_encodeBlockAsm10B + LEAL -15(DI), DI + MOVB SI, 1(CX) + SHRL $0x08, SI + SHLL $0x05, SI + ORL SI, DI + MOVB DI, (CX) + ADDQ $0x02, CX + JMP match_nolit_emitcopy_end_encodeBlockAsm10B + +emit_copy_three_match_nolit_encodeBlockAsm10B: + LEAL -2(DI), DI + MOVB DI, (CX) + MOVW SI, 1(CX) + ADDQ $0x03, CX + +match_nolit_emitcopy_end_encodeBlockAsm10B: + CMPL DX, 8(SP) + JAE emit_remainder_encodeBlockAsm10B + MOVQ -2(BX)(DX*1), DI + CMPQ CX, (SP) + JB match_nolit_dst_ok_encodeBlockAsm10B + MOVQ $0x00000000, ret+56(FP) + RET + +match_nolit_dst_ok_encodeBlockAsm10B: + MOVQ $0x9e3779b1, R9 + MOVQ DI, R8 + SHRQ $0x10, DI + MOVQ DI, SI + SHLQ $0x20, R8 + IMULQ R9, R8 + SHRQ $0x36, R8 + SHLQ $0x20, SI + IMULQ R9, SI + SHRQ $0x36, SI + LEAL -2(DX), R9 + LEAQ (AX)(SI*4), R10 + MOVL (R10), SI + MOVL R9, (AX)(R8*4) + MOVL DX, (R10) + CMPL (BX)(SI*1), DI + JEQ match_nolit_loop_encodeBlockAsm10B + INCL DX + JMP search_loop_encodeBlockAsm10B + +emit_remainder_encodeBlockAsm10B: + MOVQ src_len+32(FP), AX + SUBL 12(SP), AX + LEAQ 3(CX)(AX*1), AX + CMPQ AX, (SP) + JB emit_remainder_ok_encodeBlockAsm10B + MOVQ $0x00000000, ret+56(FP) + RET + +emit_remainder_ok_encodeBlockAsm10B: + MOVQ src_len+32(FP), AX + MOVL 12(SP), DX + CMPL DX, AX + JEQ emit_literal_done_emit_remainder_encodeBlockAsm10B + MOVL AX, SI + MOVL AX, 12(SP) + LEAQ (BX)(DX*1), AX + SUBL DX, SI + LEAL -1(SI), DX + CMPL DX, $0x3c + JB one_byte_emit_remainder_encodeBlockAsm10B + CMPL DX, $0x00000100 + JB two_bytes_emit_remainder_encodeBlockAsm10B + JB three_bytes_emit_remainder_encodeBlockAsm10B + +three_bytes_emit_remainder_encodeBlockAsm10B: + MOVB $0xf4, (CX) + MOVW DX, 1(CX) + ADDQ $0x03, CX + JMP memmove_long_emit_remainder_encodeBlockAsm10B + +two_bytes_emit_remainder_encodeBlockAsm10B: + MOVB $0xf0, (CX) + MOVB DL, 1(CX) + ADDQ $0x02, CX + CMPL DX, $0x40 + JB memmove_emit_remainder_encodeBlockAsm10B + JMP memmove_long_emit_remainder_encodeBlockAsm10B + +one_byte_emit_remainder_encodeBlockAsm10B: + SHLB $0x02, DL + MOVB DL, (CX) + ADDQ $0x01, CX + +memmove_emit_remainder_encodeBlockAsm10B: + LEAQ (CX)(SI*1), DX + MOVL SI, BX + + // genMemMoveShort + CMPQ BX, $0x03 + JB emit_lit_memmove_emit_remainder_encodeBlockAsm10B_memmove_move_1or2 + JE emit_lit_memmove_emit_remainder_encodeBlockAsm10B_memmove_move_3 + CMPQ BX, $0x08 + JB emit_lit_memmove_emit_remainder_encodeBlockAsm10B_memmove_move_4through7 + CMPQ BX, $0x10 + JBE emit_lit_memmove_emit_remainder_encodeBlockAsm10B_memmove_move_8through16 + CMPQ BX, $0x20 + JBE emit_lit_memmove_emit_remainder_encodeBlockAsm10B_memmove_move_17through32 + JMP emit_lit_memmove_emit_remainder_encodeBlockAsm10B_memmove_move_33through64 + +emit_lit_memmove_emit_remainder_encodeBlockAsm10B_memmove_move_1or2: + MOVB (AX), SI + MOVB -1(AX)(BX*1), AL + MOVB SI, (CX) + MOVB AL, -1(CX)(BX*1) + JMP memmove_end_copy_emit_remainder_encodeBlockAsm10B + +emit_lit_memmove_emit_remainder_encodeBlockAsm10B_memmove_move_3: + MOVW (AX), SI + MOVB 2(AX), AL + MOVW SI, (CX) + MOVB AL, 2(CX) + JMP memmove_end_copy_emit_remainder_encodeBlockAsm10B + +emit_lit_memmove_emit_remainder_encodeBlockAsm10B_memmove_move_4through7: + MOVL (AX), SI + MOVL -4(AX)(BX*1), AX + MOVL SI, (CX) + MOVL AX, -4(CX)(BX*1) + JMP memmove_end_copy_emit_remainder_encodeBlockAsm10B + +emit_lit_memmove_emit_remainder_encodeBlockAsm10B_memmove_move_8through16: + MOVQ (AX), SI + MOVQ -8(AX)(BX*1), AX + MOVQ SI, (CX) + MOVQ AX, -8(CX)(BX*1) + JMP memmove_end_copy_emit_remainder_encodeBlockAsm10B + +emit_lit_memmove_emit_remainder_encodeBlockAsm10B_memmove_move_17through32: + MOVOU (AX), X0 + MOVOU -16(AX)(BX*1), X1 + MOVOU X0, (CX) + MOVOU X1, -16(CX)(BX*1) + JMP memmove_end_copy_emit_remainder_encodeBlockAsm10B + +emit_lit_memmove_emit_remainder_encodeBlockAsm10B_memmove_move_33through64: + MOVOU (AX), X0 + MOVOU 16(AX), X1 + MOVOU -32(AX)(BX*1), X2 + MOVOU -16(AX)(BX*1), X3 + MOVOU X0, (CX) + MOVOU X1, 16(CX) + MOVOU X2, -32(CX)(BX*1) + MOVOU X3, -16(CX)(BX*1) + +memmove_end_copy_emit_remainder_encodeBlockAsm10B: + MOVQ DX, CX + JMP emit_literal_done_emit_remainder_encodeBlockAsm10B + +memmove_long_emit_remainder_encodeBlockAsm10B: + LEAQ (CX)(SI*1), DX + MOVL SI, BX + + // genMemMoveLong + MOVOU (AX), X0 + MOVOU 16(AX), X1 + MOVOU -32(AX)(BX*1), X2 + MOVOU -16(AX)(BX*1), X3 + MOVQ BX, DI + SHRQ $0x05, DI + MOVQ CX, SI + ANDL $0x0000001f, SI + MOVQ $0x00000040, R8 + SUBQ SI, R8 + DECQ DI + JA emit_lit_memmove_long_emit_remainder_encodeBlockAsm10Blarge_forward_sse_loop_32 + LEAQ -32(AX)(R8*1), SI + LEAQ -32(CX)(R8*1), R9 + +emit_lit_memmove_long_emit_remainder_encodeBlockAsm10Blarge_big_loop_back: + MOVOU (SI), X4 + MOVOU 16(SI), X5 + MOVOA X4, (R9) + MOVOA X5, 16(R9) + ADDQ $0x20, R9 + ADDQ $0x20, SI + ADDQ $0x20, R8 + DECQ DI + JNA emit_lit_memmove_long_emit_remainder_encodeBlockAsm10Blarge_big_loop_back + +emit_lit_memmove_long_emit_remainder_encodeBlockAsm10Blarge_forward_sse_loop_32: + MOVOU -32(AX)(R8*1), X4 + MOVOU -16(AX)(R8*1), X5 + MOVOA X4, -32(CX)(R8*1) + MOVOA X5, -16(CX)(R8*1) + ADDQ $0x20, R8 + CMPQ BX, R8 + JAE emit_lit_memmove_long_emit_remainder_encodeBlockAsm10Blarge_forward_sse_loop_32 + MOVOU X0, (CX) + MOVOU X1, 16(CX) + MOVOU X2, -32(CX)(BX*1) + MOVOU X3, -16(CX)(BX*1) + MOVQ DX, CX + +emit_literal_done_emit_remainder_encodeBlockAsm10B: + MOVQ dst_base+0(FP), AX + SUBQ AX, CX + MOVQ CX, ret+56(FP) + RET + +// func encodeBlockAsm8B(dst []byte, src []byte, tmp *[1024]byte) int +// Requires: BMI, SSE2 +TEXT ·encodeBlockAsm8B(SB), $24-64 + MOVQ tmp+48(FP), AX + MOVQ dst_base+0(FP), CX + MOVQ $0x00000008, DX + MOVQ AX, BX + PXOR X0, X0 + +zero_loop_encodeBlockAsm8B: + MOVOU X0, (BX) + MOVOU X0, 16(BX) + MOVOU X0, 32(BX) + MOVOU X0, 48(BX) + MOVOU X0, 64(BX) + MOVOU X0, 80(BX) + MOVOU X0, 96(BX) + MOVOU X0, 112(BX) + ADDQ $0x80, BX + DECQ DX + JNZ zero_loop_encodeBlockAsm8B + MOVL $0x00000000, 12(SP) + MOVQ src_len+32(FP), DX + LEAQ -9(DX), BX + LEAQ -8(DX), SI + MOVL SI, 8(SP) + SHRQ $0x05, DX + SUBL DX, BX + LEAQ (CX)(BX*1), BX + MOVQ BX, (SP) + MOVL $0x00000001, DX + MOVL DX, 16(SP) + MOVQ src_base+24(FP), BX + +search_loop_encodeBlockAsm8B: + MOVL DX, SI + SUBL 12(SP), SI + SHRL $0x04, SI + LEAL 4(DX)(SI*1), SI + CMPL SI, 8(SP) + JAE emit_remainder_encodeBlockAsm8B + MOVQ (BX)(DX*1), DI + MOVL SI, 20(SP) + MOVQ $0x9e3779b1, R9 + MOVQ DI, R10 + MOVQ DI, R11 + SHRQ $0x08, R11 + SHLQ $0x20, R10 + IMULQ R9, R10 + SHRQ $0x38, R10 + SHLQ $0x20, R11 + IMULQ R9, R11 + SHRQ $0x38, R11 + MOVL (AX)(R10*4), SI + MOVL (AX)(R11*4), R8 + MOVL DX, (AX)(R10*4) + LEAL 1(DX), R10 + MOVL R10, (AX)(R11*4) + MOVQ DI, R10 + SHRQ $0x10, R10 + SHLQ $0x20, R10 + IMULQ R9, R10 + SHRQ $0x38, R10 + MOVL DX, R9 + SUBL 16(SP), R9 + MOVL 1(BX)(R9*1), R11 + MOVQ DI, R9 + SHRQ $0x08, R9 + CMPL R9, R11 + JNE no_repeat_found_encodeBlockAsm8B + LEAL 1(DX), DI + MOVL 12(SP), R8 + MOVL DI, SI + SUBL 16(SP), SI + JZ repeat_extend_back_end_encodeBlockAsm8B + +repeat_extend_back_loop_encodeBlockAsm8B: + CMPL DI, R8 + JBE repeat_extend_back_end_encodeBlockAsm8B + MOVB -1(BX)(SI*1), R9 + MOVB -1(BX)(DI*1), R10 + CMPB R9, R10 + JNE repeat_extend_back_end_encodeBlockAsm8B + LEAL -1(DI), DI + DECL SI + JNZ repeat_extend_back_loop_encodeBlockAsm8B + +repeat_extend_back_end_encodeBlockAsm8B: + MOVL DI, SI + SUBL 12(SP), SI + LEAQ 3(CX)(SI*1), SI + CMPQ SI, (SP) + JB repeat_dst_size_check_encodeBlockAsm8B + MOVQ $0x00000000, ret+56(FP) + RET + +repeat_dst_size_check_encodeBlockAsm8B: + MOVL 12(SP), SI + CMPL SI, DI + JEQ emit_literal_done_repeat_emit_encodeBlockAsm8B + MOVL DI, R9 + MOVL DI, 12(SP) + LEAQ (BX)(SI*1), R10 + SUBL SI, R9 + LEAL -1(R9), SI + CMPL SI, $0x3c + JB one_byte_repeat_emit_encodeBlockAsm8B + CMPL SI, $0x00000100 + JB two_bytes_repeat_emit_encodeBlockAsm8B + JB three_bytes_repeat_emit_encodeBlockAsm8B + +three_bytes_repeat_emit_encodeBlockAsm8B: + MOVB $0xf4, (CX) + MOVW SI, 1(CX) + ADDQ $0x03, CX + JMP memmove_long_repeat_emit_encodeBlockAsm8B + +two_bytes_repeat_emit_encodeBlockAsm8B: + MOVB $0xf0, (CX) + MOVB SI, 1(CX) + ADDQ $0x02, CX + CMPL SI, $0x40 + JB memmove_repeat_emit_encodeBlockAsm8B + JMP memmove_long_repeat_emit_encodeBlockAsm8B + +one_byte_repeat_emit_encodeBlockAsm8B: + SHLB $0x02, SI + MOVB SI, (CX) + ADDQ $0x01, CX + +memmove_repeat_emit_encodeBlockAsm8B: + LEAQ (CX)(R9*1), SI + + // genMemMoveShort + CMPQ R9, $0x08 + JBE emit_lit_memmove_repeat_emit_encodeBlockAsm8B_memmove_move_8 + CMPQ R9, $0x10 + JBE emit_lit_memmove_repeat_emit_encodeBlockAsm8B_memmove_move_8through16 + CMPQ R9, $0x20 + JBE emit_lit_memmove_repeat_emit_encodeBlockAsm8B_memmove_move_17through32 + JMP emit_lit_memmove_repeat_emit_encodeBlockAsm8B_memmove_move_33through64 + +emit_lit_memmove_repeat_emit_encodeBlockAsm8B_memmove_move_8: + MOVQ (R10), R11 + MOVQ R11, (CX) + JMP memmove_end_copy_repeat_emit_encodeBlockAsm8B + +emit_lit_memmove_repeat_emit_encodeBlockAsm8B_memmove_move_8through16: + MOVQ (R10), R11 + MOVQ -8(R10)(R9*1), R10 + MOVQ R11, (CX) + MOVQ R10, -8(CX)(R9*1) + JMP memmove_end_copy_repeat_emit_encodeBlockAsm8B + +emit_lit_memmove_repeat_emit_encodeBlockAsm8B_memmove_move_17through32: + MOVOU (R10), X0 + MOVOU -16(R10)(R9*1), X1 + MOVOU X0, (CX) + MOVOU X1, -16(CX)(R9*1) + JMP memmove_end_copy_repeat_emit_encodeBlockAsm8B + +emit_lit_memmove_repeat_emit_encodeBlockAsm8B_memmove_move_33through64: + MOVOU (R10), X0 + MOVOU 16(R10), X1 + MOVOU -32(R10)(R9*1), X2 + MOVOU -16(R10)(R9*1), X3 + MOVOU X0, (CX) + MOVOU X1, 16(CX) + MOVOU X2, -32(CX)(R9*1) + MOVOU X3, -16(CX)(R9*1) + +memmove_end_copy_repeat_emit_encodeBlockAsm8B: + MOVQ SI, CX + JMP emit_literal_done_repeat_emit_encodeBlockAsm8B + +memmove_long_repeat_emit_encodeBlockAsm8B: + LEAQ (CX)(R9*1), SI + + // genMemMoveLong + MOVOU (R10), X0 + MOVOU 16(R10), X1 + MOVOU -32(R10)(R9*1), X2 + MOVOU -16(R10)(R9*1), X3 + MOVQ R9, R12 + SHRQ $0x05, R12 + MOVQ CX, R11 + ANDL $0x0000001f, R11 + MOVQ $0x00000040, R13 + SUBQ R11, R13 + DECQ R12 + JA emit_lit_memmove_long_repeat_emit_encodeBlockAsm8Blarge_forward_sse_loop_32 + LEAQ -32(R10)(R13*1), R11 + LEAQ -32(CX)(R13*1), R14 + +emit_lit_memmove_long_repeat_emit_encodeBlockAsm8Blarge_big_loop_back: + MOVOU (R11), X4 + MOVOU 16(R11), X5 + MOVOA X4, (R14) + MOVOA X5, 16(R14) + ADDQ $0x20, R14 + ADDQ $0x20, R11 + ADDQ $0x20, R13 + DECQ R12 + JNA emit_lit_memmove_long_repeat_emit_encodeBlockAsm8Blarge_big_loop_back + +emit_lit_memmove_long_repeat_emit_encodeBlockAsm8Blarge_forward_sse_loop_32: + MOVOU -32(R10)(R13*1), X4 + MOVOU -16(R10)(R13*1), X5 + MOVOA X4, -32(CX)(R13*1) + MOVOA X5, -16(CX)(R13*1) + ADDQ $0x20, R13 + CMPQ R9, R13 + JAE emit_lit_memmove_long_repeat_emit_encodeBlockAsm8Blarge_forward_sse_loop_32 + MOVOU X0, (CX) + MOVOU X1, 16(CX) + MOVOU X2, -32(CX)(R9*1) + MOVOU X3, -16(CX)(R9*1) + MOVQ SI, CX + +emit_literal_done_repeat_emit_encodeBlockAsm8B: + ADDL $0x05, DX + MOVL DX, SI + SUBL 16(SP), SI + MOVQ src_len+32(FP), R9 + SUBL DX, R9 + LEAQ (BX)(DX*1), R10 + LEAQ (BX)(SI*1), SI + + // matchLen + XORL R12, R12 + +matchlen_loopback_16_repeat_extend_encodeBlockAsm8B: + CMPL R9, $0x10 + JB matchlen_match8_repeat_extend_encodeBlockAsm8B + MOVQ (R10)(R12*1), R11 + MOVQ 8(R10)(R12*1), R13 + XORQ (SI)(R12*1), R11 + JNZ matchlen_bsf_8_repeat_extend_encodeBlockAsm8B + XORQ 8(SI)(R12*1), R13 + JNZ matchlen_bsf_16repeat_extend_encodeBlockAsm8B + LEAL -16(R9), R9 + LEAL 16(R12), R12 + JMP matchlen_loopback_16_repeat_extend_encodeBlockAsm8B + +matchlen_bsf_16repeat_extend_encodeBlockAsm8B: +#ifdef GOAMD64_v3 + TZCNTQ R13, R13 + +#else + BSFQ R13, R13 + +#endif + SARQ $0x03, R13 + LEAL 8(R12)(R13*1), R12 + JMP repeat_extend_forward_end_encodeBlockAsm8B + +matchlen_match8_repeat_extend_encodeBlockAsm8B: + CMPL R9, $0x08 + JB matchlen_match4_repeat_extend_encodeBlockAsm8B + MOVQ (R10)(R12*1), R11 + XORQ (SI)(R12*1), R11 + JNZ matchlen_bsf_8_repeat_extend_encodeBlockAsm8B + LEAL -8(R9), R9 + LEAL 8(R12), R12 + JMP matchlen_match4_repeat_extend_encodeBlockAsm8B + +matchlen_bsf_8_repeat_extend_encodeBlockAsm8B: +#ifdef GOAMD64_v3 + TZCNTQ R11, R11 + +#else + BSFQ R11, R11 + +#endif + SARQ $0x03, R11 + LEAL (R12)(R11*1), R12 + JMP repeat_extend_forward_end_encodeBlockAsm8B + +matchlen_match4_repeat_extend_encodeBlockAsm8B: + CMPL R9, $0x04 + JB matchlen_match2_repeat_extend_encodeBlockAsm8B + MOVL (R10)(R12*1), R11 + CMPL (SI)(R12*1), R11 + JNE matchlen_match2_repeat_extend_encodeBlockAsm8B + LEAL -4(R9), R9 + LEAL 4(R12), R12 + +matchlen_match2_repeat_extend_encodeBlockAsm8B: + CMPL R9, $0x01 + JE matchlen_match1_repeat_extend_encodeBlockAsm8B + JB repeat_extend_forward_end_encodeBlockAsm8B + MOVW (R10)(R12*1), R11 + CMPW (SI)(R12*1), R11 + JNE matchlen_match1_repeat_extend_encodeBlockAsm8B + LEAL 2(R12), R12 + SUBL $0x02, R9 + JZ repeat_extend_forward_end_encodeBlockAsm8B + +matchlen_match1_repeat_extend_encodeBlockAsm8B: + MOVB (R10)(R12*1), R11 + CMPB (SI)(R12*1), R11 + JNE repeat_extend_forward_end_encodeBlockAsm8B + LEAL 1(R12), R12 + +repeat_extend_forward_end_encodeBlockAsm8B: + ADDL R12, DX + MOVL DX, SI + SUBL DI, SI + MOVL 16(SP), DI + TESTL R8, R8 + JZ repeat_as_copy_encodeBlockAsm8B + + // emitRepeat + MOVL SI, DI + LEAL -4(SI), SI + CMPL DI, $0x08 + JBE repeat_two_match_repeat_encodeBlockAsm8B + CMPL DI, $0x0c + JAE cant_repeat_two_offset_match_repeat_encodeBlockAsm8B + +cant_repeat_two_offset_match_repeat_encodeBlockAsm8B: + CMPL SI, $0x00000104 + JB repeat_three_match_repeat_encodeBlockAsm8B + LEAL -256(SI), SI + MOVW $0x0019, (CX) + MOVW SI, 2(CX) + ADDQ $0x04, CX + JMP repeat_end_emit_encodeBlockAsm8B + +repeat_three_match_repeat_encodeBlockAsm8B: + LEAL -4(SI), SI + MOVW $0x0015, (CX) + MOVB SI, 2(CX) + ADDQ $0x03, CX + JMP repeat_end_emit_encodeBlockAsm8B + +repeat_two_match_repeat_encodeBlockAsm8B: + SHLL $0x02, SI + ORL $0x01, SI + MOVW SI, (CX) + ADDQ $0x02, CX + JMP repeat_end_emit_encodeBlockAsm8B + XORQ R8, R8 + LEAL 1(R8)(SI*4), SI + MOVB DI, 1(CX) + SARL $0x08, DI + SHLL $0x05, DI + ORL DI, SI + MOVB SI, (CX) + ADDQ $0x02, CX + JMP repeat_end_emit_encodeBlockAsm8B + +repeat_as_copy_encodeBlockAsm8B: + // emitCopy + CMPL SI, $0x40 + JBE two_byte_offset_short_repeat_as_copy_encodeBlockAsm8B + CMPL DI, $0x00000800 + JAE long_offset_short_repeat_as_copy_encodeBlockAsm8B + MOVL $0x00000001, R8 + LEAL 16(R8), R8 + MOVB DI, 1(CX) + SHRL $0x08, DI + SHLL $0x05, DI + ORL DI, R8 + MOVB R8, (CX) + ADDQ $0x02, CX + SUBL $0x08, SI + + // emitRepeat + LEAL -4(SI), SI + JMP cant_repeat_two_offset_repeat_as_copy_encodeBlockAsm8B_emit_copy_short_2b + MOVL SI, DI + LEAL -4(SI), SI + CMPL DI, $0x08 + JBE repeat_two_repeat_as_copy_encodeBlockAsm8B_emit_copy_short_2b + CMPL DI, $0x0c + JAE cant_repeat_two_offset_repeat_as_copy_encodeBlockAsm8B_emit_copy_short_2b + +cant_repeat_two_offset_repeat_as_copy_encodeBlockAsm8B_emit_copy_short_2b: + CMPL SI, $0x00000104 + JB repeat_three_repeat_as_copy_encodeBlockAsm8B_emit_copy_short_2b + LEAL -256(SI), SI + MOVW $0x0019, (CX) + MOVW SI, 2(CX) + ADDQ $0x04, CX + JMP repeat_end_emit_encodeBlockAsm8B + +repeat_three_repeat_as_copy_encodeBlockAsm8B_emit_copy_short_2b: + LEAL -4(SI), SI + MOVW $0x0015, (CX) + MOVB SI, 2(CX) + ADDQ $0x03, CX + JMP repeat_end_emit_encodeBlockAsm8B + +repeat_two_repeat_as_copy_encodeBlockAsm8B_emit_copy_short_2b: + SHLL $0x02, SI + ORL $0x01, SI + MOVW SI, (CX) + ADDQ $0x02, CX + JMP repeat_end_emit_encodeBlockAsm8B + XORQ R8, R8 + LEAL 1(R8)(SI*4), SI + MOVB DI, 1(CX) + SARL $0x08, DI + SHLL $0x05, DI + ORL DI, SI + MOVB SI, (CX) + ADDQ $0x02, CX + JMP repeat_end_emit_encodeBlockAsm8B + +long_offset_short_repeat_as_copy_encodeBlockAsm8B: + MOVB $0xee, (CX) + MOVW DI, 1(CX) + LEAL -60(SI), SI + ADDQ $0x03, CX + + // emitRepeat + MOVL SI, DI + LEAL -4(SI), SI + CMPL DI, $0x08 + JBE repeat_two_repeat_as_copy_encodeBlockAsm8B_emit_copy_short + CMPL DI, $0x0c + JAE cant_repeat_two_offset_repeat_as_copy_encodeBlockAsm8B_emit_copy_short + +cant_repeat_two_offset_repeat_as_copy_encodeBlockAsm8B_emit_copy_short: + CMPL SI, $0x00000104 + JB repeat_three_repeat_as_copy_encodeBlockAsm8B_emit_copy_short + LEAL -256(SI), SI + MOVW $0x0019, (CX) + MOVW SI, 2(CX) + ADDQ $0x04, CX + JMP repeat_end_emit_encodeBlockAsm8B + +repeat_three_repeat_as_copy_encodeBlockAsm8B_emit_copy_short: + LEAL -4(SI), SI + MOVW $0x0015, (CX) + MOVB SI, 2(CX) + ADDQ $0x03, CX + JMP repeat_end_emit_encodeBlockAsm8B + +repeat_two_repeat_as_copy_encodeBlockAsm8B_emit_copy_short: + SHLL $0x02, SI + ORL $0x01, SI + MOVW SI, (CX) + ADDQ $0x02, CX + JMP repeat_end_emit_encodeBlockAsm8B + XORQ R8, R8 + LEAL 1(R8)(SI*4), SI + MOVB DI, 1(CX) + SARL $0x08, DI + SHLL $0x05, DI + ORL DI, SI + MOVB SI, (CX) + ADDQ $0x02, CX + JMP repeat_end_emit_encodeBlockAsm8B + +two_byte_offset_short_repeat_as_copy_encodeBlockAsm8B: + MOVL SI, R8 + SHLL $0x02, R8 + CMPL SI, $0x0c + JAE emit_copy_three_repeat_as_copy_encodeBlockAsm8B + LEAL -15(R8), R8 + MOVB DI, 1(CX) + SHRL $0x08, DI + SHLL $0x05, DI + ORL DI, R8 + MOVB R8, (CX) + ADDQ $0x02, CX + JMP repeat_end_emit_encodeBlockAsm8B + +emit_copy_three_repeat_as_copy_encodeBlockAsm8B: + LEAL -2(R8), R8 + MOVB R8, (CX) + MOVW DI, 1(CX) + ADDQ $0x03, CX + +repeat_end_emit_encodeBlockAsm8B: + MOVL DX, 12(SP) + JMP search_loop_encodeBlockAsm8B + +no_repeat_found_encodeBlockAsm8B: + CMPL (BX)(SI*1), DI + JEQ candidate_match_encodeBlockAsm8B + SHRQ $0x08, DI + MOVL (AX)(R10*4), SI + LEAL 2(DX), R9 + CMPL (BX)(R8*1), DI + JEQ candidate2_match_encodeBlockAsm8B + MOVL R9, (AX)(R10*4) + SHRQ $0x08, DI + CMPL (BX)(SI*1), DI + JEQ candidate3_match_encodeBlockAsm8B + MOVL 20(SP), DX + JMP search_loop_encodeBlockAsm8B + +candidate3_match_encodeBlockAsm8B: + ADDL $0x02, DX + JMP candidate_match_encodeBlockAsm8B + +candidate2_match_encodeBlockAsm8B: + MOVL R9, (AX)(R10*4) + INCL DX + MOVL R8, SI + +candidate_match_encodeBlockAsm8B: + MOVL 12(SP), DI + TESTL SI, SI + JZ match_extend_back_end_encodeBlockAsm8B + +match_extend_back_loop_encodeBlockAsm8B: + CMPL DX, DI + JBE match_extend_back_end_encodeBlockAsm8B + MOVB -1(BX)(SI*1), R8 + MOVB -1(BX)(DX*1), R9 + CMPB R8, R9 + JNE match_extend_back_end_encodeBlockAsm8B + LEAL -1(DX), DX + DECL SI + JZ match_extend_back_end_encodeBlockAsm8B + JMP match_extend_back_loop_encodeBlockAsm8B + +match_extend_back_end_encodeBlockAsm8B: + MOVL DX, DI + SUBL 12(SP), DI + LEAQ 3(CX)(DI*1), DI + CMPQ DI, (SP) + JB match_dst_size_check_encodeBlockAsm8B + MOVQ $0x00000000, ret+56(FP) + RET + +match_dst_size_check_encodeBlockAsm8B: + MOVL DX, DI + MOVL 12(SP), R8 + CMPL R8, DI + JEQ emit_literal_done_match_emit_encodeBlockAsm8B + MOVL DI, R9 + MOVL DI, 12(SP) + LEAQ (BX)(R8*1), DI + SUBL R8, R9 + LEAL -1(R9), R8 + CMPL R8, $0x3c + JB one_byte_match_emit_encodeBlockAsm8B + CMPL R8, $0x00000100 + JB two_bytes_match_emit_encodeBlockAsm8B + JB three_bytes_match_emit_encodeBlockAsm8B + +three_bytes_match_emit_encodeBlockAsm8B: + MOVB $0xf4, (CX) + MOVW R8, 1(CX) + ADDQ $0x03, CX + JMP memmove_long_match_emit_encodeBlockAsm8B + +two_bytes_match_emit_encodeBlockAsm8B: + MOVB $0xf0, (CX) + MOVB R8, 1(CX) + ADDQ $0x02, CX + CMPL R8, $0x40 + JB memmove_match_emit_encodeBlockAsm8B + JMP memmove_long_match_emit_encodeBlockAsm8B + +one_byte_match_emit_encodeBlockAsm8B: + SHLB $0x02, R8 + MOVB R8, (CX) + ADDQ $0x01, CX + +memmove_match_emit_encodeBlockAsm8B: + LEAQ (CX)(R9*1), R8 + + // genMemMoveShort + CMPQ R9, $0x08 + JBE emit_lit_memmove_match_emit_encodeBlockAsm8B_memmove_move_8 + CMPQ R9, $0x10 + JBE emit_lit_memmove_match_emit_encodeBlockAsm8B_memmove_move_8through16 + CMPQ R9, $0x20 + JBE emit_lit_memmove_match_emit_encodeBlockAsm8B_memmove_move_17through32 + JMP emit_lit_memmove_match_emit_encodeBlockAsm8B_memmove_move_33through64 + +emit_lit_memmove_match_emit_encodeBlockAsm8B_memmove_move_8: + MOVQ (DI), R10 + MOVQ R10, (CX) + JMP memmove_end_copy_match_emit_encodeBlockAsm8B + +emit_lit_memmove_match_emit_encodeBlockAsm8B_memmove_move_8through16: + MOVQ (DI), R10 + MOVQ -8(DI)(R9*1), DI + MOVQ R10, (CX) + MOVQ DI, -8(CX)(R9*1) + JMP memmove_end_copy_match_emit_encodeBlockAsm8B + +emit_lit_memmove_match_emit_encodeBlockAsm8B_memmove_move_17through32: + MOVOU (DI), X0 + MOVOU -16(DI)(R9*1), X1 + MOVOU X0, (CX) + MOVOU X1, -16(CX)(R9*1) + JMP memmove_end_copy_match_emit_encodeBlockAsm8B + +emit_lit_memmove_match_emit_encodeBlockAsm8B_memmove_move_33through64: + MOVOU (DI), X0 + MOVOU 16(DI), X1 + MOVOU -32(DI)(R9*1), X2 + MOVOU -16(DI)(R9*1), X3 + MOVOU X0, (CX) + MOVOU X1, 16(CX) + MOVOU X2, -32(CX)(R9*1) + MOVOU X3, -16(CX)(R9*1) + +memmove_end_copy_match_emit_encodeBlockAsm8B: + MOVQ R8, CX + JMP emit_literal_done_match_emit_encodeBlockAsm8B + +memmove_long_match_emit_encodeBlockAsm8B: + LEAQ (CX)(R9*1), R8 + + // genMemMoveLong + MOVOU (DI), X0 + MOVOU 16(DI), X1 + MOVOU -32(DI)(R9*1), X2 + MOVOU -16(DI)(R9*1), X3 + MOVQ R9, R11 + SHRQ $0x05, R11 + MOVQ CX, R10 + ANDL $0x0000001f, R10 + MOVQ $0x00000040, R12 + SUBQ R10, R12 + DECQ R11 + JA emit_lit_memmove_long_match_emit_encodeBlockAsm8Blarge_forward_sse_loop_32 + LEAQ -32(DI)(R12*1), R10 + LEAQ -32(CX)(R12*1), R13 + +emit_lit_memmove_long_match_emit_encodeBlockAsm8Blarge_big_loop_back: + MOVOU (R10), X4 + MOVOU 16(R10), X5 + MOVOA X4, (R13) + MOVOA X5, 16(R13) + ADDQ $0x20, R13 + ADDQ $0x20, R10 + ADDQ $0x20, R12 + DECQ R11 + JNA emit_lit_memmove_long_match_emit_encodeBlockAsm8Blarge_big_loop_back + +emit_lit_memmove_long_match_emit_encodeBlockAsm8Blarge_forward_sse_loop_32: + MOVOU -32(DI)(R12*1), X4 + MOVOU -16(DI)(R12*1), X5 + MOVOA X4, -32(CX)(R12*1) + MOVOA X5, -16(CX)(R12*1) + ADDQ $0x20, R12 + CMPQ R9, R12 + JAE emit_lit_memmove_long_match_emit_encodeBlockAsm8Blarge_forward_sse_loop_32 + MOVOU X0, (CX) + MOVOU X1, 16(CX) + MOVOU X2, -32(CX)(R9*1) + MOVOU X3, -16(CX)(R9*1) + MOVQ R8, CX + +emit_literal_done_match_emit_encodeBlockAsm8B: +match_nolit_loop_encodeBlockAsm8B: + MOVL DX, DI + SUBL SI, DI + MOVL DI, 16(SP) + ADDL $0x04, DX + ADDL $0x04, SI + MOVQ src_len+32(FP), DI + SUBL DX, DI + LEAQ (BX)(DX*1), R8 + LEAQ (BX)(SI*1), SI + + // matchLen + XORL R10, R10 + +matchlen_loopback_16_match_nolit_encodeBlockAsm8B: + CMPL DI, $0x10 + JB matchlen_match8_match_nolit_encodeBlockAsm8B + MOVQ (R8)(R10*1), R9 + MOVQ 8(R8)(R10*1), R11 + XORQ (SI)(R10*1), R9 + JNZ matchlen_bsf_8_match_nolit_encodeBlockAsm8B + XORQ 8(SI)(R10*1), R11 + JNZ matchlen_bsf_16match_nolit_encodeBlockAsm8B + LEAL -16(DI), DI + LEAL 16(R10), R10 + JMP matchlen_loopback_16_match_nolit_encodeBlockAsm8B + +matchlen_bsf_16match_nolit_encodeBlockAsm8B: +#ifdef GOAMD64_v3 + TZCNTQ R11, R11 + +#else + BSFQ R11, R11 + +#endif + SARQ $0x03, R11 + LEAL 8(R10)(R11*1), R10 + JMP match_nolit_end_encodeBlockAsm8B + +matchlen_match8_match_nolit_encodeBlockAsm8B: + CMPL DI, $0x08 + JB matchlen_match4_match_nolit_encodeBlockAsm8B + MOVQ (R8)(R10*1), R9 + XORQ (SI)(R10*1), R9 + JNZ matchlen_bsf_8_match_nolit_encodeBlockAsm8B + LEAL -8(DI), DI + LEAL 8(R10), R10 + JMP matchlen_match4_match_nolit_encodeBlockAsm8B + +matchlen_bsf_8_match_nolit_encodeBlockAsm8B: +#ifdef GOAMD64_v3 + TZCNTQ R9, R9 + +#else + BSFQ R9, R9 + +#endif + SARQ $0x03, R9 + LEAL (R10)(R9*1), R10 + JMP match_nolit_end_encodeBlockAsm8B + +matchlen_match4_match_nolit_encodeBlockAsm8B: + CMPL DI, $0x04 + JB matchlen_match2_match_nolit_encodeBlockAsm8B + MOVL (R8)(R10*1), R9 + CMPL (SI)(R10*1), R9 + JNE matchlen_match2_match_nolit_encodeBlockAsm8B + LEAL -4(DI), DI + LEAL 4(R10), R10 + +matchlen_match2_match_nolit_encodeBlockAsm8B: + CMPL DI, $0x01 + JE matchlen_match1_match_nolit_encodeBlockAsm8B + JB match_nolit_end_encodeBlockAsm8B + MOVW (R8)(R10*1), R9 + CMPW (SI)(R10*1), R9 + JNE matchlen_match1_match_nolit_encodeBlockAsm8B + LEAL 2(R10), R10 + SUBL $0x02, DI + JZ match_nolit_end_encodeBlockAsm8B + +matchlen_match1_match_nolit_encodeBlockAsm8B: + MOVB (R8)(R10*1), R9 + CMPB (SI)(R10*1), R9 + JNE match_nolit_end_encodeBlockAsm8B + LEAL 1(R10), R10 + +match_nolit_end_encodeBlockAsm8B: + ADDL R10, DX + MOVL 16(SP), SI + ADDL $0x04, R10 + MOVL DX, 12(SP) + + // emitCopy + CMPL R10, $0x40 + JBE two_byte_offset_short_match_nolit_encodeBlockAsm8B + CMPL SI, $0x00000800 + JAE long_offset_short_match_nolit_encodeBlockAsm8B + MOVL $0x00000001, DI + LEAL 16(DI), DI + MOVB SI, 1(CX) + SHRL $0x08, SI + SHLL $0x05, SI + ORL SI, DI + MOVB DI, (CX) + ADDQ $0x02, CX + SUBL $0x08, R10 + + // emitRepeat + LEAL -4(R10), R10 + JMP cant_repeat_two_offset_match_nolit_encodeBlockAsm8B_emit_copy_short_2b + MOVL R10, SI + LEAL -4(R10), R10 + CMPL SI, $0x08 + JBE repeat_two_match_nolit_encodeBlockAsm8B_emit_copy_short_2b + CMPL SI, $0x0c + JAE cant_repeat_two_offset_match_nolit_encodeBlockAsm8B_emit_copy_short_2b + +cant_repeat_two_offset_match_nolit_encodeBlockAsm8B_emit_copy_short_2b: + CMPL R10, $0x00000104 + JB repeat_three_match_nolit_encodeBlockAsm8B_emit_copy_short_2b + LEAL -256(R10), R10 + MOVW $0x0019, (CX) + MOVW R10, 2(CX) + ADDQ $0x04, CX + JMP match_nolit_emitcopy_end_encodeBlockAsm8B + +repeat_three_match_nolit_encodeBlockAsm8B_emit_copy_short_2b: + LEAL -4(R10), R10 + MOVW $0x0015, (CX) + MOVB R10, 2(CX) + ADDQ $0x03, CX + JMP match_nolit_emitcopy_end_encodeBlockAsm8B + +repeat_two_match_nolit_encodeBlockAsm8B_emit_copy_short_2b: + SHLL $0x02, R10 + ORL $0x01, R10 + MOVW R10, (CX) + ADDQ $0x02, CX + JMP match_nolit_emitcopy_end_encodeBlockAsm8B + XORQ DI, DI + LEAL 1(DI)(R10*4), R10 + MOVB SI, 1(CX) + SARL $0x08, SI + SHLL $0x05, SI + ORL SI, R10 + MOVB R10, (CX) + ADDQ $0x02, CX + JMP match_nolit_emitcopy_end_encodeBlockAsm8B + +long_offset_short_match_nolit_encodeBlockAsm8B: + MOVB $0xee, (CX) + MOVW SI, 1(CX) + LEAL -60(R10), R10 + ADDQ $0x03, CX + + // emitRepeat + MOVL R10, SI + LEAL -4(R10), R10 + CMPL SI, $0x08 + JBE repeat_two_match_nolit_encodeBlockAsm8B_emit_copy_short + CMPL SI, $0x0c + JAE cant_repeat_two_offset_match_nolit_encodeBlockAsm8B_emit_copy_short + +cant_repeat_two_offset_match_nolit_encodeBlockAsm8B_emit_copy_short: + CMPL R10, $0x00000104 + JB repeat_three_match_nolit_encodeBlockAsm8B_emit_copy_short + LEAL -256(R10), R10 + MOVW $0x0019, (CX) + MOVW R10, 2(CX) + ADDQ $0x04, CX + JMP match_nolit_emitcopy_end_encodeBlockAsm8B + +repeat_three_match_nolit_encodeBlockAsm8B_emit_copy_short: + LEAL -4(R10), R10 + MOVW $0x0015, (CX) + MOVB R10, 2(CX) + ADDQ $0x03, CX + JMP match_nolit_emitcopy_end_encodeBlockAsm8B + +repeat_two_match_nolit_encodeBlockAsm8B_emit_copy_short: + SHLL $0x02, R10 + ORL $0x01, R10 + MOVW R10, (CX) + ADDQ $0x02, CX + JMP match_nolit_emitcopy_end_encodeBlockAsm8B + XORQ DI, DI + LEAL 1(DI)(R10*4), R10 + MOVB SI, 1(CX) + SARL $0x08, SI + SHLL $0x05, SI + ORL SI, R10 + MOVB R10, (CX) + ADDQ $0x02, CX + JMP match_nolit_emitcopy_end_encodeBlockAsm8B + +two_byte_offset_short_match_nolit_encodeBlockAsm8B: + MOVL R10, DI + SHLL $0x02, DI + CMPL R10, $0x0c + JAE emit_copy_three_match_nolit_encodeBlockAsm8B + LEAL -15(DI), DI + MOVB SI, 1(CX) + SHRL $0x08, SI + SHLL $0x05, SI + ORL SI, DI + MOVB DI, (CX) + ADDQ $0x02, CX + JMP match_nolit_emitcopy_end_encodeBlockAsm8B + +emit_copy_three_match_nolit_encodeBlockAsm8B: + LEAL -2(DI), DI + MOVB DI, (CX) + MOVW SI, 1(CX) + ADDQ $0x03, CX + +match_nolit_emitcopy_end_encodeBlockAsm8B: + CMPL DX, 8(SP) + JAE emit_remainder_encodeBlockAsm8B + MOVQ -2(BX)(DX*1), DI + CMPQ CX, (SP) + JB match_nolit_dst_ok_encodeBlockAsm8B + MOVQ $0x00000000, ret+56(FP) + RET + +match_nolit_dst_ok_encodeBlockAsm8B: + MOVQ $0x9e3779b1, R9 + MOVQ DI, R8 + SHRQ $0x10, DI + MOVQ DI, SI + SHLQ $0x20, R8 + IMULQ R9, R8 + SHRQ $0x38, R8 + SHLQ $0x20, SI + IMULQ R9, SI + SHRQ $0x38, SI + LEAL -2(DX), R9 + LEAQ (AX)(SI*4), R10 + MOVL (R10), SI + MOVL R9, (AX)(R8*4) + MOVL DX, (R10) + CMPL (BX)(SI*1), DI + JEQ match_nolit_loop_encodeBlockAsm8B + INCL DX + JMP search_loop_encodeBlockAsm8B + +emit_remainder_encodeBlockAsm8B: + MOVQ src_len+32(FP), AX + SUBL 12(SP), AX + LEAQ 3(CX)(AX*1), AX + CMPQ AX, (SP) + JB emit_remainder_ok_encodeBlockAsm8B + MOVQ $0x00000000, ret+56(FP) + RET + +emit_remainder_ok_encodeBlockAsm8B: + MOVQ src_len+32(FP), AX + MOVL 12(SP), DX + CMPL DX, AX + JEQ emit_literal_done_emit_remainder_encodeBlockAsm8B + MOVL AX, SI + MOVL AX, 12(SP) + LEAQ (BX)(DX*1), AX + SUBL DX, SI + LEAL -1(SI), DX + CMPL DX, $0x3c + JB one_byte_emit_remainder_encodeBlockAsm8B + CMPL DX, $0x00000100 + JB two_bytes_emit_remainder_encodeBlockAsm8B + JB three_bytes_emit_remainder_encodeBlockAsm8B + +three_bytes_emit_remainder_encodeBlockAsm8B: + MOVB $0xf4, (CX) + MOVW DX, 1(CX) + ADDQ $0x03, CX + JMP memmove_long_emit_remainder_encodeBlockAsm8B + +two_bytes_emit_remainder_encodeBlockAsm8B: + MOVB $0xf0, (CX) + MOVB DL, 1(CX) + ADDQ $0x02, CX + CMPL DX, $0x40 + JB memmove_emit_remainder_encodeBlockAsm8B + JMP memmove_long_emit_remainder_encodeBlockAsm8B + +one_byte_emit_remainder_encodeBlockAsm8B: + SHLB $0x02, DL + MOVB DL, (CX) + ADDQ $0x01, CX + +memmove_emit_remainder_encodeBlockAsm8B: + LEAQ (CX)(SI*1), DX + MOVL SI, BX + + // genMemMoveShort + CMPQ BX, $0x03 + JB emit_lit_memmove_emit_remainder_encodeBlockAsm8B_memmove_move_1or2 + JE emit_lit_memmove_emit_remainder_encodeBlockAsm8B_memmove_move_3 + CMPQ BX, $0x08 + JB emit_lit_memmove_emit_remainder_encodeBlockAsm8B_memmove_move_4through7 + CMPQ BX, $0x10 + JBE emit_lit_memmove_emit_remainder_encodeBlockAsm8B_memmove_move_8through16 + CMPQ BX, $0x20 + JBE emit_lit_memmove_emit_remainder_encodeBlockAsm8B_memmove_move_17through32 + JMP emit_lit_memmove_emit_remainder_encodeBlockAsm8B_memmove_move_33through64 + +emit_lit_memmove_emit_remainder_encodeBlockAsm8B_memmove_move_1or2: + MOVB (AX), SI + MOVB -1(AX)(BX*1), AL + MOVB SI, (CX) + MOVB AL, -1(CX)(BX*1) + JMP memmove_end_copy_emit_remainder_encodeBlockAsm8B + +emit_lit_memmove_emit_remainder_encodeBlockAsm8B_memmove_move_3: + MOVW (AX), SI + MOVB 2(AX), AL + MOVW SI, (CX) + MOVB AL, 2(CX) + JMP memmove_end_copy_emit_remainder_encodeBlockAsm8B + +emit_lit_memmove_emit_remainder_encodeBlockAsm8B_memmove_move_4through7: + MOVL (AX), SI + MOVL -4(AX)(BX*1), AX + MOVL SI, (CX) + MOVL AX, -4(CX)(BX*1) + JMP memmove_end_copy_emit_remainder_encodeBlockAsm8B + +emit_lit_memmove_emit_remainder_encodeBlockAsm8B_memmove_move_8through16: + MOVQ (AX), SI + MOVQ -8(AX)(BX*1), AX + MOVQ SI, (CX) + MOVQ AX, -8(CX)(BX*1) + JMP memmove_end_copy_emit_remainder_encodeBlockAsm8B + +emit_lit_memmove_emit_remainder_encodeBlockAsm8B_memmove_move_17through32: + MOVOU (AX), X0 + MOVOU -16(AX)(BX*1), X1 + MOVOU X0, (CX) + MOVOU X1, -16(CX)(BX*1) + JMP memmove_end_copy_emit_remainder_encodeBlockAsm8B + +emit_lit_memmove_emit_remainder_encodeBlockAsm8B_memmove_move_33through64: + MOVOU (AX), X0 + MOVOU 16(AX), X1 + MOVOU -32(AX)(BX*1), X2 + MOVOU -16(AX)(BX*1), X3 + MOVOU X0, (CX) + MOVOU X1, 16(CX) + MOVOU X2, -32(CX)(BX*1) + MOVOU X3, -16(CX)(BX*1) + +memmove_end_copy_emit_remainder_encodeBlockAsm8B: + MOVQ DX, CX + JMP emit_literal_done_emit_remainder_encodeBlockAsm8B + +memmove_long_emit_remainder_encodeBlockAsm8B: + LEAQ (CX)(SI*1), DX + MOVL SI, BX + + // genMemMoveLong + MOVOU (AX), X0 + MOVOU 16(AX), X1 + MOVOU -32(AX)(BX*1), X2 + MOVOU -16(AX)(BX*1), X3 + MOVQ BX, DI + SHRQ $0x05, DI + MOVQ CX, SI + ANDL $0x0000001f, SI + MOVQ $0x00000040, R8 + SUBQ SI, R8 + DECQ DI + JA emit_lit_memmove_long_emit_remainder_encodeBlockAsm8Blarge_forward_sse_loop_32 + LEAQ -32(AX)(R8*1), SI + LEAQ -32(CX)(R8*1), R9 + +emit_lit_memmove_long_emit_remainder_encodeBlockAsm8Blarge_big_loop_back: + MOVOU (SI), X4 + MOVOU 16(SI), X5 + MOVOA X4, (R9) + MOVOA X5, 16(R9) + ADDQ $0x20, R9 + ADDQ $0x20, SI + ADDQ $0x20, R8 + DECQ DI + JNA emit_lit_memmove_long_emit_remainder_encodeBlockAsm8Blarge_big_loop_back + +emit_lit_memmove_long_emit_remainder_encodeBlockAsm8Blarge_forward_sse_loop_32: + MOVOU -32(AX)(R8*1), X4 + MOVOU -16(AX)(R8*1), X5 + MOVOA X4, -32(CX)(R8*1) + MOVOA X5, -16(CX)(R8*1) + ADDQ $0x20, R8 + CMPQ BX, R8 + JAE emit_lit_memmove_long_emit_remainder_encodeBlockAsm8Blarge_forward_sse_loop_32 + MOVOU X0, (CX) + MOVOU X1, 16(CX) + MOVOU X2, -32(CX)(BX*1) + MOVOU X3, -16(CX)(BX*1) + MOVQ DX, CX + +emit_literal_done_emit_remainder_encodeBlockAsm8B: + MOVQ dst_base+0(FP), AX + SUBQ AX, CX + MOVQ CX, ret+56(FP) + RET + +// func encodeBetterBlockAsm(dst []byte, src []byte, tmp *[589824]byte) int +// Requires: BMI, SSE2 +TEXT ·encodeBetterBlockAsm(SB), $24-64 + MOVQ tmp+48(FP), AX + MOVQ dst_base+0(FP), CX + MOVQ $0x00001200, DX + MOVQ AX, BX + PXOR X0, X0 + +zero_loop_encodeBetterBlockAsm: + MOVOU X0, (BX) + MOVOU X0, 16(BX) + MOVOU X0, 32(BX) + MOVOU X0, 48(BX) + MOVOU X0, 64(BX) + MOVOU X0, 80(BX) + MOVOU X0, 96(BX) + MOVOU X0, 112(BX) + ADDQ $0x80, BX + DECQ DX + JNZ zero_loop_encodeBetterBlockAsm + MOVL $0x00000000, 12(SP) + MOVQ src_len+32(FP), DX + LEAQ -6(DX), BX + LEAQ -8(DX), SI + MOVL SI, 8(SP) + SHRQ $0x05, DX + SUBL DX, BX + LEAQ (CX)(BX*1), BX + MOVQ BX, (SP) + MOVL $0x00000001, DX + MOVL $0x00000000, 16(SP) + MOVQ src_base+24(FP), BX + +search_loop_encodeBetterBlockAsm: + MOVL DX, SI + SUBL 12(SP), SI + SHRL $0x07, SI + CMPL SI, $0x63 + JBE check_maxskip_ok_encodeBetterBlockAsm + LEAL 100(DX), SI + JMP check_maxskip_cont_encodeBetterBlockAsm + +check_maxskip_ok_encodeBetterBlockAsm: + LEAL 1(DX)(SI*1), SI + +check_maxskip_cont_encodeBetterBlockAsm: + CMPL SI, 8(SP) + JAE emit_remainder_encodeBetterBlockAsm + MOVQ (BX)(DX*1), DI + MOVL SI, 20(SP) + MOVQ $0x00cf1bbcdcbfa563, R9 + MOVQ $0x9e3779b1, SI + MOVQ DI, R10 + MOVQ DI, R11 + SHLQ $0x08, R10 + IMULQ R9, R10 + SHRQ $0x2f, R10 + SHLQ $0x20, R11 + IMULQ SI, R11 + SHRQ $0x32, R11 + MOVL (AX)(R10*4), SI + MOVL 524288(AX)(R11*4), R8 + MOVL DX, (AX)(R10*4) + MOVL DX, 524288(AX)(R11*4) + MOVQ (BX)(SI*1), R10 + MOVQ (BX)(R8*1), R11 + CMPQ R10, DI + JEQ candidate_match_encodeBetterBlockAsm + CMPQ R11, DI + JNE no_short_found_encodeBetterBlockAsm + MOVL R8, SI + JMP candidate_match_encodeBetterBlockAsm + +no_short_found_encodeBetterBlockAsm: + CMPL R10, DI + JEQ candidate_match_encodeBetterBlockAsm + CMPL R11, DI + JEQ candidateS_match_encodeBetterBlockAsm + MOVL 20(SP), DX + JMP search_loop_encodeBetterBlockAsm + +candidateS_match_encodeBetterBlockAsm: + SHRQ $0x08, DI + MOVQ DI, R10 + SHLQ $0x08, R10 + IMULQ R9, R10 + SHRQ $0x2f, R10 + MOVL (AX)(R10*4), SI + INCL DX + MOVL DX, (AX)(R10*4) + CMPL (BX)(SI*1), DI + JEQ candidate_match_encodeBetterBlockAsm + DECL DX + MOVL R8, SI + +candidate_match_encodeBetterBlockAsm: + MOVL 12(SP), DI + TESTL SI, SI + JZ match_extend_back_end_encodeBetterBlockAsm + +match_extend_back_loop_encodeBetterBlockAsm: + CMPL DX, DI + JBE match_extend_back_end_encodeBetterBlockAsm + MOVB -1(BX)(SI*1), R8 + MOVB -1(BX)(DX*1), R9 + CMPB R8, R9 + JNE match_extend_back_end_encodeBetterBlockAsm + LEAL -1(DX), DX + DECL SI + JZ match_extend_back_end_encodeBetterBlockAsm + JMP match_extend_back_loop_encodeBetterBlockAsm + +match_extend_back_end_encodeBetterBlockAsm: + MOVL DX, DI + SUBL 12(SP), DI + LEAQ 5(CX)(DI*1), DI + CMPQ DI, (SP) + JB match_dst_size_check_encodeBetterBlockAsm + MOVQ $0x00000000, ret+56(FP) + RET + +match_dst_size_check_encodeBetterBlockAsm: + MOVL DX, DI + ADDL $0x04, DX + ADDL $0x04, SI + MOVQ src_len+32(FP), R8 + SUBL DX, R8 + LEAQ (BX)(DX*1), R9 + LEAQ (BX)(SI*1), R10 + + // matchLen + XORL R12, R12 + +matchlen_loopback_16_match_nolit_encodeBetterBlockAsm: + CMPL R8, $0x10 + JB matchlen_match8_match_nolit_encodeBetterBlockAsm + MOVQ (R9)(R12*1), R11 + MOVQ 8(R9)(R12*1), R13 + XORQ (R10)(R12*1), R11 + JNZ matchlen_bsf_8_match_nolit_encodeBetterBlockAsm + XORQ 8(R10)(R12*1), R13 + JNZ matchlen_bsf_16match_nolit_encodeBetterBlockAsm + LEAL -16(R8), R8 + LEAL 16(R12), R12 + JMP matchlen_loopback_16_match_nolit_encodeBetterBlockAsm + +matchlen_bsf_16match_nolit_encodeBetterBlockAsm: +#ifdef GOAMD64_v3 + TZCNTQ R13, R13 + +#else + BSFQ R13, R13 + +#endif + SARQ $0x03, R13 + LEAL 8(R12)(R13*1), R12 + JMP match_nolit_end_encodeBetterBlockAsm + +matchlen_match8_match_nolit_encodeBetterBlockAsm: + CMPL R8, $0x08 + JB matchlen_match4_match_nolit_encodeBetterBlockAsm + MOVQ (R9)(R12*1), R11 + XORQ (R10)(R12*1), R11 + JNZ matchlen_bsf_8_match_nolit_encodeBetterBlockAsm + LEAL -8(R8), R8 + LEAL 8(R12), R12 + JMP matchlen_match4_match_nolit_encodeBetterBlockAsm + +matchlen_bsf_8_match_nolit_encodeBetterBlockAsm: +#ifdef GOAMD64_v3 + TZCNTQ R11, R11 + +#else + BSFQ R11, R11 + +#endif + SARQ $0x03, R11 + LEAL (R12)(R11*1), R12 + JMP match_nolit_end_encodeBetterBlockAsm + +matchlen_match4_match_nolit_encodeBetterBlockAsm: + CMPL R8, $0x04 + JB matchlen_match2_match_nolit_encodeBetterBlockAsm + MOVL (R9)(R12*1), R11 + CMPL (R10)(R12*1), R11 + JNE matchlen_match2_match_nolit_encodeBetterBlockAsm + LEAL -4(R8), R8 + LEAL 4(R12), R12 + +matchlen_match2_match_nolit_encodeBetterBlockAsm: + CMPL R8, $0x01 + JE matchlen_match1_match_nolit_encodeBetterBlockAsm + JB match_nolit_end_encodeBetterBlockAsm + MOVW (R9)(R12*1), R11 + CMPW (R10)(R12*1), R11 + JNE matchlen_match1_match_nolit_encodeBetterBlockAsm + LEAL 2(R12), R12 + SUBL $0x02, R8 + JZ match_nolit_end_encodeBetterBlockAsm + +matchlen_match1_match_nolit_encodeBetterBlockAsm: + MOVB (R9)(R12*1), R11 + CMPB (R10)(R12*1), R11 + JNE match_nolit_end_encodeBetterBlockAsm + LEAL 1(R12), R12 + +match_nolit_end_encodeBetterBlockAsm: + MOVL DX, R8 + SUBL SI, R8 + + // Check if repeat + CMPL 16(SP), R8 + JEQ match_is_repeat_encodeBetterBlockAsm + CMPL R12, $0x01 + JA match_length_ok_encodeBetterBlockAsm + CMPL R8, $0x0000ffff + JBE match_length_ok_encodeBetterBlockAsm + MOVL 20(SP), DX + INCL DX + JMP search_loop_encodeBetterBlockAsm + +match_length_ok_encodeBetterBlockAsm: + MOVL R8, 16(SP) + MOVL 12(SP), SI + CMPL SI, DI + JEQ emit_literal_done_match_emit_encodeBetterBlockAsm + MOVL DI, R9 + MOVL DI, 12(SP) + LEAQ (BX)(SI*1), R10 + SUBL SI, R9 + LEAL -1(R9), SI + CMPL SI, $0x3c + JB one_byte_match_emit_encodeBetterBlockAsm + CMPL SI, $0x00000100 + JB two_bytes_match_emit_encodeBetterBlockAsm + CMPL SI, $0x00010000 + JB three_bytes_match_emit_encodeBetterBlockAsm + CMPL SI, $0x01000000 + JB four_bytes_match_emit_encodeBetterBlockAsm + MOVB $0xfc, (CX) + MOVL SI, 1(CX) + ADDQ $0x05, CX + JMP memmove_long_match_emit_encodeBetterBlockAsm + +four_bytes_match_emit_encodeBetterBlockAsm: + MOVL SI, R11 + SHRL $0x10, R11 + MOVB $0xf8, (CX) + MOVW SI, 1(CX) + MOVB R11, 3(CX) + ADDQ $0x04, CX + JMP memmove_long_match_emit_encodeBetterBlockAsm + +three_bytes_match_emit_encodeBetterBlockAsm: + MOVB $0xf4, (CX) + MOVW SI, 1(CX) + ADDQ $0x03, CX + JMP memmove_long_match_emit_encodeBetterBlockAsm + +two_bytes_match_emit_encodeBetterBlockAsm: + MOVB $0xf0, (CX) + MOVB SI, 1(CX) + ADDQ $0x02, CX + CMPL SI, $0x40 + JB memmove_match_emit_encodeBetterBlockAsm + JMP memmove_long_match_emit_encodeBetterBlockAsm + +one_byte_match_emit_encodeBetterBlockAsm: + SHLB $0x02, SI + MOVB SI, (CX) + ADDQ $0x01, CX + +memmove_match_emit_encodeBetterBlockAsm: + LEAQ (CX)(R9*1), SI + + // genMemMoveShort + CMPQ R9, $0x04 + JBE emit_lit_memmove_match_emit_encodeBetterBlockAsm_memmove_move_4 + CMPQ R9, $0x08 + JB emit_lit_memmove_match_emit_encodeBetterBlockAsm_memmove_move_4through7 + CMPQ R9, $0x10 + JBE emit_lit_memmove_match_emit_encodeBetterBlockAsm_memmove_move_8through16 + CMPQ R9, $0x20 + JBE emit_lit_memmove_match_emit_encodeBetterBlockAsm_memmove_move_17through32 + JMP emit_lit_memmove_match_emit_encodeBetterBlockAsm_memmove_move_33through64 + +emit_lit_memmove_match_emit_encodeBetterBlockAsm_memmove_move_4: + MOVL (R10), R11 + MOVL R11, (CX) + JMP memmove_end_copy_match_emit_encodeBetterBlockAsm + +emit_lit_memmove_match_emit_encodeBetterBlockAsm_memmove_move_4through7: + MOVL (R10), R11 + MOVL -4(R10)(R9*1), R10 + MOVL R11, (CX) + MOVL R10, -4(CX)(R9*1) + JMP memmove_end_copy_match_emit_encodeBetterBlockAsm + +emit_lit_memmove_match_emit_encodeBetterBlockAsm_memmove_move_8through16: + MOVQ (R10), R11 + MOVQ -8(R10)(R9*1), R10 + MOVQ R11, (CX) + MOVQ R10, -8(CX)(R9*1) + JMP memmove_end_copy_match_emit_encodeBetterBlockAsm + +emit_lit_memmove_match_emit_encodeBetterBlockAsm_memmove_move_17through32: + MOVOU (R10), X0 + MOVOU -16(R10)(R9*1), X1 + MOVOU X0, (CX) + MOVOU X1, -16(CX)(R9*1) + JMP memmove_end_copy_match_emit_encodeBetterBlockAsm + +emit_lit_memmove_match_emit_encodeBetterBlockAsm_memmove_move_33through64: + MOVOU (R10), X0 + MOVOU 16(R10), X1 + MOVOU -32(R10)(R9*1), X2 + MOVOU -16(R10)(R9*1), X3 + MOVOU X0, (CX) + MOVOU X1, 16(CX) + MOVOU X2, -32(CX)(R9*1) + MOVOU X3, -16(CX)(R9*1) + +memmove_end_copy_match_emit_encodeBetterBlockAsm: + MOVQ SI, CX + JMP emit_literal_done_match_emit_encodeBetterBlockAsm + +memmove_long_match_emit_encodeBetterBlockAsm: + LEAQ (CX)(R9*1), SI + + // genMemMoveLong + MOVOU (R10), X0 + MOVOU 16(R10), X1 + MOVOU -32(R10)(R9*1), X2 + MOVOU -16(R10)(R9*1), X3 + MOVQ R9, R13 + SHRQ $0x05, R13 + MOVQ CX, R11 + ANDL $0x0000001f, R11 + MOVQ $0x00000040, R14 + SUBQ R11, R14 + DECQ R13 + JA emit_lit_memmove_long_match_emit_encodeBetterBlockAsmlarge_forward_sse_loop_32 + LEAQ -32(R10)(R14*1), R11 + LEAQ -32(CX)(R14*1), R15 + +emit_lit_memmove_long_match_emit_encodeBetterBlockAsmlarge_big_loop_back: + MOVOU (R11), X4 + MOVOU 16(R11), X5 + MOVOA X4, (R15) + MOVOA X5, 16(R15) + ADDQ $0x20, R15 + ADDQ $0x20, R11 + ADDQ $0x20, R14 + DECQ R13 + JNA emit_lit_memmove_long_match_emit_encodeBetterBlockAsmlarge_big_loop_back + +emit_lit_memmove_long_match_emit_encodeBetterBlockAsmlarge_forward_sse_loop_32: + MOVOU -32(R10)(R14*1), X4 + MOVOU -16(R10)(R14*1), X5 + MOVOA X4, -32(CX)(R14*1) + MOVOA X5, -16(CX)(R14*1) + ADDQ $0x20, R14 + CMPQ R9, R14 + JAE emit_lit_memmove_long_match_emit_encodeBetterBlockAsmlarge_forward_sse_loop_32 + MOVOU X0, (CX) + MOVOU X1, 16(CX) + MOVOU X2, -32(CX)(R9*1) + MOVOU X3, -16(CX)(R9*1) + MOVQ SI, CX + +emit_literal_done_match_emit_encodeBetterBlockAsm: + ADDL R12, DX + ADDL $0x04, R12 + MOVL DX, 12(SP) + + // emitCopy + CMPL R8, $0x00010000 + JB two_byte_offset_match_nolit_encodeBetterBlockAsm + CMPL R12, $0x40 + JBE four_bytes_remain_match_nolit_encodeBetterBlockAsm + MOVB $0xff, (CX) + MOVL R8, 1(CX) + LEAL -64(R12), R12 + ADDQ $0x05, CX + CMPL R12, $0x04 + JB four_bytes_remain_match_nolit_encodeBetterBlockAsm + + // emitRepeat +emit_repeat_again_match_nolit_encodeBetterBlockAsm_emit_copy: + MOVL R12, SI + LEAL -4(R12), R12 + CMPL SI, $0x08 + JBE repeat_two_match_nolit_encodeBetterBlockAsm_emit_copy + CMPL SI, $0x0c + JAE cant_repeat_two_offset_match_nolit_encodeBetterBlockAsm_emit_copy + CMPL R8, $0x00000800 + JB repeat_two_offset_match_nolit_encodeBetterBlockAsm_emit_copy + +cant_repeat_two_offset_match_nolit_encodeBetterBlockAsm_emit_copy: + CMPL R12, $0x00000104 + JB repeat_three_match_nolit_encodeBetterBlockAsm_emit_copy + CMPL R12, $0x00010100 + JB repeat_four_match_nolit_encodeBetterBlockAsm_emit_copy + CMPL R12, $0x0100ffff + JB repeat_five_match_nolit_encodeBetterBlockAsm_emit_copy + LEAL -16842747(R12), R12 + MOVL $0xfffb001d, (CX) + MOVB $0xff, 4(CX) + ADDQ $0x05, CX + JMP emit_repeat_again_match_nolit_encodeBetterBlockAsm_emit_copy + +repeat_five_match_nolit_encodeBetterBlockAsm_emit_copy: + LEAL -65536(R12), R12 + MOVL R12, R8 + MOVW $0x001d, (CX) + MOVW R12, 2(CX) + SARL $0x10, R8 + MOVB R8, 4(CX) + ADDQ $0x05, CX + JMP match_nolit_emitcopy_end_encodeBetterBlockAsm + +repeat_four_match_nolit_encodeBetterBlockAsm_emit_copy: + LEAL -256(R12), R12 + MOVW $0x0019, (CX) + MOVW R12, 2(CX) + ADDQ $0x04, CX + JMP match_nolit_emitcopy_end_encodeBetterBlockAsm + +repeat_three_match_nolit_encodeBetterBlockAsm_emit_copy: + LEAL -4(R12), R12 + MOVW $0x0015, (CX) + MOVB R12, 2(CX) + ADDQ $0x03, CX + JMP match_nolit_emitcopy_end_encodeBetterBlockAsm + +repeat_two_match_nolit_encodeBetterBlockAsm_emit_copy: + SHLL $0x02, R12 + ORL $0x01, R12 + MOVW R12, (CX) + ADDQ $0x02, CX + JMP match_nolit_emitcopy_end_encodeBetterBlockAsm + +repeat_two_offset_match_nolit_encodeBetterBlockAsm_emit_copy: + XORQ SI, SI + LEAL 1(SI)(R12*4), R12 + MOVB R8, 1(CX) + SARL $0x08, R8 + SHLL $0x05, R8 + ORL R8, R12 + MOVB R12, (CX) + ADDQ $0x02, CX + JMP match_nolit_emitcopy_end_encodeBetterBlockAsm + +four_bytes_remain_match_nolit_encodeBetterBlockAsm: + TESTL R12, R12 + JZ match_nolit_emitcopy_end_encodeBetterBlockAsm + XORL SI, SI + LEAL -1(SI)(R12*4), R12 + MOVB R12, (CX) + MOVL R8, 1(CX) + ADDQ $0x05, CX + JMP match_nolit_emitcopy_end_encodeBetterBlockAsm + +two_byte_offset_match_nolit_encodeBetterBlockAsm: + CMPL R12, $0x40 + JBE two_byte_offset_short_match_nolit_encodeBetterBlockAsm + CMPL R8, $0x00000800 + JAE long_offset_short_match_nolit_encodeBetterBlockAsm + MOVL $0x00000001, SI + LEAL 16(SI), SI + MOVB R8, 1(CX) + MOVL R8, R9 + SHRL $0x08, R9 + SHLL $0x05, R9 + ORL R9, SI + MOVB SI, (CX) + ADDQ $0x02, CX + SUBL $0x08, R12 + + // emitRepeat + LEAL -4(R12), R12 + JMP cant_repeat_two_offset_match_nolit_encodeBetterBlockAsm_emit_copy_short_2b + +emit_repeat_again_match_nolit_encodeBetterBlockAsm_emit_copy_short_2b: + MOVL R12, SI + LEAL -4(R12), R12 + CMPL SI, $0x08 + JBE repeat_two_match_nolit_encodeBetterBlockAsm_emit_copy_short_2b + CMPL SI, $0x0c + JAE cant_repeat_two_offset_match_nolit_encodeBetterBlockAsm_emit_copy_short_2b + CMPL R8, $0x00000800 + JB repeat_two_offset_match_nolit_encodeBetterBlockAsm_emit_copy_short_2b + +cant_repeat_two_offset_match_nolit_encodeBetterBlockAsm_emit_copy_short_2b: + CMPL R12, $0x00000104 + JB repeat_three_match_nolit_encodeBetterBlockAsm_emit_copy_short_2b + CMPL R12, $0x00010100 + JB repeat_four_match_nolit_encodeBetterBlockAsm_emit_copy_short_2b + CMPL R12, $0x0100ffff + JB repeat_five_match_nolit_encodeBetterBlockAsm_emit_copy_short_2b + LEAL -16842747(R12), R12 + MOVL $0xfffb001d, (CX) + MOVB $0xff, 4(CX) + ADDQ $0x05, CX + JMP emit_repeat_again_match_nolit_encodeBetterBlockAsm_emit_copy_short_2b + +repeat_five_match_nolit_encodeBetterBlockAsm_emit_copy_short_2b: + LEAL -65536(R12), R12 + MOVL R12, R8 + MOVW $0x001d, (CX) + MOVW R12, 2(CX) + SARL $0x10, R8 + MOVB R8, 4(CX) + ADDQ $0x05, CX + JMP match_nolit_emitcopy_end_encodeBetterBlockAsm + +repeat_four_match_nolit_encodeBetterBlockAsm_emit_copy_short_2b: + LEAL -256(R12), R12 + MOVW $0x0019, (CX) + MOVW R12, 2(CX) + ADDQ $0x04, CX + JMP match_nolit_emitcopy_end_encodeBetterBlockAsm + +repeat_three_match_nolit_encodeBetterBlockAsm_emit_copy_short_2b: + LEAL -4(R12), R12 + MOVW $0x0015, (CX) + MOVB R12, 2(CX) + ADDQ $0x03, CX + JMP match_nolit_emitcopy_end_encodeBetterBlockAsm + +repeat_two_match_nolit_encodeBetterBlockAsm_emit_copy_short_2b: + SHLL $0x02, R12 + ORL $0x01, R12 + MOVW R12, (CX) + ADDQ $0x02, CX + JMP match_nolit_emitcopy_end_encodeBetterBlockAsm + +repeat_two_offset_match_nolit_encodeBetterBlockAsm_emit_copy_short_2b: + XORQ SI, SI + LEAL 1(SI)(R12*4), R12 + MOVB R8, 1(CX) + SARL $0x08, R8 + SHLL $0x05, R8 + ORL R8, R12 + MOVB R12, (CX) + ADDQ $0x02, CX + JMP match_nolit_emitcopy_end_encodeBetterBlockAsm + +long_offset_short_match_nolit_encodeBetterBlockAsm: + MOVB $0xee, (CX) + MOVW R8, 1(CX) + LEAL -60(R12), R12 + ADDQ $0x03, CX + + // emitRepeat +emit_repeat_again_match_nolit_encodeBetterBlockAsm_emit_copy_short: + MOVL R12, SI + LEAL -4(R12), R12 + CMPL SI, $0x08 + JBE repeat_two_match_nolit_encodeBetterBlockAsm_emit_copy_short + CMPL SI, $0x0c + JAE cant_repeat_two_offset_match_nolit_encodeBetterBlockAsm_emit_copy_short + CMPL R8, $0x00000800 + JB repeat_two_offset_match_nolit_encodeBetterBlockAsm_emit_copy_short + +cant_repeat_two_offset_match_nolit_encodeBetterBlockAsm_emit_copy_short: + CMPL R12, $0x00000104 + JB repeat_three_match_nolit_encodeBetterBlockAsm_emit_copy_short + CMPL R12, $0x00010100 + JB repeat_four_match_nolit_encodeBetterBlockAsm_emit_copy_short + CMPL R12, $0x0100ffff + JB repeat_five_match_nolit_encodeBetterBlockAsm_emit_copy_short + LEAL -16842747(R12), R12 + MOVL $0xfffb001d, (CX) + MOVB $0xff, 4(CX) + ADDQ $0x05, CX + JMP emit_repeat_again_match_nolit_encodeBetterBlockAsm_emit_copy_short + +repeat_five_match_nolit_encodeBetterBlockAsm_emit_copy_short: + LEAL -65536(R12), R12 + MOVL R12, R8 + MOVW $0x001d, (CX) + MOVW R12, 2(CX) + SARL $0x10, R8 + MOVB R8, 4(CX) + ADDQ $0x05, CX + JMP match_nolit_emitcopy_end_encodeBetterBlockAsm + +repeat_four_match_nolit_encodeBetterBlockAsm_emit_copy_short: + LEAL -256(R12), R12 + MOVW $0x0019, (CX) + MOVW R12, 2(CX) + ADDQ $0x04, CX + JMP match_nolit_emitcopy_end_encodeBetterBlockAsm + +repeat_three_match_nolit_encodeBetterBlockAsm_emit_copy_short: + LEAL -4(R12), R12 + MOVW $0x0015, (CX) + MOVB R12, 2(CX) + ADDQ $0x03, CX + JMP match_nolit_emitcopy_end_encodeBetterBlockAsm + +repeat_two_match_nolit_encodeBetterBlockAsm_emit_copy_short: + SHLL $0x02, R12 + ORL $0x01, R12 + MOVW R12, (CX) + ADDQ $0x02, CX + JMP match_nolit_emitcopy_end_encodeBetterBlockAsm + +repeat_two_offset_match_nolit_encodeBetterBlockAsm_emit_copy_short: + XORQ SI, SI + LEAL 1(SI)(R12*4), R12 + MOVB R8, 1(CX) + SARL $0x08, R8 + SHLL $0x05, R8 + ORL R8, R12 + MOVB R12, (CX) + ADDQ $0x02, CX + JMP match_nolit_emitcopy_end_encodeBetterBlockAsm + +two_byte_offset_short_match_nolit_encodeBetterBlockAsm: + MOVL R12, SI + SHLL $0x02, SI + CMPL R12, $0x0c + JAE emit_copy_three_match_nolit_encodeBetterBlockAsm + CMPL R8, $0x00000800 + JAE emit_copy_three_match_nolit_encodeBetterBlockAsm + LEAL -15(SI), SI + MOVB R8, 1(CX) + SHRL $0x08, R8 + SHLL $0x05, R8 + ORL R8, SI + MOVB SI, (CX) + ADDQ $0x02, CX + JMP match_nolit_emitcopy_end_encodeBetterBlockAsm + +emit_copy_three_match_nolit_encodeBetterBlockAsm: + LEAL -2(SI), SI + MOVB SI, (CX) + MOVW R8, 1(CX) + ADDQ $0x03, CX + JMP match_nolit_emitcopy_end_encodeBetterBlockAsm + +match_is_repeat_encodeBetterBlockAsm: + MOVL 12(SP), SI + CMPL SI, DI + JEQ emit_literal_done_match_emit_repeat_encodeBetterBlockAsm + MOVL DI, R9 + MOVL DI, 12(SP) + LEAQ (BX)(SI*1), R10 + SUBL SI, R9 + LEAL -1(R9), SI + CMPL SI, $0x3c + JB one_byte_match_emit_repeat_encodeBetterBlockAsm + CMPL SI, $0x00000100 + JB two_bytes_match_emit_repeat_encodeBetterBlockAsm + CMPL SI, $0x00010000 + JB three_bytes_match_emit_repeat_encodeBetterBlockAsm + CMPL SI, $0x01000000 + JB four_bytes_match_emit_repeat_encodeBetterBlockAsm + MOVB $0xfc, (CX) + MOVL SI, 1(CX) + ADDQ $0x05, CX + JMP memmove_long_match_emit_repeat_encodeBetterBlockAsm + +four_bytes_match_emit_repeat_encodeBetterBlockAsm: + MOVL SI, R11 + SHRL $0x10, R11 + MOVB $0xf8, (CX) + MOVW SI, 1(CX) + MOVB R11, 3(CX) + ADDQ $0x04, CX + JMP memmove_long_match_emit_repeat_encodeBetterBlockAsm + +three_bytes_match_emit_repeat_encodeBetterBlockAsm: + MOVB $0xf4, (CX) + MOVW SI, 1(CX) + ADDQ $0x03, CX + JMP memmove_long_match_emit_repeat_encodeBetterBlockAsm + +two_bytes_match_emit_repeat_encodeBetterBlockAsm: + MOVB $0xf0, (CX) + MOVB SI, 1(CX) + ADDQ $0x02, CX + CMPL SI, $0x40 + JB memmove_match_emit_repeat_encodeBetterBlockAsm + JMP memmove_long_match_emit_repeat_encodeBetterBlockAsm + +one_byte_match_emit_repeat_encodeBetterBlockAsm: + SHLB $0x02, SI + MOVB SI, (CX) + ADDQ $0x01, CX + +memmove_match_emit_repeat_encodeBetterBlockAsm: + LEAQ (CX)(R9*1), SI + + // genMemMoveShort + CMPQ R9, $0x04 + JBE emit_lit_memmove_match_emit_repeat_encodeBetterBlockAsm_memmove_move_4 + CMPQ R9, $0x08 + JB emit_lit_memmove_match_emit_repeat_encodeBetterBlockAsm_memmove_move_4through7 + CMPQ R9, $0x10 + JBE emit_lit_memmove_match_emit_repeat_encodeBetterBlockAsm_memmove_move_8through16 + CMPQ R9, $0x20 + JBE emit_lit_memmove_match_emit_repeat_encodeBetterBlockAsm_memmove_move_17through32 + JMP emit_lit_memmove_match_emit_repeat_encodeBetterBlockAsm_memmove_move_33through64 + +emit_lit_memmove_match_emit_repeat_encodeBetterBlockAsm_memmove_move_4: + MOVL (R10), R11 + MOVL R11, (CX) + JMP memmove_end_copy_match_emit_repeat_encodeBetterBlockAsm + +emit_lit_memmove_match_emit_repeat_encodeBetterBlockAsm_memmove_move_4through7: + MOVL (R10), R11 + MOVL -4(R10)(R9*1), R10 + MOVL R11, (CX) + MOVL R10, -4(CX)(R9*1) + JMP memmove_end_copy_match_emit_repeat_encodeBetterBlockAsm + +emit_lit_memmove_match_emit_repeat_encodeBetterBlockAsm_memmove_move_8through16: + MOVQ (R10), R11 + MOVQ -8(R10)(R9*1), R10 + MOVQ R11, (CX) + MOVQ R10, -8(CX)(R9*1) + JMP memmove_end_copy_match_emit_repeat_encodeBetterBlockAsm + +emit_lit_memmove_match_emit_repeat_encodeBetterBlockAsm_memmove_move_17through32: + MOVOU (R10), X0 + MOVOU -16(R10)(R9*1), X1 + MOVOU X0, (CX) + MOVOU X1, -16(CX)(R9*1) + JMP memmove_end_copy_match_emit_repeat_encodeBetterBlockAsm + +emit_lit_memmove_match_emit_repeat_encodeBetterBlockAsm_memmove_move_33through64: + MOVOU (R10), X0 + MOVOU 16(R10), X1 + MOVOU -32(R10)(R9*1), X2 + MOVOU -16(R10)(R9*1), X3 + MOVOU X0, (CX) + MOVOU X1, 16(CX) + MOVOU X2, -32(CX)(R9*1) + MOVOU X3, -16(CX)(R9*1) + +memmove_end_copy_match_emit_repeat_encodeBetterBlockAsm: + MOVQ SI, CX + JMP emit_literal_done_match_emit_repeat_encodeBetterBlockAsm + +memmove_long_match_emit_repeat_encodeBetterBlockAsm: + LEAQ (CX)(R9*1), SI + + // genMemMoveLong + MOVOU (R10), X0 + MOVOU 16(R10), X1 + MOVOU -32(R10)(R9*1), X2 + MOVOU -16(R10)(R9*1), X3 + MOVQ R9, R13 + SHRQ $0x05, R13 + MOVQ CX, R11 + ANDL $0x0000001f, R11 + MOVQ $0x00000040, R14 + SUBQ R11, R14 + DECQ R13 + JA emit_lit_memmove_long_match_emit_repeat_encodeBetterBlockAsmlarge_forward_sse_loop_32 + LEAQ -32(R10)(R14*1), R11 + LEAQ -32(CX)(R14*1), R15 + +emit_lit_memmove_long_match_emit_repeat_encodeBetterBlockAsmlarge_big_loop_back: + MOVOU (R11), X4 + MOVOU 16(R11), X5 + MOVOA X4, (R15) + MOVOA X5, 16(R15) + ADDQ $0x20, R15 + ADDQ $0x20, R11 + ADDQ $0x20, R14 + DECQ R13 + JNA emit_lit_memmove_long_match_emit_repeat_encodeBetterBlockAsmlarge_big_loop_back + +emit_lit_memmove_long_match_emit_repeat_encodeBetterBlockAsmlarge_forward_sse_loop_32: + MOVOU -32(R10)(R14*1), X4 + MOVOU -16(R10)(R14*1), X5 + MOVOA X4, -32(CX)(R14*1) + MOVOA X5, -16(CX)(R14*1) + ADDQ $0x20, R14 + CMPQ R9, R14 + JAE emit_lit_memmove_long_match_emit_repeat_encodeBetterBlockAsmlarge_forward_sse_loop_32 + MOVOU X0, (CX) + MOVOU X1, 16(CX) + MOVOU X2, -32(CX)(R9*1) + MOVOU X3, -16(CX)(R9*1) + MOVQ SI, CX + +emit_literal_done_match_emit_repeat_encodeBetterBlockAsm: + ADDL R12, DX + ADDL $0x04, R12 + MOVL DX, 12(SP) + + // emitRepeat +emit_repeat_again_match_nolit_repeat_encodeBetterBlockAsm: + MOVL R12, SI + LEAL -4(R12), R12 + CMPL SI, $0x08 + JBE repeat_two_match_nolit_repeat_encodeBetterBlockAsm + CMPL SI, $0x0c + JAE cant_repeat_two_offset_match_nolit_repeat_encodeBetterBlockAsm + CMPL R8, $0x00000800 + JB repeat_two_offset_match_nolit_repeat_encodeBetterBlockAsm + +cant_repeat_two_offset_match_nolit_repeat_encodeBetterBlockAsm: + CMPL R12, $0x00000104 + JB repeat_three_match_nolit_repeat_encodeBetterBlockAsm + CMPL R12, $0x00010100 + JB repeat_four_match_nolit_repeat_encodeBetterBlockAsm + CMPL R12, $0x0100ffff + JB repeat_five_match_nolit_repeat_encodeBetterBlockAsm + LEAL -16842747(R12), R12 + MOVL $0xfffb001d, (CX) + MOVB $0xff, 4(CX) + ADDQ $0x05, CX + JMP emit_repeat_again_match_nolit_repeat_encodeBetterBlockAsm + +repeat_five_match_nolit_repeat_encodeBetterBlockAsm: + LEAL -65536(R12), R12 + MOVL R12, R8 + MOVW $0x001d, (CX) + MOVW R12, 2(CX) + SARL $0x10, R8 + MOVB R8, 4(CX) + ADDQ $0x05, CX + JMP match_nolit_emitcopy_end_encodeBetterBlockAsm + +repeat_four_match_nolit_repeat_encodeBetterBlockAsm: + LEAL -256(R12), R12 + MOVW $0x0019, (CX) + MOVW R12, 2(CX) + ADDQ $0x04, CX + JMP match_nolit_emitcopy_end_encodeBetterBlockAsm + +repeat_three_match_nolit_repeat_encodeBetterBlockAsm: + LEAL -4(R12), R12 + MOVW $0x0015, (CX) + MOVB R12, 2(CX) + ADDQ $0x03, CX + JMP match_nolit_emitcopy_end_encodeBetterBlockAsm + +repeat_two_match_nolit_repeat_encodeBetterBlockAsm: + SHLL $0x02, R12 + ORL $0x01, R12 + MOVW R12, (CX) + ADDQ $0x02, CX + JMP match_nolit_emitcopy_end_encodeBetterBlockAsm + +repeat_two_offset_match_nolit_repeat_encodeBetterBlockAsm: + XORQ SI, SI + LEAL 1(SI)(R12*4), R12 + MOVB R8, 1(CX) + SARL $0x08, R8 + SHLL $0x05, R8 + ORL R8, R12 + MOVB R12, (CX) + ADDQ $0x02, CX + +match_nolit_emitcopy_end_encodeBetterBlockAsm: + CMPL DX, 8(SP) + JAE emit_remainder_encodeBetterBlockAsm + CMPQ CX, (SP) + JB match_nolit_dst_ok_encodeBetterBlockAsm + MOVQ $0x00000000, ret+56(FP) + RET + +match_nolit_dst_ok_encodeBetterBlockAsm: + MOVQ $0x00cf1bbcdcbfa563, SI + MOVQ $0x9e3779b1, R8 + LEAQ 1(DI), DI + LEAQ -2(DX), R9 + MOVQ (BX)(DI*1), R10 + MOVQ 1(BX)(DI*1), R11 + MOVQ (BX)(R9*1), R12 + MOVQ 1(BX)(R9*1), R13 + SHLQ $0x08, R10 + IMULQ SI, R10 + SHRQ $0x2f, R10 + SHLQ $0x20, R11 + IMULQ R8, R11 + SHRQ $0x32, R11 + SHLQ $0x08, R12 + IMULQ SI, R12 + SHRQ $0x2f, R12 + SHLQ $0x20, R13 + IMULQ R8, R13 + SHRQ $0x32, R13 + LEAQ 1(DI), R8 + LEAQ 1(R9), R14 + MOVL DI, (AX)(R10*4) + MOVL R9, (AX)(R12*4) + MOVL R8, 524288(AX)(R11*4) + MOVL R14, 524288(AX)(R13*4) + LEAQ 1(R9)(DI*1), R8 + SHRQ $0x01, R8 + ADDQ $0x01, DI + SUBQ $0x01, R9 + +index_loop_encodeBetterBlockAsm: + CMPQ R8, R9 + JAE search_loop_encodeBetterBlockAsm + MOVQ (BX)(DI*1), R10 + MOVQ (BX)(R8*1), R11 + SHLQ $0x08, R10 + IMULQ SI, R10 + SHRQ $0x2f, R10 + SHLQ $0x08, R11 + IMULQ SI, R11 + SHRQ $0x2f, R11 + MOVL DI, (AX)(R10*4) + MOVL R8, (AX)(R11*4) + ADDQ $0x02, DI + ADDQ $0x02, R8 + JMP index_loop_encodeBetterBlockAsm + +emit_remainder_encodeBetterBlockAsm: + MOVQ src_len+32(FP), AX + SUBL 12(SP), AX + LEAQ 5(CX)(AX*1), AX + CMPQ AX, (SP) + JB emit_remainder_ok_encodeBetterBlockAsm + MOVQ $0x00000000, ret+56(FP) + RET + +emit_remainder_ok_encodeBetterBlockAsm: + MOVQ src_len+32(FP), AX + MOVL 12(SP), DX + CMPL DX, AX + JEQ emit_literal_done_emit_remainder_encodeBetterBlockAsm + MOVL AX, SI + MOVL AX, 12(SP) + LEAQ (BX)(DX*1), AX + SUBL DX, SI + LEAL -1(SI), DX + CMPL DX, $0x3c + JB one_byte_emit_remainder_encodeBetterBlockAsm + CMPL DX, $0x00000100 + JB two_bytes_emit_remainder_encodeBetterBlockAsm + CMPL DX, $0x00010000 + JB three_bytes_emit_remainder_encodeBetterBlockAsm + CMPL DX, $0x01000000 + JB four_bytes_emit_remainder_encodeBetterBlockAsm + MOVB $0xfc, (CX) + MOVL DX, 1(CX) + ADDQ $0x05, CX + JMP memmove_long_emit_remainder_encodeBetterBlockAsm + +four_bytes_emit_remainder_encodeBetterBlockAsm: + MOVL DX, BX + SHRL $0x10, BX + MOVB $0xf8, (CX) + MOVW DX, 1(CX) + MOVB BL, 3(CX) + ADDQ $0x04, CX + JMP memmove_long_emit_remainder_encodeBetterBlockAsm + +three_bytes_emit_remainder_encodeBetterBlockAsm: + MOVB $0xf4, (CX) + MOVW DX, 1(CX) + ADDQ $0x03, CX + JMP memmove_long_emit_remainder_encodeBetterBlockAsm + +two_bytes_emit_remainder_encodeBetterBlockAsm: + MOVB $0xf0, (CX) + MOVB DL, 1(CX) + ADDQ $0x02, CX + CMPL DX, $0x40 + JB memmove_emit_remainder_encodeBetterBlockAsm + JMP memmove_long_emit_remainder_encodeBetterBlockAsm + +one_byte_emit_remainder_encodeBetterBlockAsm: + SHLB $0x02, DL + MOVB DL, (CX) + ADDQ $0x01, CX + +memmove_emit_remainder_encodeBetterBlockAsm: + LEAQ (CX)(SI*1), DX + MOVL SI, BX + + // genMemMoveShort + CMPQ BX, $0x03 + JB emit_lit_memmove_emit_remainder_encodeBetterBlockAsm_memmove_move_1or2 + JE emit_lit_memmove_emit_remainder_encodeBetterBlockAsm_memmove_move_3 + CMPQ BX, $0x08 + JB emit_lit_memmove_emit_remainder_encodeBetterBlockAsm_memmove_move_4through7 + CMPQ BX, $0x10 + JBE emit_lit_memmove_emit_remainder_encodeBetterBlockAsm_memmove_move_8through16 + CMPQ BX, $0x20 + JBE emit_lit_memmove_emit_remainder_encodeBetterBlockAsm_memmove_move_17through32 + JMP emit_lit_memmove_emit_remainder_encodeBetterBlockAsm_memmove_move_33through64 + +emit_lit_memmove_emit_remainder_encodeBetterBlockAsm_memmove_move_1or2: + MOVB (AX), SI + MOVB -1(AX)(BX*1), AL + MOVB SI, (CX) + MOVB AL, -1(CX)(BX*1) + JMP memmove_end_copy_emit_remainder_encodeBetterBlockAsm + +emit_lit_memmove_emit_remainder_encodeBetterBlockAsm_memmove_move_3: + MOVW (AX), SI + MOVB 2(AX), AL + MOVW SI, (CX) + MOVB AL, 2(CX) + JMP memmove_end_copy_emit_remainder_encodeBetterBlockAsm + +emit_lit_memmove_emit_remainder_encodeBetterBlockAsm_memmove_move_4through7: + MOVL (AX), SI + MOVL -4(AX)(BX*1), AX + MOVL SI, (CX) + MOVL AX, -4(CX)(BX*1) + JMP memmove_end_copy_emit_remainder_encodeBetterBlockAsm + +emit_lit_memmove_emit_remainder_encodeBetterBlockAsm_memmove_move_8through16: + MOVQ (AX), SI + MOVQ -8(AX)(BX*1), AX + MOVQ SI, (CX) + MOVQ AX, -8(CX)(BX*1) + JMP memmove_end_copy_emit_remainder_encodeBetterBlockAsm + +emit_lit_memmove_emit_remainder_encodeBetterBlockAsm_memmove_move_17through32: + MOVOU (AX), X0 + MOVOU -16(AX)(BX*1), X1 + MOVOU X0, (CX) + MOVOU X1, -16(CX)(BX*1) + JMP memmove_end_copy_emit_remainder_encodeBetterBlockAsm + +emit_lit_memmove_emit_remainder_encodeBetterBlockAsm_memmove_move_33through64: + MOVOU (AX), X0 + MOVOU 16(AX), X1 + MOVOU -32(AX)(BX*1), X2 + MOVOU -16(AX)(BX*1), X3 + MOVOU X0, (CX) + MOVOU X1, 16(CX) + MOVOU X2, -32(CX)(BX*1) + MOVOU X3, -16(CX)(BX*1) + +memmove_end_copy_emit_remainder_encodeBetterBlockAsm: + MOVQ DX, CX + JMP emit_literal_done_emit_remainder_encodeBetterBlockAsm + +memmove_long_emit_remainder_encodeBetterBlockAsm: + LEAQ (CX)(SI*1), DX + MOVL SI, BX + + // genMemMoveLong + MOVOU (AX), X0 + MOVOU 16(AX), X1 + MOVOU -32(AX)(BX*1), X2 + MOVOU -16(AX)(BX*1), X3 + MOVQ BX, DI + SHRQ $0x05, DI + MOVQ CX, SI + ANDL $0x0000001f, SI + MOVQ $0x00000040, R8 + SUBQ SI, R8 + DECQ DI + JA emit_lit_memmove_long_emit_remainder_encodeBetterBlockAsmlarge_forward_sse_loop_32 + LEAQ -32(AX)(R8*1), SI + LEAQ -32(CX)(R8*1), R9 + +emit_lit_memmove_long_emit_remainder_encodeBetterBlockAsmlarge_big_loop_back: + MOVOU (SI), X4 + MOVOU 16(SI), X5 + MOVOA X4, (R9) + MOVOA X5, 16(R9) + ADDQ $0x20, R9 + ADDQ $0x20, SI + ADDQ $0x20, R8 + DECQ DI + JNA emit_lit_memmove_long_emit_remainder_encodeBetterBlockAsmlarge_big_loop_back + +emit_lit_memmove_long_emit_remainder_encodeBetterBlockAsmlarge_forward_sse_loop_32: + MOVOU -32(AX)(R8*1), X4 + MOVOU -16(AX)(R8*1), X5 + MOVOA X4, -32(CX)(R8*1) + MOVOA X5, -16(CX)(R8*1) + ADDQ $0x20, R8 + CMPQ BX, R8 + JAE emit_lit_memmove_long_emit_remainder_encodeBetterBlockAsmlarge_forward_sse_loop_32 + MOVOU X0, (CX) + MOVOU X1, 16(CX) + MOVOU X2, -32(CX)(BX*1) + MOVOU X3, -16(CX)(BX*1) + MOVQ DX, CX + +emit_literal_done_emit_remainder_encodeBetterBlockAsm: + MOVQ dst_base+0(FP), AX + SUBQ AX, CX + MOVQ CX, ret+56(FP) + RET + +// func encodeBetterBlockAsm4MB(dst []byte, src []byte, tmp *[589824]byte) int +// Requires: BMI, SSE2 +TEXT ·encodeBetterBlockAsm4MB(SB), $24-64 + MOVQ tmp+48(FP), AX + MOVQ dst_base+0(FP), CX + MOVQ $0x00001200, DX + MOVQ AX, BX + PXOR X0, X0 + +zero_loop_encodeBetterBlockAsm4MB: + MOVOU X0, (BX) + MOVOU X0, 16(BX) + MOVOU X0, 32(BX) + MOVOU X0, 48(BX) + MOVOU X0, 64(BX) + MOVOU X0, 80(BX) + MOVOU X0, 96(BX) + MOVOU X0, 112(BX) + ADDQ $0x80, BX + DECQ DX + JNZ zero_loop_encodeBetterBlockAsm4MB + MOVL $0x00000000, 12(SP) + MOVQ src_len+32(FP), DX + LEAQ -6(DX), BX + LEAQ -8(DX), SI + MOVL SI, 8(SP) + SHRQ $0x05, DX + SUBL DX, BX + LEAQ (CX)(BX*1), BX + MOVQ BX, (SP) + MOVL $0x00000001, DX + MOVL $0x00000000, 16(SP) + MOVQ src_base+24(FP), BX + +search_loop_encodeBetterBlockAsm4MB: + MOVL DX, SI + SUBL 12(SP), SI + SHRL $0x07, SI + CMPL SI, $0x63 + JBE check_maxskip_ok_encodeBetterBlockAsm4MB + LEAL 100(DX), SI + JMP check_maxskip_cont_encodeBetterBlockAsm4MB + +check_maxskip_ok_encodeBetterBlockAsm4MB: + LEAL 1(DX)(SI*1), SI + +check_maxskip_cont_encodeBetterBlockAsm4MB: + CMPL SI, 8(SP) + JAE emit_remainder_encodeBetterBlockAsm4MB + MOVQ (BX)(DX*1), DI + MOVL SI, 20(SP) + MOVQ $0x00cf1bbcdcbfa563, R9 + MOVQ $0x9e3779b1, SI + MOVQ DI, R10 + MOVQ DI, R11 + SHLQ $0x08, R10 + IMULQ R9, R10 + SHRQ $0x2f, R10 + SHLQ $0x20, R11 + IMULQ SI, R11 + SHRQ $0x32, R11 + MOVL (AX)(R10*4), SI + MOVL 524288(AX)(R11*4), R8 + MOVL DX, (AX)(R10*4) + MOVL DX, 524288(AX)(R11*4) + MOVQ (BX)(SI*1), R10 + MOVQ (BX)(R8*1), R11 + CMPQ R10, DI + JEQ candidate_match_encodeBetterBlockAsm4MB + CMPQ R11, DI + JNE no_short_found_encodeBetterBlockAsm4MB + MOVL R8, SI + JMP candidate_match_encodeBetterBlockAsm4MB + +no_short_found_encodeBetterBlockAsm4MB: + CMPL R10, DI + JEQ candidate_match_encodeBetterBlockAsm4MB + CMPL R11, DI + JEQ candidateS_match_encodeBetterBlockAsm4MB + MOVL 20(SP), DX + JMP search_loop_encodeBetterBlockAsm4MB + +candidateS_match_encodeBetterBlockAsm4MB: + SHRQ $0x08, DI + MOVQ DI, R10 + SHLQ $0x08, R10 + IMULQ R9, R10 + SHRQ $0x2f, R10 + MOVL (AX)(R10*4), SI + INCL DX + MOVL DX, (AX)(R10*4) + CMPL (BX)(SI*1), DI + JEQ candidate_match_encodeBetterBlockAsm4MB + DECL DX + MOVL R8, SI + +candidate_match_encodeBetterBlockAsm4MB: + MOVL 12(SP), DI + TESTL SI, SI + JZ match_extend_back_end_encodeBetterBlockAsm4MB + +match_extend_back_loop_encodeBetterBlockAsm4MB: + CMPL DX, DI + JBE match_extend_back_end_encodeBetterBlockAsm4MB + MOVB -1(BX)(SI*1), R8 + MOVB -1(BX)(DX*1), R9 + CMPB R8, R9 + JNE match_extend_back_end_encodeBetterBlockAsm4MB + LEAL -1(DX), DX + DECL SI + JZ match_extend_back_end_encodeBetterBlockAsm4MB + JMP match_extend_back_loop_encodeBetterBlockAsm4MB + +match_extend_back_end_encodeBetterBlockAsm4MB: + MOVL DX, DI + SUBL 12(SP), DI + LEAQ 4(CX)(DI*1), DI + CMPQ DI, (SP) + JB match_dst_size_check_encodeBetterBlockAsm4MB + MOVQ $0x00000000, ret+56(FP) + RET + +match_dst_size_check_encodeBetterBlockAsm4MB: + MOVL DX, DI + ADDL $0x04, DX + ADDL $0x04, SI + MOVQ src_len+32(FP), R8 + SUBL DX, R8 + LEAQ (BX)(DX*1), R9 + LEAQ (BX)(SI*1), R10 + + // matchLen + XORL R12, R12 + +matchlen_loopback_16_match_nolit_encodeBetterBlockAsm4MB: + CMPL R8, $0x10 + JB matchlen_match8_match_nolit_encodeBetterBlockAsm4MB + MOVQ (R9)(R12*1), R11 + MOVQ 8(R9)(R12*1), R13 + XORQ (R10)(R12*1), R11 + JNZ matchlen_bsf_8_match_nolit_encodeBetterBlockAsm4MB + XORQ 8(R10)(R12*1), R13 + JNZ matchlen_bsf_16match_nolit_encodeBetterBlockAsm4MB + LEAL -16(R8), R8 + LEAL 16(R12), R12 + JMP matchlen_loopback_16_match_nolit_encodeBetterBlockAsm4MB + +matchlen_bsf_16match_nolit_encodeBetterBlockAsm4MB: +#ifdef GOAMD64_v3 + TZCNTQ R13, R13 + +#else + BSFQ R13, R13 + +#endif + SARQ $0x03, R13 + LEAL 8(R12)(R13*1), R12 + JMP match_nolit_end_encodeBetterBlockAsm4MB + +matchlen_match8_match_nolit_encodeBetterBlockAsm4MB: + CMPL R8, $0x08 + JB matchlen_match4_match_nolit_encodeBetterBlockAsm4MB + MOVQ (R9)(R12*1), R11 + XORQ (R10)(R12*1), R11 + JNZ matchlen_bsf_8_match_nolit_encodeBetterBlockAsm4MB + LEAL -8(R8), R8 + LEAL 8(R12), R12 + JMP matchlen_match4_match_nolit_encodeBetterBlockAsm4MB + +matchlen_bsf_8_match_nolit_encodeBetterBlockAsm4MB: +#ifdef GOAMD64_v3 + TZCNTQ R11, R11 + +#else + BSFQ R11, R11 + +#endif + SARQ $0x03, R11 + LEAL (R12)(R11*1), R12 + JMP match_nolit_end_encodeBetterBlockAsm4MB + +matchlen_match4_match_nolit_encodeBetterBlockAsm4MB: + CMPL R8, $0x04 + JB matchlen_match2_match_nolit_encodeBetterBlockAsm4MB + MOVL (R9)(R12*1), R11 + CMPL (R10)(R12*1), R11 + JNE matchlen_match2_match_nolit_encodeBetterBlockAsm4MB + LEAL -4(R8), R8 + LEAL 4(R12), R12 + +matchlen_match2_match_nolit_encodeBetterBlockAsm4MB: + CMPL R8, $0x01 + JE matchlen_match1_match_nolit_encodeBetterBlockAsm4MB + JB match_nolit_end_encodeBetterBlockAsm4MB + MOVW (R9)(R12*1), R11 + CMPW (R10)(R12*1), R11 + JNE matchlen_match1_match_nolit_encodeBetterBlockAsm4MB + LEAL 2(R12), R12 + SUBL $0x02, R8 + JZ match_nolit_end_encodeBetterBlockAsm4MB + +matchlen_match1_match_nolit_encodeBetterBlockAsm4MB: + MOVB (R9)(R12*1), R11 + CMPB (R10)(R12*1), R11 + JNE match_nolit_end_encodeBetterBlockAsm4MB + LEAL 1(R12), R12 + +match_nolit_end_encodeBetterBlockAsm4MB: + MOVL DX, R8 + SUBL SI, R8 + + // Check if repeat + CMPL 16(SP), R8 + JEQ match_is_repeat_encodeBetterBlockAsm4MB + CMPL R12, $0x01 + JA match_length_ok_encodeBetterBlockAsm4MB + CMPL R8, $0x0000ffff + JBE match_length_ok_encodeBetterBlockAsm4MB + MOVL 20(SP), DX + INCL DX + JMP search_loop_encodeBetterBlockAsm4MB + +match_length_ok_encodeBetterBlockAsm4MB: + MOVL R8, 16(SP) + MOVL 12(SP), SI + CMPL SI, DI + JEQ emit_literal_done_match_emit_encodeBetterBlockAsm4MB + MOVL DI, R9 + MOVL DI, 12(SP) + LEAQ (BX)(SI*1), R10 + SUBL SI, R9 + LEAL -1(R9), SI + CMPL SI, $0x3c + JB one_byte_match_emit_encodeBetterBlockAsm4MB + CMPL SI, $0x00000100 + JB two_bytes_match_emit_encodeBetterBlockAsm4MB + CMPL SI, $0x00010000 + JB three_bytes_match_emit_encodeBetterBlockAsm4MB + MOVL SI, R11 + SHRL $0x10, R11 + MOVB $0xf8, (CX) + MOVW SI, 1(CX) + MOVB R11, 3(CX) + ADDQ $0x04, CX + JMP memmove_long_match_emit_encodeBetterBlockAsm4MB + +three_bytes_match_emit_encodeBetterBlockAsm4MB: + MOVB $0xf4, (CX) + MOVW SI, 1(CX) + ADDQ $0x03, CX + JMP memmove_long_match_emit_encodeBetterBlockAsm4MB + +two_bytes_match_emit_encodeBetterBlockAsm4MB: + MOVB $0xf0, (CX) + MOVB SI, 1(CX) + ADDQ $0x02, CX + CMPL SI, $0x40 + JB memmove_match_emit_encodeBetterBlockAsm4MB + JMP memmove_long_match_emit_encodeBetterBlockAsm4MB + +one_byte_match_emit_encodeBetterBlockAsm4MB: + SHLB $0x02, SI + MOVB SI, (CX) + ADDQ $0x01, CX + +memmove_match_emit_encodeBetterBlockAsm4MB: + LEAQ (CX)(R9*1), SI + + // genMemMoveShort + CMPQ R9, $0x04 + JBE emit_lit_memmove_match_emit_encodeBetterBlockAsm4MB_memmove_move_4 + CMPQ R9, $0x08 + JB emit_lit_memmove_match_emit_encodeBetterBlockAsm4MB_memmove_move_4through7 + CMPQ R9, $0x10 + JBE emit_lit_memmove_match_emit_encodeBetterBlockAsm4MB_memmove_move_8through16 + CMPQ R9, $0x20 + JBE emit_lit_memmove_match_emit_encodeBetterBlockAsm4MB_memmove_move_17through32 + JMP emit_lit_memmove_match_emit_encodeBetterBlockAsm4MB_memmove_move_33through64 + +emit_lit_memmove_match_emit_encodeBetterBlockAsm4MB_memmove_move_4: + MOVL (R10), R11 + MOVL R11, (CX) + JMP memmove_end_copy_match_emit_encodeBetterBlockAsm4MB + +emit_lit_memmove_match_emit_encodeBetterBlockAsm4MB_memmove_move_4through7: + MOVL (R10), R11 + MOVL -4(R10)(R9*1), R10 + MOVL R11, (CX) + MOVL R10, -4(CX)(R9*1) + JMP memmove_end_copy_match_emit_encodeBetterBlockAsm4MB + +emit_lit_memmove_match_emit_encodeBetterBlockAsm4MB_memmove_move_8through16: + MOVQ (R10), R11 + MOVQ -8(R10)(R9*1), R10 + MOVQ R11, (CX) + MOVQ R10, -8(CX)(R9*1) + JMP memmove_end_copy_match_emit_encodeBetterBlockAsm4MB + +emit_lit_memmove_match_emit_encodeBetterBlockAsm4MB_memmove_move_17through32: + MOVOU (R10), X0 + MOVOU -16(R10)(R9*1), X1 + MOVOU X0, (CX) + MOVOU X1, -16(CX)(R9*1) + JMP memmove_end_copy_match_emit_encodeBetterBlockAsm4MB + +emit_lit_memmove_match_emit_encodeBetterBlockAsm4MB_memmove_move_33through64: + MOVOU (R10), X0 + MOVOU 16(R10), X1 + MOVOU -32(R10)(R9*1), X2 + MOVOU -16(R10)(R9*1), X3 + MOVOU X0, (CX) + MOVOU X1, 16(CX) + MOVOU X2, -32(CX)(R9*1) + MOVOU X3, -16(CX)(R9*1) + +memmove_end_copy_match_emit_encodeBetterBlockAsm4MB: + MOVQ SI, CX + JMP emit_literal_done_match_emit_encodeBetterBlockAsm4MB + +memmove_long_match_emit_encodeBetterBlockAsm4MB: + LEAQ (CX)(R9*1), SI + + // genMemMoveLong + MOVOU (R10), X0 + MOVOU 16(R10), X1 + MOVOU -32(R10)(R9*1), X2 + MOVOU -16(R10)(R9*1), X3 + MOVQ R9, R13 + SHRQ $0x05, R13 + MOVQ CX, R11 + ANDL $0x0000001f, R11 + MOVQ $0x00000040, R14 + SUBQ R11, R14 + DECQ R13 + JA emit_lit_memmove_long_match_emit_encodeBetterBlockAsm4MBlarge_forward_sse_loop_32 + LEAQ -32(R10)(R14*1), R11 + LEAQ -32(CX)(R14*1), R15 + +emit_lit_memmove_long_match_emit_encodeBetterBlockAsm4MBlarge_big_loop_back: + MOVOU (R11), X4 + MOVOU 16(R11), X5 + MOVOA X4, (R15) + MOVOA X5, 16(R15) + ADDQ $0x20, R15 + ADDQ $0x20, R11 + ADDQ $0x20, R14 + DECQ R13 + JNA emit_lit_memmove_long_match_emit_encodeBetterBlockAsm4MBlarge_big_loop_back + +emit_lit_memmove_long_match_emit_encodeBetterBlockAsm4MBlarge_forward_sse_loop_32: + MOVOU -32(R10)(R14*1), X4 + MOVOU -16(R10)(R14*1), X5 + MOVOA X4, -32(CX)(R14*1) + MOVOA X5, -16(CX)(R14*1) + ADDQ $0x20, R14 + CMPQ R9, R14 + JAE emit_lit_memmove_long_match_emit_encodeBetterBlockAsm4MBlarge_forward_sse_loop_32 + MOVOU X0, (CX) + MOVOU X1, 16(CX) + MOVOU X2, -32(CX)(R9*1) + MOVOU X3, -16(CX)(R9*1) + MOVQ SI, CX + +emit_literal_done_match_emit_encodeBetterBlockAsm4MB: + ADDL R12, DX + ADDL $0x04, R12 + MOVL DX, 12(SP) + + // emitCopy + CMPL R8, $0x00010000 + JB two_byte_offset_match_nolit_encodeBetterBlockAsm4MB + CMPL R12, $0x40 + JBE four_bytes_remain_match_nolit_encodeBetterBlockAsm4MB + MOVB $0xff, (CX) + MOVL R8, 1(CX) + LEAL -64(R12), R12 + ADDQ $0x05, CX + CMPL R12, $0x04 + JB four_bytes_remain_match_nolit_encodeBetterBlockAsm4MB + + // emitRepeat + MOVL R12, SI + LEAL -4(R12), R12 + CMPL SI, $0x08 + JBE repeat_two_match_nolit_encodeBetterBlockAsm4MB_emit_copy + CMPL SI, $0x0c + JAE cant_repeat_two_offset_match_nolit_encodeBetterBlockAsm4MB_emit_copy + CMPL R8, $0x00000800 + JB repeat_two_offset_match_nolit_encodeBetterBlockAsm4MB_emit_copy + +cant_repeat_two_offset_match_nolit_encodeBetterBlockAsm4MB_emit_copy: + CMPL R12, $0x00000104 + JB repeat_three_match_nolit_encodeBetterBlockAsm4MB_emit_copy + CMPL R12, $0x00010100 + JB repeat_four_match_nolit_encodeBetterBlockAsm4MB_emit_copy + LEAL -65536(R12), R12 + MOVL R12, R8 + MOVW $0x001d, (CX) + MOVW R12, 2(CX) + SARL $0x10, R8 + MOVB R8, 4(CX) + ADDQ $0x05, CX + JMP match_nolit_emitcopy_end_encodeBetterBlockAsm4MB + +repeat_four_match_nolit_encodeBetterBlockAsm4MB_emit_copy: + LEAL -256(R12), R12 + MOVW $0x0019, (CX) + MOVW R12, 2(CX) + ADDQ $0x04, CX + JMP match_nolit_emitcopy_end_encodeBetterBlockAsm4MB + +repeat_three_match_nolit_encodeBetterBlockAsm4MB_emit_copy: + LEAL -4(R12), R12 + MOVW $0x0015, (CX) + MOVB R12, 2(CX) + ADDQ $0x03, CX + JMP match_nolit_emitcopy_end_encodeBetterBlockAsm4MB + +repeat_two_match_nolit_encodeBetterBlockAsm4MB_emit_copy: + SHLL $0x02, R12 + ORL $0x01, R12 + MOVW R12, (CX) + ADDQ $0x02, CX + JMP match_nolit_emitcopy_end_encodeBetterBlockAsm4MB + +repeat_two_offset_match_nolit_encodeBetterBlockAsm4MB_emit_copy: + XORQ SI, SI + LEAL 1(SI)(R12*4), R12 + MOVB R8, 1(CX) + SARL $0x08, R8 + SHLL $0x05, R8 + ORL R8, R12 + MOVB R12, (CX) + ADDQ $0x02, CX + JMP match_nolit_emitcopy_end_encodeBetterBlockAsm4MB + +four_bytes_remain_match_nolit_encodeBetterBlockAsm4MB: + TESTL R12, R12 + JZ match_nolit_emitcopy_end_encodeBetterBlockAsm4MB + XORL SI, SI + LEAL -1(SI)(R12*4), R12 + MOVB R12, (CX) + MOVL R8, 1(CX) + ADDQ $0x05, CX + JMP match_nolit_emitcopy_end_encodeBetterBlockAsm4MB + +two_byte_offset_match_nolit_encodeBetterBlockAsm4MB: + CMPL R12, $0x40 + JBE two_byte_offset_short_match_nolit_encodeBetterBlockAsm4MB + CMPL R8, $0x00000800 + JAE long_offset_short_match_nolit_encodeBetterBlockAsm4MB + MOVL $0x00000001, SI + LEAL 16(SI), SI + MOVB R8, 1(CX) + SHRL $0x08, R8 + SHLL $0x05, R8 + ORL R8, SI + MOVB SI, (CX) + ADDQ $0x02, CX + SUBL $0x08, R12 + + // emitRepeat + LEAL -4(R12), R12 + JMP cant_repeat_two_offset_match_nolit_encodeBetterBlockAsm4MB_emit_copy_short_2b + MOVL R12, SI + LEAL -4(R12), R12 + CMPL SI, $0x08 + JBE repeat_two_match_nolit_encodeBetterBlockAsm4MB_emit_copy_short_2b + CMPL SI, $0x0c + JAE cant_repeat_two_offset_match_nolit_encodeBetterBlockAsm4MB_emit_copy_short_2b + CMPL R8, $0x00000800 + JB repeat_two_offset_match_nolit_encodeBetterBlockAsm4MB_emit_copy_short_2b + +cant_repeat_two_offset_match_nolit_encodeBetterBlockAsm4MB_emit_copy_short_2b: + CMPL R12, $0x00000104 + JB repeat_three_match_nolit_encodeBetterBlockAsm4MB_emit_copy_short_2b + CMPL R12, $0x00010100 + JB repeat_four_match_nolit_encodeBetterBlockAsm4MB_emit_copy_short_2b + LEAL -65536(R12), R12 + MOVL R12, R8 + MOVW $0x001d, (CX) + MOVW R12, 2(CX) + SARL $0x10, R8 + MOVB R8, 4(CX) + ADDQ $0x05, CX + JMP match_nolit_emitcopy_end_encodeBetterBlockAsm4MB + +repeat_four_match_nolit_encodeBetterBlockAsm4MB_emit_copy_short_2b: + LEAL -256(R12), R12 + MOVW $0x0019, (CX) + MOVW R12, 2(CX) + ADDQ $0x04, CX + JMP match_nolit_emitcopy_end_encodeBetterBlockAsm4MB + +repeat_three_match_nolit_encodeBetterBlockAsm4MB_emit_copy_short_2b: + LEAL -4(R12), R12 + MOVW $0x0015, (CX) + MOVB R12, 2(CX) + ADDQ $0x03, CX + JMP match_nolit_emitcopy_end_encodeBetterBlockAsm4MB + +repeat_two_match_nolit_encodeBetterBlockAsm4MB_emit_copy_short_2b: + SHLL $0x02, R12 + ORL $0x01, R12 + MOVW R12, (CX) + ADDQ $0x02, CX + JMP match_nolit_emitcopy_end_encodeBetterBlockAsm4MB + +repeat_two_offset_match_nolit_encodeBetterBlockAsm4MB_emit_copy_short_2b: + XORQ SI, SI + LEAL 1(SI)(R12*4), R12 + MOVB R8, 1(CX) + SARL $0x08, R8 + SHLL $0x05, R8 + ORL R8, R12 + MOVB R12, (CX) + ADDQ $0x02, CX + JMP match_nolit_emitcopy_end_encodeBetterBlockAsm4MB + +long_offset_short_match_nolit_encodeBetterBlockAsm4MB: + MOVB $0xee, (CX) + MOVW R8, 1(CX) + LEAL -60(R12), R12 + ADDQ $0x03, CX + + // emitRepeat + MOVL R12, SI + LEAL -4(R12), R12 + CMPL SI, $0x08 + JBE repeat_two_match_nolit_encodeBetterBlockAsm4MB_emit_copy_short + CMPL SI, $0x0c + JAE cant_repeat_two_offset_match_nolit_encodeBetterBlockAsm4MB_emit_copy_short + CMPL R8, $0x00000800 + JB repeat_two_offset_match_nolit_encodeBetterBlockAsm4MB_emit_copy_short + +cant_repeat_two_offset_match_nolit_encodeBetterBlockAsm4MB_emit_copy_short: + CMPL R12, $0x00000104 + JB repeat_three_match_nolit_encodeBetterBlockAsm4MB_emit_copy_short + CMPL R12, $0x00010100 + JB repeat_four_match_nolit_encodeBetterBlockAsm4MB_emit_copy_short + LEAL -65536(R12), R12 + MOVL R12, R8 + MOVW $0x001d, (CX) + MOVW R12, 2(CX) + SARL $0x10, R8 + MOVB R8, 4(CX) + ADDQ $0x05, CX + JMP match_nolit_emitcopy_end_encodeBetterBlockAsm4MB + +repeat_four_match_nolit_encodeBetterBlockAsm4MB_emit_copy_short: + LEAL -256(R12), R12 + MOVW $0x0019, (CX) + MOVW R12, 2(CX) + ADDQ $0x04, CX + JMP match_nolit_emitcopy_end_encodeBetterBlockAsm4MB + +repeat_three_match_nolit_encodeBetterBlockAsm4MB_emit_copy_short: + LEAL -4(R12), R12 + MOVW $0x0015, (CX) + MOVB R12, 2(CX) + ADDQ $0x03, CX + JMP match_nolit_emitcopy_end_encodeBetterBlockAsm4MB + +repeat_two_match_nolit_encodeBetterBlockAsm4MB_emit_copy_short: + SHLL $0x02, R12 + ORL $0x01, R12 + MOVW R12, (CX) + ADDQ $0x02, CX + JMP match_nolit_emitcopy_end_encodeBetterBlockAsm4MB + +repeat_two_offset_match_nolit_encodeBetterBlockAsm4MB_emit_copy_short: + XORQ SI, SI + LEAL 1(SI)(R12*4), R12 + MOVB R8, 1(CX) + SARL $0x08, R8 + SHLL $0x05, R8 + ORL R8, R12 + MOVB R12, (CX) + ADDQ $0x02, CX + JMP match_nolit_emitcopy_end_encodeBetterBlockAsm4MB + +two_byte_offset_short_match_nolit_encodeBetterBlockAsm4MB: + MOVL R12, SI + SHLL $0x02, SI + CMPL R12, $0x0c + JAE emit_copy_three_match_nolit_encodeBetterBlockAsm4MB + CMPL R8, $0x00000800 + JAE emit_copy_three_match_nolit_encodeBetterBlockAsm4MB + LEAL -15(SI), SI + MOVB R8, 1(CX) + SHRL $0x08, R8 + SHLL $0x05, R8 + ORL R8, SI + MOVB SI, (CX) + ADDQ $0x02, CX + JMP match_nolit_emitcopy_end_encodeBetterBlockAsm4MB + +emit_copy_three_match_nolit_encodeBetterBlockAsm4MB: + LEAL -2(SI), SI + MOVB SI, (CX) + MOVW R8, 1(CX) + ADDQ $0x03, CX + JMP match_nolit_emitcopy_end_encodeBetterBlockAsm4MB + +match_is_repeat_encodeBetterBlockAsm4MB: + MOVL 12(SP), SI + CMPL SI, DI + JEQ emit_literal_done_match_emit_repeat_encodeBetterBlockAsm4MB + MOVL DI, R9 + MOVL DI, 12(SP) + LEAQ (BX)(SI*1), R10 + SUBL SI, R9 + LEAL -1(R9), SI + CMPL SI, $0x3c + JB one_byte_match_emit_repeat_encodeBetterBlockAsm4MB + CMPL SI, $0x00000100 + JB two_bytes_match_emit_repeat_encodeBetterBlockAsm4MB + CMPL SI, $0x00010000 + JB three_bytes_match_emit_repeat_encodeBetterBlockAsm4MB + MOVL SI, R11 + SHRL $0x10, R11 + MOVB $0xf8, (CX) + MOVW SI, 1(CX) + MOVB R11, 3(CX) + ADDQ $0x04, CX + JMP memmove_long_match_emit_repeat_encodeBetterBlockAsm4MB + +three_bytes_match_emit_repeat_encodeBetterBlockAsm4MB: + MOVB $0xf4, (CX) + MOVW SI, 1(CX) + ADDQ $0x03, CX + JMP memmove_long_match_emit_repeat_encodeBetterBlockAsm4MB + +two_bytes_match_emit_repeat_encodeBetterBlockAsm4MB: + MOVB $0xf0, (CX) + MOVB SI, 1(CX) + ADDQ $0x02, CX + CMPL SI, $0x40 + JB memmove_match_emit_repeat_encodeBetterBlockAsm4MB + JMP memmove_long_match_emit_repeat_encodeBetterBlockAsm4MB + +one_byte_match_emit_repeat_encodeBetterBlockAsm4MB: + SHLB $0x02, SI + MOVB SI, (CX) + ADDQ $0x01, CX + +memmove_match_emit_repeat_encodeBetterBlockAsm4MB: + LEAQ (CX)(R9*1), SI + + // genMemMoveShort + CMPQ R9, $0x04 + JBE emit_lit_memmove_match_emit_repeat_encodeBetterBlockAsm4MB_memmove_move_4 + CMPQ R9, $0x08 + JB emit_lit_memmove_match_emit_repeat_encodeBetterBlockAsm4MB_memmove_move_4through7 + CMPQ R9, $0x10 + JBE emit_lit_memmove_match_emit_repeat_encodeBetterBlockAsm4MB_memmove_move_8through16 + CMPQ R9, $0x20 + JBE emit_lit_memmove_match_emit_repeat_encodeBetterBlockAsm4MB_memmove_move_17through32 + JMP emit_lit_memmove_match_emit_repeat_encodeBetterBlockAsm4MB_memmove_move_33through64 + +emit_lit_memmove_match_emit_repeat_encodeBetterBlockAsm4MB_memmove_move_4: + MOVL (R10), R11 + MOVL R11, (CX) + JMP memmove_end_copy_match_emit_repeat_encodeBetterBlockAsm4MB + +emit_lit_memmove_match_emit_repeat_encodeBetterBlockAsm4MB_memmove_move_4through7: + MOVL (R10), R11 + MOVL -4(R10)(R9*1), R10 + MOVL R11, (CX) + MOVL R10, -4(CX)(R9*1) + JMP memmove_end_copy_match_emit_repeat_encodeBetterBlockAsm4MB + +emit_lit_memmove_match_emit_repeat_encodeBetterBlockAsm4MB_memmove_move_8through16: + MOVQ (R10), R11 + MOVQ -8(R10)(R9*1), R10 + MOVQ R11, (CX) + MOVQ R10, -8(CX)(R9*1) + JMP memmove_end_copy_match_emit_repeat_encodeBetterBlockAsm4MB + +emit_lit_memmove_match_emit_repeat_encodeBetterBlockAsm4MB_memmove_move_17through32: + MOVOU (R10), X0 + MOVOU -16(R10)(R9*1), X1 + MOVOU X0, (CX) + MOVOU X1, -16(CX)(R9*1) + JMP memmove_end_copy_match_emit_repeat_encodeBetterBlockAsm4MB + +emit_lit_memmove_match_emit_repeat_encodeBetterBlockAsm4MB_memmove_move_33through64: + MOVOU (R10), X0 + MOVOU 16(R10), X1 + MOVOU -32(R10)(R9*1), X2 + MOVOU -16(R10)(R9*1), X3 + MOVOU X0, (CX) + MOVOU X1, 16(CX) + MOVOU X2, -32(CX)(R9*1) + MOVOU X3, -16(CX)(R9*1) + +memmove_end_copy_match_emit_repeat_encodeBetterBlockAsm4MB: + MOVQ SI, CX + JMP emit_literal_done_match_emit_repeat_encodeBetterBlockAsm4MB + +memmove_long_match_emit_repeat_encodeBetterBlockAsm4MB: + LEAQ (CX)(R9*1), SI + + // genMemMoveLong + MOVOU (R10), X0 + MOVOU 16(R10), X1 + MOVOU -32(R10)(R9*1), X2 + MOVOU -16(R10)(R9*1), X3 + MOVQ R9, R13 + SHRQ $0x05, R13 + MOVQ CX, R11 + ANDL $0x0000001f, R11 + MOVQ $0x00000040, R14 + SUBQ R11, R14 + DECQ R13 + JA emit_lit_memmove_long_match_emit_repeat_encodeBetterBlockAsm4MBlarge_forward_sse_loop_32 + LEAQ -32(R10)(R14*1), R11 + LEAQ -32(CX)(R14*1), R15 + +emit_lit_memmove_long_match_emit_repeat_encodeBetterBlockAsm4MBlarge_big_loop_back: + MOVOU (R11), X4 + MOVOU 16(R11), X5 + MOVOA X4, (R15) + MOVOA X5, 16(R15) + ADDQ $0x20, R15 + ADDQ $0x20, R11 + ADDQ $0x20, R14 + DECQ R13 + JNA emit_lit_memmove_long_match_emit_repeat_encodeBetterBlockAsm4MBlarge_big_loop_back + +emit_lit_memmove_long_match_emit_repeat_encodeBetterBlockAsm4MBlarge_forward_sse_loop_32: + MOVOU -32(R10)(R14*1), X4 + MOVOU -16(R10)(R14*1), X5 + MOVOA X4, -32(CX)(R14*1) + MOVOA X5, -16(CX)(R14*1) + ADDQ $0x20, R14 + CMPQ R9, R14 + JAE emit_lit_memmove_long_match_emit_repeat_encodeBetterBlockAsm4MBlarge_forward_sse_loop_32 + MOVOU X0, (CX) + MOVOU X1, 16(CX) + MOVOU X2, -32(CX)(R9*1) + MOVOU X3, -16(CX)(R9*1) + MOVQ SI, CX + +emit_literal_done_match_emit_repeat_encodeBetterBlockAsm4MB: + ADDL R12, DX + ADDL $0x04, R12 + MOVL DX, 12(SP) + + // emitRepeat + MOVL R12, SI + LEAL -4(R12), R12 + CMPL SI, $0x08 + JBE repeat_two_match_nolit_repeat_encodeBetterBlockAsm4MB + CMPL SI, $0x0c + JAE cant_repeat_two_offset_match_nolit_repeat_encodeBetterBlockAsm4MB + CMPL R8, $0x00000800 + JB repeat_two_offset_match_nolit_repeat_encodeBetterBlockAsm4MB + +cant_repeat_two_offset_match_nolit_repeat_encodeBetterBlockAsm4MB: + CMPL R12, $0x00000104 + JB repeat_three_match_nolit_repeat_encodeBetterBlockAsm4MB + CMPL R12, $0x00010100 + JB repeat_four_match_nolit_repeat_encodeBetterBlockAsm4MB + LEAL -65536(R12), R12 + MOVL R12, R8 + MOVW $0x001d, (CX) + MOVW R12, 2(CX) + SARL $0x10, R8 + MOVB R8, 4(CX) + ADDQ $0x05, CX + JMP match_nolit_emitcopy_end_encodeBetterBlockAsm4MB + +repeat_four_match_nolit_repeat_encodeBetterBlockAsm4MB: + LEAL -256(R12), R12 + MOVW $0x0019, (CX) + MOVW R12, 2(CX) + ADDQ $0x04, CX + JMP match_nolit_emitcopy_end_encodeBetterBlockAsm4MB + +repeat_three_match_nolit_repeat_encodeBetterBlockAsm4MB: + LEAL -4(R12), R12 + MOVW $0x0015, (CX) + MOVB R12, 2(CX) + ADDQ $0x03, CX + JMP match_nolit_emitcopy_end_encodeBetterBlockAsm4MB + +repeat_two_match_nolit_repeat_encodeBetterBlockAsm4MB: + SHLL $0x02, R12 + ORL $0x01, R12 + MOVW R12, (CX) + ADDQ $0x02, CX + JMP match_nolit_emitcopy_end_encodeBetterBlockAsm4MB + +repeat_two_offset_match_nolit_repeat_encodeBetterBlockAsm4MB: + XORQ SI, SI + LEAL 1(SI)(R12*4), R12 + MOVB R8, 1(CX) + SARL $0x08, R8 + SHLL $0x05, R8 + ORL R8, R12 + MOVB R12, (CX) + ADDQ $0x02, CX + +match_nolit_emitcopy_end_encodeBetterBlockAsm4MB: + CMPL DX, 8(SP) + JAE emit_remainder_encodeBetterBlockAsm4MB + CMPQ CX, (SP) + JB match_nolit_dst_ok_encodeBetterBlockAsm4MB + MOVQ $0x00000000, ret+56(FP) + RET + +match_nolit_dst_ok_encodeBetterBlockAsm4MB: + MOVQ $0x00cf1bbcdcbfa563, SI + MOVQ $0x9e3779b1, R8 + LEAQ 1(DI), DI + LEAQ -2(DX), R9 + MOVQ (BX)(DI*1), R10 + MOVQ 1(BX)(DI*1), R11 + MOVQ (BX)(R9*1), R12 + MOVQ 1(BX)(R9*1), R13 + SHLQ $0x08, R10 + IMULQ SI, R10 + SHRQ $0x2f, R10 + SHLQ $0x20, R11 + IMULQ R8, R11 + SHRQ $0x32, R11 + SHLQ $0x08, R12 + IMULQ SI, R12 + SHRQ $0x2f, R12 + SHLQ $0x20, R13 + IMULQ R8, R13 + SHRQ $0x32, R13 + LEAQ 1(DI), R8 + LEAQ 1(R9), R14 + MOVL DI, (AX)(R10*4) + MOVL R9, (AX)(R12*4) + MOVL R8, 524288(AX)(R11*4) + MOVL R14, 524288(AX)(R13*4) + LEAQ 1(R9)(DI*1), R8 + SHRQ $0x01, R8 + ADDQ $0x01, DI + SUBQ $0x01, R9 + +index_loop_encodeBetterBlockAsm4MB: + CMPQ R8, R9 + JAE search_loop_encodeBetterBlockAsm4MB + MOVQ (BX)(DI*1), R10 + MOVQ (BX)(R8*1), R11 + SHLQ $0x08, R10 + IMULQ SI, R10 + SHRQ $0x2f, R10 + SHLQ $0x08, R11 + IMULQ SI, R11 + SHRQ $0x2f, R11 + MOVL DI, (AX)(R10*4) + MOVL R8, (AX)(R11*4) + ADDQ $0x02, DI + ADDQ $0x02, R8 + JMP index_loop_encodeBetterBlockAsm4MB + +emit_remainder_encodeBetterBlockAsm4MB: + MOVQ src_len+32(FP), AX + SUBL 12(SP), AX + LEAQ 4(CX)(AX*1), AX + CMPQ AX, (SP) + JB emit_remainder_ok_encodeBetterBlockAsm4MB + MOVQ $0x00000000, ret+56(FP) + RET + +emit_remainder_ok_encodeBetterBlockAsm4MB: + MOVQ src_len+32(FP), AX + MOVL 12(SP), DX + CMPL DX, AX + JEQ emit_literal_done_emit_remainder_encodeBetterBlockAsm4MB + MOVL AX, SI + MOVL AX, 12(SP) + LEAQ (BX)(DX*1), AX + SUBL DX, SI + LEAL -1(SI), DX + CMPL DX, $0x3c + JB one_byte_emit_remainder_encodeBetterBlockAsm4MB + CMPL DX, $0x00000100 + JB two_bytes_emit_remainder_encodeBetterBlockAsm4MB + CMPL DX, $0x00010000 + JB three_bytes_emit_remainder_encodeBetterBlockAsm4MB + MOVL DX, BX + SHRL $0x10, BX + MOVB $0xf8, (CX) + MOVW DX, 1(CX) + MOVB BL, 3(CX) + ADDQ $0x04, CX + JMP memmove_long_emit_remainder_encodeBetterBlockAsm4MB + +three_bytes_emit_remainder_encodeBetterBlockAsm4MB: + MOVB $0xf4, (CX) + MOVW DX, 1(CX) + ADDQ $0x03, CX + JMP memmove_long_emit_remainder_encodeBetterBlockAsm4MB + +two_bytes_emit_remainder_encodeBetterBlockAsm4MB: + MOVB $0xf0, (CX) + MOVB DL, 1(CX) + ADDQ $0x02, CX + CMPL DX, $0x40 + JB memmove_emit_remainder_encodeBetterBlockAsm4MB + JMP memmove_long_emit_remainder_encodeBetterBlockAsm4MB + +one_byte_emit_remainder_encodeBetterBlockAsm4MB: + SHLB $0x02, DL + MOVB DL, (CX) + ADDQ $0x01, CX + +memmove_emit_remainder_encodeBetterBlockAsm4MB: + LEAQ (CX)(SI*1), DX + MOVL SI, BX + + // genMemMoveShort + CMPQ BX, $0x03 + JB emit_lit_memmove_emit_remainder_encodeBetterBlockAsm4MB_memmove_move_1or2 + JE emit_lit_memmove_emit_remainder_encodeBetterBlockAsm4MB_memmove_move_3 + CMPQ BX, $0x08 + JB emit_lit_memmove_emit_remainder_encodeBetterBlockAsm4MB_memmove_move_4through7 + CMPQ BX, $0x10 + JBE emit_lit_memmove_emit_remainder_encodeBetterBlockAsm4MB_memmove_move_8through16 + CMPQ BX, $0x20 + JBE emit_lit_memmove_emit_remainder_encodeBetterBlockAsm4MB_memmove_move_17through32 + JMP emit_lit_memmove_emit_remainder_encodeBetterBlockAsm4MB_memmove_move_33through64 + +emit_lit_memmove_emit_remainder_encodeBetterBlockAsm4MB_memmove_move_1or2: + MOVB (AX), SI + MOVB -1(AX)(BX*1), AL + MOVB SI, (CX) + MOVB AL, -1(CX)(BX*1) + JMP memmove_end_copy_emit_remainder_encodeBetterBlockAsm4MB + +emit_lit_memmove_emit_remainder_encodeBetterBlockAsm4MB_memmove_move_3: + MOVW (AX), SI + MOVB 2(AX), AL + MOVW SI, (CX) + MOVB AL, 2(CX) + JMP memmove_end_copy_emit_remainder_encodeBetterBlockAsm4MB + +emit_lit_memmove_emit_remainder_encodeBetterBlockAsm4MB_memmove_move_4through7: + MOVL (AX), SI + MOVL -4(AX)(BX*1), AX + MOVL SI, (CX) + MOVL AX, -4(CX)(BX*1) + JMP memmove_end_copy_emit_remainder_encodeBetterBlockAsm4MB + +emit_lit_memmove_emit_remainder_encodeBetterBlockAsm4MB_memmove_move_8through16: + MOVQ (AX), SI + MOVQ -8(AX)(BX*1), AX + MOVQ SI, (CX) + MOVQ AX, -8(CX)(BX*1) + JMP memmove_end_copy_emit_remainder_encodeBetterBlockAsm4MB + +emit_lit_memmove_emit_remainder_encodeBetterBlockAsm4MB_memmove_move_17through32: + MOVOU (AX), X0 + MOVOU -16(AX)(BX*1), X1 + MOVOU X0, (CX) + MOVOU X1, -16(CX)(BX*1) + JMP memmove_end_copy_emit_remainder_encodeBetterBlockAsm4MB + +emit_lit_memmove_emit_remainder_encodeBetterBlockAsm4MB_memmove_move_33through64: + MOVOU (AX), X0 + MOVOU 16(AX), X1 + MOVOU -32(AX)(BX*1), X2 + MOVOU -16(AX)(BX*1), X3 + MOVOU X0, (CX) + MOVOU X1, 16(CX) + MOVOU X2, -32(CX)(BX*1) + MOVOU X3, -16(CX)(BX*1) + +memmove_end_copy_emit_remainder_encodeBetterBlockAsm4MB: + MOVQ DX, CX + JMP emit_literal_done_emit_remainder_encodeBetterBlockAsm4MB + +memmove_long_emit_remainder_encodeBetterBlockAsm4MB: + LEAQ (CX)(SI*1), DX + MOVL SI, BX + + // genMemMoveLong + MOVOU (AX), X0 + MOVOU 16(AX), X1 + MOVOU -32(AX)(BX*1), X2 + MOVOU -16(AX)(BX*1), X3 + MOVQ BX, DI + SHRQ $0x05, DI + MOVQ CX, SI + ANDL $0x0000001f, SI + MOVQ $0x00000040, R8 + SUBQ SI, R8 + DECQ DI + JA emit_lit_memmove_long_emit_remainder_encodeBetterBlockAsm4MBlarge_forward_sse_loop_32 + LEAQ -32(AX)(R8*1), SI + LEAQ -32(CX)(R8*1), R9 + +emit_lit_memmove_long_emit_remainder_encodeBetterBlockAsm4MBlarge_big_loop_back: + MOVOU (SI), X4 + MOVOU 16(SI), X5 + MOVOA X4, (R9) + MOVOA X5, 16(R9) + ADDQ $0x20, R9 + ADDQ $0x20, SI + ADDQ $0x20, R8 + DECQ DI + JNA emit_lit_memmove_long_emit_remainder_encodeBetterBlockAsm4MBlarge_big_loop_back + +emit_lit_memmove_long_emit_remainder_encodeBetterBlockAsm4MBlarge_forward_sse_loop_32: + MOVOU -32(AX)(R8*1), X4 + MOVOU -16(AX)(R8*1), X5 + MOVOA X4, -32(CX)(R8*1) + MOVOA X5, -16(CX)(R8*1) + ADDQ $0x20, R8 + CMPQ BX, R8 + JAE emit_lit_memmove_long_emit_remainder_encodeBetterBlockAsm4MBlarge_forward_sse_loop_32 + MOVOU X0, (CX) + MOVOU X1, 16(CX) + MOVOU X2, -32(CX)(BX*1) + MOVOU X3, -16(CX)(BX*1) + MOVQ DX, CX + +emit_literal_done_emit_remainder_encodeBetterBlockAsm4MB: + MOVQ dst_base+0(FP), AX + SUBQ AX, CX + MOVQ CX, ret+56(FP) + RET + +// func encodeBetterBlockAsm12B(dst []byte, src []byte, tmp *[81920]byte) int +// Requires: BMI, SSE2 +TEXT ·encodeBetterBlockAsm12B(SB), $24-64 + MOVQ tmp+48(FP), AX + MOVQ dst_base+0(FP), CX + MOVQ $0x00000280, DX + MOVQ AX, BX + PXOR X0, X0 + +zero_loop_encodeBetterBlockAsm12B: + MOVOU X0, (BX) + MOVOU X0, 16(BX) + MOVOU X0, 32(BX) + MOVOU X0, 48(BX) + MOVOU X0, 64(BX) + MOVOU X0, 80(BX) + MOVOU X0, 96(BX) + MOVOU X0, 112(BX) + ADDQ $0x80, BX + DECQ DX + JNZ zero_loop_encodeBetterBlockAsm12B + MOVL $0x00000000, 12(SP) + MOVQ src_len+32(FP), DX + LEAQ -6(DX), BX + LEAQ -8(DX), SI + MOVL SI, 8(SP) + SHRQ $0x05, DX + SUBL DX, BX + LEAQ (CX)(BX*1), BX + MOVQ BX, (SP) + MOVL $0x00000001, DX + MOVL $0x00000000, 16(SP) + MOVQ src_base+24(FP), BX + +search_loop_encodeBetterBlockAsm12B: + MOVL DX, SI + SUBL 12(SP), SI + SHRL $0x06, SI + LEAL 1(DX)(SI*1), SI + CMPL SI, 8(SP) + JAE emit_remainder_encodeBetterBlockAsm12B + MOVQ (BX)(DX*1), DI + MOVL SI, 20(SP) + MOVQ $0x0000cf1bbcdcbf9b, R9 + MOVQ $0x9e3779b1, SI + MOVQ DI, R10 + MOVQ DI, R11 + SHLQ $0x10, R10 + IMULQ R9, R10 + SHRQ $0x32, R10 + SHLQ $0x20, R11 + IMULQ SI, R11 + SHRQ $0x34, R11 + MOVL (AX)(R10*4), SI + MOVL 65536(AX)(R11*4), R8 + MOVL DX, (AX)(R10*4) + MOVL DX, 65536(AX)(R11*4) + MOVQ (BX)(SI*1), R10 + MOVQ (BX)(R8*1), R11 + CMPQ R10, DI + JEQ candidate_match_encodeBetterBlockAsm12B + CMPQ R11, DI + JNE no_short_found_encodeBetterBlockAsm12B + MOVL R8, SI + JMP candidate_match_encodeBetterBlockAsm12B + +no_short_found_encodeBetterBlockAsm12B: + CMPL R10, DI + JEQ candidate_match_encodeBetterBlockAsm12B + CMPL R11, DI + JEQ candidateS_match_encodeBetterBlockAsm12B + MOVL 20(SP), DX + JMP search_loop_encodeBetterBlockAsm12B + +candidateS_match_encodeBetterBlockAsm12B: + SHRQ $0x08, DI + MOVQ DI, R10 + SHLQ $0x10, R10 + IMULQ R9, R10 + SHRQ $0x32, R10 + MOVL (AX)(R10*4), SI + INCL DX + MOVL DX, (AX)(R10*4) + CMPL (BX)(SI*1), DI + JEQ candidate_match_encodeBetterBlockAsm12B + DECL DX + MOVL R8, SI + +candidate_match_encodeBetterBlockAsm12B: + MOVL 12(SP), DI + TESTL SI, SI + JZ match_extend_back_end_encodeBetterBlockAsm12B + +match_extend_back_loop_encodeBetterBlockAsm12B: + CMPL DX, DI + JBE match_extend_back_end_encodeBetterBlockAsm12B + MOVB -1(BX)(SI*1), R8 + MOVB -1(BX)(DX*1), R9 + CMPB R8, R9 + JNE match_extend_back_end_encodeBetterBlockAsm12B + LEAL -1(DX), DX + DECL SI + JZ match_extend_back_end_encodeBetterBlockAsm12B + JMP match_extend_back_loop_encodeBetterBlockAsm12B + +match_extend_back_end_encodeBetterBlockAsm12B: + MOVL DX, DI + SUBL 12(SP), DI + LEAQ 3(CX)(DI*1), DI + CMPQ DI, (SP) + JB match_dst_size_check_encodeBetterBlockAsm12B + MOVQ $0x00000000, ret+56(FP) + RET + +match_dst_size_check_encodeBetterBlockAsm12B: + MOVL DX, DI + ADDL $0x04, DX + ADDL $0x04, SI + MOVQ src_len+32(FP), R8 + SUBL DX, R8 + LEAQ (BX)(DX*1), R9 + LEAQ (BX)(SI*1), R10 + + // matchLen + XORL R12, R12 + +matchlen_loopback_16_match_nolit_encodeBetterBlockAsm12B: + CMPL R8, $0x10 + JB matchlen_match8_match_nolit_encodeBetterBlockAsm12B + MOVQ (R9)(R12*1), R11 + MOVQ 8(R9)(R12*1), R13 + XORQ (R10)(R12*1), R11 + JNZ matchlen_bsf_8_match_nolit_encodeBetterBlockAsm12B + XORQ 8(R10)(R12*1), R13 + JNZ matchlen_bsf_16match_nolit_encodeBetterBlockAsm12B + LEAL -16(R8), R8 + LEAL 16(R12), R12 + JMP matchlen_loopback_16_match_nolit_encodeBetterBlockAsm12B + +matchlen_bsf_16match_nolit_encodeBetterBlockAsm12B: +#ifdef GOAMD64_v3 + TZCNTQ R13, R13 + +#else + BSFQ R13, R13 + +#endif + SARQ $0x03, R13 + LEAL 8(R12)(R13*1), R12 + JMP match_nolit_end_encodeBetterBlockAsm12B + +matchlen_match8_match_nolit_encodeBetterBlockAsm12B: + CMPL R8, $0x08 + JB matchlen_match4_match_nolit_encodeBetterBlockAsm12B + MOVQ (R9)(R12*1), R11 + XORQ (R10)(R12*1), R11 + JNZ matchlen_bsf_8_match_nolit_encodeBetterBlockAsm12B + LEAL -8(R8), R8 + LEAL 8(R12), R12 + JMP matchlen_match4_match_nolit_encodeBetterBlockAsm12B + +matchlen_bsf_8_match_nolit_encodeBetterBlockAsm12B: +#ifdef GOAMD64_v3 + TZCNTQ R11, R11 + +#else + BSFQ R11, R11 + +#endif + SARQ $0x03, R11 + LEAL (R12)(R11*1), R12 + JMP match_nolit_end_encodeBetterBlockAsm12B + +matchlen_match4_match_nolit_encodeBetterBlockAsm12B: + CMPL R8, $0x04 + JB matchlen_match2_match_nolit_encodeBetterBlockAsm12B + MOVL (R9)(R12*1), R11 + CMPL (R10)(R12*1), R11 + JNE matchlen_match2_match_nolit_encodeBetterBlockAsm12B + LEAL -4(R8), R8 + LEAL 4(R12), R12 + +matchlen_match2_match_nolit_encodeBetterBlockAsm12B: + CMPL R8, $0x01 + JE matchlen_match1_match_nolit_encodeBetterBlockAsm12B + JB match_nolit_end_encodeBetterBlockAsm12B + MOVW (R9)(R12*1), R11 + CMPW (R10)(R12*1), R11 + JNE matchlen_match1_match_nolit_encodeBetterBlockAsm12B + LEAL 2(R12), R12 + SUBL $0x02, R8 + JZ match_nolit_end_encodeBetterBlockAsm12B + +matchlen_match1_match_nolit_encodeBetterBlockAsm12B: + MOVB (R9)(R12*1), R11 + CMPB (R10)(R12*1), R11 + JNE match_nolit_end_encodeBetterBlockAsm12B + LEAL 1(R12), R12 + +match_nolit_end_encodeBetterBlockAsm12B: + MOVL DX, R8 + SUBL SI, R8 + + // Check if repeat + CMPL 16(SP), R8 + JEQ match_is_repeat_encodeBetterBlockAsm12B + MOVL R8, 16(SP) + MOVL 12(SP), SI + CMPL SI, DI + JEQ emit_literal_done_match_emit_encodeBetterBlockAsm12B + MOVL DI, R9 + MOVL DI, 12(SP) + LEAQ (BX)(SI*1), R10 + SUBL SI, R9 + LEAL -1(R9), SI + CMPL SI, $0x3c + JB one_byte_match_emit_encodeBetterBlockAsm12B + CMPL SI, $0x00000100 + JB two_bytes_match_emit_encodeBetterBlockAsm12B + JB three_bytes_match_emit_encodeBetterBlockAsm12B + +three_bytes_match_emit_encodeBetterBlockAsm12B: + MOVB $0xf4, (CX) + MOVW SI, 1(CX) + ADDQ $0x03, CX + JMP memmove_long_match_emit_encodeBetterBlockAsm12B + +two_bytes_match_emit_encodeBetterBlockAsm12B: + MOVB $0xf0, (CX) + MOVB SI, 1(CX) + ADDQ $0x02, CX + CMPL SI, $0x40 + JB memmove_match_emit_encodeBetterBlockAsm12B + JMP memmove_long_match_emit_encodeBetterBlockAsm12B + +one_byte_match_emit_encodeBetterBlockAsm12B: + SHLB $0x02, SI + MOVB SI, (CX) + ADDQ $0x01, CX + +memmove_match_emit_encodeBetterBlockAsm12B: + LEAQ (CX)(R9*1), SI + + // genMemMoveShort + CMPQ R9, $0x04 + JBE emit_lit_memmove_match_emit_encodeBetterBlockAsm12B_memmove_move_4 + CMPQ R9, $0x08 + JB emit_lit_memmove_match_emit_encodeBetterBlockAsm12B_memmove_move_4through7 + CMPQ R9, $0x10 + JBE emit_lit_memmove_match_emit_encodeBetterBlockAsm12B_memmove_move_8through16 + CMPQ R9, $0x20 + JBE emit_lit_memmove_match_emit_encodeBetterBlockAsm12B_memmove_move_17through32 + JMP emit_lit_memmove_match_emit_encodeBetterBlockAsm12B_memmove_move_33through64 + +emit_lit_memmove_match_emit_encodeBetterBlockAsm12B_memmove_move_4: + MOVL (R10), R11 + MOVL R11, (CX) + JMP memmove_end_copy_match_emit_encodeBetterBlockAsm12B + +emit_lit_memmove_match_emit_encodeBetterBlockAsm12B_memmove_move_4through7: + MOVL (R10), R11 + MOVL -4(R10)(R9*1), R10 + MOVL R11, (CX) + MOVL R10, -4(CX)(R9*1) + JMP memmove_end_copy_match_emit_encodeBetterBlockAsm12B + +emit_lit_memmove_match_emit_encodeBetterBlockAsm12B_memmove_move_8through16: + MOVQ (R10), R11 + MOVQ -8(R10)(R9*1), R10 + MOVQ R11, (CX) + MOVQ R10, -8(CX)(R9*1) + JMP memmove_end_copy_match_emit_encodeBetterBlockAsm12B + +emit_lit_memmove_match_emit_encodeBetterBlockAsm12B_memmove_move_17through32: + MOVOU (R10), X0 + MOVOU -16(R10)(R9*1), X1 + MOVOU X0, (CX) + MOVOU X1, -16(CX)(R9*1) + JMP memmove_end_copy_match_emit_encodeBetterBlockAsm12B + +emit_lit_memmove_match_emit_encodeBetterBlockAsm12B_memmove_move_33through64: + MOVOU (R10), X0 + MOVOU 16(R10), X1 + MOVOU -32(R10)(R9*1), X2 + MOVOU -16(R10)(R9*1), X3 + MOVOU X0, (CX) + MOVOU X1, 16(CX) + MOVOU X2, -32(CX)(R9*1) + MOVOU X3, -16(CX)(R9*1) + +memmove_end_copy_match_emit_encodeBetterBlockAsm12B: + MOVQ SI, CX + JMP emit_literal_done_match_emit_encodeBetterBlockAsm12B + +memmove_long_match_emit_encodeBetterBlockAsm12B: + LEAQ (CX)(R9*1), SI + + // genMemMoveLong + MOVOU (R10), X0 + MOVOU 16(R10), X1 + MOVOU -32(R10)(R9*1), X2 + MOVOU -16(R10)(R9*1), X3 + MOVQ R9, R13 + SHRQ $0x05, R13 + MOVQ CX, R11 + ANDL $0x0000001f, R11 + MOVQ $0x00000040, R14 + SUBQ R11, R14 + DECQ R13 + JA emit_lit_memmove_long_match_emit_encodeBetterBlockAsm12Blarge_forward_sse_loop_32 + LEAQ -32(R10)(R14*1), R11 + LEAQ -32(CX)(R14*1), R15 + +emit_lit_memmove_long_match_emit_encodeBetterBlockAsm12Blarge_big_loop_back: + MOVOU (R11), X4 + MOVOU 16(R11), X5 + MOVOA X4, (R15) + MOVOA X5, 16(R15) + ADDQ $0x20, R15 + ADDQ $0x20, R11 + ADDQ $0x20, R14 + DECQ R13 + JNA emit_lit_memmove_long_match_emit_encodeBetterBlockAsm12Blarge_big_loop_back + +emit_lit_memmove_long_match_emit_encodeBetterBlockAsm12Blarge_forward_sse_loop_32: + MOVOU -32(R10)(R14*1), X4 + MOVOU -16(R10)(R14*1), X5 + MOVOA X4, -32(CX)(R14*1) + MOVOA X5, -16(CX)(R14*1) + ADDQ $0x20, R14 + CMPQ R9, R14 + JAE emit_lit_memmove_long_match_emit_encodeBetterBlockAsm12Blarge_forward_sse_loop_32 + MOVOU X0, (CX) + MOVOU X1, 16(CX) + MOVOU X2, -32(CX)(R9*1) + MOVOU X3, -16(CX)(R9*1) + MOVQ SI, CX + +emit_literal_done_match_emit_encodeBetterBlockAsm12B: + ADDL R12, DX + ADDL $0x04, R12 + MOVL DX, 12(SP) + + // emitCopy + CMPL R12, $0x40 + JBE two_byte_offset_short_match_nolit_encodeBetterBlockAsm12B + CMPL R8, $0x00000800 + JAE long_offset_short_match_nolit_encodeBetterBlockAsm12B + MOVL $0x00000001, SI + LEAL 16(SI), SI + MOVB R8, 1(CX) + SHRL $0x08, R8 + SHLL $0x05, R8 + ORL R8, SI + MOVB SI, (CX) + ADDQ $0x02, CX + SUBL $0x08, R12 + + // emitRepeat + LEAL -4(R12), R12 + JMP cant_repeat_two_offset_match_nolit_encodeBetterBlockAsm12B_emit_copy_short_2b + MOVL R12, SI + LEAL -4(R12), R12 + CMPL SI, $0x08 + JBE repeat_two_match_nolit_encodeBetterBlockAsm12B_emit_copy_short_2b + CMPL SI, $0x0c + JAE cant_repeat_two_offset_match_nolit_encodeBetterBlockAsm12B_emit_copy_short_2b + CMPL R8, $0x00000800 + JB repeat_two_offset_match_nolit_encodeBetterBlockAsm12B_emit_copy_short_2b + +cant_repeat_two_offset_match_nolit_encodeBetterBlockAsm12B_emit_copy_short_2b: + CMPL R12, $0x00000104 + JB repeat_three_match_nolit_encodeBetterBlockAsm12B_emit_copy_short_2b + LEAL -256(R12), R12 + MOVW $0x0019, (CX) + MOVW R12, 2(CX) + ADDQ $0x04, CX + JMP match_nolit_emitcopy_end_encodeBetterBlockAsm12B + +repeat_three_match_nolit_encodeBetterBlockAsm12B_emit_copy_short_2b: + LEAL -4(R12), R12 + MOVW $0x0015, (CX) + MOVB R12, 2(CX) + ADDQ $0x03, CX + JMP match_nolit_emitcopy_end_encodeBetterBlockAsm12B + +repeat_two_match_nolit_encodeBetterBlockAsm12B_emit_copy_short_2b: + SHLL $0x02, R12 + ORL $0x01, R12 + MOVW R12, (CX) + ADDQ $0x02, CX + JMP match_nolit_emitcopy_end_encodeBetterBlockAsm12B + +repeat_two_offset_match_nolit_encodeBetterBlockAsm12B_emit_copy_short_2b: + XORQ SI, SI + LEAL 1(SI)(R12*4), R12 + MOVB R8, 1(CX) + SARL $0x08, R8 + SHLL $0x05, R8 + ORL R8, R12 + MOVB R12, (CX) + ADDQ $0x02, CX + JMP match_nolit_emitcopy_end_encodeBetterBlockAsm12B + +long_offset_short_match_nolit_encodeBetterBlockAsm12B: + MOVB $0xee, (CX) + MOVW R8, 1(CX) + LEAL -60(R12), R12 + ADDQ $0x03, CX + + // emitRepeat + MOVL R12, SI + LEAL -4(R12), R12 + CMPL SI, $0x08 + JBE repeat_two_match_nolit_encodeBetterBlockAsm12B_emit_copy_short + CMPL SI, $0x0c + JAE cant_repeat_two_offset_match_nolit_encodeBetterBlockAsm12B_emit_copy_short + CMPL R8, $0x00000800 + JB repeat_two_offset_match_nolit_encodeBetterBlockAsm12B_emit_copy_short + +cant_repeat_two_offset_match_nolit_encodeBetterBlockAsm12B_emit_copy_short: + CMPL R12, $0x00000104 + JB repeat_three_match_nolit_encodeBetterBlockAsm12B_emit_copy_short + LEAL -256(R12), R12 + MOVW $0x0019, (CX) + MOVW R12, 2(CX) + ADDQ $0x04, CX + JMP match_nolit_emitcopy_end_encodeBetterBlockAsm12B + +repeat_three_match_nolit_encodeBetterBlockAsm12B_emit_copy_short: + LEAL -4(R12), R12 + MOVW $0x0015, (CX) + MOVB R12, 2(CX) + ADDQ $0x03, CX + JMP match_nolit_emitcopy_end_encodeBetterBlockAsm12B + +repeat_two_match_nolit_encodeBetterBlockAsm12B_emit_copy_short: + SHLL $0x02, R12 + ORL $0x01, R12 + MOVW R12, (CX) + ADDQ $0x02, CX + JMP match_nolit_emitcopy_end_encodeBetterBlockAsm12B + +repeat_two_offset_match_nolit_encodeBetterBlockAsm12B_emit_copy_short: + XORQ SI, SI + LEAL 1(SI)(R12*4), R12 + MOVB R8, 1(CX) + SARL $0x08, R8 + SHLL $0x05, R8 + ORL R8, R12 + MOVB R12, (CX) + ADDQ $0x02, CX + JMP match_nolit_emitcopy_end_encodeBetterBlockAsm12B + +two_byte_offset_short_match_nolit_encodeBetterBlockAsm12B: + MOVL R12, SI + SHLL $0x02, SI + CMPL R12, $0x0c + JAE emit_copy_three_match_nolit_encodeBetterBlockAsm12B + CMPL R8, $0x00000800 + JAE emit_copy_three_match_nolit_encodeBetterBlockAsm12B + LEAL -15(SI), SI + MOVB R8, 1(CX) + SHRL $0x08, R8 + SHLL $0x05, R8 + ORL R8, SI + MOVB SI, (CX) + ADDQ $0x02, CX + JMP match_nolit_emitcopy_end_encodeBetterBlockAsm12B + +emit_copy_three_match_nolit_encodeBetterBlockAsm12B: + LEAL -2(SI), SI + MOVB SI, (CX) + MOVW R8, 1(CX) + ADDQ $0x03, CX + JMP match_nolit_emitcopy_end_encodeBetterBlockAsm12B + +match_is_repeat_encodeBetterBlockAsm12B: + MOVL 12(SP), SI + CMPL SI, DI + JEQ emit_literal_done_match_emit_repeat_encodeBetterBlockAsm12B + MOVL DI, R9 + MOVL DI, 12(SP) + LEAQ (BX)(SI*1), R10 + SUBL SI, R9 + LEAL -1(R9), SI + CMPL SI, $0x3c + JB one_byte_match_emit_repeat_encodeBetterBlockAsm12B + CMPL SI, $0x00000100 + JB two_bytes_match_emit_repeat_encodeBetterBlockAsm12B + JB three_bytes_match_emit_repeat_encodeBetterBlockAsm12B + +three_bytes_match_emit_repeat_encodeBetterBlockAsm12B: + MOVB $0xf4, (CX) + MOVW SI, 1(CX) + ADDQ $0x03, CX + JMP memmove_long_match_emit_repeat_encodeBetterBlockAsm12B + +two_bytes_match_emit_repeat_encodeBetterBlockAsm12B: + MOVB $0xf0, (CX) + MOVB SI, 1(CX) + ADDQ $0x02, CX + CMPL SI, $0x40 + JB memmove_match_emit_repeat_encodeBetterBlockAsm12B + JMP memmove_long_match_emit_repeat_encodeBetterBlockAsm12B + +one_byte_match_emit_repeat_encodeBetterBlockAsm12B: + SHLB $0x02, SI + MOVB SI, (CX) + ADDQ $0x01, CX + +memmove_match_emit_repeat_encodeBetterBlockAsm12B: + LEAQ (CX)(R9*1), SI + + // genMemMoveShort + CMPQ R9, $0x04 + JBE emit_lit_memmove_match_emit_repeat_encodeBetterBlockAsm12B_memmove_move_4 + CMPQ R9, $0x08 + JB emit_lit_memmove_match_emit_repeat_encodeBetterBlockAsm12B_memmove_move_4through7 + CMPQ R9, $0x10 + JBE emit_lit_memmove_match_emit_repeat_encodeBetterBlockAsm12B_memmove_move_8through16 + CMPQ R9, $0x20 + JBE emit_lit_memmove_match_emit_repeat_encodeBetterBlockAsm12B_memmove_move_17through32 + JMP emit_lit_memmove_match_emit_repeat_encodeBetterBlockAsm12B_memmove_move_33through64 + +emit_lit_memmove_match_emit_repeat_encodeBetterBlockAsm12B_memmove_move_4: + MOVL (R10), R11 + MOVL R11, (CX) + JMP memmove_end_copy_match_emit_repeat_encodeBetterBlockAsm12B + +emit_lit_memmove_match_emit_repeat_encodeBetterBlockAsm12B_memmove_move_4through7: + MOVL (R10), R11 + MOVL -4(R10)(R9*1), R10 + MOVL R11, (CX) + MOVL R10, -4(CX)(R9*1) + JMP memmove_end_copy_match_emit_repeat_encodeBetterBlockAsm12B + +emit_lit_memmove_match_emit_repeat_encodeBetterBlockAsm12B_memmove_move_8through16: + MOVQ (R10), R11 + MOVQ -8(R10)(R9*1), R10 + MOVQ R11, (CX) + MOVQ R10, -8(CX)(R9*1) + JMP memmove_end_copy_match_emit_repeat_encodeBetterBlockAsm12B + +emit_lit_memmove_match_emit_repeat_encodeBetterBlockAsm12B_memmove_move_17through32: + MOVOU (R10), X0 + MOVOU -16(R10)(R9*1), X1 + MOVOU X0, (CX) + MOVOU X1, -16(CX)(R9*1) + JMP memmove_end_copy_match_emit_repeat_encodeBetterBlockAsm12B + +emit_lit_memmove_match_emit_repeat_encodeBetterBlockAsm12B_memmove_move_33through64: + MOVOU (R10), X0 + MOVOU 16(R10), X1 + MOVOU -32(R10)(R9*1), X2 + MOVOU -16(R10)(R9*1), X3 + MOVOU X0, (CX) + MOVOU X1, 16(CX) + MOVOU X2, -32(CX)(R9*1) + MOVOU X3, -16(CX)(R9*1) + +memmove_end_copy_match_emit_repeat_encodeBetterBlockAsm12B: + MOVQ SI, CX + JMP emit_literal_done_match_emit_repeat_encodeBetterBlockAsm12B + +memmove_long_match_emit_repeat_encodeBetterBlockAsm12B: + LEAQ (CX)(R9*1), SI + + // genMemMoveLong + MOVOU (R10), X0 + MOVOU 16(R10), X1 + MOVOU -32(R10)(R9*1), X2 + MOVOU -16(R10)(R9*1), X3 + MOVQ R9, R13 + SHRQ $0x05, R13 + MOVQ CX, R11 + ANDL $0x0000001f, R11 + MOVQ $0x00000040, R14 + SUBQ R11, R14 + DECQ R13 + JA emit_lit_memmove_long_match_emit_repeat_encodeBetterBlockAsm12Blarge_forward_sse_loop_32 + LEAQ -32(R10)(R14*1), R11 + LEAQ -32(CX)(R14*1), R15 + +emit_lit_memmove_long_match_emit_repeat_encodeBetterBlockAsm12Blarge_big_loop_back: + MOVOU (R11), X4 + MOVOU 16(R11), X5 + MOVOA X4, (R15) + MOVOA X5, 16(R15) + ADDQ $0x20, R15 + ADDQ $0x20, R11 + ADDQ $0x20, R14 + DECQ R13 + JNA emit_lit_memmove_long_match_emit_repeat_encodeBetterBlockAsm12Blarge_big_loop_back + +emit_lit_memmove_long_match_emit_repeat_encodeBetterBlockAsm12Blarge_forward_sse_loop_32: + MOVOU -32(R10)(R14*1), X4 + MOVOU -16(R10)(R14*1), X5 + MOVOA X4, -32(CX)(R14*1) + MOVOA X5, -16(CX)(R14*1) + ADDQ $0x20, R14 + CMPQ R9, R14 + JAE emit_lit_memmove_long_match_emit_repeat_encodeBetterBlockAsm12Blarge_forward_sse_loop_32 + MOVOU X0, (CX) + MOVOU X1, 16(CX) + MOVOU X2, -32(CX)(R9*1) + MOVOU X3, -16(CX)(R9*1) + MOVQ SI, CX + +emit_literal_done_match_emit_repeat_encodeBetterBlockAsm12B: + ADDL R12, DX + ADDL $0x04, R12 + MOVL DX, 12(SP) + + // emitRepeat + MOVL R12, SI + LEAL -4(R12), R12 + CMPL SI, $0x08 + JBE repeat_two_match_nolit_repeat_encodeBetterBlockAsm12B + CMPL SI, $0x0c + JAE cant_repeat_two_offset_match_nolit_repeat_encodeBetterBlockAsm12B + CMPL R8, $0x00000800 + JB repeat_two_offset_match_nolit_repeat_encodeBetterBlockAsm12B + +cant_repeat_two_offset_match_nolit_repeat_encodeBetterBlockAsm12B: + CMPL R12, $0x00000104 + JB repeat_three_match_nolit_repeat_encodeBetterBlockAsm12B + LEAL -256(R12), R12 + MOVW $0x0019, (CX) + MOVW R12, 2(CX) + ADDQ $0x04, CX + JMP match_nolit_emitcopy_end_encodeBetterBlockAsm12B + +repeat_three_match_nolit_repeat_encodeBetterBlockAsm12B: + LEAL -4(R12), R12 + MOVW $0x0015, (CX) + MOVB R12, 2(CX) + ADDQ $0x03, CX + JMP match_nolit_emitcopy_end_encodeBetterBlockAsm12B + +repeat_two_match_nolit_repeat_encodeBetterBlockAsm12B: + SHLL $0x02, R12 + ORL $0x01, R12 + MOVW R12, (CX) + ADDQ $0x02, CX + JMP match_nolit_emitcopy_end_encodeBetterBlockAsm12B + +repeat_two_offset_match_nolit_repeat_encodeBetterBlockAsm12B: + XORQ SI, SI + LEAL 1(SI)(R12*4), R12 + MOVB R8, 1(CX) + SARL $0x08, R8 + SHLL $0x05, R8 + ORL R8, R12 + MOVB R12, (CX) + ADDQ $0x02, CX + +match_nolit_emitcopy_end_encodeBetterBlockAsm12B: + CMPL DX, 8(SP) + JAE emit_remainder_encodeBetterBlockAsm12B + CMPQ CX, (SP) + JB match_nolit_dst_ok_encodeBetterBlockAsm12B + MOVQ $0x00000000, ret+56(FP) + RET + +match_nolit_dst_ok_encodeBetterBlockAsm12B: + MOVQ $0x0000cf1bbcdcbf9b, SI + MOVQ $0x9e3779b1, R8 + LEAQ 1(DI), DI + LEAQ -2(DX), R9 + MOVQ (BX)(DI*1), R10 + MOVQ 1(BX)(DI*1), R11 + MOVQ (BX)(R9*1), R12 + MOVQ 1(BX)(R9*1), R13 + SHLQ $0x10, R10 + IMULQ SI, R10 + SHRQ $0x32, R10 + SHLQ $0x20, R11 + IMULQ R8, R11 + SHRQ $0x34, R11 + SHLQ $0x10, R12 + IMULQ SI, R12 + SHRQ $0x32, R12 + SHLQ $0x20, R13 + IMULQ R8, R13 + SHRQ $0x34, R13 + LEAQ 1(DI), R8 + LEAQ 1(R9), R14 + MOVL DI, (AX)(R10*4) + MOVL R9, (AX)(R12*4) + MOVL R8, 65536(AX)(R11*4) + MOVL R14, 65536(AX)(R13*4) + LEAQ 1(R9)(DI*1), R8 + SHRQ $0x01, R8 + ADDQ $0x01, DI + SUBQ $0x01, R9 + +index_loop_encodeBetterBlockAsm12B: + CMPQ R8, R9 + JAE search_loop_encodeBetterBlockAsm12B + MOVQ (BX)(DI*1), R10 + MOVQ (BX)(R8*1), R11 + SHLQ $0x10, R10 + IMULQ SI, R10 + SHRQ $0x32, R10 + SHLQ $0x10, R11 + IMULQ SI, R11 + SHRQ $0x32, R11 + MOVL DI, (AX)(R10*4) + MOVL R8, (AX)(R11*4) + ADDQ $0x02, DI + ADDQ $0x02, R8 + JMP index_loop_encodeBetterBlockAsm12B + +emit_remainder_encodeBetterBlockAsm12B: + MOVQ src_len+32(FP), AX + SUBL 12(SP), AX + LEAQ 3(CX)(AX*1), AX + CMPQ AX, (SP) + JB emit_remainder_ok_encodeBetterBlockAsm12B + MOVQ $0x00000000, ret+56(FP) + RET + +emit_remainder_ok_encodeBetterBlockAsm12B: + MOVQ src_len+32(FP), AX + MOVL 12(SP), DX + CMPL DX, AX + JEQ emit_literal_done_emit_remainder_encodeBetterBlockAsm12B + MOVL AX, SI + MOVL AX, 12(SP) + LEAQ (BX)(DX*1), AX + SUBL DX, SI + LEAL -1(SI), DX + CMPL DX, $0x3c + JB one_byte_emit_remainder_encodeBetterBlockAsm12B + CMPL DX, $0x00000100 + JB two_bytes_emit_remainder_encodeBetterBlockAsm12B + JB three_bytes_emit_remainder_encodeBetterBlockAsm12B + +three_bytes_emit_remainder_encodeBetterBlockAsm12B: + MOVB $0xf4, (CX) + MOVW DX, 1(CX) + ADDQ $0x03, CX + JMP memmove_long_emit_remainder_encodeBetterBlockAsm12B + +two_bytes_emit_remainder_encodeBetterBlockAsm12B: + MOVB $0xf0, (CX) + MOVB DL, 1(CX) + ADDQ $0x02, CX + CMPL DX, $0x40 + JB memmove_emit_remainder_encodeBetterBlockAsm12B + JMP memmove_long_emit_remainder_encodeBetterBlockAsm12B + +one_byte_emit_remainder_encodeBetterBlockAsm12B: + SHLB $0x02, DL + MOVB DL, (CX) + ADDQ $0x01, CX + +memmove_emit_remainder_encodeBetterBlockAsm12B: + LEAQ (CX)(SI*1), DX + MOVL SI, BX + + // genMemMoveShort + CMPQ BX, $0x03 + JB emit_lit_memmove_emit_remainder_encodeBetterBlockAsm12B_memmove_move_1or2 + JE emit_lit_memmove_emit_remainder_encodeBetterBlockAsm12B_memmove_move_3 + CMPQ BX, $0x08 + JB emit_lit_memmove_emit_remainder_encodeBetterBlockAsm12B_memmove_move_4through7 + CMPQ BX, $0x10 + JBE emit_lit_memmove_emit_remainder_encodeBetterBlockAsm12B_memmove_move_8through16 + CMPQ BX, $0x20 + JBE emit_lit_memmove_emit_remainder_encodeBetterBlockAsm12B_memmove_move_17through32 + JMP emit_lit_memmove_emit_remainder_encodeBetterBlockAsm12B_memmove_move_33through64 + +emit_lit_memmove_emit_remainder_encodeBetterBlockAsm12B_memmove_move_1or2: + MOVB (AX), SI + MOVB -1(AX)(BX*1), AL + MOVB SI, (CX) + MOVB AL, -1(CX)(BX*1) + JMP memmove_end_copy_emit_remainder_encodeBetterBlockAsm12B + +emit_lit_memmove_emit_remainder_encodeBetterBlockAsm12B_memmove_move_3: + MOVW (AX), SI + MOVB 2(AX), AL + MOVW SI, (CX) + MOVB AL, 2(CX) + JMP memmove_end_copy_emit_remainder_encodeBetterBlockAsm12B + +emit_lit_memmove_emit_remainder_encodeBetterBlockAsm12B_memmove_move_4through7: + MOVL (AX), SI + MOVL -4(AX)(BX*1), AX + MOVL SI, (CX) + MOVL AX, -4(CX)(BX*1) + JMP memmove_end_copy_emit_remainder_encodeBetterBlockAsm12B + +emit_lit_memmove_emit_remainder_encodeBetterBlockAsm12B_memmove_move_8through16: + MOVQ (AX), SI + MOVQ -8(AX)(BX*1), AX + MOVQ SI, (CX) + MOVQ AX, -8(CX)(BX*1) + JMP memmove_end_copy_emit_remainder_encodeBetterBlockAsm12B + +emit_lit_memmove_emit_remainder_encodeBetterBlockAsm12B_memmove_move_17through32: + MOVOU (AX), X0 + MOVOU -16(AX)(BX*1), X1 + MOVOU X0, (CX) + MOVOU X1, -16(CX)(BX*1) + JMP memmove_end_copy_emit_remainder_encodeBetterBlockAsm12B + +emit_lit_memmove_emit_remainder_encodeBetterBlockAsm12B_memmove_move_33through64: + MOVOU (AX), X0 + MOVOU 16(AX), X1 + MOVOU -32(AX)(BX*1), X2 + MOVOU -16(AX)(BX*1), X3 + MOVOU X0, (CX) + MOVOU X1, 16(CX) + MOVOU X2, -32(CX)(BX*1) + MOVOU X3, -16(CX)(BX*1) + +memmove_end_copy_emit_remainder_encodeBetterBlockAsm12B: + MOVQ DX, CX + JMP emit_literal_done_emit_remainder_encodeBetterBlockAsm12B + +memmove_long_emit_remainder_encodeBetterBlockAsm12B: + LEAQ (CX)(SI*1), DX + MOVL SI, BX + + // genMemMoveLong + MOVOU (AX), X0 + MOVOU 16(AX), X1 + MOVOU -32(AX)(BX*1), X2 + MOVOU -16(AX)(BX*1), X3 + MOVQ BX, DI + SHRQ $0x05, DI + MOVQ CX, SI + ANDL $0x0000001f, SI + MOVQ $0x00000040, R8 + SUBQ SI, R8 + DECQ DI + JA emit_lit_memmove_long_emit_remainder_encodeBetterBlockAsm12Blarge_forward_sse_loop_32 + LEAQ -32(AX)(R8*1), SI + LEAQ -32(CX)(R8*1), R9 + +emit_lit_memmove_long_emit_remainder_encodeBetterBlockAsm12Blarge_big_loop_back: + MOVOU (SI), X4 + MOVOU 16(SI), X5 + MOVOA X4, (R9) + MOVOA X5, 16(R9) + ADDQ $0x20, R9 + ADDQ $0x20, SI + ADDQ $0x20, R8 + DECQ DI + JNA emit_lit_memmove_long_emit_remainder_encodeBetterBlockAsm12Blarge_big_loop_back + +emit_lit_memmove_long_emit_remainder_encodeBetterBlockAsm12Blarge_forward_sse_loop_32: + MOVOU -32(AX)(R8*1), X4 + MOVOU -16(AX)(R8*1), X5 + MOVOA X4, -32(CX)(R8*1) + MOVOA X5, -16(CX)(R8*1) + ADDQ $0x20, R8 + CMPQ BX, R8 + JAE emit_lit_memmove_long_emit_remainder_encodeBetterBlockAsm12Blarge_forward_sse_loop_32 + MOVOU X0, (CX) + MOVOU X1, 16(CX) + MOVOU X2, -32(CX)(BX*1) + MOVOU X3, -16(CX)(BX*1) + MOVQ DX, CX + +emit_literal_done_emit_remainder_encodeBetterBlockAsm12B: + MOVQ dst_base+0(FP), AX + SUBQ AX, CX + MOVQ CX, ret+56(FP) + RET + +// func encodeBetterBlockAsm10B(dst []byte, src []byte, tmp *[20480]byte) int +// Requires: BMI, SSE2 +TEXT ·encodeBetterBlockAsm10B(SB), $24-64 + MOVQ tmp+48(FP), AX + MOVQ dst_base+0(FP), CX + MOVQ $0x000000a0, DX + MOVQ AX, BX + PXOR X0, X0 + +zero_loop_encodeBetterBlockAsm10B: + MOVOU X0, (BX) + MOVOU X0, 16(BX) + MOVOU X0, 32(BX) + MOVOU X0, 48(BX) + MOVOU X0, 64(BX) + MOVOU X0, 80(BX) + MOVOU X0, 96(BX) + MOVOU X0, 112(BX) + ADDQ $0x80, BX + DECQ DX + JNZ zero_loop_encodeBetterBlockAsm10B + MOVL $0x00000000, 12(SP) + MOVQ src_len+32(FP), DX + LEAQ -6(DX), BX + LEAQ -8(DX), SI + MOVL SI, 8(SP) + SHRQ $0x05, DX + SUBL DX, BX + LEAQ (CX)(BX*1), BX + MOVQ BX, (SP) + MOVL $0x00000001, DX + MOVL $0x00000000, 16(SP) + MOVQ src_base+24(FP), BX + +search_loop_encodeBetterBlockAsm10B: + MOVL DX, SI + SUBL 12(SP), SI + SHRL $0x05, SI + LEAL 1(DX)(SI*1), SI + CMPL SI, 8(SP) + JAE emit_remainder_encodeBetterBlockAsm10B + MOVQ (BX)(DX*1), DI + MOVL SI, 20(SP) + MOVQ $0x0000cf1bbcdcbf9b, R9 + MOVQ $0x9e3779b1, SI + MOVQ DI, R10 + MOVQ DI, R11 + SHLQ $0x10, R10 + IMULQ R9, R10 + SHRQ $0x34, R10 + SHLQ $0x20, R11 + IMULQ SI, R11 + SHRQ $0x36, R11 + MOVL (AX)(R10*4), SI + MOVL 16384(AX)(R11*4), R8 + MOVL DX, (AX)(R10*4) + MOVL DX, 16384(AX)(R11*4) + MOVQ (BX)(SI*1), R10 + MOVQ (BX)(R8*1), R11 + CMPQ R10, DI + JEQ candidate_match_encodeBetterBlockAsm10B + CMPQ R11, DI + JNE no_short_found_encodeBetterBlockAsm10B + MOVL R8, SI + JMP candidate_match_encodeBetterBlockAsm10B + +no_short_found_encodeBetterBlockAsm10B: + CMPL R10, DI + JEQ candidate_match_encodeBetterBlockAsm10B + CMPL R11, DI + JEQ candidateS_match_encodeBetterBlockAsm10B + MOVL 20(SP), DX + JMP search_loop_encodeBetterBlockAsm10B + +candidateS_match_encodeBetterBlockAsm10B: + SHRQ $0x08, DI + MOVQ DI, R10 + SHLQ $0x10, R10 + IMULQ R9, R10 + SHRQ $0x34, R10 + MOVL (AX)(R10*4), SI + INCL DX + MOVL DX, (AX)(R10*4) + CMPL (BX)(SI*1), DI + JEQ candidate_match_encodeBetterBlockAsm10B + DECL DX + MOVL R8, SI + +candidate_match_encodeBetterBlockAsm10B: + MOVL 12(SP), DI + TESTL SI, SI + JZ match_extend_back_end_encodeBetterBlockAsm10B + +match_extend_back_loop_encodeBetterBlockAsm10B: + CMPL DX, DI + JBE match_extend_back_end_encodeBetterBlockAsm10B + MOVB -1(BX)(SI*1), R8 + MOVB -1(BX)(DX*1), R9 + CMPB R8, R9 + JNE match_extend_back_end_encodeBetterBlockAsm10B + LEAL -1(DX), DX + DECL SI + JZ match_extend_back_end_encodeBetterBlockAsm10B + JMP match_extend_back_loop_encodeBetterBlockAsm10B + +match_extend_back_end_encodeBetterBlockAsm10B: + MOVL DX, DI + SUBL 12(SP), DI + LEAQ 3(CX)(DI*1), DI + CMPQ DI, (SP) + JB match_dst_size_check_encodeBetterBlockAsm10B + MOVQ $0x00000000, ret+56(FP) + RET + +match_dst_size_check_encodeBetterBlockAsm10B: + MOVL DX, DI + ADDL $0x04, DX + ADDL $0x04, SI + MOVQ src_len+32(FP), R8 + SUBL DX, R8 + LEAQ (BX)(DX*1), R9 + LEAQ (BX)(SI*1), R10 + + // matchLen + XORL R12, R12 + +matchlen_loopback_16_match_nolit_encodeBetterBlockAsm10B: + CMPL R8, $0x10 + JB matchlen_match8_match_nolit_encodeBetterBlockAsm10B + MOVQ (R9)(R12*1), R11 + MOVQ 8(R9)(R12*1), R13 + XORQ (R10)(R12*1), R11 + JNZ matchlen_bsf_8_match_nolit_encodeBetterBlockAsm10B + XORQ 8(R10)(R12*1), R13 + JNZ matchlen_bsf_16match_nolit_encodeBetterBlockAsm10B + LEAL -16(R8), R8 + LEAL 16(R12), R12 + JMP matchlen_loopback_16_match_nolit_encodeBetterBlockAsm10B + +matchlen_bsf_16match_nolit_encodeBetterBlockAsm10B: +#ifdef GOAMD64_v3 + TZCNTQ R13, R13 + +#else + BSFQ R13, R13 + +#endif + SARQ $0x03, R13 + LEAL 8(R12)(R13*1), R12 + JMP match_nolit_end_encodeBetterBlockAsm10B + +matchlen_match8_match_nolit_encodeBetterBlockAsm10B: + CMPL R8, $0x08 + JB matchlen_match4_match_nolit_encodeBetterBlockAsm10B + MOVQ (R9)(R12*1), R11 + XORQ (R10)(R12*1), R11 + JNZ matchlen_bsf_8_match_nolit_encodeBetterBlockAsm10B + LEAL -8(R8), R8 + LEAL 8(R12), R12 + JMP matchlen_match4_match_nolit_encodeBetterBlockAsm10B + +matchlen_bsf_8_match_nolit_encodeBetterBlockAsm10B: +#ifdef GOAMD64_v3 + TZCNTQ R11, R11 + +#else + BSFQ R11, R11 + +#endif + SARQ $0x03, R11 + LEAL (R12)(R11*1), R12 + JMP match_nolit_end_encodeBetterBlockAsm10B + +matchlen_match4_match_nolit_encodeBetterBlockAsm10B: + CMPL R8, $0x04 + JB matchlen_match2_match_nolit_encodeBetterBlockAsm10B + MOVL (R9)(R12*1), R11 + CMPL (R10)(R12*1), R11 + JNE matchlen_match2_match_nolit_encodeBetterBlockAsm10B + LEAL -4(R8), R8 + LEAL 4(R12), R12 + +matchlen_match2_match_nolit_encodeBetterBlockAsm10B: + CMPL R8, $0x01 + JE matchlen_match1_match_nolit_encodeBetterBlockAsm10B + JB match_nolit_end_encodeBetterBlockAsm10B + MOVW (R9)(R12*1), R11 + CMPW (R10)(R12*1), R11 + JNE matchlen_match1_match_nolit_encodeBetterBlockAsm10B + LEAL 2(R12), R12 + SUBL $0x02, R8 + JZ match_nolit_end_encodeBetterBlockAsm10B + +matchlen_match1_match_nolit_encodeBetterBlockAsm10B: + MOVB (R9)(R12*1), R11 + CMPB (R10)(R12*1), R11 + JNE match_nolit_end_encodeBetterBlockAsm10B + LEAL 1(R12), R12 + +match_nolit_end_encodeBetterBlockAsm10B: + MOVL DX, R8 + SUBL SI, R8 + + // Check if repeat + CMPL 16(SP), R8 + JEQ match_is_repeat_encodeBetterBlockAsm10B + MOVL R8, 16(SP) + MOVL 12(SP), SI + CMPL SI, DI + JEQ emit_literal_done_match_emit_encodeBetterBlockAsm10B + MOVL DI, R9 + MOVL DI, 12(SP) + LEAQ (BX)(SI*1), R10 + SUBL SI, R9 + LEAL -1(R9), SI + CMPL SI, $0x3c + JB one_byte_match_emit_encodeBetterBlockAsm10B + CMPL SI, $0x00000100 + JB two_bytes_match_emit_encodeBetterBlockAsm10B + JB three_bytes_match_emit_encodeBetterBlockAsm10B + +three_bytes_match_emit_encodeBetterBlockAsm10B: + MOVB $0xf4, (CX) + MOVW SI, 1(CX) + ADDQ $0x03, CX + JMP memmove_long_match_emit_encodeBetterBlockAsm10B + +two_bytes_match_emit_encodeBetterBlockAsm10B: + MOVB $0xf0, (CX) + MOVB SI, 1(CX) + ADDQ $0x02, CX + CMPL SI, $0x40 + JB memmove_match_emit_encodeBetterBlockAsm10B + JMP memmove_long_match_emit_encodeBetterBlockAsm10B + +one_byte_match_emit_encodeBetterBlockAsm10B: + SHLB $0x02, SI + MOVB SI, (CX) + ADDQ $0x01, CX + +memmove_match_emit_encodeBetterBlockAsm10B: + LEAQ (CX)(R9*1), SI + + // genMemMoveShort + CMPQ R9, $0x04 + JBE emit_lit_memmove_match_emit_encodeBetterBlockAsm10B_memmove_move_4 + CMPQ R9, $0x08 + JB emit_lit_memmove_match_emit_encodeBetterBlockAsm10B_memmove_move_4through7 + CMPQ R9, $0x10 + JBE emit_lit_memmove_match_emit_encodeBetterBlockAsm10B_memmove_move_8through16 + CMPQ R9, $0x20 + JBE emit_lit_memmove_match_emit_encodeBetterBlockAsm10B_memmove_move_17through32 + JMP emit_lit_memmove_match_emit_encodeBetterBlockAsm10B_memmove_move_33through64 + +emit_lit_memmove_match_emit_encodeBetterBlockAsm10B_memmove_move_4: + MOVL (R10), R11 + MOVL R11, (CX) + JMP memmove_end_copy_match_emit_encodeBetterBlockAsm10B + +emit_lit_memmove_match_emit_encodeBetterBlockAsm10B_memmove_move_4through7: + MOVL (R10), R11 + MOVL -4(R10)(R9*1), R10 + MOVL R11, (CX) + MOVL R10, -4(CX)(R9*1) + JMP memmove_end_copy_match_emit_encodeBetterBlockAsm10B + +emit_lit_memmove_match_emit_encodeBetterBlockAsm10B_memmove_move_8through16: + MOVQ (R10), R11 + MOVQ -8(R10)(R9*1), R10 + MOVQ R11, (CX) + MOVQ R10, -8(CX)(R9*1) + JMP memmove_end_copy_match_emit_encodeBetterBlockAsm10B + +emit_lit_memmove_match_emit_encodeBetterBlockAsm10B_memmove_move_17through32: + MOVOU (R10), X0 + MOVOU -16(R10)(R9*1), X1 + MOVOU X0, (CX) + MOVOU X1, -16(CX)(R9*1) + JMP memmove_end_copy_match_emit_encodeBetterBlockAsm10B + +emit_lit_memmove_match_emit_encodeBetterBlockAsm10B_memmove_move_33through64: + MOVOU (R10), X0 + MOVOU 16(R10), X1 + MOVOU -32(R10)(R9*1), X2 + MOVOU -16(R10)(R9*1), X3 + MOVOU X0, (CX) + MOVOU X1, 16(CX) + MOVOU X2, -32(CX)(R9*1) + MOVOU X3, -16(CX)(R9*1) + +memmove_end_copy_match_emit_encodeBetterBlockAsm10B: + MOVQ SI, CX + JMP emit_literal_done_match_emit_encodeBetterBlockAsm10B + +memmove_long_match_emit_encodeBetterBlockAsm10B: + LEAQ (CX)(R9*1), SI + + // genMemMoveLong + MOVOU (R10), X0 + MOVOU 16(R10), X1 + MOVOU -32(R10)(R9*1), X2 + MOVOU -16(R10)(R9*1), X3 + MOVQ R9, R13 + SHRQ $0x05, R13 + MOVQ CX, R11 + ANDL $0x0000001f, R11 + MOVQ $0x00000040, R14 + SUBQ R11, R14 + DECQ R13 + JA emit_lit_memmove_long_match_emit_encodeBetterBlockAsm10Blarge_forward_sse_loop_32 + LEAQ -32(R10)(R14*1), R11 + LEAQ -32(CX)(R14*1), R15 + +emit_lit_memmove_long_match_emit_encodeBetterBlockAsm10Blarge_big_loop_back: + MOVOU (R11), X4 + MOVOU 16(R11), X5 + MOVOA X4, (R15) + MOVOA X5, 16(R15) + ADDQ $0x20, R15 + ADDQ $0x20, R11 + ADDQ $0x20, R14 + DECQ R13 + JNA emit_lit_memmove_long_match_emit_encodeBetterBlockAsm10Blarge_big_loop_back + +emit_lit_memmove_long_match_emit_encodeBetterBlockAsm10Blarge_forward_sse_loop_32: + MOVOU -32(R10)(R14*1), X4 + MOVOU -16(R10)(R14*1), X5 + MOVOA X4, -32(CX)(R14*1) + MOVOA X5, -16(CX)(R14*1) + ADDQ $0x20, R14 + CMPQ R9, R14 + JAE emit_lit_memmove_long_match_emit_encodeBetterBlockAsm10Blarge_forward_sse_loop_32 + MOVOU X0, (CX) + MOVOU X1, 16(CX) + MOVOU X2, -32(CX)(R9*1) + MOVOU X3, -16(CX)(R9*1) + MOVQ SI, CX + +emit_literal_done_match_emit_encodeBetterBlockAsm10B: + ADDL R12, DX + ADDL $0x04, R12 + MOVL DX, 12(SP) + + // emitCopy + CMPL R12, $0x40 + JBE two_byte_offset_short_match_nolit_encodeBetterBlockAsm10B + CMPL R8, $0x00000800 + JAE long_offset_short_match_nolit_encodeBetterBlockAsm10B + MOVL $0x00000001, SI + LEAL 16(SI), SI + MOVB R8, 1(CX) + SHRL $0x08, R8 + SHLL $0x05, R8 + ORL R8, SI + MOVB SI, (CX) + ADDQ $0x02, CX + SUBL $0x08, R12 + + // emitRepeat + LEAL -4(R12), R12 + JMP cant_repeat_two_offset_match_nolit_encodeBetterBlockAsm10B_emit_copy_short_2b + MOVL R12, SI + LEAL -4(R12), R12 + CMPL SI, $0x08 + JBE repeat_two_match_nolit_encodeBetterBlockAsm10B_emit_copy_short_2b + CMPL SI, $0x0c + JAE cant_repeat_two_offset_match_nolit_encodeBetterBlockAsm10B_emit_copy_short_2b + CMPL R8, $0x00000800 + JB repeat_two_offset_match_nolit_encodeBetterBlockAsm10B_emit_copy_short_2b + +cant_repeat_two_offset_match_nolit_encodeBetterBlockAsm10B_emit_copy_short_2b: + CMPL R12, $0x00000104 + JB repeat_three_match_nolit_encodeBetterBlockAsm10B_emit_copy_short_2b + LEAL -256(R12), R12 + MOVW $0x0019, (CX) + MOVW R12, 2(CX) + ADDQ $0x04, CX + JMP match_nolit_emitcopy_end_encodeBetterBlockAsm10B + +repeat_three_match_nolit_encodeBetterBlockAsm10B_emit_copy_short_2b: + LEAL -4(R12), R12 + MOVW $0x0015, (CX) + MOVB R12, 2(CX) + ADDQ $0x03, CX + JMP match_nolit_emitcopy_end_encodeBetterBlockAsm10B + +repeat_two_match_nolit_encodeBetterBlockAsm10B_emit_copy_short_2b: + SHLL $0x02, R12 + ORL $0x01, R12 + MOVW R12, (CX) + ADDQ $0x02, CX + JMP match_nolit_emitcopy_end_encodeBetterBlockAsm10B + +repeat_two_offset_match_nolit_encodeBetterBlockAsm10B_emit_copy_short_2b: + XORQ SI, SI + LEAL 1(SI)(R12*4), R12 + MOVB R8, 1(CX) + SARL $0x08, R8 + SHLL $0x05, R8 + ORL R8, R12 + MOVB R12, (CX) + ADDQ $0x02, CX + JMP match_nolit_emitcopy_end_encodeBetterBlockAsm10B + +long_offset_short_match_nolit_encodeBetterBlockAsm10B: + MOVB $0xee, (CX) + MOVW R8, 1(CX) + LEAL -60(R12), R12 + ADDQ $0x03, CX + + // emitRepeat + MOVL R12, SI + LEAL -4(R12), R12 + CMPL SI, $0x08 + JBE repeat_two_match_nolit_encodeBetterBlockAsm10B_emit_copy_short + CMPL SI, $0x0c + JAE cant_repeat_two_offset_match_nolit_encodeBetterBlockAsm10B_emit_copy_short + CMPL R8, $0x00000800 + JB repeat_two_offset_match_nolit_encodeBetterBlockAsm10B_emit_copy_short + +cant_repeat_two_offset_match_nolit_encodeBetterBlockAsm10B_emit_copy_short: + CMPL R12, $0x00000104 + JB repeat_three_match_nolit_encodeBetterBlockAsm10B_emit_copy_short + LEAL -256(R12), R12 + MOVW $0x0019, (CX) + MOVW R12, 2(CX) + ADDQ $0x04, CX + JMP match_nolit_emitcopy_end_encodeBetterBlockAsm10B + +repeat_three_match_nolit_encodeBetterBlockAsm10B_emit_copy_short: + LEAL -4(R12), R12 + MOVW $0x0015, (CX) + MOVB R12, 2(CX) + ADDQ $0x03, CX + JMP match_nolit_emitcopy_end_encodeBetterBlockAsm10B + +repeat_two_match_nolit_encodeBetterBlockAsm10B_emit_copy_short: + SHLL $0x02, R12 + ORL $0x01, R12 + MOVW R12, (CX) + ADDQ $0x02, CX + JMP match_nolit_emitcopy_end_encodeBetterBlockAsm10B + +repeat_two_offset_match_nolit_encodeBetterBlockAsm10B_emit_copy_short: + XORQ SI, SI + LEAL 1(SI)(R12*4), R12 + MOVB R8, 1(CX) + SARL $0x08, R8 + SHLL $0x05, R8 + ORL R8, R12 + MOVB R12, (CX) + ADDQ $0x02, CX + JMP match_nolit_emitcopy_end_encodeBetterBlockAsm10B + +two_byte_offset_short_match_nolit_encodeBetterBlockAsm10B: + MOVL R12, SI + SHLL $0x02, SI + CMPL R12, $0x0c + JAE emit_copy_three_match_nolit_encodeBetterBlockAsm10B + CMPL R8, $0x00000800 + JAE emit_copy_three_match_nolit_encodeBetterBlockAsm10B + LEAL -15(SI), SI + MOVB R8, 1(CX) + SHRL $0x08, R8 + SHLL $0x05, R8 + ORL R8, SI + MOVB SI, (CX) + ADDQ $0x02, CX + JMP match_nolit_emitcopy_end_encodeBetterBlockAsm10B + +emit_copy_three_match_nolit_encodeBetterBlockAsm10B: + LEAL -2(SI), SI + MOVB SI, (CX) + MOVW R8, 1(CX) + ADDQ $0x03, CX + JMP match_nolit_emitcopy_end_encodeBetterBlockAsm10B + +match_is_repeat_encodeBetterBlockAsm10B: + MOVL 12(SP), SI + CMPL SI, DI + JEQ emit_literal_done_match_emit_repeat_encodeBetterBlockAsm10B + MOVL DI, R9 + MOVL DI, 12(SP) + LEAQ (BX)(SI*1), R10 + SUBL SI, R9 + LEAL -1(R9), SI + CMPL SI, $0x3c + JB one_byte_match_emit_repeat_encodeBetterBlockAsm10B + CMPL SI, $0x00000100 + JB two_bytes_match_emit_repeat_encodeBetterBlockAsm10B + JB three_bytes_match_emit_repeat_encodeBetterBlockAsm10B + +three_bytes_match_emit_repeat_encodeBetterBlockAsm10B: + MOVB $0xf4, (CX) + MOVW SI, 1(CX) + ADDQ $0x03, CX + JMP memmove_long_match_emit_repeat_encodeBetterBlockAsm10B + +two_bytes_match_emit_repeat_encodeBetterBlockAsm10B: + MOVB $0xf0, (CX) + MOVB SI, 1(CX) + ADDQ $0x02, CX + CMPL SI, $0x40 + JB memmove_match_emit_repeat_encodeBetterBlockAsm10B + JMP memmove_long_match_emit_repeat_encodeBetterBlockAsm10B + +one_byte_match_emit_repeat_encodeBetterBlockAsm10B: + SHLB $0x02, SI + MOVB SI, (CX) + ADDQ $0x01, CX + +memmove_match_emit_repeat_encodeBetterBlockAsm10B: + LEAQ (CX)(R9*1), SI + + // genMemMoveShort + CMPQ R9, $0x04 + JBE emit_lit_memmove_match_emit_repeat_encodeBetterBlockAsm10B_memmove_move_4 + CMPQ R9, $0x08 + JB emit_lit_memmove_match_emit_repeat_encodeBetterBlockAsm10B_memmove_move_4through7 + CMPQ R9, $0x10 + JBE emit_lit_memmove_match_emit_repeat_encodeBetterBlockAsm10B_memmove_move_8through16 + CMPQ R9, $0x20 + JBE emit_lit_memmove_match_emit_repeat_encodeBetterBlockAsm10B_memmove_move_17through32 + JMP emit_lit_memmove_match_emit_repeat_encodeBetterBlockAsm10B_memmove_move_33through64 + +emit_lit_memmove_match_emit_repeat_encodeBetterBlockAsm10B_memmove_move_4: + MOVL (R10), R11 + MOVL R11, (CX) + JMP memmove_end_copy_match_emit_repeat_encodeBetterBlockAsm10B + +emit_lit_memmove_match_emit_repeat_encodeBetterBlockAsm10B_memmove_move_4through7: + MOVL (R10), R11 + MOVL -4(R10)(R9*1), R10 + MOVL R11, (CX) + MOVL R10, -4(CX)(R9*1) + JMP memmove_end_copy_match_emit_repeat_encodeBetterBlockAsm10B + +emit_lit_memmove_match_emit_repeat_encodeBetterBlockAsm10B_memmove_move_8through16: + MOVQ (R10), R11 + MOVQ -8(R10)(R9*1), R10 + MOVQ R11, (CX) + MOVQ R10, -8(CX)(R9*1) + JMP memmove_end_copy_match_emit_repeat_encodeBetterBlockAsm10B + +emit_lit_memmove_match_emit_repeat_encodeBetterBlockAsm10B_memmove_move_17through32: + MOVOU (R10), X0 + MOVOU -16(R10)(R9*1), X1 + MOVOU X0, (CX) + MOVOU X1, -16(CX)(R9*1) + JMP memmove_end_copy_match_emit_repeat_encodeBetterBlockAsm10B + +emit_lit_memmove_match_emit_repeat_encodeBetterBlockAsm10B_memmove_move_33through64: + MOVOU (R10), X0 + MOVOU 16(R10), X1 + MOVOU -32(R10)(R9*1), X2 + MOVOU -16(R10)(R9*1), X3 + MOVOU X0, (CX) + MOVOU X1, 16(CX) + MOVOU X2, -32(CX)(R9*1) + MOVOU X3, -16(CX)(R9*1) + +memmove_end_copy_match_emit_repeat_encodeBetterBlockAsm10B: + MOVQ SI, CX + JMP emit_literal_done_match_emit_repeat_encodeBetterBlockAsm10B + +memmove_long_match_emit_repeat_encodeBetterBlockAsm10B: + LEAQ (CX)(R9*1), SI + + // genMemMoveLong + MOVOU (R10), X0 + MOVOU 16(R10), X1 + MOVOU -32(R10)(R9*1), X2 + MOVOU -16(R10)(R9*1), X3 + MOVQ R9, R13 + SHRQ $0x05, R13 + MOVQ CX, R11 + ANDL $0x0000001f, R11 + MOVQ $0x00000040, R14 + SUBQ R11, R14 + DECQ R13 + JA emit_lit_memmove_long_match_emit_repeat_encodeBetterBlockAsm10Blarge_forward_sse_loop_32 + LEAQ -32(R10)(R14*1), R11 + LEAQ -32(CX)(R14*1), R15 + +emit_lit_memmove_long_match_emit_repeat_encodeBetterBlockAsm10Blarge_big_loop_back: + MOVOU (R11), X4 + MOVOU 16(R11), X5 + MOVOA X4, (R15) + MOVOA X5, 16(R15) + ADDQ $0x20, R15 + ADDQ $0x20, R11 + ADDQ $0x20, R14 + DECQ R13 + JNA emit_lit_memmove_long_match_emit_repeat_encodeBetterBlockAsm10Blarge_big_loop_back + +emit_lit_memmove_long_match_emit_repeat_encodeBetterBlockAsm10Blarge_forward_sse_loop_32: + MOVOU -32(R10)(R14*1), X4 + MOVOU -16(R10)(R14*1), X5 + MOVOA X4, -32(CX)(R14*1) + MOVOA X5, -16(CX)(R14*1) + ADDQ $0x20, R14 + CMPQ R9, R14 + JAE emit_lit_memmove_long_match_emit_repeat_encodeBetterBlockAsm10Blarge_forward_sse_loop_32 + MOVOU X0, (CX) + MOVOU X1, 16(CX) + MOVOU X2, -32(CX)(R9*1) + MOVOU X3, -16(CX)(R9*1) + MOVQ SI, CX + +emit_literal_done_match_emit_repeat_encodeBetterBlockAsm10B: + ADDL R12, DX + ADDL $0x04, R12 + MOVL DX, 12(SP) + + // emitRepeat + MOVL R12, SI + LEAL -4(R12), R12 + CMPL SI, $0x08 + JBE repeat_two_match_nolit_repeat_encodeBetterBlockAsm10B + CMPL SI, $0x0c + JAE cant_repeat_two_offset_match_nolit_repeat_encodeBetterBlockAsm10B + CMPL R8, $0x00000800 + JB repeat_two_offset_match_nolit_repeat_encodeBetterBlockAsm10B + +cant_repeat_two_offset_match_nolit_repeat_encodeBetterBlockAsm10B: + CMPL R12, $0x00000104 + JB repeat_three_match_nolit_repeat_encodeBetterBlockAsm10B + LEAL -256(R12), R12 + MOVW $0x0019, (CX) + MOVW R12, 2(CX) + ADDQ $0x04, CX + JMP match_nolit_emitcopy_end_encodeBetterBlockAsm10B + +repeat_three_match_nolit_repeat_encodeBetterBlockAsm10B: + LEAL -4(R12), R12 + MOVW $0x0015, (CX) + MOVB R12, 2(CX) + ADDQ $0x03, CX + JMP match_nolit_emitcopy_end_encodeBetterBlockAsm10B + +repeat_two_match_nolit_repeat_encodeBetterBlockAsm10B: + SHLL $0x02, R12 + ORL $0x01, R12 + MOVW R12, (CX) + ADDQ $0x02, CX + JMP match_nolit_emitcopy_end_encodeBetterBlockAsm10B + +repeat_two_offset_match_nolit_repeat_encodeBetterBlockAsm10B: + XORQ SI, SI + LEAL 1(SI)(R12*4), R12 + MOVB R8, 1(CX) + SARL $0x08, R8 + SHLL $0x05, R8 + ORL R8, R12 + MOVB R12, (CX) + ADDQ $0x02, CX + +match_nolit_emitcopy_end_encodeBetterBlockAsm10B: + CMPL DX, 8(SP) + JAE emit_remainder_encodeBetterBlockAsm10B + CMPQ CX, (SP) + JB match_nolit_dst_ok_encodeBetterBlockAsm10B + MOVQ $0x00000000, ret+56(FP) + RET + +match_nolit_dst_ok_encodeBetterBlockAsm10B: + MOVQ $0x0000cf1bbcdcbf9b, SI + MOVQ $0x9e3779b1, R8 + LEAQ 1(DI), DI + LEAQ -2(DX), R9 + MOVQ (BX)(DI*1), R10 + MOVQ 1(BX)(DI*1), R11 + MOVQ (BX)(R9*1), R12 + MOVQ 1(BX)(R9*1), R13 + SHLQ $0x10, R10 + IMULQ SI, R10 + SHRQ $0x34, R10 + SHLQ $0x20, R11 + IMULQ R8, R11 + SHRQ $0x36, R11 + SHLQ $0x10, R12 + IMULQ SI, R12 + SHRQ $0x34, R12 + SHLQ $0x20, R13 + IMULQ R8, R13 + SHRQ $0x36, R13 + LEAQ 1(DI), R8 + LEAQ 1(R9), R14 + MOVL DI, (AX)(R10*4) + MOVL R9, (AX)(R12*4) + MOVL R8, 16384(AX)(R11*4) + MOVL R14, 16384(AX)(R13*4) + LEAQ 1(R9)(DI*1), R8 + SHRQ $0x01, R8 + ADDQ $0x01, DI + SUBQ $0x01, R9 + +index_loop_encodeBetterBlockAsm10B: + CMPQ R8, R9 + JAE search_loop_encodeBetterBlockAsm10B + MOVQ (BX)(DI*1), R10 + MOVQ (BX)(R8*1), R11 + SHLQ $0x10, R10 + IMULQ SI, R10 + SHRQ $0x34, R10 + SHLQ $0x10, R11 + IMULQ SI, R11 + SHRQ $0x34, R11 + MOVL DI, (AX)(R10*4) + MOVL R8, (AX)(R11*4) + ADDQ $0x02, DI + ADDQ $0x02, R8 + JMP index_loop_encodeBetterBlockAsm10B + +emit_remainder_encodeBetterBlockAsm10B: + MOVQ src_len+32(FP), AX + SUBL 12(SP), AX + LEAQ 3(CX)(AX*1), AX + CMPQ AX, (SP) + JB emit_remainder_ok_encodeBetterBlockAsm10B + MOVQ $0x00000000, ret+56(FP) + RET + +emit_remainder_ok_encodeBetterBlockAsm10B: + MOVQ src_len+32(FP), AX + MOVL 12(SP), DX + CMPL DX, AX + JEQ emit_literal_done_emit_remainder_encodeBetterBlockAsm10B + MOVL AX, SI + MOVL AX, 12(SP) + LEAQ (BX)(DX*1), AX + SUBL DX, SI + LEAL -1(SI), DX + CMPL DX, $0x3c + JB one_byte_emit_remainder_encodeBetterBlockAsm10B + CMPL DX, $0x00000100 + JB two_bytes_emit_remainder_encodeBetterBlockAsm10B + JB three_bytes_emit_remainder_encodeBetterBlockAsm10B + +three_bytes_emit_remainder_encodeBetterBlockAsm10B: + MOVB $0xf4, (CX) + MOVW DX, 1(CX) + ADDQ $0x03, CX + JMP memmove_long_emit_remainder_encodeBetterBlockAsm10B + +two_bytes_emit_remainder_encodeBetterBlockAsm10B: + MOVB $0xf0, (CX) + MOVB DL, 1(CX) + ADDQ $0x02, CX + CMPL DX, $0x40 + JB memmove_emit_remainder_encodeBetterBlockAsm10B + JMP memmove_long_emit_remainder_encodeBetterBlockAsm10B + +one_byte_emit_remainder_encodeBetterBlockAsm10B: + SHLB $0x02, DL + MOVB DL, (CX) + ADDQ $0x01, CX + +memmove_emit_remainder_encodeBetterBlockAsm10B: + LEAQ (CX)(SI*1), DX + MOVL SI, BX + + // genMemMoveShort + CMPQ BX, $0x03 + JB emit_lit_memmove_emit_remainder_encodeBetterBlockAsm10B_memmove_move_1or2 + JE emit_lit_memmove_emit_remainder_encodeBetterBlockAsm10B_memmove_move_3 + CMPQ BX, $0x08 + JB emit_lit_memmove_emit_remainder_encodeBetterBlockAsm10B_memmove_move_4through7 + CMPQ BX, $0x10 + JBE emit_lit_memmove_emit_remainder_encodeBetterBlockAsm10B_memmove_move_8through16 + CMPQ BX, $0x20 + JBE emit_lit_memmove_emit_remainder_encodeBetterBlockAsm10B_memmove_move_17through32 + JMP emit_lit_memmove_emit_remainder_encodeBetterBlockAsm10B_memmove_move_33through64 + +emit_lit_memmove_emit_remainder_encodeBetterBlockAsm10B_memmove_move_1or2: + MOVB (AX), SI + MOVB -1(AX)(BX*1), AL + MOVB SI, (CX) + MOVB AL, -1(CX)(BX*1) + JMP memmove_end_copy_emit_remainder_encodeBetterBlockAsm10B + +emit_lit_memmove_emit_remainder_encodeBetterBlockAsm10B_memmove_move_3: + MOVW (AX), SI + MOVB 2(AX), AL + MOVW SI, (CX) + MOVB AL, 2(CX) + JMP memmove_end_copy_emit_remainder_encodeBetterBlockAsm10B + +emit_lit_memmove_emit_remainder_encodeBetterBlockAsm10B_memmove_move_4through7: + MOVL (AX), SI + MOVL -4(AX)(BX*1), AX + MOVL SI, (CX) + MOVL AX, -4(CX)(BX*1) + JMP memmove_end_copy_emit_remainder_encodeBetterBlockAsm10B + +emit_lit_memmove_emit_remainder_encodeBetterBlockAsm10B_memmove_move_8through16: + MOVQ (AX), SI + MOVQ -8(AX)(BX*1), AX + MOVQ SI, (CX) + MOVQ AX, -8(CX)(BX*1) + JMP memmove_end_copy_emit_remainder_encodeBetterBlockAsm10B + +emit_lit_memmove_emit_remainder_encodeBetterBlockAsm10B_memmove_move_17through32: + MOVOU (AX), X0 + MOVOU -16(AX)(BX*1), X1 + MOVOU X0, (CX) + MOVOU X1, -16(CX)(BX*1) + JMP memmove_end_copy_emit_remainder_encodeBetterBlockAsm10B + +emit_lit_memmove_emit_remainder_encodeBetterBlockAsm10B_memmove_move_33through64: + MOVOU (AX), X0 + MOVOU 16(AX), X1 + MOVOU -32(AX)(BX*1), X2 + MOVOU -16(AX)(BX*1), X3 + MOVOU X0, (CX) + MOVOU X1, 16(CX) + MOVOU X2, -32(CX)(BX*1) + MOVOU X3, -16(CX)(BX*1) + +memmove_end_copy_emit_remainder_encodeBetterBlockAsm10B: + MOVQ DX, CX + JMP emit_literal_done_emit_remainder_encodeBetterBlockAsm10B + +memmove_long_emit_remainder_encodeBetterBlockAsm10B: + LEAQ (CX)(SI*1), DX + MOVL SI, BX + + // genMemMoveLong + MOVOU (AX), X0 + MOVOU 16(AX), X1 + MOVOU -32(AX)(BX*1), X2 + MOVOU -16(AX)(BX*1), X3 + MOVQ BX, DI + SHRQ $0x05, DI + MOVQ CX, SI + ANDL $0x0000001f, SI + MOVQ $0x00000040, R8 + SUBQ SI, R8 + DECQ DI + JA emit_lit_memmove_long_emit_remainder_encodeBetterBlockAsm10Blarge_forward_sse_loop_32 + LEAQ -32(AX)(R8*1), SI + LEAQ -32(CX)(R8*1), R9 + +emit_lit_memmove_long_emit_remainder_encodeBetterBlockAsm10Blarge_big_loop_back: + MOVOU (SI), X4 + MOVOU 16(SI), X5 + MOVOA X4, (R9) + MOVOA X5, 16(R9) + ADDQ $0x20, R9 + ADDQ $0x20, SI + ADDQ $0x20, R8 + DECQ DI + JNA emit_lit_memmove_long_emit_remainder_encodeBetterBlockAsm10Blarge_big_loop_back + +emit_lit_memmove_long_emit_remainder_encodeBetterBlockAsm10Blarge_forward_sse_loop_32: + MOVOU -32(AX)(R8*1), X4 + MOVOU -16(AX)(R8*1), X5 + MOVOA X4, -32(CX)(R8*1) + MOVOA X5, -16(CX)(R8*1) + ADDQ $0x20, R8 + CMPQ BX, R8 + JAE emit_lit_memmove_long_emit_remainder_encodeBetterBlockAsm10Blarge_forward_sse_loop_32 + MOVOU X0, (CX) + MOVOU X1, 16(CX) + MOVOU X2, -32(CX)(BX*1) + MOVOU X3, -16(CX)(BX*1) + MOVQ DX, CX + +emit_literal_done_emit_remainder_encodeBetterBlockAsm10B: + MOVQ dst_base+0(FP), AX + SUBQ AX, CX + MOVQ CX, ret+56(FP) + RET + +// func encodeBetterBlockAsm8B(dst []byte, src []byte, tmp *[5120]byte) int +// Requires: BMI, SSE2 +TEXT ·encodeBetterBlockAsm8B(SB), $24-64 + MOVQ tmp+48(FP), AX + MOVQ dst_base+0(FP), CX + MOVQ $0x00000028, DX + MOVQ AX, BX + PXOR X0, X0 + +zero_loop_encodeBetterBlockAsm8B: + MOVOU X0, (BX) + MOVOU X0, 16(BX) + MOVOU X0, 32(BX) + MOVOU X0, 48(BX) + MOVOU X0, 64(BX) + MOVOU X0, 80(BX) + MOVOU X0, 96(BX) + MOVOU X0, 112(BX) + ADDQ $0x80, BX + DECQ DX + JNZ zero_loop_encodeBetterBlockAsm8B + MOVL $0x00000000, 12(SP) + MOVQ src_len+32(FP), DX + LEAQ -6(DX), BX + LEAQ -8(DX), SI + MOVL SI, 8(SP) + SHRQ $0x05, DX + SUBL DX, BX + LEAQ (CX)(BX*1), BX + MOVQ BX, (SP) + MOVL $0x00000001, DX + MOVL $0x00000000, 16(SP) + MOVQ src_base+24(FP), BX + +search_loop_encodeBetterBlockAsm8B: + MOVL DX, SI + SUBL 12(SP), SI + SHRL $0x04, SI + LEAL 1(DX)(SI*1), SI + CMPL SI, 8(SP) + JAE emit_remainder_encodeBetterBlockAsm8B + MOVQ (BX)(DX*1), DI + MOVL SI, 20(SP) + MOVQ $0x0000cf1bbcdcbf9b, R9 + MOVQ $0x9e3779b1, SI + MOVQ DI, R10 + MOVQ DI, R11 + SHLQ $0x10, R10 + IMULQ R9, R10 + SHRQ $0x36, R10 + SHLQ $0x20, R11 + IMULQ SI, R11 + SHRQ $0x38, R11 + MOVL (AX)(R10*4), SI + MOVL 4096(AX)(R11*4), R8 + MOVL DX, (AX)(R10*4) + MOVL DX, 4096(AX)(R11*4) + MOVQ (BX)(SI*1), R10 + MOVQ (BX)(R8*1), R11 + CMPQ R10, DI + JEQ candidate_match_encodeBetterBlockAsm8B + CMPQ R11, DI + JNE no_short_found_encodeBetterBlockAsm8B + MOVL R8, SI + JMP candidate_match_encodeBetterBlockAsm8B + +no_short_found_encodeBetterBlockAsm8B: + CMPL R10, DI + JEQ candidate_match_encodeBetterBlockAsm8B + CMPL R11, DI + JEQ candidateS_match_encodeBetterBlockAsm8B + MOVL 20(SP), DX + JMP search_loop_encodeBetterBlockAsm8B + +candidateS_match_encodeBetterBlockAsm8B: + SHRQ $0x08, DI + MOVQ DI, R10 + SHLQ $0x10, R10 + IMULQ R9, R10 + SHRQ $0x36, R10 + MOVL (AX)(R10*4), SI + INCL DX + MOVL DX, (AX)(R10*4) + CMPL (BX)(SI*1), DI + JEQ candidate_match_encodeBetterBlockAsm8B + DECL DX + MOVL R8, SI + +candidate_match_encodeBetterBlockAsm8B: + MOVL 12(SP), DI + TESTL SI, SI + JZ match_extend_back_end_encodeBetterBlockAsm8B + +match_extend_back_loop_encodeBetterBlockAsm8B: + CMPL DX, DI + JBE match_extend_back_end_encodeBetterBlockAsm8B + MOVB -1(BX)(SI*1), R8 + MOVB -1(BX)(DX*1), R9 + CMPB R8, R9 + JNE match_extend_back_end_encodeBetterBlockAsm8B + LEAL -1(DX), DX + DECL SI + JZ match_extend_back_end_encodeBetterBlockAsm8B + JMP match_extend_back_loop_encodeBetterBlockAsm8B + +match_extend_back_end_encodeBetterBlockAsm8B: + MOVL DX, DI + SUBL 12(SP), DI + LEAQ 3(CX)(DI*1), DI + CMPQ DI, (SP) + JB match_dst_size_check_encodeBetterBlockAsm8B + MOVQ $0x00000000, ret+56(FP) + RET + +match_dst_size_check_encodeBetterBlockAsm8B: + MOVL DX, DI + ADDL $0x04, DX + ADDL $0x04, SI + MOVQ src_len+32(FP), R8 + SUBL DX, R8 + LEAQ (BX)(DX*1), R9 + LEAQ (BX)(SI*1), R10 + + // matchLen + XORL R12, R12 + +matchlen_loopback_16_match_nolit_encodeBetterBlockAsm8B: + CMPL R8, $0x10 + JB matchlen_match8_match_nolit_encodeBetterBlockAsm8B + MOVQ (R9)(R12*1), R11 + MOVQ 8(R9)(R12*1), R13 + XORQ (R10)(R12*1), R11 + JNZ matchlen_bsf_8_match_nolit_encodeBetterBlockAsm8B + XORQ 8(R10)(R12*1), R13 + JNZ matchlen_bsf_16match_nolit_encodeBetterBlockAsm8B + LEAL -16(R8), R8 + LEAL 16(R12), R12 + JMP matchlen_loopback_16_match_nolit_encodeBetterBlockAsm8B + +matchlen_bsf_16match_nolit_encodeBetterBlockAsm8B: +#ifdef GOAMD64_v3 + TZCNTQ R13, R13 + +#else + BSFQ R13, R13 + +#endif + SARQ $0x03, R13 + LEAL 8(R12)(R13*1), R12 + JMP match_nolit_end_encodeBetterBlockAsm8B + +matchlen_match8_match_nolit_encodeBetterBlockAsm8B: + CMPL R8, $0x08 + JB matchlen_match4_match_nolit_encodeBetterBlockAsm8B + MOVQ (R9)(R12*1), R11 + XORQ (R10)(R12*1), R11 + JNZ matchlen_bsf_8_match_nolit_encodeBetterBlockAsm8B + LEAL -8(R8), R8 + LEAL 8(R12), R12 + JMP matchlen_match4_match_nolit_encodeBetterBlockAsm8B + +matchlen_bsf_8_match_nolit_encodeBetterBlockAsm8B: +#ifdef GOAMD64_v3 + TZCNTQ R11, R11 + +#else + BSFQ R11, R11 + +#endif + SARQ $0x03, R11 + LEAL (R12)(R11*1), R12 + JMP match_nolit_end_encodeBetterBlockAsm8B + +matchlen_match4_match_nolit_encodeBetterBlockAsm8B: + CMPL R8, $0x04 + JB matchlen_match2_match_nolit_encodeBetterBlockAsm8B + MOVL (R9)(R12*1), R11 + CMPL (R10)(R12*1), R11 + JNE matchlen_match2_match_nolit_encodeBetterBlockAsm8B + LEAL -4(R8), R8 + LEAL 4(R12), R12 + +matchlen_match2_match_nolit_encodeBetterBlockAsm8B: + CMPL R8, $0x01 + JE matchlen_match1_match_nolit_encodeBetterBlockAsm8B + JB match_nolit_end_encodeBetterBlockAsm8B + MOVW (R9)(R12*1), R11 + CMPW (R10)(R12*1), R11 + JNE matchlen_match1_match_nolit_encodeBetterBlockAsm8B + LEAL 2(R12), R12 + SUBL $0x02, R8 + JZ match_nolit_end_encodeBetterBlockAsm8B + +matchlen_match1_match_nolit_encodeBetterBlockAsm8B: + MOVB (R9)(R12*1), R11 + CMPB (R10)(R12*1), R11 + JNE match_nolit_end_encodeBetterBlockAsm8B + LEAL 1(R12), R12 + +match_nolit_end_encodeBetterBlockAsm8B: + MOVL DX, R8 + SUBL SI, R8 + + // Check if repeat + CMPL 16(SP), R8 + JEQ match_is_repeat_encodeBetterBlockAsm8B + MOVL R8, 16(SP) + MOVL 12(SP), SI + CMPL SI, DI + JEQ emit_literal_done_match_emit_encodeBetterBlockAsm8B + MOVL DI, R9 + MOVL DI, 12(SP) + LEAQ (BX)(SI*1), R10 + SUBL SI, R9 + LEAL -1(R9), SI + CMPL SI, $0x3c + JB one_byte_match_emit_encodeBetterBlockAsm8B + CMPL SI, $0x00000100 + JB two_bytes_match_emit_encodeBetterBlockAsm8B + JB three_bytes_match_emit_encodeBetterBlockAsm8B + +three_bytes_match_emit_encodeBetterBlockAsm8B: + MOVB $0xf4, (CX) + MOVW SI, 1(CX) + ADDQ $0x03, CX + JMP memmove_long_match_emit_encodeBetterBlockAsm8B + +two_bytes_match_emit_encodeBetterBlockAsm8B: + MOVB $0xf0, (CX) + MOVB SI, 1(CX) + ADDQ $0x02, CX + CMPL SI, $0x40 + JB memmove_match_emit_encodeBetterBlockAsm8B + JMP memmove_long_match_emit_encodeBetterBlockAsm8B + +one_byte_match_emit_encodeBetterBlockAsm8B: + SHLB $0x02, SI + MOVB SI, (CX) + ADDQ $0x01, CX + +memmove_match_emit_encodeBetterBlockAsm8B: + LEAQ (CX)(R9*1), SI + + // genMemMoveShort + CMPQ R9, $0x04 + JBE emit_lit_memmove_match_emit_encodeBetterBlockAsm8B_memmove_move_4 + CMPQ R9, $0x08 + JB emit_lit_memmove_match_emit_encodeBetterBlockAsm8B_memmove_move_4through7 + CMPQ R9, $0x10 + JBE emit_lit_memmove_match_emit_encodeBetterBlockAsm8B_memmove_move_8through16 + CMPQ R9, $0x20 + JBE emit_lit_memmove_match_emit_encodeBetterBlockAsm8B_memmove_move_17through32 + JMP emit_lit_memmove_match_emit_encodeBetterBlockAsm8B_memmove_move_33through64 + +emit_lit_memmove_match_emit_encodeBetterBlockAsm8B_memmove_move_4: + MOVL (R10), R11 + MOVL R11, (CX) + JMP memmove_end_copy_match_emit_encodeBetterBlockAsm8B + +emit_lit_memmove_match_emit_encodeBetterBlockAsm8B_memmove_move_4through7: + MOVL (R10), R11 + MOVL -4(R10)(R9*1), R10 + MOVL R11, (CX) + MOVL R10, -4(CX)(R9*1) + JMP memmove_end_copy_match_emit_encodeBetterBlockAsm8B + +emit_lit_memmove_match_emit_encodeBetterBlockAsm8B_memmove_move_8through16: + MOVQ (R10), R11 + MOVQ -8(R10)(R9*1), R10 + MOVQ R11, (CX) + MOVQ R10, -8(CX)(R9*1) + JMP memmove_end_copy_match_emit_encodeBetterBlockAsm8B + +emit_lit_memmove_match_emit_encodeBetterBlockAsm8B_memmove_move_17through32: + MOVOU (R10), X0 + MOVOU -16(R10)(R9*1), X1 + MOVOU X0, (CX) + MOVOU X1, -16(CX)(R9*1) + JMP memmove_end_copy_match_emit_encodeBetterBlockAsm8B + +emit_lit_memmove_match_emit_encodeBetterBlockAsm8B_memmove_move_33through64: + MOVOU (R10), X0 + MOVOU 16(R10), X1 + MOVOU -32(R10)(R9*1), X2 + MOVOU -16(R10)(R9*1), X3 + MOVOU X0, (CX) + MOVOU X1, 16(CX) + MOVOU X2, -32(CX)(R9*1) + MOVOU X3, -16(CX)(R9*1) + +memmove_end_copy_match_emit_encodeBetterBlockAsm8B: + MOVQ SI, CX + JMP emit_literal_done_match_emit_encodeBetterBlockAsm8B + +memmove_long_match_emit_encodeBetterBlockAsm8B: + LEAQ (CX)(R9*1), SI + + // genMemMoveLong + MOVOU (R10), X0 + MOVOU 16(R10), X1 + MOVOU -32(R10)(R9*1), X2 + MOVOU -16(R10)(R9*1), X3 + MOVQ R9, R13 + SHRQ $0x05, R13 + MOVQ CX, R11 + ANDL $0x0000001f, R11 + MOVQ $0x00000040, R14 + SUBQ R11, R14 + DECQ R13 + JA emit_lit_memmove_long_match_emit_encodeBetterBlockAsm8Blarge_forward_sse_loop_32 + LEAQ -32(R10)(R14*1), R11 + LEAQ -32(CX)(R14*1), R15 + +emit_lit_memmove_long_match_emit_encodeBetterBlockAsm8Blarge_big_loop_back: + MOVOU (R11), X4 + MOVOU 16(R11), X5 + MOVOA X4, (R15) + MOVOA X5, 16(R15) + ADDQ $0x20, R15 + ADDQ $0x20, R11 + ADDQ $0x20, R14 + DECQ R13 + JNA emit_lit_memmove_long_match_emit_encodeBetterBlockAsm8Blarge_big_loop_back + +emit_lit_memmove_long_match_emit_encodeBetterBlockAsm8Blarge_forward_sse_loop_32: + MOVOU -32(R10)(R14*1), X4 + MOVOU -16(R10)(R14*1), X5 + MOVOA X4, -32(CX)(R14*1) + MOVOA X5, -16(CX)(R14*1) + ADDQ $0x20, R14 + CMPQ R9, R14 + JAE emit_lit_memmove_long_match_emit_encodeBetterBlockAsm8Blarge_forward_sse_loop_32 + MOVOU X0, (CX) + MOVOU X1, 16(CX) + MOVOU X2, -32(CX)(R9*1) + MOVOU X3, -16(CX)(R9*1) + MOVQ SI, CX + +emit_literal_done_match_emit_encodeBetterBlockAsm8B: + ADDL R12, DX + ADDL $0x04, R12 + MOVL DX, 12(SP) + + // emitCopy + CMPL R12, $0x40 + JBE two_byte_offset_short_match_nolit_encodeBetterBlockAsm8B + CMPL R8, $0x00000800 + JAE long_offset_short_match_nolit_encodeBetterBlockAsm8B + MOVL $0x00000001, SI + LEAL 16(SI), SI + MOVB R8, 1(CX) + SHRL $0x08, R8 + SHLL $0x05, R8 + ORL R8, SI + MOVB SI, (CX) + ADDQ $0x02, CX + SUBL $0x08, R12 + + // emitRepeat + LEAL -4(R12), R12 + JMP cant_repeat_two_offset_match_nolit_encodeBetterBlockAsm8B_emit_copy_short_2b + MOVL R12, SI + LEAL -4(R12), R12 + CMPL SI, $0x08 + JBE repeat_two_match_nolit_encodeBetterBlockAsm8B_emit_copy_short_2b + CMPL SI, $0x0c + JAE cant_repeat_two_offset_match_nolit_encodeBetterBlockAsm8B_emit_copy_short_2b + +cant_repeat_two_offset_match_nolit_encodeBetterBlockAsm8B_emit_copy_short_2b: + CMPL R12, $0x00000104 + JB repeat_three_match_nolit_encodeBetterBlockAsm8B_emit_copy_short_2b + LEAL -256(R12), R12 + MOVW $0x0019, (CX) + MOVW R12, 2(CX) + ADDQ $0x04, CX + JMP match_nolit_emitcopy_end_encodeBetterBlockAsm8B + +repeat_three_match_nolit_encodeBetterBlockAsm8B_emit_copy_short_2b: + LEAL -4(R12), R12 + MOVW $0x0015, (CX) + MOVB R12, 2(CX) + ADDQ $0x03, CX + JMP match_nolit_emitcopy_end_encodeBetterBlockAsm8B + +repeat_two_match_nolit_encodeBetterBlockAsm8B_emit_copy_short_2b: + SHLL $0x02, R12 + ORL $0x01, R12 + MOVW R12, (CX) + ADDQ $0x02, CX + JMP match_nolit_emitcopy_end_encodeBetterBlockAsm8B + XORQ SI, SI + LEAL 1(SI)(R12*4), R12 + MOVB R8, 1(CX) + SARL $0x08, R8 + SHLL $0x05, R8 + ORL R8, R12 + MOVB R12, (CX) + ADDQ $0x02, CX + JMP match_nolit_emitcopy_end_encodeBetterBlockAsm8B + +long_offset_short_match_nolit_encodeBetterBlockAsm8B: + MOVB $0xee, (CX) + MOVW R8, 1(CX) + LEAL -60(R12), R12 + ADDQ $0x03, CX + + // emitRepeat + MOVL R12, SI + LEAL -4(R12), R12 + CMPL SI, $0x08 + JBE repeat_two_match_nolit_encodeBetterBlockAsm8B_emit_copy_short + CMPL SI, $0x0c + JAE cant_repeat_two_offset_match_nolit_encodeBetterBlockAsm8B_emit_copy_short + +cant_repeat_two_offset_match_nolit_encodeBetterBlockAsm8B_emit_copy_short: + CMPL R12, $0x00000104 + JB repeat_three_match_nolit_encodeBetterBlockAsm8B_emit_copy_short + LEAL -256(R12), R12 + MOVW $0x0019, (CX) + MOVW R12, 2(CX) + ADDQ $0x04, CX + JMP match_nolit_emitcopy_end_encodeBetterBlockAsm8B + +repeat_three_match_nolit_encodeBetterBlockAsm8B_emit_copy_short: + LEAL -4(R12), R12 + MOVW $0x0015, (CX) + MOVB R12, 2(CX) + ADDQ $0x03, CX + JMP match_nolit_emitcopy_end_encodeBetterBlockAsm8B + +repeat_two_match_nolit_encodeBetterBlockAsm8B_emit_copy_short: + SHLL $0x02, R12 + ORL $0x01, R12 + MOVW R12, (CX) + ADDQ $0x02, CX + JMP match_nolit_emitcopy_end_encodeBetterBlockAsm8B + XORQ SI, SI + LEAL 1(SI)(R12*4), R12 + MOVB R8, 1(CX) + SARL $0x08, R8 + SHLL $0x05, R8 + ORL R8, R12 + MOVB R12, (CX) + ADDQ $0x02, CX + JMP match_nolit_emitcopy_end_encodeBetterBlockAsm8B + +two_byte_offset_short_match_nolit_encodeBetterBlockAsm8B: + MOVL R12, SI + SHLL $0x02, SI + CMPL R12, $0x0c + JAE emit_copy_three_match_nolit_encodeBetterBlockAsm8B + LEAL -15(SI), SI + MOVB R8, 1(CX) + SHRL $0x08, R8 + SHLL $0x05, R8 + ORL R8, SI + MOVB SI, (CX) + ADDQ $0x02, CX + JMP match_nolit_emitcopy_end_encodeBetterBlockAsm8B + +emit_copy_three_match_nolit_encodeBetterBlockAsm8B: + LEAL -2(SI), SI + MOVB SI, (CX) + MOVW R8, 1(CX) + ADDQ $0x03, CX + JMP match_nolit_emitcopy_end_encodeBetterBlockAsm8B + +match_is_repeat_encodeBetterBlockAsm8B: + MOVL 12(SP), SI + CMPL SI, DI + JEQ emit_literal_done_match_emit_repeat_encodeBetterBlockAsm8B + MOVL DI, R8 + MOVL DI, 12(SP) + LEAQ (BX)(SI*1), R9 + SUBL SI, R8 + LEAL -1(R8), SI + CMPL SI, $0x3c + JB one_byte_match_emit_repeat_encodeBetterBlockAsm8B + CMPL SI, $0x00000100 + JB two_bytes_match_emit_repeat_encodeBetterBlockAsm8B + JB three_bytes_match_emit_repeat_encodeBetterBlockAsm8B + +three_bytes_match_emit_repeat_encodeBetterBlockAsm8B: + MOVB $0xf4, (CX) + MOVW SI, 1(CX) + ADDQ $0x03, CX + JMP memmove_long_match_emit_repeat_encodeBetterBlockAsm8B + +two_bytes_match_emit_repeat_encodeBetterBlockAsm8B: + MOVB $0xf0, (CX) + MOVB SI, 1(CX) + ADDQ $0x02, CX + CMPL SI, $0x40 + JB memmove_match_emit_repeat_encodeBetterBlockAsm8B + JMP memmove_long_match_emit_repeat_encodeBetterBlockAsm8B + +one_byte_match_emit_repeat_encodeBetterBlockAsm8B: + SHLB $0x02, SI + MOVB SI, (CX) + ADDQ $0x01, CX + +memmove_match_emit_repeat_encodeBetterBlockAsm8B: + LEAQ (CX)(R8*1), SI + + // genMemMoveShort + CMPQ R8, $0x04 + JBE emit_lit_memmove_match_emit_repeat_encodeBetterBlockAsm8B_memmove_move_4 + CMPQ R8, $0x08 + JB emit_lit_memmove_match_emit_repeat_encodeBetterBlockAsm8B_memmove_move_4through7 + CMPQ R8, $0x10 + JBE emit_lit_memmove_match_emit_repeat_encodeBetterBlockAsm8B_memmove_move_8through16 + CMPQ R8, $0x20 + JBE emit_lit_memmove_match_emit_repeat_encodeBetterBlockAsm8B_memmove_move_17through32 + JMP emit_lit_memmove_match_emit_repeat_encodeBetterBlockAsm8B_memmove_move_33through64 + +emit_lit_memmove_match_emit_repeat_encodeBetterBlockAsm8B_memmove_move_4: + MOVL (R9), R10 + MOVL R10, (CX) + JMP memmove_end_copy_match_emit_repeat_encodeBetterBlockAsm8B + +emit_lit_memmove_match_emit_repeat_encodeBetterBlockAsm8B_memmove_move_4through7: + MOVL (R9), R10 + MOVL -4(R9)(R8*1), R9 + MOVL R10, (CX) + MOVL R9, -4(CX)(R8*1) + JMP memmove_end_copy_match_emit_repeat_encodeBetterBlockAsm8B + +emit_lit_memmove_match_emit_repeat_encodeBetterBlockAsm8B_memmove_move_8through16: + MOVQ (R9), R10 + MOVQ -8(R9)(R8*1), R9 + MOVQ R10, (CX) + MOVQ R9, -8(CX)(R8*1) + JMP memmove_end_copy_match_emit_repeat_encodeBetterBlockAsm8B + +emit_lit_memmove_match_emit_repeat_encodeBetterBlockAsm8B_memmove_move_17through32: + MOVOU (R9), X0 + MOVOU -16(R9)(R8*1), X1 + MOVOU X0, (CX) + MOVOU X1, -16(CX)(R8*1) + JMP memmove_end_copy_match_emit_repeat_encodeBetterBlockAsm8B + +emit_lit_memmove_match_emit_repeat_encodeBetterBlockAsm8B_memmove_move_33through64: + MOVOU (R9), X0 + MOVOU 16(R9), X1 + MOVOU -32(R9)(R8*1), X2 + MOVOU -16(R9)(R8*1), X3 + MOVOU X0, (CX) + MOVOU X1, 16(CX) + MOVOU X2, -32(CX)(R8*1) + MOVOU X3, -16(CX)(R8*1) + +memmove_end_copy_match_emit_repeat_encodeBetterBlockAsm8B: + MOVQ SI, CX + JMP emit_literal_done_match_emit_repeat_encodeBetterBlockAsm8B + +memmove_long_match_emit_repeat_encodeBetterBlockAsm8B: + LEAQ (CX)(R8*1), SI + + // genMemMoveLong + MOVOU (R9), X0 + MOVOU 16(R9), X1 + MOVOU -32(R9)(R8*1), X2 + MOVOU -16(R9)(R8*1), X3 + MOVQ R8, R11 + SHRQ $0x05, R11 + MOVQ CX, R10 + ANDL $0x0000001f, R10 + MOVQ $0x00000040, R13 + SUBQ R10, R13 + DECQ R11 + JA emit_lit_memmove_long_match_emit_repeat_encodeBetterBlockAsm8Blarge_forward_sse_loop_32 + LEAQ -32(R9)(R13*1), R10 + LEAQ -32(CX)(R13*1), R14 + +emit_lit_memmove_long_match_emit_repeat_encodeBetterBlockAsm8Blarge_big_loop_back: + MOVOU (R10), X4 + MOVOU 16(R10), X5 + MOVOA X4, (R14) + MOVOA X5, 16(R14) + ADDQ $0x20, R14 + ADDQ $0x20, R10 + ADDQ $0x20, R13 + DECQ R11 + JNA emit_lit_memmove_long_match_emit_repeat_encodeBetterBlockAsm8Blarge_big_loop_back + +emit_lit_memmove_long_match_emit_repeat_encodeBetterBlockAsm8Blarge_forward_sse_loop_32: + MOVOU -32(R9)(R13*1), X4 + MOVOU -16(R9)(R13*1), X5 + MOVOA X4, -32(CX)(R13*1) + MOVOA X5, -16(CX)(R13*1) + ADDQ $0x20, R13 + CMPQ R8, R13 + JAE emit_lit_memmove_long_match_emit_repeat_encodeBetterBlockAsm8Blarge_forward_sse_loop_32 + MOVOU X0, (CX) + MOVOU X1, 16(CX) + MOVOU X2, -32(CX)(R8*1) + MOVOU X3, -16(CX)(R8*1) + MOVQ SI, CX + +emit_literal_done_match_emit_repeat_encodeBetterBlockAsm8B: + ADDL R12, DX + ADDL $0x04, R12 + MOVL DX, 12(SP) + + // emitRepeat + MOVL R12, SI + LEAL -4(R12), R12 + CMPL SI, $0x08 + JBE repeat_two_match_nolit_repeat_encodeBetterBlockAsm8B + CMPL SI, $0x0c + JAE cant_repeat_two_offset_match_nolit_repeat_encodeBetterBlockAsm8B + +cant_repeat_two_offset_match_nolit_repeat_encodeBetterBlockAsm8B: + CMPL R12, $0x00000104 + JB repeat_three_match_nolit_repeat_encodeBetterBlockAsm8B + LEAL -256(R12), R12 + MOVW $0x0019, (CX) + MOVW R12, 2(CX) + ADDQ $0x04, CX + JMP match_nolit_emitcopy_end_encodeBetterBlockAsm8B + +repeat_three_match_nolit_repeat_encodeBetterBlockAsm8B: + LEAL -4(R12), R12 + MOVW $0x0015, (CX) + MOVB R12, 2(CX) + ADDQ $0x03, CX + JMP match_nolit_emitcopy_end_encodeBetterBlockAsm8B + +repeat_two_match_nolit_repeat_encodeBetterBlockAsm8B: + SHLL $0x02, R12 + ORL $0x01, R12 + MOVW R12, (CX) + ADDQ $0x02, CX + JMP match_nolit_emitcopy_end_encodeBetterBlockAsm8B + XORQ SI, SI + LEAL 1(SI)(R12*4), R12 + MOVB R8, 1(CX) + SARL $0x08, R8 + SHLL $0x05, R8 + ORL R8, R12 + MOVB R12, (CX) + ADDQ $0x02, CX + +match_nolit_emitcopy_end_encodeBetterBlockAsm8B: + CMPL DX, 8(SP) + JAE emit_remainder_encodeBetterBlockAsm8B + CMPQ CX, (SP) + JB match_nolit_dst_ok_encodeBetterBlockAsm8B + MOVQ $0x00000000, ret+56(FP) + RET + +match_nolit_dst_ok_encodeBetterBlockAsm8B: + MOVQ $0x0000cf1bbcdcbf9b, SI + MOVQ $0x9e3779b1, R8 + LEAQ 1(DI), DI + LEAQ -2(DX), R9 + MOVQ (BX)(DI*1), R10 + MOVQ 1(BX)(DI*1), R11 + MOVQ (BX)(R9*1), R12 + MOVQ 1(BX)(R9*1), R13 + SHLQ $0x10, R10 + IMULQ SI, R10 + SHRQ $0x36, R10 + SHLQ $0x20, R11 + IMULQ R8, R11 + SHRQ $0x38, R11 + SHLQ $0x10, R12 + IMULQ SI, R12 + SHRQ $0x36, R12 + SHLQ $0x20, R13 + IMULQ R8, R13 + SHRQ $0x38, R13 + LEAQ 1(DI), R8 + LEAQ 1(R9), R14 + MOVL DI, (AX)(R10*4) + MOVL R9, (AX)(R12*4) + MOVL R8, 4096(AX)(R11*4) + MOVL R14, 4096(AX)(R13*4) + LEAQ 1(R9)(DI*1), R8 + SHRQ $0x01, R8 + ADDQ $0x01, DI + SUBQ $0x01, R9 + +index_loop_encodeBetterBlockAsm8B: + CMPQ R8, R9 + JAE search_loop_encodeBetterBlockAsm8B + MOVQ (BX)(DI*1), R10 + MOVQ (BX)(R8*1), R11 + SHLQ $0x10, R10 + IMULQ SI, R10 + SHRQ $0x36, R10 + SHLQ $0x10, R11 + IMULQ SI, R11 + SHRQ $0x36, R11 + MOVL DI, (AX)(R10*4) + MOVL R8, (AX)(R11*4) + ADDQ $0x02, DI + ADDQ $0x02, R8 + JMP index_loop_encodeBetterBlockAsm8B + +emit_remainder_encodeBetterBlockAsm8B: + MOVQ src_len+32(FP), AX + SUBL 12(SP), AX + LEAQ 3(CX)(AX*1), AX + CMPQ AX, (SP) + JB emit_remainder_ok_encodeBetterBlockAsm8B + MOVQ $0x00000000, ret+56(FP) + RET + +emit_remainder_ok_encodeBetterBlockAsm8B: + MOVQ src_len+32(FP), AX + MOVL 12(SP), DX + CMPL DX, AX + JEQ emit_literal_done_emit_remainder_encodeBetterBlockAsm8B + MOVL AX, SI + MOVL AX, 12(SP) + LEAQ (BX)(DX*1), AX + SUBL DX, SI + LEAL -1(SI), DX + CMPL DX, $0x3c + JB one_byte_emit_remainder_encodeBetterBlockAsm8B + CMPL DX, $0x00000100 + JB two_bytes_emit_remainder_encodeBetterBlockAsm8B + JB three_bytes_emit_remainder_encodeBetterBlockAsm8B + +three_bytes_emit_remainder_encodeBetterBlockAsm8B: + MOVB $0xf4, (CX) + MOVW DX, 1(CX) + ADDQ $0x03, CX + JMP memmove_long_emit_remainder_encodeBetterBlockAsm8B + +two_bytes_emit_remainder_encodeBetterBlockAsm8B: + MOVB $0xf0, (CX) + MOVB DL, 1(CX) + ADDQ $0x02, CX + CMPL DX, $0x40 + JB memmove_emit_remainder_encodeBetterBlockAsm8B + JMP memmove_long_emit_remainder_encodeBetterBlockAsm8B + +one_byte_emit_remainder_encodeBetterBlockAsm8B: + SHLB $0x02, DL + MOVB DL, (CX) + ADDQ $0x01, CX + +memmove_emit_remainder_encodeBetterBlockAsm8B: + LEAQ (CX)(SI*1), DX + MOVL SI, BX + + // genMemMoveShort + CMPQ BX, $0x03 + JB emit_lit_memmove_emit_remainder_encodeBetterBlockAsm8B_memmove_move_1or2 + JE emit_lit_memmove_emit_remainder_encodeBetterBlockAsm8B_memmove_move_3 + CMPQ BX, $0x08 + JB emit_lit_memmove_emit_remainder_encodeBetterBlockAsm8B_memmove_move_4through7 + CMPQ BX, $0x10 + JBE emit_lit_memmove_emit_remainder_encodeBetterBlockAsm8B_memmove_move_8through16 + CMPQ BX, $0x20 + JBE emit_lit_memmove_emit_remainder_encodeBetterBlockAsm8B_memmove_move_17through32 + JMP emit_lit_memmove_emit_remainder_encodeBetterBlockAsm8B_memmove_move_33through64 + +emit_lit_memmove_emit_remainder_encodeBetterBlockAsm8B_memmove_move_1or2: + MOVB (AX), SI + MOVB -1(AX)(BX*1), AL + MOVB SI, (CX) + MOVB AL, -1(CX)(BX*1) + JMP memmove_end_copy_emit_remainder_encodeBetterBlockAsm8B + +emit_lit_memmove_emit_remainder_encodeBetterBlockAsm8B_memmove_move_3: + MOVW (AX), SI + MOVB 2(AX), AL + MOVW SI, (CX) + MOVB AL, 2(CX) + JMP memmove_end_copy_emit_remainder_encodeBetterBlockAsm8B + +emit_lit_memmove_emit_remainder_encodeBetterBlockAsm8B_memmove_move_4through7: + MOVL (AX), SI + MOVL -4(AX)(BX*1), AX + MOVL SI, (CX) + MOVL AX, -4(CX)(BX*1) + JMP memmove_end_copy_emit_remainder_encodeBetterBlockAsm8B + +emit_lit_memmove_emit_remainder_encodeBetterBlockAsm8B_memmove_move_8through16: + MOVQ (AX), SI + MOVQ -8(AX)(BX*1), AX + MOVQ SI, (CX) + MOVQ AX, -8(CX)(BX*1) + JMP memmove_end_copy_emit_remainder_encodeBetterBlockAsm8B + +emit_lit_memmove_emit_remainder_encodeBetterBlockAsm8B_memmove_move_17through32: + MOVOU (AX), X0 + MOVOU -16(AX)(BX*1), X1 + MOVOU X0, (CX) + MOVOU X1, -16(CX)(BX*1) + JMP memmove_end_copy_emit_remainder_encodeBetterBlockAsm8B + +emit_lit_memmove_emit_remainder_encodeBetterBlockAsm8B_memmove_move_33through64: + MOVOU (AX), X0 + MOVOU 16(AX), X1 + MOVOU -32(AX)(BX*1), X2 + MOVOU -16(AX)(BX*1), X3 + MOVOU X0, (CX) + MOVOU X1, 16(CX) + MOVOU X2, -32(CX)(BX*1) + MOVOU X3, -16(CX)(BX*1) + +memmove_end_copy_emit_remainder_encodeBetterBlockAsm8B: + MOVQ DX, CX + JMP emit_literal_done_emit_remainder_encodeBetterBlockAsm8B + +memmove_long_emit_remainder_encodeBetterBlockAsm8B: + LEAQ (CX)(SI*1), DX + MOVL SI, BX + + // genMemMoveLong + MOVOU (AX), X0 + MOVOU 16(AX), X1 + MOVOU -32(AX)(BX*1), X2 + MOVOU -16(AX)(BX*1), X3 + MOVQ BX, DI + SHRQ $0x05, DI + MOVQ CX, SI + ANDL $0x0000001f, SI + MOVQ $0x00000040, R8 + SUBQ SI, R8 + DECQ DI + JA emit_lit_memmove_long_emit_remainder_encodeBetterBlockAsm8Blarge_forward_sse_loop_32 + LEAQ -32(AX)(R8*1), SI + LEAQ -32(CX)(R8*1), R9 + +emit_lit_memmove_long_emit_remainder_encodeBetterBlockAsm8Blarge_big_loop_back: + MOVOU (SI), X4 + MOVOU 16(SI), X5 + MOVOA X4, (R9) + MOVOA X5, 16(R9) + ADDQ $0x20, R9 + ADDQ $0x20, SI + ADDQ $0x20, R8 + DECQ DI + JNA emit_lit_memmove_long_emit_remainder_encodeBetterBlockAsm8Blarge_big_loop_back + +emit_lit_memmove_long_emit_remainder_encodeBetterBlockAsm8Blarge_forward_sse_loop_32: + MOVOU -32(AX)(R8*1), X4 + MOVOU -16(AX)(R8*1), X5 + MOVOA X4, -32(CX)(R8*1) + MOVOA X5, -16(CX)(R8*1) + ADDQ $0x20, R8 + CMPQ BX, R8 + JAE emit_lit_memmove_long_emit_remainder_encodeBetterBlockAsm8Blarge_forward_sse_loop_32 + MOVOU X0, (CX) + MOVOU X1, 16(CX) + MOVOU X2, -32(CX)(BX*1) + MOVOU X3, -16(CX)(BX*1) + MOVQ DX, CX + +emit_literal_done_emit_remainder_encodeBetterBlockAsm8B: + MOVQ dst_base+0(FP), AX + SUBQ AX, CX + MOVQ CX, ret+56(FP) + RET + +// func encodeSnappyBlockAsm(dst []byte, src []byte, tmp *[65536]byte) int +// Requires: BMI, SSE2 +TEXT ·encodeSnappyBlockAsm(SB), $24-64 + MOVQ tmp+48(FP), AX + MOVQ dst_base+0(FP), CX + MOVQ $0x00000200, DX + MOVQ AX, BX + PXOR X0, X0 + +zero_loop_encodeSnappyBlockAsm: + MOVOU X0, (BX) + MOVOU X0, 16(BX) + MOVOU X0, 32(BX) + MOVOU X0, 48(BX) + MOVOU X0, 64(BX) + MOVOU X0, 80(BX) + MOVOU X0, 96(BX) + MOVOU X0, 112(BX) + ADDQ $0x80, BX + DECQ DX + JNZ zero_loop_encodeSnappyBlockAsm + MOVL $0x00000000, 12(SP) + MOVQ src_len+32(FP), DX + LEAQ -9(DX), BX + LEAQ -8(DX), SI + MOVL SI, 8(SP) + SHRQ $0x05, DX + SUBL DX, BX + LEAQ (CX)(BX*1), BX + MOVQ BX, (SP) + MOVL $0x00000001, DX + MOVL DX, 16(SP) + MOVQ src_base+24(FP), BX + +search_loop_encodeSnappyBlockAsm: + MOVL DX, SI + SUBL 12(SP), SI + SHRL $0x06, SI + LEAL 4(DX)(SI*1), SI + CMPL SI, 8(SP) + JAE emit_remainder_encodeSnappyBlockAsm + MOVQ (BX)(DX*1), DI + MOVL SI, 20(SP) + MOVQ $0x0000cf1bbcdcbf9b, R9 + MOVQ DI, R10 + MOVQ DI, R11 + SHRQ $0x08, R11 + SHLQ $0x10, R10 + IMULQ R9, R10 + SHRQ $0x32, R10 + SHLQ $0x10, R11 + IMULQ R9, R11 + SHRQ $0x32, R11 + MOVL (AX)(R10*4), SI + MOVL (AX)(R11*4), R8 + MOVL DX, (AX)(R10*4) + LEAL 1(DX), R10 + MOVL R10, (AX)(R11*4) + MOVQ DI, R10 + SHRQ $0x10, R10 + SHLQ $0x10, R10 + IMULQ R9, R10 + SHRQ $0x32, R10 + MOVL DX, R9 + SUBL 16(SP), R9 + MOVL 1(BX)(R9*1), R11 + MOVQ DI, R9 + SHRQ $0x08, R9 + CMPL R9, R11 + JNE no_repeat_found_encodeSnappyBlockAsm + LEAL 1(DX), DI + MOVL 12(SP), SI + MOVL DI, R8 + SUBL 16(SP), R8 + JZ repeat_extend_back_end_encodeSnappyBlockAsm + +repeat_extend_back_loop_encodeSnappyBlockAsm: + CMPL DI, SI + JBE repeat_extend_back_end_encodeSnappyBlockAsm + MOVB -1(BX)(R8*1), R9 + MOVB -1(BX)(DI*1), R10 + CMPB R9, R10 + JNE repeat_extend_back_end_encodeSnappyBlockAsm + LEAL -1(DI), DI + DECL R8 + JNZ repeat_extend_back_loop_encodeSnappyBlockAsm + +repeat_extend_back_end_encodeSnappyBlockAsm: + MOVL DI, SI + SUBL 12(SP), SI + LEAQ 5(CX)(SI*1), SI + CMPQ SI, (SP) + JB repeat_dst_size_check_encodeSnappyBlockAsm + MOVQ $0x00000000, ret+56(FP) + RET + +repeat_dst_size_check_encodeSnappyBlockAsm: + MOVL 12(SP), SI + CMPL SI, DI + JEQ emit_literal_done_repeat_emit_encodeSnappyBlockAsm + MOVL DI, R8 + MOVL DI, 12(SP) + LEAQ (BX)(SI*1), R9 + SUBL SI, R8 + LEAL -1(R8), SI + CMPL SI, $0x3c + JB one_byte_repeat_emit_encodeSnappyBlockAsm + CMPL SI, $0x00000100 + JB two_bytes_repeat_emit_encodeSnappyBlockAsm + CMPL SI, $0x00010000 + JB three_bytes_repeat_emit_encodeSnappyBlockAsm + CMPL SI, $0x01000000 + JB four_bytes_repeat_emit_encodeSnappyBlockAsm + MOVB $0xfc, (CX) + MOVL SI, 1(CX) + ADDQ $0x05, CX + JMP memmove_long_repeat_emit_encodeSnappyBlockAsm + +four_bytes_repeat_emit_encodeSnappyBlockAsm: + MOVL SI, R10 + SHRL $0x10, R10 + MOVB $0xf8, (CX) + MOVW SI, 1(CX) + MOVB R10, 3(CX) + ADDQ $0x04, CX + JMP memmove_long_repeat_emit_encodeSnappyBlockAsm + +three_bytes_repeat_emit_encodeSnappyBlockAsm: + MOVB $0xf4, (CX) + MOVW SI, 1(CX) + ADDQ $0x03, CX + JMP memmove_long_repeat_emit_encodeSnappyBlockAsm + +two_bytes_repeat_emit_encodeSnappyBlockAsm: + MOVB $0xf0, (CX) + MOVB SI, 1(CX) + ADDQ $0x02, CX + CMPL SI, $0x40 + JB memmove_repeat_emit_encodeSnappyBlockAsm + JMP memmove_long_repeat_emit_encodeSnappyBlockAsm + +one_byte_repeat_emit_encodeSnappyBlockAsm: + SHLB $0x02, SI + MOVB SI, (CX) + ADDQ $0x01, CX + +memmove_repeat_emit_encodeSnappyBlockAsm: + LEAQ (CX)(R8*1), SI + + // genMemMoveShort + CMPQ R8, $0x08 + JBE emit_lit_memmove_repeat_emit_encodeSnappyBlockAsm_memmove_move_8 + CMPQ R8, $0x10 + JBE emit_lit_memmove_repeat_emit_encodeSnappyBlockAsm_memmove_move_8through16 + CMPQ R8, $0x20 + JBE emit_lit_memmove_repeat_emit_encodeSnappyBlockAsm_memmove_move_17through32 + JMP emit_lit_memmove_repeat_emit_encodeSnappyBlockAsm_memmove_move_33through64 + +emit_lit_memmove_repeat_emit_encodeSnappyBlockAsm_memmove_move_8: + MOVQ (R9), R10 + MOVQ R10, (CX) + JMP memmove_end_copy_repeat_emit_encodeSnappyBlockAsm + +emit_lit_memmove_repeat_emit_encodeSnappyBlockAsm_memmove_move_8through16: + MOVQ (R9), R10 + MOVQ -8(R9)(R8*1), R9 + MOVQ R10, (CX) + MOVQ R9, -8(CX)(R8*1) + JMP memmove_end_copy_repeat_emit_encodeSnappyBlockAsm + +emit_lit_memmove_repeat_emit_encodeSnappyBlockAsm_memmove_move_17through32: + MOVOU (R9), X0 + MOVOU -16(R9)(R8*1), X1 + MOVOU X0, (CX) + MOVOU X1, -16(CX)(R8*1) + JMP memmove_end_copy_repeat_emit_encodeSnappyBlockAsm + +emit_lit_memmove_repeat_emit_encodeSnappyBlockAsm_memmove_move_33through64: + MOVOU (R9), X0 + MOVOU 16(R9), X1 + MOVOU -32(R9)(R8*1), X2 + MOVOU -16(R9)(R8*1), X3 + MOVOU X0, (CX) + MOVOU X1, 16(CX) + MOVOU X2, -32(CX)(R8*1) + MOVOU X3, -16(CX)(R8*1) + +memmove_end_copy_repeat_emit_encodeSnappyBlockAsm: + MOVQ SI, CX + JMP emit_literal_done_repeat_emit_encodeSnappyBlockAsm + +memmove_long_repeat_emit_encodeSnappyBlockAsm: + LEAQ (CX)(R8*1), SI + + // genMemMoveLong + MOVOU (R9), X0 + MOVOU 16(R9), X1 + MOVOU -32(R9)(R8*1), X2 + MOVOU -16(R9)(R8*1), X3 + MOVQ R8, R11 + SHRQ $0x05, R11 + MOVQ CX, R10 + ANDL $0x0000001f, R10 + MOVQ $0x00000040, R12 + SUBQ R10, R12 + DECQ R11 + JA emit_lit_memmove_long_repeat_emit_encodeSnappyBlockAsmlarge_forward_sse_loop_32 + LEAQ -32(R9)(R12*1), R10 + LEAQ -32(CX)(R12*1), R13 + +emit_lit_memmove_long_repeat_emit_encodeSnappyBlockAsmlarge_big_loop_back: + MOVOU (R10), X4 + MOVOU 16(R10), X5 + MOVOA X4, (R13) + MOVOA X5, 16(R13) + ADDQ $0x20, R13 + ADDQ $0x20, R10 + ADDQ $0x20, R12 + DECQ R11 + JNA emit_lit_memmove_long_repeat_emit_encodeSnappyBlockAsmlarge_big_loop_back + +emit_lit_memmove_long_repeat_emit_encodeSnappyBlockAsmlarge_forward_sse_loop_32: + MOVOU -32(R9)(R12*1), X4 + MOVOU -16(R9)(R12*1), X5 + MOVOA X4, -32(CX)(R12*1) + MOVOA X5, -16(CX)(R12*1) + ADDQ $0x20, R12 + CMPQ R8, R12 + JAE emit_lit_memmove_long_repeat_emit_encodeSnappyBlockAsmlarge_forward_sse_loop_32 + MOVOU X0, (CX) + MOVOU X1, 16(CX) + MOVOU X2, -32(CX)(R8*1) + MOVOU X3, -16(CX)(R8*1) + MOVQ SI, CX + +emit_literal_done_repeat_emit_encodeSnappyBlockAsm: + ADDL $0x05, DX + MOVL DX, SI + SUBL 16(SP), SI + MOVQ src_len+32(FP), R8 + SUBL DX, R8 + LEAQ (BX)(DX*1), R9 + LEAQ (BX)(SI*1), SI + + // matchLen + XORL R11, R11 + +matchlen_loopback_16_repeat_extend_encodeSnappyBlockAsm: + CMPL R8, $0x10 + JB matchlen_match8_repeat_extend_encodeSnappyBlockAsm + MOVQ (R9)(R11*1), R10 + MOVQ 8(R9)(R11*1), R12 + XORQ (SI)(R11*1), R10 + JNZ matchlen_bsf_8_repeat_extend_encodeSnappyBlockAsm + XORQ 8(SI)(R11*1), R12 + JNZ matchlen_bsf_16repeat_extend_encodeSnappyBlockAsm + LEAL -16(R8), R8 + LEAL 16(R11), R11 + JMP matchlen_loopback_16_repeat_extend_encodeSnappyBlockAsm + +matchlen_bsf_16repeat_extend_encodeSnappyBlockAsm: +#ifdef GOAMD64_v3 + TZCNTQ R12, R12 + +#else + BSFQ R12, R12 + +#endif + SARQ $0x03, R12 + LEAL 8(R11)(R12*1), R11 + JMP repeat_extend_forward_end_encodeSnappyBlockAsm + +matchlen_match8_repeat_extend_encodeSnappyBlockAsm: + CMPL R8, $0x08 + JB matchlen_match4_repeat_extend_encodeSnappyBlockAsm + MOVQ (R9)(R11*1), R10 + XORQ (SI)(R11*1), R10 + JNZ matchlen_bsf_8_repeat_extend_encodeSnappyBlockAsm + LEAL -8(R8), R8 + LEAL 8(R11), R11 + JMP matchlen_match4_repeat_extend_encodeSnappyBlockAsm + +matchlen_bsf_8_repeat_extend_encodeSnappyBlockAsm: +#ifdef GOAMD64_v3 + TZCNTQ R10, R10 + +#else + BSFQ R10, R10 + +#endif + SARQ $0x03, R10 + LEAL (R11)(R10*1), R11 + JMP repeat_extend_forward_end_encodeSnappyBlockAsm + +matchlen_match4_repeat_extend_encodeSnappyBlockAsm: + CMPL R8, $0x04 + JB matchlen_match2_repeat_extend_encodeSnappyBlockAsm + MOVL (R9)(R11*1), R10 + CMPL (SI)(R11*1), R10 + JNE matchlen_match2_repeat_extend_encodeSnappyBlockAsm + LEAL -4(R8), R8 + LEAL 4(R11), R11 + +matchlen_match2_repeat_extend_encodeSnappyBlockAsm: + CMPL R8, $0x01 + JE matchlen_match1_repeat_extend_encodeSnappyBlockAsm + JB repeat_extend_forward_end_encodeSnappyBlockAsm + MOVW (R9)(R11*1), R10 + CMPW (SI)(R11*1), R10 + JNE matchlen_match1_repeat_extend_encodeSnappyBlockAsm + LEAL 2(R11), R11 + SUBL $0x02, R8 + JZ repeat_extend_forward_end_encodeSnappyBlockAsm + +matchlen_match1_repeat_extend_encodeSnappyBlockAsm: + MOVB (R9)(R11*1), R10 + CMPB (SI)(R11*1), R10 + JNE repeat_extend_forward_end_encodeSnappyBlockAsm + LEAL 1(R11), R11 + +repeat_extend_forward_end_encodeSnappyBlockAsm: + ADDL R11, DX + MOVL DX, SI + SUBL DI, SI + MOVL 16(SP), DI + + // emitCopy + CMPL DI, $0x00010000 + JB two_byte_offset_repeat_as_copy_encodeSnappyBlockAsm + +four_bytes_loop_back_repeat_as_copy_encodeSnappyBlockAsm: + CMPL SI, $0x40 + JBE four_bytes_remain_repeat_as_copy_encodeSnappyBlockAsm + MOVB $0xff, (CX) + MOVL DI, 1(CX) + LEAL -64(SI), SI + ADDQ $0x05, CX + CMPL SI, $0x04 + JB four_bytes_remain_repeat_as_copy_encodeSnappyBlockAsm + JMP four_bytes_loop_back_repeat_as_copy_encodeSnappyBlockAsm + +four_bytes_remain_repeat_as_copy_encodeSnappyBlockAsm: + TESTL SI, SI + JZ repeat_end_emit_encodeSnappyBlockAsm + XORL R8, R8 + LEAL -1(R8)(SI*4), SI + MOVB SI, (CX) + MOVL DI, 1(CX) + ADDQ $0x05, CX + JMP repeat_end_emit_encodeSnappyBlockAsm + +two_byte_offset_repeat_as_copy_encodeSnappyBlockAsm: + CMPL SI, $0x40 + JBE two_byte_offset_short_repeat_as_copy_encodeSnappyBlockAsm + MOVB $0xee, (CX) + MOVW DI, 1(CX) + LEAL -60(SI), SI + ADDQ $0x03, CX + JMP two_byte_offset_repeat_as_copy_encodeSnappyBlockAsm + +two_byte_offset_short_repeat_as_copy_encodeSnappyBlockAsm: + MOVL SI, R8 + SHLL $0x02, R8 + CMPL SI, $0x0c + JAE emit_copy_three_repeat_as_copy_encodeSnappyBlockAsm + CMPL DI, $0x00000800 + JAE emit_copy_three_repeat_as_copy_encodeSnappyBlockAsm + LEAL -15(R8), R8 + MOVB DI, 1(CX) + SHRL $0x08, DI + SHLL $0x05, DI + ORL DI, R8 + MOVB R8, (CX) + ADDQ $0x02, CX + JMP repeat_end_emit_encodeSnappyBlockAsm + +emit_copy_three_repeat_as_copy_encodeSnappyBlockAsm: + LEAL -2(R8), R8 + MOVB R8, (CX) + MOVW DI, 1(CX) + ADDQ $0x03, CX + +repeat_end_emit_encodeSnappyBlockAsm: + MOVL DX, 12(SP) + JMP search_loop_encodeSnappyBlockAsm + +no_repeat_found_encodeSnappyBlockAsm: + CMPL (BX)(SI*1), DI + JEQ candidate_match_encodeSnappyBlockAsm + SHRQ $0x08, DI + MOVL (AX)(R10*4), SI + LEAL 2(DX), R9 + CMPL (BX)(R8*1), DI + JEQ candidate2_match_encodeSnappyBlockAsm + MOVL R9, (AX)(R10*4) + SHRQ $0x08, DI + CMPL (BX)(SI*1), DI + JEQ candidate3_match_encodeSnappyBlockAsm + MOVL 20(SP), DX + JMP search_loop_encodeSnappyBlockAsm + +candidate3_match_encodeSnappyBlockAsm: + ADDL $0x02, DX + JMP candidate_match_encodeSnappyBlockAsm + +candidate2_match_encodeSnappyBlockAsm: + MOVL R9, (AX)(R10*4) + INCL DX + MOVL R8, SI + +candidate_match_encodeSnappyBlockAsm: + MOVL 12(SP), DI + TESTL SI, SI + JZ match_extend_back_end_encodeSnappyBlockAsm + +match_extend_back_loop_encodeSnappyBlockAsm: + CMPL DX, DI + JBE match_extend_back_end_encodeSnappyBlockAsm + MOVB -1(BX)(SI*1), R8 + MOVB -1(BX)(DX*1), R9 + CMPB R8, R9 + JNE match_extend_back_end_encodeSnappyBlockAsm + LEAL -1(DX), DX + DECL SI + JZ match_extend_back_end_encodeSnappyBlockAsm + JMP match_extend_back_loop_encodeSnappyBlockAsm + +match_extend_back_end_encodeSnappyBlockAsm: + MOVL DX, DI + SUBL 12(SP), DI + LEAQ 5(CX)(DI*1), DI + CMPQ DI, (SP) + JB match_dst_size_check_encodeSnappyBlockAsm + MOVQ $0x00000000, ret+56(FP) + RET + +match_dst_size_check_encodeSnappyBlockAsm: + MOVL DX, DI + MOVL 12(SP), R8 + CMPL R8, DI + JEQ emit_literal_done_match_emit_encodeSnappyBlockAsm + MOVL DI, R9 + MOVL DI, 12(SP) + LEAQ (BX)(R8*1), DI + SUBL R8, R9 + LEAL -1(R9), R8 + CMPL R8, $0x3c + JB one_byte_match_emit_encodeSnappyBlockAsm + CMPL R8, $0x00000100 + JB two_bytes_match_emit_encodeSnappyBlockAsm + CMPL R8, $0x00010000 + JB three_bytes_match_emit_encodeSnappyBlockAsm + CMPL R8, $0x01000000 + JB four_bytes_match_emit_encodeSnappyBlockAsm + MOVB $0xfc, (CX) + MOVL R8, 1(CX) + ADDQ $0x05, CX + JMP memmove_long_match_emit_encodeSnappyBlockAsm + +four_bytes_match_emit_encodeSnappyBlockAsm: + MOVL R8, R10 + SHRL $0x10, R10 + MOVB $0xf8, (CX) + MOVW R8, 1(CX) + MOVB R10, 3(CX) + ADDQ $0x04, CX + JMP memmove_long_match_emit_encodeSnappyBlockAsm + +three_bytes_match_emit_encodeSnappyBlockAsm: + MOVB $0xf4, (CX) + MOVW R8, 1(CX) + ADDQ $0x03, CX + JMP memmove_long_match_emit_encodeSnappyBlockAsm + +two_bytes_match_emit_encodeSnappyBlockAsm: + MOVB $0xf0, (CX) + MOVB R8, 1(CX) + ADDQ $0x02, CX + CMPL R8, $0x40 + JB memmove_match_emit_encodeSnappyBlockAsm + JMP memmove_long_match_emit_encodeSnappyBlockAsm + +one_byte_match_emit_encodeSnappyBlockAsm: + SHLB $0x02, R8 + MOVB R8, (CX) + ADDQ $0x01, CX + +memmove_match_emit_encodeSnappyBlockAsm: + LEAQ (CX)(R9*1), R8 + + // genMemMoveShort + CMPQ R9, $0x08 + JBE emit_lit_memmove_match_emit_encodeSnappyBlockAsm_memmove_move_8 + CMPQ R9, $0x10 + JBE emit_lit_memmove_match_emit_encodeSnappyBlockAsm_memmove_move_8through16 + CMPQ R9, $0x20 + JBE emit_lit_memmove_match_emit_encodeSnappyBlockAsm_memmove_move_17through32 + JMP emit_lit_memmove_match_emit_encodeSnappyBlockAsm_memmove_move_33through64 + +emit_lit_memmove_match_emit_encodeSnappyBlockAsm_memmove_move_8: + MOVQ (DI), R10 + MOVQ R10, (CX) + JMP memmove_end_copy_match_emit_encodeSnappyBlockAsm + +emit_lit_memmove_match_emit_encodeSnappyBlockAsm_memmove_move_8through16: + MOVQ (DI), R10 + MOVQ -8(DI)(R9*1), DI + MOVQ R10, (CX) + MOVQ DI, -8(CX)(R9*1) + JMP memmove_end_copy_match_emit_encodeSnappyBlockAsm + +emit_lit_memmove_match_emit_encodeSnappyBlockAsm_memmove_move_17through32: + MOVOU (DI), X0 + MOVOU -16(DI)(R9*1), X1 + MOVOU X0, (CX) + MOVOU X1, -16(CX)(R9*1) + JMP memmove_end_copy_match_emit_encodeSnappyBlockAsm + +emit_lit_memmove_match_emit_encodeSnappyBlockAsm_memmove_move_33through64: + MOVOU (DI), X0 + MOVOU 16(DI), X1 + MOVOU -32(DI)(R9*1), X2 + MOVOU -16(DI)(R9*1), X3 + MOVOU X0, (CX) + MOVOU X1, 16(CX) + MOVOU X2, -32(CX)(R9*1) + MOVOU X3, -16(CX)(R9*1) + +memmove_end_copy_match_emit_encodeSnappyBlockAsm: + MOVQ R8, CX + JMP emit_literal_done_match_emit_encodeSnappyBlockAsm + +memmove_long_match_emit_encodeSnappyBlockAsm: + LEAQ (CX)(R9*1), R8 + + // genMemMoveLong + MOVOU (DI), X0 + MOVOU 16(DI), X1 + MOVOU -32(DI)(R9*1), X2 + MOVOU -16(DI)(R9*1), X3 + MOVQ R9, R11 + SHRQ $0x05, R11 + MOVQ CX, R10 + ANDL $0x0000001f, R10 + MOVQ $0x00000040, R12 + SUBQ R10, R12 + DECQ R11 + JA emit_lit_memmove_long_match_emit_encodeSnappyBlockAsmlarge_forward_sse_loop_32 + LEAQ -32(DI)(R12*1), R10 + LEAQ -32(CX)(R12*1), R13 + +emit_lit_memmove_long_match_emit_encodeSnappyBlockAsmlarge_big_loop_back: + MOVOU (R10), X4 + MOVOU 16(R10), X5 + MOVOA X4, (R13) + MOVOA X5, 16(R13) + ADDQ $0x20, R13 + ADDQ $0x20, R10 + ADDQ $0x20, R12 + DECQ R11 + JNA emit_lit_memmove_long_match_emit_encodeSnappyBlockAsmlarge_big_loop_back + +emit_lit_memmove_long_match_emit_encodeSnappyBlockAsmlarge_forward_sse_loop_32: + MOVOU -32(DI)(R12*1), X4 + MOVOU -16(DI)(R12*1), X5 + MOVOA X4, -32(CX)(R12*1) + MOVOA X5, -16(CX)(R12*1) + ADDQ $0x20, R12 + CMPQ R9, R12 + JAE emit_lit_memmove_long_match_emit_encodeSnappyBlockAsmlarge_forward_sse_loop_32 + MOVOU X0, (CX) + MOVOU X1, 16(CX) + MOVOU X2, -32(CX)(R9*1) + MOVOU X3, -16(CX)(R9*1) + MOVQ R8, CX + +emit_literal_done_match_emit_encodeSnappyBlockAsm: +match_nolit_loop_encodeSnappyBlockAsm: + MOVL DX, DI + SUBL SI, DI + MOVL DI, 16(SP) + ADDL $0x04, DX + ADDL $0x04, SI + MOVQ src_len+32(FP), DI + SUBL DX, DI + LEAQ (BX)(DX*1), R8 + LEAQ (BX)(SI*1), SI + + // matchLen + XORL R10, R10 + +matchlen_loopback_16_match_nolit_encodeSnappyBlockAsm: + CMPL DI, $0x10 + JB matchlen_match8_match_nolit_encodeSnappyBlockAsm + MOVQ (R8)(R10*1), R9 + MOVQ 8(R8)(R10*1), R11 + XORQ (SI)(R10*1), R9 + JNZ matchlen_bsf_8_match_nolit_encodeSnappyBlockAsm + XORQ 8(SI)(R10*1), R11 + JNZ matchlen_bsf_16match_nolit_encodeSnappyBlockAsm + LEAL -16(DI), DI + LEAL 16(R10), R10 + JMP matchlen_loopback_16_match_nolit_encodeSnappyBlockAsm + +matchlen_bsf_16match_nolit_encodeSnappyBlockAsm: +#ifdef GOAMD64_v3 + TZCNTQ R11, R11 + +#else + BSFQ R11, R11 + +#endif + SARQ $0x03, R11 + LEAL 8(R10)(R11*1), R10 + JMP match_nolit_end_encodeSnappyBlockAsm + +matchlen_match8_match_nolit_encodeSnappyBlockAsm: + CMPL DI, $0x08 + JB matchlen_match4_match_nolit_encodeSnappyBlockAsm + MOVQ (R8)(R10*1), R9 + XORQ (SI)(R10*1), R9 + JNZ matchlen_bsf_8_match_nolit_encodeSnappyBlockAsm + LEAL -8(DI), DI + LEAL 8(R10), R10 + JMP matchlen_match4_match_nolit_encodeSnappyBlockAsm + +matchlen_bsf_8_match_nolit_encodeSnappyBlockAsm: +#ifdef GOAMD64_v3 + TZCNTQ R9, R9 + +#else + BSFQ R9, R9 + +#endif + SARQ $0x03, R9 + LEAL (R10)(R9*1), R10 + JMP match_nolit_end_encodeSnappyBlockAsm + +matchlen_match4_match_nolit_encodeSnappyBlockAsm: + CMPL DI, $0x04 + JB matchlen_match2_match_nolit_encodeSnappyBlockAsm + MOVL (R8)(R10*1), R9 + CMPL (SI)(R10*1), R9 + JNE matchlen_match2_match_nolit_encodeSnappyBlockAsm + LEAL -4(DI), DI + LEAL 4(R10), R10 + +matchlen_match2_match_nolit_encodeSnappyBlockAsm: + CMPL DI, $0x01 + JE matchlen_match1_match_nolit_encodeSnappyBlockAsm + JB match_nolit_end_encodeSnappyBlockAsm + MOVW (R8)(R10*1), R9 + CMPW (SI)(R10*1), R9 + JNE matchlen_match1_match_nolit_encodeSnappyBlockAsm + LEAL 2(R10), R10 + SUBL $0x02, DI + JZ match_nolit_end_encodeSnappyBlockAsm + +matchlen_match1_match_nolit_encodeSnappyBlockAsm: + MOVB (R8)(R10*1), R9 + CMPB (SI)(R10*1), R9 + JNE match_nolit_end_encodeSnappyBlockAsm + LEAL 1(R10), R10 + +match_nolit_end_encodeSnappyBlockAsm: + ADDL R10, DX + MOVL 16(SP), SI + ADDL $0x04, R10 + MOVL DX, 12(SP) + + // emitCopy + CMPL SI, $0x00010000 + JB two_byte_offset_match_nolit_encodeSnappyBlockAsm + +four_bytes_loop_back_match_nolit_encodeSnappyBlockAsm: + CMPL R10, $0x40 + JBE four_bytes_remain_match_nolit_encodeSnappyBlockAsm + MOVB $0xff, (CX) + MOVL SI, 1(CX) + LEAL -64(R10), R10 + ADDQ $0x05, CX + CMPL R10, $0x04 + JB four_bytes_remain_match_nolit_encodeSnappyBlockAsm + JMP four_bytes_loop_back_match_nolit_encodeSnappyBlockAsm + +four_bytes_remain_match_nolit_encodeSnappyBlockAsm: + TESTL R10, R10 + JZ match_nolit_emitcopy_end_encodeSnappyBlockAsm + XORL DI, DI + LEAL -1(DI)(R10*4), R10 + MOVB R10, (CX) + MOVL SI, 1(CX) + ADDQ $0x05, CX + JMP match_nolit_emitcopy_end_encodeSnappyBlockAsm + +two_byte_offset_match_nolit_encodeSnappyBlockAsm: + CMPL R10, $0x40 + JBE two_byte_offset_short_match_nolit_encodeSnappyBlockAsm + MOVB $0xee, (CX) + MOVW SI, 1(CX) + LEAL -60(R10), R10 + ADDQ $0x03, CX + JMP two_byte_offset_match_nolit_encodeSnappyBlockAsm + +two_byte_offset_short_match_nolit_encodeSnappyBlockAsm: + MOVL R10, DI + SHLL $0x02, DI + CMPL R10, $0x0c + JAE emit_copy_three_match_nolit_encodeSnappyBlockAsm + CMPL SI, $0x00000800 + JAE emit_copy_three_match_nolit_encodeSnappyBlockAsm + LEAL -15(DI), DI + MOVB SI, 1(CX) + SHRL $0x08, SI + SHLL $0x05, SI + ORL SI, DI + MOVB DI, (CX) + ADDQ $0x02, CX + JMP match_nolit_emitcopy_end_encodeSnappyBlockAsm + +emit_copy_three_match_nolit_encodeSnappyBlockAsm: + LEAL -2(DI), DI + MOVB DI, (CX) + MOVW SI, 1(CX) + ADDQ $0x03, CX + +match_nolit_emitcopy_end_encodeSnappyBlockAsm: + CMPL DX, 8(SP) + JAE emit_remainder_encodeSnappyBlockAsm + MOVQ -2(BX)(DX*1), DI + CMPQ CX, (SP) + JB match_nolit_dst_ok_encodeSnappyBlockAsm + MOVQ $0x00000000, ret+56(FP) + RET + +match_nolit_dst_ok_encodeSnappyBlockAsm: + MOVQ $0x0000cf1bbcdcbf9b, R9 + MOVQ DI, R8 + SHRQ $0x10, DI + MOVQ DI, SI + SHLQ $0x10, R8 + IMULQ R9, R8 + SHRQ $0x32, R8 + SHLQ $0x10, SI + IMULQ R9, SI + SHRQ $0x32, SI + LEAL -2(DX), R9 + LEAQ (AX)(SI*4), R10 + MOVL (R10), SI + MOVL R9, (AX)(R8*4) + MOVL DX, (R10) + CMPL (BX)(SI*1), DI + JEQ match_nolit_loop_encodeSnappyBlockAsm + INCL DX + JMP search_loop_encodeSnappyBlockAsm + +emit_remainder_encodeSnappyBlockAsm: + MOVQ src_len+32(FP), AX + SUBL 12(SP), AX + LEAQ 5(CX)(AX*1), AX + CMPQ AX, (SP) + JB emit_remainder_ok_encodeSnappyBlockAsm + MOVQ $0x00000000, ret+56(FP) + RET + +emit_remainder_ok_encodeSnappyBlockAsm: + MOVQ src_len+32(FP), AX + MOVL 12(SP), DX + CMPL DX, AX + JEQ emit_literal_done_emit_remainder_encodeSnappyBlockAsm + MOVL AX, SI + MOVL AX, 12(SP) + LEAQ (BX)(DX*1), AX + SUBL DX, SI + LEAL -1(SI), DX + CMPL DX, $0x3c + JB one_byte_emit_remainder_encodeSnappyBlockAsm + CMPL DX, $0x00000100 + JB two_bytes_emit_remainder_encodeSnappyBlockAsm + CMPL DX, $0x00010000 + JB three_bytes_emit_remainder_encodeSnappyBlockAsm + CMPL DX, $0x01000000 + JB four_bytes_emit_remainder_encodeSnappyBlockAsm + MOVB $0xfc, (CX) + MOVL DX, 1(CX) + ADDQ $0x05, CX + JMP memmove_long_emit_remainder_encodeSnappyBlockAsm + +four_bytes_emit_remainder_encodeSnappyBlockAsm: + MOVL DX, BX + SHRL $0x10, BX + MOVB $0xf8, (CX) + MOVW DX, 1(CX) + MOVB BL, 3(CX) + ADDQ $0x04, CX + JMP memmove_long_emit_remainder_encodeSnappyBlockAsm + +three_bytes_emit_remainder_encodeSnappyBlockAsm: + MOVB $0xf4, (CX) + MOVW DX, 1(CX) + ADDQ $0x03, CX + JMP memmove_long_emit_remainder_encodeSnappyBlockAsm + +two_bytes_emit_remainder_encodeSnappyBlockAsm: + MOVB $0xf0, (CX) + MOVB DL, 1(CX) + ADDQ $0x02, CX + CMPL DX, $0x40 + JB memmove_emit_remainder_encodeSnappyBlockAsm + JMP memmove_long_emit_remainder_encodeSnappyBlockAsm + +one_byte_emit_remainder_encodeSnappyBlockAsm: + SHLB $0x02, DL + MOVB DL, (CX) + ADDQ $0x01, CX + +memmove_emit_remainder_encodeSnappyBlockAsm: + LEAQ (CX)(SI*1), DX + MOVL SI, BX + + // genMemMoveShort + CMPQ BX, $0x03 + JB emit_lit_memmove_emit_remainder_encodeSnappyBlockAsm_memmove_move_1or2 + JE emit_lit_memmove_emit_remainder_encodeSnappyBlockAsm_memmove_move_3 + CMPQ BX, $0x08 + JB emit_lit_memmove_emit_remainder_encodeSnappyBlockAsm_memmove_move_4through7 + CMPQ BX, $0x10 + JBE emit_lit_memmove_emit_remainder_encodeSnappyBlockAsm_memmove_move_8through16 + CMPQ BX, $0x20 + JBE emit_lit_memmove_emit_remainder_encodeSnappyBlockAsm_memmove_move_17through32 + JMP emit_lit_memmove_emit_remainder_encodeSnappyBlockAsm_memmove_move_33through64 + +emit_lit_memmove_emit_remainder_encodeSnappyBlockAsm_memmove_move_1or2: + MOVB (AX), SI + MOVB -1(AX)(BX*1), AL + MOVB SI, (CX) + MOVB AL, -1(CX)(BX*1) + JMP memmove_end_copy_emit_remainder_encodeSnappyBlockAsm + +emit_lit_memmove_emit_remainder_encodeSnappyBlockAsm_memmove_move_3: + MOVW (AX), SI + MOVB 2(AX), AL + MOVW SI, (CX) + MOVB AL, 2(CX) + JMP memmove_end_copy_emit_remainder_encodeSnappyBlockAsm + +emit_lit_memmove_emit_remainder_encodeSnappyBlockAsm_memmove_move_4through7: + MOVL (AX), SI + MOVL -4(AX)(BX*1), AX + MOVL SI, (CX) + MOVL AX, -4(CX)(BX*1) + JMP memmove_end_copy_emit_remainder_encodeSnappyBlockAsm + +emit_lit_memmove_emit_remainder_encodeSnappyBlockAsm_memmove_move_8through16: + MOVQ (AX), SI + MOVQ -8(AX)(BX*1), AX + MOVQ SI, (CX) + MOVQ AX, -8(CX)(BX*1) + JMP memmove_end_copy_emit_remainder_encodeSnappyBlockAsm + +emit_lit_memmove_emit_remainder_encodeSnappyBlockAsm_memmove_move_17through32: + MOVOU (AX), X0 + MOVOU -16(AX)(BX*1), X1 + MOVOU X0, (CX) + MOVOU X1, -16(CX)(BX*1) + JMP memmove_end_copy_emit_remainder_encodeSnappyBlockAsm + +emit_lit_memmove_emit_remainder_encodeSnappyBlockAsm_memmove_move_33through64: + MOVOU (AX), X0 + MOVOU 16(AX), X1 + MOVOU -32(AX)(BX*1), X2 + MOVOU -16(AX)(BX*1), X3 + MOVOU X0, (CX) + MOVOU X1, 16(CX) + MOVOU X2, -32(CX)(BX*1) + MOVOU X3, -16(CX)(BX*1) + +memmove_end_copy_emit_remainder_encodeSnappyBlockAsm: + MOVQ DX, CX + JMP emit_literal_done_emit_remainder_encodeSnappyBlockAsm + +memmove_long_emit_remainder_encodeSnappyBlockAsm: + LEAQ (CX)(SI*1), DX + MOVL SI, BX + + // genMemMoveLong + MOVOU (AX), X0 + MOVOU 16(AX), X1 + MOVOU -32(AX)(BX*1), X2 + MOVOU -16(AX)(BX*1), X3 + MOVQ BX, DI + SHRQ $0x05, DI + MOVQ CX, SI + ANDL $0x0000001f, SI + MOVQ $0x00000040, R8 + SUBQ SI, R8 + DECQ DI + JA emit_lit_memmove_long_emit_remainder_encodeSnappyBlockAsmlarge_forward_sse_loop_32 + LEAQ -32(AX)(R8*1), SI + LEAQ -32(CX)(R8*1), R9 + +emit_lit_memmove_long_emit_remainder_encodeSnappyBlockAsmlarge_big_loop_back: + MOVOU (SI), X4 + MOVOU 16(SI), X5 + MOVOA X4, (R9) + MOVOA X5, 16(R9) + ADDQ $0x20, R9 + ADDQ $0x20, SI + ADDQ $0x20, R8 + DECQ DI + JNA emit_lit_memmove_long_emit_remainder_encodeSnappyBlockAsmlarge_big_loop_back + +emit_lit_memmove_long_emit_remainder_encodeSnappyBlockAsmlarge_forward_sse_loop_32: + MOVOU -32(AX)(R8*1), X4 + MOVOU -16(AX)(R8*1), X5 + MOVOA X4, -32(CX)(R8*1) + MOVOA X5, -16(CX)(R8*1) + ADDQ $0x20, R8 + CMPQ BX, R8 + JAE emit_lit_memmove_long_emit_remainder_encodeSnappyBlockAsmlarge_forward_sse_loop_32 + MOVOU X0, (CX) + MOVOU X1, 16(CX) + MOVOU X2, -32(CX)(BX*1) + MOVOU X3, -16(CX)(BX*1) + MOVQ DX, CX + +emit_literal_done_emit_remainder_encodeSnappyBlockAsm: + MOVQ dst_base+0(FP), AX + SUBQ AX, CX + MOVQ CX, ret+56(FP) + RET + +// func encodeSnappyBlockAsm64K(dst []byte, src []byte, tmp *[65536]byte) int +// Requires: BMI, SSE2 +TEXT ·encodeSnappyBlockAsm64K(SB), $24-64 + MOVQ tmp+48(FP), AX + MOVQ dst_base+0(FP), CX + MOVQ $0x00000200, DX + MOVQ AX, BX + PXOR X0, X0 + +zero_loop_encodeSnappyBlockAsm64K: + MOVOU X0, (BX) + MOVOU X0, 16(BX) + MOVOU X0, 32(BX) + MOVOU X0, 48(BX) + MOVOU X0, 64(BX) + MOVOU X0, 80(BX) + MOVOU X0, 96(BX) + MOVOU X0, 112(BX) + ADDQ $0x80, BX + DECQ DX + JNZ zero_loop_encodeSnappyBlockAsm64K + MOVL $0x00000000, 12(SP) + MOVQ src_len+32(FP), DX + LEAQ -9(DX), BX + LEAQ -8(DX), SI + MOVL SI, 8(SP) + SHRQ $0x05, DX + SUBL DX, BX + LEAQ (CX)(BX*1), BX + MOVQ BX, (SP) + MOVL $0x00000001, DX + MOVL DX, 16(SP) + MOVQ src_base+24(FP), BX + +search_loop_encodeSnappyBlockAsm64K: + MOVL DX, SI + SUBL 12(SP), SI + SHRL $0x06, SI + LEAL 4(DX)(SI*1), SI + CMPL SI, 8(SP) + JAE emit_remainder_encodeSnappyBlockAsm64K + MOVQ (BX)(DX*1), DI + MOVL SI, 20(SP) + MOVQ $0x0000cf1bbcdcbf9b, R9 + MOVQ DI, R10 + MOVQ DI, R11 + SHRQ $0x08, R11 + SHLQ $0x10, R10 + IMULQ R9, R10 + SHRQ $0x32, R10 + SHLQ $0x10, R11 + IMULQ R9, R11 + SHRQ $0x32, R11 + MOVL (AX)(R10*4), SI + MOVL (AX)(R11*4), R8 + MOVL DX, (AX)(R10*4) + LEAL 1(DX), R10 + MOVL R10, (AX)(R11*4) + MOVQ DI, R10 + SHRQ $0x10, R10 + SHLQ $0x10, R10 + IMULQ R9, R10 + SHRQ $0x32, R10 + MOVL DX, R9 + SUBL 16(SP), R9 + MOVL 1(BX)(R9*1), R11 + MOVQ DI, R9 + SHRQ $0x08, R9 + CMPL R9, R11 + JNE no_repeat_found_encodeSnappyBlockAsm64K + LEAL 1(DX), DI + MOVL 12(SP), SI + MOVL DI, R8 + SUBL 16(SP), R8 + JZ repeat_extend_back_end_encodeSnappyBlockAsm64K + +repeat_extend_back_loop_encodeSnappyBlockAsm64K: + CMPL DI, SI + JBE repeat_extend_back_end_encodeSnappyBlockAsm64K + MOVB -1(BX)(R8*1), R9 + MOVB -1(BX)(DI*1), R10 + CMPB R9, R10 + JNE repeat_extend_back_end_encodeSnappyBlockAsm64K + LEAL -1(DI), DI + DECL R8 + JNZ repeat_extend_back_loop_encodeSnappyBlockAsm64K + +repeat_extend_back_end_encodeSnappyBlockAsm64K: + MOVL DI, SI + SUBL 12(SP), SI + LEAQ 3(CX)(SI*1), SI + CMPQ SI, (SP) + JB repeat_dst_size_check_encodeSnappyBlockAsm64K + MOVQ $0x00000000, ret+56(FP) + RET + +repeat_dst_size_check_encodeSnappyBlockAsm64K: + MOVL 12(SP), SI + CMPL SI, DI + JEQ emit_literal_done_repeat_emit_encodeSnappyBlockAsm64K + MOVL DI, R8 + MOVL DI, 12(SP) + LEAQ (BX)(SI*1), R9 + SUBL SI, R8 + LEAL -1(R8), SI + CMPL SI, $0x3c + JB one_byte_repeat_emit_encodeSnappyBlockAsm64K + CMPL SI, $0x00000100 + JB two_bytes_repeat_emit_encodeSnappyBlockAsm64K + JB three_bytes_repeat_emit_encodeSnappyBlockAsm64K + +three_bytes_repeat_emit_encodeSnappyBlockAsm64K: + MOVB $0xf4, (CX) + MOVW SI, 1(CX) + ADDQ $0x03, CX + JMP memmove_long_repeat_emit_encodeSnappyBlockAsm64K + +two_bytes_repeat_emit_encodeSnappyBlockAsm64K: + MOVB $0xf0, (CX) + MOVB SI, 1(CX) + ADDQ $0x02, CX + CMPL SI, $0x40 + JB memmove_repeat_emit_encodeSnappyBlockAsm64K + JMP memmove_long_repeat_emit_encodeSnappyBlockAsm64K + +one_byte_repeat_emit_encodeSnappyBlockAsm64K: + SHLB $0x02, SI + MOVB SI, (CX) + ADDQ $0x01, CX + +memmove_repeat_emit_encodeSnappyBlockAsm64K: + LEAQ (CX)(R8*1), SI + + // genMemMoveShort + CMPQ R8, $0x08 + JBE emit_lit_memmove_repeat_emit_encodeSnappyBlockAsm64K_memmove_move_8 + CMPQ R8, $0x10 + JBE emit_lit_memmove_repeat_emit_encodeSnappyBlockAsm64K_memmove_move_8through16 + CMPQ R8, $0x20 + JBE emit_lit_memmove_repeat_emit_encodeSnappyBlockAsm64K_memmove_move_17through32 + JMP emit_lit_memmove_repeat_emit_encodeSnappyBlockAsm64K_memmove_move_33through64 + +emit_lit_memmove_repeat_emit_encodeSnappyBlockAsm64K_memmove_move_8: + MOVQ (R9), R10 + MOVQ R10, (CX) + JMP memmove_end_copy_repeat_emit_encodeSnappyBlockAsm64K + +emit_lit_memmove_repeat_emit_encodeSnappyBlockAsm64K_memmove_move_8through16: + MOVQ (R9), R10 + MOVQ -8(R9)(R8*1), R9 + MOVQ R10, (CX) + MOVQ R9, -8(CX)(R8*1) + JMP memmove_end_copy_repeat_emit_encodeSnappyBlockAsm64K + +emit_lit_memmove_repeat_emit_encodeSnappyBlockAsm64K_memmove_move_17through32: + MOVOU (R9), X0 + MOVOU -16(R9)(R8*1), X1 + MOVOU X0, (CX) + MOVOU X1, -16(CX)(R8*1) + JMP memmove_end_copy_repeat_emit_encodeSnappyBlockAsm64K + +emit_lit_memmove_repeat_emit_encodeSnappyBlockAsm64K_memmove_move_33through64: + MOVOU (R9), X0 + MOVOU 16(R9), X1 + MOVOU -32(R9)(R8*1), X2 + MOVOU -16(R9)(R8*1), X3 + MOVOU X0, (CX) + MOVOU X1, 16(CX) + MOVOU X2, -32(CX)(R8*1) + MOVOU X3, -16(CX)(R8*1) + +memmove_end_copy_repeat_emit_encodeSnappyBlockAsm64K: + MOVQ SI, CX + JMP emit_literal_done_repeat_emit_encodeSnappyBlockAsm64K + +memmove_long_repeat_emit_encodeSnappyBlockAsm64K: + LEAQ (CX)(R8*1), SI + + // genMemMoveLong + MOVOU (R9), X0 + MOVOU 16(R9), X1 + MOVOU -32(R9)(R8*1), X2 + MOVOU -16(R9)(R8*1), X3 + MOVQ R8, R11 + SHRQ $0x05, R11 + MOVQ CX, R10 + ANDL $0x0000001f, R10 + MOVQ $0x00000040, R12 + SUBQ R10, R12 + DECQ R11 + JA emit_lit_memmove_long_repeat_emit_encodeSnappyBlockAsm64Klarge_forward_sse_loop_32 + LEAQ -32(R9)(R12*1), R10 + LEAQ -32(CX)(R12*1), R13 + +emit_lit_memmove_long_repeat_emit_encodeSnappyBlockAsm64Klarge_big_loop_back: + MOVOU (R10), X4 + MOVOU 16(R10), X5 + MOVOA X4, (R13) + MOVOA X5, 16(R13) + ADDQ $0x20, R13 + ADDQ $0x20, R10 + ADDQ $0x20, R12 + DECQ R11 + JNA emit_lit_memmove_long_repeat_emit_encodeSnappyBlockAsm64Klarge_big_loop_back + +emit_lit_memmove_long_repeat_emit_encodeSnappyBlockAsm64Klarge_forward_sse_loop_32: + MOVOU -32(R9)(R12*1), X4 + MOVOU -16(R9)(R12*1), X5 + MOVOA X4, -32(CX)(R12*1) + MOVOA X5, -16(CX)(R12*1) + ADDQ $0x20, R12 + CMPQ R8, R12 + JAE emit_lit_memmove_long_repeat_emit_encodeSnappyBlockAsm64Klarge_forward_sse_loop_32 + MOVOU X0, (CX) + MOVOU X1, 16(CX) + MOVOU X2, -32(CX)(R8*1) + MOVOU X3, -16(CX)(R8*1) + MOVQ SI, CX + +emit_literal_done_repeat_emit_encodeSnappyBlockAsm64K: + ADDL $0x05, DX + MOVL DX, SI + SUBL 16(SP), SI + MOVQ src_len+32(FP), R8 + SUBL DX, R8 + LEAQ (BX)(DX*1), R9 + LEAQ (BX)(SI*1), SI + + // matchLen + XORL R11, R11 + +matchlen_loopback_16_repeat_extend_encodeSnappyBlockAsm64K: + CMPL R8, $0x10 + JB matchlen_match8_repeat_extend_encodeSnappyBlockAsm64K + MOVQ (R9)(R11*1), R10 + MOVQ 8(R9)(R11*1), R12 + XORQ (SI)(R11*1), R10 + JNZ matchlen_bsf_8_repeat_extend_encodeSnappyBlockAsm64K + XORQ 8(SI)(R11*1), R12 + JNZ matchlen_bsf_16repeat_extend_encodeSnappyBlockAsm64K + LEAL -16(R8), R8 + LEAL 16(R11), R11 + JMP matchlen_loopback_16_repeat_extend_encodeSnappyBlockAsm64K + +matchlen_bsf_16repeat_extend_encodeSnappyBlockAsm64K: +#ifdef GOAMD64_v3 + TZCNTQ R12, R12 + +#else + BSFQ R12, R12 + +#endif + SARQ $0x03, R12 + LEAL 8(R11)(R12*1), R11 + JMP repeat_extend_forward_end_encodeSnappyBlockAsm64K + +matchlen_match8_repeat_extend_encodeSnappyBlockAsm64K: + CMPL R8, $0x08 + JB matchlen_match4_repeat_extend_encodeSnappyBlockAsm64K + MOVQ (R9)(R11*1), R10 + XORQ (SI)(R11*1), R10 + JNZ matchlen_bsf_8_repeat_extend_encodeSnappyBlockAsm64K + LEAL -8(R8), R8 + LEAL 8(R11), R11 + JMP matchlen_match4_repeat_extend_encodeSnappyBlockAsm64K + +matchlen_bsf_8_repeat_extend_encodeSnappyBlockAsm64K: +#ifdef GOAMD64_v3 + TZCNTQ R10, R10 + +#else + BSFQ R10, R10 + +#endif + SARQ $0x03, R10 + LEAL (R11)(R10*1), R11 + JMP repeat_extend_forward_end_encodeSnappyBlockAsm64K + +matchlen_match4_repeat_extend_encodeSnappyBlockAsm64K: + CMPL R8, $0x04 + JB matchlen_match2_repeat_extend_encodeSnappyBlockAsm64K + MOVL (R9)(R11*1), R10 + CMPL (SI)(R11*1), R10 + JNE matchlen_match2_repeat_extend_encodeSnappyBlockAsm64K + LEAL -4(R8), R8 + LEAL 4(R11), R11 + +matchlen_match2_repeat_extend_encodeSnappyBlockAsm64K: + CMPL R8, $0x01 + JE matchlen_match1_repeat_extend_encodeSnappyBlockAsm64K + JB repeat_extend_forward_end_encodeSnappyBlockAsm64K + MOVW (R9)(R11*1), R10 + CMPW (SI)(R11*1), R10 + JNE matchlen_match1_repeat_extend_encodeSnappyBlockAsm64K + LEAL 2(R11), R11 + SUBL $0x02, R8 + JZ repeat_extend_forward_end_encodeSnappyBlockAsm64K + +matchlen_match1_repeat_extend_encodeSnappyBlockAsm64K: + MOVB (R9)(R11*1), R10 + CMPB (SI)(R11*1), R10 + JNE repeat_extend_forward_end_encodeSnappyBlockAsm64K + LEAL 1(R11), R11 + +repeat_extend_forward_end_encodeSnappyBlockAsm64K: + ADDL R11, DX + MOVL DX, SI + SUBL DI, SI + MOVL 16(SP), DI + + // emitCopy +two_byte_offset_repeat_as_copy_encodeSnappyBlockAsm64K: + CMPL SI, $0x40 + JBE two_byte_offset_short_repeat_as_copy_encodeSnappyBlockAsm64K + MOVB $0xee, (CX) + MOVW DI, 1(CX) + LEAL -60(SI), SI + ADDQ $0x03, CX + JMP two_byte_offset_repeat_as_copy_encodeSnappyBlockAsm64K + +two_byte_offset_short_repeat_as_copy_encodeSnappyBlockAsm64K: + MOVL SI, R8 + SHLL $0x02, R8 + CMPL SI, $0x0c + JAE emit_copy_three_repeat_as_copy_encodeSnappyBlockAsm64K + CMPL DI, $0x00000800 + JAE emit_copy_three_repeat_as_copy_encodeSnappyBlockAsm64K + LEAL -15(R8), R8 + MOVB DI, 1(CX) + SHRL $0x08, DI + SHLL $0x05, DI + ORL DI, R8 + MOVB R8, (CX) + ADDQ $0x02, CX + JMP repeat_end_emit_encodeSnappyBlockAsm64K + +emit_copy_three_repeat_as_copy_encodeSnappyBlockAsm64K: + LEAL -2(R8), R8 + MOVB R8, (CX) + MOVW DI, 1(CX) + ADDQ $0x03, CX + +repeat_end_emit_encodeSnappyBlockAsm64K: + MOVL DX, 12(SP) + JMP search_loop_encodeSnappyBlockAsm64K + +no_repeat_found_encodeSnappyBlockAsm64K: + CMPL (BX)(SI*1), DI + JEQ candidate_match_encodeSnappyBlockAsm64K + SHRQ $0x08, DI + MOVL (AX)(R10*4), SI + LEAL 2(DX), R9 + CMPL (BX)(R8*1), DI + JEQ candidate2_match_encodeSnappyBlockAsm64K + MOVL R9, (AX)(R10*4) + SHRQ $0x08, DI + CMPL (BX)(SI*1), DI + JEQ candidate3_match_encodeSnappyBlockAsm64K + MOVL 20(SP), DX + JMP search_loop_encodeSnappyBlockAsm64K + +candidate3_match_encodeSnappyBlockAsm64K: + ADDL $0x02, DX + JMP candidate_match_encodeSnappyBlockAsm64K + +candidate2_match_encodeSnappyBlockAsm64K: + MOVL R9, (AX)(R10*4) + INCL DX + MOVL R8, SI + +candidate_match_encodeSnappyBlockAsm64K: + MOVL 12(SP), DI + TESTL SI, SI + JZ match_extend_back_end_encodeSnappyBlockAsm64K + +match_extend_back_loop_encodeSnappyBlockAsm64K: + CMPL DX, DI + JBE match_extend_back_end_encodeSnappyBlockAsm64K + MOVB -1(BX)(SI*1), R8 + MOVB -1(BX)(DX*1), R9 + CMPB R8, R9 + JNE match_extend_back_end_encodeSnappyBlockAsm64K + LEAL -1(DX), DX + DECL SI + JZ match_extend_back_end_encodeSnappyBlockAsm64K + JMP match_extend_back_loop_encodeSnappyBlockAsm64K + +match_extend_back_end_encodeSnappyBlockAsm64K: + MOVL DX, DI + SUBL 12(SP), DI + LEAQ 3(CX)(DI*1), DI + CMPQ DI, (SP) + JB match_dst_size_check_encodeSnappyBlockAsm64K + MOVQ $0x00000000, ret+56(FP) + RET + +match_dst_size_check_encodeSnappyBlockAsm64K: + MOVL DX, DI + MOVL 12(SP), R8 + CMPL R8, DI + JEQ emit_literal_done_match_emit_encodeSnappyBlockAsm64K + MOVL DI, R9 + MOVL DI, 12(SP) + LEAQ (BX)(R8*1), DI + SUBL R8, R9 + LEAL -1(R9), R8 + CMPL R8, $0x3c + JB one_byte_match_emit_encodeSnappyBlockAsm64K + CMPL R8, $0x00000100 + JB two_bytes_match_emit_encodeSnappyBlockAsm64K + JB three_bytes_match_emit_encodeSnappyBlockAsm64K + +three_bytes_match_emit_encodeSnappyBlockAsm64K: + MOVB $0xf4, (CX) + MOVW R8, 1(CX) + ADDQ $0x03, CX + JMP memmove_long_match_emit_encodeSnappyBlockAsm64K + +two_bytes_match_emit_encodeSnappyBlockAsm64K: + MOVB $0xf0, (CX) + MOVB R8, 1(CX) + ADDQ $0x02, CX + CMPL R8, $0x40 + JB memmove_match_emit_encodeSnappyBlockAsm64K + JMP memmove_long_match_emit_encodeSnappyBlockAsm64K + +one_byte_match_emit_encodeSnappyBlockAsm64K: + SHLB $0x02, R8 + MOVB R8, (CX) + ADDQ $0x01, CX + +memmove_match_emit_encodeSnappyBlockAsm64K: + LEAQ (CX)(R9*1), R8 + + // genMemMoveShort + CMPQ R9, $0x08 + JBE emit_lit_memmove_match_emit_encodeSnappyBlockAsm64K_memmove_move_8 + CMPQ R9, $0x10 + JBE emit_lit_memmove_match_emit_encodeSnappyBlockAsm64K_memmove_move_8through16 + CMPQ R9, $0x20 + JBE emit_lit_memmove_match_emit_encodeSnappyBlockAsm64K_memmove_move_17through32 + JMP emit_lit_memmove_match_emit_encodeSnappyBlockAsm64K_memmove_move_33through64 + +emit_lit_memmove_match_emit_encodeSnappyBlockAsm64K_memmove_move_8: + MOVQ (DI), R10 + MOVQ R10, (CX) + JMP memmove_end_copy_match_emit_encodeSnappyBlockAsm64K + +emit_lit_memmove_match_emit_encodeSnappyBlockAsm64K_memmove_move_8through16: + MOVQ (DI), R10 + MOVQ -8(DI)(R9*1), DI + MOVQ R10, (CX) + MOVQ DI, -8(CX)(R9*1) + JMP memmove_end_copy_match_emit_encodeSnappyBlockAsm64K + +emit_lit_memmove_match_emit_encodeSnappyBlockAsm64K_memmove_move_17through32: + MOVOU (DI), X0 + MOVOU -16(DI)(R9*1), X1 + MOVOU X0, (CX) + MOVOU X1, -16(CX)(R9*1) + JMP memmove_end_copy_match_emit_encodeSnappyBlockAsm64K + +emit_lit_memmove_match_emit_encodeSnappyBlockAsm64K_memmove_move_33through64: + MOVOU (DI), X0 + MOVOU 16(DI), X1 + MOVOU -32(DI)(R9*1), X2 + MOVOU -16(DI)(R9*1), X3 + MOVOU X0, (CX) + MOVOU X1, 16(CX) + MOVOU X2, -32(CX)(R9*1) + MOVOU X3, -16(CX)(R9*1) + +memmove_end_copy_match_emit_encodeSnappyBlockAsm64K: + MOVQ R8, CX + JMP emit_literal_done_match_emit_encodeSnappyBlockAsm64K + +memmove_long_match_emit_encodeSnappyBlockAsm64K: + LEAQ (CX)(R9*1), R8 + + // genMemMoveLong + MOVOU (DI), X0 + MOVOU 16(DI), X1 + MOVOU -32(DI)(R9*1), X2 + MOVOU -16(DI)(R9*1), X3 + MOVQ R9, R11 + SHRQ $0x05, R11 + MOVQ CX, R10 + ANDL $0x0000001f, R10 + MOVQ $0x00000040, R12 + SUBQ R10, R12 + DECQ R11 + JA emit_lit_memmove_long_match_emit_encodeSnappyBlockAsm64Klarge_forward_sse_loop_32 + LEAQ -32(DI)(R12*1), R10 + LEAQ -32(CX)(R12*1), R13 + +emit_lit_memmove_long_match_emit_encodeSnappyBlockAsm64Klarge_big_loop_back: + MOVOU (R10), X4 + MOVOU 16(R10), X5 + MOVOA X4, (R13) + MOVOA X5, 16(R13) + ADDQ $0x20, R13 + ADDQ $0x20, R10 + ADDQ $0x20, R12 + DECQ R11 + JNA emit_lit_memmove_long_match_emit_encodeSnappyBlockAsm64Klarge_big_loop_back + +emit_lit_memmove_long_match_emit_encodeSnappyBlockAsm64Klarge_forward_sse_loop_32: + MOVOU -32(DI)(R12*1), X4 + MOVOU -16(DI)(R12*1), X5 + MOVOA X4, -32(CX)(R12*1) + MOVOA X5, -16(CX)(R12*1) + ADDQ $0x20, R12 + CMPQ R9, R12 + JAE emit_lit_memmove_long_match_emit_encodeSnappyBlockAsm64Klarge_forward_sse_loop_32 + MOVOU X0, (CX) + MOVOU X1, 16(CX) + MOVOU X2, -32(CX)(R9*1) + MOVOU X3, -16(CX)(R9*1) + MOVQ R8, CX + +emit_literal_done_match_emit_encodeSnappyBlockAsm64K: +match_nolit_loop_encodeSnappyBlockAsm64K: + MOVL DX, DI + SUBL SI, DI + MOVL DI, 16(SP) + ADDL $0x04, DX + ADDL $0x04, SI + MOVQ src_len+32(FP), DI + SUBL DX, DI + LEAQ (BX)(DX*1), R8 + LEAQ (BX)(SI*1), SI + + // matchLen + XORL R10, R10 + +matchlen_loopback_16_match_nolit_encodeSnappyBlockAsm64K: + CMPL DI, $0x10 + JB matchlen_match8_match_nolit_encodeSnappyBlockAsm64K + MOVQ (R8)(R10*1), R9 + MOVQ 8(R8)(R10*1), R11 + XORQ (SI)(R10*1), R9 + JNZ matchlen_bsf_8_match_nolit_encodeSnappyBlockAsm64K + XORQ 8(SI)(R10*1), R11 + JNZ matchlen_bsf_16match_nolit_encodeSnappyBlockAsm64K + LEAL -16(DI), DI + LEAL 16(R10), R10 + JMP matchlen_loopback_16_match_nolit_encodeSnappyBlockAsm64K + +matchlen_bsf_16match_nolit_encodeSnappyBlockAsm64K: +#ifdef GOAMD64_v3 + TZCNTQ R11, R11 + +#else + BSFQ R11, R11 + +#endif + SARQ $0x03, R11 + LEAL 8(R10)(R11*1), R10 + JMP match_nolit_end_encodeSnappyBlockAsm64K + +matchlen_match8_match_nolit_encodeSnappyBlockAsm64K: + CMPL DI, $0x08 + JB matchlen_match4_match_nolit_encodeSnappyBlockAsm64K + MOVQ (R8)(R10*1), R9 + XORQ (SI)(R10*1), R9 + JNZ matchlen_bsf_8_match_nolit_encodeSnappyBlockAsm64K + LEAL -8(DI), DI + LEAL 8(R10), R10 + JMP matchlen_match4_match_nolit_encodeSnappyBlockAsm64K + +matchlen_bsf_8_match_nolit_encodeSnappyBlockAsm64K: +#ifdef GOAMD64_v3 + TZCNTQ R9, R9 + +#else + BSFQ R9, R9 + +#endif + SARQ $0x03, R9 + LEAL (R10)(R9*1), R10 + JMP match_nolit_end_encodeSnappyBlockAsm64K + +matchlen_match4_match_nolit_encodeSnappyBlockAsm64K: + CMPL DI, $0x04 + JB matchlen_match2_match_nolit_encodeSnappyBlockAsm64K + MOVL (R8)(R10*1), R9 + CMPL (SI)(R10*1), R9 + JNE matchlen_match2_match_nolit_encodeSnappyBlockAsm64K + LEAL -4(DI), DI + LEAL 4(R10), R10 + +matchlen_match2_match_nolit_encodeSnappyBlockAsm64K: + CMPL DI, $0x01 + JE matchlen_match1_match_nolit_encodeSnappyBlockAsm64K + JB match_nolit_end_encodeSnappyBlockAsm64K + MOVW (R8)(R10*1), R9 + CMPW (SI)(R10*1), R9 + JNE matchlen_match1_match_nolit_encodeSnappyBlockAsm64K + LEAL 2(R10), R10 + SUBL $0x02, DI + JZ match_nolit_end_encodeSnappyBlockAsm64K + +matchlen_match1_match_nolit_encodeSnappyBlockAsm64K: + MOVB (R8)(R10*1), R9 + CMPB (SI)(R10*1), R9 + JNE match_nolit_end_encodeSnappyBlockAsm64K + LEAL 1(R10), R10 + +match_nolit_end_encodeSnappyBlockAsm64K: + ADDL R10, DX + MOVL 16(SP), SI + ADDL $0x04, R10 + MOVL DX, 12(SP) + + // emitCopy +two_byte_offset_match_nolit_encodeSnappyBlockAsm64K: + CMPL R10, $0x40 + JBE two_byte_offset_short_match_nolit_encodeSnappyBlockAsm64K + MOVB $0xee, (CX) + MOVW SI, 1(CX) + LEAL -60(R10), R10 + ADDQ $0x03, CX + JMP two_byte_offset_match_nolit_encodeSnappyBlockAsm64K + +two_byte_offset_short_match_nolit_encodeSnappyBlockAsm64K: + MOVL R10, DI + SHLL $0x02, DI + CMPL R10, $0x0c + JAE emit_copy_three_match_nolit_encodeSnappyBlockAsm64K + CMPL SI, $0x00000800 + JAE emit_copy_three_match_nolit_encodeSnappyBlockAsm64K + LEAL -15(DI), DI + MOVB SI, 1(CX) + SHRL $0x08, SI + SHLL $0x05, SI + ORL SI, DI + MOVB DI, (CX) + ADDQ $0x02, CX + JMP match_nolit_emitcopy_end_encodeSnappyBlockAsm64K + +emit_copy_three_match_nolit_encodeSnappyBlockAsm64K: + LEAL -2(DI), DI + MOVB DI, (CX) + MOVW SI, 1(CX) + ADDQ $0x03, CX + +match_nolit_emitcopy_end_encodeSnappyBlockAsm64K: + CMPL DX, 8(SP) + JAE emit_remainder_encodeSnappyBlockAsm64K + MOVQ -2(BX)(DX*1), DI + CMPQ CX, (SP) + JB match_nolit_dst_ok_encodeSnappyBlockAsm64K + MOVQ $0x00000000, ret+56(FP) + RET + +match_nolit_dst_ok_encodeSnappyBlockAsm64K: + MOVQ $0x0000cf1bbcdcbf9b, R9 + MOVQ DI, R8 + SHRQ $0x10, DI + MOVQ DI, SI + SHLQ $0x10, R8 + IMULQ R9, R8 + SHRQ $0x32, R8 + SHLQ $0x10, SI + IMULQ R9, SI + SHRQ $0x32, SI + LEAL -2(DX), R9 + LEAQ (AX)(SI*4), R10 + MOVL (R10), SI + MOVL R9, (AX)(R8*4) + MOVL DX, (R10) + CMPL (BX)(SI*1), DI + JEQ match_nolit_loop_encodeSnappyBlockAsm64K + INCL DX + JMP search_loop_encodeSnappyBlockAsm64K + +emit_remainder_encodeSnappyBlockAsm64K: + MOVQ src_len+32(FP), AX + SUBL 12(SP), AX + LEAQ 3(CX)(AX*1), AX + CMPQ AX, (SP) + JB emit_remainder_ok_encodeSnappyBlockAsm64K + MOVQ $0x00000000, ret+56(FP) + RET + +emit_remainder_ok_encodeSnappyBlockAsm64K: + MOVQ src_len+32(FP), AX + MOVL 12(SP), DX + CMPL DX, AX + JEQ emit_literal_done_emit_remainder_encodeSnappyBlockAsm64K + MOVL AX, SI + MOVL AX, 12(SP) + LEAQ (BX)(DX*1), AX + SUBL DX, SI + LEAL -1(SI), DX + CMPL DX, $0x3c + JB one_byte_emit_remainder_encodeSnappyBlockAsm64K + CMPL DX, $0x00000100 + JB two_bytes_emit_remainder_encodeSnappyBlockAsm64K + JB three_bytes_emit_remainder_encodeSnappyBlockAsm64K + +three_bytes_emit_remainder_encodeSnappyBlockAsm64K: + MOVB $0xf4, (CX) + MOVW DX, 1(CX) + ADDQ $0x03, CX + JMP memmove_long_emit_remainder_encodeSnappyBlockAsm64K + +two_bytes_emit_remainder_encodeSnappyBlockAsm64K: + MOVB $0xf0, (CX) + MOVB DL, 1(CX) + ADDQ $0x02, CX + CMPL DX, $0x40 + JB memmove_emit_remainder_encodeSnappyBlockAsm64K + JMP memmove_long_emit_remainder_encodeSnappyBlockAsm64K + +one_byte_emit_remainder_encodeSnappyBlockAsm64K: + SHLB $0x02, DL + MOVB DL, (CX) + ADDQ $0x01, CX + +memmove_emit_remainder_encodeSnappyBlockAsm64K: + LEAQ (CX)(SI*1), DX + MOVL SI, BX + + // genMemMoveShort + CMPQ BX, $0x03 + JB emit_lit_memmove_emit_remainder_encodeSnappyBlockAsm64K_memmove_move_1or2 + JE emit_lit_memmove_emit_remainder_encodeSnappyBlockAsm64K_memmove_move_3 + CMPQ BX, $0x08 + JB emit_lit_memmove_emit_remainder_encodeSnappyBlockAsm64K_memmove_move_4through7 + CMPQ BX, $0x10 + JBE emit_lit_memmove_emit_remainder_encodeSnappyBlockAsm64K_memmove_move_8through16 + CMPQ BX, $0x20 + JBE emit_lit_memmove_emit_remainder_encodeSnappyBlockAsm64K_memmove_move_17through32 + JMP emit_lit_memmove_emit_remainder_encodeSnappyBlockAsm64K_memmove_move_33through64 + +emit_lit_memmove_emit_remainder_encodeSnappyBlockAsm64K_memmove_move_1or2: + MOVB (AX), SI + MOVB -1(AX)(BX*1), AL + MOVB SI, (CX) + MOVB AL, -1(CX)(BX*1) + JMP memmove_end_copy_emit_remainder_encodeSnappyBlockAsm64K + +emit_lit_memmove_emit_remainder_encodeSnappyBlockAsm64K_memmove_move_3: + MOVW (AX), SI + MOVB 2(AX), AL + MOVW SI, (CX) + MOVB AL, 2(CX) + JMP memmove_end_copy_emit_remainder_encodeSnappyBlockAsm64K + +emit_lit_memmove_emit_remainder_encodeSnappyBlockAsm64K_memmove_move_4through7: + MOVL (AX), SI + MOVL -4(AX)(BX*1), AX + MOVL SI, (CX) + MOVL AX, -4(CX)(BX*1) + JMP memmove_end_copy_emit_remainder_encodeSnappyBlockAsm64K + +emit_lit_memmove_emit_remainder_encodeSnappyBlockAsm64K_memmove_move_8through16: + MOVQ (AX), SI + MOVQ -8(AX)(BX*1), AX + MOVQ SI, (CX) + MOVQ AX, -8(CX)(BX*1) + JMP memmove_end_copy_emit_remainder_encodeSnappyBlockAsm64K + +emit_lit_memmove_emit_remainder_encodeSnappyBlockAsm64K_memmove_move_17through32: + MOVOU (AX), X0 + MOVOU -16(AX)(BX*1), X1 + MOVOU X0, (CX) + MOVOU X1, -16(CX)(BX*1) + JMP memmove_end_copy_emit_remainder_encodeSnappyBlockAsm64K + +emit_lit_memmove_emit_remainder_encodeSnappyBlockAsm64K_memmove_move_33through64: + MOVOU (AX), X0 + MOVOU 16(AX), X1 + MOVOU -32(AX)(BX*1), X2 + MOVOU -16(AX)(BX*1), X3 + MOVOU X0, (CX) + MOVOU X1, 16(CX) + MOVOU X2, -32(CX)(BX*1) + MOVOU X3, -16(CX)(BX*1) + +memmove_end_copy_emit_remainder_encodeSnappyBlockAsm64K: + MOVQ DX, CX + JMP emit_literal_done_emit_remainder_encodeSnappyBlockAsm64K + +memmove_long_emit_remainder_encodeSnappyBlockAsm64K: + LEAQ (CX)(SI*1), DX + MOVL SI, BX + + // genMemMoveLong + MOVOU (AX), X0 + MOVOU 16(AX), X1 + MOVOU -32(AX)(BX*1), X2 + MOVOU -16(AX)(BX*1), X3 + MOVQ BX, DI + SHRQ $0x05, DI + MOVQ CX, SI + ANDL $0x0000001f, SI + MOVQ $0x00000040, R8 + SUBQ SI, R8 + DECQ DI + JA emit_lit_memmove_long_emit_remainder_encodeSnappyBlockAsm64Klarge_forward_sse_loop_32 + LEAQ -32(AX)(R8*1), SI + LEAQ -32(CX)(R8*1), R9 + +emit_lit_memmove_long_emit_remainder_encodeSnappyBlockAsm64Klarge_big_loop_back: + MOVOU (SI), X4 + MOVOU 16(SI), X5 + MOVOA X4, (R9) + MOVOA X5, 16(R9) + ADDQ $0x20, R9 + ADDQ $0x20, SI + ADDQ $0x20, R8 + DECQ DI + JNA emit_lit_memmove_long_emit_remainder_encodeSnappyBlockAsm64Klarge_big_loop_back + +emit_lit_memmove_long_emit_remainder_encodeSnappyBlockAsm64Klarge_forward_sse_loop_32: + MOVOU -32(AX)(R8*1), X4 + MOVOU -16(AX)(R8*1), X5 + MOVOA X4, -32(CX)(R8*1) + MOVOA X5, -16(CX)(R8*1) + ADDQ $0x20, R8 + CMPQ BX, R8 + JAE emit_lit_memmove_long_emit_remainder_encodeSnappyBlockAsm64Klarge_forward_sse_loop_32 + MOVOU X0, (CX) + MOVOU X1, 16(CX) + MOVOU X2, -32(CX)(BX*1) + MOVOU X3, -16(CX)(BX*1) + MOVQ DX, CX + +emit_literal_done_emit_remainder_encodeSnappyBlockAsm64K: + MOVQ dst_base+0(FP), AX + SUBQ AX, CX + MOVQ CX, ret+56(FP) + RET + +// func encodeSnappyBlockAsm12B(dst []byte, src []byte, tmp *[16384]byte) int +// Requires: BMI, SSE2 +TEXT ·encodeSnappyBlockAsm12B(SB), $24-64 + MOVQ tmp+48(FP), AX + MOVQ dst_base+0(FP), CX + MOVQ $0x00000080, DX + MOVQ AX, BX + PXOR X0, X0 + +zero_loop_encodeSnappyBlockAsm12B: + MOVOU X0, (BX) + MOVOU X0, 16(BX) + MOVOU X0, 32(BX) + MOVOU X0, 48(BX) + MOVOU X0, 64(BX) + MOVOU X0, 80(BX) + MOVOU X0, 96(BX) + MOVOU X0, 112(BX) + ADDQ $0x80, BX + DECQ DX + JNZ zero_loop_encodeSnappyBlockAsm12B + MOVL $0x00000000, 12(SP) + MOVQ src_len+32(FP), DX + LEAQ -9(DX), BX + LEAQ -8(DX), SI + MOVL SI, 8(SP) + SHRQ $0x05, DX + SUBL DX, BX + LEAQ (CX)(BX*1), BX + MOVQ BX, (SP) + MOVL $0x00000001, DX + MOVL DX, 16(SP) + MOVQ src_base+24(FP), BX + +search_loop_encodeSnappyBlockAsm12B: + MOVL DX, SI + SUBL 12(SP), SI + SHRL $0x05, SI + LEAL 4(DX)(SI*1), SI + CMPL SI, 8(SP) + JAE emit_remainder_encodeSnappyBlockAsm12B + MOVQ (BX)(DX*1), DI + MOVL SI, 20(SP) + MOVQ $0x000000cf1bbcdcbb, R9 + MOVQ DI, R10 + MOVQ DI, R11 + SHRQ $0x08, R11 + SHLQ $0x18, R10 + IMULQ R9, R10 + SHRQ $0x34, R10 + SHLQ $0x18, R11 + IMULQ R9, R11 + SHRQ $0x34, R11 + MOVL (AX)(R10*4), SI + MOVL (AX)(R11*4), R8 + MOVL DX, (AX)(R10*4) + LEAL 1(DX), R10 + MOVL R10, (AX)(R11*4) + MOVQ DI, R10 + SHRQ $0x10, R10 + SHLQ $0x18, R10 + IMULQ R9, R10 + SHRQ $0x34, R10 + MOVL DX, R9 + SUBL 16(SP), R9 + MOVL 1(BX)(R9*1), R11 + MOVQ DI, R9 + SHRQ $0x08, R9 + CMPL R9, R11 + JNE no_repeat_found_encodeSnappyBlockAsm12B + LEAL 1(DX), DI + MOVL 12(SP), SI + MOVL DI, R8 + SUBL 16(SP), R8 + JZ repeat_extend_back_end_encodeSnappyBlockAsm12B + +repeat_extend_back_loop_encodeSnappyBlockAsm12B: + CMPL DI, SI + JBE repeat_extend_back_end_encodeSnappyBlockAsm12B + MOVB -1(BX)(R8*1), R9 + MOVB -1(BX)(DI*1), R10 + CMPB R9, R10 + JNE repeat_extend_back_end_encodeSnappyBlockAsm12B + LEAL -1(DI), DI + DECL R8 + JNZ repeat_extend_back_loop_encodeSnappyBlockAsm12B + +repeat_extend_back_end_encodeSnappyBlockAsm12B: + MOVL DI, SI + SUBL 12(SP), SI + LEAQ 3(CX)(SI*1), SI + CMPQ SI, (SP) + JB repeat_dst_size_check_encodeSnappyBlockAsm12B + MOVQ $0x00000000, ret+56(FP) + RET + +repeat_dst_size_check_encodeSnappyBlockAsm12B: + MOVL 12(SP), SI + CMPL SI, DI + JEQ emit_literal_done_repeat_emit_encodeSnappyBlockAsm12B + MOVL DI, R8 + MOVL DI, 12(SP) + LEAQ (BX)(SI*1), R9 + SUBL SI, R8 + LEAL -1(R8), SI + CMPL SI, $0x3c + JB one_byte_repeat_emit_encodeSnappyBlockAsm12B + CMPL SI, $0x00000100 + JB two_bytes_repeat_emit_encodeSnappyBlockAsm12B + JB three_bytes_repeat_emit_encodeSnappyBlockAsm12B + +three_bytes_repeat_emit_encodeSnappyBlockAsm12B: + MOVB $0xf4, (CX) + MOVW SI, 1(CX) + ADDQ $0x03, CX + JMP memmove_long_repeat_emit_encodeSnappyBlockAsm12B + +two_bytes_repeat_emit_encodeSnappyBlockAsm12B: + MOVB $0xf0, (CX) + MOVB SI, 1(CX) + ADDQ $0x02, CX + CMPL SI, $0x40 + JB memmove_repeat_emit_encodeSnappyBlockAsm12B + JMP memmove_long_repeat_emit_encodeSnappyBlockAsm12B + +one_byte_repeat_emit_encodeSnappyBlockAsm12B: + SHLB $0x02, SI + MOVB SI, (CX) + ADDQ $0x01, CX + +memmove_repeat_emit_encodeSnappyBlockAsm12B: + LEAQ (CX)(R8*1), SI + + // genMemMoveShort + CMPQ R8, $0x08 + JBE emit_lit_memmove_repeat_emit_encodeSnappyBlockAsm12B_memmove_move_8 + CMPQ R8, $0x10 + JBE emit_lit_memmove_repeat_emit_encodeSnappyBlockAsm12B_memmove_move_8through16 + CMPQ R8, $0x20 + JBE emit_lit_memmove_repeat_emit_encodeSnappyBlockAsm12B_memmove_move_17through32 + JMP emit_lit_memmove_repeat_emit_encodeSnappyBlockAsm12B_memmove_move_33through64 + +emit_lit_memmove_repeat_emit_encodeSnappyBlockAsm12B_memmove_move_8: + MOVQ (R9), R10 + MOVQ R10, (CX) + JMP memmove_end_copy_repeat_emit_encodeSnappyBlockAsm12B + +emit_lit_memmove_repeat_emit_encodeSnappyBlockAsm12B_memmove_move_8through16: + MOVQ (R9), R10 + MOVQ -8(R9)(R8*1), R9 + MOVQ R10, (CX) + MOVQ R9, -8(CX)(R8*1) + JMP memmove_end_copy_repeat_emit_encodeSnappyBlockAsm12B + +emit_lit_memmove_repeat_emit_encodeSnappyBlockAsm12B_memmove_move_17through32: + MOVOU (R9), X0 + MOVOU -16(R9)(R8*1), X1 + MOVOU X0, (CX) + MOVOU X1, -16(CX)(R8*1) + JMP memmove_end_copy_repeat_emit_encodeSnappyBlockAsm12B + +emit_lit_memmove_repeat_emit_encodeSnappyBlockAsm12B_memmove_move_33through64: + MOVOU (R9), X0 + MOVOU 16(R9), X1 + MOVOU -32(R9)(R8*1), X2 + MOVOU -16(R9)(R8*1), X3 + MOVOU X0, (CX) + MOVOU X1, 16(CX) + MOVOU X2, -32(CX)(R8*1) + MOVOU X3, -16(CX)(R8*1) + +memmove_end_copy_repeat_emit_encodeSnappyBlockAsm12B: + MOVQ SI, CX + JMP emit_literal_done_repeat_emit_encodeSnappyBlockAsm12B + +memmove_long_repeat_emit_encodeSnappyBlockAsm12B: + LEAQ (CX)(R8*1), SI + + // genMemMoveLong + MOVOU (R9), X0 + MOVOU 16(R9), X1 + MOVOU -32(R9)(R8*1), X2 + MOVOU -16(R9)(R8*1), X3 + MOVQ R8, R11 + SHRQ $0x05, R11 + MOVQ CX, R10 + ANDL $0x0000001f, R10 + MOVQ $0x00000040, R12 + SUBQ R10, R12 + DECQ R11 + JA emit_lit_memmove_long_repeat_emit_encodeSnappyBlockAsm12Blarge_forward_sse_loop_32 + LEAQ -32(R9)(R12*1), R10 + LEAQ -32(CX)(R12*1), R13 + +emit_lit_memmove_long_repeat_emit_encodeSnappyBlockAsm12Blarge_big_loop_back: + MOVOU (R10), X4 + MOVOU 16(R10), X5 + MOVOA X4, (R13) + MOVOA X5, 16(R13) + ADDQ $0x20, R13 + ADDQ $0x20, R10 + ADDQ $0x20, R12 + DECQ R11 + JNA emit_lit_memmove_long_repeat_emit_encodeSnappyBlockAsm12Blarge_big_loop_back + +emit_lit_memmove_long_repeat_emit_encodeSnappyBlockAsm12Blarge_forward_sse_loop_32: + MOVOU -32(R9)(R12*1), X4 + MOVOU -16(R9)(R12*1), X5 + MOVOA X4, -32(CX)(R12*1) + MOVOA X5, -16(CX)(R12*1) + ADDQ $0x20, R12 + CMPQ R8, R12 + JAE emit_lit_memmove_long_repeat_emit_encodeSnappyBlockAsm12Blarge_forward_sse_loop_32 + MOVOU X0, (CX) + MOVOU X1, 16(CX) + MOVOU X2, -32(CX)(R8*1) + MOVOU X3, -16(CX)(R8*1) + MOVQ SI, CX + +emit_literal_done_repeat_emit_encodeSnappyBlockAsm12B: + ADDL $0x05, DX + MOVL DX, SI + SUBL 16(SP), SI + MOVQ src_len+32(FP), R8 + SUBL DX, R8 + LEAQ (BX)(DX*1), R9 + LEAQ (BX)(SI*1), SI + + // matchLen + XORL R11, R11 + +matchlen_loopback_16_repeat_extend_encodeSnappyBlockAsm12B: + CMPL R8, $0x10 + JB matchlen_match8_repeat_extend_encodeSnappyBlockAsm12B + MOVQ (R9)(R11*1), R10 + MOVQ 8(R9)(R11*1), R12 + XORQ (SI)(R11*1), R10 + JNZ matchlen_bsf_8_repeat_extend_encodeSnappyBlockAsm12B + XORQ 8(SI)(R11*1), R12 + JNZ matchlen_bsf_16repeat_extend_encodeSnappyBlockAsm12B + LEAL -16(R8), R8 + LEAL 16(R11), R11 + JMP matchlen_loopback_16_repeat_extend_encodeSnappyBlockAsm12B + +matchlen_bsf_16repeat_extend_encodeSnappyBlockAsm12B: +#ifdef GOAMD64_v3 + TZCNTQ R12, R12 + +#else + BSFQ R12, R12 + +#endif + SARQ $0x03, R12 + LEAL 8(R11)(R12*1), R11 + JMP repeat_extend_forward_end_encodeSnappyBlockAsm12B + +matchlen_match8_repeat_extend_encodeSnappyBlockAsm12B: + CMPL R8, $0x08 + JB matchlen_match4_repeat_extend_encodeSnappyBlockAsm12B + MOVQ (R9)(R11*1), R10 + XORQ (SI)(R11*1), R10 + JNZ matchlen_bsf_8_repeat_extend_encodeSnappyBlockAsm12B + LEAL -8(R8), R8 + LEAL 8(R11), R11 + JMP matchlen_match4_repeat_extend_encodeSnappyBlockAsm12B + +matchlen_bsf_8_repeat_extend_encodeSnappyBlockAsm12B: +#ifdef GOAMD64_v3 + TZCNTQ R10, R10 + +#else + BSFQ R10, R10 + +#endif + SARQ $0x03, R10 + LEAL (R11)(R10*1), R11 + JMP repeat_extend_forward_end_encodeSnappyBlockAsm12B + +matchlen_match4_repeat_extend_encodeSnappyBlockAsm12B: + CMPL R8, $0x04 + JB matchlen_match2_repeat_extend_encodeSnappyBlockAsm12B + MOVL (R9)(R11*1), R10 + CMPL (SI)(R11*1), R10 + JNE matchlen_match2_repeat_extend_encodeSnappyBlockAsm12B + LEAL -4(R8), R8 + LEAL 4(R11), R11 + +matchlen_match2_repeat_extend_encodeSnappyBlockAsm12B: + CMPL R8, $0x01 + JE matchlen_match1_repeat_extend_encodeSnappyBlockAsm12B + JB repeat_extend_forward_end_encodeSnappyBlockAsm12B + MOVW (R9)(R11*1), R10 + CMPW (SI)(R11*1), R10 + JNE matchlen_match1_repeat_extend_encodeSnappyBlockAsm12B + LEAL 2(R11), R11 + SUBL $0x02, R8 + JZ repeat_extend_forward_end_encodeSnappyBlockAsm12B + +matchlen_match1_repeat_extend_encodeSnappyBlockAsm12B: + MOVB (R9)(R11*1), R10 + CMPB (SI)(R11*1), R10 + JNE repeat_extend_forward_end_encodeSnappyBlockAsm12B + LEAL 1(R11), R11 + +repeat_extend_forward_end_encodeSnappyBlockAsm12B: + ADDL R11, DX + MOVL DX, SI + SUBL DI, SI + MOVL 16(SP), DI + + // emitCopy +two_byte_offset_repeat_as_copy_encodeSnappyBlockAsm12B: + CMPL SI, $0x40 + JBE two_byte_offset_short_repeat_as_copy_encodeSnappyBlockAsm12B + MOVB $0xee, (CX) + MOVW DI, 1(CX) + LEAL -60(SI), SI + ADDQ $0x03, CX + JMP two_byte_offset_repeat_as_copy_encodeSnappyBlockAsm12B + +two_byte_offset_short_repeat_as_copy_encodeSnappyBlockAsm12B: + MOVL SI, R8 + SHLL $0x02, R8 + CMPL SI, $0x0c + JAE emit_copy_three_repeat_as_copy_encodeSnappyBlockAsm12B + CMPL DI, $0x00000800 + JAE emit_copy_three_repeat_as_copy_encodeSnappyBlockAsm12B + LEAL -15(R8), R8 + MOVB DI, 1(CX) + SHRL $0x08, DI + SHLL $0x05, DI + ORL DI, R8 + MOVB R8, (CX) + ADDQ $0x02, CX + JMP repeat_end_emit_encodeSnappyBlockAsm12B + +emit_copy_three_repeat_as_copy_encodeSnappyBlockAsm12B: + LEAL -2(R8), R8 + MOVB R8, (CX) + MOVW DI, 1(CX) + ADDQ $0x03, CX + +repeat_end_emit_encodeSnappyBlockAsm12B: + MOVL DX, 12(SP) + JMP search_loop_encodeSnappyBlockAsm12B + +no_repeat_found_encodeSnappyBlockAsm12B: + CMPL (BX)(SI*1), DI + JEQ candidate_match_encodeSnappyBlockAsm12B + SHRQ $0x08, DI + MOVL (AX)(R10*4), SI + LEAL 2(DX), R9 + CMPL (BX)(R8*1), DI + JEQ candidate2_match_encodeSnappyBlockAsm12B + MOVL R9, (AX)(R10*4) + SHRQ $0x08, DI + CMPL (BX)(SI*1), DI + JEQ candidate3_match_encodeSnappyBlockAsm12B + MOVL 20(SP), DX + JMP search_loop_encodeSnappyBlockAsm12B + +candidate3_match_encodeSnappyBlockAsm12B: + ADDL $0x02, DX + JMP candidate_match_encodeSnappyBlockAsm12B + +candidate2_match_encodeSnappyBlockAsm12B: + MOVL R9, (AX)(R10*4) + INCL DX + MOVL R8, SI + +candidate_match_encodeSnappyBlockAsm12B: + MOVL 12(SP), DI + TESTL SI, SI + JZ match_extend_back_end_encodeSnappyBlockAsm12B + +match_extend_back_loop_encodeSnappyBlockAsm12B: + CMPL DX, DI + JBE match_extend_back_end_encodeSnappyBlockAsm12B + MOVB -1(BX)(SI*1), R8 + MOVB -1(BX)(DX*1), R9 + CMPB R8, R9 + JNE match_extend_back_end_encodeSnappyBlockAsm12B + LEAL -1(DX), DX + DECL SI + JZ match_extend_back_end_encodeSnappyBlockAsm12B + JMP match_extend_back_loop_encodeSnappyBlockAsm12B + +match_extend_back_end_encodeSnappyBlockAsm12B: + MOVL DX, DI + SUBL 12(SP), DI + LEAQ 3(CX)(DI*1), DI + CMPQ DI, (SP) + JB match_dst_size_check_encodeSnappyBlockAsm12B + MOVQ $0x00000000, ret+56(FP) + RET + +match_dst_size_check_encodeSnappyBlockAsm12B: + MOVL DX, DI + MOVL 12(SP), R8 + CMPL R8, DI + JEQ emit_literal_done_match_emit_encodeSnappyBlockAsm12B + MOVL DI, R9 + MOVL DI, 12(SP) + LEAQ (BX)(R8*1), DI + SUBL R8, R9 + LEAL -1(R9), R8 + CMPL R8, $0x3c + JB one_byte_match_emit_encodeSnappyBlockAsm12B + CMPL R8, $0x00000100 + JB two_bytes_match_emit_encodeSnappyBlockAsm12B + JB three_bytes_match_emit_encodeSnappyBlockAsm12B + +three_bytes_match_emit_encodeSnappyBlockAsm12B: + MOVB $0xf4, (CX) + MOVW R8, 1(CX) + ADDQ $0x03, CX + JMP memmove_long_match_emit_encodeSnappyBlockAsm12B + +two_bytes_match_emit_encodeSnappyBlockAsm12B: + MOVB $0xf0, (CX) + MOVB R8, 1(CX) + ADDQ $0x02, CX + CMPL R8, $0x40 + JB memmove_match_emit_encodeSnappyBlockAsm12B + JMP memmove_long_match_emit_encodeSnappyBlockAsm12B + +one_byte_match_emit_encodeSnappyBlockAsm12B: + SHLB $0x02, R8 + MOVB R8, (CX) + ADDQ $0x01, CX + +memmove_match_emit_encodeSnappyBlockAsm12B: + LEAQ (CX)(R9*1), R8 + + // genMemMoveShort + CMPQ R9, $0x08 + JBE emit_lit_memmove_match_emit_encodeSnappyBlockAsm12B_memmove_move_8 + CMPQ R9, $0x10 + JBE emit_lit_memmove_match_emit_encodeSnappyBlockAsm12B_memmove_move_8through16 + CMPQ R9, $0x20 + JBE emit_lit_memmove_match_emit_encodeSnappyBlockAsm12B_memmove_move_17through32 + JMP emit_lit_memmove_match_emit_encodeSnappyBlockAsm12B_memmove_move_33through64 + +emit_lit_memmove_match_emit_encodeSnappyBlockAsm12B_memmove_move_8: + MOVQ (DI), R10 + MOVQ R10, (CX) + JMP memmove_end_copy_match_emit_encodeSnappyBlockAsm12B + +emit_lit_memmove_match_emit_encodeSnappyBlockAsm12B_memmove_move_8through16: + MOVQ (DI), R10 + MOVQ -8(DI)(R9*1), DI + MOVQ R10, (CX) + MOVQ DI, -8(CX)(R9*1) + JMP memmove_end_copy_match_emit_encodeSnappyBlockAsm12B + +emit_lit_memmove_match_emit_encodeSnappyBlockAsm12B_memmove_move_17through32: + MOVOU (DI), X0 + MOVOU -16(DI)(R9*1), X1 + MOVOU X0, (CX) + MOVOU X1, -16(CX)(R9*1) + JMP memmove_end_copy_match_emit_encodeSnappyBlockAsm12B + +emit_lit_memmove_match_emit_encodeSnappyBlockAsm12B_memmove_move_33through64: + MOVOU (DI), X0 + MOVOU 16(DI), X1 + MOVOU -32(DI)(R9*1), X2 + MOVOU -16(DI)(R9*1), X3 + MOVOU X0, (CX) + MOVOU X1, 16(CX) + MOVOU X2, -32(CX)(R9*1) + MOVOU X3, -16(CX)(R9*1) + +memmove_end_copy_match_emit_encodeSnappyBlockAsm12B: + MOVQ R8, CX + JMP emit_literal_done_match_emit_encodeSnappyBlockAsm12B + +memmove_long_match_emit_encodeSnappyBlockAsm12B: + LEAQ (CX)(R9*1), R8 + + // genMemMoveLong + MOVOU (DI), X0 + MOVOU 16(DI), X1 + MOVOU -32(DI)(R9*1), X2 + MOVOU -16(DI)(R9*1), X3 + MOVQ R9, R11 + SHRQ $0x05, R11 + MOVQ CX, R10 + ANDL $0x0000001f, R10 + MOVQ $0x00000040, R12 + SUBQ R10, R12 + DECQ R11 + JA emit_lit_memmove_long_match_emit_encodeSnappyBlockAsm12Blarge_forward_sse_loop_32 + LEAQ -32(DI)(R12*1), R10 + LEAQ -32(CX)(R12*1), R13 + +emit_lit_memmove_long_match_emit_encodeSnappyBlockAsm12Blarge_big_loop_back: + MOVOU (R10), X4 + MOVOU 16(R10), X5 + MOVOA X4, (R13) + MOVOA X5, 16(R13) + ADDQ $0x20, R13 + ADDQ $0x20, R10 + ADDQ $0x20, R12 + DECQ R11 + JNA emit_lit_memmove_long_match_emit_encodeSnappyBlockAsm12Blarge_big_loop_back + +emit_lit_memmove_long_match_emit_encodeSnappyBlockAsm12Blarge_forward_sse_loop_32: + MOVOU -32(DI)(R12*1), X4 + MOVOU -16(DI)(R12*1), X5 + MOVOA X4, -32(CX)(R12*1) + MOVOA X5, -16(CX)(R12*1) + ADDQ $0x20, R12 + CMPQ R9, R12 + JAE emit_lit_memmove_long_match_emit_encodeSnappyBlockAsm12Blarge_forward_sse_loop_32 + MOVOU X0, (CX) + MOVOU X1, 16(CX) + MOVOU X2, -32(CX)(R9*1) + MOVOU X3, -16(CX)(R9*1) + MOVQ R8, CX + +emit_literal_done_match_emit_encodeSnappyBlockAsm12B: +match_nolit_loop_encodeSnappyBlockAsm12B: + MOVL DX, DI + SUBL SI, DI + MOVL DI, 16(SP) + ADDL $0x04, DX + ADDL $0x04, SI + MOVQ src_len+32(FP), DI + SUBL DX, DI + LEAQ (BX)(DX*1), R8 + LEAQ (BX)(SI*1), SI + + // matchLen + XORL R10, R10 + +matchlen_loopback_16_match_nolit_encodeSnappyBlockAsm12B: + CMPL DI, $0x10 + JB matchlen_match8_match_nolit_encodeSnappyBlockAsm12B + MOVQ (R8)(R10*1), R9 + MOVQ 8(R8)(R10*1), R11 + XORQ (SI)(R10*1), R9 + JNZ matchlen_bsf_8_match_nolit_encodeSnappyBlockAsm12B + XORQ 8(SI)(R10*1), R11 + JNZ matchlen_bsf_16match_nolit_encodeSnappyBlockAsm12B + LEAL -16(DI), DI + LEAL 16(R10), R10 + JMP matchlen_loopback_16_match_nolit_encodeSnappyBlockAsm12B + +matchlen_bsf_16match_nolit_encodeSnappyBlockAsm12B: +#ifdef GOAMD64_v3 + TZCNTQ R11, R11 + +#else + BSFQ R11, R11 + +#endif + SARQ $0x03, R11 + LEAL 8(R10)(R11*1), R10 + JMP match_nolit_end_encodeSnappyBlockAsm12B + +matchlen_match8_match_nolit_encodeSnappyBlockAsm12B: + CMPL DI, $0x08 + JB matchlen_match4_match_nolit_encodeSnappyBlockAsm12B + MOVQ (R8)(R10*1), R9 + XORQ (SI)(R10*1), R9 + JNZ matchlen_bsf_8_match_nolit_encodeSnappyBlockAsm12B + LEAL -8(DI), DI + LEAL 8(R10), R10 + JMP matchlen_match4_match_nolit_encodeSnappyBlockAsm12B + +matchlen_bsf_8_match_nolit_encodeSnappyBlockAsm12B: +#ifdef GOAMD64_v3 + TZCNTQ R9, R9 + +#else + BSFQ R9, R9 + +#endif + SARQ $0x03, R9 + LEAL (R10)(R9*1), R10 + JMP match_nolit_end_encodeSnappyBlockAsm12B + +matchlen_match4_match_nolit_encodeSnappyBlockAsm12B: + CMPL DI, $0x04 + JB matchlen_match2_match_nolit_encodeSnappyBlockAsm12B + MOVL (R8)(R10*1), R9 + CMPL (SI)(R10*1), R9 + JNE matchlen_match2_match_nolit_encodeSnappyBlockAsm12B + LEAL -4(DI), DI + LEAL 4(R10), R10 + +matchlen_match2_match_nolit_encodeSnappyBlockAsm12B: + CMPL DI, $0x01 + JE matchlen_match1_match_nolit_encodeSnappyBlockAsm12B + JB match_nolit_end_encodeSnappyBlockAsm12B + MOVW (R8)(R10*1), R9 + CMPW (SI)(R10*1), R9 + JNE matchlen_match1_match_nolit_encodeSnappyBlockAsm12B + LEAL 2(R10), R10 + SUBL $0x02, DI + JZ match_nolit_end_encodeSnappyBlockAsm12B + +matchlen_match1_match_nolit_encodeSnappyBlockAsm12B: + MOVB (R8)(R10*1), R9 + CMPB (SI)(R10*1), R9 + JNE match_nolit_end_encodeSnappyBlockAsm12B + LEAL 1(R10), R10 + +match_nolit_end_encodeSnappyBlockAsm12B: + ADDL R10, DX + MOVL 16(SP), SI + ADDL $0x04, R10 + MOVL DX, 12(SP) + + // emitCopy +two_byte_offset_match_nolit_encodeSnappyBlockAsm12B: + CMPL R10, $0x40 + JBE two_byte_offset_short_match_nolit_encodeSnappyBlockAsm12B + MOVB $0xee, (CX) + MOVW SI, 1(CX) + LEAL -60(R10), R10 + ADDQ $0x03, CX + JMP two_byte_offset_match_nolit_encodeSnappyBlockAsm12B + +two_byte_offset_short_match_nolit_encodeSnappyBlockAsm12B: + MOVL R10, DI + SHLL $0x02, DI + CMPL R10, $0x0c + JAE emit_copy_three_match_nolit_encodeSnappyBlockAsm12B + CMPL SI, $0x00000800 + JAE emit_copy_three_match_nolit_encodeSnappyBlockAsm12B + LEAL -15(DI), DI + MOVB SI, 1(CX) + SHRL $0x08, SI + SHLL $0x05, SI + ORL SI, DI + MOVB DI, (CX) + ADDQ $0x02, CX + JMP match_nolit_emitcopy_end_encodeSnappyBlockAsm12B + +emit_copy_three_match_nolit_encodeSnappyBlockAsm12B: + LEAL -2(DI), DI + MOVB DI, (CX) + MOVW SI, 1(CX) + ADDQ $0x03, CX + +match_nolit_emitcopy_end_encodeSnappyBlockAsm12B: + CMPL DX, 8(SP) + JAE emit_remainder_encodeSnappyBlockAsm12B + MOVQ -2(BX)(DX*1), DI + CMPQ CX, (SP) + JB match_nolit_dst_ok_encodeSnappyBlockAsm12B + MOVQ $0x00000000, ret+56(FP) + RET + +match_nolit_dst_ok_encodeSnappyBlockAsm12B: + MOVQ $0x000000cf1bbcdcbb, R9 + MOVQ DI, R8 + SHRQ $0x10, DI + MOVQ DI, SI + SHLQ $0x18, R8 + IMULQ R9, R8 + SHRQ $0x34, R8 + SHLQ $0x18, SI + IMULQ R9, SI + SHRQ $0x34, SI + LEAL -2(DX), R9 + LEAQ (AX)(SI*4), R10 + MOVL (R10), SI + MOVL R9, (AX)(R8*4) + MOVL DX, (R10) + CMPL (BX)(SI*1), DI + JEQ match_nolit_loop_encodeSnappyBlockAsm12B + INCL DX + JMP search_loop_encodeSnappyBlockAsm12B + +emit_remainder_encodeSnappyBlockAsm12B: + MOVQ src_len+32(FP), AX + SUBL 12(SP), AX + LEAQ 3(CX)(AX*1), AX + CMPQ AX, (SP) + JB emit_remainder_ok_encodeSnappyBlockAsm12B + MOVQ $0x00000000, ret+56(FP) + RET + +emit_remainder_ok_encodeSnappyBlockAsm12B: + MOVQ src_len+32(FP), AX + MOVL 12(SP), DX + CMPL DX, AX + JEQ emit_literal_done_emit_remainder_encodeSnappyBlockAsm12B + MOVL AX, SI + MOVL AX, 12(SP) + LEAQ (BX)(DX*1), AX + SUBL DX, SI + LEAL -1(SI), DX + CMPL DX, $0x3c + JB one_byte_emit_remainder_encodeSnappyBlockAsm12B + CMPL DX, $0x00000100 + JB two_bytes_emit_remainder_encodeSnappyBlockAsm12B + JB three_bytes_emit_remainder_encodeSnappyBlockAsm12B + +three_bytes_emit_remainder_encodeSnappyBlockAsm12B: + MOVB $0xf4, (CX) + MOVW DX, 1(CX) + ADDQ $0x03, CX + JMP memmove_long_emit_remainder_encodeSnappyBlockAsm12B + +two_bytes_emit_remainder_encodeSnappyBlockAsm12B: + MOVB $0xf0, (CX) + MOVB DL, 1(CX) + ADDQ $0x02, CX + CMPL DX, $0x40 + JB memmove_emit_remainder_encodeSnappyBlockAsm12B + JMP memmove_long_emit_remainder_encodeSnappyBlockAsm12B + +one_byte_emit_remainder_encodeSnappyBlockAsm12B: + SHLB $0x02, DL + MOVB DL, (CX) + ADDQ $0x01, CX + +memmove_emit_remainder_encodeSnappyBlockAsm12B: + LEAQ (CX)(SI*1), DX + MOVL SI, BX + + // genMemMoveShort + CMPQ BX, $0x03 + JB emit_lit_memmove_emit_remainder_encodeSnappyBlockAsm12B_memmove_move_1or2 + JE emit_lit_memmove_emit_remainder_encodeSnappyBlockAsm12B_memmove_move_3 + CMPQ BX, $0x08 + JB emit_lit_memmove_emit_remainder_encodeSnappyBlockAsm12B_memmove_move_4through7 + CMPQ BX, $0x10 + JBE emit_lit_memmove_emit_remainder_encodeSnappyBlockAsm12B_memmove_move_8through16 + CMPQ BX, $0x20 + JBE emit_lit_memmove_emit_remainder_encodeSnappyBlockAsm12B_memmove_move_17through32 + JMP emit_lit_memmove_emit_remainder_encodeSnappyBlockAsm12B_memmove_move_33through64 + +emit_lit_memmove_emit_remainder_encodeSnappyBlockAsm12B_memmove_move_1or2: + MOVB (AX), SI + MOVB -1(AX)(BX*1), AL + MOVB SI, (CX) + MOVB AL, -1(CX)(BX*1) + JMP memmove_end_copy_emit_remainder_encodeSnappyBlockAsm12B + +emit_lit_memmove_emit_remainder_encodeSnappyBlockAsm12B_memmove_move_3: + MOVW (AX), SI + MOVB 2(AX), AL + MOVW SI, (CX) + MOVB AL, 2(CX) + JMP memmove_end_copy_emit_remainder_encodeSnappyBlockAsm12B + +emit_lit_memmove_emit_remainder_encodeSnappyBlockAsm12B_memmove_move_4through7: + MOVL (AX), SI + MOVL -4(AX)(BX*1), AX + MOVL SI, (CX) + MOVL AX, -4(CX)(BX*1) + JMP memmove_end_copy_emit_remainder_encodeSnappyBlockAsm12B + +emit_lit_memmove_emit_remainder_encodeSnappyBlockAsm12B_memmove_move_8through16: + MOVQ (AX), SI + MOVQ -8(AX)(BX*1), AX + MOVQ SI, (CX) + MOVQ AX, -8(CX)(BX*1) + JMP memmove_end_copy_emit_remainder_encodeSnappyBlockAsm12B + +emit_lit_memmove_emit_remainder_encodeSnappyBlockAsm12B_memmove_move_17through32: + MOVOU (AX), X0 + MOVOU -16(AX)(BX*1), X1 + MOVOU X0, (CX) + MOVOU X1, -16(CX)(BX*1) + JMP memmove_end_copy_emit_remainder_encodeSnappyBlockAsm12B + +emit_lit_memmove_emit_remainder_encodeSnappyBlockAsm12B_memmove_move_33through64: + MOVOU (AX), X0 + MOVOU 16(AX), X1 + MOVOU -32(AX)(BX*1), X2 + MOVOU -16(AX)(BX*1), X3 + MOVOU X0, (CX) + MOVOU X1, 16(CX) + MOVOU X2, -32(CX)(BX*1) + MOVOU X3, -16(CX)(BX*1) + +memmove_end_copy_emit_remainder_encodeSnappyBlockAsm12B: + MOVQ DX, CX + JMP emit_literal_done_emit_remainder_encodeSnappyBlockAsm12B + +memmove_long_emit_remainder_encodeSnappyBlockAsm12B: + LEAQ (CX)(SI*1), DX + MOVL SI, BX + + // genMemMoveLong + MOVOU (AX), X0 + MOVOU 16(AX), X1 + MOVOU -32(AX)(BX*1), X2 + MOVOU -16(AX)(BX*1), X3 + MOVQ BX, DI + SHRQ $0x05, DI + MOVQ CX, SI + ANDL $0x0000001f, SI + MOVQ $0x00000040, R8 + SUBQ SI, R8 + DECQ DI + JA emit_lit_memmove_long_emit_remainder_encodeSnappyBlockAsm12Blarge_forward_sse_loop_32 + LEAQ -32(AX)(R8*1), SI + LEAQ -32(CX)(R8*1), R9 + +emit_lit_memmove_long_emit_remainder_encodeSnappyBlockAsm12Blarge_big_loop_back: + MOVOU (SI), X4 + MOVOU 16(SI), X5 + MOVOA X4, (R9) + MOVOA X5, 16(R9) + ADDQ $0x20, R9 + ADDQ $0x20, SI + ADDQ $0x20, R8 + DECQ DI + JNA emit_lit_memmove_long_emit_remainder_encodeSnappyBlockAsm12Blarge_big_loop_back + +emit_lit_memmove_long_emit_remainder_encodeSnappyBlockAsm12Blarge_forward_sse_loop_32: + MOVOU -32(AX)(R8*1), X4 + MOVOU -16(AX)(R8*1), X5 + MOVOA X4, -32(CX)(R8*1) + MOVOA X5, -16(CX)(R8*1) + ADDQ $0x20, R8 + CMPQ BX, R8 + JAE emit_lit_memmove_long_emit_remainder_encodeSnappyBlockAsm12Blarge_forward_sse_loop_32 + MOVOU X0, (CX) + MOVOU X1, 16(CX) + MOVOU X2, -32(CX)(BX*1) + MOVOU X3, -16(CX)(BX*1) + MOVQ DX, CX + +emit_literal_done_emit_remainder_encodeSnappyBlockAsm12B: + MOVQ dst_base+0(FP), AX + SUBQ AX, CX + MOVQ CX, ret+56(FP) + RET + +// func encodeSnappyBlockAsm10B(dst []byte, src []byte, tmp *[4096]byte) int +// Requires: BMI, SSE2 +TEXT ·encodeSnappyBlockAsm10B(SB), $24-64 + MOVQ tmp+48(FP), AX + MOVQ dst_base+0(FP), CX + MOVQ $0x00000020, DX + MOVQ AX, BX + PXOR X0, X0 + +zero_loop_encodeSnappyBlockAsm10B: + MOVOU X0, (BX) + MOVOU X0, 16(BX) + MOVOU X0, 32(BX) + MOVOU X0, 48(BX) + MOVOU X0, 64(BX) + MOVOU X0, 80(BX) + MOVOU X0, 96(BX) + MOVOU X0, 112(BX) + ADDQ $0x80, BX + DECQ DX + JNZ zero_loop_encodeSnappyBlockAsm10B + MOVL $0x00000000, 12(SP) + MOVQ src_len+32(FP), DX + LEAQ -9(DX), BX + LEAQ -8(DX), SI + MOVL SI, 8(SP) + SHRQ $0x05, DX + SUBL DX, BX + LEAQ (CX)(BX*1), BX + MOVQ BX, (SP) + MOVL $0x00000001, DX + MOVL DX, 16(SP) + MOVQ src_base+24(FP), BX + +search_loop_encodeSnappyBlockAsm10B: + MOVL DX, SI + SUBL 12(SP), SI + SHRL $0x05, SI + LEAL 4(DX)(SI*1), SI + CMPL SI, 8(SP) + JAE emit_remainder_encodeSnappyBlockAsm10B + MOVQ (BX)(DX*1), DI + MOVL SI, 20(SP) + MOVQ $0x9e3779b1, R9 + MOVQ DI, R10 + MOVQ DI, R11 + SHRQ $0x08, R11 + SHLQ $0x20, R10 + IMULQ R9, R10 + SHRQ $0x36, R10 + SHLQ $0x20, R11 + IMULQ R9, R11 + SHRQ $0x36, R11 + MOVL (AX)(R10*4), SI + MOVL (AX)(R11*4), R8 + MOVL DX, (AX)(R10*4) + LEAL 1(DX), R10 + MOVL R10, (AX)(R11*4) + MOVQ DI, R10 + SHRQ $0x10, R10 + SHLQ $0x20, R10 + IMULQ R9, R10 + SHRQ $0x36, R10 + MOVL DX, R9 + SUBL 16(SP), R9 + MOVL 1(BX)(R9*1), R11 + MOVQ DI, R9 + SHRQ $0x08, R9 + CMPL R9, R11 + JNE no_repeat_found_encodeSnappyBlockAsm10B + LEAL 1(DX), DI + MOVL 12(SP), SI + MOVL DI, R8 + SUBL 16(SP), R8 + JZ repeat_extend_back_end_encodeSnappyBlockAsm10B + +repeat_extend_back_loop_encodeSnappyBlockAsm10B: + CMPL DI, SI + JBE repeat_extend_back_end_encodeSnappyBlockAsm10B + MOVB -1(BX)(R8*1), R9 + MOVB -1(BX)(DI*1), R10 + CMPB R9, R10 + JNE repeat_extend_back_end_encodeSnappyBlockAsm10B + LEAL -1(DI), DI + DECL R8 + JNZ repeat_extend_back_loop_encodeSnappyBlockAsm10B + +repeat_extend_back_end_encodeSnappyBlockAsm10B: + MOVL DI, SI + SUBL 12(SP), SI + LEAQ 3(CX)(SI*1), SI + CMPQ SI, (SP) + JB repeat_dst_size_check_encodeSnappyBlockAsm10B + MOVQ $0x00000000, ret+56(FP) + RET + +repeat_dst_size_check_encodeSnappyBlockAsm10B: + MOVL 12(SP), SI + CMPL SI, DI + JEQ emit_literal_done_repeat_emit_encodeSnappyBlockAsm10B + MOVL DI, R8 + MOVL DI, 12(SP) + LEAQ (BX)(SI*1), R9 + SUBL SI, R8 + LEAL -1(R8), SI + CMPL SI, $0x3c + JB one_byte_repeat_emit_encodeSnappyBlockAsm10B + CMPL SI, $0x00000100 + JB two_bytes_repeat_emit_encodeSnappyBlockAsm10B + JB three_bytes_repeat_emit_encodeSnappyBlockAsm10B + +three_bytes_repeat_emit_encodeSnappyBlockAsm10B: + MOVB $0xf4, (CX) + MOVW SI, 1(CX) + ADDQ $0x03, CX + JMP memmove_long_repeat_emit_encodeSnappyBlockAsm10B + +two_bytes_repeat_emit_encodeSnappyBlockAsm10B: + MOVB $0xf0, (CX) + MOVB SI, 1(CX) + ADDQ $0x02, CX + CMPL SI, $0x40 + JB memmove_repeat_emit_encodeSnappyBlockAsm10B + JMP memmove_long_repeat_emit_encodeSnappyBlockAsm10B + +one_byte_repeat_emit_encodeSnappyBlockAsm10B: + SHLB $0x02, SI + MOVB SI, (CX) + ADDQ $0x01, CX + +memmove_repeat_emit_encodeSnappyBlockAsm10B: + LEAQ (CX)(R8*1), SI + + // genMemMoveShort + CMPQ R8, $0x08 + JBE emit_lit_memmove_repeat_emit_encodeSnappyBlockAsm10B_memmove_move_8 + CMPQ R8, $0x10 + JBE emit_lit_memmove_repeat_emit_encodeSnappyBlockAsm10B_memmove_move_8through16 + CMPQ R8, $0x20 + JBE emit_lit_memmove_repeat_emit_encodeSnappyBlockAsm10B_memmove_move_17through32 + JMP emit_lit_memmove_repeat_emit_encodeSnappyBlockAsm10B_memmove_move_33through64 + +emit_lit_memmove_repeat_emit_encodeSnappyBlockAsm10B_memmove_move_8: + MOVQ (R9), R10 + MOVQ R10, (CX) + JMP memmove_end_copy_repeat_emit_encodeSnappyBlockAsm10B + +emit_lit_memmove_repeat_emit_encodeSnappyBlockAsm10B_memmove_move_8through16: + MOVQ (R9), R10 + MOVQ -8(R9)(R8*1), R9 + MOVQ R10, (CX) + MOVQ R9, -8(CX)(R8*1) + JMP memmove_end_copy_repeat_emit_encodeSnappyBlockAsm10B + +emit_lit_memmove_repeat_emit_encodeSnappyBlockAsm10B_memmove_move_17through32: + MOVOU (R9), X0 + MOVOU -16(R9)(R8*1), X1 + MOVOU X0, (CX) + MOVOU X1, -16(CX)(R8*1) + JMP memmove_end_copy_repeat_emit_encodeSnappyBlockAsm10B + +emit_lit_memmove_repeat_emit_encodeSnappyBlockAsm10B_memmove_move_33through64: + MOVOU (R9), X0 + MOVOU 16(R9), X1 + MOVOU -32(R9)(R8*1), X2 + MOVOU -16(R9)(R8*1), X3 + MOVOU X0, (CX) + MOVOU X1, 16(CX) + MOVOU X2, -32(CX)(R8*1) + MOVOU X3, -16(CX)(R8*1) + +memmove_end_copy_repeat_emit_encodeSnappyBlockAsm10B: + MOVQ SI, CX + JMP emit_literal_done_repeat_emit_encodeSnappyBlockAsm10B + +memmove_long_repeat_emit_encodeSnappyBlockAsm10B: + LEAQ (CX)(R8*1), SI + + // genMemMoveLong + MOVOU (R9), X0 + MOVOU 16(R9), X1 + MOVOU -32(R9)(R8*1), X2 + MOVOU -16(R9)(R8*1), X3 + MOVQ R8, R11 + SHRQ $0x05, R11 + MOVQ CX, R10 + ANDL $0x0000001f, R10 + MOVQ $0x00000040, R12 + SUBQ R10, R12 + DECQ R11 + JA emit_lit_memmove_long_repeat_emit_encodeSnappyBlockAsm10Blarge_forward_sse_loop_32 + LEAQ -32(R9)(R12*1), R10 + LEAQ -32(CX)(R12*1), R13 + +emit_lit_memmove_long_repeat_emit_encodeSnappyBlockAsm10Blarge_big_loop_back: + MOVOU (R10), X4 + MOVOU 16(R10), X5 + MOVOA X4, (R13) + MOVOA X5, 16(R13) + ADDQ $0x20, R13 + ADDQ $0x20, R10 + ADDQ $0x20, R12 + DECQ R11 + JNA emit_lit_memmove_long_repeat_emit_encodeSnappyBlockAsm10Blarge_big_loop_back + +emit_lit_memmove_long_repeat_emit_encodeSnappyBlockAsm10Blarge_forward_sse_loop_32: + MOVOU -32(R9)(R12*1), X4 + MOVOU -16(R9)(R12*1), X5 + MOVOA X4, -32(CX)(R12*1) + MOVOA X5, -16(CX)(R12*1) + ADDQ $0x20, R12 + CMPQ R8, R12 + JAE emit_lit_memmove_long_repeat_emit_encodeSnappyBlockAsm10Blarge_forward_sse_loop_32 + MOVOU X0, (CX) + MOVOU X1, 16(CX) + MOVOU X2, -32(CX)(R8*1) + MOVOU X3, -16(CX)(R8*1) + MOVQ SI, CX + +emit_literal_done_repeat_emit_encodeSnappyBlockAsm10B: + ADDL $0x05, DX + MOVL DX, SI + SUBL 16(SP), SI + MOVQ src_len+32(FP), R8 + SUBL DX, R8 + LEAQ (BX)(DX*1), R9 + LEAQ (BX)(SI*1), SI + + // matchLen + XORL R11, R11 + +matchlen_loopback_16_repeat_extend_encodeSnappyBlockAsm10B: + CMPL R8, $0x10 + JB matchlen_match8_repeat_extend_encodeSnappyBlockAsm10B + MOVQ (R9)(R11*1), R10 + MOVQ 8(R9)(R11*1), R12 + XORQ (SI)(R11*1), R10 + JNZ matchlen_bsf_8_repeat_extend_encodeSnappyBlockAsm10B + XORQ 8(SI)(R11*1), R12 + JNZ matchlen_bsf_16repeat_extend_encodeSnappyBlockAsm10B + LEAL -16(R8), R8 + LEAL 16(R11), R11 + JMP matchlen_loopback_16_repeat_extend_encodeSnappyBlockAsm10B + +matchlen_bsf_16repeat_extend_encodeSnappyBlockAsm10B: +#ifdef GOAMD64_v3 + TZCNTQ R12, R12 + +#else + BSFQ R12, R12 + +#endif + SARQ $0x03, R12 + LEAL 8(R11)(R12*1), R11 + JMP repeat_extend_forward_end_encodeSnappyBlockAsm10B + +matchlen_match8_repeat_extend_encodeSnappyBlockAsm10B: + CMPL R8, $0x08 + JB matchlen_match4_repeat_extend_encodeSnappyBlockAsm10B + MOVQ (R9)(R11*1), R10 + XORQ (SI)(R11*1), R10 + JNZ matchlen_bsf_8_repeat_extend_encodeSnappyBlockAsm10B + LEAL -8(R8), R8 + LEAL 8(R11), R11 + JMP matchlen_match4_repeat_extend_encodeSnappyBlockAsm10B + +matchlen_bsf_8_repeat_extend_encodeSnappyBlockAsm10B: +#ifdef GOAMD64_v3 + TZCNTQ R10, R10 + +#else + BSFQ R10, R10 + +#endif + SARQ $0x03, R10 + LEAL (R11)(R10*1), R11 + JMP repeat_extend_forward_end_encodeSnappyBlockAsm10B + +matchlen_match4_repeat_extend_encodeSnappyBlockAsm10B: + CMPL R8, $0x04 + JB matchlen_match2_repeat_extend_encodeSnappyBlockAsm10B + MOVL (R9)(R11*1), R10 + CMPL (SI)(R11*1), R10 + JNE matchlen_match2_repeat_extend_encodeSnappyBlockAsm10B + LEAL -4(R8), R8 + LEAL 4(R11), R11 + +matchlen_match2_repeat_extend_encodeSnappyBlockAsm10B: + CMPL R8, $0x01 + JE matchlen_match1_repeat_extend_encodeSnappyBlockAsm10B + JB repeat_extend_forward_end_encodeSnappyBlockAsm10B + MOVW (R9)(R11*1), R10 + CMPW (SI)(R11*1), R10 + JNE matchlen_match1_repeat_extend_encodeSnappyBlockAsm10B + LEAL 2(R11), R11 + SUBL $0x02, R8 + JZ repeat_extend_forward_end_encodeSnappyBlockAsm10B + +matchlen_match1_repeat_extend_encodeSnappyBlockAsm10B: + MOVB (R9)(R11*1), R10 + CMPB (SI)(R11*1), R10 + JNE repeat_extend_forward_end_encodeSnappyBlockAsm10B + LEAL 1(R11), R11 + +repeat_extend_forward_end_encodeSnappyBlockAsm10B: + ADDL R11, DX + MOVL DX, SI + SUBL DI, SI + MOVL 16(SP), DI + + // emitCopy +two_byte_offset_repeat_as_copy_encodeSnappyBlockAsm10B: + CMPL SI, $0x40 + JBE two_byte_offset_short_repeat_as_copy_encodeSnappyBlockAsm10B + MOVB $0xee, (CX) + MOVW DI, 1(CX) + LEAL -60(SI), SI + ADDQ $0x03, CX + JMP two_byte_offset_repeat_as_copy_encodeSnappyBlockAsm10B + +two_byte_offset_short_repeat_as_copy_encodeSnappyBlockAsm10B: + MOVL SI, R8 + SHLL $0x02, R8 + CMPL SI, $0x0c + JAE emit_copy_three_repeat_as_copy_encodeSnappyBlockAsm10B + CMPL DI, $0x00000800 + JAE emit_copy_three_repeat_as_copy_encodeSnappyBlockAsm10B + LEAL -15(R8), R8 + MOVB DI, 1(CX) + SHRL $0x08, DI + SHLL $0x05, DI + ORL DI, R8 + MOVB R8, (CX) + ADDQ $0x02, CX + JMP repeat_end_emit_encodeSnappyBlockAsm10B + +emit_copy_three_repeat_as_copy_encodeSnappyBlockAsm10B: + LEAL -2(R8), R8 + MOVB R8, (CX) + MOVW DI, 1(CX) + ADDQ $0x03, CX + +repeat_end_emit_encodeSnappyBlockAsm10B: + MOVL DX, 12(SP) + JMP search_loop_encodeSnappyBlockAsm10B + +no_repeat_found_encodeSnappyBlockAsm10B: + CMPL (BX)(SI*1), DI + JEQ candidate_match_encodeSnappyBlockAsm10B + SHRQ $0x08, DI + MOVL (AX)(R10*4), SI + LEAL 2(DX), R9 + CMPL (BX)(R8*1), DI + JEQ candidate2_match_encodeSnappyBlockAsm10B + MOVL R9, (AX)(R10*4) + SHRQ $0x08, DI + CMPL (BX)(SI*1), DI + JEQ candidate3_match_encodeSnappyBlockAsm10B + MOVL 20(SP), DX + JMP search_loop_encodeSnappyBlockAsm10B + +candidate3_match_encodeSnappyBlockAsm10B: + ADDL $0x02, DX + JMP candidate_match_encodeSnappyBlockAsm10B + +candidate2_match_encodeSnappyBlockAsm10B: + MOVL R9, (AX)(R10*4) + INCL DX + MOVL R8, SI + +candidate_match_encodeSnappyBlockAsm10B: + MOVL 12(SP), DI + TESTL SI, SI + JZ match_extend_back_end_encodeSnappyBlockAsm10B + +match_extend_back_loop_encodeSnappyBlockAsm10B: + CMPL DX, DI + JBE match_extend_back_end_encodeSnappyBlockAsm10B + MOVB -1(BX)(SI*1), R8 + MOVB -1(BX)(DX*1), R9 + CMPB R8, R9 + JNE match_extend_back_end_encodeSnappyBlockAsm10B + LEAL -1(DX), DX + DECL SI + JZ match_extend_back_end_encodeSnappyBlockAsm10B + JMP match_extend_back_loop_encodeSnappyBlockAsm10B + +match_extend_back_end_encodeSnappyBlockAsm10B: + MOVL DX, DI + SUBL 12(SP), DI + LEAQ 3(CX)(DI*1), DI + CMPQ DI, (SP) + JB match_dst_size_check_encodeSnappyBlockAsm10B + MOVQ $0x00000000, ret+56(FP) + RET + +match_dst_size_check_encodeSnappyBlockAsm10B: + MOVL DX, DI + MOVL 12(SP), R8 + CMPL R8, DI + JEQ emit_literal_done_match_emit_encodeSnappyBlockAsm10B + MOVL DI, R9 + MOVL DI, 12(SP) + LEAQ (BX)(R8*1), DI + SUBL R8, R9 + LEAL -1(R9), R8 + CMPL R8, $0x3c + JB one_byte_match_emit_encodeSnappyBlockAsm10B + CMPL R8, $0x00000100 + JB two_bytes_match_emit_encodeSnappyBlockAsm10B + JB three_bytes_match_emit_encodeSnappyBlockAsm10B + +three_bytes_match_emit_encodeSnappyBlockAsm10B: + MOVB $0xf4, (CX) + MOVW R8, 1(CX) + ADDQ $0x03, CX + JMP memmove_long_match_emit_encodeSnappyBlockAsm10B + +two_bytes_match_emit_encodeSnappyBlockAsm10B: + MOVB $0xf0, (CX) + MOVB R8, 1(CX) + ADDQ $0x02, CX + CMPL R8, $0x40 + JB memmove_match_emit_encodeSnappyBlockAsm10B + JMP memmove_long_match_emit_encodeSnappyBlockAsm10B + +one_byte_match_emit_encodeSnappyBlockAsm10B: + SHLB $0x02, R8 + MOVB R8, (CX) + ADDQ $0x01, CX + +memmove_match_emit_encodeSnappyBlockAsm10B: + LEAQ (CX)(R9*1), R8 + + // genMemMoveShort + CMPQ R9, $0x08 + JBE emit_lit_memmove_match_emit_encodeSnappyBlockAsm10B_memmove_move_8 + CMPQ R9, $0x10 + JBE emit_lit_memmove_match_emit_encodeSnappyBlockAsm10B_memmove_move_8through16 + CMPQ R9, $0x20 + JBE emit_lit_memmove_match_emit_encodeSnappyBlockAsm10B_memmove_move_17through32 + JMP emit_lit_memmove_match_emit_encodeSnappyBlockAsm10B_memmove_move_33through64 + +emit_lit_memmove_match_emit_encodeSnappyBlockAsm10B_memmove_move_8: + MOVQ (DI), R10 + MOVQ R10, (CX) + JMP memmove_end_copy_match_emit_encodeSnappyBlockAsm10B + +emit_lit_memmove_match_emit_encodeSnappyBlockAsm10B_memmove_move_8through16: + MOVQ (DI), R10 + MOVQ -8(DI)(R9*1), DI + MOVQ R10, (CX) + MOVQ DI, -8(CX)(R9*1) + JMP memmove_end_copy_match_emit_encodeSnappyBlockAsm10B + +emit_lit_memmove_match_emit_encodeSnappyBlockAsm10B_memmove_move_17through32: + MOVOU (DI), X0 + MOVOU -16(DI)(R9*1), X1 + MOVOU X0, (CX) + MOVOU X1, -16(CX)(R9*1) + JMP memmove_end_copy_match_emit_encodeSnappyBlockAsm10B + +emit_lit_memmove_match_emit_encodeSnappyBlockAsm10B_memmove_move_33through64: + MOVOU (DI), X0 + MOVOU 16(DI), X1 + MOVOU -32(DI)(R9*1), X2 + MOVOU -16(DI)(R9*1), X3 + MOVOU X0, (CX) + MOVOU X1, 16(CX) + MOVOU X2, -32(CX)(R9*1) + MOVOU X3, -16(CX)(R9*1) + +memmove_end_copy_match_emit_encodeSnappyBlockAsm10B: + MOVQ R8, CX + JMP emit_literal_done_match_emit_encodeSnappyBlockAsm10B + +memmove_long_match_emit_encodeSnappyBlockAsm10B: + LEAQ (CX)(R9*1), R8 + + // genMemMoveLong + MOVOU (DI), X0 + MOVOU 16(DI), X1 + MOVOU -32(DI)(R9*1), X2 + MOVOU -16(DI)(R9*1), X3 + MOVQ R9, R11 + SHRQ $0x05, R11 + MOVQ CX, R10 + ANDL $0x0000001f, R10 + MOVQ $0x00000040, R12 + SUBQ R10, R12 + DECQ R11 + JA emit_lit_memmove_long_match_emit_encodeSnappyBlockAsm10Blarge_forward_sse_loop_32 + LEAQ -32(DI)(R12*1), R10 + LEAQ -32(CX)(R12*1), R13 + +emit_lit_memmove_long_match_emit_encodeSnappyBlockAsm10Blarge_big_loop_back: + MOVOU (R10), X4 + MOVOU 16(R10), X5 + MOVOA X4, (R13) + MOVOA X5, 16(R13) + ADDQ $0x20, R13 + ADDQ $0x20, R10 + ADDQ $0x20, R12 + DECQ R11 + JNA emit_lit_memmove_long_match_emit_encodeSnappyBlockAsm10Blarge_big_loop_back + +emit_lit_memmove_long_match_emit_encodeSnappyBlockAsm10Blarge_forward_sse_loop_32: + MOVOU -32(DI)(R12*1), X4 + MOVOU -16(DI)(R12*1), X5 + MOVOA X4, -32(CX)(R12*1) + MOVOA X5, -16(CX)(R12*1) + ADDQ $0x20, R12 + CMPQ R9, R12 + JAE emit_lit_memmove_long_match_emit_encodeSnappyBlockAsm10Blarge_forward_sse_loop_32 + MOVOU X0, (CX) + MOVOU X1, 16(CX) + MOVOU X2, -32(CX)(R9*1) + MOVOU X3, -16(CX)(R9*1) + MOVQ R8, CX + +emit_literal_done_match_emit_encodeSnappyBlockAsm10B: +match_nolit_loop_encodeSnappyBlockAsm10B: + MOVL DX, DI + SUBL SI, DI + MOVL DI, 16(SP) + ADDL $0x04, DX + ADDL $0x04, SI + MOVQ src_len+32(FP), DI + SUBL DX, DI + LEAQ (BX)(DX*1), R8 + LEAQ (BX)(SI*1), SI + + // matchLen + XORL R10, R10 + +matchlen_loopback_16_match_nolit_encodeSnappyBlockAsm10B: + CMPL DI, $0x10 + JB matchlen_match8_match_nolit_encodeSnappyBlockAsm10B + MOVQ (R8)(R10*1), R9 + MOVQ 8(R8)(R10*1), R11 + XORQ (SI)(R10*1), R9 + JNZ matchlen_bsf_8_match_nolit_encodeSnappyBlockAsm10B + XORQ 8(SI)(R10*1), R11 + JNZ matchlen_bsf_16match_nolit_encodeSnappyBlockAsm10B + LEAL -16(DI), DI + LEAL 16(R10), R10 + JMP matchlen_loopback_16_match_nolit_encodeSnappyBlockAsm10B + +matchlen_bsf_16match_nolit_encodeSnappyBlockAsm10B: +#ifdef GOAMD64_v3 + TZCNTQ R11, R11 + +#else + BSFQ R11, R11 + +#endif + SARQ $0x03, R11 + LEAL 8(R10)(R11*1), R10 + JMP match_nolit_end_encodeSnappyBlockAsm10B + +matchlen_match8_match_nolit_encodeSnappyBlockAsm10B: + CMPL DI, $0x08 + JB matchlen_match4_match_nolit_encodeSnappyBlockAsm10B + MOVQ (R8)(R10*1), R9 + XORQ (SI)(R10*1), R9 + JNZ matchlen_bsf_8_match_nolit_encodeSnappyBlockAsm10B + LEAL -8(DI), DI + LEAL 8(R10), R10 + JMP matchlen_match4_match_nolit_encodeSnappyBlockAsm10B + +matchlen_bsf_8_match_nolit_encodeSnappyBlockAsm10B: +#ifdef GOAMD64_v3 + TZCNTQ R9, R9 + +#else + BSFQ R9, R9 + +#endif + SARQ $0x03, R9 + LEAL (R10)(R9*1), R10 + JMP match_nolit_end_encodeSnappyBlockAsm10B + +matchlen_match4_match_nolit_encodeSnappyBlockAsm10B: + CMPL DI, $0x04 + JB matchlen_match2_match_nolit_encodeSnappyBlockAsm10B + MOVL (R8)(R10*1), R9 + CMPL (SI)(R10*1), R9 + JNE matchlen_match2_match_nolit_encodeSnappyBlockAsm10B + LEAL -4(DI), DI + LEAL 4(R10), R10 + +matchlen_match2_match_nolit_encodeSnappyBlockAsm10B: + CMPL DI, $0x01 + JE matchlen_match1_match_nolit_encodeSnappyBlockAsm10B + JB match_nolit_end_encodeSnappyBlockAsm10B + MOVW (R8)(R10*1), R9 + CMPW (SI)(R10*1), R9 + JNE matchlen_match1_match_nolit_encodeSnappyBlockAsm10B + LEAL 2(R10), R10 + SUBL $0x02, DI + JZ match_nolit_end_encodeSnappyBlockAsm10B + +matchlen_match1_match_nolit_encodeSnappyBlockAsm10B: + MOVB (R8)(R10*1), R9 + CMPB (SI)(R10*1), R9 + JNE match_nolit_end_encodeSnappyBlockAsm10B + LEAL 1(R10), R10 + +match_nolit_end_encodeSnappyBlockAsm10B: + ADDL R10, DX + MOVL 16(SP), SI + ADDL $0x04, R10 + MOVL DX, 12(SP) + + // emitCopy +two_byte_offset_match_nolit_encodeSnappyBlockAsm10B: + CMPL R10, $0x40 + JBE two_byte_offset_short_match_nolit_encodeSnappyBlockAsm10B + MOVB $0xee, (CX) + MOVW SI, 1(CX) + LEAL -60(R10), R10 + ADDQ $0x03, CX + JMP two_byte_offset_match_nolit_encodeSnappyBlockAsm10B + +two_byte_offset_short_match_nolit_encodeSnappyBlockAsm10B: + MOVL R10, DI + SHLL $0x02, DI + CMPL R10, $0x0c + JAE emit_copy_three_match_nolit_encodeSnappyBlockAsm10B + CMPL SI, $0x00000800 + JAE emit_copy_three_match_nolit_encodeSnappyBlockAsm10B + LEAL -15(DI), DI + MOVB SI, 1(CX) + SHRL $0x08, SI + SHLL $0x05, SI + ORL SI, DI + MOVB DI, (CX) + ADDQ $0x02, CX + JMP match_nolit_emitcopy_end_encodeSnappyBlockAsm10B + +emit_copy_three_match_nolit_encodeSnappyBlockAsm10B: + LEAL -2(DI), DI + MOVB DI, (CX) + MOVW SI, 1(CX) + ADDQ $0x03, CX + +match_nolit_emitcopy_end_encodeSnappyBlockAsm10B: + CMPL DX, 8(SP) + JAE emit_remainder_encodeSnappyBlockAsm10B + MOVQ -2(BX)(DX*1), DI + CMPQ CX, (SP) + JB match_nolit_dst_ok_encodeSnappyBlockAsm10B + MOVQ $0x00000000, ret+56(FP) + RET + +match_nolit_dst_ok_encodeSnappyBlockAsm10B: + MOVQ $0x9e3779b1, R9 + MOVQ DI, R8 + SHRQ $0x10, DI + MOVQ DI, SI + SHLQ $0x20, R8 + IMULQ R9, R8 + SHRQ $0x36, R8 + SHLQ $0x20, SI + IMULQ R9, SI + SHRQ $0x36, SI + LEAL -2(DX), R9 + LEAQ (AX)(SI*4), R10 + MOVL (R10), SI + MOVL R9, (AX)(R8*4) + MOVL DX, (R10) + CMPL (BX)(SI*1), DI + JEQ match_nolit_loop_encodeSnappyBlockAsm10B + INCL DX + JMP search_loop_encodeSnappyBlockAsm10B + +emit_remainder_encodeSnappyBlockAsm10B: + MOVQ src_len+32(FP), AX + SUBL 12(SP), AX + LEAQ 3(CX)(AX*1), AX + CMPQ AX, (SP) + JB emit_remainder_ok_encodeSnappyBlockAsm10B + MOVQ $0x00000000, ret+56(FP) + RET + +emit_remainder_ok_encodeSnappyBlockAsm10B: + MOVQ src_len+32(FP), AX + MOVL 12(SP), DX + CMPL DX, AX + JEQ emit_literal_done_emit_remainder_encodeSnappyBlockAsm10B + MOVL AX, SI + MOVL AX, 12(SP) + LEAQ (BX)(DX*1), AX + SUBL DX, SI + LEAL -1(SI), DX + CMPL DX, $0x3c + JB one_byte_emit_remainder_encodeSnappyBlockAsm10B + CMPL DX, $0x00000100 + JB two_bytes_emit_remainder_encodeSnappyBlockAsm10B + JB three_bytes_emit_remainder_encodeSnappyBlockAsm10B + +three_bytes_emit_remainder_encodeSnappyBlockAsm10B: + MOVB $0xf4, (CX) + MOVW DX, 1(CX) + ADDQ $0x03, CX + JMP memmove_long_emit_remainder_encodeSnappyBlockAsm10B + +two_bytes_emit_remainder_encodeSnappyBlockAsm10B: + MOVB $0xf0, (CX) + MOVB DL, 1(CX) + ADDQ $0x02, CX + CMPL DX, $0x40 + JB memmove_emit_remainder_encodeSnappyBlockAsm10B + JMP memmove_long_emit_remainder_encodeSnappyBlockAsm10B + +one_byte_emit_remainder_encodeSnappyBlockAsm10B: + SHLB $0x02, DL + MOVB DL, (CX) + ADDQ $0x01, CX + +memmove_emit_remainder_encodeSnappyBlockAsm10B: + LEAQ (CX)(SI*1), DX + MOVL SI, BX + + // genMemMoveShort + CMPQ BX, $0x03 + JB emit_lit_memmove_emit_remainder_encodeSnappyBlockAsm10B_memmove_move_1or2 + JE emit_lit_memmove_emit_remainder_encodeSnappyBlockAsm10B_memmove_move_3 + CMPQ BX, $0x08 + JB emit_lit_memmove_emit_remainder_encodeSnappyBlockAsm10B_memmove_move_4through7 + CMPQ BX, $0x10 + JBE emit_lit_memmove_emit_remainder_encodeSnappyBlockAsm10B_memmove_move_8through16 + CMPQ BX, $0x20 + JBE emit_lit_memmove_emit_remainder_encodeSnappyBlockAsm10B_memmove_move_17through32 + JMP emit_lit_memmove_emit_remainder_encodeSnappyBlockAsm10B_memmove_move_33through64 + +emit_lit_memmove_emit_remainder_encodeSnappyBlockAsm10B_memmove_move_1or2: + MOVB (AX), SI + MOVB -1(AX)(BX*1), AL + MOVB SI, (CX) + MOVB AL, -1(CX)(BX*1) + JMP memmove_end_copy_emit_remainder_encodeSnappyBlockAsm10B + +emit_lit_memmove_emit_remainder_encodeSnappyBlockAsm10B_memmove_move_3: + MOVW (AX), SI + MOVB 2(AX), AL + MOVW SI, (CX) + MOVB AL, 2(CX) + JMP memmove_end_copy_emit_remainder_encodeSnappyBlockAsm10B + +emit_lit_memmove_emit_remainder_encodeSnappyBlockAsm10B_memmove_move_4through7: + MOVL (AX), SI + MOVL -4(AX)(BX*1), AX + MOVL SI, (CX) + MOVL AX, -4(CX)(BX*1) + JMP memmove_end_copy_emit_remainder_encodeSnappyBlockAsm10B + +emit_lit_memmove_emit_remainder_encodeSnappyBlockAsm10B_memmove_move_8through16: + MOVQ (AX), SI + MOVQ -8(AX)(BX*1), AX + MOVQ SI, (CX) + MOVQ AX, -8(CX)(BX*1) + JMP memmove_end_copy_emit_remainder_encodeSnappyBlockAsm10B + +emit_lit_memmove_emit_remainder_encodeSnappyBlockAsm10B_memmove_move_17through32: + MOVOU (AX), X0 + MOVOU -16(AX)(BX*1), X1 + MOVOU X0, (CX) + MOVOU X1, -16(CX)(BX*1) + JMP memmove_end_copy_emit_remainder_encodeSnappyBlockAsm10B + +emit_lit_memmove_emit_remainder_encodeSnappyBlockAsm10B_memmove_move_33through64: + MOVOU (AX), X0 + MOVOU 16(AX), X1 + MOVOU -32(AX)(BX*1), X2 + MOVOU -16(AX)(BX*1), X3 + MOVOU X0, (CX) + MOVOU X1, 16(CX) + MOVOU X2, -32(CX)(BX*1) + MOVOU X3, -16(CX)(BX*1) + +memmove_end_copy_emit_remainder_encodeSnappyBlockAsm10B: + MOVQ DX, CX + JMP emit_literal_done_emit_remainder_encodeSnappyBlockAsm10B + +memmove_long_emit_remainder_encodeSnappyBlockAsm10B: + LEAQ (CX)(SI*1), DX + MOVL SI, BX + + // genMemMoveLong + MOVOU (AX), X0 + MOVOU 16(AX), X1 + MOVOU -32(AX)(BX*1), X2 + MOVOU -16(AX)(BX*1), X3 + MOVQ BX, DI + SHRQ $0x05, DI + MOVQ CX, SI + ANDL $0x0000001f, SI + MOVQ $0x00000040, R8 + SUBQ SI, R8 + DECQ DI + JA emit_lit_memmove_long_emit_remainder_encodeSnappyBlockAsm10Blarge_forward_sse_loop_32 + LEAQ -32(AX)(R8*1), SI + LEAQ -32(CX)(R8*1), R9 + +emit_lit_memmove_long_emit_remainder_encodeSnappyBlockAsm10Blarge_big_loop_back: + MOVOU (SI), X4 + MOVOU 16(SI), X5 + MOVOA X4, (R9) + MOVOA X5, 16(R9) + ADDQ $0x20, R9 + ADDQ $0x20, SI + ADDQ $0x20, R8 + DECQ DI + JNA emit_lit_memmove_long_emit_remainder_encodeSnappyBlockAsm10Blarge_big_loop_back + +emit_lit_memmove_long_emit_remainder_encodeSnappyBlockAsm10Blarge_forward_sse_loop_32: + MOVOU -32(AX)(R8*1), X4 + MOVOU -16(AX)(R8*1), X5 + MOVOA X4, -32(CX)(R8*1) + MOVOA X5, -16(CX)(R8*1) + ADDQ $0x20, R8 + CMPQ BX, R8 + JAE emit_lit_memmove_long_emit_remainder_encodeSnappyBlockAsm10Blarge_forward_sse_loop_32 + MOVOU X0, (CX) + MOVOU X1, 16(CX) + MOVOU X2, -32(CX)(BX*1) + MOVOU X3, -16(CX)(BX*1) + MOVQ DX, CX + +emit_literal_done_emit_remainder_encodeSnappyBlockAsm10B: + MOVQ dst_base+0(FP), AX + SUBQ AX, CX + MOVQ CX, ret+56(FP) + RET + +// func encodeSnappyBlockAsm8B(dst []byte, src []byte, tmp *[1024]byte) int +// Requires: BMI, SSE2 +TEXT ·encodeSnappyBlockAsm8B(SB), $24-64 + MOVQ tmp+48(FP), AX + MOVQ dst_base+0(FP), CX + MOVQ $0x00000008, DX + MOVQ AX, BX + PXOR X0, X0 + +zero_loop_encodeSnappyBlockAsm8B: + MOVOU X0, (BX) + MOVOU X0, 16(BX) + MOVOU X0, 32(BX) + MOVOU X0, 48(BX) + MOVOU X0, 64(BX) + MOVOU X0, 80(BX) + MOVOU X0, 96(BX) + MOVOU X0, 112(BX) + ADDQ $0x80, BX + DECQ DX + JNZ zero_loop_encodeSnappyBlockAsm8B + MOVL $0x00000000, 12(SP) + MOVQ src_len+32(FP), DX + LEAQ -9(DX), BX + LEAQ -8(DX), SI + MOVL SI, 8(SP) + SHRQ $0x05, DX + SUBL DX, BX + LEAQ (CX)(BX*1), BX + MOVQ BX, (SP) + MOVL $0x00000001, DX + MOVL DX, 16(SP) + MOVQ src_base+24(FP), BX + +search_loop_encodeSnappyBlockAsm8B: + MOVL DX, SI + SUBL 12(SP), SI + SHRL $0x04, SI + LEAL 4(DX)(SI*1), SI + CMPL SI, 8(SP) + JAE emit_remainder_encodeSnappyBlockAsm8B + MOVQ (BX)(DX*1), DI + MOVL SI, 20(SP) + MOVQ $0x9e3779b1, R9 + MOVQ DI, R10 + MOVQ DI, R11 + SHRQ $0x08, R11 + SHLQ $0x20, R10 + IMULQ R9, R10 + SHRQ $0x38, R10 + SHLQ $0x20, R11 + IMULQ R9, R11 + SHRQ $0x38, R11 + MOVL (AX)(R10*4), SI + MOVL (AX)(R11*4), R8 + MOVL DX, (AX)(R10*4) + LEAL 1(DX), R10 + MOVL R10, (AX)(R11*4) + MOVQ DI, R10 + SHRQ $0x10, R10 + SHLQ $0x20, R10 + IMULQ R9, R10 + SHRQ $0x38, R10 + MOVL DX, R9 + SUBL 16(SP), R9 + MOVL 1(BX)(R9*1), R11 + MOVQ DI, R9 + SHRQ $0x08, R9 + CMPL R9, R11 + JNE no_repeat_found_encodeSnappyBlockAsm8B + LEAL 1(DX), DI + MOVL 12(SP), SI + MOVL DI, R8 + SUBL 16(SP), R8 + JZ repeat_extend_back_end_encodeSnappyBlockAsm8B + +repeat_extend_back_loop_encodeSnappyBlockAsm8B: + CMPL DI, SI + JBE repeat_extend_back_end_encodeSnappyBlockAsm8B + MOVB -1(BX)(R8*1), R9 + MOVB -1(BX)(DI*1), R10 + CMPB R9, R10 + JNE repeat_extend_back_end_encodeSnappyBlockAsm8B + LEAL -1(DI), DI + DECL R8 + JNZ repeat_extend_back_loop_encodeSnappyBlockAsm8B + +repeat_extend_back_end_encodeSnappyBlockAsm8B: + MOVL DI, SI + SUBL 12(SP), SI + LEAQ 3(CX)(SI*1), SI + CMPQ SI, (SP) + JB repeat_dst_size_check_encodeSnappyBlockAsm8B + MOVQ $0x00000000, ret+56(FP) + RET + +repeat_dst_size_check_encodeSnappyBlockAsm8B: + MOVL 12(SP), SI + CMPL SI, DI + JEQ emit_literal_done_repeat_emit_encodeSnappyBlockAsm8B + MOVL DI, R8 + MOVL DI, 12(SP) + LEAQ (BX)(SI*1), R9 + SUBL SI, R8 + LEAL -1(R8), SI + CMPL SI, $0x3c + JB one_byte_repeat_emit_encodeSnappyBlockAsm8B + CMPL SI, $0x00000100 + JB two_bytes_repeat_emit_encodeSnappyBlockAsm8B + JB three_bytes_repeat_emit_encodeSnappyBlockAsm8B + +three_bytes_repeat_emit_encodeSnappyBlockAsm8B: + MOVB $0xf4, (CX) + MOVW SI, 1(CX) + ADDQ $0x03, CX + JMP memmove_long_repeat_emit_encodeSnappyBlockAsm8B + +two_bytes_repeat_emit_encodeSnappyBlockAsm8B: + MOVB $0xf0, (CX) + MOVB SI, 1(CX) + ADDQ $0x02, CX + CMPL SI, $0x40 + JB memmove_repeat_emit_encodeSnappyBlockAsm8B + JMP memmove_long_repeat_emit_encodeSnappyBlockAsm8B + +one_byte_repeat_emit_encodeSnappyBlockAsm8B: + SHLB $0x02, SI + MOVB SI, (CX) + ADDQ $0x01, CX + +memmove_repeat_emit_encodeSnappyBlockAsm8B: + LEAQ (CX)(R8*1), SI + + // genMemMoveShort + CMPQ R8, $0x08 + JBE emit_lit_memmove_repeat_emit_encodeSnappyBlockAsm8B_memmove_move_8 + CMPQ R8, $0x10 + JBE emit_lit_memmove_repeat_emit_encodeSnappyBlockAsm8B_memmove_move_8through16 + CMPQ R8, $0x20 + JBE emit_lit_memmove_repeat_emit_encodeSnappyBlockAsm8B_memmove_move_17through32 + JMP emit_lit_memmove_repeat_emit_encodeSnappyBlockAsm8B_memmove_move_33through64 + +emit_lit_memmove_repeat_emit_encodeSnappyBlockAsm8B_memmove_move_8: + MOVQ (R9), R10 + MOVQ R10, (CX) + JMP memmove_end_copy_repeat_emit_encodeSnappyBlockAsm8B + +emit_lit_memmove_repeat_emit_encodeSnappyBlockAsm8B_memmove_move_8through16: + MOVQ (R9), R10 + MOVQ -8(R9)(R8*1), R9 + MOVQ R10, (CX) + MOVQ R9, -8(CX)(R8*1) + JMP memmove_end_copy_repeat_emit_encodeSnappyBlockAsm8B + +emit_lit_memmove_repeat_emit_encodeSnappyBlockAsm8B_memmove_move_17through32: + MOVOU (R9), X0 + MOVOU -16(R9)(R8*1), X1 + MOVOU X0, (CX) + MOVOU X1, -16(CX)(R8*1) + JMP memmove_end_copy_repeat_emit_encodeSnappyBlockAsm8B + +emit_lit_memmove_repeat_emit_encodeSnappyBlockAsm8B_memmove_move_33through64: + MOVOU (R9), X0 + MOVOU 16(R9), X1 + MOVOU -32(R9)(R8*1), X2 + MOVOU -16(R9)(R8*1), X3 + MOVOU X0, (CX) + MOVOU X1, 16(CX) + MOVOU X2, -32(CX)(R8*1) + MOVOU X3, -16(CX)(R8*1) + +memmove_end_copy_repeat_emit_encodeSnappyBlockAsm8B: + MOVQ SI, CX + JMP emit_literal_done_repeat_emit_encodeSnappyBlockAsm8B + +memmove_long_repeat_emit_encodeSnappyBlockAsm8B: + LEAQ (CX)(R8*1), SI + + // genMemMoveLong + MOVOU (R9), X0 + MOVOU 16(R9), X1 + MOVOU -32(R9)(R8*1), X2 + MOVOU -16(R9)(R8*1), X3 + MOVQ R8, R11 + SHRQ $0x05, R11 + MOVQ CX, R10 + ANDL $0x0000001f, R10 + MOVQ $0x00000040, R12 + SUBQ R10, R12 + DECQ R11 + JA emit_lit_memmove_long_repeat_emit_encodeSnappyBlockAsm8Blarge_forward_sse_loop_32 + LEAQ -32(R9)(R12*1), R10 + LEAQ -32(CX)(R12*1), R13 + +emit_lit_memmove_long_repeat_emit_encodeSnappyBlockAsm8Blarge_big_loop_back: + MOVOU (R10), X4 + MOVOU 16(R10), X5 + MOVOA X4, (R13) + MOVOA X5, 16(R13) + ADDQ $0x20, R13 + ADDQ $0x20, R10 + ADDQ $0x20, R12 + DECQ R11 + JNA emit_lit_memmove_long_repeat_emit_encodeSnappyBlockAsm8Blarge_big_loop_back + +emit_lit_memmove_long_repeat_emit_encodeSnappyBlockAsm8Blarge_forward_sse_loop_32: + MOVOU -32(R9)(R12*1), X4 + MOVOU -16(R9)(R12*1), X5 + MOVOA X4, -32(CX)(R12*1) + MOVOA X5, -16(CX)(R12*1) + ADDQ $0x20, R12 + CMPQ R8, R12 + JAE emit_lit_memmove_long_repeat_emit_encodeSnappyBlockAsm8Blarge_forward_sse_loop_32 + MOVOU X0, (CX) + MOVOU X1, 16(CX) + MOVOU X2, -32(CX)(R8*1) + MOVOU X3, -16(CX)(R8*1) + MOVQ SI, CX + +emit_literal_done_repeat_emit_encodeSnappyBlockAsm8B: + ADDL $0x05, DX + MOVL DX, SI + SUBL 16(SP), SI + MOVQ src_len+32(FP), R8 + SUBL DX, R8 + LEAQ (BX)(DX*1), R9 + LEAQ (BX)(SI*1), SI + + // matchLen + XORL R11, R11 + +matchlen_loopback_16_repeat_extend_encodeSnappyBlockAsm8B: + CMPL R8, $0x10 + JB matchlen_match8_repeat_extend_encodeSnappyBlockAsm8B + MOVQ (R9)(R11*1), R10 + MOVQ 8(R9)(R11*1), R12 + XORQ (SI)(R11*1), R10 + JNZ matchlen_bsf_8_repeat_extend_encodeSnappyBlockAsm8B + XORQ 8(SI)(R11*1), R12 + JNZ matchlen_bsf_16repeat_extend_encodeSnappyBlockAsm8B + LEAL -16(R8), R8 + LEAL 16(R11), R11 + JMP matchlen_loopback_16_repeat_extend_encodeSnappyBlockAsm8B + +matchlen_bsf_16repeat_extend_encodeSnappyBlockAsm8B: +#ifdef GOAMD64_v3 + TZCNTQ R12, R12 + +#else + BSFQ R12, R12 + +#endif + SARQ $0x03, R12 + LEAL 8(R11)(R12*1), R11 + JMP repeat_extend_forward_end_encodeSnappyBlockAsm8B + +matchlen_match8_repeat_extend_encodeSnappyBlockAsm8B: + CMPL R8, $0x08 + JB matchlen_match4_repeat_extend_encodeSnappyBlockAsm8B + MOVQ (R9)(R11*1), R10 + XORQ (SI)(R11*1), R10 + JNZ matchlen_bsf_8_repeat_extend_encodeSnappyBlockAsm8B + LEAL -8(R8), R8 + LEAL 8(R11), R11 + JMP matchlen_match4_repeat_extend_encodeSnappyBlockAsm8B + +matchlen_bsf_8_repeat_extend_encodeSnappyBlockAsm8B: +#ifdef GOAMD64_v3 + TZCNTQ R10, R10 + +#else + BSFQ R10, R10 + +#endif + SARQ $0x03, R10 + LEAL (R11)(R10*1), R11 + JMP repeat_extend_forward_end_encodeSnappyBlockAsm8B + +matchlen_match4_repeat_extend_encodeSnappyBlockAsm8B: + CMPL R8, $0x04 + JB matchlen_match2_repeat_extend_encodeSnappyBlockAsm8B + MOVL (R9)(R11*1), R10 + CMPL (SI)(R11*1), R10 + JNE matchlen_match2_repeat_extend_encodeSnappyBlockAsm8B + LEAL -4(R8), R8 + LEAL 4(R11), R11 + +matchlen_match2_repeat_extend_encodeSnappyBlockAsm8B: + CMPL R8, $0x01 + JE matchlen_match1_repeat_extend_encodeSnappyBlockAsm8B + JB repeat_extend_forward_end_encodeSnappyBlockAsm8B + MOVW (R9)(R11*1), R10 + CMPW (SI)(R11*1), R10 + JNE matchlen_match1_repeat_extend_encodeSnappyBlockAsm8B + LEAL 2(R11), R11 + SUBL $0x02, R8 + JZ repeat_extend_forward_end_encodeSnappyBlockAsm8B + +matchlen_match1_repeat_extend_encodeSnappyBlockAsm8B: + MOVB (R9)(R11*1), R10 + CMPB (SI)(R11*1), R10 + JNE repeat_extend_forward_end_encodeSnappyBlockAsm8B + LEAL 1(R11), R11 + +repeat_extend_forward_end_encodeSnappyBlockAsm8B: + ADDL R11, DX + MOVL DX, SI + SUBL DI, SI + MOVL 16(SP), DI + + // emitCopy +two_byte_offset_repeat_as_copy_encodeSnappyBlockAsm8B: + CMPL SI, $0x40 + JBE two_byte_offset_short_repeat_as_copy_encodeSnappyBlockAsm8B + MOVB $0xee, (CX) + MOVW DI, 1(CX) + LEAL -60(SI), SI + ADDQ $0x03, CX + JMP two_byte_offset_repeat_as_copy_encodeSnappyBlockAsm8B + +two_byte_offset_short_repeat_as_copy_encodeSnappyBlockAsm8B: + MOVL SI, R8 + SHLL $0x02, R8 + CMPL SI, $0x0c + JAE emit_copy_three_repeat_as_copy_encodeSnappyBlockAsm8B + LEAL -15(R8), R8 + MOVB DI, 1(CX) + SHRL $0x08, DI + SHLL $0x05, DI + ORL DI, R8 + MOVB R8, (CX) + ADDQ $0x02, CX + JMP repeat_end_emit_encodeSnappyBlockAsm8B + +emit_copy_three_repeat_as_copy_encodeSnappyBlockAsm8B: + LEAL -2(R8), R8 + MOVB R8, (CX) + MOVW DI, 1(CX) + ADDQ $0x03, CX + +repeat_end_emit_encodeSnappyBlockAsm8B: + MOVL DX, 12(SP) + JMP search_loop_encodeSnappyBlockAsm8B + +no_repeat_found_encodeSnappyBlockAsm8B: + CMPL (BX)(SI*1), DI + JEQ candidate_match_encodeSnappyBlockAsm8B + SHRQ $0x08, DI + MOVL (AX)(R10*4), SI + LEAL 2(DX), R9 + CMPL (BX)(R8*1), DI + JEQ candidate2_match_encodeSnappyBlockAsm8B + MOVL R9, (AX)(R10*4) + SHRQ $0x08, DI + CMPL (BX)(SI*1), DI + JEQ candidate3_match_encodeSnappyBlockAsm8B + MOVL 20(SP), DX + JMP search_loop_encodeSnappyBlockAsm8B + +candidate3_match_encodeSnappyBlockAsm8B: + ADDL $0x02, DX + JMP candidate_match_encodeSnappyBlockAsm8B + +candidate2_match_encodeSnappyBlockAsm8B: + MOVL R9, (AX)(R10*4) + INCL DX + MOVL R8, SI + +candidate_match_encodeSnappyBlockAsm8B: + MOVL 12(SP), DI + TESTL SI, SI + JZ match_extend_back_end_encodeSnappyBlockAsm8B + +match_extend_back_loop_encodeSnappyBlockAsm8B: + CMPL DX, DI + JBE match_extend_back_end_encodeSnappyBlockAsm8B + MOVB -1(BX)(SI*1), R8 + MOVB -1(BX)(DX*1), R9 + CMPB R8, R9 + JNE match_extend_back_end_encodeSnappyBlockAsm8B + LEAL -1(DX), DX + DECL SI + JZ match_extend_back_end_encodeSnappyBlockAsm8B + JMP match_extend_back_loop_encodeSnappyBlockAsm8B + +match_extend_back_end_encodeSnappyBlockAsm8B: + MOVL DX, DI + SUBL 12(SP), DI + LEAQ 3(CX)(DI*1), DI + CMPQ DI, (SP) + JB match_dst_size_check_encodeSnappyBlockAsm8B + MOVQ $0x00000000, ret+56(FP) + RET + +match_dst_size_check_encodeSnappyBlockAsm8B: + MOVL DX, DI + MOVL 12(SP), R8 + CMPL R8, DI + JEQ emit_literal_done_match_emit_encodeSnappyBlockAsm8B + MOVL DI, R9 + MOVL DI, 12(SP) + LEAQ (BX)(R8*1), DI + SUBL R8, R9 + LEAL -1(R9), R8 + CMPL R8, $0x3c + JB one_byte_match_emit_encodeSnappyBlockAsm8B + CMPL R8, $0x00000100 + JB two_bytes_match_emit_encodeSnappyBlockAsm8B + JB three_bytes_match_emit_encodeSnappyBlockAsm8B + +three_bytes_match_emit_encodeSnappyBlockAsm8B: + MOVB $0xf4, (CX) + MOVW R8, 1(CX) + ADDQ $0x03, CX + JMP memmove_long_match_emit_encodeSnappyBlockAsm8B + +two_bytes_match_emit_encodeSnappyBlockAsm8B: + MOVB $0xf0, (CX) + MOVB R8, 1(CX) + ADDQ $0x02, CX + CMPL R8, $0x40 + JB memmove_match_emit_encodeSnappyBlockAsm8B + JMP memmove_long_match_emit_encodeSnappyBlockAsm8B + +one_byte_match_emit_encodeSnappyBlockAsm8B: + SHLB $0x02, R8 + MOVB R8, (CX) + ADDQ $0x01, CX + +memmove_match_emit_encodeSnappyBlockAsm8B: + LEAQ (CX)(R9*1), R8 + + // genMemMoveShort + CMPQ R9, $0x08 + JBE emit_lit_memmove_match_emit_encodeSnappyBlockAsm8B_memmove_move_8 + CMPQ R9, $0x10 + JBE emit_lit_memmove_match_emit_encodeSnappyBlockAsm8B_memmove_move_8through16 + CMPQ R9, $0x20 + JBE emit_lit_memmove_match_emit_encodeSnappyBlockAsm8B_memmove_move_17through32 + JMP emit_lit_memmove_match_emit_encodeSnappyBlockAsm8B_memmove_move_33through64 + +emit_lit_memmove_match_emit_encodeSnappyBlockAsm8B_memmove_move_8: + MOVQ (DI), R10 + MOVQ R10, (CX) + JMP memmove_end_copy_match_emit_encodeSnappyBlockAsm8B + +emit_lit_memmove_match_emit_encodeSnappyBlockAsm8B_memmove_move_8through16: + MOVQ (DI), R10 + MOVQ -8(DI)(R9*1), DI + MOVQ R10, (CX) + MOVQ DI, -8(CX)(R9*1) + JMP memmove_end_copy_match_emit_encodeSnappyBlockAsm8B + +emit_lit_memmove_match_emit_encodeSnappyBlockAsm8B_memmove_move_17through32: + MOVOU (DI), X0 + MOVOU -16(DI)(R9*1), X1 + MOVOU X0, (CX) + MOVOU X1, -16(CX)(R9*1) + JMP memmove_end_copy_match_emit_encodeSnappyBlockAsm8B + +emit_lit_memmove_match_emit_encodeSnappyBlockAsm8B_memmove_move_33through64: + MOVOU (DI), X0 + MOVOU 16(DI), X1 + MOVOU -32(DI)(R9*1), X2 + MOVOU -16(DI)(R9*1), X3 + MOVOU X0, (CX) + MOVOU X1, 16(CX) + MOVOU X2, -32(CX)(R9*1) + MOVOU X3, -16(CX)(R9*1) + +memmove_end_copy_match_emit_encodeSnappyBlockAsm8B: + MOVQ R8, CX + JMP emit_literal_done_match_emit_encodeSnappyBlockAsm8B + +memmove_long_match_emit_encodeSnappyBlockAsm8B: + LEAQ (CX)(R9*1), R8 + + // genMemMoveLong + MOVOU (DI), X0 + MOVOU 16(DI), X1 + MOVOU -32(DI)(R9*1), X2 + MOVOU -16(DI)(R9*1), X3 + MOVQ R9, R11 + SHRQ $0x05, R11 + MOVQ CX, R10 + ANDL $0x0000001f, R10 + MOVQ $0x00000040, R12 + SUBQ R10, R12 + DECQ R11 + JA emit_lit_memmove_long_match_emit_encodeSnappyBlockAsm8Blarge_forward_sse_loop_32 + LEAQ -32(DI)(R12*1), R10 + LEAQ -32(CX)(R12*1), R13 + +emit_lit_memmove_long_match_emit_encodeSnappyBlockAsm8Blarge_big_loop_back: + MOVOU (R10), X4 + MOVOU 16(R10), X5 + MOVOA X4, (R13) + MOVOA X5, 16(R13) + ADDQ $0x20, R13 + ADDQ $0x20, R10 + ADDQ $0x20, R12 + DECQ R11 + JNA emit_lit_memmove_long_match_emit_encodeSnappyBlockAsm8Blarge_big_loop_back + +emit_lit_memmove_long_match_emit_encodeSnappyBlockAsm8Blarge_forward_sse_loop_32: + MOVOU -32(DI)(R12*1), X4 + MOVOU -16(DI)(R12*1), X5 + MOVOA X4, -32(CX)(R12*1) + MOVOA X5, -16(CX)(R12*1) + ADDQ $0x20, R12 + CMPQ R9, R12 + JAE emit_lit_memmove_long_match_emit_encodeSnappyBlockAsm8Blarge_forward_sse_loop_32 + MOVOU X0, (CX) + MOVOU X1, 16(CX) + MOVOU X2, -32(CX)(R9*1) + MOVOU X3, -16(CX)(R9*1) + MOVQ R8, CX + +emit_literal_done_match_emit_encodeSnappyBlockAsm8B: +match_nolit_loop_encodeSnappyBlockAsm8B: + MOVL DX, DI + SUBL SI, DI + MOVL DI, 16(SP) + ADDL $0x04, DX + ADDL $0x04, SI + MOVQ src_len+32(FP), DI + SUBL DX, DI + LEAQ (BX)(DX*1), R8 + LEAQ (BX)(SI*1), SI + + // matchLen + XORL R10, R10 + +matchlen_loopback_16_match_nolit_encodeSnappyBlockAsm8B: + CMPL DI, $0x10 + JB matchlen_match8_match_nolit_encodeSnappyBlockAsm8B + MOVQ (R8)(R10*1), R9 + MOVQ 8(R8)(R10*1), R11 + XORQ (SI)(R10*1), R9 + JNZ matchlen_bsf_8_match_nolit_encodeSnappyBlockAsm8B + XORQ 8(SI)(R10*1), R11 + JNZ matchlen_bsf_16match_nolit_encodeSnappyBlockAsm8B + LEAL -16(DI), DI + LEAL 16(R10), R10 + JMP matchlen_loopback_16_match_nolit_encodeSnappyBlockAsm8B + +matchlen_bsf_16match_nolit_encodeSnappyBlockAsm8B: +#ifdef GOAMD64_v3 + TZCNTQ R11, R11 + +#else + BSFQ R11, R11 + +#endif + SARQ $0x03, R11 + LEAL 8(R10)(R11*1), R10 + JMP match_nolit_end_encodeSnappyBlockAsm8B + +matchlen_match8_match_nolit_encodeSnappyBlockAsm8B: + CMPL DI, $0x08 + JB matchlen_match4_match_nolit_encodeSnappyBlockAsm8B + MOVQ (R8)(R10*1), R9 + XORQ (SI)(R10*1), R9 + JNZ matchlen_bsf_8_match_nolit_encodeSnappyBlockAsm8B + LEAL -8(DI), DI + LEAL 8(R10), R10 + JMP matchlen_match4_match_nolit_encodeSnappyBlockAsm8B + +matchlen_bsf_8_match_nolit_encodeSnappyBlockAsm8B: +#ifdef GOAMD64_v3 + TZCNTQ R9, R9 + +#else + BSFQ R9, R9 + +#endif + SARQ $0x03, R9 + LEAL (R10)(R9*1), R10 + JMP match_nolit_end_encodeSnappyBlockAsm8B + +matchlen_match4_match_nolit_encodeSnappyBlockAsm8B: + CMPL DI, $0x04 + JB matchlen_match2_match_nolit_encodeSnappyBlockAsm8B + MOVL (R8)(R10*1), R9 + CMPL (SI)(R10*1), R9 + JNE matchlen_match2_match_nolit_encodeSnappyBlockAsm8B + LEAL -4(DI), DI + LEAL 4(R10), R10 + +matchlen_match2_match_nolit_encodeSnappyBlockAsm8B: + CMPL DI, $0x01 + JE matchlen_match1_match_nolit_encodeSnappyBlockAsm8B + JB match_nolit_end_encodeSnappyBlockAsm8B + MOVW (R8)(R10*1), R9 + CMPW (SI)(R10*1), R9 + JNE matchlen_match1_match_nolit_encodeSnappyBlockAsm8B + LEAL 2(R10), R10 + SUBL $0x02, DI + JZ match_nolit_end_encodeSnappyBlockAsm8B + +matchlen_match1_match_nolit_encodeSnappyBlockAsm8B: + MOVB (R8)(R10*1), R9 + CMPB (SI)(R10*1), R9 + JNE match_nolit_end_encodeSnappyBlockAsm8B + LEAL 1(R10), R10 + +match_nolit_end_encodeSnappyBlockAsm8B: + ADDL R10, DX + MOVL 16(SP), SI + ADDL $0x04, R10 + MOVL DX, 12(SP) + + // emitCopy +two_byte_offset_match_nolit_encodeSnappyBlockAsm8B: + CMPL R10, $0x40 + JBE two_byte_offset_short_match_nolit_encodeSnappyBlockAsm8B + MOVB $0xee, (CX) + MOVW SI, 1(CX) + LEAL -60(R10), R10 + ADDQ $0x03, CX + JMP two_byte_offset_match_nolit_encodeSnappyBlockAsm8B + +two_byte_offset_short_match_nolit_encodeSnappyBlockAsm8B: + MOVL R10, DI + SHLL $0x02, DI + CMPL R10, $0x0c + JAE emit_copy_three_match_nolit_encodeSnappyBlockAsm8B + LEAL -15(DI), DI + MOVB SI, 1(CX) + SHRL $0x08, SI + SHLL $0x05, SI + ORL SI, DI + MOVB DI, (CX) + ADDQ $0x02, CX + JMP match_nolit_emitcopy_end_encodeSnappyBlockAsm8B + +emit_copy_three_match_nolit_encodeSnappyBlockAsm8B: + LEAL -2(DI), DI + MOVB DI, (CX) + MOVW SI, 1(CX) + ADDQ $0x03, CX + +match_nolit_emitcopy_end_encodeSnappyBlockAsm8B: + CMPL DX, 8(SP) + JAE emit_remainder_encodeSnappyBlockAsm8B + MOVQ -2(BX)(DX*1), DI + CMPQ CX, (SP) + JB match_nolit_dst_ok_encodeSnappyBlockAsm8B + MOVQ $0x00000000, ret+56(FP) + RET + +match_nolit_dst_ok_encodeSnappyBlockAsm8B: + MOVQ $0x9e3779b1, R9 + MOVQ DI, R8 + SHRQ $0x10, DI + MOVQ DI, SI + SHLQ $0x20, R8 + IMULQ R9, R8 + SHRQ $0x38, R8 + SHLQ $0x20, SI + IMULQ R9, SI + SHRQ $0x38, SI + LEAL -2(DX), R9 + LEAQ (AX)(SI*4), R10 + MOVL (R10), SI + MOVL R9, (AX)(R8*4) + MOVL DX, (R10) + CMPL (BX)(SI*1), DI + JEQ match_nolit_loop_encodeSnappyBlockAsm8B + INCL DX + JMP search_loop_encodeSnappyBlockAsm8B + +emit_remainder_encodeSnappyBlockAsm8B: + MOVQ src_len+32(FP), AX + SUBL 12(SP), AX + LEAQ 3(CX)(AX*1), AX + CMPQ AX, (SP) + JB emit_remainder_ok_encodeSnappyBlockAsm8B + MOVQ $0x00000000, ret+56(FP) + RET + +emit_remainder_ok_encodeSnappyBlockAsm8B: + MOVQ src_len+32(FP), AX + MOVL 12(SP), DX + CMPL DX, AX + JEQ emit_literal_done_emit_remainder_encodeSnappyBlockAsm8B + MOVL AX, SI + MOVL AX, 12(SP) + LEAQ (BX)(DX*1), AX + SUBL DX, SI + LEAL -1(SI), DX + CMPL DX, $0x3c + JB one_byte_emit_remainder_encodeSnappyBlockAsm8B + CMPL DX, $0x00000100 + JB two_bytes_emit_remainder_encodeSnappyBlockAsm8B + JB three_bytes_emit_remainder_encodeSnappyBlockAsm8B + +three_bytes_emit_remainder_encodeSnappyBlockAsm8B: + MOVB $0xf4, (CX) + MOVW DX, 1(CX) + ADDQ $0x03, CX + JMP memmove_long_emit_remainder_encodeSnappyBlockAsm8B + +two_bytes_emit_remainder_encodeSnappyBlockAsm8B: + MOVB $0xf0, (CX) + MOVB DL, 1(CX) + ADDQ $0x02, CX + CMPL DX, $0x40 + JB memmove_emit_remainder_encodeSnappyBlockAsm8B + JMP memmove_long_emit_remainder_encodeSnappyBlockAsm8B + +one_byte_emit_remainder_encodeSnappyBlockAsm8B: + SHLB $0x02, DL + MOVB DL, (CX) + ADDQ $0x01, CX + +memmove_emit_remainder_encodeSnappyBlockAsm8B: + LEAQ (CX)(SI*1), DX + MOVL SI, BX + + // genMemMoveShort + CMPQ BX, $0x03 + JB emit_lit_memmove_emit_remainder_encodeSnappyBlockAsm8B_memmove_move_1or2 + JE emit_lit_memmove_emit_remainder_encodeSnappyBlockAsm8B_memmove_move_3 + CMPQ BX, $0x08 + JB emit_lit_memmove_emit_remainder_encodeSnappyBlockAsm8B_memmove_move_4through7 + CMPQ BX, $0x10 + JBE emit_lit_memmove_emit_remainder_encodeSnappyBlockAsm8B_memmove_move_8through16 + CMPQ BX, $0x20 + JBE emit_lit_memmove_emit_remainder_encodeSnappyBlockAsm8B_memmove_move_17through32 + JMP emit_lit_memmove_emit_remainder_encodeSnappyBlockAsm8B_memmove_move_33through64 + +emit_lit_memmove_emit_remainder_encodeSnappyBlockAsm8B_memmove_move_1or2: + MOVB (AX), SI + MOVB -1(AX)(BX*1), AL + MOVB SI, (CX) + MOVB AL, -1(CX)(BX*1) + JMP memmove_end_copy_emit_remainder_encodeSnappyBlockAsm8B + +emit_lit_memmove_emit_remainder_encodeSnappyBlockAsm8B_memmove_move_3: + MOVW (AX), SI + MOVB 2(AX), AL + MOVW SI, (CX) + MOVB AL, 2(CX) + JMP memmove_end_copy_emit_remainder_encodeSnappyBlockAsm8B + +emit_lit_memmove_emit_remainder_encodeSnappyBlockAsm8B_memmove_move_4through7: + MOVL (AX), SI + MOVL -4(AX)(BX*1), AX + MOVL SI, (CX) + MOVL AX, -4(CX)(BX*1) + JMP memmove_end_copy_emit_remainder_encodeSnappyBlockAsm8B + +emit_lit_memmove_emit_remainder_encodeSnappyBlockAsm8B_memmove_move_8through16: + MOVQ (AX), SI + MOVQ -8(AX)(BX*1), AX + MOVQ SI, (CX) + MOVQ AX, -8(CX)(BX*1) + JMP memmove_end_copy_emit_remainder_encodeSnappyBlockAsm8B + +emit_lit_memmove_emit_remainder_encodeSnappyBlockAsm8B_memmove_move_17through32: + MOVOU (AX), X0 + MOVOU -16(AX)(BX*1), X1 + MOVOU X0, (CX) + MOVOU X1, -16(CX)(BX*1) + JMP memmove_end_copy_emit_remainder_encodeSnappyBlockAsm8B + +emit_lit_memmove_emit_remainder_encodeSnappyBlockAsm8B_memmove_move_33through64: + MOVOU (AX), X0 + MOVOU 16(AX), X1 + MOVOU -32(AX)(BX*1), X2 + MOVOU -16(AX)(BX*1), X3 + MOVOU X0, (CX) + MOVOU X1, 16(CX) + MOVOU X2, -32(CX)(BX*1) + MOVOU X3, -16(CX)(BX*1) + +memmove_end_copy_emit_remainder_encodeSnappyBlockAsm8B: + MOVQ DX, CX + JMP emit_literal_done_emit_remainder_encodeSnappyBlockAsm8B + +memmove_long_emit_remainder_encodeSnappyBlockAsm8B: + LEAQ (CX)(SI*1), DX + MOVL SI, BX + + // genMemMoveLong + MOVOU (AX), X0 + MOVOU 16(AX), X1 + MOVOU -32(AX)(BX*1), X2 + MOVOU -16(AX)(BX*1), X3 + MOVQ BX, DI + SHRQ $0x05, DI + MOVQ CX, SI + ANDL $0x0000001f, SI + MOVQ $0x00000040, R8 + SUBQ SI, R8 + DECQ DI + JA emit_lit_memmove_long_emit_remainder_encodeSnappyBlockAsm8Blarge_forward_sse_loop_32 + LEAQ -32(AX)(R8*1), SI + LEAQ -32(CX)(R8*1), R9 + +emit_lit_memmove_long_emit_remainder_encodeSnappyBlockAsm8Blarge_big_loop_back: + MOVOU (SI), X4 + MOVOU 16(SI), X5 + MOVOA X4, (R9) + MOVOA X5, 16(R9) + ADDQ $0x20, R9 + ADDQ $0x20, SI + ADDQ $0x20, R8 + DECQ DI + JNA emit_lit_memmove_long_emit_remainder_encodeSnappyBlockAsm8Blarge_big_loop_back + +emit_lit_memmove_long_emit_remainder_encodeSnappyBlockAsm8Blarge_forward_sse_loop_32: + MOVOU -32(AX)(R8*1), X4 + MOVOU -16(AX)(R8*1), X5 + MOVOA X4, -32(CX)(R8*1) + MOVOA X5, -16(CX)(R8*1) + ADDQ $0x20, R8 + CMPQ BX, R8 + JAE emit_lit_memmove_long_emit_remainder_encodeSnappyBlockAsm8Blarge_forward_sse_loop_32 + MOVOU X0, (CX) + MOVOU X1, 16(CX) + MOVOU X2, -32(CX)(BX*1) + MOVOU X3, -16(CX)(BX*1) + MOVQ DX, CX + +emit_literal_done_emit_remainder_encodeSnappyBlockAsm8B: + MOVQ dst_base+0(FP), AX + SUBQ AX, CX + MOVQ CX, ret+56(FP) + RET + +// func encodeSnappyBetterBlockAsm(dst []byte, src []byte, tmp *[589824]byte) int +// Requires: BMI, SSE2 +TEXT ·encodeSnappyBetterBlockAsm(SB), $24-64 + MOVQ tmp+48(FP), AX + MOVQ dst_base+0(FP), CX + MOVQ $0x00001200, DX + MOVQ AX, BX + PXOR X0, X0 + +zero_loop_encodeSnappyBetterBlockAsm: + MOVOU X0, (BX) + MOVOU X0, 16(BX) + MOVOU X0, 32(BX) + MOVOU X0, 48(BX) + MOVOU X0, 64(BX) + MOVOU X0, 80(BX) + MOVOU X0, 96(BX) + MOVOU X0, 112(BX) + ADDQ $0x80, BX + DECQ DX + JNZ zero_loop_encodeSnappyBetterBlockAsm + MOVL $0x00000000, 12(SP) + MOVQ src_len+32(FP), DX + LEAQ -9(DX), BX + LEAQ -8(DX), SI + MOVL SI, 8(SP) + SHRQ $0x05, DX + SUBL DX, BX + LEAQ (CX)(BX*1), BX + MOVQ BX, (SP) + MOVL $0x00000001, DX + MOVL $0x00000000, 16(SP) + MOVQ src_base+24(FP), BX + +search_loop_encodeSnappyBetterBlockAsm: + MOVL DX, SI + SUBL 12(SP), SI + SHRL $0x07, SI + CMPL SI, $0x63 + JBE check_maxskip_ok_encodeSnappyBetterBlockAsm + LEAL 100(DX), SI + JMP check_maxskip_cont_encodeSnappyBetterBlockAsm + +check_maxskip_ok_encodeSnappyBetterBlockAsm: + LEAL 1(DX)(SI*1), SI + +check_maxskip_cont_encodeSnappyBetterBlockAsm: + CMPL SI, 8(SP) + JAE emit_remainder_encodeSnappyBetterBlockAsm + MOVQ (BX)(DX*1), DI + MOVL SI, 20(SP) + MOVQ $0x00cf1bbcdcbfa563, R9 + MOVQ $0x9e3779b1, SI + MOVQ DI, R10 + MOVQ DI, R11 + SHLQ $0x08, R10 + IMULQ R9, R10 + SHRQ $0x2f, R10 + SHLQ $0x20, R11 + IMULQ SI, R11 + SHRQ $0x32, R11 + MOVL (AX)(R10*4), SI + MOVL 524288(AX)(R11*4), R8 + MOVL DX, (AX)(R10*4) + MOVL DX, 524288(AX)(R11*4) + MOVQ (BX)(SI*1), R10 + MOVQ (BX)(R8*1), R11 + CMPQ R10, DI + JEQ candidate_match_encodeSnappyBetterBlockAsm + CMPQ R11, DI + JNE no_short_found_encodeSnappyBetterBlockAsm + MOVL R8, SI + JMP candidate_match_encodeSnappyBetterBlockAsm + +no_short_found_encodeSnappyBetterBlockAsm: + CMPL R10, DI + JEQ candidate_match_encodeSnappyBetterBlockAsm + CMPL R11, DI + JEQ candidateS_match_encodeSnappyBetterBlockAsm + MOVL 20(SP), DX + JMP search_loop_encodeSnappyBetterBlockAsm + +candidateS_match_encodeSnappyBetterBlockAsm: + SHRQ $0x08, DI + MOVQ DI, R10 + SHLQ $0x08, R10 + IMULQ R9, R10 + SHRQ $0x2f, R10 + MOVL (AX)(R10*4), SI + INCL DX + MOVL DX, (AX)(R10*4) + CMPL (BX)(SI*1), DI + JEQ candidate_match_encodeSnappyBetterBlockAsm + DECL DX + MOVL R8, SI + +candidate_match_encodeSnappyBetterBlockAsm: + MOVL 12(SP), DI + TESTL SI, SI + JZ match_extend_back_end_encodeSnappyBetterBlockAsm + +match_extend_back_loop_encodeSnappyBetterBlockAsm: + CMPL DX, DI + JBE match_extend_back_end_encodeSnappyBetterBlockAsm + MOVB -1(BX)(SI*1), R8 + MOVB -1(BX)(DX*1), R9 + CMPB R8, R9 + JNE match_extend_back_end_encodeSnappyBetterBlockAsm + LEAL -1(DX), DX + DECL SI + JZ match_extend_back_end_encodeSnappyBetterBlockAsm + JMP match_extend_back_loop_encodeSnappyBetterBlockAsm + +match_extend_back_end_encodeSnappyBetterBlockAsm: + MOVL DX, DI + SUBL 12(SP), DI + LEAQ 5(CX)(DI*1), DI + CMPQ DI, (SP) + JB match_dst_size_check_encodeSnappyBetterBlockAsm + MOVQ $0x00000000, ret+56(FP) + RET + +match_dst_size_check_encodeSnappyBetterBlockAsm: + MOVL DX, DI + ADDL $0x04, DX + ADDL $0x04, SI + MOVQ src_len+32(FP), R8 + SUBL DX, R8 + LEAQ (BX)(DX*1), R9 + LEAQ (BX)(SI*1), R10 + + // matchLen + XORL R12, R12 + +matchlen_loopback_16_match_nolit_encodeSnappyBetterBlockAsm: + CMPL R8, $0x10 + JB matchlen_match8_match_nolit_encodeSnappyBetterBlockAsm + MOVQ (R9)(R12*1), R11 + MOVQ 8(R9)(R12*1), R13 + XORQ (R10)(R12*1), R11 + JNZ matchlen_bsf_8_match_nolit_encodeSnappyBetterBlockAsm + XORQ 8(R10)(R12*1), R13 + JNZ matchlen_bsf_16match_nolit_encodeSnappyBetterBlockAsm + LEAL -16(R8), R8 + LEAL 16(R12), R12 + JMP matchlen_loopback_16_match_nolit_encodeSnappyBetterBlockAsm + +matchlen_bsf_16match_nolit_encodeSnappyBetterBlockAsm: +#ifdef GOAMD64_v3 + TZCNTQ R13, R13 + +#else + BSFQ R13, R13 + +#endif + SARQ $0x03, R13 + LEAL 8(R12)(R13*1), R12 + JMP match_nolit_end_encodeSnappyBetterBlockAsm + +matchlen_match8_match_nolit_encodeSnappyBetterBlockAsm: + CMPL R8, $0x08 + JB matchlen_match4_match_nolit_encodeSnappyBetterBlockAsm + MOVQ (R9)(R12*1), R11 + XORQ (R10)(R12*1), R11 + JNZ matchlen_bsf_8_match_nolit_encodeSnappyBetterBlockAsm + LEAL -8(R8), R8 + LEAL 8(R12), R12 + JMP matchlen_match4_match_nolit_encodeSnappyBetterBlockAsm + +matchlen_bsf_8_match_nolit_encodeSnappyBetterBlockAsm: +#ifdef GOAMD64_v3 + TZCNTQ R11, R11 + +#else + BSFQ R11, R11 + +#endif + SARQ $0x03, R11 + LEAL (R12)(R11*1), R12 + JMP match_nolit_end_encodeSnappyBetterBlockAsm + +matchlen_match4_match_nolit_encodeSnappyBetterBlockAsm: + CMPL R8, $0x04 + JB matchlen_match2_match_nolit_encodeSnappyBetterBlockAsm + MOVL (R9)(R12*1), R11 + CMPL (R10)(R12*1), R11 + JNE matchlen_match2_match_nolit_encodeSnappyBetterBlockAsm + LEAL -4(R8), R8 + LEAL 4(R12), R12 + +matchlen_match2_match_nolit_encodeSnappyBetterBlockAsm: + CMPL R8, $0x01 + JE matchlen_match1_match_nolit_encodeSnappyBetterBlockAsm + JB match_nolit_end_encodeSnappyBetterBlockAsm + MOVW (R9)(R12*1), R11 + CMPW (R10)(R12*1), R11 + JNE matchlen_match1_match_nolit_encodeSnappyBetterBlockAsm + LEAL 2(R12), R12 + SUBL $0x02, R8 + JZ match_nolit_end_encodeSnappyBetterBlockAsm + +matchlen_match1_match_nolit_encodeSnappyBetterBlockAsm: + MOVB (R9)(R12*1), R11 + CMPB (R10)(R12*1), R11 + JNE match_nolit_end_encodeSnappyBetterBlockAsm + LEAL 1(R12), R12 + +match_nolit_end_encodeSnappyBetterBlockAsm: + MOVL DX, R8 + SUBL SI, R8 + + // Check if repeat + CMPL R12, $0x01 + JA match_length_ok_encodeSnappyBetterBlockAsm + CMPL R8, $0x0000ffff + JBE match_length_ok_encodeSnappyBetterBlockAsm + MOVL 20(SP), DX + INCL DX + JMP search_loop_encodeSnappyBetterBlockAsm + +match_length_ok_encodeSnappyBetterBlockAsm: + MOVL R8, 16(SP) + MOVL 12(SP), SI + CMPL SI, DI + JEQ emit_literal_done_match_emit_encodeSnappyBetterBlockAsm + MOVL DI, R9 + MOVL DI, 12(SP) + LEAQ (BX)(SI*1), R10 + SUBL SI, R9 + LEAL -1(R9), SI + CMPL SI, $0x3c + JB one_byte_match_emit_encodeSnappyBetterBlockAsm + CMPL SI, $0x00000100 + JB two_bytes_match_emit_encodeSnappyBetterBlockAsm + CMPL SI, $0x00010000 + JB three_bytes_match_emit_encodeSnappyBetterBlockAsm + CMPL SI, $0x01000000 + JB four_bytes_match_emit_encodeSnappyBetterBlockAsm + MOVB $0xfc, (CX) + MOVL SI, 1(CX) + ADDQ $0x05, CX + JMP memmove_long_match_emit_encodeSnappyBetterBlockAsm + +four_bytes_match_emit_encodeSnappyBetterBlockAsm: + MOVL SI, R11 + SHRL $0x10, R11 + MOVB $0xf8, (CX) + MOVW SI, 1(CX) + MOVB R11, 3(CX) + ADDQ $0x04, CX + JMP memmove_long_match_emit_encodeSnappyBetterBlockAsm + +three_bytes_match_emit_encodeSnappyBetterBlockAsm: + MOVB $0xf4, (CX) + MOVW SI, 1(CX) + ADDQ $0x03, CX + JMP memmove_long_match_emit_encodeSnappyBetterBlockAsm + +two_bytes_match_emit_encodeSnappyBetterBlockAsm: + MOVB $0xf0, (CX) + MOVB SI, 1(CX) + ADDQ $0x02, CX + CMPL SI, $0x40 + JB memmove_match_emit_encodeSnappyBetterBlockAsm + JMP memmove_long_match_emit_encodeSnappyBetterBlockAsm + +one_byte_match_emit_encodeSnappyBetterBlockAsm: + SHLB $0x02, SI + MOVB SI, (CX) + ADDQ $0x01, CX + +memmove_match_emit_encodeSnappyBetterBlockAsm: + LEAQ (CX)(R9*1), SI + + // genMemMoveShort + CMPQ R9, $0x08 + JBE emit_lit_memmove_match_emit_encodeSnappyBetterBlockAsm_memmove_move_8 + CMPQ R9, $0x10 + JBE emit_lit_memmove_match_emit_encodeSnappyBetterBlockAsm_memmove_move_8through16 + CMPQ R9, $0x20 + JBE emit_lit_memmove_match_emit_encodeSnappyBetterBlockAsm_memmove_move_17through32 + JMP emit_lit_memmove_match_emit_encodeSnappyBetterBlockAsm_memmove_move_33through64 + +emit_lit_memmove_match_emit_encodeSnappyBetterBlockAsm_memmove_move_8: + MOVQ (R10), R11 + MOVQ R11, (CX) + JMP memmove_end_copy_match_emit_encodeSnappyBetterBlockAsm + +emit_lit_memmove_match_emit_encodeSnappyBetterBlockAsm_memmove_move_8through16: + MOVQ (R10), R11 + MOVQ -8(R10)(R9*1), R10 + MOVQ R11, (CX) + MOVQ R10, -8(CX)(R9*1) + JMP memmove_end_copy_match_emit_encodeSnappyBetterBlockAsm + +emit_lit_memmove_match_emit_encodeSnappyBetterBlockAsm_memmove_move_17through32: + MOVOU (R10), X0 + MOVOU -16(R10)(R9*1), X1 + MOVOU X0, (CX) + MOVOU X1, -16(CX)(R9*1) + JMP memmove_end_copy_match_emit_encodeSnappyBetterBlockAsm + +emit_lit_memmove_match_emit_encodeSnappyBetterBlockAsm_memmove_move_33through64: + MOVOU (R10), X0 + MOVOU 16(R10), X1 + MOVOU -32(R10)(R9*1), X2 + MOVOU -16(R10)(R9*1), X3 + MOVOU X0, (CX) + MOVOU X1, 16(CX) + MOVOU X2, -32(CX)(R9*1) + MOVOU X3, -16(CX)(R9*1) + +memmove_end_copy_match_emit_encodeSnappyBetterBlockAsm: + MOVQ SI, CX + JMP emit_literal_done_match_emit_encodeSnappyBetterBlockAsm + +memmove_long_match_emit_encodeSnappyBetterBlockAsm: + LEAQ (CX)(R9*1), SI + + // genMemMoveLong + MOVOU (R10), X0 + MOVOU 16(R10), X1 + MOVOU -32(R10)(R9*1), X2 + MOVOU -16(R10)(R9*1), X3 + MOVQ R9, R13 + SHRQ $0x05, R13 + MOVQ CX, R11 + ANDL $0x0000001f, R11 + MOVQ $0x00000040, R14 + SUBQ R11, R14 + DECQ R13 + JA emit_lit_memmove_long_match_emit_encodeSnappyBetterBlockAsmlarge_forward_sse_loop_32 + LEAQ -32(R10)(R14*1), R11 + LEAQ -32(CX)(R14*1), R15 + +emit_lit_memmove_long_match_emit_encodeSnappyBetterBlockAsmlarge_big_loop_back: + MOVOU (R11), X4 + MOVOU 16(R11), X5 + MOVOA X4, (R15) + MOVOA X5, 16(R15) + ADDQ $0x20, R15 + ADDQ $0x20, R11 + ADDQ $0x20, R14 + DECQ R13 + JNA emit_lit_memmove_long_match_emit_encodeSnappyBetterBlockAsmlarge_big_loop_back + +emit_lit_memmove_long_match_emit_encodeSnappyBetterBlockAsmlarge_forward_sse_loop_32: + MOVOU -32(R10)(R14*1), X4 + MOVOU -16(R10)(R14*1), X5 + MOVOA X4, -32(CX)(R14*1) + MOVOA X5, -16(CX)(R14*1) + ADDQ $0x20, R14 + CMPQ R9, R14 + JAE emit_lit_memmove_long_match_emit_encodeSnappyBetterBlockAsmlarge_forward_sse_loop_32 + MOVOU X0, (CX) + MOVOU X1, 16(CX) + MOVOU X2, -32(CX)(R9*1) + MOVOU X3, -16(CX)(R9*1) + MOVQ SI, CX + +emit_literal_done_match_emit_encodeSnappyBetterBlockAsm: + ADDL R12, DX + ADDL $0x04, R12 + MOVL DX, 12(SP) + + // emitCopy + CMPL R8, $0x00010000 + JB two_byte_offset_match_nolit_encodeSnappyBetterBlockAsm + +four_bytes_loop_back_match_nolit_encodeSnappyBetterBlockAsm: + CMPL R12, $0x40 + JBE four_bytes_remain_match_nolit_encodeSnappyBetterBlockAsm + MOVB $0xff, (CX) + MOVL R8, 1(CX) + LEAL -64(R12), R12 + ADDQ $0x05, CX + CMPL R12, $0x04 + JB four_bytes_remain_match_nolit_encodeSnappyBetterBlockAsm + JMP four_bytes_loop_back_match_nolit_encodeSnappyBetterBlockAsm + +four_bytes_remain_match_nolit_encodeSnappyBetterBlockAsm: + TESTL R12, R12 + JZ match_nolit_emitcopy_end_encodeSnappyBetterBlockAsm + XORL SI, SI + LEAL -1(SI)(R12*4), R12 + MOVB R12, (CX) + MOVL R8, 1(CX) + ADDQ $0x05, CX + JMP match_nolit_emitcopy_end_encodeSnappyBetterBlockAsm + +two_byte_offset_match_nolit_encodeSnappyBetterBlockAsm: + CMPL R12, $0x40 + JBE two_byte_offset_short_match_nolit_encodeSnappyBetterBlockAsm + MOVB $0xee, (CX) + MOVW R8, 1(CX) + LEAL -60(R12), R12 + ADDQ $0x03, CX + JMP two_byte_offset_match_nolit_encodeSnappyBetterBlockAsm + +two_byte_offset_short_match_nolit_encodeSnappyBetterBlockAsm: + MOVL R12, SI + SHLL $0x02, SI + CMPL R12, $0x0c + JAE emit_copy_three_match_nolit_encodeSnappyBetterBlockAsm + CMPL R8, $0x00000800 + JAE emit_copy_three_match_nolit_encodeSnappyBetterBlockAsm + LEAL -15(SI), SI + MOVB R8, 1(CX) + SHRL $0x08, R8 + SHLL $0x05, R8 + ORL R8, SI + MOVB SI, (CX) + ADDQ $0x02, CX + JMP match_nolit_emitcopy_end_encodeSnappyBetterBlockAsm + +emit_copy_three_match_nolit_encodeSnappyBetterBlockAsm: + LEAL -2(SI), SI + MOVB SI, (CX) + MOVW R8, 1(CX) + ADDQ $0x03, CX + +match_nolit_emitcopy_end_encodeSnappyBetterBlockAsm: + CMPL DX, 8(SP) + JAE emit_remainder_encodeSnappyBetterBlockAsm + CMPQ CX, (SP) + JB match_nolit_dst_ok_encodeSnappyBetterBlockAsm + MOVQ $0x00000000, ret+56(FP) + RET + +match_nolit_dst_ok_encodeSnappyBetterBlockAsm: + MOVQ $0x00cf1bbcdcbfa563, SI + MOVQ $0x9e3779b1, R8 + LEAQ 1(DI), DI + LEAQ -2(DX), R9 + MOVQ (BX)(DI*1), R10 + MOVQ 1(BX)(DI*1), R11 + MOVQ (BX)(R9*1), R12 + MOVQ 1(BX)(R9*1), R13 + SHLQ $0x08, R10 + IMULQ SI, R10 + SHRQ $0x2f, R10 + SHLQ $0x20, R11 + IMULQ R8, R11 + SHRQ $0x32, R11 + SHLQ $0x08, R12 + IMULQ SI, R12 + SHRQ $0x2f, R12 + SHLQ $0x20, R13 + IMULQ R8, R13 + SHRQ $0x32, R13 + LEAQ 1(DI), R8 + LEAQ 1(R9), R14 + MOVL DI, (AX)(R10*4) + MOVL R9, (AX)(R12*4) + MOVL R8, 524288(AX)(R11*4) + MOVL R14, 524288(AX)(R13*4) + LEAQ 1(R9)(DI*1), R8 + SHRQ $0x01, R8 + ADDQ $0x01, DI + SUBQ $0x01, R9 + +index_loop_encodeSnappyBetterBlockAsm: + CMPQ R8, R9 + JAE search_loop_encodeSnappyBetterBlockAsm + MOVQ (BX)(DI*1), R10 + MOVQ (BX)(R8*1), R11 + SHLQ $0x08, R10 + IMULQ SI, R10 + SHRQ $0x2f, R10 + SHLQ $0x08, R11 + IMULQ SI, R11 + SHRQ $0x2f, R11 + MOVL DI, (AX)(R10*4) + MOVL R8, (AX)(R11*4) + ADDQ $0x02, DI + ADDQ $0x02, R8 + JMP index_loop_encodeSnappyBetterBlockAsm + +emit_remainder_encodeSnappyBetterBlockAsm: + MOVQ src_len+32(FP), AX + SUBL 12(SP), AX + LEAQ 5(CX)(AX*1), AX + CMPQ AX, (SP) + JB emit_remainder_ok_encodeSnappyBetterBlockAsm + MOVQ $0x00000000, ret+56(FP) + RET + +emit_remainder_ok_encodeSnappyBetterBlockAsm: + MOVQ src_len+32(FP), AX + MOVL 12(SP), DX + CMPL DX, AX + JEQ emit_literal_done_emit_remainder_encodeSnappyBetterBlockAsm + MOVL AX, SI + MOVL AX, 12(SP) + LEAQ (BX)(DX*1), AX + SUBL DX, SI + LEAL -1(SI), DX + CMPL DX, $0x3c + JB one_byte_emit_remainder_encodeSnappyBetterBlockAsm + CMPL DX, $0x00000100 + JB two_bytes_emit_remainder_encodeSnappyBetterBlockAsm + CMPL DX, $0x00010000 + JB three_bytes_emit_remainder_encodeSnappyBetterBlockAsm + CMPL DX, $0x01000000 + JB four_bytes_emit_remainder_encodeSnappyBetterBlockAsm + MOVB $0xfc, (CX) + MOVL DX, 1(CX) + ADDQ $0x05, CX + JMP memmove_long_emit_remainder_encodeSnappyBetterBlockAsm + +four_bytes_emit_remainder_encodeSnappyBetterBlockAsm: + MOVL DX, BX + SHRL $0x10, BX + MOVB $0xf8, (CX) + MOVW DX, 1(CX) + MOVB BL, 3(CX) + ADDQ $0x04, CX + JMP memmove_long_emit_remainder_encodeSnappyBetterBlockAsm + +three_bytes_emit_remainder_encodeSnappyBetterBlockAsm: + MOVB $0xf4, (CX) + MOVW DX, 1(CX) + ADDQ $0x03, CX + JMP memmove_long_emit_remainder_encodeSnappyBetterBlockAsm + +two_bytes_emit_remainder_encodeSnappyBetterBlockAsm: + MOVB $0xf0, (CX) + MOVB DL, 1(CX) + ADDQ $0x02, CX + CMPL DX, $0x40 + JB memmove_emit_remainder_encodeSnappyBetterBlockAsm + JMP memmove_long_emit_remainder_encodeSnappyBetterBlockAsm + +one_byte_emit_remainder_encodeSnappyBetterBlockAsm: + SHLB $0x02, DL + MOVB DL, (CX) + ADDQ $0x01, CX + +memmove_emit_remainder_encodeSnappyBetterBlockAsm: + LEAQ (CX)(SI*1), DX + MOVL SI, BX + + // genMemMoveShort + CMPQ BX, $0x03 + JB emit_lit_memmove_emit_remainder_encodeSnappyBetterBlockAsm_memmove_move_1or2 + JE emit_lit_memmove_emit_remainder_encodeSnappyBetterBlockAsm_memmove_move_3 + CMPQ BX, $0x08 + JB emit_lit_memmove_emit_remainder_encodeSnappyBetterBlockAsm_memmove_move_4through7 + CMPQ BX, $0x10 + JBE emit_lit_memmove_emit_remainder_encodeSnappyBetterBlockAsm_memmove_move_8through16 + CMPQ BX, $0x20 + JBE emit_lit_memmove_emit_remainder_encodeSnappyBetterBlockAsm_memmove_move_17through32 + JMP emit_lit_memmove_emit_remainder_encodeSnappyBetterBlockAsm_memmove_move_33through64 + +emit_lit_memmove_emit_remainder_encodeSnappyBetterBlockAsm_memmove_move_1or2: + MOVB (AX), SI + MOVB -1(AX)(BX*1), AL + MOVB SI, (CX) + MOVB AL, -1(CX)(BX*1) + JMP memmove_end_copy_emit_remainder_encodeSnappyBetterBlockAsm + +emit_lit_memmove_emit_remainder_encodeSnappyBetterBlockAsm_memmove_move_3: + MOVW (AX), SI + MOVB 2(AX), AL + MOVW SI, (CX) + MOVB AL, 2(CX) + JMP memmove_end_copy_emit_remainder_encodeSnappyBetterBlockAsm + +emit_lit_memmove_emit_remainder_encodeSnappyBetterBlockAsm_memmove_move_4through7: + MOVL (AX), SI + MOVL -4(AX)(BX*1), AX + MOVL SI, (CX) + MOVL AX, -4(CX)(BX*1) + JMP memmove_end_copy_emit_remainder_encodeSnappyBetterBlockAsm + +emit_lit_memmove_emit_remainder_encodeSnappyBetterBlockAsm_memmove_move_8through16: + MOVQ (AX), SI + MOVQ -8(AX)(BX*1), AX + MOVQ SI, (CX) + MOVQ AX, -8(CX)(BX*1) + JMP memmove_end_copy_emit_remainder_encodeSnappyBetterBlockAsm + +emit_lit_memmove_emit_remainder_encodeSnappyBetterBlockAsm_memmove_move_17through32: + MOVOU (AX), X0 + MOVOU -16(AX)(BX*1), X1 + MOVOU X0, (CX) + MOVOU X1, -16(CX)(BX*1) + JMP memmove_end_copy_emit_remainder_encodeSnappyBetterBlockAsm + +emit_lit_memmove_emit_remainder_encodeSnappyBetterBlockAsm_memmove_move_33through64: + MOVOU (AX), X0 + MOVOU 16(AX), X1 + MOVOU -32(AX)(BX*1), X2 + MOVOU -16(AX)(BX*1), X3 + MOVOU X0, (CX) + MOVOU X1, 16(CX) + MOVOU X2, -32(CX)(BX*1) + MOVOU X3, -16(CX)(BX*1) + +memmove_end_copy_emit_remainder_encodeSnappyBetterBlockAsm: + MOVQ DX, CX + JMP emit_literal_done_emit_remainder_encodeSnappyBetterBlockAsm + +memmove_long_emit_remainder_encodeSnappyBetterBlockAsm: + LEAQ (CX)(SI*1), DX + MOVL SI, BX + + // genMemMoveLong + MOVOU (AX), X0 + MOVOU 16(AX), X1 + MOVOU -32(AX)(BX*1), X2 + MOVOU -16(AX)(BX*1), X3 + MOVQ BX, DI + SHRQ $0x05, DI + MOVQ CX, SI + ANDL $0x0000001f, SI + MOVQ $0x00000040, R8 + SUBQ SI, R8 + DECQ DI + JA emit_lit_memmove_long_emit_remainder_encodeSnappyBetterBlockAsmlarge_forward_sse_loop_32 + LEAQ -32(AX)(R8*1), SI + LEAQ -32(CX)(R8*1), R9 + +emit_lit_memmove_long_emit_remainder_encodeSnappyBetterBlockAsmlarge_big_loop_back: + MOVOU (SI), X4 + MOVOU 16(SI), X5 + MOVOA X4, (R9) + MOVOA X5, 16(R9) + ADDQ $0x20, R9 + ADDQ $0x20, SI + ADDQ $0x20, R8 + DECQ DI + JNA emit_lit_memmove_long_emit_remainder_encodeSnappyBetterBlockAsmlarge_big_loop_back + +emit_lit_memmove_long_emit_remainder_encodeSnappyBetterBlockAsmlarge_forward_sse_loop_32: + MOVOU -32(AX)(R8*1), X4 + MOVOU -16(AX)(R8*1), X5 + MOVOA X4, -32(CX)(R8*1) + MOVOA X5, -16(CX)(R8*1) + ADDQ $0x20, R8 + CMPQ BX, R8 + JAE emit_lit_memmove_long_emit_remainder_encodeSnappyBetterBlockAsmlarge_forward_sse_loop_32 + MOVOU X0, (CX) + MOVOU X1, 16(CX) + MOVOU X2, -32(CX)(BX*1) + MOVOU X3, -16(CX)(BX*1) + MOVQ DX, CX + +emit_literal_done_emit_remainder_encodeSnappyBetterBlockAsm: + MOVQ dst_base+0(FP), AX + SUBQ AX, CX + MOVQ CX, ret+56(FP) + RET + +// func encodeSnappyBetterBlockAsm64K(dst []byte, src []byte, tmp *[294912]byte) int +// Requires: BMI, SSE2 +TEXT ·encodeSnappyBetterBlockAsm64K(SB), $24-64 + MOVQ tmp+48(FP), AX + MOVQ dst_base+0(FP), CX + MOVQ $0x00000900, DX + MOVQ AX, BX + PXOR X0, X0 + +zero_loop_encodeSnappyBetterBlockAsm64K: + MOVOU X0, (BX) + MOVOU X0, 16(BX) + MOVOU X0, 32(BX) + MOVOU X0, 48(BX) + MOVOU X0, 64(BX) + MOVOU X0, 80(BX) + MOVOU X0, 96(BX) + MOVOU X0, 112(BX) + ADDQ $0x80, BX + DECQ DX + JNZ zero_loop_encodeSnappyBetterBlockAsm64K + MOVL $0x00000000, 12(SP) + MOVQ src_len+32(FP), DX + LEAQ -9(DX), BX + LEAQ -8(DX), SI + MOVL SI, 8(SP) + SHRQ $0x05, DX + SUBL DX, BX + LEAQ (CX)(BX*1), BX + MOVQ BX, (SP) + MOVL $0x00000001, DX + MOVL $0x00000000, 16(SP) + MOVQ src_base+24(FP), BX + +search_loop_encodeSnappyBetterBlockAsm64K: + MOVL DX, SI + SUBL 12(SP), SI + SHRL $0x07, SI + LEAL 1(DX)(SI*1), SI + CMPL SI, 8(SP) + JAE emit_remainder_encodeSnappyBetterBlockAsm64K + MOVQ (BX)(DX*1), DI + MOVL SI, 20(SP) + MOVQ $0x00cf1bbcdcbfa563, R9 + MOVQ $0x9e3779b1, SI + MOVQ DI, R10 + MOVQ DI, R11 + SHLQ $0x08, R10 + IMULQ R9, R10 + SHRQ $0x30, R10 + SHLQ $0x20, R11 + IMULQ SI, R11 + SHRQ $0x33, R11 + MOVL (AX)(R10*4), SI + MOVL 262144(AX)(R11*4), R8 + MOVL DX, (AX)(R10*4) + MOVL DX, 262144(AX)(R11*4) + MOVQ (BX)(SI*1), R10 + MOVQ (BX)(R8*1), R11 + CMPQ R10, DI + JEQ candidate_match_encodeSnappyBetterBlockAsm64K + CMPQ R11, DI + JNE no_short_found_encodeSnappyBetterBlockAsm64K + MOVL R8, SI + JMP candidate_match_encodeSnappyBetterBlockAsm64K + +no_short_found_encodeSnappyBetterBlockAsm64K: + CMPL R10, DI + JEQ candidate_match_encodeSnappyBetterBlockAsm64K + CMPL R11, DI + JEQ candidateS_match_encodeSnappyBetterBlockAsm64K + MOVL 20(SP), DX + JMP search_loop_encodeSnappyBetterBlockAsm64K + +candidateS_match_encodeSnappyBetterBlockAsm64K: + SHRQ $0x08, DI + MOVQ DI, R10 + SHLQ $0x08, R10 + IMULQ R9, R10 + SHRQ $0x30, R10 + MOVL (AX)(R10*4), SI + INCL DX + MOVL DX, (AX)(R10*4) + CMPL (BX)(SI*1), DI + JEQ candidate_match_encodeSnappyBetterBlockAsm64K + DECL DX + MOVL R8, SI + +candidate_match_encodeSnappyBetterBlockAsm64K: + MOVL 12(SP), DI + TESTL SI, SI + JZ match_extend_back_end_encodeSnappyBetterBlockAsm64K + +match_extend_back_loop_encodeSnappyBetterBlockAsm64K: + CMPL DX, DI + JBE match_extend_back_end_encodeSnappyBetterBlockAsm64K + MOVB -1(BX)(SI*1), R8 + MOVB -1(BX)(DX*1), R9 + CMPB R8, R9 + JNE match_extend_back_end_encodeSnappyBetterBlockAsm64K + LEAL -1(DX), DX + DECL SI + JZ match_extend_back_end_encodeSnappyBetterBlockAsm64K + JMP match_extend_back_loop_encodeSnappyBetterBlockAsm64K + +match_extend_back_end_encodeSnappyBetterBlockAsm64K: + MOVL DX, DI + SUBL 12(SP), DI + LEAQ 3(CX)(DI*1), DI + CMPQ DI, (SP) + JB match_dst_size_check_encodeSnappyBetterBlockAsm64K + MOVQ $0x00000000, ret+56(FP) + RET + +match_dst_size_check_encodeSnappyBetterBlockAsm64K: + MOVL DX, DI + ADDL $0x04, DX + ADDL $0x04, SI + MOVQ src_len+32(FP), R8 + SUBL DX, R8 + LEAQ (BX)(DX*1), R9 + LEAQ (BX)(SI*1), R10 + + // matchLen + XORL R12, R12 + +matchlen_loopback_16_match_nolit_encodeSnappyBetterBlockAsm64K: + CMPL R8, $0x10 + JB matchlen_match8_match_nolit_encodeSnappyBetterBlockAsm64K + MOVQ (R9)(R12*1), R11 + MOVQ 8(R9)(R12*1), R13 + XORQ (R10)(R12*1), R11 + JNZ matchlen_bsf_8_match_nolit_encodeSnappyBetterBlockAsm64K + XORQ 8(R10)(R12*1), R13 + JNZ matchlen_bsf_16match_nolit_encodeSnappyBetterBlockAsm64K + LEAL -16(R8), R8 + LEAL 16(R12), R12 + JMP matchlen_loopback_16_match_nolit_encodeSnappyBetterBlockAsm64K + +matchlen_bsf_16match_nolit_encodeSnappyBetterBlockAsm64K: +#ifdef GOAMD64_v3 + TZCNTQ R13, R13 + +#else + BSFQ R13, R13 + +#endif + SARQ $0x03, R13 + LEAL 8(R12)(R13*1), R12 + JMP match_nolit_end_encodeSnappyBetterBlockAsm64K + +matchlen_match8_match_nolit_encodeSnappyBetterBlockAsm64K: + CMPL R8, $0x08 + JB matchlen_match4_match_nolit_encodeSnappyBetterBlockAsm64K + MOVQ (R9)(R12*1), R11 + XORQ (R10)(R12*1), R11 + JNZ matchlen_bsf_8_match_nolit_encodeSnappyBetterBlockAsm64K + LEAL -8(R8), R8 + LEAL 8(R12), R12 + JMP matchlen_match4_match_nolit_encodeSnappyBetterBlockAsm64K + +matchlen_bsf_8_match_nolit_encodeSnappyBetterBlockAsm64K: +#ifdef GOAMD64_v3 + TZCNTQ R11, R11 + +#else + BSFQ R11, R11 + +#endif + SARQ $0x03, R11 + LEAL (R12)(R11*1), R12 + JMP match_nolit_end_encodeSnappyBetterBlockAsm64K + +matchlen_match4_match_nolit_encodeSnappyBetterBlockAsm64K: + CMPL R8, $0x04 + JB matchlen_match2_match_nolit_encodeSnappyBetterBlockAsm64K + MOVL (R9)(R12*1), R11 + CMPL (R10)(R12*1), R11 + JNE matchlen_match2_match_nolit_encodeSnappyBetterBlockAsm64K + LEAL -4(R8), R8 + LEAL 4(R12), R12 + +matchlen_match2_match_nolit_encodeSnappyBetterBlockAsm64K: + CMPL R8, $0x01 + JE matchlen_match1_match_nolit_encodeSnappyBetterBlockAsm64K + JB match_nolit_end_encodeSnappyBetterBlockAsm64K + MOVW (R9)(R12*1), R11 + CMPW (R10)(R12*1), R11 + JNE matchlen_match1_match_nolit_encodeSnappyBetterBlockAsm64K + LEAL 2(R12), R12 + SUBL $0x02, R8 + JZ match_nolit_end_encodeSnappyBetterBlockAsm64K + +matchlen_match1_match_nolit_encodeSnappyBetterBlockAsm64K: + MOVB (R9)(R12*1), R11 + CMPB (R10)(R12*1), R11 + JNE match_nolit_end_encodeSnappyBetterBlockAsm64K + LEAL 1(R12), R12 + +match_nolit_end_encodeSnappyBetterBlockAsm64K: + MOVL DX, R8 + SUBL SI, R8 + + // Check if repeat + MOVL R8, 16(SP) + MOVL 12(SP), SI + CMPL SI, DI + JEQ emit_literal_done_match_emit_encodeSnappyBetterBlockAsm64K + MOVL DI, R9 + MOVL DI, 12(SP) + LEAQ (BX)(SI*1), R10 + SUBL SI, R9 + LEAL -1(R9), SI + CMPL SI, $0x3c + JB one_byte_match_emit_encodeSnappyBetterBlockAsm64K + CMPL SI, $0x00000100 + JB two_bytes_match_emit_encodeSnappyBetterBlockAsm64K + JB three_bytes_match_emit_encodeSnappyBetterBlockAsm64K + +three_bytes_match_emit_encodeSnappyBetterBlockAsm64K: + MOVB $0xf4, (CX) + MOVW SI, 1(CX) + ADDQ $0x03, CX + JMP memmove_long_match_emit_encodeSnappyBetterBlockAsm64K + +two_bytes_match_emit_encodeSnappyBetterBlockAsm64K: + MOVB $0xf0, (CX) + MOVB SI, 1(CX) + ADDQ $0x02, CX + CMPL SI, $0x40 + JB memmove_match_emit_encodeSnappyBetterBlockAsm64K + JMP memmove_long_match_emit_encodeSnappyBetterBlockAsm64K + +one_byte_match_emit_encodeSnappyBetterBlockAsm64K: + SHLB $0x02, SI + MOVB SI, (CX) + ADDQ $0x01, CX + +memmove_match_emit_encodeSnappyBetterBlockAsm64K: + LEAQ (CX)(R9*1), SI + + // genMemMoveShort + CMPQ R9, $0x08 + JBE emit_lit_memmove_match_emit_encodeSnappyBetterBlockAsm64K_memmove_move_8 + CMPQ R9, $0x10 + JBE emit_lit_memmove_match_emit_encodeSnappyBetterBlockAsm64K_memmove_move_8through16 + CMPQ R9, $0x20 + JBE emit_lit_memmove_match_emit_encodeSnappyBetterBlockAsm64K_memmove_move_17through32 + JMP emit_lit_memmove_match_emit_encodeSnappyBetterBlockAsm64K_memmove_move_33through64 + +emit_lit_memmove_match_emit_encodeSnappyBetterBlockAsm64K_memmove_move_8: + MOVQ (R10), R11 + MOVQ R11, (CX) + JMP memmove_end_copy_match_emit_encodeSnappyBetterBlockAsm64K + +emit_lit_memmove_match_emit_encodeSnappyBetterBlockAsm64K_memmove_move_8through16: + MOVQ (R10), R11 + MOVQ -8(R10)(R9*1), R10 + MOVQ R11, (CX) + MOVQ R10, -8(CX)(R9*1) + JMP memmove_end_copy_match_emit_encodeSnappyBetterBlockAsm64K + +emit_lit_memmove_match_emit_encodeSnappyBetterBlockAsm64K_memmove_move_17through32: + MOVOU (R10), X0 + MOVOU -16(R10)(R9*1), X1 + MOVOU X0, (CX) + MOVOU X1, -16(CX)(R9*1) + JMP memmove_end_copy_match_emit_encodeSnappyBetterBlockAsm64K + +emit_lit_memmove_match_emit_encodeSnappyBetterBlockAsm64K_memmove_move_33through64: + MOVOU (R10), X0 + MOVOU 16(R10), X1 + MOVOU -32(R10)(R9*1), X2 + MOVOU -16(R10)(R9*1), X3 + MOVOU X0, (CX) + MOVOU X1, 16(CX) + MOVOU X2, -32(CX)(R9*1) + MOVOU X3, -16(CX)(R9*1) + +memmove_end_copy_match_emit_encodeSnappyBetterBlockAsm64K: + MOVQ SI, CX + JMP emit_literal_done_match_emit_encodeSnappyBetterBlockAsm64K + +memmove_long_match_emit_encodeSnappyBetterBlockAsm64K: + LEAQ (CX)(R9*1), SI + + // genMemMoveLong + MOVOU (R10), X0 + MOVOU 16(R10), X1 + MOVOU -32(R10)(R9*1), X2 + MOVOU -16(R10)(R9*1), X3 + MOVQ R9, R13 + SHRQ $0x05, R13 + MOVQ CX, R11 + ANDL $0x0000001f, R11 + MOVQ $0x00000040, R14 + SUBQ R11, R14 + DECQ R13 + JA emit_lit_memmove_long_match_emit_encodeSnappyBetterBlockAsm64Klarge_forward_sse_loop_32 + LEAQ -32(R10)(R14*1), R11 + LEAQ -32(CX)(R14*1), R15 + +emit_lit_memmove_long_match_emit_encodeSnappyBetterBlockAsm64Klarge_big_loop_back: + MOVOU (R11), X4 + MOVOU 16(R11), X5 + MOVOA X4, (R15) + MOVOA X5, 16(R15) + ADDQ $0x20, R15 + ADDQ $0x20, R11 + ADDQ $0x20, R14 + DECQ R13 + JNA emit_lit_memmove_long_match_emit_encodeSnappyBetterBlockAsm64Klarge_big_loop_back + +emit_lit_memmove_long_match_emit_encodeSnappyBetterBlockAsm64Klarge_forward_sse_loop_32: + MOVOU -32(R10)(R14*1), X4 + MOVOU -16(R10)(R14*1), X5 + MOVOA X4, -32(CX)(R14*1) + MOVOA X5, -16(CX)(R14*1) + ADDQ $0x20, R14 + CMPQ R9, R14 + JAE emit_lit_memmove_long_match_emit_encodeSnappyBetterBlockAsm64Klarge_forward_sse_loop_32 + MOVOU X0, (CX) + MOVOU X1, 16(CX) + MOVOU X2, -32(CX)(R9*1) + MOVOU X3, -16(CX)(R9*1) + MOVQ SI, CX + +emit_literal_done_match_emit_encodeSnappyBetterBlockAsm64K: + ADDL R12, DX + ADDL $0x04, R12 + MOVL DX, 12(SP) + + // emitCopy +two_byte_offset_match_nolit_encodeSnappyBetterBlockAsm64K: + CMPL R12, $0x40 + JBE two_byte_offset_short_match_nolit_encodeSnappyBetterBlockAsm64K + MOVB $0xee, (CX) + MOVW R8, 1(CX) + LEAL -60(R12), R12 + ADDQ $0x03, CX + JMP two_byte_offset_match_nolit_encodeSnappyBetterBlockAsm64K + +two_byte_offset_short_match_nolit_encodeSnappyBetterBlockAsm64K: + MOVL R12, SI + SHLL $0x02, SI + CMPL R12, $0x0c + JAE emit_copy_three_match_nolit_encodeSnappyBetterBlockAsm64K + CMPL R8, $0x00000800 + JAE emit_copy_three_match_nolit_encodeSnappyBetterBlockAsm64K + LEAL -15(SI), SI + MOVB R8, 1(CX) + SHRL $0x08, R8 + SHLL $0x05, R8 + ORL R8, SI + MOVB SI, (CX) + ADDQ $0x02, CX + JMP match_nolit_emitcopy_end_encodeSnappyBetterBlockAsm64K + +emit_copy_three_match_nolit_encodeSnappyBetterBlockAsm64K: + LEAL -2(SI), SI + MOVB SI, (CX) + MOVW R8, 1(CX) + ADDQ $0x03, CX + +match_nolit_emitcopy_end_encodeSnappyBetterBlockAsm64K: + CMPL DX, 8(SP) + JAE emit_remainder_encodeSnappyBetterBlockAsm64K + CMPQ CX, (SP) + JB match_nolit_dst_ok_encodeSnappyBetterBlockAsm64K + MOVQ $0x00000000, ret+56(FP) + RET + +match_nolit_dst_ok_encodeSnappyBetterBlockAsm64K: + MOVQ $0x00cf1bbcdcbfa563, SI + MOVQ $0x9e3779b1, R8 + LEAQ 1(DI), DI + LEAQ -2(DX), R9 + MOVQ (BX)(DI*1), R10 + MOVQ 1(BX)(DI*1), R11 + MOVQ (BX)(R9*1), R12 + MOVQ 1(BX)(R9*1), R13 + SHLQ $0x08, R10 + IMULQ SI, R10 + SHRQ $0x30, R10 + SHLQ $0x20, R11 + IMULQ R8, R11 + SHRQ $0x33, R11 + SHLQ $0x08, R12 + IMULQ SI, R12 + SHRQ $0x30, R12 + SHLQ $0x20, R13 + IMULQ R8, R13 + SHRQ $0x33, R13 + LEAQ 1(DI), R8 + LEAQ 1(R9), R14 + MOVL DI, (AX)(R10*4) + MOVL R9, (AX)(R12*4) + MOVL R8, 262144(AX)(R11*4) + MOVL R14, 262144(AX)(R13*4) + LEAQ 1(R9)(DI*1), R8 + SHRQ $0x01, R8 + ADDQ $0x01, DI + SUBQ $0x01, R9 + +index_loop_encodeSnappyBetterBlockAsm64K: + CMPQ R8, R9 + JAE search_loop_encodeSnappyBetterBlockAsm64K + MOVQ (BX)(DI*1), R10 + MOVQ (BX)(R8*1), R11 + SHLQ $0x08, R10 + IMULQ SI, R10 + SHRQ $0x30, R10 + SHLQ $0x08, R11 + IMULQ SI, R11 + SHRQ $0x30, R11 + MOVL DI, (AX)(R10*4) + MOVL R8, (AX)(R11*4) + ADDQ $0x02, DI + ADDQ $0x02, R8 + JMP index_loop_encodeSnappyBetterBlockAsm64K + +emit_remainder_encodeSnappyBetterBlockAsm64K: + MOVQ src_len+32(FP), AX + SUBL 12(SP), AX + LEAQ 3(CX)(AX*1), AX + CMPQ AX, (SP) + JB emit_remainder_ok_encodeSnappyBetterBlockAsm64K + MOVQ $0x00000000, ret+56(FP) + RET + +emit_remainder_ok_encodeSnappyBetterBlockAsm64K: + MOVQ src_len+32(FP), AX + MOVL 12(SP), DX + CMPL DX, AX + JEQ emit_literal_done_emit_remainder_encodeSnappyBetterBlockAsm64K + MOVL AX, SI + MOVL AX, 12(SP) + LEAQ (BX)(DX*1), AX + SUBL DX, SI + LEAL -1(SI), DX + CMPL DX, $0x3c + JB one_byte_emit_remainder_encodeSnappyBetterBlockAsm64K + CMPL DX, $0x00000100 + JB two_bytes_emit_remainder_encodeSnappyBetterBlockAsm64K + JB three_bytes_emit_remainder_encodeSnappyBetterBlockAsm64K + +three_bytes_emit_remainder_encodeSnappyBetterBlockAsm64K: + MOVB $0xf4, (CX) + MOVW DX, 1(CX) + ADDQ $0x03, CX + JMP memmove_long_emit_remainder_encodeSnappyBetterBlockAsm64K + +two_bytes_emit_remainder_encodeSnappyBetterBlockAsm64K: + MOVB $0xf0, (CX) + MOVB DL, 1(CX) + ADDQ $0x02, CX + CMPL DX, $0x40 + JB memmove_emit_remainder_encodeSnappyBetterBlockAsm64K + JMP memmove_long_emit_remainder_encodeSnappyBetterBlockAsm64K + +one_byte_emit_remainder_encodeSnappyBetterBlockAsm64K: + SHLB $0x02, DL + MOVB DL, (CX) + ADDQ $0x01, CX + +memmove_emit_remainder_encodeSnappyBetterBlockAsm64K: + LEAQ (CX)(SI*1), DX + MOVL SI, BX + + // genMemMoveShort + CMPQ BX, $0x03 + JB emit_lit_memmove_emit_remainder_encodeSnappyBetterBlockAsm64K_memmove_move_1or2 + JE emit_lit_memmove_emit_remainder_encodeSnappyBetterBlockAsm64K_memmove_move_3 + CMPQ BX, $0x08 + JB emit_lit_memmove_emit_remainder_encodeSnappyBetterBlockAsm64K_memmove_move_4through7 + CMPQ BX, $0x10 + JBE emit_lit_memmove_emit_remainder_encodeSnappyBetterBlockAsm64K_memmove_move_8through16 + CMPQ BX, $0x20 + JBE emit_lit_memmove_emit_remainder_encodeSnappyBetterBlockAsm64K_memmove_move_17through32 + JMP emit_lit_memmove_emit_remainder_encodeSnappyBetterBlockAsm64K_memmove_move_33through64 + +emit_lit_memmove_emit_remainder_encodeSnappyBetterBlockAsm64K_memmove_move_1or2: + MOVB (AX), SI + MOVB -1(AX)(BX*1), AL + MOVB SI, (CX) + MOVB AL, -1(CX)(BX*1) + JMP memmove_end_copy_emit_remainder_encodeSnappyBetterBlockAsm64K + +emit_lit_memmove_emit_remainder_encodeSnappyBetterBlockAsm64K_memmove_move_3: + MOVW (AX), SI + MOVB 2(AX), AL + MOVW SI, (CX) + MOVB AL, 2(CX) + JMP memmove_end_copy_emit_remainder_encodeSnappyBetterBlockAsm64K + +emit_lit_memmove_emit_remainder_encodeSnappyBetterBlockAsm64K_memmove_move_4through7: + MOVL (AX), SI + MOVL -4(AX)(BX*1), AX + MOVL SI, (CX) + MOVL AX, -4(CX)(BX*1) + JMP memmove_end_copy_emit_remainder_encodeSnappyBetterBlockAsm64K + +emit_lit_memmove_emit_remainder_encodeSnappyBetterBlockAsm64K_memmove_move_8through16: + MOVQ (AX), SI + MOVQ -8(AX)(BX*1), AX + MOVQ SI, (CX) + MOVQ AX, -8(CX)(BX*1) + JMP memmove_end_copy_emit_remainder_encodeSnappyBetterBlockAsm64K + +emit_lit_memmove_emit_remainder_encodeSnappyBetterBlockAsm64K_memmove_move_17through32: + MOVOU (AX), X0 + MOVOU -16(AX)(BX*1), X1 + MOVOU X0, (CX) + MOVOU X1, -16(CX)(BX*1) + JMP memmove_end_copy_emit_remainder_encodeSnappyBetterBlockAsm64K + +emit_lit_memmove_emit_remainder_encodeSnappyBetterBlockAsm64K_memmove_move_33through64: + MOVOU (AX), X0 + MOVOU 16(AX), X1 + MOVOU -32(AX)(BX*1), X2 + MOVOU -16(AX)(BX*1), X3 + MOVOU X0, (CX) + MOVOU X1, 16(CX) + MOVOU X2, -32(CX)(BX*1) + MOVOU X3, -16(CX)(BX*1) + +memmove_end_copy_emit_remainder_encodeSnappyBetterBlockAsm64K: + MOVQ DX, CX + JMP emit_literal_done_emit_remainder_encodeSnappyBetterBlockAsm64K + +memmove_long_emit_remainder_encodeSnappyBetterBlockAsm64K: + LEAQ (CX)(SI*1), DX + MOVL SI, BX + + // genMemMoveLong + MOVOU (AX), X0 + MOVOU 16(AX), X1 + MOVOU -32(AX)(BX*1), X2 + MOVOU -16(AX)(BX*1), X3 + MOVQ BX, DI + SHRQ $0x05, DI + MOVQ CX, SI + ANDL $0x0000001f, SI + MOVQ $0x00000040, R8 + SUBQ SI, R8 + DECQ DI + JA emit_lit_memmove_long_emit_remainder_encodeSnappyBetterBlockAsm64Klarge_forward_sse_loop_32 + LEAQ -32(AX)(R8*1), SI + LEAQ -32(CX)(R8*1), R9 + +emit_lit_memmove_long_emit_remainder_encodeSnappyBetterBlockAsm64Klarge_big_loop_back: + MOVOU (SI), X4 + MOVOU 16(SI), X5 + MOVOA X4, (R9) + MOVOA X5, 16(R9) + ADDQ $0x20, R9 + ADDQ $0x20, SI + ADDQ $0x20, R8 + DECQ DI + JNA emit_lit_memmove_long_emit_remainder_encodeSnappyBetterBlockAsm64Klarge_big_loop_back + +emit_lit_memmove_long_emit_remainder_encodeSnappyBetterBlockAsm64Klarge_forward_sse_loop_32: + MOVOU -32(AX)(R8*1), X4 + MOVOU -16(AX)(R8*1), X5 + MOVOA X4, -32(CX)(R8*1) + MOVOA X5, -16(CX)(R8*1) + ADDQ $0x20, R8 + CMPQ BX, R8 + JAE emit_lit_memmove_long_emit_remainder_encodeSnappyBetterBlockAsm64Klarge_forward_sse_loop_32 + MOVOU X0, (CX) + MOVOU X1, 16(CX) + MOVOU X2, -32(CX)(BX*1) + MOVOU X3, -16(CX)(BX*1) + MOVQ DX, CX + +emit_literal_done_emit_remainder_encodeSnappyBetterBlockAsm64K: + MOVQ dst_base+0(FP), AX + SUBQ AX, CX + MOVQ CX, ret+56(FP) + RET + +// func encodeSnappyBetterBlockAsm12B(dst []byte, src []byte, tmp *[81920]byte) int +// Requires: BMI, SSE2 +TEXT ·encodeSnappyBetterBlockAsm12B(SB), $24-64 + MOVQ tmp+48(FP), AX + MOVQ dst_base+0(FP), CX + MOVQ $0x00000280, DX + MOVQ AX, BX + PXOR X0, X0 + +zero_loop_encodeSnappyBetterBlockAsm12B: + MOVOU X0, (BX) + MOVOU X0, 16(BX) + MOVOU X0, 32(BX) + MOVOU X0, 48(BX) + MOVOU X0, 64(BX) + MOVOU X0, 80(BX) + MOVOU X0, 96(BX) + MOVOU X0, 112(BX) + ADDQ $0x80, BX + DECQ DX + JNZ zero_loop_encodeSnappyBetterBlockAsm12B + MOVL $0x00000000, 12(SP) + MOVQ src_len+32(FP), DX + LEAQ -9(DX), BX + LEAQ -8(DX), SI + MOVL SI, 8(SP) + SHRQ $0x05, DX + SUBL DX, BX + LEAQ (CX)(BX*1), BX + MOVQ BX, (SP) + MOVL $0x00000001, DX + MOVL $0x00000000, 16(SP) + MOVQ src_base+24(FP), BX + +search_loop_encodeSnappyBetterBlockAsm12B: + MOVL DX, SI + SUBL 12(SP), SI + SHRL $0x06, SI + LEAL 1(DX)(SI*1), SI + CMPL SI, 8(SP) + JAE emit_remainder_encodeSnappyBetterBlockAsm12B + MOVQ (BX)(DX*1), DI + MOVL SI, 20(SP) + MOVQ $0x0000cf1bbcdcbf9b, R9 + MOVQ $0x9e3779b1, SI + MOVQ DI, R10 + MOVQ DI, R11 + SHLQ $0x10, R10 + IMULQ R9, R10 + SHRQ $0x32, R10 + SHLQ $0x20, R11 + IMULQ SI, R11 + SHRQ $0x34, R11 + MOVL (AX)(R10*4), SI + MOVL 65536(AX)(R11*4), R8 + MOVL DX, (AX)(R10*4) + MOVL DX, 65536(AX)(R11*4) + MOVQ (BX)(SI*1), R10 + MOVQ (BX)(R8*1), R11 + CMPQ R10, DI + JEQ candidate_match_encodeSnappyBetterBlockAsm12B + CMPQ R11, DI + JNE no_short_found_encodeSnappyBetterBlockAsm12B + MOVL R8, SI + JMP candidate_match_encodeSnappyBetterBlockAsm12B + +no_short_found_encodeSnappyBetterBlockAsm12B: + CMPL R10, DI + JEQ candidate_match_encodeSnappyBetterBlockAsm12B + CMPL R11, DI + JEQ candidateS_match_encodeSnappyBetterBlockAsm12B + MOVL 20(SP), DX + JMP search_loop_encodeSnappyBetterBlockAsm12B + +candidateS_match_encodeSnappyBetterBlockAsm12B: + SHRQ $0x08, DI + MOVQ DI, R10 + SHLQ $0x10, R10 + IMULQ R9, R10 + SHRQ $0x32, R10 + MOVL (AX)(R10*4), SI + INCL DX + MOVL DX, (AX)(R10*4) + CMPL (BX)(SI*1), DI + JEQ candidate_match_encodeSnappyBetterBlockAsm12B + DECL DX + MOVL R8, SI + +candidate_match_encodeSnappyBetterBlockAsm12B: + MOVL 12(SP), DI + TESTL SI, SI + JZ match_extend_back_end_encodeSnappyBetterBlockAsm12B + +match_extend_back_loop_encodeSnappyBetterBlockAsm12B: + CMPL DX, DI + JBE match_extend_back_end_encodeSnappyBetterBlockAsm12B + MOVB -1(BX)(SI*1), R8 + MOVB -1(BX)(DX*1), R9 + CMPB R8, R9 + JNE match_extend_back_end_encodeSnappyBetterBlockAsm12B + LEAL -1(DX), DX + DECL SI + JZ match_extend_back_end_encodeSnappyBetterBlockAsm12B + JMP match_extend_back_loop_encodeSnappyBetterBlockAsm12B + +match_extend_back_end_encodeSnappyBetterBlockAsm12B: + MOVL DX, DI + SUBL 12(SP), DI + LEAQ 3(CX)(DI*1), DI + CMPQ DI, (SP) + JB match_dst_size_check_encodeSnappyBetterBlockAsm12B + MOVQ $0x00000000, ret+56(FP) + RET + +match_dst_size_check_encodeSnappyBetterBlockAsm12B: + MOVL DX, DI + ADDL $0x04, DX + ADDL $0x04, SI + MOVQ src_len+32(FP), R8 + SUBL DX, R8 + LEAQ (BX)(DX*1), R9 + LEAQ (BX)(SI*1), R10 + + // matchLen + XORL R12, R12 + +matchlen_loopback_16_match_nolit_encodeSnappyBetterBlockAsm12B: + CMPL R8, $0x10 + JB matchlen_match8_match_nolit_encodeSnappyBetterBlockAsm12B + MOVQ (R9)(R12*1), R11 + MOVQ 8(R9)(R12*1), R13 + XORQ (R10)(R12*1), R11 + JNZ matchlen_bsf_8_match_nolit_encodeSnappyBetterBlockAsm12B + XORQ 8(R10)(R12*1), R13 + JNZ matchlen_bsf_16match_nolit_encodeSnappyBetterBlockAsm12B + LEAL -16(R8), R8 + LEAL 16(R12), R12 + JMP matchlen_loopback_16_match_nolit_encodeSnappyBetterBlockAsm12B + +matchlen_bsf_16match_nolit_encodeSnappyBetterBlockAsm12B: +#ifdef GOAMD64_v3 + TZCNTQ R13, R13 + +#else + BSFQ R13, R13 + +#endif + SARQ $0x03, R13 + LEAL 8(R12)(R13*1), R12 + JMP match_nolit_end_encodeSnappyBetterBlockAsm12B + +matchlen_match8_match_nolit_encodeSnappyBetterBlockAsm12B: + CMPL R8, $0x08 + JB matchlen_match4_match_nolit_encodeSnappyBetterBlockAsm12B + MOVQ (R9)(R12*1), R11 + XORQ (R10)(R12*1), R11 + JNZ matchlen_bsf_8_match_nolit_encodeSnappyBetterBlockAsm12B + LEAL -8(R8), R8 + LEAL 8(R12), R12 + JMP matchlen_match4_match_nolit_encodeSnappyBetterBlockAsm12B + +matchlen_bsf_8_match_nolit_encodeSnappyBetterBlockAsm12B: +#ifdef GOAMD64_v3 + TZCNTQ R11, R11 + +#else + BSFQ R11, R11 + +#endif + SARQ $0x03, R11 + LEAL (R12)(R11*1), R12 + JMP match_nolit_end_encodeSnappyBetterBlockAsm12B + +matchlen_match4_match_nolit_encodeSnappyBetterBlockAsm12B: + CMPL R8, $0x04 + JB matchlen_match2_match_nolit_encodeSnappyBetterBlockAsm12B + MOVL (R9)(R12*1), R11 + CMPL (R10)(R12*1), R11 + JNE matchlen_match2_match_nolit_encodeSnappyBetterBlockAsm12B + LEAL -4(R8), R8 + LEAL 4(R12), R12 + +matchlen_match2_match_nolit_encodeSnappyBetterBlockAsm12B: + CMPL R8, $0x01 + JE matchlen_match1_match_nolit_encodeSnappyBetterBlockAsm12B + JB match_nolit_end_encodeSnappyBetterBlockAsm12B + MOVW (R9)(R12*1), R11 + CMPW (R10)(R12*1), R11 + JNE matchlen_match1_match_nolit_encodeSnappyBetterBlockAsm12B + LEAL 2(R12), R12 + SUBL $0x02, R8 + JZ match_nolit_end_encodeSnappyBetterBlockAsm12B + +matchlen_match1_match_nolit_encodeSnappyBetterBlockAsm12B: + MOVB (R9)(R12*1), R11 + CMPB (R10)(R12*1), R11 + JNE match_nolit_end_encodeSnappyBetterBlockAsm12B + LEAL 1(R12), R12 + +match_nolit_end_encodeSnappyBetterBlockAsm12B: + MOVL DX, R8 + SUBL SI, R8 + + // Check if repeat + MOVL R8, 16(SP) + MOVL 12(SP), SI + CMPL SI, DI + JEQ emit_literal_done_match_emit_encodeSnappyBetterBlockAsm12B + MOVL DI, R9 + MOVL DI, 12(SP) + LEAQ (BX)(SI*1), R10 + SUBL SI, R9 + LEAL -1(R9), SI + CMPL SI, $0x3c + JB one_byte_match_emit_encodeSnappyBetterBlockAsm12B + CMPL SI, $0x00000100 + JB two_bytes_match_emit_encodeSnappyBetterBlockAsm12B + JB three_bytes_match_emit_encodeSnappyBetterBlockAsm12B + +three_bytes_match_emit_encodeSnappyBetterBlockAsm12B: + MOVB $0xf4, (CX) + MOVW SI, 1(CX) + ADDQ $0x03, CX + JMP memmove_long_match_emit_encodeSnappyBetterBlockAsm12B + +two_bytes_match_emit_encodeSnappyBetterBlockAsm12B: + MOVB $0xf0, (CX) + MOVB SI, 1(CX) + ADDQ $0x02, CX + CMPL SI, $0x40 + JB memmove_match_emit_encodeSnappyBetterBlockAsm12B + JMP memmove_long_match_emit_encodeSnappyBetterBlockAsm12B + +one_byte_match_emit_encodeSnappyBetterBlockAsm12B: + SHLB $0x02, SI + MOVB SI, (CX) + ADDQ $0x01, CX + +memmove_match_emit_encodeSnappyBetterBlockAsm12B: + LEAQ (CX)(R9*1), SI + + // genMemMoveShort + CMPQ R9, $0x08 + JBE emit_lit_memmove_match_emit_encodeSnappyBetterBlockAsm12B_memmove_move_8 + CMPQ R9, $0x10 + JBE emit_lit_memmove_match_emit_encodeSnappyBetterBlockAsm12B_memmove_move_8through16 + CMPQ R9, $0x20 + JBE emit_lit_memmove_match_emit_encodeSnappyBetterBlockAsm12B_memmove_move_17through32 + JMP emit_lit_memmove_match_emit_encodeSnappyBetterBlockAsm12B_memmove_move_33through64 + +emit_lit_memmove_match_emit_encodeSnappyBetterBlockAsm12B_memmove_move_8: + MOVQ (R10), R11 + MOVQ R11, (CX) + JMP memmove_end_copy_match_emit_encodeSnappyBetterBlockAsm12B + +emit_lit_memmove_match_emit_encodeSnappyBetterBlockAsm12B_memmove_move_8through16: + MOVQ (R10), R11 + MOVQ -8(R10)(R9*1), R10 + MOVQ R11, (CX) + MOVQ R10, -8(CX)(R9*1) + JMP memmove_end_copy_match_emit_encodeSnappyBetterBlockAsm12B + +emit_lit_memmove_match_emit_encodeSnappyBetterBlockAsm12B_memmove_move_17through32: + MOVOU (R10), X0 + MOVOU -16(R10)(R9*1), X1 + MOVOU X0, (CX) + MOVOU X1, -16(CX)(R9*1) + JMP memmove_end_copy_match_emit_encodeSnappyBetterBlockAsm12B + +emit_lit_memmove_match_emit_encodeSnappyBetterBlockAsm12B_memmove_move_33through64: + MOVOU (R10), X0 + MOVOU 16(R10), X1 + MOVOU -32(R10)(R9*1), X2 + MOVOU -16(R10)(R9*1), X3 + MOVOU X0, (CX) + MOVOU X1, 16(CX) + MOVOU X2, -32(CX)(R9*1) + MOVOU X3, -16(CX)(R9*1) + +memmove_end_copy_match_emit_encodeSnappyBetterBlockAsm12B: + MOVQ SI, CX + JMP emit_literal_done_match_emit_encodeSnappyBetterBlockAsm12B + +memmove_long_match_emit_encodeSnappyBetterBlockAsm12B: + LEAQ (CX)(R9*1), SI + + // genMemMoveLong + MOVOU (R10), X0 + MOVOU 16(R10), X1 + MOVOU -32(R10)(R9*1), X2 + MOVOU -16(R10)(R9*1), X3 + MOVQ R9, R13 + SHRQ $0x05, R13 + MOVQ CX, R11 + ANDL $0x0000001f, R11 + MOVQ $0x00000040, R14 + SUBQ R11, R14 + DECQ R13 + JA emit_lit_memmove_long_match_emit_encodeSnappyBetterBlockAsm12Blarge_forward_sse_loop_32 + LEAQ -32(R10)(R14*1), R11 + LEAQ -32(CX)(R14*1), R15 + +emit_lit_memmove_long_match_emit_encodeSnappyBetterBlockAsm12Blarge_big_loop_back: + MOVOU (R11), X4 + MOVOU 16(R11), X5 + MOVOA X4, (R15) + MOVOA X5, 16(R15) + ADDQ $0x20, R15 + ADDQ $0x20, R11 + ADDQ $0x20, R14 + DECQ R13 + JNA emit_lit_memmove_long_match_emit_encodeSnappyBetterBlockAsm12Blarge_big_loop_back + +emit_lit_memmove_long_match_emit_encodeSnappyBetterBlockAsm12Blarge_forward_sse_loop_32: + MOVOU -32(R10)(R14*1), X4 + MOVOU -16(R10)(R14*1), X5 + MOVOA X4, -32(CX)(R14*1) + MOVOA X5, -16(CX)(R14*1) + ADDQ $0x20, R14 + CMPQ R9, R14 + JAE emit_lit_memmove_long_match_emit_encodeSnappyBetterBlockAsm12Blarge_forward_sse_loop_32 + MOVOU X0, (CX) + MOVOU X1, 16(CX) + MOVOU X2, -32(CX)(R9*1) + MOVOU X3, -16(CX)(R9*1) + MOVQ SI, CX + +emit_literal_done_match_emit_encodeSnappyBetterBlockAsm12B: + ADDL R12, DX + ADDL $0x04, R12 + MOVL DX, 12(SP) + + // emitCopy +two_byte_offset_match_nolit_encodeSnappyBetterBlockAsm12B: + CMPL R12, $0x40 + JBE two_byte_offset_short_match_nolit_encodeSnappyBetterBlockAsm12B + MOVB $0xee, (CX) + MOVW R8, 1(CX) + LEAL -60(R12), R12 + ADDQ $0x03, CX + JMP two_byte_offset_match_nolit_encodeSnappyBetterBlockAsm12B + +two_byte_offset_short_match_nolit_encodeSnappyBetterBlockAsm12B: + MOVL R12, SI + SHLL $0x02, SI + CMPL R12, $0x0c + JAE emit_copy_three_match_nolit_encodeSnappyBetterBlockAsm12B + CMPL R8, $0x00000800 + JAE emit_copy_three_match_nolit_encodeSnappyBetterBlockAsm12B + LEAL -15(SI), SI + MOVB R8, 1(CX) + SHRL $0x08, R8 + SHLL $0x05, R8 + ORL R8, SI + MOVB SI, (CX) + ADDQ $0x02, CX + JMP match_nolit_emitcopy_end_encodeSnappyBetterBlockAsm12B + +emit_copy_three_match_nolit_encodeSnappyBetterBlockAsm12B: + LEAL -2(SI), SI + MOVB SI, (CX) + MOVW R8, 1(CX) + ADDQ $0x03, CX + +match_nolit_emitcopy_end_encodeSnappyBetterBlockAsm12B: + CMPL DX, 8(SP) + JAE emit_remainder_encodeSnappyBetterBlockAsm12B + CMPQ CX, (SP) + JB match_nolit_dst_ok_encodeSnappyBetterBlockAsm12B + MOVQ $0x00000000, ret+56(FP) + RET + +match_nolit_dst_ok_encodeSnappyBetterBlockAsm12B: + MOVQ $0x0000cf1bbcdcbf9b, SI + MOVQ $0x9e3779b1, R8 + LEAQ 1(DI), DI + LEAQ -2(DX), R9 + MOVQ (BX)(DI*1), R10 + MOVQ 1(BX)(DI*1), R11 + MOVQ (BX)(R9*1), R12 + MOVQ 1(BX)(R9*1), R13 + SHLQ $0x10, R10 + IMULQ SI, R10 + SHRQ $0x32, R10 + SHLQ $0x20, R11 + IMULQ R8, R11 + SHRQ $0x34, R11 + SHLQ $0x10, R12 + IMULQ SI, R12 + SHRQ $0x32, R12 + SHLQ $0x20, R13 + IMULQ R8, R13 + SHRQ $0x34, R13 + LEAQ 1(DI), R8 + LEAQ 1(R9), R14 + MOVL DI, (AX)(R10*4) + MOVL R9, (AX)(R12*4) + MOVL R8, 65536(AX)(R11*4) + MOVL R14, 65536(AX)(R13*4) + LEAQ 1(R9)(DI*1), R8 + SHRQ $0x01, R8 + ADDQ $0x01, DI + SUBQ $0x01, R9 + +index_loop_encodeSnappyBetterBlockAsm12B: + CMPQ R8, R9 + JAE search_loop_encodeSnappyBetterBlockAsm12B + MOVQ (BX)(DI*1), R10 + MOVQ (BX)(R8*1), R11 + SHLQ $0x10, R10 + IMULQ SI, R10 + SHRQ $0x32, R10 + SHLQ $0x10, R11 + IMULQ SI, R11 + SHRQ $0x32, R11 + MOVL DI, (AX)(R10*4) + MOVL R8, (AX)(R11*4) + ADDQ $0x02, DI + ADDQ $0x02, R8 + JMP index_loop_encodeSnappyBetterBlockAsm12B + +emit_remainder_encodeSnappyBetterBlockAsm12B: + MOVQ src_len+32(FP), AX + SUBL 12(SP), AX + LEAQ 3(CX)(AX*1), AX + CMPQ AX, (SP) + JB emit_remainder_ok_encodeSnappyBetterBlockAsm12B + MOVQ $0x00000000, ret+56(FP) + RET + +emit_remainder_ok_encodeSnappyBetterBlockAsm12B: + MOVQ src_len+32(FP), AX + MOVL 12(SP), DX + CMPL DX, AX + JEQ emit_literal_done_emit_remainder_encodeSnappyBetterBlockAsm12B + MOVL AX, SI + MOVL AX, 12(SP) + LEAQ (BX)(DX*1), AX + SUBL DX, SI + LEAL -1(SI), DX + CMPL DX, $0x3c + JB one_byte_emit_remainder_encodeSnappyBetterBlockAsm12B + CMPL DX, $0x00000100 + JB two_bytes_emit_remainder_encodeSnappyBetterBlockAsm12B + JB three_bytes_emit_remainder_encodeSnappyBetterBlockAsm12B + +three_bytes_emit_remainder_encodeSnappyBetterBlockAsm12B: + MOVB $0xf4, (CX) + MOVW DX, 1(CX) + ADDQ $0x03, CX + JMP memmove_long_emit_remainder_encodeSnappyBetterBlockAsm12B + +two_bytes_emit_remainder_encodeSnappyBetterBlockAsm12B: + MOVB $0xf0, (CX) + MOVB DL, 1(CX) + ADDQ $0x02, CX + CMPL DX, $0x40 + JB memmove_emit_remainder_encodeSnappyBetterBlockAsm12B + JMP memmove_long_emit_remainder_encodeSnappyBetterBlockAsm12B + +one_byte_emit_remainder_encodeSnappyBetterBlockAsm12B: + SHLB $0x02, DL + MOVB DL, (CX) + ADDQ $0x01, CX + +memmove_emit_remainder_encodeSnappyBetterBlockAsm12B: + LEAQ (CX)(SI*1), DX + MOVL SI, BX + + // genMemMoveShort + CMPQ BX, $0x03 + JB emit_lit_memmove_emit_remainder_encodeSnappyBetterBlockAsm12B_memmove_move_1or2 + JE emit_lit_memmove_emit_remainder_encodeSnappyBetterBlockAsm12B_memmove_move_3 + CMPQ BX, $0x08 + JB emit_lit_memmove_emit_remainder_encodeSnappyBetterBlockAsm12B_memmove_move_4through7 + CMPQ BX, $0x10 + JBE emit_lit_memmove_emit_remainder_encodeSnappyBetterBlockAsm12B_memmove_move_8through16 + CMPQ BX, $0x20 + JBE emit_lit_memmove_emit_remainder_encodeSnappyBetterBlockAsm12B_memmove_move_17through32 + JMP emit_lit_memmove_emit_remainder_encodeSnappyBetterBlockAsm12B_memmove_move_33through64 + +emit_lit_memmove_emit_remainder_encodeSnappyBetterBlockAsm12B_memmove_move_1or2: + MOVB (AX), SI + MOVB -1(AX)(BX*1), AL + MOVB SI, (CX) + MOVB AL, -1(CX)(BX*1) + JMP memmove_end_copy_emit_remainder_encodeSnappyBetterBlockAsm12B + +emit_lit_memmove_emit_remainder_encodeSnappyBetterBlockAsm12B_memmove_move_3: + MOVW (AX), SI + MOVB 2(AX), AL + MOVW SI, (CX) + MOVB AL, 2(CX) + JMP memmove_end_copy_emit_remainder_encodeSnappyBetterBlockAsm12B + +emit_lit_memmove_emit_remainder_encodeSnappyBetterBlockAsm12B_memmove_move_4through7: + MOVL (AX), SI + MOVL -4(AX)(BX*1), AX + MOVL SI, (CX) + MOVL AX, -4(CX)(BX*1) + JMP memmove_end_copy_emit_remainder_encodeSnappyBetterBlockAsm12B + +emit_lit_memmove_emit_remainder_encodeSnappyBetterBlockAsm12B_memmove_move_8through16: + MOVQ (AX), SI + MOVQ -8(AX)(BX*1), AX + MOVQ SI, (CX) + MOVQ AX, -8(CX)(BX*1) + JMP memmove_end_copy_emit_remainder_encodeSnappyBetterBlockAsm12B + +emit_lit_memmove_emit_remainder_encodeSnappyBetterBlockAsm12B_memmove_move_17through32: + MOVOU (AX), X0 + MOVOU -16(AX)(BX*1), X1 + MOVOU X0, (CX) + MOVOU X1, -16(CX)(BX*1) + JMP memmove_end_copy_emit_remainder_encodeSnappyBetterBlockAsm12B + +emit_lit_memmove_emit_remainder_encodeSnappyBetterBlockAsm12B_memmove_move_33through64: + MOVOU (AX), X0 + MOVOU 16(AX), X1 + MOVOU -32(AX)(BX*1), X2 + MOVOU -16(AX)(BX*1), X3 + MOVOU X0, (CX) + MOVOU X1, 16(CX) + MOVOU X2, -32(CX)(BX*1) + MOVOU X3, -16(CX)(BX*1) + +memmove_end_copy_emit_remainder_encodeSnappyBetterBlockAsm12B: + MOVQ DX, CX + JMP emit_literal_done_emit_remainder_encodeSnappyBetterBlockAsm12B + +memmove_long_emit_remainder_encodeSnappyBetterBlockAsm12B: + LEAQ (CX)(SI*1), DX + MOVL SI, BX + + // genMemMoveLong + MOVOU (AX), X0 + MOVOU 16(AX), X1 + MOVOU -32(AX)(BX*1), X2 + MOVOU -16(AX)(BX*1), X3 + MOVQ BX, DI + SHRQ $0x05, DI + MOVQ CX, SI + ANDL $0x0000001f, SI + MOVQ $0x00000040, R8 + SUBQ SI, R8 + DECQ DI + JA emit_lit_memmove_long_emit_remainder_encodeSnappyBetterBlockAsm12Blarge_forward_sse_loop_32 + LEAQ -32(AX)(R8*1), SI + LEAQ -32(CX)(R8*1), R9 + +emit_lit_memmove_long_emit_remainder_encodeSnappyBetterBlockAsm12Blarge_big_loop_back: + MOVOU (SI), X4 + MOVOU 16(SI), X5 + MOVOA X4, (R9) + MOVOA X5, 16(R9) + ADDQ $0x20, R9 + ADDQ $0x20, SI + ADDQ $0x20, R8 + DECQ DI + JNA emit_lit_memmove_long_emit_remainder_encodeSnappyBetterBlockAsm12Blarge_big_loop_back + +emit_lit_memmove_long_emit_remainder_encodeSnappyBetterBlockAsm12Blarge_forward_sse_loop_32: + MOVOU -32(AX)(R8*1), X4 + MOVOU -16(AX)(R8*1), X5 + MOVOA X4, -32(CX)(R8*1) + MOVOA X5, -16(CX)(R8*1) + ADDQ $0x20, R8 + CMPQ BX, R8 + JAE emit_lit_memmove_long_emit_remainder_encodeSnappyBetterBlockAsm12Blarge_forward_sse_loop_32 + MOVOU X0, (CX) + MOVOU X1, 16(CX) + MOVOU X2, -32(CX)(BX*1) + MOVOU X3, -16(CX)(BX*1) + MOVQ DX, CX + +emit_literal_done_emit_remainder_encodeSnappyBetterBlockAsm12B: + MOVQ dst_base+0(FP), AX + SUBQ AX, CX + MOVQ CX, ret+56(FP) + RET + +// func encodeSnappyBetterBlockAsm10B(dst []byte, src []byte, tmp *[20480]byte) int +// Requires: BMI, SSE2 +TEXT ·encodeSnappyBetterBlockAsm10B(SB), $24-64 + MOVQ tmp+48(FP), AX + MOVQ dst_base+0(FP), CX + MOVQ $0x000000a0, DX + MOVQ AX, BX + PXOR X0, X0 + +zero_loop_encodeSnappyBetterBlockAsm10B: + MOVOU X0, (BX) + MOVOU X0, 16(BX) + MOVOU X0, 32(BX) + MOVOU X0, 48(BX) + MOVOU X0, 64(BX) + MOVOU X0, 80(BX) + MOVOU X0, 96(BX) + MOVOU X0, 112(BX) + ADDQ $0x80, BX + DECQ DX + JNZ zero_loop_encodeSnappyBetterBlockAsm10B + MOVL $0x00000000, 12(SP) + MOVQ src_len+32(FP), DX + LEAQ -9(DX), BX + LEAQ -8(DX), SI + MOVL SI, 8(SP) + SHRQ $0x05, DX + SUBL DX, BX + LEAQ (CX)(BX*1), BX + MOVQ BX, (SP) + MOVL $0x00000001, DX + MOVL $0x00000000, 16(SP) + MOVQ src_base+24(FP), BX + +search_loop_encodeSnappyBetterBlockAsm10B: + MOVL DX, SI + SUBL 12(SP), SI + SHRL $0x05, SI + LEAL 1(DX)(SI*1), SI + CMPL SI, 8(SP) + JAE emit_remainder_encodeSnappyBetterBlockAsm10B + MOVQ (BX)(DX*1), DI + MOVL SI, 20(SP) + MOVQ $0x0000cf1bbcdcbf9b, R9 + MOVQ $0x9e3779b1, SI + MOVQ DI, R10 + MOVQ DI, R11 + SHLQ $0x10, R10 + IMULQ R9, R10 + SHRQ $0x34, R10 + SHLQ $0x20, R11 + IMULQ SI, R11 + SHRQ $0x36, R11 + MOVL (AX)(R10*4), SI + MOVL 16384(AX)(R11*4), R8 + MOVL DX, (AX)(R10*4) + MOVL DX, 16384(AX)(R11*4) + MOVQ (BX)(SI*1), R10 + MOVQ (BX)(R8*1), R11 + CMPQ R10, DI + JEQ candidate_match_encodeSnappyBetterBlockAsm10B + CMPQ R11, DI + JNE no_short_found_encodeSnappyBetterBlockAsm10B + MOVL R8, SI + JMP candidate_match_encodeSnappyBetterBlockAsm10B + +no_short_found_encodeSnappyBetterBlockAsm10B: + CMPL R10, DI + JEQ candidate_match_encodeSnappyBetterBlockAsm10B + CMPL R11, DI + JEQ candidateS_match_encodeSnappyBetterBlockAsm10B + MOVL 20(SP), DX + JMP search_loop_encodeSnappyBetterBlockAsm10B + +candidateS_match_encodeSnappyBetterBlockAsm10B: + SHRQ $0x08, DI + MOVQ DI, R10 + SHLQ $0x10, R10 + IMULQ R9, R10 + SHRQ $0x34, R10 + MOVL (AX)(R10*4), SI + INCL DX + MOVL DX, (AX)(R10*4) + CMPL (BX)(SI*1), DI + JEQ candidate_match_encodeSnappyBetterBlockAsm10B + DECL DX + MOVL R8, SI + +candidate_match_encodeSnappyBetterBlockAsm10B: + MOVL 12(SP), DI + TESTL SI, SI + JZ match_extend_back_end_encodeSnappyBetterBlockAsm10B + +match_extend_back_loop_encodeSnappyBetterBlockAsm10B: + CMPL DX, DI + JBE match_extend_back_end_encodeSnappyBetterBlockAsm10B + MOVB -1(BX)(SI*1), R8 + MOVB -1(BX)(DX*1), R9 + CMPB R8, R9 + JNE match_extend_back_end_encodeSnappyBetterBlockAsm10B + LEAL -1(DX), DX + DECL SI + JZ match_extend_back_end_encodeSnappyBetterBlockAsm10B + JMP match_extend_back_loop_encodeSnappyBetterBlockAsm10B + +match_extend_back_end_encodeSnappyBetterBlockAsm10B: + MOVL DX, DI + SUBL 12(SP), DI + LEAQ 3(CX)(DI*1), DI + CMPQ DI, (SP) + JB match_dst_size_check_encodeSnappyBetterBlockAsm10B + MOVQ $0x00000000, ret+56(FP) + RET + +match_dst_size_check_encodeSnappyBetterBlockAsm10B: + MOVL DX, DI + ADDL $0x04, DX + ADDL $0x04, SI + MOVQ src_len+32(FP), R8 + SUBL DX, R8 + LEAQ (BX)(DX*1), R9 + LEAQ (BX)(SI*1), R10 + + // matchLen + XORL R12, R12 + +matchlen_loopback_16_match_nolit_encodeSnappyBetterBlockAsm10B: + CMPL R8, $0x10 + JB matchlen_match8_match_nolit_encodeSnappyBetterBlockAsm10B + MOVQ (R9)(R12*1), R11 + MOVQ 8(R9)(R12*1), R13 + XORQ (R10)(R12*1), R11 + JNZ matchlen_bsf_8_match_nolit_encodeSnappyBetterBlockAsm10B + XORQ 8(R10)(R12*1), R13 + JNZ matchlen_bsf_16match_nolit_encodeSnappyBetterBlockAsm10B + LEAL -16(R8), R8 + LEAL 16(R12), R12 + JMP matchlen_loopback_16_match_nolit_encodeSnappyBetterBlockAsm10B + +matchlen_bsf_16match_nolit_encodeSnappyBetterBlockAsm10B: +#ifdef GOAMD64_v3 + TZCNTQ R13, R13 + +#else + BSFQ R13, R13 + +#endif + SARQ $0x03, R13 + LEAL 8(R12)(R13*1), R12 + JMP match_nolit_end_encodeSnappyBetterBlockAsm10B + +matchlen_match8_match_nolit_encodeSnappyBetterBlockAsm10B: + CMPL R8, $0x08 + JB matchlen_match4_match_nolit_encodeSnappyBetterBlockAsm10B + MOVQ (R9)(R12*1), R11 + XORQ (R10)(R12*1), R11 + JNZ matchlen_bsf_8_match_nolit_encodeSnappyBetterBlockAsm10B + LEAL -8(R8), R8 + LEAL 8(R12), R12 + JMP matchlen_match4_match_nolit_encodeSnappyBetterBlockAsm10B + +matchlen_bsf_8_match_nolit_encodeSnappyBetterBlockAsm10B: +#ifdef GOAMD64_v3 + TZCNTQ R11, R11 + +#else + BSFQ R11, R11 + +#endif + SARQ $0x03, R11 + LEAL (R12)(R11*1), R12 + JMP match_nolit_end_encodeSnappyBetterBlockAsm10B + +matchlen_match4_match_nolit_encodeSnappyBetterBlockAsm10B: + CMPL R8, $0x04 + JB matchlen_match2_match_nolit_encodeSnappyBetterBlockAsm10B + MOVL (R9)(R12*1), R11 + CMPL (R10)(R12*1), R11 + JNE matchlen_match2_match_nolit_encodeSnappyBetterBlockAsm10B + LEAL -4(R8), R8 + LEAL 4(R12), R12 + +matchlen_match2_match_nolit_encodeSnappyBetterBlockAsm10B: + CMPL R8, $0x01 + JE matchlen_match1_match_nolit_encodeSnappyBetterBlockAsm10B + JB match_nolit_end_encodeSnappyBetterBlockAsm10B + MOVW (R9)(R12*1), R11 + CMPW (R10)(R12*1), R11 + JNE matchlen_match1_match_nolit_encodeSnappyBetterBlockAsm10B + LEAL 2(R12), R12 + SUBL $0x02, R8 + JZ match_nolit_end_encodeSnappyBetterBlockAsm10B + +matchlen_match1_match_nolit_encodeSnappyBetterBlockAsm10B: + MOVB (R9)(R12*1), R11 + CMPB (R10)(R12*1), R11 + JNE match_nolit_end_encodeSnappyBetterBlockAsm10B + LEAL 1(R12), R12 + +match_nolit_end_encodeSnappyBetterBlockAsm10B: + MOVL DX, R8 + SUBL SI, R8 + + // Check if repeat + MOVL R8, 16(SP) + MOVL 12(SP), SI + CMPL SI, DI + JEQ emit_literal_done_match_emit_encodeSnappyBetterBlockAsm10B + MOVL DI, R9 + MOVL DI, 12(SP) + LEAQ (BX)(SI*1), R10 + SUBL SI, R9 + LEAL -1(R9), SI + CMPL SI, $0x3c + JB one_byte_match_emit_encodeSnappyBetterBlockAsm10B + CMPL SI, $0x00000100 + JB two_bytes_match_emit_encodeSnappyBetterBlockAsm10B + JB three_bytes_match_emit_encodeSnappyBetterBlockAsm10B + +three_bytes_match_emit_encodeSnappyBetterBlockAsm10B: + MOVB $0xf4, (CX) + MOVW SI, 1(CX) + ADDQ $0x03, CX + JMP memmove_long_match_emit_encodeSnappyBetterBlockAsm10B + +two_bytes_match_emit_encodeSnappyBetterBlockAsm10B: + MOVB $0xf0, (CX) + MOVB SI, 1(CX) + ADDQ $0x02, CX + CMPL SI, $0x40 + JB memmove_match_emit_encodeSnappyBetterBlockAsm10B + JMP memmove_long_match_emit_encodeSnappyBetterBlockAsm10B + +one_byte_match_emit_encodeSnappyBetterBlockAsm10B: + SHLB $0x02, SI + MOVB SI, (CX) + ADDQ $0x01, CX + +memmove_match_emit_encodeSnappyBetterBlockAsm10B: + LEAQ (CX)(R9*1), SI + + // genMemMoveShort + CMPQ R9, $0x08 + JBE emit_lit_memmove_match_emit_encodeSnappyBetterBlockAsm10B_memmove_move_8 + CMPQ R9, $0x10 + JBE emit_lit_memmove_match_emit_encodeSnappyBetterBlockAsm10B_memmove_move_8through16 + CMPQ R9, $0x20 + JBE emit_lit_memmove_match_emit_encodeSnappyBetterBlockAsm10B_memmove_move_17through32 + JMP emit_lit_memmove_match_emit_encodeSnappyBetterBlockAsm10B_memmove_move_33through64 + +emit_lit_memmove_match_emit_encodeSnappyBetterBlockAsm10B_memmove_move_8: + MOVQ (R10), R11 + MOVQ R11, (CX) + JMP memmove_end_copy_match_emit_encodeSnappyBetterBlockAsm10B + +emit_lit_memmove_match_emit_encodeSnappyBetterBlockAsm10B_memmove_move_8through16: + MOVQ (R10), R11 + MOVQ -8(R10)(R9*1), R10 + MOVQ R11, (CX) + MOVQ R10, -8(CX)(R9*1) + JMP memmove_end_copy_match_emit_encodeSnappyBetterBlockAsm10B + +emit_lit_memmove_match_emit_encodeSnappyBetterBlockAsm10B_memmove_move_17through32: + MOVOU (R10), X0 + MOVOU -16(R10)(R9*1), X1 + MOVOU X0, (CX) + MOVOU X1, -16(CX)(R9*1) + JMP memmove_end_copy_match_emit_encodeSnappyBetterBlockAsm10B + +emit_lit_memmove_match_emit_encodeSnappyBetterBlockAsm10B_memmove_move_33through64: + MOVOU (R10), X0 + MOVOU 16(R10), X1 + MOVOU -32(R10)(R9*1), X2 + MOVOU -16(R10)(R9*1), X3 + MOVOU X0, (CX) + MOVOU X1, 16(CX) + MOVOU X2, -32(CX)(R9*1) + MOVOU X3, -16(CX)(R9*1) + +memmove_end_copy_match_emit_encodeSnappyBetterBlockAsm10B: + MOVQ SI, CX + JMP emit_literal_done_match_emit_encodeSnappyBetterBlockAsm10B + +memmove_long_match_emit_encodeSnappyBetterBlockAsm10B: + LEAQ (CX)(R9*1), SI + + // genMemMoveLong + MOVOU (R10), X0 + MOVOU 16(R10), X1 + MOVOU -32(R10)(R9*1), X2 + MOVOU -16(R10)(R9*1), X3 + MOVQ R9, R13 + SHRQ $0x05, R13 + MOVQ CX, R11 + ANDL $0x0000001f, R11 + MOVQ $0x00000040, R14 + SUBQ R11, R14 + DECQ R13 + JA emit_lit_memmove_long_match_emit_encodeSnappyBetterBlockAsm10Blarge_forward_sse_loop_32 + LEAQ -32(R10)(R14*1), R11 + LEAQ -32(CX)(R14*1), R15 + +emit_lit_memmove_long_match_emit_encodeSnappyBetterBlockAsm10Blarge_big_loop_back: + MOVOU (R11), X4 + MOVOU 16(R11), X5 + MOVOA X4, (R15) + MOVOA X5, 16(R15) + ADDQ $0x20, R15 + ADDQ $0x20, R11 + ADDQ $0x20, R14 + DECQ R13 + JNA emit_lit_memmove_long_match_emit_encodeSnappyBetterBlockAsm10Blarge_big_loop_back + +emit_lit_memmove_long_match_emit_encodeSnappyBetterBlockAsm10Blarge_forward_sse_loop_32: + MOVOU -32(R10)(R14*1), X4 + MOVOU -16(R10)(R14*1), X5 + MOVOA X4, -32(CX)(R14*1) + MOVOA X5, -16(CX)(R14*1) + ADDQ $0x20, R14 + CMPQ R9, R14 + JAE emit_lit_memmove_long_match_emit_encodeSnappyBetterBlockAsm10Blarge_forward_sse_loop_32 + MOVOU X0, (CX) + MOVOU X1, 16(CX) + MOVOU X2, -32(CX)(R9*1) + MOVOU X3, -16(CX)(R9*1) + MOVQ SI, CX + +emit_literal_done_match_emit_encodeSnappyBetterBlockAsm10B: + ADDL R12, DX + ADDL $0x04, R12 + MOVL DX, 12(SP) + + // emitCopy +two_byte_offset_match_nolit_encodeSnappyBetterBlockAsm10B: + CMPL R12, $0x40 + JBE two_byte_offset_short_match_nolit_encodeSnappyBetterBlockAsm10B + MOVB $0xee, (CX) + MOVW R8, 1(CX) + LEAL -60(R12), R12 + ADDQ $0x03, CX + JMP two_byte_offset_match_nolit_encodeSnappyBetterBlockAsm10B + +two_byte_offset_short_match_nolit_encodeSnappyBetterBlockAsm10B: + MOVL R12, SI + SHLL $0x02, SI + CMPL R12, $0x0c + JAE emit_copy_three_match_nolit_encodeSnappyBetterBlockAsm10B + CMPL R8, $0x00000800 + JAE emit_copy_three_match_nolit_encodeSnappyBetterBlockAsm10B + LEAL -15(SI), SI + MOVB R8, 1(CX) + SHRL $0x08, R8 + SHLL $0x05, R8 + ORL R8, SI + MOVB SI, (CX) + ADDQ $0x02, CX + JMP match_nolit_emitcopy_end_encodeSnappyBetterBlockAsm10B + +emit_copy_three_match_nolit_encodeSnappyBetterBlockAsm10B: + LEAL -2(SI), SI + MOVB SI, (CX) + MOVW R8, 1(CX) + ADDQ $0x03, CX + +match_nolit_emitcopy_end_encodeSnappyBetterBlockAsm10B: + CMPL DX, 8(SP) + JAE emit_remainder_encodeSnappyBetterBlockAsm10B + CMPQ CX, (SP) + JB match_nolit_dst_ok_encodeSnappyBetterBlockAsm10B + MOVQ $0x00000000, ret+56(FP) + RET + +match_nolit_dst_ok_encodeSnappyBetterBlockAsm10B: + MOVQ $0x0000cf1bbcdcbf9b, SI + MOVQ $0x9e3779b1, R8 + LEAQ 1(DI), DI + LEAQ -2(DX), R9 + MOVQ (BX)(DI*1), R10 + MOVQ 1(BX)(DI*1), R11 + MOVQ (BX)(R9*1), R12 + MOVQ 1(BX)(R9*1), R13 + SHLQ $0x10, R10 + IMULQ SI, R10 + SHRQ $0x34, R10 + SHLQ $0x20, R11 + IMULQ R8, R11 + SHRQ $0x36, R11 + SHLQ $0x10, R12 + IMULQ SI, R12 + SHRQ $0x34, R12 + SHLQ $0x20, R13 + IMULQ R8, R13 + SHRQ $0x36, R13 + LEAQ 1(DI), R8 + LEAQ 1(R9), R14 + MOVL DI, (AX)(R10*4) + MOVL R9, (AX)(R12*4) + MOVL R8, 16384(AX)(R11*4) + MOVL R14, 16384(AX)(R13*4) + LEAQ 1(R9)(DI*1), R8 + SHRQ $0x01, R8 + ADDQ $0x01, DI + SUBQ $0x01, R9 + +index_loop_encodeSnappyBetterBlockAsm10B: + CMPQ R8, R9 + JAE search_loop_encodeSnappyBetterBlockAsm10B + MOVQ (BX)(DI*1), R10 + MOVQ (BX)(R8*1), R11 + SHLQ $0x10, R10 + IMULQ SI, R10 + SHRQ $0x34, R10 + SHLQ $0x10, R11 + IMULQ SI, R11 + SHRQ $0x34, R11 + MOVL DI, (AX)(R10*4) + MOVL R8, (AX)(R11*4) + ADDQ $0x02, DI + ADDQ $0x02, R8 + JMP index_loop_encodeSnappyBetterBlockAsm10B + +emit_remainder_encodeSnappyBetterBlockAsm10B: + MOVQ src_len+32(FP), AX + SUBL 12(SP), AX + LEAQ 3(CX)(AX*1), AX + CMPQ AX, (SP) + JB emit_remainder_ok_encodeSnappyBetterBlockAsm10B + MOVQ $0x00000000, ret+56(FP) + RET + +emit_remainder_ok_encodeSnappyBetterBlockAsm10B: + MOVQ src_len+32(FP), AX + MOVL 12(SP), DX + CMPL DX, AX + JEQ emit_literal_done_emit_remainder_encodeSnappyBetterBlockAsm10B + MOVL AX, SI + MOVL AX, 12(SP) + LEAQ (BX)(DX*1), AX + SUBL DX, SI + LEAL -1(SI), DX + CMPL DX, $0x3c + JB one_byte_emit_remainder_encodeSnappyBetterBlockAsm10B + CMPL DX, $0x00000100 + JB two_bytes_emit_remainder_encodeSnappyBetterBlockAsm10B + JB three_bytes_emit_remainder_encodeSnappyBetterBlockAsm10B + +three_bytes_emit_remainder_encodeSnappyBetterBlockAsm10B: + MOVB $0xf4, (CX) + MOVW DX, 1(CX) + ADDQ $0x03, CX + JMP memmove_long_emit_remainder_encodeSnappyBetterBlockAsm10B + +two_bytes_emit_remainder_encodeSnappyBetterBlockAsm10B: + MOVB $0xf0, (CX) + MOVB DL, 1(CX) + ADDQ $0x02, CX + CMPL DX, $0x40 + JB memmove_emit_remainder_encodeSnappyBetterBlockAsm10B + JMP memmove_long_emit_remainder_encodeSnappyBetterBlockAsm10B + +one_byte_emit_remainder_encodeSnappyBetterBlockAsm10B: + SHLB $0x02, DL + MOVB DL, (CX) + ADDQ $0x01, CX + +memmove_emit_remainder_encodeSnappyBetterBlockAsm10B: + LEAQ (CX)(SI*1), DX + MOVL SI, BX + + // genMemMoveShort + CMPQ BX, $0x03 + JB emit_lit_memmove_emit_remainder_encodeSnappyBetterBlockAsm10B_memmove_move_1or2 + JE emit_lit_memmove_emit_remainder_encodeSnappyBetterBlockAsm10B_memmove_move_3 + CMPQ BX, $0x08 + JB emit_lit_memmove_emit_remainder_encodeSnappyBetterBlockAsm10B_memmove_move_4through7 + CMPQ BX, $0x10 + JBE emit_lit_memmove_emit_remainder_encodeSnappyBetterBlockAsm10B_memmove_move_8through16 + CMPQ BX, $0x20 + JBE emit_lit_memmove_emit_remainder_encodeSnappyBetterBlockAsm10B_memmove_move_17through32 + JMP emit_lit_memmove_emit_remainder_encodeSnappyBetterBlockAsm10B_memmove_move_33through64 + +emit_lit_memmove_emit_remainder_encodeSnappyBetterBlockAsm10B_memmove_move_1or2: + MOVB (AX), SI + MOVB -1(AX)(BX*1), AL + MOVB SI, (CX) + MOVB AL, -1(CX)(BX*1) + JMP memmove_end_copy_emit_remainder_encodeSnappyBetterBlockAsm10B + +emit_lit_memmove_emit_remainder_encodeSnappyBetterBlockAsm10B_memmove_move_3: + MOVW (AX), SI + MOVB 2(AX), AL + MOVW SI, (CX) + MOVB AL, 2(CX) + JMP memmove_end_copy_emit_remainder_encodeSnappyBetterBlockAsm10B + +emit_lit_memmove_emit_remainder_encodeSnappyBetterBlockAsm10B_memmove_move_4through7: + MOVL (AX), SI + MOVL -4(AX)(BX*1), AX + MOVL SI, (CX) + MOVL AX, -4(CX)(BX*1) + JMP memmove_end_copy_emit_remainder_encodeSnappyBetterBlockAsm10B + +emit_lit_memmove_emit_remainder_encodeSnappyBetterBlockAsm10B_memmove_move_8through16: + MOVQ (AX), SI + MOVQ -8(AX)(BX*1), AX + MOVQ SI, (CX) + MOVQ AX, -8(CX)(BX*1) + JMP memmove_end_copy_emit_remainder_encodeSnappyBetterBlockAsm10B + +emit_lit_memmove_emit_remainder_encodeSnappyBetterBlockAsm10B_memmove_move_17through32: + MOVOU (AX), X0 + MOVOU -16(AX)(BX*1), X1 + MOVOU X0, (CX) + MOVOU X1, -16(CX)(BX*1) + JMP memmove_end_copy_emit_remainder_encodeSnappyBetterBlockAsm10B + +emit_lit_memmove_emit_remainder_encodeSnappyBetterBlockAsm10B_memmove_move_33through64: + MOVOU (AX), X0 + MOVOU 16(AX), X1 + MOVOU -32(AX)(BX*1), X2 + MOVOU -16(AX)(BX*1), X3 + MOVOU X0, (CX) + MOVOU X1, 16(CX) + MOVOU X2, -32(CX)(BX*1) + MOVOU X3, -16(CX)(BX*1) + +memmove_end_copy_emit_remainder_encodeSnappyBetterBlockAsm10B: + MOVQ DX, CX + JMP emit_literal_done_emit_remainder_encodeSnappyBetterBlockAsm10B + +memmove_long_emit_remainder_encodeSnappyBetterBlockAsm10B: + LEAQ (CX)(SI*1), DX + MOVL SI, BX + + // genMemMoveLong + MOVOU (AX), X0 + MOVOU 16(AX), X1 + MOVOU -32(AX)(BX*1), X2 + MOVOU -16(AX)(BX*1), X3 + MOVQ BX, DI + SHRQ $0x05, DI + MOVQ CX, SI + ANDL $0x0000001f, SI + MOVQ $0x00000040, R8 + SUBQ SI, R8 + DECQ DI + JA emit_lit_memmove_long_emit_remainder_encodeSnappyBetterBlockAsm10Blarge_forward_sse_loop_32 + LEAQ -32(AX)(R8*1), SI + LEAQ -32(CX)(R8*1), R9 + +emit_lit_memmove_long_emit_remainder_encodeSnappyBetterBlockAsm10Blarge_big_loop_back: + MOVOU (SI), X4 + MOVOU 16(SI), X5 + MOVOA X4, (R9) + MOVOA X5, 16(R9) + ADDQ $0x20, R9 + ADDQ $0x20, SI + ADDQ $0x20, R8 + DECQ DI + JNA emit_lit_memmove_long_emit_remainder_encodeSnappyBetterBlockAsm10Blarge_big_loop_back + +emit_lit_memmove_long_emit_remainder_encodeSnappyBetterBlockAsm10Blarge_forward_sse_loop_32: + MOVOU -32(AX)(R8*1), X4 + MOVOU -16(AX)(R8*1), X5 + MOVOA X4, -32(CX)(R8*1) + MOVOA X5, -16(CX)(R8*1) + ADDQ $0x20, R8 + CMPQ BX, R8 + JAE emit_lit_memmove_long_emit_remainder_encodeSnappyBetterBlockAsm10Blarge_forward_sse_loop_32 + MOVOU X0, (CX) + MOVOU X1, 16(CX) + MOVOU X2, -32(CX)(BX*1) + MOVOU X3, -16(CX)(BX*1) + MOVQ DX, CX + +emit_literal_done_emit_remainder_encodeSnappyBetterBlockAsm10B: + MOVQ dst_base+0(FP), AX + SUBQ AX, CX + MOVQ CX, ret+56(FP) + RET + +// func encodeSnappyBetterBlockAsm8B(dst []byte, src []byte, tmp *[5120]byte) int +// Requires: BMI, SSE2 +TEXT ·encodeSnappyBetterBlockAsm8B(SB), $24-64 + MOVQ tmp+48(FP), AX + MOVQ dst_base+0(FP), CX + MOVQ $0x00000028, DX + MOVQ AX, BX + PXOR X0, X0 + +zero_loop_encodeSnappyBetterBlockAsm8B: + MOVOU X0, (BX) + MOVOU X0, 16(BX) + MOVOU X0, 32(BX) + MOVOU X0, 48(BX) + MOVOU X0, 64(BX) + MOVOU X0, 80(BX) + MOVOU X0, 96(BX) + MOVOU X0, 112(BX) + ADDQ $0x80, BX + DECQ DX + JNZ zero_loop_encodeSnappyBetterBlockAsm8B + MOVL $0x00000000, 12(SP) + MOVQ src_len+32(FP), DX + LEAQ -9(DX), BX + LEAQ -8(DX), SI + MOVL SI, 8(SP) + SHRQ $0x05, DX + SUBL DX, BX + LEAQ (CX)(BX*1), BX + MOVQ BX, (SP) + MOVL $0x00000001, DX + MOVL $0x00000000, 16(SP) + MOVQ src_base+24(FP), BX + +search_loop_encodeSnappyBetterBlockAsm8B: + MOVL DX, SI + SUBL 12(SP), SI + SHRL $0x04, SI + LEAL 1(DX)(SI*1), SI + CMPL SI, 8(SP) + JAE emit_remainder_encodeSnappyBetterBlockAsm8B + MOVQ (BX)(DX*1), DI + MOVL SI, 20(SP) + MOVQ $0x0000cf1bbcdcbf9b, R9 + MOVQ $0x9e3779b1, SI + MOVQ DI, R10 + MOVQ DI, R11 + SHLQ $0x10, R10 + IMULQ R9, R10 + SHRQ $0x36, R10 + SHLQ $0x20, R11 + IMULQ SI, R11 + SHRQ $0x38, R11 + MOVL (AX)(R10*4), SI + MOVL 4096(AX)(R11*4), R8 + MOVL DX, (AX)(R10*4) + MOVL DX, 4096(AX)(R11*4) + MOVQ (BX)(SI*1), R10 + MOVQ (BX)(R8*1), R11 + CMPQ R10, DI + JEQ candidate_match_encodeSnappyBetterBlockAsm8B + CMPQ R11, DI + JNE no_short_found_encodeSnappyBetterBlockAsm8B + MOVL R8, SI + JMP candidate_match_encodeSnappyBetterBlockAsm8B + +no_short_found_encodeSnappyBetterBlockAsm8B: + CMPL R10, DI + JEQ candidate_match_encodeSnappyBetterBlockAsm8B + CMPL R11, DI + JEQ candidateS_match_encodeSnappyBetterBlockAsm8B + MOVL 20(SP), DX + JMP search_loop_encodeSnappyBetterBlockAsm8B + +candidateS_match_encodeSnappyBetterBlockAsm8B: + SHRQ $0x08, DI + MOVQ DI, R10 + SHLQ $0x10, R10 + IMULQ R9, R10 + SHRQ $0x36, R10 + MOVL (AX)(R10*4), SI + INCL DX + MOVL DX, (AX)(R10*4) + CMPL (BX)(SI*1), DI + JEQ candidate_match_encodeSnappyBetterBlockAsm8B + DECL DX + MOVL R8, SI + +candidate_match_encodeSnappyBetterBlockAsm8B: + MOVL 12(SP), DI + TESTL SI, SI + JZ match_extend_back_end_encodeSnappyBetterBlockAsm8B + +match_extend_back_loop_encodeSnappyBetterBlockAsm8B: + CMPL DX, DI + JBE match_extend_back_end_encodeSnappyBetterBlockAsm8B + MOVB -1(BX)(SI*1), R8 + MOVB -1(BX)(DX*1), R9 + CMPB R8, R9 + JNE match_extend_back_end_encodeSnappyBetterBlockAsm8B + LEAL -1(DX), DX + DECL SI + JZ match_extend_back_end_encodeSnappyBetterBlockAsm8B + JMP match_extend_back_loop_encodeSnappyBetterBlockAsm8B + +match_extend_back_end_encodeSnappyBetterBlockAsm8B: + MOVL DX, DI + SUBL 12(SP), DI + LEAQ 3(CX)(DI*1), DI + CMPQ DI, (SP) + JB match_dst_size_check_encodeSnappyBetterBlockAsm8B + MOVQ $0x00000000, ret+56(FP) + RET + +match_dst_size_check_encodeSnappyBetterBlockAsm8B: + MOVL DX, DI + ADDL $0x04, DX + ADDL $0x04, SI + MOVQ src_len+32(FP), R8 + SUBL DX, R8 + LEAQ (BX)(DX*1), R9 + LEAQ (BX)(SI*1), R10 + + // matchLen + XORL R12, R12 + +matchlen_loopback_16_match_nolit_encodeSnappyBetterBlockAsm8B: + CMPL R8, $0x10 + JB matchlen_match8_match_nolit_encodeSnappyBetterBlockAsm8B + MOVQ (R9)(R12*1), R11 + MOVQ 8(R9)(R12*1), R13 + XORQ (R10)(R12*1), R11 + JNZ matchlen_bsf_8_match_nolit_encodeSnappyBetterBlockAsm8B + XORQ 8(R10)(R12*1), R13 + JNZ matchlen_bsf_16match_nolit_encodeSnappyBetterBlockAsm8B + LEAL -16(R8), R8 + LEAL 16(R12), R12 + JMP matchlen_loopback_16_match_nolit_encodeSnappyBetterBlockAsm8B + +matchlen_bsf_16match_nolit_encodeSnappyBetterBlockAsm8B: +#ifdef GOAMD64_v3 + TZCNTQ R13, R13 + +#else + BSFQ R13, R13 + +#endif + SARQ $0x03, R13 + LEAL 8(R12)(R13*1), R12 + JMP match_nolit_end_encodeSnappyBetterBlockAsm8B + +matchlen_match8_match_nolit_encodeSnappyBetterBlockAsm8B: + CMPL R8, $0x08 + JB matchlen_match4_match_nolit_encodeSnappyBetterBlockAsm8B + MOVQ (R9)(R12*1), R11 + XORQ (R10)(R12*1), R11 + JNZ matchlen_bsf_8_match_nolit_encodeSnappyBetterBlockAsm8B + LEAL -8(R8), R8 + LEAL 8(R12), R12 + JMP matchlen_match4_match_nolit_encodeSnappyBetterBlockAsm8B + +matchlen_bsf_8_match_nolit_encodeSnappyBetterBlockAsm8B: +#ifdef GOAMD64_v3 + TZCNTQ R11, R11 + +#else + BSFQ R11, R11 + +#endif + SARQ $0x03, R11 + LEAL (R12)(R11*1), R12 + JMP match_nolit_end_encodeSnappyBetterBlockAsm8B + +matchlen_match4_match_nolit_encodeSnappyBetterBlockAsm8B: + CMPL R8, $0x04 + JB matchlen_match2_match_nolit_encodeSnappyBetterBlockAsm8B + MOVL (R9)(R12*1), R11 + CMPL (R10)(R12*1), R11 + JNE matchlen_match2_match_nolit_encodeSnappyBetterBlockAsm8B + LEAL -4(R8), R8 + LEAL 4(R12), R12 + +matchlen_match2_match_nolit_encodeSnappyBetterBlockAsm8B: + CMPL R8, $0x01 + JE matchlen_match1_match_nolit_encodeSnappyBetterBlockAsm8B + JB match_nolit_end_encodeSnappyBetterBlockAsm8B + MOVW (R9)(R12*1), R11 + CMPW (R10)(R12*1), R11 + JNE matchlen_match1_match_nolit_encodeSnappyBetterBlockAsm8B + LEAL 2(R12), R12 + SUBL $0x02, R8 + JZ match_nolit_end_encodeSnappyBetterBlockAsm8B + +matchlen_match1_match_nolit_encodeSnappyBetterBlockAsm8B: + MOVB (R9)(R12*1), R11 + CMPB (R10)(R12*1), R11 + JNE match_nolit_end_encodeSnappyBetterBlockAsm8B + LEAL 1(R12), R12 + +match_nolit_end_encodeSnappyBetterBlockAsm8B: + MOVL DX, R8 + SUBL SI, R8 + + // Check if repeat + MOVL R8, 16(SP) + MOVL 12(SP), SI + CMPL SI, DI + JEQ emit_literal_done_match_emit_encodeSnappyBetterBlockAsm8B + MOVL DI, R9 + MOVL DI, 12(SP) + LEAQ (BX)(SI*1), R10 + SUBL SI, R9 + LEAL -1(R9), SI + CMPL SI, $0x3c + JB one_byte_match_emit_encodeSnappyBetterBlockAsm8B + CMPL SI, $0x00000100 + JB two_bytes_match_emit_encodeSnappyBetterBlockAsm8B + JB three_bytes_match_emit_encodeSnappyBetterBlockAsm8B + +three_bytes_match_emit_encodeSnappyBetterBlockAsm8B: + MOVB $0xf4, (CX) + MOVW SI, 1(CX) + ADDQ $0x03, CX + JMP memmove_long_match_emit_encodeSnappyBetterBlockAsm8B + +two_bytes_match_emit_encodeSnappyBetterBlockAsm8B: + MOVB $0xf0, (CX) + MOVB SI, 1(CX) + ADDQ $0x02, CX + CMPL SI, $0x40 + JB memmove_match_emit_encodeSnappyBetterBlockAsm8B + JMP memmove_long_match_emit_encodeSnappyBetterBlockAsm8B + +one_byte_match_emit_encodeSnappyBetterBlockAsm8B: + SHLB $0x02, SI + MOVB SI, (CX) + ADDQ $0x01, CX + +memmove_match_emit_encodeSnappyBetterBlockAsm8B: + LEAQ (CX)(R9*1), SI + + // genMemMoveShort + CMPQ R9, $0x08 + JBE emit_lit_memmove_match_emit_encodeSnappyBetterBlockAsm8B_memmove_move_8 + CMPQ R9, $0x10 + JBE emit_lit_memmove_match_emit_encodeSnappyBetterBlockAsm8B_memmove_move_8through16 + CMPQ R9, $0x20 + JBE emit_lit_memmove_match_emit_encodeSnappyBetterBlockAsm8B_memmove_move_17through32 + JMP emit_lit_memmove_match_emit_encodeSnappyBetterBlockAsm8B_memmove_move_33through64 + +emit_lit_memmove_match_emit_encodeSnappyBetterBlockAsm8B_memmove_move_8: + MOVQ (R10), R11 + MOVQ R11, (CX) + JMP memmove_end_copy_match_emit_encodeSnappyBetterBlockAsm8B + +emit_lit_memmove_match_emit_encodeSnappyBetterBlockAsm8B_memmove_move_8through16: + MOVQ (R10), R11 + MOVQ -8(R10)(R9*1), R10 + MOVQ R11, (CX) + MOVQ R10, -8(CX)(R9*1) + JMP memmove_end_copy_match_emit_encodeSnappyBetterBlockAsm8B + +emit_lit_memmove_match_emit_encodeSnappyBetterBlockAsm8B_memmove_move_17through32: + MOVOU (R10), X0 + MOVOU -16(R10)(R9*1), X1 + MOVOU X0, (CX) + MOVOU X1, -16(CX)(R9*1) + JMP memmove_end_copy_match_emit_encodeSnappyBetterBlockAsm8B + +emit_lit_memmove_match_emit_encodeSnappyBetterBlockAsm8B_memmove_move_33through64: + MOVOU (R10), X0 + MOVOU 16(R10), X1 + MOVOU -32(R10)(R9*1), X2 + MOVOU -16(R10)(R9*1), X3 + MOVOU X0, (CX) + MOVOU X1, 16(CX) + MOVOU X2, -32(CX)(R9*1) + MOVOU X3, -16(CX)(R9*1) + +memmove_end_copy_match_emit_encodeSnappyBetterBlockAsm8B: + MOVQ SI, CX + JMP emit_literal_done_match_emit_encodeSnappyBetterBlockAsm8B + +memmove_long_match_emit_encodeSnappyBetterBlockAsm8B: + LEAQ (CX)(R9*1), SI + + // genMemMoveLong + MOVOU (R10), X0 + MOVOU 16(R10), X1 + MOVOU -32(R10)(R9*1), X2 + MOVOU -16(R10)(R9*1), X3 + MOVQ R9, R13 + SHRQ $0x05, R13 + MOVQ CX, R11 + ANDL $0x0000001f, R11 + MOVQ $0x00000040, R14 + SUBQ R11, R14 + DECQ R13 + JA emit_lit_memmove_long_match_emit_encodeSnappyBetterBlockAsm8Blarge_forward_sse_loop_32 + LEAQ -32(R10)(R14*1), R11 + LEAQ -32(CX)(R14*1), R15 + +emit_lit_memmove_long_match_emit_encodeSnappyBetterBlockAsm8Blarge_big_loop_back: + MOVOU (R11), X4 + MOVOU 16(R11), X5 + MOVOA X4, (R15) + MOVOA X5, 16(R15) + ADDQ $0x20, R15 + ADDQ $0x20, R11 + ADDQ $0x20, R14 + DECQ R13 + JNA emit_lit_memmove_long_match_emit_encodeSnappyBetterBlockAsm8Blarge_big_loop_back + +emit_lit_memmove_long_match_emit_encodeSnappyBetterBlockAsm8Blarge_forward_sse_loop_32: + MOVOU -32(R10)(R14*1), X4 + MOVOU -16(R10)(R14*1), X5 + MOVOA X4, -32(CX)(R14*1) + MOVOA X5, -16(CX)(R14*1) + ADDQ $0x20, R14 + CMPQ R9, R14 + JAE emit_lit_memmove_long_match_emit_encodeSnappyBetterBlockAsm8Blarge_forward_sse_loop_32 + MOVOU X0, (CX) + MOVOU X1, 16(CX) + MOVOU X2, -32(CX)(R9*1) + MOVOU X3, -16(CX)(R9*1) + MOVQ SI, CX + +emit_literal_done_match_emit_encodeSnappyBetterBlockAsm8B: + ADDL R12, DX + ADDL $0x04, R12 + MOVL DX, 12(SP) + + // emitCopy +two_byte_offset_match_nolit_encodeSnappyBetterBlockAsm8B: + CMPL R12, $0x40 + JBE two_byte_offset_short_match_nolit_encodeSnappyBetterBlockAsm8B + MOVB $0xee, (CX) + MOVW R8, 1(CX) + LEAL -60(R12), R12 + ADDQ $0x03, CX + JMP two_byte_offset_match_nolit_encodeSnappyBetterBlockAsm8B + +two_byte_offset_short_match_nolit_encodeSnappyBetterBlockAsm8B: + MOVL R12, SI + SHLL $0x02, SI + CMPL R12, $0x0c + JAE emit_copy_three_match_nolit_encodeSnappyBetterBlockAsm8B + LEAL -15(SI), SI + MOVB R8, 1(CX) + SHRL $0x08, R8 + SHLL $0x05, R8 + ORL R8, SI + MOVB SI, (CX) + ADDQ $0x02, CX + JMP match_nolit_emitcopy_end_encodeSnappyBetterBlockAsm8B + +emit_copy_three_match_nolit_encodeSnappyBetterBlockAsm8B: + LEAL -2(SI), SI + MOVB SI, (CX) + MOVW R8, 1(CX) + ADDQ $0x03, CX + +match_nolit_emitcopy_end_encodeSnappyBetterBlockAsm8B: + CMPL DX, 8(SP) + JAE emit_remainder_encodeSnappyBetterBlockAsm8B + CMPQ CX, (SP) + JB match_nolit_dst_ok_encodeSnappyBetterBlockAsm8B + MOVQ $0x00000000, ret+56(FP) + RET + +match_nolit_dst_ok_encodeSnappyBetterBlockAsm8B: + MOVQ $0x0000cf1bbcdcbf9b, SI + MOVQ $0x9e3779b1, R8 + LEAQ 1(DI), DI + LEAQ -2(DX), R9 + MOVQ (BX)(DI*1), R10 + MOVQ 1(BX)(DI*1), R11 + MOVQ (BX)(R9*1), R12 + MOVQ 1(BX)(R9*1), R13 + SHLQ $0x10, R10 + IMULQ SI, R10 + SHRQ $0x36, R10 + SHLQ $0x20, R11 + IMULQ R8, R11 + SHRQ $0x38, R11 + SHLQ $0x10, R12 + IMULQ SI, R12 + SHRQ $0x36, R12 + SHLQ $0x20, R13 + IMULQ R8, R13 + SHRQ $0x38, R13 + LEAQ 1(DI), R8 + LEAQ 1(R9), R14 + MOVL DI, (AX)(R10*4) + MOVL R9, (AX)(R12*4) + MOVL R8, 4096(AX)(R11*4) + MOVL R14, 4096(AX)(R13*4) + LEAQ 1(R9)(DI*1), R8 + SHRQ $0x01, R8 + ADDQ $0x01, DI + SUBQ $0x01, R9 + +index_loop_encodeSnappyBetterBlockAsm8B: + CMPQ R8, R9 + JAE search_loop_encodeSnappyBetterBlockAsm8B + MOVQ (BX)(DI*1), R10 + MOVQ (BX)(R8*1), R11 + SHLQ $0x10, R10 + IMULQ SI, R10 + SHRQ $0x36, R10 + SHLQ $0x10, R11 + IMULQ SI, R11 + SHRQ $0x36, R11 + MOVL DI, (AX)(R10*4) + MOVL R8, (AX)(R11*4) + ADDQ $0x02, DI + ADDQ $0x02, R8 + JMP index_loop_encodeSnappyBetterBlockAsm8B + +emit_remainder_encodeSnappyBetterBlockAsm8B: + MOVQ src_len+32(FP), AX + SUBL 12(SP), AX + LEAQ 3(CX)(AX*1), AX + CMPQ AX, (SP) + JB emit_remainder_ok_encodeSnappyBetterBlockAsm8B + MOVQ $0x00000000, ret+56(FP) + RET + +emit_remainder_ok_encodeSnappyBetterBlockAsm8B: + MOVQ src_len+32(FP), AX + MOVL 12(SP), DX + CMPL DX, AX + JEQ emit_literal_done_emit_remainder_encodeSnappyBetterBlockAsm8B + MOVL AX, SI + MOVL AX, 12(SP) + LEAQ (BX)(DX*1), AX + SUBL DX, SI + LEAL -1(SI), DX + CMPL DX, $0x3c + JB one_byte_emit_remainder_encodeSnappyBetterBlockAsm8B + CMPL DX, $0x00000100 + JB two_bytes_emit_remainder_encodeSnappyBetterBlockAsm8B + JB three_bytes_emit_remainder_encodeSnappyBetterBlockAsm8B + +three_bytes_emit_remainder_encodeSnappyBetterBlockAsm8B: + MOVB $0xf4, (CX) + MOVW DX, 1(CX) + ADDQ $0x03, CX + JMP memmove_long_emit_remainder_encodeSnappyBetterBlockAsm8B + +two_bytes_emit_remainder_encodeSnappyBetterBlockAsm8B: + MOVB $0xf0, (CX) + MOVB DL, 1(CX) + ADDQ $0x02, CX + CMPL DX, $0x40 + JB memmove_emit_remainder_encodeSnappyBetterBlockAsm8B + JMP memmove_long_emit_remainder_encodeSnappyBetterBlockAsm8B + +one_byte_emit_remainder_encodeSnappyBetterBlockAsm8B: + SHLB $0x02, DL + MOVB DL, (CX) + ADDQ $0x01, CX + +memmove_emit_remainder_encodeSnappyBetterBlockAsm8B: + LEAQ (CX)(SI*1), DX + MOVL SI, BX + + // genMemMoveShort + CMPQ BX, $0x03 + JB emit_lit_memmove_emit_remainder_encodeSnappyBetterBlockAsm8B_memmove_move_1or2 + JE emit_lit_memmove_emit_remainder_encodeSnappyBetterBlockAsm8B_memmove_move_3 + CMPQ BX, $0x08 + JB emit_lit_memmove_emit_remainder_encodeSnappyBetterBlockAsm8B_memmove_move_4through7 + CMPQ BX, $0x10 + JBE emit_lit_memmove_emit_remainder_encodeSnappyBetterBlockAsm8B_memmove_move_8through16 + CMPQ BX, $0x20 + JBE emit_lit_memmove_emit_remainder_encodeSnappyBetterBlockAsm8B_memmove_move_17through32 + JMP emit_lit_memmove_emit_remainder_encodeSnappyBetterBlockAsm8B_memmove_move_33through64 + +emit_lit_memmove_emit_remainder_encodeSnappyBetterBlockAsm8B_memmove_move_1or2: + MOVB (AX), SI + MOVB -1(AX)(BX*1), AL + MOVB SI, (CX) + MOVB AL, -1(CX)(BX*1) + JMP memmove_end_copy_emit_remainder_encodeSnappyBetterBlockAsm8B + +emit_lit_memmove_emit_remainder_encodeSnappyBetterBlockAsm8B_memmove_move_3: + MOVW (AX), SI + MOVB 2(AX), AL + MOVW SI, (CX) + MOVB AL, 2(CX) + JMP memmove_end_copy_emit_remainder_encodeSnappyBetterBlockAsm8B + +emit_lit_memmove_emit_remainder_encodeSnappyBetterBlockAsm8B_memmove_move_4through7: + MOVL (AX), SI + MOVL -4(AX)(BX*1), AX + MOVL SI, (CX) + MOVL AX, -4(CX)(BX*1) + JMP memmove_end_copy_emit_remainder_encodeSnappyBetterBlockAsm8B + +emit_lit_memmove_emit_remainder_encodeSnappyBetterBlockAsm8B_memmove_move_8through16: + MOVQ (AX), SI + MOVQ -8(AX)(BX*1), AX + MOVQ SI, (CX) + MOVQ AX, -8(CX)(BX*1) + JMP memmove_end_copy_emit_remainder_encodeSnappyBetterBlockAsm8B + +emit_lit_memmove_emit_remainder_encodeSnappyBetterBlockAsm8B_memmove_move_17through32: + MOVOU (AX), X0 + MOVOU -16(AX)(BX*1), X1 + MOVOU X0, (CX) + MOVOU X1, -16(CX)(BX*1) + JMP memmove_end_copy_emit_remainder_encodeSnappyBetterBlockAsm8B + +emit_lit_memmove_emit_remainder_encodeSnappyBetterBlockAsm8B_memmove_move_33through64: + MOVOU (AX), X0 + MOVOU 16(AX), X1 + MOVOU -32(AX)(BX*1), X2 + MOVOU -16(AX)(BX*1), X3 + MOVOU X0, (CX) + MOVOU X1, 16(CX) + MOVOU X2, -32(CX)(BX*1) + MOVOU X3, -16(CX)(BX*1) + +memmove_end_copy_emit_remainder_encodeSnappyBetterBlockAsm8B: + MOVQ DX, CX + JMP emit_literal_done_emit_remainder_encodeSnappyBetterBlockAsm8B + +memmove_long_emit_remainder_encodeSnappyBetterBlockAsm8B: + LEAQ (CX)(SI*1), DX + MOVL SI, BX + + // genMemMoveLong + MOVOU (AX), X0 + MOVOU 16(AX), X1 + MOVOU -32(AX)(BX*1), X2 + MOVOU -16(AX)(BX*1), X3 + MOVQ BX, DI + SHRQ $0x05, DI + MOVQ CX, SI + ANDL $0x0000001f, SI + MOVQ $0x00000040, R8 + SUBQ SI, R8 + DECQ DI + JA emit_lit_memmove_long_emit_remainder_encodeSnappyBetterBlockAsm8Blarge_forward_sse_loop_32 + LEAQ -32(AX)(R8*1), SI + LEAQ -32(CX)(R8*1), R9 + +emit_lit_memmove_long_emit_remainder_encodeSnappyBetterBlockAsm8Blarge_big_loop_back: + MOVOU (SI), X4 + MOVOU 16(SI), X5 + MOVOA X4, (R9) + MOVOA X5, 16(R9) + ADDQ $0x20, R9 + ADDQ $0x20, SI + ADDQ $0x20, R8 + DECQ DI + JNA emit_lit_memmove_long_emit_remainder_encodeSnappyBetterBlockAsm8Blarge_big_loop_back + +emit_lit_memmove_long_emit_remainder_encodeSnappyBetterBlockAsm8Blarge_forward_sse_loop_32: + MOVOU -32(AX)(R8*1), X4 + MOVOU -16(AX)(R8*1), X5 + MOVOA X4, -32(CX)(R8*1) + MOVOA X5, -16(CX)(R8*1) + ADDQ $0x20, R8 + CMPQ BX, R8 + JAE emit_lit_memmove_long_emit_remainder_encodeSnappyBetterBlockAsm8Blarge_forward_sse_loop_32 + MOVOU X0, (CX) + MOVOU X1, 16(CX) + MOVOU X2, -32(CX)(BX*1) + MOVOU X3, -16(CX)(BX*1) + MOVQ DX, CX + +emit_literal_done_emit_remainder_encodeSnappyBetterBlockAsm8B: + MOVQ dst_base+0(FP), AX + SUBQ AX, CX + MOVQ CX, ret+56(FP) + RET + +// func calcBlockSize(src []byte, tmp *[32768]byte) int +// Requires: BMI, SSE2 +TEXT ·calcBlockSize(SB), $24-40 + MOVQ tmp+24(FP), AX + XORQ CX, CX + MOVQ $0x00000100, DX + MOVQ AX, BX + PXOR X0, X0 + +zero_loop_calcBlockSize: + MOVOU X0, (BX) + MOVOU X0, 16(BX) + MOVOU X0, 32(BX) + MOVOU X0, 48(BX) + MOVOU X0, 64(BX) + MOVOU X0, 80(BX) + MOVOU X0, 96(BX) + MOVOU X0, 112(BX) + ADDQ $0x80, BX + DECQ DX + JNZ zero_loop_calcBlockSize + MOVL $0x00000000, 12(SP) + MOVQ src_len+8(FP), DX + LEAQ -9(DX), BX + LEAQ -8(DX), SI + MOVL SI, 8(SP) + SHRQ $0x05, DX + SUBL DX, BX + LEAQ (CX)(BX*1), BX + MOVQ BX, (SP) + MOVL $0x00000001, DX + MOVL DX, 16(SP) + MOVQ src_base+0(FP), BX + +search_loop_calcBlockSize: + MOVL DX, SI + SUBL 12(SP), SI + SHRL $0x05, SI + LEAL 4(DX)(SI*1), SI + CMPL SI, 8(SP) + JAE emit_remainder_calcBlockSize + MOVQ (BX)(DX*1), DI + MOVL SI, 20(SP) + MOVQ $0x0000cf1bbcdcbf9b, R9 + MOVQ DI, R10 + MOVQ DI, R11 + SHRQ $0x08, R11 + SHLQ $0x10, R10 + IMULQ R9, R10 + SHRQ $0x33, R10 + SHLQ $0x10, R11 + IMULQ R9, R11 + SHRQ $0x33, R11 + MOVL (AX)(R10*4), SI + MOVL (AX)(R11*4), R8 + MOVL DX, (AX)(R10*4) + LEAL 1(DX), R10 + MOVL R10, (AX)(R11*4) + MOVQ DI, R10 + SHRQ $0x10, R10 + SHLQ $0x10, R10 + IMULQ R9, R10 + SHRQ $0x33, R10 + MOVL DX, R9 + SUBL 16(SP), R9 + MOVL 1(BX)(R9*1), R11 + MOVQ DI, R9 + SHRQ $0x08, R9 + CMPL R9, R11 + JNE no_repeat_found_calcBlockSize + LEAL 1(DX), DI + MOVL 12(SP), SI + MOVL DI, R8 + SUBL 16(SP), R8 + JZ repeat_extend_back_end_calcBlockSize + +repeat_extend_back_loop_calcBlockSize: + CMPL DI, SI + JBE repeat_extend_back_end_calcBlockSize + MOVB -1(BX)(R8*1), R9 + MOVB -1(BX)(DI*1), R10 + CMPB R9, R10 + JNE repeat_extend_back_end_calcBlockSize + LEAL -1(DI), DI + DECL R8 + JNZ repeat_extend_back_loop_calcBlockSize + +repeat_extend_back_end_calcBlockSize: + MOVL DI, SI + SUBL 12(SP), SI + LEAQ 5(CX)(SI*1), SI + CMPQ SI, (SP) + JB repeat_dst_size_check_calcBlockSize + MOVQ $0x00000000, ret+32(FP) + RET + +repeat_dst_size_check_calcBlockSize: + MOVL 12(SP), SI + CMPL SI, DI + JEQ emit_literal_done_repeat_emit_calcBlockSize + MOVL DI, R8 + MOVL DI, 12(SP) + LEAQ (BX)(SI*1), R9 + SUBL SI, R8 + LEAL -1(R8), SI + CMPL SI, $0x3c + JB one_byte_repeat_emit_calcBlockSize + CMPL SI, $0x00000100 + JB two_bytes_repeat_emit_calcBlockSize + CMPL SI, $0x00010000 + JB three_bytes_repeat_emit_calcBlockSize + CMPL SI, $0x01000000 + JB four_bytes_repeat_emit_calcBlockSize + ADDQ $0x05, CX + JMP memmove_long_repeat_emit_calcBlockSize + +four_bytes_repeat_emit_calcBlockSize: + ADDQ $0x04, CX + JMP memmove_long_repeat_emit_calcBlockSize + +three_bytes_repeat_emit_calcBlockSize: + ADDQ $0x03, CX + JMP memmove_long_repeat_emit_calcBlockSize + +two_bytes_repeat_emit_calcBlockSize: + ADDQ $0x02, CX + CMPL SI, $0x40 + JB memmove_repeat_emit_calcBlockSize + JMP memmove_long_repeat_emit_calcBlockSize + +one_byte_repeat_emit_calcBlockSize: + ADDQ $0x01, CX + +memmove_repeat_emit_calcBlockSize: + LEAQ (CX)(R8*1), CX + JMP emit_literal_done_repeat_emit_calcBlockSize + +memmove_long_repeat_emit_calcBlockSize: + LEAQ (CX)(R8*1), CX + +emit_literal_done_repeat_emit_calcBlockSize: + ADDL $0x05, DX + MOVL DX, SI + SUBL 16(SP), SI + MOVQ src_len+8(FP), R8 + SUBL DX, R8 + LEAQ (BX)(DX*1), R9 + LEAQ (BX)(SI*1), SI + + // matchLen + XORL R11, R11 + +matchlen_loopback_16_repeat_extend_calcBlockSize: + CMPL R8, $0x10 + JB matchlen_match8_repeat_extend_calcBlockSize + MOVQ (R9)(R11*1), R10 + MOVQ 8(R9)(R11*1), R12 + XORQ (SI)(R11*1), R10 + JNZ matchlen_bsf_8_repeat_extend_calcBlockSize + XORQ 8(SI)(R11*1), R12 + JNZ matchlen_bsf_16repeat_extend_calcBlockSize + LEAL -16(R8), R8 + LEAL 16(R11), R11 + JMP matchlen_loopback_16_repeat_extend_calcBlockSize + +matchlen_bsf_16repeat_extend_calcBlockSize: +#ifdef GOAMD64_v3 + TZCNTQ R12, R12 + +#else + BSFQ R12, R12 + +#endif + SARQ $0x03, R12 + LEAL 8(R11)(R12*1), R11 + JMP repeat_extend_forward_end_calcBlockSize + +matchlen_match8_repeat_extend_calcBlockSize: + CMPL R8, $0x08 + JB matchlen_match4_repeat_extend_calcBlockSize + MOVQ (R9)(R11*1), R10 + XORQ (SI)(R11*1), R10 + JNZ matchlen_bsf_8_repeat_extend_calcBlockSize + LEAL -8(R8), R8 + LEAL 8(R11), R11 + JMP matchlen_match4_repeat_extend_calcBlockSize + +matchlen_bsf_8_repeat_extend_calcBlockSize: +#ifdef GOAMD64_v3 + TZCNTQ R10, R10 + +#else + BSFQ R10, R10 + +#endif + SARQ $0x03, R10 + LEAL (R11)(R10*1), R11 + JMP repeat_extend_forward_end_calcBlockSize + +matchlen_match4_repeat_extend_calcBlockSize: + CMPL R8, $0x04 + JB matchlen_match2_repeat_extend_calcBlockSize + MOVL (R9)(R11*1), R10 + CMPL (SI)(R11*1), R10 + JNE matchlen_match2_repeat_extend_calcBlockSize + LEAL -4(R8), R8 + LEAL 4(R11), R11 + +matchlen_match2_repeat_extend_calcBlockSize: + CMPL R8, $0x01 + JE matchlen_match1_repeat_extend_calcBlockSize + JB repeat_extend_forward_end_calcBlockSize + MOVW (R9)(R11*1), R10 + CMPW (SI)(R11*1), R10 + JNE matchlen_match1_repeat_extend_calcBlockSize + LEAL 2(R11), R11 + SUBL $0x02, R8 + JZ repeat_extend_forward_end_calcBlockSize + +matchlen_match1_repeat_extend_calcBlockSize: + MOVB (R9)(R11*1), R10 + CMPB (SI)(R11*1), R10 + JNE repeat_extend_forward_end_calcBlockSize + LEAL 1(R11), R11 + +repeat_extend_forward_end_calcBlockSize: + ADDL R11, DX + MOVL DX, SI + SUBL DI, SI + MOVL 16(SP), DI + + // emitCopy + CMPL DI, $0x00010000 + JB two_byte_offset_repeat_as_copy_calcBlockSize + +four_bytes_loop_back_repeat_as_copy_calcBlockSize: + CMPL SI, $0x40 + JBE four_bytes_remain_repeat_as_copy_calcBlockSize + LEAL -64(SI), SI + ADDQ $0x05, CX + CMPL SI, $0x04 + JB four_bytes_remain_repeat_as_copy_calcBlockSize + JMP four_bytes_loop_back_repeat_as_copy_calcBlockSize + +four_bytes_remain_repeat_as_copy_calcBlockSize: + TESTL SI, SI + JZ repeat_end_emit_calcBlockSize + XORL SI, SI + ADDQ $0x05, CX + JMP repeat_end_emit_calcBlockSize + +two_byte_offset_repeat_as_copy_calcBlockSize: + CMPL SI, $0x40 + JBE two_byte_offset_short_repeat_as_copy_calcBlockSize + LEAL -60(SI), SI + ADDQ $0x03, CX + JMP two_byte_offset_repeat_as_copy_calcBlockSize + +two_byte_offset_short_repeat_as_copy_calcBlockSize: + MOVL SI, R8 + SHLL $0x02, R8 + CMPL SI, $0x0c + JAE emit_copy_three_repeat_as_copy_calcBlockSize + CMPL DI, $0x00000800 + JAE emit_copy_three_repeat_as_copy_calcBlockSize + ADDQ $0x02, CX + JMP repeat_end_emit_calcBlockSize + +emit_copy_three_repeat_as_copy_calcBlockSize: + ADDQ $0x03, CX + +repeat_end_emit_calcBlockSize: + MOVL DX, 12(SP) + JMP search_loop_calcBlockSize + +no_repeat_found_calcBlockSize: + CMPL (BX)(SI*1), DI + JEQ candidate_match_calcBlockSize + SHRQ $0x08, DI + MOVL (AX)(R10*4), SI + LEAL 2(DX), R9 + CMPL (BX)(R8*1), DI + JEQ candidate2_match_calcBlockSize + MOVL R9, (AX)(R10*4) + SHRQ $0x08, DI + CMPL (BX)(SI*1), DI + JEQ candidate3_match_calcBlockSize + MOVL 20(SP), DX + JMP search_loop_calcBlockSize + +candidate3_match_calcBlockSize: + ADDL $0x02, DX + JMP candidate_match_calcBlockSize + +candidate2_match_calcBlockSize: + MOVL R9, (AX)(R10*4) + INCL DX + MOVL R8, SI + +candidate_match_calcBlockSize: + MOVL 12(SP), DI + TESTL SI, SI + JZ match_extend_back_end_calcBlockSize + +match_extend_back_loop_calcBlockSize: + CMPL DX, DI + JBE match_extend_back_end_calcBlockSize + MOVB -1(BX)(SI*1), R8 + MOVB -1(BX)(DX*1), R9 + CMPB R8, R9 + JNE match_extend_back_end_calcBlockSize + LEAL -1(DX), DX + DECL SI + JZ match_extend_back_end_calcBlockSize + JMP match_extend_back_loop_calcBlockSize + +match_extend_back_end_calcBlockSize: + MOVL DX, DI + SUBL 12(SP), DI + LEAQ 5(CX)(DI*1), DI + CMPQ DI, (SP) + JB match_dst_size_check_calcBlockSize + MOVQ $0x00000000, ret+32(FP) + RET + +match_dst_size_check_calcBlockSize: + MOVL DX, DI + MOVL 12(SP), R8 + CMPL R8, DI + JEQ emit_literal_done_match_emit_calcBlockSize + MOVL DI, R9 + MOVL DI, 12(SP) + LEAQ (BX)(R8*1), DI + SUBL R8, R9 + LEAL -1(R9), DI + CMPL DI, $0x3c + JB one_byte_match_emit_calcBlockSize + CMPL DI, $0x00000100 + JB two_bytes_match_emit_calcBlockSize + CMPL DI, $0x00010000 + JB three_bytes_match_emit_calcBlockSize + CMPL DI, $0x01000000 + JB four_bytes_match_emit_calcBlockSize + ADDQ $0x05, CX + JMP memmove_long_match_emit_calcBlockSize + +four_bytes_match_emit_calcBlockSize: + ADDQ $0x04, CX + JMP memmove_long_match_emit_calcBlockSize + +three_bytes_match_emit_calcBlockSize: + ADDQ $0x03, CX + JMP memmove_long_match_emit_calcBlockSize + +two_bytes_match_emit_calcBlockSize: + ADDQ $0x02, CX + CMPL DI, $0x40 + JB memmove_match_emit_calcBlockSize + JMP memmove_long_match_emit_calcBlockSize + +one_byte_match_emit_calcBlockSize: + ADDQ $0x01, CX + +memmove_match_emit_calcBlockSize: + LEAQ (CX)(R9*1), CX + JMP emit_literal_done_match_emit_calcBlockSize + +memmove_long_match_emit_calcBlockSize: + LEAQ (CX)(R9*1), CX + +emit_literal_done_match_emit_calcBlockSize: +match_nolit_loop_calcBlockSize: + MOVL DX, DI + SUBL SI, DI + MOVL DI, 16(SP) + ADDL $0x04, DX + ADDL $0x04, SI + MOVQ src_len+8(FP), DI + SUBL DX, DI + LEAQ (BX)(DX*1), R8 + LEAQ (BX)(SI*1), SI + + // matchLen + XORL R10, R10 + +matchlen_loopback_16_match_nolit_calcBlockSize: + CMPL DI, $0x10 + JB matchlen_match8_match_nolit_calcBlockSize + MOVQ (R8)(R10*1), R9 + MOVQ 8(R8)(R10*1), R11 + XORQ (SI)(R10*1), R9 + JNZ matchlen_bsf_8_match_nolit_calcBlockSize + XORQ 8(SI)(R10*1), R11 + JNZ matchlen_bsf_16match_nolit_calcBlockSize + LEAL -16(DI), DI + LEAL 16(R10), R10 + JMP matchlen_loopback_16_match_nolit_calcBlockSize + +matchlen_bsf_16match_nolit_calcBlockSize: +#ifdef GOAMD64_v3 + TZCNTQ R11, R11 + +#else + BSFQ R11, R11 + +#endif + SARQ $0x03, R11 + LEAL 8(R10)(R11*1), R10 + JMP match_nolit_end_calcBlockSize + +matchlen_match8_match_nolit_calcBlockSize: + CMPL DI, $0x08 + JB matchlen_match4_match_nolit_calcBlockSize + MOVQ (R8)(R10*1), R9 + XORQ (SI)(R10*1), R9 + JNZ matchlen_bsf_8_match_nolit_calcBlockSize + LEAL -8(DI), DI + LEAL 8(R10), R10 + JMP matchlen_match4_match_nolit_calcBlockSize + +matchlen_bsf_8_match_nolit_calcBlockSize: +#ifdef GOAMD64_v3 + TZCNTQ R9, R9 + +#else + BSFQ R9, R9 + +#endif + SARQ $0x03, R9 + LEAL (R10)(R9*1), R10 + JMP match_nolit_end_calcBlockSize + +matchlen_match4_match_nolit_calcBlockSize: + CMPL DI, $0x04 + JB matchlen_match2_match_nolit_calcBlockSize + MOVL (R8)(R10*1), R9 + CMPL (SI)(R10*1), R9 + JNE matchlen_match2_match_nolit_calcBlockSize + LEAL -4(DI), DI + LEAL 4(R10), R10 + +matchlen_match2_match_nolit_calcBlockSize: + CMPL DI, $0x01 + JE matchlen_match1_match_nolit_calcBlockSize + JB match_nolit_end_calcBlockSize + MOVW (R8)(R10*1), R9 + CMPW (SI)(R10*1), R9 + JNE matchlen_match1_match_nolit_calcBlockSize + LEAL 2(R10), R10 + SUBL $0x02, DI + JZ match_nolit_end_calcBlockSize + +matchlen_match1_match_nolit_calcBlockSize: + MOVB (R8)(R10*1), R9 + CMPB (SI)(R10*1), R9 + JNE match_nolit_end_calcBlockSize + LEAL 1(R10), R10 + +match_nolit_end_calcBlockSize: + ADDL R10, DX + MOVL 16(SP), SI + ADDL $0x04, R10 + MOVL DX, 12(SP) + + // emitCopy + CMPL SI, $0x00010000 + JB two_byte_offset_match_nolit_calcBlockSize + +four_bytes_loop_back_match_nolit_calcBlockSize: + CMPL R10, $0x40 + JBE four_bytes_remain_match_nolit_calcBlockSize + LEAL -64(R10), R10 + ADDQ $0x05, CX + CMPL R10, $0x04 + JB four_bytes_remain_match_nolit_calcBlockSize + JMP four_bytes_loop_back_match_nolit_calcBlockSize + +four_bytes_remain_match_nolit_calcBlockSize: + TESTL R10, R10 + JZ match_nolit_emitcopy_end_calcBlockSize + XORL SI, SI + ADDQ $0x05, CX + JMP match_nolit_emitcopy_end_calcBlockSize + +two_byte_offset_match_nolit_calcBlockSize: + CMPL R10, $0x40 + JBE two_byte_offset_short_match_nolit_calcBlockSize + LEAL -60(R10), R10 + ADDQ $0x03, CX + JMP two_byte_offset_match_nolit_calcBlockSize + +two_byte_offset_short_match_nolit_calcBlockSize: + MOVL R10, DI + SHLL $0x02, DI + CMPL R10, $0x0c + JAE emit_copy_three_match_nolit_calcBlockSize + CMPL SI, $0x00000800 + JAE emit_copy_three_match_nolit_calcBlockSize + ADDQ $0x02, CX + JMP match_nolit_emitcopy_end_calcBlockSize + +emit_copy_three_match_nolit_calcBlockSize: + ADDQ $0x03, CX + +match_nolit_emitcopy_end_calcBlockSize: + CMPL DX, 8(SP) + JAE emit_remainder_calcBlockSize + MOVQ -2(BX)(DX*1), DI + CMPQ CX, (SP) + JB match_nolit_dst_ok_calcBlockSize + MOVQ $0x00000000, ret+32(FP) + RET + +match_nolit_dst_ok_calcBlockSize: + MOVQ $0x0000cf1bbcdcbf9b, R9 + MOVQ DI, R8 + SHRQ $0x10, DI + MOVQ DI, SI + SHLQ $0x10, R8 + IMULQ R9, R8 + SHRQ $0x33, R8 + SHLQ $0x10, SI + IMULQ R9, SI + SHRQ $0x33, SI + LEAL -2(DX), R9 + LEAQ (AX)(SI*4), R10 + MOVL (R10), SI + MOVL R9, (AX)(R8*4) + MOVL DX, (R10) + CMPL (BX)(SI*1), DI + JEQ match_nolit_loop_calcBlockSize + INCL DX + JMP search_loop_calcBlockSize + +emit_remainder_calcBlockSize: + MOVQ src_len+8(FP), AX + SUBL 12(SP), AX + LEAQ 5(CX)(AX*1), AX + CMPQ AX, (SP) + JB emit_remainder_ok_calcBlockSize + MOVQ $0x00000000, ret+32(FP) + RET + +emit_remainder_ok_calcBlockSize: + MOVQ src_len+8(FP), AX + MOVL 12(SP), DX + CMPL DX, AX + JEQ emit_literal_done_emit_remainder_calcBlockSize + MOVL AX, SI + MOVL AX, 12(SP) + LEAQ (BX)(DX*1), AX + SUBL DX, SI + LEAL -1(SI), AX + CMPL AX, $0x3c + JB one_byte_emit_remainder_calcBlockSize + CMPL AX, $0x00000100 + JB two_bytes_emit_remainder_calcBlockSize + CMPL AX, $0x00010000 + JB three_bytes_emit_remainder_calcBlockSize + CMPL AX, $0x01000000 + JB four_bytes_emit_remainder_calcBlockSize + ADDQ $0x05, CX + JMP memmove_long_emit_remainder_calcBlockSize + +four_bytes_emit_remainder_calcBlockSize: + ADDQ $0x04, CX + JMP memmove_long_emit_remainder_calcBlockSize + +three_bytes_emit_remainder_calcBlockSize: + ADDQ $0x03, CX + JMP memmove_long_emit_remainder_calcBlockSize + +two_bytes_emit_remainder_calcBlockSize: + ADDQ $0x02, CX + CMPL AX, $0x40 + JB memmove_emit_remainder_calcBlockSize + JMP memmove_long_emit_remainder_calcBlockSize + +one_byte_emit_remainder_calcBlockSize: + ADDQ $0x01, CX + +memmove_emit_remainder_calcBlockSize: + LEAQ (CX)(SI*1), AX + MOVQ AX, CX + JMP emit_literal_done_emit_remainder_calcBlockSize + +memmove_long_emit_remainder_calcBlockSize: + LEAQ (CX)(SI*1), AX + MOVQ AX, CX + +emit_literal_done_emit_remainder_calcBlockSize: + MOVQ CX, ret+32(FP) + RET + +// func calcBlockSizeSmall(src []byte, tmp *[2048]byte) int +// Requires: BMI, SSE2 +TEXT ·calcBlockSizeSmall(SB), $24-40 + MOVQ tmp+24(FP), AX + XORQ CX, CX + MOVQ $0x00000010, DX + MOVQ AX, BX + PXOR X0, X0 + +zero_loop_calcBlockSizeSmall: + MOVOU X0, (BX) + MOVOU X0, 16(BX) + MOVOU X0, 32(BX) + MOVOU X0, 48(BX) + MOVOU X0, 64(BX) + MOVOU X0, 80(BX) + MOVOU X0, 96(BX) + MOVOU X0, 112(BX) + ADDQ $0x80, BX + DECQ DX + JNZ zero_loop_calcBlockSizeSmall + MOVL $0x00000000, 12(SP) + MOVQ src_len+8(FP), DX + LEAQ -9(DX), BX + LEAQ -8(DX), SI + MOVL SI, 8(SP) + SHRQ $0x05, DX + SUBL DX, BX + LEAQ (CX)(BX*1), BX + MOVQ BX, (SP) + MOVL $0x00000001, DX + MOVL DX, 16(SP) + MOVQ src_base+0(FP), BX + +search_loop_calcBlockSizeSmall: + MOVL DX, SI + SUBL 12(SP), SI + SHRL $0x04, SI + LEAL 4(DX)(SI*1), SI + CMPL SI, 8(SP) + JAE emit_remainder_calcBlockSizeSmall + MOVQ (BX)(DX*1), DI + MOVL SI, 20(SP) + MOVQ $0x9e3779b1, R9 + MOVQ DI, R10 + MOVQ DI, R11 + SHRQ $0x08, R11 + SHLQ $0x20, R10 + IMULQ R9, R10 + SHRQ $0x37, R10 + SHLQ $0x20, R11 + IMULQ R9, R11 + SHRQ $0x37, R11 + MOVL (AX)(R10*4), SI + MOVL (AX)(R11*4), R8 + MOVL DX, (AX)(R10*4) + LEAL 1(DX), R10 + MOVL R10, (AX)(R11*4) + MOVQ DI, R10 + SHRQ $0x10, R10 + SHLQ $0x20, R10 + IMULQ R9, R10 + SHRQ $0x37, R10 + MOVL DX, R9 + SUBL 16(SP), R9 + MOVL 1(BX)(R9*1), R11 + MOVQ DI, R9 + SHRQ $0x08, R9 + CMPL R9, R11 + JNE no_repeat_found_calcBlockSizeSmall + LEAL 1(DX), DI + MOVL 12(SP), SI + MOVL DI, R8 + SUBL 16(SP), R8 + JZ repeat_extend_back_end_calcBlockSizeSmall + +repeat_extend_back_loop_calcBlockSizeSmall: + CMPL DI, SI + JBE repeat_extend_back_end_calcBlockSizeSmall + MOVB -1(BX)(R8*1), R9 + MOVB -1(BX)(DI*1), R10 + CMPB R9, R10 + JNE repeat_extend_back_end_calcBlockSizeSmall + LEAL -1(DI), DI + DECL R8 + JNZ repeat_extend_back_loop_calcBlockSizeSmall + +repeat_extend_back_end_calcBlockSizeSmall: + MOVL DI, SI + SUBL 12(SP), SI + LEAQ 3(CX)(SI*1), SI + CMPQ SI, (SP) + JB repeat_dst_size_check_calcBlockSizeSmall + MOVQ $0x00000000, ret+32(FP) + RET + +repeat_dst_size_check_calcBlockSizeSmall: + MOVL 12(SP), SI + CMPL SI, DI + JEQ emit_literal_done_repeat_emit_calcBlockSizeSmall + MOVL DI, R8 + MOVL DI, 12(SP) + LEAQ (BX)(SI*1), R9 + SUBL SI, R8 + LEAL -1(R8), SI + CMPL SI, $0x3c + JB one_byte_repeat_emit_calcBlockSizeSmall + CMPL SI, $0x00000100 + JB two_bytes_repeat_emit_calcBlockSizeSmall + JB three_bytes_repeat_emit_calcBlockSizeSmall + +three_bytes_repeat_emit_calcBlockSizeSmall: + ADDQ $0x03, CX + JMP memmove_long_repeat_emit_calcBlockSizeSmall + +two_bytes_repeat_emit_calcBlockSizeSmall: + ADDQ $0x02, CX + CMPL SI, $0x40 + JB memmove_repeat_emit_calcBlockSizeSmall + JMP memmove_long_repeat_emit_calcBlockSizeSmall + +one_byte_repeat_emit_calcBlockSizeSmall: + ADDQ $0x01, CX + +memmove_repeat_emit_calcBlockSizeSmall: + LEAQ (CX)(R8*1), CX + JMP emit_literal_done_repeat_emit_calcBlockSizeSmall + +memmove_long_repeat_emit_calcBlockSizeSmall: + LEAQ (CX)(R8*1), CX + +emit_literal_done_repeat_emit_calcBlockSizeSmall: + ADDL $0x05, DX + MOVL DX, SI + SUBL 16(SP), SI + MOVQ src_len+8(FP), R8 + SUBL DX, R8 + LEAQ (BX)(DX*1), R9 + LEAQ (BX)(SI*1), SI + + // matchLen + XORL R11, R11 + +matchlen_loopback_16_repeat_extend_calcBlockSizeSmall: + CMPL R8, $0x10 + JB matchlen_match8_repeat_extend_calcBlockSizeSmall + MOVQ (R9)(R11*1), R10 + MOVQ 8(R9)(R11*1), R12 + XORQ (SI)(R11*1), R10 + JNZ matchlen_bsf_8_repeat_extend_calcBlockSizeSmall + XORQ 8(SI)(R11*1), R12 + JNZ matchlen_bsf_16repeat_extend_calcBlockSizeSmall + LEAL -16(R8), R8 + LEAL 16(R11), R11 + JMP matchlen_loopback_16_repeat_extend_calcBlockSizeSmall + +matchlen_bsf_16repeat_extend_calcBlockSizeSmall: +#ifdef GOAMD64_v3 + TZCNTQ R12, R12 + +#else + BSFQ R12, R12 + +#endif + SARQ $0x03, R12 + LEAL 8(R11)(R12*1), R11 + JMP repeat_extend_forward_end_calcBlockSizeSmall + +matchlen_match8_repeat_extend_calcBlockSizeSmall: + CMPL R8, $0x08 + JB matchlen_match4_repeat_extend_calcBlockSizeSmall + MOVQ (R9)(R11*1), R10 + XORQ (SI)(R11*1), R10 + JNZ matchlen_bsf_8_repeat_extend_calcBlockSizeSmall + LEAL -8(R8), R8 + LEAL 8(R11), R11 + JMP matchlen_match4_repeat_extend_calcBlockSizeSmall + +matchlen_bsf_8_repeat_extend_calcBlockSizeSmall: +#ifdef GOAMD64_v3 + TZCNTQ R10, R10 + +#else + BSFQ R10, R10 + +#endif + SARQ $0x03, R10 + LEAL (R11)(R10*1), R11 + JMP repeat_extend_forward_end_calcBlockSizeSmall + +matchlen_match4_repeat_extend_calcBlockSizeSmall: + CMPL R8, $0x04 + JB matchlen_match2_repeat_extend_calcBlockSizeSmall + MOVL (R9)(R11*1), R10 + CMPL (SI)(R11*1), R10 + JNE matchlen_match2_repeat_extend_calcBlockSizeSmall + LEAL -4(R8), R8 + LEAL 4(R11), R11 + +matchlen_match2_repeat_extend_calcBlockSizeSmall: + CMPL R8, $0x01 + JE matchlen_match1_repeat_extend_calcBlockSizeSmall + JB repeat_extend_forward_end_calcBlockSizeSmall + MOVW (R9)(R11*1), R10 + CMPW (SI)(R11*1), R10 + JNE matchlen_match1_repeat_extend_calcBlockSizeSmall + LEAL 2(R11), R11 + SUBL $0x02, R8 + JZ repeat_extend_forward_end_calcBlockSizeSmall + +matchlen_match1_repeat_extend_calcBlockSizeSmall: + MOVB (R9)(R11*1), R10 + CMPB (SI)(R11*1), R10 + JNE repeat_extend_forward_end_calcBlockSizeSmall + LEAL 1(R11), R11 + +repeat_extend_forward_end_calcBlockSizeSmall: + ADDL R11, DX + MOVL DX, SI + SUBL DI, SI + MOVL 16(SP), DI + + // emitCopy +two_byte_offset_repeat_as_copy_calcBlockSizeSmall: + CMPL SI, $0x40 + JBE two_byte_offset_short_repeat_as_copy_calcBlockSizeSmall + LEAL -60(SI), SI + ADDQ $0x03, CX + JMP two_byte_offset_repeat_as_copy_calcBlockSizeSmall + +two_byte_offset_short_repeat_as_copy_calcBlockSizeSmall: + MOVL SI, DI + SHLL $0x02, DI + CMPL SI, $0x0c + JAE emit_copy_three_repeat_as_copy_calcBlockSizeSmall + ADDQ $0x02, CX + JMP repeat_end_emit_calcBlockSizeSmall + +emit_copy_three_repeat_as_copy_calcBlockSizeSmall: + ADDQ $0x03, CX + +repeat_end_emit_calcBlockSizeSmall: + MOVL DX, 12(SP) + JMP search_loop_calcBlockSizeSmall + +no_repeat_found_calcBlockSizeSmall: + CMPL (BX)(SI*1), DI + JEQ candidate_match_calcBlockSizeSmall + SHRQ $0x08, DI + MOVL (AX)(R10*4), SI + LEAL 2(DX), R9 + CMPL (BX)(R8*1), DI + JEQ candidate2_match_calcBlockSizeSmall + MOVL R9, (AX)(R10*4) + SHRQ $0x08, DI + CMPL (BX)(SI*1), DI + JEQ candidate3_match_calcBlockSizeSmall + MOVL 20(SP), DX + JMP search_loop_calcBlockSizeSmall + +candidate3_match_calcBlockSizeSmall: + ADDL $0x02, DX + JMP candidate_match_calcBlockSizeSmall + +candidate2_match_calcBlockSizeSmall: + MOVL R9, (AX)(R10*4) + INCL DX + MOVL R8, SI + +candidate_match_calcBlockSizeSmall: + MOVL 12(SP), DI + TESTL SI, SI + JZ match_extend_back_end_calcBlockSizeSmall + +match_extend_back_loop_calcBlockSizeSmall: + CMPL DX, DI + JBE match_extend_back_end_calcBlockSizeSmall + MOVB -1(BX)(SI*1), R8 + MOVB -1(BX)(DX*1), R9 + CMPB R8, R9 + JNE match_extend_back_end_calcBlockSizeSmall + LEAL -1(DX), DX + DECL SI + JZ match_extend_back_end_calcBlockSizeSmall + JMP match_extend_back_loop_calcBlockSizeSmall + +match_extend_back_end_calcBlockSizeSmall: + MOVL DX, DI + SUBL 12(SP), DI + LEAQ 3(CX)(DI*1), DI + CMPQ DI, (SP) + JB match_dst_size_check_calcBlockSizeSmall + MOVQ $0x00000000, ret+32(FP) + RET + +match_dst_size_check_calcBlockSizeSmall: + MOVL DX, DI + MOVL 12(SP), R8 + CMPL R8, DI + JEQ emit_literal_done_match_emit_calcBlockSizeSmall + MOVL DI, R9 + MOVL DI, 12(SP) + LEAQ (BX)(R8*1), DI + SUBL R8, R9 + LEAL -1(R9), DI + CMPL DI, $0x3c + JB one_byte_match_emit_calcBlockSizeSmall + CMPL DI, $0x00000100 + JB two_bytes_match_emit_calcBlockSizeSmall + JB three_bytes_match_emit_calcBlockSizeSmall + +three_bytes_match_emit_calcBlockSizeSmall: + ADDQ $0x03, CX + JMP memmove_long_match_emit_calcBlockSizeSmall + +two_bytes_match_emit_calcBlockSizeSmall: + ADDQ $0x02, CX + CMPL DI, $0x40 + JB memmove_match_emit_calcBlockSizeSmall + JMP memmove_long_match_emit_calcBlockSizeSmall + +one_byte_match_emit_calcBlockSizeSmall: + ADDQ $0x01, CX + +memmove_match_emit_calcBlockSizeSmall: + LEAQ (CX)(R9*1), CX + JMP emit_literal_done_match_emit_calcBlockSizeSmall + +memmove_long_match_emit_calcBlockSizeSmall: + LEAQ (CX)(R9*1), CX + +emit_literal_done_match_emit_calcBlockSizeSmall: +match_nolit_loop_calcBlockSizeSmall: + MOVL DX, DI + SUBL SI, DI + MOVL DI, 16(SP) + ADDL $0x04, DX + ADDL $0x04, SI + MOVQ src_len+8(FP), DI + SUBL DX, DI + LEAQ (BX)(DX*1), R8 + LEAQ (BX)(SI*1), SI + + // matchLen + XORL R10, R10 + +matchlen_loopback_16_match_nolit_calcBlockSizeSmall: + CMPL DI, $0x10 + JB matchlen_match8_match_nolit_calcBlockSizeSmall + MOVQ (R8)(R10*1), R9 + MOVQ 8(R8)(R10*1), R11 + XORQ (SI)(R10*1), R9 + JNZ matchlen_bsf_8_match_nolit_calcBlockSizeSmall + XORQ 8(SI)(R10*1), R11 + JNZ matchlen_bsf_16match_nolit_calcBlockSizeSmall + LEAL -16(DI), DI + LEAL 16(R10), R10 + JMP matchlen_loopback_16_match_nolit_calcBlockSizeSmall + +matchlen_bsf_16match_nolit_calcBlockSizeSmall: +#ifdef GOAMD64_v3 + TZCNTQ R11, R11 + +#else + BSFQ R11, R11 + +#endif + SARQ $0x03, R11 + LEAL 8(R10)(R11*1), R10 + JMP match_nolit_end_calcBlockSizeSmall + +matchlen_match8_match_nolit_calcBlockSizeSmall: + CMPL DI, $0x08 + JB matchlen_match4_match_nolit_calcBlockSizeSmall + MOVQ (R8)(R10*1), R9 + XORQ (SI)(R10*1), R9 + JNZ matchlen_bsf_8_match_nolit_calcBlockSizeSmall + LEAL -8(DI), DI + LEAL 8(R10), R10 + JMP matchlen_match4_match_nolit_calcBlockSizeSmall + +matchlen_bsf_8_match_nolit_calcBlockSizeSmall: +#ifdef GOAMD64_v3 + TZCNTQ R9, R9 + +#else + BSFQ R9, R9 + +#endif + SARQ $0x03, R9 + LEAL (R10)(R9*1), R10 + JMP match_nolit_end_calcBlockSizeSmall + +matchlen_match4_match_nolit_calcBlockSizeSmall: + CMPL DI, $0x04 + JB matchlen_match2_match_nolit_calcBlockSizeSmall + MOVL (R8)(R10*1), R9 + CMPL (SI)(R10*1), R9 + JNE matchlen_match2_match_nolit_calcBlockSizeSmall + LEAL -4(DI), DI + LEAL 4(R10), R10 + +matchlen_match2_match_nolit_calcBlockSizeSmall: + CMPL DI, $0x01 + JE matchlen_match1_match_nolit_calcBlockSizeSmall + JB match_nolit_end_calcBlockSizeSmall + MOVW (R8)(R10*1), R9 + CMPW (SI)(R10*1), R9 + JNE matchlen_match1_match_nolit_calcBlockSizeSmall + LEAL 2(R10), R10 + SUBL $0x02, DI + JZ match_nolit_end_calcBlockSizeSmall + +matchlen_match1_match_nolit_calcBlockSizeSmall: + MOVB (R8)(R10*1), R9 + CMPB (SI)(R10*1), R9 + JNE match_nolit_end_calcBlockSizeSmall + LEAL 1(R10), R10 + +match_nolit_end_calcBlockSizeSmall: + ADDL R10, DX + MOVL 16(SP), SI + ADDL $0x04, R10 + MOVL DX, 12(SP) + + // emitCopy +two_byte_offset_match_nolit_calcBlockSizeSmall: + CMPL R10, $0x40 + JBE two_byte_offset_short_match_nolit_calcBlockSizeSmall + LEAL -60(R10), R10 + ADDQ $0x03, CX + JMP two_byte_offset_match_nolit_calcBlockSizeSmall + +two_byte_offset_short_match_nolit_calcBlockSizeSmall: + MOVL R10, SI + SHLL $0x02, SI + CMPL R10, $0x0c + JAE emit_copy_three_match_nolit_calcBlockSizeSmall + ADDQ $0x02, CX + JMP match_nolit_emitcopy_end_calcBlockSizeSmall + +emit_copy_three_match_nolit_calcBlockSizeSmall: + ADDQ $0x03, CX + +match_nolit_emitcopy_end_calcBlockSizeSmall: + CMPL DX, 8(SP) + JAE emit_remainder_calcBlockSizeSmall + MOVQ -2(BX)(DX*1), DI + CMPQ CX, (SP) + JB match_nolit_dst_ok_calcBlockSizeSmall + MOVQ $0x00000000, ret+32(FP) + RET + +match_nolit_dst_ok_calcBlockSizeSmall: + MOVQ $0x9e3779b1, R9 + MOVQ DI, R8 + SHRQ $0x10, DI + MOVQ DI, SI + SHLQ $0x20, R8 + IMULQ R9, R8 + SHRQ $0x37, R8 + SHLQ $0x20, SI + IMULQ R9, SI + SHRQ $0x37, SI + LEAL -2(DX), R9 + LEAQ (AX)(SI*4), R10 + MOVL (R10), SI + MOVL R9, (AX)(R8*4) + MOVL DX, (R10) + CMPL (BX)(SI*1), DI + JEQ match_nolit_loop_calcBlockSizeSmall + INCL DX + JMP search_loop_calcBlockSizeSmall + +emit_remainder_calcBlockSizeSmall: + MOVQ src_len+8(FP), AX + SUBL 12(SP), AX + LEAQ 3(CX)(AX*1), AX + CMPQ AX, (SP) + JB emit_remainder_ok_calcBlockSizeSmall + MOVQ $0x00000000, ret+32(FP) + RET + +emit_remainder_ok_calcBlockSizeSmall: + MOVQ src_len+8(FP), AX + MOVL 12(SP), DX + CMPL DX, AX + JEQ emit_literal_done_emit_remainder_calcBlockSizeSmall + MOVL AX, SI + MOVL AX, 12(SP) + LEAQ (BX)(DX*1), AX + SUBL DX, SI + LEAL -1(SI), AX + CMPL AX, $0x3c + JB one_byte_emit_remainder_calcBlockSizeSmall + CMPL AX, $0x00000100 + JB two_bytes_emit_remainder_calcBlockSizeSmall + JB three_bytes_emit_remainder_calcBlockSizeSmall + +three_bytes_emit_remainder_calcBlockSizeSmall: + ADDQ $0x03, CX + JMP memmove_long_emit_remainder_calcBlockSizeSmall + +two_bytes_emit_remainder_calcBlockSizeSmall: + ADDQ $0x02, CX + CMPL AX, $0x40 + JB memmove_emit_remainder_calcBlockSizeSmall + JMP memmove_long_emit_remainder_calcBlockSizeSmall + +one_byte_emit_remainder_calcBlockSizeSmall: + ADDQ $0x01, CX + +memmove_emit_remainder_calcBlockSizeSmall: + LEAQ (CX)(SI*1), AX + MOVQ AX, CX + JMP emit_literal_done_emit_remainder_calcBlockSizeSmall + +memmove_long_emit_remainder_calcBlockSizeSmall: + LEAQ (CX)(SI*1), AX + MOVQ AX, CX + +emit_literal_done_emit_remainder_calcBlockSizeSmall: + MOVQ CX, ret+32(FP) + RET + +// func emitLiteral(dst []byte, lit []byte) int +// Requires: SSE2 +TEXT ·emitLiteral(SB), NOSPLIT, $0-56 + MOVQ lit_len+32(FP), DX + MOVQ dst_base+0(FP), AX + MOVQ lit_base+24(FP), CX + TESTQ DX, DX + JZ emit_literal_end_standalone_skip + MOVL DX, BX + LEAL -1(DX), SI + CMPL SI, $0x3c + JB one_byte_standalone + CMPL SI, $0x00000100 + JB two_bytes_standalone + CMPL SI, $0x00010000 + JB three_bytes_standalone + CMPL SI, $0x01000000 + JB four_bytes_standalone + MOVB $0xfc, (AX) + MOVL SI, 1(AX) + ADDQ $0x05, BX + ADDQ $0x05, AX + JMP memmove_long_standalone + +four_bytes_standalone: + MOVL SI, DI + SHRL $0x10, DI + MOVB $0xf8, (AX) + MOVW SI, 1(AX) + MOVB DI, 3(AX) + ADDQ $0x04, BX + ADDQ $0x04, AX + JMP memmove_long_standalone + +three_bytes_standalone: + MOVB $0xf4, (AX) + MOVW SI, 1(AX) + ADDQ $0x03, BX + ADDQ $0x03, AX + JMP memmove_long_standalone + +two_bytes_standalone: + MOVB $0xf0, (AX) + MOVB SI, 1(AX) + ADDQ $0x02, BX + ADDQ $0x02, AX + CMPL SI, $0x40 + JB memmove_standalone + JMP memmove_long_standalone + +one_byte_standalone: + SHLB $0x02, SI + MOVB SI, (AX) + ADDQ $0x01, BX + ADDQ $0x01, AX + +memmove_standalone: + // genMemMoveShort + CMPQ DX, $0x03 + JB emit_lit_memmove_standalone_memmove_move_1or2 + JE emit_lit_memmove_standalone_memmove_move_3 + CMPQ DX, $0x08 + JB emit_lit_memmove_standalone_memmove_move_4through7 + CMPQ DX, $0x10 + JBE emit_lit_memmove_standalone_memmove_move_8through16 + CMPQ DX, $0x20 + JBE emit_lit_memmove_standalone_memmove_move_17through32 + JMP emit_lit_memmove_standalone_memmove_move_33through64 + +emit_lit_memmove_standalone_memmove_move_1or2: + MOVB (CX), SI + MOVB -1(CX)(DX*1), CL + MOVB SI, (AX) + MOVB CL, -1(AX)(DX*1) + JMP emit_literal_end_standalone + +emit_lit_memmove_standalone_memmove_move_3: + MOVW (CX), SI + MOVB 2(CX), CL + MOVW SI, (AX) + MOVB CL, 2(AX) + JMP emit_literal_end_standalone + +emit_lit_memmove_standalone_memmove_move_4through7: + MOVL (CX), SI + MOVL -4(CX)(DX*1), CX + MOVL SI, (AX) + MOVL CX, -4(AX)(DX*1) + JMP emit_literal_end_standalone + +emit_lit_memmove_standalone_memmove_move_8through16: + MOVQ (CX), SI + MOVQ -8(CX)(DX*1), CX + MOVQ SI, (AX) + MOVQ CX, -8(AX)(DX*1) + JMP emit_literal_end_standalone + +emit_lit_memmove_standalone_memmove_move_17through32: + MOVOU (CX), X0 + MOVOU -16(CX)(DX*1), X1 + MOVOU X0, (AX) + MOVOU X1, -16(AX)(DX*1) + JMP emit_literal_end_standalone + +emit_lit_memmove_standalone_memmove_move_33through64: + MOVOU (CX), X0 + MOVOU 16(CX), X1 + MOVOU -32(CX)(DX*1), X2 + MOVOU -16(CX)(DX*1), X3 + MOVOU X0, (AX) + MOVOU X1, 16(AX) + MOVOU X2, -32(AX)(DX*1) + MOVOU X3, -16(AX)(DX*1) + JMP emit_literal_end_standalone + JMP emit_literal_end_standalone + +memmove_long_standalone: + // genMemMoveLong + MOVOU (CX), X0 + MOVOU 16(CX), X1 + MOVOU -32(CX)(DX*1), X2 + MOVOU -16(CX)(DX*1), X3 + MOVQ DX, DI + SHRQ $0x05, DI + MOVQ AX, SI + ANDL $0x0000001f, SI + MOVQ $0x00000040, R8 + SUBQ SI, R8 + DECQ DI + JA emit_lit_memmove_long_standalonelarge_forward_sse_loop_32 + LEAQ -32(CX)(R8*1), SI + LEAQ -32(AX)(R8*1), R9 + +emit_lit_memmove_long_standalonelarge_big_loop_back: + MOVOU (SI), X4 + MOVOU 16(SI), X5 + MOVOA X4, (R9) + MOVOA X5, 16(R9) + ADDQ $0x20, R9 + ADDQ $0x20, SI + ADDQ $0x20, R8 + DECQ DI + JNA emit_lit_memmove_long_standalonelarge_big_loop_back + +emit_lit_memmove_long_standalonelarge_forward_sse_loop_32: + MOVOU -32(CX)(R8*1), X4 + MOVOU -16(CX)(R8*1), X5 + MOVOA X4, -32(AX)(R8*1) + MOVOA X5, -16(AX)(R8*1) + ADDQ $0x20, R8 + CMPQ DX, R8 + JAE emit_lit_memmove_long_standalonelarge_forward_sse_loop_32 + MOVOU X0, (AX) + MOVOU X1, 16(AX) + MOVOU X2, -32(AX)(DX*1) + MOVOU X3, -16(AX)(DX*1) + JMP emit_literal_end_standalone + JMP emit_literal_end_standalone + +emit_literal_end_standalone_skip: + XORQ BX, BX + +emit_literal_end_standalone: + MOVQ BX, ret+48(FP) + RET + +// func emitRepeat(dst []byte, offset int, length int) int +TEXT ·emitRepeat(SB), NOSPLIT, $0-48 + XORQ BX, BX + MOVQ dst_base+0(FP), AX + MOVQ offset+24(FP), CX + MOVQ length+32(FP), DX + + // emitRepeat +emit_repeat_again_standalone: + MOVL DX, SI + LEAL -4(DX), DX + CMPL SI, $0x08 + JBE repeat_two_standalone + CMPL SI, $0x0c + JAE cant_repeat_two_offset_standalone + CMPL CX, $0x00000800 + JB repeat_two_offset_standalone + +cant_repeat_two_offset_standalone: + CMPL DX, $0x00000104 + JB repeat_three_standalone + CMPL DX, $0x00010100 + JB repeat_four_standalone + CMPL DX, $0x0100ffff + JB repeat_five_standalone + LEAL -16842747(DX), DX + MOVL $0xfffb001d, (AX) + MOVB $0xff, 4(AX) + ADDQ $0x05, AX + ADDQ $0x05, BX + JMP emit_repeat_again_standalone + +repeat_five_standalone: + LEAL -65536(DX), DX + MOVL DX, CX + MOVW $0x001d, (AX) + MOVW DX, 2(AX) + SARL $0x10, CX + MOVB CL, 4(AX) + ADDQ $0x05, BX + ADDQ $0x05, AX + JMP gen_emit_repeat_end + +repeat_four_standalone: + LEAL -256(DX), DX + MOVW $0x0019, (AX) + MOVW DX, 2(AX) + ADDQ $0x04, BX + ADDQ $0x04, AX + JMP gen_emit_repeat_end + +repeat_three_standalone: + LEAL -4(DX), DX + MOVW $0x0015, (AX) + MOVB DL, 2(AX) + ADDQ $0x03, BX + ADDQ $0x03, AX + JMP gen_emit_repeat_end + +repeat_two_standalone: + SHLL $0x02, DX + ORL $0x01, DX + MOVW DX, (AX) + ADDQ $0x02, BX + ADDQ $0x02, AX + JMP gen_emit_repeat_end + +repeat_two_offset_standalone: + XORQ SI, SI + LEAL 1(SI)(DX*4), DX + MOVB CL, 1(AX) + SARL $0x08, CX + SHLL $0x05, CX + ORL CX, DX + MOVB DL, (AX) + ADDQ $0x02, BX + ADDQ $0x02, AX + +gen_emit_repeat_end: + MOVQ BX, ret+40(FP) + RET + +// func emitCopy(dst []byte, offset int, length int) int +TEXT ·emitCopy(SB), NOSPLIT, $0-48 + XORQ BX, BX + MOVQ dst_base+0(FP), AX + MOVQ offset+24(FP), CX + MOVQ length+32(FP), DX + + // emitCopy + CMPL CX, $0x00010000 + JB two_byte_offset_standalone + CMPL DX, $0x40 + JBE four_bytes_remain_standalone + MOVB $0xff, (AX) + MOVL CX, 1(AX) + LEAL -64(DX), DX + ADDQ $0x05, BX + ADDQ $0x05, AX + CMPL DX, $0x04 + JB four_bytes_remain_standalone + + // emitRepeat +emit_repeat_again_standalone_emit_copy: + MOVL DX, SI + LEAL -4(DX), DX + CMPL SI, $0x08 + JBE repeat_two_standalone_emit_copy + CMPL SI, $0x0c + JAE cant_repeat_two_offset_standalone_emit_copy + CMPL CX, $0x00000800 + JB repeat_two_offset_standalone_emit_copy + +cant_repeat_two_offset_standalone_emit_copy: + CMPL DX, $0x00000104 + JB repeat_three_standalone_emit_copy + CMPL DX, $0x00010100 + JB repeat_four_standalone_emit_copy + CMPL DX, $0x0100ffff + JB repeat_five_standalone_emit_copy + LEAL -16842747(DX), DX + MOVL $0xfffb001d, (AX) + MOVB $0xff, 4(AX) + ADDQ $0x05, AX + ADDQ $0x05, BX + JMP emit_repeat_again_standalone_emit_copy + +repeat_five_standalone_emit_copy: + LEAL -65536(DX), DX + MOVL DX, CX + MOVW $0x001d, (AX) + MOVW DX, 2(AX) + SARL $0x10, CX + MOVB CL, 4(AX) + ADDQ $0x05, BX + ADDQ $0x05, AX + JMP gen_emit_copy_end + +repeat_four_standalone_emit_copy: + LEAL -256(DX), DX + MOVW $0x0019, (AX) + MOVW DX, 2(AX) + ADDQ $0x04, BX + ADDQ $0x04, AX + JMP gen_emit_copy_end + +repeat_three_standalone_emit_copy: + LEAL -4(DX), DX + MOVW $0x0015, (AX) + MOVB DL, 2(AX) + ADDQ $0x03, BX + ADDQ $0x03, AX + JMP gen_emit_copy_end + +repeat_two_standalone_emit_copy: + SHLL $0x02, DX + ORL $0x01, DX + MOVW DX, (AX) + ADDQ $0x02, BX + ADDQ $0x02, AX + JMP gen_emit_copy_end + +repeat_two_offset_standalone_emit_copy: + XORQ SI, SI + LEAL 1(SI)(DX*4), DX + MOVB CL, 1(AX) + SARL $0x08, CX + SHLL $0x05, CX + ORL CX, DX + MOVB DL, (AX) + ADDQ $0x02, BX + ADDQ $0x02, AX + JMP gen_emit_copy_end + +four_bytes_remain_standalone: + TESTL DX, DX + JZ gen_emit_copy_end + XORL SI, SI + LEAL -1(SI)(DX*4), DX + MOVB DL, (AX) + MOVL CX, 1(AX) + ADDQ $0x05, BX + ADDQ $0x05, AX + JMP gen_emit_copy_end + +two_byte_offset_standalone: + CMPL DX, $0x40 + JBE two_byte_offset_short_standalone + CMPL CX, $0x00000800 + JAE long_offset_short_standalone + MOVL $0x00000001, SI + LEAL 16(SI), SI + MOVB CL, 1(AX) + MOVL CX, DI + SHRL $0x08, DI + SHLL $0x05, DI + ORL DI, SI + MOVB SI, (AX) + ADDQ $0x02, BX + ADDQ $0x02, AX + SUBL $0x08, DX + + // emitRepeat + LEAL -4(DX), DX + JMP cant_repeat_two_offset_standalone_emit_copy_short_2b + +emit_repeat_again_standalone_emit_copy_short_2b: + MOVL DX, SI + LEAL -4(DX), DX + CMPL SI, $0x08 + JBE repeat_two_standalone_emit_copy_short_2b + CMPL SI, $0x0c + JAE cant_repeat_two_offset_standalone_emit_copy_short_2b + CMPL CX, $0x00000800 + JB repeat_two_offset_standalone_emit_copy_short_2b + +cant_repeat_two_offset_standalone_emit_copy_short_2b: + CMPL DX, $0x00000104 + JB repeat_three_standalone_emit_copy_short_2b + CMPL DX, $0x00010100 + JB repeat_four_standalone_emit_copy_short_2b + CMPL DX, $0x0100ffff + JB repeat_five_standalone_emit_copy_short_2b + LEAL -16842747(DX), DX + MOVL $0xfffb001d, (AX) + MOVB $0xff, 4(AX) + ADDQ $0x05, AX + ADDQ $0x05, BX + JMP emit_repeat_again_standalone_emit_copy_short_2b + +repeat_five_standalone_emit_copy_short_2b: + LEAL -65536(DX), DX + MOVL DX, CX + MOVW $0x001d, (AX) + MOVW DX, 2(AX) + SARL $0x10, CX + MOVB CL, 4(AX) + ADDQ $0x05, BX + ADDQ $0x05, AX + JMP gen_emit_copy_end + +repeat_four_standalone_emit_copy_short_2b: + LEAL -256(DX), DX + MOVW $0x0019, (AX) + MOVW DX, 2(AX) + ADDQ $0x04, BX + ADDQ $0x04, AX + JMP gen_emit_copy_end + +repeat_three_standalone_emit_copy_short_2b: + LEAL -4(DX), DX + MOVW $0x0015, (AX) + MOVB DL, 2(AX) + ADDQ $0x03, BX + ADDQ $0x03, AX + JMP gen_emit_copy_end + +repeat_two_standalone_emit_copy_short_2b: + SHLL $0x02, DX + ORL $0x01, DX + MOVW DX, (AX) + ADDQ $0x02, BX + ADDQ $0x02, AX + JMP gen_emit_copy_end + +repeat_two_offset_standalone_emit_copy_short_2b: + XORQ SI, SI + LEAL 1(SI)(DX*4), DX + MOVB CL, 1(AX) + SARL $0x08, CX + SHLL $0x05, CX + ORL CX, DX + MOVB DL, (AX) + ADDQ $0x02, BX + ADDQ $0x02, AX + JMP gen_emit_copy_end + +long_offset_short_standalone: + MOVB $0xee, (AX) + MOVW CX, 1(AX) + LEAL -60(DX), DX + ADDQ $0x03, AX + ADDQ $0x03, BX + + // emitRepeat +emit_repeat_again_standalone_emit_copy_short: + MOVL DX, SI + LEAL -4(DX), DX + CMPL SI, $0x08 + JBE repeat_two_standalone_emit_copy_short + CMPL SI, $0x0c + JAE cant_repeat_two_offset_standalone_emit_copy_short + CMPL CX, $0x00000800 + JB repeat_two_offset_standalone_emit_copy_short + +cant_repeat_two_offset_standalone_emit_copy_short: + CMPL DX, $0x00000104 + JB repeat_three_standalone_emit_copy_short + CMPL DX, $0x00010100 + JB repeat_four_standalone_emit_copy_short + CMPL DX, $0x0100ffff + JB repeat_five_standalone_emit_copy_short + LEAL -16842747(DX), DX + MOVL $0xfffb001d, (AX) + MOVB $0xff, 4(AX) + ADDQ $0x05, AX + ADDQ $0x05, BX + JMP emit_repeat_again_standalone_emit_copy_short + +repeat_five_standalone_emit_copy_short: + LEAL -65536(DX), DX + MOVL DX, CX + MOVW $0x001d, (AX) + MOVW DX, 2(AX) + SARL $0x10, CX + MOVB CL, 4(AX) + ADDQ $0x05, BX + ADDQ $0x05, AX + JMP gen_emit_copy_end + +repeat_four_standalone_emit_copy_short: + LEAL -256(DX), DX + MOVW $0x0019, (AX) + MOVW DX, 2(AX) + ADDQ $0x04, BX + ADDQ $0x04, AX + JMP gen_emit_copy_end + +repeat_three_standalone_emit_copy_short: + LEAL -4(DX), DX + MOVW $0x0015, (AX) + MOVB DL, 2(AX) + ADDQ $0x03, BX + ADDQ $0x03, AX + JMP gen_emit_copy_end + +repeat_two_standalone_emit_copy_short: + SHLL $0x02, DX + ORL $0x01, DX + MOVW DX, (AX) + ADDQ $0x02, BX + ADDQ $0x02, AX + JMP gen_emit_copy_end + +repeat_two_offset_standalone_emit_copy_short: + XORQ SI, SI + LEAL 1(SI)(DX*4), DX + MOVB CL, 1(AX) + SARL $0x08, CX + SHLL $0x05, CX + ORL CX, DX + MOVB DL, (AX) + ADDQ $0x02, BX + ADDQ $0x02, AX + JMP gen_emit_copy_end + +two_byte_offset_short_standalone: + MOVL DX, SI + SHLL $0x02, SI + CMPL DX, $0x0c + JAE emit_copy_three_standalone + CMPL CX, $0x00000800 + JAE emit_copy_three_standalone + LEAL -15(SI), SI + MOVB CL, 1(AX) + SHRL $0x08, CX + SHLL $0x05, CX + ORL CX, SI + MOVB SI, (AX) + ADDQ $0x02, BX + ADDQ $0x02, AX + JMP gen_emit_copy_end + +emit_copy_three_standalone: + LEAL -2(SI), SI + MOVB SI, (AX) + MOVW CX, 1(AX) + ADDQ $0x03, BX + ADDQ $0x03, AX + +gen_emit_copy_end: + MOVQ BX, ret+40(FP) + RET + +// func emitCopyNoRepeat(dst []byte, offset int, length int) int +TEXT ·emitCopyNoRepeat(SB), NOSPLIT, $0-48 + XORQ BX, BX + MOVQ dst_base+0(FP), AX + MOVQ offset+24(FP), CX + MOVQ length+32(FP), DX + + // emitCopy + CMPL CX, $0x00010000 + JB two_byte_offset_standalone_snappy + +four_bytes_loop_back_standalone_snappy: + CMPL DX, $0x40 + JBE four_bytes_remain_standalone_snappy + MOVB $0xff, (AX) + MOVL CX, 1(AX) + LEAL -64(DX), DX + ADDQ $0x05, BX + ADDQ $0x05, AX + CMPL DX, $0x04 + JB four_bytes_remain_standalone_snappy + JMP four_bytes_loop_back_standalone_snappy + +four_bytes_remain_standalone_snappy: + TESTL DX, DX + JZ gen_emit_copy_end_snappy + XORL SI, SI + LEAL -1(SI)(DX*4), DX + MOVB DL, (AX) + MOVL CX, 1(AX) + ADDQ $0x05, BX + ADDQ $0x05, AX + JMP gen_emit_copy_end_snappy + +two_byte_offset_standalone_snappy: + CMPL DX, $0x40 + JBE two_byte_offset_short_standalone_snappy + MOVB $0xee, (AX) + MOVW CX, 1(AX) + LEAL -60(DX), DX + ADDQ $0x03, AX + ADDQ $0x03, BX + JMP two_byte_offset_standalone_snappy + +two_byte_offset_short_standalone_snappy: + MOVL DX, SI + SHLL $0x02, SI + CMPL DX, $0x0c + JAE emit_copy_three_standalone_snappy + CMPL CX, $0x00000800 + JAE emit_copy_three_standalone_snappy + LEAL -15(SI), SI + MOVB CL, 1(AX) + SHRL $0x08, CX + SHLL $0x05, CX + ORL CX, SI + MOVB SI, (AX) + ADDQ $0x02, BX + ADDQ $0x02, AX + JMP gen_emit_copy_end_snappy + +emit_copy_three_standalone_snappy: + LEAL -2(SI), SI + MOVB SI, (AX) + MOVW CX, 1(AX) + ADDQ $0x03, BX + ADDQ $0x03, AX + +gen_emit_copy_end_snappy: + MOVQ BX, ret+40(FP) + RET + +// func matchLen(a []byte, b []byte) int +// Requires: BMI +TEXT ·matchLen(SB), NOSPLIT, $0-56 + MOVQ a_base+0(FP), AX + MOVQ b_base+24(FP), CX + MOVQ a_len+8(FP), DX + + // matchLen + XORL SI, SI + +matchlen_loopback_16_standalone: + CMPL DX, $0x10 + JB matchlen_match8_standalone + MOVQ (AX)(SI*1), BX + MOVQ 8(AX)(SI*1), DI + XORQ (CX)(SI*1), BX + JNZ matchlen_bsf_8_standalone + XORQ 8(CX)(SI*1), DI + JNZ matchlen_bsf_16standalone + LEAL -16(DX), DX + LEAL 16(SI), SI + JMP matchlen_loopback_16_standalone + +matchlen_bsf_16standalone: +#ifdef GOAMD64_v3 + TZCNTQ DI, DI + +#else + BSFQ DI, DI + +#endif + SARQ $0x03, DI + LEAL 8(SI)(DI*1), SI + JMP gen_match_len_end + +matchlen_match8_standalone: + CMPL DX, $0x08 + JB matchlen_match4_standalone + MOVQ (AX)(SI*1), BX + XORQ (CX)(SI*1), BX + JNZ matchlen_bsf_8_standalone + LEAL -8(DX), DX + LEAL 8(SI), SI + JMP matchlen_match4_standalone + +matchlen_bsf_8_standalone: +#ifdef GOAMD64_v3 + TZCNTQ BX, BX + +#else + BSFQ BX, BX + +#endif + SARQ $0x03, BX + LEAL (SI)(BX*1), SI + JMP gen_match_len_end + +matchlen_match4_standalone: + CMPL DX, $0x04 + JB matchlen_match2_standalone + MOVL (AX)(SI*1), BX + CMPL (CX)(SI*1), BX + JNE matchlen_match2_standalone + LEAL -4(DX), DX + LEAL 4(SI), SI + +matchlen_match2_standalone: + CMPL DX, $0x01 + JE matchlen_match1_standalone + JB gen_match_len_end + MOVW (AX)(SI*1), BX + CMPW (CX)(SI*1), BX + JNE matchlen_match1_standalone + LEAL 2(SI), SI + SUBL $0x02, DX + JZ gen_match_len_end + +matchlen_match1_standalone: + MOVB (AX)(SI*1), BL + CMPB (CX)(SI*1), BL + JNE gen_match_len_end + LEAL 1(SI), SI + +gen_match_len_end: + MOVQ SI, ret+48(FP) + RET + +// func cvtLZ4BlockAsm(dst []byte, src []byte) (uncompressed int, dstUsed int) +// Requires: SSE2 +TEXT ·cvtLZ4BlockAsm(SB), NOSPLIT, $0-64 + XORQ SI, SI + MOVQ dst_base+0(FP), AX + MOVQ dst_len+8(FP), CX + MOVQ src_base+24(FP), DX + MOVQ src_len+32(FP), BX + LEAQ (DX)(BX*1), BX + LEAQ -8(AX)(CX*1), CX + XORQ DI, DI + +lz4_s2_loop: + CMPQ DX, BX + JAE lz4_s2_corrupt + CMPQ AX, CX + JAE lz4_s2_dstfull + MOVBQZX (DX), R8 + MOVQ R8, R9 + MOVQ R8, R10 + SHRQ $0x04, R9 + ANDQ $0x0f, R10 + CMPQ R8, $0xf0 + JB lz4_s2_ll_end + +lz4_s2_ll_loop: + INCQ DX + CMPQ DX, BX + JAE lz4_s2_corrupt + MOVBQZX (DX), R8 + ADDQ R8, R9 + CMPQ R8, $0xff + JEQ lz4_s2_ll_loop + +lz4_s2_ll_end: + LEAQ (DX)(R9*1), R8 + ADDQ $0x04, R10 + CMPQ R8, BX + JAE lz4_s2_corrupt + INCQ DX + INCQ R8 + TESTQ R9, R9 + JZ lz4_s2_lits_done + LEAQ (AX)(R9*1), R11 + CMPQ R11, CX + JAE lz4_s2_dstfull + ADDQ R9, SI + LEAL -1(R9), R11 + CMPL R11, $0x3c + JB one_byte_lz4_s2 + CMPL R11, $0x00000100 + JB two_bytes_lz4_s2 + CMPL R11, $0x00010000 + JB three_bytes_lz4_s2 + CMPL R11, $0x01000000 + JB four_bytes_lz4_s2 + MOVB $0xfc, (AX) + MOVL R11, 1(AX) + ADDQ $0x05, AX + JMP memmove_long_lz4_s2 + +four_bytes_lz4_s2: + MOVL R11, R12 + SHRL $0x10, R12 + MOVB $0xf8, (AX) + MOVW R11, 1(AX) + MOVB R12, 3(AX) + ADDQ $0x04, AX + JMP memmove_long_lz4_s2 + +three_bytes_lz4_s2: + MOVB $0xf4, (AX) + MOVW R11, 1(AX) + ADDQ $0x03, AX + JMP memmove_long_lz4_s2 + +two_bytes_lz4_s2: + MOVB $0xf0, (AX) + MOVB R11, 1(AX) + ADDQ $0x02, AX + CMPL R11, $0x40 + JB memmove_lz4_s2 + JMP memmove_long_lz4_s2 + +one_byte_lz4_s2: + SHLB $0x02, R11 + MOVB R11, (AX) + ADDQ $0x01, AX + +memmove_lz4_s2: + LEAQ (AX)(R9*1), R11 + + // genMemMoveShort + CMPQ R9, $0x08 + JBE emit_lit_memmove_lz4_s2_memmove_move_8 + CMPQ R9, $0x10 + JBE emit_lit_memmove_lz4_s2_memmove_move_8through16 + CMPQ R9, $0x20 + JBE emit_lit_memmove_lz4_s2_memmove_move_17through32 + JMP emit_lit_memmove_lz4_s2_memmove_move_33through64 + +emit_lit_memmove_lz4_s2_memmove_move_8: + MOVQ (DX), R12 + MOVQ R12, (AX) + JMP memmove_end_copy_lz4_s2 + +emit_lit_memmove_lz4_s2_memmove_move_8through16: + MOVQ (DX), R12 + MOVQ -8(DX)(R9*1), DX + MOVQ R12, (AX) + MOVQ DX, -8(AX)(R9*1) + JMP memmove_end_copy_lz4_s2 + +emit_lit_memmove_lz4_s2_memmove_move_17through32: + MOVOU (DX), X0 + MOVOU -16(DX)(R9*1), X1 + MOVOU X0, (AX) + MOVOU X1, -16(AX)(R9*1) + JMP memmove_end_copy_lz4_s2 + +emit_lit_memmove_lz4_s2_memmove_move_33through64: + MOVOU (DX), X0 + MOVOU 16(DX), X1 + MOVOU -32(DX)(R9*1), X2 + MOVOU -16(DX)(R9*1), X3 + MOVOU X0, (AX) + MOVOU X1, 16(AX) + MOVOU X2, -32(AX)(R9*1) + MOVOU X3, -16(AX)(R9*1) + +memmove_end_copy_lz4_s2: + MOVQ R11, AX + JMP lz4_s2_lits_emit_done + +memmove_long_lz4_s2: + LEAQ (AX)(R9*1), R11 + + // genMemMoveLong + MOVOU (DX), X0 + MOVOU 16(DX), X1 + MOVOU -32(DX)(R9*1), X2 + MOVOU -16(DX)(R9*1), X3 + MOVQ R9, R13 + SHRQ $0x05, R13 + MOVQ AX, R12 + ANDL $0x0000001f, R12 + MOVQ $0x00000040, R14 + SUBQ R12, R14 + DECQ R13 + JA emit_lit_memmove_long_lz4_s2large_forward_sse_loop_32 + LEAQ -32(DX)(R14*1), R12 + LEAQ -32(AX)(R14*1), R15 + +emit_lit_memmove_long_lz4_s2large_big_loop_back: + MOVOU (R12), X4 + MOVOU 16(R12), X5 + MOVOA X4, (R15) + MOVOA X5, 16(R15) + ADDQ $0x20, R15 + ADDQ $0x20, R12 + ADDQ $0x20, R14 + DECQ R13 + JNA emit_lit_memmove_long_lz4_s2large_big_loop_back + +emit_lit_memmove_long_lz4_s2large_forward_sse_loop_32: + MOVOU -32(DX)(R14*1), X4 + MOVOU -16(DX)(R14*1), X5 + MOVOA X4, -32(AX)(R14*1) + MOVOA X5, -16(AX)(R14*1) + ADDQ $0x20, R14 + CMPQ R9, R14 + JAE emit_lit_memmove_long_lz4_s2large_forward_sse_loop_32 + MOVOU X0, (AX) + MOVOU X1, 16(AX) + MOVOU X2, -32(AX)(R9*1) + MOVOU X3, -16(AX)(R9*1) + MOVQ R11, AX + +lz4_s2_lits_emit_done: + MOVQ R8, DX + +lz4_s2_lits_done: + CMPQ DX, BX + JNE lz4_s2_match + CMPQ R10, $0x04 + JEQ lz4_s2_done + JMP lz4_s2_corrupt + +lz4_s2_match: + LEAQ 2(DX), R8 + CMPQ R8, BX + JAE lz4_s2_corrupt + MOVWQZX (DX), R9 + MOVQ R8, DX + TESTQ R9, R9 + JZ lz4_s2_corrupt + CMPQ R9, SI + JA lz4_s2_corrupt + CMPQ R10, $0x13 + JNE lz4_s2_ml_done + +lz4_s2_ml_loop: + MOVBQZX (DX), R8 + INCQ DX + ADDQ R8, R10 + CMPQ DX, BX + JAE lz4_s2_corrupt + CMPQ R8, $0xff + JEQ lz4_s2_ml_loop + +lz4_s2_ml_done: + ADDQ R10, SI + CMPQ R9, DI + JNE lz4_s2_docopy + + // emitRepeat +emit_repeat_again_lz4_s2: + MOVL R10, R8 + LEAL -4(R10), R10 + CMPL R8, $0x08 + JBE repeat_two_lz4_s2 + CMPL R8, $0x0c + JAE cant_repeat_two_offset_lz4_s2 + CMPL R9, $0x00000800 + JB repeat_two_offset_lz4_s2 + +cant_repeat_two_offset_lz4_s2: + CMPL R10, $0x00000104 + JB repeat_three_lz4_s2 + CMPL R10, $0x00010100 + JB repeat_four_lz4_s2 + CMPL R10, $0x0100ffff + JB repeat_five_lz4_s2 + LEAL -16842747(R10), R10 + MOVL $0xfffb001d, (AX) + MOVB $0xff, 4(AX) + ADDQ $0x05, AX + JMP emit_repeat_again_lz4_s2 + +repeat_five_lz4_s2: + LEAL -65536(R10), R10 + MOVL R10, R9 + MOVW $0x001d, (AX) + MOVW R10, 2(AX) + SARL $0x10, R9 + MOVB R9, 4(AX) + ADDQ $0x05, AX + JMP lz4_s2_loop + +repeat_four_lz4_s2: + LEAL -256(R10), R10 + MOVW $0x0019, (AX) + MOVW R10, 2(AX) + ADDQ $0x04, AX + JMP lz4_s2_loop + +repeat_three_lz4_s2: + LEAL -4(R10), R10 + MOVW $0x0015, (AX) + MOVB R10, 2(AX) + ADDQ $0x03, AX + JMP lz4_s2_loop + +repeat_two_lz4_s2: + SHLL $0x02, R10 + ORL $0x01, R10 + MOVW R10, (AX) + ADDQ $0x02, AX + JMP lz4_s2_loop + +repeat_two_offset_lz4_s2: + XORQ R8, R8 + LEAL 1(R8)(R10*4), R10 + MOVB R9, 1(AX) + SARL $0x08, R9 + SHLL $0x05, R9 + ORL R9, R10 + MOVB R10, (AX) + ADDQ $0x02, AX + JMP lz4_s2_loop + +lz4_s2_docopy: + MOVQ R9, DI + + // emitCopy + CMPL R10, $0x40 + JBE two_byte_offset_short_lz4_s2 + CMPL R9, $0x00000800 + JAE long_offset_short_lz4_s2 + MOVL $0x00000001, R8 + LEAL 16(R8), R8 + MOVB R9, 1(AX) + MOVL R9, R11 + SHRL $0x08, R11 + SHLL $0x05, R11 + ORL R11, R8 + MOVB R8, (AX) + ADDQ $0x02, AX + SUBL $0x08, R10 + + // emitRepeat + LEAL -4(R10), R10 + JMP cant_repeat_two_offset_lz4_s2_emit_copy_short_2b + +emit_repeat_again_lz4_s2_emit_copy_short_2b: + MOVL R10, R8 + LEAL -4(R10), R10 + CMPL R8, $0x08 + JBE repeat_two_lz4_s2_emit_copy_short_2b + CMPL R8, $0x0c + JAE cant_repeat_two_offset_lz4_s2_emit_copy_short_2b + CMPL R9, $0x00000800 + JB repeat_two_offset_lz4_s2_emit_copy_short_2b + +cant_repeat_two_offset_lz4_s2_emit_copy_short_2b: + CMPL R10, $0x00000104 + JB repeat_three_lz4_s2_emit_copy_short_2b + CMPL R10, $0x00010100 + JB repeat_four_lz4_s2_emit_copy_short_2b + CMPL R10, $0x0100ffff + JB repeat_five_lz4_s2_emit_copy_short_2b + LEAL -16842747(R10), R10 + MOVL $0xfffb001d, (AX) + MOVB $0xff, 4(AX) + ADDQ $0x05, AX + JMP emit_repeat_again_lz4_s2_emit_copy_short_2b + +repeat_five_lz4_s2_emit_copy_short_2b: + LEAL -65536(R10), R10 + MOVL R10, R9 + MOVW $0x001d, (AX) + MOVW R10, 2(AX) + SARL $0x10, R9 + MOVB R9, 4(AX) + ADDQ $0x05, AX + JMP lz4_s2_loop + +repeat_four_lz4_s2_emit_copy_short_2b: + LEAL -256(R10), R10 + MOVW $0x0019, (AX) + MOVW R10, 2(AX) + ADDQ $0x04, AX + JMP lz4_s2_loop + +repeat_three_lz4_s2_emit_copy_short_2b: + LEAL -4(R10), R10 + MOVW $0x0015, (AX) + MOVB R10, 2(AX) + ADDQ $0x03, AX + JMP lz4_s2_loop + +repeat_two_lz4_s2_emit_copy_short_2b: + SHLL $0x02, R10 + ORL $0x01, R10 + MOVW R10, (AX) + ADDQ $0x02, AX + JMP lz4_s2_loop + +repeat_two_offset_lz4_s2_emit_copy_short_2b: + XORQ R8, R8 + LEAL 1(R8)(R10*4), R10 + MOVB R9, 1(AX) + SARL $0x08, R9 + SHLL $0x05, R9 + ORL R9, R10 + MOVB R10, (AX) + ADDQ $0x02, AX + JMP lz4_s2_loop + +long_offset_short_lz4_s2: + MOVB $0xee, (AX) + MOVW R9, 1(AX) + LEAL -60(R10), R10 + ADDQ $0x03, AX + + // emitRepeat +emit_repeat_again_lz4_s2_emit_copy_short: + MOVL R10, R8 + LEAL -4(R10), R10 + CMPL R8, $0x08 + JBE repeat_two_lz4_s2_emit_copy_short + CMPL R8, $0x0c + JAE cant_repeat_two_offset_lz4_s2_emit_copy_short + CMPL R9, $0x00000800 + JB repeat_two_offset_lz4_s2_emit_copy_short + +cant_repeat_two_offset_lz4_s2_emit_copy_short: + CMPL R10, $0x00000104 + JB repeat_three_lz4_s2_emit_copy_short + CMPL R10, $0x00010100 + JB repeat_four_lz4_s2_emit_copy_short + CMPL R10, $0x0100ffff + JB repeat_five_lz4_s2_emit_copy_short + LEAL -16842747(R10), R10 + MOVL $0xfffb001d, (AX) + MOVB $0xff, 4(AX) + ADDQ $0x05, AX + JMP emit_repeat_again_lz4_s2_emit_copy_short + +repeat_five_lz4_s2_emit_copy_short: + LEAL -65536(R10), R10 + MOVL R10, R9 + MOVW $0x001d, (AX) + MOVW R10, 2(AX) + SARL $0x10, R9 + MOVB R9, 4(AX) + ADDQ $0x05, AX + JMP lz4_s2_loop + +repeat_four_lz4_s2_emit_copy_short: + LEAL -256(R10), R10 + MOVW $0x0019, (AX) + MOVW R10, 2(AX) + ADDQ $0x04, AX + JMP lz4_s2_loop + +repeat_three_lz4_s2_emit_copy_short: + LEAL -4(R10), R10 + MOVW $0x0015, (AX) + MOVB R10, 2(AX) + ADDQ $0x03, AX + JMP lz4_s2_loop + +repeat_two_lz4_s2_emit_copy_short: + SHLL $0x02, R10 + ORL $0x01, R10 + MOVW R10, (AX) + ADDQ $0x02, AX + JMP lz4_s2_loop + +repeat_two_offset_lz4_s2_emit_copy_short: + XORQ R8, R8 + LEAL 1(R8)(R10*4), R10 + MOVB R9, 1(AX) + SARL $0x08, R9 + SHLL $0x05, R9 + ORL R9, R10 + MOVB R10, (AX) + ADDQ $0x02, AX + JMP lz4_s2_loop + +two_byte_offset_short_lz4_s2: + MOVL R10, R8 + SHLL $0x02, R8 + CMPL R10, $0x0c + JAE emit_copy_three_lz4_s2 + CMPL R9, $0x00000800 + JAE emit_copy_three_lz4_s2 + LEAL -15(R8), R8 + MOVB R9, 1(AX) + SHRL $0x08, R9 + SHLL $0x05, R9 + ORL R9, R8 + MOVB R8, (AX) + ADDQ $0x02, AX + JMP lz4_s2_loop + +emit_copy_three_lz4_s2: + LEAL -2(R8), R8 + MOVB R8, (AX) + MOVW R9, 1(AX) + ADDQ $0x03, AX + JMP lz4_s2_loop + +lz4_s2_done: + MOVQ dst_base+0(FP), CX + SUBQ CX, AX + MOVQ SI, uncompressed+48(FP) + MOVQ AX, dstUsed+56(FP) + RET + +lz4_s2_corrupt: + XORQ AX, AX + LEAQ -1(AX), SI + MOVQ SI, uncompressed+48(FP) + RET + +lz4_s2_dstfull: + XORQ AX, AX + LEAQ -2(AX), SI + MOVQ SI, uncompressed+48(FP) + RET + +// func cvtLZ4sBlockAsm(dst []byte, src []byte) (uncompressed int, dstUsed int) +// Requires: SSE2 +TEXT ·cvtLZ4sBlockAsm(SB), NOSPLIT, $0-64 + XORQ SI, SI + MOVQ dst_base+0(FP), AX + MOVQ dst_len+8(FP), CX + MOVQ src_base+24(FP), DX + MOVQ src_len+32(FP), BX + LEAQ (DX)(BX*1), BX + LEAQ -8(AX)(CX*1), CX + XORQ DI, DI + +lz4s_s2_loop: + CMPQ DX, BX + JAE lz4s_s2_corrupt + CMPQ AX, CX + JAE lz4s_s2_dstfull + MOVBQZX (DX), R8 + MOVQ R8, R9 + MOVQ R8, R10 + SHRQ $0x04, R9 + ANDQ $0x0f, R10 + CMPQ R8, $0xf0 + JB lz4s_s2_ll_end + +lz4s_s2_ll_loop: + INCQ DX + CMPQ DX, BX + JAE lz4s_s2_corrupt + MOVBQZX (DX), R8 + ADDQ R8, R9 + CMPQ R8, $0xff + JEQ lz4s_s2_ll_loop + +lz4s_s2_ll_end: + LEAQ (DX)(R9*1), R8 + ADDQ $0x03, R10 + CMPQ R8, BX + JAE lz4s_s2_corrupt + INCQ DX + INCQ R8 + TESTQ R9, R9 + JZ lz4s_s2_lits_done + LEAQ (AX)(R9*1), R11 + CMPQ R11, CX + JAE lz4s_s2_dstfull + ADDQ R9, SI + LEAL -1(R9), R11 + CMPL R11, $0x3c + JB one_byte_lz4s_s2 + CMPL R11, $0x00000100 + JB two_bytes_lz4s_s2 + CMPL R11, $0x00010000 + JB three_bytes_lz4s_s2 + CMPL R11, $0x01000000 + JB four_bytes_lz4s_s2 + MOVB $0xfc, (AX) + MOVL R11, 1(AX) + ADDQ $0x05, AX + JMP memmove_long_lz4s_s2 + +four_bytes_lz4s_s2: + MOVL R11, R12 + SHRL $0x10, R12 + MOVB $0xf8, (AX) + MOVW R11, 1(AX) + MOVB R12, 3(AX) + ADDQ $0x04, AX + JMP memmove_long_lz4s_s2 + +three_bytes_lz4s_s2: + MOVB $0xf4, (AX) + MOVW R11, 1(AX) + ADDQ $0x03, AX + JMP memmove_long_lz4s_s2 + +two_bytes_lz4s_s2: + MOVB $0xf0, (AX) + MOVB R11, 1(AX) + ADDQ $0x02, AX + CMPL R11, $0x40 + JB memmove_lz4s_s2 + JMP memmove_long_lz4s_s2 + +one_byte_lz4s_s2: + SHLB $0x02, R11 + MOVB R11, (AX) + ADDQ $0x01, AX + +memmove_lz4s_s2: + LEAQ (AX)(R9*1), R11 + + // genMemMoveShort + CMPQ R9, $0x08 + JBE emit_lit_memmove_lz4s_s2_memmove_move_8 + CMPQ R9, $0x10 + JBE emit_lit_memmove_lz4s_s2_memmove_move_8through16 + CMPQ R9, $0x20 + JBE emit_lit_memmove_lz4s_s2_memmove_move_17through32 + JMP emit_lit_memmove_lz4s_s2_memmove_move_33through64 + +emit_lit_memmove_lz4s_s2_memmove_move_8: + MOVQ (DX), R12 + MOVQ R12, (AX) + JMP memmove_end_copy_lz4s_s2 + +emit_lit_memmove_lz4s_s2_memmove_move_8through16: + MOVQ (DX), R12 + MOVQ -8(DX)(R9*1), DX + MOVQ R12, (AX) + MOVQ DX, -8(AX)(R9*1) + JMP memmove_end_copy_lz4s_s2 + +emit_lit_memmove_lz4s_s2_memmove_move_17through32: + MOVOU (DX), X0 + MOVOU -16(DX)(R9*1), X1 + MOVOU X0, (AX) + MOVOU X1, -16(AX)(R9*1) + JMP memmove_end_copy_lz4s_s2 + +emit_lit_memmove_lz4s_s2_memmove_move_33through64: + MOVOU (DX), X0 + MOVOU 16(DX), X1 + MOVOU -32(DX)(R9*1), X2 + MOVOU -16(DX)(R9*1), X3 + MOVOU X0, (AX) + MOVOU X1, 16(AX) + MOVOU X2, -32(AX)(R9*1) + MOVOU X3, -16(AX)(R9*1) + +memmove_end_copy_lz4s_s2: + MOVQ R11, AX + JMP lz4s_s2_lits_emit_done + +memmove_long_lz4s_s2: + LEAQ (AX)(R9*1), R11 + + // genMemMoveLong + MOVOU (DX), X0 + MOVOU 16(DX), X1 + MOVOU -32(DX)(R9*1), X2 + MOVOU -16(DX)(R9*1), X3 + MOVQ R9, R13 + SHRQ $0x05, R13 + MOVQ AX, R12 + ANDL $0x0000001f, R12 + MOVQ $0x00000040, R14 + SUBQ R12, R14 + DECQ R13 + JA emit_lit_memmove_long_lz4s_s2large_forward_sse_loop_32 + LEAQ -32(DX)(R14*1), R12 + LEAQ -32(AX)(R14*1), R15 + +emit_lit_memmove_long_lz4s_s2large_big_loop_back: + MOVOU (R12), X4 + MOVOU 16(R12), X5 + MOVOA X4, (R15) + MOVOA X5, 16(R15) + ADDQ $0x20, R15 + ADDQ $0x20, R12 + ADDQ $0x20, R14 + DECQ R13 + JNA emit_lit_memmove_long_lz4s_s2large_big_loop_back + +emit_lit_memmove_long_lz4s_s2large_forward_sse_loop_32: + MOVOU -32(DX)(R14*1), X4 + MOVOU -16(DX)(R14*1), X5 + MOVOA X4, -32(AX)(R14*1) + MOVOA X5, -16(AX)(R14*1) + ADDQ $0x20, R14 + CMPQ R9, R14 + JAE emit_lit_memmove_long_lz4s_s2large_forward_sse_loop_32 + MOVOU X0, (AX) + MOVOU X1, 16(AX) + MOVOU X2, -32(AX)(R9*1) + MOVOU X3, -16(AX)(R9*1) + MOVQ R11, AX + +lz4s_s2_lits_emit_done: + MOVQ R8, DX + +lz4s_s2_lits_done: + CMPQ DX, BX + JNE lz4s_s2_match + CMPQ R10, $0x03 + JEQ lz4s_s2_done + JMP lz4s_s2_corrupt + +lz4s_s2_match: + CMPQ R10, $0x03 + JEQ lz4s_s2_loop + LEAQ 2(DX), R8 + CMPQ R8, BX + JAE lz4s_s2_corrupt + MOVWQZX (DX), R9 + MOVQ R8, DX + TESTQ R9, R9 + JZ lz4s_s2_corrupt + CMPQ R9, SI + JA lz4s_s2_corrupt + CMPQ R10, $0x12 + JNE lz4s_s2_ml_done + +lz4s_s2_ml_loop: + MOVBQZX (DX), R8 + INCQ DX + ADDQ R8, R10 + CMPQ DX, BX + JAE lz4s_s2_corrupt + CMPQ R8, $0xff + JEQ lz4s_s2_ml_loop + +lz4s_s2_ml_done: + ADDQ R10, SI + CMPQ R9, DI + JNE lz4s_s2_docopy + + // emitRepeat +emit_repeat_again_lz4_s2: + MOVL R10, R8 + LEAL -4(R10), R10 + CMPL R8, $0x08 + JBE repeat_two_lz4_s2 + CMPL R8, $0x0c + JAE cant_repeat_two_offset_lz4_s2 + CMPL R9, $0x00000800 + JB repeat_two_offset_lz4_s2 + +cant_repeat_two_offset_lz4_s2: + CMPL R10, $0x00000104 + JB repeat_three_lz4_s2 + CMPL R10, $0x00010100 + JB repeat_four_lz4_s2 + CMPL R10, $0x0100ffff + JB repeat_five_lz4_s2 + LEAL -16842747(R10), R10 + MOVL $0xfffb001d, (AX) + MOVB $0xff, 4(AX) + ADDQ $0x05, AX + JMP emit_repeat_again_lz4_s2 + +repeat_five_lz4_s2: + LEAL -65536(R10), R10 + MOVL R10, R9 + MOVW $0x001d, (AX) + MOVW R10, 2(AX) + SARL $0x10, R9 + MOVB R9, 4(AX) + ADDQ $0x05, AX + JMP lz4s_s2_loop + +repeat_four_lz4_s2: + LEAL -256(R10), R10 + MOVW $0x0019, (AX) + MOVW R10, 2(AX) + ADDQ $0x04, AX + JMP lz4s_s2_loop + +repeat_three_lz4_s2: + LEAL -4(R10), R10 + MOVW $0x0015, (AX) + MOVB R10, 2(AX) + ADDQ $0x03, AX + JMP lz4s_s2_loop + +repeat_two_lz4_s2: + SHLL $0x02, R10 + ORL $0x01, R10 + MOVW R10, (AX) + ADDQ $0x02, AX + JMP lz4s_s2_loop + +repeat_two_offset_lz4_s2: + XORQ R8, R8 + LEAL 1(R8)(R10*4), R10 + MOVB R9, 1(AX) + SARL $0x08, R9 + SHLL $0x05, R9 + ORL R9, R10 + MOVB R10, (AX) + ADDQ $0x02, AX + JMP lz4s_s2_loop + +lz4s_s2_docopy: + MOVQ R9, DI + + // emitCopy + CMPL R10, $0x40 + JBE two_byte_offset_short_lz4_s2 + CMPL R9, $0x00000800 + JAE long_offset_short_lz4_s2 + MOVL $0x00000001, R8 + LEAL 16(R8), R8 + MOVB R9, 1(AX) + MOVL R9, R11 + SHRL $0x08, R11 + SHLL $0x05, R11 + ORL R11, R8 + MOVB R8, (AX) + ADDQ $0x02, AX + SUBL $0x08, R10 + + // emitRepeat + LEAL -4(R10), R10 + JMP cant_repeat_two_offset_lz4_s2_emit_copy_short_2b + +emit_repeat_again_lz4_s2_emit_copy_short_2b: + MOVL R10, R8 + LEAL -4(R10), R10 + CMPL R8, $0x08 + JBE repeat_two_lz4_s2_emit_copy_short_2b + CMPL R8, $0x0c + JAE cant_repeat_two_offset_lz4_s2_emit_copy_short_2b + CMPL R9, $0x00000800 + JB repeat_two_offset_lz4_s2_emit_copy_short_2b + +cant_repeat_two_offset_lz4_s2_emit_copy_short_2b: + CMPL R10, $0x00000104 + JB repeat_three_lz4_s2_emit_copy_short_2b + CMPL R10, $0x00010100 + JB repeat_four_lz4_s2_emit_copy_short_2b + CMPL R10, $0x0100ffff + JB repeat_five_lz4_s2_emit_copy_short_2b + LEAL -16842747(R10), R10 + MOVL $0xfffb001d, (AX) + MOVB $0xff, 4(AX) + ADDQ $0x05, AX + JMP emit_repeat_again_lz4_s2_emit_copy_short_2b + +repeat_five_lz4_s2_emit_copy_short_2b: + LEAL -65536(R10), R10 + MOVL R10, R9 + MOVW $0x001d, (AX) + MOVW R10, 2(AX) + SARL $0x10, R9 + MOVB R9, 4(AX) + ADDQ $0x05, AX + JMP lz4s_s2_loop + +repeat_four_lz4_s2_emit_copy_short_2b: + LEAL -256(R10), R10 + MOVW $0x0019, (AX) + MOVW R10, 2(AX) + ADDQ $0x04, AX + JMP lz4s_s2_loop + +repeat_three_lz4_s2_emit_copy_short_2b: + LEAL -4(R10), R10 + MOVW $0x0015, (AX) + MOVB R10, 2(AX) + ADDQ $0x03, AX + JMP lz4s_s2_loop + +repeat_two_lz4_s2_emit_copy_short_2b: + SHLL $0x02, R10 + ORL $0x01, R10 + MOVW R10, (AX) + ADDQ $0x02, AX + JMP lz4s_s2_loop + +repeat_two_offset_lz4_s2_emit_copy_short_2b: + XORQ R8, R8 + LEAL 1(R8)(R10*4), R10 + MOVB R9, 1(AX) + SARL $0x08, R9 + SHLL $0x05, R9 + ORL R9, R10 + MOVB R10, (AX) + ADDQ $0x02, AX + JMP lz4s_s2_loop + +long_offset_short_lz4_s2: + MOVB $0xee, (AX) + MOVW R9, 1(AX) + LEAL -60(R10), R10 + ADDQ $0x03, AX + + // emitRepeat +emit_repeat_again_lz4_s2_emit_copy_short: + MOVL R10, R8 + LEAL -4(R10), R10 + CMPL R8, $0x08 + JBE repeat_two_lz4_s2_emit_copy_short + CMPL R8, $0x0c + JAE cant_repeat_two_offset_lz4_s2_emit_copy_short + CMPL R9, $0x00000800 + JB repeat_two_offset_lz4_s2_emit_copy_short + +cant_repeat_two_offset_lz4_s2_emit_copy_short: + CMPL R10, $0x00000104 + JB repeat_three_lz4_s2_emit_copy_short + CMPL R10, $0x00010100 + JB repeat_four_lz4_s2_emit_copy_short + CMPL R10, $0x0100ffff + JB repeat_five_lz4_s2_emit_copy_short + LEAL -16842747(R10), R10 + MOVL $0xfffb001d, (AX) + MOVB $0xff, 4(AX) + ADDQ $0x05, AX + JMP emit_repeat_again_lz4_s2_emit_copy_short + +repeat_five_lz4_s2_emit_copy_short: + LEAL -65536(R10), R10 + MOVL R10, R9 + MOVW $0x001d, (AX) + MOVW R10, 2(AX) + SARL $0x10, R9 + MOVB R9, 4(AX) + ADDQ $0x05, AX + JMP lz4s_s2_loop + +repeat_four_lz4_s2_emit_copy_short: + LEAL -256(R10), R10 + MOVW $0x0019, (AX) + MOVW R10, 2(AX) + ADDQ $0x04, AX + JMP lz4s_s2_loop + +repeat_three_lz4_s2_emit_copy_short: + LEAL -4(R10), R10 + MOVW $0x0015, (AX) + MOVB R10, 2(AX) + ADDQ $0x03, AX + JMP lz4s_s2_loop + +repeat_two_lz4_s2_emit_copy_short: + SHLL $0x02, R10 + ORL $0x01, R10 + MOVW R10, (AX) + ADDQ $0x02, AX + JMP lz4s_s2_loop + +repeat_two_offset_lz4_s2_emit_copy_short: + XORQ R8, R8 + LEAL 1(R8)(R10*4), R10 + MOVB R9, 1(AX) + SARL $0x08, R9 + SHLL $0x05, R9 + ORL R9, R10 + MOVB R10, (AX) + ADDQ $0x02, AX + JMP lz4s_s2_loop + +two_byte_offset_short_lz4_s2: + MOVL R10, R8 + SHLL $0x02, R8 + CMPL R10, $0x0c + JAE emit_copy_three_lz4_s2 + CMPL R9, $0x00000800 + JAE emit_copy_three_lz4_s2 + LEAL -15(R8), R8 + MOVB R9, 1(AX) + SHRL $0x08, R9 + SHLL $0x05, R9 + ORL R9, R8 + MOVB R8, (AX) + ADDQ $0x02, AX + JMP lz4s_s2_loop + +emit_copy_three_lz4_s2: + LEAL -2(R8), R8 + MOVB R8, (AX) + MOVW R9, 1(AX) + ADDQ $0x03, AX + JMP lz4s_s2_loop + +lz4s_s2_done: + MOVQ dst_base+0(FP), CX + SUBQ CX, AX + MOVQ SI, uncompressed+48(FP) + MOVQ AX, dstUsed+56(FP) + RET + +lz4s_s2_corrupt: + XORQ AX, AX + LEAQ -1(AX), SI + MOVQ SI, uncompressed+48(FP) + RET + +lz4s_s2_dstfull: + XORQ AX, AX + LEAQ -2(AX), SI + MOVQ SI, uncompressed+48(FP) + RET + +// func cvtLZ4BlockSnappyAsm(dst []byte, src []byte) (uncompressed int, dstUsed int) +// Requires: SSE2 +TEXT ·cvtLZ4BlockSnappyAsm(SB), NOSPLIT, $0-64 + XORQ SI, SI + MOVQ dst_base+0(FP), AX + MOVQ dst_len+8(FP), CX + MOVQ src_base+24(FP), DX + MOVQ src_len+32(FP), BX + LEAQ (DX)(BX*1), BX + LEAQ -8(AX)(CX*1), CX + +lz4_snappy_loop: + CMPQ DX, BX + JAE lz4_snappy_corrupt + CMPQ AX, CX + JAE lz4_snappy_dstfull + MOVBQZX (DX), DI + MOVQ DI, R8 + MOVQ DI, R9 + SHRQ $0x04, R8 + ANDQ $0x0f, R9 + CMPQ DI, $0xf0 + JB lz4_snappy_ll_end + +lz4_snappy_ll_loop: + INCQ DX + CMPQ DX, BX + JAE lz4_snappy_corrupt + MOVBQZX (DX), DI + ADDQ DI, R8 + CMPQ DI, $0xff + JEQ lz4_snappy_ll_loop + +lz4_snappy_ll_end: + LEAQ (DX)(R8*1), DI + ADDQ $0x04, R9 + CMPQ DI, BX + JAE lz4_snappy_corrupt + INCQ DX + INCQ DI + TESTQ R8, R8 + JZ lz4_snappy_lits_done + LEAQ (AX)(R8*1), R10 + CMPQ R10, CX + JAE lz4_snappy_dstfull + ADDQ R8, SI + LEAL -1(R8), R10 + CMPL R10, $0x3c + JB one_byte_lz4_snappy + CMPL R10, $0x00000100 + JB two_bytes_lz4_snappy + CMPL R10, $0x00010000 + JB three_bytes_lz4_snappy + CMPL R10, $0x01000000 + JB four_bytes_lz4_snappy + MOVB $0xfc, (AX) + MOVL R10, 1(AX) + ADDQ $0x05, AX + JMP memmove_long_lz4_snappy + +four_bytes_lz4_snappy: + MOVL R10, R11 + SHRL $0x10, R11 + MOVB $0xf8, (AX) + MOVW R10, 1(AX) + MOVB R11, 3(AX) + ADDQ $0x04, AX + JMP memmove_long_lz4_snappy + +three_bytes_lz4_snappy: + MOVB $0xf4, (AX) + MOVW R10, 1(AX) + ADDQ $0x03, AX + JMP memmove_long_lz4_snappy + +two_bytes_lz4_snappy: + MOVB $0xf0, (AX) + MOVB R10, 1(AX) + ADDQ $0x02, AX + CMPL R10, $0x40 + JB memmove_lz4_snappy + JMP memmove_long_lz4_snappy + +one_byte_lz4_snappy: + SHLB $0x02, R10 + MOVB R10, (AX) + ADDQ $0x01, AX + +memmove_lz4_snappy: + LEAQ (AX)(R8*1), R10 + + // genMemMoveShort + CMPQ R8, $0x08 + JBE emit_lit_memmove_lz4_snappy_memmove_move_8 + CMPQ R8, $0x10 + JBE emit_lit_memmove_lz4_snappy_memmove_move_8through16 + CMPQ R8, $0x20 + JBE emit_lit_memmove_lz4_snappy_memmove_move_17through32 + JMP emit_lit_memmove_lz4_snappy_memmove_move_33through64 + +emit_lit_memmove_lz4_snappy_memmove_move_8: + MOVQ (DX), R11 + MOVQ R11, (AX) + JMP memmove_end_copy_lz4_snappy + +emit_lit_memmove_lz4_snappy_memmove_move_8through16: + MOVQ (DX), R11 + MOVQ -8(DX)(R8*1), DX + MOVQ R11, (AX) + MOVQ DX, -8(AX)(R8*1) + JMP memmove_end_copy_lz4_snappy + +emit_lit_memmove_lz4_snappy_memmove_move_17through32: + MOVOU (DX), X0 + MOVOU -16(DX)(R8*1), X1 + MOVOU X0, (AX) + MOVOU X1, -16(AX)(R8*1) + JMP memmove_end_copy_lz4_snappy + +emit_lit_memmove_lz4_snappy_memmove_move_33through64: + MOVOU (DX), X0 + MOVOU 16(DX), X1 + MOVOU -32(DX)(R8*1), X2 + MOVOU -16(DX)(R8*1), X3 + MOVOU X0, (AX) + MOVOU X1, 16(AX) + MOVOU X2, -32(AX)(R8*1) + MOVOU X3, -16(AX)(R8*1) + +memmove_end_copy_lz4_snappy: + MOVQ R10, AX + JMP lz4_snappy_lits_emit_done + +memmove_long_lz4_snappy: + LEAQ (AX)(R8*1), R10 + + // genMemMoveLong + MOVOU (DX), X0 + MOVOU 16(DX), X1 + MOVOU -32(DX)(R8*1), X2 + MOVOU -16(DX)(R8*1), X3 + MOVQ R8, R12 + SHRQ $0x05, R12 + MOVQ AX, R11 + ANDL $0x0000001f, R11 + MOVQ $0x00000040, R13 + SUBQ R11, R13 + DECQ R12 + JA emit_lit_memmove_long_lz4_snappylarge_forward_sse_loop_32 + LEAQ -32(DX)(R13*1), R11 + LEAQ -32(AX)(R13*1), R14 + +emit_lit_memmove_long_lz4_snappylarge_big_loop_back: + MOVOU (R11), X4 + MOVOU 16(R11), X5 + MOVOA X4, (R14) + MOVOA X5, 16(R14) + ADDQ $0x20, R14 + ADDQ $0x20, R11 + ADDQ $0x20, R13 + DECQ R12 + JNA emit_lit_memmove_long_lz4_snappylarge_big_loop_back + +emit_lit_memmove_long_lz4_snappylarge_forward_sse_loop_32: + MOVOU -32(DX)(R13*1), X4 + MOVOU -16(DX)(R13*1), X5 + MOVOA X4, -32(AX)(R13*1) + MOVOA X5, -16(AX)(R13*1) + ADDQ $0x20, R13 + CMPQ R8, R13 + JAE emit_lit_memmove_long_lz4_snappylarge_forward_sse_loop_32 + MOVOU X0, (AX) + MOVOU X1, 16(AX) + MOVOU X2, -32(AX)(R8*1) + MOVOU X3, -16(AX)(R8*1) + MOVQ R10, AX + +lz4_snappy_lits_emit_done: + MOVQ DI, DX + +lz4_snappy_lits_done: + CMPQ DX, BX + JNE lz4_snappy_match + CMPQ R9, $0x04 + JEQ lz4_snappy_done + JMP lz4_snappy_corrupt + +lz4_snappy_match: + LEAQ 2(DX), DI + CMPQ DI, BX + JAE lz4_snappy_corrupt + MOVWQZX (DX), R8 + MOVQ DI, DX + TESTQ R8, R8 + JZ lz4_snappy_corrupt + CMPQ R8, SI + JA lz4_snappy_corrupt + CMPQ R9, $0x13 + JNE lz4_snappy_ml_done + +lz4_snappy_ml_loop: + MOVBQZX (DX), DI + INCQ DX + ADDQ DI, R9 + CMPQ DX, BX + JAE lz4_snappy_corrupt + CMPQ DI, $0xff + JEQ lz4_snappy_ml_loop + +lz4_snappy_ml_done: + ADDQ R9, SI + + // emitCopy +two_byte_offset_lz4_s2: + CMPL R9, $0x40 + JBE two_byte_offset_short_lz4_s2 + MOVB $0xee, (AX) + MOVW R8, 1(AX) + LEAL -60(R9), R9 + ADDQ $0x03, AX + CMPQ AX, CX + JAE lz4_snappy_loop + JMP two_byte_offset_lz4_s2 + +two_byte_offset_short_lz4_s2: + MOVL R9, DI + SHLL $0x02, DI + CMPL R9, $0x0c + JAE emit_copy_three_lz4_s2 + CMPL R8, $0x00000800 + JAE emit_copy_three_lz4_s2 + LEAL -15(DI), DI + MOVB R8, 1(AX) + SHRL $0x08, R8 + SHLL $0x05, R8 + ORL R8, DI + MOVB DI, (AX) + ADDQ $0x02, AX + JMP lz4_snappy_loop + +emit_copy_three_lz4_s2: + LEAL -2(DI), DI + MOVB DI, (AX) + MOVW R8, 1(AX) + ADDQ $0x03, AX + JMP lz4_snappy_loop + +lz4_snappy_done: + MOVQ dst_base+0(FP), CX + SUBQ CX, AX + MOVQ SI, uncompressed+48(FP) + MOVQ AX, dstUsed+56(FP) + RET + +lz4_snappy_corrupt: + XORQ AX, AX + LEAQ -1(AX), SI + MOVQ SI, uncompressed+48(FP) + RET + +lz4_snappy_dstfull: + XORQ AX, AX + LEAQ -2(AX), SI + MOVQ SI, uncompressed+48(FP) + RET + +// func cvtLZ4sBlockSnappyAsm(dst []byte, src []byte) (uncompressed int, dstUsed int) +// Requires: SSE2 +TEXT ·cvtLZ4sBlockSnappyAsm(SB), NOSPLIT, $0-64 + XORQ SI, SI + MOVQ dst_base+0(FP), AX + MOVQ dst_len+8(FP), CX + MOVQ src_base+24(FP), DX + MOVQ src_len+32(FP), BX + LEAQ (DX)(BX*1), BX + LEAQ -8(AX)(CX*1), CX + +lz4s_snappy_loop: + CMPQ DX, BX + JAE lz4s_snappy_corrupt + CMPQ AX, CX + JAE lz4s_snappy_dstfull + MOVBQZX (DX), DI + MOVQ DI, R8 + MOVQ DI, R9 + SHRQ $0x04, R8 + ANDQ $0x0f, R9 + CMPQ DI, $0xf0 + JB lz4s_snappy_ll_end + +lz4s_snappy_ll_loop: + INCQ DX + CMPQ DX, BX + JAE lz4s_snappy_corrupt + MOVBQZX (DX), DI + ADDQ DI, R8 + CMPQ DI, $0xff + JEQ lz4s_snappy_ll_loop + +lz4s_snappy_ll_end: + LEAQ (DX)(R8*1), DI + ADDQ $0x03, R9 + CMPQ DI, BX + JAE lz4s_snappy_corrupt + INCQ DX + INCQ DI + TESTQ R8, R8 + JZ lz4s_snappy_lits_done + LEAQ (AX)(R8*1), R10 + CMPQ R10, CX + JAE lz4s_snappy_dstfull + ADDQ R8, SI + LEAL -1(R8), R10 + CMPL R10, $0x3c + JB one_byte_lz4s_snappy + CMPL R10, $0x00000100 + JB two_bytes_lz4s_snappy + CMPL R10, $0x00010000 + JB three_bytes_lz4s_snappy + CMPL R10, $0x01000000 + JB four_bytes_lz4s_snappy + MOVB $0xfc, (AX) + MOVL R10, 1(AX) + ADDQ $0x05, AX + JMP memmove_long_lz4s_snappy + +four_bytes_lz4s_snappy: + MOVL R10, R11 + SHRL $0x10, R11 + MOVB $0xf8, (AX) + MOVW R10, 1(AX) + MOVB R11, 3(AX) + ADDQ $0x04, AX + JMP memmove_long_lz4s_snappy + +three_bytes_lz4s_snappy: + MOVB $0xf4, (AX) + MOVW R10, 1(AX) + ADDQ $0x03, AX + JMP memmove_long_lz4s_snappy + +two_bytes_lz4s_snappy: + MOVB $0xf0, (AX) + MOVB R10, 1(AX) + ADDQ $0x02, AX + CMPL R10, $0x40 + JB memmove_lz4s_snappy + JMP memmove_long_lz4s_snappy + +one_byte_lz4s_snappy: + SHLB $0x02, R10 + MOVB R10, (AX) + ADDQ $0x01, AX + +memmove_lz4s_snappy: + LEAQ (AX)(R8*1), R10 + + // genMemMoveShort + CMPQ R8, $0x08 + JBE emit_lit_memmove_lz4s_snappy_memmove_move_8 + CMPQ R8, $0x10 + JBE emit_lit_memmove_lz4s_snappy_memmove_move_8through16 + CMPQ R8, $0x20 + JBE emit_lit_memmove_lz4s_snappy_memmove_move_17through32 + JMP emit_lit_memmove_lz4s_snappy_memmove_move_33through64 + +emit_lit_memmove_lz4s_snappy_memmove_move_8: + MOVQ (DX), R11 + MOVQ R11, (AX) + JMP memmove_end_copy_lz4s_snappy + +emit_lit_memmove_lz4s_snappy_memmove_move_8through16: + MOVQ (DX), R11 + MOVQ -8(DX)(R8*1), DX + MOVQ R11, (AX) + MOVQ DX, -8(AX)(R8*1) + JMP memmove_end_copy_lz4s_snappy + +emit_lit_memmove_lz4s_snappy_memmove_move_17through32: + MOVOU (DX), X0 + MOVOU -16(DX)(R8*1), X1 + MOVOU X0, (AX) + MOVOU X1, -16(AX)(R8*1) + JMP memmove_end_copy_lz4s_snappy + +emit_lit_memmove_lz4s_snappy_memmove_move_33through64: + MOVOU (DX), X0 + MOVOU 16(DX), X1 + MOVOU -32(DX)(R8*1), X2 + MOVOU -16(DX)(R8*1), X3 + MOVOU X0, (AX) + MOVOU X1, 16(AX) + MOVOU X2, -32(AX)(R8*1) + MOVOU X3, -16(AX)(R8*1) + +memmove_end_copy_lz4s_snappy: + MOVQ R10, AX + JMP lz4s_snappy_lits_emit_done + +memmove_long_lz4s_snappy: + LEAQ (AX)(R8*1), R10 + + // genMemMoveLong + MOVOU (DX), X0 + MOVOU 16(DX), X1 + MOVOU -32(DX)(R8*1), X2 + MOVOU -16(DX)(R8*1), X3 + MOVQ R8, R12 + SHRQ $0x05, R12 + MOVQ AX, R11 + ANDL $0x0000001f, R11 + MOVQ $0x00000040, R13 + SUBQ R11, R13 + DECQ R12 + JA emit_lit_memmove_long_lz4s_snappylarge_forward_sse_loop_32 + LEAQ -32(DX)(R13*1), R11 + LEAQ -32(AX)(R13*1), R14 + +emit_lit_memmove_long_lz4s_snappylarge_big_loop_back: + MOVOU (R11), X4 + MOVOU 16(R11), X5 + MOVOA X4, (R14) + MOVOA X5, 16(R14) + ADDQ $0x20, R14 + ADDQ $0x20, R11 + ADDQ $0x20, R13 + DECQ R12 + JNA emit_lit_memmove_long_lz4s_snappylarge_big_loop_back + +emit_lit_memmove_long_lz4s_snappylarge_forward_sse_loop_32: + MOVOU -32(DX)(R13*1), X4 + MOVOU -16(DX)(R13*1), X5 + MOVOA X4, -32(AX)(R13*1) + MOVOA X5, -16(AX)(R13*1) + ADDQ $0x20, R13 + CMPQ R8, R13 + JAE emit_lit_memmove_long_lz4s_snappylarge_forward_sse_loop_32 + MOVOU X0, (AX) + MOVOU X1, 16(AX) + MOVOU X2, -32(AX)(R8*1) + MOVOU X3, -16(AX)(R8*1) + MOVQ R10, AX + +lz4s_snappy_lits_emit_done: + MOVQ DI, DX + +lz4s_snappy_lits_done: + CMPQ DX, BX + JNE lz4s_snappy_match + CMPQ R9, $0x03 + JEQ lz4s_snappy_done + JMP lz4s_snappy_corrupt + +lz4s_snappy_match: + CMPQ R9, $0x03 + JEQ lz4s_snappy_loop + LEAQ 2(DX), DI + CMPQ DI, BX + JAE lz4s_snappy_corrupt + MOVWQZX (DX), R8 + MOVQ DI, DX + TESTQ R8, R8 + JZ lz4s_snappy_corrupt + CMPQ R8, SI + JA lz4s_snappy_corrupt + CMPQ R9, $0x12 + JNE lz4s_snappy_ml_done + +lz4s_snappy_ml_loop: + MOVBQZX (DX), DI + INCQ DX + ADDQ DI, R9 + CMPQ DX, BX + JAE lz4s_snappy_corrupt + CMPQ DI, $0xff + JEQ lz4s_snappy_ml_loop + +lz4s_snappy_ml_done: + ADDQ R9, SI + + // emitCopy +two_byte_offset_lz4_s2: + CMPL R9, $0x40 + JBE two_byte_offset_short_lz4_s2 + MOVB $0xee, (AX) + MOVW R8, 1(AX) + LEAL -60(R9), R9 + ADDQ $0x03, AX + CMPQ AX, CX + JAE lz4s_snappy_loop + JMP two_byte_offset_lz4_s2 + +two_byte_offset_short_lz4_s2: + MOVL R9, DI + SHLL $0x02, DI + CMPL R9, $0x0c + JAE emit_copy_three_lz4_s2 + CMPL R8, $0x00000800 + JAE emit_copy_three_lz4_s2 + LEAL -15(DI), DI + MOVB R8, 1(AX) + SHRL $0x08, R8 + SHLL $0x05, R8 + ORL R8, DI + MOVB DI, (AX) + ADDQ $0x02, AX + JMP lz4s_snappy_loop + +emit_copy_three_lz4_s2: + LEAL -2(DI), DI + MOVB DI, (AX) + MOVW R8, 1(AX) + ADDQ $0x03, AX + JMP lz4s_snappy_loop + +lz4s_snappy_done: + MOVQ dst_base+0(FP), CX + SUBQ CX, AX + MOVQ SI, uncompressed+48(FP) + MOVQ AX, dstUsed+56(FP) + RET + +lz4s_snappy_corrupt: + XORQ AX, AX + LEAQ -1(AX), SI + MOVQ SI, uncompressed+48(FP) + RET + +lz4s_snappy_dstfull: + XORQ AX, AX + LEAQ -2(AX), SI + MOVQ SI, uncompressed+48(FP) + RET diff --git a/vendor/github.com/klauspost/compress/s2/hashtable_pool.go b/vendor/github.com/klauspost/compress/s2/hashtable_pool.go new file mode 100644 index 000000000..bc7cabd5c --- /dev/null +++ b/vendor/github.com/klauspost/compress/s2/hashtable_pool.go @@ -0,0 +1,65 @@ +package s2 + +import "sync" + +// Table size constants +const ( + betterLongTableBits = 17 + betterLongTableSize = 1 << betterLongTableBits // 131072 + + betterShortTableBits = 14 + betterShortTableSize = 1 << betterShortTableBits // 16384 + + betterSnappyLongTableBits = 16 + betterSnappyLongTableSize = 1 << betterSnappyLongTableBits // 65536 + + bestLongTableBits = 19 + bestLongTableSize = 1 << bestLongTableBits // 524288 + + bestShortTableBits = 16 + bestShortTableSize = 1 << bestShortTableBits // 65536 +) + +type betterTables struct { + lTable [betterLongTableSize]uint32 + sTable [betterShortTableSize]uint32 +} + +var betterTablePool = sync.Pool{New: func() interface{} { return &betterTables{} }} + +// betterSnappyTables holds better-snappy compression hash tables. +type betterSnappyTables struct { + lTable [betterSnappyLongTableSize]uint32 + sTable [betterShortTableSize]uint32 +} + +var betterSnappyTablePool = sync.Pool{New: func() interface{} { return &betterSnappyTables{} }} + +// bestTables holds best compression hash tables. +type bestTables struct { + lTable [bestLongTableSize]uint64 + sTable [bestShortTableSize]uint64 +} + +var bestTablePool = sync.Pool{New: func() interface{} { return &bestTables{} }} + +// getBetterTables gets a zeroed betterTables from the pool. +func getBetterTables() *betterTables { + t := betterTablePool.Get().(*betterTables) + *t = betterTables{} + return t +} + +// getBetterSnappyTables gets a zeroed betterSnappyTables from the pool. +func getBetterSnappyTables() *betterSnappyTables { + t := betterSnappyTablePool.Get().(*betterSnappyTables) + *t = betterSnappyTables{} + return t +} + +// getBestTables gets a zeroed bestTables from the pool. +func getBestTables() *bestTables { + t := bestTablePool.Get().(*bestTables) + *t = bestTables{} + return t +} diff --git a/vendor/github.com/klauspost/compress/s2/index.go b/vendor/github.com/klauspost/compress/s2/index.go new file mode 100644 index 000000000..fb7db2531 --- /dev/null +++ b/vendor/github.com/klauspost/compress/s2/index.go @@ -0,0 +1,602 @@ +// Copyright (c) 2022+ Klaus Post. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package s2 + +import ( + "bytes" + "encoding/binary" + "encoding/json" + "fmt" + "io" + "sort" +) + +const ( + S2IndexHeader = "s2idx\x00" + S2IndexTrailer = "\x00xdi2s" + maxIndexEntries = 1 << 16 + // If distance is less than this, we do not add the entry. + minIndexDist = 1 << 20 +) + +// Index represents an S2/Snappy index. +type Index struct { + TotalUncompressed int64 // Total Uncompressed size if known. Will be -1 if unknown. + TotalCompressed int64 // Total Compressed size if known. Will be -1 if unknown. + info []struct { + compressedOffset int64 + uncompressedOffset int64 + } + estBlockUncomp int64 +} + +func (i *Index) reset(maxBlock int) { + i.estBlockUncomp = int64(maxBlock) + i.TotalCompressed = -1 + i.TotalUncompressed = -1 + if len(i.info) > 0 { + i.info = i.info[:0] + } +} + +// allocInfos will allocate an empty slice of infos. +func (i *Index) allocInfos(n int) { + if n > maxIndexEntries { + panic("n > maxIndexEntries") + } + i.info = make([]struct { + compressedOffset int64 + uncompressedOffset int64 + }, 0, n) +} + +// add an uncompressed and compressed pair. +// Entries must be sent in order. +func (i *Index) add(compressedOffset, uncompressedOffset int64) error { + if i == nil { + return nil + } + lastIdx := len(i.info) - 1 + if lastIdx >= 0 { + latest := i.info[lastIdx] + if latest.uncompressedOffset == uncompressedOffset { + // Uncompressed didn't change, don't add entry, + // but update start index. + latest.compressedOffset = compressedOffset + i.info[lastIdx] = latest + return nil + } + if latest.uncompressedOffset > uncompressedOffset { + return fmt.Errorf("internal error: Earlier uncompressed received (%d > %d)", latest.uncompressedOffset, uncompressedOffset) + } + if latest.compressedOffset > compressedOffset { + return fmt.Errorf("internal error: Earlier compressed received (%d > %d)", latest.compressedOffset, compressedOffset) + } + if latest.uncompressedOffset+minIndexDist > uncompressedOffset { + // Only add entry if distance is large enough. + return nil + } + } + i.info = append(i.info, struct { + compressedOffset int64 + uncompressedOffset int64 + }{compressedOffset: compressedOffset, uncompressedOffset: uncompressedOffset}) + return nil +} + +// Find the offset at or before the wanted (uncompressed) offset. +// If offset is 0 or positive it is the offset from the beginning of the file. +// If the uncompressed size is known, the offset must be within the file. +// If an offset outside the file is requested io.ErrUnexpectedEOF is returned. +// If the offset is negative, it is interpreted as the distance from the end of the file, +// where -1 represents the last byte. +// If offset from the end of the file is requested, but size is unknown, +// ErrUnsupported will be returned. +func (i *Index) Find(offset int64) (compressedOff, uncompressedOff int64, err error) { + if i.TotalUncompressed < 0 { + return 0, 0, ErrCorrupt + } + if offset < 0 { + offset = i.TotalUncompressed + offset + if offset < 0 { + return 0, 0, io.ErrUnexpectedEOF + } + } + if offset > i.TotalUncompressed { + return 0, 0, io.ErrUnexpectedEOF + } + if len(i.info) > 200 { + n := sort.Search(len(i.info), func(n int) bool { + return i.info[n].uncompressedOffset > offset + }) + if n == 0 { + n = 1 + } + return i.info[n-1].compressedOffset, i.info[n-1].uncompressedOffset, nil + } + for _, info := range i.info { + if info.uncompressedOffset > offset { + break + } + compressedOff = info.compressedOffset + uncompressedOff = info.uncompressedOffset + } + return compressedOff, uncompressedOff, nil +} + +// reduce to stay below maxIndexEntries +func (i *Index) reduce() { + if len(i.info) < maxIndexEntries && i.estBlockUncomp >= minIndexDist { + return + } + + // Algorithm, keep 1, remove removeN entries... + removeN := (len(i.info) + 1) / maxIndexEntries + src := i.info + j := 0 + + // Each block should be at least 1MB, but don't reduce below 1000 entries. + for i.estBlockUncomp*(int64(removeN)+1) < minIndexDist && len(i.info)/(removeN+1) > 1000 { + removeN++ + } + for idx := 0; idx < len(src); idx++ { + i.info[j] = src[idx] + j++ + idx += removeN + } + i.info = i.info[:j] + // Update maxblock estimate. + i.estBlockUncomp += i.estBlockUncomp * int64(removeN) +} + +func (i *Index) appendTo(b []byte, uncompTotal, compTotal int64) []byte { + i.reduce() + var tmp [binary.MaxVarintLen64]byte + + initSize := len(b) + // We make the start a skippable header+size. + b = append(b, ChunkTypeIndex, 0, 0, 0) + b = append(b, []byte(S2IndexHeader)...) + // Total Uncompressed size + n := binary.PutVarint(tmp[:], uncompTotal) + b = append(b, tmp[:n]...) + // Total Compressed size + n = binary.PutVarint(tmp[:], compTotal) + b = append(b, tmp[:n]...) + // Put EstBlockUncomp size + n = binary.PutVarint(tmp[:], i.estBlockUncomp) + b = append(b, tmp[:n]...) + // Put length + n = binary.PutVarint(tmp[:], int64(len(i.info))) + b = append(b, tmp[:n]...) + + // Check if we should add uncompressed offsets + var hasUncompressed byte + for idx, info := range i.info { + if idx == 0 { + if info.uncompressedOffset != 0 { + hasUncompressed = 1 + break + } + continue + } + if info.uncompressedOffset != i.info[idx-1].uncompressedOffset+i.estBlockUncomp { + hasUncompressed = 1 + break + } + } + b = append(b, hasUncompressed) + + // Add each entry + if hasUncompressed == 1 { + for idx, info := range i.info { + uOff := info.uncompressedOffset + if idx > 0 { + prev := i.info[idx-1] + uOff -= prev.uncompressedOffset + (i.estBlockUncomp) + } + n = binary.PutVarint(tmp[:], uOff) + b = append(b, tmp[:n]...) + } + } + + // Initial compressed size estimate. + cPredict := i.estBlockUncomp / 2 + + for idx, info := range i.info { + cOff := info.compressedOffset + if idx > 0 { + prev := i.info[idx-1] + cOff -= prev.compressedOffset + cPredict + // Update compressed size prediction, with half the error. + cPredict += cOff / 2 + } + n = binary.PutVarint(tmp[:], cOff) + b = append(b, tmp[:n]...) + } + + // Add Total Size. + // Stored as fixed size for easier reading. + binary.LittleEndian.PutUint32(tmp[:], uint32(len(b)-initSize+4+len(S2IndexTrailer))) + b = append(b, tmp[:4]...) + // Trailer + b = append(b, []byte(S2IndexTrailer)...) + + // Update size + chunkLen := len(b) - initSize - skippableFrameHeader + b[initSize+1] = uint8(chunkLen >> 0) + b[initSize+2] = uint8(chunkLen >> 8) + b[initSize+3] = uint8(chunkLen >> 16) + //fmt.Printf("chunklen: 0x%x Uncomp:%d, Comp:%d\n", chunkLen, uncompTotal, compTotal) + return b +} + +// Load a binary index. +// A zero value Index can be used or a previous one can be reused. +func (i *Index) Load(b []byte) ([]byte, error) { + if len(b) <= 4+len(S2IndexHeader)+len(S2IndexTrailer) { + return b, io.ErrUnexpectedEOF + } + if b[0] != ChunkTypeIndex { + return b, ErrCorrupt + } + chunkLen := int(b[1]) | int(b[2])<<8 | int(b[3])<<16 + b = b[4:] + + // Validate we have enough... + if len(b) < chunkLen { + return b, io.ErrUnexpectedEOF + } + if !bytes.Equal(b[:len(S2IndexHeader)], []byte(S2IndexHeader)) { + return b, ErrUnsupported + } + b = b[len(S2IndexHeader):] + + // Total Uncompressed + if v, n := binary.Varint(b); n <= 0 || v < 0 { + return b, ErrCorrupt + } else { + i.TotalUncompressed = v + b = b[n:] + } + + // Total Compressed + if v, n := binary.Varint(b); n <= 0 { + return b, ErrCorrupt + } else { + i.TotalCompressed = v + b = b[n:] + } + + // Read EstBlockUncomp + if v, n := binary.Varint(b); n <= 0 { + return b, ErrCorrupt + } else { + if v < 0 { + return b, ErrCorrupt + } + i.estBlockUncomp = v + b = b[n:] + } + + var entries int + if v, n := binary.Varint(b); n <= 0 { + return b, ErrCorrupt + } else { + if v < 0 || v > maxIndexEntries { + return b, ErrCorrupt + } + entries = int(v) + b = b[n:] + } + if cap(i.info) < entries { + i.allocInfos(entries) + } + i.info = i.info[:entries] + + if len(b) < 1 { + return b, io.ErrUnexpectedEOF + } + hasUncompressed := b[0] + b = b[1:] + if hasUncompressed&1 != hasUncompressed { + return b, ErrCorrupt + } + + // Add each uncompressed entry + for idx := range i.info { + var uOff int64 + if hasUncompressed != 0 { + // Load delta + if v, n := binary.Varint(b); n <= 0 { + return b, ErrCorrupt + } else { + uOff = v + b = b[n:] + } + } + + if idx > 0 { + prev := i.info[idx-1].uncompressedOffset + uOff += prev + (i.estBlockUncomp) + if uOff <= prev { + return b, ErrCorrupt + } + } + if uOff < 0 { + return b, ErrCorrupt + } + i.info[idx].uncompressedOffset = uOff + } + + // Initial compressed size estimate. + cPredict := i.estBlockUncomp / 2 + + // Add each compressed entry + for idx := range i.info { + var cOff int64 + if v, n := binary.Varint(b); n <= 0 { + return b, ErrCorrupt + } else { + cOff = v + b = b[n:] + } + + if idx > 0 { + // Update compressed size prediction, with half the error. + cPredictNew := cPredict + cOff/2 + + prev := i.info[idx-1].compressedOffset + cOff += prev + cPredict + if cOff <= prev { + return b, ErrCorrupt + } + cPredict = cPredictNew + } + if cOff < 0 { + return b, ErrCorrupt + } + i.info[idx].compressedOffset = cOff + } + if len(b) < 4+len(S2IndexTrailer) { + return b, io.ErrUnexpectedEOF + } + // Skip size... + b = b[4:] + + // Check trailer... + if !bytes.Equal(b[:len(S2IndexTrailer)], []byte(S2IndexTrailer)) { + return b, ErrCorrupt + } + return b[len(S2IndexTrailer):], nil +} + +// LoadStream will load an index from the end of the supplied stream. +// ErrUnsupported will be returned if the signature cannot be found. +// ErrCorrupt will be returned if unexpected values are found. +// io.ErrUnexpectedEOF is returned if there are too few bytes. +// IO errors are returned as-is. +func (i *Index) LoadStream(rs io.ReadSeeker) error { + // Go to end. + _, err := rs.Seek(-10, io.SeekEnd) + if err != nil { + return err + } + var tmp [10]byte + _, err = io.ReadFull(rs, tmp[:]) + if err != nil { + return err + } + // Check trailer... + if !bytes.Equal(tmp[4:4+len(S2IndexTrailer)], []byte(S2IndexTrailer)) { + return ErrUnsupported + } + sz := binary.LittleEndian.Uint32(tmp[:4]) + if sz > maxChunkSize+skippableFrameHeader { + return ErrCorrupt + } + _, err = rs.Seek(-int64(sz), io.SeekEnd) + if err != nil { + return err + } + + // Read index. + buf := make([]byte, sz) + _, err = io.ReadFull(rs, buf) + if err != nil { + return err + } + _, err = i.Load(buf) + return err +} + +// IndexStream will return an index for a stream. +// The stream structure will be checked, but +// data within blocks is not verified. +// The returned index can either be appended to the end of the stream +// or stored separately. +func IndexStream(r io.Reader) ([]byte, error) { + var i Index + var buf [maxChunkSize]byte + var readHeader bool + for { + _, err := io.ReadFull(r, buf[:4]) + if err != nil { + if err == io.EOF { + return i.appendTo(nil, i.TotalUncompressed, i.TotalCompressed), nil + } + return nil, err + } + // Start of this chunk. + startChunk := i.TotalCompressed + i.TotalCompressed += 4 + + chunkType := buf[0] + if !readHeader { + if chunkType != chunkTypeStreamIdentifier { + return nil, ErrCorrupt + } + readHeader = true + } + chunkLen := int(buf[1]) | int(buf[2])<<8 | int(buf[3])<<16 + if chunkLen < checksumSize { + return nil, ErrCorrupt + } + + i.TotalCompressed += int64(chunkLen) + _, err = io.ReadFull(r, buf[:chunkLen]) + if err != nil { + return nil, io.ErrUnexpectedEOF + } + // The chunk types are specified at + // https://github.com/google/snappy/blob/master/framing_format.txt + switch chunkType { + case chunkTypeCompressedData: + // Section 4.2. Compressed data (chunk type 0x00). + // Skip checksum. + dLen, err := DecodedLen(buf[checksumSize:]) + if err != nil { + return nil, err + } + if dLen > maxBlockSize { + return nil, ErrCorrupt + } + if i.estBlockUncomp == 0 { + // Use first block for estimate... + i.estBlockUncomp = int64(dLen) + } + err = i.add(startChunk, i.TotalUncompressed) + if err != nil { + return nil, err + } + i.TotalUncompressed += int64(dLen) + continue + case chunkTypeUncompressedData: + n2 := chunkLen - checksumSize + if n2 > maxBlockSize { + return nil, ErrCorrupt + } + if i.estBlockUncomp == 0 { + // Use first block for estimate... + i.estBlockUncomp = int64(n2) + } + err = i.add(startChunk, i.TotalUncompressed) + if err != nil { + return nil, err + } + i.TotalUncompressed += int64(n2) + continue + case chunkTypeStreamIdentifier: + // Section 4.1. Stream identifier (chunk type 0xff). + if chunkLen != len(magicBody) { + return nil, ErrCorrupt + } + + if string(buf[:len(magicBody)]) != magicBody { + if string(buf[:len(magicBody)]) != magicBodySnappy { + return nil, ErrCorrupt + } + } + + continue + } + + if chunkType <= 0x7f { + // Section 4.5. Reserved unskippable chunks (chunk types 0x02-0x7f). + return nil, ErrUnsupported + } + if chunkLen > maxChunkSize { + return nil, ErrUnsupported + } + // Section 4.4 Padding (chunk type 0xfe). + // Section 4.6. Reserved skippable chunks (chunk types 0x80-0xfd). + } +} + +// JSON returns the index as JSON text. +func (i *Index) JSON() []byte { + type offset struct { + CompressedOffset int64 `json:"compressed"` + UncompressedOffset int64 `json:"uncompressed"` + } + x := struct { + TotalUncompressed int64 `json:"total_uncompressed"` // Total Uncompressed size if known. Will be -1 if unknown. + TotalCompressed int64 `json:"total_compressed"` // Total Compressed size if known. Will be -1 if unknown. + Offsets []offset `json:"offsets"` + EstBlockUncomp int64 `json:"est_block_uncompressed"` + }{ + TotalUncompressed: i.TotalUncompressed, + TotalCompressed: i.TotalCompressed, + EstBlockUncomp: i.estBlockUncomp, + } + for _, v := range i.info { + x.Offsets = append(x.Offsets, offset{CompressedOffset: v.compressedOffset, UncompressedOffset: v.uncompressedOffset}) + } + b, _ := json.MarshalIndent(x, "", " ") + return b +} + +// RemoveIndexHeaders will trim all headers and trailers from a given index. +// This is expected to save 20 bytes. +// These can be restored using RestoreIndexHeaders. +// This removes a layer of security, but is the most compact representation. +// Returns nil if headers contains errors. +// The returned slice references the provided slice. +func RemoveIndexHeaders(b []byte) []byte { + const save = 4 + len(S2IndexHeader) + len(S2IndexTrailer) + 4 + if len(b) <= save { + return nil + } + if b[0] != ChunkTypeIndex { + return nil + } + chunkLen := int(b[1]) | int(b[2])<<8 | int(b[3])<<16 + b = b[4:] + + // Validate we have enough... + if len(b) < chunkLen { + return nil + } + b = b[:chunkLen] + + if !bytes.Equal(b[:len(S2IndexHeader)], []byte(S2IndexHeader)) { + return nil + } + b = b[len(S2IndexHeader):] + if !bytes.HasSuffix(b, []byte(S2IndexTrailer)) { + return nil + } + b = bytes.TrimSuffix(b, []byte(S2IndexTrailer)) + + if len(b) < 4 { + return nil + } + return b[:len(b)-4] +} + +// RestoreIndexHeaders will index restore headers removed by RemoveIndexHeaders. +// No error checking is performed on the input. +// If a 0 length slice is sent, it is returned without modification. +func RestoreIndexHeaders(in []byte) []byte { + if len(in) == 0 { + return in + } + b := make([]byte, 0, 4+len(S2IndexHeader)+len(in)+len(S2IndexTrailer)+4) + b = append(b, ChunkTypeIndex, 0, 0, 0) + b = append(b, []byte(S2IndexHeader)...) + b = append(b, in...) + + var tmp [4]byte + binary.LittleEndian.PutUint32(tmp[:], uint32(len(b)+4+len(S2IndexTrailer))) + b = append(b, tmp[:4]...) + // Trailer + b = append(b, []byte(S2IndexTrailer)...) + + chunkLen := len(b) - skippableFrameHeader + b[1] = uint8(chunkLen >> 0) + b[2] = uint8(chunkLen >> 8) + b[3] = uint8(chunkLen >> 16) + return b +} diff --git a/vendor/github.com/klauspost/compress/s2/lz4convert.go b/vendor/github.com/klauspost/compress/s2/lz4convert.go new file mode 100644 index 000000000..46ed908e3 --- /dev/null +++ b/vendor/github.com/klauspost/compress/s2/lz4convert.go @@ -0,0 +1,585 @@ +// Copyright (c) 2022 Klaus Post. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package s2 + +import ( + "encoding/binary" + "errors" + "fmt" +) + +// LZ4Converter provides conversion from LZ4 blocks as defined here: +// https://github.com/lz4/lz4/blob/dev/doc/lz4_Block_format.md +type LZ4Converter struct { +} + +// ErrDstTooSmall is returned when provided destination is too small. +var ErrDstTooSmall = errors.New("s2: destination too small") + +// ConvertBlock will convert an LZ4 block and append it as an S2 +// block without block length to dst. +// The uncompressed size is returned as well. +// dst must have capacity to contain the entire compressed block. +func (l *LZ4Converter) ConvertBlock(dst, src []byte) ([]byte, int, error) { + if len(src) == 0 { + return dst, 0, nil + } + const debug = false + const inline = true + const lz4MinMatch = 4 + + s, d := 0, len(dst) + dst = dst[:cap(dst)] + if !debug && hasAmd64Asm { + res, sz := cvtLZ4BlockAsm(dst[d:], src) + if res < 0 { + const ( + errCorrupt = -1 + errDstTooSmall = -2 + ) + switch res { + case errCorrupt: + return nil, 0, ErrCorrupt + case errDstTooSmall: + return nil, 0, ErrDstTooSmall + default: + return nil, 0, fmt.Errorf("unexpected result: %d", res) + } + } + if d+sz > len(dst) { + return nil, 0, ErrDstTooSmall + } + return dst[:d+sz], res, nil + } + + dLimit := len(dst) - 10 + var lastOffset uint16 + var uncompressed int + if debug { + fmt.Printf("convert block start: len(src): %d, len(dst):%d \n", len(src), len(dst)) + } + + for { + if s >= len(src) { + return dst[:d], 0, ErrCorrupt + } + // Read literal info + token := src[s] + ll := int(token >> 4) + ml := int(lz4MinMatch + (token & 0xf)) + + // If upper nibble is 15, literal length is extended + if token >= 0xf0 { + for { + s++ + if s >= len(src) { + if debug { + fmt.Printf("error reading ll: s (%d) >= len(src) (%d)\n", s, len(src)) + } + return dst[:d], 0, ErrCorrupt + } + val := src[s] + ll += int(val) + if val != 255 { + break + } + } + } + // Skip past token + if s+ll >= len(src) { + if debug { + fmt.Printf("error literals: s+ll (%d+%d) >= len(src) (%d)\n", s, ll, len(src)) + } + return nil, 0, ErrCorrupt + } + s++ + if ll > 0 { + if d+ll > dLimit { + return nil, 0, ErrDstTooSmall + } + if debug { + fmt.Printf("emit %d literals\n", ll) + } + d += emitLiteralGo(dst[d:], src[s:s+ll]) + s += ll + uncompressed += ll + } + + // Check if we are done... + if s == len(src) && ml == lz4MinMatch { + break + } + // 2 byte offset + if s >= len(src)-2 { + if debug { + fmt.Printf("s (%d) >= len(src)-2 (%d)", s, len(src)-2) + } + return nil, 0, ErrCorrupt + } + offset := binary.LittleEndian.Uint16(src[s:]) + s += 2 + if offset == 0 { + if debug { + fmt.Printf("error: offset 0, ml: %d, len(src)-s: %d\n", ml, len(src)-s) + } + return nil, 0, ErrCorrupt + } + if int(offset) > uncompressed { + if debug { + fmt.Printf("error: offset (%d)> uncompressed (%d)\n", offset, uncompressed) + } + return nil, 0, ErrCorrupt + } + + if ml == lz4MinMatch+15 { + for { + if s >= len(src) { + if debug { + fmt.Printf("error reading ml: s (%d) >= len(src) (%d)\n", s, len(src)) + } + return nil, 0, ErrCorrupt + } + val := src[s] + s++ + ml += int(val) + if val != 255 { + if s >= len(src) { + if debug { + fmt.Printf("error reading ml: s (%d) >= len(src) (%d)\n", s, len(src)) + } + return nil, 0, ErrCorrupt + } + break + } + } + } + if offset == lastOffset { + if debug { + fmt.Printf("emit repeat, length: %d, offset: %d\n", ml, offset) + } + if !inline { + d += emitRepeat16(dst[d:], offset, ml) + } else { + length := ml + dst := dst[d:] + for len(dst) > 5 { + // Repeat offset, make length cheaper + length -= 4 + if length <= 4 { + dst[0] = uint8(length)<<2 | tagCopy1 + dst[1] = 0 + d += 2 + break + } + if length < 8 && offset < 2048 { + // Encode WITH offset + dst[1] = uint8(offset) + dst[0] = uint8(offset>>8)<<5 | uint8(length)<<2 | tagCopy1 + d += 2 + break + } + if length < (1<<8)+4 { + length -= 4 + dst[2] = uint8(length) + dst[1] = 0 + dst[0] = 5<<2 | tagCopy1 + d += 3 + break + } + if length < (1<<16)+(1<<8) { + length -= 1 << 8 + dst[3] = uint8(length >> 8) + dst[2] = uint8(length >> 0) + dst[1] = 0 + dst[0] = 6<<2 | tagCopy1 + d += 4 + break + } + const maxRepeat = (1 << 24) - 1 + length -= 1 << 16 + left := 0 + if length > maxRepeat { + left = length - maxRepeat + 4 + length = maxRepeat - 4 + } + dst[4] = uint8(length >> 16) + dst[3] = uint8(length >> 8) + dst[2] = uint8(length >> 0) + dst[1] = 0 + dst[0] = 7<<2 | tagCopy1 + if left > 0 { + d += 5 + emitRepeat16(dst[5:], offset, left) + break + } + d += 5 + break + } + } + } else { + if debug { + fmt.Printf("emit copy, length: %d, offset: %d\n", ml, offset) + } + if !inline { + d += emitCopy16(dst[d:], offset, ml) + } else { + length := ml + dst := dst[d:] + for len(dst) > 5 { + // Offset no more than 2 bytes. + if length > 64 { + off := 3 + if offset < 2048 { + // emit 8 bytes as tagCopy1, rest as repeats. + dst[1] = uint8(offset) + dst[0] = uint8(offset>>8)<<5 | uint8(8-4)<<2 | tagCopy1 + length -= 8 + off = 2 + } else { + // Emit a length 60 copy, encoded as 3 bytes. + // Emit remaining as repeat value (minimum 4 bytes). + dst[2] = uint8(offset >> 8) + dst[1] = uint8(offset) + dst[0] = 59<<2 | tagCopy2 + length -= 60 + } + // Emit remaining as repeats, at least 4 bytes remain. + d += off + emitRepeat16(dst[off:], offset, length) + break + } + if length >= 12 || offset >= 2048 { + // Emit the remaining copy, encoded as 3 bytes. + dst[2] = uint8(offset >> 8) + dst[1] = uint8(offset) + dst[0] = uint8(length-1)<<2 | tagCopy2 + d += 3 + break + } + // Emit the remaining copy, encoded as 2 bytes. + dst[1] = uint8(offset) + dst[0] = uint8(offset>>8)<<5 | uint8(length-4)<<2 | tagCopy1 + d += 2 + break + } + } + lastOffset = offset + } + uncompressed += ml + if d > dLimit { + return nil, 0, ErrDstTooSmall + } + } + + return dst[:d], uncompressed, nil +} + +// ConvertBlockSnappy will convert an LZ4 block and append it +// as a Snappy block without block length to dst. +// The uncompressed size is returned as well. +// dst must have capacity to contain the entire compressed block. +func (l *LZ4Converter) ConvertBlockSnappy(dst, src []byte) ([]byte, int, error) { + if len(src) == 0 { + return dst, 0, nil + } + const debug = false + const lz4MinMatch = 4 + + s, d := 0, len(dst) + dst = dst[:cap(dst)] + // Use assembly when possible + if !debug && hasAmd64Asm { + res, sz := cvtLZ4BlockSnappyAsm(dst[d:], src) + if res < 0 { + const ( + errCorrupt = -1 + errDstTooSmall = -2 + ) + switch res { + case errCorrupt: + return nil, 0, ErrCorrupt + case errDstTooSmall: + return nil, 0, ErrDstTooSmall + default: + return nil, 0, fmt.Errorf("unexpected result: %d", res) + } + } + if d+sz > len(dst) { + return nil, 0, ErrDstTooSmall + } + return dst[:d+sz], res, nil + } + + dLimit := len(dst) - 10 + var uncompressed int + if debug { + fmt.Printf("convert block start: len(src): %d, len(dst):%d \n", len(src), len(dst)) + } + + for { + if s >= len(src) { + return nil, 0, ErrCorrupt + } + // Read literal info + token := src[s] + ll := int(token >> 4) + ml := int(lz4MinMatch + (token & 0xf)) + + // If upper nibble is 15, literal length is extended + if token >= 0xf0 { + for { + s++ + if s >= len(src) { + if debug { + fmt.Printf("error reading ll: s (%d) >= len(src) (%d)\n", s, len(src)) + } + return nil, 0, ErrCorrupt + } + val := src[s] + ll += int(val) + if val != 255 { + break + } + } + } + // Skip past token + if s+ll >= len(src) { + if debug { + fmt.Printf("error literals: s+ll (%d+%d) >= len(src) (%d)\n", s, ll, len(src)) + } + return nil, 0, ErrCorrupt + } + s++ + if ll > 0 { + if d+ll > dLimit { + return nil, 0, ErrDstTooSmall + } + if debug { + fmt.Printf("emit %d literals\n", ll) + } + d += emitLiteralGo(dst[d:], src[s:s+ll]) + s += ll + uncompressed += ll + } + + // Check if we are done... + if s == len(src) && ml == lz4MinMatch { + break + } + // 2 byte offset + if s >= len(src)-2 { + if debug { + fmt.Printf("s (%d) >= len(src)-2 (%d)", s, len(src)-2) + } + return nil, 0, ErrCorrupt + } + offset := binary.LittleEndian.Uint16(src[s:]) + s += 2 + if offset == 0 { + if debug { + fmt.Printf("error: offset 0, ml: %d, len(src)-s: %d\n", ml, len(src)-s) + } + return nil, 0, ErrCorrupt + } + if int(offset) > uncompressed { + if debug { + fmt.Printf("error: offset (%d)> uncompressed (%d)\n", offset, uncompressed) + } + return nil, 0, ErrCorrupt + } + + if ml == lz4MinMatch+15 { + for { + if s >= len(src) { + if debug { + fmt.Printf("error reading ml: s (%d) >= len(src) (%d)\n", s, len(src)) + } + return nil, 0, ErrCorrupt + } + val := src[s] + s++ + ml += int(val) + if val != 255 { + if s >= len(src) { + if debug { + fmt.Printf("error reading ml: s (%d) >= len(src) (%d)\n", s, len(src)) + } + return nil, 0, ErrCorrupt + } + break + } + } + } + if debug { + fmt.Printf("emit copy, length: %d, offset: %d\n", ml, offset) + } + length := ml + // d += emitCopyNoRepeat(dst[d:], int(offset), ml) + for length > 0 { + if d >= dLimit { + return nil, 0, ErrDstTooSmall + } + + // Offset no more than 2 bytes. + if length > 64 { + // Emit a length 64 copy, encoded as 3 bytes. + dst[d+2] = uint8(offset >> 8) + dst[d+1] = uint8(offset) + dst[d+0] = 63<<2 | tagCopy2 + length -= 64 + d += 3 + continue + } + if length >= 12 || offset >= 2048 || length < 4 { + // Emit the remaining copy, encoded as 3 bytes. + dst[d+2] = uint8(offset >> 8) + dst[d+1] = uint8(offset) + dst[d+0] = uint8(length-1)<<2 | tagCopy2 + d += 3 + break + } + // Emit the remaining copy, encoded as 2 bytes. + dst[d+1] = uint8(offset) + dst[d+0] = uint8(offset>>8)<<5 | uint8(length-4)<<2 | tagCopy1 + d += 2 + break + } + uncompressed += ml + if d > dLimit { + return nil, 0, ErrDstTooSmall + } + } + + return dst[:d], uncompressed, nil +} + +// emitRepeat writes a repeat chunk and returns the number of bytes written. +// Length must be at least 4 and < 1<<24 +func emitRepeat16(dst []byte, offset uint16, length int) int { + // Repeat offset, make length cheaper + length -= 4 + if length <= 4 { + dst[0] = uint8(length)<<2 | tagCopy1 + dst[1] = 0 + return 2 + } + if length < 8 && offset < 2048 { + // Encode WITH offset + dst[1] = uint8(offset) + dst[0] = uint8(offset>>8)<<5 | uint8(length)<<2 | tagCopy1 + return 2 + } + if length < (1<<8)+4 { + length -= 4 + dst[2] = uint8(length) + dst[1] = 0 + dst[0] = 5<<2 | tagCopy1 + return 3 + } + if length < (1<<16)+(1<<8) { + length -= 1 << 8 + dst[3] = uint8(length >> 8) + dst[2] = uint8(length >> 0) + dst[1] = 0 + dst[0] = 6<<2 | tagCopy1 + return 4 + } + const maxRepeat = (1 << 24) - 1 + length -= 1 << 16 + left := 0 + if length > maxRepeat { + left = length - maxRepeat + 4 + length = maxRepeat - 4 + } + dst[4] = uint8(length >> 16) + dst[3] = uint8(length >> 8) + dst[2] = uint8(length >> 0) + dst[1] = 0 + dst[0] = 7<<2 | tagCopy1 + if left > 0 { + return 5 + emitRepeat16(dst[5:], offset, left) + } + return 5 +} + +// emitCopy writes a copy chunk and returns the number of bytes written. +// +// It assumes that: +// +// dst is long enough to hold the encoded bytes +// 1 <= offset && offset <= math.MaxUint16 +// 4 <= length && length <= math.MaxUint32 +func emitCopy16(dst []byte, offset uint16, length int) int { + // Offset no more than 2 bytes. + if length > 64 { + off := 3 + if offset < 2048 { + // emit 8 bytes as tagCopy1, rest as repeats. + dst[1] = uint8(offset) + dst[0] = uint8(offset>>8)<<5 | uint8(8-4)<<2 | tagCopy1 + length -= 8 + off = 2 + } else { + // Emit a length 60 copy, encoded as 3 bytes. + // Emit remaining as repeat value (minimum 4 bytes). + dst[2] = uint8(offset >> 8) + dst[1] = uint8(offset) + dst[0] = 59<<2 | tagCopy2 + length -= 60 + } + // Emit remaining as repeats, at least 4 bytes remain. + return off + emitRepeat16(dst[off:], offset, length) + } + if length >= 12 || offset >= 2048 { + // Emit the remaining copy, encoded as 3 bytes. + dst[2] = uint8(offset >> 8) + dst[1] = uint8(offset) + dst[0] = uint8(length-1)<<2 | tagCopy2 + return 3 + } + // Emit the remaining copy, encoded as 2 bytes. + dst[1] = uint8(offset) + dst[0] = uint8(offset>>8)<<5 | uint8(length-4)<<2 | tagCopy1 + return 2 +} + +// emitLiteral writes a literal chunk and returns the number of bytes written. +// +// It assumes that: +// +// dst is long enough to hold the encoded bytes +// 0 <= len(lit) && len(lit) <= math.MaxUint32 +func emitLiteralGo(dst, lit []byte) int { + if len(lit) == 0 { + return 0 + } + i, n := 0, uint(len(lit)-1) + switch { + case n < 60: + dst[0] = uint8(n)<<2 | tagLiteral + i = 1 + case n < 1<<8: + dst[1] = uint8(n) + dst[0] = 60<<2 | tagLiteral + i = 2 + case n < 1<<16: + dst[2] = uint8(n >> 8) + dst[1] = uint8(n) + dst[0] = 61<<2 | tagLiteral + i = 3 + case n < 1<<24: + dst[3] = uint8(n >> 16) + dst[2] = uint8(n >> 8) + dst[1] = uint8(n) + dst[0] = 62<<2 | tagLiteral + i = 4 + default: + dst[4] = uint8(n >> 24) + dst[3] = uint8(n >> 16) + dst[2] = uint8(n >> 8) + dst[1] = uint8(n) + dst[0] = 63<<2 | tagLiteral + i = 5 + } + return i + copy(dst[i:], lit) +} diff --git a/vendor/github.com/klauspost/compress/s2/lz4sconvert.go b/vendor/github.com/klauspost/compress/s2/lz4sconvert.go new file mode 100644 index 000000000..000f39719 --- /dev/null +++ b/vendor/github.com/klauspost/compress/s2/lz4sconvert.go @@ -0,0 +1,467 @@ +// Copyright (c) 2022 Klaus Post. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package s2 + +import ( + "encoding/binary" + "fmt" +) + +// LZ4sConverter provides conversion from LZ4s. +// (Intel modified LZ4 Blocks) +// https://cdrdv2-public.intel.com/743912/743912-qat-programmers-guide-v2.0.pdf +// LZ4s is a variant of LZ4 block format. LZ4s should be considered as an intermediate compressed block format. +// The LZ4s format is selected when the application sets the compType to CPA_DC_LZ4S in CpaDcSessionSetupData. +// The LZ4s block returned by the Intel® QAT hardware can be used by an external +// software post-processing to generate other compressed data formats. +// The following table lists the differences between LZ4 and LZ4s block format. LZ4s block format uses +// the same high-level formatting as LZ4 block format with the following encoding changes: +// For Min Match of 4 bytes, Copy length value 1-15 means length 4-18 with 18 bytes adding an extra byte. +// ONLY "Min match of 4 bytes" is supported. +type LZ4sConverter struct { +} + +// ConvertBlock will convert an LZ4s block and append it as an S2 +// block without block length to dst. +// The uncompressed size is returned as well. +// dst must have capacity to contain the entire compressed block. +func (l *LZ4sConverter) ConvertBlock(dst, src []byte) ([]byte, int, error) { + if len(src) == 0 { + return dst, 0, nil + } + const debug = false + const inline = true + const lz4MinMatch = 3 + + s, d := 0, len(dst) + dst = dst[:cap(dst)] + if !debug && hasAmd64Asm { + res, sz := cvtLZ4sBlockAsm(dst[d:], src) + if res < 0 { + const ( + errCorrupt = -1 + errDstTooSmall = -2 + ) + switch res { + case errCorrupt: + return nil, 0, ErrCorrupt + case errDstTooSmall: + return nil, 0, ErrDstTooSmall + default: + return nil, 0, fmt.Errorf("unexpected result: %d", res) + } + } + if d+sz > len(dst) { + return nil, 0, ErrDstTooSmall + } + return dst[:d+sz], res, nil + } + + dLimit := len(dst) - 10 + var lastOffset uint16 + var uncompressed int + if debug { + fmt.Printf("convert block start: len(src): %d, len(dst):%d \n", len(src), len(dst)) + } + + for { + if s >= len(src) { + return dst[:d], 0, ErrCorrupt + } + // Read literal info + token := src[s] + ll := int(token >> 4) + ml := int(lz4MinMatch + (token & 0xf)) + + // If upper nibble is 15, literal length is extended + if token >= 0xf0 { + for { + s++ + if s >= len(src) { + if debug { + fmt.Printf("error reading ll: s (%d) >= len(src) (%d)\n", s, len(src)) + } + return dst[:d], 0, ErrCorrupt + } + val := src[s] + ll += int(val) + if val != 255 { + break + } + } + } + // Skip past token + if s+ll >= len(src) { + if debug { + fmt.Printf("error literals: s+ll (%d+%d) >= len(src) (%d)\n", s, ll, len(src)) + } + return nil, 0, ErrCorrupt + } + s++ + if ll > 0 { + if d+ll > dLimit { + return nil, 0, ErrDstTooSmall + } + if debug { + fmt.Printf("emit %d literals\n", ll) + } + d += emitLiteralGo(dst[d:], src[s:s+ll]) + s += ll + uncompressed += ll + } + + // Check if we are done... + if ml == lz4MinMatch { + if s == len(src) { + break + } + // 0 bytes. + continue + } + // 2 byte offset + if s >= len(src)-2 { + if debug { + fmt.Printf("s (%d) >= len(src)-2 (%d)", s, len(src)-2) + } + return nil, 0, ErrCorrupt + } + offset := binary.LittleEndian.Uint16(src[s:]) + s += 2 + if offset == 0 { + if debug { + fmt.Printf("error: offset 0, ml: %d, len(src)-s: %d\n", ml, len(src)-s) + } + return nil, 0, ErrCorrupt + } + if int(offset) > uncompressed { + if debug { + fmt.Printf("error: offset (%d)> uncompressed (%d)\n", offset, uncompressed) + } + return nil, 0, ErrCorrupt + } + + if ml == lz4MinMatch+15 { + for { + if s >= len(src) { + if debug { + fmt.Printf("error reading ml: s (%d) >= len(src) (%d)\n", s, len(src)) + } + return nil, 0, ErrCorrupt + } + val := src[s] + s++ + ml += int(val) + if val != 255 { + if s >= len(src) { + if debug { + fmt.Printf("error reading ml: s (%d) >= len(src) (%d)\n", s, len(src)) + } + return nil, 0, ErrCorrupt + } + break + } + } + } + if offset == lastOffset { + if debug { + fmt.Printf("emit repeat, length: %d, offset: %d\n", ml, offset) + } + if !inline { + d += emitRepeat16(dst[d:], offset, ml) + } else { + length := ml + dst := dst[d:] + for len(dst) > 5 { + // Repeat offset, make length cheaper + length -= 4 + if length <= 4 { + dst[0] = uint8(length)<<2 | tagCopy1 + dst[1] = 0 + d += 2 + break + } + if length < 8 && offset < 2048 { + // Encode WITH offset + dst[1] = uint8(offset) + dst[0] = uint8(offset>>8)<<5 | uint8(length)<<2 | tagCopy1 + d += 2 + break + } + if length < (1<<8)+4 { + length -= 4 + dst[2] = uint8(length) + dst[1] = 0 + dst[0] = 5<<2 | tagCopy1 + d += 3 + break + } + if length < (1<<16)+(1<<8) { + length -= 1 << 8 + dst[3] = uint8(length >> 8) + dst[2] = uint8(length >> 0) + dst[1] = 0 + dst[0] = 6<<2 | tagCopy1 + d += 4 + break + } + const maxRepeat = (1 << 24) - 1 + length -= 1 << 16 + left := 0 + if length > maxRepeat { + left = length - maxRepeat + 4 + length = maxRepeat - 4 + } + dst[4] = uint8(length >> 16) + dst[3] = uint8(length >> 8) + dst[2] = uint8(length >> 0) + dst[1] = 0 + dst[0] = 7<<2 | tagCopy1 + if left > 0 { + d += 5 + emitRepeat16(dst[5:], offset, left) + break + } + d += 5 + break + } + } + } else { + if debug { + fmt.Printf("emit copy, length: %d, offset: %d\n", ml, offset) + } + if !inline { + d += emitCopy16(dst[d:], offset, ml) + } else { + length := ml + dst := dst[d:] + for len(dst) > 5 { + // Offset no more than 2 bytes. + if length > 64 { + off := 3 + if offset < 2048 { + // emit 8 bytes as tagCopy1, rest as repeats. + dst[1] = uint8(offset) + dst[0] = uint8(offset>>8)<<5 | uint8(8-4)<<2 | tagCopy1 + length -= 8 + off = 2 + } else { + // Emit a length 60 copy, encoded as 3 bytes. + // Emit remaining as repeat value (minimum 4 bytes). + dst[2] = uint8(offset >> 8) + dst[1] = uint8(offset) + dst[0] = 59<<2 | tagCopy2 + length -= 60 + } + // Emit remaining as repeats, at least 4 bytes remain. + d += off + emitRepeat16(dst[off:], offset, length) + break + } + if length >= 12 || offset >= 2048 { + // Emit the remaining copy, encoded as 3 bytes. + dst[2] = uint8(offset >> 8) + dst[1] = uint8(offset) + dst[0] = uint8(length-1)<<2 | tagCopy2 + d += 3 + break + } + // Emit the remaining copy, encoded as 2 bytes. + dst[1] = uint8(offset) + dst[0] = uint8(offset>>8)<<5 | uint8(length-4)<<2 | tagCopy1 + d += 2 + break + } + } + lastOffset = offset + } + uncompressed += ml + if d > dLimit { + return nil, 0, ErrDstTooSmall + } + } + + return dst[:d], uncompressed, nil +} + +// ConvertBlockSnappy will convert an LZ4s block and append it +// as a Snappy block without block length to dst. +// The uncompressed size is returned as well. +// dst must have capacity to contain the entire compressed block. +func (l *LZ4sConverter) ConvertBlockSnappy(dst, src []byte) ([]byte, int, error) { + if len(src) == 0 { + return dst, 0, nil + } + const debug = false + const lz4MinMatch = 3 + + s, d := 0, len(dst) + dst = dst[:cap(dst)] + // Use assembly when possible + if !debug && hasAmd64Asm { + res, sz := cvtLZ4sBlockSnappyAsm(dst[d:], src) + if res < 0 { + const ( + errCorrupt = -1 + errDstTooSmall = -2 + ) + switch res { + case errCorrupt: + return nil, 0, ErrCorrupt + case errDstTooSmall: + return nil, 0, ErrDstTooSmall + default: + return nil, 0, fmt.Errorf("unexpected result: %d", res) + } + } + if d+sz > len(dst) { + return nil, 0, ErrDstTooSmall + } + return dst[:d+sz], res, nil + } + + dLimit := len(dst) - 10 + var uncompressed int + if debug { + fmt.Printf("convert block start: len(src): %d, len(dst):%d \n", len(src), len(dst)) + } + + for { + if s >= len(src) { + return nil, 0, ErrCorrupt + } + // Read literal info + token := src[s] + ll := int(token >> 4) + ml := int(lz4MinMatch + (token & 0xf)) + + // If upper nibble is 15, literal length is extended + if token >= 0xf0 { + for { + s++ + if s >= len(src) { + if debug { + fmt.Printf("error reading ll: s (%d) >= len(src) (%d)\n", s, len(src)) + } + return nil, 0, ErrCorrupt + } + val := src[s] + ll += int(val) + if val != 255 { + break + } + } + } + // Skip past token + if s+ll >= len(src) { + if debug { + fmt.Printf("error literals: s+ll (%d+%d) >= len(src) (%d)\n", s, ll, len(src)) + } + return nil, 0, ErrCorrupt + } + s++ + if ll > 0 { + if d+ll > dLimit { + return nil, 0, ErrDstTooSmall + } + if debug { + fmt.Printf("emit %d literals\n", ll) + } + d += emitLiteralGo(dst[d:], src[s:s+ll]) + s += ll + uncompressed += ll + } + + // Check if we are done... + if ml == lz4MinMatch { + if s == len(src) { + break + } + // 0 bytes. + continue + } + // 2 byte offset + if s >= len(src)-2 { + if debug { + fmt.Printf("s (%d) >= len(src)-2 (%d)", s, len(src)-2) + } + return nil, 0, ErrCorrupt + } + offset := binary.LittleEndian.Uint16(src[s:]) + s += 2 + if offset == 0 { + if debug { + fmt.Printf("error: offset 0, ml: %d, len(src)-s: %d\n", ml, len(src)-s) + } + return nil, 0, ErrCorrupt + } + if int(offset) > uncompressed { + if debug { + fmt.Printf("error: offset (%d)> uncompressed (%d)\n", offset, uncompressed) + } + return nil, 0, ErrCorrupt + } + + if ml == lz4MinMatch+15 { + for { + if s >= len(src) { + if debug { + fmt.Printf("error reading ml: s (%d) >= len(src) (%d)\n", s, len(src)) + } + return nil, 0, ErrCorrupt + } + val := src[s] + s++ + ml += int(val) + if val != 255 { + if s >= len(src) { + if debug { + fmt.Printf("error reading ml: s (%d) >= len(src) (%d)\n", s, len(src)) + } + return nil, 0, ErrCorrupt + } + break + } + } + } + if debug { + fmt.Printf("emit copy, length: %d, offset: %d\n", ml, offset) + } + length := ml + // d += emitCopyNoRepeat(dst[d:], int(offset), ml) + for length > 0 { + if d >= dLimit { + return nil, 0, ErrDstTooSmall + } + + // Offset no more than 2 bytes. + if length > 64 { + // Emit a length 64 copy, encoded as 3 bytes. + dst[d+2] = uint8(offset >> 8) + dst[d+1] = uint8(offset) + dst[d+0] = 63<<2 | tagCopy2 + length -= 64 + d += 3 + continue + } + if length >= 12 || offset >= 2048 || length < 4 { + // Emit the remaining copy, encoded as 3 bytes. + dst[d+2] = uint8(offset >> 8) + dst[d+1] = uint8(offset) + dst[d+0] = uint8(length-1)<<2 | tagCopy2 + d += 3 + break + } + // Emit the remaining copy, encoded as 2 bytes. + dst[d+1] = uint8(offset) + dst[d+0] = uint8(offset>>8)<<5 | uint8(length-4)<<2 | tagCopy1 + d += 2 + break + } + uncompressed += ml + if d > dLimit { + return nil, 0, ErrDstTooSmall + } + } + + return dst[:d], uncompressed, nil +} diff --git a/vendor/github.com/klauspost/compress/s2/reader.go b/vendor/github.com/klauspost/compress/s2/reader.go new file mode 100644 index 000000000..4d01c4190 --- /dev/null +++ b/vendor/github.com/klauspost/compress/s2/reader.go @@ -0,0 +1,1075 @@ +// Copyright 2011 The Snappy-Go Authors. All rights reserved. +// Copyright (c) 2019+ Klaus Post. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package s2 + +import ( + "errors" + "fmt" + "io" + "io/ioutil" + "math" + "runtime" + "sync" +) + +// ErrCantSeek is returned if the stream cannot be seeked. +type ErrCantSeek struct { + Reason string +} + +// Error returns the error as string. +func (e ErrCantSeek) Error() string { + return fmt.Sprintf("s2: Can't seek because %s", e.Reason) +} + +// NewReader returns a new Reader that decompresses from r, using the framing +// format described at +// https://github.com/google/snappy/blob/master/framing_format.txt with S2 changes. +func NewReader(r io.Reader, opts ...ReaderOption) *Reader { + nr := Reader{ + r: r, + maxBlock: maxBlockSize, + } + for _, opt := range opts { + if err := opt(&nr); err != nil { + nr.err = err + return &nr + } + } + nr.maxBufSize = MaxEncodedLen(nr.maxBlock) + checksumSize + if nr.lazyBuf > 0 { + nr.buf = make([]byte, MaxEncodedLen(nr.lazyBuf)+checksumSize) + } else { + nr.buf = make([]byte, MaxEncodedLen(defaultBlockSize)+checksumSize) + } + nr.readHeader = nr.ignoreStreamID + nr.paramsOK = true + return &nr +} + +// ReaderOption is an option for creating a decoder. +type ReaderOption func(*Reader) error + +// ReaderMaxBlockSize allows to control allocations if the stream +// has been compressed with a smaller WriterBlockSize, or with the default 1MB. +// Blocks must be this size or smaller to decompress, +// otherwise the decoder will return ErrUnsupported. +// +// For streams compressed with Snappy this can safely be set to 64KB (64 << 10). +// +// Default is the maximum limit of 4MB. +func ReaderMaxBlockSize(blockSize int) ReaderOption { + return func(r *Reader) error { + if blockSize > maxBlockSize || blockSize <= 0 { + return errors.New("s2: block size too large. Must be <= 4MB and > 0") + } + if r.lazyBuf == 0 && blockSize < defaultBlockSize { + r.lazyBuf = blockSize + } + r.maxBlock = blockSize + return nil + } +} + +// ReaderAllocBlock allows to control upfront stream allocations +// and not allocate for frames bigger than this initially. +// If frames bigger than this is seen a bigger buffer will be allocated. +// +// Default is 1MB, which is default output size. +func ReaderAllocBlock(blockSize int) ReaderOption { + return func(r *Reader) error { + if blockSize > maxBlockSize || blockSize < 1024 { + return errors.New("s2: invalid ReaderAllocBlock. Must be <= 4MB and >= 1024") + } + r.lazyBuf = blockSize + return nil + } +} + +// ReaderIgnoreStreamIdentifier will make the reader skip the expected +// stream identifier at the beginning of the stream. +// This can be used when serving a stream that has been forwarded to a specific point. +func ReaderIgnoreStreamIdentifier() ReaderOption { + return func(r *Reader) error { + r.ignoreStreamID = true + return nil + } +} + +// ReaderSkippableCB will register a callback for chuncks with the specified ID. +// ID must be a Reserved skippable chunks ID, 0x80-0xfd (inclusive). +// For each chunk with the ID, the callback is called with the content. +// Any returned non-nil error will abort decompression. +// Only one callback per ID is supported, latest sent will be used. +// You can peek the stream, triggering the callback, by doing a Read with a 0 +// byte buffer. +func ReaderSkippableCB(id uint8, fn func(r io.Reader) error) ReaderOption { + return func(r *Reader) error { + if id < 0x80 || id > 0xfd { + return fmt.Errorf("ReaderSkippableCB: Invalid id provided, must be 0x80-0xfd (inclusive)") + } + r.skippableCB[id-0x80] = fn + return nil + } +} + +// ReaderIgnoreCRC will make the reader skip CRC calculation and checks. +func ReaderIgnoreCRC() ReaderOption { + return func(r *Reader) error { + r.ignoreCRC = true + return nil + } +} + +// Reader is an io.Reader that can read Snappy-compressed bytes. +type Reader struct { + r io.Reader + err error + decoded []byte + buf []byte + skippableCB [0xff - 0x80]func(r io.Reader) error + blockStart int64 // Uncompressed offset at start of current. + index *Index + + // decoded[i:j] contains decoded bytes that have not yet been passed on. + i, j int + // maximum block size allowed. + maxBlock int + // maximum expected buffer size. + maxBufSize int + // alloc a buffer this size if > 0. + lazyBuf int + readHeader bool + paramsOK bool + snappyFrame bool + ignoreStreamID bool + ignoreCRC bool +} + +// GetBufferCapacity returns the capacity of the internal buffer. +// This might be useful to know when reusing the same reader in combination +// with the lazy buffer option. +func (r *Reader) GetBufferCapacity() int { + return cap(r.buf) +} + +// ensureBufferSize will ensure that the buffer can take at least n bytes. +// If false is returned the buffer exceeds maximum allowed size. +func (r *Reader) ensureBufferSize(n int) bool { + if n > r.maxBufSize { + r.err = ErrCorrupt + return false + } + if cap(r.buf) >= n { + return true + } + // Realloc buffer. + r.buf = make([]byte, n) + return true +} + +// Reset discards any buffered data, resets all state, and switches the Snappy +// reader to read from r. This permits reusing a Reader rather than allocating +// a new one. +func (r *Reader) Reset(reader io.Reader) { + if !r.paramsOK { + return + } + r.index = nil + r.r = reader + r.err = nil + r.i = 0 + r.j = 0 + r.blockStart = 0 + r.readHeader = r.ignoreStreamID +} + +func (r *Reader) readFull(p []byte, allowEOF bool) (ok bool) { + if _, r.err = io.ReadFull(r.r, p); r.err != nil { + if r.err == io.ErrUnexpectedEOF || (r.err == io.EOF && !allowEOF) { + r.err = ErrCorrupt + } + return false + } + return true +} + +// skippable will skip n bytes. +// If the supplied reader supports seeking that is used. +// tmp is used as a temporary buffer for reading. +// The supplied slice does not need to be the size of the read. +func (r *Reader) skippable(tmp []byte, n int, allowEOF bool, id uint8) (ok bool) { + if id < 0x80 { + r.err = fmt.Errorf("internal error: skippable id < 0x80") + return false + } + if fn := r.skippableCB[id-0x80]; fn != nil { + rd := io.LimitReader(r.r, int64(n)) + r.err = fn(rd) + if r.err != nil { + return false + } + _, r.err = io.CopyBuffer(ioutil.Discard, rd, tmp) + return r.err == nil + } + if rs, ok := r.r.(io.ReadSeeker); ok { + _, err := rs.Seek(int64(n), io.SeekCurrent) + if err == nil { + return true + } + if err == io.ErrUnexpectedEOF || (r.err == io.EOF && !allowEOF) { + r.err = ErrCorrupt + return false + } + } + for n > 0 { + if n < len(tmp) { + tmp = tmp[:n] + } + if _, r.err = io.ReadFull(r.r, tmp); r.err != nil { + if r.err == io.ErrUnexpectedEOF || (r.err == io.EOF && !allowEOF) { + r.err = ErrCorrupt + } + return false + } + n -= len(tmp) + } + return true +} + +// Read satisfies the io.Reader interface. +func (r *Reader) Read(p []byte) (int, error) { + if r.err != nil { + return 0, r.err + } + for { + if r.i < r.j { + n := copy(p, r.decoded[r.i:r.j]) + r.i += n + return n, nil + } + if !r.readFull(r.buf[:4], true) { + return 0, r.err + } + chunkType := r.buf[0] + if !r.readHeader { + if chunkType != chunkTypeStreamIdentifier { + r.err = ErrCorrupt + return 0, r.err + } + r.readHeader = true + } + chunkLen := int(r.buf[1]) | int(r.buf[2])<<8 | int(r.buf[3])<<16 + + // The chunk types are specified at + // https://github.com/google/snappy/blob/master/framing_format.txt + switch chunkType { + case chunkTypeCompressedData: + r.blockStart += int64(r.j) + // Section 4.2. Compressed data (chunk type 0x00). + if chunkLen < checksumSize { + r.err = ErrCorrupt + return 0, r.err + } + if !r.ensureBufferSize(chunkLen) { + if r.err == nil { + r.err = ErrUnsupported + } + return 0, r.err + } + buf := r.buf[:chunkLen] + if !r.readFull(buf, false) { + return 0, r.err + } + checksum := uint32(buf[0]) | uint32(buf[1])<<8 | uint32(buf[2])<<16 | uint32(buf[3])<<24 + buf = buf[checksumSize:] + + n, err := DecodedLen(buf) + if err != nil { + r.err = err + return 0, r.err + } + if r.snappyFrame && n > maxSnappyBlockSize { + r.err = ErrCorrupt + return 0, r.err + } + + if n > len(r.decoded) { + if n > r.maxBlock { + r.err = ErrCorrupt + return 0, r.err + } + r.decoded = make([]byte, n) + } + if _, err := Decode(r.decoded, buf); err != nil { + r.err = err + return 0, r.err + } + if !r.ignoreCRC && crc(r.decoded[:n]) != checksum { + r.err = ErrCRC + return 0, r.err + } + r.i, r.j = 0, n + continue + + case chunkTypeUncompressedData: + r.blockStart += int64(r.j) + // Section 4.3. Uncompressed data (chunk type 0x01). + if chunkLen < checksumSize { + r.err = ErrCorrupt + return 0, r.err + } + if !r.ensureBufferSize(chunkLen) { + if r.err == nil { + r.err = ErrUnsupported + } + return 0, r.err + } + buf := r.buf[:checksumSize] + if !r.readFull(buf, false) { + return 0, r.err + } + checksum := uint32(buf[0]) | uint32(buf[1])<<8 | uint32(buf[2])<<16 | uint32(buf[3])<<24 + // Read directly into r.decoded instead of via r.buf. + n := chunkLen - checksumSize + if r.snappyFrame && n > maxSnappyBlockSize { + r.err = ErrCorrupt + return 0, r.err + } + if n > len(r.decoded) { + if n > r.maxBlock { + r.err = ErrCorrupt + return 0, r.err + } + r.decoded = make([]byte, n) + } + if !r.readFull(r.decoded[:n], false) { + return 0, r.err + } + if !r.ignoreCRC && crc(r.decoded[:n]) != checksum { + r.err = ErrCRC + return 0, r.err + } + r.i, r.j = 0, n + continue + + case chunkTypeStreamIdentifier: + // Section 4.1. Stream identifier (chunk type 0xff). + if chunkLen != len(magicBody) { + r.err = ErrCorrupt + return 0, r.err + } + if !r.readFull(r.buf[:len(magicBody)], false) { + return 0, r.err + } + if string(r.buf[:len(magicBody)]) != magicBody { + if string(r.buf[:len(magicBody)]) != magicBodySnappy { + r.err = ErrCorrupt + return 0, r.err + } else { + r.snappyFrame = true + } + } else { + r.snappyFrame = false + } + continue + } + + if chunkType <= 0x7f { + // Section 4.5. Reserved unskippable chunks (chunk types 0x02-0x7f). + // fmt.Printf("ERR chunktype: 0x%x\n", chunkType) + r.err = ErrUnsupported + return 0, r.err + } + // Section 4.4 Padding (chunk type 0xfe). + // Section 4.6. Reserved skippable chunks (chunk types 0x80-0xfd). + if chunkLen > maxChunkSize { + // fmt.Printf("ERR chunkLen: 0x%x\n", chunkLen) + r.err = ErrUnsupported + return 0, r.err + } + + // fmt.Printf("skippable: ID: 0x%x, len: 0x%x\n", chunkType, chunkLen) + if !r.skippable(r.buf, chunkLen, false, chunkType) { + return 0, r.err + } + } +} + +// DecodeConcurrent will decode the full stream to w. +// This function should not be combined with reading, seeking or other operations. +// Up to 'concurrent' goroutines will be used. +// If <= 0, runtime.NumCPU will be used. +// On success the number of bytes decompressed nil and is returned. +// This is mainly intended for bigger streams. +func (r *Reader) DecodeConcurrent(w io.Writer, concurrent int) (written int64, err error) { + if r.i > 0 || r.j > 0 || r.blockStart > 0 { + return 0, errors.New("DecodeConcurrent called after ") + } + if concurrent <= 0 { + concurrent = runtime.NumCPU() + } + + // Write to output + var errMu sync.Mutex + var aErr error + setErr := func(e error) (ok bool) { + errMu.Lock() + defer errMu.Unlock() + if e == nil { + return aErr == nil + } + if aErr == nil { + aErr = e + } + return false + } + hasErr := func() (ok bool) { + errMu.Lock() + v := aErr != nil + errMu.Unlock() + return v + } + + var aWritten int64 + toRead := make(chan []byte, concurrent) + writtenBlocks := make(chan []byte, concurrent) + queue := make(chan chan []byte, concurrent) + reUse := make(chan chan []byte, concurrent) + for i := 0; i < concurrent; i++ { + toRead <- make([]byte, 0, r.maxBufSize) + writtenBlocks <- make([]byte, 0, r.maxBufSize) + reUse <- make(chan []byte, 1) + } + // Writer + var wg sync.WaitGroup + wg.Add(1) + go func() { + defer wg.Done() + for toWrite := range queue { + entry := <-toWrite + reUse <- toWrite + if hasErr() || entry == nil { + if entry != nil { + writtenBlocks <- entry + } + continue + } + if hasErr() { + writtenBlocks <- entry + continue + } + n, err := w.Write(entry) + want := len(entry) + writtenBlocks <- entry + if err != nil { + setErr(err) + continue + } + if n != want { + setErr(io.ErrShortWrite) + continue + } + aWritten += int64(n) + } + }() + + defer func() { + if r.err != nil { + setErr(r.err) + } else if err != nil { + setErr(err) + } + close(queue) + wg.Wait() + if err == nil { + err = aErr + } + written = aWritten + }() + + // Reader + for !hasErr() { + if !r.readFull(r.buf[:4], true) { + if r.err == io.EOF { + r.err = nil + } + return 0, r.err + } + chunkType := r.buf[0] + if !r.readHeader { + if chunkType != chunkTypeStreamIdentifier { + r.err = ErrCorrupt + return 0, r.err + } + r.readHeader = true + } + chunkLen := int(r.buf[1]) | int(r.buf[2])<<8 | int(r.buf[3])<<16 + + // The chunk types are specified at + // https://github.com/google/snappy/blob/master/framing_format.txt + switch chunkType { + case chunkTypeCompressedData: + r.blockStart += int64(r.j) + // Section 4.2. Compressed data (chunk type 0x00). + if chunkLen < checksumSize { + r.err = ErrCorrupt + return 0, r.err + } + if chunkLen > r.maxBufSize { + r.err = ErrCorrupt + return 0, r.err + } + orgBuf := <-toRead + buf := orgBuf[:chunkLen] + + if !r.readFull(buf, false) { + return 0, r.err + } + + checksum := uint32(buf[0]) | uint32(buf[1])<<8 | uint32(buf[2])<<16 | uint32(buf[3])<<24 + buf = buf[checksumSize:] + + n, err := DecodedLen(buf) + if err != nil { + r.err = err + return 0, r.err + } + if r.snappyFrame && n > maxSnappyBlockSize { + r.err = ErrCorrupt + return 0, r.err + } + + if n > r.maxBlock { + r.err = ErrCorrupt + return 0, r.err + } + wg.Add(1) + + decoded := <-writtenBlocks + entry := <-reUse + queue <- entry + go func() { + defer wg.Done() + decoded = decoded[:n] + _, err := Decode(decoded, buf) + toRead <- orgBuf + if err != nil { + writtenBlocks <- decoded + setErr(err) + entry <- nil + return + } + if !r.ignoreCRC && crc(decoded) != checksum { + writtenBlocks <- decoded + setErr(ErrCRC) + entry <- nil + return + } + entry <- decoded + }() + continue + + case chunkTypeUncompressedData: + + // Section 4.3. Uncompressed data (chunk type 0x01). + if chunkLen < checksumSize { + r.err = ErrCorrupt + return 0, r.err + } + if chunkLen > r.maxBufSize { + r.err = ErrCorrupt + return 0, r.err + } + // Grab write buffer + orgBuf := <-writtenBlocks + buf := orgBuf[:checksumSize] + if !r.readFull(buf, false) { + return 0, r.err + } + checksum := uint32(buf[0]) | uint32(buf[1])<<8 | uint32(buf[2])<<16 | uint32(buf[3])<<24 + // Read content. + n := chunkLen - checksumSize + + if r.snappyFrame && n > maxSnappyBlockSize { + r.err = ErrCorrupt + return 0, r.err + } + if n > r.maxBlock { + r.err = ErrCorrupt + return 0, r.err + } + // Read uncompressed + buf = orgBuf[:n] + if !r.readFull(buf, false) { + return 0, r.err + } + + if !r.ignoreCRC && crc(buf) != checksum { + r.err = ErrCRC + return 0, r.err + } + entry := <-reUse + queue <- entry + entry <- buf + continue + + case chunkTypeStreamIdentifier: + // Section 4.1. Stream identifier (chunk type 0xff). + if chunkLen != len(magicBody) { + r.err = ErrCorrupt + return 0, r.err + } + if !r.readFull(r.buf[:len(magicBody)], false) { + return 0, r.err + } + if string(r.buf[:len(magicBody)]) != magicBody { + if string(r.buf[:len(magicBody)]) != magicBodySnappy { + r.err = ErrCorrupt + return 0, r.err + } else { + r.snappyFrame = true + } + } else { + r.snappyFrame = false + } + continue + } + + if chunkType <= 0x7f { + // Section 4.5. Reserved unskippable chunks (chunk types 0x02-0x7f). + // fmt.Printf("ERR chunktype: 0x%x\n", chunkType) + r.err = ErrUnsupported + return 0, r.err + } + // Section 4.4 Padding (chunk type 0xfe). + // Section 4.6. Reserved skippable chunks (chunk types 0x80-0xfd). + if chunkLen > maxChunkSize { + // fmt.Printf("ERR chunkLen: 0x%x\n", chunkLen) + r.err = ErrUnsupported + return 0, r.err + } + + // fmt.Printf("skippable: ID: 0x%x, len: 0x%x\n", chunkType, chunkLen) + if !r.skippable(r.buf, chunkLen, false, chunkType) { + return 0, r.err + } + } + return 0, r.err +} + +// Skip will skip n bytes forward in the decompressed output. +// For larger skips this consumes less CPU and is faster than reading output and discarding it. +// CRC is not checked on skipped blocks. +// io.ErrUnexpectedEOF is returned if the stream ends before all bytes have been skipped. +// If a decoding error is encountered subsequent calls to Read will also fail. +func (r *Reader) Skip(n int64) error { + if n < 0 { + return errors.New("attempted negative skip") + } + if r.err != nil { + return r.err + } + + for n > 0 { + if r.i < r.j { + // Skip in buffer. + // decoded[i:j] contains decoded bytes that have not yet been passed on. + left := int64(r.j - r.i) + if left >= n { + tmp := int64(r.i) + n + if tmp > math.MaxInt32 { + return errors.New("s2: internal overflow in skip") + } + r.i = int(tmp) + return nil + } + n -= int64(r.j - r.i) + r.i = r.j + } + + // Buffer empty; read blocks until we have content. + if !r.readFull(r.buf[:4], true) { + if r.err == io.EOF { + r.err = io.ErrUnexpectedEOF + } + return r.err + } + chunkType := r.buf[0] + if !r.readHeader { + if chunkType != chunkTypeStreamIdentifier { + r.err = ErrCorrupt + return r.err + } + r.readHeader = true + } + chunkLen := int(r.buf[1]) | int(r.buf[2])<<8 | int(r.buf[3])<<16 + + // The chunk types are specified at + // https://github.com/google/snappy/blob/master/framing_format.txt + switch chunkType { + case chunkTypeCompressedData: + r.blockStart += int64(r.j) + // Section 4.2. Compressed data (chunk type 0x00). + if chunkLen < checksumSize { + r.err = ErrCorrupt + return r.err + } + if !r.ensureBufferSize(chunkLen) { + if r.err == nil { + r.err = ErrUnsupported + } + return r.err + } + buf := r.buf[:chunkLen] + if !r.readFull(buf, false) { + return r.err + } + checksum := uint32(buf[0]) | uint32(buf[1])<<8 | uint32(buf[2])<<16 | uint32(buf[3])<<24 + buf = buf[checksumSize:] + + dLen, err := DecodedLen(buf) + if err != nil { + r.err = err + return r.err + } + if dLen > r.maxBlock { + r.err = ErrCorrupt + return r.err + } + // Check if destination is within this block + if int64(dLen) > n { + if len(r.decoded) < dLen { + r.decoded = make([]byte, dLen) + } + if _, err := Decode(r.decoded, buf); err != nil { + r.err = err + return r.err + } + if crc(r.decoded[:dLen]) != checksum { + r.err = ErrCorrupt + return r.err + } + } else { + // Skip block completely + n -= int64(dLen) + r.blockStart += int64(dLen) + dLen = 0 + } + r.i, r.j = 0, dLen + continue + case chunkTypeUncompressedData: + r.blockStart += int64(r.j) + // Section 4.3. Uncompressed data (chunk type 0x01). + if chunkLen < checksumSize { + r.err = ErrCorrupt + return r.err + } + if !r.ensureBufferSize(chunkLen) { + if r.err != nil { + r.err = ErrUnsupported + } + return r.err + } + buf := r.buf[:checksumSize] + if !r.readFull(buf, false) { + return r.err + } + checksum := uint32(buf[0]) | uint32(buf[1])<<8 | uint32(buf[2])<<16 | uint32(buf[3])<<24 + // Read directly into r.decoded instead of via r.buf. + n2 := chunkLen - checksumSize + if n2 > len(r.decoded) { + if n2 > r.maxBlock { + r.err = ErrCorrupt + return r.err + } + r.decoded = make([]byte, n2) + } + if !r.readFull(r.decoded[:n2], false) { + return r.err + } + if int64(n2) < n { + if crc(r.decoded[:n2]) != checksum { + r.err = ErrCorrupt + return r.err + } + } + r.i, r.j = 0, n2 + continue + case chunkTypeStreamIdentifier: + // Section 4.1. Stream identifier (chunk type 0xff). + if chunkLen != len(magicBody) { + r.err = ErrCorrupt + return r.err + } + if !r.readFull(r.buf[:len(magicBody)], false) { + return r.err + } + if string(r.buf[:len(magicBody)]) != magicBody { + if string(r.buf[:len(magicBody)]) != magicBodySnappy { + r.err = ErrCorrupt + return r.err + } + } + + continue + } + + if chunkType <= 0x7f { + // Section 4.5. Reserved unskippable chunks (chunk types 0x02-0x7f). + r.err = ErrUnsupported + return r.err + } + if chunkLen > maxChunkSize { + r.err = ErrUnsupported + return r.err + } + // Section 4.4 Padding (chunk type 0xfe). + // Section 4.6. Reserved skippable chunks (chunk types 0x80-0xfd). + if !r.skippable(r.buf, chunkLen, false, chunkType) { + return r.err + } + } + return nil +} + +// ReadSeeker provides random or forward seeking in compressed content. +// See Reader.ReadSeeker +type ReadSeeker struct { + *Reader + readAtMu sync.Mutex +} + +// ReadSeeker will return an io.ReadSeeker and io.ReaderAt +// compatible version of the reader. +// If 'random' is specified the returned io.Seeker can be used for +// random seeking, otherwise only forward seeking is supported. +// Enabling random seeking requires the original input to support +// the io.Seeker interface. +// A custom index can be specified which will be used if supplied. +// When using a custom index, it will not be read from the input stream. +// The ReadAt position will affect regular reads and the current position of Seek. +// So using Read after ReadAt will continue from where the ReadAt stopped. +// No functions should be used concurrently. +// The returned ReadSeeker contains a shallow reference to the existing Reader, +// meaning changes performed to one is reflected in the other. +func (r *Reader) ReadSeeker(random bool, index []byte) (*ReadSeeker, error) { + // Read index if provided. + if len(index) != 0 { + if r.index == nil { + r.index = &Index{} + } + if _, err := r.index.Load(index); err != nil { + return nil, ErrCantSeek{Reason: "loading index returned: " + err.Error()} + } + } + + // Check if input is seekable + rs, ok := r.r.(io.ReadSeeker) + if !ok { + if !random { + return &ReadSeeker{Reader: r}, nil + } + return nil, ErrCantSeek{Reason: "input stream isn't seekable"} + } + + if r.index != nil { + // Seekable and index, ok... + return &ReadSeeker{Reader: r}, nil + } + + // Load from stream. + r.index = &Index{} + + // Read current position. + pos, err := rs.Seek(0, io.SeekCurrent) + if err != nil { + return nil, ErrCantSeek{Reason: "seeking input returned: " + err.Error()} + } + err = r.index.LoadStream(rs) + if err != nil { + if err == ErrUnsupported { + // If we don't require random seeking, reset input and return. + if !random { + _, err = rs.Seek(pos, io.SeekStart) + if err != nil { + return nil, ErrCantSeek{Reason: "resetting stream returned: " + err.Error()} + } + r.index = nil + return &ReadSeeker{Reader: r}, nil + } + return nil, ErrCantSeek{Reason: "input stream does not contain an index"} + } + return nil, ErrCantSeek{Reason: "reading index returned: " + err.Error()} + } + + // reset position. + _, err = rs.Seek(pos, io.SeekStart) + if err != nil { + return nil, ErrCantSeek{Reason: "seeking input returned: " + err.Error()} + } + return &ReadSeeker{Reader: r}, nil +} + +// Seek allows seeking in compressed data. +func (r *ReadSeeker) Seek(offset int64, whence int) (int64, error) { + if r.err != nil { + if !errors.Is(r.err, io.EOF) { + return 0, r.err + } + // Reset on EOF + r.err = nil + } + + // Calculate absolute offset. + absOffset := offset + + switch whence { + case io.SeekStart: + case io.SeekCurrent: + absOffset = r.blockStart + int64(r.i) + offset + case io.SeekEnd: + if r.index == nil { + return 0, ErrUnsupported + } + absOffset = r.index.TotalUncompressed + offset + default: + r.err = ErrUnsupported + return 0, r.err + } + + if absOffset < 0 { + return 0, errors.New("seek before start of file") + } + + if !r.readHeader { + // Make sure we read the header. + _, r.err = r.Read([]byte{}) + if r.err != nil { + return 0, r.err + } + } + + // If we are inside current block no need to seek. + // This includes no offset changes. + if absOffset >= r.blockStart && absOffset < r.blockStart+int64(r.j) { + r.i = int(absOffset - r.blockStart) + return r.blockStart + int64(r.i), nil + } + + rs, ok := r.r.(io.ReadSeeker) + if r.index == nil || !ok { + currOffset := r.blockStart + int64(r.i) + if absOffset >= currOffset { + err := r.Skip(absOffset - currOffset) + return r.blockStart + int64(r.i), err + } + return 0, ErrUnsupported + } + + // We can seek and we have an index. + c, u, err := r.index.Find(absOffset) + if err != nil { + return r.blockStart + int64(r.i), err + } + + // Seek to next block + _, err = rs.Seek(c, io.SeekStart) + if err != nil { + return 0, err + } + + r.i = r.j // Remove rest of current block. + r.blockStart = u - int64(r.j) // Adjust current block start for accounting. + if u < absOffset { + // Forward inside block + return absOffset, r.Skip(absOffset - u) + } + if u > absOffset { + return 0, fmt.Errorf("s2 seek: (internal error) u (%d) > absOffset (%d)", u, absOffset) + } + return absOffset, nil +} + +// ReadAt reads len(p) bytes into p starting at offset off in the +// underlying input source. It returns the number of bytes +// read (0 <= n <= len(p)) and any error encountered. +// +// When ReadAt returns n < len(p), it returns a non-nil error +// explaining why more bytes were not returned. In this respect, +// ReadAt is stricter than Read. +// +// Even if ReadAt returns n < len(p), it may use all of p as scratch +// space during the call. If some data is available but not len(p) bytes, +// ReadAt blocks until either all the data is available or an error occurs. +// In this respect ReadAt is different from Read. +// +// If the n = len(p) bytes returned by ReadAt are at the end of the +// input source, ReadAt may return either err == EOF or err == nil. +// +// If ReadAt is reading from an input source with a seek offset, +// ReadAt should not affect nor be affected by the underlying +// seek offset. +// +// Clients of ReadAt can execute parallel ReadAt calls on the +// same input source. This is however not recommended. +func (r *ReadSeeker) ReadAt(p []byte, offset int64) (int, error) { + r.readAtMu.Lock() + defer r.readAtMu.Unlock() + _, err := r.Seek(offset, io.SeekStart) + if err != nil { + return 0, err + } + n := 0 + for n < len(p) { + n2, err := r.Read(p[n:]) + if err != nil { + // This will include io.EOF + return n + n2, err + } + n += n2 + } + return n, nil +} + +// ReadByte satisfies the io.ByteReader interface. +func (r *Reader) ReadByte() (byte, error) { + if r.err != nil { + return 0, r.err + } + if r.i < r.j { + c := r.decoded[r.i] + r.i++ + return c, nil + } + var tmp [1]byte + for range 10 { + n, err := r.Read(tmp[:]) + if err != nil { + return 0, err + } + if n == 1 { + return tmp[0], nil + } + } + return 0, io.ErrNoProgress +} + +// SkippableCB will register a callback for chunks with the specified ID. +// ID must be a Reserved skippable chunks ID, 0x80-0xfd (inclusive). +// For each chunk with the ID, the callback is called with the content. +// Any returned non-nil error will abort decompression. +// Only one callback per ID is supported, latest sent will be used. +// Sending a nil function will disable previous callbacks. +// You can peek the stream, triggering the callback, by doing a Read with a 0 +// byte buffer. +func (r *Reader) SkippableCB(id uint8, fn func(r io.Reader) error) error { + if id < 0x80 || id >= chunkTypePadding { + return fmt.Errorf("ReaderSkippableCB: Invalid id provided, must be 0x80-0xfe (inclusive)") + } + r.skippableCB[id-0x80] = fn + return nil +} diff --git a/vendor/github.com/klauspost/compress/s2/s2.go b/vendor/github.com/klauspost/compress/s2/s2.go new file mode 100644 index 000000000..cbd1ed64d --- /dev/null +++ b/vendor/github.com/klauspost/compress/s2/s2.go @@ -0,0 +1,151 @@ +// Copyright 2011 The Snappy-Go Authors. All rights reserved. +// Copyright (c) 2019 Klaus Post. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package s2 implements the S2 compression format. +// +// S2 is an extension of Snappy. Similar to Snappy S2 is aimed for high throughput, +// which is why it features concurrent compression for bigger payloads. +// +// Decoding is compatible with Snappy compressed content, +// but content compressed with S2 cannot be decompressed by Snappy. +// +// For more information on Snappy/S2 differences see README in: https://github.com/klauspost/compress/tree/master/s2 +// +// There are actually two S2 formats: block and stream. They are related, +// but different: trying to decompress block-compressed data as a S2 stream +// will fail, and vice versa. The block format is the Decode and Encode +// functions and the stream format is the Reader and Writer types. +// +// A "better" compression option is available. This will trade some compression +// speed +// +// The block format, the more common case, is used when the complete size (the +// number of bytes) of the original data is known upfront, at the time +// compression starts. The stream format, also known as the framing format, is +// for when that isn't always true. +// +// Blocks to not offer much data protection, so it is up to you to +// add data validation of decompressed blocks. +// +// Streams perform CRC validation of the decompressed data. +// Stream compression will also be performed on multiple CPU cores concurrently +// significantly improving throughput. +package s2 + +import ( + "bytes" + "hash/crc32" + + "github.com/klauspost/compress/internal/race" +) + +/* +Each encoded block begins with the varint-encoded length of the decoded data, +followed by a sequence of chunks. Chunks begin and end on byte boundaries. The +first byte of each chunk is broken into its 2 least and 6 most significant bits +called l and m: l ranges in [0, 4) and m ranges in [0, 64). l is the chunk tag. +Zero means a literal tag. All other values mean a copy tag. + +For literal tags: + - If m < 60, the next 1 + m bytes are literal bytes. + - Otherwise, let n be the little-endian unsigned integer denoted by the next + m - 59 bytes. The next 1 + n bytes after that are literal bytes. + +For copy tags, length bytes are copied from offset bytes ago, in the style of +Lempel-Ziv compression algorithms. In particular: + - For l == 1, the offset ranges in [0, 1<<11) and the length in [4, 12). + The length is 4 + the low 3 bits of m. The high 3 bits of m form bits 8-10 + of the offset. The next byte is bits 0-7 of the offset. + - For l == 2, the offset ranges in [0, 1<<16) and the length in [1, 65). + The length is 1 + m. The offset is the little-endian unsigned integer + denoted by the next 2 bytes. + - For l == 3, the offset ranges in [0, 1<<32) and the length in + [1, 65). The length is 1 + m. The offset is the little-endian unsigned + integer denoted by the next 4 bytes. +*/ +const ( + tagLiteral = 0x00 + tagCopy1 = 0x01 + tagCopy2 = 0x02 + tagCopy4 = 0x03 +) + +const ( + checksumSize = 4 + chunkHeaderSize = 4 + magicChunk = "\xff\x06\x00\x00" + magicBody + magicChunkSnappy = "\xff\x06\x00\x00" + magicBodySnappy + magicBodySnappy = "sNaPpY" + magicBody = "S2sTwO" + + // maxBlockSize is the maximum size of the input to encodeBlock. + // + // For the framing format (Writer type instead of Encode function), + // this is the maximum uncompressed size of a block. + maxBlockSize = 4 << 20 + + // minBlockSize is the minimum size of block setting when creating a writer. + minBlockSize = 4 << 10 + + skippableFrameHeader = 4 + maxChunkSize = 1<<24 - 1 // 16777215 + + // Default block size + defaultBlockSize = 1 << 20 + + // maxSnappyBlockSize is the maximum snappy block size. + maxSnappyBlockSize = 1 << 16 + + obufHeaderLen = checksumSize + chunkHeaderSize +) + +const ( + chunkTypeCompressedData = 0x00 + chunkTypeUncompressedData = 0x01 + ChunkTypeIndex = 0x99 + chunkTypePadding = 0xfe + chunkTypeStreamIdentifier = 0xff +) + +var ( + crcTable = crc32.MakeTable(crc32.Castagnoli) + magicChunkSnappyBytes = []byte(magicChunkSnappy) // Can be passed to functions where it escapes. + magicChunkBytes = []byte(magicChunk) // Can be passed to functions where it escapes. +) + +// crc implements the checksum specified in section 3 of +// https://github.com/google/snappy/blob/master/framing_format.txt +func crc(b []byte) uint32 { + race.ReadSlice(b) + + c := crc32.Update(0, crcTable, b) + return c>>15 | c<<17 + 0xa282ead8 +} + +// literalExtraSize returns the extra size of encoding n literals. +// n should be >= 0 and <= math.MaxUint32. +func literalExtraSize(n int64) int64 { + if n == 0 { + return 0 + } + switch { + case n < 60: + return 1 + case n < 1<<8: + return 2 + case n < 1<<16: + return 3 + case n < 1<<24: + return 4 + default: + return 5 + } +} + +type byter interface { + Bytes() []byte +} + +var _ byter = &bytes.Buffer{} diff --git a/vendor/github.com/klauspost/compress/s2/writer.go b/vendor/github.com/klauspost/compress/s2/writer.go new file mode 100644 index 000000000..09f1cff3a --- /dev/null +++ b/vendor/github.com/klauspost/compress/s2/writer.go @@ -0,0 +1,1064 @@ +// Copyright 2011 The Snappy-Go Authors. All rights reserved. +// Copyright (c) 2019+ Klaus Post. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package s2 + +import ( + "crypto/rand" + "encoding/binary" + "errors" + "fmt" + "io" + "runtime" + "sync" + + "github.com/klauspost/compress/internal/race" +) + +const ( + levelUncompressed = iota + 1 + levelFast + levelBetter + levelBest +) + +// NewWriter returns a new Writer that compresses to w, using the +// framing format described at +// https://github.com/google/snappy/blob/master/framing_format.txt +// +// Users must call Close to guarantee all data has been forwarded to +// the underlying io.Writer and that resources are released. +// They may also call Flush zero or more times before calling Close. +func NewWriter(w io.Writer, opts ...WriterOption) *Writer { + w2 := Writer{ + blockSize: defaultBlockSize, + concurrency: runtime.GOMAXPROCS(0), + randSrc: rand.Reader, + level: levelFast, + } + for _, opt := range opts { + if err := opt(&w2); err != nil { + w2.errState = err + return &w2 + } + } + w2.obufLen = obufHeaderLen + MaxEncodedLen(w2.blockSize) + w2.paramsOK = true + w2.ibuf = make([]byte, 0, w2.blockSize) + w2.buffers.New = func() any { + return make([]byte, w2.obufLen) + } + w2.Reset(w) + return &w2 +} + +// Writer is an io.Writer that can write Snappy-compressed bytes. +type Writer struct { + errMu sync.Mutex + errState error + + // ibuf is a buffer for the incoming (uncompressed) bytes. + ibuf []byte + + blockSize int + obufLen int + concurrency int + written int64 + uncompWritten int64 // Bytes sent to compression + output chan chan result + buffers sync.Pool + pad int + + writer io.Writer + randSrc io.Reader + writerWg sync.WaitGroup + index Index + customEnc func(dst, src []byte) int + + // wroteStreamHeader is whether we have written the stream header. + wroteStreamHeader bool + paramsOK bool + snappy bool + flushOnWrite bool + appendIndex bool + bufferCB func([]byte) + level uint8 +} + +type result struct { + b []byte + // return when writing + ret []byte + // Uncompressed start offset + startOffset int64 +} + +// err returns the previously set error. +// If no error has been set it is set to err if not nil. +func (w *Writer) err(err error) error { + w.errMu.Lock() + errSet := w.errState + if errSet == nil && err != nil { + w.errState = err + errSet = err + } + w.errMu.Unlock() + return errSet +} + +// Reset discards the writer's state and switches the Snappy writer to write to w. +// This permits reusing a Writer rather than allocating a new one. +func (w *Writer) Reset(writer io.Writer) { + if !w.paramsOK { + return + } + // Close previous writer, if any. + if w.output != nil { + close(w.output) + w.writerWg.Wait() + w.output = nil + } + w.errState = nil + w.ibuf = w.ibuf[:0] + w.wroteStreamHeader = false + w.written = 0 + w.writer = writer + w.uncompWritten = 0 + w.index.reset(w.blockSize) + + // If we didn't get a writer, stop here. + if writer == nil { + return + } + // If no concurrency requested, don't spin up writer goroutine. + if w.concurrency == 1 { + return + } + + toWrite := make(chan chan result, w.concurrency) + w.output = toWrite + w.writerWg.Add(1) + + // Start a writer goroutine that will write all output in order. + go func() { + defer w.writerWg.Done() + + // Get a queued write. + for write := range toWrite { + // Wait for the data to be available. + input := <-write + if input.ret != nil && w.bufferCB != nil { + w.bufferCB(input.ret) + input.ret = nil + } + in := input.b + if len(in) > 0 { + if w.err(nil) == nil { + // Don't expose data from previous buffers. + toWrite := in[:len(in):len(in)] + // Write to output. + n, err := writer.Write(toWrite) + if err == nil && n != len(toWrite) { + err = io.ErrShortBuffer + } + _ = w.err(err) + w.err(w.index.add(w.written, input.startOffset)) + w.written += int64(n) + } + } + if cap(in) >= w.obufLen { + w.buffers.Put(in) + } + // close the incoming write request. + // This can be used for synchronizing flushes. + close(write) + } + }() +} + +// Write satisfies the io.Writer interface. +func (w *Writer) Write(p []byte) (nRet int, errRet error) { + if err := w.err(nil); err != nil { + return 0, err + } + if w.flushOnWrite { + return w.write(p) + } + // If we exceed the input buffer size, start writing + for len(p) > (cap(w.ibuf)-len(w.ibuf)) && w.err(nil) == nil { + var n int + if len(w.ibuf) == 0 { + // Large write, empty buffer. + // Write directly from p to avoid copy. + n, _ = w.write(p) + } else { + n = copy(w.ibuf[len(w.ibuf):cap(w.ibuf)], p) + w.ibuf = w.ibuf[:len(w.ibuf)+n] + w.write(w.ibuf) + w.ibuf = w.ibuf[:0] + } + nRet += n + p = p[n:] + } + if err := w.err(nil); err != nil { + return nRet, err + } + // p should always be able to fit into w.ibuf now. + n := copy(w.ibuf[len(w.ibuf):cap(w.ibuf)], p) + w.ibuf = w.ibuf[:len(w.ibuf)+n] + nRet += n + return nRet, nil +} + +// ReadFrom implements the io.ReaderFrom interface. +// Using this is typically more efficient since it avoids a memory copy. +// ReadFrom reads data from r until EOF or error. +// The return value n is the number of bytes read. +// Any error except io.EOF encountered during the read is also returned. +func (w *Writer) ReadFrom(r io.Reader) (n int64, err error) { + if err := w.err(nil); err != nil { + return 0, err + } + if len(w.ibuf) > 0 { + err := w.AsyncFlush() + if err != nil { + return 0, err + } + } + if br, ok := r.(byter); ok { + buf := br.Bytes() + if err := w.EncodeBuffer(buf); err != nil { + return 0, err + } + return int64(len(buf)), w.AsyncFlush() + } + for { + inbuf := w.buffers.Get().([]byte)[:w.blockSize+obufHeaderLen] + n2, err := io.ReadFull(r, inbuf[obufHeaderLen:]) + if err != nil { + if err == io.ErrUnexpectedEOF { + err = io.EOF + } + if err != io.EOF { + return n, w.err(err) + } + } + if n2 == 0 { + if cap(inbuf) >= w.obufLen { + w.buffers.Put(inbuf) + } + break + } + n += int64(n2) + err2 := w.writeFull(inbuf[:n2+obufHeaderLen]) + if w.err(err2) != nil { + break + } + + if err != nil { + // We got EOF and wrote everything + break + } + } + + return n, w.err(nil) +} + +// AddSkippableBlock will add a skippable block to the stream. +// The ID must be 0x80-0xfe (inclusive). +// Length of the skippable block must be <= 16777215 bytes. +func (w *Writer) AddSkippableBlock(id uint8, data []byte) (err error) { + if err := w.err(nil); err != nil { + return err + } + if len(data) == 0 { + return nil + } + if id < 0x80 || id > chunkTypePadding { + return fmt.Errorf("invalid skippable block id %x", id) + } + if len(data) > maxChunkSize { + return fmt.Errorf("skippable block excessed maximum size") + } + var header [4]byte + chunkLen := len(data) + header[0] = id + header[1] = uint8(chunkLen >> 0) + header[2] = uint8(chunkLen >> 8) + header[3] = uint8(chunkLen >> 16) + if w.concurrency == 1 { + write := func(b []byte) error { + n, err := w.writer.Write(b) + if err = w.err(err); err != nil { + return err + } + if n != len(b) { + return w.err(io.ErrShortWrite) + } + w.written += int64(n) + return w.err(nil) + } + if !w.wroteStreamHeader { + w.wroteStreamHeader = true + if w.snappy { + if err := write([]byte(magicChunkSnappy)); err != nil { + return err + } + } else { + if err := write([]byte(magicChunk)); err != nil { + return err + } + } + } + if err := write(header[:]); err != nil { + return err + } + return write(data) + } + + // Create output... + if !w.wroteStreamHeader { + w.wroteStreamHeader = true + hWriter := make(chan result) + w.output <- hWriter + if w.snappy { + hWriter <- result{startOffset: w.uncompWritten, b: magicChunkSnappyBytes} + } else { + hWriter <- result{startOffset: w.uncompWritten, b: magicChunkBytes} + } + } + + // Copy input. + inbuf := w.buffers.Get().([]byte)[:4] + copy(inbuf, header[:]) + inbuf = append(inbuf, data...) + + output := make(chan result, 1) + // Queue output. + w.output <- output + output <- result{startOffset: w.uncompWritten, b: inbuf} + + return nil +} + +// EncodeBuffer will add a buffer to the stream. +// This is the fastest way to encode a stream, +// but the input buffer cannot be written to by the caller +// until Flush or Close has been called when concurrency != 1. +// +// Use the WriterBufferDone to receive a callback when the buffer is done +// Processing. +// +// Note that input is not buffered. +// This means that each write will result in discrete blocks being created. +// For buffered writes, use the regular Write function. +func (w *Writer) EncodeBuffer(buf []byte) (err error) { + if err := w.err(nil); err != nil { + return err + } + + if w.flushOnWrite { + _, err := w.write(buf) + return err + } + // Flush queued data first. + if len(w.ibuf) > 0 { + err := w.AsyncFlush() + if err != nil { + return err + } + } + if w.concurrency == 1 { + _, err := w.writeSync(buf) + if w.bufferCB != nil { + w.bufferCB(buf) + } + return err + } + + // Spawn goroutine and write block to output channel. + if !w.wroteStreamHeader { + w.wroteStreamHeader = true + hWriter := make(chan result) + w.output <- hWriter + if w.snappy { + hWriter <- result{startOffset: w.uncompWritten, b: magicChunkSnappyBytes} + } else { + hWriter <- result{startOffset: w.uncompWritten, b: magicChunkBytes} + } + } + orgBuf := buf + for len(buf) > 0 { + // Cut input. + uncompressed := buf + if len(uncompressed) > w.blockSize { + uncompressed = uncompressed[:w.blockSize] + } + buf = buf[len(uncompressed):] + // Get an output buffer. + obuf := w.buffers.Get().([]byte)[:len(uncompressed)+obufHeaderLen] + race.WriteSlice(obuf) + + output := make(chan result) + // Queue output now, so we keep order. + w.output <- output + res := result{ + startOffset: w.uncompWritten, + } + w.uncompWritten += int64(len(uncompressed)) + if len(buf) == 0 && w.bufferCB != nil { + res.ret = orgBuf + } + go func() { + race.ReadSlice(uncompressed) + + checksum := crc(uncompressed) + + // Set to uncompressed. + chunkType := uint8(chunkTypeUncompressedData) + chunkLen := 4 + len(uncompressed) + + // Attempt compressing. + n := binary.PutUvarint(obuf[obufHeaderLen:], uint64(len(uncompressed))) + n2 := w.encodeBlock(obuf[obufHeaderLen+n:], uncompressed) + + // Check if we should use this, or store as uncompressed instead. + if n2 > 0 { + chunkType = uint8(chunkTypeCompressedData) + chunkLen = 4 + n + n2 + obuf = obuf[:obufHeaderLen+n+n2] + } else { + // copy uncompressed + copy(obuf[obufHeaderLen:], uncompressed) + } + + // Fill in the per-chunk header that comes before the body. + obuf[0] = chunkType + obuf[1] = uint8(chunkLen >> 0) + obuf[2] = uint8(chunkLen >> 8) + obuf[3] = uint8(chunkLen >> 16) + obuf[4] = uint8(checksum >> 0) + obuf[5] = uint8(checksum >> 8) + obuf[6] = uint8(checksum >> 16) + obuf[7] = uint8(checksum >> 24) + + // Queue final output. + res.b = obuf + output <- res + }() + } + return nil +} + +func (w *Writer) encodeBlock(obuf, uncompressed []byte) int { + if w.customEnc != nil { + if ret := w.customEnc(obuf, uncompressed); ret >= 0 { + return ret + } + } + if w.snappy { + switch w.level { + case levelFast: + return encodeBlockSnappy(obuf, uncompressed) + case levelBetter: + return encodeBlockBetterSnappy(obuf, uncompressed) + case levelBest: + return encodeBlockBestSnappy(obuf, uncompressed) + } + return 0 + } + switch w.level { + case levelFast: + return encodeBlock(obuf, uncompressed) + case levelBetter: + return encodeBlockBetter(obuf, uncompressed) + case levelBest: + return encodeBlockBest(obuf, uncompressed, nil) + } + return 0 +} + +func (w *Writer) write(p []byte) (nRet int, errRet error) { + if err := w.err(nil); err != nil { + return 0, err + } + if w.concurrency == 1 { + return w.writeSync(p) + } + + // Spawn goroutine and write block to output channel. + for len(p) > 0 { + if !w.wroteStreamHeader { + w.wroteStreamHeader = true + hWriter := make(chan result) + w.output <- hWriter + if w.snappy { + hWriter <- result{startOffset: w.uncompWritten, b: magicChunkSnappyBytes} + } else { + hWriter <- result{startOffset: w.uncompWritten, b: magicChunkBytes} + } + } + + var uncompressed []byte + if len(p) > w.blockSize { + uncompressed, p = p[:w.blockSize], p[w.blockSize:] + } else { + uncompressed, p = p, nil + } + + // Copy input. + // If the block is incompressible, this is used for the result. + inbuf := w.buffers.Get().([]byte)[:len(uncompressed)+obufHeaderLen] + obuf := w.buffers.Get().([]byte)[:w.obufLen] + copy(inbuf[obufHeaderLen:], uncompressed) + uncompressed = inbuf[obufHeaderLen:] + + output := make(chan result) + // Queue output now, so we keep order. + w.output <- output + res := result{ + startOffset: w.uncompWritten, + } + w.uncompWritten += int64(len(uncompressed)) + + go func() { + checksum := crc(uncompressed) + + // Set to uncompressed. + chunkType := uint8(chunkTypeUncompressedData) + chunkLen := 4 + len(uncompressed) + + // Attempt compressing. + n := binary.PutUvarint(obuf[obufHeaderLen:], uint64(len(uncompressed))) + n2 := w.encodeBlock(obuf[obufHeaderLen+n:], uncompressed) + + // Check if we should use this, or store as uncompressed instead. + if n2 > 0 { + chunkType = uint8(chunkTypeCompressedData) + chunkLen = 4 + n + n2 + obuf = obuf[:obufHeaderLen+n+n2] + } else { + // Use input as output. + obuf, inbuf = inbuf, obuf + } + + // Fill in the per-chunk header that comes before the body. + obuf[0] = chunkType + obuf[1] = uint8(chunkLen >> 0) + obuf[2] = uint8(chunkLen >> 8) + obuf[3] = uint8(chunkLen >> 16) + obuf[4] = uint8(checksum >> 0) + obuf[5] = uint8(checksum >> 8) + obuf[6] = uint8(checksum >> 16) + obuf[7] = uint8(checksum >> 24) + + // Queue final output. + res.b = obuf + output <- res + + // Put unused buffer back in pool. + w.buffers.Put(inbuf) + }() + nRet += len(uncompressed) + } + return nRet, nil +} + +// writeFull is a special version of write that will always write the full buffer. +// Data to be compressed should start at offset obufHeaderLen and fill the remainder of the buffer. +// The data will be written as a single block. +// The caller is not allowed to use inbuf after this function has been called. +func (w *Writer) writeFull(inbuf []byte) (errRet error) { + if err := w.err(nil); err != nil { + return err + } + + if w.concurrency == 1 { + _, err := w.writeSync(inbuf[obufHeaderLen:]) + if cap(inbuf) >= w.obufLen { + w.buffers.Put(inbuf) + } + return err + } + + // Spawn goroutine and write block to output channel. + if !w.wroteStreamHeader { + w.wroteStreamHeader = true + hWriter := make(chan result) + w.output <- hWriter + if w.snappy { + hWriter <- result{startOffset: w.uncompWritten, b: magicChunkSnappyBytes} + } else { + hWriter <- result{startOffset: w.uncompWritten, b: magicChunkBytes} + } + } + + // Get an output buffer. + obuf := w.buffers.Get().([]byte)[:w.obufLen] + uncompressed := inbuf[obufHeaderLen:] + + output := make(chan result) + // Queue output now, so we keep order. + w.output <- output + res := result{ + startOffset: w.uncompWritten, + } + w.uncompWritten += int64(len(uncompressed)) + + go func() { + checksum := crc(uncompressed) + + // Set to uncompressed. + chunkType := uint8(chunkTypeUncompressedData) + chunkLen := 4 + len(uncompressed) + + // Attempt compressing. + n := binary.PutUvarint(obuf[obufHeaderLen:], uint64(len(uncompressed))) + n2 := w.encodeBlock(obuf[obufHeaderLen+n:], uncompressed) + + // Check if we should use this, or store as uncompressed instead. + if n2 > 0 { + chunkType = uint8(chunkTypeCompressedData) + chunkLen = 4 + n + n2 + obuf = obuf[:obufHeaderLen+n+n2] + } else { + // Use input as output. + obuf, inbuf = inbuf, obuf + } + + // Fill in the per-chunk header that comes before the body. + obuf[0] = chunkType + obuf[1] = uint8(chunkLen >> 0) + obuf[2] = uint8(chunkLen >> 8) + obuf[3] = uint8(chunkLen >> 16) + obuf[4] = uint8(checksum >> 0) + obuf[5] = uint8(checksum >> 8) + obuf[6] = uint8(checksum >> 16) + obuf[7] = uint8(checksum >> 24) + + // Queue final output. + res.b = obuf + output <- res + + // Put unused buffer back in pool. + w.buffers.Put(inbuf) + }() + return nil +} + +func (w *Writer) writeSync(p []byte) (nRet int, errRet error) { + if err := w.err(nil); err != nil { + return 0, err + } + if !w.wroteStreamHeader { + w.wroteStreamHeader = true + var n int + var err error + if w.snappy { + n, err = w.writer.Write(magicChunkSnappyBytes) + } else { + n, err = w.writer.Write(magicChunkBytes) + } + if err != nil { + return 0, w.err(err) + } + if n != len(magicChunk) { + return 0, w.err(io.ErrShortWrite) + } + w.written += int64(n) + } + + for len(p) > 0 { + var uncompressed []byte + if len(p) > w.blockSize { + uncompressed, p = p[:w.blockSize], p[w.blockSize:] + } else { + uncompressed, p = p, nil + } + + obuf := w.buffers.Get().([]byte)[:w.obufLen] + checksum := crc(uncompressed) + + // Set to uncompressed. + chunkType := uint8(chunkTypeUncompressedData) + chunkLen := 4 + len(uncompressed) + + // Attempt compressing. + n := binary.PutUvarint(obuf[obufHeaderLen:], uint64(len(uncompressed))) + n2 := w.encodeBlock(obuf[obufHeaderLen+n:], uncompressed) + + if n2 > 0 { + chunkType = uint8(chunkTypeCompressedData) + chunkLen = 4 + n + n2 + obuf = obuf[:obufHeaderLen+n+n2] + } else { + obuf = obuf[:8] + } + + // Fill in the per-chunk header that comes before the body. + obuf[0] = chunkType + obuf[1] = uint8(chunkLen >> 0) + obuf[2] = uint8(chunkLen >> 8) + obuf[3] = uint8(chunkLen >> 16) + obuf[4] = uint8(checksum >> 0) + obuf[5] = uint8(checksum >> 8) + obuf[6] = uint8(checksum >> 16) + obuf[7] = uint8(checksum >> 24) + + n, err := w.writer.Write(obuf) + if err != nil { + return 0, w.err(err) + } + if n != len(obuf) { + return 0, w.err(io.ErrShortWrite) + } + w.err(w.index.add(w.written, w.uncompWritten)) + w.written += int64(n) + w.uncompWritten += int64(len(uncompressed)) + + if chunkType == chunkTypeUncompressedData { + // Write uncompressed data. + n, err := w.writer.Write(uncompressed) + if err != nil { + return 0, w.err(err) + } + if n != len(uncompressed) { + return 0, w.err(io.ErrShortWrite) + } + w.written += int64(n) + } + w.buffers.Put(obuf) + // Queue final output. + nRet += len(uncompressed) + } + return nRet, nil +} + +// AsyncFlush writes any buffered bytes to a block and starts compressing it. +// It does not wait for the output has been written as Flush() does. +func (w *Writer) AsyncFlush() error { + if err := w.err(nil); err != nil { + return err + } + + // Queue any data still in input buffer. + if len(w.ibuf) != 0 { + if !w.wroteStreamHeader { + _, err := w.writeSync(w.ibuf) + w.ibuf = w.ibuf[:0] + return w.err(err) + } else { + _, err := w.write(w.ibuf) + w.ibuf = w.ibuf[:0] + err = w.err(err) + if err != nil { + return err + } + } + } + return w.err(nil) +} + +// Flush flushes the Writer to its underlying io.Writer. +// This does not apply padding. +func (w *Writer) Flush() error { + if err := w.AsyncFlush(); err != nil { + return err + } + if w.output == nil { + return w.err(nil) + } + + // Send empty buffer + res := make(chan result) + w.output <- res + // Block until this has been picked up. + res <- result{b: nil, startOffset: w.uncompWritten} + // When it is closed, we have flushed. + <-res + return w.err(nil) +} + +// Close calls Flush and then closes the Writer. +// Calling Close multiple times is ok, +// but calling CloseIndex after this will make it not return the index. +func (w *Writer) Close() error { + _, err := w.closeIndex(w.appendIndex) + return err +} + +// CloseIndex calls Close and returns an index on first call. +// This is not required if you are only adding index to a stream. +func (w *Writer) CloseIndex() ([]byte, error) { + return w.closeIndex(true) +} + +func (w *Writer) closeIndex(idx bool) ([]byte, error) { + err := w.Flush() + if w.output != nil { + close(w.output) + w.writerWg.Wait() + w.output = nil + } + + var index []byte + if w.err(err) == nil && w.writer != nil { + // Create index. + if idx { + compSize := int64(-1) + if w.pad <= 1 { + compSize = w.written + } + index = w.index.appendTo(w.ibuf[:0], w.uncompWritten, compSize) + // Count as written for padding. + if w.appendIndex { + w.written += int64(len(index)) + } + } + + if w.pad > 1 { + tmp := w.ibuf[:0] + if len(index) > 0 { + // Allocate another buffer. + tmp = w.buffers.Get().([]byte)[:0] + defer w.buffers.Put(tmp) + } + add := calcSkippableFrame(w.written, int64(w.pad)) + frame, err := skippableFrame(tmp, add, w.randSrc) + if err = w.err(err); err != nil { + return nil, err + } + n, err2 := w.writer.Write(frame) + if err2 == nil && n != len(frame) { + err2 = io.ErrShortWrite + } + _ = w.err(err2) + } + if len(index) > 0 && w.appendIndex { + n, err2 := w.writer.Write(index) + if err2 == nil && n != len(index) { + err2 = io.ErrShortWrite + } + _ = w.err(err2) + } + } + err = w.err(errClosed) + if err == errClosed { + return index, nil + } + return nil, err +} + +// calcSkippableFrame will return a total size to be added for written +// to be divisible by multiple. +// The value will always be > skippableFrameHeader. +// The function will panic if written < 0 or wantMultiple <= 0. +func calcSkippableFrame(written, wantMultiple int64) int { + if wantMultiple <= 0 { + panic("wantMultiple <= 0") + } + if written < 0 { + panic("written < 0") + } + leftOver := written % wantMultiple + if leftOver == 0 { + return 0 + } + toAdd := wantMultiple - leftOver + for toAdd < skippableFrameHeader { + toAdd += wantMultiple + } + return int(toAdd) +} + +// skippableFrame will add a skippable frame with a total size of bytes. +// total should be >= skippableFrameHeader and < maxBlockSize + skippableFrameHeader +func skippableFrame(dst []byte, total int, r io.Reader) ([]byte, error) { + if total == 0 { + return dst, nil + } + if total < skippableFrameHeader { + return dst, fmt.Errorf("s2: requested skippable frame (%d) < 4", total) + } + if int64(total) >= maxBlockSize+skippableFrameHeader { + return dst, fmt.Errorf("s2: requested skippable frame (%d) >= max 1<<24", total) + } + // Chunk type 0xfe "Section 4.4 Padding (chunk type 0xfe)" + dst = append(dst, chunkTypePadding) + f := uint32(total - skippableFrameHeader) + // Add chunk length. + dst = append(dst, uint8(f), uint8(f>>8), uint8(f>>16)) + // Add data + start := len(dst) + dst = append(dst, make([]byte, f)...) + _, err := io.ReadFull(r, dst[start:]) + return dst, err +} + +var errClosed = errors.New("s2: Writer is closed") + +// WriterOption is an option for creating a encoder. +type WriterOption func(*Writer) error + +// WriterConcurrency will set the concurrency, +// meaning the maximum number of decoders to run concurrently. +// The value supplied must be at least 1. +// By default this will be set to GOMAXPROCS. +func WriterConcurrency(n int) WriterOption { + return func(w *Writer) error { + if n <= 0 { + return errors.New("concurrency must be at least 1") + } + w.concurrency = n + return nil + } +} + +// WriterAddIndex will append an index to the end of a stream +// when it is closed. +func WriterAddIndex() WriterOption { + return func(w *Writer) error { + w.appendIndex = true + return nil + } +} + +// WriterBetterCompression will enable better compression. +// EncodeBetter compresses better than Encode but typically with a +// 10-40% speed decrease on both compression and decompression. +func WriterBetterCompression() WriterOption { + return func(w *Writer) error { + w.level = levelBetter + return nil + } +} + +// WriterBestCompression will enable better compression. +// EncodeBest compresses better than Encode but typically with a +// big speed decrease on compression. +func WriterBestCompression() WriterOption { + return func(w *Writer) error { + w.level = levelBest + return nil + } +} + +// WriterUncompressed will bypass compression. +// The stream will be written as uncompressed blocks only. +// If concurrency is > 1 CRC and output will still be done async. +func WriterUncompressed() WriterOption { + return func(w *Writer) error { + w.level = levelUncompressed + return nil + } +} + +// WriterBufferDone will perform a callback when EncodeBuffer has finished +// writing a buffer to the output and the buffer can safely be reused. +// If the buffer was split into several blocks, it will be sent after the last block. +// Callbacks will not be done concurrently. +func WriterBufferDone(fn func(b []byte)) WriterOption { + return func(w *Writer) error { + w.bufferCB = fn + return nil + } +} + +// WriterBlockSize allows to override the default block size. +// Blocks will be this size or smaller. +// Minimum size is 4KB and maximum size is 4MB. +// +// Bigger blocks may give bigger throughput on systems with many cores, +// and will increase compression slightly, but it will limit the possible +// concurrency for smaller payloads for both encoding and decoding. +// Default block size is 1MB. +// +// When writing Snappy compatible output using WriterSnappyCompat, +// the maximum block size is 64KB. +func WriterBlockSize(n int) WriterOption { + return func(w *Writer) error { + if w.snappy && n > maxSnappyBlockSize || n < minBlockSize { + return errors.New("s2: block size too large. Must be <= 64K and >=4KB on for snappy compatible output") + } + if n > maxBlockSize || n < minBlockSize { + return errors.New("s2: block size too large. Must be <= 4MB and >=4KB") + } + w.blockSize = n + return nil + } +} + +// WriterPadding will add padding to all output so the size will be a multiple of n. +// This can be used to obfuscate the exact output size or make blocks of a certain size. +// The contents will be a skippable frame, so it will be invisible by the decoder. +// n must be > 0 and <= 4MB. +// The padded area will be filled with data from crypto/rand.Reader. +// The padding will be applied whenever Close is called on the writer. +func WriterPadding(n int) WriterOption { + return func(w *Writer) error { + if n <= 0 { + return fmt.Errorf("s2: padding must be at least 1") + } + // No need to waste our time. + if n == 1 { + w.pad = 0 + } + if n > maxBlockSize { + return fmt.Errorf("s2: padding must less than 4MB") + } + w.pad = n + return nil + } +} + +// WriterPaddingSrc will get random data for padding from the supplied source. +// By default crypto/rand is used. +func WriterPaddingSrc(reader io.Reader) WriterOption { + return func(w *Writer) error { + w.randSrc = reader + return nil + } +} + +// WriterSnappyCompat will write snappy compatible output. +// The output can be decompressed using either snappy or s2. +// If block size is more than 64KB it is set to that. +func WriterSnappyCompat() WriterOption { + return func(w *Writer) error { + w.snappy = true + if w.blockSize > 64<<10 { + // We choose 8 bytes less than 64K, since that will make literal emits slightly more effective. + // And allows us to skip some size checks. + w.blockSize = (64 << 10) - 8 + } + return nil + } +} + +// WriterFlushOnWrite will compress blocks on each call to the Write function. +// +// This is quite inefficient as blocks size will depend on the write size. +// +// Use WriterConcurrency(1) to also make sure that output is flushed. +// When Write calls return, otherwise they will be written when compression is done. +func WriterFlushOnWrite() WriterOption { + return func(w *Writer) error { + w.flushOnWrite = true + return nil + } +} + +// WriterCustomEncoder allows to override the encoder for blocks on the stream. +// The function must compress 'src' into 'dst' and return the bytes used in dst as an integer. +// Block size (initial varint) should not be added by the encoder. +// Returning value 0 indicates the block could not be compressed. +// Returning a negative value indicates that compression should be attempted. +// The function should expect to be called concurrently. +func WriterCustomEncoder(fn func(dst, src []byte) int) WriterOption { + return func(w *Writer) error { + w.customEnc = fn + return nil + } +} diff --git a/vendor/github.com/klauspost/compress/s2sx.mod b/vendor/github.com/klauspost/compress/s2sx.mod new file mode 100644 index 000000000..81bda5e29 --- /dev/null +++ b/vendor/github.com/klauspost/compress/s2sx.mod @@ -0,0 +1,3 @@ +module github.com/klauspost/compress + +go 1.22 diff --git a/vendor/github.com/klauspost/compress/s2sx.sum b/vendor/github.com/klauspost/compress/s2sx.sum new file mode 100644 index 000000000..e69de29bb diff --git a/vendor/github.com/klauspost/compress/snappy/.gitignore b/vendor/github.com/klauspost/compress/snappy/.gitignore new file mode 100644 index 000000000..042091d9b --- /dev/null +++ b/vendor/github.com/klauspost/compress/snappy/.gitignore @@ -0,0 +1,16 @@ +cmd/snappytool/snappytool +testdata/bench + +# These explicitly listed benchmark data files are for an obsolete version of +# snappy_test.go. +testdata/alice29.txt +testdata/asyoulik.txt +testdata/fireworks.jpeg +testdata/geo.protodata +testdata/html +testdata/html_x_4 +testdata/kppkn.gtb +testdata/lcet10.txt +testdata/paper-100k.pdf +testdata/plrabn12.txt +testdata/urls.10K diff --git a/vendor/github.com/klauspost/compress/snappy/AUTHORS b/vendor/github.com/klauspost/compress/snappy/AUTHORS new file mode 100644 index 000000000..52ccb5a93 --- /dev/null +++ b/vendor/github.com/klauspost/compress/snappy/AUTHORS @@ -0,0 +1,18 @@ +# This is the official list of Snappy-Go authors for copyright purposes. +# This file is distinct from the CONTRIBUTORS files. +# See the latter for an explanation. + +# Names should be added to this file as +# Name or Organization +# The email address is not required for organizations. + +# Please keep the list sorted. + +Amazon.com, Inc +Damian Gryski +Eric Buth +Google Inc. +Jan Mercl <0xjnml@gmail.com> +Klaus Post +Rodolfo Carvalho +Sebastien Binet diff --git a/vendor/github.com/klauspost/compress/snappy/CONTRIBUTORS b/vendor/github.com/klauspost/compress/snappy/CONTRIBUTORS new file mode 100644 index 000000000..ea6524ddd --- /dev/null +++ b/vendor/github.com/klauspost/compress/snappy/CONTRIBUTORS @@ -0,0 +1,41 @@ +# This is the official list of people who can contribute +# (and typically have contributed) code to the Snappy-Go repository. +# The AUTHORS file lists the copyright holders; this file +# lists people. For example, Google employees are listed here +# but not in AUTHORS, because Google holds the copyright. +# +# The submission process automatically checks to make sure +# that people submitting code are listed in this file (by email address). +# +# Names should be added to this file only after verifying that +# the individual or the individual's organization has agreed to +# the appropriate Contributor License Agreement, found here: +# +# http://code.google.com/legal/individual-cla-v1.0.html +# http://code.google.com/legal/corporate-cla-v1.0.html +# +# The agreement for individuals can be filled out on the web. +# +# When adding J Random Contributor's name to this file, +# either J's name or J's organization's name should be +# added to the AUTHORS file, depending on whether the +# individual or corporate CLA was used. + +# Names should be added to this file like so: +# Name + +# Please keep the list sorted. + +Alex Legg +Damian Gryski +Eric Buth +Jan Mercl <0xjnml@gmail.com> +Jonathan Swinney +Kai Backman +Klaus Post +Marc-Antoine Ruel +Nigel Tao +Rob Pike +Rodolfo Carvalho +Russ Cox +Sebastien Binet diff --git a/vendor/github.com/klauspost/compress/snappy/LICENSE b/vendor/github.com/klauspost/compress/snappy/LICENSE new file mode 100644 index 000000000..6050c10f4 --- /dev/null +++ b/vendor/github.com/klauspost/compress/snappy/LICENSE @@ -0,0 +1,27 @@ +Copyright (c) 2011 The Snappy-Go Authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/vendor/github.com/klauspost/compress/snappy/README.md b/vendor/github.com/klauspost/compress/snappy/README.md new file mode 100644 index 000000000..8271bbd09 --- /dev/null +++ b/vendor/github.com/klauspost/compress/snappy/README.md @@ -0,0 +1,17 @@ +# snappy + +The Snappy compression format in the Go programming language. + +This is a drop-in replacement for `github.com/golang/snappy`. + +It provides a full, compatible replacement of the Snappy package by simply changing imports. + +See [Snappy Compatibility](https://github.com/klauspost/compress/tree/master/s2#snappy-compatibility) in the S2 documentation. + +"Better" compression mode is used. For buffered streams concurrent compression is used. + +For more options use the [s2 package](https://pkg.go.dev/github.com/klauspost/compress/s2). + +# usage + +Replace imports `github.com/golang/snappy` with `github.com/klauspost/compress/snappy`. diff --git a/vendor/github.com/klauspost/compress/snappy/decode.go b/vendor/github.com/klauspost/compress/snappy/decode.go new file mode 100644 index 000000000..89f1fa234 --- /dev/null +++ b/vendor/github.com/klauspost/compress/snappy/decode.go @@ -0,0 +1,60 @@ +// Copyright 2011 The Snappy-Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package snappy + +import ( + "io" + + "github.com/klauspost/compress/s2" +) + +var ( + // ErrCorrupt reports that the input is invalid. + ErrCorrupt = s2.ErrCorrupt + // ErrTooLarge reports that the uncompressed length is too large. + ErrTooLarge = s2.ErrTooLarge + // ErrUnsupported reports that the input isn't supported. + ErrUnsupported = s2.ErrUnsupported +) + +const ( + // maxBlockSize is the maximum size of the input to encodeBlock. It is not + // part of the wire format per se, but some parts of the encoder assume + // that an offset fits into a uint16. + // + // Also, for the framing format (Writer type instead of Encode function), + // https://github.com/google/snappy/blob/master/framing_format.txt says + // that "the uncompressed data in a chunk must be no longer than 65536 + // bytes". + maxBlockSize = 65536 +) + +// DecodedLen returns the length of the decoded block. +func DecodedLen(src []byte) (int, error) { + return s2.DecodedLen(src) +} + +// Decode returns the decoded form of src. The returned slice may be a sub- +// slice of dst if dst was large enough to hold the entire decoded block. +// Otherwise, a newly allocated slice will be returned. +// +// The dst and src must not overlap. It is valid to pass a nil dst. +// +// Decode handles the Snappy block format, not the Snappy stream format. +func Decode(dst, src []byte) ([]byte, error) { + return s2.Decode(dst, src) +} + +// NewReader returns a new Reader that decompresses from r, using the framing +// format described at +// https://github.com/google/snappy/blob/master/framing_format.txt +func NewReader(r io.Reader) *Reader { + return s2.NewReader(r, s2.ReaderMaxBlockSize(maxBlockSize)) +} + +// Reader is an io.Reader that can read Snappy-compressed bytes. +// +// Reader handles the Snappy stream format, not the Snappy block format. +type Reader = s2.Reader diff --git a/vendor/github.com/klauspost/compress/snappy/encode.go b/vendor/github.com/klauspost/compress/snappy/encode.go new file mode 100644 index 000000000..e8bd72c18 --- /dev/null +++ b/vendor/github.com/klauspost/compress/snappy/encode.go @@ -0,0 +1,59 @@ +// Copyright 2011 The Snappy-Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package snappy + +import ( + "io" + + "github.com/klauspost/compress/s2" +) + +// Encode returns the encoded form of src. The returned slice may be a sub- +// slice of dst if dst was large enough to hold the entire encoded block. +// Otherwise, a newly allocated slice will be returned. +// +// The dst and src must not overlap. It is valid to pass a nil dst. +// +// Encode handles the Snappy block format, not the Snappy stream format. +func Encode(dst, src []byte) []byte { + return s2.EncodeSnappyBetter(dst, src) +} + +// MaxEncodedLen returns the maximum length of a snappy block, given its +// uncompressed length. +// +// It will return a negative value if srcLen is too large to encode. +func MaxEncodedLen(srcLen int) int { + return s2.MaxEncodedLen(srcLen) +} + +// NewWriter returns a new Writer that compresses to w. +// +// The Writer returned does not buffer writes. There is no need to Flush or +// Close such a Writer. +// +// Deprecated: the Writer returned is not suitable for many small writes, only +// for few large writes. Use NewBufferedWriter instead, which is efficient +// regardless of the frequency and shape of the writes, and remember to Close +// that Writer when done. +func NewWriter(w io.Writer) *Writer { + return s2.NewWriter(w, s2.WriterSnappyCompat(), s2.WriterBetterCompression(), s2.WriterFlushOnWrite(), s2.WriterConcurrency(1)) +} + +// NewBufferedWriter returns a new Writer that compresses to w, using the +// framing format described at +// https://github.com/google/snappy/blob/master/framing_format.txt +// +// The Writer returned buffers writes. Users must call Close to guarantee all +// data has been forwarded to the underlying io.Writer. They may also call +// Flush zero or more times before calling Close. +func NewBufferedWriter(w io.Writer) *Writer { + return s2.NewWriter(w, s2.WriterSnappyCompat(), s2.WriterBetterCompression()) +} + +// Writer is an io.Writer that can write Snappy-compressed bytes. +// +// Writer handles the Snappy stream format, not the Snappy block format. +type Writer = s2.Writer diff --git a/vendor/github.com/klauspost/compress/snappy/snappy.go b/vendor/github.com/klauspost/compress/snappy/snappy.go new file mode 100644 index 000000000..398cdc95a --- /dev/null +++ b/vendor/github.com/klauspost/compress/snappy/snappy.go @@ -0,0 +1,46 @@ +// Copyright 2011 The Snappy-Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package snappy implements the Snappy compression format. It aims for very +// high speeds and reasonable compression. +// +// There are actually two Snappy formats: block and stream. They are related, +// but different: trying to decompress block-compressed data as a Snappy stream +// will fail, and vice versa. The block format is the Decode and Encode +// functions and the stream format is the Reader and Writer types. +// +// The block format, the more common case, is used when the complete size (the +// number of bytes) of the original data is known upfront, at the time +// compression starts. The stream format, also known as the framing format, is +// for when that isn't always true. +// +// The canonical, C++ implementation is at https://github.com/google/snappy and +// it only implements the block format. +package snappy + +/* +Each encoded block begins with the varint-encoded length of the decoded data, +followed by a sequence of chunks. Chunks begin and end on byte boundaries. The +first byte of each chunk is broken into its 2 least and 6 most significant bits +called l and m: l ranges in [0, 4) and m ranges in [0, 64). l is the chunk tag. +Zero means a literal tag. All other values mean a copy tag. + +For literal tags: + - If m < 60, the next 1 + m bytes are literal bytes. + - Otherwise, let n be the little-endian unsigned integer denoted by the next + m - 59 bytes. The next 1 + n bytes after that are literal bytes. + +For copy tags, length bytes are copied from offset bytes ago, in the style of +Lempel-Ziv compression algorithms. In particular: + - For l == 1, the offset ranges in [0, 1<<11) and the length in [4, 12). + The length is 4 + the low 3 bits of m. The high 3 bits of m form bits 8-10 + of the offset. The next byte is bits 0-7 of the offset. + - For l == 2, the offset ranges in [0, 1<<16) and the length in [1, 65). + The length is 1 + m. The offset is the little-endian unsigned integer + denoted by the next 2 bytes. + - For l == 3, this tag is a legacy format that is no longer issued by most + encoders. Nonetheless, the offset ranges in [0, 1<<32) and the length in + [1, 65). The length is 1 + m. The offset is the little-endian unsigned + integer denoted by the next 4 bytes. +*/ diff --git a/vendor/github.com/klauspost/compress/zstd/README.md b/vendor/github.com/klauspost/compress/zstd/README.md new file mode 100644 index 000000000..c11d7fa28 --- /dev/null +++ b/vendor/github.com/klauspost/compress/zstd/README.md @@ -0,0 +1,441 @@ +# zstd + +[Zstandard](https://facebook.github.io/zstd/) is a real-time compression algorithm, providing high compression ratios. +It offers a very wide range of compression / speed trade-off, while being backed by a very fast decoder. +A high performance compression algorithm is implemented. For now focused on speed. + +This package provides [compression](#Compressor) to and [decompression](#Decompressor) of Zstandard content. + +This package is pure Go. Use `noasm` and `nounsafe` to disable relevant features. + +The `zstd` package is provided as open source software using a Go standard license. + +Currently the package is heavily optimized for 64 bit processors and will be significantly slower on 32 bit processors. + +For seekable zstd streams, see [this excellent package](https://github.com/SaveTheRbtz/zstd-seekable-format-go). + +## Installation + +Install using `go get -u github.com/klauspost/compress`. The package is located in `github.com/klauspost/compress/zstd`. + +[![Go Reference](https://pkg.go.dev/badge/github.com/klauspost/compress/zstd.svg)](https://pkg.go.dev/github.com/klauspost/compress/zstd) + +## Compressor + +### Status: + +STABLE - there may always be subtle bugs, a wide variety of content has been tested and the library is actively +used by several projects. This library is being [fuzz-tested](https://github.com/klauspost/compress-fuzz) for all updates. + +There may still be specific combinations of data types/size/settings that could lead to edge cases, +so as always, testing is recommended. + +For now, a high speed (fastest) and medium-fast (default) compressor has been implemented. + +* The "Fastest" compression ratio is roughly equivalent to zstd level 1. +* The "Default" compression ratio is roughly equivalent to zstd level 3 (default). +* The "Better" compression ratio is roughly equivalent to zstd level 7. +* The "Best" compression ratio is roughly equivalent to zstd level 11. + +In terms of speed, it is typically 2x as fast as the stdlib deflate/gzip in its fastest mode. +The compression ratio compared to stdlib is around level 3, but usually 3x as fast. + + +### Usage + +An Encoder can be used for either compressing a stream via the +`io.WriteCloser` interface supported by the Encoder or as multiple independent +tasks via the `EncodeAll` function. +Smaller encodes are encouraged to use the EncodeAll function. +Use `NewWriter` to create a new instance that can be used for both. + +To create a writer with default options, do like this: + +```Go +// Compress input to output. +func Compress(in io.Reader, out io.Writer) error { + enc, err := zstd.NewWriter(out) + if err != nil { + return err + } + _, err = io.Copy(enc, in) + if err != nil { + enc.Close() + return err + } + return enc.Close() +} +``` + +Now you can encode by writing data to `enc`. The output will be finished writing when `Close()` is called. +Even if your encode fails, you should still call `Close()` to release any resources that may be held up. + +The above is fine for big encodes. However, whenever possible try to *reuse* the writer. + +To reuse the encoder, you can use the `Reset(io.Writer)` function to change to another output. +This will allow the encoder to reuse all resources and avoid wasteful allocations. + +Currently stream encoding has 'light' concurrency, meaning up to 2 goroutines can be working on part +of a stream. This is independent of the `WithEncoderConcurrency(n)`, but that is likely to change +in the future. So if you want to limit concurrency for future updates, specify the concurrency +you would like. + +If you would like stream encoding to be done without spawning async goroutines, use `WithEncoderConcurrency(1)` +which will compress input as each block is completed, blocking on writes until each has completed. + +You can specify your desired compression level using `WithEncoderLevel()` option. Currently only pre-defined +compression settings can be specified. + +#### Future Compatibility Guarantees + +This will be an evolving project. When using this package it is important to note that both the compression efficiency and speed may change. + +The goal will be to keep the default efficiency at the default zstd (level 3). +However the encoding should never be assumed to remain the same, +and you should not use hashes of compressed output for similarity checks. + +The Encoder can be assumed to produce the same output from the exact same code version. +However, the may be modes in the future that break this, +although they will not be enabled without an explicit option. + +This encoder is not designed to (and will probably never) output the exact same bitstream as the reference encoder. + +Also note, that the cgo decompressor currently does not [report all errors on invalid input](https://github.com/DataDog/zstd/issues/59), +[omits error checks](https://github.com/DataDog/zstd/issues/61), [ignores checksums](https://github.com/DataDog/zstd/issues/43) +and seems to ignore concatenated streams, even though [it is part of the spec](https://github.com/facebook/zstd/blob/dev/doc/zstd_compression_format.md#frames). + +#### Blocks + +For compressing small blocks, the returned encoder has a function called `EncodeAll(src, dst []byte) []byte`. + +`EncodeAll` will encode all input in src and append it to dst. +This function can be called concurrently. +Each call will only run on a same goroutine as the caller. + +Encoded blocks can be concatenated and the result will be the combined input stream. +Data compressed with EncodeAll can be decoded with the Decoder, using either a stream or `DecodeAll`. + +Especially when encoding blocks you should take special care to reuse the encoder. +This will effectively make it run without allocations after a warmup period. +To make it run completely without allocations, supply a destination buffer with space for all content. + +```Go +import "github.com/klauspost/compress/zstd" + +// Create a writer that caches compressors. +// For this operation type we supply a nil Reader. +var encoder, _ = zstd.NewWriter(nil) + +// Compress a buffer. +// If you have a destination buffer, the allocation in the call can also be eliminated. +func Compress(src []byte) []byte { + return encoder.EncodeAll(src, make([]byte, 0, len(src))) +} +``` + +You can control the maximum number of concurrent encodes using the `WithEncoderConcurrency(n)` +option when creating the writer. + +Using the Encoder for both a stream and individual blocks concurrently is safe. + +### Performance + +I have collected some speed examples to compare speed and compression against other compressors. + +* `file` is the input file. +* `out` is the compressor used. `zskp` is this package. `zstd` is the Datadog cgo library. `gzstd/gzkp` is gzip standard and this library. +* `level` is the compression level used. For `zskp` level 1 is "fastest", level 2 is "default"; 3 is "better", 4 is "best". +* `insize`/`outsize` is the input/output size. +* `millis` is the number of milliseconds used for compression. +* `mb/s` is megabytes (2^20 bytes) per second. + +``` +Silesia Corpus: +http://sun.aei.polsl.pl/~sdeor/corpus/silesia.zip + +This package: +file out level insize outsize millis mb/s +silesia.tar zskp 1 211947520 73821326 634 318.47 +silesia.tar zskp 2 211947520 67655404 1508 133.96 +silesia.tar zskp 3 211947520 64746933 3000 67.37 +silesia.tar zskp 4 211947520 60073508 16926 11.94 + +cgo zstd: +silesia.tar zstd 1 211947520 73605392 543 371.56 +silesia.tar zstd 3 211947520 66793289 864 233.68 +silesia.tar zstd 6 211947520 62916450 1913 105.66 +silesia.tar zstd 9 211947520 60212393 5063 39.92 + +gzip, stdlib/this package: +silesia.tar gzstd 1 211947520 80007735 1498 134.87 +silesia.tar gzkp 1 211947520 80088272 1009 200.31 + +GOB stream of binary data. Highly compressible. +https://files.klauspost.com/compress/gob-stream.7z + +file out level insize outsize millis mb/s +gob-stream zskp 1 1911399616 233948096 3230 564.34 +gob-stream zskp 2 1911399616 203997694 4997 364.73 +gob-stream zskp 3 1911399616 173526523 13435 135.68 +gob-stream zskp 4 1911399616 162195235 47559 38.33 + +gob-stream zstd 1 1911399616 249810424 2637 691.26 +gob-stream zstd 3 1911399616 208192146 3490 522.31 +gob-stream zstd 6 1911399616 193632038 6687 272.56 +gob-stream zstd 9 1911399616 177620386 16175 112.70 + +gob-stream gzstd 1 1911399616 357382013 9046 201.49 +gob-stream gzkp 1 1911399616 359136669 4885 373.08 + +The test data for the Large Text Compression Benchmark is the first +10^9 bytes of the English Wikipedia dump on Mar. 3, 2006. +http://mattmahoney.net/dc/textdata.html + +file out level insize outsize millis mb/s +enwik9 zskp 1 1000000000 343833605 3687 258.64 +enwik9 zskp 2 1000000000 317001237 7672 124.29 +enwik9 zskp 3 1000000000 291915823 15923 59.89 +enwik9 zskp 4 1000000000 261710291 77697 12.27 + +enwik9 zstd 1 1000000000 358072021 3110 306.65 +enwik9 zstd 3 1000000000 313734672 4784 199.35 +enwik9 zstd 6 1000000000 295138875 10290 92.68 +enwik9 zstd 9 1000000000 278348700 28549 33.40 + +enwik9 gzstd 1 1000000000 382578136 8608 110.78 +enwik9 gzkp 1 1000000000 382781160 5628 169.45 + +Highly compressible JSON file. +https://files.klauspost.com/compress/github-june-2days-2019.json.zst + +file out level insize outsize millis mb/s +github-june-2days-2019.json zskp 1 6273951764 697439532 9789 611.17 +github-june-2days-2019.json zskp 2 6273951764 610876538 18553 322.49 +github-june-2days-2019.json zskp 3 6273951764 517662858 44186 135.41 +github-june-2days-2019.json zskp 4 6273951764 464617114 165373 36.18 + +github-june-2days-2019.json zstd 1 6273951764 766284037 8450 708.00 +github-june-2days-2019.json zstd 3 6273951764 661889476 10927 547.57 +github-june-2days-2019.json zstd 6 6273951764 642756859 22996 260.18 +github-june-2days-2019.json zstd 9 6273951764 601974523 52413 114.16 + +github-june-2days-2019.json gzstd 1 6273951764 1164397768 26793 223.32 +github-june-2days-2019.json gzkp 1 6273951764 1120631856 17693 338.16 + +VM Image, Linux mint with a few installed applications: +https://files.klauspost.com/compress/rawstudio-mint14.7z + +file out level insize outsize millis mb/s +rawstudio-mint14.tar zskp 1 8558382592 3718400221 18206 448.29 +rawstudio-mint14.tar zskp 2 8558382592 3326118337 37074 220.15 +rawstudio-mint14.tar zskp 3 8558382592 3163842361 87306 93.49 +rawstudio-mint14.tar zskp 4 8558382592 2970480650 783862 10.41 + +rawstudio-mint14.tar zstd 1 8558382592 3609250104 17136 476.27 +rawstudio-mint14.tar zstd 3 8558382592 3341679997 29262 278.92 +rawstudio-mint14.tar zstd 6 8558382592 3235846406 77904 104.77 +rawstudio-mint14.tar zstd 9 8558382592 3160778861 140946 57.91 + +rawstudio-mint14.tar gzstd 1 8558382592 3926234992 51345 158.96 +rawstudio-mint14.tar gzkp 1 8558382592 3960117298 36722 222.26 + +CSV data: +https://files.klauspost.com/compress/nyc-taxi-data-10M.csv.zst + +file out level insize outsize millis mb/s +nyc-taxi-data-10M.csv zskp 1 3325605752 641319332 9462 335.17 +nyc-taxi-data-10M.csv zskp 2 3325605752 588976126 17570 180.50 +nyc-taxi-data-10M.csv zskp 3 3325605752 529329260 32432 97.79 +nyc-taxi-data-10M.csv zskp 4 3325605752 474949772 138025 22.98 + +nyc-taxi-data-10M.csv zstd 1 3325605752 687399637 8233 385.18 +nyc-taxi-data-10M.csv zstd 3 3325605752 598514411 10065 315.07 +nyc-taxi-data-10M.csv zstd 6 3325605752 570522953 20038 158.27 +nyc-taxi-data-10M.csv zstd 9 3325605752 517554797 64565 49.12 + +nyc-taxi-data-10M.csv gzstd 1 3325605752 928654908 21270 149.11 +nyc-taxi-data-10M.csv gzkp 1 3325605752 922273214 13929 227.68 +``` + +## Decompressor + +Status: STABLE - there may still be subtle bugs, but a wide variety of content has been tested. + +This library is being continuously [fuzz-tested](https://github.com/klauspost/compress-fuzz), +kindly supplied by [fuzzit.dev](https://fuzzit.dev/). +The main purpose of the fuzz testing is to ensure that it is not possible to crash the decoder, +or run it past its limits with ANY input provided. + +### Usage + +The package has been designed for two main usages, big streams of data and smaller in-memory buffers. +There are two main usages of the package for these. Both of them are accessed by creating a `Decoder`. + +For streaming use a simple setup could look like this: + +```Go +import "github.com/klauspost/compress/zstd" + +func Decompress(in io.Reader, out io.Writer) error { + d, err := zstd.NewReader(in) + if err != nil { + return err + } + defer d.Close() + + // Copy content... + _, err = io.Copy(out, d) + return err +} +``` + +It is important to use the "Close" function when you no longer need the Reader to stop running goroutines, +when running with default settings. +Goroutines will exit once an error has been returned, including `io.EOF` at the end of a stream. + +Streams are decoded concurrently in 4 asynchronous stages to give the best possible throughput. +However, if you prefer synchronous decompression, use `WithDecoderConcurrency(1)` which will decompress data +as it is being requested only. + +For decoding buffers, it could look something like this: + +```Go +import "github.com/klauspost/compress/zstd" + +// Create a reader that caches decompressors. +// For this operation type we supply a nil Reader. +var decoder, _ = zstd.NewReader(nil, zstd.WithDecoderConcurrency(0)) + +// Decompress a buffer. We don't supply a destination buffer, +// so it will be allocated by the decoder. +func Decompress(src []byte) ([]byte, error) { + return decoder.DecodeAll(src, nil) +} +``` + +Both of these cases should provide the functionality needed. +The decoder can be used for *concurrent* decompression of multiple buffers. +By default 4 decompressors will be created. + +It will only allow a certain number of concurrent operations to run. +To tweak that yourself use the `WithDecoderConcurrency(n)` option when creating the decoder. +It is possible to use `WithDecoderConcurrency(0)` to create GOMAXPROCS decoders. + +### Dictionaries + +Data compressed with [dictionaries](https://github.com/facebook/zstd#the-case-for-small-data-compression) can be decompressed. + +Dictionaries are added individually to Decoders. +Dictionaries are generated by the `zstd --train` command and contains an initial state for the decoder. +To add a dictionary use the `WithDecoderDicts(dicts ...[]byte)` option with the dictionary data. +Several dictionaries can be added at once. + +The dictionary will be used automatically for the data that specifies them. +A re-used Decoder will still contain the dictionaries registered. + +When registering multiple dictionaries with the same ID, the last one will be used. + +It is possible to use dictionaries when compressing data. + +To enable a dictionary use `WithEncoderDict(dict []byte)`. Here only one dictionary will be used +and it will likely be used even if it doesn't improve compression. + +The used dictionary must be used to decompress the content. + +For any real gains, the dictionary should be built with similar data. +If an unsuitable dictionary is used the output may be slightly larger than using no dictionary. +Use the [zstd commandline tool](https://github.com/facebook/zstd/releases) to build a dictionary from sample data. +For information see [zstd dictionary information](https://github.com/facebook/zstd#the-case-for-small-data-compression). + +For now there is a fixed startup performance penalty for compressing content with dictionaries. +This will likely be improved over time. Just be aware to test performance when implementing. + +### Allocation-less operation + +The decoder has been designed to operate without allocations after a warmup. + +This means that you should *store* the decoder for best performance. +To re-use a stream decoder, use the `Reset(r io.Reader) error` to switch to another stream. +A decoder can safely be re-used even if the previous stream failed. + +To release the resources, you must call the `Close()` function on a decoder. +After this it can *no longer be reused*, but all running goroutines will be stopped. +So you *must* use this if you will no longer need the Reader. + +For decompressing smaller buffers a single decoder can be used. +When decoding buffers, you can supply a destination slice with length 0 and your expected capacity. +In this case no unneeded allocations should be made. + +### Concurrency + +The buffer decoder does everything on the same goroutine and does nothing concurrently. +It can however decode several buffers concurrently. Use `WithDecoderConcurrency(n)` to limit that. + +The stream decoder will create goroutines that: + +1) Reads input and splits the input into blocks. +2) Decompression of literals. +3) Decompression of sequences. +4) Reconstruction of output stream. + +So effectively this also means the decoder will "read ahead" and prepare data to always be available for output. + +The concurrency level will, for streams, determine how many blocks ahead the compression will start. + +Since "blocks" are quite dependent on the output of the previous block stream decoding will only have limited concurrency. + +In practice this means that concurrency is often limited to utilizing about 3 cores effectively. + +### Benchmarks + +The first two are streaming decodes and the last are smaller inputs. + +Running on AMD Ryzen 9 3950X 16-Core Processor. AMD64 assembly used. + +``` +BenchmarkDecoderSilesia-32 5 206878840 ns/op 1024.50 MB/s 49808 B/op 43 allocs/op +BenchmarkDecoderEnwik9-32 1 1271809000 ns/op 786.28 MB/s 72048 B/op 52 allocs/op + +Concurrent blocks, performance: + +BenchmarkDecoder_DecodeAllParallel/kppkn.gtb.zst-32 67356 17857 ns/op 10321.96 MB/s 22.48 pct 102 B/op 0 allocs/op +BenchmarkDecoder_DecodeAllParallel/geo.protodata.zst-32 266656 4421 ns/op 26823.21 MB/s 11.89 pct 19 B/op 0 allocs/op +BenchmarkDecoder_DecodeAllParallel/plrabn12.txt.zst-32 20992 56842 ns/op 8477.17 MB/s 39.90 pct 754 B/op 0 allocs/op +BenchmarkDecoder_DecodeAllParallel/lcet10.txt.zst-32 27456 43932 ns/op 9714.01 MB/s 33.27 pct 524 B/op 0 allocs/op +BenchmarkDecoder_DecodeAllParallel/asyoulik.txt.zst-32 78432 15047 ns/op 8319.15 MB/s 40.34 pct 66 B/op 0 allocs/op +BenchmarkDecoder_DecodeAllParallel/alice29.txt.zst-32 65800 18436 ns/op 8249.63 MB/s 37.75 pct 88 B/op 0 allocs/op +BenchmarkDecoder_DecodeAllParallel/html_x_4.zst-32 102993 11523 ns/op 35546.09 MB/s 3.637 pct 143 B/op 0 allocs/op +BenchmarkDecoder_DecodeAllParallel/paper-100k.pdf.zst-32 1000000 1070 ns/op 95720.98 MB/s 80.53 pct 3 B/op 0 allocs/op +BenchmarkDecoder_DecodeAllParallel/fireworks.jpeg.zst-32 749802 1752 ns/op 70272.35 MB/s 100.0 pct 5 B/op 0 allocs/op +BenchmarkDecoder_DecodeAllParallel/urls.10K.zst-32 22640 52934 ns/op 13263.37 MB/s 26.25 pct 1014 B/op 0 allocs/op +BenchmarkDecoder_DecodeAllParallel/html.zst-32 226412 5232 ns/op 19572.27 MB/s 14.49 pct 20 B/op 0 allocs/op +BenchmarkDecoder_DecodeAllParallel/comp-data.bin.zst-32 923041 1276 ns/op 3194.71 MB/s 31.26 pct 0 B/op 0 allocs/op +``` + +This reflects the performance around May 2022, but this may be out of date. + +## Zstd inside ZIP files + +It is possible to use zstandard to compress individual files inside zip archives. +While this isn't widely supported it can be useful for internal files. + +To support the compression and decompression of these files you must register a compressor and decompressor. + +It is highly recommended registering the (de)compressors on individual zip Reader/Writer and NOT +use the global registration functions. The main reason for this is that 2 registrations from +different packages will result in a panic. + +It is a good idea to only have a single compressor and decompressor, since they can be used for multiple zip +files concurrently, and using a single instance will allow reusing some resources. + +See [this example](https://pkg.go.dev/github.com/klauspost/compress/zstd#example-ZipCompressor) for +how to compress and decompress files inside zip archives. + +# Contributions + +Contributions are always welcome. +For new features/fixes, remember to add tests and for performance enhancements include benchmarks. + +For general feedback and experience reports, feel free to open an issue or write me on [Twitter](https://twitter.com/sh0dan). + +This package includes the excellent [`github.com/cespare/xxhash`](https://github.com/cespare/xxhash) package Copyright (c) 2016 Caleb Spare. diff --git a/vendor/github.com/klauspost/compress/zstd/bitreader.go b/vendor/github.com/klauspost/compress/zstd/bitreader.go new file mode 100644 index 000000000..d41e3e170 --- /dev/null +++ b/vendor/github.com/klauspost/compress/zstd/bitreader.go @@ -0,0 +1,135 @@ +// Copyright 2019+ Klaus Post. All rights reserved. +// License information can be found in the LICENSE file. +// Based on work by Yann Collet, released under BSD License. + +package zstd + +import ( + "errors" + "fmt" + "io" + "math/bits" + + "github.com/klauspost/compress/internal/le" +) + +// bitReader reads a bitstream in reverse. +// The last set bit indicates the start of the stream and is used +// for aligning the input. +type bitReader struct { + in []byte + value uint64 // Maybe use [16]byte, but shifting is awkward. + cursor int // offset where next read should end + bitsRead uint8 +} + +// init initializes and resets the bit reader. +func (b *bitReader) init(in []byte) error { + if len(in) < 1 { + return errors.New("corrupt stream: too short") + } + b.in = in + // The highest bit of the last byte indicates where to start + v := in[len(in)-1] + if v == 0 { + return errors.New("corrupt stream, did not find end of stream") + } + b.cursor = len(in) + b.bitsRead = 64 + b.value = 0 + if len(in) >= 8 { + b.fillFastStart() + } else { + b.fill() + b.fill() + } + b.bitsRead += 8 - uint8(highBits(uint32(v))) + return nil +} + +// getBits will return n bits. n can be 0. +func (b *bitReader) getBits(n uint8) int { + if n == 0 /*|| b.bitsRead >= 64 */ { + return 0 + } + return int(b.get32BitsFast(n)) +} + +// get32BitsFast requires that at least one bit is requested every time. +// There are no checks if the buffer is filled. +func (b *bitReader) get32BitsFast(n uint8) uint32 { + const regMask = 64 - 1 + v := uint32((b.value << (b.bitsRead & regMask)) >> ((regMask + 1 - n) & regMask)) + b.bitsRead += n + return v +} + +// fillFast() will make sure at least 32 bits are available. +// There must be at least 4 bytes available. +func (b *bitReader) fillFast() { + if b.bitsRead < 32 { + return + } + b.cursor -= 4 + b.value = (b.value << 32) | uint64(le.Load32(b.in, b.cursor)) + b.bitsRead -= 32 +} + +// fillFastStart() assumes the bitreader is empty and there is at least 8 bytes to read. +func (b *bitReader) fillFastStart() { + b.cursor -= 8 + b.value = le.Load64(b.in, b.cursor) + b.bitsRead = 0 +} + +// fill() will make sure at least 32 bits are available. +func (b *bitReader) fill() { + if b.bitsRead < 32 { + return + } + if b.cursor >= 4 { + b.cursor -= 4 + b.value = (b.value << 32) | uint64(le.Load32(b.in, b.cursor)) + b.bitsRead -= 32 + return + } + + b.bitsRead -= uint8(8 * b.cursor) + for b.cursor > 0 { + b.cursor -= 1 + b.value = (b.value << 8) | uint64(b.in[b.cursor]) + } +} + +// finished returns true if all bits have been read from the bit stream. +func (b *bitReader) finished() bool { + return b.cursor == 0 && b.bitsRead >= 64 +} + +// overread returns true if more bits have been requested than is on the stream. +func (b *bitReader) overread() bool { + return b.bitsRead > 64 +} + +// remain returns the number of bits remaining. +func (b *bitReader) remain() uint { + return 8*uint(b.cursor) + 64 - uint(b.bitsRead) +} + +// close the bitstream and returns an error if out-of-buffer reads occurred. +func (b *bitReader) close() error { + // Release reference. + b.in = nil + b.cursor = 0 + if !b.finished() { + return fmt.Errorf("%d extra bits on block, should be 0", b.remain()) + } + if b.bitsRead > 64 { + return io.ErrUnexpectedEOF + } + return nil +} + +func highBits(val uint32) (n uint32) { + return uint32(bits.Len32(val) - 1) +} diff --git a/vendor/github.com/klauspost/compress/zstd/bitwriter.go b/vendor/github.com/klauspost/compress/zstd/bitwriter.go new file mode 100644 index 000000000..b22b297e6 --- /dev/null +++ b/vendor/github.com/klauspost/compress/zstd/bitwriter.go @@ -0,0 +1,112 @@ +// Copyright 2018 Klaus Post. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. +// Based on work Copyright (c) 2013, Yann Collet, released under BSD License. + +package zstd + +// bitWriter will write bits. +// First bit will be LSB of the first byte of output. +type bitWriter struct { + bitContainer uint64 + nBits uint8 + out []byte +} + +// bitMask16 is bitmasks. Has extra to avoid bounds check. +var bitMask16 = [32]uint16{ + 0, 1, 3, 7, 0xF, 0x1F, + 0x3F, 0x7F, 0xFF, 0x1FF, 0x3FF, 0x7FF, + 0xFFF, 0x1FFF, 0x3FFF, 0x7FFF, 0xFFFF, 0xFFFF, + 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, + 0xFFFF, 0xFFFF} /* up to 16 bits */ + +var bitMask32 = [32]uint32{ + 0, 1, 3, 7, 0xF, 0x1F, 0x3F, 0x7F, 0xFF, + 0x1FF, 0x3FF, 0x7FF, 0xFFF, 0x1FFF, 0x3FFF, 0x7FFF, 0xFFFF, + 0x1ffff, 0x3ffff, 0x7FFFF, 0xfFFFF, 0x1fFFFF, 0x3fFFFF, 0x7fFFFF, 0xffFFFF, + 0x1ffFFFF, 0x3ffFFFF, 0x7ffFFFF, 0xfffFFFF, 0x1fffFFFF, 0x3fffFFFF, 0x7fffFFFF, +} // up to 32 bits + +// addBits16NC will add up to 16 bits. +// It will not check if there is space for them, +// so the caller must ensure that it has flushed recently. +func (b *bitWriter) addBits16NC(value uint16, bits uint8) { + b.bitContainer |= uint64(value&bitMask16[bits&31]) << (b.nBits & 63) + b.nBits += bits +} + +// addBits32NC will add up to 31 bits. +// It will not check if there is space for them, +// so the caller must ensure that it has flushed recently. +func (b *bitWriter) addBits32NC(value uint32, bits uint8) { + b.bitContainer |= uint64(value&bitMask32[bits&31]) << (b.nBits & 63) + b.nBits += bits +} + +// addBits64NC will add up to 64 bits. +// There must be space for 32 bits. +func (b *bitWriter) addBits64NC(value uint64, bits uint8) { + if bits <= 31 { + b.addBits32Clean(uint32(value), bits) + return + } + b.addBits32Clean(uint32(value), 32) + b.flush32() + b.addBits32Clean(uint32(value>>32), bits-32) +} + +// addBits32Clean will add up to 32 bits. +// It will not check if there is space for them. +// The input must not contain more bits than specified. +func (b *bitWriter) addBits32Clean(value uint32, bits uint8) { + b.bitContainer |= uint64(value) << (b.nBits & 63) + b.nBits += bits +} + +// addBits16Clean will add up to 16 bits. value may not contain more set bits than indicated. +// It will not check if there is space for them, so the caller must ensure that it has flushed recently. +func (b *bitWriter) addBits16Clean(value uint16, bits uint8) { + b.bitContainer |= uint64(value) << (b.nBits & 63) + b.nBits += bits +} + +// flush32 will flush out, so there are at least 32 bits available for writing. +func (b *bitWriter) flush32() { + if b.nBits < 32 { + return + } + b.out = append(b.out, + byte(b.bitContainer), + byte(b.bitContainer>>8), + byte(b.bitContainer>>16), + byte(b.bitContainer>>24)) + b.nBits -= 32 + b.bitContainer >>= 32 +} + +// flushAlign will flush remaining full bytes and align to next byte boundary. +func (b *bitWriter) flushAlign() { + nbBytes := (b.nBits + 7) >> 3 + for i := range nbBytes { + b.out = append(b.out, byte(b.bitContainer>>(i*8))) + } + b.nBits = 0 + b.bitContainer = 0 +} + +// close will write the alignment bit and write the final byte(s) +// to the output. +func (b *bitWriter) close() { + // End mark + b.addBits16Clean(1, 1) + // flush until next byte. + b.flushAlign() +} + +// reset and continue writing by appending to out. +func (b *bitWriter) reset(out []byte) { + b.bitContainer = 0 + b.nBits = 0 + b.out = out +} diff --git a/vendor/github.com/klauspost/compress/zstd/blockdec.go b/vendor/github.com/klauspost/compress/zstd/blockdec.go new file mode 100644 index 000000000..2329e996f --- /dev/null +++ b/vendor/github.com/klauspost/compress/zstd/blockdec.go @@ -0,0 +1,712 @@ +// Copyright 2019+ Klaus Post. All rights reserved. +// License information can be found in the LICENSE file. +// Based on work by Yann Collet, released under BSD License. + +package zstd + +import ( + "errors" + "fmt" + "hash/crc32" + "io" + "sync" + + "github.com/klauspost/compress/huff0" + "github.com/klauspost/compress/zstd/internal/xxhash" +) + +type blockType uint8 + +//go:generate stringer -type=blockType,literalsBlockType,seqCompMode,tableIndex + +const ( + blockTypeRaw blockType = iota + blockTypeRLE + blockTypeCompressed + blockTypeReserved +) + +type literalsBlockType uint8 + +const ( + literalsBlockRaw literalsBlockType = iota + literalsBlockRLE + literalsBlockCompressed + literalsBlockTreeless +) + +const ( + // maxCompressedBlockSize is the biggest allowed compressed block size (128KB) + maxCompressedBlockSize = 128 << 10 + + compressedBlockOverAlloc = 16 + maxCompressedBlockSizeAlloc = 128<<10 + compressedBlockOverAlloc + + // Maximum possible block size (all Raw+Uncompressed). + maxBlockSize = (1 << 21) - 1 + + maxMatchLen = 131074 + maxSequences = 0x7f00 + 0xffff + + // We support slightly less than the reference decoder to be able to + // use ints on 32 bit archs. + maxOffsetBits = 30 +) + +var ( + huffDecoderPool = sync.Pool{New: func() any { + return &huff0.Scratch{} + }} + + fseDecoderPool = sync.Pool{New: func() any { + return &fseDecoder{} + }} +) + +type blockDec struct { + // Raw source data of the block. + data []byte + dataStorage []byte + + // Destination of the decoded data. + dst []byte + + // Buffer for literals data. + literalBuf []byte + + // Window size of the block. + WindowSize uint64 + + err error + + // Check against this crc, if hasCRC is true. + checkCRC uint32 + hasCRC bool + + // Frame to use for singlethreaded decoding. + // Should not be used by the decoder itself since parent may be another frame. + localFrame *frameDec + + sequence []seqVals + + async struct { + newHist *history + literals []byte + seqData []byte + seqSize int // Size of uncompressed sequences + fcs uint64 + } + + // Block is RLE, this is the size. + RLESize uint32 + + Type blockType + + // Is this the last block of a frame? + Last bool + + // Use less memory + lowMem bool +} + +func (b *blockDec) String() string { + if b == nil { + return "" + } + return fmt.Sprintf("Steam Size: %d, Type: %v, Last: %t, Window: %d", len(b.data), b.Type, b.Last, b.WindowSize) +} + +func newBlockDec(lowMem bool) *blockDec { + b := blockDec{ + lowMem: lowMem, + } + return &b +} + +// reset will reset the block. +// Input must be a start of a block and will be at the end of the block when returned. +func (b *blockDec) reset(br byteBuffer, windowSize uint64) error { + b.WindowSize = windowSize + tmp, err := br.readSmall(3) + if err != nil { + println("Reading block header:", err) + return err + } + bh := uint32(tmp[0]) | (uint32(tmp[1]) << 8) | (uint32(tmp[2]) << 16) + b.Last = bh&1 != 0 + b.Type = blockType((bh >> 1) & 3) + // find size. + cSize := int(bh >> 3) + maxSize := maxCompressedBlockSizeAlloc + switch b.Type { + case blockTypeReserved: + return ErrReservedBlockType + case blockTypeRLE: + if cSize > maxCompressedBlockSize || cSize > int(b.WindowSize) { + if debugDecoder { + printf("rle block too big: csize:%d block: %+v\n", uint64(cSize), b) + } + return ErrWindowSizeExceeded + } + b.RLESize = uint32(cSize) + if b.lowMem { + maxSize = cSize + } + cSize = 1 + case blockTypeCompressed: + if debugDecoder { + println("Data size on stream:", cSize) + } + b.RLESize = 0 + maxSize = maxCompressedBlockSizeAlloc + if windowSize < maxCompressedBlockSize && b.lowMem { + maxSize = int(windowSize) + compressedBlockOverAlloc + } + if cSize > maxCompressedBlockSize || uint64(cSize) > b.WindowSize { + if debugDecoder { + printf("compressed block too big: csize:%d block: %+v\n", uint64(cSize), b) + } + return ErrCompressedSizeTooBig + } + // Empty compressed blocks must at least be 2 bytes + // for Literals_Block_Type and one for Sequences_Section_Header. + if cSize < 2 { + return ErrBlockTooSmall + } + case blockTypeRaw: + if cSize > maxCompressedBlockSize || cSize > int(b.WindowSize) { + if debugDecoder { + printf("rle block too big: csize:%d block: %+v\n", uint64(cSize), b) + } + return ErrWindowSizeExceeded + } + + b.RLESize = 0 + // We do not need a destination for raw blocks. + maxSize = -1 + default: + panic("Invalid block type") + } + + // Read block data. + if _, ok := br.(*byteBuf); !ok && cap(b.dataStorage) < cSize { + // byteBuf doesn't need a destination buffer. + if b.lowMem || cSize > maxCompressedBlockSize { + b.dataStorage = make([]byte, 0, cSize+compressedBlockOverAlloc) + } else { + b.dataStorage = make([]byte, 0, maxCompressedBlockSizeAlloc) + } + } + b.data, err = br.readBig(cSize, b.dataStorage) + if err != nil { + if debugDecoder { + println("Reading block:", err, "(", cSize, ")", len(b.data)) + printf("%T", br) + } + return err + } + if cap(b.dst) <= maxSize { + b.dst = make([]byte, 0, maxSize+1) + } + return nil +} + +// sendEOF will make the decoder send EOF on this frame. +func (b *blockDec) sendErr(err error) { + b.Last = true + b.Type = blockTypeReserved + b.err = err +} + +// Close will release resources. +// Closed blockDec cannot be reset. +func (b *blockDec) Close() { +} + +// decodeBuf +func (b *blockDec) decodeBuf(hist *history) error { + switch b.Type { + case blockTypeRLE: + if cap(b.dst) < int(b.RLESize) { + if b.lowMem { + b.dst = make([]byte, b.RLESize) + } else { + b.dst = make([]byte, maxCompressedBlockSize) + } + } + b.dst = b.dst[:b.RLESize] + v := b.data[0] + for i := range b.dst { + b.dst[i] = v + } + hist.appendKeep(b.dst) + return nil + case blockTypeRaw: + hist.appendKeep(b.data) + return nil + case blockTypeCompressed: + saved := b.dst + // Append directly to history + if hist.ignoreBuffer == 0 { + b.dst = hist.b + hist.b = nil + } else { + b.dst = b.dst[:0] + } + err := b.decodeCompressed(hist) + if debugDecoder { + println("Decompressed to total", len(b.dst), "bytes, hash:", xxhash.Sum64(b.dst), "error:", err) + } + if hist.ignoreBuffer == 0 { + hist.b = b.dst + b.dst = saved + } else { + hist.appendKeep(b.dst) + } + return err + case blockTypeReserved: + // Used for returning errors. + return b.err + default: + panic("Invalid block type") + } +} + +func (b *blockDec) decodeLiterals(in []byte, hist *history) (remain []byte, err error) { + // There must be at least one byte for Literals_Block_Type and one for Sequences_Section_Header + if len(in) < 2 { + return in, ErrBlockTooSmall + } + + litType := literalsBlockType(in[0] & 3) + var litRegenSize int + var litCompSize int + sizeFormat := (in[0] >> 2) & 3 + var fourStreams bool + var literals []byte + switch litType { + case literalsBlockRaw, literalsBlockRLE: + switch sizeFormat { + case 0, 2: + // Regenerated_Size uses 5 bits (0-31). Literals_Section_Header uses 1 byte. + litRegenSize = int(in[0] >> 3) + in = in[1:] + case 1: + // Regenerated_Size uses 12 bits (0-4095). Literals_Section_Header uses 2 bytes. + litRegenSize = int(in[0]>>4) + (int(in[1]) << 4) + in = in[2:] + case 3: + // Regenerated_Size uses 20 bits (0-1048575). Literals_Section_Header uses 3 bytes. + if len(in) < 3 { + println("too small: litType:", litType, " sizeFormat", sizeFormat, len(in)) + return in, ErrBlockTooSmall + } + litRegenSize = int(in[0]>>4) + (int(in[1]) << 4) + (int(in[2]) << 12) + in = in[3:] + } + case literalsBlockCompressed, literalsBlockTreeless: + switch sizeFormat { + case 0, 1: + // Both Regenerated_Size and Compressed_Size use 10 bits (0-1023). + if len(in) < 3 { + println("too small: litType:", litType, " sizeFormat", sizeFormat, len(in)) + return in, ErrBlockTooSmall + } + n := uint64(in[0]>>4) + (uint64(in[1]) << 4) + (uint64(in[2]) << 12) + litRegenSize = int(n & 1023) + litCompSize = int(n >> 10) + fourStreams = sizeFormat == 1 + in = in[3:] + case 2: + fourStreams = true + if len(in) < 4 { + println("too small: litType:", litType, " sizeFormat", sizeFormat, len(in)) + return in, ErrBlockTooSmall + } + n := uint64(in[0]>>4) + (uint64(in[1]) << 4) + (uint64(in[2]) << 12) + (uint64(in[3]) << 20) + litRegenSize = int(n & 16383) + litCompSize = int(n >> 14) + in = in[4:] + case 3: + fourStreams = true + if len(in) < 5 { + println("too small: litType:", litType, " sizeFormat", sizeFormat, len(in)) + return in, ErrBlockTooSmall + } + n := uint64(in[0]>>4) + (uint64(in[1]) << 4) + (uint64(in[2]) << 12) + (uint64(in[3]) << 20) + (uint64(in[4]) << 28) + litRegenSize = int(n & 262143) + litCompSize = int(n >> 18) + in = in[5:] + } + } + if debugDecoder { + println("literals type:", litType, "litRegenSize:", litRegenSize, "litCompSize:", litCompSize, "sizeFormat:", sizeFormat, "4X:", fourStreams) + } + if litRegenSize > int(b.WindowSize) || litRegenSize > maxCompressedBlockSize { + return in, ErrWindowSizeExceeded + } + + switch litType { + case literalsBlockRaw: + if len(in) < litRegenSize { + println("too small: litType:", litType, " sizeFormat", sizeFormat, "remain:", len(in), "want:", litRegenSize) + return in, ErrBlockTooSmall + } + literals = in[:litRegenSize] + in = in[litRegenSize:] + //printf("Found %d uncompressed literals\n", litRegenSize) + case literalsBlockRLE: + if len(in) < 1 { + println("too small: litType:", litType, " sizeFormat", sizeFormat, "remain:", len(in), "want:", 1) + return in, ErrBlockTooSmall + } + if cap(b.literalBuf) < litRegenSize { + if b.lowMem { + b.literalBuf = make([]byte, litRegenSize, litRegenSize+compressedBlockOverAlloc) + } else { + b.literalBuf = make([]byte, litRegenSize, maxCompressedBlockSize+compressedBlockOverAlloc) + } + } + literals = b.literalBuf[:litRegenSize] + v := in[0] + for i := range literals { + literals[i] = v + } + in = in[1:] + if debugDecoder { + printf("Found %d RLE compressed literals\n", litRegenSize) + } + case literalsBlockTreeless: + if len(in) < litCompSize { + println("too small: litType:", litType, " sizeFormat", sizeFormat, "remain:", len(in), "want:", litCompSize) + return in, ErrBlockTooSmall + } + // Store compressed literals, so we defer decoding until we get history. + literals = in[:litCompSize] + in = in[litCompSize:] + if debugDecoder { + printf("Found %d compressed literals\n", litCompSize) + } + huff := hist.huffTree + if huff == nil { + return in, errors.New("literal block was treeless, but no history was defined") + } + // Ensure we have space to store it. + if cap(b.literalBuf) < litRegenSize { + if b.lowMem { + b.literalBuf = make([]byte, 0, litRegenSize+compressedBlockOverAlloc) + } else { + b.literalBuf = make([]byte, 0, maxCompressedBlockSize+compressedBlockOverAlloc) + } + } + var err error + // Use our out buffer. + huff.MaxDecodedSize = litRegenSize + if fourStreams { + literals, err = huff.Decoder().Decompress4X(b.literalBuf[:0:litRegenSize], literals) + } else { + literals, err = huff.Decoder().Decompress1X(b.literalBuf[:0:litRegenSize], literals) + } + // Make sure we don't leak our literals buffer + if err != nil { + println("decompressing literals:", err) + return in, err + } + if len(literals) != litRegenSize { + return in, fmt.Errorf("literal output size mismatch want %d, got %d", litRegenSize, len(literals)) + } + + case literalsBlockCompressed: + if len(in) < litCompSize { + println("too small: litType:", litType, " sizeFormat", sizeFormat, "remain:", len(in), "want:", litCompSize) + return in, ErrBlockTooSmall + } + literals = in[:litCompSize] + in = in[litCompSize:] + // Ensure we have space to store it. + if cap(b.literalBuf) < litRegenSize { + if b.lowMem { + b.literalBuf = make([]byte, 0, litRegenSize+compressedBlockOverAlloc) + } else { + b.literalBuf = make([]byte, 0, maxCompressedBlockSize+compressedBlockOverAlloc) + } + } + huff := hist.huffTree + if huff == nil || (hist.dict != nil && huff == hist.dict.litEnc) { + huff = huffDecoderPool.Get().(*huff0.Scratch) + if huff == nil { + huff = &huff0.Scratch{} + } + } + var err error + if debugDecoder { + println("huff table input:", len(literals), "CRC:", crc32.ChecksumIEEE(literals)) + } + huff, literals, err = huff0.ReadTable(literals, huff) + if err != nil { + println("reading huffman table:", err) + return in, err + } + hist.huffTree = huff + huff.MaxDecodedSize = litRegenSize + // Use our out buffer. + if fourStreams { + literals, err = huff.Decoder().Decompress4X(b.literalBuf[:0:litRegenSize], literals) + } else { + literals, err = huff.Decoder().Decompress1X(b.literalBuf[:0:litRegenSize], literals) + } + if err != nil { + println("decoding compressed literals:", err) + return in, err + } + // Make sure we don't leak our literals buffer + if len(literals) != litRegenSize { + return in, fmt.Errorf("literal output size mismatch want %d, got %d", litRegenSize, len(literals)) + } + // Re-cap to get extra size. + literals = b.literalBuf[:len(literals)] + if debugDecoder { + printf("Decompressed %d literals into %d bytes\n", litCompSize, litRegenSize) + } + } + hist.decoders.literals = literals + return in, nil +} + +// decodeCompressed will start decompressing a block. +func (b *blockDec) decodeCompressed(hist *history) error { + in := b.data + in, err := b.decodeLiterals(in, hist) + if err != nil { + return err + } + err = b.prepareSequences(in, hist) + if err != nil { + return err + } + if hist.decoders.nSeqs == 0 { + b.dst = append(b.dst, hist.decoders.literals...) + return nil + } + before := len(hist.decoders.out) + err = hist.decoders.decodeSync(hist.b[hist.ignoreBuffer:]) + if err != nil { + return err + } + if hist.decoders.maxSyncLen > 0 { + hist.decoders.maxSyncLen += uint64(before) + hist.decoders.maxSyncLen -= uint64(len(hist.decoders.out)) + } + b.dst = hist.decoders.out + hist.recentOffsets = hist.decoders.prevOffset + return nil +} + +func (b *blockDec) prepareSequences(in []byte, hist *history) (err error) { + if debugDecoder { + printf("prepareSequences: %d byte(s) input\n", len(in)) + } + // Decode Sequences + // https://github.com/facebook/zstd/blob/dev/doc/zstd_compression_format.md#sequences-section + if len(in) < 1 { + return ErrBlockTooSmall + } + var nSeqs int + seqHeader := in[0] + switch { + case seqHeader < 128: + nSeqs = int(seqHeader) + in = in[1:] + case seqHeader < 255: + if len(in) < 2 { + return ErrBlockTooSmall + } + nSeqs = int(seqHeader-128)<<8 | int(in[1]) + in = in[2:] + case seqHeader == 255: + if len(in) < 3 { + return ErrBlockTooSmall + } + nSeqs = 0x7f00 + int(in[1]) + (int(in[2]) << 8) + in = in[3:] + } + if nSeqs == 0 && len(in) != 0 { + // When no sequences, there should not be any more data... + if debugDecoder { + printf("prepareSequences: 0 sequences, but %d byte(s) left on stream\n", len(in)) + } + return ErrUnexpectedBlockSize + } + + var seqs = &hist.decoders + seqs.nSeqs = nSeqs + if nSeqs > 0 { + if len(in) < 1 { + return ErrBlockTooSmall + } + br := byteReader{b: in, off: 0} + compMode := br.Uint8() + br.advance(1) + if debugDecoder { + printf("Compression modes: 0b%b", compMode) + } + if compMode&3 != 0 { + return errors.New("corrupt block: reserved bits not zero") + } + for i := range uint(3) { + mode := seqCompMode((compMode >> (6 - i*2)) & 3) + if debugDecoder { + println("Table", tableIndex(i), "is", mode) + } + var seq *sequenceDec + switch tableIndex(i) { + case tableLiteralLengths: + seq = &seqs.litLengths + case tableOffsets: + seq = &seqs.offsets + case tableMatchLengths: + seq = &seqs.matchLengths + default: + panic("unknown table") + } + switch mode { + case compModePredefined: + if seq.fse != nil && !seq.fse.preDefined { + fseDecoderPool.Put(seq.fse) + } + seq.fse = &fsePredef[i] + case compModeRLE: + if br.remain() < 1 { + return ErrBlockTooSmall + } + v := br.Uint8() + br.advance(1) + if seq.fse == nil || seq.fse.preDefined { + seq.fse = fseDecoderPool.Get().(*fseDecoder) + } + symb, err := decSymbolValue(v, symbolTableX[i]) + if err != nil { + printf("RLE Transform table (%v) error: %v", tableIndex(i), err) + return err + } + seq.fse.setRLE(symb) + if debugDecoder { + printf("RLE set to 0x%x, code: %v", symb, v) + } + case compModeFSE: + if debugDecoder { + println("Reading table for", tableIndex(i)) + } + if seq.fse == nil || seq.fse.preDefined { + seq.fse = fseDecoderPool.Get().(*fseDecoder) + } + err := seq.fse.readNCount(&br, uint16(maxTableSymbol[i])) + if err != nil { + println("Read table error:", err) + return err + } + err = seq.fse.transform(symbolTableX[i]) + if err != nil { + println("Transform table error:", err) + return err + } + if debugDecoder { + println("Read table ok", "symbolLen:", seq.fse.symbolLen) + } + case compModeRepeat: + seq.repeat = true + } + if br.overread() { + return io.ErrUnexpectedEOF + } + } + in = br.unread() + } + if debugDecoder { + println("Literals:", len(seqs.literals), "hash:", xxhash.Sum64(seqs.literals), "and", seqs.nSeqs, "sequences.") + } + + if nSeqs == 0 { + if len(b.sequence) > 0 { + b.sequence = b.sequence[:0] + } + return nil + } + br := seqs.br + if br == nil { + br = &bitReader{} + } + if err := br.init(in); err != nil { + return err + } + + if err := seqs.initialize(br, hist, b.dst); err != nil { + println("initializing sequences:", err) + return err + } + + return nil +} + +func (b *blockDec) decodeSequences(hist *history) error { + if cap(b.sequence) < hist.decoders.nSeqs { + if b.lowMem { + b.sequence = make([]seqVals, 0, hist.decoders.nSeqs) + } else { + b.sequence = make([]seqVals, 0, 0x7F00+0xffff) + } + } + b.sequence = b.sequence[:hist.decoders.nSeqs] + if hist.decoders.nSeqs == 0 { + hist.decoders.seqSize = len(hist.decoders.literals) + return nil + } + hist.decoders.windowSize = hist.windowSize + hist.decoders.prevOffset = hist.recentOffsets + + err := hist.decoders.decode(b.sequence) + hist.recentOffsets = hist.decoders.prevOffset + return err +} + +func (b *blockDec) executeSequences(hist *history) error { + hbytes := hist.b + if len(hbytes) > hist.windowSize { + hbytes = hbytes[len(hbytes)-hist.windowSize:] + // We do not need history anymore. + if hist.dict != nil { + hist.dict.content = nil + } + } + hist.decoders.windowSize = hist.windowSize + hist.decoders.out = b.dst[:0] + err := hist.decoders.execute(b.sequence, hbytes) + if err != nil { + return err + } + return b.updateHistory(hist) +} + +func (b *blockDec) updateHistory(hist *history) error { + if len(b.data) > maxCompressedBlockSize { + return fmt.Errorf("compressed block size too large (%d)", len(b.data)) + } + // Set output and release references. + b.dst = hist.decoders.out + hist.recentOffsets = hist.decoders.prevOffset + + if b.Last { + // if last block we don't care about history. + println("Last block, no history returned") + hist.b = hist.b[:0] + return nil + } else { + hist.append(b.dst) + if debugDecoder { + println("Finished block with ", len(b.sequence), "sequences. Added", len(b.dst), "to history, now length", len(hist.b)) + } + } + hist.decoders.out, hist.decoders.literals = nil, nil + + return nil +} diff --git a/vendor/github.com/klauspost/compress/zstd/blockenc.go b/vendor/github.com/klauspost/compress/zstd/blockenc.go new file mode 100644 index 000000000..0e33aea44 --- /dev/null +++ b/vendor/github.com/klauspost/compress/zstd/blockenc.go @@ -0,0 +1,893 @@ +// Copyright 2019+ Klaus Post. All rights reserved. +// License information can be found in the LICENSE file. +// Based on work by Yann Collet, released under BSD License. + +package zstd + +import ( + "errors" + "fmt" + "math" + "math/bits" + "slices" + + "github.com/klauspost/compress/huff0" +) + +type blockEnc struct { + size int + literals []byte + sequences []seq + coders seqCoders + litEnc *huff0.Scratch + dictLitEnc *huff0.Scratch + wr bitWriter + + extraLits int + output []byte + recentOffsets [3]uint32 + prevRecentOffsets [3]uint32 + + last bool + lowMem bool +} + +// init should be used once the block has been created. +// If called more than once, the effect is the same as calling reset. +func (b *blockEnc) init() { + if b.lowMem { + // 1K literals + if cap(b.literals) < 1<<10 { + b.literals = make([]byte, 0, 1<<10) + } + const defSeqs = 20 + if cap(b.sequences) < defSeqs { + b.sequences = make([]seq, 0, defSeqs) + } + // 1K + if cap(b.output) < 1<<10 { + b.output = make([]byte, 0, 1<<10) + } + } else { + if cap(b.literals) < maxCompressedBlockSize { + b.literals = make([]byte, 0, maxCompressedBlockSize) + } + const defSeqs = 2000 + if cap(b.sequences) < defSeqs { + b.sequences = make([]seq, 0, defSeqs) + } + if cap(b.output) < maxCompressedBlockSize { + b.output = make([]byte, 0, maxCompressedBlockSize) + } + } + + if b.coders.mlEnc == nil { + b.coders.mlEnc = &fseEncoder{} + b.coders.mlPrev = &fseEncoder{} + b.coders.ofEnc = &fseEncoder{} + b.coders.ofPrev = &fseEncoder{} + b.coders.llEnc = &fseEncoder{} + b.coders.llPrev = &fseEncoder{} + } + b.litEnc = &huff0.Scratch{WantLogLess: 4} + b.reset(nil) +} + +// initNewEncode can be used to reset offsets and encoders to the initial state. +func (b *blockEnc) initNewEncode() { + b.recentOffsets = [3]uint32{1, 4, 8} + b.litEnc.Reuse = huff0.ReusePolicyNone + b.coders.setPrev(nil, nil, nil) + b.dictLitEnc = nil +} + +// reset will reset the block for a new encode, but in the same stream, +// meaning that state will be carried over, but the block content is reset. +// If a previous block is provided, the recent offsets are carried over. +func (b *blockEnc) reset(prev *blockEnc) { + b.extraLits = 0 + b.literals = b.literals[:0] + b.size = 0 + b.sequences = b.sequences[:0] + b.output = b.output[:0] + b.last = false + if prev != nil { + b.recentOffsets = prev.prevRecentOffsets + } + b.dictLitEnc = nil +} + +// reset will reset the block for a new encode, but in the same stream, +// meaning that state will be carried over, but the block content is reset. +// If a previous block is provided, the recent offsets are carried over. +func (b *blockEnc) swapEncoders(prev *blockEnc) { + b.coders.swap(&prev.coders) + b.litEnc, prev.litEnc = prev.litEnc, b.litEnc +} + +// blockHeader contains the information for a block header. +type blockHeader uint32 + +// setLast sets the 'last' indicator on a block. +func (h *blockHeader) setLast(b bool) { + if b { + *h = *h | 1 + } else { + const mask = (1 << 24) - 2 + *h = *h & mask + } +} + +// setSize will store the compressed size of a block. +func (h *blockHeader) setSize(v uint32) { + const mask = 7 + *h = (*h)&mask | blockHeader(v<<3) +} + +// setType sets the block type. +func (h *blockHeader) setType(t blockType) { + const mask = 1 | (((1 << 24) - 1) ^ 7) + *h = (*h & mask) | blockHeader(t<<1) +} + +// appendTo will append the block header to a slice. +func (h blockHeader) appendTo(b []byte) []byte { + return append(b, uint8(h), uint8(h>>8), uint8(h>>16)) +} + +// String returns a string representation of the block. +func (h blockHeader) String() string { + return fmt.Sprintf("Type: %d, Size: %d, Last:%t", (h>>1)&3, h>>3, h&1 == 1) +} + +// literalsHeader contains literals header information. +type literalsHeader uint64 + +// setType can be used to set the type of literal block. +func (h *literalsHeader) setType(t literalsBlockType) { + const mask = math.MaxUint64 - 3 + *h = (*h & mask) | literalsHeader(t) +} + +// setSize can be used to set a single size, for uncompressed and RLE content. +func (h *literalsHeader) setSize(regenLen int) { + inBits := bits.Len32(uint32(regenLen)) + // Only retain 2 bits + const mask = 3 + lh := uint64(*h & mask) + switch { + case inBits < 5: + lh |= (uint64(regenLen) << 3) | (1 << 60) + if debugEncoder { + got := int(lh>>3) & 0xff + if got != regenLen { + panic(fmt.Sprint("litRegenSize = ", regenLen, "(want) != ", got, "(got)")) + } + } + case inBits < 12: + lh |= (1 << 2) | (uint64(regenLen) << 4) | (2 << 60) + case inBits < 20: + lh |= (3 << 2) | (uint64(regenLen) << 4) | (3 << 60) + default: + panic(fmt.Errorf("internal error: block too big (%d)", regenLen)) + } + *h = literalsHeader(lh) +} + +// setSizes will set the size of a compressed literals section and the input length. +func (h *literalsHeader) setSizes(compLen, inLen int, single bool) { + compBits, inBits := bits.Len32(uint32(compLen)), bits.Len32(uint32(inLen)) + // Only retain 2 bits + const mask = 3 + lh := uint64(*h & mask) + switch { + case compBits <= 10 && inBits <= 10: + if !single { + lh |= 1 << 2 + } + lh |= (uint64(inLen) << 4) | (uint64(compLen) << (10 + 4)) | (3 << 60) + if debugEncoder { + const mmask = (1 << 24) - 1 + n := (lh >> 4) & mmask + if int(n&1023) != inLen { + panic(fmt.Sprint("regensize:", int(n&1023), "!=", inLen, inBits)) + } + if int(n>>10) != compLen { + panic(fmt.Sprint("compsize:", int(n>>10), "!=", compLen, compBits)) + } + } + case compBits <= 14 && inBits <= 14: + lh |= (2 << 2) | (uint64(inLen) << 4) | (uint64(compLen) << (14 + 4)) | (4 << 60) + if single { + panic("single stream used with more than 10 bits length.") + } + case compBits <= 18 && inBits <= 18: + lh |= (3 << 2) | (uint64(inLen) << 4) | (uint64(compLen) << (18 + 4)) | (5 << 60) + if single { + panic("single stream used with more than 10 bits length.") + } + default: + panic("internal error: block too big") + } + *h = literalsHeader(lh) +} + +// appendTo will append the literals header to a byte slice. +func (h literalsHeader) appendTo(b []byte) []byte { + size := uint8(h >> 60) + switch size { + case 1: + b = append(b, uint8(h)) + case 2: + b = append(b, uint8(h), uint8(h>>8)) + case 3: + b = append(b, uint8(h), uint8(h>>8), uint8(h>>16)) + case 4: + b = append(b, uint8(h), uint8(h>>8), uint8(h>>16), uint8(h>>24)) + case 5: + b = append(b, uint8(h), uint8(h>>8), uint8(h>>16), uint8(h>>24), uint8(h>>32)) + default: + panic(fmt.Errorf("internal error: literalsHeader has invalid size (%d)", size)) + } + return b +} + +// size returns the output size with currently set values. +func (h literalsHeader) size() int { + return int(h >> 60) +} + +func (h literalsHeader) String() string { + return fmt.Sprintf("Type: %d, SizeFormat: %d, Size: 0x%d, Bytes:%d", literalsBlockType(h&3), (h>>2)&3, h&((1<<60)-1)>>4, h>>60) +} + +// pushOffsets will push the recent offsets to the backup store. +func (b *blockEnc) pushOffsets() { + b.prevRecentOffsets = b.recentOffsets +} + +// pushOffsets will push the recent offsets to the backup store. +func (b *blockEnc) popOffsets() { + b.recentOffsets = b.prevRecentOffsets +} + +// matchOffset will adjust recent offsets and return the adjusted one, +// if it matches a previous offset. +func (b *blockEnc) matchOffset(offset, lits uint32) uint32 { + // Check if offset is one of the recent offsets. + // Adjusts the output offset accordingly. + // Gives a tiny bit of compression, typically around 1%. + if true { + if lits > 0 { + switch offset { + case b.recentOffsets[0]: + offset = 1 + case b.recentOffsets[1]: + b.recentOffsets[1] = b.recentOffsets[0] + b.recentOffsets[0] = offset + offset = 2 + case b.recentOffsets[2]: + b.recentOffsets[2] = b.recentOffsets[1] + b.recentOffsets[1] = b.recentOffsets[0] + b.recentOffsets[0] = offset + offset = 3 + default: + b.recentOffsets[2] = b.recentOffsets[1] + b.recentOffsets[1] = b.recentOffsets[0] + b.recentOffsets[0] = offset + offset += 3 + } + } else { + switch offset { + case b.recentOffsets[1]: + b.recentOffsets[1] = b.recentOffsets[0] + b.recentOffsets[0] = offset + offset = 1 + case b.recentOffsets[2]: + b.recentOffsets[2] = b.recentOffsets[1] + b.recentOffsets[1] = b.recentOffsets[0] + b.recentOffsets[0] = offset + offset = 2 + case b.recentOffsets[0] - 1: + b.recentOffsets[2] = b.recentOffsets[1] + b.recentOffsets[1] = b.recentOffsets[0] + b.recentOffsets[0] = offset + offset = 3 + default: + b.recentOffsets[2] = b.recentOffsets[1] + b.recentOffsets[1] = b.recentOffsets[0] + b.recentOffsets[0] = offset + offset += 3 + } + } + } else { + offset += 3 + } + return offset +} + +// encodeRaw can be used to set the output to a raw representation of supplied bytes. +func (b *blockEnc) encodeRaw(a []byte) { + var bh blockHeader + bh.setLast(b.last) + bh.setSize(uint32(len(a))) + bh.setType(blockTypeRaw) + b.output = bh.appendTo(b.output[:0]) + b.output = append(b.output, a...) + if debugEncoder { + println("Adding RAW block, length", len(a), "last:", b.last) + } +} + +// encodeRaw can be used to set the output to a raw representation of supplied bytes. +func (b *blockEnc) encodeRawTo(dst, src []byte) []byte { + var bh blockHeader + bh.setLast(b.last) + bh.setSize(uint32(len(src))) + bh.setType(blockTypeRaw) + dst = bh.appendTo(dst) + dst = append(dst, src...) + if debugEncoder { + println("Adding RAW block, length", len(src), "last:", b.last) + } + return dst +} + +// encodeLits can be used if the block is only litLen. +func (b *blockEnc) encodeLits(lits []byte, raw bool) error { + var bh blockHeader + bh.setLast(b.last) + bh.setSize(uint32(len(lits))) + + // Don't compress extremely small blocks + if len(lits) < 8 || (len(lits) < 32 && b.dictLitEnc == nil) || raw { + if debugEncoder { + println("Adding RAW block, length", len(lits), "last:", b.last) + } + bh.setType(blockTypeRaw) + b.output = bh.appendTo(b.output) + b.output = append(b.output, lits...) + return nil + } + + var ( + out []byte + reUsed, single bool + err error + ) + if b.dictLitEnc != nil { + b.litEnc.TransferCTable(b.dictLitEnc) + b.litEnc.Reuse = huff0.ReusePolicyAllow + b.dictLitEnc = nil + } + if len(lits) >= 1024 { + // Use 4 Streams. + out, reUsed, err = huff0.Compress4X(lits, b.litEnc) + } else if len(lits) > 16 { + // Use 1 stream + single = true + out, reUsed, err = huff0.Compress1X(lits, b.litEnc) + } else { + err = huff0.ErrIncompressible + } + if err == nil && len(out)+5 > len(lits) { + // If we are close, we may still be worse or equal to raw. + var lh literalsHeader + lh.setSizes(len(out), len(lits), single) + if len(out)+lh.size() >= len(lits) { + err = huff0.ErrIncompressible + } + } + switch err { + case huff0.ErrIncompressible: + if debugEncoder { + println("Adding RAW block, length", len(lits), "last:", b.last) + } + bh.setType(blockTypeRaw) + b.output = bh.appendTo(b.output) + b.output = append(b.output, lits...) + return nil + case huff0.ErrUseRLE: + if debugEncoder { + println("Adding RLE block, length", len(lits)) + } + bh.setType(blockTypeRLE) + b.output = bh.appendTo(b.output) + b.output = append(b.output, lits[0]) + return nil + case nil: + default: + return err + } + // Compressed... + // Now, allow reuse + b.litEnc.Reuse = huff0.ReusePolicyAllow + bh.setType(blockTypeCompressed) + var lh literalsHeader + if reUsed { + if debugEncoder { + println("Reused tree, compressed to", len(out)) + } + lh.setType(literalsBlockTreeless) + } else { + if debugEncoder { + println("New tree, compressed to", len(out), "tree size:", len(b.litEnc.OutTable)) + } + lh.setType(literalsBlockCompressed) + } + // Set sizes + lh.setSizes(len(out), len(lits), single) + bh.setSize(uint32(len(out) + lh.size() + 1)) + + // Write block headers. + b.output = bh.appendTo(b.output) + b.output = lh.appendTo(b.output) + // Add compressed data. + b.output = append(b.output, out...) + // No sequences. + b.output = append(b.output, 0) + return nil +} + +// encodeRLE will encode an RLE block. +func (b *blockEnc) encodeRLE(val byte, length uint32) { + var bh blockHeader + bh.setLast(b.last) + bh.setSize(length) + bh.setType(blockTypeRLE) + b.output = bh.appendTo(b.output) + b.output = append(b.output, val) +} + +// fuzzFseEncoder can be used to fuzz the FSE encoder. +func fuzzFseEncoder(data []byte) int { + if len(data) > maxSequences || len(data) < 2 { + return 0 + } + enc := fseEncoder{} + hist := enc.Histogram() + maxSym := uint8(0) + for i, v := range data { + v = v & 63 + data[i] = v + hist[v]++ + if v > maxSym { + maxSym = v + } + } + if maxSym == 0 { + // All 0 + return 0 + } + cnt := int(slices.Max(hist[:maxSym])) + if cnt == len(data) { + // RLE + return 0 + } + enc.HistogramFinished(maxSym, cnt) + err := enc.normalizeCount(len(data)) + if err != nil { + return 0 + } + _, err = enc.writeCount(nil) + if err != nil { + panic(err) + } + return 1 +} + +// encode will encode the block and append the output in b.output. +// Previous offset codes must be pushed if more blocks are expected. +func (b *blockEnc) encode(org []byte, raw, rawAllLits bool) error { + if len(b.sequences) == 0 { + return b.encodeLits(b.literals, rawAllLits) + } + if len(b.sequences) == 1 && len(org) > 0 && len(b.literals) <= 1 { + // Check common RLE cases. + seq := b.sequences[0] + if seq.litLen == uint32(len(b.literals)) && seq.offset-3 == 1 { + // Offset == 1 and 0 or 1 literals. + b.encodeRLE(org[0], b.sequences[0].matchLen+zstdMinMatch+seq.litLen) + return nil + } + } + + // We want some difference to at least account for the headers. + saved := b.size - len(b.literals) - (b.size >> 6) + if saved < 16 { + if org == nil { + return errIncompressible + } + b.popOffsets() + return b.encodeLits(org, rawAllLits) + } + + var bh blockHeader + var lh literalsHeader + bh.setLast(b.last) + bh.setType(blockTypeCompressed) + // Store offset of the block header. Needed when we know the size. + bhOffset := len(b.output) + b.output = bh.appendTo(b.output) + + var ( + out []byte + reUsed, single bool + err error + ) + if b.dictLitEnc != nil { + b.litEnc.TransferCTable(b.dictLitEnc) + b.litEnc.Reuse = huff0.ReusePolicyAllow + b.dictLitEnc = nil + } + if len(b.literals) >= 1024 && !raw { + // Use 4 Streams. + out, reUsed, err = huff0.Compress4X(b.literals, b.litEnc) + } else if len(b.literals) > 16 && !raw { + // Use 1 stream + single = true + out, reUsed, err = huff0.Compress1X(b.literals, b.litEnc) + } else { + err = huff0.ErrIncompressible + } + + if err == nil && len(out)+5 > len(b.literals) { + // If we are close, we may still be worse or equal to raw. + var lh literalsHeader + lh.setSize(len(b.literals)) + szRaw := lh.size() + lh.setSizes(len(out), len(b.literals), single) + szComp := lh.size() + if len(out)+szComp >= len(b.literals)+szRaw { + err = huff0.ErrIncompressible + } + } + switch err { + case huff0.ErrIncompressible: + lh.setType(literalsBlockRaw) + lh.setSize(len(b.literals)) + b.output = lh.appendTo(b.output) + b.output = append(b.output, b.literals...) + if debugEncoder { + println("Adding literals RAW, length", len(b.literals)) + } + case huff0.ErrUseRLE: + lh.setType(literalsBlockRLE) + lh.setSize(len(b.literals)) + b.output = lh.appendTo(b.output) + b.output = append(b.output, b.literals[0]) + if debugEncoder { + println("Adding literals RLE") + } + case nil: + // Compressed litLen... + if reUsed { + if debugEncoder { + println("reused tree") + } + lh.setType(literalsBlockTreeless) + } else { + if debugEncoder { + println("new tree, size:", len(b.litEnc.OutTable)) + } + lh.setType(literalsBlockCompressed) + if debugEncoder { + _, _, err := huff0.ReadTable(out, nil) + if err != nil { + panic(err) + } + } + } + lh.setSizes(len(out), len(b.literals), single) + if debugEncoder { + printf("Compressed %d literals to %d bytes", len(b.literals), len(out)) + println("Adding literal header:", lh) + } + b.output = lh.appendTo(b.output) + b.output = append(b.output, out...) + b.litEnc.Reuse = huff0.ReusePolicyAllow + if debugEncoder { + println("Adding literals compressed") + } + default: + if debugEncoder { + println("Adding literals ERROR:", err) + } + return err + } + // Sequence compression + + // Write the number of sequences + switch { + case len(b.sequences) < 128: + b.output = append(b.output, uint8(len(b.sequences))) + case len(b.sequences) < 0x7f00: // TODO: this could be wrong + n := len(b.sequences) + b.output = append(b.output, 128+uint8(n>>8), uint8(n)) + default: + n := len(b.sequences) - 0x7f00 + b.output = append(b.output, 255, uint8(n), uint8(n>>8)) + } + if debugEncoder { + println("Encoding", len(b.sequences), "sequences") + } + b.genCodes() + llEnc := b.coders.llEnc + ofEnc := b.coders.ofEnc + mlEnc := b.coders.mlEnc + err = llEnc.normalizeCount(len(b.sequences)) + if err != nil { + return err + } + err = ofEnc.normalizeCount(len(b.sequences)) + if err != nil { + return err + } + err = mlEnc.normalizeCount(len(b.sequences)) + if err != nil { + return err + } + + // Choose the best compression mode for each type. + // Will evaluate the new vs predefined and previous. + chooseComp := func(cur, prev, preDef *fseEncoder) (*fseEncoder, seqCompMode) { + // See if predefined/previous is better + hist := cur.count[:cur.symbolLen] + nSize := cur.approxSize(hist) + cur.maxHeaderSize() + predefSize := preDef.approxSize(hist) + prevSize := prev.approxSize(hist) + + // Add a small penalty for new encoders. + // Don't bother with extremely small (<2 byte gains). + nSize = nSize + (nSize+2*8*16)>>4 + switch { + case predefSize <= prevSize && predefSize <= nSize || forcePreDef: + if debugEncoder { + println("Using predefined", predefSize>>3, "<=", nSize>>3) + } + return preDef, compModePredefined + case prevSize <= nSize: + if debugEncoder { + println("Using previous", prevSize>>3, "<=", nSize>>3) + } + return prev, compModeRepeat + default: + if debugEncoder { + println("Using new, predef", predefSize>>3, ". previous:", prevSize>>3, ">", nSize>>3, "header max:", cur.maxHeaderSize()>>3, "bytes") + println("tl:", cur.actualTableLog, "symbolLen:", cur.symbolLen, "norm:", cur.norm[:cur.symbolLen], "hist", cur.count[:cur.symbolLen]) + } + return cur, compModeFSE + } + } + + // Write compression mode + var mode uint8 + if llEnc.useRLE { + mode |= uint8(compModeRLE) << 6 + llEnc.setRLE(b.sequences[0].llCode) + if debugEncoder { + println("llEnc.useRLE") + } + } else { + var m seqCompMode + llEnc, m = chooseComp(llEnc, b.coders.llPrev, &fsePredefEnc[tableLiteralLengths]) + mode |= uint8(m) << 6 + } + if ofEnc.useRLE { + mode |= uint8(compModeRLE) << 4 + ofEnc.setRLE(b.sequences[0].ofCode) + if debugEncoder { + println("ofEnc.useRLE") + } + } else { + var m seqCompMode + ofEnc, m = chooseComp(ofEnc, b.coders.ofPrev, &fsePredefEnc[tableOffsets]) + mode |= uint8(m) << 4 + } + + if mlEnc.useRLE { + mode |= uint8(compModeRLE) << 2 + mlEnc.setRLE(b.sequences[0].mlCode) + if debugEncoder { + println("mlEnc.useRLE, code: ", b.sequences[0].mlCode, "value", b.sequences[0].matchLen) + } + } else { + var m seqCompMode + mlEnc, m = chooseComp(mlEnc, b.coders.mlPrev, &fsePredefEnc[tableMatchLengths]) + mode |= uint8(m) << 2 + } + b.output = append(b.output, mode) + if debugEncoder { + printf("Compression modes: 0b%b", mode) + } + b.output, err = llEnc.writeCount(b.output) + if err != nil { + return err + } + start := len(b.output) + b.output, err = ofEnc.writeCount(b.output) + if err != nil { + return err + } + if false { + println("block:", b.output[start:], "tablelog", ofEnc.actualTableLog, "maxcount:", ofEnc.maxCount) + fmt.Printf("selected TableLog: %d, Symbol length: %d\n", ofEnc.actualTableLog, ofEnc.symbolLen) + for i, v := range ofEnc.norm[:ofEnc.symbolLen] { + fmt.Printf("%3d: %5d -> %4d \n", i, ofEnc.count[i], v) + } + } + b.output, err = mlEnc.writeCount(b.output) + if err != nil { + return err + } + + // Maybe in block? + wr := &b.wr + wr.reset(b.output) + + var ll, of, ml cState + + // Current sequence + seq := len(b.sequences) - 1 + s := b.sequences[seq] + llEnc.setBits(llBitsTable[:]) + mlEnc.setBits(mlBitsTable[:]) + ofEnc.setBits(nil) + + llTT, ofTT, mlTT := llEnc.ct.symbolTT[:256], ofEnc.ct.symbolTT[:256], mlEnc.ct.symbolTT[:256] + + // We have 3 bounds checks here (and in the loop). + // Since we are iterating backwards it is kinda hard to avoid. + llB, ofB, mlB := llTT[s.llCode], ofTT[s.ofCode], mlTT[s.mlCode] + ll.init(wr, &llEnc.ct, llB) + of.init(wr, &ofEnc.ct, ofB) + wr.flush32() + ml.init(wr, &mlEnc.ct, mlB) + + // Each of these lookups also generates a bounds check. + wr.addBits32NC(s.litLen, llB.outBits) + wr.addBits32NC(s.matchLen, mlB.outBits) + wr.flush32() + wr.addBits32NC(s.offset, ofB.outBits) + if debugSequences { + println("Encoded seq", seq, s, "codes:", s.llCode, s.mlCode, s.ofCode, "states:", ll.state, ml.state, of.state, "bits:", llB, mlB, ofB) + } + seq-- + // Store sequences in reverse... + for seq >= 0 { + s = b.sequences[seq] + + ofB := ofTT[s.ofCode] + wr.flush32() // tablelog max is below 8 for each, so it will fill max 24 bits. + //of.encode(ofB) + nbBitsOut := (uint32(of.state) + ofB.deltaNbBits) >> 16 + dstState := int32(of.state>>(nbBitsOut&15)) + int32(ofB.deltaFindState) + wr.addBits16NC(of.state, uint8(nbBitsOut)) + of.state = of.stateTable[dstState] + + // Accumulate extra bits. + outBits := ofB.outBits & 31 + extraBits := uint64(s.offset & bitMask32[outBits]) + extraBitsN := outBits + + mlB := mlTT[s.mlCode] + //ml.encode(mlB) + nbBitsOut = (uint32(ml.state) + mlB.deltaNbBits) >> 16 + dstState = int32(ml.state>>(nbBitsOut&15)) + int32(mlB.deltaFindState) + wr.addBits16NC(ml.state, uint8(nbBitsOut)) + ml.state = ml.stateTable[dstState] + + outBits = mlB.outBits & 31 + extraBits = extraBits<> 16 + dstState = int32(ll.state>>(nbBitsOut&15)) + int32(llB.deltaFindState) + wr.addBits16NC(ll.state, uint8(nbBitsOut)) + ll.state = ll.stateTable[dstState] + + outBits = llB.outBits & 31 + extraBits = extraBits<= b.size { + // Discard and encode as raw block. + b.output = b.encodeRawTo(b.output[:bhOffset], org) + b.popOffsets() + b.litEnc.Reuse = huff0.ReusePolicyNone + return nil + } + + // Size is output minus block header. + bh.setSize(uint32(len(b.output)-bhOffset) - 3) + if debugEncoder { + println("Rewriting block header", bh) + } + _ = bh.appendTo(b.output[bhOffset:bhOffset]) + b.coders.setPrev(llEnc, mlEnc, ofEnc) + return nil +} + +var errIncompressible = errors.New("incompressible") + +func (b *blockEnc) genCodes() { + if len(b.sequences) == 0 { + // nothing to do + return + } + if len(b.sequences) > math.MaxUint16 { + panic("can only encode up to 64K sequences") + } + // No bounds checks after here: + llH := b.coders.llEnc.Histogram() + ofH := b.coders.ofEnc.Histogram() + mlH := b.coders.mlEnc.Histogram() + for i := range llH { + llH[i] = 0 + } + for i := range ofH { + ofH[i] = 0 + } + for i := range mlH { + mlH[i] = 0 + } + + var llMax, ofMax, mlMax uint8 + for i := range b.sequences { + seq := &b.sequences[i] + v := llCode(seq.litLen) + seq.llCode = v + llH[v]++ + if v > llMax { + llMax = v + } + + v = ofCode(seq.offset) + seq.ofCode = v + ofH[v]++ + if v > ofMax { + ofMax = v + } + + v = mlCode(seq.matchLen) + seq.mlCode = v + mlH[v]++ + if v > mlMax { + mlMax = v + if debugAsserts && mlMax > maxMatchLengthSymbol { + panic(fmt.Errorf("mlMax > maxMatchLengthSymbol (%d), matchlen: %d", mlMax, seq.matchLen)) + } + } + } + if debugAsserts && mlMax > maxMatchLengthSymbol { + panic(fmt.Errorf("mlMax > maxMatchLengthSymbol (%d)", mlMax)) + } + if debugAsserts && ofMax > maxOffsetBits { + panic(fmt.Errorf("ofMax > maxOffsetBits (%d)", ofMax)) + } + if debugAsserts && llMax > maxLiteralLengthSymbol { + panic(fmt.Errorf("llMax > maxLiteralLengthSymbol (%d)", llMax)) + } + + b.coders.mlEnc.HistogramFinished(mlMax, int(slices.Max(mlH[:mlMax+1]))) + b.coders.ofEnc.HistogramFinished(ofMax, int(slices.Max(ofH[:ofMax+1]))) + b.coders.llEnc.HistogramFinished(llMax, int(slices.Max(llH[:llMax+1]))) +} diff --git a/vendor/github.com/klauspost/compress/zstd/blocktype_string.go b/vendor/github.com/klauspost/compress/zstd/blocktype_string.go new file mode 100644 index 000000000..01a01e486 --- /dev/null +++ b/vendor/github.com/klauspost/compress/zstd/blocktype_string.go @@ -0,0 +1,85 @@ +// Code generated by "stringer -type=blockType,literalsBlockType,seqCompMode,tableIndex"; DO NOT EDIT. + +package zstd + +import "strconv" + +func _() { + // An "invalid array index" compiler error signifies that the constant values have changed. + // Re-run the stringer command to generate them again. + var x [1]struct{} + _ = x[blockTypeRaw-0] + _ = x[blockTypeRLE-1] + _ = x[blockTypeCompressed-2] + _ = x[blockTypeReserved-3] +} + +const _blockType_name = "blockTypeRawblockTypeRLEblockTypeCompressedblockTypeReserved" + +var _blockType_index = [...]uint8{0, 12, 24, 43, 60} + +func (i blockType) String() string { + if i >= blockType(len(_blockType_index)-1) { + return "blockType(" + strconv.FormatInt(int64(i), 10) + ")" + } + return _blockType_name[_blockType_index[i]:_blockType_index[i+1]] +} +func _() { + // An "invalid array index" compiler error signifies that the constant values have changed. + // Re-run the stringer command to generate them again. + var x [1]struct{} + _ = x[literalsBlockRaw-0] + _ = x[literalsBlockRLE-1] + _ = x[literalsBlockCompressed-2] + _ = x[literalsBlockTreeless-3] +} + +const _literalsBlockType_name = "literalsBlockRawliteralsBlockRLEliteralsBlockCompressedliteralsBlockTreeless" + +var _literalsBlockType_index = [...]uint8{0, 16, 32, 55, 76} + +func (i literalsBlockType) String() string { + if i >= literalsBlockType(len(_literalsBlockType_index)-1) { + return "literalsBlockType(" + strconv.FormatInt(int64(i), 10) + ")" + } + return _literalsBlockType_name[_literalsBlockType_index[i]:_literalsBlockType_index[i+1]] +} +func _() { + // An "invalid array index" compiler error signifies that the constant values have changed. + // Re-run the stringer command to generate them again. + var x [1]struct{} + _ = x[compModePredefined-0] + _ = x[compModeRLE-1] + _ = x[compModeFSE-2] + _ = x[compModeRepeat-3] +} + +const _seqCompMode_name = "compModePredefinedcompModeRLEcompModeFSEcompModeRepeat" + +var _seqCompMode_index = [...]uint8{0, 18, 29, 40, 54} + +func (i seqCompMode) String() string { + if i >= seqCompMode(len(_seqCompMode_index)-1) { + return "seqCompMode(" + strconv.FormatInt(int64(i), 10) + ")" + } + return _seqCompMode_name[_seqCompMode_index[i]:_seqCompMode_index[i+1]] +} +func _() { + // An "invalid array index" compiler error signifies that the constant values have changed. + // Re-run the stringer command to generate them again. + var x [1]struct{} + _ = x[tableLiteralLengths-0] + _ = x[tableOffsets-1] + _ = x[tableMatchLengths-2] +} + +const _tableIndex_name = "tableLiteralLengthstableOffsetstableMatchLengths" + +var _tableIndex_index = [...]uint8{0, 19, 31, 48} + +func (i tableIndex) String() string { + if i >= tableIndex(len(_tableIndex_index)-1) { + return "tableIndex(" + strconv.FormatInt(int64(i), 10) + ")" + } + return _tableIndex_name[_tableIndex_index[i]:_tableIndex_index[i+1]] +} diff --git a/vendor/github.com/klauspost/compress/zstd/bytebuf.go b/vendor/github.com/klauspost/compress/zstd/bytebuf.go new file mode 100644 index 000000000..55a388553 --- /dev/null +++ b/vendor/github.com/klauspost/compress/zstd/bytebuf.go @@ -0,0 +1,131 @@ +// Copyright 2019+ Klaus Post. All rights reserved. +// License information can be found in the LICENSE file. +// Based on work by Yann Collet, released under BSD License. + +package zstd + +import ( + "fmt" + "io" +) + +type byteBuffer interface { + // Read up to 8 bytes. + // Returns io.ErrUnexpectedEOF if this cannot be satisfied. + readSmall(n int) ([]byte, error) + + // Read >8 bytes. + // MAY use the destination slice. + readBig(n int, dst []byte) ([]byte, error) + + // Read a single byte. + readByte() (byte, error) + + // Skip n bytes. + skipN(n int64) error +} + +// in-memory buffer +type byteBuf []byte + +func (b *byteBuf) readSmall(n int) ([]byte, error) { + if debugAsserts && n > 8 { + panic(fmt.Errorf("small read > 8 (%d). use readBig", n)) + } + bb := *b + if len(bb) < n { + return nil, io.ErrUnexpectedEOF + } + r := bb[:n] + *b = bb[n:] + return r, nil +} + +func (b *byteBuf) readBig(n int, dst []byte) ([]byte, error) { + bb := *b + if len(bb) < n { + return nil, io.ErrUnexpectedEOF + } + r := bb[:n] + *b = bb[n:] + return r, nil +} + +func (b *byteBuf) readByte() (byte, error) { + bb := *b + if len(bb) < 1 { + return 0, io.ErrUnexpectedEOF + } + r := bb[0] + *b = bb[1:] + return r, nil +} + +func (b *byteBuf) skipN(n int64) error { + bb := *b + if n < 0 { + return fmt.Errorf("negative skip (%d) requested", n) + } + if int64(len(bb)) < n { + return io.ErrUnexpectedEOF + } + *b = bb[n:] + return nil +} + +// wrapper around a reader. +type readerWrapper struct { + r io.Reader + tmp [8]byte +} + +func (r *readerWrapper) readSmall(n int) ([]byte, error) { + if debugAsserts && n > 8 { + panic(fmt.Errorf("small read > 8 (%d). use readBig", n)) + } + n2, err := io.ReadFull(r.r, r.tmp[:n]) + // We only really care about the actual bytes read. + if err != nil { + if err == io.EOF { + return nil, io.ErrUnexpectedEOF + } + if debugDecoder { + println("readSmall: got", n2, "want", n, "err", err) + } + return nil, err + } + return r.tmp[:n], nil +} + +func (r *readerWrapper) readBig(n int, dst []byte) ([]byte, error) { + if cap(dst) < n { + dst = make([]byte, n) + } + n2, err := io.ReadFull(r.r, dst[:n]) + if err == io.EOF && n > 0 { + err = io.ErrUnexpectedEOF + } + return dst[:n2], err +} + +func (r *readerWrapper) readByte() (byte, error) { + n2, err := io.ReadFull(r.r, r.tmp[:1]) + if err != nil { + if err == io.EOF { + err = io.ErrUnexpectedEOF + } + return 0, err + } + if n2 != 1 { + return 0, io.ErrUnexpectedEOF + } + return r.tmp[0], nil +} + +func (r *readerWrapper) skipN(n int64) error { + n2, err := io.CopyN(io.Discard, r.r, n) + if n2 != n { + err = io.ErrUnexpectedEOF + } + return err +} diff --git a/vendor/github.com/klauspost/compress/zstd/bytereader.go b/vendor/github.com/klauspost/compress/zstd/bytereader.go new file mode 100644 index 000000000..0e59a242d --- /dev/null +++ b/vendor/github.com/klauspost/compress/zstd/bytereader.go @@ -0,0 +1,82 @@ +// Copyright 2019+ Klaus Post. All rights reserved. +// License information can be found in the LICENSE file. +// Based on work by Yann Collet, released under BSD License. + +package zstd + +// byteReader provides a byte reader that reads +// little endian values from a byte stream. +// The input stream is manually advanced. +// The reader performs no bounds checks. +type byteReader struct { + b []byte + off int +} + +// advance the stream b n bytes. +func (b *byteReader) advance(n uint) { + b.off += int(n) +} + +// overread returns whether we have advanced too far. +func (b *byteReader) overread() bool { + return b.off > len(b.b) +} + +// Int32 returns a little endian int32 starting at current offset. +func (b byteReader) Int32() int32 { + b2 := b.b[b.off:] + b2 = b2[:4] + v3 := int32(b2[3]) + v2 := int32(b2[2]) + v1 := int32(b2[1]) + v0 := int32(b2[0]) + return v0 | (v1 << 8) | (v2 << 16) | (v3 << 24) +} + +// Uint8 returns the next byte +func (b *byteReader) Uint8() uint8 { + v := b.b[b.off] + return v +} + +// Uint32 returns a little endian uint32 starting at current offset. +func (b byteReader) Uint32() uint32 { + if r := b.remain(); r < 4 { + // Very rare + v := uint32(0) + for i := 1; i <= r; i++ { + v = (v << 8) | uint32(b.b[len(b.b)-i]) + } + return v + } + b2 := b.b[b.off:] + b2 = b2[:4] + v3 := uint32(b2[3]) + v2 := uint32(b2[2]) + v1 := uint32(b2[1]) + v0 := uint32(b2[0]) + return v0 | (v1 << 8) | (v2 << 16) | (v3 << 24) +} + +// Uint32NC returns a little endian uint32 starting at current offset. +// The caller must be sure if there are at least 4 bytes left. +func (b byteReader) Uint32NC() uint32 { + b2 := b.b[b.off:] + b2 = b2[:4] + v3 := uint32(b2[3]) + v2 := uint32(b2[2]) + v1 := uint32(b2[1]) + v0 := uint32(b2[0]) + return v0 | (v1 << 8) | (v2 << 16) | (v3 << 24) +} + +// unread returns the unread portion of the input. +func (b byteReader) unread() []byte { + return b.b[b.off:] +} + +// remain will return the number of bytes remaining. +func (b byteReader) remain() int { + return len(b.b) - b.off +} diff --git a/vendor/github.com/klauspost/compress/zstd/decodeheader.go b/vendor/github.com/klauspost/compress/zstd/decodeheader.go new file mode 100644 index 000000000..6a5a2988b --- /dev/null +++ b/vendor/github.com/klauspost/compress/zstd/decodeheader.go @@ -0,0 +1,261 @@ +// Copyright 2020+ Klaus Post. All rights reserved. +// License information can be found in the LICENSE file. + +package zstd + +import ( + "encoding/binary" + "errors" + "io" +) + +// HeaderMaxSize is the maximum size of a Frame and Block Header. +// If less is sent to Header.Decode it *may* still contain enough information. +const HeaderMaxSize = 14 + 3 + +// Header contains information about the first frame and block within that. +type Header struct { + // SingleSegment specifies whether the data is to be decompressed into a + // single contiguous memory segment. + // It implies that WindowSize is invalid and that FrameContentSize is valid. + SingleSegment bool + + // WindowSize is the window of data to keep while decoding. + // Will only be set if SingleSegment is false. + WindowSize uint64 + + // Dictionary ID. + // If 0, no dictionary. + DictionaryID uint32 + + // HasFCS specifies whether FrameContentSize has a valid value. + HasFCS bool + + // FrameContentSize is the expected uncompressed size of the entire frame. + FrameContentSize uint64 + + // Skippable will be true if the frame is meant to be skipped. + // This implies that FirstBlock.OK is false. + Skippable bool + + // SkippableID is the user-specific ID for the skippable frame. + // Valid values are between 0 to 15, inclusive. + SkippableID int + + // SkippableSize is the length of the user data to skip following + // the header. + SkippableSize uint32 + + // HeaderSize is the raw size of the frame header. + // + // For normal frames, it includes the size of the magic number and + // the size of the header (per section 3.1.1.1). + // It does not include the size for any data blocks (section 3.1.1.2) nor + // the size for the trailing content checksum. + // + // For skippable frames, this counts the size of the magic number + // along with the size of the size field of the payload. + // It does not include the size of the skippable payload itself. + // The total frame size is the HeaderSize plus the SkippableSize. + HeaderSize int + + // First block information. + FirstBlock struct { + // OK will be set if first block could be decoded. + OK bool + + // Is this the last block of a frame? + Last bool + + // Is the data compressed? + // If true CompressedSize will be populated. + // Unfortunately DecompressedSize cannot be determined + // without decoding the blocks. + Compressed bool + + // DecompressedSize is the expected decompressed size of the block. + // Will be 0 if it cannot be determined. + DecompressedSize int + + // CompressedSize of the data in the block. + // Does not include the block header. + // Will be equal to DecompressedSize if not Compressed. + CompressedSize int + } + + // If set there is a checksum present for the block content. + // The checksum field at the end is always 4 bytes long. + HasCheckSum bool +} + +// Decode the header from the beginning of the stream. +// This will decode the frame header and the first block header if enough bytes are provided. +// It is recommended to provide at least HeaderMaxSize bytes. +// If the frame header cannot be read an error will be returned. +// If there isn't enough input, io.ErrUnexpectedEOF is returned. +// The FirstBlock.OK will indicate if enough information was available to decode the first block header. +func (h *Header) Decode(in []byte) error { + _, err := h.DecodeAndStrip(in) + return err +} + +// DecodeAndStrip will decode the header from the beginning of the stream +// and on success return the remaining bytes. +// This will decode the frame header and the first block header if enough bytes are provided. +// It is recommended to provide at least HeaderMaxSize bytes. +// If the frame header cannot be read an error will be returned. +// If there isn't enough input, io.ErrUnexpectedEOF is returned. +// The FirstBlock.OK will indicate if enough information was available to decode the first block header. +func (h *Header) DecodeAndStrip(in []byte) (remain []byte, err error) { + *h = Header{} + if len(in) < 4 { + return nil, io.ErrUnexpectedEOF + } + h.HeaderSize += 4 + b, in := in[:4], in[4:] + if string(b) != frameMagic { + if string(b[1:4]) != skippableFrameMagic || b[0]&0xf0 != 0x50 { + return nil, ErrMagicMismatch + } + if len(in) < 4 { + return nil, io.ErrUnexpectedEOF + } + h.HeaderSize += 4 + h.Skippable = true + h.SkippableID = int(b[0] & 0xf) + h.SkippableSize = binary.LittleEndian.Uint32(in) + return in[4:], nil + } + + // Read Window_Descriptor + // https://github.com/facebook/zstd/blob/dev/doc/zstd_compression_format.md#window_descriptor + if len(in) < 1 { + return nil, io.ErrUnexpectedEOF + } + fhd, in := in[0], in[1:] + h.HeaderSize++ + h.SingleSegment = fhd&(1<<5) != 0 + h.HasCheckSum = fhd&(1<<2) != 0 + if fhd&(1<<3) != 0 { + return nil, errors.New("reserved bit set on frame header") + } + + if !h.SingleSegment { + if len(in) < 1 { + return nil, io.ErrUnexpectedEOF + } + var wd byte + wd, in = in[0], in[1:] + h.HeaderSize++ + windowLog := 10 + (wd >> 3) + windowBase := uint64(1) << windowLog + windowAdd := (windowBase / 8) * uint64(wd&0x7) + h.WindowSize = windowBase + windowAdd + } + + // Read Dictionary_ID + // https://github.com/facebook/zstd/blob/dev/doc/zstd_compression_format.md#dictionary_id + if size := fhd & 3; size != 0 { + if size == 3 { + size = 4 + } + if len(in) < int(size) { + return nil, io.ErrUnexpectedEOF + } + b, in = in[:size], in[size:] + h.HeaderSize += int(size) + switch len(b) { + case 1: + h.DictionaryID = uint32(b[0]) + case 2: + h.DictionaryID = uint32(b[0]) | (uint32(b[1]) << 8) + case 4: + h.DictionaryID = uint32(b[0]) | (uint32(b[1]) << 8) | (uint32(b[2]) << 16) | (uint32(b[3]) << 24) + } + } + + // Read Frame_Content_Size + // https://github.com/facebook/zstd/blob/dev/doc/zstd_compression_format.md#frame_content_size + var fcsSize int + v := fhd >> 6 + switch v { + case 0: + if h.SingleSegment { + fcsSize = 1 + } + default: + fcsSize = 1 << v + } + + if fcsSize > 0 { + h.HasFCS = true + if len(in) < fcsSize { + return nil, io.ErrUnexpectedEOF + } + b, in = in[:fcsSize], in[fcsSize:] + h.HeaderSize += int(fcsSize) + switch len(b) { + case 1: + h.FrameContentSize = uint64(b[0]) + case 2: + // When FCS_Field_Size is 2, the offset of 256 is added. + h.FrameContentSize = uint64(b[0]) | (uint64(b[1]) << 8) + 256 + case 4: + h.FrameContentSize = uint64(b[0]) | (uint64(b[1]) << 8) | (uint64(b[2]) << 16) | (uint64(b[3]) << 24) + case 8: + d1 := uint32(b[0]) | (uint32(b[1]) << 8) | (uint32(b[2]) << 16) | (uint32(b[3]) << 24) + d2 := uint32(b[4]) | (uint32(b[5]) << 8) | (uint32(b[6]) << 16) | (uint32(b[7]) << 24) + h.FrameContentSize = uint64(d1) | (uint64(d2) << 32) + } + } + + // Frame Header done, we will not fail from now on. + if len(in) < 3 { + return in, nil + } + tmp := in[:3] + bh := uint32(tmp[0]) | (uint32(tmp[1]) << 8) | (uint32(tmp[2]) << 16) + h.FirstBlock.Last = bh&1 != 0 + blockType := blockType((bh >> 1) & 3) + // find size. + cSize := int(bh >> 3) + switch blockType { + case blockTypeReserved: + return in, nil + case blockTypeRLE: + h.FirstBlock.Compressed = true + h.FirstBlock.DecompressedSize = cSize + h.FirstBlock.CompressedSize = 1 + case blockTypeCompressed: + h.FirstBlock.Compressed = true + h.FirstBlock.CompressedSize = cSize + case blockTypeRaw: + h.FirstBlock.DecompressedSize = cSize + h.FirstBlock.CompressedSize = cSize + default: + panic("Invalid block type") + } + + h.FirstBlock.OK = true + return in, nil +} + +// AppendTo will append the encoded header to the dst slice. +// There is no error checking performed on the header values. +func (h *Header) AppendTo(dst []byte) ([]byte, error) { + if h.Skippable { + magic := [4]byte{0x50, 0x2a, 0x4d, 0x18} + magic[0] |= byte(h.SkippableID & 0xf) + dst = append(dst, magic[:]...) + f := h.SkippableSize + return append(dst, uint8(f), uint8(f>>8), uint8(f>>16), uint8(f>>24)), nil + } + f := frameHeader{ + ContentSize: h.FrameContentSize, + WindowSize: uint32(h.WindowSize), + SingleSegment: h.SingleSegment, + Checksum: h.HasCheckSum, + DictID: h.DictionaryID, + } + return f.appendTo(dst), nil +} diff --git a/vendor/github.com/klauspost/compress/zstd/decoder.go b/vendor/github.com/klauspost/compress/zstd/decoder.go new file mode 100644 index 000000000..c7e500f02 --- /dev/null +++ b/vendor/github.com/klauspost/compress/zstd/decoder.go @@ -0,0 +1,957 @@ +// Copyright 2019+ Klaus Post. All rights reserved. +// License information can be found in the LICENSE file. +// Based on work by Yann Collet, released under BSD License. + +package zstd + +import ( + "context" + "encoding/binary" + "io" + "sync" + + "github.com/klauspost/compress/zstd/internal/xxhash" +) + +// Decoder provides decoding of zstandard streams. +// The decoder has been designed to operate without allocations after a warmup. +// This means that you should store the decoder for best performance. +// To re-use a stream decoder, use the Reset(r io.Reader) error to switch to another stream. +// A decoder can safely be re-used even if the previous stream failed. +// To release the resources, you must call the Close() function on a decoder. +type Decoder struct { + o decoderOptions + + // Unreferenced decoders, ready for use. + decoders chan *blockDec + + // Current read position used for Reader functionality. + current decoderState + + // sync stream decoding + syncStream struct { + decodedFrame uint64 + br readerWrapper + enabled bool + inFrame bool + dstBuf []byte + } + + frame *frameDec + + // streamWg is the waitgroup for all streams + streamWg sync.WaitGroup +} + +// decoderState is used for maintaining state when the decoder +// is used for streaming. +type decoderState struct { + // current block being written to stream. + decodeOutput + + // output in order to be written to stream. + output chan decodeOutput + + // cancel remaining output. + cancel context.CancelFunc + + // crc of current frame + crc *xxhash.Digest + + flushed bool +} + +var ( + // Check the interfaces we want to support. + _ = io.WriterTo(&Decoder{}) + _ = io.Reader(&Decoder{}) +) + +// NewReader creates a new decoder. +// A nil Reader can be provided in which case Reset can be used to start a decode. +// +// A Decoder can be used in two modes: +// +// 1) As a stream, or +// 2) For stateless decoding using DecodeAll. +// +// Only a single stream can be decoded concurrently, but the same decoder +// can run multiple concurrent stateless decodes. It is even possible to +// use stateless decodes while a stream is being decoded. +// +// The Reset function can be used to initiate a new stream, which will considerably +// reduce the allocations normally caused by NewReader. +func NewReader(r io.Reader, opts ...DOption) (*Decoder, error) { + initPredefined() + var d Decoder + d.o.setDefault() + for _, o := range opts { + err := o(&d.o) + if err != nil { + return nil, err + } + } + d.current.crc = xxhash.New() + d.current.flushed = true + + if r == nil { + d.current.err = ErrDecoderNilInput + } + + // Initialize dict map if needed. + if d.o.dicts == nil { + d.o.dicts = make(map[uint32]*dict) + } + + // Create decoders + d.decoders = make(chan *blockDec, d.o.concurrent) + for i := 0; i < d.o.concurrent; i++ { + dec := newBlockDec(d.o.lowMem) + dec.localFrame = newFrameDec(d.o) + d.decoders <- dec + } + + if r == nil { + return &d, nil + } + return &d, d.Reset(r) +} + +// Read bytes from the decompressed stream into p. +// Returns the number of bytes read and any error that occurred. +// When the stream is done, io.EOF will be returned. +func (d *Decoder) Read(p []byte) (int, error) { + var n int + for { + if len(d.current.b) > 0 { + filled := copy(p, d.current.b) + p = p[filled:] + d.current.b = d.current.b[filled:] + n += filled + } + if len(p) == 0 { + break + } + if len(d.current.b) == 0 { + // We have an error and no more data + if d.current.err != nil { + break + } + if !d.nextBlock(n == 0) { + return n, d.current.err + } + } + } + if len(d.current.b) > 0 { + if debugDecoder { + println("returning", n, "still bytes left:", len(d.current.b)) + } + // Only return error at end of block + return n, nil + } + if d.current.err != nil { + d.drainOutput() + } + if debugDecoder { + println("returning", n, d.current.err, len(d.decoders)) + } + return n, d.current.err +} + +// Reset will reset the decoder the supplied stream after the current has finished processing. +// Note that this functionality cannot be used after Close has been called. +// Reset can be called with a nil reader to release references to the previous reader. +// After being called with a nil reader, no other operations than Reset or DecodeAll or Close +// should be used. +func (d *Decoder) Reset(r io.Reader) error { + if d.current.err == ErrDecoderClosed { + return d.current.err + } + + d.drainOutput() + + d.syncStream.br.r = nil + if r == nil { + d.current.err = ErrDecoderNilInput + if len(d.current.b) > 0 { + d.current.b = d.current.b[:0] + } + d.current.flushed = true + return nil + } + + // If bytes buffer and < 5MB, do sync decoding anyway. + if bb, ok := r.(byter); ok && bb.Len() < d.o.decodeBufsBelow && !d.o.limitToCap { + bb2 := bb + if debugDecoder { + println("*bytes.Buffer detected, doing sync decode, len:", bb.Len()) + } + b := bb2.Bytes() + var dst []byte + if cap(d.syncStream.dstBuf) > 0 { + dst = d.syncStream.dstBuf[:0] + } + + dst, err := d.DecodeAll(b, dst) + if err == nil { + err = io.EOF + } + // Save output buffer + d.syncStream.dstBuf = dst + d.current.b = dst + d.current.err = err + d.current.flushed = true + if debugDecoder { + println("sync decode to", len(dst), "bytes, err:", err) + } + return nil + } + // Remove current block. + d.stashDecoder() + d.current.decodeOutput = decodeOutput{} + d.current.err = nil + d.current.flushed = false + d.current.d = nil + d.syncStream.dstBuf = nil + + // Ensure no-one else is still running... + d.streamWg.Wait() + if d.frame == nil { + d.frame = newFrameDec(d.o) + } + + if d.o.concurrent == 1 { + return d.startSyncDecoder(r) + } + + d.current.output = make(chan decodeOutput, d.o.concurrent) + ctx, cancel := context.WithCancel(context.Background()) + d.current.cancel = cancel + d.streamWg.Add(1) + go d.startStreamDecoder(ctx, r, d.current.output) + + return nil +} + +// ResetWithOptions will reset the decoder and apply the given options +// for the next stream or DecodeAll operation. +// Options are applied on top of the existing options. +// Some options cannot be changed on reset and will return an error. +func (d *Decoder) ResetWithOptions(r io.Reader, opts ...DOption) error { + d.o.resetOpt = true + defer func() { d.o.resetOpt = false }() + for _, o := range opts { + if err := o(&d.o); err != nil { + return err + } + } + return d.Reset(r) +} + +// drainOutput will drain the output until errEndOfStream is sent. +func (d *Decoder) drainOutput() { + if d.current.cancel != nil { + if debugDecoder { + println("cancelling current") + } + d.current.cancel() + d.current.cancel = nil + } + if d.current.d != nil { + if debugDecoder { + printf("re-adding current decoder %p, decoders: %d", d.current.d, len(d.decoders)) + } + d.decoders <- d.current.d + d.current.d = nil + d.current.b = nil + } + if d.current.output == nil || d.current.flushed { + println("current already flushed") + return + } + for v := range d.current.output { + if v.d != nil { + if debugDecoder { + printf("re-adding decoder %p", v.d) + } + d.decoders <- v.d + } + } + d.current.output = nil + d.current.flushed = true +} + +// WriteTo writes data to w until there's no more data to write or when an error occurs. +// The return value n is the number of bytes written. +// Any error encountered during the write is also returned. +func (d *Decoder) WriteTo(w io.Writer) (int64, error) { + var n int64 + for { + if len(d.current.b) > 0 { + n2, err2 := w.Write(d.current.b) + n += int64(n2) + if err2 != nil && (d.current.err == nil || d.current.err == io.EOF) { + d.current.err = err2 + } else if n2 != len(d.current.b) { + d.current.err = io.ErrShortWrite + } + } + if d.current.err != nil { + break + } + d.nextBlock(true) + } + err := d.current.err + if err != nil { + d.drainOutput() + } + if err == io.EOF { + err = nil + } + return n, err +} + +// DecodeAll allows stateless decoding of a blob of bytes. +// Output will be appended to dst, so if the destination size is known +// you can pre-allocate the destination slice to avoid allocations. +// DecodeAll can be used concurrently. +// The Decoder concurrency limits will be respected. +func (d *Decoder) DecodeAll(input, dst []byte) ([]byte, error) { + if d.decoders == nil { + return dst, ErrDecoderClosed + } + + // Grab a block decoder and frame decoder. + block := <-d.decoders + frame := block.localFrame + initialSize := len(dst) + defer func() { + if debugDecoder { + printf("re-adding decoder: %p", block) + } + frame.rawInput = nil + frame.bBuf = nil + if frame.history.decoders.br != nil { + frame.history.decoders.br.in = nil + frame.history.decoders.br.cursor = 0 + } + d.decoders <- block + }() + frame.bBuf = input + + for { + frame.history.reset() + err := frame.reset(&frame.bBuf) + if err != nil { + if err == io.EOF { + if debugDecoder { + println("frame reset return EOF") + } + return dst, nil + } + return dst, err + } + if err = d.setDict(frame); err != nil { + return nil, err + } + if frame.WindowSize > d.o.maxWindowSize { + if debugDecoder { + println("window size exceeded:", frame.WindowSize, ">", d.o.maxWindowSize) + } + return dst, ErrWindowSizeExceeded + } + if frame.FrameContentSize != fcsUnknown { + if frame.FrameContentSize > d.o.maxDecodedSize-uint64(len(dst)-initialSize) { + if debugDecoder { + println("decoder size exceeded; fcs:", frame.FrameContentSize, "> mcs:", d.o.maxDecodedSize-uint64(len(dst)-initialSize), "len:", len(dst)) + } + return dst, ErrDecoderSizeExceeded + } + if d.o.limitToCap && frame.FrameContentSize > uint64(cap(dst)-len(dst)) { + if debugDecoder { + println("decoder size exceeded; fcs:", frame.FrameContentSize, "> (cap-len)", cap(dst)-len(dst)) + } + return dst, ErrDecoderSizeExceeded + } + if cap(dst)-len(dst) < int(frame.FrameContentSize) { + dst2 := make([]byte, len(dst), len(dst)+int(frame.FrameContentSize)+compressedBlockOverAlloc) + copy(dst2, dst) + dst = dst2 + } + } + + if cap(dst) == 0 && !d.o.limitToCap { + // Allocate len(input) * 2 by default if nothing is provided + // and we didn't get frame content size. + size := min( + // Cap to 1 MB. + len(input)*2, 1<<20) + if uint64(size) > d.o.maxDecodedSize { + size = int(d.o.maxDecodedSize) + } + dst = make([]byte, 0, size) + } + + dst, err = frame.runDecoder(dst, block) + if err != nil { + return dst, err + } + if uint64(len(dst)-initialSize) > d.o.maxDecodedSize { + return dst, ErrDecoderSizeExceeded + } + if len(frame.bBuf) == 0 { + if debugDecoder { + println("frame dbuf empty") + } + break + } + } + return dst, nil +} + +// nextBlock returns the next block. +// If an error occurs d.err will be set. +// Optionally the function can block for new output. +// If non-blocking mode is used the returned boolean will be false +// if no data was available without blocking. +func (d *Decoder) nextBlock(blocking bool) (ok bool) { + if d.current.err != nil { + // Keep error state. + return false + } + d.current.b = d.current.b[:0] + + // SYNC: + if d.syncStream.enabled { + if !blocking { + return false + } + ok = d.nextBlockSync() + if !ok { + d.stashDecoder() + } + return ok + } + + //ASYNC: + d.stashDecoder() + if blocking { + d.current.decodeOutput, ok = <-d.current.output + } else { + select { + case d.current.decodeOutput, ok = <-d.current.output: + default: + return false + } + } + if !ok { + // This should not happen, so signal error state... + d.current.err = io.ErrUnexpectedEOF + return false + } + next := d.current.decodeOutput + if next.d != nil && next.d.async.newHist != nil { + d.current.crc.Reset() + } + if debugDecoder { + var tmp [4]byte + binary.LittleEndian.PutUint32(tmp[:], uint32(xxhash.Sum64(next.b))) + println("got", len(d.current.b), "bytes, error:", d.current.err, "data crc:", tmp) + } + + if d.o.ignoreChecksum { + return true + } + + if len(next.b) > 0 { + d.current.crc.Write(next.b) + } + if next.err == nil && next.d != nil && next.d.hasCRC { + got := uint32(d.current.crc.Sum64()) + if got != next.d.checkCRC { + if debugDecoder { + printf("CRC Check Failed: %08x (got) != %08x (on stream)\n", got, next.d.checkCRC) + } + d.current.err = ErrCRCMismatch + } else { + if debugDecoder { + printf("CRC ok %08x\n", got) + } + } + } + + return true +} + +func (d *Decoder) nextBlockSync() (ok bool) { + if d.current.d == nil { + d.current.d = <-d.decoders + } + for len(d.current.b) == 0 { + if !d.syncStream.inFrame { + d.frame.history.reset() + d.current.err = d.frame.reset(&d.syncStream.br) + if d.current.err == nil { + d.current.err = d.setDict(d.frame) + } + if d.current.err != nil { + return false + } + if d.frame.WindowSize > d.o.maxDecodedSize || d.frame.WindowSize > d.o.maxWindowSize { + d.current.err = ErrDecoderSizeExceeded + return false + } + + d.syncStream.decodedFrame = 0 + d.syncStream.inFrame = true + } + d.current.err = d.frame.next(d.current.d) + if d.current.err != nil { + return false + } + d.frame.history.ensureBlock() + if debugDecoder { + println("History trimmed:", len(d.frame.history.b), "decoded already:", d.syncStream.decodedFrame) + } + histBefore := len(d.frame.history.b) + d.current.err = d.current.d.decodeBuf(&d.frame.history) + + if d.current.err != nil { + println("error after:", d.current.err) + return false + } + d.current.b = d.frame.history.b[histBefore:] + if debugDecoder { + println("history after:", len(d.frame.history.b)) + } + + // Check frame size (before CRC) + d.syncStream.decodedFrame += uint64(len(d.current.b)) + if d.syncStream.decodedFrame > d.frame.FrameContentSize { + if debugDecoder { + printf("DecodedFrame (%d) > FrameContentSize (%d)\n", d.syncStream.decodedFrame, d.frame.FrameContentSize) + } + d.current.err = ErrFrameSizeExceeded + return false + } + + // Check FCS + if d.current.d.Last && d.frame.FrameContentSize != fcsUnknown && d.syncStream.decodedFrame != d.frame.FrameContentSize { + if debugDecoder { + printf("DecodedFrame (%d) != FrameContentSize (%d)\n", d.syncStream.decodedFrame, d.frame.FrameContentSize) + } + d.current.err = ErrFrameSizeMismatch + return false + } + + // Update/Check CRC + if d.frame.HasCheckSum { + if !d.o.ignoreChecksum { + d.frame.crc.Write(d.current.b) + } + if d.current.d.Last { + if !d.o.ignoreChecksum { + d.current.err = d.frame.checkCRC() + } else { + d.current.err = d.frame.consumeCRC() + } + if d.current.err != nil { + println("CRC error:", d.current.err) + return false + } + } + } + d.syncStream.inFrame = !d.current.d.Last + } + return true +} + +func (d *Decoder) stashDecoder() { + if d.current.d != nil { + if debugDecoder { + printf("re-adding current decoder %p", d.current.d) + } + d.decoders <- d.current.d + d.current.d = nil + } +} + +// Close will release all resources. +// It is NOT possible to reuse the decoder after this. +func (d *Decoder) Close() { + if d.current.err == ErrDecoderClosed { + return + } + d.drainOutput() + if d.current.cancel != nil { + d.current.cancel() + d.streamWg.Wait() + d.current.cancel = nil + } + if d.decoders != nil { + close(d.decoders) + for dec := range d.decoders { + dec.Close() + } + d.decoders = nil + } + if d.current.d != nil { + d.current.d.Close() + d.current.d = nil + } + d.current.err = ErrDecoderClosed +} + +// IOReadCloser returns the decoder as an io.ReadCloser for convenience. +// Any changes to the decoder will be reflected, so the returned ReadCloser +// can be reused along with the decoder. +// io.WriterTo is also supported by the returned ReadCloser. +func (d *Decoder) IOReadCloser() io.ReadCloser { + return closeWrapper{d: d} +} + +// closeWrapper wraps a function call as a closer. +type closeWrapper struct { + d *Decoder +} + +// WriteTo forwards WriteTo calls to the decoder. +func (c closeWrapper) WriteTo(w io.Writer) (n int64, err error) { + return c.d.WriteTo(w) +} + +// Read forwards read calls to the decoder. +func (c closeWrapper) Read(p []byte) (n int, err error) { + return c.d.Read(p) +} + +// Close closes the decoder. +func (c closeWrapper) Close() error { + c.d.Close() + return nil +} + +type decodeOutput struct { + d *blockDec + b []byte + err error +} + +func (d *Decoder) startSyncDecoder(r io.Reader) error { + d.frame.history.reset() + d.syncStream.br = readerWrapper{r: r} + d.syncStream.inFrame = false + d.syncStream.enabled = true + d.syncStream.decodedFrame = 0 + return nil +} + +// Create Decoder: +// ASYNC: +// Spawn 3 go routines. +// 0: Read frames and decode block literals. +// 1: Decode sequences. +// 2: Execute sequences, send to output. +func (d *Decoder) startStreamDecoder(ctx context.Context, r io.Reader, output chan decodeOutput) { + defer d.streamWg.Done() + br := readerWrapper{r: r} + + var seqDecode = make(chan *blockDec, d.o.concurrent) + var seqExecute = make(chan *blockDec, d.o.concurrent) + + // Async 1: Decode sequences... + go func() { + var hist history + var hasErr bool + + for block := range seqDecode { + if hasErr { + if block != nil { + seqExecute <- block + } + continue + } + if block.async.newHist != nil { + if debugDecoder { + println("Async 1: new history, recent:", block.async.newHist.recentOffsets) + } + hist.reset() + hist.decoders = block.async.newHist.decoders + hist.recentOffsets = block.async.newHist.recentOffsets + hist.windowSize = block.async.newHist.windowSize + if block.async.newHist.dict != nil { + hist.setDict(block.async.newHist.dict) + } + } + if block.err != nil || block.Type != blockTypeCompressed { + hasErr = block.err != nil + seqExecute <- block + continue + } + + hist.decoders.literals = block.async.literals + block.err = block.prepareSequences(block.async.seqData, &hist) + if debugDecoder && block.err != nil { + println("prepareSequences returned:", block.err) + } + hasErr = block.err != nil + if block.err == nil { + block.err = block.decodeSequences(&hist) + if debugDecoder && block.err != nil { + println("decodeSequences returned:", block.err) + } + hasErr = block.err != nil + // block.async.sequence = hist.decoders.seq[:hist.decoders.nSeqs] + block.async.seqSize = hist.decoders.seqSize + } + seqExecute <- block + } + close(seqExecute) + hist.reset() + }() + + var wg sync.WaitGroup + wg.Add(1) + + // Async 3: Execute sequences... + frameHistCache := d.frame.history.b + go func() { + var hist history + var decodedFrame uint64 + var fcs uint64 + var hasErr bool + for block := range seqExecute { + out := decodeOutput{err: block.err, d: block} + if block.err != nil || hasErr { + hasErr = true + output <- out + continue + } + if block.async.newHist != nil { + if debugDecoder { + println("Async 2: new history") + } + hist.reset() + hist.windowSize = block.async.newHist.windowSize + hist.allocFrameBuffer = block.async.newHist.allocFrameBuffer + if block.async.newHist.dict != nil { + hist.setDict(block.async.newHist.dict) + } + + if cap(hist.b) < hist.allocFrameBuffer { + if cap(frameHistCache) >= hist.allocFrameBuffer { + hist.b = frameHistCache + } else { + hist.b = make([]byte, 0, hist.allocFrameBuffer) + println("Alloc history sized", hist.allocFrameBuffer) + } + } + hist.b = hist.b[:0] + fcs = block.async.fcs + decodedFrame = 0 + } + do := decodeOutput{err: block.err, d: block} + switch block.Type { + case blockTypeRLE: + if debugDecoder { + println("add rle block length:", block.RLESize) + } + + if cap(block.dst) < int(block.RLESize) { + if block.lowMem { + block.dst = make([]byte, block.RLESize) + } else { + block.dst = make([]byte, maxCompressedBlockSize) + } + } + block.dst = block.dst[:block.RLESize] + v := block.data[0] + for i := range block.dst { + block.dst[i] = v + } + hist.append(block.dst) + do.b = block.dst + case blockTypeRaw: + if debugDecoder { + println("add raw block length:", len(block.data)) + } + hist.append(block.data) + do.b = block.data + case blockTypeCompressed: + if debugDecoder { + println("execute with history length:", len(hist.b), "window:", hist.windowSize) + } + hist.decoders.seqSize = block.async.seqSize + hist.decoders.literals = block.async.literals + do.err = block.executeSequences(&hist) + hasErr = do.err != nil + if debugDecoder && hasErr { + println("executeSequences returned:", do.err) + } + do.b = block.dst + } + if !hasErr { + decodedFrame += uint64(len(do.b)) + if decodedFrame > fcs { + println("fcs exceeded", block.Last, fcs, decodedFrame) + do.err = ErrFrameSizeExceeded + hasErr = true + } else if block.Last && fcs != fcsUnknown && decodedFrame != fcs { + do.err = ErrFrameSizeMismatch + hasErr = true + } else { + if debugDecoder { + println("fcs ok", block.Last, fcs, decodedFrame) + } + } + } + output <- do + } + close(output) + frameHistCache = hist.b + wg.Done() + if debugDecoder { + println("decoder goroutines finished") + } + hist.reset() + }() + + var hist history +decodeStream: + for { + var hasErr bool + hist.reset() + decodeBlock := func(block *blockDec) { + if hasErr { + if block != nil { + seqDecode <- block + } + return + } + if block.err != nil || block.Type != blockTypeCompressed { + hasErr = block.err != nil + seqDecode <- block + return + } + + remain, err := block.decodeLiterals(block.data, &hist) + block.err = err + hasErr = block.err != nil + if err == nil { + block.async.literals = hist.decoders.literals + block.async.seqData = remain + } else if debugDecoder { + println("decodeLiterals error:", err) + } + seqDecode <- block + } + frame := d.frame + if debugDecoder { + println("New frame...") + } + var historySent bool + frame.history.reset() + err := frame.reset(&br) + if debugDecoder && err != nil { + println("Frame decoder returned", err) + } + if err == nil { + err = d.setDict(frame) + } + if err == nil && d.frame.WindowSize > d.o.maxWindowSize { + if debugDecoder { + println("decoder size exceeded, fws:", d.frame.WindowSize, "> mws:", d.o.maxWindowSize) + } + + err = ErrDecoderSizeExceeded + } + if err != nil { + select { + case <-ctx.Done(): + case dec := <-d.decoders: + dec.sendErr(err) + decodeBlock(dec) + } + break decodeStream + } + + // Go through all blocks of the frame. + for { + var dec *blockDec + select { + case <-ctx.Done(): + break decodeStream + case dec = <-d.decoders: + // Once we have a decoder, we MUST return it. + } + err := frame.next(dec) + if !historySent { + h := frame.history + if debugDecoder { + println("Alloc History:", h.allocFrameBuffer) + } + hist.reset() + if h.dict != nil { + hist.setDict(h.dict) + } + dec.async.newHist = &h + dec.async.fcs = frame.FrameContentSize + historySent = true + } else { + dec.async.newHist = nil + } + if debugDecoder && err != nil { + println("next block returned error:", err) + } + dec.err = err + dec.hasCRC = false + if dec.Last && frame.HasCheckSum && err == nil { + crc, err := frame.rawInput.readSmall(4) + if len(crc) < 4 { + if err == nil { + err = io.ErrUnexpectedEOF + + } + println("CRC missing?", err) + dec.err = err + } else { + dec.checkCRC = binary.LittleEndian.Uint32(crc) + dec.hasCRC = true + if debugDecoder { + printf("found crc to check: %08x\n", dec.checkCRC) + } + } + } + err = dec.err + last := dec.Last + decodeBlock(dec) + if err != nil { + break decodeStream + } + if last { + break + } + } + } + close(seqDecode) + wg.Wait() + hist.reset() + d.frame.history.b = frameHistCache +} + +func (d *Decoder) setDict(frame *frameDec) (err error) { + dict, ok := d.o.dicts[frame.DictionaryID] + if ok { + if debugDecoder { + println("setting dict", frame.DictionaryID) + } + frame.history.setDict(dict) + } else if frame.DictionaryID != 0 { + // A zero or missing dictionary id is ambiguous: + // either dictionary zero, or no dictionary. In particular, + // zstd --patch-from uses this id for the source file, + // so only return an error if the dictionary id is not zero. + err = ErrUnknownDictionary + } + return err +} diff --git a/vendor/github.com/klauspost/compress/zstd/decoder_options.go b/vendor/github.com/klauspost/compress/zstd/decoder_options.go new file mode 100644 index 000000000..537627a07 --- /dev/null +++ b/vendor/github.com/klauspost/compress/zstd/decoder_options.go @@ -0,0 +1,213 @@ +// Copyright 2019+ Klaus Post. All rights reserved. +// License information can be found in the LICENSE file. +// Based on work by Yann Collet, released under BSD License. + +package zstd + +import ( + "errors" + "fmt" + "math/bits" + "runtime" +) + +// DOption is an option for creating a decoder. +type DOption func(*decoderOptions) error + +// options retains accumulated state of multiple options. +type decoderOptions struct { + lowMem bool + concurrent int + maxDecodedSize uint64 + maxWindowSize uint64 + dicts map[uint32]*dict + ignoreChecksum bool + limitToCap bool + decodeBufsBelow int + resetOpt bool +} + +func (o *decoderOptions) setDefault() { + *o = decoderOptions{ + // use less ram: true for now, but may change. + lowMem: true, + concurrent: runtime.GOMAXPROCS(0), + maxWindowSize: MaxWindowSize, + decodeBufsBelow: 128 << 10, + } + if o.concurrent > 4 { + o.concurrent = 4 + } + o.maxDecodedSize = 64 << 30 +} + +// WithDecoderLowmem will set whether to use a lower amount of memory, +// but possibly have to allocate more while running. +// Cannot be changed with ResetWithOptions. +func WithDecoderLowmem(b bool) DOption { + return func(o *decoderOptions) error { + if o.resetOpt && b != o.lowMem { + return errors.New("WithDecoderLowmem cannot be changed on Reset") + } + o.lowMem = b + return nil + } +} + +// WithDecoderConcurrency sets the number of created decoders. +// When decoding block with DecodeAll, this will limit the number +// of possible concurrently running decodes. +// When decoding streams, this will limit the number of +// inflight blocks. +// When decoding streams and setting maximum to 1, +// no async decoding will be done. +// The value supplied must be at least 0. +// When a value of 0 is provided GOMAXPROCS will be used. +// By default this will be set to 4 or GOMAXPROCS, whatever is lower. +// Cannot be changed with ResetWithOptions. +func WithDecoderConcurrency(n int) DOption { + return func(o *decoderOptions) error { + if n < 0 { + return errors.New("concurrency must be at least 0") + } + newVal := n + if n == 0 { + newVal = runtime.GOMAXPROCS(0) + } + if o.resetOpt && newVal != o.concurrent { + return errors.New("WithDecoderConcurrency cannot be changed on Reset") + } + o.concurrent = newVal + return nil + } +} + +// WithDecoderMaxMemory allows to set a maximum decoded size for in-memory +// non-streaming operations or maximum window size for streaming operations. +// This can be used to control memory usage of potentially hostile content. +// Maximum is 1 << 63 bytes. Default is 64GiB. +// Can be changed with ResetWithOptions. +func WithDecoderMaxMemory(n uint64) DOption { + return func(o *decoderOptions) error { + if n == 0 { + return errors.New("WithDecoderMaxMemory must be at least 1") + } + if n > 1<<63 { + return errors.New("WithDecoderMaxmemory must be less than 1 << 63") + } + o.maxDecodedSize = n + return nil + } +} + +// WithDecoderDicts allows to register one or more dictionaries for the decoder. +// +// Each slice in dict must be in the [dictionary format] produced by +// "zstd --train" from the Zstandard reference implementation. +// +// If several dictionaries with the same ID are provided, the last one will be used. +// Can be changed with ResetWithOptions. +// +// [dictionary format]: https://github.com/facebook/zstd/blob/dev/doc/zstd_compression_format.md#dictionary-format +func WithDecoderDicts(dicts ...[]byte) DOption { + return func(o *decoderOptions) error { + if o.dicts == nil { + o.dicts = make(map[uint32]*dict) + } + for _, b := range dicts { + d, err := loadDict(b) + if err != nil { + return err + } + o.dicts[d.id] = d + } + return nil + } +} + +// WithDecoderDictRaw registers a dictionary that may be used by the decoder. +// The slice content can be arbitrary data. +// Can be changed with ResetWithOptions. +func WithDecoderDictRaw(id uint32, content []byte) DOption { + return func(o *decoderOptions) error { + if bits.UintSize > 32 && uint(len(content)) > dictMaxLength { + return fmt.Errorf("dictionary of size %d > 2GiB too large", len(content)) + } + if o.dicts == nil { + o.dicts = make(map[uint32]*dict) + } + o.dicts[id] = &dict{id: id, content: content, offsets: [3]int{1, 4, 8}} + return nil + } +} + +// WithDecoderMaxWindow allows to set a maximum window size for decodes. +// This allows rejecting packets that will cause big memory usage. +// The Decoder will likely allocate more memory based on the WithDecoderLowmem setting. +// If WithDecoderMaxMemory is set to a lower value, that will be used. +// Default is 512MB, Maximum is ~3.75 TB as per zstandard spec. +// Can be changed with ResetWithOptions. +func WithDecoderMaxWindow(size uint64) DOption { + return func(o *decoderOptions) error { + if size < MinWindowSize { + return errors.New("WithMaxWindowSize must be at least 1KB, 1024 bytes") + } + if size > (1<<41)+7*(1<<38) { + return errors.New("WithMaxWindowSize must be less than (1<<41) + 7*(1<<38) ~ 3.75TB") + } + o.maxWindowSize = size + return nil + } +} + +// WithDecodeAllCapLimit will limit DecodeAll to decoding cap(dst)-len(dst) bytes, +// or any size set in WithDecoderMaxMemory. +// This can be used to limit decoding to a specific maximum output size. +// Disabled by default. +// Can be changed with ResetWithOptions. +func WithDecodeAllCapLimit(b bool) DOption { + return func(o *decoderOptions) error { + o.limitToCap = b + return nil + } +} + +// WithDecodeBuffersBelow will fully decode readers that have a +// `Bytes() []byte` and `Len() int` interface similar to bytes.Buffer. +// This typically uses less allocations but will have the full decompressed object in memory. +// Note that DecodeAllCapLimit will disable this, as well as giving a size of 0 or less. +// Default is 128KiB. +// Cannot be changed with ResetWithOptions. +func WithDecodeBuffersBelow(size int) DOption { + return func(o *decoderOptions) error { + if o.resetOpt && size != o.decodeBufsBelow { + return errors.New("WithDecodeBuffersBelow cannot be changed on Reset") + } + o.decodeBufsBelow = size + return nil + } +} + +// IgnoreChecksum allows to forcibly ignore checksum checking. +// Can be changed with ResetWithOptions. +func IgnoreChecksum(b bool) DOption { + return func(o *decoderOptions) error { + o.ignoreChecksum = b + return nil + } +} + +// WithDecoderDictDelete removes dictionaries by ID. +// If no ids are passed, all dictionaries are deleted. +// Should be used with ResetWithOptions. +func WithDecoderDictDelete(ids ...uint32) DOption { + return func(o *decoderOptions) error { + if len(ids) == 0 { + clear(o.dicts) + } + for _, id := range ids { + delete(o.dicts, id) + } + return nil + } +} diff --git a/vendor/github.com/klauspost/compress/zstd/dict.go b/vendor/github.com/klauspost/compress/zstd/dict.go new file mode 100644 index 000000000..2ffbfdf37 --- /dev/null +++ b/vendor/github.com/klauspost/compress/zstd/dict.go @@ -0,0 +1,559 @@ +package zstd + +import ( + "bytes" + "encoding/binary" + "errors" + "fmt" + "io" + "math" + "sort" + + "github.com/klauspost/compress/huff0" +) + +type dict struct { + id uint32 + + litEnc *huff0.Scratch + llDec, ofDec, mlDec sequenceDec + offsets [3]int + content []byte +} + +const dictMagic = "\x37\xa4\x30\xec" + +// Maximum dictionary size for the reference implementation (1.5.3) is 2 GiB. +const dictMaxLength = 1 << 31 + +// ID returns the dictionary id or 0 if d is nil. +func (d *dict) ID() uint32 { + if d == nil { + return 0 + } + return d.id +} + +// ContentSize returns the dictionary content size or 0 if d is nil. +func (d *dict) ContentSize() int { + if d == nil { + return 0 + } + return len(d.content) +} + +// Content returns the dictionary content. +func (d *dict) Content() []byte { + if d == nil { + return nil + } + return d.content +} + +// Offsets returns the initial offsets. +func (d *dict) Offsets() [3]int { + if d == nil { + return [3]int{} + } + return d.offsets +} + +// LitEncoder returns the literal encoder. +func (d *dict) LitEncoder() *huff0.Scratch { + if d == nil { + return nil + } + return d.litEnc +} + +// Load a dictionary as described in +// https://github.com/facebook/zstd/blob/master/doc/zstd_compression_format.md#dictionary-format +func loadDict(b []byte) (*dict, error) { + // Check static field size. + if len(b) <= 8+(3*4) { + return nil, io.ErrUnexpectedEOF + } + d := dict{ + llDec: sequenceDec{fse: &fseDecoder{}}, + ofDec: sequenceDec{fse: &fseDecoder{}}, + mlDec: sequenceDec{fse: &fseDecoder{}}, + } + if string(b[:4]) != dictMagic { + return nil, ErrMagicMismatch + } + d.id = binary.LittleEndian.Uint32(b[4:8]) + if d.id == 0 { + return nil, errors.New("dictionaries cannot have ID 0") + } + + // Read literal table + var err error + d.litEnc, b, err = huff0.ReadTable(b[8:], nil) + if err != nil { + return nil, fmt.Errorf("loading literal table: %w", err) + } + d.litEnc.Reuse = huff0.ReusePolicyMust + + br := byteReader{ + b: b, + off: 0, + } + readDec := func(i tableIndex, dec *fseDecoder) error { + if err := dec.readNCount(&br, uint16(maxTableSymbol[i])); err != nil { + return err + } + if br.overread() { + return io.ErrUnexpectedEOF + } + err = dec.transform(symbolTableX[i]) + if err != nil { + println("Transform table error:", err) + return err + } + if debugDecoder || debugEncoder { + println("Read table ok", "symbolLen:", dec.symbolLen) + } + // Set decoders as predefined so they aren't reused. + dec.preDefined = true + return nil + } + + if err := readDec(tableOffsets, d.ofDec.fse); err != nil { + return nil, err + } + if err := readDec(tableMatchLengths, d.mlDec.fse); err != nil { + return nil, err + } + if err := readDec(tableLiteralLengths, d.llDec.fse); err != nil { + return nil, err + } + if br.remain() < 12 { + return nil, io.ErrUnexpectedEOF + } + + d.offsets[0] = int(br.Uint32()) + br.advance(4) + d.offsets[1] = int(br.Uint32()) + br.advance(4) + d.offsets[2] = int(br.Uint32()) + br.advance(4) + if d.offsets[0] <= 0 || d.offsets[1] <= 0 || d.offsets[2] <= 0 { + return nil, errors.New("invalid offset in dictionary") + } + d.content = make([]byte, br.remain()) + copy(d.content, br.unread()) + if d.offsets[0] > len(d.content) || d.offsets[1] > len(d.content) || d.offsets[2] > len(d.content) { + return nil, fmt.Errorf("initial offset bigger than dictionary content size %d, offsets: %v", len(d.content), d.offsets) + } + + return &d, nil +} + +// InspectDictionary loads a zstd dictionary and provides functions to inspect the content. +func InspectDictionary(b []byte) (interface { + ID() uint32 + ContentSize() int + Content() []byte + Offsets() [3]int + LitEncoder() *huff0.Scratch +}, error) { + initPredefined() + d, err := loadDict(b) + return d, err +} + +type BuildDictOptions struct { + // Dictionary ID. + ID uint32 + + // Content to use to create dictionary tables. + Contents [][]byte + + // History to use for all blocks. + History []byte + + // Offsets to use. + Offsets [3]int + + // CompatV155 will make the dictionary compatible with Zstd v1.5.5 and earlier. + // See https://github.com/facebook/zstd/issues/3724 + CompatV155 bool + + // Use the specified encoder level. + // The dictionary will be built using the specified encoder level, + // which will reflect speed and make the dictionary tailored for that level. + // If not set SpeedBestCompression will be used. + Level EncoderLevel + + // DebugOut will write stats and other details here if set. + DebugOut io.Writer +} + +func BuildDict(o BuildDictOptions) ([]byte, error) { + initPredefined() + hist := o.History + contents := o.Contents + debug := o.DebugOut != nil + println := func(args ...any) { + if o.DebugOut != nil { + fmt.Fprintln(o.DebugOut, args...) + } + } + printf := func(s string, args ...any) { + if o.DebugOut != nil { + fmt.Fprintf(o.DebugOut, s, args...) + } + } + print := func(args ...any) { + if o.DebugOut != nil { + fmt.Fprint(o.DebugOut, args...) + } + } + + if int64(len(hist)) > dictMaxLength { + return nil, fmt.Errorf("dictionary of size %d > %d", len(hist), int64(dictMaxLength)) + } + if len(hist) < 8 { + return nil, fmt.Errorf("dictionary of size %d < %d", len(hist), 8) + } + if len(contents) == 0 { + return nil, errors.New("no content provided") + } + d := dict{ + id: o.ID, + litEnc: nil, + llDec: sequenceDec{}, + ofDec: sequenceDec{}, + mlDec: sequenceDec{}, + offsets: o.Offsets, + content: hist, + } + block := blockEnc{lowMem: false} + block.init() + enc := encoder(&bestFastEncoder{fastBase: fastBase{maxMatchOff: int32(maxMatchLen), bufferReset: math.MaxInt32 - int32(maxMatchLen*2), lowMem: false}}) + if o.Level != 0 { + eOpts := encoderOptions{ + level: o.Level, + blockSize: maxMatchLen, + windowSize: maxMatchLen, + dict: &d, + lowMem: false, + } + enc = eOpts.encoder() + } else { + o.Level = SpeedBestCompression + } + var ( + remain [256]int + ll [256]int + ml [256]int + of [256]int + ) + addValues := func(dst *[256]int, src []byte) { + for _, v := range src { + dst[v]++ + } + } + addHist := func(dst *[256]int, src *[256]uint32) { + for i, v := range src { + dst[i] += int(v) + } + } + seqs := 0 + nUsed := 0 + litTotal := 0 + newOffsets := make(map[uint32]int, 1000) + for _, b := range contents { + block.reset(nil) + if len(b) < 8 { + continue + } + nUsed++ + enc.Reset(&d, true) + enc.Encode(&block, b) + addValues(&remain, block.literals) + litTotal += len(block.literals) + if len(block.sequences) == 0 { + continue + } + seqs += len(block.sequences) + block.genCodes() + addHist(&ll, block.coders.llEnc.Histogram()) + addHist(&ml, block.coders.mlEnc.Histogram()) + addHist(&of, block.coders.ofEnc.Histogram()) + for i, seq := range block.sequences { + if i > 3 { + break + } + offset := seq.offset + if offset == 0 { + continue + } + if int(offset) >= len(o.History) { + continue + } + if offset > 3 { + newOffsets[offset-3]++ + } else { + newOffsets[uint32(o.Offsets[offset-1])]++ + } + } + } + // Find most used offsets. + var sortedOffsets []uint32 + for k := range newOffsets { + sortedOffsets = append(sortedOffsets, k) + } + sort.Slice(sortedOffsets, func(i, j int) bool { + a, b := sortedOffsets[i], sortedOffsets[j] + if a == b { + // Prefer the longer offset + return sortedOffsets[i] > sortedOffsets[j] + } + return newOffsets[sortedOffsets[i]] > newOffsets[sortedOffsets[j]] + }) + if len(sortedOffsets) > 3 { + if debug { + print("Offsets:") + for i, v := range sortedOffsets { + if i > 20 { + break + } + printf("[%d: %d],", v, newOffsets[v]) + } + println("") + } + + sortedOffsets = sortedOffsets[:3] + } + for i, v := range sortedOffsets { + o.Offsets[i] = int(v) + } + if debug { + println("New repeat offsets", o.Offsets) + } + + if nUsed == 0 || seqs == 0 { + return nil, fmt.Errorf("%d blocks, %d sequences found", nUsed, seqs) + } + if debug { + println("Sequences:", seqs, "Blocks:", nUsed, "Literals:", litTotal) + } + if seqs/nUsed < 512 { + // Use 512 as minimum. + nUsed = seqs / 512 + if nUsed == 0 { + nUsed = 1 + } + } + copyHist := func(dst *fseEncoder, src *[256]int) ([]byte, error) { + hist := dst.Histogram() + var maxSym uint8 + var maxCount int + var fakeLength int + for i, v := range src { + if v > 0 { + v = v / nUsed + if v == 0 { + v = 1 + } + } + if v > maxCount { + maxCount = v + } + if v != 0 { + maxSym = uint8(i) + } + fakeLength += v + hist[i] = uint32(v) + } + + // Ensure we aren't trying to represent RLE. + if maxCount == fakeLength { + for i := range hist { + if uint8(i) == maxSym { + fakeLength++ + maxSym++ + hist[i+1] = 1 + if maxSym > 1 { + break + } + } + if hist[0] == 0 { + fakeLength++ + hist[i] = 1 + if maxSym > 1 { + break + } + } + } + } + + dst.HistogramFinished(maxSym, maxCount) + dst.reUsed = false + dst.useRLE = false + err := dst.normalizeCount(fakeLength) + if err != nil { + return nil, err + } + if debug { + println("RAW:", dst.count[:maxSym+1], "NORM:", dst.norm[:maxSym+1], "LEN:", fakeLength) + } + return dst.writeCount(nil) + } + if debug { + print("Literal lengths: ") + } + llTable, err := copyHist(block.coders.llEnc, &ll) + if err != nil { + return nil, err + } + if debug { + print("Match lengths: ") + } + mlTable, err := copyHist(block.coders.mlEnc, &ml) + if err != nil { + return nil, err + } + if debug { + print("Offsets: ") + } + ofTable, err := copyHist(block.coders.ofEnc, &of) + if err != nil { + return nil, err + } + + // Literal table + avgSize := min(litTotal, huff0.BlockSizeMax/2) + huffBuff := make([]byte, 0, avgSize) + // Target size + div := max(litTotal/avgSize, 1) + if debug { + println("Huffman weights:") + } + for i, n := range remain[:] { + if n > 0 { + n = n / div + // Allow all entries to be represented. + if n == 0 { + n = 1 + } + huffBuff = append(huffBuff, bytes.Repeat([]byte{byte(i)}, n)...) + if debug { + printf("[%d: %d], ", i, n) + } + } + } + if o.CompatV155 && remain[255]/div == 0 { + huffBuff = append(huffBuff, 255) + } + scratch := &huff0.Scratch{TableLog: 11} + for tries := range 255 { + scratch = &huff0.Scratch{TableLog: 11} + _, _, err = huff0.Compress1X(huffBuff, scratch) + if err == nil { + break + } + if debug { + printf("Try %d: Huffman error: %v\n", tries+1, err) + } + huffBuff = huffBuff[:0] + if tries == 250 { + if debug { + println("Huffman: Bailing out with predefined table") + } + + // Bail out.... Just generate something + huffBuff = append(huffBuff, bytes.Repeat([]byte{255}, 10000)...) + for i := range 128 { + huffBuff = append(huffBuff, byte(i)) + } + continue + } + if errors.Is(err, huff0.ErrIncompressible) { + // Try truncating least common. + for i, n := range remain[:] { + if n > 0 { + n = n / (div * (i + 1)) + if n > 0 { + huffBuff = append(huffBuff, bytes.Repeat([]byte{byte(i)}, n)...) + } + } + } + if o.CompatV155 && len(huffBuff) > 0 && huffBuff[len(huffBuff)-1] != 255 { + huffBuff = append(huffBuff, 255) + } + if len(huffBuff) == 0 { + huffBuff = append(huffBuff, 0, 255) + } + } + if errors.Is(err, huff0.ErrUseRLE) { + for i, n := range remain[:] { + n = n / (div * (i + 1)) + // Allow all entries to be represented. + if n == 0 { + n = 1 + } + huffBuff = append(huffBuff, bytes.Repeat([]byte{byte(i)}, n)...) + } + } + } + + var out bytes.Buffer + out.Write([]byte(dictMagic)) + out.Write(binary.LittleEndian.AppendUint32(nil, o.ID)) + out.Write(scratch.OutTable) + if debug { + println("huff table:", len(scratch.OutTable), "bytes") + println("of table:", len(ofTable), "bytes") + println("ml table:", len(mlTable), "bytes") + println("ll table:", len(llTable), "bytes") + } + out.Write(ofTable) + out.Write(mlTable) + out.Write(llTable) + out.Write(binary.LittleEndian.AppendUint32(nil, uint32(o.Offsets[0]))) + out.Write(binary.LittleEndian.AppendUint32(nil, uint32(o.Offsets[1]))) + out.Write(binary.LittleEndian.AppendUint32(nil, uint32(o.Offsets[2]))) + out.Write(hist) + if debug { + _, err := loadDict(out.Bytes()) + if err != nil { + panic(err) + } + i, err := InspectDictionary(out.Bytes()) + if err != nil { + panic(err) + } + println("ID:", i.ID()) + println("Content size:", i.ContentSize()) + println("Encoder:", i.LitEncoder() != nil) + println("Offsets:", i.Offsets()) + var totalSize int + for _, b := range contents { + totalSize += len(b) + } + + encWith := func(opts ...EOption) int { + enc, err := NewWriter(nil, opts...) + if err != nil { + panic(err) + } + defer enc.Close() + var dst []byte + var totalSize int + for _, b := range contents { + dst = enc.EncodeAll(b, dst[:0]) + totalSize += len(dst) + } + return totalSize + } + plain := encWith(WithEncoderLevel(o.Level)) + withDict := encWith(WithEncoderLevel(o.Level), WithEncoderDict(out.Bytes())) + println("Input size:", totalSize) + println("Plain Compressed:", plain) + println("Dict Compressed:", withDict) + println("Saved:", plain-withDict, (plain-withDict)/len(contents), "bytes per input (rounded down)") + } + return out.Bytes(), nil +} diff --git a/vendor/github.com/klauspost/compress/zstd/enc_base.go b/vendor/github.com/klauspost/compress/zstd/enc_base.go new file mode 100644 index 000000000..c4de134a7 --- /dev/null +++ b/vendor/github.com/klauspost/compress/zstd/enc_base.go @@ -0,0 +1,171 @@ +package zstd + +import ( + "fmt" + "math/bits" + + "github.com/klauspost/compress/zstd/internal/xxhash" +) + +const ( + dictShardBits = 7 +) + +type fastBase struct { + // cur is the offset at the start of hist + cur int32 + // maximum offset. Should be at least 2x block size. + maxMatchOff int32 + bufferReset int32 + hist []byte + crc *xxhash.Digest + tmp [8]byte + blk *blockEnc + lastDict *dict + lowMem bool +} + +// CRC returns the underlying CRC writer. +func (e *fastBase) CRC() *xxhash.Digest { + return e.crc +} + +// AppendCRC will append the CRC to the destination slice and return it. +func (e *fastBase) AppendCRC(dst []byte) []byte { + crc := e.crc.Sum(e.tmp[:0]) + dst = append(dst, crc[7], crc[6], crc[5], crc[4]) + return dst +} + +// WindowSize returns the window size of the encoder, +// or a window size small enough to contain the input size, if > 0. +func (e *fastBase) WindowSize(size int64) int32 { + if size > 0 && size < int64(e.maxMatchOff) { + b := max( + // Keep minimum window. + int32(1)< e.bufferReset { + panic(fmt.Sprintf("ecur (%d) > buffer reset (%d)", e.cur, e.bufferReset)) + } + // check if we have space already + if len(e.hist)+len(src) > cap(e.hist) { + if cap(e.hist) == 0 { + e.ensureHist(len(src)) + } else { + if cap(e.hist) < int(e.maxMatchOff+maxCompressedBlockSize) { + panic(fmt.Errorf("unexpected buffer cap %d, want at least %d with window %d", cap(e.hist), e.maxMatchOff+maxCompressedBlockSize, e.maxMatchOff)) + } + // Move down + offset := int32(len(e.hist)) - e.maxMatchOff + copy(e.hist[0:e.maxMatchOff], e.hist[offset:]) + e.cur += offset + e.hist = e.hist[:e.maxMatchOff] + } + } + s := int32(len(e.hist)) + e.hist = append(e.hist, src...) + return s +} + +// ensureHist will ensure that history can keep at least this many bytes. +func (e *fastBase) ensureHist(n int) { + if cap(e.hist) >= n { + return + } + l := e.maxMatchOff + if (e.lowMem && e.maxMatchOff > maxCompressedBlockSize) || e.maxMatchOff <= maxCompressedBlockSize { + l += maxCompressedBlockSize + } else { + l += e.maxMatchOff + } + // Make it at least 1MB. + if l < 1<<20 && !e.lowMem { + l = 1 << 20 + } + // Make it at least the requested size. + if l < int32(n) { + l = int32(n) + } + e.hist = make([]byte, 0, l) +} + +// useBlock will replace the block with the provided one, +// but transfer recent offsets from the previous. +func (e *fastBase) UseBlock(enc *blockEnc) { + enc.reset(e.blk) + e.blk = enc +} + +func (e *fastBase) matchlen(s, t int32, src []byte) int32 { + if debugAsserts { + if s < 0 { + err := fmt.Sprintf("s (%d) < 0", s) + panic(err) + } + if t < 0 { + err := fmt.Sprintf("t (%d) < 0", t) + panic(err) + } + if s-t > e.maxMatchOff { + err := fmt.Sprintf("s (%d) - t (%d) > maxMatchOff (%d)", s, t, e.maxMatchOff) + panic(err) + } + if len(src)-int(s) > maxCompressedBlockSize { + panic(fmt.Sprintf("len(src)-s (%d) > maxCompressedBlockSize (%d)", len(src)-int(s), maxCompressedBlockSize)) + } + } + return int32(matchLen(src[s:], src[t:])) +} + +// Reset the encoding table. +func (e *fastBase) resetBase(d *dict, singleBlock bool) { + if e.blk == nil { + e.blk = &blockEnc{lowMem: e.lowMem} + e.blk.init() + } else { + e.blk.reset(nil) + } + e.blk.initNewEncode() + if e.crc == nil { + e.crc = xxhash.New() + } else { + e.crc.Reset() + } + e.blk.dictLitEnc = nil + if d != nil { + low := e.lowMem + if singleBlock { + e.lowMem = true + } + e.ensureHist(d.ContentSize() + maxCompressedBlockSize) + e.lowMem = low + } + + // We offset current position so everything will be out of reach. + // If above reset line, history will be purged. + if e.cur < e.bufferReset { + e.cur += e.maxMatchOff + int32(len(e.hist)) + } + e.hist = e.hist[:0] + if d != nil { + // Set offsets (currently not used) + for i, off := range d.offsets { + e.blk.recentOffsets[i] = uint32(off) + e.blk.prevRecentOffsets[i] = e.blk.recentOffsets[i] + } + // Transfer litenc. + e.blk.dictLitEnc = d.litEnc + e.hist = append(e.hist, d.content...) + } +} diff --git a/vendor/github.com/klauspost/compress/zstd/enc_best.go b/vendor/github.com/klauspost/compress/zstd/enc_best.go new file mode 100644 index 000000000..851799322 --- /dev/null +++ b/vendor/github.com/klauspost/compress/zstd/enc_best.go @@ -0,0 +1,553 @@ +// Copyright 2019+ Klaus Post. All rights reserved. +// License information can be found in the LICENSE file. +// Based on work by Yann Collet, released under BSD License. + +package zstd + +import ( + "bytes" + "fmt" + + "github.com/klauspost/compress" +) + +const ( + bestLongTableBits = 22 // Bits used in the long match table + bestLongTableSize = 1 << bestLongTableBits // Size of the table + bestLongLen = 8 // Bytes used for table hash + + // Note: Increasing the short table bits or making the hash shorter + // can actually lead to compression degradation since it will 'steal' more from the + // long match table and match offsets are quite big. + // This greatly depends on the type of input. + bestShortTableBits = 18 // Bits used in the short match table + bestShortTableSize = 1 << bestShortTableBits // Size of the table + bestShortLen = 4 // Bytes used for table hash + +) + +type match struct { + offset int32 + s int32 + length int32 + rep int32 + est int32 +} + +const highScore = maxMatchLen * 8 + +// estBits will estimate output bits from predefined tables. +func (m *match) estBits(bitsPerByte int32) { + mlc := mlCode(uint32(m.length - zstdMinMatch)) + var ofc uint8 + if m.rep < 0 { + ofc = ofCode(uint32(m.s-m.offset) + 3) + } else { + ofc = ofCode(uint32(m.rep) & 3) + } + // Cost, excluding + ofTT, mlTT := fsePredefEnc[tableOffsets].ct.symbolTT[ofc], fsePredefEnc[tableMatchLengths].ct.symbolTT[mlc] + + // Add cost of match encoding... + m.est = int32(ofTT.outBits + mlTT.outBits) + m.est += int32(ofTT.deltaNbBits>>16 + mlTT.deltaNbBits>>16) + // Subtract savings compared to literal encoding... + m.est -= (m.length * bitsPerByte) >> 10 + if m.est > 0 { + // Unlikely gain.. + m.length = 0 + m.est = highScore + } +} + +// bestFastEncoder uses 2 tables, one for short matches (5 bytes) and one for long matches. +// The long match table contains the previous entry with the same hash, +// effectively making it a "chain" of length 2. +// When we find a long match we choose between the two values and select the longest. +// When we find a short match, after checking the long, we check if we can find a long at n+1 +// and that it is longer (lazy matching). +type bestFastEncoder struct { + fastBase + table [bestShortTableSize]prevEntry + longTable [bestLongTableSize]prevEntry + dictTable []prevEntry + dictLongTable []prevEntry +} + +// Encode improves compression... +func (e *bestFastEncoder) Encode(blk *blockEnc, src []byte) { + const ( + // Input margin is the number of bytes we read (8) + // and the maximum we will read ahead (2) + inputMargin = 8 + 4 + minNonLiteralBlockSize = 16 + ) + + // Protect against e.cur wraparound. + for e.cur >= e.bufferReset-int32(len(e.hist)) { + if len(e.hist) == 0 { + e.table = [bestShortTableSize]prevEntry{} + e.longTable = [bestLongTableSize]prevEntry{} + e.cur = e.maxMatchOff + break + } + // Shift down everything in the table that isn't already too far away. + minOff := e.cur + int32(len(e.hist)) - e.maxMatchOff + for i := range e.table[:] { + v := e.table[i].offset + v2 := e.table[i].prev + if v < minOff { + v = 0 + v2 = 0 + } else { + v = v - e.cur + e.maxMatchOff + if v2 < minOff { + v2 = 0 + } else { + v2 = v2 - e.cur + e.maxMatchOff + } + } + e.table[i] = prevEntry{ + offset: v, + prev: v2, + } + } + for i := range e.longTable[:] { + v := e.longTable[i].offset + v2 := e.longTable[i].prev + if v < minOff { + v = 0 + v2 = 0 + } else { + v = v - e.cur + e.maxMatchOff + if v2 < minOff { + v2 = 0 + } else { + v2 = v2 - e.cur + e.maxMatchOff + } + } + e.longTable[i] = prevEntry{ + offset: v, + prev: v2, + } + } + e.cur = e.maxMatchOff + break + } + + // Add block to history + s := e.addBlock(src) + blk.size = len(src) + + // Check RLE first + if len(src) > zstdMinMatch { + ml := matchLen(src[1:], src) + if ml == len(src)-1 { + blk.literals = append(blk.literals, src[0]) + blk.sequences = append(blk.sequences, seq{litLen: 1, matchLen: uint32(len(src)-1) - zstdMinMatch, offset: 1 + 3}) + return + } + } + + if len(src) < minNonLiteralBlockSize { + blk.extraLits = len(src) + blk.literals = blk.literals[:len(src)] + copy(blk.literals, src) + return + } + + // Use this to estimate literal cost. + // Scaled by 10 bits. + bitsPerByte := max( + // Huffman can never go < 1 bit/byte + int32((compress.ShannonEntropyBits(src)*1024)/len(src)), 1024) + + // Override src + src = e.hist + sLimit := int32(len(src)) - inputMargin + const kSearchStrength = 10 + + // nextEmit is where in src the next emitLiteral should start from. + nextEmit := s + + // Relative offsets + offset1 := int32(blk.recentOffsets[0]) + offset2 := int32(blk.recentOffsets[1]) + offset3 := int32(blk.recentOffsets[2]) + + addLiterals := func(s *seq, until int32) { + if until == nextEmit { + return + } + blk.literals = append(blk.literals, src[nextEmit:until]...) + s.litLen = uint32(until - nextEmit) + } + + if debugEncoder { + println("recent offsets:", blk.recentOffsets) + } + +encodeLoop: + for { + // We allow the encoder to optionally turn off repeat offsets across blocks + canRepeat := len(blk.sequences) > 2 + + if debugAsserts && canRepeat && offset1 == 0 { + panic("offset0 was 0") + } + + const goodEnough = 250 + + cv := load6432(src, s) + + nextHashL := hashLen(cv, bestLongTableBits, bestLongLen) + nextHashS := hashLen(cv, bestShortTableBits, bestShortLen) + candidateL := e.longTable[nextHashL] + candidateS := e.table[nextHashS] + + // Set m to a match at offset if it looks like that will improve compression. + improve := func(m *match, offset int32, s int32, first uint32, rep int32) { + delta := s - offset + if delta >= e.maxMatchOff || delta <= 0 || load3232(src, offset) != first { + return + } + // Try to quick reject if we already have a long match. + if m.length > 16 { + left := len(src) - int(m.s+m.length) + // If we are too close to the end, keep as is. + if left <= 0 { + return + } + checkLen := m.length - (s - m.s) - 8 + if left > 2 && checkLen > 4 { + // Check 4 bytes, 4 bytes from the end of the current match. + a := load3232(src, offset+checkLen) + b := load3232(src, s+checkLen) + if a != b { + return + } + } + } + l := 4 + e.matchlen(s+4, offset+4, src) + if m.rep <= 0 { + // Extend candidate match backwards as far as possible. + // Do not extend repeats as we can assume they are optimal + // and offsets change if s == nextEmit. + tMin := max(s-e.maxMatchOff, 0) + for offset > tMin && s > nextEmit && src[offset-1] == src[s-1] && l < maxMatchLength { + s-- + offset-- + l++ + } + } + if debugAsserts { + if offset >= s { + panic(fmt.Sprintf("offset: %d - s:%d - rep: %d - cur :%d - max: %d", offset, s, rep, e.cur, e.maxMatchOff)) + } + if !bytes.Equal(src[s:s+l], src[offset:offset+l]) { + panic(fmt.Sprintf("second match mismatch: %v != %v, first: %08x", src[s:s+4], src[offset:offset+4], first)) + } + } + cand := match{offset: offset, s: s, length: l, rep: rep} + cand.estBits(bitsPerByte) + if m.est >= highScore || cand.est-m.est+(cand.s-m.s)*bitsPerByte>>10 < 0 { + *m = cand + } + } + + best := match{s: s, est: highScore} + improve(&best, candidateL.offset-e.cur, s, uint32(cv), -1) + improve(&best, candidateL.prev-e.cur, s, uint32(cv), -1) + improve(&best, candidateS.offset-e.cur, s, uint32(cv), -1) + improve(&best, candidateS.prev-e.cur, s, uint32(cv), -1) + + if canRepeat && best.length < goodEnough { + if s == nextEmit { + // Check repeats straight after a match. + improve(&best, s-offset2, s, uint32(cv), 1|4) + improve(&best, s-offset3, s, uint32(cv), 2|4) + if offset1 > 1 { + improve(&best, s-(offset1-1), s, uint32(cv), 3|4) + } + } + + // If either no match or a non-repeat match, check at + 1 + if best.rep <= 0 { + cv32 := uint32(cv >> 8) + spp := s + 1 + improve(&best, spp-offset1, spp, cv32, 1) + improve(&best, spp-offset2, spp, cv32, 2) + improve(&best, spp-offset3, spp, cv32, 3) + if best.rep < 0 { + cv32 = uint32(cv >> 24) + spp += 2 + improve(&best, spp-offset1, spp, cv32, 1) + improve(&best, spp-offset2, spp, cv32, 2) + improve(&best, spp-offset3, spp, cv32, 3) + } + } + } + // Load next and check... + e.longTable[nextHashL] = prevEntry{offset: s + e.cur, prev: candidateL.offset} + e.table[nextHashS] = prevEntry{offset: s + e.cur, prev: candidateS.offset} + index0 := s + 1 + + // Look far ahead, unless we have a really long match already... + if best.length < goodEnough { + // No match found, move forward on input, no need to check forward... + if best.length < 4 { + s += 1 + (s-nextEmit)>>(kSearchStrength-1) + if s >= sLimit { + break encodeLoop + } + continue + } + + candidateS = e.table[hashLen(cv>>8, bestShortTableBits, bestShortLen)] + cv = load6432(src, s+1) + cv2 := load6432(src, s+2) + candidateL = e.longTable[hashLen(cv, bestLongTableBits, bestLongLen)] + candidateL2 := e.longTable[hashLen(cv2, bestLongTableBits, bestLongLen)] + + // Short at s+1 + improve(&best, candidateS.offset-e.cur, s+1, uint32(cv), -1) + // Long at s+1, s+2 + improve(&best, candidateL.offset-e.cur, s+1, uint32(cv), -1) + improve(&best, candidateL.prev-e.cur, s+1, uint32(cv), -1) + improve(&best, candidateL2.offset-e.cur, s+2, uint32(cv2), -1) + improve(&best, candidateL2.prev-e.cur, s+2, uint32(cv2), -1) + if false { + // Short at s+3. + // Too often worse... + improve(&best, e.table[hashLen(cv2>>8, bestShortTableBits, bestShortLen)].offset-e.cur, s+3, uint32(cv2>>8), -1) + } + + // Start check at a fixed offset to allow for a few mismatches. + // For this compression level 2 yields the best results. + // We cannot do this if we have already indexed this position. + const skipBeginning = 2 + if best.s > s-skipBeginning { + // See if we can find a better match by checking where the current best ends. + // Use that offset to see if we can find a better full match. + if sAt := best.s + best.length; sAt < sLimit { + nextHashL := hashLen(load6432(src, sAt), bestLongTableBits, bestLongLen) + candidateEnd := e.longTable[nextHashL] + + if off := candidateEnd.offset - e.cur - best.length + skipBeginning; off >= 0 { + improve(&best, off, best.s+skipBeginning, load3232(src, best.s+skipBeginning), -1) + if off := candidateEnd.prev - e.cur - best.length + skipBeginning; off >= 0 { + improve(&best, off, best.s+skipBeginning, load3232(src, best.s+skipBeginning), -1) + } + } + } + } + } + + if debugAsserts { + if best.offset >= best.s { + panic(fmt.Sprintf("best.offset > s: %d >= %d", best.offset, best.s)) + } + if best.s < nextEmit { + panic(fmt.Sprintf("s %d < nextEmit %d", best.s, nextEmit)) + } + if best.offset < s-e.maxMatchOff { + panic(fmt.Sprintf("best.offset < s-e.maxMatchOff: %d < %d", best.offset, s-e.maxMatchOff)) + } + if !bytes.Equal(src[best.s:best.s+best.length], src[best.offset:best.offset+best.length]) { + panic(fmt.Sprintf("match mismatch: %v != %v", src[best.s:best.s+best.length], src[best.offset:best.offset+best.length])) + } + } + + // We have a match, we can store the forward value + s = best.s + if best.rep > 0 { + var seq seq + seq.matchLen = uint32(best.length - zstdMinMatch) + addLiterals(&seq, best.s) + + // Repeat. If bit 4 is set, this is a non-lit repeat. + seq.offset = uint32(best.rep & 3) + if debugSequences { + println("repeat sequence", seq, "next s:", best.s, "off:", best.s-best.offset) + } + blk.sequences = append(blk.sequences, seq) + + // Index old s + 1 -> s - 1 + s = best.s + best.length + nextEmit = s + + // Index skipped... + end := min(s, sLimit+4) + off := index0 + e.cur + for index0 < end { + cv0 := load6432(src, index0) + h0 := hashLen(cv0, bestLongTableBits, bestLongLen) + h1 := hashLen(cv0, bestShortTableBits, bestShortLen) + e.longTable[h0] = prevEntry{offset: off, prev: e.longTable[h0].offset} + e.table[h1] = prevEntry{offset: off, prev: e.table[h1].offset} + off++ + index0++ + } + + switch best.rep { + case 2, 4 | 1: + offset1, offset2 = offset2, offset1 + case 3, 4 | 2: + offset1, offset2, offset3 = offset3, offset1, offset2 + case 4 | 3: + offset1, offset2, offset3 = offset1-1, offset1, offset2 + } + if s >= sLimit { + if debugEncoder { + println("repeat ended", s, best.length) + } + break encodeLoop + } + continue + } + + // A 4-byte match has been found. Update recent offsets. + // We'll later see if more than 4 bytes. + t := best.offset + offset1, offset2, offset3 = s-t, offset1, offset2 + + if debugAsserts && s <= t { + panic(fmt.Sprintf("s (%d) <= t (%d)", s, t)) + } + + if debugAsserts && int(offset1) > len(src) { + panic("invalid offset") + } + + // Write our sequence + var seq seq + l := best.length + seq.litLen = uint32(s - nextEmit) + seq.matchLen = uint32(l - zstdMinMatch) + if seq.litLen > 0 { + blk.literals = append(blk.literals, src[nextEmit:s]...) + } + seq.offset = uint32(s-t) + 3 + s += l + if debugSequences { + println("sequence", seq, "next s:", s) + } + blk.sequences = append(blk.sequences, seq) + nextEmit = s + + // Index old s + 1 -> s - 1 or sLimit + end := min(s, sLimit-4) + + off := index0 + e.cur + for index0 < end { + cv0 := load6432(src, index0) + h0 := hashLen(cv0, bestLongTableBits, bestLongLen) + h1 := hashLen(cv0, bestShortTableBits, bestShortLen) + e.longTable[h0] = prevEntry{offset: off, prev: e.longTable[h0].offset} + e.table[h1] = prevEntry{offset: off, prev: e.table[h1].offset} + index0++ + off++ + } + if s >= sLimit { + break encodeLoop + } + } + + if int(nextEmit) < len(src) { + blk.literals = append(blk.literals, src[nextEmit:]...) + blk.extraLits = len(src) - int(nextEmit) + } + blk.recentOffsets[0] = uint32(offset1) + blk.recentOffsets[1] = uint32(offset2) + blk.recentOffsets[2] = uint32(offset3) + if debugEncoder { + println("returning, recent offsets:", blk.recentOffsets, "extra literals:", blk.extraLits) + } +} + +// EncodeNoHist will encode a block with no history and no following blocks. +// Most notable difference is that src will not be copied for history and +// we do not need to check for max match length. +func (e *bestFastEncoder) EncodeNoHist(blk *blockEnc, src []byte) { + e.ensureHist(len(src)) + e.Encode(blk, src) +} + +// Reset will reset and set a dictionary if not nil +func (e *bestFastEncoder) Reset(d *dict, singleBlock bool) { + e.resetBase(d, singleBlock) + if d == nil { + return + } + dictChanged := d != e.lastDict + // Init or copy dict table + if len(e.dictTable) != len(e.table) || dictChanged { + if len(e.dictTable) != len(e.table) { + e.dictTable = make([]prevEntry, len(e.table)) + } else { + clear(e.dictTable) + } + end := int32(len(d.content)) - 8 + e.maxMatchOff + for i := e.maxMatchOff; i < end; i += 4 { + const hashLog = bestShortTableBits + + cv := load6432(d.content, i-e.maxMatchOff) + nextHash := hashLen(cv, hashLog, bestShortLen) // 0 -> 4 + nextHash1 := hashLen(cv>>8, hashLog, bestShortLen) // 1 -> 5 + nextHash2 := hashLen(cv>>16, hashLog, bestShortLen) // 2 -> 6 + nextHash3 := hashLen(cv>>24, hashLog, bestShortLen) // 3 -> 7 + e.dictTable[nextHash] = prevEntry{ + prev: e.dictTable[nextHash].offset, + offset: i, + } + e.dictTable[nextHash1] = prevEntry{ + prev: e.dictTable[nextHash1].offset, + offset: i + 1, + } + e.dictTable[nextHash2] = prevEntry{ + prev: e.dictTable[nextHash2].offset, + offset: i + 2, + } + e.dictTable[nextHash3] = prevEntry{ + prev: e.dictTable[nextHash3].offset, + offset: i + 3, + } + } + } + + // Init or copy dict long table + if len(e.dictLongTable) != len(e.longTable) || dictChanged { + if len(e.dictLongTable) != len(e.longTable) { + e.dictLongTable = make([]prevEntry, len(e.longTable)) + } else { + clear(e.dictLongTable) + } + if len(d.content) >= 8 { + cv := load6432(d.content, 0) + h := hashLen(cv, bestLongTableBits, bestLongLen) + e.dictLongTable[h] = prevEntry{ + offset: e.maxMatchOff, + prev: e.dictLongTable[h].offset, + } + + end := int32(len(d.content)) - 8 + e.maxMatchOff + off := 8 // First to read + for i := e.maxMatchOff + 1; i < end; i++ { + cv = cv>>8 | (uint64(d.content[off]) << 56) + h := hashLen(cv, bestLongTableBits, bestLongLen) + e.dictLongTable[h] = prevEntry{ + offset: i, + prev: e.dictLongTable[h].offset, + } + off++ + } + } + } + e.lastDict = d + // Reset table to initial state + copy(e.longTable[:], e.dictLongTable) + + e.cur = e.maxMatchOff + // Reset table to initial state + copy(e.table[:], e.dictTable) +} diff --git a/vendor/github.com/klauspost/compress/zstd/enc_better.go b/vendor/github.com/klauspost/compress/zstd/enc_better.go new file mode 100644 index 000000000..3305f0924 --- /dev/null +++ b/vendor/github.com/klauspost/compress/zstd/enc_better.go @@ -0,0 +1,1238 @@ +// Copyright 2019+ Klaus Post. All rights reserved. +// License information can be found in the LICENSE file. +// Based on work by Yann Collet, released under BSD License. + +package zstd + +import "fmt" + +const ( + betterLongTableBits = 19 // Bits used in the long match table + betterLongTableSize = 1 << betterLongTableBits // Size of the table + betterLongLen = 8 // Bytes used for table hash + + // Note: Increasing the short table bits or making the hash shorter + // can actually lead to compression degradation since it will 'steal' more from the + // long match table and match offsets are quite big. + // This greatly depends on the type of input. + betterShortTableBits = 13 // Bits used in the short match table + betterShortTableSize = 1 << betterShortTableBits // Size of the table + betterShortLen = 5 // Bytes used for table hash + + betterLongTableShardCnt = 1 << (betterLongTableBits - dictShardBits) // Number of shards in the table + betterLongTableShardSize = betterLongTableSize / betterLongTableShardCnt // Size of an individual shard + + betterShortTableShardCnt = 1 << (betterShortTableBits - dictShardBits) // Number of shards in the table + betterShortTableShardSize = betterShortTableSize / betterShortTableShardCnt // Size of an individual shard +) + +type prevEntry struct { + offset int32 + prev int32 +} + +// betterFastEncoder uses 2 tables, one for short matches (5 bytes) and one for long matches. +// The long match table contains the previous entry with the same hash, +// effectively making it a "chain" of length 2. +// When we find a long match we choose between the two values and select the longest. +// When we find a short match, after checking the long, we check if we can find a long at n+1 +// and that it is longer (lazy matching). +type betterFastEncoder struct { + fastBase + table [betterShortTableSize]tableEntry + longTable [betterLongTableSize]prevEntry +} + +type betterFastEncoderDict struct { + betterFastEncoder + dictTable []tableEntry + dictLongTable []prevEntry + shortTableShardDirty [betterShortTableShardCnt]bool + longTableShardDirty [betterLongTableShardCnt]bool + allDirty bool +} + +// Encode improves compression... +func (e *betterFastEncoder) Encode(blk *blockEnc, src []byte) { + const ( + // Input margin is the number of bytes we read (8) + // and the maximum we will read ahead (2) + inputMargin = 8 + 2 + minNonLiteralBlockSize = 16 + ) + + // Protect against e.cur wraparound. + for e.cur >= e.bufferReset-int32(len(e.hist)) { + if len(e.hist) == 0 { + e.table = [betterShortTableSize]tableEntry{} + e.longTable = [betterLongTableSize]prevEntry{} + e.cur = e.maxMatchOff + break + } + // Shift down everything in the table that isn't already too far away. + minOff := e.cur + int32(len(e.hist)) - e.maxMatchOff + for i := range e.table[:] { + v := e.table[i].offset + if v < minOff { + v = 0 + } else { + v = v - e.cur + e.maxMatchOff + } + e.table[i].offset = v + } + for i := range e.longTable[:] { + v := e.longTable[i].offset + v2 := e.longTable[i].prev + if v < minOff { + v = 0 + v2 = 0 + } else { + v = v - e.cur + e.maxMatchOff + if v2 < minOff { + v2 = 0 + } else { + v2 = v2 - e.cur + e.maxMatchOff + } + } + e.longTable[i] = prevEntry{ + offset: v, + prev: v2, + } + } + e.cur = e.maxMatchOff + break + } + // Add block to history + s := e.addBlock(src) + blk.size = len(src) + + // Check RLE first + if len(src) > zstdMinMatch { + ml := matchLen(src[1:], src) + if ml == len(src)-1 { + blk.literals = append(blk.literals, src[0]) + blk.sequences = append(blk.sequences, seq{litLen: 1, matchLen: uint32(len(src)-1) - zstdMinMatch, offset: 1 + 3}) + return + } + } + + if len(src) < minNonLiteralBlockSize { + blk.extraLits = len(src) + blk.literals = blk.literals[:len(src)] + copy(blk.literals, src) + return + } + + // Override src + src = e.hist + sLimit := int32(len(src)) - inputMargin + // stepSize is the number of bytes to skip on every main loop iteration. + // It should be >= 1. + const stepSize = 1 + + const kSearchStrength = 9 + + // nextEmit is where in src the next emitLiteral should start from. + nextEmit := s + cv := load6432(src, s) + + // Relative offsets + offset1 := int32(blk.recentOffsets[0]) + offset2 := int32(blk.recentOffsets[1]) + + addLiterals := func(s *seq, until int32) { + if until == nextEmit { + return + } + blk.literals = append(blk.literals, src[nextEmit:until]...) + s.litLen = uint32(until - nextEmit) + } + if debugEncoder { + println("recent offsets:", blk.recentOffsets) + } + +encodeLoop: + for { + var t int32 + // We allow the encoder to optionally turn off repeat offsets across blocks + canRepeat := len(blk.sequences) > 2 + var matched, index0 int32 + + for { + if debugAsserts && canRepeat && offset1 == 0 { + panic("offset0 was 0") + } + + nextHashL := hashLen(cv, betterLongTableBits, betterLongLen) + nextHashS := hashLen(cv, betterShortTableBits, betterShortLen) + candidateL := e.longTable[nextHashL] + candidateS := e.table[nextHashS] + + const repOff = 1 + repIndex := s - offset1 + repOff + off := s + e.cur + e.longTable[nextHashL] = prevEntry{offset: off, prev: candidateL.offset} + e.table[nextHashS] = tableEntry{offset: off, val: uint32(cv)} + index0 = s + 1 + + if canRepeat { + if repIndex >= 0 && load3232(src, repIndex) == uint32(cv>>(repOff*8)) { + // Consider history as well. + var seq seq + length := 4 + e.matchlen(s+4+repOff, repIndex+4, src) + + seq.matchLen = uint32(length - zstdMinMatch) + + // We might be able to match backwards. + // Extend as long as we can. + start := s + repOff + // We end the search early, so we don't risk 0 literals + // and have to do special offset treatment. + startLimit := nextEmit + 1 + + tMin := max(s-e.maxMatchOff, 0) + for repIndex > tMin && start > startLimit && src[repIndex-1] == src[start-1] && seq.matchLen < maxMatchLength-zstdMinMatch-1 { + repIndex-- + start-- + seq.matchLen++ + } + addLiterals(&seq, start) + + // rep 0 + seq.offset = 1 + if debugSequences { + println("repeat sequence", seq, "next s:", s) + } + blk.sequences = append(blk.sequences, seq) + + // Index match start+1 (long) -> s - 1 + index0 := s + repOff + s += length + repOff + + nextEmit = s + if s >= sLimit { + if debugEncoder { + println("repeat ended", s, length) + + } + break encodeLoop + } + // Index skipped... + for index0 < s-1 { + cv0 := load6432(src, index0) + cv1 := cv0 >> 8 + h0 := hashLen(cv0, betterLongTableBits, betterLongLen) + off := index0 + e.cur + e.longTable[h0] = prevEntry{offset: off, prev: e.longTable[h0].offset} + e.table[hashLen(cv1, betterShortTableBits, betterShortLen)] = tableEntry{offset: off + 1, val: uint32(cv1)} + index0 += 2 + } + cv = load6432(src, s) + continue + } + const repOff2 = 1 + + // We deviate from the reference encoder and also check offset 2. + // Still slower and not much better, so disabled. + // repIndex = s - offset2 + repOff2 + if false && repIndex >= 0 && load6432(src, repIndex) == load6432(src, s+repOff) { + // Consider history as well. + var seq seq + length := 8 + e.matchlen(s+8+repOff2, repIndex+8, src) + + seq.matchLen = uint32(length - zstdMinMatch) + + // We might be able to match backwards. + // Extend as long as we can. + start := s + repOff2 + // We end the search early, so we don't risk 0 literals + // and have to do special offset treatment. + startLimit := nextEmit + 1 + + tMin := max(s-e.maxMatchOff, 0) + for repIndex > tMin && start > startLimit && src[repIndex-1] == src[start-1] && seq.matchLen < maxMatchLength-zstdMinMatch-1 { + repIndex-- + start-- + seq.matchLen++ + } + addLiterals(&seq, start) + + // rep 2 + seq.offset = 2 + if debugSequences { + println("repeat sequence 2", seq, "next s:", s) + } + blk.sequences = append(blk.sequences, seq) + + s += length + repOff2 + nextEmit = s + if s >= sLimit { + if debugEncoder { + println("repeat ended", s, length) + + } + break encodeLoop + } + + // Index skipped... + for index0 < s-1 { + cv0 := load6432(src, index0) + cv1 := cv0 >> 8 + h0 := hashLen(cv0, betterLongTableBits, betterLongLen) + off := index0 + e.cur + e.longTable[h0] = prevEntry{offset: off, prev: e.longTable[h0].offset} + e.table[hashLen(cv1, betterShortTableBits, betterShortLen)] = tableEntry{offset: off + 1, val: uint32(cv1)} + index0 += 2 + } + cv = load6432(src, s) + // Swap offsets + offset1, offset2 = offset2, offset1 + continue + } + } + // Find the offsets of our two matches. + coffsetL := candidateL.offset - e.cur + coffsetLP := candidateL.prev - e.cur + + // Check if we have a long match. + if s-coffsetL < e.maxMatchOff && cv == load6432(src, coffsetL) { + // Found a long match, at least 8 bytes. + matched = e.matchlen(s+8, coffsetL+8, src) + 8 + t = coffsetL + if debugAsserts && s <= t { + panic(fmt.Sprintf("s (%d) <= t (%d)", s, t)) + } + if debugAsserts && s-t > e.maxMatchOff { + panic("s - t >e.maxMatchOff") + } + if debugMatches { + println("long match") + } + + if s-coffsetLP < e.maxMatchOff && cv == load6432(src, coffsetLP) { + // Found a long match, at least 8 bytes. + prevMatch := e.matchlen(s+8, coffsetLP+8, src) + 8 + if prevMatch > matched { + matched = prevMatch + t = coffsetLP + } + if debugAsserts && s <= t { + panic(fmt.Sprintf("s (%d) <= t (%d)", s, t)) + } + if debugAsserts && s-t > e.maxMatchOff { + panic("s - t >e.maxMatchOff") + } + if debugMatches { + println("long match") + } + } + break + } + + // Check if we have a long match on prev. + if s-coffsetLP < e.maxMatchOff && cv == load6432(src, coffsetLP) { + // Found a long match, at least 8 bytes. + matched = e.matchlen(s+8, coffsetLP+8, src) + 8 + t = coffsetLP + if debugAsserts && s <= t { + panic(fmt.Sprintf("s (%d) <= t (%d)", s, t)) + } + if debugAsserts && s-t > e.maxMatchOff { + panic("s - t >e.maxMatchOff") + } + if debugMatches { + println("long match") + } + break + } + + coffsetS := candidateS.offset - e.cur + + // Check if we have a short match. + if s-coffsetS < e.maxMatchOff && uint32(cv) == candidateS.val { + // found a regular match + matched = e.matchlen(s+4, coffsetS+4, src) + 4 + + // See if we can find a long match at s+1 + const checkAt = 1 + cv := load6432(src, s+checkAt) + nextHashL = hashLen(cv, betterLongTableBits, betterLongLen) + candidateL = e.longTable[nextHashL] + coffsetL = candidateL.offset - e.cur + + // We can store it, since we have at least a 4 byte match. + e.longTable[nextHashL] = prevEntry{offset: s + checkAt + e.cur, prev: candidateL.offset} + if s-coffsetL < e.maxMatchOff && cv == load6432(src, coffsetL) { + // Found a long match, at least 8 bytes. + matchedNext := e.matchlen(s+8+checkAt, coffsetL+8, src) + 8 + if matchedNext > matched { + t = coffsetL + s += checkAt + matched = matchedNext + if debugMatches { + println("long match (after short)") + } + break + } + } + + // Check prev long... + coffsetL = candidateL.prev - e.cur + if s-coffsetL < e.maxMatchOff && cv == load6432(src, coffsetL) { + // Found a long match, at least 8 bytes. + matchedNext := e.matchlen(s+8+checkAt, coffsetL+8, src) + 8 + if matchedNext > matched { + t = coffsetL + s += checkAt + matched = matchedNext + if debugMatches { + println("prev long match (after short)") + } + break + } + } + t = coffsetS + if debugAsserts && s <= t { + panic(fmt.Sprintf("s (%d) <= t (%d)", s, t)) + } + if debugAsserts && s-t > e.maxMatchOff { + panic("s - t >e.maxMatchOff") + } + if debugAsserts && t < 0 { + panic("t<0") + } + if debugMatches { + println("short match") + } + break + } + + // No match found, move forward in input. + s += stepSize + ((s - nextEmit) >> (kSearchStrength - 1)) + if s >= sLimit { + break encodeLoop + } + cv = load6432(src, s) + } + + // Try to find a better match by searching for a long match at the end of the current best match + if s+matched < sLimit { + // Allow some bytes at the beginning to mismatch. + // Sweet spot is around 3 bytes, but depends on input. + // The skipped bytes are tested in Extend backwards, + // and still picked up as part of the match if they do. + const skipBeginning = 3 + + nextHashL := hashLen(load6432(src, s+matched), betterLongTableBits, betterLongLen) + s2 := s + skipBeginning + cv := load3232(src, s2) + candidateL := e.longTable[nextHashL] + coffsetL := candidateL.offset - e.cur - matched + skipBeginning + if coffsetL >= 0 && coffsetL < s2 && s2-coffsetL < e.maxMatchOff && cv == load3232(src, coffsetL) { + // Found a long match, at least 4 bytes. + matchedNext := e.matchlen(s2+4, coffsetL+4, src) + 4 + if matchedNext > matched { + t = coffsetL + s = s2 + matched = matchedNext + if debugMatches { + println("long match at end-of-match") + } + } + } + + // Check prev long... + if true { + coffsetL = candidateL.prev - e.cur - matched + skipBeginning + if coffsetL >= 0 && coffsetL < s2 && s2-coffsetL < e.maxMatchOff && cv == load3232(src, coffsetL) { + // Found a long match, at least 4 bytes. + matchedNext := e.matchlen(s2+4, coffsetL+4, src) + 4 + if matchedNext > matched { + t = coffsetL + s = s2 + matched = matchedNext + if debugMatches { + println("prev long match at end-of-match") + } + } + } + } + } + // A match has been found. Update recent offsets. + offset2 = offset1 + offset1 = s - t + + if debugAsserts && s <= t { + panic(fmt.Sprintf("s (%d) <= t (%d)", s, t)) + } + + if debugAsserts && canRepeat && int(offset1) > len(src) { + panic("invalid offset") + } + + // Extend the n-byte match as long as possible. + l := matched + + // Extend backwards + tMin := max(s-e.maxMatchOff, 0) + for t > tMin && s > nextEmit && src[t-1] == src[s-1] && l < maxMatchLength { + s-- + t-- + l++ + } + + // Write our sequence + var seq seq + seq.litLen = uint32(s - nextEmit) + seq.matchLen = uint32(l - zstdMinMatch) + if seq.litLen > 0 { + blk.literals = append(blk.literals, src[nextEmit:s]...) + } + seq.offset = uint32(s-t) + 3 + s += l + if debugSequences { + println("sequence", seq, "next s:", s) + } + blk.sequences = append(blk.sequences, seq) + nextEmit = s + if s >= sLimit { + break encodeLoop + } + + // Index match start+1 (long) -> s - 1 + off := index0 + e.cur + for index0 < s-1 { + cv0 := load6432(src, index0) + cv1 := cv0 >> 8 + h0 := hashLen(cv0, betterLongTableBits, betterLongLen) + e.longTable[h0] = prevEntry{offset: off, prev: e.longTable[h0].offset} + e.table[hashLen(cv1, betterShortTableBits, betterShortLen)] = tableEntry{offset: off + 1, val: uint32(cv1)} + index0 += 2 + off += 2 + } + + cv = load6432(src, s) + if !canRepeat { + continue + } + + // Check offset 2 + for { + o2 := s - offset2 + if load3232(src, o2) != uint32(cv) { + // Do regular search + break + } + + // Store this, since we have it. + nextHashL := hashLen(cv, betterLongTableBits, betterLongLen) + nextHashS := hashLen(cv, betterShortTableBits, betterShortLen) + + // We have at least 4 byte match. + // No need to check backwards. We come straight from a match + l := 4 + e.matchlen(s+4, o2+4, src) + + e.longTable[nextHashL] = prevEntry{offset: s + e.cur, prev: e.longTable[nextHashL].offset} + e.table[nextHashS] = tableEntry{offset: s + e.cur, val: uint32(cv)} + seq.matchLen = uint32(l) - zstdMinMatch + seq.litLen = 0 + + // Since litlen is always 0, this is offset 1. + seq.offset = 1 + s += l + nextEmit = s + if debugSequences { + println("sequence", seq, "next s:", s) + } + blk.sequences = append(blk.sequences, seq) + + // Swap offset 1 and 2. + offset1, offset2 = offset2, offset1 + if s >= sLimit { + // Finished + break encodeLoop + } + cv = load6432(src, s) + } + } + + if int(nextEmit) < len(src) { + blk.literals = append(blk.literals, src[nextEmit:]...) + blk.extraLits = len(src) - int(nextEmit) + } + blk.recentOffsets[0] = uint32(offset1) + blk.recentOffsets[1] = uint32(offset2) + if debugEncoder { + println("returning, recent offsets:", blk.recentOffsets, "extra literals:", blk.extraLits) + } +} + +// EncodeNoHist will encode a block with no history and no following blocks. +// Most notable difference is that src will not be copied for history and +// we do not need to check for max match length. +func (e *betterFastEncoder) EncodeNoHist(blk *blockEnc, src []byte) { + e.ensureHist(len(src)) + e.Encode(blk, src) +} + +// Encode improves compression... +func (e *betterFastEncoderDict) Encode(blk *blockEnc, src []byte) { + const ( + // Input margin is the number of bytes we read (8) + // and the maximum we will read ahead (2) + inputMargin = 8 + 2 + minNonLiteralBlockSize = 16 + ) + + // Protect against e.cur wraparound. + for e.cur >= e.bufferReset-int32(len(e.hist)) { + if len(e.hist) == 0 { + for i := range e.table[:] { + e.table[i] = tableEntry{} + } + for i := range e.longTable[:] { + e.longTable[i] = prevEntry{} + } + e.cur = e.maxMatchOff + e.allDirty = true + break + } + // Shift down everything in the table that isn't already too far away. + minOff := e.cur + int32(len(e.hist)) - e.maxMatchOff + for i := range e.table[:] { + v := e.table[i].offset + if v < minOff { + v = 0 + } else { + v = v - e.cur + e.maxMatchOff + } + e.table[i].offset = v + } + for i := range e.longTable[:] { + v := e.longTable[i].offset + v2 := e.longTable[i].prev + if v < minOff { + v = 0 + v2 = 0 + } else { + v = v - e.cur + e.maxMatchOff + if v2 < minOff { + v2 = 0 + } else { + v2 = v2 - e.cur + e.maxMatchOff + } + } + e.longTable[i] = prevEntry{ + offset: v, + prev: v2, + } + } + e.allDirty = true + e.cur = e.maxMatchOff + break + } + + s := e.addBlock(src) + blk.size = len(src) + if len(src) < minNonLiteralBlockSize { + blk.extraLits = len(src) + blk.literals = blk.literals[:len(src)] + copy(blk.literals, src) + return + } + + // Override src + src = e.hist + sLimit := int32(len(src)) - inputMargin + // stepSize is the number of bytes to skip on every main loop iteration. + // It should be >= 1. + const stepSize = 1 + + const kSearchStrength = 9 + + // nextEmit is where in src the next emitLiteral should start from. + nextEmit := s + cv := load6432(src, s) + + // Relative offsets + offset1 := int32(blk.recentOffsets[0]) + offset2 := int32(blk.recentOffsets[1]) + + addLiterals := func(s *seq, until int32) { + if until == nextEmit { + return + } + blk.literals = append(blk.literals, src[nextEmit:until]...) + s.litLen = uint32(until - nextEmit) + } + if debugEncoder { + println("recent offsets:", blk.recentOffsets) + } + +encodeLoop: + for { + var t int32 + // We allow the encoder to optionally turn off repeat offsets across blocks + canRepeat := len(blk.sequences) > 2 + var matched, index0 int32 + + for { + if debugAsserts && canRepeat && offset1 == 0 { + panic("offset0 was 0") + } + + nextHashL := hashLen(cv, betterLongTableBits, betterLongLen) + nextHashS := hashLen(cv, betterShortTableBits, betterShortLen) + candidateL := e.longTable[nextHashL] + candidateS := e.table[nextHashS] + + const repOff = 1 + repIndex := s - offset1 + repOff + off := s + e.cur + e.longTable[nextHashL] = prevEntry{offset: off, prev: candidateL.offset} + e.markLongShardDirty(nextHashL) + e.table[nextHashS] = tableEntry{offset: off, val: uint32(cv)} + e.markShortShardDirty(nextHashS) + index0 = s + 1 + + if canRepeat { + if repIndex >= 0 && load3232(src, repIndex) == uint32(cv>>(repOff*8)) { + // Consider history as well. + var seq seq + length := 4 + e.matchlen(s+4+repOff, repIndex+4, src) + + seq.matchLen = uint32(length - zstdMinMatch) + + // We might be able to match backwards. + // Extend as long as we can. + start := s + repOff + // We end the search early, so we don't risk 0 literals + // and have to do special offset treatment. + startLimit := nextEmit + 1 + + tMin := max(s-e.maxMatchOff, 0) + for repIndex > tMin && start > startLimit && src[repIndex-1] == src[start-1] && seq.matchLen < maxMatchLength-zstdMinMatch-1 { + repIndex-- + start-- + seq.matchLen++ + } + addLiterals(&seq, start) + + // rep 0 + seq.offset = 1 + if debugSequences { + println("repeat sequence", seq, "next s:", s) + } + blk.sequences = append(blk.sequences, seq) + + // Index match start+1 (long) -> s - 1 + s += length + repOff + + nextEmit = s + if s >= sLimit { + if debugEncoder { + println("repeat ended", s, length) + + } + break encodeLoop + } + // Index skipped... + for index0 < s-1 { + cv0 := load6432(src, index0) + cv1 := cv0 >> 8 + h0 := hashLen(cv0, betterLongTableBits, betterLongLen) + off := index0 + e.cur + e.longTable[h0] = prevEntry{offset: off, prev: e.longTable[h0].offset} + e.markLongShardDirty(h0) + h1 := hashLen(cv1, betterShortTableBits, betterShortLen) + e.table[h1] = tableEntry{offset: off + 1, val: uint32(cv1)} + e.markShortShardDirty(h1) + index0 += 2 + } + cv = load6432(src, s) + continue + } + const repOff2 = 1 + + // We deviate from the reference encoder and also check offset 2. + // Still slower and not much better, so disabled. + // repIndex = s - offset2 + repOff2 + if false && repIndex >= 0 && load6432(src, repIndex) == load6432(src, s+repOff) { + // Consider history as well. + var seq seq + length := 8 + e.matchlen(s+8+repOff2, repIndex+8, src) + + seq.matchLen = uint32(length - zstdMinMatch) + + // We might be able to match backwards. + // Extend as long as we can. + start := s + repOff2 + // We end the search early, so we don't risk 0 literals + // and have to do special offset treatment. + startLimit := nextEmit + 1 + + tMin := max(s-e.maxMatchOff, 0) + for repIndex > tMin && start > startLimit && src[repIndex-1] == src[start-1] && seq.matchLen < maxMatchLength-zstdMinMatch-1 { + repIndex-- + start-- + seq.matchLen++ + } + addLiterals(&seq, start) + + // rep 2 + seq.offset = 2 + if debugSequences { + println("repeat sequence 2", seq, "next s:", s) + } + blk.sequences = append(blk.sequences, seq) + + s += length + repOff2 + nextEmit = s + if s >= sLimit { + if debugEncoder { + println("repeat ended", s, length) + + } + break encodeLoop + } + + // Index skipped... + for index0 < s-1 { + cv0 := load6432(src, index0) + cv1 := cv0 >> 8 + h0 := hashLen(cv0, betterLongTableBits, betterLongLen) + off := index0 + e.cur + e.longTable[h0] = prevEntry{offset: off, prev: e.longTable[h0].offset} + e.markLongShardDirty(h0) + h1 := hashLen(cv1, betterShortTableBits, betterShortLen) + e.table[h1] = tableEntry{offset: off + 1, val: uint32(cv1)} + e.markShortShardDirty(h1) + index0 += 2 + } + cv = load6432(src, s) + // Swap offsets + offset1, offset2 = offset2, offset1 + continue + } + } + // Find the offsets of our two matches. + coffsetL := candidateL.offset - e.cur + coffsetLP := candidateL.prev - e.cur + + // Check if we have a long match. + if s-coffsetL < e.maxMatchOff && cv == load6432(src, coffsetL) { + // Found a long match, at least 8 bytes. + matched = e.matchlen(s+8, coffsetL+8, src) + 8 + t = coffsetL + if debugAsserts && s <= t { + panic(fmt.Sprintf("s (%d) <= t (%d)", s, t)) + } + if debugAsserts && s-t > e.maxMatchOff { + panic("s - t >e.maxMatchOff") + } + if debugMatches { + println("long match") + } + + if s-coffsetLP < e.maxMatchOff && cv == load6432(src, coffsetLP) { + // Found a long match, at least 8 bytes. + prevMatch := e.matchlen(s+8, coffsetLP+8, src) + 8 + if prevMatch > matched { + matched = prevMatch + t = coffsetLP + } + if debugAsserts && s <= t { + panic(fmt.Sprintf("s (%d) <= t (%d)", s, t)) + } + if debugAsserts && s-t > e.maxMatchOff { + panic("s - t >e.maxMatchOff") + } + if debugMatches { + println("long match") + } + } + break + } + + // Check if we have a long match on prev. + if s-coffsetLP < e.maxMatchOff && cv == load6432(src, coffsetLP) { + // Found a long match, at least 8 bytes. + matched = e.matchlen(s+8, coffsetLP+8, src) + 8 + t = coffsetLP + if debugAsserts && s <= t { + panic(fmt.Sprintf("s (%d) <= t (%d)", s, t)) + } + if debugAsserts && s-t > e.maxMatchOff { + panic("s - t >e.maxMatchOff") + } + if debugMatches { + println("long match") + } + break + } + + coffsetS := candidateS.offset - e.cur + + // Check if we have a short match. + if s-coffsetS < e.maxMatchOff && uint32(cv) == candidateS.val { + // found a regular match + matched = e.matchlen(s+4, coffsetS+4, src) + 4 + + // See if we can find a long match at s+1 + const checkAt = 1 + cv := load6432(src, s+checkAt) + nextHashL = hashLen(cv, betterLongTableBits, betterLongLen) + candidateL = e.longTable[nextHashL] + coffsetL = candidateL.offset - e.cur + + // We can store it, since we have at least a 4 byte match. + e.longTable[nextHashL] = prevEntry{offset: s + checkAt + e.cur, prev: candidateL.offset} + e.markLongShardDirty(nextHashL) + if s-coffsetL < e.maxMatchOff && cv == load6432(src, coffsetL) { + // Found a long match, at least 8 bytes. + matchedNext := e.matchlen(s+8+checkAt, coffsetL+8, src) + 8 + if matchedNext > matched { + t = coffsetL + s += checkAt + matched = matchedNext + if debugMatches { + println("long match (after short)") + } + break + } + } + + // Check prev long... + coffsetL = candidateL.prev - e.cur + if s-coffsetL < e.maxMatchOff && cv == load6432(src, coffsetL) { + // Found a long match, at least 8 bytes. + matchedNext := e.matchlen(s+8+checkAt, coffsetL+8, src) + 8 + if matchedNext > matched { + t = coffsetL + s += checkAt + matched = matchedNext + if debugMatches { + println("prev long match (after short)") + } + break + } + } + t = coffsetS + if debugAsserts && s <= t { + panic(fmt.Sprintf("s (%d) <= t (%d)", s, t)) + } + if debugAsserts && s-t > e.maxMatchOff { + panic("s - t >e.maxMatchOff") + } + if debugAsserts && t < 0 { + panic("t<0") + } + if debugMatches { + println("short match") + } + break + } + + // No match found, move forward in input. + s += stepSize + ((s - nextEmit) >> (kSearchStrength - 1)) + if s >= sLimit { + break encodeLoop + } + cv = load6432(src, s) + } + // Try to find a better match by searching for a long match at the end of the current best match + if s+matched < sLimit { + nextHashL := hashLen(load6432(src, s+matched), betterLongTableBits, betterLongLen) + cv := load3232(src, s) + candidateL := e.longTable[nextHashL] + coffsetL := candidateL.offset - e.cur - matched + if coffsetL >= 0 && coffsetL < s && s-coffsetL < e.maxMatchOff && cv == load3232(src, coffsetL) { + // Found a long match, at least 4 bytes. + matchedNext := e.matchlen(s+4, coffsetL+4, src) + 4 + if matchedNext > matched { + t = coffsetL + matched = matchedNext + if debugMatches { + println("long match at end-of-match") + } + } + } + + // Check prev long... + if true { + coffsetL = candidateL.prev - e.cur - matched + if coffsetL >= 0 && coffsetL < s && s-coffsetL < e.maxMatchOff && cv == load3232(src, coffsetL) { + // Found a long match, at least 4 bytes. + matchedNext := e.matchlen(s+4, coffsetL+4, src) + 4 + if matchedNext > matched { + t = coffsetL + matched = matchedNext + if debugMatches { + println("prev long match at end-of-match") + } + } + } + } + } + // A match has been found. Update recent offsets. + offset2 = offset1 + offset1 = s - t + + if debugAsserts && s <= t { + panic(fmt.Sprintf("s (%d) <= t (%d)", s, t)) + } + + if debugAsserts && canRepeat && int(offset1) > len(src) { + panic("invalid offset") + } + + // Extend the n-byte match as long as possible. + l := matched + + // Extend backwards + tMin := max(s-e.maxMatchOff, 0) + for t > tMin && s > nextEmit && src[t-1] == src[s-1] && l < maxMatchLength { + s-- + t-- + l++ + } + + // Write our sequence + var seq seq + seq.litLen = uint32(s - nextEmit) + seq.matchLen = uint32(l - zstdMinMatch) + if seq.litLen > 0 { + blk.literals = append(blk.literals, src[nextEmit:s]...) + } + seq.offset = uint32(s-t) + 3 + s += l + if debugSequences { + println("sequence", seq, "next s:", s) + } + blk.sequences = append(blk.sequences, seq) + nextEmit = s + if s >= sLimit { + break encodeLoop + } + + // Index match start+1 (long) -> s - 1 + off := index0 + e.cur + for index0 < s-1 { + cv0 := load6432(src, index0) + cv1 := cv0 >> 8 + h0 := hashLen(cv0, betterLongTableBits, betterLongLen) + e.longTable[h0] = prevEntry{offset: off, prev: e.longTable[h0].offset} + e.markLongShardDirty(h0) + h1 := hashLen(cv1, betterShortTableBits, betterShortLen) + e.table[h1] = tableEntry{offset: off + 1, val: uint32(cv1)} + e.markShortShardDirty(h1) + index0 += 2 + off += 2 + } + + cv = load6432(src, s) + if !canRepeat { + continue + } + + // Check offset 2 + for { + o2 := s - offset2 + if load3232(src, o2) != uint32(cv) { + // Do regular search + break + } + + // Store this, since we have it. + nextHashL := hashLen(cv, betterLongTableBits, betterLongLen) + nextHashS := hashLen(cv, betterShortTableBits, betterShortLen) + + // We have at least 4 byte match. + // No need to check backwards. We come straight from a match + l := 4 + e.matchlen(s+4, o2+4, src) + + e.longTable[nextHashL] = prevEntry{offset: s + e.cur, prev: e.longTable[nextHashL].offset} + e.markLongShardDirty(nextHashL) + e.table[nextHashS] = tableEntry{offset: s + e.cur, val: uint32(cv)} + e.markShortShardDirty(nextHashS) + seq.matchLen = uint32(l) - zstdMinMatch + seq.litLen = 0 + + // Since litlen is always 0, this is offset 1. + seq.offset = 1 + s += l + nextEmit = s + if debugSequences { + println("sequence", seq, "next s:", s) + } + blk.sequences = append(blk.sequences, seq) + + // Swap offset 1 and 2. + offset1, offset2 = offset2, offset1 + if s >= sLimit { + // Finished + break encodeLoop + } + cv = load6432(src, s) + } + } + + if int(nextEmit) < len(src) { + blk.literals = append(blk.literals, src[nextEmit:]...) + blk.extraLits = len(src) - int(nextEmit) + } + blk.recentOffsets[0] = uint32(offset1) + blk.recentOffsets[1] = uint32(offset2) + if debugEncoder { + println("returning, recent offsets:", blk.recentOffsets, "extra literals:", blk.extraLits) + } +} + +// ResetDict will reset and set a dictionary if not nil +func (e *betterFastEncoder) Reset(d *dict, singleBlock bool) { + e.resetBase(d, singleBlock) + if d != nil { + panic("betterFastEncoder: Reset with dict") + } +} + +// ResetDict will reset and set a dictionary if not nil +func (e *betterFastEncoderDict) Reset(d *dict, singleBlock bool) { + e.resetBase(d, singleBlock) + if d == nil { + return + } + dictChanged := d != e.lastDict + // Init or copy dict table + if len(e.dictTable) != len(e.table) || dictChanged { + if len(e.dictTable) != len(e.table) { + e.dictTable = make([]tableEntry, len(e.table)) + } else { + clear(e.dictTable) + } + end := int32(len(d.content)) - 8 + e.maxMatchOff + for i := e.maxMatchOff; i < end; i += 4 { + const hashLog = betterShortTableBits + + cv := load6432(d.content, i-e.maxMatchOff) + nextHash := hashLen(cv, hashLog, betterShortLen) // 0 -> 4 + nextHash1 := hashLen(cv>>8, hashLog, betterShortLen) // 1 -> 5 + nextHash2 := hashLen(cv>>16, hashLog, betterShortLen) // 2 -> 6 + nextHash3 := hashLen(cv>>24, hashLog, betterShortLen) // 3 -> 7 + e.dictTable[nextHash] = tableEntry{ + val: uint32(cv), + offset: i, + } + e.dictTable[nextHash1] = tableEntry{ + val: uint32(cv >> 8), + offset: i + 1, + } + e.dictTable[nextHash2] = tableEntry{ + val: uint32(cv >> 16), + offset: i + 2, + } + e.dictTable[nextHash3] = tableEntry{ + val: uint32(cv >> 24), + offset: i + 3, + } + } + e.allDirty = true + } + + // Init or copy dict long table + if len(e.dictLongTable) != len(e.longTable) || dictChanged { + if len(e.dictLongTable) != len(e.longTable) { + e.dictLongTable = make([]prevEntry, len(e.longTable)) + } else { + clear(e.dictLongTable) + } + if len(d.content) >= 8 { + cv := load6432(d.content, 0) + h := hashLen(cv, betterLongTableBits, betterLongLen) + e.dictLongTable[h] = prevEntry{ + offset: e.maxMatchOff, + prev: e.dictLongTable[h].offset, + } + + end := int32(len(d.content)) - 8 + e.maxMatchOff + off := 8 // First to read + for i := e.maxMatchOff + 1; i < end; i++ { + cv = cv>>8 | (uint64(d.content[off]) << 56) + h := hashLen(cv, betterLongTableBits, betterLongLen) + e.dictLongTable[h] = prevEntry{ + offset: i, + prev: e.dictLongTable[h].offset, + } + off++ + } + } + e.allDirty = true + } + e.lastDict = d + + // Reset table to initial state + { + dirtyShardCnt := 0 + if !e.allDirty { + for i := range e.shortTableShardDirty { + if e.shortTableShardDirty[i] { + dirtyShardCnt++ + } + } + } + const shardCnt = betterShortTableShardCnt + const shardSize = betterShortTableShardSize + if e.allDirty || dirtyShardCnt > shardCnt*4/6 { + copy(e.table[:], e.dictTable) + for i := range e.shortTableShardDirty { + e.shortTableShardDirty[i] = false + } + } else { + for i := range e.shortTableShardDirty { + if !e.shortTableShardDirty[i] { + continue + } + + copy(e.table[i*shardSize:(i+1)*shardSize], e.dictTable[i*shardSize:(i+1)*shardSize]) + e.shortTableShardDirty[i] = false + } + } + } + { + dirtyShardCnt := 0 + if !e.allDirty { + for i := range e.shortTableShardDirty { + if e.shortTableShardDirty[i] { + dirtyShardCnt++ + } + } + } + const shardCnt = betterLongTableShardCnt + const shardSize = betterLongTableShardSize + if e.allDirty || dirtyShardCnt > shardCnt*4/6 { + copy(e.longTable[:], e.dictLongTable) + for i := range e.longTableShardDirty { + e.longTableShardDirty[i] = false + } + } else { + for i := range e.longTableShardDirty { + if !e.longTableShardDirty[i] { + continue + } + + copy(e.longTable[i*shardSize:(i+1)*shardSize], e.dictLongTable[i*shardSize:(i+1)*shardSize]) + e.longTableShardDirty[i] = false + } + } + } + e.cur = e.maxMatchOff + e.allDirty = false +} + +func (e *betterFastEncoderDict) markLongShardDirty(entryNum uint32) { + e.longTableShardDirty[entryNum/betterLongTableShardSize] = true +} + +func (e *betterFastEncoderDict) markShortShardDirty(entryNum uint32) { + e.shortTableShardDirty[entryNum/betterShortTableShardSize] = true +} diff --git a/vendor/github.com/klauspost/compress/zstd/enc_dfast.go b/vendor/github.com/klauspost/compress/zstd/enc_dfast.go new file mode 100644 index 000000000..2fb6da112 --- /dev/null +++ b/vendor/github.com/klauspost/compress/zstd/enc_dfast.go @@ -0,0 +1,1107 @@ +// Copyright 2019+ Klaus Post. All rights reserved. +// License information can be found in the LICENSE file. +// Based on work by Yann Collet, released under BSD License. + +package zstd + +import "fmt" + +const ( + dFastLongTableBits = 17 // Bits used in the long match table + dFastLongTableSize = 1 << dFastLongTableBits // Size of the table + dFastLongTableMask = dFastLongTableSize - 1 // Mask for table indices. Redundant, but can eliminate bounds checks. + dFastLongLen = 8 // Bytes used for table hash + + dLongTableShardCnt = 1 << (dFastLongTableBits - dictShardBits) // Number of shards in the table + dLongTableShardSize = dFastLongTableSize / dLongTableShardCnt // Size of an individual shard + + dFastShortTableBits = tableBits // Bits used in the short match table + dFastShortTableSize = 1 << dFastShortTableBits // Size of the table + dFastShortTableMask = dFastShortTableSize - 1 // Mask for table indices. Redundant, but can eliminate bounds checks. + dFastShortLen = 5 // Bytes used for table hash + +) + +type doubleFastEncoder struct { + fastEncoder + longTable [dFastLongTableSize]tableEntry +} + +type doubleFastEncoderDict struct { + fastEncoderDict + longTable [dFastLongTableSize]tableEntry + dictLongTable []tableEntry + longTableShardDirty [dLongTableShardCnt]bool +} + +// Encode mimmics functionality in zstd_dfast.c +func (e *doubleFastEncoder) Encode(blk *blockEnc, src []byte) { + const ( + // Input margin is the number of bytes we read (8) + // and the maximum we will read ahead (2) + inputMargin = 8 + 2 + minNonLiteralBlockSize = 16 + ) + + // Protect against e.cur wraparound. + for e.cur >= e.bufferReset-int32(len(e.hist)) { + if len(e.hist) == 0 { + e.table = [dFastShortTableSize]tableEntry{} + e.longTable = [dFastLongTableSize]tableEntry{} + e.cur = e.maxMatchOff + break + } + // Shift down everything in the table that isn't already too far away. + minOff := e.cur + int32(len(e.hist)) - e.maxMatchOff + for i := range e.table[:] { + v := e.table[i].offset + if v < minOff { + v = 0 + } else { + v = v - e.cur + e.maxMatchOff + } + e.table[i].offset = v + } + for i := range e.longTable[:] { + v := e.longTable[i].offset + if v < minOff { + v = 0 + } else { + v = v - e.cur + e.maxMatchOff + } + e.longTable[i].offset = v + } + e.cur = e.maxMatchOff + break + } + + s := e.addBlock(src) + blk.size = len(src) + if len(src) < minNonLiteralBlockSize { + blk.extraLits = len(src) + blk.literals = blk.literals[:len(src)] + copy(blk.literals, src) + return + } + + // Override src + src = e.hist + sLimit := int32(len(src)) - inputMargin + // stepSize is the number of bytes to skip on every main loop iteration. + // It should be >= 1. + const stepSize = 1 + + const kSearchStrength = 8 + + // nextEmit is where in src the next emitLiteral should start from. + nextEmit := s + cv := load6432(src, s) + + // Relative offsets + offset1 := int32(blk.recentOffsets[0]) + offset2 := int32(blk.recentOffsets[1]) + + addLiterals := func(s *seq, until int32) { + if until == nextEmit { + return + } + blk.literals = append(blk.literals, src[nextEmit:until]...) + s.litLen = uint32(until - nextEmit) + } + if debugEncoder { + println("recent offsets:", blk.recentOffsets) + } + +encodeLoop: + for { + var t int32 + // We allow the encoder to optionally turn off repeat offsets across blocks + canRepeat := len(blk.sequences) > 2 + + for { + if debugAsserts && canRepeat && offset1 == 0 { + panic("offset0 was 0") + } + + nextHashL := hashLen(cv, dFastLongTableBits, dFastLongLen) + nextHashS := hashLen(cv, dFastShortTableBits, dFastShortLen) + candidateL := e.longTable[nextHashL] + candidateS := e.table[nextHashS] + + const repOff = 1 + repIndex := s - offset1 + repOff + entry := tableEntry{offset: s + e.cur, val: uint32(cv)} + e.longTable[nextHashL] = entry + e.table[nextHashS] = entry + + if canRepeat { + if repIndex >= 0 && load3232(src, repIndex) == uint32(cv>>(repOff*8)) { + // Consider history as well. + var seq seq + length := 4 + e.matchlen(s+4+repOff, repIndex+4, src) + + seq.matchLen = uint32(length - zstdMinMatch) + + // We might be able to match backwards. + // Extend as long as we can. + start := s + repOff + // We end the search early, so we don't risk 0 literals + // and have to do special offset treatment. + startLimit := nextEmit + 1 + + tMin := max(s-e.maxMatchOff, 0) + for repIndex > tMin && start > startLimit && src[repIndex-1] == src[start-1] && seq.matchLen < maxMatchLength-zstdMinMatch-1 { + repIndex-- + start-- + seq.matchLen++ + } + addLiterals(&seq, start) + + // rep 0 + seq.offset = 1 + if debugSequences { + println("repeat sequence", seq, "next s:", s) + } + blk.sequences = append(blk.sequences, seq) + s += length + repOff + nextEmit = s + if s >= sLimit { + if debugEncoder { + println("repeat ended", s, length) + + } + break encodeLoop + } + cv = load6432(src, s) + continue + } + } + // Find the offsets of our two matches. + coffsetL := s - (candidateL.offset - e.cur) + coffsetS := s - (candidateS.offset - e.cur) + + // Check if we have a long match. + if coffsetL < e.maxMatchOff && uint32(cv) == candidateL.val { + // Found a long match, likely at least 8 bytes. + // Reference encoder checks all 8 bytes, we only check 4, + // but the likelihood of both the first 4 bytes and the hash matching should be enough. + t = candidateL.offset - e.cur + if debugAsserts && s <= t { + panic(fmt.Sprintf("s (%d) <= t (%d)", s, t)) + } + if debugAsserts && s-t > e.maxMatchOff { + panic("s - t >e.maxMatchOff") + } + if debugMatches { + println("long match") + } + break + } + + // Check if we have a short match. + if coffsetS < e.maxMatchOff && uint32(cv) == candidateS.val { + // found a regular match + // See if we can find a long match at s+1 + const checkAt = 1 + cv := load6432(src, s+checkAt) + nextHashL = hashLen(cv, dFastLongTableBits, dFastLongLen) + candidateL = e.longTable[nextHashL] + coffsetL = s - (candidateL.offset - e.cur) + checkAt + + // We can store it, since we have at least a 4 byte match. + e.longTable[nextHashL] = tableEntry{offset: s + checkAt + e.cur, val: uint32(cv)} + if coffsetL < e.maxMatchOff && uint32(cv) == candidateL.val { + // Found a long match, likely at least 8 bytes. + // Reference encoder checks all 8 bytes, we only check 4, + // but the likelihood of both the first 4 bytes and the hash matching should be enough. + t = candidateL.offset - e.cur + s += checkAt + if debugMatches { + println("long match (after short)") + } + break + } + + t = candidateS.offset - e.cur + if debugAsserts && s <= t { + panic(fmt.Sprintf("s (%d) <= t (%d)", s, t)) + } + if debugAsserts && s-t > e.maxMatchOff { + panic("s - t >e.maxMatchOff") + } + if debugAsserts && t < 0 { + panic("t<0") + } + if debugMatches { + println("short match") + } + break + } + + // No match found, move forward in input. + s += stepSize + ((s - nextEmit) >> (kSearchStrength - 1)) + if s >= sLimit { + break encodeLoop + } + cv = load6432(src, s) + } + + // A 4-byte match has been found. Update recent offsets. + // We'll later see if more than 4 bytes. + offset2 = offset1 + offset1 = s - t + + if debugAsserts && s <= t { + panic(fmt.Sprintf("s (%d) <= t (%d)", s, t)) + } + + if debugAsserts && canRepeat && int(offset1) > len(src) { + panic("invalid offset") + } + + // Extend the 4-byte match as long as possible. + l := e.matchlen(s+4, t+4, src) + 4 + + // Extend backwards + tMin := max(s-e.maxMatchOff, 0) + for t > tMin && s > nextEmit && src[t-1] == src[s-1] && l < maxMatchLength { + s-- + t-- + l++ + } + + // Write our sequence + var seq seq + seq.litLen = uint32(s - nextEmit) + seq.matchLen = uint32(l - zstdMinMatch) + if seq.litLen > 0 { + blk.literals = append(blk.literals, src[nextEmit:s]...) + } + seq.offset = uint32(s-t) + 3 + s += l + if debugSequences { + println("sequence", seq, "next s:", s) + } + blk.sequences = append(blk.sequences, seq) + nextEmit = s + if s >= sLimit { + break encodeLoop + } + + // Index match start+1 (long) and start+2 (short) + index0 := s - l + 1 + // Index match end-2 (long) and end-1 (short) + index1 := s - 2 + + cv0 := load6432(src, index0) + cv1 := load6432(src, index1) + te0 := tableEntry{offset: index0 + e.cur, val: uint32(cv0)} + te1 := tableEntry{offset: index1 + e.cur, val: uint32(cv1)} + e.longTable[hashLen(cv0, dFastLongTableBits, dFastLongLen)] = te0 + e.longTable[hashLen(cv1, dFastLongTableBits, dFastLongLen)] = te1 + cv0 >>= 8 + cv1 >>= 8 + te0.offset++ + te1.offset++ + te0.val = uint32(cv0) + te1.val = uint32(cv1) + e.table[hashLen(cv0, dFastShortTableBits, dFastShortLen)] = te0 + e.table[hashLen(cv1, dFastShortTableBits, dFastShortLen)] = te1 + + cv = load6432(src, s) + + if !canRepeat { + continue + } + + // Check offset 2 + for { + o2 := s - offset2 + if load3232(src, o2) != uint32(cv) { + // Do regular search + break + } + + // Store this, since we have it. + nextHashS := hashLen(cv, dFastShortTableBits, dFastShortLen) + nextHashL := hashLen(cv, dFastLongTableBits, dFastLongLen) + + // We have at least 4 byte match. + // No need to check backwards. We come straight from a match + l := 4 + e.matchlen(s+4, o2+4, src) + + entry := tableEntry{offset: s + e.cur, val: uint32(cv)} + e.longTable[nextHashL] = entry + e.table[nextHashS] = entry + seq.matchLen = uint32(l) - zstdMinMatch + seq.litLen = 0 + + // Since litlen is always 0, this is offset 1. + seq.offset = 1 + s += l + nextEmit = s + if debugSequences { + println("sequence", seq, "next s:", s) + } + blk.sequences = append(blk.sequences, seq) + + // Swap offset 1 and 2. + offset1, offset2 = offset2, offset1 + if s >= sLimit { + // Finished + break encodeLoop + } + cv = load6432(src, s) + } + } + + if int(nextEmit) < len(src) { + blk.literals = append(blk.literals, src[nextEmit:]...) + blk.extraLits = len(src) - int(nextEmit) + } + blk.recentOffsets[0] = uint32(offset1) + blk.recentOffsets[1] = uint32(offset2) + if debugEncoder { + println("returning, recent offsets:", blk.recentOffsets, "extra literals:", blk.extraLits) + } +} + +// EncodeNoHist will encode a block with no history and no following blocks. +// Most notable difference is that src will not be copied for history and +// we do not need to check for max match length. +func (e *doubleFastEncoder) EncodeNoHist(blk *blockEnc, src []byte) { + const ( + // Input margin is the number of bytes we read (8) + // and the maximum we will read ahead (2) + inputMargin = 8 + 2 + minNonLiteralBlockSize = 16 + ) + + // Protect against e.cur wraparound. + if e.cur >= e.bufferReset { + for i := range e.table[:] { + e.table[i] = tableEntry{} + } + for i := range e.longTable[:] { + e.longTable[i] = tableEntry{} + } + e.cur = e.maxMatchOff + } + + s := int32(0) + blk.size = len(src) + if len(src) < minNonLiteralBlockSize { + blk.extraLits = len(src) + blk.literals = blk.literals[:len(src)] + copy(blk.literals, src) + return + } + + // Override src + sLimit := int32(len(src)) - inputMargin + // stepSize is the number of bytes to skip on every main loop iteration. + // It should be >= 1. + const stepSize = 1 + + const kSearchStrength = 8 + + // nextEmit is where in src the next emitLiteral should start from. + nextEmit := s + cv := load6432(src, s) + + // Relative offsets + offset1 := int32(blk.recentOffsets[0]) + offset2 := int32(blk.recentOffsets[1]) + + addLiterals := func(s *seq, until int32) { + if until == nextEmit { + return + } + blk.literals = append(blk.literals, src[nextEmit:until]...) + s.litLen = uint32(until - nextEmit) + } + if debugEncoder { + println("recent offsets:", blk.recentOffsets) + } + +encodeLoop: + for { + var t int32 + for { + + nextHashL := hashLen(cv, dFastLongTableBits, dFastLongLen) + nextHashS := hashLen(cv, dFastShortTableBits, dFastShortLen) + candidateL := e.longTable[nextHashL] + candidateS := e.table[nextHashS] + + const repOff = 1 + repIndex := s - offset1 + repOff + entry := tableEntry{offset: s + e.cur, val: uint32(cv)} + e.longTable[nextHashL] = entry + e.table[nextHashS] = entry + + if len(blk.sequences) > 2 { + if load3232(src, repIndex) == uint32(cv>>(repOff*8)) { + // Consider history as well. + var seq seq + //length := 4 + e.matchlen(s+4+repOff, repIndex+4, src) + length := 4 + int32(matchLen(src[s+4+repOff:], src[repIndex+4:])) + + seq.matchLen = uint32(length - zstdMinMatch) + + // We might be able to match backwards. + // Extend as long as we can. + start := s + repOff + // We end the search early, so we don't risk 0 literals + // and have to do special offset treatment. + startLimit := nextEmit + 1 + + tMin := max(s-e.maxMatchOff, 0) + for repIndex > tMin && start > startLimit && src[repIndex-1] == src[start-1] { + repIndex-- + start-- + seq.matchLen++ + } + addLiterals(&seq, start) + + // rep 0 + seq.offset = 1 + if debugSequences { + println("repeat sequence", seq, "next s:", s) + } + blk.sequences = append(blk.sequences, seq) + s += length + repOff + nextEmit = s + if s >= sLimit { + if debugEncoder { + println("repeat ended", s, length) + + } + break encodeLoop + } + cv = load6432(src, s) + continue + } + } + // Find the offsets of our two matches. + coffsetL := s - (candidateL.offset - e.cur) + coffsetS := s - (candidateS.offset - e.cur) + + // Check if we have a long match. + if coffsetL < e.maxMatchOff && uint32(cv) == candidateL.val { + // Found a long match, likely at least 8 bytes. + // Reference encoder checks all 8 bytes, we only check 4, + // but the likelihood of both the first 4 bytes and the hash matching should be enough. + t = candidateL.offset - e.cur + if debugAsserts && s <= t { + panic(fmt.Sprintf("s (%d) <= t (%d). cur: %d", s, t, e.cur)) + } + if debugAsserts && s-t > e.maxMatchOff { + panic("s - t >e.maxMatchOff") + } + if debugMatches { + println("long match") + } + break + } + + // Check if we have a short match. + if coffsetS < e.maxMatchOff && uint32(cv) == candidateS.val { + // found a regular match + // See if we can find a long match at s+1 + const checkAt = 1 + cv := load6432(src, s+checkAt) + nextHashL = hashLen(cv, dFastLongTableBits, dFastLongLen) + candidateL = e.longTable[nextHashL] + coffsetL = s - (candidateL.offset - e.cur) + checkAt + + // We can store it, since we have at least a 4 byte match. + e.longTable[nextHashL] = tableEntry{offset: s + checkAt + e.cur, val: uint32(cv)} + if coffsetL < e.maxMatchOff && uint32(cv) == candidateL.val { + // Found a long match, likely at least 8 bytes. + // Reference encoder checks all 8 bytes, we only check 4, + // but the likelihood of both the first 4 bytes and the hash matching should be enough. + t = candidateL.offset - e.cur + s += checkAt + if debugMatches { + println("long match (after short)") + } + break + } + + t = candidateS.offset - e.cur + if debugAsserts && s <= t { + panic(fmt.Sprintf("s (%d) <= t (%d)", s, t)) + } + if debugAsserts && s-t > e.maxMatchOff { + panic("s - t >e.maxMatchOff") + } + if debugAsserts && t < 0 { + panic("t<0") + } + if debugMatches { + println("short match") + } + break + } + + // No match found, move forward in input. + s += stepSize + ((s - nextEmit) >> (kSearchStrength - 1)) + if s >= sLimit { + break encodeLoop + } + cv = load6432(src, s) + } + + // A 4-byte match has been found. Update recent offsets. + // We'll later see if more than 4 bytes. + offset2 = offset1 + offset1 = s - t + + if debugAsserts && s <= t { + panic(fmt.Sprintf("s (%d) <= t (%d)", s, t)) + } + + // Extend the 4-byte match as long as possible. + //l := e.matchlen(s+4, t+4, src) + 4 + l := int32(matchLen(src[s+4:], src[t+4:])) + 4 + + // Extend backwards + tMin := max(s-e.maxMatchOff, 0) + for t > tMin && s > nextEmit && src[t-1] == src[s-1] { + s-- + t-- + l++ + } + + // Write our sequence + var seq seq + seq.litLen = uint32(s - nextEmit) + seq.matchLen = uint32(l - zstdMinMatch) + if seq.litLen > 0 { + blk.literals = append(blk.literals, src[nextEmit:s]...) + } + seq.offset = uint32(s-t) + 3 + s += l + if debugSequences { + println("sequence", seq, "next s:", s) + } + blk.sequences = append(blk.sequences, seq) + nextEmit = s + if s >= sLimit { + break encodeLoop + } + + // Index match start+1 (long) and start+2 (short) + index0 := s - l + 1 + // Index match end-2 (long) and end-1 (short) + index1 := s - 2 + + cv0 := load6432(src, index0) + cv1 := load6432(src, index1) + te0 := tableEntry{offset: index0 + e.cur, val: uint32(cv0)} + te1 := tableEntry{offset: index1 + e.cur, val: uint32(cv1)} + e.longTable[hashLen(cv0, dFastLongTableBits, dFastLongLen)] = te0 + e.longTable[hashLen(cv1, dFastLongTableBits, dFastLongLen)] = te1 + cv0 >>= 8 + cv1 >>= 8 + te0.offset++ + te1.offset++ + te0.val = uint32(cv0) + te1.val = uint32(cv1) + e.table[hashLen(cv0, dFastShortTableBits, dFastShortLen)] = te0 + e.table[hashLen(cv1, dFastShortTableBits, dFastShortLen)] = te1 + + cv = load6432(src, s) + + if len(blk.sequences) <= 2 { + continue + } + + // Check offset 2 + for { + o2 := s - offset2 + if load3232(src, o2) != uint32(cv) { + // Do regular search + break + } + + // Store this, since we have it. + nextHashS := hashLen(cv1>>8, dFastShortTableBits, dFastShortLen) + nextHashL := hashLen(cv, dFastLongTableBits, dFastLongLen) + + // We have at least 4 byte match. + // No need to check backwards. We come straight from a match + //l := 4 + e.matchlen(s+4, o2+4, src) + l := 4 + int32(matchLen(src[s+4:], src[o2+4:])) + + entry := tableEntry{offset: s + e.cur, val: uint32(cv)} + e.longTable[nextHashL] = entry + e.table[nextHashS] = entry + seq.matchLen = uint32(l) - zstdMinMatch + seq.litLen = 0 + + // Since litlen is always 0, this is offset 1. + seq.offset = 1 + s += l + nextEmit = s + if debugSequences { + println("sequence", seq, "next s:", s) + } + blk.sequences = append(blk.sequences, seq) + + // Swap offset 1 and 2. + offset1, offset2 = offset2, offset1 + if s >= sLimit { + // Finished + break encodeLoop + } + cv = load6432(src, s) + } + } + + if int(nextEmit) < len(src) { + blk.literals = append(blk.literals, src[nextEmit:]...) + blk.extraLits = len(src) - int(nextEmit) + } + if debugEncoder { + println("returning, recent offsets:", blk.recentOffsets, "extra literals:", blk.extraLits) + } + + // We do not store history, so we must offset e.cur to avoid false matches for next user. + if e.cur < e.bufferReset { + e.cur += int32(len(src)) + } +} + +// Encode will encode the content, with a dictionary if initialized for it. +func (e *doubleFastEncoderDict) Encode(blk *blockEnc, src []byte) { + const ( + // Input margin is the number of bytes we read (8) + // and the maximum we will read ahead (2) + inputMargin = 8 + 2 + minNonLiteralBlockSize = 16 + ) + + // Protect against e.cur wraparound. + for e.cur >= e.bufferReset-int32(len(e.hist)) { + if len(e.hist) == 0 { + for i := range e.table[:] { + e.table[i] = tableEntry{} + } + for i := range e.longTable[:] { + e.longTable[i] = tableEntry{} + } + e.markAllShardsDirty() + e.cur = e.maxMatchOff + break + } + // Shift down everything in the table that isn't already too far away. + minOff := e.cur + int32(len(e.hist)) - e.maxMatchOff + for i := range e.table[:] { + v := e.table[i].offset + if v < minOff { + v = 0 + } else { + v = v - e.cur + e.maxMatchOff + } + e.table[i].offset = v + } + for i := range e.longTable[:] { + v := e.longTable[i].offset + if v < minOff { + v = 0 + } else { + v = v - e.cur + e.maxMatchOff + } + e.longTable[i].offset = v + } + e.markAllShardsDirty() + e.cur = e.maxMatchOff + break + } + + s := e.addBlock(src) + blk.size = len(src) + if len(src) < minNonLiteralBlockSize { + blk.extraLits = len(src) + blk.literals = blk.literals[:len(src)] + copy(blk.literals, src) + return + } + + // Override src + src = e.hist + sLimit := int32(len(src)) - inputMargin + // stepSize is the number of bytes to skip on every main loop iteration. + // It should be >= 1. + const stepSize = 1 + + const kSearchStrength = 8 + + // nextEmit is where in src the next emitLiteral should start from. + nextEmit := s + cv := load6432(src, s) + + // Relative offsets + offset1 := int32(blk.recentOffsets[0]) + offset2 := int32(blk.recentOffsets[1]) + + addLiterals := func(s *seq, until int32) { + if until == nextEmit { + return + } + blk.literals = append(blk.literals, src[nextEmit:until]...) + s.litLen = uint32(until - nextEmit) + } + if debugEncoder { + println("recent offsets:", blk.recentOffsets) + } + +encodeLoop: + for { + var t int32 + // We allow the encoder to optionally turn off repeat offsets across blocks + canRepeat := len(blk.sequences) > 2 + + for { + if debugAsserts && canRepeat && offset1 == 0 { + panic("offset0 was 0") + } + + nextHashL := hashLen(cv, dFastLongTableBits, dFastLongLen) + nextHashS := hashLen(cv, dFastShortTableBits, dFastShortLen) + candidateL := e.longTable[nextHashL] + candidateS := e.table[nextHashS] + + const repOff = 1 + repIndex := s - offset1 + repOff + entry := tableEntry{offset: s + e.cur, val: uint32(cv)} + e.longTable[nextHashL] = entry + e.markLongShardDirty(nextHashL) + e.table[nextHashS] = entry + e.markShardDirty(nextHashS) + + if canRepeat { + if repIndex >= 0 && load3232(src, repIndex) == uint32(cv>>(repOff*8)) { + // Consider history as well. + var seq seq + length := 4 + e.matchlen(s+4+repOff, repIndex+4, src) + + seq.matchLen = uint32(length - zstdMinMatch) + + // We might be able to match backwards. + // Extend as long as we can. + start := s + repOff + // We end the search early, so we don't risk 0 literals + // and have to do special offset treatment. + startLimit := nextEmit + 1 + + tMin := max(s-e.maxMatchOff, 0) + for repIndex > tMin && start > startLimit && src[repIndex-1] == src[start-1] && seq.matchLen < maxMatchLength-zstdMinMatch-1 { + repIndex-- + start-- + seq.matchLen++ + } + addLiterals(&seq, start) + + // rep 0 + seq.offset = 1 + if debugSequences { + println("repeat sequence", seq, "next s:", s) + } + blk.sequences = append(blk.sequences, seq) + s += length + repOff + nextEmit = s + if s >= sLimit { + if debugEncoder { + println("repeat ended", s, length) + + } + break encodeLoop + } + cv = load6432(src, s) + continue + } + } + // Find the offsets of our two matches. + coffsetL := s - (candidateL.offset - e.cur) + coffsetS := s - (candidateS.offset - e.cur) + + // Check if we have a long match. + if coffsetL < e.maxMatchOff && uint32(cv) == candidateL.val { + // Found a long match, likely at least 8 bytes. + // Reference encoder checks all 8 bytes, we only check 4, + // but the likelihood of both the first 4 bytes and the hash matching should be enough. + t = candidateL.offset - e.cur + if debugAsserts && s <= t { + panic(fmt.Sprintf("s (%d) <= t (%d)", s, t)) + } + if debugAsserts && s-t > e.maxMatchOff { + panic("s - t >e.maxMatchOff") + } + if debugMatches { + println("long match") + } + break + } + + // Check if we have a short match. + if coffsetS < e.maxMatchOff && uint32(cv) == candidateS.val { + // found a regular match + // See if we can find a long match at s+1 + const checkAt = 1 + cv := load6432(src, s+checkAt) + nextHashL = hashLen(cv, dFastLongTableBits, dFastLongLen) + candidateL = e.longTable[nextHashL] + coffsetL = s - (candidateL.offset - e.cur) + checkAt + + // We can store it, since we have at least a 4 byte match. + e.longTable[nextHashL] = tableEntry{offset: s + checkAt + e.cur, val: uint32(cv)} + e.markLongShardDirty(nextHashL) + if coffsetL < e.maxMatchOff && uint32(cv) == candidateL.val { + // Found a long match, likely at least 8 bytes. + // Reference encoder checks all 8 bytes, we only check 4, + // but the likelihood of both the first 4 bytes and the hash matching should be enough. + t = candidateL.offset - e.cur + s += checkAt + if debugMatches { + println("long match (after short)") + } + break + } + + t = candidateS.offset - e.cur + if debugAsserts && s <= t { + panic(fmt.Sprintf("s (%d) <= t (%d)", s, t)) + } + if debugAsserts && s-t > e.maxMatchOff { + panic("s - t >e.maxMatchOff") + } + if debugAsserts && t < 0 { + panic("t<0") + } + if debugMatches { + println("short match") + } + break + } + + // No match found, move forward in input. + s += stepSize + ((s - nextEmit) >> (kSearchStrength - 1)) + if s >= sLimit { + break encodeLoop + } + cv = load6432(src, s) + } + + // A 4-byte match has been found. Update recent offsets. + // We'll later see if more than 4 bytes. + offset2 = offset1 + offset1 = s - t + + if debugAsserts && s <= t { + panic(fmt.Sprintf("s (%d) <= t (%d)", s, t)) + } + + if debugAsserts && canRepeat && int(offset1) > len(src) { + panic("invalid offset") + } + + // Extend the 4-byte match as long as possible. + l := e.matchlen(s+4, t+4, src) + 4 + + // Extend backwards + tMin := max(s-e.maxMatchOff, 0) + for t > tMin && s > nextEmit && src[t-1] == src[s-1] && l < maxMatchLength { + s-- + t-- + l++ + } + + // Write our sequence + var seq seq + seq.litLen = uint32(s - nextEmit) + seq.matchLen = uint32(l - zstdMinMatch) + if seq.litLen > 0 { + blk.literals = append(blk.literals, src[nextEmit:s]...) + } + seq.offset = uint32(s-t) + 3 + s += l + if debugSequences { + println("sequence", seq, "next s:", s) + } + blk.sequences = append(blk.sequences, seq) + nextEmit = s + if s >= sLimit { + break encodeLoop + } + + // Index match start+1 (long) and start+2 (short) + index0 := s - l + 1 + // Index match end-2 (long) and end-1 (short) + index1 := s - 2 + + cv0 := load6432(src, index0) + cv1 := load6432(src, index1) + te0 := tableEntry{offset: index0 + e.cur, val: uint32(cv0)} + te1 := tableEntry{offset: index1 + e.cur, val: uint32(cv1)} + longHash1 := hashLen(cv0, dFastLongTableBits, dFastLongLen) + longHash2 := hashLen(cv1, dFastLongTableBits, dFastLongLen) + e.longTable[longHash1] = te0 + e.longTable[longHash2] = te1 + e.markLongShardDirty(longHash1) + e.markLongShardDirty(longHash2) + cv0 >>= 8 + cv1 >>= 8 + te0.offset++ + te1.offset++ + te0.val = uint32(cv0) + te1.val = uint32(cv1) + hashVal1 := hashLen(cv0, dFastShortTableBits, dFastShortLen) + hashVal2 := hashLen(cv1, dFastShortTableBits, dFastShortLen) + e.table[hashVal1] = te0 + e.markShardDirty(hashVal1) + e.table[hashVal2] = te1 + e.markShardDirty(hashVal2) + + cv = load6432(src, s) + + if !canRepeat { + continue + } + + // Check offset 2 + for { + o2 := s - offset2 + if load3232(src, o2) != uint32(cv) { + // Do regular search + break + } + + // Store this, since we have it. + nextHashL := hashLen(cv, dFastLongTableBits, dFastLongLen) + nextHashS := hashLen(cv, dFastShortTableBits, dFastShortLen) + + // We have at least 4 byte match. + // No need to check backwards. We come straight from a match + l := 4 + e.matchlen(s+4, o2+4, src) + + entry := tableEntry{offset: s + e.cur, val: uint32(cv)} + e.longTable[nextHashL] = entry + e.markLongShardDirty(nextHashL) + e.table[nextHashS] = entry + e.markShardDirty(nextHashS) + seq.matchLen = uint32(l) - zstdMinMatch + seq.litLen = 0 + + // Since litlen is always 0, this is offset 1. + seq.offset = 1 + s += l + nextEmit = s + if debugSequences { + println("sequence", seq, "next s:", s) + } + blk.sequences = append(blk.sequences, seq) + + // Swap offset 1 and 2. + offset1, offset2 = offset2, offset1 + if s >= sLimit { + // Finished + break encodeLoop + } + cv = load6432(src, s) + } + } + + if int(nextEmit) < len(src) { + blk.literals = append(blk.literals, src[nextEmit:]...) + blk.extraLits = len(src) - int(nextEmit) + } + blk.recentOffsets[0] = uint32(offset1) + blk.recentOffsets[1] = uint32(offset2) + if debugEncoder { + println("returning, recent offsets:", blk.recentOffsets, "extra literals:", blk.extraLits) + } + // If we encoded more than 64K mark all dirty. + if len(src) > 64<<10 { + e.markAllShardsDirty() + } +} + +// ResetDict will reset and set a dictionary if not nil +func (e *doubleFastEncoder) Reset(d *dict, singleBlock bool) { + e.fastEncoder.Reset(d, singleBlock) + if d != nil { + panic("doubleFastEncoder: Reset with dict not supported") + } +} + +// ResetDict will reset and set a dictionary if not nil +func (e *doubleFastEncoderDict) Reset(d *dict, singleBlock bool) { + allDirty := e.allDirty + dictChanged := d != e.lastDict + e.fastEncoderDict.Reset(d, singleBlock) + if d == nil { + return + } + + // Init or copy dict table + if len(e.dictLongTable) != len(e.longTable) || dictChanged { + if len(e.dictLongTable) != len(e.longTable) { + e.dictLongTable = make([]tableEntry, len(e.longTable)) + } else { + clear(e.dictLongTable) + } + if len(d.content) >= 8 { + cv := load6432(d.content, 0) + e.dictLongTable[hashLen(cv, dFastLongTableBits, dFastLongLen)] = tableEntry{ + val: uint32(cv), + offset: e.maxMatchOff, + } + end := int32(len(d.content)) - 8 + e.maxMatchOff + for i := e.maxMatchOff + 1; i < end; i++ { + cv = cv>>8 | (uint64(d.content[i-e.maxMatchOff+7]) << 56) + e.dictLongTable[hashLen(cv, dFastLongTableBits, dFastLongLen)] = tableEntry{ + val: uint32(cv), + offset: i, + } + } + } + allDirty = true + } + // Reset table to initial state + e.cur = e.maxMatchOff + + dirtyShardCnt := 0 + if !allDirty { + for i := range e.longTableShardDirty { + if e.longTableShardDirty[i] { + dirtyShardCnt++ + } + } + } + + if allDirty || dirtyShardCnt > dLongTableShardCnt/2 { + //copy(e.longTable[:], e.dictLongTable) + e.longTable = *(*[dFastLongTableSize]tableEntry)(e.dictLongTable) + for i := range e.longTableShardDirty { + e.longTableShardDirty[i] = false + } + return + } + for i := range e.longTableShardDirty { + if !e.longTableShardDirty[i] { + continue + } + + // copy(e.longTable[i*dLongTableShardSize:(i+1)*dLongTableShardSize], e.dictLongTable[i*dLongTableShardSize:(i+1)*dLongTableShardSize]) + *(*[dLongTableShardSize]tableEntry)(e.longTable[i*dLongTableShardSize:]) = *(*[dLongTableShardSize]tableEntry)(e.dictLongTable[i*dLongTableShardSize:]) + + e.longTableShardDirty[i] = false + } +} + +func (e *doubleFastEncoderDict) markLongShardDirty(entryNum uint32) { + e.longTableShardDirty[entryNum/dLongTableShardSize] = true +} diff --git a/vendor/github.com/klauspost/compress/zstd/enc_fast.go b/vendor/github.com/klauspost/compress/zstd/enc_fast.go new file mode 100644 index 000000000..5e104f1a4 --- /dev/null +++ b/vendor/github.com/klauspost/compress/zstd/enc_fast.go @@ -0,0 +1,875 @@ +// Copyright 2019+ Klaus Post. All rights reserved. +// License information can be found in the LICENSE file. +// Based on work by Yann Collet, released under BSD License. + +package zstd + +import ( + "fmt" +) + +const ( + tableBits = 15 // Bits used in the table + tableSize = 1 << tableBits // Size of the table + tableShardCnt = 1 << (tableBits - dictShardBits) // Number of shards in the table + tableShardSize = tableSize / tableShardCnt // Size of an individual shard + tableFastHashLen = 6 + tableMask = tableSize - 1 // Mask for table indices. Redundant, but can eliminate bounds checks. + maxMatchLength = 131074 +) + +type tableEntry struct { + val uint32 + offset int32 +} + +type fastEncoder struct { + fastBase + table [tableSize]tableEntry +} + +type fastEncoderDict struct { + fastEncoder + dictTable []tableEntry + tableShardDirty [tableShardCnt]bool + allDirty bool +} + +// Encode mimmics functionality in zstd_fast.c +func (e *fastEncoder) Encode(blk *blockEnc, src []byte) { + const ( + inputMargin = 8 + minNonLiteralBlockSize = 1 + 1 + inputMargin + ) + + // Protect against e.cur wraparound. + for e.cur >= e.bufferReset-int32(len(e.hist)) { + if len(e.hist) == 0 { + for i := range e.table[:] { + e.table[i] = tableEntry{} + } + e.cur = e.maxMatchOff + break + } + // Shift down everything in the table that isn't already too far away. + minOff := e.cur + int32(len(e.hist)) - e.maxMatchOff + for i := range e.table[:] { + v := e.table[i].offset + if v < minOff { + v = 0 + } else { + v = v - e.cur + e.maxMatchOff + } + e.table[i].offset = v + } + e.cur = e.maxMatchOff + break + } + + s := e.addBlock(src) + blk.size = len(src) + if len(src) < minNonLiteralBlockSize { + blk.extraLits = len(src) + blk.literals = blk.literals[:len(src)] + copy(blk.literals, src) + return + } + + // Override src + src = e.hist + sLimit := int32(len(src)) - inputMargin + // stepSize is the number of bytes to skip on every main loop iteration. + // It should be >= 2. + const stepSize = 2 + + // TEMPLATE + const hashLog = tableBits + // seems global, but would be nice to tweak. + const kSearchStrength = 6 + + // nextEmit is where in src the next emitLiteral should start from. + nextEmit := s + cv := load6432(src, s) + + // Relative offsets + offset1 := int32(blk.recentOffsets[0]) + offset2 := int32(blk.recentOffsets[1]) + + addLiterals := func(s *seq, until int32) { + if until == nextEmit { + return + } + blk.literals = append(blk.literals, src[nextEmit:until]...) + s.litLen = uint32(until - nextEmit) + } + if debugEncoder { + println("recent offsets:", blk.recentOffsets) + } + +encodeLoop: + for { + // t will contain the match offset when we find one. + // When existing the search loop, we have already checked 4 bytes. + var t int32 + + // We will not use repeat offsets across blocks. + // By not using them for the first 3 matches + canRepeat := len(blk.sequences) > 2 + + for { + if debugAsserts && canRepeat && offset1 == 0 { + panic("offset0 was 0") + } + + nextHash := hashLen(cv, hashLog, tableFastHashLen) + nextHash2 := hashLen(cv>>8, hashLog, tableFastHashLen) + candidate := e.table[nextHash] + candidate2 := e.table[nextHash2] + repIndex := s - offset1 + 2 + + e.table[nextHash] = tableEntry{offset: s + e.cur, val: uint32(cv)} + e.table[nextHash2] = tableEntry{offset: s + e.cur + 1, val: uint32(cv >> 8)} + + if canRepeat && repIndex >= 0 && load3232(src, repIndex) == uint32(cv>>16) { + // Consider history as well. + var seq seq + length := 4 + e.matchlen(s+6, repIndex+4, src) + seq.matchLen = uint32(length - zstdMinMatch) + + // We might be able to match backwards. + // Extend as long as we can. + start := s + 2 + // We end the search early, so we don't risk 0 literals + // and have to do special offset treatment. + startLimit := nextEmit + 1 + + sMin := max(s-e.maxMatchOff, 0) + for repIndex > sMin && start > startLimit && src[repIndex-1] == src[start-1] && seq.matchLen < maxMatchLength-zstdMinMatch { + repIndex-- + start-- + seq.matchLen++ + } + addLiterals(&seq, start) + + // rep 0 + seq.offset = 1 + if debugSequences { + println("repeat sequence", seq, "next s:", s) + } + blk.sequences = append(blk.sequences, seq) + s += length + 2 + nextEmit = s + if s >= sLimit { + if debugEncoder { + println("repeat ended", s, length) + + } + break encodeLoop + } + cv = load6432(src, s) + continue + } + coffset0 := s - (candidate.offset - e.cur) + coffset1 := s - (candidate2.offset - e.cur) + 1 + if coffset0 < e.maxMatchOff && uint32(cv) == candidate.val { + // found a regular match + t = candidate.offset - e.cur + if debugAsserts && s <= t { + panic(fmt.Sprintf("s (%d) <= t (%d)", s, t)) + } + if debugAsserts && s-t > e.maxMatchOff { + panic("s - t >e.maxMatchOff") + } + break + } + + if coffset1 < e.maxMatchOff && uint32(cv>>8) == candidate2.val { + // found a regular match + t = candidate2.offset - e.cur + s++ + if debugAsserts && s <= t { + panic(fmt.Sprintf("s (%d) <= t (%d)", s, t)) + } + if debugAsserts && s-t > e.maxMatchOff { + panic("s - t >e.maxMatchOff") + } + if debugAsserts && t < 0 { + panic("t<0") + } + break + } + s += stepSize + ((s - nextEmit) >> (kSearchStrength - 1)) + if s >= sLimit { + break encodeLoop + } + cv = load6432(src, s) + } + // A 4-byte match has been found. We'll later see if more than 4 bytes. + offset2 = offset1 + offset1 = s - t + + if debugAsserts && s <= t { + panic(fmt.Sprintf("s (%d) <= t (%d)", s, t)) + } + + if debugAsserts && canRepeat && int(offset1) > len(src) { + panic("invalid offset") + } + + // Extend the 4-byte match as long as possible. + l := e.matchlen(s+4, t+4, src) + 4 + + // Extend backwards + tMin := max(s-e.maxMatchOff, 0) + for t > tMin && s > nextEmit && src[t-1] == src[s-1] && l < maxMatchLength { + s-- + t-- + l++ + } + + // Write our sequence. + var seq seq + seq.litLen = uint32(s - nextEmit) + seq.matchLen = uint32(l - zstdMinMatch) + if seq.litLen > 0 { + blk.literals = append(blk.literals, src[nextEmit:s]...) + } + // Don't use repeat offsets + seq.offset = uint32(s-t) + 3 + s += l + if debugSequences { + println("sequence", seq, "next s:", s) + } + blk.sequences = append(blk.sequences, seq) + nextEmit = s + if s >= sLimit { + break encodeLoop + } + cv = load6432(src, s) + + // Check offset 2 + if o2 := s - offset2; canRepeat && load3232(src, o2) == uint32(cv) { + // We have at least 4 byte match. + // No need to check backwards. We come straight from a match + l := 4 + e.matchlen(s+4, o2+4, src) + + // Store this, since we have it. + nextHash := hashLen(cv, hashLog, tableFastHashLen) + e.table[nextHash] = tableEntry{offset: s + e.cur, val: uint32(cv)} + seq.matchLen = uint32(l) - zstdMinMatch + seq.litLen = 0 + // Since litlen is always 0, this is offset 1. + seq.offset = 1 + s += l + nextEmit = s + if debugSequences { + println("sequence", seq, "next s:", s) + } + blk.sequences = append(blk.sequences, seq) + + // Swap offset 1 and 2. + offset1, offset2 = offset2, offset1 + if s >= sLimit { + break encodeLoop + } + // Prepare next loop. + cv = load6432(src, s) + } + } + + if int(nextEmit) < len(src) { + blk.literals = append(blk.literals, src[nextEmit:]...) + blk.extraLits = len(src) - int(nextEmit) + } + blk.recentOffsets[0] = uint32(offset1) + blk.recentOffsets[1] = uint32(offset2) + if debugEncoder { + println("returning, recent offsets:", blk.recentOffsets, "extra literals:", blk.extraLits) + } +} + +// EncodeNoHist will encode a block with no history and no following blocks. +// Most notable difference is that src will not be copied for history and +// we do not need to check for max match length. +func (e *fastEncoder) EncodeNoHist(blk *blockEnc, src []byte) { + const ( + inputMargin = 8 + minNonLiteralBlockSize = 1 + 1 + inputMargin + ) + if debugEncoder { + if len(src) > maxCompressedBlockSize { + panic("src too big") + } + } + + // Protect against e.cur wraparound. + if e.cur >= e.bufferReset { + for i := range e.table[:] { + e.table[i] = tableEntry{} + } + e.cur = e.maxMatchOff + } + + s := int32(0) + blk.size = len(src) + if len(src) < minNonLiteralBlockSize { + blk.extraLits = len(src) + blk.literals = blk.literals[:len(src)] + copy(blk.literals, src) + return + } + + sLimit := int32(len(src)) - inputMargin + // stepSize is the number of bytes to skip on every main loop iteration. + // It should be >= 2. + const stepSize = 2 + + // TEMPLATE + const hashLog = tableBits + // seems global, but would be nice to tweak. + const kSearchStrength = 6 + + // nextEmit is where in src the next emitLiteral should start from. + nextEmit := s + cv := load6432(src, s) + + // Relative offsets + offset1 := int32(blk.recentOffsets[0]) + offset2 := int32(blk.recentOffsets[1]) + + addLiterals := func(s *seq, until int32) { + if until == nextEmit { + return + } + blk.literals = append(blk.literals, src[nextEmit:until]...) + s.litLen = uint32(until - nextEmit) + } + if debugEncoder { + println("recent offsets:", blk.recentOffsets) + } + +encodeLoop: + for { + // t will contain the match offset when we find one. + // When existing the search loop, we have already checked 4 bytes. + var t int32 + + // We will not use repeat offsets across blocks. + // By not using them for the first 3 matches + + for { + nextHash := hashLen(cv, hashLog, tableFastHashLen) + nextHash2 := hashLen(cv>>8, hashLog, tableFastHashLen) + candidate := e.table[nextHash] + candidate2 := e.table[nextHash2] + repIndex := s - offset1 + 2 + + e.table[nextHash] = tableEntry{offset: s + e.cur, val: uint32(cv)} + e.table[nextHash2] = tableEntry{offset: s + e.cur + 1, val: uint32(cv >> 8)} + + if len(blk.sequences) > 2 && load3232(src, repIndex) == uint32(cv>>16) { + // Consider history as well. + var seq seq + length := 4 + e.matchlen(s+6, repIndex+4, src) + + seq.matchLen = uint32(length - zstdMinMatch) + + // We might be able to match backwards. + // Extend as long as we can. + start := s + 2 + // We end the search early, so we don't risk 0 literals + // and have to do special offset treatment. + startLimit := nextEmit + 1 + + sMin := max(s-e.maxMatchOff, 0) + for repIndex > sMin && start > startLimit && src[repIndex-1] == src[start-1] { + repIndex-- + start-- + seq.matchLen++ + } + addLiterals(&seq, start) + + // rep 0 + seq.offset = 1 + if debugSequences { + println("repeat sequence", seq, "next s:", s) + } + blk.sequences = append(blk.sequences, seq) + s += length + 2 + nextEmit = s + if s >= sLimit { + if debugEncoder { + println("repeat ended", s, length) + + } + break encodeLoop + } + cv = load6432(src, s) + continue + } + coffset0 := s - (candidate.offset - e.cur) + coffset1 := s - (candidate2.offset - e.cur) + 1 + if coffset0 < e.maxMatchOff && uint32(cv) == candidate.val { + // found a regular match + t = candidate.offset - e.cur + if debugAsserts && s <= t { + panic(fmt.Sprintf("s (%d) <= t (%d)", s, t)) + } + if debugAsserts && s-t > e.maxMatchOff { + panic("s - t >e.maxMatchOff") + } + if debugAsserts && t < 0 { + panic(fmt.Sprintf("t (%d) < 0, candidate.offset: %d, e.cur: %d, coffset0: %d, e.maxMatchOff: %d", t, candidate.offset, e.cur, coffset0, e.maxMatchOff)) + } + break + } + + if coffset1 < e.maxMatchOff && uint32(cv>>8) == candidate2.val { + // found a regular match + t = candidate2.offset - e.cur + s++ + if debugAsserts && s <= t { + panic(fmt.Sprintf("s (%d) <= t (%d)", s, t)) + } + if debugAsserts && s-t > e.maxMatchOff { + panic("s - t >e.maxMatchOff") + } + if debugAsserts && t < 0 { + panic("t<0") + } + break + } + s += stepSize + ((s - nextEmit) >> (kSearchStrength - 1)) + if s >= sLimit { + break encodeLoop + } + cv = load6432(src, s) + } + // A 4-byte match has been found. We'll later see if more than 4 bytes. + offset2 = offset1 + offset1 = s - t + + if debugAsserts && s <= t { + panic(fmt.Sprintf("s (%d) <= t (%d)", s, t)) + } + + if debugAsserts && t < 0 { + panic(fmt.Sprintf("t (%d) < 0 ", t)) + } + // Extend the 4-byte match as long as possible. + l := e.matchlen(s+4, t+4, src) + 4 + + // Extend backwards + tMin := max(s-e.maxMatchOff, 0) + for t > tMin && s > nextEmit && src[t-1] == src[s-1] { + s-- + t-- + l++ + } + + // Write our sequence. + var seq seq + seq.litLen = uint32(s - nextEmit) + seq.matchLen = uint32(l - zstdMinMatch) + if seq.litLen > 0 { + blk.literals = append(blk.literals, src[nextEmit:s]...) + } + // Don't use repeat offsets + seq.offset = uint32(s-t) + 3 + s += l + if debugSequences { + println("sequence", seq, "next s:", s) + } + blk.sequences = append(blk.sequences, seq) + nextEmit = s + if s >= sLimit { + break encodeLoop + } + cv = load6432(src, s) + + // Check offset 2 + if o2 := s - offset2; len(blk.sequences) > 2 && load3232(src, o2) == uint32(cv) { + // We have at least 4 byte match. + // No need to check backwards. We come straight from a match + l := 4 + e.matchlen(s+4, o2+4, src) + + // Store this, since we have it. + nextHash := hashLen(cv, hashLog, tableFastHashLen) + e.table[nextHash] = tableEntry{offset: s + e.cur, val: uint32(cv)} + seq.matchLen = uint32(l) - zstdMinMatch + seq.litLen = 0 + // Since litlen is always 0, this is offset 1. + seq.offset = 1 + s += l + nextEmit = s + if debugSequences { + println("sequence", seq, "next s:", s) + } + blk.sequences = append(blk.sequences, seq) + + // Swap offset 1 and 2. + offset1, offset2 = offset2, offset1 + if s >= sLimit { + break encodeLoop + } + // Prepare next loop. + cv = load6432(src, s) + } + } + + if int(nextEmit) < len(src) { + blk.literals = append(blk.literals, src[nextEmit:]...) + blk.extraLits = len(src) - int(nextEmit) + } + if debugEncoder { + println("returning, recent offsets:", blk.recentOffsets, "extra literals:", blk.extraLits) + } + // We do not store history, so we must offset e.cur to avoid false matches for next user. + if e.cur < e.bufferReset { + e.cur += int32(len(src)) + } +} + +// Encode will encode the content, with a dictionary if initialized for it. +func (e *fastEncoderDict) Encode(blk *blockEnc, src []byte) { + const ( + inputMargin = 8 + minNonLiteralBlockSize = 1 + 1 + inputMargin + ) + if e.allDirty || len(src) > 32<<10 { + e.fastEncoder.Encode(blk, src) + e.allDirty = true + return + } + // Protect against e.cur wraparound. + for e.cur >= e.bufferReset-int32(len(e.hist)) { + if len(e.hist) == 0 { + e.table = [tableSize]tableEntry{} + e.cur = e.maxMatchOff + break + } + // Shift down everything in the table that isn't already too far away. + minOff := e.cur + int32(len(e.hist)) - e.maxMatchOff + for i := range e.table[:] { + v := e.table[i].offset + if v < minOff { + v = 0 + } else { + v = v - e.cur + e.maxMatchOff + } + e.table[i].offset = v + } + e.cur = e.maxMatchOff + break + } + + s := e.addBlock(src) + blk.size = len(src) + if len(src) < minNonLiteralBlockSize { + blk.extraLits = len(src) + blk.literals = blk.literals[:len(src)] + copy(blk.literals, src) + return + } + + // Override src + src = e.hist + sLimit := int32(len(src)) - inputMargin + // stepSize is the number of bytes to skip on every main loop iteration. + // It should be >= 2. + const stepSize = 2 + + // TEMPLATE + const hashLog = tableBits + // seems global, but would be nice to tweak. + const kSearchStrength = 7 + + // nextEmit is where in src the next emitLiteral should start from. + nextEmit := s + cv := load6432(src, s) + + // Relative offsets + offset1 := int32(blk.recentOffsets[0]) + offset2 := int32(blk.recentOffsets[1]) + + addLiterals := func(s *seq, until int32) { + if until == nextEmit { + return + } + blk.literals = append(blk.literals, src[nextEmit:until]...) + s.litLen = uint32(until - nextEmit) + } + if debugEncoder { + println("recent offsets:", blk.recentOffsets) + } + +encodeLoop: + for { + // t will contain the match offset when we find one. + // When existing the search loop, we have already checked 4 bytes. + var t int32 + + // We will not use repeat offsets across blocks. + // By not using them for the first 3 matches + canRepeat := len(blk.sequences) > 2 + + for { + if debugAsserts && canRepeat && offset1 == 0 { + panic("offset0 was 0") + } + + nextHash := hashLen(cv, hashLog, tableFastHashLen) + nextHash2 := hashLen(cv>>8, hashLog, tableFastHashLen) + candidate := e.table[nextHash] + candidate2 := e.table[nextHash2] + repIndex := s - offset1 + 2 + + e.table[nextHash] = tableEntry{offset: s + e.cur, val: uint32(cv)} + e.markShardDirty(nextHash) + e.table[nextHash2] = tableEntry{offset: s + e.cur + 1, val: uint32(cv >> 8)} + e.markShardDirty(nextHash2) + + if canRepeat && repIndex >= 0 && load3232(src, repIndex) == uint32(cv>>16) { + // Consider history as well. + var seq seq + length := 4 + e.matchlen(s+6, repIndex+4, src) + + seq.matchLen = uint32(length - zstdMinMatch) + + // We might be able to match backwards. + // Extend as long as we can. + start := s + 2 + // We end the search early, so we don't risk 0 literals + // and have to do special offset treatment. + startLimit := nextEmit + 1 + + sMin := max(s-e.maxMatchOff, 0) + for repIndex > sMin && start > startLimit && src[repIndex-1] == src[start-1] && seq.matchLen < maxMatchLength-zstdMinMatch { + repIndex-- + start-- + seq.matchLen++ + } + addLiterals(&seq, start) + + // rep 0 + seq.offset = 1 + if debugSequences { + println("repeat sequence", seq, "next s:", s) + } + blk.sequences = append(blk.sequences, seq) + s += length + 2 + nextEmit = s + if s >= sLimit { + if debugEncoder { + println("repeat ended", s, length) + + } + break encodeLoop + } + cv = load6432(src, s) + continue + } + coffset0 := s - (candidate.offset - e.cur) + coffset1 := s - (candidate2.offset - e.cur) + 1 + if coffset0 < e.maxMatchOff && uint32(cv) == candidate.val { + // found a regular match + t = candidate.offset - e.cur + if debugAsserts && s <= t { + panic(fmt.Sprintf("s (%d) <= t (%d)", s, t)) + } + if debugAsserts && s-t > e.maxMatchOff { + panic("s - t >e.maxMatchOff") + } + break + } + + if coffset1 < e.maxMatchOff && uint32(cv>>8) == candidate2.val { + // found a regular match + t = candidate2.offset - e.cur + s++ + if debugAsserts && s <= t { + panic(fmt.Sprintf("s (%d) <= t (%d)", s, t)) + } + if debugAsserts && s-t > e.maxMatchOff { + panic("s - t >e.maxMatchOff") + } + if debugAsserts && t < 0 { + panic("t<0") + } + break + } + s += stepSize + ((s - nextEmit) >> (kSearchStrength - 1)) + if s >= sLimit { + break encodeLoop + } + cv = load6432(src, s) + } + // A 4-byte match has been found. We'll later see if more than 4 bytes. + offset2 = offset1 + offset1 = s - t + + if debugAsserts && s <= t { + panic(fmt.Sprintf("s (%d) <= t (%d)", s, t)) + } + + if debugAsserts && canRepeat && int(offset1) > len(src) { + panic("invalid offset") + } + + // Extend the 4-byte match as long as possible. + l := e.matchlen(s+4, t+4, src) + 4 + + // Extend backwards + tMin := max(s-e.maxMatchOff, 0) + for t > tMin && s > nextEmit && src[t-1] == src[s-1] && l < maxMatchLength { + s-- + t-- + l++ + } + + // Write our sequence. + var seq seq + seq.litLen = uint32(s - nextEmit) + seq.matchLen = uint32(l - zstdMinMatch) + if seq.litLen > 0 { + blk.literals = append(blk.literals, src[nextEmit:s]...) + } + // Don't use repeat offsets + seq.offset = uint32(s-t) + 3 + s += l + if debugSequences { + println("sequence", seq, "next s:", s) + } + blk.sequences = append(blk.sequences, seq) + nextEmit = s + if s >= sLimit { + break encodeLoop + } + cv = load6432(src, s) + + // Check offset 2 + if o2 := s - offset2; canRepeat && load3232(src, o2) == uint32(cv) { + // We have at least 4 byte match. + // No need to check backwards. We come straight from a match + l := 4 + e.matchlen(s+4, o2+4, src) + + // Store this, since we have it. + nextHash := hashLen(cv, hashLog, tableFastHashLen) + e.table[nextHash] = tableEntry{offset: s + e.cur, val: uint32(cv)} + e.markShardDirty(nextHash) + seq.matchLen = uint32(l) - zstdMinMatch + seq.litLen = 0 + // Since litlen is always 0, this is offset 1. + seq.offset = 1 + s += l + nextEmit = s + if debugSequences { + println("sequence", seq, "next s:", s) + } + blk.sequences = append(blk.sequences, seq) + + // Swap offset 1 and 2. + offset1, offset2 = offset2, offset1 + if s >= sLimit { + break encodeLoop + } + // Prepare next loop. + cv = load6432(src, s) + } + } + + if int(nextEmit) < len(src) { + blk.literals = append(blk.literals, src[nextEmit:]...) + blk.extraLits = len(src) - int(nextEmit) + } + blk.recentOffsets[0] = uint32(offset1) + blk.recentOffsets[1] = uint32(offset2) + if debugEncoder { + println("returning, recent offsets:", blk.recentOffsets, "extra literals:", blk.extraLits) + } +} + +// ResetDict will reset and set a dictionary if not nil +func (e *fastEncoder) Reset(d *dict, singleBlock bool) { + e.resetBase(d, singleBlock) + if d != nil { + panic("fastEncoder: Reset with dict") + } +} + +// ResetDict will reset and set a dictionary if not nil +func (e *fastEncoderDict) Reset(d *dict, singleBlock bool) { + e.resetBase(d, singleBlock) + if d == nil { + return + } + + // Init or copy dict table + if len(e.dictTable) != len(e.table) || d != e.lastDict { + if len(e.dictTable) != len(e.table) { + e.dictTable = make([]tableEntry, len(e.table)) + } else { + clear(e.dictTable) + } + if true { + end := e.maxMatchOff + int32(len(d.content)) - 8 + for i := e.maxMatchOff; i < end; i += 2 { + const hashLog = tableBits + + cv := load6432(d.content, i-e.maxMatchOff) + nextHash := hashLen(cv, hashLog, tableFastHashLen) // 0 -> 6 + nextHash1 := hashLen(cv>>8, hashLog, tableFastHashLen) // 1 -> 7 + e.dictTable[nextHash] = tableEntry{ + val: uint32(cv), + offset: i, + } + e.dictTable[nextHash1] = tableEntry{ + val: uint32(cv >> 8), + offset: i + 1, + } + } + } + e.lastDict = d + e.allDirty = true + } + + e.cur = e.maxMatchOff + dirtyShardCnt := 0 + if !e.allDirty { + for i := range e.tableShardDirty { + if e.tableShardDirty[i] { + dirtyShardCnt++ + } + } + } + + const shardCnt = tableShardCnt + const shardSize = tableShardSize + if e.allDirty || dirtyShardCnt > shardCnt*4/6 { + //copy(e.table[:], e.dictTable) + e.table = *(*[tableSize]tableEntry)(e.dictTable) + for i := range e.tableShardDirty { + e.tableShardDirty[i] = false + } + e.allDirty = false + return + } + for i := range e.tableShardDirty { + if !e.tableShardDirty[i] { + continue + } + + //copy(e.table[i*shardSize:(i+1)*shardSize], e.dictTable[i*shardSize:(i+1)*shardSize]) + *(*[shardSize]tableEntry)(e.table[i*shardSize:]) = *(*[shardSize]tableEntry)(e.dictTable[i*shardSize:]) + e.tableShardDirty[i] = false + } + e.allDirty = false +} + +func (e *fastEncoderDict) markAllShardsDirty() { + e.allDirty = true +} + +func (e *fastEncoderDict) markShardDirty(entryNum uint32) { + e.tableShardDirty[entryNum/tableShardSize] = true +} diff --git a/vendor/github.com/klauspost/compress/zstd/encoder.go b/vendor/github.com/klauspost/compress/zstd/encoder.go new file mode 100644 index 000000000..0f2a00a00 --- /dev/null +++ b/vendor/github.com/klauspost/compress/zstd/encoder.go @@ -0,0 +1,671 @@ +// Copyright 2019+ Klaus Post. All rights reserved. +// License information can be found in the LICENSE file. +// Based on work by Yann Collet, released under BSD License. + +package zstd + +import ( + "crypto/rand" + "errors" + "fmt" + "io" + "math" + rdebug "runtime/debug" + "sync" + + "github.com/klauspost/compress/zstd/internal/xxhash" +) + +// Encoder provides encoding to Zstandard. +// An Encoder can be used for either compressing a stream via the +// io.WriteCloser interface supported by the Encoder or as multiple independent +// tasks via the EncodeAll function. +// Smaller encodes are encouraged to use the EncodeAll function. +// Use NewWriter to create a new instance. +type Encoder struct { + o encoderOptions + encoders chan encoder + state encoderState + init sync.Once +} + +type encoder interface { + Encode(blk *blockEnc, src []byte) + EncodeNoHist(blk *blockEnc, src []byte) + Block() *blockEnc + CRC() *xxhash.Digest + AppendCRC([]byte) []byte + WindowSize(size int64) int32 + UseBlock(*blockEnc) + Reset(d *dict, singleBlock bool) +} + +type encoderState struct { + w io.Writer + filling []byte + current []byte + previous []byte + encoder encoder + writing *blockEnc + err error + writeErr error + nWritten int64 + nInput int64 + frameContentSize int64 + headerWritten bool + eofWritten bool + fullFrameWritten bool + + // This waitgroup indicates an encode is running. + wg sync.WaitGroup + // This waitgroup indicates we have a block encoding/writing. + wWg sync.WaitGroup +} + +// NewWriter will create a new Zstandard encoder. +// If the encoder will be used for encoding blocks a nil writer can be used. +func NewWriter(w io.Writer, opts ...EOption) (*Encoder, error) { + initPredefined() + var e Encoder + e.o.setDefault() + for _, o := range opts { + err := o(&e.o) + if err != nil { + return nil, err + } + } + if w != nil { + e.Reset(w) + } + return &e, nil +} + +func (e *Encoder) initialize() { + if e.o.concurrent == 0 { + e.o.setDefault() + } + e.encoders = make(chan encoder, e.o.concurrent) + for i := 0; i < e.o.concurrent; i++ { + enc := e.o.encoder() + e.encoders <- enc + } +} + +// Reset will re-initialize the writer and new writes will encode to the supplied writer +// as a new, independent stream. +func (e *Encoder) Reset(w io.Writer) { + s := &e.state + s.wg.Wait() + s.wWg.Wait() + if cap(s.filling) == 0 { + s.filling = make([]byte, 0, e.o.blockSize) + } + if e.o.concurrent > 1 { + if cap(s.current) == 0 { + s.current = make([]byte, 0, e.o.blockSize) + } + if cap(s.previous) == 0 { + s.previous = make([]byte, 0, e.o.blockSize) + } + s.current = s.current[:0] + s.previous = s.previous[:0] + if s.writing == nil { + s.writing = &blockEnc{lowMem: e.o.lowMem} + s.writing.init() + } + s.writing.initNewEncode() + } + if s.encoder == nil { + s.encoder = e.o.encoder() + } + s.filling = s.filling[:0] + s.encoder.Reset(e.o.dict, false) + s.headerWritten = false + s.eofWritten = false + s.fullFrameWritten = false + s.w = w + s.err = nil + s.nWritten = 0 + s.nInput = 0 + s.writeErr = nil + s.frameContentSize = 0 +} + +// ResetWithOptions will re-initialize the writer and apply the given options +// as a new, independent stream. +// Options are applied on top of the existing options. +// Some options cannot be changed on reset and will return an error. +func (e *Encoder) ResetWithOptions(w io.Writer, opts ...EOption) error { + e.o.resetOpt = true + defer func() { e.o.resetOpt = false }() + hadDict := e.o.dict != nil + for _, o := range opts { + if err := o(&e.o); err != nil { + return err + } + } + hasDict := e.o.dict != nil + if hadDict != hasDict { + // Dict presence changed — encoder type must be recreated. + e.state.encoder = nil + e.init = sync.Once{} + } + e.Reset(w) + return nil +} + +// ResetContentSize will reset and set a content size for the next stream. +// If the bytes written does not match the size given an error will be returned +// when calling Close(). +// This is removed when Reset is called. +// Sizes <= 0 results in no content size set. +func (e *Encoder) ResetContentSize(w io.Writer, size int64) { + e.Reset(w) + if size >= 0 { + e.state.frameContentSize = size + } +} + +// Write data to the encoder. +// Input data will be buffered and as the buffer fills up +// content will be compressed and written to the output. +// When done writing, use Close to flush the remaining output +// and write CRC if requested. +func (e *Encoder) Write(p []byte) (n int, err error) { + s := &e.state + if s.eofWritten { + return 0, ErrEncoderClosed + } + for len(p) > 0 { + if len(p)+len(s.filling) < e.o.blockSize { + if e.o.crc { + _, _ = s.encoder.CRC().Write(p) + } + s.filling = append(s.filling, p...) + return n + len(p), nil + } + add := p + if len(p)+len(s.filling) > e.o.blockSize { + add = add[:e.o.blockSize-len(s.filling)] + } + if e.o.crc { + _, _ = s.encoder.CRC().Write(add) + } + s.filling = append(s.filling, add...) + p = p[len(add):] + n += len(add) + if len(s.filling) < e.o.blockSize { + return n, nil + } + err := e.nextBlock(false) + if err != nil { + return n, err + } + if debugAsserts && len(s.filling) > 0 { + panic(len(s.filling)) + } + } + return n, nil +} + +// nextBlock will synchronize and start compressing input in e.state.filling. +// If an error has occurred during encoding it will be returned. +func (e *Encoder) nextBlock(final bool) error { + s := &e.state + // Wait for current block. + s.wg.Wait() + if s.err != nil { + return s.err + } + if len(s.filling) > e.o.blockSize { + return fmt.Errorf("block > maxStoreBlockSize") + } + if !s.headerWritten { + // If we have a single block encode, do a sync compression. + if final && len(s.filling) == 0 && !e.o.fullZero { + s.headerWritten = true + s.fullFrameWritten = true + s.eofWritten = true + return nil + } + if final && len(s.filling) > 0 { + s.current = e.encodeAll(s.encoder, s.filling, s.current[:0]) + var n2 int + n2, s.err = s.w.Write(s.current) + if s.err != nil { + return s.err + } + s.nWritten += int64(n2) + s.nInput += int64(len(s.filling)) + s.current = s.current[:0] + s.filling = s.filling[:0] + s.headerWritten = true + s.fullFrameWritten = true + s.eofWritten = true + return nil + } + + var tmp [maxHeaderSize]byte + fh := frameHeader{ + ContentSize: uint64(s.frameContentSize), + WindowSize: uint32(s.encoder.WindowSize(s.frameContentSize)), + SingleSegment: false, + Checksum: e.o.crc, + DictID: e.o.dict.ID(), + } + + dst := fh.appendTo(tmp[:0]) + s.headerWritten = true + s.wWg.Wait() + var n2 int + n2, s.err = s.w.Write(dst) + if s.err != nil { + return s.err + } + s.nWritten += int64(n2) + } + if s.eofWritten { + // Ensure we only write it once. + final = false + } + + if len(s.filling) == 0 { + // Final block, but no data. + if final { + enc := s.encoder + blk := enc.Block() + blk.reset(nil) + blk.last = true + blk.encodeRaw(nil) + s.wWg.Wait() + _, s.err = s.w.Write(blk.output) + s.nWritten += int64(len(blk.output)) + s.eofWritten = true + } + return s.err + } + + // SYNC: + if e.o.concurrent == 1 { + src := s.filling + s.nInput += int64(len(s.filling)) + if debugEncoder { + println("Adding sync block,", len(src), "bytes, final:", final) + } + enc := s.encoder + blk := enc.Block() + blk.reset(nil) + enc.Encode(blk, src) + blk.last = final + if final { + s.eofWritten = true + } + + s.err = blk.encode(src, e.o.noEntropy, !e.o.allLitEntropy) + if s.err != nil { + return s.err + } + _, s.err = s.w.Write(blk.output) + s.nWritten += int64(len(blk.output)) + s.filling = s.filling[:0] + return s.err + } + + // Move blocks forward. + s.filling, s.current, s.previous = s.previous[:0], s.filling, s.current + s.nInput += int64(len(s.current)) + s.wg.Add(1) + if final { + s.eofWritten = true + } + go func(src []byte) { + if debugEncoder { + println("Adding block,", len(src), "bytes, final:", final) + } + defer func() { + if r := recover(); r != nil { + s.err = fmt.Errorf("panic while encoding: %v", r) + rdebug.PrintStack() + } + s.wg.Done() + }() + enc := s.encoder + blk := enc.Block() + enc.Encode(blk, src) + blk.last = final + // Wait for pending writes. + s.wWg.Wait() + if s.writeErr != nil { + s.err = s.writeErr + return + } + // Transfer encoders from previous write block. + blk.swapEncoders(s.writing) + // Transfer recent offsets to next. + enc.UseBlock(s.writing) + s.writing = blk + s.wWg.Add(1) + go func() { + defer func() { + if r := recover(); r != nil { + s.writeErr = fmt.Errorf("panic while encoding/writing: %v", r) + rdebug.PrintStack() + } + s.wWg.Done() + }() + s.writeErr = blk.encode(src, e.o.noEntropy, !e.o.allLitEntropy) + if s.writeErr != nil { + return + } + _, s.writeErr = s.w.Write(blk.output) + s.nWritten += int64(len(blk.output)) + }() + }(s.current) + return nil +} + +// ReadFrom reads data from r until EOF or error. +// The return value n is the number of bytes read. +// Any error except io.EOF encountered during the read is also returned. +// +// The Copy function uses ReaderFrom if available. +func (e *Encoder) ReadFrom(r io.Reader) (n int64, err error) { + if debugEncoder { + println("Using ReadFrom") + } + + // Flush any current writes. + if len(e.state.filling) > 0 { + if err := e.nextBlock(false); err != nil { + return 0, err + } + } + e.state.filling = e.state.filling[:e.o.blockSize] + src := e.state.filling + for { + n2, err := r.Read(src) + if e.o.crc { + _, _ = e.state.encoder.CRC().Write(src[:n2]) + } + // src is now the unfilled part... + src = src[n2:] + n += int64(n2) + switch err { + case io.EOF: + e.state.filling = e.state.filling[:len(e.state.filling)-len(src)] + if debugEncoder { + println("ReadFrom: got EOF final block:", len(e.state.filling)) + } + return n, nil + case nil: + default: + if debugEncoder { + println("ReadFrom: got error:", err) + } + e.state.err = err + return n, err + } + if len(src) > 0 { + if debugEncoder { + println("ReadFrom: got space left in source:", len(src)) + } + continue + } + err = e.nextBlock(false) + if err != nil { + return n, err + } + e.state.filling = e.state.filling[:e.o.blockSize] + src = e.state.filling + } +} + +// Flush will send the currently written data to output +// and block until everything has been written. +// This should only be used on rare occasions where pushing the currently queued data is critical. +func (e *Encoder) Flush() error { + s := &e.state + if len(s.filling) > 0 { + err := e.nextBlock(false) + if err != nil { + // Ignore Flush after Close. + if errors.Is(s.err, ErrEncoderClosed) { + return nil + } + return err + } + } + s.wg.Wait() + s.wWg.Wait() + if s.err != nil { + // Ignore Flush after Close. + if errors.Is(s.err, ErrEncoderClosed) { + return nil + } + return s.err + } + return s.writeErr +} + +// Close will flush the final output and close the stream. +// The function will block until everything has been written. +// The Encoder can still be re-used after calling this. +func (e *Encoder) Close() error { + s := &e.state + if s.encoder == nil { + return nil + } + if s.w == nil { + if len(s.filling) == 0 && !s.headerWritten && !s.eofWritten && s.nInput == 0 { + return nil + } + return errors.New("zstd: encoder has no writer") + } + err := e.nextBlock(true) + if err != nil { + if errors.Is(s.err, ErrEncoderClosed) { + return nil + } + return err + } + if s.frameContentSize > 0 { + if s.nInput != s.frameContentSize { + return fmt.Errorf("frame content size %d given, but %d bytes was written", s.frameContentSize, s.nInput) + } + } + if e.state.fullFrameWritten { + return s.err + } + s.wg.Wait() + s.wWg.Wait() + + if s.err != nil { + return s.err + } + if s.writeErr != nil { + return s.writeErr + } + + // Write CRC + if e.o.crc && s.err == nil { + // heap alloc. + var tmp [4]byte + _, s.err = s.w.Write(s.encoder.AppendCRC(tmp[:0])) + s.nWritten += 4 + } + + // Add padding with content from crypto/rand.Reader + if s.err == nil && e.o.pad > 0 { + add := calcSkippableFrame(s.nWritten, int64(e.o.pad)) + frame, err := skippableFrame(s.filling[:0], add, rand.Reader) + if err != nil { + return err + } + _, s.err = s.w.Write(frame) + } + if s.err == nil { + s.err = ErrEncoderClosed + return nil + } + + return s.err +} + +// EncodeAll will encode all input in src and append it to dst. +// This function can be called concurrently, but each call will only run on a single goroutine. +// If empty input is given, nothing is returned, unless WithZeroFrames is specified. +// Encoded blocks can be concatenated and the result will be the combined input stream. +// Data compressed with EncodeAll can be decoded with the Decoder, +// using either a stream or DecodeAll. +func (e *Encoder) EncodeAll(src, dst []byte) []byte { + e.init.Do(e.initialize) + enc := <-e.encoders + defer func() { + e.encoders <- enc + }() + return e.encodeAll(enc, src, dst) +} + +func (e *Encoder) encodeAll(enc encoder, src, dst []byte) []byte { + if len(src) == 0 { + if e.o.fullZero { + // Add frame header. + fh := frameHeader{ + ContentSize: 0, + WindowSize: MinWindowSize, + SingleSegment: true, + // Adding a checksum would be a waste of space. + Checksum: false, + DictID: 0, + } + dst = fh.appendTo(dst) + + // Write raw block as last one only. + var blk blockHeader + blk.setSize(0) + blk.setType(blockTypeRaw) + blk.setLast(true) + dst = blk.appendTo(dst) + } + return dst + } + + // Use single segments when above minimum window and below window size. + single := len(src) <= e.o.windowSize && len(src) > MinWindowSize + if e.o.single != nil { + single = *e.o.single + } + fh := frameHeader{ + ContentSize: uint64(len(src)), + WindowSize: uint32(enc.WindowSize(int64(len(src)))), + SingleSegment: single, + Checksum: e.o.crc, + DictID: e.o.dict.ID(), + } + + // If less than 1MB, allocate a buffer up front. + if len(dst) == 0 && cap(dst) == 0 && len(src) < 1<<20 && !e.o.lowMem { + dst = make([]byte, 0, len(src)) + } + dst = fh.appendTo(dst) + + // If we can do everything in one block, prefer that. + if len(src) <= e.o.blockSize { + enc.Reset(e.o.dict, true) + // Slightly faster with no history and everything in one block. + if e.o.crc { + _, _ = enc.CRC().Write(src) + } + blk := enc.Block() + blk.last = true + if e.o.dict == nil { + enc.EncodeNoHist(blk, src) + } else { + enc.Encode(blk, src) + } + + // If we got the exact same number of literals as input, + // assume the literals cannot be compressed. + oldout := blk.output + // Output directly to dst + blk.output = dst + + err := blk.encode(src, e.o.noEntropy, !e.o.allLitEntropy) + if err != nil { + panic(err) + } + dst = blk.output + blk.output = oldout + } else { + enc.Reset(e.o.dict, false) + blk := enc.Block() + for len(src) > 0 { + todo := src + if len(todo) > e.o.blockSize { + todo = todo[:e.o.blockSize] + } + src = src[len(todo):] + if e.o.crc { + _, _ = enc.CRC().Write(todo) + } + blk.pushOffsets() + enc.Encode(blk, todo) + if len(src) == 0 { + blk.last = true + } + err := blk.encode(todo, e.o.noEntropy, !e.o.allLitEntropy) + if err != nil { + panic(err) + } + dst = append(dst, blk.output...) + blk.reset(nil) + } + } + if e.o.crc { + dst = enc.AppendCRC(dst) + } + // Add padding with content from crypto/rand.Reader + if e.o.pad > 0 { + add := calcSkippableFrame(int64(len(dst)), int64(e.o.pad)) + var err error + dst, err = skippableFrame(dst, add, rand.Reader) + if err != nil { + panic(err) + } + } + return dst +} + +// MaxEncodedSize returns the expected maximum +// size of an encoded block or stream. +func (e *Encoder) MaxEncodedSize(size int) int { + frameHeader := 4 + 2 // magic + frame header & window descriptor + if e.o.dict != nil { + frameHeader += 4 + } + // Frame content size: + if size < 256 { + frameHeader++ + } else if size < 65536+256 { + frameHeader += 2 + } else if size < math.MaxInt32 { + frameHeader += 4 + } else { + frameHeader += 8 + } + // Final crc + if e.o.crc { + frameHeader += 4 + } + + // Max overhead is 3 bytes/block. + // There cannot be 0 blocks. + blocks := (size + e.o.blockSize) / e.o.blockSize + + // Combine, add padding. + maxSz := frameHeader + 3*blocks + size + if e.o.pad > 1 { + maxSz += calcSkippableFrame(int64(maxSz), int64(e.o.pad)) + } + return maxSz +} diff --git a/vendor/github.com/klauspost/compress/zstd/encoder_options.go b/vendor/github.com/klauspost/compress/zstd/encoder_options.go new file mode 100644 index 000000000..e217be0a1 --- /dev/null +++ b/vendor/github.com/klauspost/compress/zstd/encoder_options.go @@ -0,0 +1,378 @@ +package zstd + +import ( + "errors" + "fmt" + "math" + "math/bits" + "runtime" + "strings" +) + +// EOption is an option for creating a encoder. +type EOption func(*encoderOptions) error + +// options retains accumulated state of multiple options. +type encoderOptions struct { + resetOpt bool + concurrent int + level EncoderLevel + single *bool + pad int + blockSize int + windowSize int + crc bool + fullZero bool + noEntropy bool + allLitEntropy bool + customWindow bool + customALEntropy bool + customBlockSize bool + lowMem bool + dict *dict +} + +func (o *encoderOptions) setDefault() { + *o = encoderOptions{ + concurrent: runtime.GOMAXPROCS(0), + crc: true, + single: nil, + blockSize: maxCompressedBlockSize, + windowSize: 8 << 20, + level: SpeedDefault, + allLitEntropy: false, + lowMem: false, + fullZero: true, + } +} + +// encoder returns an encoder with the selected options. +func (o encoderOptions) encoder() encoder { + switch o.level { + case SpeedFastest: + if o.dict != nil { + return &fastEncoderDict{fastEncoder: fastEncoder{fastBase: fastBase{maxMatchOff: int32(o.windowSize), bufferReset: math.MaxInt32 - int32(o.windowSize*2), lowMem: o.lowMem}}} + } + return &fastEncoder{fastBase: fastBase{maxMatchOff: int32(o.windowSize), bufferReset: math.MaxInt32 - int32(o.windowSize*2), lowMem: o.lowMem}} + + case SpeedDefault: + if o.dict != nil { + return &doubleFastEncoderDict{fastEncoderDict: fastEncoderDict{fastEncoder: fastEncoder{fastBase: fastBase{maxMatchOff: int32(o.windowSize), bufferReset: math.MaxInt32 - int32(o.windowSize*2), lowMem: o.lowMem}}}} + } + return &doubleFastEncoder{fastEncoder: fastEncoder{fastBase: fastBase{maxMatchOff: int32(o.windowSize), bufferReset: math.MaxInt32 - int32(o.windowSize*2), lowMem: o.lowMem}}} + case SpeedBetterCompression: + if o.dict != nil { + return &betterFastEncoderDict{betterFastEncoder: betterFastEncoder{fastBase: fastBase{maxMatchOff: int32(o.windowSize), bufferReset: math.MaxInt32 - int32(o.windowSize*2), lowMem: o.lowMem}}} + } + return &betterFastEncoder{fastBase: fastBase{maxMatchOff: int32(o.windowSize), bufferReset: math.MaxInt32 - int32(o.windowSize*2), lowMem: o.lowMem}} + case SpeedBestCompression: + return &bestFastEncoder{fastBase: fastBase{maxMatchOff: int32(o.windowSize), bufferReset: math.MaxInt32 - int32(o.windowSize*2), lowMem: o.lowMem}} + } + panic("unknown compression level") +} + +// WithEncoderCRC will add CRC value to output. +// Output will be 4 bytes larger. +// Can be changed with ResetWithOptions. +func WithEncoderCRC(b bool) EOption { + return func(o *encoderOptions) error { o.crc = b; return nil } +} + +// WithEncoderConcurrency will set the concurrency, +// meaning the maximum number of encoders to run concurrently. +// The value supplied must be at least 0. +// When a value of 0 is provided GOMAXPROCS will be used. +// For streams, setting a value of 1 will disable async compression. +// By default this will be set to GOMAXPROCS. +// Cannot be changed with ResetWithOptions. +func WithEncoderConcurrency(n int) EOption { + return func(o *encoderOptions) error { + if n < 0 { + return errors.New("concurrency must at least 0") + } + if n == 0 { + n = runtime.GOMAXPROCS(0) + } + if o.resetOpt && n != o.concurrent { + return errors.New("WithEncoderConcurrency cannot be changed on Reset") + } + o.concurrent = n + return nil + } +} + +// WithWindowSize will set the maximum allowed back-reference distance. +// The value must be a power of two between MinWindowSize and MaxWindowSize. +// A larger value will enable better compression but allocate more memory and, +// for above-default values, take considerably longer. +// The default value is determined by the compression level and max 8MB. +// Cannot be changed with ResetWithOptions. +func WithWindowSize(n int) EOption { + return func(o *encoderOptions) error { + switch { + case n < MinWindowSize: + return fmt.Errorf("window size must be at least %d", MinWindowSize) + case n > MaxWindowSize: + return fmt.Errorf("window size must be at most %d", MaxWindowSize) + case (n & (n - 1)) != 0: + return errors.New("window size must be a power of 2") + } + if o.resetOpt && n != o.windowSize { + return errors.New("WithWindowSize cannot be changed on Reset") + } + + o.windowSize = n + o.customWindow = true + if o.blockSize > o.windowSize { + o.blockSize = o.windowSize + o.customBlockSize = true + } + return nil + } +} + +// WithEncoderPadding will add padding to all output so the size will be a multiple of n. +// This can be used to obfuscate the exact output size or make blocks of a certain size. +// The contents will be a skippable frame, so it will be invisible by the decoder. +// n must be > 0 and <= 1GB, 1<<30 bytes. +// The padded area will be filled with data from crypto/rand.Reader. +// If `EncodeAll` is used with data already in the destination, the total size will be multiple of this. +// Can be changed with ResetWithOptions. +func WithEncoderPadding(n int) EOption { + return func(o *encoderOptions) error { + if n <= 0 { + return fmt.Errorf("padding must be at least 1") + } + // No need to waste our time. + if n == 1 { + n = 0 + } + if n > 1<<30 { + return fmt.Errorf("padding must less than 1GB (1<<30 bytes) ") + } + o.pad = n + return nil + } +} + +// EncoderLevel predefines encoder compression levels. +// Only use the constants made available, since the actual mapping +// of these values are very likely to change and your compression could change +// unpredictably when upgrading the library. +type EncoderLevel int + +const ( + speedNotSet EncoderLevel = iota + + // SpeedFastest will choose the fastest reasonable compression. + // This is roughly equivalent to the fastest Zstandard mode. + SpeedFastest + + // SpeedDefault is the default "pretty fast" compression option. + // This is roughly equivalent to the default Zstandard mode (level 3). + SpeedDefault + + // SpeedBetterCompression will yield better compression than the default. + // Currently it is about zstd level 7-8 with ~ 2x-3x the default CPU usage. + // By using this, notice that CPU usage may go up in the future. + SpeedBetterCompression + + // SpeedBestCompression will choose the best available compression option. + // This will offer the best compression no matter the CPU cost. + SpeedBestCompression + + // speedLast should be kept as the last actual compression option. + // The is not for external usage, but is used to keep track of the valid options. + speedLast +) + +// EncoderLevelFromString will convert a string representation of an encoding level back +// to a compression level. The compare is not case sensitive. +// If the string wasn't recognized, (false, SpeedDefault) will be returned. +func EncoderLevelFromString(s string) (bool, EncoderLevel) { + for l := speedNotSet + 1; l < speedLast; l++ { + if strings.EqualFold(s, l.String()) { + return true, l + } + } + return false, SpeedDefault +} + +// EncoderLevelFromZstd will return an encoder level that closest matches the compression +// ratio of a specific zstd compression level. +// Many input values will provide the same compression level. +func EncoderLevelFromZstd(level int) EncoderLevel { + switch { + case level < 3: + return SpeedFastest + case level >= 3 && level < 6: + return SpeedDefault + case level >= 6 && level < 10: + return SpeedBetterCompression + default: + return SpeedBestCompression + } +} + +// String provides a string representation of the compression level. +func (e EncoderLevel) String() string { + switch e { + case SpeedFastest: + return "fastest" + case SpeedDefault: + return "default" + case SpeedBetterCompression: + return "better" + case SpeedBestCompression: + return "best" + default: + return "invalid" + } +} + +// WithEncoderLevel specifies a predefined compression level. +// Cannot be changed with ResetWithOptions. +func WithEncoderLevel(l EncoderLevel) EOption { + return func(o *encoderOptions) error { + switch { + case l <= speedNotSet || l >= speedLast: + return fmt.Errorf("unknown encoder level") + } + if o.resetOpt && l != o.level { + return errors.New("WithEncoderLevel cannot be changed on Reset") + } + o.level = l + if !o.customWindow { + switch o.level { + case SpeedFastest: + o.windowSize = 4 << 20 + if !o.customBlockSize { + o.blockSize = 1 << 16 + } + case SpeedDefault: + o.windowSize = 8 << 20 + case SpeedBetterCompression: + o.windowSize = 8 << 20 + case SpeedBestCompression: + o.windowSize = 8 << 20 + } + } + if !o.customALEntropy { + o.allLitEntropy = l > SpeedDefault + } + + return nil + } +} + +// WithZeroFrames will encode 0 length input as full frames. +// This can be needed for compatibility with zstandard usage, +// but is not needed for this package. +// Can be changed with ResetWithOptions. +func WithZeroFrames(b bool) EOption { + return func(o *encoderOptions) error { + o.fullZero = b + return nil + } +} + +// WithAllLitEntropyCompression will apply entropy compression if no matches are found. +// Disabling this will skip incompressible data faster, but in cases with no matches but +// skewed character distribution compression is lost. +// Default value depends on the compression level selected. +// Can be changed with ResetWithOptions. +func WithAllLitEntropyCompression(b bool) EOption { + return func(o *encoderOptions) error { + o.customALEntropy = true + o.allLitEntropy = b + return nil + } +} + +// WithNoEntropyCompression will always skip entropy compression of literals. +// This can be useful if content has matches, but unlikely to benefit from entropy +// compression. Usually the slight speed improvement is not worth enabling this. +// Can be changed with ResetWithOptions. +func WithNoEntropyCompression(b bool) EOption { + return func(o *encoderOptions) error { + o.noEntropy = b + return nil + } +} + +// WithSingleSegment will set the "single segment" flag when EncodeAll is used. +// If this flag is set, data must be regenerated within a single continuous memory segment. +// In this case, Window_Descriptor byte is skipped, but Frame_Content_Size is necessarily present. +// As a consequence, the decoder must allocate a memory segment of size equal or larger than size of your content. +// In order to preserve the decoder from unreasonable memory requirements, +// a decoder is allowed to reject a compressed frame which requests a memory size beyond decoder's authorized range. +// For broader compatibility, decoders are recommended to support memory sizes of at least 8 MB. +// This is only a recommendation, each decoder is free to support higher or lower limits, depending on local limitations. +// If this is not specified, block encodes will automatically choose this based on the input size and the window size. +// This setting has no effect on streamed encodes. +// Can be changed with ResetWithOptions. +func WithSingleSegment(b bool) EOption { + return func(o *encoderOptions) error { + o.single = &b + return nil + } +} + +// WithLowerEncoderMem will trade in some memory cases trade less memory usage for +// slower encoding speed. +// This will not change the window size which is the primary function for reducing +// memory usage. See WithWindowSize. +// Cannot be changed with ResetWithOptions. +func WithLowerEncoderMem(b bool) EOption { + return func(o *encoderOptions) error { + if o.resetOpt && b != o.lowMem { + return errors.New("WithLowerEncoderMem cannot be changed on Reset") + } + o.lowMem = b + return nil + } +} + +// WithEncoderDict allows to register a dictionary that will be used for the encode. +// +// The slice dict must be in the [dictionary format] produced by +// "zstd --train" from the Zstandard reference implementation. +// +// The encoder *may* choose to use no dictionary instead for certain payloads. +// Can be changed with ResetWithOptions. +// +// [dictionary format]: https://github.com/facebook/zstd/blob/dev/doc/zstd_compression_format.md#dictionary-format +func WithEncoderDict(dict []byte) EOption { + return func(o *encoderOptions) error { + d, err := loadDict(dict) + if err != nil { + return err + } + o.dict = d + return nil + } +} + +// WithEncoderDictRaw registers a dictionary that may be used by the encoder. +// +// The slice content may contain arbitrary data. It will be used as an initial +// history. +// Can be changed with ResetWithOptions. +func WithEncoderDictRaw(id uint32, content []byte) EOption { + return func(o *encoderOptions) error { + if bits.UintSize > 32 && uint(len(content)) > dictMaxLength { + return fmt.Errorf("dictionary of size %d > 2GiB too large", len(content)) + } + o.dict = &dict{id: id, content: content, offsets: [3]int{1, 4, 8}} + return nil + } +} + +// WithEncoderDictDelete clears the dictionary, so no dictionary will be used. +// Should be used with ResetWithOptions. +func WithEncoderDictDelete() EOption { + return func(o *encoderOptions) error { + o.dict = nil + return nil + } +} diff --git a/vendor/github.com/klauspost/compress/zstd/framedec.go b/vendor/github.com/klauspost/compress/zstd/framedec.go new file mode 100644 index 000000000..d88f067e5 --- /dev/null +++ b/vendor/github.com/klauspost/compress/zstd/framedec.go @@ -0,0 +1,412 @@ +// Copyright 2019+ Klaus Post. All rights reserved. +// License information can be found in the LICENSE file. +// Based on work by Yann Collet, released under BSD License. + +package zstd + +import ( + "encoding/binary" + "encoding/hex" + "errors" + "io" + + "github.com/klauspost/compress/zstd/internal/xxhash" +) + +type frameDec struct { + o decoderOptions + crc *xxhash.Digest + + WindowSize uint64 + + // Frame history passed between blocks + history history + + rawInput byteBuffer + + // Byte buffer that can be reused for small input blocks. + bBuf byteBuf + + FrameContentSize uint64 + + DictionaryID uint32 + HasCheckSum bool + SingleSegment bool +} + +const ( + // MinWindowSize is the minimum Window Size, which is 1 KB. + MinWindowSize = 1 << 10 + + // MaxWindowSize is the maximum encoder window size + // and the default decoder maximum window size. + MaxWindowSize = 1 << 29 +) + +const ( + frameMagic = "\x28\xb5\x2f\xfd" + skippableFrameMagic = "\x2a\x4d\x18" +) + +func newFrameDec(o decoderOptions) *frameDec { + if o.maxWindowSize > o.maxDecodedSize { + o.maxWindowSize = o.maxDecodedSize + } + d := frameDec{ + o: o, + } + return &d +} + +// reset will read the frame header and prepare for block decoding. +// If nothing can be read from the input, io.EOF will be returned. +// Any other error indicated that the stream contained data, but +// there was a problem. +func (d *frameDec) reset(br byteBuffer) error { + d.HasCheckSum = false + d.WindowSize = 0 + var signature [4]byte + for { + var err error + // Check if we can read more... + b, err := br.readSmall(1) + switch err { + case io.EOF, io.ErrUnexpectedEOF: + return io.EOF + case nil: + signature[0] = b[0] + default: + return err + } + // Read the rest, don't allow io.ErrUnexpectedEOF + b, err = br.readSmall(3) + switch err { + case io.EOF: + return io.EOF + case nil: + copy(signature[1:], b) + default: + return err + } + + if string(signature[1:4]) != skippableFrameMagic || signature[0]&0xf0 != 0x50 { + if debugDecoder { + println("Not skippable", hex.EncodeToString(signature[:]), hex.EncodeToString([]byte(skippableFrameMagic))) + } + // Break if not skippable frame. + break + } + // Read size to skip + b, err = br.readSmall(4) + if err != nil { + if debugDecoder { + println("Reading Frame Size", err) + } + return err + } + n := uint32(b[0]) | (uint32(b[1]) << 8) | (uint32(b[2]) << 16) | (uint32(b[3]) << 24) + println("Skipping frame with", n, "bytes.") + err = br.skipN(int64(n)) + if err != nil { + if debugDecoder { + println("Reading discarded frame", err) + } + return err + } + } + if string(signature[:]) != frameMagic { + if debugDecoder { + println("Got magic numbers: ", signature, "want:", []byte(frameMagic)) + } + return ErrMagicMismatch + } + + // Read Frame_Header_Descriptor + fhd, err := br.readByte() + if err != nil { + if debugDecoder { + println("Reading Frame_Header_Descriptor", err) + } + return err + } + d.SingleSegment = fhd&(1<<5) != 0 + + if fhd&(1<<3) != 0 { + return errors.New("reserved bit set on frame header") + } + + // Read Window_Descriptor + // https://github.com/facebook/zstd/blob/dev/doc/zstd_compression_format.md#window_descriptor + d.WindowSize = 0 + if !d.SingleSegment { + wd, err := br.readByte() + if err != nil { + if debugDecoder { + println("Reading Window_Descriptor", err) + } + return err + } + if debugDecoder { + printf("raw: %x, mantissa: %d, exponent: %d\n", wd, wd&7, wd>>3) + } + windowLog := 10 + (wd >> 3) + windowBase := uint64(1) << windowLog + windowAdd := (windowBase / 8) * uint64(wd&0x7) + d.WindowSize = windowBase + windowAdd + } + + // Read Dictionary_ID + // https://github.com/facebook/zstd/blob/dev/doc/zstd_compression_format.md#dictionary_id + d.DictionaryID = 0 + if size := fhd & 3; size != 0 { + if size == 3 { + size = 4 + } + + b, err := br.readSmall(int(size)) + if err != nil { + println("Reading Dictionary_ID", err) + return err + } + var id uint32 + switch len(b) { + case 1: + id = uint32(b[0]) + case 2: + id = uint32(b[0]) | (uint32(b[1]) << 8) + case 4: + id = uint32(b[0]) | (uint32(b[1]) << 8) | (uint32(b[2]) << 16) | (uint32(b[3]) << 24) + } + if debugDecoder { + println("Dict size", size, "ID:", id) + } + d.DictionaryID = id + } + + // Read Frame_Content_Size + // https://github.com/facebook/zstd/blob/dev/doc/zstd_compression_format.md#frame_content_size + var fcsSize int + v := fhd >> 6 + switch v { + case 0: + if d.SingleSegment { + fcsSize = 1 + } + default: + fcsSize = 1 << v + } + d.FrameContentSize = fcsUnknown + if fcsSize > 0 { + b, err := br.readSmall(fcsSize) + if err != nil { + println("Reading Frame content", err) + return err + } + switch len(b) { + case 1: + d.FrameContentSize = uint64(b[0]) + case 2: + // When FCS_Field_Size is 2, the offset of 256 is added. + d.FrameContentSize = uint64(b[0]) | (uint64(b[1]) << 8) + 256 + case 4: + d.FrameContentSize = uint64(b[0]) | (uint64(b[1]) << 8) | (uint64(b[2]) << 16) | (uint64(b[3]) << 24) + case 8: + d1 := uint32(b[0]) | (uint32(b[1]) << 8) | (uint32(b[2]) << 16) | (uint32(b[3]) << 24) + d2 := uint32(b[4]) | (uint32(b[5]) << 8) | (uint32(b[6]) << 16) | (uint32(b[7]) << 24) + d.FrameContentSize = uint64(d1) | (uint64(d2) << 32) + } + if debugDecoder { + println("Read FCS:", d.FrameContentSize) + } + } + + // Move this to shared. + d.HasCheckSum = fhd&(1<<2) != 0 + if d.HasCheckSum { + if d.crc == nil { + d.crc = xxhash.New() + } + d.crc.Reset() + } + + if d.WindowSize > d.o.maxWindowSize { + if debugDecoder { + printf("window size %d > max %d\n", d.WindowSize, d.o.maxWindowSize) + } + return ErrWindowSizeExceeded + } + + if d.WindowSize == 0 && d.SingleSegment { + // We may not need window in this case. + d.WindowSize = max(d.FrameContentSize, MinWindowSize) + if d.WindowSize > d.o.maxDecodedSize { + if debugDecoder { + printf("window size %d > max %d\n", d.WindowSize, d.o.maxWindowSize) + } + return ErrDecoderSizeExceeded + } + } + + // The minimum Window_Size is 1 KB. + if d.WindowSize < MinWindowSize { + if debugDecoder { + println("got window size: ", d.WindowSize) + } + return ErrWindowSizeTooSmall + } + d.history.windowSize = int(d.WindowSize) + if !d.o.lowMem || d.history.windowSize < maxBlockSize { + // Alloc 2x window size if not low-mem, or window size below 2MB. + d.history.allocFrameBuffer = d.history.windowSize * 2 + } else { + if d.o.lowMem { + // Alloc with 1MB extra. + d.history.allocFrameBuffer = d.history.windowSize + maxBlockSize/2 + } else { + // Alloc with 2MB extra. + d.history.allocFrameBuffer = d.history.windowSize + maxBlockSize + } + } + + if debugDecoder { + println("Frame: Dict:", d.DictionaryID, "FrameContentSize:", d.FrameContentSize, "singleseg:", d.SingleSegment, "window:", d.WindowSize, "crc:", d.HasCheckSum) + } + + // history contains input - maybe we do something + d.rawInput = br + return nil +} + +// next will start decoding the next block from stream. +func (d *frameDec) next(block *blockDec) error { + if debugDecoder { + println("decoding new block") + } + err := block.reset(d.rawInput, d.WindowSize) + if err != nil { + println("block error:", err) + // Signal the frame decoder we have a problem. + block.sendErr(err) + return err + } + return nil +} + +// checkCRC will check the checksum, assuming the frame has one. +// Will return ErrCRCMismatch if crc check failed, otherwise nil. +func (d *frameDec) checkCRC() error { + // We can overwrite upper tmp now + buf, err := d.rawInput.readSmall(4) + if err != nil { + println("CRC missing?", err) + return err + } + + want := binary.LittleEndian.Uint32(buf[:4]) + got := uint32(d.crc.Sum64()) + + if got != want { + if debugDecoder { + printf("CRC check failed: got %08x, want %08x\n", got, want) + } + return ErrCRCMismatch + } + if debugDecoder { + printf("CRC ok %08x\n", got) + } + return nil +} + +// consumeCRC skips over the checksum, assuming the frame has one. +func (d *frameDec) consumeCRC() error { + _, err := d.rawInput.readSmall(4) + if err != nil { + println("CRC missing?", err) + } + return err +} + +// runDecoder will run the decoder for the remainder of the frame. +func (d *frameDec) runDecoder(dst []byte, dec *blockDec) ([]byte, error) { + saved := d.history.b + + // We use the history for output to avoid copying it. + d.history.b = dst + d.history.ignoreBuffer = len(dst) + // Store input length, so we only check new data. + crcStart := len(dst) + d.history.decoders.maxSyncLen = 0 + if d.o.limitToCap { + d.history.decoders.maxSyncLen = uint64(cap(dst) - len(dst)) + } + if d.FrameContentSize != fcsUnknown { + if !d.o.limitToCap || d.FrameContentSize+uint64(len(dst)) < d.history.decoders.maxSyncLen { + d.history.decoders.maxSyncLen = d.FrameContentSize + uint64(len(dst)) + } + if d.history.decoders.maxSyncLen > d.o.maxDecodedSize { + if debugDecoder { + println("maxSyncLen:", d.history.decoders.maxSyncLen, "> maxDecodedSize:", d.o.maxDecodedSize) + } + return dst, ErrDecoderSizeExceeded + } + if debugDecoder { + println("maxSyncLen:", d.history.decoders.maxSyncLen) + } + if !d.o.limitToCap && uint64(cap(dst)) < d.history.decoders.maxSyncLen { + // Alloc for output + dst2 := make([]byte, len(dst), d.history.decoders.maxSyncLen+compressedBlockOverAlloc) + copy(dst2, dst) + dst = dst2 + } + } + var err error + for { + err = dec.reset(d.rawInput, d.WindowSize) + if err != nil { + break + } + if debugDecoder { + println("next block:", dec) + } + err = dec.decodeBuf(&d.history) + if err != nil { + break + } + if uint64(len(d.history.b)-crcStart) > d.o.maxDecodedSize { + println("runDecoder: maxDecodedSize exceeded", uint64(len(d.history.b)-crcStart), ">", d.o.maxDecodedSize) + err = ErrDecoderSizeExceeded + break + } + if d.o.limitToCap && len(d.history.b) > cap(dst) { + println("runDecoder: cap exceeded", uint64(len(d.history.b)), ">", cap(dst)) + err = ErrDecoderSizeExceeded + break + } + if uint64(len(d.history.b)-crcStart) > d.FrameContentSize { + println("runDecoder: FrameContentSize exceeded", uint64(len(d.history.b)-crcStart), ">", d.FrameContentSize) + err = ErrFrameSizeExceeded + break + } + if dec.Last { + break + } + if debugDecoder { + println("runDecoder: FrameContentSize", uint64(len(d.history.b)-crcStart), "<=", d.FrameContentSize) + } + } + dst = d.history.b + if err == nil { + if d.FrameContentSize != fcsUnknown && uint64(len(d.history.b)-crcStart) != d.FrameContentSize { + err = ErrFrameSizeMismatch + } else if d.HasCheckSum { + if d.o.ignoreChecksum { + err = d.consumeCRC() + } else { + d.crc.Write(dst[crcStart:]) + err = d.checkCRC() + } + } + } + d.history.b = saved + return dst, err +} diff --git a/vendor/github.com/klauspost/compress/zstd/frameenc.go b/vendor/github.com/klauspost/compress/zstd/frameenc.go new file mode 100644 index 000000000..667ca0679 --- /dev/null +++ b/vendor/github.com/klauspost/compress/zstd/frameenc.go @@ -0,0 +1,137 @@ +// Copyright 2019+ Klaus Post. All rights reserved. +// License information can be found in the LICENSE file. +// Based on work by Yann Collet, released under BSD License. + +package zstd + +import ( + "encoding/binary" + "fmt" + "io" + "math" + "math/bits" +) + +type frameHeader struct { + ContentSize uint64 + WindowSize uint32 + SingleSegment bool + Checksum bool + DictID uint32 +} + +const maxHeaderSize = 14 + +func (f frameHeader) appendTo(dst []byte) []byte { + dst = append(dst, frameMagic...) + var fhd uint8 + if f.Checksum { + fhd |= 1 << 2 + } + if f.SingleSegment { + fhd |= 1 << 5 + } + + var dictIDContent []byte + if f.DictID > 0 { + var tmp [4]byte + if f.DictID < 256 { + fhd |= 1 + tmp[0] = uint8(f.DictID) + dictIDContent = tmp[:1] + } else if f.DictID < 1<<16 { + fhd |= 2 + binary.LittleEndian.PutUint16(tmp[:2], uint16(f.DictID)) + dictIDContent = tmp[:2] + } else { + fhd |= 3 + binary.LittleEndian.PutUint32(tmp[:4], f.DictID) + dictIDContent = tmp[:4] + } + } + var fcs uint8 + if f.ContentSize >= 256 { + fcs++ + } + if f.ContentSize >= 65536+256 { + fcs++ + } + if f.ContentSize >= 0xffffffff { + fcs++ + } + + fhd |= fcs << 6 + + dst = append(dst, fhd) + if !f.SingleSegment { + const winLogMin = 10 + windowLog := (bits.Len32(f.WindowSize-1) - winLogMin) << 3 + dst = append(dst, uint8(windowLog)) + } + if f.DictID > 0 { + dst = append(dst, dictIDContent...) + } + switch fcs { + case 0: + if f.SingleSegment { + dst = append(dst, uint8(f.ContentSize)) + } + // Unless SingleSegment is set, framessizes < 256 are not stored. + case 1: + f.ContentSize -= 256 + dst = append(dst, uint8(f.ContentSize), uint8(f.ContentSize>>8)) + case 2: + dst = append(dst, uint8(f.ContentSize), uint8(f.ContentSize>>8), uint8(f.ContentSize>>16), uint8(f.ContentSize>>24)) + case 3: + dst = append(dst, uint8(f.ContentSize), uint8(f.ContentSize>>8), uint8(f.ContentSize>>16), uint8(f.ContentSize>>24), + uint8(f.ContentSize>>32), uint8(f.ContentSize>>40), uint8(f.ContentSize>>48), uint8(f.ContentSize>>56)) + default: + panic("invalid fcs") + } + return dst +} + +const skippableFrameHeader = 4 + 4 + +// calcSkippableFrame will return a total size to be added for written +// to be divisible by multiple. +// The value will always be > skippableFrameHeader. +// The function will panic if written < 0 or wantMultiple <= 0. +func calcSkippableFrame(written, wantMultiple int64) int { + if wantMultiple <= 0 { + panic("wantMultiple <= 0") + } + if written < 0 { + panic("written < 0") + } + leftOver := written % wantMultiple + if leftOver == 0 { + return 0 + } + toAdd := wantMultiple - leftOver + for toAdd < skippableFrameHeader { + toAdd += wantMultiple + } + return int(toAdd) +} + +// skippableFrame will add a skippable frame with a total size of bytes. +// total should be >= skippableFrameHeader and < math.MaxUint32. +func skippableFrame(dst []byte, total int, r io.Reader) ([]byte, error) { + if total == 0 { + return dst, nil + } + if total < skippableFrameHeader { + return dst, fmt.Errorf("requested skippable frame (%d) < 8", total) + } + if int64(total) > math.MaxUint32 { + return dst, fmt.Errorf("requested skippable frame (%d) > max uint32", total) + } + dst = append(dst, 0x50, 0x2a, 0x4d, 0x18) + f := uint32(total - skippableFrameHeader) + dst = append(dst, uint8(f), uint8(f>>8), uint8(f>>16), uint8(f>>24)) + start := len(dst) + dst = append(dst, make([]byte, f)...) + _, err := io.ReadFull(r, dst[start:]) + return dst, err +} diff --git a/vendor/github.com/klauspost/compress/zstd/fse_decoder.go b/vendor/github.com/klauspost/compress/zstd/fse_decoder.go new file mode 100644 index 000000000..2f8860a72 --- /dev/null +++ b/vendor/github.com/klauspost/compress/zstd/fse_decoder.go @@ -0,0 +1,307 @@ +// Copyright 2019+ Klaus Post. All rights reserved. +// License information can be found in the LICENSE file. +// Based on work by Yann Collet, released under BSD License. + +package zstd + +import ( + "encoding/binary" + "errors" + "fmt" + "io" +) + +const ( + tablelogAbsoluteMax = 9 +) + +const ( + /*!MEMORY_USAGE : + * Memory usage formula : N->2^N Bytes (examples : 10 -> 1KB; 12 -> 4KB ; 16 -> 64KB; 20 -> 1MB; etc.) + * Increasing memory usage improves compression ratio + * Reduced memory usage can improve speed, due to cache effect + * Recommended max value is 14, for 16KB, which nicely fits into Intel x86 L1 cache */ + maxMemoryUsage = tablelogAbsoluteMax + 2 + + maxTableLog = maxMemoryUsage - 2 + maxTablesize = 1 << maxTableLog + maxTableMask = (1 << maxTableLog) - 1 + minTablelog = 5 + maxSymbolValue = 255 +) + +// fseDecoder provides temporary storage for compression and decompression. +type fseDecoder struct { + dt [maxTablesize]decSymbol // Decompression table. + symbolLen uint16 // Length of active part of the symbol table. + actualTableLog uint8 // Selected tablelog. + maxBits uint8 // Maximum number of additional bits + + // used for table creation to avoid allocations. + stateTable [256]uint16 + norm [maxSymbolValue + 1]int16 + preDefined bool +} + +// tableStep returns the next table index. +func tableStep(tableSize uint32) uint32 { + return (tableSize >> 1) + (tableSize >> 3) + 3 +} + +// readNCount will read the symbol distribution so decoding tables can be constructed. +func (s *fseDecoder) readNCount(b *byteReader, maxSymbol uint16) error { + var ( + charnum uint16 + previous0 bool + ) + if b.remain() < 4 { + return errors.New("input too small") + } + bitStream := b.Uint32NC() + nbBits := uint((bitStream & 0xF) + minTablelog) // extract tableLog + if nbBits > tablelogAbsoluteMax { + println("Invalid tablelog:", nbBits) + return errors.New("tableLog too large") + } + bitStream >>= 4 + bitCount := uint(4) + + s.actualTableLog = uint8(nbBits) + remaining := int32((1 << nbBits) + 1) + threshold := int32(1 << nbBits) + gotTotal := int32(0) + nbBits++ + + for remaining > 1 && charnum <= maxSymbol { + if previous0 { + //println("prev0") + n0 := charnum + for (bitStream & 0xFFFF) == 0xFFFF { + //println("24 x 0") + n0 += 24 + if r := b.remain(); r > 5 { + b.advance(2) + // The check above should make sure we can read 32 bits + bitStream = b.Uint32NC() >> bitCount + } else { + // end of bit stream + bitStream >>= 16 + bitCount += 16 + } + } + //printf("bitstream: %d, 0b%b", bitStream&3, bitStream) + for (bitStream & 3) == 3 { + n0 += 3 + bitStream >>= 2 + bitCount += 2 + } + n0 += uint16(bitStream & 3) + bitCount += 2 + + if n0 > maxSymbolValue { + return errors.New("maxSymbolValue too small") + } + //println("inserting ", n0-charnum, "zeroes from idx", charnum, "ending before", n0) + for charnum < n0 { + s.norm[uint8(charnum)] = 0 + charnum++ + } + + if r := b.remain(); r >= 7 || r-int(bitCount>>3) >= 4 { + b.advance(bitCount >> 3) + bitCount &= 7 + // The check above should make sure we can read 32 bits + bitStream = b.Uint32NC() >> bitCount + } else { + bitStream >>= 2 + } + } + + max := (2*threshold - 1) - remaining + var count int32 + + if int32(bitStream)&(threshold-1) < max { + count = int32(bitStream) & (threshold - 1) + if debugAsserts && nbBits < 1 { + panic("nbBits underflow") + } + bitCount += nbBits - 1 + } else { + count = int32(bitStream) & (2*threshold - 1) + if count >= threshold { + count -= max + } + bitCount += nbBits + } + + // extra accuracy + count-- + if count < 0 { + // -1 means +1 + remaining += count + gotTotal -= count + } else { + remaining -= count + gotTotal += count + } + s.norm[charnum&0xff] = int16(count) + charnum++ + previous0 = count == 0 + for remaining < threshold { + nbBits-- + threshold >>= 1 + } + + if r := b.remain(); r >= 7 || r-int(bitCount>>3) >= 4 { + b.advance(bitCount >> 3) + bitCount &= 7 + // The check above should make sure we can read 32 bits + bitStream = b.Uint32NC() >> (bitCount & 31) + } else { + bitCount -= (uint)(8 * (len(b.b) - 4 - b.off)) + b.off = len(b.b) - 4 + bitStream = b.Uint32() >> (bitCount & 31) + } + } + s.symbolLen = charnum + if s.symbolLen <= 1 { + return fmt.Errorf("symbolLen (%d) too small", s.symbolLen) + } + if s.symbolLen > maxSymbolValue+1 { + return fmt.Errorf("symbolLen (%d) too big", s.symbolLen) + } + if remaining != 1 { + return fmt.Errorf("corruption detected (remaining %d != 1)", remaining) + } + if bitCount > 32 { + return fmt.Errorf("corruption detected (bitCount %d > 32)", bitCount) + } + if gotTotal != 1<> 3) + return s.buildDtable() +} + +func (s *fseDecoder) mustReadFrom(r io.Reader) { + fatalErr := func(err error) { + if err != nil { + panic(err) + } + } + // dt [maxTablesize]decSymbol // Decompression table. + // symbolLen uint16 // Length of active part of the symbol table. + // actualTableLog uint8 // Selected tablelog. + // maxBits uint8 // Maximum number of additional bits + // // used for table creation to avoid allocations. + // stateTable [256]uint16 + // norm [maxSymbolValue + 1]int16 + // preDefined bool + fatalErr(binary.Read(r, binary.LittleEndian, &s.dt)) + fatalErr(binary.Read(r, binary.LittleEndian, &s.symbolLen)) + fatalErr(binary.Read(r, binary.LittleEndian, &s.actualTableLog)) + fatalErr(binary.Read(r, binary.LittleEndian, &s.maxBits)) + fatalErr(binary.Read(r, binary.LittleEndian, &s.stateTable)) + fatalErr(binary.Read(r, binary.LittleEndian, &s.norm)) + fatalErr(binary.Read(r, binary.LittleEndian, &s.preDefined)) +} + +// decSymbol contains information about a state entry, +// Including the state offset base, the output symbol and +// the number of bits to read for the low part of the destination state. +// Using a composite uint64 is faster than a struct with separate members. +type decSymbol uint64 + +func newDecSymbol(nbits, addBits uint8, newState uint16, baseline uint32) decSymbol { + return decSymbol(nbits) | (decSymbol(addBits) << 8) | (decSymbol(newState) << 16) | (decSymbol(baseline) << 32) +} + +func (d decSymbol) nbBits() uint8 { + return uint8(d) +} + +func (d decSymbol) addBits() uint8 { + return uint8(d >> 8) +} + +func (d decSymbol) newState() uint16 { + return uint16(d >> 16) +} + +func (d decSymbol) baselineInt() int { + return int(d >> 32) +} + +func (d *decSymbol) setNBits(nBits uint8) { + const mask = 0xffffffffffffff00 + *d = (*d & mask) | decSymbol(nBits) +} + +func (d *decSymbol) setAddBits(addBits uint8) { + const mask = 0xffffffffffff00ff + *d = (*d & mask) | (decSymbol(addBits) << 8) +} + +func (d *decSymbol) setNewState(state uint16) { + const mask = 0xffffffff0000ffff + *d = (*d & mask) | decSymbol(state)<<16 +} + +func (d *decSymbol) setExt(addBits uint8, baseline uint32) { + const mask = 0xffff00ff + *d = (*d & mask) | (decSymbol(addBits) << 8) | (decSymbol(baseline) << 32) +} + +// decSymbolValue returns the transformed decSymbol for the given symbol. +func decSymbolValue(symb uint8, t []baseOffset) (decSymbol, error) { + if int(symb) >= len(t) { + return 0, fmt.Errorf("rle symbol %d >= max %d", symb, len(t)) + } + lu := t[symb] + return newDecSymbol(0, lu.addBits, 0, lu.baseLine), nil +} + +// setRLE will set the decoder til RLE mode. +func (s *fseDecoder) setRLE(symbol decSymbol) { + s.actualTableLog = 0 + s.maxBits = symbol.addBits() + s.dt[0] = symbol +} + +// transform will transform the decoder table into a table usable for +// decoding without having to apply the transformation while decoding. +// The state will contain the base value and the number of bits to read. +func (s *fseDecoder) transform(t []baseOffset) error { + tableSize := uint16(1 << s.actualTableLog) + s.maxBits = 0 + for i, v := range s.dt[:tableSize] { + add := v.addBits() + if int(add) >= len(t) { + return fmt.Errorf("invalid decoding table entry %d, symbol %d >= max (%d)", i, v.addBits(), len(t)) + } + lu := t[add] + if lu.addBits > s.maxBits { + s.maxBits = lu.addBits + } + v.setExt(lu.addBits, lu.baseLine) + s.dt[i] = v + } + return nil +} + +type fseState struct { + dt []decSymbol + state decSymbol +} + +// Initialize and decodeAsync first state and symbol. +func (s *fseState) init(br *bitReader, tableLog uint8, dt []decSymbol) { + s.dt = dt + br.fill() + s.state = dt[br.getBits(tableLog)] +} + +// final returns the current state symbol without decoding the next. +func (s decSymbol) final() (int, uint8) { + return s.baselineInt(), s.addBits() +} diff --git a/vendor/github.com/klauspost/compress/zstd/fse_decoder_amd64.go b/vendor/github.com/klauspost/compress/zstd/fse_decoder_amd64.go new file mode 100644 index 000000000..b8c8607b5 --- /dev/null +++ b/vendor/github.com/klauspost/compress/zstd/fse_decoder_amd64.go @@ -0,0 +1,64 @@ +//go:build amd64 && !appengine && !noasm && gc + +package zstd + +import ( + "fmt" +) + +type buildDtableAsmContext struct { + // inputs + stateTable *uint16 + norm *int16 + dt *uint64 + + // outputs --- set by the procedure in the case of error; + // for interpretation please see the error handling part below + errParam1 uint64 + errParam2 uint64 +} + +// buildDtable_asm is an x86 assembly implementation of fseDecoder.buildDtable. +// Function returns non-zero exit code on error. +// +//go:noescape +func buildDtable_asm(s *fseDecoder, ctx *buildDtableAsmContext) int + +// please keep in sync with _generate/gen_fse.go +const ( + errorCorruptedNormalizedCounter = 1 + errorNewStateTooBig = 2 + errorNewStateNoBits = 3 +) + +// buildDtable will build the decoding table. +func (s *fseDecoder) buildDtable() error { + ctx := buildDtableAsmContext{ + stateTable: &s.stateTable[0], + norm: &s.norm[0], + dt: (*uint64)(&s.dt[0]), + } + code := buildDtable_asm(s, &ctx) + + if code != 0 { + switch code { + case errorCorruptedNormalizedCounter: + position := ctx.errParam1 + return fmt.Errorf("corrupted input (position=%d, expected 0)", position) + + case errorNewStateTooBig: + newState := decSymbol(ctx.errParam1) + size := ctx.errParam2 + return fmt.Errorf("newState (%d) outside table size (%d)", newState, size) + + case errorNewStateNoBits: + newState := decSymbol(ctx.errParam1) + oldState := decSymbol(ctx.errParam2) + return fmt.Errorf("newState (%d) == oldState (%d) and no bits", newState, oldState) + + default: + return fmt.Errorf("buildDtable_asm returned unhandled nonzero code = %d", code) + } + } + return nil +} diff --git a/vendor/github.com/klauspost/compress/zstd/fse_decoder_amd64.s b/vendor/github.com/klauspost/compress/zstd/fse_decoder_amd64.s new file mode 100644 index 000000000..bcde39869 --- /dev/null +++ b/vendor/github.com/klauspost/compress/zstd/fse_decoder_amd64.s @@ -0,0 +1,126 @@ +// Code generated by command: go run gen_fse.go -out ../fse_decoder_amd64.s -pkg=zstd. DO NOT EDIT. + +//go:build !appengine && !noasm && gc && !noasm + +// func buildDtable_asm(s *fseDecoder, ctx *buildDtableAsmContext) int +TEXT ·buildDtable_asm(SB), $0-24 + MOVQ ctx+8(FP), CX + MOVQ s+0(FP), DI + + // Load values + MOVBQZX 4098(DI), DX + XORQ AX, AX + BTSQ DX, AX + MOVQ (CX), BX + MOVQ 16(CX), SI + LEAQ -1(AX), R8 + MOVQ 8(CX), CX + MOVWQZX 4096(DI), DI + + // End load values + // Init, lay down lowprob symbols + XORQ R9, R9 + JMP init_main_loop_condition + +init_main_loop: + MOVWQSX (CX)(R9*2), R10 + CMPW R10, $-1 + JNE do_not_update_high_threshold + MOVB R9, 1(SI)(R8*8) + DECQ R8 + MOVQ $0x0000000000000001, R10 + +do_not_update_high_threshold: + MOVW R10, (BX)(R9*2) + INCQ R9 + +init_main_loop_condition: + CMPQ R9, DI + JL init_main_loop + + // Spread symbols + // Calculate table step + MOVQ AX, R9 + SHRQ $0x01, R9 + MOVQ AX, R10 + SHRQ $0x03, R10 + LEAQ 3(R9)(R10*1), R9 + + // Fill add bits values + LEAQ -1(AX), R10 + XORQ R11, R11 + XORQ R12, R12 + JMP spread_main_loop_condition + +spread_main_loop: + XORQ R13, R13 + MOVWQSX (CX)(R12*2), R14 + JMP spread_inner_loop_condition + +spread_inner_loop: + MOVB R12, 1(SI)(R11*8) + +adjust_position: + ADDQ R9, R11 + ANDQ R10, R11 + CMPQ R11, R8 + JG adjust_position + INCQ R13 + +spread_inner_loop_condition: + CMPQ R13, R14 + JL spread_inner_loop + INCQ R12 + +spread_main_loop_condition: + CMPQ R12, DI + JL spread_main_loop + TESTQ R11, R11 + JZ spread_check_ok + MOVQ ctx+8(FP), AX + MOVQ R11, 24(AX) + MOVQ $+1, ret+16(FP) + RET + +spread_check_ok: + // Build Decoding table + XORQ DI, DI + +build_table_main_table: + MOVBQZX 1(SI)(DI*8), CX + MOVWQZX (BX)(CX*2), R8 + LEAQ 1(R8), R9 + MOVW R9, (BX)(CX*2) + MOVQ R8, R9 + BSRQ R9, R9 + MOVQ DX, CX + SUBQ R9, CX + SHLQ CL, R8 + SUBQ AX, R8 + MOVB CL, (SI)(DI*8) + MOVW R8, 2(SI)(DI*8) + CMPQ R8, AX + JLE build_table_check1_ok + MOVQ ctx+8(FP), CX + MOVQ R8, 24(CX) + MOVQ AX, 32(CX) + MOVQ $+2, ret+16(FP) + RET + +build_table_check1_ok: + TESTB CL, CL + JNZ build_table_check2_ok + CMPW R8, DI + JNE build_table_check2_ok + MOVQ ctx+8(FP), AX + MOVQ R8, 24(AX) + MOVQ DI, 32(AX) + MOVQ $+3, ret+16(FP) + RET + +build_table_check2_ok: + INCQ DI + CMPQ DI, AX + JL build_table_main_table + MOVQ $+0, ret+16(FP) + RET diff --git a/vendor/github.com/klauspost/compress/zstd/fse_decoder_generic.go b/vendor/github.com/klauspost/compress/zstd/fse_decoder_generic.go new file mode 100644 index 000000000..2138f8091 --- /dev/null +++ b/vendor/github.com/klauspost/compress/zstd/fse_decoder_generic.go @@ -0,0 +1,72 @@ +//go:build !amd64 || appengine || !gc || noasm + +package zstd + +import ( + "errors" + "fmt" +) + +// buildDtable will build the decoding table. +func (s *fseDecoder) buildDtable() error { + tableSize := uint32(1 << s.actualTableLog) + highThreshold := tableSize - 1 + symbolNext := s.stateTable[:256] + + // Init, lay down lowprob symbols + { + for i, v := range s.norm[:s.symbolLen] { + if v == -1 { + s.dt[highThreshold].setAddBits(uint8(i)) + highThreshold-- + v = 1 + } + symbolNext[i] = uint16(v) + } + } + + // Spread symbols + { + tableMask := tableSize - 1 + step := tableStep(tableSize) + position := uint32(0) + for ss, v := range s.norm[:s.symbolLen] { + for i := 0; i < int(v); i++ { + s.dt[position].setAddBits(uint8(ss)) + for { + // lowprob area + position = (position + step) & tableMask + if position <= highThreshold { + break + } + } + } + } + if position != 0 { + // position must reach all cells once, otherwise normalizedCounter is incorrect + return errors.New("corrupted input (position != 0)") + } + } + + // Build Decoding table + { + tableSize := uint16(1 << s.actualTableLog) + for u, v := range s.dt[:tableSize] { + symbol := v.addBits() + nextState := symbolNext[symbol] + symbolNext[symbol] = nextState + 1 + nBits := s.actualTableLog - byte(highBits(uint32(nextState))) + s.dt[u&maxTableMask].setNBits(nBits) + newState := (nextState << nBits) - tableSize + if newState > tableSize { + return fmt.Errorf("newState (%d) outside table size (%d)", newState, tableSize) + } + if newState == uint16(u) && nBits == 0 { + // Seems weird that this is possible with nbits > 0. + return fmt.Errorf("newState (%d) == oldState (%d) and no bits", newState, u) + } + s.dt[u&maxTableMask].setNewState(newState) + } + } + return nil +} diff --git a/vendor/github.com/klauspost/compress/zstd/fse_encoder.go b/vendor/github.com/klauspost/compress/zstd/fse_encoder.go new file mode 100644 index 000000000..3a0f4e7fb --- /dev/null +++ b/vendor/github.com/klauspost/compress/zstd/fse_encoder.go @@ -0,0 +1,701 @@ +// Copyright 2019+ Klaus Post. All rights reserved. +// License information can be found in the LICENSE file. +// Based on work by Yann Collet, released under BSD License. + +package zstd + +import ( + "errors" + "fmt" + "math" +) + +const ( + // For encoding we only support up to + maxEncTableLog = 8 + maxEncTablesize = 1 << maxTableLog + maxEncTableMask = (1 << maxTableLog) - 1 + minEncTablelog = 5 + maxEncSymbolValue = maxMatchLengthSymbol +) + +// Scratch provides temporary storage for compression and decompression. +type fseEncoder struct { + symbolLen uint16 // Length of active part of the symbol table. + actualTableLog uint8 // Selected tablelog. + ct cTable // Compression tables. + maxCount int // count of the most probable symbol + zeroBits bool // no bits has prob > 50%. + clearCount bool // clear count + useRLE bool // This encoder is for RLE + preDefined bool // This encoder is predefined. + reUsed bool // Set to know when the encoder has been reused. + rleVal uint8 // RLE Symbol + maxBits uint8 // Maximum output bits after transform. + + // TODO: Technically zstd should be fine with 64 bytes. + count [256]uint32 + norm [256]int16 +} + +// cTable contains tables used for compression. +type cTable struct { + tableSymbol []byte + stateTable []uint16 + symbolTT []symbolTransform +} + +// symbolTransform contains the state transform for a symbol. +type symbolTransform struct { + deltaNbBits uint32 + deltaFindState int16 + outBits uint8 +} + +// String prints values as a human readable string. +func (s symbolTransform) String() string { + return fmt.Sprintf("{deltabits: %08x, findstate:%d outbits:%d}", s.deltaNbBits, s.deltaFindState, s.outBits) +} + +// Histogram allows to populate the histogram and skip that step in the compression, +// It otherwise allows to inspect the histogram when compression is done. +// To indicate that you have populated the histogram call HistogramFinished +// with the value of the highest populated symbol, as well as the number of entries +// in the most populated entry. These are accepted at face value. +func (s *fseEncoder) Histogram() *[256]uint32 { + return &s.count +} + +// HistogramFinished can be called to indicate that the histogram has been populated. +// maxSymbol is the index of the highest set symbol of the next data segment. +// maxCount is the number of entries in the most populated entry. +// These are accepted at face value. +func (s *fseEncoder) HistogramFinished(maxSymbol uint8, maxCount int) { + s.maxCount = maxCount + s.symbolLen = uint16(maxSymbol) + 1 + s.clearCount = maxCount != 0 +} + +// allocCtable will allocate tables needed for compression. +// If existing tables a re big enough, they are simply re-used. +func (s *fseEncoder) allocCtable() { + tableSize := 1 << s.actualTableLog + // get tableSymbol that is big enough. + if cap(s.ct.tableSymbol) < tableSize { + s.ct.tableSymbol = make([]byte, tableSize) + } + s.ct.tableSymbol = s.ct.tableSymbol[:tableSize] + + ctSize := tableSize + if cap(s.ct.stateTable) < ctSize { + s.ct.stateTable = make([]uint16, ctSize) + } + s.ct.stateTable = s.ct.stateTable[:ctSize] + + if cap(s.ct.symbolTT) < 256 { + s.ct.symbolTT = make([]symbolTransform, 256) + } + s.ct.symbolTT = s.ct.symbolTT[:256] +} + +// buildCTable will populate the compression table so it is ready to be used. +func (s *fseEncoder) buildCTable() error { + tableSize := uint32(1 << s.actualTableLog) + highThreshold := tableSize - 1 + var cumul [256]int16 + + s.allocCtable() + tableSymbol := s.ct.tableSymbol[:tableSize] + // symbol start positions + { + cumul[0] = 0 + for ui, v := range s.norm[:s.symbolLen-1] { + u := byte(ui) // one less than reference + if v == -1 { + // Low proba symbol + cumul[u+1] = cumul[u] + 1 + tableSymbol[highThreshold] = u + highThreshold-- + } else { + cumul[u+1] = cumul[u] + v + } + } + // Encode last symbol separately to avoid overflowing u + u := int(s.symbolLen - 1) + v := s.norm[s.symbolLen-1] + if v == -1 { + // Low proba symbol + cumul[u+1] = cumul[u] + 1 + tableSymbol[highThreshold] = byte(u) + highThreshold-- + } else { + cumul[u+1] = cumul[u] + v + } + if uint32(cumul[s.symbolLen]) != tableSize { + return fmt.Errorf("internal error: expected cumul[s.symbolLen] (%d) == tableSize (%d)", cumul[s.symbolLen], tableSize) + } + cumul[s.symbolLen] = int16(tableSize) + 1 + } + // Spread symbols + s.zeroBits = false + { + step := tableStep(tableSize) + tableMask := tableSize - 1 + var position uint32 + // if any symbol > largeLimit, we may have 0 bits output. + largeLimit := int16(1 << (s.actualTableLog - 1)) + for ui, v := range s.norm[:s.symbolLen] { + symbol := byte(ui) + if v > largeLimit { + s.zeroBits = true + } + for range v { + tableSymbol[position] = symbol + position = (position + step) & tableMask + for position > highThreshold { + position = (position + step) & tableMask + } /* Low proba area */ + } + } + + // Check if we have gone through all positions + if position != 0 { + return errors.New("position!=0") + } + } + + // Build table + table := s.ct.stateTable + { + tsi := int(tableSize) + for u, v := range tableSymbol { + // TableU16 : sorted by symbol order; gives next state value + table[cumul[v]] = uint16(tsi + u) + cumul[v]++ + } + } + + // Build Symbol Transformation Table + { + total := int16(0) + symbolTT := s.ct.symbolTT[:s.symbolLen] + tableLog := s.actualTableLog + tl := (uint32(tableLog) << 16) - (1 << tableLog) + for i, v := range s.norm[:s.symbolLen] { + switch v { + case 0: + case -1, 1: + symbolTT[i].deltaNbBits = tl + symbolTT[i].deltaFindState = total - 1 + total++ + default: + maxBitsOut := uint32(tableLog) - highBit(uint32(v-1)) + minStatePlus := uint32(v) << maxBitsOut + symbolTT[i].deltaNbBits = (maxBitsOut << 16) - minStatePlus + symbolTT[i].deltaFindState = total - v + total += v + } + } + if total != int16(tableSize) { + return fmt.Errorf("total mismatch %d (got) != %d (want)", total, tableSize) + } + } + return nil +} + +var rtbTable = [...]uint32{0, 473195, 504333, 520860, 550000, 700000, 750000, 830000} + +func (s *fseEncoder) setRLE(val byte) { + s.allocCtable() + s.actualTableLog = 0 + s.ct.stateTable = s.ct.stateTable[:1] + s.ct.symbolTT[val] = symbolTransform{ + deltaFindState: 0, + deltaNbBits: 0, + } + if debugEncoder { + println("setRLE: val", val, "symbolTT", s.ct.symbolTT[val]) + } + s.rleVal = val + s.useRLE = true +} + +// setBits will set output bits for the transform. +// if nil is provided, the number of bits is equal to the index. +func (s *fseEncoder) setBits(transform []byte) { + if s.reUsed || s.preDefined { + return + } + if s.useRLE { + if transform == nil { + s.ct.symbolTT[s.rleVal].outBits = s.rleVal + s.maxBits = s.rleVal + return + } + s.maxBits = transform[s.rleVal] + s.ct.symbolTT[s.rleVal].outBits = s.maxBits + return + } + if transform == nil { + for i := range s.ct.symbolTT[:s.symbolLen] { + s.ct.symbolTT[i].outBits = uint8(i) + } + s.maxBits = uint8(s.symbolLen - 1) + return + } + s.maxBits = 0 + for i, v := range transform[:s.symbolLen] { + s.ct.symbolTT[i].outBits = v + if v > s.maxBits { + // We could assume bits always going up, but we play safe. + s.maxBits = v + } + } +} + +// normalizeCount will normalize the count of the symbols so +// the total is equal to the table size. +// If successful, compression tables will also be made ready. +func (s *fseEncoder) normalizeCount(length int) error { + if s.reUsed { + return nil + } + s.optimalTableLog(length) + var ( + tableLog = s.actualTableLog + scale = 62 - uint64(tableLog) + step = (1 << 62) / uint64(length) + vStep = uint64(1) << (scale - 20) + stillToDistribute = int16(1 << tableLog) + largest int + largestP int16 + lowThreshold = (uint32)(length >> tableLog) + ) + if s.maxCount == length { + s.useRLE = true + return nil + } + s.useRLE = false + for i, cnt := range s.count[:s.symbolLen] { + // already handled + // if (count[s] == s.length) return 0; /* rle special case */ + + if cnt == 0 { + s.norm[i] = 0 + continue + } + if cnt <= lowThreshold { + s.norm[i] = -1 + stillToDistribute-- + } else { + proba := (int16)((uint64(cnt) * step) >> scale) + if proba < 8 { + restToBeat := vStep * uint64(rtbTable[proba]) + v := uint64(cnt)*step - (uint64(proba) << scale) + if v > restToBeat { + proba++ + } + } + if proba > largestP { + largestP = proba + largest = i + } + s.norm[i] = proba + stillToDistribute -= proba + } + } + + if -stillToDistribute >= (s.norm[largest] >> 1) { + // corner case, need another normalization method + err := s.normalizeCount2(length) + if err != nil { + return err + } + if debugAsserts { + err = s.validateNorm() + if err != nil { + return err + } + } + return s.buildCTable() + } + s.norm[largest] += stillToDistribute + if debugAsserts { + err := s.validateNorm() + if err != nil { + return err + } + } + return s.buildCTable() +} + +// Secondary normalization method. +// To be used when primary method fails. +func (s *fseEncoder) normalizeCount2(length int) error { + const notYetAssigned = -2 + var ( + distributed uint32 + total = uint32(length) + tableLog = s.actualTableLog + lowThreshold = total >> tableLog + lowOne = (total * 3) >> (tableLog + 1) + ) + for i, cnt := range s.count[:s.symbolLen] { + if cnt == 0 { + s.norm[i] = 0 + continue + } + if cnt <= lowThreshold { + s.norm[i] = -1 + distributed++ + total -= cnt + continue + } + if cnt <= lowOne { + s.norm[i] = 1 + distributed++ + total -= cnt + continue + } + s.norm[i] = notYetAssigned + } + toDistribute := (1 << tableLog) - distributed + + if (total / toDistribute) > lowOne { + // risk of rounding to zero + lowOne = (total * 3) / (toDistribute * 2) + for i, cnt := range s.count[:s.symbolLen] { + if (s.norm[i] == notYetAssigned) && (cnt <= lowOne) { + s.norm[i] = 1 + distributed++ + total -= cnt + continue + } + } + toDistribute = (1 << tableLog) - distributed + } + if distributed == uint32(s.symbolLen)+1 { + // all values are pretty poor; + // probably incompressible data (should have already been detected); + // find max, then give all remaining points to max + var maxV int + var maxC uint32 + for i, cnt := range s.count[:s.symbolLen] { + if cnt > maxC { + maxV = i + maxC = cnt + } + } + s.norm[maxV] += int16(toDistribute) + return nil + } + + if total == 0 { + // all of the symbols were low enough for the lowOne or lowThreshold + for i := uint32(0); toDistribute > 0; i = (i + 1) % (uint32(s.symbolLen)) { + if s.norm[i] > 0 { + toDistribute-- + s.norm[i]++ + } + } + return nil + } + + var ( + vStepLog = 62 - uint64(tableLog) + mid = uint64((1 << (vStepLog - 1)) - 1) + rStep = (((1 << vStepLog) * uint64(toDistribute)) + mid) / uint64(total) // scale on remaining + tmpTotal = mid + ) + for i, cnt := range s.count[:s.symbolLen] { + if s.norm[i] == notYetAssigned { + var ( + end = tmpTotal + uint64(cnt)*rStep + sStart = uint32(tmpTotal >> vStepLog) + sEnd = uint32(end >> vStepLog) + weight = sEnd - sStart + ) + if weight < 1 { + return errors.New("weight < 1") + } + s.norm[i] = int16(weight) + tmpTotal = end + } + } + return nil +} + +// optimalTableLog calculates and sets the optimal tableLog in s.actualTableLog +func (s *fseEncoder) optimalTableLog(length int) { + tableLog := uint8(maxEncTableLog) + minBitsSrc := highBit(uint32(length)) + 1 + minBitsSymbols := highBit(uint32(s.symbolLen-1)) + 2 + minBits := uint8(minBitsSymbols) + if minBitsSrc < minBitsSymbols { + minBits = uint8(minBitsSrc) + } + + maxBitsSrc := uint8(highBit(uint32(length-1))) - 2 + if maxBitsSrc < tableLog { + // Accuracy can be reduced + tableLog = maxBitsSrc + } + if minBits > tableLog { + tableLog = minBits + } + // Need a minimum to safely represent all symbol values + if tableLog < minEncTablelog { + tableLog = minEncTablelog + } + if tableLog > maxEncTableLog { + tableLog = maxEncTableLog + } + s.actualTableLog = tableLog +} + +// validateNorm validates the normalized histogram table. +func (s *fseEncoder) validateNorm() (err error) { + var total int + for _, v := range s.norm[:s.symbolLen] { + if v >= 0 { + total += int(v) + } else { + total -= int(v) + } + } + defer func() { + if err == nil { + return + } + fmt.Printf("selected TableLog: %d, Symbol length: %d\n", s.actualTableLog, s.symbolLen) + for i, v := range s.norm[:s.symbolLen] { + fmt.Printf("%3d: %5d -> %4d \n", i, s.count[i], v) + } + }() + if total != (1 << s.actualTableLog) { + return fmt.Errorf("warning: Total == %d != %d", total, 1<> 3) + 3 + 2 + + // Write Table Size + bitStream = uint32(tableLog - minEncTablelog) + bitCount = uint(4) + remaining = int16(tableSize + 1) /* +1 for extra accuracy */ + threshold = int16(tableSize) + nbBits = uint(tableLog + 1) + outP = len(out) + ) + if cap(out) < outP+maxHeaderSize { + out = append(out, make([]byte, maxHeaderSize*3)...) + out = out[:len(out)-maxHeaderSize*3] + } + out = out[:outP+maxHeaderSize] + + // stops at 1 + for remaining > 1 { + if previous0 { + start := charnum + for s.norm[charnum] == 0 { + charnum++ + } + for charnum >= start+24 { + start += 24 + bitStream += uint32(0xFFFF) << bitCount + out[outP] = byte(bitStream) + out[outP+1] = byte(bitStream >> 8) + outP += 2 + bitStream >>= 16 + } + for charnum >= start+3 { + start += 3 + bitStream += 3 << bitCount + bitCount += 2 + } + bitStream += uint32(charnum-start) << bitCount + bitCount += 2 + if bitCount > 16 { + out[outP] = byte(bitStream) + out[outP+1] = byte(bitStream >> 8) + outP += 2 + bitStream >>= 16 + bitCount -= 16 + } + } + + count := s.norm[charnum] + charnum++ + max := (2*threshold - 1) - remaining + if count < 0 { + remaining += count + } else { + remaining -= count + } + count++ // +1 for extra accuracy + if count >= threshold { + count += max // [0..max[ [max..threshold[ (...) [threshold+max 2*threshold[ + } + bitStream += uint32(count) << bitCount + bitCount += nbBits + if count < max { + bitCount-- + } + + previous0 = count == 1 + if remaining < 1 { + return nil, errors.New("internal error: remaining < 1") + } + for remaining < threshold { + nbBits-- + threshold >>= 1 + } + + if bitCount > 16 { + out[outP] = byte(bitStream) + out[outP+1] = byte(bitStream >> 8) + outP += 2 + bitStream >>= 16 + bitCount -= 16 + } + } + + if outP+2 > len(out) { + return nil, fmt.Errorf("internal error: %d > %d, maxheader: %d, sl: %d, tl: %d, normcount: %v", outP+2, len(out), maxHeaderSize, s.symbolLen, int(tableLog), s.norm[:s.symbolLen]) + } + out[outP] = byte(bitStream) + out[outP+1] = byte(bitStream >> 8) + outP += int((bitCount + 7) / 8) + + if charnum > s.symbolLen { + return nil, errors.New("internal error: charnum > s.symbolLen") + } + return out[:outP], nil +} + +// Approximate symbol cost, as fractional value, using fixed-point format (accuracyLog fractional bits) +// note 1 : assume symbolValue is valid (<= maxSymbolValue) +// note 2 : if freq[symbolValue]==0, @return a fake cost of tableLog+1 bits * +func (s *fseEncoder) bitCost(symbolValue uint8, accuracyLog uint32) uint32 { + minNbBits := s.ct.symbolTT[symbolValue].deltaNbBits >> 16 + threshold := (minNbBits + 1) << 16 + if debugAsserts { + if !(s.actualTableLog < 16) { + panic("!s.actualTableLog < 16") + } + // ensure enough room for renormalization double shift + if !(uint8(accuracyLog) < 31-s.actualTableLog) { + panic("!uint8(accuracyLog) < 31-s.actualTableLog") + } + } + tableSize := uint32(1) << s.actualTableLog + deltaFromThreshold := threshold - (s.ct.symbolTT[symbolValue].deltaNbBits + tableSize) + // linear interpolation (very approximate) + normalizedDeltaFromThreshold := (deltaFromThreshold << accuracyLog) >> s.actualTableLog + bitMultiplier := uint32(1) << accuracyLog + if debugAsserts { + if s.ct.symbolTT[symbolValue].deltaNbBits+tableSize > threshold { + panic("s.ct.symbolTT[symbolValue].deltaNbBits+tableSize > threshold") + } + if normalizedDeltaFromThreshold > bitMultiplier { + panic("normalizedDeltaFromThreshold > bitMultiplier") + } + } + return (minNbBits+1)*bitMultiplier - normalizedDeltaFromThreshold +} + +// Returns the cost in bits of encoding the distribution in count using ctable. +// Histogram should only be up to the last non-zero symbol. +// Returns an -1 if ctable cannot represent all the symbols in count. +func (s *fseEncoder) approxSize(hist []uint32) uint32 { + if int(s.symbolLen) < len(hist) { + // More symbols than we have. + return math.MaxUint32 + } + if s.useRLE { + // We will never reuse RLE encoders. + return math.MaxUint32 + } + const kAccuracyLog = 8 + badCost := (uint32(s.actualTableLog) + 1) << kAccuracyLog + var cost uint32 + for i, v := range hist { + if v == 0 { + continue + } + if s.norm[i] == 0 { + return math.MaxUint32 + } + bitCost := s.bitCost(uint8(i), kAccuracyLog) + if bitCost > badCost { + return math.MaxUint32 + } + cost += v * bitCost + } + return cost >> kAccuracyLog +} + +// maxHeaderSize returns the maximum header size in bits. +// This is not exact size, but we want a penalty for new tables anyway. +func (s *fseEncoder) maxHeaderSize() uint32 { + if s.preDefined { + return 0 + } + if s.useRLE { + return 8 + } + return (((uint32(s.symbolLen) * uint32(s.actualTableLog)) >> 3) + 3) * 8 +} + +// cState contains the compression state of a stream. +type cState struct { + bw *bitWriter + stateTable []uint16 + state uint16 +} + +// init will initialize the compression state to the first symbol of the stream. +func (c *cState) init(bw *bitWriter, ct *cTable, first symbolTransform) { + c.bw = bw + c.stateTable = ct.stateTable + if len(c.stateTable) == 1 { + // RLE + c.stateTable[0] = uint16(0) + c.state = 0 + return + } + nbBitsOut := (first.deltaNbBits + (1 << 15)) >> 16 + im := int32((nbBitsOut << 16) - first.deltaNbBits) + lu := (im >> nbBitsOut) + int32(first.deltaFindState) + c.state = c.stateTable[lu] +} + +// flush will write the tablelog to the output and flush the remaining full bytes. +func (c *cState) flush(tableLog uint8) { + c.bw.flush32() + c.bw.addBits16NC(c.state, tableLog) +} diff --git a/vendor/github.com/klauspost/compress/zstd/fse_predefined.go b/vendor/github.com/klauspost/compress/zstd/fse_predefined.go new file mode 100644 index 000000000..474cb77d2 --- /dev/null +++ b/vendor/github.com/klauspost/compress/zstd/fse_predefined.go @@ -0,0 +1,158 @@ +// Copyright 2019+ Klaus Post. All rights reserved. +// License information can be found in the LICENSE file. +// Based on work by Yann Collet, released under BSD License. + +package zstd + +import ( + "fmt" + "math" + "sync" +) + +var ( + // fsePredef are the predefined fse tables as defined here: + // https://github.com/facebook/zstd/blob/dev/doc/zstd_compression_format.md#default-distributions + // These values are already transformed. + fsePredef [3]fseDecoder + + // fsePredefEnc are the predefined encoder based on fse tables as defined here: + // https://github.com/facebook/zstd/blob/dev/doc/zstd_compression_format.md#default-distributions + // These values are already transformed. + fsePredefEnc [3]fseEncoder + + // symbolTableX contain the transformations needed for each type as defined in + // https://github.com/facebook/zstd/blob/dev/doc/zstd_compression_format.md#the-codes-for-literals-lengths-match-lengths-and-offsets + symbolTableX [3][]baseOffset + + // maxTableSymbol is the biggest supported symbol for each table type + // https://github.com/facebook/zstd/blob/dev/doc/zstd_compression_format.md#the-codes-for-literals-lengths-match-lengths-and-offsets + maxTableSymbol = [3]uint8{tableLiteralLengths: maxLiteralLengthSymbol, tableOffsets: maxOffsetLengthSymbol, tableMatchLengths: maxMatchLengthSymbol} + + // bitTables is the bits table for each table. + bitTables = [3][]byte{tableLiteralLengths: llBitsTable[:], tableOffsets: nil, tableMatchLengths: mlBitsTable[:]} +) + +type tableIndex uint8 + +const ( + // indexes for fsePredef and symbolTableX + tableLiteralLengths tableIndex = 0 + tableOffsets tableIndex = 1 + tableMatchLengths tableIndex = 2 + + maxLiteralLengthSymbol = 35 + maxOffsetLengthSymbol = 30 + maxMatchLengthSymbol = 52 +) + +// baseOffset is used for calculating transformations. +type baseOffset struct { + baseLine uint32 + addBits uint8 +} + +// fillBase will precalculate base offsets with the given bit distributions. +func fillBase(dst []baseOffset, base uint32, bits ...uint8) { + if len(bits) != len(dst) { + panic(fmt.Sprintf("len(dst) (%d) != len(bits) (%d)", len(dst), len(bits))) + } + for i, bit := range bits { + if base > math.MaxInt32 { + panic("invalid decoding table, base overflows int32") + } + + dst[i] = baseOffset{ + baseLine: base, + addBits: bit, + } + base += 1 << bit + } +} + +var predef sync.Once + +func initPredefined() { + predef.Do(func() { + // Literals length codes + tmp := make([]baseOffset, 36) + for i := range tmp[:16] { + tmp[i] = baseOffset{ + baseLine: uint32(i), + addBits: 0, + } + } + fillBase(tmp[16:], 16, 1, 1, 1, 1, 2, 2, 3, 3, 4, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16) + symbolTableX[tableLiteralLengths] = tmp + + // Match length codes + tmp = make([]baseOffset, 53) + for i := range tmp[:32] { + tmp[i] = baseOffset{ + // The transformation adds the 3 length. + baseLine: uint32(i) + 3, + addBits: 0, + } + } + fillBase(tmp[32:], 35, 1, 1, 1, 1, 2, 2, 3, 3, 4, 4, 5, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16) + symbolTableX[tableMatchLengths] = tmp + + // Offset codes + tmp = make([]baseOffset, maxOffsetBits+1) + tmp[1] = baseOffset{ + baseLine: 1, + addBits: 1, + } + fillBase(tmp[2:], 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30) + symbolTableX[tableOffsets] = tmp + + // Fill predefined tables and transform them. + // https://github.com/facebook/zstd/blob/dev/doc/zstd_compression_format.md#default-distributions + for i := range fsePredef[:] { + f := &fsePredef[i] + switch tableIndex(i) { + case tableLiteralLengths: + // https://github.com/facebook/zstd/blob/ededcfca57366461021c922720878c81a5854a0a/lib/decompress/zstd_decompress_block.c#L243 + f.actualTableLog = 6 + copy(f.norm[:], []int16{4, 3, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 2, 1, 1, 1, 1, 1, + -1, -1, -1, -1}) + f.symbolLen = 36 + case tableOffsets: + // https://github.com/facebook/zstd/blob/ededcfca57366461021c922720878c81a5854a0a/lib/decompress/zstd_decompress_block.c#L281 + f.actualTableLog = 5 + copy(f.norm[:], []int16{ + 1, 1, 1, 1, 1, 1, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1}) + f.symbolLen = 29 + case tableMatchLengths: + //https://github.com/facebook/zstd/blob/ededcfca57366461021c922720878c81a5854a0a/lib/decompress/zstd_decompress_block.c#L304 + f.actualTableLog = 6 + copy(f.norm[:], []int16{ + 1, 4, 3, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, + -1, -1, -1, -1, -1}) + f.symbolLen = 53 + } + if err := f.buildDtable(); err != nil { + panic(fmt.Errorf("building table %v: %v", tableIndex(i), err)) + } + if err := f.transform(symbolTableX[i]); err != nil { + panic(fmt.Errorf("building table %v: %v", tableIndex(i), err)) + } + f.preDefined = true + + // Create encoder as well + enc := &fsePredefEnc[i] + copy(enc.norm[:], f.norm[:]) + enc.symbolLen = f.symbolLen + enc.actualTableLog = f.actualTableLog + if err := enc.buildCTable(); err != nil { + panic(fmt.Errorf("building encoding table %v: %v", tableIndex(i), err)) + } + enc.setBits(bitTables[i]) + enc.preDefined = true + } + }) +} diff --git a/vendor/github.com/klauspost/compress/zstd/hash.go b/vendor/github.com/klauspost/compress/zstd/hash.go new file mode 100644 index 000000000..5d73c21eb --- /dev/null +++ b/vendor/github.com/klauspost/compress/zstd/hash.go @@ -0,0 +1,35 @@ +// Copyright 2019+ Klaus Post. All rights reserved. +// License information can be found in the LICENSE file. +// Based on work by Yann Collet, released under BSD License. + +package zstd + +const ( + prime3bytes = 506832829 + prime4bytes = 2654435761 + prime5bytes = 889523592379 + prime6bytes = 227718039650203 + prime7bytes = 58295818150454627 + prime8bytes = 0xcf1bbcdcb7a56463 +) + +// hashLen returns a hash of the lowest mls bytes of with length output bits. +// mls must be >=3 and <=8. Any other value will return hash for 4 bytes. +// length should always be < 32. +// Preferably length and mls should be a constant for inlining. +func hashLen(u uint64, length, mls uint8) uint32 { + switch mls { + case 3: + return (uint32(u<<8) * prime3bytes) >> (32 - length) + case 5: + return uint32(((u << (64 - 40)) * prime5bytes) >> (64 - length)) + case 6: + return uint32(((u << (64 - 48)) * prime6bytes) >> (64 - length)) + case 7: + return uint32(((u << (64 - 56)) * prime7bytes) >> (64 - length)) + case 8: + return uint32((u * prime8bytes) >> (64 - length)) + default: + return (uint32(u) * prime4bytes) >> (32 - length) + } +} diff --git a/vendor/github.com/klauspost/compress/zstd/history.go b/vendor/github.com/klauspost/compress/zstd/history.go new file mode 100644 index 000000000..09164856d --- /dev/null +++ b/vendor/github.com/klauspost/compress/zstd/history.go @@ -0,0 +1,116 @@ +// Copyright 2019+ Klaus Post. All rights reserved. +// License information can be found in the LICENSE file. +// Based on work by Yann Collet, released under BSD License. + +package zstd + +import ( + "github.com/klauspost/compress/huff0" +) + +// history contains the information transferred between blocks. +type history struct { + // Literal decompression + huffTree *huff0.Scratch + + // Sequence decompression + decoders sequenceDecs + recentOffsets [3]int + + // History buffer... + b []byte + + // ignoreBuffer is meant to ignore a number of bytes + // when checking for matches in history + ignoreBuffer int + + windowSize int + allocFrameBuffer int // needed? + error bool + dict *dict +} + +// reset will reset the history to initial state of a frame. +// The history must already have been initialized to the desired size. +func (h *history) reset() { + h.b = h.b[:0] + h.ignoreBuffer = 0 + h.error = false + h.recentOffsets = [3]int{1, 4, 8} + h.decoders.freeDecoders() + h.decoders = sequenceDecs{br: h.decoders.br} + h.freeHuffDecoder() + h.huffTree = nil + h.dict = nil + //printf("history created: %+v (l: %d, c: %d)", *h, len(h.b), cap(h.b)) +} + +func (h *history) freeHuffDecoder() { + if h.huffTree != nil { + if h.dict == nil || h.dict.litEnc != h.huffTree { + huffDecoderPool.Put(h.huffTree) + h.huffTree = nil + } + } +} + +func (h *history) setDict(dict *dict) { + if dict == nil { + return + } + h.dict = dict + h.decoders.litLengths = dict.llDec + h.decoders.offsets = dict.ofDec + h.decoders.matchLengths = dict.mlDec + h.decoders.dict = dict.content + h.recentOffsets = dict.offsets + h.huffTree = dict.litEnc +} + +// append bytes to history. +// This function will make sure there is space for it, +// if the buffer has been allocated with enough extra space. +func (h *history) append(b []byte) { + if len(b) >= h.windowSize { + // Discard all history by simply overwriting + h.b = h.b[:h.windowSize] + copy(h.b, b[len(b)-h.windowSize:]) + return + } + + // If there is space, append it. + if len(b) < cap(h.b)-len(h.b) { + h.b = append(h.b, b...) + return + } + + // Move data down so we only have window size left. + // We know we have less than window size in b at this point. + discard := len(b) + len(h.b) - h.windowSize + copy(h.b, h.b[discard:]) + h.b = h.b[:h.windowSize] + copy(h.b[h.windowSize-len(b):], b) +} + +// ensureBlock will ensure there is space for at least one block... +func (h *history) ensureBlock() { + if cap(h.b) < h.allocFrameBuffer { + h.b = make([]byte, 0, h.allocFrameBuffer) + return + } + + avail := cap(h.b) - len(h.b) + if avail >= h.windowSize || avail > maxCompressedBlockSize { + return + } + // Move data down so we only have window size left. + // We know we have less than window size in b at this point. + discard := len(h.b) - h.windowSize + copy(h.b, h.b[discard:]) + h.b = h.b[:h.windowSize] +} + +// append bytes to history without ever discarding anything. +func (h *history) appendKeep(b []byte) { + h.b = append(h.b, b...) +} diff --git a/vendor/github.com/klauspost/compress/zstd/internal/xxhash/LICENSE.txt b/vendor/github.com/klauspost/compress/zstd/internal/xxhash/LICENSE.txt new file mode 100644 index 000000000..24b53065f --- /dev/null +++ b/vendor/github.com/klauspost/compress/zstd/internal/xxhash/LICENSE.txt @@ -0,0 +1,22 @@ +Copyright (c) 2016 Caleb Spare + +MIT License + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/vendor/github.com/klauspost/compress/zstd/internal/xxhash/README.md b/vendor/github.com/klauspost/compress/zstd/internal/xxhash/README.md new file mode 100644 index 000000000..777290d44 --- /dev/null +++ b/vendor/github.com/klauspost/compress/zstd/internal/xxhash/README.md @@ -0,0 +1,71 @@ +# xxhash + +VENDORED: Go to [github.com/cespare/xxhash](https://github.com/cespare/xxhash) for original package. + +xxhash is a Go implementation of the 64-bit [xxHash] algorithm, XXH64. This is a +high-quality hashing algorithm that is much faster than anything in the Go +standard library. + +This package provides a straightforward API: + +``` +func Sum64(b []byte) uint64 +func Sum64String(s string) uint64 +type Digest struct{ ... } + func New() *Digest +``` + +The `Digest` type implements hash.Hash64. Its key methods are: + +``` +func (*Digest) Write([]byte) (int, error) +func (*Digest) WriteString(string) (int, error) +func (*Digest) Sum64() uint64 +``` + +The package is written with optimized pure Go and also contains even faster +assembly implementations for amd64 and arm64. If desired, the `purego` build tag +opts into using the Go code even on those architectures. + +[xxHash]: http://cyan4973.github.io/xxHash/ + +## Compatibility + +This package is in a module and the latest code is in version 2 of the module. +You need a version of Go with at least "minimal module compatibility" to use +github.com/cespare/xxhash/v2: + +* 1.9.7+ for Go 1.9 +* 1.10.3+ for Go 1.10 +* Go 1.11 or later + +I recommend using the latest release of Go. + +## Benchmarks + +Here are some quick benchmarks comparing the pure-Go and assembly +implementations of Sum64. + +| input size | purego | asm | +| ---------- | --------- | --------- | +| 4 B | 1.3 GB/s | 1.2 GB/s | +| 16 B | 2.9 GB/s | 3.5 GB/s | +| 100 B | 6.9 GB/s | 8.1 GB/s | +| 4 KB | 11.7 GB/s | 16.7 GB/s | +| 10 MB | 12.0 GB/s | 17.3 GB/s | + +These numbers were generated on Ubuntu 20.04 with an Intel Xeon Platinum 8252C +CPU using the following commands under Go 1.19.2: + +``` +benchstat <(go test -tags purego -benchtime 500ms -count 15 -bench 'Sum64$') +benchstat <(go test -benchtime 500ms -count 15 -bench 'Sum64$') +``` + +## Projects using this package + +- [InfluxDB](https://github.com/influxdata/influxdb) +- [Prometheus](https://github.com/prometheus/prometheus) +- [VictoriaMetrics](https://github.com/VictoriaMetrics/VictoriaMetrics) +- [FreeCache](https://github.com/coocood/freecache) +- [FastCache](https://github.com/VictoriaMetrics/fastcache) diff --git a/vendor/github.com/klauspost/compress/zstd/internal/xxhash/xxhash.go b/vendor/github.com/klauspost/compress/zstd/internal/xxhash/xxhash.go new file mode 100644 index 000000000..fc40c8200 --- /dev/null +++ b/vendor/github.com/klauspost/compress/zstd/internal/xxhash/xxhash.go @@ -0,0 +1,230 @@ +// Package xxhash implements the 64-bit variant of xxHash (XXH64) as described +// at http://cyan4973.github.io/xxHash/. +// THIS IS VENDORED: Go to github.com/cespare/xxhash for original package. + +package xxhash + +import ( + "encoding/binary" + "errors" + "math/bits" +) + +const ( + prime1 uint64 = 11400714785074694791 + prime2 uint64 = 14029467366897019727 + prime3 uint64 = 1609587929392839161 + prime4 uint64 = 9650029242287828579 + prime5 uint64 = 2870177450012600261 +) + +// Store the primes in an array as well. +// +// The consts are used when possible in Go code to avoid MOVs but we need a +// contiguous array of the assembly code. +var primes = [...]uint64{prime1, prime2, prime3, prime4, prime5} + +// Digest implements hash.Hash64. +type Digest struct { + v1 uint64 + v2 uint64 + v3 uint64 + v4 uint64 + total uint64 + mem [32]byte + n int // how much of mem is used +} + +// New creates a new Digest that computes the 64-bit xxHash algorithm. +func New() *Digest { + var d Digest + d.Reset() + return &d +} + +// Reset clears the Digest's state so that it can be reused. +func (d *Digest) Reset() { + d.v1 = primes[0] + prime2 + d.v2 = prime2 + d.v3 = 0 + d.v4 = -primes[0] + d.total = 0 + d.n = 0 +} + +// Size always returns 8 bytes. +func (d *Digest) Size() int { return 8 } + +// BlockSize always returns 32 bytes. +func (d *Digest) BlockSize() int { return 32 } + +// Write adds more data to d. It always returns len(b), nil. +func (d *Digest) Write(b []byte) (n int, err error) { + n = len(b) + d.total += uint64(n) + + memleft := d.mem[d.n&(len(d.mem)-1):] + + if d.n+n < 32 { + // This new data doesn't even fill the current block. + copy(memleft, b) + d.n += n + return + } + + if d.n > 0 { + // Finish off the partial block. + c := copy(memleft, b) + d.v1 = round(d.v1, u64(d.mem[0:8])) + d.v2 = round(d.v2, u64(d.mem[8:16])) + d.v3 = round(d.v3, u64(d.mem[16:24])) + d.v4 = round(d.v4, u64(d.mem[24:32])) + b = b[c:] + d.n = 0 + } + + if len(b) >= 32 { + // One or more full blocks left. + nw := writeBlocks(d, b) + b = b[nw:] + } + + // Store any remaining partial block. + copy(d.mem[:], b) + d.n = len(b) + + return +} + +// Sum appends the current hash to b and returns the resulting slice. +func (d *Digest) Sum(b []byte) []byte { + s := d.Sum64() + return append( + b, + byte(s>>56), + byte(s>>48), + byte(s>>40), + byte(s>>32), + byte(s>>24), + byte(s>>16), + byte(s>>8), + byte(s), + ) +} + +// Sum64 returns the current hash. +func (d *Digest) Sum64() uint64 { + var h uint64 + + if d.total >= 32 { + v1, v2, v3, v4 := d.v1, d.v2, d.v3, d.v4 + h = rol1(v1) + rol7(v2) + rol12(v3) + rol18(v4) + h = mergeRound(h, v1) + h = mergeRound(h, v2) + h = mergeRound(h, v3) + h = mergeRound(h, v4) + } else { + h = d.v3 + prime5 + } + + h += d.total + + b := d.mem[:d.n&(len(d.mem)-1)] + for ; len(b) >= 8; b = b[8:] { + k1 := round(0, u64(b[:8])) + h ^= k1 + h = rol27(h)*prime1 + prime4 + } + if len(b) >= 4 { + h ^= uint64(u32(b[:4])) * prime1 + h = rol23(h)*prime2 + prime3 + b = b[4:] + } + for ; len(b) > 0; b = b[1:] { + h ^= uint64(b[0]) * prime5 + h = rol11(h) * prime1 + } + + h ^= h >> 33 + h *= prime2 + h ^= h >> 29 + h *= prime3 + h ^= h >> 32 + + return h +} + +const ( + magic = "xxh\x06" + marshaledSize = len(magic) + 8*5 + 32 +) + +// MarshalBinary implements the encoding.BinaryMarshaler interface. +func (d *Digest) MarshalBinary() ([]byte, error) { + b := make([]byte, 0, marshaledSize) + b = append(b, magic...) + b = appendUint64(b, d.v1) + b = appendUint64(b, d.v2) + b = appendUint64(b, d.v3) + b = appendUint64(b, d.v4) + b = appendUint64(b, d.total) + b = append(b, d.mem[:d.n]...) + b = b[:len(b)+len(d.mem)-d.n] + return b, nil +} + +// UnmarshalBinary implements the encoding.BinaryUnmarshaler interface. +func (d *Digest) UnmarshalBinary(b []byte) error { + if len(b) < len(magic) || string(b[:len(magic)]) != magic { + return errors.New("xxhash: invalid hash state identifier") + } + if len(b) != marshaledSize { + return errors.New("xxhash: invalid hash state size") + } + b = b[len(magic):] + b, d.v1 = consumeUint64(b) + b, d.v2 = consumeUint64(b) + b, d.v3 = consumeUint64(b) + b, d.v4 = consumeUint64(b) + b, d.total = consumeUint64(b) + copy(d.mem[:], b) + d.n = int(d.total % uint64(len(d.mem))) + return nil +} + +func appendUint64(b []byte, x uint64) []byte { + var a [8]byte + binary.LittleEndian.PutUint64(a[:], x) + return append(b, a[:]...) +} + +func consumeUint64(b []byte) ([]byte, uint64) { + x := u64(b) + return b[8:], x +} + +func u64(b []byte) uint64 { return binary.LittleEndian.Uint64(b) } +func u32(b []byte) uint32 { return binary.LittleEndian.Uint32(b) } + +func round(acc, input uint64) uint64 { + acc += input * prime2 + acc = rol31(acc) + acc *= prime1 + return acc +} + +func mergeRound(acc, val uint64) uint64 { + val = round(0, val) + acc ^= val + acc = acc*prime1 + prime4 + return acc +} + +func rol1(x uint64) uint64 { return bits.RotateLeft64(x, 1) } +func rol7(x uint64) uint64 { return bits.RotateLeft64(x, 7) } +func rol11(x uint64) uint64 { return bits.RotateLeft64(x, 11) } +func rol12(x uint64) uint64 { return bits.RotateLeft64(x, 12) } +func rol18(x uint64) uint64 { return bits.RotateLeft64(x, 18) } +func rol23(x uint64) uint64 { return bits.RotateLeft64(x, 23) } +func rol27(x uint64) uint64 { return bits.RotateLeft64(x, 27) } +func rol31(x uint64) uint64 { return bits.RotateLeft64(x, 31) } diff --git a/vendor/github.com/klauspost/compress/zstd/internal/xxhash/xxhash_amd64.s b/vendor/github.com/klauspost/compress/zstd/internal/xxhash/xxhash_amd64.s new file mode 100644 index 000000000..ddb63aa91 --- /dev/null +++ b/vendor/github.com/klauspost/compress/zstd/internal/xxhash/xxhash_amd64.s @@ -0,0 +1,210 @@ +//go:build !appengine && gc && !purego && !noasm +// +build !appengine +// +build gc +// +build !purego +// +build !noasm + +#include "textflag.h" + +// Registers: +#define h AX +#define d AX +#define p SI // pointer to advance through b +#define n DX +#define end BX // loop end +#define v1 R8 +#define v2 R9 +#define v3 R10 +#define v4 R11 +#define x R12 +#define prime1 R13 +#define prime2 R14 +#define prime4 DI + +#define round(acc, x) \ + IMULQ prime2, x \ + ADDQ x, acc \ + ROLQ $31, acc \ + IMULQ prime1, acc + +// round0 performs the operation x = round(0, x). +#define round0(x) \ + IMULQ prime2, x \ + ROLQ $31, x \ + IMULQ prime1, x + +// mergeRound applies a merge round on the two registers acc and x. +// It assumes that prime1, prime2, and prime4 have been loaded. +#define mergeRound(acc, x) \ + round0(x) \ + XORQ x, acc \ + IMULQ prime1, acc \ + ADDQ prime4, acc + +// blockLoop processes as many 32-byte blocks as possible, +// updating v1, v2, v3, and v4. It assumes that there is at least one block +// to process. +#define blockLoop() \ +loop: \ + MOVQ +0(p), x \ + round(v1, x) \ + MOVQ +8(p), x \ + round(v2, x) \ + MOVQ +16(p), x \ + round(v3, x) \ + MOVQ +24(p), x \ + round(v4, x) \ + ADDQ $32, p \ + CMPQ p, end \ + JLE loop + +// func Sum64(b []byte) uint64 +TEXT ·Sum64(SB), NOSPLIT|NOFRAME, $0-32 + // Load fixed primes. + MOVQ ·primes+0(SB), prime1 + MOVQ ·primes+8(SB), prime2 + MOVQ ·primes+24(SB), prime4 + + // Load slice. + MOVQ b_base+0(FP), p + MOVQ b_len+8(FP), n + LEAQ (p)(n*1), end + + // The first loop limit will be len(b)-32. + SUBQ $32, end + + // Check whether we have at least one block. + CMPQ n, $32 + JLT noBlocks + + // Set up initial state (v1, v2, v3, v4). + MOVQ prime1, v1 + ADDQ prime2, v1 + MOVQ prime2, v2 + XORQ v3, v3 + XORQ v4, v4 + SUBQ prime1, v4 + + blockLoop() + + MOVQ v1, h + ROLQ $1, h + MOVQ v2, x + ROLQ $7, x + ADDQ x, h + MOVQ v3, x + ROLQ $12, x + ADDQ x, h + MOVQ v4, x + ROLQ $18, x + ADDQ x, h + + mergeRound(h, v1) + mergeRound(h, v2) + mergeRound(h, v3) + mergeRound(h, v4) + + JMP afterBlocks + +noBlocks: + MOVQ ·primes+32(SB), h + +afterBlocks: + ADDQ n, h + + ADDQ $24, end + CMPQ p, end + JG try4 + +loop8: + MOVQ (p), x + ADDQ $8, p + round0(x) + XORQ x, h + ROLQ $27, h + IMULQ prime1, h + ADDQ prime4, h + + CMPQ p, end + JLE loop8 + +try4: + ADDQ $4, end + CMPQ p, end + JG try1 + + MOVL (p), x + ADDQ $4, p + IMULQ prime1, x + XORQ x, h + + ROLQ $23, h + IMULQ prime2, h + ADDQ ·primes+16(SB), h + +try1: + ADDQ $4, end + CMPQ p, end + JGE finalize + +loop1: + MOVBQZX (p), x + ADDQ $1, p + IMULQ ·primes+32(SB), x + XORQ x, h + ROLQ $11, h + IMULQ prime1, h + + CMPQ p, end + JL loop1 + +finalize: + MOVQ h, x + SHRQ $33, x + XORQ x, h + IMULQ prime2, h + MOVQ h, x + SHRQ $29, x + XORQ x, h + IMULQ ·primes+16(SB), h + MOVQ h, x + SHRQ $32, x + XORQ x, h + + MOVQ h, ret+24(FP) + RET + +// func writeBlocks(d *Digest, b []byte) int +TEXT ·writeBlocks(SB), NOSPLIT|NOFRAME, $0-40 + // Load fixed primes needed for round. + MOVQ ·primes+0(SB), prime1 + MOVQ ·primes+8(SB), prime2 + + // Load slice. + MOVQ b_base+8(FP), p + MOVQ b_len+16(FP), n + LEAQ (p)(n*1), end + SUBQ $32, end + + // Load vN from d. + MOVQ s+0(FP), d + MOVQ 0(d), v1 + MOVQ 8(d), v2 + MOVQ 16(d), v3 + MOVQ 24(d), v4 + + // We don't need to check the loop condition here; this function is + // always called with at least one block of data to process. + blockLoop() + + // Copy vN back to d. + MOVQ v1, 0(d) + MOVQ v2, 8(d) + MOVQ v3, 16(d) + MOVQ v4, 24(d) + + // The number of bytes written is p minus the old base pointer. + SUBQ b_base+8(FP), p + MOVQ p, ret+32(FP) + + RET diff --git a/vendor/github.com/klauspost/compress/zstd/internal/xxhash/xxhash_arm64.s b/vendor/github.com/klauspost/compress/zstd/internal/xxhash/xxhash_arm64.s new file mode 100644 index 000000000..ae7d4d329 --- /dev/null +++ b/vendor/github.com/klauspost/compress/zstd/internal/xxhash/xxhash_arm64.s @@ -0,0 +1,184 @@ +//go:build !appengine && gc && !purego && !noasm +// +build !appengine +// +build gc +// +build !purego +// +build !noasm + +#include "textflag.h" + +// Registers: +#define digest R1 +#define h R2 // return value +#define p R3 // input pointer +#define n R4 // input length +#define nblocks R5 // n / 32 +#define prime1 R7 +#define prime2 R8 +#define prime3 R9 +#define prime4 R10 +#define prime5 R11 +#define v1 R12 +#define v2 R13 +#define v3 R14 +#define v4 R15 +#define x1 R20 +#define x2 R21 +#define x3 R22 +#define x4 R23 + +#define round(acc, x) \ + MADD prime2, acc, x, acc \ + ROR $64-31, acc \ + MUL prime1, acc + +// round0 performs the operation x = round(0, x). +#define round0(x) \ + MUL prime2, x \ + ROR $64-31, x \ + MUL prime1, x + +#define mergeRound(acc, x) \ + round0(x) \ + EOR x, acc \ + MADD acc, prime4, prime1, acc + +// blockLoop processes as many 32-byte blocks as possible, +// updating v1, v2, v3, and v4. It assumes that n >= 32. +#define blockLoop() \ + LSR $5, n, nblocks \ + PCALIGN $16 \ + loop: \ + LDP.P 16(p), (x1, x2) \ + LDP.P 16(p), (x3, x4) \ + round(v1, x1) \ + round(v2, x2) \ + round(v3, x3) \ + round(v4, x4) \ + SUB $1, nblocks \ + CBNZ nblocks, loop + +// func Sum64(b []byte) uint64 +TEXT ·Sum64(SB), NOSPLIT|NOFRAME, $0-32 + LDP b_base+0(FP), (p, n) + + LDP ·primes+0(SB), (prime1, prime2) + LDP ·primes+16(SB), (prime3, prime4) + MOVD ·primes+32(SB), prime5 + + CMP $32, n + CSEL LT, prime5, ZR, h // if n < 32 { h = prime5 } else { h = 0 } + BLT afterLoop + + ADD prime1, prime2, v1 + MOVD prime2, v2 + MOVD $0, v3 + NEG prime1, v4 + + blockLoop() + + ROR $64-1, v1, x1 + ROR $64-7, v2, x2 + ADD x1, x2 + ROR $64-12, v3, x3 + ROR $64-18, v4, x4 + ADD x3, x4 + ADD x2, x4, h + + mergeRound(h, v1) + mergeRound(h, v2) + mergeRound(h, v3) + mergeRound(h, v4) + +afterLoop: + ADD n, h + + TBZ $4, n, try8 + LDP.P 16(p), (x1, x2) + + round0(x1) + + // NOTE: here and below, sequencing the EOR after the ROR (using a + // rotated register) is worth a small but measurable speedup for small + // inputs. + ROR $64-27, h + EOR x1 @> 64-27, h, h + MADD h, prime4, prime1, h + + round0(x2) + ROR $64-27, h + EOR x2 @> 64-27, h, h + MADD h, prime4, prime1, h + +try8: + TBZ $3, n, try4 + MOVD.P 8(p), x1 + + round0(x1) + ROR $64-27, h + EOR x1 @> 64-27, h, h + MADD h, prime4, prime1, h + +try4: + TBZ $2, n, try2 + MOVWU.P 4(p), x2 + + MUL prime1, x2 + ROR $64-23, h + EOR x2 @> 64-23, h, h + MADD h, prime3, prime2, h + +try2: + TBZ $1, n, try1 + MOVHU.P 2(p), x3 + AND $255, x3, x1 + LSR $8, x3, x2 + + MUL prime5, x1 + ROR $64-11, h + EOR x1 @> 64-11, h, h + MUL prime1, h + + MUL prime5, x2 + ROR $64-11, h + EOR x2 @> 64-11, h, h + MUL prime1, h + +try1: + TBZ $0, n, finalize + MOVBU (p), x4 + + MUL prime5, x4 + ROR $64-11, h + EOR x4 @> 64-11, h, h + MUL prime1, h + +finalize: + EOR h >> 33, h + MUL prime2, h + EOR h >> 29, h + MUL prime3, h + EOR h >> 32, h + + MOVD h, ret+24(FP) + RET + +// func writeBlocks(s *Digest, b []byte) int +TEXT ·writeBlocks(SB), NOSPLIT|NOFRAME, $0-40 + LDP ·primes+0(SB), (prime1, prime2) + + // Load state. Assume v[1-4] are stored contiguously. + MOVD s+0(FP), digest + LDP 0(digest), (v1, v2) + LDP 16(digest), (v3, v4) + + LDP b_base+8(FP), (p, n) + + blockLoop() + + // Store updated state. + STP (v1, v2), 0(digest) + STP (v3, v4), 16(digest) + + BIC $31, n + MOVD n, ret+32(FP) + RET diff --git a/vendor/github.com/klauspost/compress/zstd/internal/xxhash/xxhash_asm.go b/vendor/github.com/klauspost/compress/zstd/internal/xxhash/xxhash_asm.go new file mode 100644 index 000000000..d4221edf4 --- /dev/null +++ b/vendor/github.com/klauspost/compress/zstd/internal/xxhash/xxhash_asm.go @@ -0,0 +1,16 @@ +//go:build (amd64 || arm64) && !appengine && gc && !purego && !noasm +// +build amd64 arm64 +// +build !appengine +// +build gc +// +build !purego +// +build !noasm + +package xxhash + +// Sum64 computes the 64-bit xxHash digest of b. +// +//go:noescape +func Sum64(b []byte) uint64 + +//go:noescape +func writeBlocks(s *Digest, b []byte) int diff --git a/vendor/github.com/klauspost/compress/zstd/internal/xxhash/xxhash_other.go b/vendor/github.com/klauspost/compress/zstd/internal/xxhash/xxhash_other.go new file mode 100644 index 000000000..9576426e6 --- /dev/null +++ b/vendor/github.com/klauspost/compress/zstd/internal/xxhash/xxhash_other.go @@ -0,0 +1,75 @@ +//go:build (!amd64 && !arm64) || appengine || !gc || purego || noasm + +package xxhash + +// Sum64 computes the 64-bit xxHash digest of b. +func Sum64(b []byte) uint64 { + // A simpler version would be + // d := New() + // d.Write(b) + // return d.Sum64() + // but this is faster, particularly for small inputs. + + n := len(b) + var h uint64 + + if n >= 32 { + v1 := primes[0] + prime2 + v2 := prime2 + v3 := uint64(0) + v4 := -primes[0] + for len(b) >= 32 { + v1 = round(v1, u64(b[0:8:len(b)])) + v2 = round(v2, u64(b[8:16:len(b)])) + v3 = round(v3, u64(b[16:24:len(b)])) + v4 = round(v4, u64(b[24:32:len(b)])) + b = b[32:len(b):len(b)] + } + h = rol1(v1) + rol7(v2) + rol12(v3) + rol18(v4) + h = mergeRound(h, v1) + h = mergeRound(h, v2) + h = mergeRound(h, v3) + h = mergeRound(h, v4) + } else { + h = prime5 + } + + h += uint64(n) + + for ; len(b) >= 8; b = b[8:] { + k1 := round(0, u64(b[:8])) + h ^= k1 + h = rol27(h)*prime1 + prime4 + } + if len(b) >= 4 { + h ^= uint64(u32(b[:4])) * prime1 + h = rol23(h)*prime2 + prime3 + b = b[4:] + } + for ; len(b) > 0; b = b[1:] { + h ^= uint64(b[0]) * prime5 + h = rol11(h) * prime1 + } + + h ^= h >> 33 + h *= prime2 + h ^= h >> 29 + h *= prime3 + h ^= h >> 32 + + return h +} + +func writeBlocks(d *Digest, b []byte) int { + v1, v2, v3, v4 := d.v1, d.v2, d.v3, d.v4 + n := len(b) + for len(b) >= 32 { + v1 = round(v1, u64(b[0:8:len(b)])) + v2 = round(v2, u64(b[8:16:len(b)])) + v3 = round(v3, u64(b[16:24:len(b)])) + v4 = round(v4, u64(b[24:32:len(b)])) + b = b[32:len(b):len(b)] + } + d.v1, d.v2, d.v3, d.v4 = v1, v2, v3, v4 + return n - len(b) +} diff --git a/vendor/github.com/klauspost/compress/zstd/internal/xxhash/xxhash_safe.go b/vendor/github.com/klauspost/compress/zstd/internal/xxhash/xxhash_safe.go new file mode 100644 index 000000000..6f3b0cb10 --- /dev/null +++ b/vendor/github.com/klauspost/compress/zstd/internal/xxhash/xxhash_safe.go @@ -0,0 +1,11 @@ +package xxhash + +// Sum64String computes the 64-bit xxHash digest of s. +func Sum64String(s string) uint64 { + return Sum64([]byte(s)) +} + +// WriteString adds more data to d. It always returns len(s), nil. +func (d *Digest) WriteString(s string) (n int, err error) { + return d.Write([]byte(s)) +} diff --git a/vendor/github.com/klauspost/compress/zstd/matchlen_amd64.go b/vendor/github.com/klauspost/compress/zstd/matchlen_amd64.go new file mode 100644 index 000000000..1ed18927f --- /dev/null +++ b/vendor/github.com/klauspost/compress/zstd/matchlen_amd64.go @@ -0,0 +1,15 @@ +//go:build amd64 && !appengine && !noasm && gc + +// Copyright 2019+ Klaus Post. All rights reserved. +// License information can be found in the LICENSE file. + +package zstd + +// matchLen returns how many bytes match in a and b +// +// It assumes that: +// +// len(a) <= len(b) and len(a) > 0 +// +//go:noescape +func matchLen(a []byte, b []byte) int diff --git a/vendor/github.com/klauspost/compress/zstd/matchlen_amd64.s b/vendor/github.com/klauspost/compress/zstd/matchlen_amd64.s new file mode 100644 index 000000000..0782b86e3 --- /dev/null +++ b/vendor/github.com/klauspost/compress/zstd/matchlen_amd64.s @@ -0,0 +1,66 @@ +// Copied from S2 implementation. + +//go:build !appengine && !noasm && gc && !noasm + +#include "textflag.h" + +// func matchLen(a []byte, b []byte) int +TEXT ·matchLen(SB), NOSPLIT, $0-56 + MOVQ a_base+0(FP), AX + MOVQ b_base+24(FP), CX + MOVQ a_len+8(FP), DX + + // matchLen + XORL SI, SI + CMPL DX, $0x08 + JB matchlen_match4_standalone + +matchlen_loopback_standalone: + MOVQ (AX)(SI*1), BX + XORQ (CX)(SI*1), BX + JZ matchlen_loop_standalone + +#ifdef GOAMD64_v3 + TZCNTQ BX, BX +#else + BSFQ BX, BX +#endif + SHRL $0x03, BX + LEAL (SI)(BX*1), SI + JMP gen_match_len_end + +matchlen_loop_standalone: + LEAL -8(DX), DX + LEAL 8(SI), SI + CMPL DX, $0x08 + JAE matchlen_loopback_standalone + +matchlen_match4_standalone: + CMPL DX, $0x04 + JB matchlen_match2_standalone + MOVL (AX)(SI*1), BX + CMPL (CX)(SI*1), BX + JNE matchlen_match2_standalone + LEAL -4(DX), DX + LEAL 4(SI), SI + +matchlen_match2_standalone: + CMPL DX, $0x02 + JB matchlen_match1_standalone + MOVW (AX)(SI*1), BX + CMPW (CX)(SI*1), BX + JNE matchlen_match1_standalone + LEAL -2(DX), DX + LEAL 2(SI), SI + +matchlen_match1_standalone: + CMPL DX, $0x01 + JB gen_match_len_end + MOVB (AX)(SI*1), BL + CMPB (CX)(SI*1), BL + JNE gen_match_len_end + INCL SI + +gen_match_len_end: + MOVQ SI, ret+48(FP) + RET diff --git a/vendor/github.com/klauspost/compress/zstd/matchlen_generic.go b/vendor/github.com/klauspost/compress/zstd/matchlen_generic.go new file mode 100644 index 000000000..379746c96 --- /dev/null +++ b/vendor/github.com/klauspost/compress/zstd/matchlen_generic.go @@ -0,0 +1,37 @@ +//go:build !amd64 || appengine || !gc || noasm + +// Copyright 2019+ Klaus Post. All rights reserved. +// License information can be found in the LICENSE file. + +package zstd + +import ( + "math/bits" + + "github.com/klauspost/compress/internal/le" +) + +// matchLen returns the maximum common prefix length of a and b. +// a must be the shortest of the two. +func matchLen(a, b []byte) (n int) { + left := len(a) + for left >= 8 { + diff := le.Load64(a, n) ^ le.Load64(b, n) + if diff != 0 { + return n + bits.TrailingZeros64(diff)>>3 + } + n += 8 + left -= 8 + } + a = a[n:] + b = b[n:] + + for i := range a { + if a[i] != b[i] { + break + } + n++ + } + return n + +} diff --git a/vendor/github.com/klauspost/compress/zstd/seqdec.go b/vendor/github.com/klauspost/compress/zstd/seqdec.go new file mode 100644 index 000000000..0bfb0e43c --- /dev/null +++ b/vendor/github.com/klauspost/compress/zstd/seqdec.go @@ -0,0 +1,500 @@ +// Copyright 2019+ Klaus Post. All rights reserved. +// License information can be found in the LICENSE file. +// Based on work by Yann Collet, released under BSD License. + +package zstd + +import ( + "errors" + "fmt" + "io" +) + +type seq struct { + litLen uint32 + matchLen uint32 + offset uint32 + + // Codes are stored here for the encoder + // so they only have to be looked up once. + llCode, mlCode, ofCode uint8 +} + +type seqVals struct { + ll, ml, mo int +} + +func (s seq) String() string { + if s.offset <= 3 { + if s.offset == 0 { + return fmt.Sprint("litLen:", s.litLen, ", matchLen:", s.matchLen+zstdMinMatch, ", offset: INVALID (0)") + } + return fmt.Sprint("litLen:", s.litLen, ", matchLen:", s.matchLen+zstdMinMatch, ", offset:", s.offset, " (repeat)") + } + return fmt.Sprint("litLen:", s.litLen, ", matchLen:", s.matchLen+zstdMinMatch, ", offset:", s.offset-3, " (new)") +} + +type seqCompMode uint8 + +const ( + compModePredefined seqCompMode = iota + compModeRLE + compModeFSE + compModeRepeat +) + +type sequenceDec struct { + // decoder keeps track of the current state and updates it from the bitstream. + fse *fseDecoder + state fseState + repeat bool +} + +// init the state of the decoder with input from stream. +func (s *sequenceDec) init(br *bitReader) error { + if s.fse == nil { + return errors.New("sequence decoder not defined") + } + s.state.init(br, s.fse.actualTableLog, s.fse.dt[:1< cap(s.out) { + addBytes := s.seqSize + len(s.out) + s.out = append(s.out, make([]byte, addBytes)...) + s.out = s.out[:len(s.out)-addBytes] + } + + if debugDecoder { + printf("Execute %d seqs with hist %d, dict %d, literals: %d into %d bytes\n", len(seqs), len(hist), len(s.dict), len(s.literals), s.seqSize) + } + + var t = len(s.out) + out := s.out[:t+s.seqSize] + + for _, seq := range seqs { + // Add literals + copy(out[t:], s.literals[:seq.ll]) + t += seq.ll + s.literals = s.literals[seq.ll:] + + // Copy from dictionary... + if seq.mo > t+len(hist) || seq.mo > s.windowSize { + if len(s.dict) == 0 { + return fmt.Errorf("match offset (%d) bigger than current history (%d)", seq.mo, t+len(hist)) + } + + // we may be in dictionary. + dictO := len(s.dict) - (seq.mo - (t + len(hist))) + if dictO < 0 || dictO >= len(s.dict) { + return fmt.Errorf("match offset (%d) bigger than current history+dict (%d)", seq.mo, t+len(hist)+len(s.dict)) + } + end := dictO + seq.ml + if end > len(s.dict) { + n := len(s.dict) - dictO + copy(out[t:], s.dict[dictO:]) + t += n + seq.ml -= n + } else { + copy(out[t:], s.dict[dictO:end]) + t += end - dictO + continue + } + } + + // Copy from history. + if v := seq.mo - t; v > 0 { + // v is the start position in history from end. + start := len(hist) - v + if seq.ml > v { + // Some goes into current block. + // Copy remainder of history + copy(out[t:], hist[start:]) + t += v + seq.ml -= v + } else { + copy(out[t:], hist[start:start+seq.ml]) + t += seq.ml + continue + } + } + // We must be in current buffer now + if seq.ml > 0 { + start := t - seq.mo + if seq.ml <= t-start { + // No overlap + copy(out[t:], out[start:start+seq.ml]) + t += seq.ml + continue + } else { + // Overlapping copy + // Extend destination slice and copy one byte at the time. + src := out[start : start+seq.ml] + dst := out[t:] + dst = dst[:len(src)] + t += len(src) + // Destination is the space we just added. + for i := range src { + dst[i] = src[i] + } + } + } + } + + // Add final literals + copy(out[t:], s.literals) + if debugDecoder { + t += len(s.literals) + if t != len(out) { + panic(fmt.Errorf("length mismatch, want %d, got %d, ss: %d", len(out), t, s.seqSize)) + } + } + s.out = out + + return nil +} + +// decode sequences from the stream with the provided history. +func (s *sequenceDecs) decodeSync(hist []byte) error { + supported, err := s.decodeSyncSimple(hist) + if supported { + return err + } + + br := s.br + seqs := s.nSeqs + startSize := len(s.out) + // Grab full sizes tables, to avoid bounds checks. + llTable, mlTable, ofTable := s.litLengths.fse.dt[:maxTablesize], s.matchLengths.fse.dt[:maxTablesize], s.offsets.fse.dt[:maxTablesize] + llState, mlState, ofState := s.litLengths.state.state, s.matchLengths.state.state, s.offsets.state.state + out := s.out + maxBlockSize := min(s.windowSize, maxCompressedBlockSize) + + if debugDecoder { + println("decodeSync: decoding", seqs, "sequences", br.remain(), "bits remain on stream") + } + for i := seqs - 1; i >= 0; i-- { + if br.overread() { + printf("reading sequence %d, exceeded available data. Overread by %d\n", seqs-i, -br.remain()) + return io.ErrUnexpectedEOF + } + var ll, mo, ml int + if br.cursor > 4+((maxOffsetBits+16+16)>>3) { + // inlined function: + // ll, mo, ml = s.nextFast(br, llState, mlState, ofState) + + // Final will not read from stream. + var llB, mlB, moB uint8 + ll, llB = llState.final() + ml, mlB = mlState.final() + mo, moB = ofState.final() + + // extra bits are stored in reverse order. + br.fillFast() + mo += br.getBits(moB) + if s.maxBits > 32 { + br.fillFast() + } + ml += br.getBits(mlB) + ll += br.getBits(llB) + + if moB > 1 { + s.prevOffset[2] = s.prevOffset[1] + s.prevOffset[1] = s.prevOffset[0] + s.prevOffset[0] = mo + } else { + // mo = s.adjustOffset(mo, ll, moB) + // Inlined for rather big speedup + if ll == 0 { + // There is an exception though, when current sequence's literals_length = 0. + // In this case, repeated offsets are shifted by one, so an offset_value of 1 means Repeated_Offset2, + // an offset_value of 2 means Repeated_Offset3, and an offset_value of 3 means Repeated_Offset1 - 1_byte. + mo++ + } + + if mo == 0 { + mo = s.prevOffset[0] + } else { + var temp int + if mo == 3 { + temp = s.prevOffset[0] - 1 + } else { + temp = s.prevOffset[mo] + } + + if temp == 0 { + // 0 is not valid; input is corrupted; force offset to 1 + println("WARNING: temp was 0") + temp = 1 + } + + if mo != 1 { + s.prevOffset[2] = s.prevOffset[1] + } + s.prevOffset[1] = s.prevOffset[0] + s.prevOffset[0] = temp + mo = temp + } + } + br.fillFast() + } else { + ll, mo, ml = s.next(br, llState, mlState, ofState) + br.fill() + } + + if debugSequences { + println("Seq", seqs-i-1, "Litlen:", ll, "mo:", mo, "(abs) ml:", ml) + } + + if ll > len(s.literals) { + return fmt.Errorf("unexpected literal count, want %d bytes, but only %d is available", ll, len(s.literals)) + } + size := ll + ml + len(out) + if size-startSize > maxBlockSize { + return fmt.Errorf("output bigger than max block size (%d)", maxBlockSize) + } + if size > cap(out) { + // Not enough size, which can happen under high volume block streaming conditions + // but could be if destination slice is too small for sync operations. + // over-allocating here can create a large amount of GC pressure so we try to keep + // it as contained as possible + used := len(out) - startSize + addBytes := 256 + ll + ml + used>>2 + // Clamp to max block size. + if used+addBytes > maxBlockSize { + addBytes = maxBlockSize - used + } + out = append(out, make([]byte, addBytes)...) + out = out[:len(out)-addBytes] + } + if ml > maxMatchLen { + return fmt.Errorf("match len (%d) bigger than max allowed length", ml) + } + + // Add literals + out = append(out, s.literals[:ll]...) + s.literals = s.literals[ll:] + + if mo == 0 && ml > 0 { + return fmt.Errorf("zero matchoff and matchlen (%d) > 0", ml) + } + + if mo > len(out)+len(hist) || mo > s.windowSize { + if len(s.dict) == 0 { + return fmt.Errorf("match offset (%d) bigger than current history (%d)", mo, len(out)+len(hist)-startSize) + } + + // we may be in dictionary. + dictO := len(s.dict) - (mo - (len(out) + len(hist))) + if dictO < 0 || dictO >= len(s.dict) { + return fmt.Errorf("match offset (%d) bigger than current history (%d)", mo, len(out)+len(hist)-startSize) + } + end := dictO + ml + if end > len(s.dict) { + out = append(out, s.dict[dictO:]...) + ml -= len(s.dict) - dictO + } else { + out = append(out, s.dict[dictO:end]...) + mo = 0 + ml = 0 + } + } + + // Copy from history. + // TODO: Blocks without history could be made to ignore this completely. + if v := mo - len(out); v > 0 { + // v is the start position in history from end. + start := len(hist) - v + if ml > v { + // Some goes into current block. + // Copy remainder of history + out = append(out, hist[start:]...) + ml -= v + } else { + out = append(out, hist[start:start+ml]...) + ml = 0 + } + } + // We must be in current buffer now + if ml > 0 { + start := len(out) - mo + if ml <= len(out)-start { + // No overlap + out = append(out, out[start:start+ml]...) + } else { + // Overlapping copy + // Extend destination slice and copy one byte at the time. + out = out[:len(out)+ml] + src := out[start : start+ml] + // Destination is the space we just added. + dst := out[len(out)-ml:] + dst = dst[:len(src)] + for i := range src { + dst[i] = src[i] + } + } + } + if i == 0 { + // This is the last sequence, so we shouldn't update state. + break + } + + // Manually inlined, ~ 5-20% faster + // Update all 3 states at once. Approx 20% faster. + nBits := llState.nbBits() + mlState.nbBits() + ofState.nbBits() + if nBits == 0 { + llState = llTable[llState.newState()&maxTableMask] + mlState = mlTable[mlState.newState()&maxTableMask] + ofState = ofTable[ofState.newState()&maxTableMask] + } else { + bits := br.get32BitsFast(nBits) + + lowBits := uint16(bits >> ((ofState.nbBits() + mlState.nbBits()) & 31)) + llState = llTable[(llState.newState()+lowBits)&maxTableMask] + + lowBits = uint16(bits >> (ofState.nbBits() & 31)) + lowBits &= bitMask[mlState.nbBits()&15] + mlState = mlTable[(mlState.newState()+lowBits)&maxTableMask] + + lowBits = uint16(bits) & bitMask[ofState.nbBits()&15] + ofState = ofTable[(ofState.newState()+lowBits)&maxTableMask] + } + } + + if size := len(s.literals) + len(out) - startSize; size > maxBlockSize { + return fmt.Errorf("output bigger than max block size (%d)", maxBlockSize) + } + + // Add final literals + s.out = append(out, s.literals...) + return br.close() +} + +var bitMask [16]uint16 + +func init() { + for i := range bitMask[:] { + bitMask[i] = uint16((1 << uint(i)) - 1) + } +} + +func (s *sequenceDecs) next(br *bitReader, llState, mlState, ofState decSymbol) (ll, mo, ml int) { + // Final will not read from stream. + ll, llB := llState.final() + ml, mlB := mlState.final() + mo, moB := ofState.final() + + // extra bits are stored in reverse order. + br.fill() + mo += br.getBits(moB) + if s.maxBits > 32 { + br.fill() + } + // matchlength+literal length, max 32 bits + ml += br.getBits(mlB) + ll += br.getBits(llB) + mo = s.adjustOffset(mo, ll, moB) + return +} + +func (s *sequenceDecs) adjustOffset(offset, litLen int, offsetB uint8) int { + if offsetB > 1 { + s.prevOffset[2] = s.prevOffset[1] + s.prevOffset[1] = s.prevOffset[0] + s.prevOffset[0] = offset + return offset + } + + if litLen == 0 { + // There is an exception though, when current sequence's literals_length = 0. + // In this case, repeated offsets are shifted by one, so an offset_value of 1 means Repeated_Offset2, + // an offset_value of 2 means Repeated_Offset3, and an offset_value of 3 means Repeated_Offset1 - 1_byte. + offset++ + } + + if offset == 0 { + return s.prevOffset[0] + } + var temp int + if offset == 3 { + temp = s.prevOffset[0] - 1 + } else { + temp = s.prevOffset[offset] + } + + if temp == 0 { + // 0 is not valid; input is corrupted; force offset to 1 + println("temp was 0") + temp = 1 + } + + if offset != 1 { + s.prevOffset[2] = s.prevOffset[1] + } + s.prevOffset[1] = s.prevOffset[0] + s.prevOffset[0] = temp + return temp +} diff --git a/vendor/github.com/klauspost/compress/zstd/seqdec_amd64.go b/vendor/github.com/klauspost/compress/zstd/seqdec_amd64.go new file mode 100644 index 000000000..18c3703dd --- /dev/null +++ b/vendor/github.com/klauspost/compress/zstd/seqdec_amd64.go @@ -0,0 +1,387 @@ +//go:build amd64 && !appengine && !noasm && gc + +package zstd + +import ( + "fmt" + "io" + + "github.com/klauspost/compress/internal/cpuinfo" +) + +type decodeSyncAsmContext struct { + llTable []decSymbol + mlTable []decSymbol + ofTable []decSymbol + llState uint64 + mlState uint64 + ofState uint64 + iteration int + litRemain int + out []byte + outPosition int + literals []byte + litPosition int + history []byte + windowSize int + ll int // set on error (not for all errors, please refer to _generate/gen.go) + ml int // set on error (not for all errors, please refer to _generate/gen.go) + mo int // set on error (not for all errors, please refer to _generate/gen.go) +} + +// sequenceDecs_decodeSync_amd64 implements the main loop of sequenceDecs.decodeSync in x86 asm. +// +// Please refer to seqdec_generic.go for the reference implementation. +// +//go:noescape +func sequenceDecs_decodeSync_amd64(s *sequenceDecs, br *bitReader, ctx *decodeSyncAsmContext) int + +// sequenceDecs_decodeSync_bmi2 implements the main loop of sequenceDecs.decodeSync in x86 asm with BMI2 extensions. +// +//go:noescape +func sequenceDecs_decodeSync_bmi2(s *sequenceDecs, br *bitReader, ctx *decodeSyncAsmContext) int + +// sequenceDecs_decodeSync_safe_amd64 does the same as above, but does not write more than output buffer. +// +//go:noescape +func sequenceDecs_decodeSync_safe_amd64(s *sequenceDecs, br *bitReader, ctx *decodeSyncAsmContext) int + +// sequenceDecs_decodeSync_safe_bmi2 does the same as above, but does not write more than output buffer. +// +//go:noescape +func sequenceDecs_decodeSync_safe_bmi2(s *sequenceDecs, br *bitReader, ctx *decodeSyncAsmContext) int + +// decode sequences from the stream with the provided history but without a dictionary. +func (s *sequenceDecs) decodeSyncSimple(hist []byte) (bool, error) { + if len(s.dict) > 0 { + return false, nil + } + if s.maxSyncLen == 0 && cap(s.out)-len(s.out) < maxCompressedBlockSize { + return false, nil + } + + // FIXME: Using unsafe memory copies leads to rare, random crashes + // with fuzz testing. It is therefore disabled for now. + const useSafe = true + /* + useSafe := false + if s.maxSyncLen == 0 && cap(s.out)-len(s.out) < maxCompressedBlockSizeAlloc { + useSafe = true + } + if s.maxSyncLen > 0 && cap(s.out)-len(s.out)-compressedBlockOverAlloc < int(s.maxSyncLen) { + useSafe = true + } + if cap(s.literals) < len(s.literals)+compressedBlockOverAlloc { + useSafe = true + } + */ + + br := s.br + + maxBlockSize := min(s.windowSize, maxCompressedBlockSize) + + ctx := decodeSyncAsmContext{ + llTable: s.litLengths.fse.dt[:maxTablesize], + mlTable: s.matchLengths.fse.dt[:maxTablesize], + ofTable: s.offsets.fse.dt[:maxTablesize], + llState: uint64(s.litLengths.state.state), + mlState: uint64(s.matchLengths.state.state), + ofState: uint64(s.offsets.state.state), + iteration: s.nSeqs - 1, + litRemain: len(s.literals), + out: s.out, + outPosition: len(s.out), + literals: s.literals, + windowSize: s.windowSize, + history: hist, + } + + s.seqSize = 0 + startSize := len(s.out) + + var errCode int + if cpuinfo.HasBMI2() { + if useSafe { + errCode = sequenceDecs_decodeSync_safe_bmi2(s, br, &ctx) + } else { + errCode = sequenceDecs_decodeSync_bmi2(s, br, &ctx) + } + } else { + if useSafe { + errCode = sequenceDecs_decodeSync_safe_amd64(s, br, &ctx) + } else { + errCode = sequenceDecs_decodeSync_amd64(s, br, &ctx) + } + } + switch errCode { + case noError: + break + + case errorMatchLenOfsMismatch: + return true, fmt.Errorf("zero matchoff and matchlen (%d) > 0", ctx.ml) + + case errorMatchLenTooBig: + return true, fmt.Errorf("match len (%d) bigger than max allowed length", ctx.ml) + + case errorMatchOffTooBig: + return true, fmt.Errorf("match offset (%d) bigger than current history (%d)", + ctx.mo, ctx.outPosition+len(hist)-startSize) + + case errorNotEnoughLiterals: + return true, fmt.Errorf("unexpected literal count, want %d bytes, but only %d is available", + ctx.ll, ctx.litRemain+ctx.ll) + + case errorOverread: + return true, io.ErrUnexpectedEOF + + case errorNotEnoughSpace: + size := ctx.outPosition + ctx.ll + ctx.ml + if debugDecoder { + println("msl:", s.maxSyncLen, "cap", cap(s.out), "bef:", startSize, "sz:", size-startSize, "mbs:", maxBlockSize, "outsz:", cap(s.out)-startSize) + } + return true, fmt.Errorf("output bigger than max block size (%d)", maxBlockSize) + + default: + return true, fmt.Errorf("sequenceDecs_decode returned erroneous code %d", errCode) + } + + s.seqSize += ctx.litRemain + if s.seqSize > maxBlockSize { + return true, fmt.Errorf("output bigger than max block size (%d)", maxBlockSize) + } + err := br.close() + if err != nil { + printf("Closing sequences: %v, %+v\n", err, *br) + return true, err + } + + s.literals = s.literals[ctx.litPosition:] + t := ctx.outPosition + s.out = s.out[:t] + + // Add final literals + s.out = append(s.out, s.literals...) + if debugDecoder { + t += len(s.literals) + if t != len(s.out) { + panic(fmt.Errorf("length mismatch, want %d, got %d", len(s.out), t)) + } + } + + return true, nil +} + +// -------------------------------------------------------------------------------- + +type decodeAsmContext struct { + llTable []decSymbol + mlTable []decSymbol + ofTable []decSymbol + llState uint64 + mlState uint64 + ofState uint64 + iteration int + seqs []seqVals + litRemain int +} + +const noError = 0 + +// error reported when mo == 0 && ml > 0 +const errorMatchLenOfsMismatch = 1 + +// error reported when ml > maxMatchLen +const errorMatchLenTooBig = 2 + +// error reported when mo > available history or mo > s.windowSize +const errorMatchOffTooBig = 3 + +// error reported when the sum of literal lengths exeeceds the literal buffer size +const errorNotEnoughLiterals = 4 + +// error reported when capacity of `out` is too small +const errorNotEnoughSpace = 5 + +// error reported when bits are overread. +const errorOverread = 6 + +// sequenceDecs_decode implements the main loop of sequenceDecs in x86 asm. +// +// Please refer to seqdec_generic.go for the reference implementation. +// +//go:noescape +func sequenceDecs_decode_amd64(s *sequenceDecs, br *bitReader, ctx *decodeAsmContext) int + +// sequenceDecs_decode implements the main loop of sequenceDecs in x86 asm. +// +// Please refer to seqdec_generic.go for the reference implementation. +// +//go:noescape +func sequenceDecs_decode_56_amd64(s *sequenceDecs, br *bitReader, ctx *decodeAsmContext) int + +// sequenceDecs_decode implements the main loop of sequenceDecs in x86 asm with BMI2 extensions. +// +//go:noescape +func sequenceDecs_decode_bmi2(s *sequenceDecs, br *bitReader, ctx *decodeAsmContext) int + +// sequenceDecs_decode implements the main loop of sequenceDecs in x86 asm with BMI2 extensions. +// +//go:noescape +func sequenceDecs_decode_56_bmi2(s *sequenceDecs, br *bitReader, ctx *decodeAsmContext) int + +// decode sequences from the stream without the provided history. +func (s *sequenceDecs) decode(seqs []seqVals) error { + br := s.br + + maxBlockSize := min(s.windowSize, maxCompressedBlockSize) + + ctx := decodeAsmContext{ + llTable: s.litLengths.fse.dt[:maxTablesize], + mlTable: s.matchLengths.fse.dt[:maxTablesize], + ofTable: s.offsets.fse.dt[:maxTablesize], + llState: uint64(s.litLengths.state.state), + mlState: uint64(s.matchLengths.state.state), + ofState: uint64(s.offsets.state.state), + seqs: seqs, + iteration: len(seqs) - 1, + litRemain: len(s.literals), + } + + if debugDecoder { + println("decode: decoding", len(seqs), "sequences", br.remain(), "bits remain on stream") + } + + s.seqSize = 0 + lte56bits := s.maxBits+s.offsets.fse.actualTableLog+s.matchLengths.fse.actualTableLog+s.litLengths.fse.actualTableLog <= 56 + var errCode int + if cpuinfo.HasBMI2() { + if lte56bits { + errCode = sequenceDecs_decode_56_bmi2(s, br, &ctx) + } else { + errCode = sequenceDecs_decode_bmi2(s, br, &ctx) + } + } else { + if lte56bits { + errCode = sequenceDecs_decode_56_amd64(s, br, &ctx) + } else { + errCode = sequenceDecs_decode_amd64(s, br, &ctx) + } + } + if errCode != 0 { + i := len(seqs) - ctx.iteration - 1 + switch errCode { + case errorMatchLenOfsMismatch: + ml := ctx.seqs[i].ml + return fmt.Errorf("zero matchoff and matchlen (%d) > 0", ml) + + case errorMatchLenTooBig: + ml := ctx.seqs[i].ml + return fmt.Errorf("match len (%d) bigger than max allowed length", ml) + + case errorNotEnoughLiterals: + ll := ctx.seqs[i].ll + return fmt.Errorf("unexpected literal count, want %d bytes, but only %d is available", ll, ctx.litRemain+ll) + case errorOverread: + return io.ErrUnexpectedEOF + } + + return fmt.Errorf("sequenceDecs_decode_amd64 returned erroneous code %d", errCode) + } + + if ctx.litRemain < 0 { + return fmt.Errorf("literal count is too big: total available %d, total requested %d", + len(s.literals), len(s.literals)-ctx.litRemain) + } + + s.seqSize += ctx.litRemain + if s.seqSize > maxBlockSize { + return fmt.Errorf("output bigger than max block size (%d)", maxBlockSize) + } + if debugDecoder { + println("decode: ", br.remain(), "bits remain on stream. code:", errCode) + } + err := br.close() + if err != nil { + printf("Closing sequences: %v, %+v\n", err, *br) + } + return err +} + +// -------------------------------------------------------------------------------- + +type executeAsmContext struct { + seqs []seqVals + seqIndex int + out []byte + history []byte + literals []byte + outPosition int + litPosition int + windowSize int +} + +// sequenceDecs_executeSimple_amd64 implements the main loop of sequenceDecs.executeSimple in x86 asm. +// +// Returns false if a match offset is too big. +// +// Please refer to seqdec_generic.go for the reference implementation. +// +//go:noescape +func sequenceDecs_executeSimple_amd64(ctx *executeAsmContext) bool + +// Same as above, but with safe memcopies +// +//go:noescape +func sequenceDecs_executeSimple_safe_amd64(ctx *executeAsmContext) bool + +// executeSimple handles cases when dictionary is not used. +func (s *sequenceDecs) executeSimple(seqs []seqVals, hist []byte) error { + // Ensure we have enough output size... + if len(s.out)+s.seqSize+compressedBlockOverAlloc > cap(s.out) { + addBytes := s.seqSize + len(s.out) + compressedBlockOverAlloc + s.out = append(s.out, make([]byte, addBytes)...) + s.out = s.out[:len(s.out)-addBytes] + } + + if debugDecoder { + printf("Execute %d seqs with literals: %d into %d bytes\n", len(seqs), len(s.literals), s.seqSize) + } + + var t = len(s.out) + out := s.out[:t+s.seqSize] + + ctx := executeAsmContext{ + seqs: seqs, + seqIndex: 0, + out: out, + history: hist, + outPosition: t, + litPosition: 0, + literals: s.literals, + windowSize: s.windowSize, + } + var ok bool + if cap(s.literals) < len(s.literals)+compressedBlockOverAlloc { + ok = sequenceDecs_executeSimple_safe_amd64(&ctx) + } else { + ok = sequenceDecs_executeSimple_amd64(&ctx) + } + if !ok { + return fmt.Errorf("match offset (%d) bigger than current history (%d)", + seqs[ctx.seqIndex].mo, ctx.outPosition+len(hist)) + } + s.literals = s.literals[ctx.litPosition:] + t = ctx.outPosition + + // Add final literals + copy(out[t:], s.literals) + if debugDecoder { + t += len(s.literals) + if t != len(out) { + panic(fmt.Errorf("length mismatch, want %d, got %d, ss: %d", len(out), t, s.seqSize)) + } + } + s.out = out + + return nil +} diff --git a/vendor/github.com/klauspost/compress/zstd/seqdec_amd64.s b/vendor/github.com/klauspost/compress/zstd/seqdec_amd64.s new file mode 100644 index 000000000..a708ca6d3 --- /dev/null +++ b/vendor/github.com/klauspost/compress/zstd/seqdec_amd64.s @@ -0,0 +1,4151 @@ +// Code generated by command: go run gen.go -out ../seqdec_amd64.s -pkg=zstd. DO NOT EDIT. + +//go:build !appengine && !noasm && gc && !noasm + +// func sequenceDecs_decode_amd64(s *sequenceDecs, br *bitReader, ctx *decodeAsmContext) int +// Requires: CMOV +TEXT ·sequenceDecs_decode_amd64(SB), $8-32 + MOVQ br+8(FP), CX + MOVQ 24(CX), DX + MOVBQZX 40(CX), BX + MOVQ (CX), AX + MOVQ 32(CX), SI + ADDQ SI, AX + MOVQ AX, (SP) + MOVQ ctx+16(FP), AX + MOVQ 72(AX), DI + MOVQ 80(AX), R8 + MOVQ 88(AX), R9 + MOVQ 104(AX), R10 + MOVQ s+0(FP), AX + MOVQ 144(AX), R11 + MOVQ 152(AX), R12 + MOVQ 160(AX), R13 + +sequenceDecs_decode_amd64_main_loop: + MOVQ (SP), R14 + + // Fill bitreader to have enough for the offset and match length. + CMPQ SI, $0x08 + JL sequenceDecs_decode_amd64_fill_byte_by_byte + MOVQ BX, AX + SHRQ $0x03, AX + SUBQ AX, R14 + MOVQ (R14), DX + SUBQ AX, SI + ANDQ $0x07, BX + JMP sequenceDecs_decode_amd64_fill_end + +sequenceDecs_decode_amd64_fill_byte_by_byte: + CMPQ SI, $0x00 + JLE sequenceDecs_decode_amd64_fill_check_overread + CMPQ BX, $0x07 + JLE sequenceDecs_decode_amd64_fill_end + SHLQ $0x08, DX + SUBQ $0x01, R14 + SUBQ $0x01, SI + SUBQ $0x08, BX + MOVBQZX (R14), AX + ORQ AX, DX + JMP sequenceDecs_decode_amd64_fill_byte_by_byte + +sequenceDecs_decode_amd64_fill_check_overread: + CMPQ BX, $0x40 + JA error_overread + +sequenceDecs_decode_amd64_fill_end: + // Update offset + MOVQ R9, AX + MOVQ BX, CX + MOVQ DX, R15 + SHLQ CL, R15 + MOVB AH, CL + SHRQ $0x20, AX + TESTQ CX, CX + JZ sequenceDecs_decode_amd64_of_update_zero + ADDQ CX, BX + CMPQ BX, $0x40 + JA sequenceDecs_decode_amd64_of_update_zero + CMPQ CX, $0x40 + JAE sequenceDecs_decode_amd64_of_update_zero + NEGQ CX + SHRQ CL, R15 + ADDQ R15, AX + +sequenceDecs_decode_amd64_of_update_zero: + MOVQ AX, 16(R10) + + // Update match length + MOVQ R8, AX + MOVQ BX, CX + MOVQ DX, R15 + SHLQ CL, R15 + MOVB AH, CL + SHRQ $0x20, AX + TESTQ CX, CX + JZ sequenceDecs_decode_amd64_ml_update_zero + ADDQ CX, BX + CMPQ BX, $0x40 + JA sequenceDecs_decode_amd64_ml_update_zero + CMPQ CX, $0x40 + JAE sequenceDecs_decode_amd64_ml_update_zero + NEGQ CX + SHRQ CL, R15 + ADDQ R15, AX + +sequenceDecs_decode_amd64_ml_update_zero: + MOVQ AX, 8(R10) + + // Fill bitreader to have enough for the remaining + CMPQ SI, $0x08 + JL sequenceDecs_decode_amd64_fill_2_byte_by_byte + MOVQ BX, AX + SHRQ $0x03, AX + SUBQ AX, R14 + MOVQ (R14), DX + SUBQ AX, SI + ANDQ $0x07, BX + JMP sequenceDecs_decode_amd64_fill_2_end + +sequenceDecs_decode_amd64_fill_2_byte_by_byte: + CMPQ SI, $0x00 + JLE sequenceDecs_decode_amd64_fill_2_check_overread + CMPQ BX, $0x07 + JLE sequenceDecs_decode_amd64_fill_2_end + SHLQ $0x08, DX + SUBQ $0x01, R14 + SUBQ $0x01, SI + SUBQ $0x08, BX + MOVBQZX (R14), AX + ORQ AX, DX + JMP sequenceDecs_decode_amd64_fill_2_byte_by_byte + +sequenceDecs_decode_amd64_fill_2_check_overread: + CMPQ BX, $0x40 + JA error_overread + +sequenceDecs_decode_amd64_fill_2_end: + // Update literal length + MOVQ DI, AX + MOVQ BX, CX + MOVQ DX, R15 + SHLQ CL, R15 + MOVB AH, CL + SHRQ $0x20, AX + TESTQ CX, CX + JZ sequenceDecs_decode_amd64_ll_update_zero + ADDQ CX, BX + CMPQ BX, $0x40 + JA sequenceDecs_decode_amd64_ll_update_zero + CMPQ CX, $0x40 + JAE sequenceDecs_decode_amd64_ll_update_zero + NEGQ CX + SHRQ CL, R15 + ADDQ R15, AX + +sequenceDecs_decode_amd64_ll_update_zero: + MOVQ AX, (R10) + + // Fill bitreader for state updates + MOVQ R14, (SP) + MOVQ R9, AX + SHRQ $0x08, AX + MOVBQZX AL, AX + MOVQ ctx+16(FP), CX + CMPQ 96(CX), $0x00 + JZ sequenceDecs_decode_amd64_skip_update + + // Update Literal Length State + MOVBQZX DI, R14 + SHRL $0x10, DI + LEAQ (BX)(R14*1), CX + MOVQ DX, R15 + MOVQ CX, BX + ROLQ CL, R15 + MOVL $0x00000001, BP + MOVB R14, CL + SHLL CL, BP + DECL BP + ANDQ BP, R15 + ADDQ R15, DI + + // Load ctx.llTable + MOVQ ctx+16(FP), CX + MOVQ (CX), CX + MOVQ (CX)(DI*8), DI + + // Update Match Length State + MOVBQZX R8, R14 + SHRL $0x10, R8 + LEAQ (BX)(R14*1), CX + MOVQ DX, R15 + MOVQ CX, BX + ROLQ CL, R15 + MOVL $0x00000001, BP + MOVB R14, CL + SHLL CL, BP + DECL BP + ANDQ BP, R15 + ADDQ R15, R8 + + // Load ctx.mlTable + MOVQ ctx+16(FP), CX + MOVQ 24(CX), CX + MOVQ (CX)(R8*8), R8 + + // Update Offset State + MOVBQZX R9, R14 + SHRL $0x10, R9 + LEAQ (BX)(R14*1), CX + MOVQ DX, R15 + MOVQ CX, BX + ROLQ CL, R15 + MOVL $0x00000001, BP + MOVB R14, CL + SHLL CL, BP + DECL BP + ANDQ BP, R15 + ADDQ R15, R9 + + // Load ctx.ofTable + MOVQ ctx+16(FP), CX + MOVQ 48(CX), CX + MOVQ (CX)(R9*8), R9 + +sequenceDecs_decode_amd64_skip_update: + // Adjust offset + MOVQ 16(R10), CX + CMPQ AX, $0x01 + JBE sequenceDecs_decode_amd64_adjust_offsetB_1_or_0 + MOVQ R12, R13 + MOVQ R11, R12 + MOVQ CX, R11 + JMP sequenceDecs_decode_amd64_after_adjust + +sequenceDecs_decode_amd64_adjust_offsetB_1_or_0: + CMPQ (R10), $0x00000000 + JNE sequenceDecs_decode_amd64_adjust_offset_maybezero + INCQ CX + JMP sequenceDecs_decode_amd64_adjust_offset_nonzero + +sequenceDecs_decode_amd64_adjust_offset_maybezero: + TESTQ CX, CX + JNZ sequenceDecs_decode_amd64_adjust_offset_nonzero + MOVQ R11, CX + JMP sequenceDecs_decode_amd64_after_adjust + +sequenceDecs_decode_amd64_adjust_offset_nonzero: + CMPQ CX, $0x01 + JB sequenceDecs_decode_amd64_adjust_zero + JEQ sequenceDecs_decode_amd64_adjust_one + CMPQ CX, $0x02 + JA sequenceDecs_decode_amd64_adjust_three + JMP sequenceDecs_decode_amd64_adjust_two + +sequenceDecs_decode_amd64_adjust_zero: + MOVQ R11, AX + JMP sequenceDecs_decode_amd64_adjust_test_temp_valid + +sequenceDecs_decode_amd64_adjust_one: + MOVQ R12, AX + JMP sequenceDecs_decode_amd64_adjust_test_temp_valid + +sequenceDecs_decode_amd64_adjust_two: + MOVQ R13, AX + JMP sequenceDecs_decode_amd64_adjust_test_temp_valid + +sequenceDecs_decode_amd64_adjust_three: + LEAQ -1(R11), AX + +sequenceDecs_decode_amd64_adjust_test_temp_valid: + TESTQ AX, AX + JNZ sequenceDecs_decode_amd64_adjust_temp_valid + MOVQ $0x00000001, AX + +sequenceDecs_decode_amd64_adjust_temp_valid: + CMPQ CX, $0x01 + CMOVQNE R12, R13 + MOVQ R11, R12 + MOVQ AX, R11 + MOVQ AX, CX + +sequenceDecs_decode_amd64_after_adjust: + MOVQ CX, 16(R10) + + // Check values + MOVQ 8(R10), AX + MOVQ (R10), R14 + LEAQ (AX)(R14*1), R15 + MOVQ s+0(FP), BP + ADDQ R15, 256(BP) + MOVQ ctx+16(FP), R15 + SUBQ R14, 128(R15) + JS error_not_enough_literals + CMPQ AX, $0x00020002 + JA sequenceDecs_decode_amd64_error_match_len_too_big + TESTQ CX, CX + JNZ sequenceDecs_decode_amd64_match_len_ofs_ok + TESTQ AX, AX + JNZ sequenceDecs_decode_amd64_error_match_len_ofs_mismatch + +sequenceDecs_decode_amd64_match_len_ofs_ok: + ADDQ $0x18, R10 + MOVQ ctx+16(FP), AX + DECQ 96(AX) + JNS sequenceDecs_decode_amd64_main_loop + MOVQ s+0(FP), AX + MOVQ R11, 144(AX) + MOVQ R12, 152(AX) + MOVQ R13, 160(AX) + MOVQ br+8(FP), AX + MOVQ DX, 24(AX) + MOVB BL, 40(AX) + MOVQ SI, 32(AX) + + // Return success + MOVQ $0x00000000, ret+24(FP) + RET + + // Return with match length error +sequenceDecs_decode_amd64_error_match_len_ofs_mismatch: + MOVQ $0x00000001, ret+24(FP) + RET + + // Return with match too long error +sequenceDecs_decode_amd64_error_match_len_too_big: + MOVQ $0x00000002, ret+24(FP) + RET + + // Return with match offset too long error + MOVQ $0x00000003, ret+24(FP) + RET + + // Return with not enough literals error +error_not_enough_literals: + MOVQ $0x00000004, ret+24(FP) + RET + + // Return with overread error +error_overread: + MOVQ $0x00000006, ret+24(FP) + RET + +// func sequenceDecs_decode_56_amd64(s *sequenceDecs, br *bitReader, ctx *decodeAsmContext) int +// Requires: CMOV +TEXT ·sequenceDecs_decode_56_amd64(SB), $8-32 + MOVQ br+8(FP), CX + MOVQ 24(CX), DX + MOVBQZX 40(CX), BX + MOVQ (CX), AX + MOVQ 32(CX), SI + ADDQ SI, AX + MOVQ AX, (SP) + MOVQ ctx+16(FP), AX + MOVQ 72(AX), DI + MOVQ 80(AX), R8 + MOVQ 88(AX), R9 + MOVQ 104(AX), R10 + MOVQ s+0(FP), AX + MOVQ 144(AX), R11 + MOVQ 152(AX), R12 + MOVQ 160(AX), R13 + +sequenceDecs_decode_56_amd64_main_loop: + MOVQ (SP), R14 + + // Fill bitreader to have enough for the offset and match length. + CMPQ SI, $0x08 + JL sequenceDecs_decode_56_amd64_fill_byte_by_byte + MOVQ BX, AX + SHRQ $0x03, AX + SUBQ AX, R14 + MOVQ (R14), DX + SUBQ AX, SI + ANDQ $0x07, BX + JMP sequenceDecs_decode_56_amd64_fill_end + +sequenceDecs_decode_56_amd64_fill_byte_by_byte: + CMPQ SI, $0x00 + JLE sequenceDecs_decode_56_amd64_fill_check_overread + CMPQ BX, $0x07 + JLE sequenceDecs_decode_56_amd64_fill_end + SHLQ $0x08, DX + SUBQ $0x01, R14 + SUBQ $0x01, SI + SUBQ $0x08, BX + MOVBQZX (R14), AX + ORQ AX, DX + JMP sequenceDecs_decode_56_amd64_fill_byte_by_byte + +sequenceDecs_decode_56_amd64_fill_check_overread: + CMPQ BX, $0x40 + JA error_overread + +sequenceDecs_decode_56_amd64_fill_end: + // Update offset + MOVQ R9, AX + MOVQ BX, CX + MOVQ DX, R15 + SHLQ CL, R15 + MOVB AH, CL + SHRQ $0x20, AX + TESTQ CX, CX + JZ sequenceDecs_decode_56_amd64_of_update_zero + ADDQ CX, BX + CMPQ BX, $0x40 + JA sequenceDecs_decode_56_amd64_of_update_zero + CMPQ CX, $0x40 + JAE sequenceDecs_decode_56_amd64_of_update_zero + NEGQ CX + SHRQ CL, R15 + ADDQ R15, AX + +sequenceDecs_decode_56_amd64_of_update_zero: + MOVQ AX, 16(R10) + + // Update match length + MOVQ R8, AX + MOVQ BX, CX + MOVQ DX, R15 + SHLQ CL, R15 + MOVB AH, CL + SHRQ $0x20, AX + TESTQ CX, CX + JZ sequenceDecs_decode_56_amd64_ml_update_zero + ADDQ CX, BX + CMPQ BX, $0x40 + JA sequenceDecs_decode_56_amd64_ml_update_zero + CMPQ CX, $0x40 + JAE sequenceDecs_decode_56_amd64_ml_update_zero + NEGQ CX + SHRQ CL, R15 + ADDQ R15, AX + +sequenceDecs_decode_56_amd64_ml_update_zero: + MOVQ AX, 8(R10) + + // Update literal length + MOVQ DI, AX + MOVQ BX, CX + MOVQ DX, R15 + SHLQ CL, R15 + MOVB AH, CL + SHRQ $0x20, AX + TESTQ CX, CX + JZ sequenceDecs_decode_56_amd64_ll_update_zero + ADDQ CX, BX + CMPQ BX, $0x40 + JA sequenceDecs_decode_56_amd64_ll_update_zero + CMPQ CX, $0x40 + JAE sequenceDecs_decode_56_amd64_ll_update_zero + NEGQ CX + SHRQ CL, R15 + ADDQ R15, AX + +sequenceDecs_decode_56_amd64_ll_update_zero: + MOVQ AX, (R10) + + // Fill bitreader for state updates + MOVQ R14, (SP) + MOVQ R9, AX + SHRQ $0x08, AX + MOVBQZX AL, AX + MOVQ ctx+16(FP), CX + CMPQ 96(CX), $0x00 + JZ sequenceDecs_decode_56_amd64_skip_update + + // Update Literal Length State + MOVBQZX DI, R14 + SHRL $0x10, DI + LEAQ (BX)(R14*1), CX + MOVQ DX, R15 + MOVQ CX, BX + ROLQ CL, R15 + MOVL $0x00000001, BP + MOVB R14, CL + SHLL CL, BP + DECL BP + ANDQ BP, R15 + ADDQ R15, DI + + // Load ctx.llTable + MOVQ ctx+16(FP), CX + MOVQ (CX), CX + MOVQ (CX)(DI*8), DI + + // Update Match Length State + MOVBQZX R8, R14 + SHRL $0x10, R8 + LEAQ (BX)(R14*1), CX + MOVQ DX, R15 + MOVQ CX, BX + ROLQ CL, R15 + MOVL $0x00000001, BP + MOVB R14, CL + SHLL CL, BP + DECL BP + ANDQ BP, R15 + ADDQ R15, R8 + + // Load ctx.mlTable + MOVQ ctx+16(FP), CX + MOVQ 24(CX), CX + MOVQ (CX)(R8*8), R8 + + // Update Offset State + MOVBQZX R9, R14 + SHRL $0x10, R9 + LEAQ (BX)(R14*1), CX + MOVQ DX, R15 + MOVQ CX, BX + ROLQ CL, R15 + MOVL $0x00000001, BP + MOVB R14, CL + SHLL CL, BP + DECL BP + ANDQ BP, R15 + ADDQ R15, R9 + + // Load ctx.ofTable + MOVQ ctx+16(FP), CX + MOVQ 48(CX), CX + MOVQ (CX)(R9*8), R9 + +sequenceDecs_decode_56_amd64_skip_update: + // Adjust offset + MOVQ 16(R10), CX + CMPQ AX, $0x01 + JBE sequenceDecs_decode_56_amd64_adjust_offsetB_1_or_0 + MOVQ R12, R13 + MOVQ R11, R12 + MOVQ CX, R11 + JMP sequenceDecs_decode_56_amd64_after_adjust + +sequenceDecs_decode_56_amd64_adjust_offsetB_1_or_0: + CMPQ (R10), $0x00000000 + JNE sequenceDecs_decode_56_amd64_adjust_offset_maybezero + INCQ CX + JMP sequenceDecs_decode_56_amd64_adjust_offset_nonzero + +sequenceDecs_decode_56_amd64_adjust_offset_maybezero: + TESTQ CX, CX + JNZ sequenceDecs_decode_56_amd64_adjust_offset_nonzero + MOVQ R11, CX + JMP sequenceDecs_decode_56_amd64_after_adjust + +sequenceDecs_decode_56_amd64_adjust_offset_nonzero: + CMPQ CX, $0x01 + JB sequenceDecs_decode_56_amd64_adjust_zero + JEQ sequenceDecs_decode_56_amd64_adjust_one + CMPQ CX, $0x02 + JA sequenceDecs_decode_56_amd64_adjust_three + JMP sequenceDecs_decode_56_amd64_adjust_two + +sequenceDecs_decode_56_amd64_adjust_zero: + MOVQ R11, AX + JMP sequenceDecs_decode_56_amd64_adjust_test_temp_valid + +sequenceDecs_decode_56_amd64_adjust_one: + MOVQ R12, AX + JMP sequenceDecs_decode_56_amd64_adjust_test_temp_valid + +sequenceDecs_decode_56_amd64_adjust_two: + MOVQ R13, AX + JMP sequenceDecs_decode_56_amd64_adjust_test_temp_valid + +sequenceDecs_decode_56_amd64_adjust_three: + LEAQ -1(R11), AX + +sequenceDecs_decode_56_amd64_adjust_test_temp_valid: + TESTQ AX, AX + JNZ sequenceDecs_decode_56_amd64_adjust_temp_valid + MOVQ $0x00000001, AX + +sequenceDecs_decode_56_amd64_adjust_temp_valid: + CMPQ CX, $0x01 + CMOVQNE R12, R13 + MOVQ R11, R12 + MOVQ AX, R11 + MOVQ AX, CX + +sequenceDecs_decode_56_amd64_after_adjust: + MOVQ CX, 16(R10) + + // Check values + MOVQ 8(R10), AX + MOVQ (R10), R14 + LEAQ (AX)(R14*1), R15 + MOVQ s+0(FP), BP + ADDQ R15, 256(BP) + MOVQ ctx+16(FP), R15 + SUBQ R14, 128(R15) + JS error_not_enough_literals + CMPQ AX, $0x00020002 + JA sequenceDecs_decode_56_amd64_error_match_len_too_big + TESTQ CX, CX + JNZ sequenceDecs_decode_56_amd64_match_len_ofs_ok + TESTQ AX, AX + JNZ sequenceDecs_decode_56_amd64_error_match_len_ofs_mismatch + +sequenceDecs_decode_56_amd64_match_len_ofs_ok: + ADDQ $0x18, R10 + MOVQ ctx+16(FP), AX + DECQ 96(AX) + JNS sequenceDecs_decode_56_amd64_main_loop + MOVQ s+0(FP), AX + MOVQ R11, 144(AX) + MOVQ R12, 152(AX) + MOVQ R13, 160(AX) + MOVQ br+8(FP), AX + MOVQ DX, 24(AX) + MOVB BL, 40(AX) + MOVQ SI, 32(AX) + + // Return success + MOVQ $0x00000000, ret+24(FP) + RET + + // Return with match length error +sequenceDecs_decode_56_amd64_error_match_len_ofs_mismatch: + MOVQ $0x00000001, ret+24(FP) + RET + + // Return with match too long error +sequenceDecs_decode_56_amd64_error_match_len_too_big: + MOVQ $0x00000002, ret+24(FP) + RET + + // Return with match offset too long error + MOVQ $0x00000003, ret+24(FP) + RET + + // Return with not enough literals error +error_not_enough_literals: + MOVQ $0x00000004, ret+24(FP) + RET + + // Return with overread error +error_overread: + MOVQ $0x00000006, ret+24(FP) + RET + +// func sequenceDecs_decode_bmi2(s *sequenceDecs, br *bitReader, ctx *decodeAsmContext) int +// Requires: BMI, BMI2, CMOV +TEXT ·sequenceDecs_decode_bmi2(SB), $8-32 + MOVQ br+8(FP), BX + MOVQ 24(BX), AX + MOVBQZX 40(BX), DX + MOVQ (BX), CX + MOVQ 32(BX), BX + ADDQ BX, CX + MOVQ CX, (SP) + MOVQ ctx+16(FP), CX + MOVQ 72(CX), SI + MOVQ 80(CX), DI + MOVQ 88(CX), R8 + MOVQ 104(CX), R9 + MOVQ s+0(FP), CX + MOVQ 144(CX), R10 + MOVQ 152(CX), R11 + MOVQ 160(CX), R12 + +sequenceDecs_decode_bmi2_main_loop: + MOVQ (SP), R13 + + // Fill bitreader to have enough for the offset and match length. + CMPQ BX, $0x08 + JL sequenceDecs_decode_bmi2_fill_byte_by_byte + MOVQ DX, CX + SHRQ $0x03, CX + SUBQ CX, R13 + MOVQ (R13), AX + SUBQ CX, BX + ANDQ $0x07, DX + JMP sequenceDecs_decode_bmi2_fill_end + +sequenceDecs_decode_bmi2_fill_byte_by_byte: + CMPQ BX, $0x00 + JLE sequenceDecs_decode_bmi2_fill_check_overread + CMPQ DX, $0x07 + JLE sequenceDecs_decode_bmi2_fill_end + SHLQ $0x08, AX + SUBQ $0x01, R13 + SUBQ $0x01, BX + SUBQ $0x08, DX + MOVBQZX (R13), CX + ORQ CX, AX + JMP sequenceDecs_decode_bmi2_fill_byte_by_byte + +sequenceDecs_decode_bmi2_fill_check_overread: + CMPQ DX, $0x40 + JA error_overread + +sequenceDecs_decode_bmi2_fill_end: + // Update offset + MOVQ $0x00000808, CX + BEXTRQ CX, R8, R14 + MOVQ AX, R15 + LEAQ (DX)(R14*1), CX + ROLQ CL, R15 + BZHIQ R14, R15, R15 + MOVQ CX, DX + MOVQ R8, CX + SHRQ $0x20, CX + ADDQ R15, CX + MOVQ CX, 16(R9) + + // Update match length + MOVQ $0x00000808, CX + BEXTRQ CX, DI, R14 + MOVQ AX, R15 + LEAQ (DX)(R14*1), CX + ROLQ CL, R15 + BZHIQ R14, R15, R15 + MOVQ CX, DX + MOVQ DI, CX + SHRQ $0x20, CX + ADDQ R15, CX + MOVQ CX, 8(R9) + + // Fill bitreader to have enough for the remaining + CMPQ BX, $0x08 + JL sequenceDecs_decode_bmi2_fill_2_byte_by_byte + MOVQ DX, CX + SHRQ $0x03, CX + SUBQ CX, R13 + MOVQ (R13), AX + SUBQ CX, BX + ANDQ $0x07, DX + JMP sequenceDecs_decode_bmi2_fill_2_end + +sequenceDecs_decode_bmi2_fill_2_byte_by_byte: + CMPQ BX, $0x00 + JLE sequenceDecs_decode_bmi2_fill_2_check_overread + CMPQ DX, $0x07 + JLE sequenceDecs_decode_bmi2_fill_2_end + SHLQ $0x08, AX + SUBQ $0x01, R13 + SUBQ $0x01, BX + SUBQ $0x08, DX + MOVBQZX (R13), CX + ORQ CX, AX + JMP sequenceDecs_decode_bmi2_fill_2_byte_by_byte + +sequenceDecs_decode_bmi2_fill_2_check_overread: + CMPQ DX, $0x40 + JA error_overread + +sequenceDecs_decode_bmi2_fill_2_end: + // Update literal length + MOVQ $0x00000808, CX + BEXTRQ CX, SI, R14 + MOVQ AX, R15 + LEAQ (DX)(R14*1), CX + ROLQ CL, R15 + BZHIQ R14, R15, R15 + MOVQ CX, DX + MOVQ SI, CX + SHRQ $0x20, CX + ADDQ R15, CX + MOVQ CX, (R9) + + // Fill bitreader for state updates + MOVQ R13, (SP) + MOVQ $0x00000808, CX + BEXTRQ CX, R8, R13 + MOVQ ctx+16(FP), CX + CMPQ 96(CX), $0x00 + JZ sequenceDecs_decode_bmi2_skip_update + LEAQ (SI)(DI*1), R14 + ADDQ R8, R14 + MOVBQZX R14, R14 + LEAQ (DX)(R14*1), CX + MOVQ AX, R15 + MOVQ CX, DX + ROLQ CL, R15 + BZHIQ R14, R15, R15 + + // Update Offset State + BZHIQ R8, R15, CX + SHRXQ R8, R15, R15 + SHRL $0x10, R8 + ADDQ CX, R8 + + // Load ctx.ofTable + MOVQ ctx+16(FP), CX + MOVQ 48(CX), CX + MOVQ (CX)(R8*8), R8 + + // Update Match Length State + BZHIQ DI, R15, CX + SHRXQ DI, R15, R15 + SHRL $0x10, DI + ADDQ CX, DI + + // Load ctx.mlTable + MOVQ ctx+16(FP), CX + MOVQ 24(CX), CX + MOVQ (CX)(DI*8), DI + + // Update Literal Length State + BZHIQ SI, R15, CX + SHRL $0x10, SI + ADDQ CX, SI + + // Load ctx.llTable + MOVQ ctx+16(FP), CX + MOVQ (CX), CX + MOVQ (CX)(SI*8), SI + +sequenceDecs_decode_bmi2_skip_update: + // Adjust offset + MOVQ 16(R9), CX + CMPQ R13, $0x01 + JBE sequenceDecs_decode_bmi2_adjust_offsetB_1_or_0 + MOVQ R11, R12 + MOVQ R10, R11 + MOVQ CX, R10 + JMP sequenceDecs_decode_bmi2_after_adjust + +sequenceDecs_decode_bmi2_adjust_offsetB_1_or_0: + CMPQ (R9), $0x00000000 + JNE sequenceDecs_decode_bmi2_adjust_offset_maybezero + INCQ CX + JMP sequenceDecs_decode_bmi2_adjust_offset_nonzero + +sequenceDecs_decode_bmi2_adjust_offset_maybezero: + TESTQ CX, CX + JNZ sequenceDecs_decode_bmi2_adjust_offset_nonzero + MOVQ R10, CX + JMP sequenceDecs_decode_bmi2_after_adjust + +sequenceDecs_decode_bmi2_adjust_offset_nonzero: + CMPQ CX, $0x01 + JB sequenceDecs_decode_bmi2_adjust_zero + JEQ sequenceDecs_decode_bmi2_adjust_one + CMPQ CX, $0x02 + JA sequenceDecs_decode_bmi2_adjust_three + JMP sequenceDecs_decode_bmi2_adjust_two + +sequenceDecs_decode_bmi2_adjust_zero: + MOVQ R10, R13 + JMP sequenceDecs_decode_bmi2_adjust_test_temp_valid + +sequenceDecs_decode_bmi2_adjust_one: + MOVQ R11, R13 + JMP sequenceDecs_decode_bmi2_adjust_test_temp_valid + +sequenceDecs_decode_bmi2_adjust_two: + MOVQ R12, R13 + JMP sequenceDecs_decode_bmi2_adjust_test_temp_valid + +sequenceDecs_decode_bmi2_adjust_three: + LEAQ -1(R10), R13 + +sequenceDecs_decode_bmi2_adjust_test_temp_valid: + TESTQ R13, R13 + JNZ sequenceDecs_decode_bmi2_adjust_temp_valid + MOVQ $0x00000001, R13 + +sequenceDecs_decode_bmi2_adjust_temp_valid: + CMPQ CX, $0x01 + CMOVQNE R11, R12 + MOVQ R10, R11 + MOVQ R13, R10 + MOVQ R13, CX + +sequenceDecs_decode_bmi2_after_adjust: + MOVQ CX, 16(R9) + + // Check values + MOVQ 8(R9), R13 + MOVQ (R9), R14 + LEAQ (R13)(R14*1), R15 + MOVQ s+0(FP), BP + ADDQ R15, 256(BP) + MOVQ ctx+16(FP), R15 + SUBQ R14, 128(R15) + JS error_not_enough_literals + CMPQ R13, $0x00020002 + JA sequenceDecs_decode_bmi2_error_match_len_too_big + TESTQ CX, CX + JNZ sequenceDecs_decode_bmi2_match_len_ofs_ok + TESTQ R13, R13 + JNZ sequenceDecs_decode_bmi2_error_match_len_ofs_mismatch + +sequenceDecs_decode_bmi2_match_len_ofs_ok: + ADDQ $0x18, R9 + MOVQ ctx+16(FP), CX + DECQ 96(CX) + JNS sequenceDecs_decode_bmi2_main_loop + MOVQ s+0(FP), CX + MOVQ R10, 144(CX) + MOVQ R11, 152(CX) + MOVQ R12, 160(CX) + MOVQ br+8(FP), CX + MOVQ AX, 24(CX) + MOVB DL, 40(CX) + MOVQ BX, 32(CX) + + // Return success + MOVQ $0x00000000, ret+24(FP) + RET + + // Return with match length error +sequenceDecs_decode_bmi2_error_match_len_ofs_mismatch: + MOVQ $0x00000001, ret+24(FP) + RET + + // Return with match too long error +sequenceDecs_decode_bmi2_error_match_len_too_big: + MOVQ $0x00000002, ret+24(FP) + RET + + // Return with match offset too long error + MOVQ $0x00000003, ret+24(FP) + RET + + // Return with not enough literals error +error_not_enough_literals: + MOVQ $0x00000004, ret+24(FP) + RET + + // Return with overread error +error_overread: + MOVQ $0x00000006, ret+24(FP) + RET + +// func sequenceDecs_decode_56_bmi2(s *sequenceDecs, br *bitReader, ctx *decodeAsmContext) int +// Requires: BMI, BMI2, CMOV +TEXT ·sequenceDecs_decode_56_bmi2(SB), $8-32 + MOVQ br+8(FP), BX + MOVQ 24(BX), AX + MOVBQZX 40(BX), DX + MOVQ (BX), CX + MOVQ 32(BX), BX + ADDQ BX, CX + MOVQ CX, (SP) + MOVQ ctx+16(FP), CX + MOVQ 72(CX), SI + MOVQ 80(CX), DI + MOVQ 88(CX), R8 + MOVQ 104(CX), R9 + MOVQ s+0(FP), CX + MOVQ 144(CX), R10 + MOVQ 152(CX), R11 + MOVQ 160(CX), R12 + +sequenceDecs_decode_56_bmi2_main_loop: + MOVQ (SP), R13 + + // Fill bitreader to have enough for the offset and match length. + CMPQ BX, $0x08 + JL sequenceDecs_decode_56_bmi2_fill_byte_by_byte + MOVQ DX, CX + SHRQ $0x03, CX + SUBQ CX, R13 + MOVQ (R13), AX + SUBQ CX, BX + ANDQ $0x07, DX + JMP sequenceDecs_decode_56_bmi2_fill_end + +sequenceDecs_decode_56_bmi2_fill_byte_by_byte: + CMPQ BX, $0x00 + JLE sequenceDecs_decode_56_bmi2_fill_check_overread + CMPQ DX, $0x07 + JLE sequenceDecs_decode_56_bmi2_fill_end + SHLQ $0x08, AX + SUBQ $0x01, R13 + SUBQ $0x01, BX + SUBQ $0x08, DX + MOVBQZX (R13), CX + ORQ CX, AX + JMP sequenceDecs_decode_56_bmi2_fill_byte_by_byte + +sequenceDecs_decode_56_bmi2_fill_check_overread: + CMPQ DX, $0x40 + JA error_overread + +sequenceDecs_decode_56_bmi2_fill_end: + // Update offset + MOVQ $0x00000808, CX + BEXTRQ CX, R8, R14 + MOVQ AX, R15 + LEAQ (DX)(R14*1), CX + ROLQ CL, R15 + BZHIQ R14, R15, R15 + MOVQ CX, DX + MOVQ R8, CX + SHRQ $0x20, CX + ADDQ R15, CX + MOVQ CX, 16(R9) + + // Update match length + MOVQ $0x00000808, CX + BEXTRQ CX, DI, R14 + MOVQ AX, R15 + LEAQ (DX)(R14*1), CX + ROLQ CL, R15 + BZHIQ R14, R15, R15 + MOVQ CX, DX + MOVQ DI, CX + SHRQ $0x20, CX + ADDQ R15, CX + MOVQ CX, 8(R9) + + // Update literal length + MOVQ $0x00000808, CX + BEXTRQ CX, SI, R14 + MOVQ AX, R15 + LEAQ (DX)(R14*1), CX + ROLQ CL, R15 + BZHIQ R14, R15, R15 + MOVQ CX, DX + MOVQ SI, CX + SHRQ $0x20, CX + ADDQ R15, CX + MOVQ CX, (R9) + + // Fill bitreader for state updates + MOVQ R13, (SP) + MOVQ $0x00000808, CX + BEXTRQ CX, R8, R13 + MOVQ ctx+16(FP), CX + CMPQ 96(CX), $0x00 + JZ sequenceDecs_decode_56_bmi2_skip_update + LEAQ (SI)(DI*1), R14 + ADDQ R8, R14 + MOVBQZX R14, R14 + LEAQ (DX)(R14*1), CX + MOVQ AX, R15 + MOVQ CX, DX + ROLQ CL, R15 + BZHIQ R14, R15, R15 + + // Update Offset State + BZHIQ R8, R15, CX + SHRXQ R8, R15, R15 + SHRL $0x10, R8 + ADDQ CX, R8 + + // Load ctx.ofTable + MOVQ ctx+16(FP), CX + MOVQ 48(CX), CX + MOVQ (CX)(R8*8), R8 + + // Update Match Length State + BZHIQ DI, R15, CX + SHRXQ DI, R15, R15 + SHRL $0x10, DI + ADDQ CX, DI + + // Load ctx.mlTable + MOVQ ctx+16(FP), CX + MOVQ 24(CX), CX + MOVQ (CX)(DI*8), DI + + // Update Literal Length State + BZHIQ SI, R15, CX + SHRL $0x10, SI + ADDQ CX, SI + + // Load ctx.llTable + MOVQ ctx+16(FP), CX + MOVQ (CX), CX + MOVQ (CX)(SI*8), SI + +sequenceDecs_decode_56_bmi2_skip_update: + // Adjust offset + MOVQ 16(R9), CX + CMPQ R13, $0x01 + JBE sequenceDecs_decode_56_bmi2_adjust_offsetB_1_or_0 + MOVQ R11, R12 + MOVQ R10, R11 + MOVQ CX, R10 + JMP sequenceDecs_decode_56_bmi2_after_adjust + +sequenceDecs_decode_56_bmi2_adjust_offsetB_1_or_0: + CMPQ (R9), $0x00000000 + JNE sequenceDecs_decode_56_bmi2_adjust_offset_maybezero + INCQ CX + JMP sequenceDecs_decode_56_bmi2_adjust_offset_nonzero + +sequenceDecs_decode_56_bmi2_adjust_offset_maybezero: + TESTQ CX, CX + JNZ sequenceDecs_decode_56_bmi2_adjust_offset_nonzero + MOVQ R10, CX + JMP sequenceDecs_decode_56_bmi2_after_adjust + +sequenceDecs_decode_56_bmi2_adjust_offset_nonzero: + CMPQ CX, $0x01 + JB sequenceDecs_decode_56_bmi2_adjust_zero + JEQ sequenceDecs_decode_56_bmi2_adjust_one + CMPQ CX, $0x02 + JA sequenceDecs_decode_56_bmi2_adjust_three + JMP sequenceDecs_decode_56_bmi2_adjust_two + +sequenceDecs_decode_56_bmi2_adjust_zero: + MOVQ R10, R13 + JMP sequenceDecs_decode_56_bmi2_adjust_test_temp_valid + +sequenceDecs_decode_56_bmi2_adjust_one: + MOVQ R11, R13 + JMP sequenceDecs_decode_56_bmi2_adjust_test_temp_valid + +sequenceDecs_decode_56_bmi2_adjust_two: + MOVQ R12, R13 + JMP sequenceDecs_decode_56_bmi2_adjust_test_temp_valid + +sequenceDecs_decode_56_bmi2_adjust_three: + LEAQ -1(R10), R13 + +sequenceDecs_decode_56_bmi2_adjust_test_temp_valid: + TESTQ R13, R13 + JNZ sequenceDecs_decode_56_bmi2_adjust_temp_valid + MOVQ $0x00000001, R13 + +sequenceDecs_decode_56_bmi2_adjust_temp_valid: + CMPQ CX, $0x01 + CMOVQNE R11, R12 + MOVQ R10, R11 + MOVQ R13, R10 + MOVQ R13, CX + +sequenceDecs_decode_56_bmi2_after_adjust: + MOVQ CX, 16(R9) + + // Check values + MOVQ 8(R9), R13 + MOVQ (R9), R14 + LEAQ (R13)(R14*1), R15 + MOVQ s+0(FP), BP + ADDQ R15, 256(BP) + MOVQ ctx+16(FP), R15 + SUBQ R14, 128(R15) + JS error_not_enough_literals + CMPQ R13, $0x00020002 + JA sequenceDecs_decode_56_bmi2_error_match_len_too_big + TESTQ CX, CX + JNZ sequenceDecs_decode_56_bmi2_match_len_ofs_ok + TESTQ R13, R13 + JNZ sequenceDecs_decode_56_bmi2_error_match_len_ofs_mismatch + +sequenceDecs_decode_56_bmi2_match_len_ofs_ok: + ADDQ $0x18, R9 + MOVQ ctx+16(FP), CX + DECQ 96(CX) + JNS sequenceDecs_decode_56_bmi2_main_loop + MOVQ s+0(FP), CX + MOVQ R10, 144(CX) + MOVQ R11, 152(CX) + MOVQ R12, 160(CX) + MOVQ br+8(FP), CX + MOVQ AX, 24(CX) + MOVB DL, 40(CX) + MOVQ BX, 32(CX) + + // Return success + MOVQ $0x00000000, ret+24(FP) + RET + + // Return with match length error +sequenceDecs_decode_56_bmi2_error_match_len_ofs_mismatch: + MOVQ $0x00000001, ret+24(FP) + RET + + // Return with match too long error +sequenceDecs_decode_56_bmi2_error_match_len_too_big: + MOVQ $0x00000002, ret+24(FP) + RET + + // Return with match offset too long error + MOVQ $0x00000003, ret+24(FP) + RET + + // Return with not enough literals error +error_not_enough_literals: + MOVQ $0x00000004, ret+24(FP) + RET + + // Return with overread error +error_overread: + MOVQ $0x00000006, ret+24(FP) + RET + +// func sequenceDecs_executeSimple_amd64(ctx *executeAsmContext) bool +// Requires: SSE +TEXT ·sequenceDecs_executeSimple_amd64(SB), $8-9 + MOVQ ctx+0(FP), R10 + MOVQ 8(R10), CX + TESTQ CX, CX + JZ empty_seqs + MOVQ (R10), AX + MOVQ 24(R10), DX + MOVQ 32(R10), BX + MOVQ 80(R10), SI + MOVQ 104(R10), DI + MOVQ 120(R10), R8 + MOVQ 56(R10), R9 + MOVQ 64(R10), R10 + ADDQ R10, R9 + + // seqsBase += 24 * seqIndex + LEAQ (DX)(DX*2), R11 + SHLQ $0x03, R11 + ADDQ R11, AX + + // outBase += outPosition + ADDQ DI, BX + +main_loop: + MOVQ (AX), R11 + MOVQ 16(AX), R12 + MOVQ 8(AX), R13 + + // Copy literals + TESTQ R11, R11 + JZ check_offset + XORQ R14, R14 + +copy_1: + MOVUPS (SI)(R14*1), X0 + MOVUPS X0, (BX)(R14*1) + ADDQ $0x10, R14 + CMPQ R14, R11 + JB copy_1 + ADDQ R11, SI + ADDQ R11, BX + ADDQ R11, DI + + // Malformed input if seq.mo > t+len(hist) || seq.mo > s.windowSize) +check_offset: + LEAQ (DI)(R10*1), R11 + CMPQ R12, R11 + JG error_match_off_too_big + CMPQ R12, R8 + JG error_match_off_too_big + + // Copy match from history + MOVQ R12, R11 + SUBQ DI, R11 + JLS copy_match + MOVQ R9, R14 + SUBQ R11, R14 + CMPQ R13, R11 + JG copy_all_from_history + MOVQ R13, R11 + SUBQ $0x10, R11 + JB copy_4_small + +copy_4_loop: + MOVUPS (R14), X0 + MOVUPS X0, (BX) + ADDQ $0x10, R14 + ADDQ $0x10, BX + SUBQ $0x10, R11 + JAE copy_4_loop + LEAQ 16(R14)(R11*1), R14 + LEAQ 16(BX)(R11*1), BX + MOVUPS -16(R14), X0 + MOVUPS X0, -16(BX) + JMP copy_4_end + +copy_4_small: + CMPQ R13, $0x03 + JE copy_4_move_3 + CMPQ R13, $0x08 + JB copy_4_move_4through7 + JMP copy_4_move_8through16 + +copy_4_move_3: + MOVW (R14), R11 + MOVB 2(R14), R12 + MOVW R11, (BX) + MOVB R12, 2(BX) + ADDQ R13, R14 + ADDQ R13, BX + JMP copy_4_end + +copy_4_move_4through7: + MOVL (R14), R11 + MOVL -4(R14)(R13*1), R12 + MOVL R11, (BX) + MOVL R12, -4(BX)(R13*1) + ADDQ R13, R14 + ADDQ R13, BX + JMP copy_4_end + +copy_4_move_8through16: + MOVQ (R14), R11 + MOVQ -8(R14)(R13*1), R12 + MOVQ R11, (BX) + MOVQ R12, -8(BX)(R13*1) + ADDQ R13, R14 + ADDQ R13, BX + +copy_4_end: + ADDQ R13, DI + ADDQ $0x18, AX + INCQ DX + CMPQ DX, CX + JB main_loop + JMP loop_finished + +copy_all_from_history: + MOVQ R11, R15 + SUBQ $0x10, R15 + JB copy_5_small + +copy_5_loop: + MOVUPS (R14), X0 + MOVUPS X0, (BX) + ADDQ $0x10, R14 + ADDQ $0x10, BX + SUBQ $0x10, R15 + JAE copy_5_loop + LEAQ 16(R14)(R15*1), R14 + LEAQ 16(BX)(R15*1), BX + MOVUPS -16(R14), X0 + MOVUPS X0, -16(BX) + JMP copy_5_end + +copy_5_small: + CMPQ R11, $0x03 + JE copy_5_move_3 + JB copy_5_move_1or2 + CMPQ R11, $0x08 + JB copy_5_move_4through7 + JMP copy_5_move_8through16 + +copy_5_move_1or2: + MOVB (R14), R15 + MOVB -1(R14)(R11*1), BP + MOVB R15, (BX) + MOVB BP, -1(BX)(R11*1) + ADDQ R11, R14 + ADDQ R11, BX + JMP copy_5_end + +copy_5_move_3: + MOVW (R14), R15 + MOVB 2(R14), BP + MOVW R15, (BX) + MOVB BP, 2(BX) + ADDQ R11, R14 + ADDQ R11, BX + JMP copy_5_end + +copy_5_move_4through7: + MOVL (R14), R15 + MOVL -4(R14)(R11*1), BP + MOVL R15, (BX) + MOVL BP, -4(BX)(R11*1) + ADDQ R11, R14 + ADDQ R11, BX + JMP copy_5_end + +copy_5_move_8through16: + MOVQ (R14), R15 + MOVQ -8(R14)(R11*1), BP + MOVQ R15, (BX) + MOVQ BP, -8(BX)(R11*1) + ADDQ R11, R14 + ADDQ R11, BX + +copy_5_end: + ADDQ R11, DI + SUBQ R11, R13 + + // Copy match from the current buffer +copy_match: + MOVQ BX, R11 + SUBQ R12, R11 + + // ml <= mo + CMPQ R13, R12 + JA copy_overlapping_match + + // Copy non-overlapping match + ADDQ R13, DI + MOVQ BX, R12 + ADDQ R13, BX + +copy_2: + MOVUPS (R11), X0 + MOVUPS X0, (R12) + ADDQ $0x10, R11 + ADDQ $0x10, R12 + SUBQ $0x10, R13 + JHI copy_2 + JMP handle_loop + + // Copy overlapping match +copy_overlapping_match: + ADDQ R13, DI + +copy_slow_3: + MOVB (R11), R12 + MOVB R12, (BX) + INCQ R11 + INCQ BX + DECQ R13 + JNZ copy_slow_3 + +handle_loop: + ADDQ $0x18, AX + INCQ DX + CMPQ DX, CX + JB main_loop + +loop_finished: + // Return value + MOVB $0x01, ret+8(FP) + + // Update the context + MOVQ ctx+0(FP), AX + MOVQ DX, 24(AX) + MOVQ DI, 104(AX) + SUBQ 80(AX), SI + MOVQ SI, 112(AX) + RET + +error_match_off_too_big: + // Return value + MOVB $0x00, ret+8(FP) + + // Update the context + MOVQ ctx+0(FP), AX + MOVQ DX, 24(AX) + MOVQ DI, 104(AX) + SUBQ 80(AX), SI + MOVQ SI, 112(AX) + RET + +empty_seqs: + // Return value + MOVB $0x01, ret+8(FP) + RET + +// func sequenceDecs_executeSimple_safe_amd64(ctx *executeAsmContext) bool +// Requires: SSE +TEXT ·sequenceDecs_executeSimple_safe_amd64(SB), $8-9 + MOVQ ctx+0(FP), R10 + MOVQ 8(R10), CX + TESTQ CX, CX + JZ empty_seqs + MOVQ (R10), AX + MOVQ 24(R10), DX + MOVQ 32(R10), BX + MOVQ 80(R10), SI + MOVQ 104(R10), DI + MOVQ 120(R10), R8 + MOVQ 56(R10), R9 + MOVQ 64(R10), R10 + ADDQ R10, R9 + + // seqsBase += 24 * seqIndex + LEAQ (DX)(DX*2), R11 + SHLQ $0x03, R11 + ADDQ R11, AX + + // outBase += outPosition + ADDQ DI, BX + +main_loop: + MOVQ (AX), R11 + MOVQ 16(AX), R12 + MOVQ 8(AX), R13 + + // Copy literals + TESTQ R11, R11 + JZ check_offset + MOVQ R11, R14 + SUBQ $0x10, R14 + JB copy_1_small + +copy_1_loop: + MOVUPS (SI), X0 + MOVUPS X0, (BX) + ADDQ $0x10, SI + ADDQ $0x10, BX + SUBQ $0x10, R14 + JAE copy_1_loop + LEAQ 16(SI)(R14*1), SI + LEAQ 16(BX)(R14*1), BX + MOVUPS -16(SI), X0 + MOVUPS X0, -16(BX) + JMP copy_1_end + +copy_1_small: + CMPQ R11, $0x03 + JE copy_1_move_3 + JB copy_1_move_1or2 + CMPQ R11, $0x08 + JB copy_1_move_4through7 + JMP copy_1_move_8through16 + +copy_1_move_1or2: + MOVB (SI), R14 + MOVB -1(SI)(R11*1), R15 + MOVB R14, (BX) + MOVB R15, -1(BX)(R11*1) + ADDQ R11, SI + ADDQ R11, BX + JMP copy_1_end + +copy_1_move_3: + MOVW (SI), R14 + MOVB 2(SI), R15 + MOVW R14, (BX) + MOVB R15, 2(BX) + ADDQ R11, SI + ADDQ R11, BX + JMP copy_1_end + +copy_1_move_4through7: + MOVL (SI), R14 + MOVL -4(SI)(R11*1), R15 + MOVL R14, (BX) + MOVL R15, -4(BX)(R11*1) + ADDQ R11, SI + ADDQ R11, BX + JMP copy_1_end + +copy_1_move_8through16: + MOVQ (SI), R14 + MOVQ -8(SI)(R11*1), R15 + MOVQ R14, (BX) + MOVQ R15, -8(BX)(R11*1) + ADDQ R11, SI + ADDQ R11, BX + +copy_1_end: + ADDQ R11, DI + + // Malformed input if seq.mo > t+len(hist) || seq.mo > s.windowSize) +check_offset: + LEAQ (DI)(R10*1), R11 + CMPQ R12, R11 + JG error_match_off_too_big + CMPQ R12, R8 + JG error_match_off_too_big + + // Copy match from history + MOVQ R12, R11 + SUBQ DI, R11 + JLS copy_match + MOVQ R9, R14 + SUBQ R11, R14 + CMPQ R13, R11 + JG copy_all_from_history + MOVQ R13, R11 + SUBQ $0x10, R11 + JB copy_4_small + +copy_4_loop: + MOVUPS (R14), X0 + MOVUPS X0, (BX) + ADDQ $0x10, R14 + ADDQ $0x10, BX + SUBQ $0x10, R11 + JAE copy_4_loop + LEAQ 16(R14)(R11*1), R14 + LEAQ 16(BX)(R11*1), BX + MOVUPS -16(R14), X0 + MOVUPS X0, -16(BX) + JMP copy_4_end + +copy_4_small: + CMPQ R13, $0x03 + JE copy_4_move_3 + CMPQ R13, $0x08 + JB copy_4_move_4through7 + JMP copy_4_move_8through16 + +copy_4_move_3: + MOVW (R14), R11 + MOVB 2(R14), R12 + MOVW R11, (BX) + MOVB R12, 2(BX) + ADDQ R13, R14 + ADDQ R13, BX + JMP copy_4_end + +copy_4_move_4through7: + MOVL (R14), R11 + MOVL -4(R14)(R13*1), R12 + MOVL R11, (BX) + MOVL R12, -4(BX)(R13*1) + ADDQ R13, R14 + ADDQ R13, BX + JMP copy_4_end + +copy_4_move_8through16: + MOVQ (R14), R11 + MOVQ -8(R14)(R13*1), R12 + MOVQ R11, (BX) + MOVQ R12, -8(BX)(R13*1) + ADDQ R13, R14 + ADDQ R13, BX + +copy_4_end: + ADDQ R13, DI + ADDQ $0x18, AX + INCQ DX + CMPQ DX, CX + JB main_loop + JMP loop_finished + +copy_all_from_history: + MOVQ R11, R15 + SUBQ $0x10, R15 + JB copy_5_small + +copy_5_loop: + MOVUPS (R14), X0 + MOVUPS X0, (BX) + ADDQ $0x10, R14 + ADDQ $0x10, BX + SUBQ $0x10, R15 + JAE copy_5_loop + LEAQ 16(R14)(R15*1), R14 + LEAQ 16(BX)(R15*1), BX + MOVUPS -16(R14), X0 + MOVUPS X0, -16(BX) + JMP copy_5_end + +copy_5_small: + CMPQ R11, $0x03 + JE copy_5_move_3 + JB copy_5_move_1or2 + CMPQ R11, $0x08 + JB copy_5_move_4through7 + JMP copy_5_move_8through16 + +copy_5_move_1or2: + MOVB (R14), R15 + MOVB -1(R14)(R11*1), BP + MOVB R15, (BX) + MOVB BP, -1(BX)(R11*1) + ADDQ R11, R14 + ADDQ R11, BX + JMP copy_5_end + +copy_5_move_3: + MOVW (R14), R15 + MOVB 2(R14), BP + MOVW R15, (BX) + MOVB BP, 2(BX) + ADDQ R11, R14 + ADDQ R11, BX + JMP copy_5_end + +copy_5_move_4through7: + MOVL (R14), R15 + MOVL -4(R14)(R11*1), BP + MOVL R15, (BX) + MOVL BP, -4(BX)(R11*1) + ADDQ R11, R14 + ADDQ R11, BX + JMP copy_5_end + +copy_5_move_8through16: + MOVQ (R14), R15 + MOVQ -8(R14)(R11*1), BP + MOVQ R15, (BX) + MOVQ BP, -8(BX)(R11*1) + ADDQ R11, R14 + ADDQ R11, BX + +copy_5_end: + ADDQ R11, DI + SUBQ R11, R13 + + // Copy match from the current buffer +copy_match: + MOVQ BX, R11 + SUBQ R12, R11 + + // ml <= mo + CMPQ R13, R12 + JA copy_overlapping_match + + // Copy non-overlapping match + ADDQ R13, DI + MOVQ R13, R12 + SUBQ $0x10, R12 + JB copy_2_small + +copy_2_loop: + MOVUPS (R11), X0 + MOVUPS X0, (BX) + ADDQ $0x10, R11 + ADDQ $0x10, BX + SUBQ $0x10, R12 + JAE copy_2_loop + LEAQ 16(R11)(R12*1), R11 + LEAQ 16(BX)(R12*1), BX + MOVUPS -16(R11), X0 + MOVUPS X0, -16(BX) + JMP copy_2_end + +copy_2_small: + CMPQ R13, $0x03 + JE copy_2_move_3 + JB copy_2_move_1or2 + CMPQ R13, $0x08 + JB copy_2_move_4through7 + JMP copy_2_move_8through16 + +copy_2_move_1or2: + MOVB (R11), R12 + MOVB -1(R11)(R13*1), R14 + MOVB R12, (BX) + MOVB R14, -1(BX)(R13*1) + ADDQ R13, R11 + ADDQ R13, BX + JMP copy_2_end + +copy_2_move_3: + MOVW (R11), R12 + MOVB 2(R11), R14 + MOVW R12, (BX) + MOVB R14, 2(BX) + ADDQ R13, R11 + ADDQ R13, BX + JMP copy_2_end + +copy_2_move_4through7: + MOVL (R11), R12 + MOVL -4(R11)(R13*1), R14 + MOVL R12, (BX) + MOVL R14, -4(BX)(R13*1) + ADDQ R13, R11 + ADDQ R13, BX + JMP copy_2_end + +copy_2_move_8through16: + MOVQ (R11), R12 + MOVQ -8(R11)(R13*1), R14 + MOVQ R12, (BX) + MOVQ R14, -8(BX)(R13*1) + ADDQ R13, R11 + ADDQ R13, BX + +copy_2_end: + JMP handle_loop + + // Copy overlapping match +copy_overlapping_match: + ADDQ R13, DI + +copy_slow_3: + MOVB (R11), R12 + MOVB R12, (BX) + INCQ R11 + INCQ BX + DECQ R13 + JNZ copy_slow_3 + +handle_loop: + ADDQ $0x18, AX + INCQ DX + CMPQ DX, CX + JB main_loop + +loop_finished: + // Return value + MOVB $0x01, ret+8(FP) + + // Update the context + MOVQ ctx+0(FP), AX + MOVQ DX, 24(AX) + MOVQ DI, 104(AX) + SUBQ 80(AX), SI + MOVQ SI, 112(AX) + RET + +error_match_off_too_big: + // Return value + MOVB $0x00, ret+8(FP) + + // Update the context + MOVQ ctx+0(FP), AX + MOVQ DX, 24(AX) + MOVQ DI, 104(AX) + SUBQ 80(AX), SI + MOVQ SI, 112(AX) + RET + +empty_seqs: + // Return value + MOVB $0x01, ret+8(FP) + RET + +// func sequenceDecs_decodeSync_amd64(s *sequenceDecs, br *bitReader, ctx *decodeSyncAsmContext) int +// Requires: CMOV, SSE +TEXT ·sequenceDecs_decodeSync_amd64(SB), $64-32 + MOVQ br+8(FP), CX + MOVQ 24(CX), DX + MOVBQZX 40(CX), BX + MOVQ (CX), AX + MOVQ 32(CX), SI + ADDQ SI, AX + MOVQ AX, (SP) + MOVQ ctx+16(FP), AX + MOVQ 72(AX), DI + MOVQ 80(AX), R8 + MOVQ 88(AX), R9 + XORQ CX, CX + MOVQ CX, 8(SP) + MOVQ CX, 16(SP) + MOVQ CX, 24(SP) + MOVQ 112(AX), R10 + MOVQ 128(AX), CX + MOVQ CX, 32(SP) + MOVQ 144(AX), R11 + MOVQ 136(AX), R12 + MOVQ 200(AX), CX + MOVQ CX, 56(SP) + MOVQ 176(AX), CX + MOVQ CX, 48(SP) + MOVQ 184(AX), AX + MOVQ AX, 40(SP) + MOVQ 40(SP), AX + ADDQ AX, 48(SP) + + // Calculate pointer to s.out[cap(s.out)] (a past-end pointer) + ADDQ R10, 32(SP) + + // outBase += outPosition + ADDQ R12, R10 + +sequenceDecs_decodeSync_amd64_main_loop: + MOVQ (SP), R13 + + // Fill bitreader to have enough for the offset and match length. + CMPQ SI, $0x08 + JL sequenceDecs_decodeSync_amd64_fill_byte_by_byte + MOVQ BX, AX + SHRQ $0x03, AX + SUBQ AX, R13 + MOVQ (R13), DX + SUBQ AX, SI + ANDQ $0x07, BX + JMP sequenceDecs_decodeSync_amd64_fill_end + +sequenceDecs_decodeSync_amd64_fill_byte_by_byte: + CMPQ SI, $0x00 + JLE sequenceDecs_decodeSync_amd64_fill_check_overread + CMPQ BX, $0x07 + JLE sequenceDecs_decodeSync_amd64_fill_end + SHLQ $0x08, DX + SUBQ $0x01, R13 + SUBQ $0x01, SI + SUBQ $0x08, BX + MOVBQZX (R13), AX + ORQ AX, DX + JMP sequenceDecs_decodeSync_amd64_fill_byte_by_byte + +sequenceDecs_decodeSync_amd64_fill_check_overread: + CMPQ BX, $0x40 + JA error_overread + +sequenceDecs_decodeSync_amd64_fill_end: + // Update offset + MOVQ R9, AX + MOVQ BX, CX + MOVQ DX, R14 + SHLQ CL, R14 + MOVB AH, CL + SHRQ $0x20, AX + TESTQ CX, CX + JZ sequenceDecs_decodeSync_amd64_of_update_zero + ADDQ CX, BX + CMPQ BX, $0x40 + JA sequenceDecs_decodeSync_amd64_of_update_zero + CMPQ CX, $0x40 + JAE sequenceDecs_decodeSync_amd64_of_update_zero + NEGQ CX + SHRQ CL, R14 + ADDQ R14, AX + +sequenceDecs_decodeSync_amd64_of_update_zero: + MOVQ AX, 8(SP) + + // Update match length + MOVQ R8, AX + MOVQ BX, CX + MOVQ DX, R14 + SHLQ CL, R14 + MOVB AH, CL + SHRQ $0x20, AX + TESTQ CX, CX + JZ sequenceDecs_decodeSync_amd64_ml_update_zero + ADDQ CX, BX + CMPQ BX, $0x40 + JA sequenceDecs_decodeSync_amd64_ml_update_zero + CMPQ CX, $0x40 + JAE sequenceDecs_decodeSync_amd64_ml_update_zero + NEGQ CX + SHRQ CL, R14 + ADDQ R14, AX + +sequenceDecs_decodeSync_amd64_ml_update_zero: + MOVQ AX, 16(SP) + + // Fill bitreader to have enough for the remaining + CMPQ SI, $0x08 + JL sequenceDecs_decodeSync_amd64_fill_2_byte_by_byte + MOVQ BX, AX + SHRQ $0x03, AX + SUBQ AX, R13 + MOVQ (R13), DX + SUBQ AX, SI + ANDQ $0x07, BX + JMP sequenceDecs_decodeSync_amd64_fill_2_end + +sequenceDecs_decodeSync_amd64_fill_2_byte_by_byte: + CMPQ SI, $0x00 + JLE sequenceDecs_decodeSync_amd64_fill_2_check_overread + CMPQ BX, $0x07 + JLE sequenceDecs_decodeSync_amd64_fill_2_end + SHLQ $0x08, DX + SUBQ $0x01, R13 + SUBQ $0x01, SI + SUBQ $0x08, BX + MOVBQZX (R13), AX + ORQ AX, DX + JMP sequenceDecs_decodeSync_amd64_fill_2_byte_by_byte + +sequenceDecs_decodeSync_amd64_fill_2_check_overread: + CMPQ BX, $0x40 + JA error_overread + +sequenceDecs_decodeSync_amd64_fill_2_end: + // Update literal length + MOVQ DI, AX + MOVQ BX, CX + MOVQ DX, R14 + SHLQ CL, R14 + MOVB AH, CL + SHRQ $0x20, AX + TESTQ CX, CX + JZ sequenceDecs_decodeSync_amd64_ll_update_zero + ADDQ CX, BX + CMPQ BX, $0x40 + JA sequenceDecs_decodeSync_amd64_ll_update_zero + CMPQ CX, $0x40 + JAE sequenceDecs_decodeSync_amd64_ll_update_zero + NEGQ CX + SHRQ CL, R14 + ADDQ R14, AX + +sequenceDecs_decodeSync_amd64_ll_update_zero: + MOVQ AX, 24(SP) + + // Fill bitreader for state updates + MOVQ R13, (SP) + MOVQ R9, AX + SHRQ $0x08, AX + MOVBQZX AL, AX + MOVQ ctx+16(FP), CX + CMPQ 96(CX), $0x00 + JZ sequenceDecs_decodeSync_amd64_skip_update + + // Update Literal Length State + MOVBQZX DI, R13 + SHRL $0x10, DI + LEAQ (BX)(R13*1), CX + MOVQ DX, R14 + MOVQ CX, BX + ROLQ CL, R14 + MOVL $0x00000001, R15 + MOVB R13, CL + SHLL CL, R15 + DECL R15 + ANDQ R15, R14 + ADDQ R14, DI + + // Load ctx.llTable + MOVQ ctx+16(FP), CX + MOVQ (CX), CX + MOVQ (CX)(DI*8), DI + + // Update Match Length State + MOVBQZX R8, R13 + SHRL $0x10, R8 + LEAQ (BX)(R13*1), CX + MOVQ DX, R14 + MOVQ CX, BX + ROLQ CL, R14 + MOVL $0x00000001, R15 + MOVB R13, CL + SHLL CL, R15 + DECL R15 + ANDQ R15, R14 + ADDQ R14, R8 + + // Load ctx.mlTable + MOVQ ctx+16(FP), CX + MOVQ 24(CX), CX + MOVQ (CX)(R8*8), R8 + + // Update Offset State + MOVBQZX R9, R13 + SHRL $0x10, R9 + LEAQ (BX)(R13*1), CX + MOVQ DX, R14 + MOVQ CX, BX + ROLQ CL, R14 + MOVL $0x00000001, R15 + MOVB R13, CL + SHLL CL, R15 + DECL R15 + ANDQ R15, R14 + ADDQ R14, R9 + + // Load ctx.ofTable + MOVQ ctx+16(FP), CX + MOVQ 48(CX), CX + MOVQ (CX)(R9*8), R9 + +sequenceDecs_decodeSync_amd64_skip_update: + // Adjust offset + MOVQ s+0(FP), CX + MOVQ 8(SP), R13 + CMPQ AX, $0x01 + JBE sequenceDecs_decodeSync_amd64_adjust_offsetB_1_or_0 + MOVUPS 144(CX), X0 + MOVQ R13, 144(CX) + MOVUPS X0, 152(CX) + JMP sequenceDecs_decodeSync_amd64_after_adjust + +sequenceDecs_decodeSync_amd64_adjust_offsetB_1_or_0: + CMPQ 24(SP), $0x00000000 + JNE sequenceDecs_decodeSync_amd64_adjust_offset_maybezero + INCQ R13 + JMP sequenceDecs_decodeSync_amd64_adjust_offset_nonzero + +sequenceDecs_decodeSync_amd64_adjust_offset_maybezero: + TESTQ R13, R13 + JNZ sequenceDecs_decodeSync_amd64_adjust_offset_nonzero + MOVQ 144(CX), R13 + JMP sequenceDecs_decodeSync_amd64_after_adjust + +sequenceDecs_decodeSync_amd64_adjust_offset_nonzero: + MOVQ R13, AX + XORQ R14, R14 + MOVQ $-1, R15 + CMPQ R13, $0x03 + CMOVQEQ R14, AX + CMOVQEQ R15, R14 + ADDQ 144(CX)(AX*8), R14 + JNZ sequenceDecs_decodeSync_amd64_adjust_temp_valid + MOVQ $0x00000001, R14 + +sequenceDecs_decodeSync_amd64_adjust_temp_valid: + CMPQ R13, $0x01 + JZ sequenceDecs_decodeSync_amd64_adjust_skip + MOVQ 152(CX), AX + MOVQ AX, 160(CX) + +sequenceDecs_decodeSync_amd64_adjust_skip: + MOVQ 144(CX), AX + MOVQ AX, 152(CX) + MOVQ R14, 144(CX) + MOVQ R14, R13 + +sequenceDecs_decodeSync_amd64_after_adjust: + MOVQ R13, 8(SP) + + // Check values + MOVQ 16(SP), AX + MOVQ 24(SP), CX + LEAQ (AX)(CX*1), R14 + MOVQ s+0(FP), R15 + ADDQ R14, 256(R15) + MOVQ ctx+16(FP), R14 + SUBQ CX, 104(R14) + JS error_not_enough_literals + CMPQ AX, $0x00020002 + JA sequenceDecs_decodeSync_amd64_error_match_len_too_big + TESTQ R13, R13 + JNZ sequenceDecs_decodeSync_amd64_match_len_ofs_ok + TESTQ AX, AX + JNZ sequenceDecs_decodeSync_amd64_error_match_len_ofs_mismatch + +sequenceDecs_decodeSync_amd64_match_len_ofs_ok: + MOVQ 24(SP), AX + MOVQ 8(SP), CX + MOVQ 16(SP), R13 + + // Check if we have enough space in s.out + LEAQ (AX)(R13*1), R14 + ADDQ R10, R14 + CMPQ R14, 32(SP) + JA error_not_enough_space + + // Copy literals + TESTQ AX, AX + JZ check_offset + XORQ R14, R14 + +copy_1: + MOVUPS (R11)(R14*1), X0 + MOVUPS X0, (R10)(R14*1) + ADDQ $0x10, R14 + CMPQ R14, AX + JB copy_1 + ADDQ AX, R11 + ADDQ AX, R10 + ADDQ AX, R12 + + // Malformed input if seq.mo > t+len(hist) || seq.mo > s.windowSize) +check_offset: + MOVQ R12, AX + ADDQ 40(SP), AX + CMPQ CX, AX + JG error_match_off_too_big + CMPQ CX, 56(SP) + JG error_match_off_too_big + + // Copy match from history + MOVQ CX, AX + SUBQ R12, AX + JLS copy_match + MOVQ 48(SP), R14 + SUBQ AX, R14 + CMPQ R13, AX + JG copy_all_from_history + MOVQ R13, AX + SUBQ $0x10, AX + JB copy_4_small + +copy_4_loop: + MOVUPS (R14), X0 + MOVUPS X0, (R10) + ADDQ $0x10, R14 + ADDQ $0x10, R10 + SUBQ $0x10, AX + JAE copy_4_loop + LEAQ 16(R14)(AX*1), R14 + LEAQ 16(R10)(AX*1), R10 + MOVUPS -16(R14), X0 + MOVUPS X0, -16(R10) + JMP copy_4_end + +copy_4_small: + CMPQ R13, $0x03 + JE copy_4_move_3 + CMPQ R13, $0x08 + JB copy_4_move_4through7 + JMP copy_4_move_8through16 + +copy_4_move_3: + MOVW (R14), AX + MOVB 2(R14), CL + MOVW AX, (R10) + MOVB CL, 2(R10) + ADDQ R13, R14 + ADDQ R13, R10 + JMP copy_4_end + +copy_4_move_4through7: + MOVL (R14), AX + MOVL -4(R14)(R13*1), CX + MOVL AX, (R10) + MOVL CX, -4(R10)(R13*1) + ADDQ R13, R14 + ADDQ R13, R10 + JMP copy_4_end + +copy_4_move_8through16: + MOVQ (R14), AX + MOVQ -8(R14)(R13*1), CX + MOVQ AX, (R10) + MOVQ CX, -8(R10)(R13*1) + ADDQ R13, R14 + ADDQ R13, R10 + +copy_4_end: + ADDQ R13, R12 + JMP handle_loop + JMP loop_finished + +copy_all_from_history: + MOVQ AX, R15 + SUBQ $0x10, R15 + JB copy_5_small + +copy_5_loop: + MOVUPS (R14), X0 + MOVUPS X0, (R10) + ADDQ $0x10, R14 + ADDQ $0x10, R10 + SUBQ $0x10, R15 + JAE copy_5_loop + LEAQ 16(R14)(R15*1), R14 + LEAQ 16(R10)(R15*1), R10 + MOVUPS -16(R14), X0 + MOVUPS X0, -16(R10) + JMP copy_5_end + +copy_5_small: + CMPQ AX, $0x03 + JE copy_5_move_3 + JB copy_5_move_1or2 + CMPQ AX, $0x08 + JB copy_5_move_4through7 + JMP copy_5_move_8through16 + +copy_5_move_1or2: + MOVB (R14), R15 + MOVB -1(R14)(AX*1), BP + MOVB R15, (R10) + MOVB BP, -1(R10)(AX*1) + ADDQ AX, R14 + ADDQ AX, R10 + JMP copy_5_end + +copy_5_move_3: + MOVW (R14), R15 + MOVB 2(R14), BP + MOVW R15, (R10) + MOVB BP, 2(R10) + ADDQ AX, R14 + ADDQ AX, R10 + JMP copy_5_end + +copy_5_move_4through7: + MOVL (R14), R15 + MOVL -4(R14)(AX*1), BP + MOVL R15, (R10) + MOVL BP, -4(R10)(AX*1) + ADDQ AX, R14 + ADDQ AX, R10 + JMP copy_5_end + +copy_5_move_8through16: + MOVQ (R14), R15 + MOVQ -8(R14)(AX*1), BP + MOVQ R15, (R10) + MOVQ BP, -8(R10)(AX*1) + ADDQ AX, R14 + ADDQ AX, R10 + +copy_5_end: + ADDQ AX, R12 + SUBQ AX, R13 + + // Copy match from the current buffer +copy_match: + MOVQ R10, AX + SUBQ CX, AX + + // ml <= mo + CMPQ R13, CX + JA copy_overlapping_match + + // Copy non-overlapping match + ADDQ R13, R12 + MOVQ R10, CX + ADDQ R13, R10 + +copy_2: + MOVUPS (AX), X0 + MOVUPS X0, (CX) + ADDQ $0x10, AX + ADDQ $0x10, CX + SUBQ $0x10, R13 + JHI copy_2 + JMP handle_loop + + // Copy overlapping match +copy_overlapping_match: + ADDQ R13, R12 + +copy_slow_3: + MOVB (AX), CL + MOVB CL, (R10) + INCQ AX + INCQ R10 + DECQ R13 + JNZ copy_slow_3 + +handle_loop: + MOVQ ctx+16(FP), AX + DECQ 96(AX) + JNS sequenceDecs_decodeSync_amd64_main_loop + +loop_finished: + MOVQ br+8(FP), AX + MOVQ DX, 24(AX) + MOVB BL, 40(AX) + MOVQ SI, 32(AX) + + // Update the context + MOVQ ctx+16(FP), AX + MOVQ R12, 136(AX) + MOVQ 144(AX), CX + SUBQ CX, R11 + MOVQ R11, 168(AX) + + // Return success + MOVQ $0x00000000, ret+24(FP) + RET + + // Return with match length error +sequenceDecs_decodeSync_amd64_error_match_len_ofs_mismatch: + MOVQ 16(SP), AX + MOVQ ctx+16(FP), CX + MOVQ AX, 216(CX) + MOVQ $0x00000001, ret+24(FP) + RET + + // Return with match too long error +sequenceDecs_decodeSync_amd64_error_match_len_too_big: + MOVQ ctx+16(FP), AX + MOVQ 16(SP), CX + MOVQ CX, 216(AX) + MOVQ $0x00000002, ret+24(FP) + RET + + // Return with match offset too long error +error_match_off_too_big: + MOVQ ctx+16(FP), AX + MOVQ 8(SP), CX + MOVQ CX, 224(AX) + MOVQ R12, 136(AX) + MOVQ $0x00000003, ret+24(FP) + RET + + // Return with not enough literals error +error_not_enough_literals: + MOVQ ctx+16(FP), AX + MOVQ 24(SP), CX + MOVQ CX, 208(AX) + MOVQ $0x00000004, ret+24(FP) + RET + + // Return with overread error +error_overread: + MOVQ $0x00000006, ret+24(FP) + RET + + // Return with not enough output space error +error_not_enough_space: + MOVQ ctx+16(FP), AX + MOVQ 24(SP), CX + MOVQ CX, 208(AX) + MOVQ 16(SP), CX + MOVQ CX, 216(AX) + MOVQ R12, 136(AX) + MOVQ $0x00000005, ret+24(FP) + RET + +// func sequenceDecs_decodeSync_bmi2(s *sequenceDecs, br *bitReader, ctx *decodeSyncAsmContext) int +// Requires: BMI, BMI2, CMOV, SSE +TEXT ·sequenceDecs_decodeSync_bmi2(SB), $64-32 + MOVQ br+8(FP), BX + MOVQ 24(BX), AX + MOVBQZX 40(BX), DX + MOVQ (BX), CX + MOVQ 32(BX), BX + ADDQ BX, CX + MOVQ CX, (SP) + MOVQ ctx+16(FP), CX + MOVQ 72(CX), SI + MOVQ 80(CX), DI + MOVQ 88(CX), R8 + XORQ R9, R9 + MOVQ R9, 8(SP) + MOVQ R9, 16(SP) + MOVQ R9, 24(SP) + MOVQ 112(CX), R9 + MOVQ 128(CX), R10 + MOVQ R10, 32(SP) + MOVQ 144(CX), R10 + MOVQ 136(CX), R11 + MOVQ 200(CX), R12 + MOVQ R12, 56(SP) + MOVQ 176(CX), R12 + MOVQ R12, 48(SP) + MOVQ 184(CX), CX + MOVQ CX, 40(SP) + MOVQ 40(SP), CX + ADDQ CX, 48(SP) + + // Calculate pointer to s.out[cap(s.out)] (a past-end pointer) + ADDQ R9, 32(SP) + + // outBase += outPosition + ADDQ R11, R9 + +sequenceDecs_decodeSync_bmi2_main_loop: + MOVQ (SP), R12 + + // Fill bitreader to have enough for the offset and match length. + CMPQ BX, $0x08 + JL sequenceDecs_decodeSync_bmi2_fill_byte_by_byte + MOVQ DX, CX + SHRQ $0x03, CX + SUBQ CX, R12 + MOVQ (R12), AX + SUBQ CX, BX + ANDQ $0x07, DX + JMP sequenceDecs_decodeSync_bmi2_fill_end + +sequenceDecs_decodeSync_bmi2_fill_byte_by_byte: + CMPQ BX, $0x00 + JLE sequenceDecs_decodeSync_bmi2_fill_check_overread + CMPQ DX, $0x07 + JLE sequenceDecs_decodeSync_bmi2_fill_end + SHLQ $0x08, AX + SUBQ $0x01, R12 + SUBQ $0x01, BX + SUBQ $0x08, DX + MOVBQZX (R12), CX + ORQ CX, AX + JMP sequenceDecs_decodeSync_bmi2_fill_byte_by_byte + +sequenceDecs_decodeSync_bmi2_fill_check_overread: + CMPQ DX, $0x40 + JA error_overread + +sequenceDecs_decodeSync_bmi2_fill_end: + // Update offset + MOVQ $0x00000808, CX + BEXTRQ CX, R8, R13 + MOVQ AX, R14 + LEAQ (DX)(R13*1), CX + ROLQ CL, R14 + BZHIQ R13, R14, R14 + MOVQ CX, DX + MOVQ R8, CX + SHRQ $0x20, CX + ADDQ R14, CX + MOVQ CX, 8(SP) + + // Update match length + MOVQ $0x00000808, CX + BEXTRQ CX, DI, R13 + MOVQ AX, R14 + LEAQ (DX)(R13*1), CX + ROLQ CL, R14 + BZHIQ R13, R14, R14 + MOVQ CX, DX + MOVQ DI, CX + SHRQ $0x20, CX + ADDQ R14, CX + MOVQ CX, 16(SP) + + // Fill bitreader to have enough for the remaining + CMPQ BX, $0x08 + JL sequenceDecs_decodeSync_bmi2_fill_2_byte_by_byte + MOVQ DX, CX + SHRQ $0x03, CX + SUBQ CX, R12 + MOVQ (R12), AX + SUBQ CX, BX + ANDQ $0x07, DX + JMP sequenceDecs_decodeSync_bmi2_fill_2_end + +sequenceDecs_decodeSync_bmi2_fill_2_byte_by_byte: + CMPQ BX, $0x00 + JLE sequenceDecs_decodeSync_bmi2_fill_2_check_overread + CMPQ DX, $0x07 + JLE sequenceDecs_decodeSync_bmi2_fill_2_end + SHLQ $0x08, AX + SUBQ $0x01, R12 + SUBQ $0x01, BX + SUBQ $0x08, DX + MOVBQZX (R12), CX + ORQ CX, AX + JMP sequenceDecs_decodeSync_bmi2_fill_2_byte_by_byte + +sequenceDecs_decodeSync_bmi2_fill_2_check_overread: + CMPQ DX, $0x40 + JA error_overread + +sequenceDecs_decodeSync_bmi2_fill_2_end: + // Update literal length + MOVQ $0x00000808, CX + BEXTRQ CX, SI, R13 + MOVQ AX, R14 + LEAQ (DX)(R13*1), CX + ROLQ CL, R14 + BZHIQ R13, R14, R14 + MOVQ CX, DX + MOVQ SI, CX + SHRQ $0x20, CX + ADDQ R14, CX + MOVQ CX, 24(SP) + + // Fill bitreader for state updates + MOVQ R12, (SP) + MOVQ $0x00000808, CX + BEXTRQ CX, R8, R12 + MOVQ ctx+16(FP), CX + CMPQ 96(CX), $0x00 + JZ sequenceDecs_decodeSync_bmi2_skip_update + LEAQ (SI)(DI*1), R13 + ADDQ R8, R13 + MOVBQZX R13, R13 + LEAQ (DX)(R13*1), CX + MOVQ AX, R14 + MOVQ CX, DX + ROLQ CL, R14 + BZHIQ R13, R14, R14 + + // Update Offset State + BZHIQ R8, R14, CX + SHRXQ R8, R14, R14 + SHRL $0x10, R8 + ADDQ CX, R8 + + // Load ctx.ofTable + MOVQ ctx+16(FP), CX + MOVQ 48(CX), CX + MOVQ (CX)(R8*8), R8 + + // Update Match Length State + BZHIQ DI, R14, CX + SHRXQ DI, R14, R14 + SHRL $0x10, DI + ADDQ CX, DI + + // Load ctx.mlTable + MOVQ ctx+16(FP), CX + MOVQ 24(CX), CX + MOVQ (CX)(DI*8), DI + + // Update Literal Length State + BZHIQ SI, R14, CX + SHRL $0x10, SI + ADDQ CX, SI + + // Load ctx.llTable + MOVQ ctx+16(FP), CX + MOVQ (CX), CX + MOVQ (CX)(SI*8), SI + +sequenceDecs_decodeSync_bmi2_skip_update: + // Adjust offset + MOVQ s+0(FP), CX + MOVQ 8(SP), R13 + CMPQ R12, $0x01 + JBE sequenceDecs_decodeSync_bmi2_adjust_offsetB_1_or_0 + MOVUPS 144(CX), X0 + MOVQ R13, 144(CX) + MOVUPS X0, 152(CX) + JMP sequenceDecs_decodeSync_bmi2_after_adjust + +sequenceDecs_decodeSync_bmi2_adjust_offsetB_1_or_0: + CMPQ 24(SP), $0x00000000 + JNE sequenceDecs_decodeSync_bmi2_adjust_offset_maybezero + INCQ R13 + JMP sequenceDecs_decodeSync_bmi2_adjust_offset_nonzero + +sequenceDecs_decodeSync_bmi2_adjust_offset_maybezero: + TESTQ R13, R13 + JNZ sequenceDecs_decodeSync_bmi2_adjust_offset_nonzero + MOVQ 144(CX), R13 + JMP sequenceDecs_decodeSync_bmi2_after_adjust + +sequenceDecs_decodeSync_bmi2_adjust_offset_nonzero: + MOVQ R13, R12 + XORQ R14, R14 + MOVQ $-1, R15 + CMPQ R13, $0x03 + CMOVQEQ R14, R12 + CMOVQEQ R15, R14 + ADDQ 144(CX)(R12*8), R14 + JNZ sequenceDecs_decodeSync_bmi2_adjust_temp_valid + MOVQ $0x00000001, R14 + +sequenceDecs_decodeSync_bmi2_adjust_temp_valid: + CMPQ R13, $0x01 + JZ sequenceDecs_decodeSync_bmi2_adjust_skip + MOVQ 152(CX), R12 + MOVQ R12, 160(CX) + +sequenceDecs_decodeSync_bmi2_adjust_skip: + MOVQ 144(CX), R12 + MOVQ R12, 152(CX) + MOVQ R14, 144(CX) + MOVQ R14, R13 + +sequenceDecs_decodeSync_bmi2_after_adjust: + MOVQ R13, 8(SP) + + // Check values + MOVQ 16(SP), CX + MOVQ 24(SP), R12 + LEAQ (CX)(R12*1), R14 + MOVQ s+0(FP), R15 + ADDQ R14, 256(R15) + MOVQ ctx+16(FP), R14 + SUBQ R12, 104(R14) + JS error_not_enough_literals + CMPQ CX, $0x00020002 + JA sequenceDecs_decodeSync_bmi2_error_match_len_too_big + TESTQ R13, R13 + JNZ sequenceDecs_decodeSync_bmi2_match_len_ofs_ok + TESTQ CX, CX + JNZ sequenceDecs_decodeSync_bmi2_error_match_len_ofs_mismatch + +sequenceDecs_decodeSync_bmi2_match_len_ofs_ok: + MOVQ 24(SP), CX + MOVQ 8(SP), R12 + MOVQ 16(SP), R13 + + // Check if we have enough space in s.out + LEAQ (CX)(R13*1), R14 + ADDQ R9, R14 + CMPQ R14, 32(SP) + JA error_not_enough_space + + // Copy literals + TESTQ CX, CX + JZ check_offset + XORQ R14, R14 + +copy_1: + MOVUPS (R10)(R14*1), X0 + MOVUPS X0, (R9)(R14*1) + ADDQ $0x10, R14 + CMPQ R14, CX + JB copy_1 + ADDQ CX, R10 + ADDQ CX, R9 + ADDQ CX, R11 + + // Malformed input if seq.mo > t+len(hist) || seq.mo > s.windowSize) +check_offset: + MOVQ R11, CX + ADDQ 40(SP), CX + CMPQ R12, CX + JG error_match_off_too_big + CMPQ R12, 56(SP) + JG error_match_off_too_big + + // Copy match from history + MOVQ R12, CX + SUBQ R11, CX + JLS copy_match + MOVQ 48(SP), R14 + SUBQ CX, R14 + CMPQ R13, CX + JG copy_all_from_history + MOVQ R13, CX + SUBQ $0x10, CX + JB copy_4_small + +copy_4_loop: + MOVUPS (R14), X0 + MOVUPS X0, (R9) + ADDQ $0x10, R14 + ADDQ $0x10, R9 + SUBQ $0x10, CX + JAE copy_4_loop + LEAQ 16(R14)(CX*1), R14 + LEAQ 16(R9)(CX*1), R9 + MOVUPS -16(R14), X0 + MOVUPS X0, -16(R9) + JMP copy_4_end + +copy_4_small: + CMPQ R13, $0x03 + JE copy_4_move_3 + CMPQ R13, $0x08 + JB copy_4_move_4through7 + JMP copy_4_move_8through16 + +copy_4_move_3: + MOVW (R14), CX + MOVB 2(R14), R12 + MOVW CX, (R9) + MOVB R12, 2(R9) + ADDQ R13, R14 + ADDQ R13, R9 + JMP copy_4_end + +copy_4_move_4through7: + MOVL (R14), CX + MOVL -4(R14)(R13*1), R12 + MOVL CX, (R9) + MOVL R12, -4(R9)(R13*1) + ADDQ R13, R14 + ADDQ R13, R9 + JMP copy_4_end + +copy_4_move_8through16: + MOVQ (R14), CX + MOVQ -8(R14)(R13*1), R12 + MOVQ CX, (R9) + MOVQ R12, -8(R9)(R13*1) + ADDQ R13, R14 + ADDQ R13, R9 + +copy_4_end: + ADDQ R13, R11 + JMP handle_loop + JMP loop_finished + +copy_all_from_history: + MOVQ CX, R15 + SUBQ $0x10, R15 + JB copy_5_small + +copy_5_loop: + MOVUPS (R14), X0 + MOVUPS X0, (R9) + ADDQ $0x10, R14 + ADDQ $0x10, R9 + SUBQ $0x10, R15 + JAE copy_5_loop + LEAQ 16(R14)(R15*1), R14 + LEAQ 16(R9)(R15*1), R9 + MOVUPS -16(R14), X0 + MOVUPS X0, -16(R9) + JMP copy_5_end + +copy_5_small: + CMPQ CX, $0x03 + JE copy_5_move_3 + JB copy_5_move_1or2 + CMPQ CX, $0x08 + JB copy_5_move_4through7 + JMP copy_5_move_8through16 + +copy_5_move_1or2: + MOVB (R14), R15 + MOVB -1(R14)(CX*1), BP + MOVB R15, (R9) + MOVB BP, -1(R9)(CX*1) + ADDQ CX, R14 + ADDQ CX, R9 + JMP copy_5_end + +copy_5_move_3: + MOVW (R14), R15 + MOVB 2(R14), BP + MOVW R15, (R9) + MOVB BP, 2(R9) + ADDQ CX, R14 + ADDQ CX, R9 + JMP copy_5_end + +copy_5_move_4through7: + MOVL (R14), R15 + MOVL -4(R14)(CX*1), BP + MOVL R15, (R9) + MOVL BP, -4(R9)(CX*1) + ADDQ CX, R14 + ADDQ CX, R9 + JMP copy_5_end + +copy_5_move_8through16: + MOVQ (R14), R15 + MOVQ -8(R14)(CX*1), BP + MOVQ R15, (R9) + MOVQ BP, -8(R9)(CX*1) + ADDQ CX, R14 + ADDQ CX, R9 + +copy_5_end: + ADDQ CX, R11 + SUBQ CX, R13 + + // Copy match from the current buffer +copy_match: + MOVQ R9, CX + SUBQ R12, CX + + // ml <= mo + CMPQ R13, R12 + JA copy_overlapping_match + + // Copy non-overlapping match + ADDQ R13, R11 + MOVQ R9, R12 + ADDQ R13, R9 + +copy_2: + MOVUPS (CX), X0 + MOVUPS X0, (R12) + ADDQ $0x10, CX + ADDQ $0x10, R12 + SUBQ $0x10, R13 + JHI copy_2 + JMP handle_loop + + // Copy overlapping match +copy_overlapping_match: + ADDQ R13, R11 + +copy_slow_3: + MOVB (CX), R12 + MOVB R12, (R9) + INCQ CX + INCQ R9 + DECQ R13 + JNZ copy_slow_3 + +handle_loop: + MOVQ ctx+16(FP), CX + DECQ 96(CX) + JNS sequenceDecs_decodeSync_bmi2_main_loop + +loop_finished: + MOVQ br+8(FP), CX + MOVQ AX, 24(CX) + MOVB DL, 40(CX) + MOVQ BX, 32(CX) + + // Update the context + MOVQ ctx+16(FP), AX + MOVQ R11, 136(AX) + MOVQ 144(AX), CX + SUBQ CX, R10 + MOVQ R10, 168(AX) + + // Return success + MOVQ $0x00000000, ret+24(FP) + RET + + // Return with match length error +sequenceDecs_decodeSync_bmi2_error_match_len_ofs_mismatch: + MOVQ 16(SP), AX + MOVQ ctx+16(FP), CX + MOVQ AX, 216(CX) + MOVQ $0x00000001, ret+24(FP) + RET + + // Return with match too long error +sequenceDecs_decodeSync_bmi2_error_match_len_too_big: + MOVQ ctx+16(FP), AX + MOVQ 16(SP), CX + MOVQ CX, 216(AX) + MOVQ $0x00000002, ret+24(FP) + RET + + // Return with match offset too long error +error_match_off_too_big: + MOVQ ctx+16(FP), AX + MOVQ 8(SP), CX + MOVQ CX, 224(AX) + MOVQ R11, 136(AX) + MOVQ $0x00000003, ret+24(FP) + RET + + // Return with not enough literals error +error_not_enough_literals: + MOVQ ctx+16(FP), AX + MOVQ 24(SP), CX + MOVQ CX, 208(AX) + MOVQ $0x00000004, ret+24(FP) + RET + + // Return with overread error +error_overread: + MOVQ $0x00000006, ret+24(FP) + RET + + // Return with not enough output space error +error_not_enough_space: + MOVQ ctx+16(FP), AX + MOVQ 24(SP), CX + MOVQ CX, 208(AX) + MOVQ 16(SP), CX + MOVQ CX, 216(AX) + MOVQ R11, 136(AX) + MOVQ $0x00000005, ret+24(FP) + RET + +// func sequenceDecs_decodeSync_safe_amd64(s *sequenceDecs, br *bitReader, ctx *decodeSyncAsmContext) int +// Requires: CMOV, SSE +TEXT ·sequenceDecs_decodeSync_safe_amd64(SB), $64-32 + MOVQ br+8(FP), CX + MOVQ 24(CX), DX + MOVBQZX 40(CX), BX + MOVQ (CX), AX + MOVQ 32(CX), SI + ADDQ SI, AX + MOVQ AX, (SP) + MOVQ ctx+16(FP), AX + MOVQ 72(AX), DI + MOVQ 80(AX), R8 + MOVQ 88(AX), R9 + XORQ CX, CX + MOVQ CX, 8(SP) + MOVQ CX, 16(SP) + MOVQ CX, 24(SP) + MOVQ 112(AX), R10 + MOVQ 128(AX), CX + MOVQ CX, 32(SP) + MOVQ 144(AX), R11 + MOVQ 136(AX), R12 + MOVQ 200(AX), CX + MOVQ CX, 56(SP) + MOVQ 176(AX), CX + MOVQ CX, 48(SP) + MOVQ 184(AX), AX + MOVQ AX, 40(SP) + MOVQ 40(SP), AX + ADDQ AX, 48(SP) + + // Calculate pointer to s.out[cap(s.out)] (a past-end pointer) + ADDQ R10, 32(SP) + + // outBase += outPosition + ADDQ R12, R10 + +sequenceDecs_decodeSync_safe_amd64_main_loop: + MOVQ (SP), R13 + + // Fill bitreader to have enough for the offset and match length. + CMPQ SI, $0x08 + JL sequenceDecs_decodeSync_safe_amd64_fill_byte_by_byte + MOVQ BX, AX + SHRQ $0x03, AX + SUBQ AX, R13 + MOVQ (R13), DX + SUBQ AX, SI + ANDQ $0x07, BX + JMP sequenceDecs_decodeSync_safe_amd64_fill_end + +sequenceDecs_decodeSync_safe_amd64_fill_byte_by_byte: + CMPQ SI, $0x00 + JLE sequenceDecs_decodeSync_safe_amd64_fill_check_overread + CMPQ BX, $0x07 + JLE sequenceDecs_decodeSync_safe_amd64_fill_end + SHLQ $0x08, DX + SUBQ $0x01, R13 + SUBQ $0x01, SI + SUBQ $0x08, BX + MOVBQZX (R13), AX + ORQ AX, DX + JMP sequenceDecs_decodeSync_safe_amd64_fill_byte_by_byte + +sequenceDecs_decodeSync_safe_amd64_fill_check_overread: + CMPQ BX, $0x40 + JA error_overread + +sequenceDecs_decodeSync_safe_amd64_fill_end: + // Update offset + MOVQ R9, AX + MOVQ BX, CX + MOVQ DX, R14 + SHLQ CL, R14 + MOVB AH, CL + SHRQ $0x20, AX + TESTQ CX, CX + JZ sequenceDecs_decodeSync_safe_amd64_of_update_zero + ADDQ CX, BX + CMPQ BX, $0x40 + JA sequenceDecs_decodeSync_safe_amd64_of_update_zero + CMPQ CX, $0x40 + JAE sequenceDecs_decodeSync_safe_amd64_of_update_zero + NEGQ CX + SHRQ CL, R14 + ADDQ R14, AX + +sequenceDecs_decodeSync_safe_amd64_of_update_zero: + MOVQ AX, 8(SP) + + // Update match length + MOVQ R8, AX + MOVQ BX, CX + MOVQ DX, R14 + SHLQ CL, R14 + MOVB AH, CL + SHRQ $0x20, AX + TESTQ CX, CX + JZ sequenceDecs_decodeSync_safe_amd64_ml_update_zero + ADDQ CX, BX + CMPQ BX, $0x40 + JA sequenceDecs_decodeSync_safe_amd64_ml_update_zero + CMPQ CX, $0x40 + JAE sequenceDecs_decodeSync_safe_amd64_ml_update_zero + NEGQ CX + SHRQ CL, R14 + ADDQ R14, AX + +sequenceDecs_decodeSync_safe_amd64_ml_update_zero: + MOVQ AX, 16(SP) + + // Fill bitreader to have enough for the remaining + CMPQ SI, $0x08 + JL sequenceDecs_decodeSync_safe_amd64_fill_2_byte_by_byte + MOVQ BX, AX + SHRQ $0x03, AX + SUBQ AX, R13 + MOVQ (R13), DX + SUBQ AX, SI + ANDQ $0x07, BX + JMP sequenceDecs_decodeSync_safe_amd64_fill_2_end + +sequenceDecs_decodeSync_safe_amd64_fill_2_byte_by_byte: + CMPQ SI, $0x00 + JLE sequenceDecs_decodeSync_safe_amd64_fill_2_check_overread + CMPQ BX, $0x07 + JLE sequenceDecs_decodeSync_safe_amd64_fill_2_end + SHLQ $0x08, DX + SUBQ $0x01, R13 + SUBQ $0x01, SI + SUBQ $0x08, BX + MOVBQZX (R13), AX + ORQ AX, DX + JMP sequenceDecs_decodeSync_safe_amd64_fill_2_byte_by_byte + +sequenceDecs_decodeSync_safe_amd64_fill_2_check_overread: + CMPQ BX, $0x40 + JA error_overread + +sequenceDecs_decodeSync_safe_amd64_fill_2_end: + // Update literal length + MOVQ DI, AX + MOVQ BX, CX + MOVQ DX, R14 + SHLQ CL, R14 + MOVB AH, CL + SHRQ $0x20, AX + TESTQ CX, CX + JZ sequenceDecs_decodeSync_safe_amd64_ll_update_zero + ADDQ CX, BX + CMPQ BX, $0x40 + JA sequenceDecs_decodeSync_safe_amd64_ll_update_zero + CMPQ CX, $0x40 + JAE sequenceDecs_decodeSync_safe_amd64_ll_update_zero + NEGQ CX + SHRQ CL, R14 + ADDQ R14, AX + +sequenceDecs_decodeSync_safe_amd64_ll_update_zero: + MOVQ AX, 24(SP) + + // Fill bitreader for state updates + MOVQ R13, (SP) + MOVQ R9, AX + SHRQ $0x08, AX + MOVBQZX AL, AX + MOVQ ctx+16(FP), CX + CMPQ 96(CX), $0x00 + JZ sequenceDecs_decodeSync_safe_amd64_skip_update + + // Update Literal Length State + MOVBQZX DI, R13 + SHRL $0x10, DI + LEAQ (BX)(R13*1), CX + MOVQ DX, R14 + MOVQ CX, BX + ROLQ CL, R14 + MOVL $0x00000001, R15 + MOVB R13, CL + SHLL CL, R15 + DECL R15 + ANDQ R15, R14 + ADDQ R14, DI + + // Load ctx.llTable + MOVQ ctx+16(FP), CX + MOVQ (CX), CX + MOVQ (CX)(DI*8), DI + + // Update Match Length State + MOVBQZX R8, R13 + SHRL $0x10, R8 + LEAQ (BX)(R13*1), CX + MOVQ DX, R14 + MOVQ CX, BX + ROLQ CL, R14 + MOVL $0x00000001, R15 + MOVB R13, CL + SHLL CL, R15 + DECL R15 + ANDQ R15, R14 + ADDQ R14, R8 + + // Load ctx.mlTable + MOVQ ctx+16(FP), CX + MOVQ 24(CX), CX + MOVQ (CX)(R8*8), R8 + + // Update Offset State + MOVBQZX R9, R13 + SHRL $0x10, R9 + LEAQ (BX)(R13*1), CX + MOVQ DX, R14 + MOVQ CX, BX + ROLQ CL, R14 + MOVL $0x00000001, R15 + MOVB R13, CL + SHLL CL, R15 + DECL R15 + ANDQ R15, R14 + ADDQ R14, R9 + + // Load ctx.ofTable + MOVQ ctx+16(FP), CX + MOVQ 48(CX), CX + MOVQ (CX)(R9*8), R9 + +sequenceDecs_decodeSync_safe_amd64_skip_update: + // Adjust offset + MOVQ s+0(FP), CX + MOVQ 8(SP), R13 + CMPQ AX, $0x01 + JBE sequenceDecs_decodeSync_safe_amd64_adjust_offsetB_1_or_0 + MOVUPS 144(CX), X0 + MOVQ R13, 144(CX) + MOVUPS X0, 152(CX) + JMP sequenceDecs_decodeSync_safe_amd64_after_adjust + +sequenceDecs_decodeSync_safe_amd64_adjust_offsetB_1_or_0: + CMPQ 24(SP), $0x00000000 + JNE sequenceDecs_decodeSync_safe_amd64_adjust_offset_maybezero + INCQ R13 + JMP sequenceDecs_decodeSync_safe_amd64_adjust_offset_nonzero + +sequenceDecs_decodeSync_safe_amd64_adjust_offset_maybezero: + TESTQ R13, R13 + JNZ sequenceDecs_decodeSync_safe_amd64_adjust_offset_nonzero + MOVQ 144(CX), R13 + JMP sequenceDecs_decodeSync_safe_amd64_after_adjust + +sequenceDecs_decodeSync_safe_amd64_adjust_offset_nonzero: + MOVQ R13, AX + XORQ R14, R14 + MOVQ $-1, R15 + CMPQ R13, $0x03 + CMOVQEQ R14, AX + CMOVQEQ R15, R14 + ADDQ 144(CX)(AX*8), R14 + JNZ sequenceDecs_decodeSync_safe_amd64_adjust_temp_valid + MOVQ $0x00000001, R14 + +sequenceDecs_decodeSync_safe_amd64_adjust_temp_valid: + CMPQ R13, $0x01 + JZ sequenceDecs_decodeSync_safe_amd64_adjust_skip + MOVQ 152(CX), AX + MOVQ AX, 160(CX) + +sequenceDecs_decodeSync_safe_amd64_adjust_skip: + MOVQ 144(CX), AX + MOVQ AX, 152(CX) + MOVQ R14, 144(CX) + MOVQ R14, R13 + +sequenceDecs_decodeSync_safe_amd64_after_adjust: + MOVQ R13, 8(SP) + + // Check values + MOVQ 16(SP), AX + MOVQ 24(SP), CX + LEAQ (AX)(CX*1), R14 + MOVQ s+0(FP), R15 + ADDQ R14, 256(R15) + MOVQ ctx+16(FP), R14 + SUBQ CX, 104(R14) + JS error_not_enough_literals + CMPQ AX, $0x00020002 + JA sequenceDecs_decodeSync_safe_amd64_error_match_len_too_big + TESTQ R13, R13 + JNZ sequenceDecs_decodeSync_safe_amd64_match_len_ofs_ok + TESTQ AX, AX + JNZ sequenceDecs_decodeSync_safe_amd64_error_match_len_ofs_mismatch + +sequenceDecs_decodeSync_safe_amd64_match_len_ofs_ok: + MOVQ 24(SP), AX + MOVQ 8(SP), CX + MOVQ 16(SP), R13 + + // Check if we have enough space in s.out + LEAQ (AX)(R13*1), R14 + ADDQ R10, R14 + CMPQ R14, 32(SP) + JA error_not_enough_space + + // Copy literals + TESTQ AX, AX + JZ check_offset + MOVQ AX, R14 + SUBQ $0x10, R14 + JB copy_1_small + +copy_1_loop: + MOVUPS (R11), X0 + MOVUPS X0, (R10) + ADDQ $0x10, R11 + ADDQ $0x10, R10 + SUBQ $0x10, R14 + JAE copy_1_loop + LEAQ 16(R11)(R14*1), R11 + LEAQ 16(R10)(R14*1), R10 + MOVUPS -16(R11), X0 + MOVUPS X0, -16(R10) + JMP copy_1_end + +copy_1_small: + CMPQ AX, $0x03 + JE copy_1_move_3 + JB copy_1_move_1or2 + CMPQ AX, $0x08 + JB copy_1_move_4through7 + JMP copy_1_move_8through16 + +copy_1_move_1or2: + MOVB (R11), R14 + MOVB -1(R11)(AX*1), R15 + MOVB R14, (R10) + MOVB R15, -1(R10)(AX*1) + ADDQ AX, R11 + ADDQ AX, R10 + JMP copy_1_end + +copy_1_move_3: + MOVW (R11), R14 + MOVB 2(R11), R15 + MOVW R14, (R10) + MOVB R15, 2(R10) + ADDQ AX, R11 + ADDQ AX, R10 + JMP copy_1_end + +copy_1_move_4through7: + MOVL (R11), R14 + MOVL -4(R11)(AX*1), R15 + MOVL R14, (R10) + MOVL R15, -4(R10)(AX*1) + ADDQ AX, R11 + ADDQ AX, R10 + JMP copy_1_end + +copy_1_move_8through16: + MOVQ (R11), R14 + MOVQ -8(R11)(AX*1), R15 + MOVQ R14, (R10) + MOVQ R15, -8(R10)(AX*1) + ADDQ AX, R11 + ADDQ AX, R10 + +copy_1_end: + ADDQ AX, R12 + + // Malformed input if seq.mo > t+len(hist) || seq.mo > s.windowSize) +check_offset: + MOVQ R12, AX + ADDQ 40(SP), AX + CMPQ CX, AX + JG error_match_off_too_big + CMPQ CX, 56(SP) + JG error_match_off_too_big + + // Copy match from history + MOVQ CX, AX + SUBQ R12, AX + JLS copy_match + MOVQ 48(SP), R14 + SUBQ AX, R14 + CMPQ R13, AX + JG copy_all_from_history + MOVQ R13, AX + SUBQ $0x10, AX + JB copy_4_small + +copy_4_loop: + MOVUPS (R14), X0 + MOVUPS X0, (R10) + ADDQ $0x10, R14 + ADDQ $0x10, R10 + SUBQ $0x10, AX + JAE copy_4_loop + LEAQ 16(R14)(AX*1), R14 + LEAQ 16(R10)(AX*1), R10 + MOVUPS -16(R14), X0 + MOVUPS X0, -16(R10) + JMP copy_4_end + +copy_4_small: + CMPQ R13, $0x03 + JE copy_4_move_3 + CMPQ R13, $0x08 + JB copy_4_move_4through7 + JMP copy_4_move_8through16 + +copy_4_move_3: + MOVW (R14), AX + MOVB 2(R14), CL + MOVW AX, (R10) + MOVB CL, 2(R10) + ADDQ R13, R14 + ADDQ R13, R10 + JMP copy_4_end + +copy_4_move_4through7: + MOVL (R14), AX + MOVL -4(R14)(R13*1), CX + MOVL AX, (R10) + MOVL CX, -4(R10)(R13*1) + ADDQ R13, R14 + ADDQ R13, R10 + JMP copy_4_end + +copy_4_move_8through16: + MOVQ (R14), AX + MOVQ -8(R14)(R13*1), CX + MOVQ AX, (R10) + MOVQ CX, -8(R10)(R13*1) + ADDQ R13, R14 + ADDQ R13, R10 + +copy_4_end: + ADDQ R13, R12 + JMP handle_loop + JMP loop_finished + +copy_all_from_history: + MOVQ AX, R15 + SUBQ $0x10, R15 + JB copy_5_small + +copy_5_loop: + MOVUPS (R14), X0 + MOVUPS X0, (R10) + ADDQ $0x10, R14 + ADDQ $0x10, R10 + SUBQ $0x10, R15 + JAE copy_5_loop + LEAQ 16(R14)(R15*1), R14 + LEAQ 16(R10)(R15*1), R10 + MOVUPS -16(R14), X0 + MOVUPS X0, -16(R10) + JMP copy_5_end + +copy_5_small: + CMPQ AX, $0x03 + JE copy_5_move_3 + JB copy_5_move_1or2 + CMPQ AX, $0x08 + JB copy_5_move_4through7 + JMP copy_5_move_8through16 + +copy_5_move_1or2: + MOVB (R14), R15 + MOVB -1(R14)(AX*1), BP + MOVB R15, (R10) + MOVB BP, -1(R10)(AX*1) + ADDQ AX, R14 + ADDQ AX, R10 + JMP copy_5_end + +copy_5_move_3: + MOVW (R14), R15 + MOVB 2(R14), BP + MOVW R15, (R10) + MOVB BP, 2(R10) + ADDQ AX, R14 + ADDQ AX, R10 + JMP copy_5_end + +copy_5_move_4through7: + MOVL (R14), R15 + MOVL -4(R14)(AX*1), BP + MOVL R15, (R10) + MOVL BP, -4(R10)(AX*1) + ADDQ AX, R14 + ADDQ AX, R10 + JMP copy_5_end + +copy_5_move_8through16: + MOVQ (R14), R15 + MOVQ -8(R14)(AX*1), BP + MOVQ R15, (R10) + MOVQ BP, -8(R10)(AX*1) + ADDQ AX, R14 + ADDQ AX, R10 + +copy_5_end: + ADDQ AX, R12 + SUBQ AX, R13 + + // Copy match from the current buffer +copy_match: + MOVQ R10, AX + SUBQ CX, AX + + // ml <= mo + CMPQ R13, CX + JA copy_overlapping_match + + // Copy non-overlapping match + ADDQ R13, R12 + MOVQ R13, CX + SUBQ $0x10, CX + JB copy_2_small + +copy_2_loop: + MOVUPS (AX), X0 + MOVUPS X0, (R10) + ADDQ $0x10, AX + ADDQ $0x10, R10 + SUBQ $0x10, CX + JAE copy_2_loop + LEAQ 16(AX)(CX*1), AX + LEAQ 16(R10)(CX*1), R10 + MOVUPS -16(AX), X0 + MOVUPS X0, -16(R10) + JMP copy_2_end + +copy_2_small: + CMPQ R13, $0x03 + JE copy_2_move_3 + JB copy_2_move_1or2 + CMPQ R13, $0x08 + JB copy_2_move_4through7 + JMP copy_2_move_8through16 + +copy_2_move_1or2: + MOVB (AX), CL + MOVB -1(AX)(R13*1), R14 + MOVB CL, (R10) + MOVB R14, -1(R10)(R13*1) + ADDQ R13, AX + ADDQ R13, R10 + JMP copy_2_end + +copy_2_move_3: + MOVW (AX), CX + MOVB 2(AX), R14 + MOVW CX, (R10) + MOVB R14, 2(R10) + ADDQ R13, AX + ADDQ R13, R10 + JMP copy_2_end + +copy_2_move_4through7: + MOVL (AX), CX + MOVL -4(AX)(R13*1), R14 + MOVL CX, (R10) + MOVL R14, -4(R10)(R13*1) + ADDQ R13, AX + ADDQ R13, R10 + JMP copy_2_end + +copy_2_move_8through16: + MOVQ (AX), CX + MOVQ -8(AX)(R13*1), R14 + MOVQ CX, (R10) + MOVQ R14, -8(R10)(R13*1) + ADDQ R13, AX + ADDQ R13, R10 + +copy_2_end: + JMP handle_loop + + // Copy overlapping match +copy_overlapping_match: + ADDQ R13, R12 + +copy_slow_3: + MOVB (AX), CL + MOVB CL, (R10) + INCQ AX + INCQ R10 + DECQ R13 + JNZ copy_slow_3 + +handle_loop: + MOVQ ctx+16(FP), AX + DECQ 96(AX) + JNS sequenceDecs_decodeSync_safe_amd64_main_loop + +loop_finished: + MOVQ br+8(FP), AX + MOVQ DX, 24(AX) + MOVB BL, 40(AX) + MOVQ SI, 32(AX) + + // Update the context + MOVQ ctx+16(FP), AX + MOVQ R12, 136(AX) + MOVQ 144(AX), CX + SUBQ CX, R11 + MOVQ R11, 168(AX) + + // Return success + MOVQ $0x00000000, ret+24(FP) + RET + + // Return with match length error +sequenceDecs_decodeSync_safe_amd64_error_match_len_ofs_mismatch: + MOVQ 16(SP), AX + MOVQ ctx+16(FP), CX + MOVQ AX, 216(CX) + MOVQ $0x00000001, ret+24(FP) + RET + + // Return with match too long error +sequenceDecs_decodeSync_safe_amd64_error_match_len_too_big: + MOVQ ctx+16(FP), AX + MOVQ 16(SP), CX + MOVQ CX, 216(AX) + MOVQ $0x00000002, ret+24(FP) + RET + + // Return with match offset too long error +error_match_off_too_big: + MOVQ ctx+16(FP), AX + MOVQ 8(SP), CX + MOVQ CX, 224(AX) + MOVQ R12, 136(AX) + MOVQ $0x00000003, ret+24(FP) + RET + + // Return with not enough literals error +error_not_enough_literals: + MOVQ ctx+16(FP), AX + MOVQ 24(SP), CX + MOVQ CX, 208(AX) + MOVQ $0x00000004, ret+24(FP) + RET + + // Return with overread error +error_overread: + MOVQ $0x00000006, ret+24(FP) + RET + + // Return with not enough output space error +error_not_enough_space: + MOVQ ctx+16(FP), AX + MOVQ 24(SP), CX + MOVQ CX, 208(AX) + MOVQ 16(SP), CX + MOVQ CX, 216(AX) + MOVQ R12, 136(AX) + MOVQ $0x00000005, ret+24(FP) + RET + +// func sequenceDecs_decodeSync_safe_bmi2(s *sequenceDecs, br *bitReader, ctx *decodeSyncAsmContext) int +// Requires: BMI, BMI2, CMOV, SSE +TEXT ·sequenceDecs_decodeSync_safe_bmi2(SB), $64-32 + MOVQ br+8(FP), BX + MOVQ 24(BX), AX + MOVBQZX 40(BX), DX + MOVQ (BX), CX + MOVQ 32(BX), BX + ADDQ BX, CX + MOVQ CX, (SP) + MOVQ ctx+16(FP), CX + MOVQ 72(CX), SI + MOVQ 80(CX), DI + MOVQ 88(CX), R8 + XORQ R9, R9 + MOVQ R9, 8(SP) + MOVQ R9, 16(SP) + MOVQ R9, 24(SP) + MOVQ 112(CX), R9 + MOVQ 128(CX), R10 + MOVQ R10, 32(SP) + MOVQ 144(CX), R10 + MOVQ 136(CX), R11 + MOVQ 200(CX), R12 + MOVQ R12, 56(SP) + MOVQ 176(CX), R12 + MOVQ R12, 48(SP) + MOVQ 184(CX), CX + MOVQ CX, 40(SP) + MOVQ 40(SP), CX + ADDQ CX, 48(SP) + + // Calculate pointer to s.out[cap(s.out)] (a past-end pointer) + ADDQ R9, 32(SP) + + // outBase += outPosition + ADDQ R11, R9 + +sequenceDecs_decodeSync_safe_bmi2_main_loop: + MOVQ (SP), R12 + + // Fill bitreader to have enough for the offset and match length. + CMPQ BX, $0x08 + JL sequenceDecs_decodeSync_safe_bmi2_fill_byte_by_byte + MOVQ DX, CX + SHRQ $0x03, CX + SUBQ CX, R12 + MOVQ (R12), AX + SUBQ CX, BX + ANDQ $0x07, DX + JMP sequenceDecs_decodeSync_safe_bmi2_fill_end + +sequenceDecs_decodeSync_safe_bmi2_fill_byte_by_byte: + CMPQ BX, $0x00 + JLE sequenceDecs_decodeSync_safe_bmi2_fill_check_overread + CMPQ DX, $0x07 + JLE sequenceDecs_decodeSync_safe_bmi2_fill_end + SHLQ $0x08, AX + SUBQ $0x01, R12 + SUBQ $0x01, BX + SUBQ $0x08, DX + MOVBQZX (R12), CX + ORQ CX, AX + JMP sequenceDecs_decodeSync_safe_bmi2_fill_byte_by_byte + +sequenceDecs_decodeSync_safe_bmi2_fill_check_overread: + CMPQ DX, $0x40 + JA error_overread + +sequenceDecs_decodeSync_safe_bmi2_fill_end: + // Update offset + MOVQ $0x00000808, CX + BEXTRQ CX, R8, R13 + MOVQ AX, R14 + LEAQ (DX)(R13*1), CX + ROLQ CL, R14 + BZHIQ R13, R14, R14 + MOVQ CX, DX + MOVQ R8, CX + SHRQ $0x20, CX + ADDQ R14, CX + MOVQ CX, 8(SP) + + // Update match length + MOVQ $0x00000808, CX + BEXTRQ CX, DI, R13 + MOVQ AX, R14 + LEAQ (DX)(R13*1), CX + ROLQ CL, R14 + BZHIQ R13, R14, R14 + MOVQ CX, DX + MOVQ DI, CX + SHRQ $0x20, CX + ADDQ R14, CX + MOVQ CX, 16(SP) + + // Fill bitreader to have enough for the remaining + CMPQ BX, $0x08 + JL sequenceDecs_decodeSync_safe_bmi2_fill_2_byte_by_byte + MOVQ DX, CX + SHRQ $0x03, CX + SUBQ CX, R12 + MOVQ (R12), AX + SUBQ CX, BX + ANDQ $0x07, DX + JMP sequenceDecs_decodeSync_safe_bmi2_fill_2_end + +sequenceDecs_decodeSync_safe_bmi2_fill_2_byte_by_byte: + CMPQ BX, $0x00 + JLE sequenceDecs_decodeSync_safe_bmi2_fill_2_check_overread + CMPQ DX, $0x07 + JLE sequenceDecs_decodeSync_safe_bmi2_fill_2_end + SHLQ $0x08, AX + SUBQ $0x01, R12 + SUBQ $0x01, BX + SUBQ $0x08, DX + MOVBQZX (R12), CX + ORQ CX, AX + JMP sequenceDecs_decodeSync_safe_bmi2_fill_2_byte_by_byte + +sequenceDecs_decodeSync_safe_bmi2_fill_2_check_overread: + CMPQ DX, $0x40 + JA error_overread + +sequenceDecs_decodeSync_safe_bmi2_fill_2_end: + // Update literal length + MOVQ $0x00000808, CX + BEXTRQ CX, SI, R13 + MOVQ AX, R14 + LEAQ (DX)(R13*1), CX + ROLQ CL, R14 + BZHIQ R13, R14, R14 + MOVQ CX, DX + MOVQ SI, CX + SHRQ $0x20, CX + ADDQ R14, CX + MOVQ CX, 24(SP) + + // Fill bitreader for state updates + MOVQ R12, (SP) + MOVQ $0x00000808, CX + BEXTRQ CX, R8, R12 + MOVQ ctx+16(FP), CX + CMPQ 96(CX), $0x00 + JZ sequenceDecs_decodeSync_safe_bmi2_skip_update + LEAQ (SI)(DI*1), R13 + ADDQ R8, R13 + MOVBQZX R13, R13 + LEAQ (DX)(R13*1), CX + MOVQ AX, R14 + MOVQ CX, DX + ROLQ CL, R14 + BZHIQ R13, R14, R14 + + // Update Offset State + BZHIQ R8, R14, CX + SHRXQ R8, R14, R14 + SHRL $0x10, R8 + ADDQ CX, R8 + + // Load ctx.ofTable + MOVQ ctx+16(FP), CX + MOVQ 48(CX), CX + MOVQ (CX)(R8*8), R8 + + // Update Match Length State + BZHIQ DI, R14, CX + SHRXQ DI, R14, R14 + SHRL $0x10, DI + ADDQ CX, DI + + // Load ctx.mlTable + MOVQ ctx+16(FP), CX + MOVQ 24(CX), CX + MOVQ (CX)(DI*8), DI + + // Update Literal Length State + BZHIQ SI, R14, CX + SHRL $0x10, SI + ADDQ CX, SI + + // Load ctx.llTable + MOVQ ctx+16(FP), CX + MOVQ (CX), CX + MOVQ (CX)(SI*8), SI + +sequenceDecs_decodeSync_safe_bmi2_skip_update: + // Adjust offset + MOVQ s+0(FP), CX + MOVQ 8(SP), R13 + CMPQ R12, $0x01 + JBE sequenceDecs_decodeSync_safe_bmi2_adjust_offsetB_1_or_0 + MOVUPS 144(CX), X0 + MOVQ R13, 144(CX) + MOVUPS X0, 152(CX) + JMP sequenceDecs_decodeSync_safe_bmi2_after_adjust + +sequenceDecs_decodeSync_safe_bmi2_adjust_offsetB_1_or_0: + CMPQ 24(SP), $0x00000000 + JNE sequenceDecs_decodeSync_safe_bmi2_adjust_offset_maybezero + INCQ R13 + JMP sequenceDecs_decodeSync_safe_bmi2_adjust_offset_nonzero + +sequenceDecs_decodeSync_safe_bmi2_adjust_offset_maybezero: + TESTQ R13, R13 + JNZ sequenceDecs_decodeSync_safe_bmi2_adjust_offset_nonzero + MOVQ 144(CX), R13 + JMP sequenceDecs_decodeSync_safe_bmi2_after_adjust + +sequenceDecs_decodeSync_safe_bmi2_adjust_offset_nonzero: + MOVQ R13, R12 + XORQ R14, R14 + MOVQ $-1, R15 + CMPQ R13, $0x03 + CMOVQEQ R14, R12 + CMOVQEQ R15, R14 + ADDQ 144(CX)(R12*8), R14 + JNZ sequenceDecs_decodeSync_safe_bmi2_adjust_temp_valid + MOVQ $0x00000001, R14 + +sequenceDecs_decodeSync_safe_bmi2_adjust_temp_valid: + CMPQ R13, $0x01 + JZ sequenceDecs_decodeSync_safe_bmi2_adjust_skip + MOVQ 152(CX), R12 + MOVQ R12, 160(CX) + +sequenceDecs_decodeSync_safe_bmi2_adjust_skip: + MOVQ 144(CX), R12 + MOVQ R12, 152(CX) + MOVQ R14, 144(CX) + MOVQ R14, R13 + +sequenceDecs_decodeSync_safe_bmi2_after_adjust: + MOVQ R13, 8(SP) + + // Check values + MOVQ 16(SP), CX + MOVQ 24(SP), R12 + LEAQ (CX)(R12*1), R14 + MOVQ s+0(FP), R15 + ADDQ R14, 256(R15) + MOVQ ctx+16(FP), R14 + SUBQ R12, 104(R14) + JS error_not_enough_literals + CMPQ CX, $0x00020002 + JA sequenceDecs_decodeSync_safe_bmi2_error_match_len_too_big + TESTQ R13, R13 + JNZ sequenceDecs_decodeSync_safe_bmi2_match_len_ofs_ok + TESTQ CX, CX + JNZ sequenceDecs_decodeSync_safe_bmi2_error_match_len_ofs_mismatch + +sequenceDecs_decodeSync_safe_bmi2_match_len_ofs_ok: + MOVQ 24(SP), CX + MOVQ 8(SP), R12 + MOVQ 16(SP), R13 + + // Check if we have enough space in s.out + LEAQ (CX)(R13*1), R14 + ADDQ R9, R14 + CMPQ R14, 32(SP) + JA error_not_enough_space + + // Copy literals + TESTQ CX, CX + JZ check_offset + MOVQ CX, R14 + SUBQ $0x10, R14 + JB copy_1_small + +copy_1_loop: + MOVUPS (R10), X0 + MOVUPS X0, (R9) + ADDQ $0x10, R10 + ADDQ $0x10, R9 + SUBQ $0x10, R14 + JAE copy_1_loop + LEAQ 16(R10)(R14*1), R10 + LEAQ 16(R9)(R14*1), R9 + MOVUPS -16(R10), X0 + MOVUPS X0, -16(R9) + JMP copy_1_end + +copy_1_small: + CMPQ CX, $0x03 + JE copy_1_move_3 + JB copy_1_move_1or2 + CMPQ CX, $0x08 + JB copy_1_move_4through7 + JMP copy_1_move_8through16 + +copy_1_move_1or2: + MOVB (R10), R14 + MOVB -1(R10)(CX*1), R15 + MOVB R14, (R9) + MOVB R15, -1(R9)(CX*1) + ADDQ CX, R10 + ADDQ CX, R9 + JMP copy_1_end + +copy_1_move_3: + MOVW (R10), R14 + MOVB 2(R10), R15 + MOVW R14, (R9) + MOVB R15, 2(R9) + ADDQ CX, R10 + ADDQ CX, R9 + JMP copy_1_end + +copy_1_move_4through7: + MOVL (R10), R14 + MOVL -4(R10)(CX*1), R15 + MOVL R14, (R9) + MOVL R15, -4(R9)(CX*1) + ADDQ CX, R10 + ADDQ CX, R9 + JMP copy_1_end + +copy_1_move_8through16: + MOVQ (R10), R14 + MOVQ -8(R10)(CX*1), R15 + MOVQ R14, (R9) + MOVQ R15, -8(R9)(CX*1) + ADDQ CX, R10 + ADDQ CX, R9 + +copy_1_end: + ADDQ CX, R11 + + // Malformed input if seq.mo > t+len(hist) || seq.mo > s.windowSize) +check_offset: + MOVQ R11, CX + ADDQ 40(SP), CX + CMPQ R12, CX + JG error_match_off_too_big + CMPQ R12, 56(SP) + JG error_match_off_too_big + + // Copy match from history + MOVQ R12, CX + SUBQ R11, CX + JLS copy_match + MOVQ 48(SP), R14 + SUBQ CX, R14 + CMPQ R13, CX + JG copy_all_from_history + MOVQ R13, CX + SUBQ $0x10, CX + JB copy_4_small + +copy_4_loop: + MOVUPS (R14), X0 + MOVUPS X0, (R9) + ADDQ $0x10, R14 + ADDQ $0x10, R9 + SUBQ $0x10, CX + JAE copy_4_loop + LEAQ 16(R14)(CX*1), R14 + LEAQ 16(R9)(CX*1), R9 + MOVUPS -16(R14), X0 + MOVUPS X0, -16(R9) + JMP copy_4_end + +copy_4_small: + CMPQ R13, $0x03 + JE copy_4_move_3 + CMPQ R13, $0x08 + JB copy_4_move_4through7 + JMP copy_4_move_8through16 + +copy_4_move_3: + MOVW (R14), CX + MOVB 2(R14), R12 + MOVW CX, (R9) + MOVB R12, 2(R9) + ADDQ R13, R14 + ADDQ R13, R9 + JMP copy_4_end + +copy_4_move_4through7: + MOVL (R14), CX + MOVL -4(R14)(R13*1), R12 + MOVL CX, (R9) + MOVL R12, -4(R9)(R13*1) + ADDQ R13, R14 + ADDQ R13, R9 + JMP copy_4_end + +copy_4_move_8through16: + MOVQ (R14), CX + MOVQ -8(R14)(R13*1), R12 + MOVQ CX, (R9) + MOVQ R12, -8(R9)(R13*1) + ADDQ R13, R14 + ADDQ R13, R9 + +copy_4_end: + ADDQ R13, R11 + JMP handle_loop + JMP loop_finished + +copy_all_from_history: + MOVQ CX, R15 + SUBQ $0x10, R15 + JB copy_5_small + +copy_5_loop: + MOVUPS (R14), X0 + MOVUPS X0, (R9) + ADDQ $0x10, R14 + ADDQ $0x10, R9 + SUBQ $0x10, R15 + JAE copy_5_loop + LEAQ 16(R14)(R15*1), R14 + LEAQ 16(R9)(R15*1), R9 + MOVUPS -16(R14), X0 + MOVUPS X0, -16(R9) + JMP copy_5_end + +copy_5_small: + CMPQ CX, $0x03 + JE copy_5_move_3 + JB copy_5_move_1or2 + CMPQ CX, $0x08 + JB copy_5_move_4through7 + JMP copy_5_move_8through16 + +copy_5_move_1or2: + MOVB (R14), R15 + MOVB -1(R14)(CX*1), BP + MOVB R15, (R9) + MOVB BP, -1(R9)(CX*1) + ADDQ CX, R14 + ADDQ CX, R9 + JMP copy_5_end + +copy_5_move_3: + MOVW (R14), R15 + MOVB 2(R14), BP + MOVW R15, (R9) + MOVB BP, 2(R9) + ADDQ CX, R14 + ADDQ CX, R9 + JMP copy_5_end + +copy_5_move_4through7: + MOVL (R14), R15 + MOVL -4(R14)(CX*1), BP + MOVL R15, (R9) + MOVL BP, -4(R9)(CX*1) + ADDQ CX, R14 + ADDQ CX, R9 + JMP copy_5_end + +copy_5_move_8through16: + MOVQ (R14), R15 + MOVQ -8(R14)(CX*1), BP + MOVQ R15, (R9) + MOVQ BP, -8(R9)(CX*1) + ADDQ CX, R14 + ADDQ CX, R9 + +copy_5_end: + ADDQ CX, R11 + SUBQ CX, R13 + + // Copy match from the current buffer +copy_match: + MOVQ R9, CX + SUBQ R12, CX + + // ml <= mo + CMPQ R13, R12 + JA copy_overlapping_match + + // Copy non-overlapping match + ADDQ R13, R11 + MOVQ R13, R12 + SUBQ $0x10, R12 + JB copy_2_small + +copy_2_loop: + MOVUPS (CX), X0 + MOVUPS X0, (R9) + ADDQ $0x10, CX + ADDQ $0x10, R9 + SUBQ $0x10, R12 + JAE copy_2_loop + LEAQ 16(CX)(R12*1), CX + LEAQ 16(R9)(R12*1), R9 + MOVUPS -16(CX), X0 + MOVUPS X0, -16(R9) + JMP copy_2_end + +copy_2_small: + CMPQ R13, $0x03 + JE copy_2_move_3 + JB copy_2_move_1or2 + CMPQ R13, $0x08 + JB copy_2_move_4through7 + JMP copy_2_move_8through16 + +copy_2_move_1or2: + MOVB (CX), R12 + MOVB -1(CX)(R13*1), R14 + MOVB R12, (R9) + MOVB R14, -1(R9)(R13*1) + ADDQ R13, CX + ADDQ R13, R9 + JMP copy_2_end + +copy_2_move_3: + MOVW (CX), R12 + MOVB 2(CX), R14 + MOVW R12, (R9) + MOVB R14, 2(R9) + ADDQ R13, CX + ADDQ R13, R9 + JMP copy_2_end + +copy_2_move_4through7: + MOVL (CX), R12 + MOVL -4(CX)(R13*1), R14 + MOVL R12, (R9) + MOVL R14, -4(R9)(R13*1) + ADDQ R13, CX + ADDQ R13, R9 + JMP copy_2_end + +copy_2_move_8through16: + MOVQ (CX), R12 + MOVQ -8(CX)(R13*1), R14 + MOVQ R12, (R9) + MOVQ R14, -8(R9)(R13*1) + ADDQ R13, CX + ADDQ R13, R9 + +copy_2_end: + JMP handle_loop + + // Copy overlapping match +copy_overlapping_match: + ADDQ R13, R11 + +copy_slow_3: + MOVB (CX), R12 + MOVB R12, (R9) + INCQ CX + INCQ R9 + DECQ R13 + JNZ copy_slow_3 + +handle_loop: + MOVQ ctx+16(FP), CX + DECQ 96(CX) + JNS sequenceDecs_decodeSync_safe_bmi2_main_loop + +loop_finished: + MOVQ br+8(FP), CX + MOVQ AX, 24(CX) + MOVB DL, 40(CX) + MOVQ BX, 32(CX) + + // Update the context + MOVQ ctx+16(FP), AX + MOVQ R11, 136(AX) + MOVQ 144(AX), CX + SUBQ CX, R10 + MOVQ R10, 168(AX) + + // Return success + MOVQ $0x00000000, ret+24(FP) + RET + + // Return with match length error +sequenceDecs_decodeSync_safe_bmi2_error_match_len_ofs_mismatch: + MOVQ 16(SP), AX + MOVQ ctx+16(FP), CX + MOVQ AX, 216(CX) + MOVQ $0x00000001, ret+24(FP) + RET + + // Return with match too long error +sequenceDecs_decodeSync_safe_bmi2_error_match_len_too_big: + MOVQ ctx+16(FP), AX + MOVQ 16(SP), CX + MOVQ CX, 216(AX) + MOVQ $0x00000002, ret+24(FP) + RET + + // Return with match offset too long error +error_match_off_too_big: + MOVQ ctx+16(FP), AX + MOVQ 8(SP), CX + MOVQ CX, 224(AX) + MOVQ R11, 136(AX) + MOVQ $0x00000003, ret+24(FP) + RET + + // Return with not enough literals error +error_not_enough_literals: + MOVQ ctx+16(FP), AX + MOVQ 24(SP), CX + MOVQ CX, 208(AX) + MOVQ $0x00000004, ret+24(FP) + RET + + // Return with overread error +error_overread: + MOVQ $0x00000006, ret+24(FP) + RET + + // Return with not enough output space error +error_not_enough_space: + MOVQ ctx+16(FP), AX + MOVQ 24(SP), CX + MOVQ CX, 208(AX) + MOVQ 16(SP), CX + MOVQ CX, 216(AX) + MOVQ R11, 136(AX) + MOVQ $0x00000005, ret+24(FP) + RET diff --git a/vendor/github.com/klauspost/compress/zstd/seqdec_generic.go b/vendor/github.com/klauspost/compress/zstd/seqdec_generic.go new file mode 100644 index 000000000..516cd9b07 --- /dev/null +++ b/vendor/github.com/klauspost/compress/zstd/seqdec_generic.go @@ -0,0 +1,236 @@ +//go:build !amd64 || appengine || !gc || noasm + +package zstd + +import ( + "fmt" + "io" +) + +// decode sequences from the stream with the provided history but without dictionary. +func (s *sequenceDecs) decodeSyncSimple(hist []byte) (bool, error) { + return false, nil +} + +// decode sequences from the stream without the provided history. +func (s *sequenceDecs) decode(seqs []seqVals) error { + br := s.br + + // Grab full sizes tables, to avoid bounds checks. + llTable, mlTable, ofTable := s.litLengths.fse.dt[:maxTablesize], s.matchLengths.fse.dt[:maxTablesize], s.offsets.fse.dt[:maxTablesize] + llState, mlState, ofState := s.litLengths.state.state, s.matchLengths.state.state, s.offsets.state.state + s.seqSize = 0 + litRemain := len(s.literals) + + maxBlockSize := maxCompressedBlockSize + if s.windowSize < maxBlockSize { + maxBlockSize = s.windowSize + } + for i := range seqs { + var ll, mo, ml int + if br.cursor > 4+((maxOffsetBits+16+16)>>3) { + // inlined function: + // ll, mo, ml = s.nextFast(br, llState, mlState, ofState) + + // Final will not read from stream. + var llB, mlB, moB uint8 + ll, llB = llState.final() + ml, mlB = mlState.final() + mo, moB = ofState.final() + + // extra bits are stored in reverse order. + br.fillFast() + mo += br.getBits(moB) + if s.maxBits > 32 { + br.fillFast() + } + ml += br.getBits(mlB) + ll += br.getBits(llB) + + if moB > 1 { + s.prevOffset[2] = s.prevOffset[1] + s.prevOffset[1] = s.prevOffset[0] + s.prevOffset[0] = mo + } else { + // mo = s.adjustOffset(mo, ll, moB) + // Inlined for rather big speedup + if ll == 0 { + // There is an exception though, when current sequence's literals_length = 0. + // In this case, repeated offsets are shifted by one, so an offset_value of 1 means Repeated_Offset2, + // an offset_value of 2 means Repeated_Offset3, and an offset_value of 3 means Repeated_Offset1 - 1_byte. + mo++ + } + + if mo == 0 { + mo = s.prevOffset[0] + } else { + var temp int + if mo == 3 { + temp = s.prevOffset[0] - 1 + } else { + temp = s.prevOffset[mo] + } + + if temp == 0 { + // 0 is not valid; input is corrupted; force offset to 1 + println("WARNING: temp was 0") + temp = 1 + } + + if mo != 1 { + s.prevOffset[2] = s.prevOffset[1] + } + s.prevOffset[1] = s.prevOffset[0] + s.prevOffset[0] = temp + mo = temp + } + } + br.fillFast() + } else { + if br.overread() { + if debugDecoder { + printf("reading sequence %d, exceeded available data\n", i) + } + return io.ErrUnexpectedEOF + } + ll, mo, ml = s.next(br, llState, mlState, ofState) + br.fill() + } + + if debugSequences { + println("Seq", i, "Litlen:", ll, "mo:", mo, "(abs) ml:", ml) + } + // Evaluate. + // We might be doing this async, so do it early. + if mo == 0 && ml > 0 { + return fmt.Errorf("zero matchoff and matchlen (%d) > 0", ml) + } + if ml > maxMatchLen { + return fmt.Errorf("match len (%d) bigger than max allowed length", ml) + } + s.seqSize += ll + ml + if s.seqSize > maxBlockSize { + return fmt.Errorf("output bigger than max block size (%d)", maxBlockSize) + } + litRemain -= ll + if litRemain < 0 { + return fmt.Errorf("unexpected literal count, want %d bytes, but only %d is available", ll, litRemain+ll) + } + seqs[i] = seqVals{ + ll: ll, + ml: ml, + mo: mo, + } + if i == len(seqs)-1 { + // This is the last sequence, so we shouldn't update state. + break + } + + // Manually inlined, ~ 5-20% faster + // Update all 3 states at once. Approx 20% faster. + nBits := llState.nbBits() + mlState.nbBits() + ofState.nbBits() + if nBits == 0 { + llState = llTable[llState.newState()&maxTableMask] + mlState = mlTable[mlState.newState()&maxTableMask] + ofState = ofTable[ofState.newState()&maxTableMask] + } else { + bits := br.get32BitsFast(nBits) + lowBits := uint16(bits >> ((ofState.nbBits() + mlState.nbBits()) & 31)) + llState = llTable[(llState.newState()+lowBits)&maxTableMask] + + lowBits = uint16(bits >> (ofState.nbBits() & 31)) + lowBits &= bitMask[mlState.nbBits()&15] + mlState = mlTable[(mlState.newState()+lowBits)&maxTableMask] + + lowBits = uint16(bits) & bitMask[ofState.nbBits()&15] + ofState = ofTable[(ofState.newState()+lowBits)&maxTableMask] + } + } + s.seqSize += litRemain + if s.seqSize > maxBlockSize { + return fmt.Errorf("output bigger than max block size (%d)", maxBlockSize) + } + err := br.close() + if err != nil { + printf("Closing sequences: %v, %+v\n", err, *br) + } + return err +} + +// executeSimple handles cases when a dictionary is not used. +func (s *sequenceDecs) executeSimple(seqs []seqVals, hist []byte) error { + // Ensure we have enough output size... + if len(s.out)+s.seqSize > cap(s.out) { + addBytes := s.seqSize + len(s.out) + s.out = append(s.out, make([]byte, addBytes)...) + s.out = s.out[:len(s.out)-addBytes] + } + + if debugDecoder { + printf("Execute %d seqs with literals: %d into %d bytes\n", len(seqs), len(s.literals), s.seqSize) + } + + var t = len(s.out) + out := s.out[:t+s.seqSize] + + for _, seq := range seqs { + // Add literals + copy(out[t:], s.literals[:seq.ll]) + t += seq.ll + s.literals = s.literals[seq.ll:] + + // Malformed input + if seq.mo > t+len(hist) || seq.mo > s.windowSize { + return fmt.Errorf("match offset (%d) bigger than current history (%d)", seq.mo, t+len(hist)) + } + + // Copy from history. + if v := seq.mo - t; v > 0 { + // v is the start position in history from end. + start := len(hist) - v + if seq.ml > v { + // Some goes into the current block. + // Copy remainder of history + copy(out[t:], hist[start:]) + t += v + seq.ml -= v + } else { + copy(out[t:], hist[start:start+seq.ml]) + t += seq.ml + continue + } + } + + // We must be in the current buffer now + if seq.ml > 0 { + start := t - seq.mo + if seq.ml <= t-start { + // No overlap + copy(out[t:], out[start:start+seq.ml]) + t += seq.ml + } else { + // Overlapping copy + // Extend destination slice and copy one byte at the time. + src := out[start : start+seq.ml] + dst := out[t:] + dst = dst[:len(src)] + t += len(src) + // Destination is the space we just added. + for i := range src { + dst[i] = src[i] + } + } + } + } + // Add final literals + copy(out[t:], s.literals) + if debugDecoder { + t += len(s.literals) + if t != len(out) { + panic(fmt.Errorf("length mismatch, want %d, got %d, ss: %d", len(out), t, s.seqSize)) + } + } + s.out = out + + return nil +} diff --git a/vendor/github.com/klauspost/compress/zstd/seqenc.go b/vendor/github.com/klauspost/compress/zstd/seqenc.go new file mode 100644 index 000000000..65045eabd --- /dev/null +++ b/vendor/github.com/klauspost/compress/zstd/seqenc.go @@ -0,0 +1,112 @@ +// Copyright 2019+ Klaus Post. All rights reserved. +// License information can be found in the LICENSE file. +// Based on work by Yann Collet, released under BSD License. + +package zstd + +import "math/bits" + +type seqCoders struct { + llEnc, ofEnc, mlEnc *fseEncoder + llPrev, ofPrev, mlPrev *fseEncoder +} + +// swap coders with another (block). +func (s *seqCoders) swap(other *seqCoders) { + *s, *other = *other, *s +} + +// setPrev will update the previous encoders to the actually used ones +// and make sure a fresh one is in the main slot. +func (s *seqCoders) setPrev(ll, ml, of *fseEncoder) { + compareSwap := func(used *fseEncoder, current, prev **fseEncoder) { + // We used the new one, more current to history and reuse the previous history + if *current == used { + *prev, *current = *current, *prev + c := *current + p := *prev + c.reUsed = false + p.reUsed = true + return + } + if used == *prev { + return + } + // Ensure we cannot reuse by accident + prevEnc := *prev + prevEnc.symbolLen = 0 + } + compareSwap(ll, &s.llEnc, &s.llPrev) + compareSwap(ml, &s.mlEnc, &s.mlPrev) + compareSwap(of, &s.ofEnc, &s.ofPrev) +} + +func highBit(val uint32) (n uint32) { + return uint32(bits.Len32(val) - 1) +} + +var llCodeTable = [64]byte{0, 1, 2, 3, 4, 5, 6, 7, + 8, 9, 10, 11, 12, 13, 14, 15, + 16, 16, 17, 17, 18, 18, 19, 19, + 20, 20, 20, 20, 21, 21, 21, 21, + 22, 22, 22, 22, 22, 22, 22, 22, + 23, 23, 23, 23, 23, 23, 23, 23, + 24, 24, 24, 24, 24, 24, 24, 24, + 24, 24, 24, 24, 24, 24, 24, 24} + +// Up to 6 bits +const maxLLCode = 35 + +// llBitsTable translates from ll code to number of bits. +var llBitsTable = [maxLLCode + 1]byte{ + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 1, 1, 1, 1, 2, 2, 3, 3, + 4, 6, 7, 8, 9, 10, 11, 12, + 13, 14, 15, 16} + +// llCode returns the code that represents the literal length requested. +func llCode(litLength uint32) uint8 { + const llDeltaCode = 19 + if litLength <= 63 { + return llCodeTable[litLength&63] + } + return uint8(highBit(litLength)) + llDeltaCode +} + +var mlCodeTable = [128]byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, + 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, + 32, 32, 33, 33, 34, 34, 35, 35, 36, 36, 36, 36, 37, 37, 37, 37, + 38, 38, 38, 38, 38, 38, 38, 38, 39, 39, 39, 39, 39, 39, 39, 39, + 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, + 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, + 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, + 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42} + +// Up to 6 bits +const maxMLCode = 52 + +// mlBitsTable translates from ml code to number of bits. +var mlBitsTable = [maxMLCode + 1]byte{ + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 1, 1, 1, 1, 2, 2, 3, 3, + 4, 4, 5, 7, 8, 9, 10, 11, + 12, 13, 14, 15, 16} + +// note : mlBase = matchLength - MINMATCH; +// because it's the format it's stored in seqStore->sequences +func mlCode(mlBase uint32) uint8 { + const mlDeltaCode = 36 + if mlBase <= 127 { + return mlCodeTable[mlBase&127] + } + return uint8(highBit(mlBase)) + mlDeltaCode +} + +func ofCode(offset uint32) uint8 { + // A valid offset will always be > 0. + return uint8(bits.Len32(offset) - 1) +} diff --git a/vendor/github.com/klauspost/compress/zstd/simple_go124.go b/vendor/github.com/klauspost/compress/zstd/simple_go124.go new file mode 100644 index 000000000..2efc0497b --- /dev/null +++ b/vendor/github.com/klauspost/compress/zstd/simple_go124.go @@ -0,0 +1,56 @@ +// Copyright 2025+ Klaus Post. All rights reserved. +// License information can be found in the LICENSE file. + +//go:build go1.24 + +package zstd + +import ( + "errors" + "runtime" + "sync" + "weak" +) + +var weakMu sync.Mutex +var simpleEnc weak.Pointer[Encoder] +var simpleDec weak.Pointer[Decoder] + +// EncodeTo appends the encoded data from src to dst. +func EncodeTo(dst []byte, src []byte) []byte { + weakMu.Lock() + enc := simpleEnc.Value() + if enc == nil { + var err error + enc, err = NewWriter(nil, WithEncoderConcurrency(runtime.NumCPU()), WithWindowSize(1<<20), WithLowerEncoderMem(true), WithZeroFrames(true)) + if err != nil { + panic("failed to create simple encoder: " + err.Error()) + } + simpleEnc = weak.Make(enc) + } + weakMu.Unlock() + + return enc.EncodeAll(src, dst) +} + +// DecodeTo appends the decoded data from src to dst. +// The maximum decoded size is 1GiB, +// not including what may already be in dst. +func DecodeTo(dst []byte, src []byte) ([]byte, error) { + weakMu.Lock() + dec := simpleDec.Value() + if dec == nil { + var err error + dec, err = NewReader(nil, WithDecoderConcurrency(runtime.NumCPU()), WithDecoderLowmem(true), WithDecoderMaxMemory(1<<30)) + if err != nil { + weakMu.Unlock() + return nil, errors.New("failed to create simple decoder: " + err.Error()) + } + runtime.SetFinalizer(dec, func(d *Decoder) { + d.Close() + }) + simpleDec = weak.Make(dec) + } + weakMu.Unlock() + return dec.DecodeAll(src, dst) +} diff --git a/vendor/github.com/klauspost/compress/zstd/snappy.go b/vendor/github.com/klauspost/compress/zstd/snappy.go new file mode 100644 index 000000000..336c28893 --- /dev/null +++ b/vendor/github.com/klauspost/compress/zstd/snappy.go @@ -0,0 +1,434 @@ +// Copyright 2019+ Klaus Post. All rights reserved. +// License information can be found in the LICENSE file. +// Based on work by Yann Collet, released under BSD License. + +package zstd + +import ( + "encoding/binary" + "errors" + "hash/crc32" + "io" + + "github.com/klauspost/compress/huff0" + snappy "github.com/klauspost/compress/internal/snapref" +) + +const ( + snappyTagLiteral = 0x00 + snappyTagCopy1 = 0x01 + snappyTagCopy2 = 0x02 + snappyTagCopy4 = 0x03 +) + +const ( + snappyChecksumSize = 4 + snappyMagicBody = "sNaPpY" + + // snappyMaxBlockSize is the maximum size of the input to encodeBlock. It is not + // part of the wire format per se, but some parts of the encoder assume + // that an offset fits into a uint16. + // + // Also, for the framing format (Writer type instead of Encode function), + // https://github.com/google/snappy/blob/master/framing_format.txt says + // that "the uncompressed data in a chunk must be no longer than 65536 + // bytes". + snappyMaxBlockSize = 65536 + + // snappyMaxEncodedLenOfMaxBlockSize equals MaxEncodedLen(snappyMaxBlockSize), but is + // hard coded to be a const instead of a variable, so that obufLen can also + // be a const. Their equivalence is confirmed by + // TestMaxEncodedLenOfMaxBlockSize. + snappyMaxEncodedLenOfMaxBlockSize = 76490 +) + +const ( + chunkTypeCompressedData = 0x00 + chunkTypeUncompressedData = 0x01 + chunkTypePadding = 0xfe + chunkTypeStreamIdentifier = 0xff +) + +var ( + // ErrSnappyCorrupt reports that the input is invalid. + ErrSnappyCorrupt = errors.New("snappy: corrupt input") + // ErrSnappyTooLarge reports that the uncompressed length is too large. + ErrSnappyTooLarge = errors.New("snappy: decoded block is too large") + // ErrSnappyUnsupported reports that the input isn't supported. + ErrSnappyUnsupported = errors.New("snappy: unsupported input") + + errUnsupportedLiteralLength = errors.New("snappy: unsupported literal length") +) + +// SnappyConverter can read SnappyConverter-compressed streams and convert them to zstd. +// Conversion is done by converting the stream directly from Snappy without intermediate +// full decoding. +// Therefore the compression ratio is much less than what can be done by a full decompression +// and compression, and a faulty Snappy stream may lead to a faulty Zstandard stream without +// any errors being generated. +// No CRC value is being generated and not all CRC values of the Snappy stream are checked. +// However, it provides really fast recompression of Snappy streams. +// The converter can be reused to avoid allocations, even after errors. +type SnappyConverter struct { + r io.Reader + err error + buf []byte + block *blockEnc +} + +// Convert the Snappy stream supplied in 'in' and write the zStandard stream to 'w'. +// If any error is detected on the Snappy stream it is returned. +// The number of bytes written is returned. +func (r *SnappyConverter) Convert(in io.Reader, w io.Writer) (int64, error) { + initPredefined() + r.err = nil + r.r = in + if r.block == nil { + r.block = &blockEnc{} + r.block.init() + } + r.block.initNewEncode() + if len(r.buf) != snappyMaxEncodedLenOfMaxBlockSize+snappyChecksumSize { + r.buf = make([]byte, snappyMaxEncodedLenOfMaxBlockSize+snappyChecksumSize) + } + r.block.litEnc.Reuse = huff0.ReusePolicyNone + var written int64 + var readHeader bool + { + header := frameHeader{WindowSize: snappyMaxBlockSize}.appendTo(r.buf[:0]) + + var n int + n, r.err = w.Write(header) + if r.err != nil { + return written, r.err + } + written += int64(n) + } + + for { + if !r.readFull(r.buf[:4], true) { + // Add empty last block + r.block.reset(nil) + r.block.last = true + err := r.block.encodeLits(r.block.literals, false) + if err != nil { + return written, err + } + n, err := w.Write(r.block.output) + if err != nil { + return written, err + } + written += int64(n) + + return written, r.err + } + chunkType := r.buf[0] + if !readHeader { + if chunkType != chunkTypeStreamIdentifier { + println("chunkType != chunkTypeStreamIdentifier", chunkType) + r.err = ErrSnappyCorrupt + return written, r.err + } + readHeader = true + } + chunkLen := int(r.buf[1]) | int(r.buf[2])<<8 | int(r.buf[3])<<16 + if chunkLen > len(r.buf) { + println("chunkLen > len(r.buf)", chunkType) + r.err = ErrSnappyUnsupported + return written, r.err + } + + // The chunk types are specified at + // https://github.com/google/snappy/blob/master/framing_format.txt + switch chunkType { + case chunkTypeCompressedData: + // Section 4.2. Compressed data (chunk type 0x00). + if chunkLen < snappyChecksumSize { + println("chunkLen < snappyChecksumSize", chunkLen, snappyChecksumSize) + r.err = ErrSnappyCorrupt + return written, r.err + } + buf := r.buf[:chunkLen] + if !r.readFull(buf, false) { + return written, r.err + } + //checksum := uint32(buf[0]) | uint32(buf[1])<<8 | uint32(buf[2])<<16 | uint32(buf[3])<<24 + buf = buf[snappyChecksumSize:] + + n, hdr, err := snappyDecodedLen(buf) + if err != nil { + r.err = err + return written, r.err + } + buf = buf[hdr:] + if n > snappyMaxBlockSize { + println("n > snappyMaxBlockSize", n, snappyMaxBlockSize) + r.err = ErrSnappyCorrupt + return written, r.err + } + r.block.reset(nil) + r.block.pushOffsets() + if err := decodeSnappy(r.block, buf); err != nil { + r.err = err + return written, r.err + } + if r.block.size+r.block.extraLits != n { + printf("invalid size, want %d, got %d\n", n, r.block.size+r.block.extraLits) + r.err = ErrSnappyCorrupt + return written, r.err + } + err = r.block.encode(nil, false, false) + switch err { + case errIncompressible: + r.block.popOffsets() + r.block.reset(nil) + r.block.literals, err = snappy.Decode(r.block.literals[:n], r.buf[snappyChecksumSize:chunkLen]) + if err != nil { + return written, err + } + err = r.block.encodeLits(r.block.literals, false) + if err != nil { + return written, err + } + case nil: + default: + return written, err + } + + n, r.err = w.Write(r.block.output) + if r.err != nil { + return written, r.err + } + written += int64(n) + continue + case chunkTypeUncompressedData: + if debugEncoder { + println("Uncompressed, chunklen", chunkLen) + } + // Section 4.3. Uncompressed data (chunk type 0x01). + if chunkLen < snappyChecksumSize { + println("chunkLen < snappyChecksumSize", chunkLen, snappyChecksumSize) + r.err = ErrSnappyCorrupt + return written, r.err + } + r.block.reset(nil) + buf := r.buf[:snappyChecksumSize] + if !r.readFull(buf, false) { + return written, r.err + } + checksum := uint32(buf[0]) | uint32(buf[1])<<8 | uint32(buf[2])<<16 | uint32(buf[3])<<24 + // Read directly into r.decoded instead of via r.buf. + n := chunkLen - snappyChecksumSize + if n > snappyMaxBlockSize { + println("n > snappyMaxBlockSize", n, snappyMaxBlockSize) + r.err = ErrSnappyCorrupt + return written, r.err + } + r.block.literals = r.block.literals[:n] + if !r.readFull(r.block.literals, false) { + return written, r.err + } + if snappyCRC(r.block.literals) != checksum { + println("literals crc mismatch") + r.err = ErrSnappyCorrupt + return written, r.err + } + err := r.block.encodeLits(r.block.literals, false) + if err != nil { + return written, err + } + n, r.err = w.Write(r.block.output) + if r.err != nil { + return written, r.err + } + written += int64(n) + continue + + case chunkTypeStreamIdentifier: + if debugEncoder { + println("stream id", chunkLen, len(snappyMagicBody)) + } + // Section 4.1. Stream identifier (chunk type 0xff). + if chunkLen != len(snappyMagicBody) { + println("chunkLen != len(snappyMagicBody)", chunkLen, len(snappyMagicBody)) + r.err = ErrSnappyCorrupt + return written, r.err + } + if !r.readFull(r.buf[:len(snappyMagicBody)], false) { + return written, r.err + } + for i := range len(snappyMagicBody) { + if r.buf[i] != snappyMagicBody[i] { + println("r.buf[i] != snappyMagicBody[i]", r.buf[i], snappyMagicBody[i], i) + r.err = ErrSnappyCorrupt + return written, r.err + } + } + continue + } + + if chunkType <= 0x7f { + // Section 4.5. Reserved unskippable chunks (chunk types 0x02-0x7f). + println("chunkType <= 0x7f") + r.err = ErrSnappyUnsupported + return written, r.err + } + // Section 4.4 Padding (chunk type 0xfe). + // Section 4.6. Reserved skippable chunks (chunk types 0x80-0xfd). + if !r.readFull(r.buf[:chunkLen], false) { + return written, r.err + } + } +} + +// decodeSnappy writes the decoding of src to dst. It assumes that the varint-encoded +// length of the decompressed bytes has already been read. +func decodeSnappy(blk *blockEnc, src []byte) error { + //decodeRef(make([]byte, snappyMaxBlockSize), src) + var s, length int + lits := blk.extraLits + var offset uint32 + for s < len(src) { + switch src[s] & 0x03 { + case snappyTagLiteral: + x := uint32(src[s] >> 2) + switch { + case x < 60: + s++ + case x == 60: + s += 2 + if uint(s) > uint(len(src)) { // The uint conversions catch overflow from the previous line. + println("uint(s) > uint(len(src)", s, src) + return ErrSnappyCorrupt + } + x = uint32(src[s-1]) + case x == 61: + s += 3 + if uint(s) > uint(len(src)) { // The uint conversions catch overflow from the previous line. + println("uint(s) > uint(len(src)", s, src) + return ErrSnappyCorrupt + } + x = uint32(src[s-2]) | uint32(src[s-1])<<8 + case x == 62: + s += 4 + if uint(s) > uint(len(src)) { // The uint conversions catch overflow from the previous line. + println("uint(s) > uint(len(src)", s, src) + return ErrSnappyCorrupt + } + x = uint32(src[s-3]) | uint32(src[s-2])<<8 | uint32(src[s-1])<<16 + case x == 63: + s += 5 + if uint(s) > uint(len(src)) { // The uint conversions catch overflow from the previous line. + println("uint(s) > uint(len(src)", s, src) + return ErrSnappyCorrupt + } + x = uint32(src[s-4]) | uint32(src[s-3])<<8 | uint32(src[s-2])<<16 | uint32(src[s-1])<<24 + } + if x > snappyMaxBlockSize { + println("x > snappyMaxBlockSize", x, snappyMaxBlockSize) + return ErrSnappyCorrupt + } + length = int(x) + 1 + if length <= 0 { + println("length <= 0 ", length) + + return errUnsupportedLiteralLength + } + //if length > snappyMaxBlockSize-d || uint32(length) > len(src)-s { + // return ErrSnappyCorrupt + //} + + blk.literals = append(blk.literals, src[s:s+length]...) + //println(length, "litLen") + lits += length + s += length + continue + + case snappyTagCopy1: + s += 2 + if uint(s) > uint(len(src)) { // The uint conversions catch overflow from the previous line. + println("uint(s) > uint(len(src)", s, len(src)) + return ErrSnappyCorrupt + } + length = 4 + int(src[s-2])>>2&0x7 + offset = uint32(src[s-2])&0xe0<<3 | uint32(src[s-1]) + + case snappyTagCopy2: + s += 3 + if uint(s) > uint(len(src)) { // The uint conversions catch overflow from the previous line. + println("uint(s) > uint(len(src)", s, len(src)) + return ErrSnappyCorrupt + } + length = 1 + int(src[s-3])>>2 + offset = uint32(src[s-2]) | uint32(src[s-1])<<8 + + case snappyTagCopy4: + s += 5 + if uint(s) > uint(len(src)) { // The uint conversions catch overflow from the previous line. + println("uint(s) > uint(len(src)", s, len(src)) + return ErrSnappyCorrupt + } + length = 1 + int(src[s-5])>>2 + offset = uint32(src[s-4]) | uint32(src[s-3])<<8 | uint32(src[s-2])<<16 | uint32(src[s-1])<<24 + } + + if offset <= 0 || blk.size+lits < int(offset) /*|| length > len(blk)-d */ { + println("offset <= 0 || blk.size+lits < int(offset)", offset, blk.size+lits, int(offset), blk.size, lits) + + return ErrSnappyCorrupt + } + + // Check if offset is one of the recent offsets. + // Adjusts the output offset accordingly. + // Gives a tiny bit of compression, typically around 1%. + if false { + offset = blk.matchOffset(offset, uint32(lits)) + } else { + offset += 3 + } + + blk.sequences = append(blk.sequences, seq{ + litLen: uint32(lits), + offset: offset, + matchLen: uint32(length) - zstdMinMatch, + }) + blk.size += length + lits + lits = 0 + } + blk.extraLits = lits + return nil +} + +func (r *SnappyConverter) readFull(p []byte, allowEOF bool) (ok bool) { + if _, r.err = io.ReadFull(r.r, p); r.err != nil { + if r.err == io.ErrUnexpectedEOF || (r.err == io.EOF && !allowEOF) { + r.err = ErrSnappyCorrupt + } + return false + } + return true +} + +var crcTable = crc32.MakeTable(crc32.Castagnoli) + +// crc implements the checksum specified in section 3 of +// https://github.com/google/snappy/blob/master/framing_format.txt +func snappyCRC(b []byte) uint32 { + c := crc32.Update(0, crcTable, b) + return c>>15 | c<<17 + 0xa282ead8 +} + +// snappyDecodedLen returns the length of the decoded block and the number of bytes +// that the length header occupied. +func snappyDecodedLen(src []byte) (blockLen, headerLen int, err error) { + v, n := binary.Uvarint(src) + if n <= 0 || v > 0xffffffff { + return 0, 0, ErrSnappyCorrupt + } + + const wordSize = 32 << (^uint(0) >> 32 & 1) + if wordSize == 32 && v > 0x7fffffff { + return 0, 0, ErrSnappyTooLarge + } + return int(v), n, nil +} diff --git a/vendor/github.com/klauspost/compress/zstd/zip.go b/vendor/github.com/klauspost/compress/zstd/zip.go new file mode 100644 index 000000000..3198d7189 --- /dev/null +++ b/vendor/github.com/klauspost/compress/zstd/zip.go @@ -0,0 +1,141 @@ +// Copyright 2019+ Klaus Post. All rights reserved. +// License information can be found in the LICENSE file. + +package zstd + +import ( + "errors" + "io" + "sync" +) + +// ZipMethodWinZip is the method for Zstandard compressed data inside Zip files for WinZip. +// See https://www.winzip.com/win/en/comp_info.html +const ZipMethodWinZip = 93 + +// ZipMethodPKWare is the original method number used by PKWARE to indicate Zstandard compression. +// Deprecated: This has been deprecated by PKWARE, use ZipMethodWinZip instead for compression. +// See https://pkware.cachefly.net/webdocs/APPNOTE/APPNOTE-6.3.9.TXT +const ZipMethodPKWare = 20 + +// zipReaderPool is the default reader pool. +var zipReaderPool = sync.Pool{New: func() any { + z, err := NewReader(nil, WithDecoderLowmem(true), WithDecoderMaxWindow(128<<20), WithDecoderConcurrency(1)) + if err != nil { + panic(err) + } + return z +}} + +// newZipReader creates a pooled zip decompressor. +func newZipReader(opts ...DOption) func(r io.Reader) io.ReadCloser { + pool := &zipReaderPool + if len(opts) > 0 { + opts = append([]DOption{WithDecoderLowmem(true), WithDecoderMaxWindow(128 << 20)}, opts...) + // Force concurrency 1 + opts = append(opts, WithDecoderConcurrency(1)) + // Create our own pool + pool = &sync.Pool{} + } + return func(r io.Reader) io.ReadCloser { + dec, ok := pool.Get().(*Decoder) + if ok { + dec.Reset(r) + } else { + d, err := NewReader(r, opts...) + if err != nil { + panic(err) + } + dec = d + } + return &pooledZipReader{dec: dec, pool: pool} + } +} + +type pooledZipReader struct { + mu sync.Mutex // guards Close and Read + pool *sync.Pool + dec *Decoder +} + +func (r *pooledZipReader) Read(p []byte) (n int, err error) { + r.mu.Lock() + defer r.mu.Unlock() + if r.dec == nil { + return 0, errors.New("read after close or EOF") + } + dec, err := r.dec.Read(p) + if err == io.EOF { + r.dec.Reset(nil) + r.pool.Put(r.dec) + r.dec = nil + } + return dec, err +} + +func (r *pooledZipReader) Close() error { + r.mu.Lock() + defer r.mu.Unlock() + var err error + if r.dec != nil { + err = r.dec.Reset(nil) + r.pool.Put(r.dec) + r.dec = nil + } + return err +} + +type pooledZipWriter struct { + mu sync.Mutex // guards Close and Read + enc *Encoder + pool *sync.Pool +} + +func (w *pooledZipWriter) Write(p []byte) (n int, err error) { + w.mu.Lock() + defer w.mu.Unlock() + if w.enc == nil { + return 0, errors.New("Write after Close") + } + return w.enc.Write(p) +} + +func (w *pooledZipWriter) Close() error { + w.mu.Lock() + defer w.mu.Unlock() + var err error + if w.enc != nil { + err = w.enc.Close() + w.pool.Put(w.enc) + w.enc = nil + } + return err +} + +// ZipCompressor returns a compressor that can be registered with zip libraries. +// The provided encoder options will be used on all encodes. +func ZipCompressor(opts ...EOption) func(w io.Writer) (io.WriteCloser, error) { + var pool sync.Pool + return func(w io.Writer) (io.WriteCloser, error) { + enc, ok := pool.Get().(*Encoder) + if ok { + enc.Reset(w) + } else { + var err error + enc, err = NewWriter(w, opts...) + if err != nil { + return nil, err + } + } + return &pooledZipWriter{enc: enc, pool: &pool}, nil + } +} + +// ZipDecompressor returns a decompressor that can be registered with zip libraries. +// See ZipCompressor for example. +// Options can be specified. WithDecoderConcurrency(1) is forced, +// and by default a 128MB maximum decompression window is specified. +// The window size can be overridden if required. +func ZipDecompressor(opts ...DOption) func(r io.Reader) io.ReadCloser { + return newZipReader(opts...) +} diff --git a/vendor/github.com/klauspost/compress/zstd/zstd.go b/vendor/github.com/klauspost/compress/zstd/zstd.go new file mode 100644 index 000000000..1a869710d --- /dev/null +++ b/vendor/github.com/klauspost/compress/zstd/zstd.go @@ -0,0 +1,126 @@ +// Package zstd provides decompression of zstandard files. +// +// For advanced usage and examples, go to the README: https://github.com/klauspost/compress/tree/master/zstd#zstd +package zstd + +import ( + "bytes" + "errors" + "log" + "math" + + "github.com/klauspost/compress/internal/le" +) + +// enable debug printing +const debug = false + +// enable encoding debug printing +const debugEncoder = debug + +// enable decoding debug printing +const debugDecoder = debug + +// Enable extra assertions. +const debugAsserts = debug || false + +// print sequence details +const debugSequences = false + +// print detailed matching information +const debugMatches = false + +// force encoder to use predefined tables. +const forcePreDef = false + +// zstdMinMatch is the minimum zstd match length. +const zstdMinMatch = 3 + +// fcsUnknown is used for unknown frame content size. +const fcsUnknown = math.MaxUint64 + +var ( + // ErrReservedBlockType is returned when a reserved block type is found. + // Typically this indicates wrong or corrupted input. + ErrReservedBlockType = errors.New("invalid input: reserved block type encountered") + + // ErrCompressedSizeTooBig is returned when a block is bigger than allowed. + // Typically this indicates wrong or corrupted input. + ErrCompressedSizeTooBig = errors.New("invalid input: compressed size too big") + + // ErrBlockTooSmall is returned when a block is too small to be decoded. + // Typically returned on invalid input. + ErrBlockTooSmall = errors.New("block too small") + + // ErrUnexpectedBlockSize is returned when a block has unexpected size. + // Typically returned on invalid input. + ErrUnexpectedBlockSize = errors.New("unexpected block size") + + // ErrMagicMismatch is returned when a "magic" number isn't what is expected. + // Typically this indicates wrong or corrupted input. + ErrMagicMismatch = errors.New("invalid input: magic number mismatch") + + // ErrWindowSizeExceeded is returned when a reference exceeds the valid window size. + // Typically this indicates wrong or corrupted input. + ErrWindowSizeExceeded = errors.New("window size exceeded") + + // ErrWindowSizeTooSmall is returned when no window size is specified. + // Typically this indicates wrong or corrupted input. + ErrWindowSizeTooSmall = errors.New("invalid input: window size was too small") + + // ErrDecoderSizeExceeded is returned if decompressed size exceeds the configured limit. + ErrDecoderSizeExceeded = errors.New("decompressed size exceeds configured limit") + + // ErrUnknownDictionary is returned if the dictionary ID is unknown. + ErrUnknownDictionary = errors.New("unknown dictionary") + + // ErrFrameSizeExceeded is returned if the stated frame size is exceeded. + // This is only returned if SingleSegment is specified on the frame. + ErrFrameSizeExceeded = errors.New("frame size exceeded") + + // ErrFrameSizeMismatch is returned if the stated frame size does not match the expected size. + // This is only returned if SingleSegment is specified on the frame. + ErrFrameSizeMismatch = errors.New("frame size does not match size on stream") + + // ErrCRCMismatch is returned if CRC mismatches. + ErrCRCMismatch = errors.New("CRC check failed") + + // ErrDecoderClosed will be returned if the Decoder was used after + // Close has been called. + ErrDecoderClosed = errors.New("decoder used after Close") + + // ErrEncoderClosed will be returned if the Encoder was used after + // Close has been called. + ErrEncoderClosed = errors.New("encoder used after Close") + + // ErrDecoderNilInput is returned when a nil Reader was provided + // and an operation other than Reset/DecodeAll/Close was attempted. + ErrDecoderNilInput = errors.New("nil input provided as reader") +) + +func println(a ...any) { + if debug || debugDecoder || debugEncoder { + log.Println(a...) + } +} + +func printf(format string, a ...any) { + if debug || debugDecoder || debugEncoder { + log.Printf(format, a...) + } +} + +func load3232(b []byte, i int32) uint32 { + return le.Load32(b, i) +} + +func load6432(b []byte, i int32) uint64 { + return le.Load64(b, i) +} + +type byter interface { + Bytes() []byte + Len() int +} + +var _ byter = &bytes.Buffer{} diff --git a/vendor/github.com/netobserv/flowlogs-pipeline/pkg/api/api.go b/vendor/github.com/netobserv/flowlogs-pipeline/pkg/api/api.go index 574d9f723..468549c8d 100644 --- a/vendor/github.com/netobserv/flowlogs-pipeline/pkg/api/api.go +++ b/vendor/github.com/netobserv/flowlogs-pipeline/pkg/api/api.go @@ -42,6 +42,7 @@ const ( FilterType = "filter" ConnTrackType = "conntrack" NoneType = "none" + FlowBufferType = "flowBuffer" TagYaml = "yaml" TagDoc = "doc" @@ -63,6 +64,7 @@ type API struct { WriteLoki WriteLoki `yaml:"loki" doc:"## Write Loki API\nFollowing is the supported API format for writing to loki:\n"` WriteStdout WriteStdout `yaml:"stdout" doc:"## Write Standard Output\nFollowing is the supported API format for writing to standard output:\n"` WriteIPFIX WriteIpfix `yaml:"ipfix" doc:"## Write IPFIX\nFollowing is the supported API format for writing to an IPFIX collector:\n"` + WriteFlowBuffer WriteFlowBuffer `yaml:"flowBuffer" doc:"## Write FlowBuffer API\nFollowing is the supported API format for the in-memory flow buffer and query API:\n"` ExtractAggregate Aggregates `yaml:"aggregates" doc:"## Aggregate metrics API\nFollowing is the supported API format for specifying metrics aggregations:\n"` ConnectionTracking ConnTrack `yaml:"conntrack" doc:"## Connection tracking API\nFollowing is the supported API format for specifying connection tracking:\n"` ExtractTimebased ExtractTimebased `yaml:"timebased" doc:"## Time-based Filters API\nFollowing is the supported API format for specifying metrics time-based filters:\n"` diff --git a/vendor/github.com/netobserv/flowlogs-pipeline/pkg/api/encode_s3.go b/vendor/github.com/netobserv/flowlogs-pipeline/pkg/api/encode_s3.go index 71cc6d6fb..6e50cbf56 100644 --- a/vendor/github.com/netobserv/flowlogs-pipeline/pkg/api/encode_s3.go +++ b/vendor/github.com/netobserv/flowlogs-pipeline/pkg/api/encode_s3.go @@ -1,5 +1,6 @@ /* * Copyright (C) 2022 IBM, Inc. + * Copyright (C) 2026 NetObserv Authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,16 +18,23 @@ package api +const ( + // S3FormatParquet is the default object format (Hive-partitioned Parquet schema v1). + S3FormatParquet = "parquet" + // S3FormatJSON is the legacy JSON object format (explicit opt-in). + S3FormatJSON = "json" +) + type EncodeS3 struct { - Account string `yaml:"account" json:"account" doc:"tenant id for this flow collector"` + Account string `yaml:"account" json:"account" doc:"cluster / tenant id used as Hive cluster_id partition"` Endpoint string `yaml:"endpoint" json:"endpoint" doc:"address of s3 server"` AccessKeyID string `yaml:"accessKeyId" json:"accessKeyId" doc:"username to connect to server"` - SecretAccessKey string `yaml:"secretAccessKey" json:"secretAccessKey" doc:"password to connect to server"` + SecretAccessKey RedactedText `yaml:"secretAccessKey" json:"secretAccessKey" doc:"password to connect to server"` Bucket string `yaml:"bucket" json:"bucket" doc:"bucket into which to store objects"` - WriteTimeout Duration `yaml:"writeTimeout,omitempty" json:"writeTimeout,omitempty" doc:"timeout (in seconds) for write operation"` - BatchSize int `yaml:"batchSize,omitempty" json:"batchSize,omitempty" doc:"limit on how many flows will be buffered before being sent to an object"` + Prefix string `yaml:"prefix,omitempty" json:"prefix,omitempty" doc:"optional key prefix before cluster_id partition"` + Format string `yaml:"format,omitempty" json:"format,omitempty" doc:"object format: parquet (default) or json"` + WriteTimeout Duration `yaml:"writeTimeout,omitempty" json:"writeTimeout,omitempty" doc:"timeout for flush when batch is not full (default: 60s)"` + BatchSize int `yaml:"batchSize,omitempty" json:"batchSize,omitempty" doc:"max flows buffered before writing an object (default: 5000 parquet / 10 json)"` Secure bool `yaml:"secure,omitempty" json:"secure,omitempty" doc:"true for https, false for http (default: false)"` - ObjectHeaderParameters map[string]interface{} `yaml:"objectHeaderParameters,omitempty" json:"objectHeaderParameters,omitempty" doc:"parameters to include in object header (key/value pairs)"` - // TBD: (TLS?) security parameters - // TLS *ClientTLS `yaml:"tls" json:"tls" doc:"TLS client configuration (optional)"` + ObjectHeaderParameters map[string]interface{} `yaml:"objectHeaderParameters,omitempty" json:"objectHeaderParameters,omitempty" doc:"parameters to include in JSON object header (json format only)"` } diff --git a/vendor/github.com/netobserv/flowlogs-pipeline/pkg/api/redacted.go b/vendor/github.com/netobserv/flowlogs-pipeline/pkg/api/redacted.go new file mode 100644 index 000000000..45f580830 --- /dev/null +++ b/vendor/github.com/netobserv/flowlogs-pipeline/pkg/api/redacted.go @@ -0,0 +1,16 @@ +package api + +import ( + "fmt" + "log/slog" +) + +type RedactedText string + +func (RedactedText) Format(f fmt.State, _ rune) { + _, _ = f.Write([]byte("[REDACTED]")) +} + +func (RedactedText) LogValue() slog.Value { + return slog.StringValue("[REDACTED]") +} diff --git a/vendor/github.com/netobserv/flowlogs-pipeline/pkg/api/tls.go b/vendor/github.com/netobserv/flowlogs-pipeline/pkg/api/tls.go index 5d8abfbf9..9ae087ae1 100644 --- a/vendor/github.com/netobserv/flowlogs-pipeline/pkg/api/tls.go +++ b/vendor/github.com/netobserv/flowlogs-pipeline/pkg/api/tls.go @@ -34,6 +34,7 @@ type ClientTLS struct { func (c *ClientTLS) Build() (*tls.Config, error) { tlsConfig := &tls.Config{ InsecureSkipVerify: c.InsecureSkipVerify, + MinVersion: tls.VersionTLS13, } if c.CACertPath != "" { caCert, err := os.ReadFile(c.CACertPath) diff --git a/vendor/github.com/netobserv/flowlogs-pipeline/pkg/api/write_flowbuffer.go b/vendor/github.com/netobserv/flowlogs-pipeline/pkg/api/write_flowbuffer.go new file mode 100644 index 000000000..01b853e37 --- /dev/null +++ b/vendor/github.com/netobserv/flowlogs-pipeline/pkg/api/write_flowbuffer.go @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2026 NetObserv Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package api + +// WriteFlowBuffer configures the in-memory ring buffer of enriched flows and its query API. +// When this write stage is absent from the pipeline, no RAM buffer or query listener is started. +type WriteFlowBuffer struct { + // MaxEntries is the per-instance ring buffer capacity. Oldest entries are evicted first. + MaxEntries int `yaml:"maxEntries,omitempty" json:"maxEntries,omitempty" doc:"maximum number of enriched flows retained per FLP instance (default: 50000)"` + // QueryListenAddress is the bind address for the flowBuffer HTTP API (default: ":9200"). + QueryListenAddress string `yaml:"queryListenAddress,omitempty" json:"queryListenAddress,omitempty" doc:"listen address for flowBuffer HTTP query API (default: :9200)"` + // QueryTimeout bounds local and peer queries (default: 2s). + QueryTimeout Duration `yaml:"queryTimeout,omitempty" json:"queryTimeout,omitempty" doc:"timeout for buffer and peer queries (default: 2s)"` + // PeerPort is the port used when discovering sibling pods via EndpointSlice/Endpoints (default: same as listen port). + PeerPort int `yaml:"peerPort,omitempty" json:"peerPort,omitempty" doc:"port for peer local queries (default: derived from queryListenAddress)"` + // PeerURLs is an explicit list of peer base URLs for tests / non-K8s (e.g. http://127.0.0.1:9201). + // When set, Kubernetes discovery is skipped. + PeerURLs []string `yaml:"peerURLs,omitempty" json:"peerURLs,omitempty" doc:"explicit peer base URLs (skips Kubernetes discovery; for tests)"` + // ServiceName is the Kubernetes Service name whose EndpointSlice/Endpoints list sibling pods. + ServiceName string `yaml:"serviceName,omitempty" json:"serviceName,omitempty" doc:"Kubernetes Service name for peer discovery via EndpointSlice/Endpoints"` + // Namespace overrides the namespace for peer discovery (default: POD_NAMESPACE env). + Namespace string `yaml:"namespace,omitempty" json:"namespace,omitempty" doc:"Kubernetes namespace for peer discovery (default: POD_NAMESPACE)"` + // KubeConfigPath is an optional kubeconfig path (empty = in-cluster). + KubeConfigPath string `yaml:"kubeConfigPath,omitempty" json:"kubeConfigPath,omitempty" doc:"path to kubeconfig (empty for in-cluster)"` +} diff --git a/vendor/github.com/netobserv/flowlogs-pipeline/pkg/config/config.go b/vendor/github.com/netobserv/flowlogs-pipeline/pkg/config/config.go index 444f278f5..508984284 100644 --- a/vendor/github.com/netobserv/flowlogs-pipeline/pkg/config/config.go +++ b/vendor/github.com/netobserv/flowlogs-pipeline/pkg/config/config.go @@ -31,8 +31,9 @@ type Options struct { Parameters string DynamicParameters string MetricsSettings string - Health Health - Profile Profile + HealthAddr string + PprofAddr string + K8sCacheServer K8sCacheServer } type Root struct { @@ -55,13 +56,14 @@ type HotReloadStruct struct { Parameters []StageParam `yaml:"parameters,omitempty" json:"parameters,omitempty"` } -type Health struct { +type K8sCacheServer struct { Address string Port int -} - -type Profile struct { - Port int + // TLS configuration + TLSEnabled bool + TLSCertPath string + TLSKeyPath string + TLSCAPath string } // MetricsSettings is similar to api.PromEncode, but is global to the application, ie. it also works with operational metrics. @@ -140,11 +142,12 @@ type Encode struct { } type Write struct { - Type string `yaml:"type" json:"type"` - Loki *api.WriteLoki `yaml:"loki,omitempty" json:"loki,omitempty"` - Stdout *api.WriteStdout `yaml:"stdout,omitempty" json:"stdout,omitempty"` - Ipfix *api.WriteIpfix `yaml:"ipfix,omitempty" json:"ipfix,omitempty"` - GRPC *api.WriteGRPC `yaml:"grpc,omitempty" json:"grpc,omitempty"` + Type string `yaml:"type" json:"type"` + Loki *api.WriteLoki `yaml:"loki,omitempty" json:"loki,omitempty"` + Stdout *api.WriteStdout `yaml:"stdout,omitempty" json:"stdout,omitempty"` + Ipfix *api.WriteIpfix `yaml:"ipfix,omitempty" json:"ipfix,omitempty"` + GRPC *api.WriteGRPC `yaml:"grpc,omitempty" json:"grpc,omitempty"` + FlowBuffer *api.WriteFlowBuffer `yaml:"flowBuffer,omitempty" json:"flowBuffer,omitempty"` } // ParseConfig creates the internal unmarshalled representation from the Pipeline and Parameters json diff --git a/vendor/github.com/netobserv/flowlogs-pipeline/pkg/config/pipeline_builder.go b/vendor/github.com/netobserv/flowlogs-pipeline/pkg/config/pipeline_builder.go index c800ebb40..9348147de 100644 --- a/vendor/github.com/netobserv/flowlogs-pipeline/pkg/config/pipeline_builder.go +++ b/vendor/github.com/netobserv/flowlogs-pipeline/pkg/config/pipeline_builder.go @@ -202,6 +202,11 @@ func (b *PipelineBuilderStage) WriteIpfix(name string, ipfix api.WriteIpfix) Pip return b.next(name, NewWriteIpfixParams(name, ipfix)) } +// WriteFlowBuffer chains the current stage with a WriteFlowBuffer stage and returns that new stage +func (b *PipelineBuilderStage) WriteFlowBuffer(name string, fb api.WriteFlowBuffer) PipelineBuilderStage { + return b.next(name, NewWriteFlowBufferParams(name, fb)) +} + // GetStages returns the current pipeline stages. It can be called from any of the stages, they share the same pipeline reference. func (b *PipelineBuilderStage) GetStages() []Stage { return b.pipeline.stages diff --git a/vendor/github.com/netobserv/flowlogs-pipeline/pkg/config/stage_params.go b/vendor/github.com/netobserv/flowlogs-pipeline/pkg/config/stage_params.go index c7c42cf3c..4976e11b1 100644 --- a/vendor/github.com/netobserv/flowlogs-pipeline/pkg/config/stage_params.go +++ b/vendor/github.com/netobserv/flowlogs-pipeline/pkg/config/stage_params.go @@ -98,3 +98,7 @@ func NewWriteLokiParams(name string, loki api.WriteLoki) StageParam { func NewWriteIpfixParams(name string, ipfix api.WriteIpfix) StageParam { return StageParam{Name: name, Write: &Write{Type: api.IpfixType, Ipfix: &ipfix}} } + +func NewWriteFlowBufferParams(name string, fb api.WriteFlowBuffer) StageParam { + return StageParam{Name: name, Write: &Write{Type: api.FlowBufferType, FlowBuffer: &fb}} +} diff --git a/vendor/github.com/netobserv/flowlogs-pipeline/pkg/schema/flow_v1.go b/vendor/github.com/netobserv/flowlogs-pipeline/pkg/schema/flow_v1.go new file mode 100644 index 000000000..003085029 --- /dev/null +++ b/vendor/github.com/netobserv/flowlogs-pipeline/pkg/schema/flow_v1.go @@ -0,0 +1,197 @@ +/* + * Copyright (C) 2026 NetObserv Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +// Package schema defines the shared NetObserv flow record schema used by +// flowBuffer (in-memory) and the Parquet S3 encoder. +package schema + +import ( + "github.com/netobserv/flowlogs-pipeline/pkg/config" + "github.com/netobserv/flowlogs-pipeline/pkg/utils" +) + +const ( + // ParquetVersion is stored as key/value metadata on Parquet objects. + ParquetVersion = "1" + // ParquetVersionKey is the metadata key for the schema version. + ParquetVersionKey = "netobserv.parquet.version" +) + +// FlowRecordV1 is the Parquet / buffer schema v1. +// Nullable feature fields use pointers so missing values stay null in Parquet. +type FlowRecordV1 struct { + TimeFlowStartMs int64 `parquet:"TimeFlowStartMs"` + TimeFlowEndMs int64 `parquet:"TimeFlowEndMs"` + TimeReceived *int64 `parquet:"TimeReceived,optional"` + + SrcAddr string `parquet:"SrcAddr,optional"` + DstAddr string `parquet:"DstAddr,optional"` + SrcPort *int32 `parquet:"SrcPort,optional"` + DstPort *int32 `parquet:"DstPort,optional"` + Proto *int32 `parquet:"Proto,optional"` + Etype *int32 `parquet:"Etype,optional"` + Dscp *int32 `parquet:"Dscp,optional"` + Flags *int32 `parquet:"Flags,optional"` + + Bytes *int64 `parquet:"Bytes,optional"` + Packets *int64 `parquet:"Packets,optional"` + Sampling *int64 `parquet:"Sampling,optional"` + + SrcK8S_Name string `parquet:"SrcK8S_Name,optional"` + SrcK8S_Namespace string `parquet:"SrcK8S_Namespace,optional"` + SrcK8S_Type string `parquet:"SrcK8S_Type,optional"` + SrcK8S_OwnerName string `parquet:"SrcK8S_OwnerName,optional"` + SrcK8S_OwnerType string `parquet:"SrcK8S_OwnerType,optional"` + SrcK8S_HostIP string `parquet:"SrcK8S_HostIP,optional"` + SrcK8S_HostName string `parquet:"SrcK8S_HostName,optional"` + SrcK8S_Zone string `parquet:"SrcK8S_Zone,optional"` + SrcK8S_NetworkName string `parquet:"SrcK8S_NetworkName,optional"` + + DstK8S_Name string `parquet:"DstK8S_Name,optional"` + DstK8S_Namespace string `parquet:"DstK8S_Namespace,optional"` + DstK8S_Type string `parquet:"DstK8S_Type,optional"` + DstK8S_OwnerName string `parquet:"DstK8S_OwnerName,optional"` + DstK8S_OwnerType string `parquet:"DstK8S_OwnerType,optional"` + DstK8S_HostIP string `parquet:"DstK8S_HostIP,optional"` + DstK8S_HostName string `parquet:"DstK8S_HostName,optional"` + DstK8S_Zone string `parquet:"DstK8S_Zone,optional"` + DstK8S_NetworkName string `parquet:"DstK8S_NetworkName,optional"` + + // Feature fields (nullable) + TimeFlowRttNs *int64 `parquet:"TimeFlowRttNs,optional"` + DnsId *int32 `parquet:"DnsId,optional"` + DnsLatencyMs *int64 `parquet:"DnsLatencyMs,optional"` + DnsFlagsResponseCode string `parquet:"DnsFlagsResponseCode,optional"` + PktDropBytes *int64 `parquet:"PktDropBytes,optional"` + PktDropPackets *int64 `parquet:"PktDropPackets,optional"` + PktDropLatestState string `parquet:"PktDropLatestState,optional"` + PktDropLatestDropCause string `parquet:"PktDropLatestDropCause,optional"` + FlowDirection *int32 `parquet:"FlowDirection,optional"` + AgentIP string `parquet:"AgentIP,optional"` +} + +// FlowTimestampMs returns the best available flow timestamp in milliseconds. +// Prefers TimeFlowEndMs, then TimeFlowStartMs, then TimeReceived (seconds→ms). +func FlowTimestampMs(m config.GenericMap) (int64, bool) { + if v, ok := int64Field(m, "TimeFlowEndMs"); ok && v > 0 { + return v, true + } + if v, ok := int64Field(m, "TimeFlowStartMs"); ok && v > 0 { + return v, true + } + if v, ok := int64Field(m, "TimeReceived"); ok && v > 0 { + // TimeReceived is typically unix seconds + if v < 1e12 { + return v * 1000, true + } + return v, true + } + return 0, false +} + +// FromGenericMap maps an enriched GenericMap onto schema v1. +func FromGenericMap(m config.GenericMap) FlowRecordV1 { + rec := FlowRecordV1{ + SrcAddr: stringField(m, "SrcAddr"), + DstAddr: stringField(m, "DstAddr"), + SrcK8S_Name: stringField(m, "SrcK8S_Name"), + SrcK8S_Namespace: stringField(m, "SrcK8S_Namespace"), + SrcK8S_Type: stringField(m, "SrcK8S_Type"), + SrcK8S_OwnerName: stringField(m, "SrcK8S_OwnerName"), + SrcK8S_OwnerType: stringField(m, "SrcK8S_OwnerType"), + SrcK8S_HostIP: stringField(m, "SrcK8S_HostIP"), + SrcK8S_HostName: stringField(m, "SrcK8S_HostName"), + SrcK8S_Zone: stringField(m, "SrcK8S_Zone"), + SrcK8S_NetworkName: stringField(m, "SrcK8S_NetworkName"), + DstK8S_Name: stringField(m, "DstK8S_Name"), + DstK8S_Namespace: stringField(m, "DstK8S_Namespace"), + DstK8S_Type: stringField(m, "DstK8S_Type"), + DstK8S_OwnerName: stringField(m, "DstK8S_OwnerName"), + DstK8S_OwnerType: stringField(m, "DstK8S_OwnerType"), + DstK8S_HostIP: stringField(m, "DstK8S_HostIP"), + DstK8S_HostName: stringField(m, "DstK8S_HostName"), + DstK8S_Zone: stringField(m, "DstK8S_Zone"), + DstK8S_NetworkName: stringField(m, "DstK8S_NetworkName"), + DnsFlagsResponseCode: stringField(m, "DnsFlagsResponseCode"), + PktDropLatestState: stringField(m, "PktDropLatestState"), + PktDropLatestDropCause: stringField(m, "PktDropLatestDropCause"), + AgentIP: stringField(m, "AgentIP"), + } + if v, ok := int64Field(m, "TimeFlowStartMs"); ok { + rec.TimeFlowStartMs = v + } + if v, ok := int64Field(m, "TimeFlowEndMs"); ok { + rec.TimeFlowEndMs = v + } + rec.TimeReceived = optionalInt64(m, "TimeReceived") + rec.SrcPort = optionalInt32(m, "SrcPort") + rec.DstPort = optionalInt32(m, "DstPort") + rec.Proto = optionalInt32(m, "Proto") + rec.Etype = optionalInt32(m, "Etype") + rec.Dscp = optionalInt32(m, "Dscp") + rec.Flags = optionalInt32(m, "Flags") + rec.Bytes = optionalInt64(m, "Bytes") + rec.Packets = optionalInt64(m, "Packets") + rec.Sampling = optionalInt64(m, "Sampling") + rec.TimeFlowRttNs = optionalInt64(m, "TimeFlowRttNs") + rec.DnsId = optionalInt32(m, "DnsId") + rec.DnsLatencyMs = optionalInt64(m, "DnsLatencyMs") + rec.PktDropBytes = optionalInt64(m, "PktDropBytes") + rec.PktDropPackets = optionalInt64(m, "PktDropPackets") + rec.FlowDirection = optionalInt32(m, "FlowDirection") + return rec +} + +func stringField(m config.GenericMap, key string) string { + if v, ok := m[key]; ok { + return utils.ConvertToString(v) + } + return "" +} + +func int64Field(m config.GenericMap, key string) (int64, bool) { + v, ok := m[key] + if !ok || v == nil { + return 0, false + } + n, err := utils.ConvertToInt64(v) + if err != nil { + return 0, false + } + return n, true +} + +func optionalInt64(m config.GenericMap, key string) *int64 { + n, ok := int64Field(m, key) + if !ok { + return nil + } + return &n +} + +func optionalInt32(m config.GenericMap, key string) *int32 { + v, ok := m[key] + if !ok || v == nil { + return nil + } + n, err := utils.ConvertToInt64(v) + if err != nil { + return nil + } + i := int32(n) + return &i +} diff --git a/vendor/github.com/netobserv/flowlogs-pipeline/pkg/schema/parquet_encode.go b/vendor/github.com/netobserv/flowlogs-pipeline/pkg/schema/parquet_encode.go new file mode 100644 index 000000000..2ca66ff84 --- /dev/null +++ b/vendor/github.com/netobserv/flowlogs-pipeline/pkg/schema/parquet_encode.go @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2026 NetObserv Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package schema + +import ( + "bytes" + + "github.com/netobserv/flowlogs-pipeline/pkg/config" + parquet "github.com/parquet-go/parquet-go" +) + +// EncodeParquetBytes encodes flows as Parquet schema v1 with netobserv.parquet.version metadata. +func EncodeParquetBytes(flows []config.GenericMap) ([]byte, error) { + records := make([]FlowRecordV1, 0, len(flows)) + for _, f := range flows { + records = append(records, FromGenericMap(f)) + } + buf := new(bytes.Buffer) + w := parquet.NewGenericWriter[FlowRecordV1](buf, + parquet.KeyValueMetadata(ParquetVersionKey, ParquetVersion), + ) + if _, err := w.Write(records); err != nil { + _ = w.Close() + return nil, err + } + if err := w.Close(); err != nil { + return nil, err + } + return buf.Bytes(), nil +} diff --git a/vendor/github.com/parquet-go/bitpack/.gitignore b/vendor/github.com/parquet-go/bitpack/.gitignore new file mode 100644 index 000000000..b3584c8d4 --- /dev/null +++ b/vendor/github.com/parquet-go/bitpack/.gitignore @@ -0,0 +1,21 @@ +# Binaries for programs and plugins +*.exe +*.exe~ +*.dll +*.so +*.dylib +*.py + +# Test binary, built with `go test -c` +*.test + +# Output of the go coverage tool, specifically when used with LiteIDE +*.out + +# Dependency directories (remove the comment below to include it) +# vendor/ + +# Emacs +*~ +#*# +.# diff --git a/vendor/github.com/parquet-go/bitpack/LICENSE b/vendor/github.com/parquet-go/bitpack/LICENSE new file mode 100644 index 000000000..c3e15a69d --- /dev/null +++ b/vendor/github.com/parquet-go/bitpack/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2025 Achille Roussel, Filip Petkovski + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/vendor/github.com/parquet-go/bitpack/README.md b/vendor/github.com/parquet-go/bitpack/README.md new file mode 100644 index 000000000..1f02d8f3b --- /dev/null +++ b/vendor/github.com/parquet-go/bitpack/README.md @@ -0,0 +1,31 @@ +# bitpack + +[![Go Reference](https://pkg.go.dev/badge/github.com/parquet-go/bitpack.svg)](https://pkg.go.dev/github.com/parquet-go/bitpack) + +A high-performance Go library for bit packing and unpacking integers of various bit widths. Part of +the [parquet-go](https://github.com/parquet-go/parquet-go) ecosystem. + +Includes AMD64 assembly optimizations with pure Go fallback for portability. + +```bash +go get github.com/parquet-go/bitpack +``` + +## Usage + +```go +import "github.com/parquet-go/bitpack" + +// Pack int32 values with 3-bit width +values := []int32{1, 2, 3, 4, 5} +bitWidth := uint(3) +packedSize := bitpack.ByteCount(uint(len(values)) * bitWidth) +dst := make([]byte, packedSize+bitpack.PaddingInt32) +bitpack.PackInt32(dst, values, bitWidth) + +// Unpack int32 values +unpacked := make([]int32, len(values)) +bitpack.UnpackInt32(unpacked, dst, bitWidth) +``` + +For complete working examples, see the [examples](./examples) directory. diff --git a/vendor/github.com/parquet-go/bitpack/bitpack.go b/vendor/github.com/parquet-go/bitpack/bitpack.go new file mode 100644 index 000000000..4a5547251 --- /dev/null +++ b/vendor/github.com/parquet-go/bitpack/bitpack.go @@ -0,0 +1,14 @@ +// Package bitpack implements efficient bit packing and unpacking routines for +// integers of various bit widths. +package bitpack + +// Int is a type constraint representing the integer types that this package +// supports. +type Int interface { + ~int32 | ~uint32 | ~int64 | ~uint64 | ~int | ~uintptr +} + +// ByteCount returns the number of bytes needed to hold the given bit count. +func ByteCount(bitCount uint) int { + return int((bitCount + 7) / 8) +} diff --git a/vendor/github.com/parquet-go/bitpack/masks_int32_amd64.s b/vendor/github.com/parquet-go/bitpack/masks_int32_amd64.s new file mode 100644 index 000000000..6ffe4e2c5 --- /dev/null +++ b/vendor/github.com/parquet-go/bitpack/masks_int32_amd64.s @@ -0,0 +1,1288 @@ +//go:build !purego + +#include "textflag.h" + +// ----------------------------------------------------------------------------- +// Shuffle masks used to broadcast bytes of bit-packed valued into vector +// registers at positions where they can then be shifted into the right +// locations. +// ----------------------------------------------------------------------------- + +// Shuffle masks for unpacking values from bit widths 1 to 16. +// +// The masks are grouped in 32 bytes chunks containing 2 masks of 16 bytes, with +// the following layout: +// +// - The first mask is used to shuffle values from the 16 bytes of input into +// the lower 16 bytes of output. These values are then shifted RIGHT to be +// aligned on the begining of each 32 bit word. +// +// - The second mask selects values from the 16 bytes of input into the upper +// 16 bytes of output. These values are then shifted RIGHT to be aligned on +// the beginning of each 32 bit word. +// +// The bit width is intended to be used as an index into this array, using this +// formula to convert from the index to a byte offset: +// +// offset = 32 * (bitWidth - 1) +// +GLOBL ·shuffleInt32x1to16bits(SB), RODATA|NOPTR, $512 + +// 1 bit => 32 bits +// ----------------- +// 0: [a,b,c,d,e,f,g,h] +// ... +DATA ·shuffleInt32x1to16bits+0+0(SB)/4, $0x80808000 +DATA ·shuffleInt32x1to16bits+0+4(SB)/4, $0x80808000 +DATA ·shuffleInt32x1to16bits+0+8(SB)/4, $0x80808000 +DATA ·shuffleInt32x1to16bits+0+12(SB)/4, $0x80808000 + +DATA ·shuffleInt32x1to16bits+0+16(SB)/4, $0x80808000 +DATA ·shuffleInt32x1to16bits+0+20(SB)/4, $0x80808000 +DATA ·shuffleInt32x1to16bits+0+24(SB)/4, $0x80808000 +DATA ·shuffleInt32x1to16bits+0+28(SB)/4, $0x80808000 + +// 2 bits => 32 bits +// ----------------- +// 0: [a,a,b,b,c,c,d,d] +// 1: [e,e,f,f,g,g,h,h] +// ... +DATA ·shuffleInt32x1to16bits+32+0(SB)/4, $0x80808000 +DATA ·shuffleInt32x1to16bits+32+4(SB)/4, $0x80808000 +DATA ·shuffleInt32x1to16bits+32+8(SB)/4, $0x80808000 +DATA ·shuffleInt32x1to16bits+32+12(SB)/4, $0x80808000 + +DATA ·shuffleInt32x1to16bits+32+16(SB)/4, $0x80808001 +DATA ·shuffleInt32x1to16bits+32+20(SB)/4, $0x80808001 +DATA ·shuffleInt32x1to16bits+32+24(SB)/4, $0x80808001 +DATA ·shuffleInt32x1to16bits+32+28(SB)/4, $0x80808001 + +// 3 bits => 32 bits +// ----------------- +// 0: [a,a,a,b,b,b,c,c] +// 1: [c,d,d,d,e,e,e,f] +// 2: [f,f,g,g,g,h,h,h] +// ... +DATA ·shuffleInt32x1to16bits+64+0(SB)/4, $0x80808000 +DATA ·shuffleInt32x1to16bits+64+4(SB)/4, $0x80808000 +DATA ·shuffleInt32x1to16bits+64+8(SB)/4, $0x80800100 +DATA ·shuffleInt32x1to16bits+64+12(SB)/4, $0x80808001 + +DATA ·shuffleInt32x1to16bits+64+16(SB)/4, $0x80808001 +DATA ·shuffleInt32x1to16bits+64+20(SB)/4, $0x80800201 +DATA ·shuffleInt32x1to16bits+64+24(SB)/4, $0x80808002 +DATA ·shuffleInt32x1to16bits+64+28(SB)/4, $0x80808002 + +// 4 bits => 32 bits +// ----------------- +// 0: [a,a,a,a,b,b,b,b] +// 1: [c,c,c,c,d,d,d,d] +// 2: [e,e,e,e,f,f,f,f] +// 3: [g,g,g,g,h,h,h,h] +// ... +DATA ·shuffleInt32x1to16bits+96+0(SB)/4, $0x80808000 +DATA ·shuffleInt32x1to16bits+96+4(SB)/4, $0x80808000 +DATA ·shuffleInt32x1to16bits+96+8(SB)/4, $0x80808001 +DATA ·shuffleInt32x1to16bits+96+12(SB)/4, $0x80808001 + +DATA ·shuffleInt32x1to16bits+96+16(SB)/4, $0x80808002 +DATA ·shuffleInt32x1to16bits+96+20(SB)/4, $0x80808002 +DATA ·shuffleInt32x1to16bits+96+24(SB)/4, $0x80808003 +DATA ·shuffleInt32x1to16bits+96+28(SB)/4, $0x80808003 + +// 5 bits => 32 bits +// ----------------- +// 0: [a,a,a,a,a,b,b,b] +// 1: [b,b,c,c,c,c,c,d] +// 2: [d,d,d,d,e,e,e,e] +// 3: [e,f,f,f,f,f,g,g] +// 4: [g,g,g,h,h,h,h,h] +// ... +DATA ·shuffleInt32x1to16bits+128+0(SB)/4, $0x80808000 +DATA ·shuffleInt32x1to16bits+128+4(SB)/4, $0x80800100 +DATA ·shuffleInt32x1to16bits+128+8(SB)/4, $0x80808001 +DATA ·shuffleInt32x1to16bits+128+12(SB)/4, $0x80800201 + +DATA ·shuffleInt32x1to16bits+128+16(SB)/4, $0x80800302 +DATA ·shuffleInt32x1to16bits+128+20(SB)/4, $0x80808003 +DATA ·shuffleInt32x1to16bits+128+24(SB)/4, $0x80800403 +DATA ·shuffleInt32x1to16bits+128+28(SB)/4, $0x80808004 + +// 6 bits => 32 bits +// ----------------- +// 0: [a,a,a,a,a,a,b,b] +// 1: [b,b,b,b,c,c,c,c] +// 2: [c,c,d,d,d,d,d,d] +// 3: [e,e,e,e,e,e,f,f] +// 4: [f,f,f,f,g,g,g,g] +// 5: [g,g,h,h,h,h,h,h] +// ... +DATA ·shuffleInt32x1to16bits+160+0(SB)/4, $0x80808000 +DATA ·shuffleInt32x1to16bits+160+4(SB)/4, $0x80800100 +DATA ·shuffleInt32x1to16bits+160+8(SB)/4, $0x80800201 +DATA ·shuffleInt32x1to16bits+160+12(SB)/4, $0x80808002 + +DATA ·shuffleInt32x1to16bits+160+16(SB)/4, $0x80808003 +DATA ·shuffleInt32x1to16bits+160+20(SB)/4, $0x80800403 +DATA ·shuffleInt32x1to16bits+160+24(SB)/4, $0x80800504 +DATA ·shuffleInt32x1to16bits+160+28(SB)/4, $0x80808005 + +// 7 bits => 32 bits +// ----------------- +// 0: [a,a,a,a,a,a,a,b] +// 1: [b,b,b,b,b,b,c,c] +// 2: [c,c,c,c,c,d,d,d] +// 3: [d,d,d,d,e,e,e,e] +// 4: [e,e,e,f,f,f,f,f] +// 5: [f,f,g,g,g,g,g,g] +// 6: [g,h,h,h,h,h,h,h] +// ... +DATA ·shuffleInt32x1to16bits+192+0(SB)/4, $0x80808000 +DATA ·shuffleInt32x1to16bits+192+4(SB)/4, $0x80800100 +DATA ·shuffleInt32x1to16bits+192+8(SB)/4, $0x80800201 +DATA ·shuffleInt32x1to16bits+192+12(SB)/4, $0x80800302 + +DATA ·shuffleInt32x1to16bits+192+16(SB)/4, $0x80800403 +DATA ·shuffleInt32x1to16bits+192+20(SB)/4, $0x80800504 +DATA ·shuffleInt32x1to16bits+192+24(SB)/4, $0x80800605 +DATA ·shuffleInt32x1to16bits+192+28(SB)/4, $0x80808006 + +// 8 bits => 32 bits +// ----------------- +// 0: [a,a,a,a,a,a,a,a] +// 1: [b,b,b,b,b,b,b,b] +// 2: [c,c,c,c,c,c,c,c] +// 3: [d,d,d,d,d,d,d,d] +// 4: [e,e,e,e,e,e,e,e] +// 5: [f,f,f,f,f,f,f,f] +// 6: [g,g,g,g,g,g,g,g] +// 7: [h,h,h,h,h,h,h,h] +// ... +DATA ·shuffleInt32x1to16bits+224+0(SB)/4, $0x80808000 +DATA ·shuffleInt32x1to16bits+224+4(SB)/4, $0x80808001 +DATA ·shuffleInt32x1to16bits+224+8(SB)/4, $0x80808002 +DATA ·shuffleInt32x1to16bits+224+12(SB)/4, $0x80808003 + +DATA ·shuffleInt32x1to16bits+224+16(SB)/4, $0x80808004 +DATA ·shuffleInt32x1to16bits+224+20(SB)/4, $0x80808005 +DATA ·shuffleInt32x1to16bits+224+24(SB)/4, $0x80808006 +DATA ·shuffleInt32x1to16bits+224+28(SB)/4, $0x80808007 + +// 9 bits => 32 bits +// ----------------- +// 0: [a,a,a,a,a,a,a,a] +// 1: [a,b,b,b,b,b,b,b] +// 2: [b,b,c,c,c,c,c,c] +// 3: [c,c,c,d,d,d,d,d] +// 4: [d,d,d,d,e,e,e,e] +// 5: [e,e,e,e,e,f,f,f] +// 6: [f,f,f,f,f,f,g,g] +// 7: [g,g,g,g,g,g,g,h] +// 8: [h,h,h,h,h,h,h,h] +// ... +DATA ·shuffleInt32x1to16bits+256+0(SB)/4, $0x80800100 +DATA ·shuffleInt32x1to16bits+256+4(SB)/4, $0x80800201 +DATA ·shuffleInt32x1to16bits+256+8(SB)/4, $0x80800302 +DATA ·shuffleInt32x1to16bits+256+12(SB)/4, $0x80800403 + +DATA ·shuffleInt32x1to16bits+256+16(SB)/4, $0x80800504 +DATA ·shuffleInt32x1to16bits+256+20(SB)/4, $0x80800605 +DATA ·shuffleInt32x1to16bits+256+24(SB)/4, $0x80800706 +DATA ·shuffleInt32x1to16bits+256+28(SB)/4, $0x80800807 + +// 10 bits => 32 bits +// ------------------ +// 0: [a,a,a,a,a,a,a,a] +// 1: [a,a,b,b,b,b,b,b] +// 2: [b,b,b,b,c,c,c,c] +// 3: [c,c,c,c,c,c,d,d] +// 4: [d,d,d,d,d,d,d,d] +// 5: [e,e,e,e,e,e,e,e] +// 6: [e,e,f,f,f,f,f,f] +// 7: [f,f,f,f,g,g,g,g] +// 8: [g,g,g,g,g,g,h,h] +// 9: [h,h,h,h,h,h,h,h] +// ... +DATA ·shuffleInt32x1to16bits+288+0(SB)/4, $0x80800100 +DATA ·shuffleInt32x1to16bits+288+4(SB)/4, $0x80800201 +DATA ·shuffleInt32x1to16bits+288+8(SB)/4, $0x80800302 +DATA ·shuffleInt32x1to16bits+288+12(SB)/4, $0x80800403 + +DATA ·shuffleInt32x1to16bits+288+16(SB)/4, $0x80800605 +DATA ·shuffleInt32x1to16bits+288+20(SB)/4, $0x80800706 +DATA ·shuffleInt32x1to16bits+288+24(SB)/4, $0x80800807 +DATA ·shuffleInt32x1to16bits+288+28(SB)/4, $0x80800908 + +// 11 bits => 32 bits +// ------------------ +// 0: [a,a,a,a,a,a,a,a] +// 1: [a,a,a,b,b,b,b,b] +// 2: [b,b,b,b,b,b,c,c] +// 3: [c,c,c,c,c,c,c,c] +// 4: [c,d,d,d,d,d,d,d] +// 5: [d,d,d,d,e,e,e,e] +// 6: [e,e,e,e,e,e,e,f] +// 7: [f,f,f,f,f,f,f,f] +// 8: [f,f,g,g,g,g,g,g] +// 9: [g,g,g,g,g,h,h,h] +// A: [h,h,h,h,h,h,h,h] +// ... +DATA ·shuffleInt32x1to16bits+320+0(SB)/4, $0x80800100 +DATA ·shuffleInt32x1to16bits+320+4(SB)/4, $0x80800201 +DATA ·shuffleInt32x1to16bits+320+8(SB)/4, $0x80040302 +DATA ·shuffleInt32x1to16bits+320+12(SB)/4, $0x80800504 + +DATA ·shuffleInt32x1to16bits+320+16(SB)/4, $0x80800605 +DATA ·shuffleInt32x1to16bits+320+20(SB)/4, $0x80080706 +DATA ·shuffleInt32x1to16bits+320+24(SB)/4, $0x80800908 +DATA ·shuffleInt32x1to16bits+320+28(SB)/4, $0x80800A09 + +// 12 bits => 32 bits +// ------------------ +// 0: [a,a,a,a,a,a,a,a] +// 1: [a,a,a,a,b,b,b,b] +// 2: [b,b,b,b,b,b,b,b] +// 3: [c,c,c,c,c,c,c,c] +// 4: [c,c,c,c,d,d,d,d] +// 5: [d,d,d,d,d,d,d,d] +// 6: [e,e,e,e,e,e,e,e] +// 7: [e,e,e,e,f,f,f,f] +// 8: [f,f,f,f,f,f,f,f] +// 9: [g,g,g,g,g,g,g,g] +// A: [g,g,g,g,h,h,h,h] +// B: [h,h,h,h,h,h,h,h] +// ... +DATA ·shuffleInt32x1to16bits+352+0(SB)/4, $0x80800100 +DATA ·shuffleInt32x1to16bits+352+4(SB)/4, $0x80800201 +DATA ·shuffleInt32x1to16bits+352+8(SB)/4, $0x80080403 +DATA ·shuffleInt32x1to16bits+352+12(SB)/4, $0x80800504 + +DATA ·shuffleInt32x1to16bits+352+16(SB)/4, $0x80800706 +DATA ·shuffleInt32x1to16bits+352+20(SB)/4, $0x80800807 +DATA ·shuffleInt32x1to16bits+352+24(SB)/4, $0x80800A09 +DATA ·shuffleInt32x1to16bits+352+28(SB)/4, $0x80800B0A + +// 13 bits => 32 bits +// ------------------ +// 0: [a,a,a,a,a,a,a,a] +// 1: [a,a,a,a,a,b,b,b] +// 2: [b,b,b,b,b,b,b,b] +// 3: [b,b,c,c,c,c,c,c] +// 4: [c,c,c,c,c,c,c,d] +// 5: [d,d,d,d,d,d,d,d] +// 6: [d,d,d,d,e,e,e,e] +// 7: [e,e,e,e,e,e,e,e] +// 8: [e,f,f,f,f,f,f,f] +// 9: [f,f,f,f,f,f,g,g] +// A: [g,g,g,g,g,g,g,g] +// B: [g,g,g,h,h,h,h,h] +// C: [h,h,h,h,h,h,h,h] +// ... +DATA ·shuffleInt32x1to16bits+384+0(SB)/4, $0x80800100 +DATA ·shuffleInt32x1to16bits+384+4(SB)/4, $0x80030201 +DATA ·shuffleInt32x1to16bits+384+8(SB)/4, $0x80800403 +DATA ·shuffleInt32x1to16bits+384+12(SB)/4, $0x80060504 + +DATA ·shuffleInt32x1to16bits+384+16(SB)/4, $0x80080706 +DATA ·shuffleInt32x1to16bits+384+20(SB)/4, $0x80800908 +DATA ·shuffleInt32x1to16bits+384+24(SB)/4, $0x800B0A09 +DATA ·shuffleInt32x1to16bits+384+28(SB)/4, $0x80800C0B + +// 14 bits => 32 bits +// ------------------ +// 0: [a,a,a,a,a,a,a,a] +// 1: [a,a,a,a,a,a,b,b] +// 2: [b,b,b,b,b,b,b,b] +// 3: [b,b,b,b,c,c,c,c] +// 4: [c,c,c,c,c,c,c,c] +// 5: [c,c,d,d,d,d,d,d] +// 6: [d,d,d,d,d,d,d,d] +// 7: [e,e,e,e,e,e,e,e] +// 8: [e,e,e,e,e,e,f,f] +// 9: [f,f,f,f,f,f,f,f] +// A: [f,f,f,f,g,g,g,g] +// B: [g,g,g,g,g,g,g,g] +// C: [g,g,h,h,h,h,h,h] +// D: [h,h,h,h,h,h,h,h] +// ... +DATA ·shuffleInt32x1to16bits+416+0(SB)/4, $0x80800100 +DATA ·shuffleInt32x1to16bits+416+4(SB)/4, $0x80030201 +DATA ·shuffleInt32x1to16bits+416+8(SB)/4, $0x80050403 +DATA ·shuffleInt32x1to16bits+416+12(SB)/4, $0x80800605 + +DATA ·shuffleInt32x1to16bits+416+16(SB)/4, $0x80080807 +DATA ·shuffleInt32x1to16bits+416+20(SB)/4, $0x800A0908 +DATA ·shuffleInt32x1to16bits+416+24(SB)/4, $0x800C0B0A +DATA ·shuffleInt32x1to16bits+416+28(SB)/4, $0x80800D0C + +// 15 bits => 32 bits +// ------------------ +// 0: [a,a,a,a,a,a,a,a] +// 1: [a,a,a,a,a,a,a,b] +// 2: [b,b,b,b,b,b,b,b] +// 3: [b,b,b,b,b,b,c,c] +// 4: [c,c,c,c,c,c,c,c] +// 5: [c,c,c,c,c,d,d,d] +// 6: [d,d,d,d,d,d,d,d] +// 7: [d,d,d,d,e,e,e,e] +// 8: [e,e,e,e,e,e,e,e] +// 9: [e,e,e,f,f,f,f,f] +// A: [f,f,f,f,f,f,f,f] +// B: [f,f,g,g,g,g,g,g] +// C: [g,g,g,g,g,g,g,g] +// D: [g,h,h,h,h,h,h,h] +// E: [h,h,h,h,h,h,h,h] +// ... +DATA ·shuffleInt32x1to16bits+448+0(SB)/4, $0x80800100 +DATA ·shuffleInt32x1to16bits+448+4(SB)/4, $0x80030201 +DATA ·shuffleInt32x1to16bits+448+8(SB)/4, $0x80050403 +DATA ·shuffleInt32x1to16bits+448+12(SB)/4, $0x80070605 + +DATA ·shuffleInt32x1to16bits+448+16(SB)/4, $0x80090807 +DATA ·shuffleInt32x1to16bits+448+20(SB)/4, $0x800B0A09 +DATA ·shuffleInt32x1to16bits+448+24(SB)/4, $0x800D0C0B +DATA ·shuffleInt32x1to16bits+448+28(SB)/4, $0x80800E0D + +// 16 bits => 32 bits +// ------------------ +// 0: [a,a,a,a,a,a,a,a] +// 1: [a,a,a,a,a,a,a,a] +// 2: [b,b,b,b,b,b,b,b] +// 3: [b,b,b,b,b,b,c,b] +// 4: [c,c,c,c,c,c,c,c] +// 5: [c,c,c,c,c,c,c,c] +// 6: [d,d,d,d,d,d,d,d] +// 7: [d,d,d,d,d,d,d,d] +// 8: [e,e,e,e,e,e,e,e] +// 9: [e,e,e,e,e,e,e,e] +// A: [f,f,f,f,f,f,f,f] +// B: [f,f,f,f,f,f,f,f] +// C: [g,g,g,g,g,g,g,g] +// D: [g,g,g,g,g,g,g,g] +// E: [h,h,h,h,h,h,h,h] +// F: [h,h,h,h,h,h,h,h] +// ... +DATA ·shuffleInt32x1to16bits+480+0(SB)/4, $0x80800100 +DATA ·shuffleInt32x1to16bits+480+4(SB)/4, $0x80800302 +DATA ·shuffleInt32x1to16bits+480+8(SB)/4, $0x80800504 +DATA ·shuffleInt32x1to16bits+480+12(SB)/4, $0x80800706 + +DATA ·shuffleInt32x1to16bits+480+16(SB)/4, $0x80800908 +DATA ·shuffleInt32x1to16bits+480+20(SB)/4, $0x80800B0A +DATA ·shuffleInt32x1to16bits+480+24(SB)/4, $0x80800D0C +DATA ·shuffleInt32x1to16bits+480+28(SB)/4, $0x80800F0E + +// Shuffle masks for unpacking values from bit widths 17 to 26. +// +// The masks are grouped in 48 bytes chunks containing 3 masks of 16 bytes, with +// the following layout: +// +// - The first mask is used to shuffle values from the first 16 bytes of input +// into the lower 16 bytes of output. These values are then shifted RIGHT to +// be aligned on the begining of each 32 bit word. +// +// - The second mask selects values from the first 16 bytes of input into the +// upper 16 bytes of output. These values are then shifted RIGHT to be aligned +// on the beginning of each 32 bit word. +// +// - The third mask selects values from the second 16 bytes of input into the +// upper 16 bytes of output. These values are then shifted RIGHT to be aligned +// on the beginning of each 32 bit word. +// +// The bit width is intended to be used as an index into this array, using this +// formula to convert from the index to a byte offset: +// +// offset = 48 * (bitWidth - 17) +// +GLOBL ·shuffleInt32x17to26bits(SB), RODATA|NOPTR, $480 + +// 17 bits => 32 bits +// ------------------ +// 0: [a,a,a,a,a,a,a,a] +// 1: [a,a,a,a,a,a,a,a] +// 2: [a,b,b,b,b,b,b,b] +// 3: [b,b,b,b,b,b,b,b] +// 4: [b,b,c,c,c,c,c,c] +// 5: [c,c,c,c,c,c,c,c] +// 6: [c,c,c,d,d,d,d,d] +// 7: [d,d,d,d,d,d,d,d] +// 8: [d,d,d,d,e,e,e,e] +// 9: [e,e,e,e,e,e,e,e] +// A: [e,e,e,e,e,f,f,f] +// B: [f,f,f,f,f,f,f,f] +// C: [f,f,f,f,f,f,g,g] +// D: [g,g,g,g,g,g,g,g] +// E: [g,g,g,g,g,g,g,h] +// F: [h,h,h,h,h,h,h,h] +// --- +// 0: [h,h,h,h,h,h,h,h] +// ... +DATA ·shuffleInt32x17to26bits+0+0(SB)/4, $0x80020100 +DATA ·shuffleInt32x17to26bits+0+4(SB)/4, $0x80040302 +DATA ·shuffleInt32x17to26bits+0+8(SB)/4, $0x80060504 +DATA ·shuffleInt32x17to26bits+0+12(SB)/4, $0x80080706 + +DATA ·shuffleInt32x17to26bits+0+16(SB)/4, $0x800A0908 +DATA ·shuffleInt32x17to26bits+0+20(SB)/4, $0x800C0B0A +DATA ·shuffleInt32x17to26bits+0+24(SB)/4, $0x800E0D0C +DATA ·shuffleInt32x17to26bits+0+28(SB)/4, $0x80800F0E + +DATA ·shuffleInt32x17to26bits+0+32(SB)/4, $0x80808080 +DATA ·shuffleInt32x17to26bits+0+36(SB)/4, $0x80808080 +DATA ·shuffleInt32x17to26bits+0+40(SB)/4, $0x80808080 +DATA ·shuffleInt32x17to26bits+0+44(SB)/4, $0x80008080 + +// 18 bits => 32 bits +// ------------------ +// 0: [a,a,a,a,a,a,a,a] +// 1: [a,a,a,a,a,a,a,a] +// 2: [a,a,b,b,b,b,b,b] +// 3: [b,b,b,b,b,b,b,b] +// 4: [b,b,b,b,c,c,c,c] +// 5: [c,c,c,c,c,c,c,c] +// 6: [c,c,c,c,c,c,d,d] +// 7: [d,d,d,d,d,d,d,d] +// 8: [d,d,d,d,d,d,d,d] +// 9: [e,e,e,e,e,e,e,e] +// A: [e,e,e,e,e,e,e,e] +// B: [e,e,f,f,f,f,f,f] +// C: [f,f,f,f,f,f,f,f] +// D: [f,f,f,f,g,g,g,g] +// E: [g,g,g,g,g,g,g,g] +// F: [g,g,g,g,g,g,h,h] +// --- +// 0: [h,h,h,h,h,h,h,h] +// 1: [h,h,h,h,h,h,h,h] +// ... +DATA ·shuffleInt32x17to26bits+48+0(SB)/4, $0x80020100 +DATA ·shuffleInt32x17to26bits+48+4(SB)/4, $0x80040302 +DATA ·shuffleInt32x17to26bits+48+8(SB)/4, $0x80060504 +DATA ·shuffleInt32x17to26bits+48+12(SB)/4, $0x80080706 + +DATA ·shuffleInt32x17to26bits+48+16(SB)/4, $0x800B0A09 +DATA ·shuffleInt32x17to26bits+48+20(SB)/4, $0x800D0C0B +DATA ·shuffleInt32x17to26bits+48+24(SB)/4, $0x800F0E0D +DATA ·shuffleInt32x17to26bits+48+28(SB)/4, $0x8080800F + +DATA ·shuffleInt32x17to26bits+48+32(SB)/4, $0x80808080 +DATA ·shuffleInt32x17to26bits+48+36(SB)/4, $0x80808080 +DATA ·shuffleInt32x17to26bits+48+40(SB)/4, $0x80808080 +DATA ·shuffleInt32x17to26bits+48+44(SB)/4, $0x80010080 + +// 19 bits => 32 bits +// ------------------ +// 0: [a,a,a,a,a,a,a,a] +// 1: [a,a,a,a,a,a,a,a] +// 2: [a,a,a,b,b,b,b,b] +// 3: [b,b,b,b,b,b,b,b] +// 4: [b,b,b,b,b,b,c,c] +// 5: [c,c,c,c,c,c,c,c] +// 6: [c,c,c,c,c,c,c,c] +// 7: [c,d,d,d,d,d,d,d] +// 8: [d,d,d,d,d,d,d,d] +// 9: [d,d,d,d,e,e,e,e] +// A: [e,e,e,e,e,e,e,e] +// B: [e,e,e,e,e,e,e,f] +// C: [f,f,f,f,f,f,f,f] +// D: [f,f,f,f,f,f,f,f] +// E: [f,f,g,g,g,g,g,g] +// F: [g,g,g,g,g,g,g,g] +// --- +// 0: [g,g,g,g,g,h,h,h] +// 1: [h,h,h,h,h,h,h,h] +// 2: [h,h,h,h,h,h,h,h] +// ... +DATA ·shuffleInt32x17to26bits+96+0(SB)/4, $0x80020100 +DATA ·shuffleInt32x17to26bits+96+4(SB)/4, $0x80040302 +DATA ·shuffleInt32x17to26bits+96+8(SB)/4, $0x07060504 +DATA ·shuffleInt32x17to26bits+96+12(SB)/4, $0x80090807 + +DATA ·shuffleInt32x17to26bits+96+16(SB)/4, $0x800B0A09 +DATA ·shuffleInt32x17to26bits+96+20(SB)/4, $0x0E0D0C0B +DATA ·shuffleInt32x17to26bits+96+24(SB)/4, $0x80800F0E +DATA ·shuffleInt32x17to26bits+96+28(SB)/4, $0x80808080 + +DATA ·shuffleInt32x17to26bits+96+32(SB)/4, $0x80808080 +DATA ·shuffleInt32x17to26bits+96+36(SB)/4, $0x80808080 +DATA ·shuffleInt32x17to26bits+96+40(SB)/4, $0x80008080 +DATA ·shuffleInt32x17to26bits+96+44(SB)/4, $0x80020100 + +// 20 bits => 32 bits +// ------------------ +// 0: [a,a,a,a,a,a,a,a] +// 1: [a,a,a,a,a,a,a,a] +// 2: [a,a,a,a,b,b,b,b] +// 3: [b,b,b,b,b,b,b,b] +// 4: [b,b,b,b,b,b,b,b] +// 5: [c,c,c,c,c,c,c,c] +// 6: [c,c,c,c,c,c,c,c] +// 7: [c,c,c,c,d,d,d,d] +// 8: [d,d,d,d,d,d,d,d] +// 9: [d,d,d,d,d,d,d,d] +// A: [e,e,e,e,e,e,e,e] +// B: [e,e,e,e,e,e,e,e] +// C: [e,e,e,e,f,f,f,f] +// D: [f,f,f,f,f,f,f,f] +// E: [f,f,f,f,f,f,f,f] +// F: [g,g,g,g,g,g,g,g] +// --- +// 0: [g,g,g,g,g,g,g,g] +// 1: [g,g,g,g,h,h,h,h] +// 2: [h,h,h,h,h,h,h,h] +// 3: [h,h,h,h,h,h,h,h] +// ... +DATA ·shuffleInt32x17to26bits+144+0(SB)/4, $0x80020100 +DATA ·shuffleInt32x17to26bits+144+4(SB)/4, $0x80040302 +DATA ·shuffleInt32x17to26bits+144+8(SB)/4, $0x80070605 +DATA ·shuffleInt32x17to26bits+144+12(SB)/4, $0x80090807 + +DATA ·shuffleInt32x17to26bits+144+16(SB)/4, $0x800C0B0A +DATA ·shuffleInt32x17to26bits+144+20(SB)/4, $0x800E0D0C +DATA ·shuffleInt32x17to26bits+144+24(SB)/4, $0x8080800F +DATA ·shuffleInt32x17to26bits+144+28(SB)/4, $0x80808080 + +DATA ·shuffleInt32x17to26bits+144+32(SB)/4, $0x80808080 +DATA ·shuffleInt32x17to26bits+144+36(SB)/4, $0x80808080 +DATA ·shuffleInt32x17to26bits+144+40(SB)/4, $0x80010080 +DATA ·shuffleInt32x17to26bits+144+44(SB)/4, $0x80030201 + +// 21 bits => 32 bits +// ------------------ +// 0: [a,a,a,a,a,a,a,a] +// 1: [a,a,a,a,a,a,a,a] +// 2: [a,a,a,a,a,b,b,b] +// 3: [b,b,b,b,b,b,b,b] +// 4: [b,b,b,b,b,b,b,b] +// 5: [b,b,c,c,c,c,c,c] +// 6: [c,c,c,c,c,c,c,c] +// 7: [c,c,c,c,c,c,c,d] +// 8: [d,d,d,d,d,d,d,d] +// 9: [d,d,d,d,d,d,d,d] +// A: [d,d,d,d,e,e,e,e] +// B: [e,e,e,e,e,e,e,e] +// C: [e,e,e,e,e,e,e,e] +// D: [e,f,f,f,f,f,f,f] +// E: [f,f,f,f,f,f,f,f] +// F: [f,f,f,f,f,f,g,g] +// --- +// 0: [g,g,g,g,g,g,g,g] +// 1: [g,g,g,g,g,g,g,g] +// 2: [g,g,g,h,h,h,h,h] +// 3: [h,h,h,h,h,h,h,h] +// 4: [h,h,h,h,h,h,h,h] +// ... +DATA ·shuffleInt32x17to26bits+192+0(SB)/4, $0x80020100 +DATA ·shuffleInt32x17to26bits+192+4(SB)/4, $0x05040302 +DATA ·shuffleInt32x17to26bits+192+8(SB)/4, $0x80070605 +DATA ·shuffleInt32x17to26bits+192+12(SB)/4, $0x0A090807 + +DATA ·shuffleInt32x17to26bits+192+16(SB)/4, $0x0D0C0B0A +DATA ·shuffleInt32x17to26bits+192+20(SB)/4, $0x800F0E0D +DATA ·shuffleInt32x17to26bits+192+24(SB)/4, $0x8080800F +DATA ·shuffleInt32x17to26bits+192+28(SB)/4, $0x80808080 + +DATA ·shuffleInt32x17to26bits+192+32(SB)/4, $0x80808080 +DATA ·shuffleInt32x17to26bits+192+36(SB)/4, $0x80808080 +DATA ·shuffleInt32x17to26bits+192+40(SB)/4, $0x02010080 +DATA ·shuffleInt32x17to26bits+192+44(SB)/4, $0x80040302 + +// 22 bits => 32 bits +// ------------------ +// 0: [a,a,a,a,a,a,a,a] +// 1: [a,a,a,a,a,a,a,a] +// 2: [a,a,a,a,a,a,b,b] +// 3: [b,b,b,b,b,b,b,b] +// 4: [b,b,b,b,b,b,b,b] +// 5: [b,b,b,b,c,c,c,c] +// 6: [c,c,c,c,c,c,c,c] +// 7: [c,c,c,c,c,c,c,c] +// 8: [c,c,d,d,d,d,d,d] +// 9: [d,d,d,d,d,d,d,d] +// A: [d,d,d,d,d,d,d,d] +// B: [e,e,e,e,e,e,e,e] +// C: [e,e,e,e,e,e,e,e] +// D: [e,e,e,e,e,e,f,f] +// E: [f,f,f,f,f,f,f,f] +// F: [f,f,f,f,f,f,f,f] +// --- +// 0: [f,f,f,f,g,g,g,g] +// 1: [g,g,g,g,g,g,g,g] +// 2: [g,g,g,g,g,g,g,g] +// 3: [g,g,h,h,h,h,h,h] +// 4: [h,h,h,h,h,h,h,h] +// 5: [h,h,h,h,h,h,h,h] +// ... +DATA ·shuffleInt32x17to26bits+240+0(SB)/4, $0x80020100 +DATA ·shuffleInt32x17to26bits+240+4(SB)/4, $0x05040302 +DATA ·shuffleInt32x17to26bits+240+8(SB)/4, $0x08070605 +DATA ·shuffleInt32x17to26bits+240+12(SB)/4, $0x800A0908 + +DATA ·shuffleInt32x17to26bits+240+16(SB)/4, $0x800D0C0B +DATA ·shuffleInt32x17to26bits+240+20(SB)/4, $0x800F0E0D +DATA ·shuffleInt32x17to26bits+240+24(SB)/4, $0x80808080 +DATA ·shuffleInt32x17to26bits+240+28(SB)/4, $0x80808080 + +DATA ·shuffleInt32x17to26bits+240+32(SB)/4, $0x80808080 +DATA ·shuffleInt32x17to26bits+240+36(SB)/4, $0x00808080 +DATA ·shuffleInt32x17to26bits+240+40(SB)/4, $0x03020100 +DATA ·shuffleInt32x17to26bits+240+44(SB)/4, $0x80050403 + +// 23 bits => 32 bits +// ------------------ +// 0: [a,a,a,a,a,a,a,a] +// 1: [a,a,a,a,a,a,a,a] +// 2: [a,a,a,a,a,a,a,b] +// 3: [b,b,b,b,b,b,b,b] +// 4: [b,b,b,b,b,b,b,b] +// 5: [b,b,b,b,b,b,c,c] +// 6: [c,c,c,c,c,c,c,c] +// 7: [c,c,c,c,c,c,c,c] +// 8: [c,c,c,c,c,d,d,d] +// 9: [d,d,d,d,d,d,d,d] +// A: [d,d,d,d,d,d,d,d] +// B: [d,d,d,d,e,e,e,e] +// C: [e,e,e,e,e,e,e,e] +// D: [e,e,e,e,e,e,e,e] +// E: [e,e,e,f,f,f,f,f] +// F: [f,f,f,f,f,f,f,f] +// --- +// 0: [f,f,f,f,f,f,f,f] +// 1: [f,f,g,g,g,g,g,g] +// 2: [g,g,g,g,g,g,g,g] +// 3: [g,g,g,g,g,g,g,g] +// 4: [g,h,h,h,h,h,h,h] +// 5: [h,h,h,h,h,h,h,h] +// 6: [h,h,h,h,h,h,h,h] +// ... +DATA ·shuffleInt32x17to26bits+288+0(SB)/4, $0x80020100 +DATA ·shuffleInt32x17to26bits+288+4(SB)/4, $0x05040302 +DATA ·shuffleInt32x17to26bits+288+8(SB)/4, $0x08070605 +DATA ·shuffleInt32x17to26bits+288+12(SB)/4, $0x0B0A0908 + +DATA ·shuffleInt32x17to26bits+288+16(SB)/4, $0x0E0D0C0B +DATA ·shuffleInt32x17to26bits+288+20(SB)/4, $0x80800F0E +DATA ·shuffleInt32x17to26bits+288+24(SB)/4, $0x80808080 +DATA ·shuffleInt32x17to26bits+288+28(SB)/4, $0x80808080 + +DATA ·shuffleInt32x17to26bits+288+32(SB)/4, $0x80808080 +DATA ·shuffleInt32x17to26bits+288+36(SB)/4, $0x01008080 +DATA ·shuffleInt32x17to26bits+288+40(SB)/4, $0x04030201 +DATA ·shuffleInt32x17to26bits+288+44(SB)/4, $0x80060504 + +// 24 bits => 32 bits +// ------------------ +// 0: [a,a,a,a,a,a,a,a] +// 1: [a,a,a,a,a,a,a,a] +// 2: [a,a,a,a,a,a,a,a] +// 3: [b,b,b,b,b,b,b,b] +// 4: [b,b,b,b,b,b,b,b] +// 5: [b,b,b,b,b,b,b,b] +// 6: [c,c,c,c,c,c,c,c] +// 7: [c,c,c,c,c,c,c,c] +// 8: [c,c,c,c,c,c,c,c] +// 9: [d,d,d,d,d,d,d,d] +// A: [d,d,d,d,d,d,d,d] +// B: [d,d,d,d,d,d,d,d] +// C: [e,e,e,e,e,e,e,e] +// D: [e,e,e,e,e,e,e,e] +// E: [e,e,e,e,e,e,e,e] +// F: [f,f,f,f,f,f,f,f] +// --- +// 0: [f,f,f,f,f,f,f,f] +// 1: [f,f,f,f,f,f,f,f] +// 2: [g,g,g,g,g,g,g,g] +// 3: [g,g,g,g,g,g,g,g] +// 4: [g,g,g,g,g,g,g,g] +// 5: [h,h,h,h,h,h,h,h] +// 6: [h,h,h,h,h,h,h,h] +// 7: [h,h,h,h,h,h,h,h] +// ... +DATA ·shuffleInt32x17to26bits+336+0(SB)/4, $0x80020100 +DATA ·shuffleInt32x17to26bits+336+4(SB)/4, $0x80050403 +DATA ·shuffleInt32x17to26bits+336+8(SB)/4, $0x80080706 +DATA ·shuffleInt32x17to26bits+336+12(SB)/4, $0x800B0A09 + +DATA ·shuffleInt32x17to26bits+336+16(SB)/4, $0x800E0D0C +DATA ·shuffleInt32x17to26bits+336+20(SB)/4, $0x8080800F +DATA ·shuffleInt32x17to26bits+336+24(SB)/4, $0x80808080 +DATA ·shuffleInt32x17to26bits+336+28(SB)/4, $0x80808080 + +DATA ·shuffleInt32x17to26bits+336+32(SB)/4, $0x80808080 +DATA ·shuffleInt32x17to26bits+336+36(SB)/4, $0x80010080 +DATA ·shuffleInt32x17to26bits+336+40(SB)/4, $0x80040302 +DATA ·shuffleInt32x17to26bits+336+44(SB)/4, $0x80070605 + +// 25 bits => 32 bits +// ------------------ +// 0: [a,a,a,a,a,a,a,a] +// 1: [a,a,a,a,a,a,a,a] +// 2: [a,a,a,a,a,a,a,a] +// 3: [a,b,b,b,b,b,b,b] +// 4: [b,b,b,b,b,b,b,b] +// 5: [b,b,b,b,b,b,b,b] +// 6: [b,b,c,c,c,c,c,c] +// 7: [c,c,c,c,c,c,c,c] +// 8: [c,c,c,c,c,c,c,c] +// 9: [c,c,c,d,d,d,d,d] +// A: [d,d,d,d,d,d,d,d] +// B: [d,d,d,d,d,d,d,d] +// C: [d,d,d,d,e,e,e,e] +// D: [e,e,e,e,e,e,e,e] +// E: [e,e,e,e,e,e,e,e] +// F: [e,e,e,e,e,f,f,f] +// --- +// 0: [f,f,f,f,f,f,f,f] +// 1: [f,f,f,f,f,f,f,f] +// 2: [f,f,f,f,f,f,g,g] +// 3: [g,g,g,g,g,g,g,g] +// 4: [g,g,g,g,g,g,g,g] +// 5: [g,g,g,g,g,g,g,h] +// 6: [h,h,h,h,h,h,h,h] +// 7: [h,h,h,h,h,h,h,h] +// 8: [h,h,h,h,h,h,h,h] +// ... +DATA ·shuffleInt32x17to26bits+384+0(SB)/4, $0x03020100 +DATA ·shuffleInt32x17to26bits+384+4(SB)/4, $0x06050403 +DATA ·shuffleInt32x17to26bits+384+8(SB)/4, $0x09080706 +DATA ·shuffleInt32x17to26bits+384+12(SB)/4, $0x0C0B0A09 + +DATA ·shuffleInt32x17to26bits+384+16(SB)/4, $0x0F0E0D0C +DATA ·shuffleInt32x17to26bits+384+20(SB)/4, $0x8080800F +DATA ·shuffleInt32x17to26bits+384+24(SB)/4, $0x80808080 +DATA ·shuffleInt32x17to26bits+384+28(SB)/4, $0x80808080 + +DATA ·shuffleInt32x17to26bits+384+32(SB)/4, $0x80808080 +DATA ·shuffleInt32x17to26bits+384+36(SB)/4, $0x02010080 +DATA ·shuffleInt32x17to26bits+384+40(SB)/4, $0x05040302 +DATA ·shuffleInt32x17to26bits+384+44(SB)/4, $0x08070605 + +// 26 bits => 32 bits +// ------------------ +// 0: [a,a,a,a,a,a,a,a] +// 1: [a,a,a,a,a,a,a,a] +// 2: [a,a,a,a,a,a,a,a] +// 3: [a,a,b,b,b,b,b,b] +// 4: [b,b,b,b,b,b,b,b] +// 5: [b,b,b,b,b,b,b,b] +// 6: [b,b,b,b,c,c,c,c] +// 7: [c,c,c,c,c,c,c,c] +// 8: [c,c,c,c,c,c,c,c] +// 9: [c,c,c,c,c,c,d,d] +// A: [d,d,d,d,d,d,d,d] +// B: [d,d,d,d,d,d,d,d] +// C: [d,d,d,d,d,d,d,d] +// D: [e,e,e,e,e,e,e,e] +// E: [e,e,e,e,e,e,e,e] +// F: [e,e,e,e,e,e,e,e] +// --- +// 0: [e,e,f,f,f,f,f,f] +// 1: [f,f,f,f,f,f,f,f] +// 2: [f,f,f,f,f,f,f,f] +// 3: [f,f,f,f,g,g,g,g] +// 4: [g,g,g,g,g,g,g,g] +// 5: [g,g,g,g,g,g,g,g] +// 6: [g,g,g,g,g,g,h,h] +// 7: [h,h,h,h,h,h,h,h] +// 8: [h,h,h,h,h,h,h,h] +// 9: [h,h,h,h,h,h,h,h] +// ... +DATA ·shuffleInt32x17to26bits+432+0(SB)/4, $0x03020100 +DATA ·shuffleInt32x17to26bits+432+4(SB)/4, $0x06050403 +DATA ·shuffleInt32x17to26bits+432+8(SB)/4, $0x09080706 +DATA ·shuffleInt32x17to26bits+432+12(SB)/4, $0x0C0B0A09 + +DATA ·shuffleInt32x17to26bits+432+16(SB)/4, $0x800F0E0D +DATA ·shuffleInt32x17to26bits+432+20(SB)/4, $0x80808080 +DATA ·shuffleInt32x17to26bits+432+24(SB)/4, $0x80808080 +DATA ·shuffleInt32x17to26bits+432+28(SB)/4, $0x80808080 + +DATA ·shuffleInt32x17to26bits+432+32(SB)/4, $0x00808080 +DATA ·shuffleInt32x17to26bits+432+36(SB)/4, $0x03020100 +DATA ·shuffleInt32x17to26bits+432+40(SB)/4, $0x06050403 +DATA ·shuffleInt32x17to26bits+432+44(SB)/4, $0x09080706 + +// Shuffle masks for unpacking values from bit widths 27 to 31. +// +// The masks are grouped in 80 bytes chunks containing 5 masks of 16 bytes, with +// the following layout: +// +// - The first mask is used to shuffle values from the first 16 bytes of input +// into the lower 16 bytes of output. These values are then shifted RIGHT to +// be aligned on the begining of each 32 bit word. +// +// - The second mask is used to shuffle upper bits of bit-packed values of the +// first 16 bytes of input that spanned across 5 bytes. These extra bits cannot +// be selected by the first mask (which can select at most 4 bytes per word). +// The extra bits are then shifted LEFT to be positioned at the end of the +// words, after the bits extracted by the first mask. +// +// - The third mask selects values from the first 16 bytes of input into the +// upper 16 bytes of output. These values are then shifted RIGHT to be aligned +// on the beginning of each 32 bit word. +// +// - The fourth mask selects values from the second 16 bytes of input into the +// upper 16 bytes of output. These values are then shifted RIGHT to be aligned +// on the beginning of each 32 bit word. +// +// - The fifth mask is used to shuffle upper bits of bit-packed values values of +// second 16 bytes of input that spanned across 5 bytes. These values are then +// shifted LEFT to be aligned on the beginning of each 32 bit word. +// +// The bit width is intended to be used as an index into this array, using this +// formula to convert from the index to a byte offset: +// +// offset = 80 * (bitWidth - 27) +// +GLOBL ·shuffleInt32x27to31bits(SB), RODATA|NOPTR, $400 + +// 27 bits => 32 bits +// ------------------ +// 0: [a,a,a,a,a,a,a,a] +// 1: [a,a,a,a,a,a,a,a] +// 2: [a,a,a,a,a,a,a,a] +// 3: [a,a,a,b,b,b,b,b] +// 4: [b,b,b,b,b,b,b,b] +// 5: [b,b,b,b,b,b,b,b] +// 6: [b,b,b,b,b,b,c,c] +// 7: [c,c,c,c,c,c,c,c] +// 8: [c,c,c,c,c,c,c,c] +// 9: [c,c,c,c,c,c,c,c] +// A: [c,d,d,d,d,d,d,d] +// B: [d,d,d,d,d,d,d,d] +// C: [d,d,d,d,d,d,d,d] +// D: [d,d,d,d,e,e,e,e] +// E: [e,e,e,e,e,e,e,e] +// F: [e,e,e,e,e,e,e,e] +// --- +// 0: [e,e,e,e,e,e,e,f] +// 1: [f,f,f,f,f,f,f,f] +// 2: [f,f,f,f,f,f,f,f] +// 3: [f,f,f,f,f,f,f,f] +// 4: [f,f,g,g,g,g,g,g] +// 5: [g,g,g,g,g,g,g,g] +// 6: [g,g,g,g,g,g,g,g] +// 7: [g,g,g,g,g,h,h,h] +// 8: [h,h,h,h,h,h,h,h] +// 9: [h,h,h,h,h,h,h,h] +// A: [h,h,h,h,h,h,h,h] +// ... +DATA ·shuffleInt32x27to31bits+0+0(SB)/4, $0x03020100 +DATA ·shuffleInt32x27to31bits+0+4(SB)/4, $0x06050403 +DATA ·shuffleInt32x27to31bits+0+8(SB)/4, $0x09080706 +DATA ·shuffleInt32x27to31bits+0+12(SB)/4, $0x0D0C0B0A + +DATA ·shuffleInt32x27to31bits+0+16(SB)/4, $0x80808080 +DATA ·shuffleInt32x27to31bits+0+20(SB)/4, $0x80808080 +DATA ·shuffleInt32x27to31bits+0+24(SB)/4, $0x0A808080 +DATA ·shuffleInt32x27to31bits+0+28(SB)/4, $0x80808080 + +DATA ·shuffleInt32x27to31bits+0+32(SB)/4, $0x800F0E0D +DATA ·shuffleInt32x27to31bits+0+36(SB)/4, $0x80808080 +DATA ·shuffleInt32x27to31bits+0+40(SB)/4, $0x80808080 +DATA ·shuffleInt32x27to31bits+0+44(SB)/4, $0x80808080 + +DATA ·shuffleInt32x27to31bits+0+48(SB)/4, $0x00808080 +DATA ·shuffleInt32x27to31bits+0+52(SB)/4, $0x03020100 +DATA ·shuffleInt32x27to31bits+0+56(SB)/4, $0x07060504 +DATA ·shuffleInt32x27to31bits+0+60(SB)/4, $0x0A090807 + +DATA ·shuffleInt32x27to31bits+0+64(SB)/4, $0x80808080 +DATA ·shuffleInt32x27to31bits+0+68(SB)/4, $0x04808080 +DATA ·shuffleInt32x27to31bits+0+72(SB)/4, $0x80808080 +DATA ·shuffleInt32x27to31bits+0+76(SB)/4, $0x80808080 + +// 28 bits => 32 bits +// ------------------ +// 0: [a,a,a,a,a,a,a,a] +// 1: [a,a,a,a,a,a,a,a] +// 2: [a,a,a,a,a,a,a,a] +// 3: [a,a,a,a,b,b,b,b] +// 4: [b,b,b,b,b,b,b,b] +// 5: [b,b,b,b,b,b,b,b] +// 6: [b,b,b,b,b,b,b,b] +// 7: [c,c,c,c,c,c,c,c] +// 8: [c,c,c,c,c,c,c,c] +// 9: [c,c,c,c,c,c,c,c] +// A: [c,c,c,c,d,d,d,d] +// B: [d,d,d,d,d,d,d,d] +// C: [d,d,d,d,d,d,d,d] +// D: [d,d,d,d,d,d,d,d] +// E: [e,e,e,e,e,e,e,e] +// F: [e,e,e,e,e,e,e,e] +// --- +// 0: [e,e,e,e,e,e,e,e] +// 1: [e,e,e,e,f,f,f,f] +// 2: [f,f,f,f,f,f,f,f] +// 3: [f,f,f,f,f,f,f,f] +// 4: [f,f,f,f,f,f,f,f] +// 5: [g,g,g,g,g,g,g,g] +// 6: [g,g,g,g,g,g,g,g] +// 7: [g,g,g,g,g,g,g,g] +// 8: [g,g,g,g,h,h,h,h] +// 9: [h,h,h,h,h,h,h,h] +// A: [h,h,h,h,h,h,h,h] +// B: [h,h,h,h,h,h,h,h] +// ... +DATA ·shuffleInt32x27to31bits+80+0(SB)/4, $0x03020100 +DATA ·shuffleInt32x27to31bits+80+4(SB)/4, $0x06050403 +DATA ·shuffleInt32x27to31bits+80+8(SB)/4, $0x0A090807 +DATA ·shuffleInt32x27to31bits+80+12(SB)/4, $0x0D0C0B0A + +DATA ·shuffleInt32x27to31bits+80+16(SB)/4, $0x80808080 +DATA ·shuffleInt32x27to31bits+80+20(SB)/4, $0x80808080 +DATA ·shuffleInt32x27to31bits+80+24(SB)/4, $0x80808080 +DATA ·shuffleInt32x27to31bits+80+28(SB)/4, $0x80808080 + +DATA ·shuffleInt32x27to31bits+80+32(SB)/4, $0x80800F0E +DATA ·shuffleInt32x27to31bits+80+36(SB)/4, $0x80808080 +DATA ·shuffleInt32x27to31bits+80+40(SB)/4, $0x80808080 +DATA ·shuffleInt32x27to31bits+80+44(SB)/4, $0x80808080 + +DATA ·shuffleInt32x27to31bits+80+48(SB)/4, $0x01008080 +DATA ·shuffleInt32x27to31bits+80+52(SB)/4, $0x04030201 +DATA ·shuffleInt32x27to31bits+80+56(SB)/4, $0x08070605 +DATA ·shuffleInt32x27to31bits+80+60(SB)/4, $0x0B0A0908 + +DATA ·shuffleInt32x27to31bits+80+64(SB)/4, $0x80808080 +DATA ·shuffleInt32x27to31bits+80+68(SB)/4, $0x80808080 +DATA ·shuffleInt32x27to31bits+80+72(SB)/4, $0x80808080 +DATA ·shuffleInt32x27to31bits+80+76(SB)/4, $0x80808080 + +// 29 bits => 32 bits +// ------------------ +// 0: [a,a,a,a,a,a,a,a] +// 1: [a,a,a,a,a,a,a,a] +// 2: [a,a,a,a,a,a,a,a] +// 3: [a,a,a,a,a,b,b,b] +// 4: [b,b,b,b,b,b,b,b] +// 5: [b,b,b,b,b,b,b,b] +// 6: [b,b,b,b,b,b,b,b] +// 7: [b,b,c,c,c,c,c,c] +// 8: [c,c,c,c,c,c,c,c] +// 9: [c,c,c,c,c,c,c,c] +// A: [c,c,c,c,c,c,c,d] +// B: [d,d,d,d,d,d,d,d] +// C: [d,d,d,d,d,d,d,d] +// D: [d,d,d,d,d,d,d,d] +// E: [d,d,d,d,e,e,e,e] +// F: [e,e,e,e,e,e,e,e] +// --- +// 0: [e,e,e,e,e,e,e,e] +// 1: [e,e,e,e,e,e,e,e] +// 2: [e,f,f,f,f,f,f,f] +// 3: [f,f,f,f,f,f,f,f] +// 4: [f,f,f,f,f,f,f,f] +// 5: [f,f,f,f,f,f,g,g] +// 6: [g,g,g,g,g,g,g,g] +// 7: [g,g,g,g,g,g,g,g] +// 8: [g,g,g,g,g,g,g,g] +// 9: [g,g,g,h,h,h,h,h] +// A: [h,h,h,h,h,h,h,h] +// B: [h,h,h,h,h,h,h,h] +// C: [h,h,h,h,h,h,h,h] +// ... +DATA ·shuffleInt32x27to31bits+160+0(SB)/4, $0x03020100 +DATA ·shuffleInt32x27to31bits+160+4(SB)/4, $0x06050403 +DATA ·shuffleInt32x27to31bits+160+8(SB)/4, $0x0A090807 +DATA ·shuffleInt32x27to31bits+160+12(SB)/4, $0x0D0C0B0A + +DATA ·shuffleInt32x27to31bits+160+16(SB)/4, $0x80808080 +DATA ·shuffleInt32x27to31bits+160+20(SB)/4, $0x07808080 +DATA ·shuffleInt32x27to31bits+160+24(SB)/4, $0x80808080 +DATA ·shuffleInt32x27to31bits+160+28(SB)/4, $0x0E808080 + +DATA ·shuffleInt32x27to31bits+160+32(SB)/4, $0x80800F0E +DATA ·shuffleInt32x27to31bits+160+36(SB)/4, $0x80808080 +DATA ·shuffleInt32x27to31bits+160+40(SB)/4, $0x80808080 +DATA ·shuffleInt32x27to31bits+160+44(SB)/4, $0x80808080 + +DATA ·shuffleInt32x27to31bits+160+48(SB)/4, $0x01008080 +DATA ·shuffleInt32x27to31bits+160+52(SB)/4, $0x05040302 +DATA ·shuffleInt32x27to31bits+160+56(SB)/4, $0x08070605 +DATA ·shuffleInt32x27to31bits+160+60(SB)/4, $0x0C0B0A09 + +DATA ·shuffleInt32x27to31bits+160+64(SB)/4, $0x02808080 +DATA ·shuffleInt32x27to31bits+160+68(SB)/4, $0x80808080 +DATA ·shuffleInt32x27to31bits+160+72(SB)/4, $0x09808080 +DATA ·shuffleInt32x27to31bits+160+76(SB)/4, $0x80808080 + +// 30 bits => 32 bits +// ------------------ +// 0: [a,a,a,a,a,a,a,a] +// 1: [a,a,a,a,a,a,a,a] +// 2: [a,a,a,a,a,a,a,a] +// 3: [a,a,a,a,a,a,b,b] +// 4: [b,b,b,b,b,b,b,b] +// 5: [b,b,b,b,b,b,b,b] +// 6: [b,b,b,b,b,b,b,b] +// 7: [b,b,b,b,c,c,c,c] +// 8: [c,c,c,c,c,c,c,c] +// 9: [c,c,c,c,c,c,c,c] +// A: [c,c,c,c,c,c,c,c] +// B: [c,c,d,d,d,d,d,d] +// C: [d,d,d,d,d,d,d,d] +// D: [d,d,d,d,d,d,d,d] +// E: [d,d,d,d,d,d,d,d] +// F: [e,e,e,e,e,e,e,e] +// --- +// 0: [e,e,e,e,e,e,e,e] +// 1: [e,e,e,e,e,e,e,e] +// 2: [e,e,e,e,e,e,f,f] +// 3: [f,f,f,f,f,f,f,f] +// 4: [f,f,f,f,f,f,f,f] +// 5: [f,f,f,f,f,f,f,f] +// 6: [f,f,f,f,g,g,g,g] +// 7: [g,g,g,g,g,g,g,g] +// 8: [g,g,g,g,g,g,g,g] +// 9: [g,g,g,g,g,g,g,g] +// A: [g,g,h,h,h,h,h,h] +// B: [h,h,h,h,h,h,h,h] +// C: [h,h,h,h,h,h,h,h] +// D: [h,h,h,h,h,h,h,h] +// ... +DATA ·shuffleInt32x27to31bits+240+0(SB)/4, $0x03020100 +DATA ·shuffleInt32x27to31bits+240+4(SB)/4, $0x06050403 +DATA ·shuffleInt32x27to31bits+240+8(SB)/4, $0x0A090807 +DATA ·shuffleInt32x27to31bits+240+12(SB)/4, $0x0E0D0C0B + +DATA ·shuffleInt32x27to31bits+240+16(SB)/4, $0x80808080 +DATA ·shuffleInt32x27to31bits+240+20(SB)/4, $0x07808080 +DATA ·shuffleInt32x27to31bits+240+24(SB)/4, $0x0B808080 +DATA ·shuffleInt32x27to31bits+240+28(SB)/4, $0x80808080 + +DATA ·shuffleInt32x27to31bits+240+32(SB)/4, $0x8080800F +DATA ·shuffleInt32x27to31bits+240+36(SB)/4, $0x80808080 +DATA ·shuffleInt32x27to31bits+240+40(SB)/4, $0x80808080 +DATA ·shuffleInt32x27to31bits+240+44(SB)/4, $0x80808080 + +DATA ·shuffleInt32x27to31bits+240+48(SB)/4, $0x02010080 +DATA ·shuffleInt32x27to31bits+240+52(SB)/4, $0x05040302 +DATA ·shuffleInt32x27to31bits+240+56(SB)/4, $0x09080706 +DATA ·shuffleInt32x27to31bits+240+60(SB)/4, $0x0D0C0B0A + +DATA ·shuffleInt32x27to31bits+240+64(SB)/4, $0x80808080 +DATA ·shuffleInt32x27to31bits+240+68(SB)/4, $0x06808080 +DATA ·shuffleInt32x27to31bits+240+72(SB)/4, $0x0A808080 +DATA ·shuffleInt32x27to31bits+240+76(SB)/4, $0x80808080 + +// 31 bits => 32 bits +// ------------------ +// 0: [a,a,a,a,a,a,a,a] +// 1: [a,a,a,a,a,a,a,a] +// 2: [a,a,a,a,a,a,a,a] +// 3: [a,a,a,a,a,a,a,b] +// 4: [b,b,b,b,b,b,b,b] +// 5: [b,b,b,b,b,b,b,b] +// 6: [b,b,b,b,b,b,b,b] +// 7: [b,b,b,b,b,b,c,c] +// 8: [c,c,c,c,c,c,c,c] +// 9: [c,c,c,c,c,c,c,c] +// A: [c,c,c,c,c,c,c,c] +// B: [c,c,c,c,c,d,d,d] +// C: [d,d,d,d,d,d,d,d] +// D: [d,d,d,d,d,d,d,d] +// E: [d,d,d,d,d,d,d,d] +// F: [d,d,d,d,e,e,e,e] +// --- +// 0: [e,e,e,e,e,e,e,e] +// 1: [e,e,e,e,e,e,e,e] +// 2: [e,e,e,e,e,e,e,e] +// 3: [e,e,e,f,f,f,f,f] +// 4: [f,f,f,f,f,f,f,f] +// 5: [f,f,f,f,f,f,f,f] +// 6: [f,f,f,f,f,f,f,f] +// 7: [f,f,g,g,g,g,g,g] +// 8: [g,g,g,g,g,g,g,g] +// 9: [g,g,g,g,g,g,g,g] +// A: [g,g,g,g,g,g,g,g] +// B: [g,h,h,h,h,h,h,h] +// C: [h,h,h,h,h,h,h,h] +// D: [h,h,h,h,h,h,h,h] +// E: [h,h,h,h,h,h,h,h] +// ... +DATA ·shuffleInt32x27to31bits+320+0(SB)/4, $0x03020100 +DATA ·shuffleInt32x27to31bits+320+4(SB)/4, $0x06050403 +DATA ·shuffleInt32x27to31bits+320+8(SB)/4, $0x0A090807 +DATA ·shuffleInt32x27to31bits+320+12(SB)/4, $0x0E0D0C0B + +DATA ·shuffleInt32x27to31bits+320+16(SB)/4, $0x80808080 +DATA ·shuffleInt32x27to31bits+320+20(SB)/4, $0x07808080 +DATA ·shuffleInt32x27to31bits+320+24(SB)/4, $0x0B808080 +DATA ·shuffleInt32x27to31bits+320+28(SB)/4, $0x0F808080 + +DATA ·shuffleInt32x27to31bits+320+32(SB)/4, $0x8080800F +DATA ·shuffleInt32x27to31bits+320+36(SB)/4, $0x80808080 +DATA ·shuffleInt32x27to31bits+320+40(SB)/4, $0x80808080 +DATA ·shuffleInt32x27to31bits+320+44(SB)/4, $0x80808080 + +DATA ·shuffleInt32x27to31bits+320+48(SB)/4, $0x02010080 +DATA ·shuffleInt32x27to31bits+320+52(SB)/4, $0x06050403 +DATA ·shuffleInt32x27to31bits+320+56(SB)/4, $0x0A090807 +DATA ·shuffleInt32x27to31bits+320+60(SB)/4, $0x0E0D0C0B + +DATA ·shuffleInt32x27to31bits+320+64(SB)/4, $0x03808080 +DATA ·shuffleInt32x27to31bits+320+68(SB)/4, $0x07808080 +DATA ·shuffleInt32x27to31bits+320+72(SB)/4, $0x0B808080 +DATA ·shuffleInt32x27to31bits+320+76(SB)/4, $0x80808080 + +// The RIGHT shifts to unpack 32 bits integers. +// +// The following formula was determined empirically as the expression which +// generates shift values: +// +// shift[i] = (i * bitWidth) % 8 +// +GLOBL ·shiftRightInt32(SB), RODATA|NOPTR, $256 + +DATA ·shiftRightInt32+0+0(SB)/4, $0 +DATA ·shiftRightInt32+0+4(SB)/4, $1 +DATA ·shiftRightInt32+0+8(SB)/4, $2 +DATA ·shiftRightInt32+0+12(SB)/4, $3 +DATA ·shiftRightInt32+0+16(SB)/4, $4 +DATA ·shiftRightInt32+0+20(SB)/4, $5 +DATA ·shiftRightInt32+0+24(SB)/4, $6 +DATA ·shiftRightInt32+0+28(SB)/4, $7 + +DATA ·shiftRightInt32+32+0(SB)/4, $0 +DATA ·shiftRightInt32+32+4(SB)/4, $2 +DATA ·shiftRightInt32+32+8(SB)/4, $4 +DATA ·shiftRightInt32+32+12(SB)/4, $6 +DATA ·shiftRightInt32+32+16(SB)/4, $0 +DATA ·shiftRightInt32+32+20(SB)/4, $2 +DATA ·shiftRightInt32+32+24(SB)/4, $4 +DATA ·shiftRightInt32+32+28(SB)/4, $6 + +DATA ·shiftRightInt32+64+0(SB)/4, $0 +DATA ·shiftRightInt32+64+4(SB)/4, $3 +DATA ·shiftRightInt32+64+8(SB)/4, $6 +DATA ·shiftRightInt32+64+12(SB)/4, $1 +DATA ·shiftRightInt32+64+16(SB)/4, $4 +DATA ·shiftRightInt32+64+20(SB)/4, $7 +DATA ·shiftRightInt32+64+24(SB)/4, $2 +DATA ·shiftRightInt32+64+28(SB)/4, $5 + +DATA ·shiftRightInt32+96+0(SB)/4, $0 +DATA ·shiftRightInt32+96+4(SB)/4, $4 +DATA ·shiftRightInt32+96+8(SB)/4, $0 +DATA ·shiftRightInt32+96+12(SB)/4, $4 +DATA ·shiftRightInt32+96+16(SB)/4, $0 +DATA ·shiftRightInt32+96+20(SB)/4, $4 +DATA ·shiftRightInt32+96+24(SB)/4, $0 +DATA ·shiftRightInt32+96+28(SB)/4, $4 + +DATA ·shiftRightInt32+128+0(SB)/4, $0 +DATA ·shiftRightInt32+128+4(SB)/4, $5 +DATA ·shiftRightInt32+128+8(SB)/4, $2 +DATA ·shiftRightInt32+128+12(SB)/4, $7 +DATA ·shiftRightInt32+128+16(SB)/4, $4 +DATA ·shiftRightInt32+128+20(SB)/4, $1 +DATA ·shiftRightInt32+128+24(SB)/4, $6 +DATA ·shiftRightInt32+128+28(SB)/4, $3 + +DATA ·shiftRightInt32+160+0(SB)/4, $0 +DATA ·shiftRightInt32+160+4(SB)/4, $6 +DATA ·shiftRightInt32+160+8(SB)/4, $4 +DATA ·shiftRightInt32+160+12(SB)/4, $2 +DATA ·shiftRightInt32+160+16(SB)/4, $0 +DATA ·shiftRightInt32+160+20(SB)/4, $6 +DATA ·shiftRightInt32+160+24(SB)/4, $4 +DATA ·shiftRightInt32+160+28(SB)/4, $2 + +DATA ·shiftRightInt32+192+0(SB)/4, $0 +DATA ·shiftRightInt32+192+4(SB)/4, $7 +DATA ·shiftRightInt32+192+8(SB)/4, $6 +DATA ·shiftRightInt32+192+12(SB)/4, $5 +DATA ·shiftRightInt32+192+16(SB)/4, $4 +DATA ·shiftRightInt32+192+20(SB)/4, $3 +DATA ·shiftRightInt32+192+24(SB)/4, $2 +DATA ·shiftRightInt32+192+28(SB)/4, $1 + +DATA ·shiftRightInt32+224+0(SB)/4, $0 +DATA ·shiftRightInt32+224+4(SB)/4, $0 +DATA ·shiftRightInt32+224+8(SB)/4, $0 +DATA ·shiftRightInt32+224+12(SB)/4, $0 +DATA ·shiftRightInt32+224+16(SB)/4, $0 +DATA ·shiftRightInt32+224+20(SB)/4, $0 +DATA ·shiftRightInt32+224+24(SB)/4, $0 +DATA ·shiftRightInt32+224+28(SB)/4, $0 + +// The LEFT shifts to unpack 32 bits integers. +// +// The following formula was determined empirically as the expression which +// generates shift values: +// +// shift[i] = (8 - (i * bitWidth)) % 8 +// +GLOBL ·shiftLeftInt32(SB), RODATA|NOPTR, $256 + +DATA ·shiftLeftInt32+0+0(SB)/4, $0 +DATA ·shiftLeftInt32+0+4(SB)/4, $7 +DATA ·shiftLeftInt32+0+8(SB)/4, $6 +DATA ·shiftLeftInt32+0+12(SB)/4, $5 +DATA ·shiftLeftInt32+0+16(SB)/4, $4 +DATA ·shiftLeftInt32+0+20(SB)/4, $3 +DATA ·shiftLeftInt32+0+24(SB)/4, $2 +DATA ·shiftLeftInt32+0+28(SB)/4, $1 + +DATA ·shiftLeftInt32+32+0(SB)/4, $0 +DATA ·shiftLeftInt32+32+4(SB)/4, $6 +DATA ·shiftLeftInt32+32+8(SB)/4, $4 +DATA ·shiftLeftInt32+32+12(SB)/4, $2 +DATA ·shiftLeftInt32+32+16(SB)/4, $0 +DATA ·shiftLeftInt32+32+20(SB)/4, $6 +DATA ·shiftLeftInt32+32+24(SB)/4, $4 +DATA ·shiftLeftInt32+32+28(SB)/4, $2 + +DATA ·shiftLeftInt32+64+0(SB)/4, $0 +DATA ·shiftLeftInt32+64+4(SB)/4, $5 +DATA ·shiftLeftInt32+64+8(SB)/4, $2 +DATA ·shiftLeftInt32+64+12(SB)/4, $7 +DATA ·shiftLeftInt32+64+16(SB)/4, $4 +DATA ·shiftLeftInt32+64+20(SB)/4, $1 +DATA ·shiftLeftInt32+64+24(SB)/4, $6 +DATA ·shiftLeftInt32+64+28(SB)/4, $3 + +DATA ·shiftLeftInt32+96+0(SB)/4, $0 +DATA ·shiftLeftInt32+96+4(SB)/4, $4 +DATA ·shiftLeftInt32+96+8(SB)/4, $0 +DATA ·shiftLeftInt32+96+12(SB)/4, $4 +DATA ·shiftLeftInt32+96+16(SB)/4, $0 +DATA ·shiftLeftInt32+96+20(SB)/4, $4 +DATA ·shiftLeftInt32+96+24(SB)/4, $0 +DATA ·shiftLeftInt32+96+28(SB)/4, $4 + +DATA ·shiftLeftInt32+128+0(SB)/4, $0 +DATA ·shiftLeftInt32+128+4(SB)/4, $3 +DATA ·shiftLeftInt32+128+8(SB)/4, $6 +DATA ·shiftLeftInt32+128+12(SB)/4, $1 +DATA ·shiftLeftInt32+128+16(SB)/4, $4 +DATA ·shiftLeftInt32+128+20(SB)/4, $7 +DATA ·shiftLeftInt32+128+24(SB)/4, $2 +DATA ·shiftLeftInt32+128+28(SB)/4, $5 + +DATA ·shiftLeftInt32+160+0(SB)/4, $0 +DATA ·shiftLeftInt32+160+4(SB)/4, $2 +DATA ·shiftLeftInt32+160+8(SB)/4, $4 +DATA ·shiftLeftInt32+160+12(SB)/4, $6 +DATA ·shiftLeftInt32+160+16(SB)/4, $0 +DATA ·shiftLeftInt32+160+20(SB)/4, $2 +DATA ·shiftLeftInt32+160+24(SB)/4, $4 +DATA ·shiftLeftInt32+160+28(SB)/4, $6 + +DATA ·shiftLeftInt32+192+0(SB)/4, $0 +DATA ·shiftLeftInt32+192+4(SB)/4, $1 +DATA ·shiftLeftInt32+192+8(SB)/4, $2 +DATA ·shiftLeftInt32+192+12(SB)/4, $3 +DATA ·shiftLeftInt32+192+16(SB)/4, $4 +DATA ·shiftLeftInt32+192+20(SB)/4, $5 +DATA ·shiftLeftInt32+192+24(SB)/4, $6 +DATA ·shiftLeftInt32+192+28(SB)/4, $7 + +DATA ·shiftLeftInt32+224+0(SB)/4, $0 +DATA ·shiftLeftInt32+224+4(SB)/4, $0 +DATA ·shiftLeftInt32+224+8(SB)/4, $0 +DATA ·shiftLeftInt32+224+12(SB)/4, $0 +DATA ·shiftLeftInt32+224+16(SB)/4, $0 +DATA ·shiftLeftInt32+224+20(SB)/4, $0 +DATA ·shiftLeftInt32+224+24(SB)/4, $0 +DATA ·shiftLeftInt32+224+28(SB)/4, $0 diff --git a/vendor/github.com/parquet-go/bitpack/masks_int64_amd64.s b/vendor/github.com/parquet-go/bitpack/masks_int64_amd64.s new file mode 100644 index 000000000..39d64714c --- /dev/null +++ b/vendor/github.com/parquet-go/bitpack/masks_int64_amd64.s @@ -0,0 +1,427 @@ +//go:build !purego + +// Auto-generated by gen_int64_masks.go - DO NOT EDIT + +#include "textflag.h" + +// Pre-computed permutation and shift tables for int64 unpacking +// Each entry is 128 bytes: [permute_0_3][permute_4_7][shifts_0_3][shifts_4_7] +// Format: 4 YMM registers worth of data per bitWidth + +GLOBL ·permuteInt64Table(SB), RODATA|NOPTR, $2944 + +// BitWidth 9 (offset 0) +DATA ·permuteInt64Table+0(SB)/8, $0x0000000100000000 +DATA ·permuteInt64Table+8(SB)/8, $0x0000000100000000 +DATA ·permuteInt64Table+16(SB)/8, $0x0000000100000000 +DATA ·permuteInt64Table+24(SB)/8, $0x0000000100000000 +DATA ·permuteInt64Table+32(SB)/8, $0x0000000200000001 +DATA ·permuteInt64Table+40(SB)/8, $0x0000000200000001 +DATA ·permuteInt64Table+48(SB)/8, $0x0000000200000001 +DATA ·permuteInt64Table+56(SB)/8, $0x0000000200000001 +DATA ·permuteInt64Table+64(SB)/8, $0x0000000000000000 +DATA ·permuteInt64Table+72(SB)/8, $0x0000000000000009 +DATA ·permuteInt64Table+80(SB)/8, $0x0000000000000012 +DATA ·permuteInt64Table+88(SB)/8, $0x000000000000001b +DATA ·permuteInt64Table+96(SB)/8, $0x0000000000000004 +DATA ·permuteInt64Table+104(SB)/8, $0x000000000000000d +DATA ·permuteInt64Table+112(SB)/8, $0x0000000000000016 +DATA ·permuteInt64Table+120(SB)/8, $0x000000000000001f + +// BitWidth 10 (offset 128) +DATA ·permuteInt64Table+128(SB)/8, $0x0000000100000000 +DATA ·permuteInt64Table+136(SB)/8, $0x0000000100000000 +DATA ·permuteInt64Table+144(SB)/8, $0x0000000100000000 +DATA ·permuteInt64Table+152(SB)/8, $0x0000000100000000 +DATA ·permuteInt64Table+160(SB)/8, $0x0000000200000001 +DATA ·permuteInt64Table+168(SB)/8, $0x0000000200000001 +DATA ·permuteInt64Table+176(SB)/8, $0x0000000200000001 +DATA ·permuteInt64Table+184(SB)/8, $0x0000000300000002 +DATA ·permuteInt64Table+192(SB)/8, $0x0000000000000000 +DATA ·permuteInt64Table+200(SB)/8, $0x000000000000000a +DATA ·permuteInt64Table+208(SB)/8, $0x0000000000000014 +DATA ·permuteInt64Table+216(SB)/8, $0x000000000000001e +DATA ·permuteInt64Table+224(SB)/8, $0x0000000000000008 +DATA ·permuteInt64Table+232(SB)/8, $0x0000000000000012 +DATA ·permuteInt64Table+240(SB)/8, $0x000000000000001c +DATA ·permuteInt64Table+248(SB)/8, $0x0000000000000006 + +// BitWidth 11 (offset 256) +DATA ·permuteInt64Table+256(SB)/8, $0x0000000100000000 +DATA ·permuteInt64Table+264(SB)/8, $0x0000000100000000 +DATA ·permuteInt64Table+272(SB)/8, $0x0000000100000000 +DATA ·permuteInt64Table+280(SB)/8, $0x0000000200000001 +DATA ·permuteInt64Table+288(SB)/8, $0x0000000200000001 +DATA ·permuteInt64Table+296(SB)/8, $0x0000000200000001 +DATA ·permuteInt64Table+304(SB)/8, $0x0000000300000002 +DATA ·permuteInt64Table+312(SB)/8, $0x0000000300000002 +DATA ·permuteInt64Table+320(SB)/8, $0x0000000000000000 +DATA ·permuteInt64Table+328(SB)/8, $0x000000000000000b +DATA ·permuteInt64Table+336(SB)/8, $0x0000000000000016 +DATA ·permuteInt64Table+344(SB)/8, $0x0000000000000001 +DATA ·permuteInt64Table+352(SB)/8, $0x000000000000000c +DATA ·permuteInt64Table+360(SB)/8, $0x0000000000000017 +DATA ·permuteInt64Table+368(SB)/8, $0x0000000000000002 +DATA ·permuteInt64Table+376(SB)/8, $0x000000000000000d + +// BitWidth 12 (offset 384) +DATA ·permuteInt64Table+384(SB)/8, $0x0000000100000000 +DATA ·permuteInt64Table+392(SB)/8, $0x0000000100000000 +DATA ·permuteInt64Table+400(SB)/8, $0x0000000100000000 +DATA ·permuteInt64Table+408(SB)/8, $0x0000000200000001 +DATA ·permuteInt64Table+416(SB)/8, $0x0000000200000001 +DATA ·permuteInt64Table+424(SB)/8, $0x0000000200000001 +DATA ·permuteInt64Table+432(SB)/8, $0x0000000300000002 +DATA ·permuteInt64Table+440(SB)/8, $0x0000000300000002 +DATA ·permuteInt64Table+448(SB)/8, $0x0000000000000000 +DATA ·permuteInt64Table+456(SB)/8, $0x000000000000000c +DATA ·permuteInt64Table+464(SB)/8, $0x0000000000000018 +DATA ·permuteInt64Table+472(SB)/8, $0x0000000000000004 +DATA ·permuteInt64Table+480(SB)/8, $0x0000000000000010 +DATA ·permuteInt64Table+488(SB)/8, $0x000000000000001c +DATA ·permuteInt64Table+496(SB)/8, $0x0000000000000008 +DATA ·permuteInt64Table+504(SB)/8, $0x0000000000000014 + +// BitWidth 13 (offset 512) +DATA ·permuteInt64Table+512(SB)/8, $0x0000000100000000 +DATA ·permuteInt64Table+520(SB)/8, $0x0000000100000000 +DATA ·permuteInt64Table+528(SB)/8, $0x0000000100000000 +DATA ·permuteInt64Table+536(SB)/8, $0x0000000200000001 +DATA ·permuteInt64Table+544(SB)/8, $0x0000000200000001 +DATA ·permuteInt64Table+552(SB)/8, $0x0000000300000002 +DATA ·permuteInt64Table+560(SB)/8, $0x0000000300000002 +DATA ·permuteInt64Table+568(SB)/8, $0x0000000300000002 +DATA ·permuteInt64Table+576(SB)/8, $0x0000000000000000 +DATA ·permuteInt64Table+584(SB)/8, $0x000000000000000d +DATA ·permuteInt64Table+592(SB)/8, $0x000000000000001a +DATA ·permuteInt64Table+600(SB)/8, $0x0000000000000007 +DATA ·permuteInt64Table+608(SB)/8, $0x0000000000000014 +DATA ·permuteInt64Table+616(SB)/8, $0x0000000000000001 +DATA ·permuteInt64Table+624(SB)/8, $0x000000000000000e +DATA ·permuteInt64Table+632(SB)/8, $0x000000000000001b + +// BitWidth 14 (offset 640) +DATA ·permuteInt64Table+640(SB)/8, $0x0000000100000000 +DATA ·permuteInt64Table+648(SB)/8, $0x0000000100000000 +DATA ·permuteInt64Table+656(SB)/8, $0x0000000100000000 +DATA ·permuteInt64Table+664(SB)/8, $0x0000000200000001 +DATA ·permuteInt64Table+672(SB)/8, $0x0000000200000001 +DATA ·permuteInt64Table+680(SB)/8, $0x0000000300000002 +DATA ·permuteInt64Table+688(SB)/8, $0x0000000300000002 +DATA ·permuteInt64Table+696(SB)/8, $0x0000000400000003 +DATA ·permuteInt64Table+704(SB)/8, $0x0000000000000000 +DATA ·permuteInt64Table+712(SB)/8, $0x000000000000000e +DATA ·permuteInt64Table+720(SB)/8, $0x000000000000001c +DATA ·permuteInt64Table+728(SB)/8, $0x000000000000000a +DATA ·permuteInt64Table+736(SB)/8, $0x0000000000000018 +DATA ·permuteInt64Table+744(SB)/8, $0x0000000000000006 +DATA ·permuteInt64Table+752(SB)/8, $0x0000000000000014 +DATA ·permuteInt64Table+760(SB)/8, $0x0000000000000002 + +// BitWidth 15 (offset 768) +DATA ·permuteInt64Table+768(SB)/8, $0x0000000100000000 +DATA ·permuteInt64Table+776(SB)/8, $0x0000000100000000 +DATA ·permuteInt64Table+784(SB)/8, $0x0000000100000000 +DATA ·permuteInt64Table+792(SB)/8, $0x0000000200000001 +DATA ·permuteInt64Table+800(SB)/8, $0x0000000200000001 +DATA ·permuteInt64Table+808(SB)/8, $0x0000000300000002 +DATA ·permuteInt64Table+816(SB)/8, $0x0000000300000002 +DATA ·permuteInt64Table+824(SB)/8, $0x0000000400000003 +DATA ·permuteInt64Table+832(SB)/8, $0x0000000000000000 +DATA ·permuteInt64Table+840(SB)/8, $0x000000000000000f +DATA ·permuteInt64Table+848(SB)/8, $0x000000000000001e +DATA ·permuteInt64Table+856(SB)/8, $0x000000000000000d +DATA ·permuteInt64Table+864(SB)/8, $0x000000000000001c +DATA ·permuteInt64Table+872(SB)/8, $0x000000000000000b +DATA ·permuteInt64Table+880(SB)/8, $0x000000000000001a +DATA ·permuteInt64Table+888(SB)/8, $0x0000000000000009 + +// BitWidth 16 (offset 896) +DATA ·permuteInt64Table+896(SB)/8, $0x0000000100000000 +DATA ·permuteInt64Table+904(SB)/8, $0x0000000100000000 +DATA ·permuteInt64Table+912(SB)/8, $0x0000000200000001 +DATA ·permuteInt64Table+920(SB)/8, $0x0000000200000001 +DATA ·permuteInt64Table+928(SB)/8, $0x0000000300000002 +DATA ·permuteInt64Table+936(SB)/8, $0x0000000300000002 +DATA ·permuteInt64Table+944(SB)/8, $0x0000000400000003 +DATA ·permuteInt64Table+952(SB)/8, $0x0000000400000003 +DATA ·permuteInt64Table+960(SB)/8, $0x0000000000000000 +DATA ·permuteInt64Table+968(SB)/8, $0x0000000000000010 +DATA ·permuteInt64Table+976(SB)/8, $0x0000000000000000 +DATA ·permuteInt64Table+984(SB)/8, $0x0000000000000010 +DATA ·permuteInt64Table+992(SB)/8, $0x0000000000000000 +DATA ·permuteInt64Table+1000(SB)/8, $0x0000000000000010 +DATA ·permuteInt64Table+1008(SB)/8, $0x0000000000000000 +DATA ·permuteInt64Table+1016(SB)/8, $0x0000000000000010 + +// BitWidth 17 (offset 1024) +DATA ·permuteInt64Table+1024(SB)/8, $0x0000000100000000 +DATA ·permuteInt64Table+1032(SB)/8, $0x0000000100000000 +DATA ·permuteInt64Table+1040(SB)/8, $0x0000000200000001 +DATA ·permuteInt64Table+1048(SB)/8, $0x0000000200000001 +DATA ·permuteInt64Table+1056(SB)/8, $0x0000000300000002 +DATA ·permuteInt64Table+1064(SB)/8, $0x0000000300000002 +DATA ·permuteInt64Table+1072(SB)/8, $0x0000000400000003 +DATA ·permuteInt64Table+1080(SB)/8, $0x0000000400000003 +DATA ·permuteInt64Table+1088(SB)/8, $0x0000000000000000 +DATA ·permuteInt64Table+1096(SB)/8, $0x0000000000000011 +DATA ·permuteInt64Table+1104(SB)/8, $0x0000000000000002 +DATA ·permuteInt64Table+1112(SB)/8, $0x0000000000000013 +DATA ·permuteInt64Table+1120(SB)/8, $0x0000000000000004 +DATA ·permuteInt64Table+1128(SB)/8, $0x0000000000000015 +DATA ·permuteInt64Table+1136(SB)/8, $0x0000000000000006 +DATA ·permuteInt64Table+1144(SB)/8, $0x0000000000000017 + +// BitWidth 18 (offset 1152) +DATA ·permuteInt64Table+1152(SB)/8, $0x0000000100000000 +DATA ·permuteInt64Table+1160(SB)/8, $0x0000000100000000 +DATA ·permuteInt64Table+1168(SB)/8, $0x0000000200000001 +DATA ·permuteInt64Table+1176(SB)/8, $0x0000000200000001 +DATA ·permuteInt64Table+1184(SB)/8, $0x0000000300000002 +DATA ·permuteInt64Table+1192(SB)/8, $0x0000000300000002 +DATA ·permuteInt64Table+1200(SB)/8, $0x0000000400000003 +DATA ·permuteInt64Table+1208(SB)/8, $0x0000000400000003 +DATA ·permuteInt64Table+1216(SB)/8, $0x0000000000000000 +DATA ·permuteInt64Table+1224(SB)/8, $0x0000000000000012 +DATA ·permuteInt64Table+1232(SB)/8, $0x0000000000000004 +DATA ·permuteInt64Table+1240(SB)/8, $0x0000000000000016 +DATA ·permuteInt64Table+1248(SB)/8, $0x0000000000000008 +DATA ·permuteInt64Table+1256(SB)/8, $0x000000000000001a +DATA ·permuteInt64Table+1264(SB)/8, $0x000000000000000c +DATA ·permuteInt64Table+1272(SB)/8, $0x000000000000001e + +// BitWidth 19 (offset 1280) +DATA ·permuteInt64Table+1280(SB)/8, $0x0000000100000000 +DATA ·permuteInt64Table+1288(SB)/8, $0x0000000100000000 +DATA ·permuteInt64Table+1296(SB)/8, $0x0000000200000001 +DATA ·permuteInt64Table+1304(SB)/8, $0x0000000200000001 +DATA ·permuteInt64Table+1312(SB)/8, $0x0000000300000002 +DATA ·permuteInt64Table+1320(SB)/8, $0x0000000300000002 +DATA ·permuteInt64Table+1328(SB)/8, $0x0000000400000003 +DATA ·permuteInt64Table+1336(SB)/8, $0x0000000500000004 +DATA ·permuteInt64Table+1344(SB)/8, $0x0000000000000000 +DATA ·permuteInt64Table+1352(SB)/8, $0x0000000000000013 +DATA ·permuteInt64Table+1360(SB)/8, $0x0000000000000006 +DATA ·permuteInt64Table+1368(SB)/8, $0x0000000000000019 +DATA ·permuteInt64Table+1376(SB)/8, $0x000000000000000c +DATA ·permuteInt64Table+1384(SB)/8, $0x000000000000001f +DATA ·permuteInt64Table+1392(SB)/8, $0x0000000000000012 +DATA ·permuteInt64Table+1400(SB)/8, $0x0000000000000005 + +// BitWidth 20 (offset 1408) +DATA ·permuteInt64Table+1408(SB)/8, $0x0000000100000000 +DATA ·permuteInt64Table+1416(SB)/8, $0x0000000100000000 +DATA ·permuteInt64Table+1424(SB)/8, $0x0000000200000001 +DATA ·permuteInt64Table+1432(SB)/8, $0x0000000200000001 +DATA ·permuteInt64Table+1440(SB)/8, $0x0000000300000002 +DATA ·permuteInt64Table+1448(SB)/8, $0x0000000400000003 +DATA ·permuteInt64Table+1456(SB)/8, $0x0000000400000003 +DATA ·permuteInt64Table+1464(SB)/8, $0x0000000500000004 +DATA ·permuteInt64Table+1472(SB)/8, $0x0000000000000000 +DATA ·permuteInt64Table+1480(SB)/8, $0x0000000000000014 +DATA ·permuteInt64Table+1488(SB)/8, $0x0000000000000008 +DATA ·permuteInt64Table+1496(SB)/8, $0x000000000000001c +DATA ·permuteInt64Table+1504(SB)/8, $0x0000000000000010 +DATA ·permuteInt64Table+1512(SB)/8, $0x0000000000000004 +DATA ·permuteInt64Table+1520(SB)/8, $0x0000000000000018 +DATA ·permuteInt64Table+1528(SB)/8, $0x000000000000000c + +// BitWidth 21 (offset 1536) +DATA ·permuteInt64Table+1536(SB)/8, $0x0000000100000000 +DATA ·permuteInt64Table+1544(SB)/8, $0x0000000100000000 +DATA ·permuteInt64Table+1552(SB)/8, $0x0000000200000001 +DATA ·permuteInt64Table+1560(SB)/8, $0x0000000200000001 +DATA ·permuteInt64Table+1568(SB)/8, $0x0000000300000002 +DATA ·permuteInt64Table+1576(SB)/8, $0x0000000400000003 +DATA ·permuteInt64Table+1584(SB)/8, $0x0000000400000003 +DATA ·permuteInt64Table+1592(SB)/8, $0x0000000500000004 +DATA ·permuteInt64Table+1600(SB)/8, $0x0000000000000000 +DATA ·permuteInt64Table+1608(SB)/8, $0x0000000000000015 +DATA ·permuteInt64Table+1616(SB)/8, $0x000000000000000a +DATA ·permuteInt64Table+1624(SB)/8, $0x000000000000001f +DATA ·permuteInt64Table+1632(SB)/8, $0x0000000000000014 +DATA ·permuteInt64Table+1640(SB)/8, $0x0000000000000009 +DATA ·permuteInt64Table+1648(SB)/8, $0x000000000000001e +DATA ·permuteInt64Table+1656(SB)/8, $0x0000000000000013 + +// BitWidth 22 (offset 1664) +DATA ·permuteInt64Table+1664(SB)/8, $0x0000000100000000 +DATA ·permuteInt64Table+1672(SB)/8, $0x0000000100000000 +DATA ·permuteInt64Table+1680(SB)/8, $0x0000000200000001 +DATA ·permuteInt64Table+1688(SB)/8, $0x0000000300000002 +DATA ·permuteInt64Table+1696(SB)/8, $0x0000000300000002 +DATA ·permuteInt64Table+1704(SB)/8, $0x0000000400000003 +DATA ·permuteInt64Table+1712(SB)/8, $0x0000000500000004 +DATA ·permuteInt64Table+1720(SB)/8, $0x0000000500000004 +DATA ·permuteInt64Table+1728(SB)/8, $0x0000000000000000 +DATA ·permuteInt64Table+1736(SB)/8, $0x0000000000000016 +DATA ·permuteInt64Table+1744(SB)/8, $0x000000000000000c +DATA ·permuteInt64Table+1752(SB)/8, $0x0000000000000002 +DATA ·permuteInt64Table+1760(SB)/8, $0x0000000000000018 +DATA ·permuteInt64Table+1768(SB)/8, $0x000000000000000e +DATA ·permuteInt64Table+1776(SB)/8, $0x0000000000000004 +DATA ·permuteInt64Table+1784(SB)/8, $0x000000000000001a + +// BitWidth 23 (offset 1792) +DATA ·permuteInt64Table+1792(SB)/8, $0x0000000100000000 +DATA ·permuteInt64Table+1800(SB)/8, $0x0000000100000000 +DATA ·permuteInt64Table+1808(SB)/8, $0x0000000200000001 +DATA ·permuteInt64Table+1816(SB)/8, $0x0000000300000002 +DATA ·permuteInt64Table+1824(SB)/8, $0x0000000300000002 +DATA ·permuteInt64Table+1832(SB)/8, $0x0000000400000003 +DATA ·permuteInt64Table+1840(SB)/8, $0x0000000500000004 +DATA ·permuteInt64Table+1848(SB)/8, $0x0000000600000005 +DATA ·permuteInt64Table+1856(SB)/8, $0x0000000000000000 +DATA ·permuteInt64Table+1864(SB)/8, $0x0000000000000017 +DATA ·permuteInt64Table+1872(SB)/8, $0x000000000000000e +DATA ·permuteInt64Table+1880(SB)/8, $0x0000000000000005 +DATA ·permuteInt64Table+1888(SB)/8, $0x000000000000001c +DATA ·permuteInt64Table+1896(SB)/8, $0x0000000000000013 +DATA ·permuteInt64Table+1904(SB)/8, $0x000000000000000a +DATA ·permuteInt64Table+1912(SB)/8, $0x0000000000000001 + +// BitWidth 24 (offset 1920) +DATA ·permuteInt64Table+1920(SB)/8, $0x0000000100000000 +DATA ·permuteInt64Table+1928(SB)/8, $0x0000000100000000 +DATA ·permuteInt64Table+1936(SB)/8, $0x0000000200000001 +DATA ·permuteInt64Table+1944(SB)/8, $0x0000000300000002 +DATA ·permuteInt64Table+1952(SB)/8, $0x0000000400000003 +DATA ·permuteInt64Table+1960(SB)/8, $0x0000000400000003 +DATA ·permuteInt64Table+1968(SB)/8, $0x0000000500000004 +DATA ·permuteInt64Table+1976(SB)/8, $0x0000000600000005 +DATA ·permuteInt64Table+1984(SB)/8, $0x0000000000000000 +DATA ·permuteInt64Table+1992(SB)/8, $0x0000000000000018 +DATA ·permuteInt64Table+2000(SB)/8, $0x0000000000000010 +DATA ·permuteInt64Table+2008(SB)/8, $0x0000000000000008 +DATA ·permuteInt64Table+2016(SB)/8, $0x0000000000000000 +DATA ·permuteInt64Table+2024(SB)/8, $0x0000000000000018 +DATA ·permuteInt64Table+2032(SB)/8, $0x0000000000000010 +DATA ·permuteInt64Table+2040(SB)/8, $0x0000000000000008 + +// BitWidth 25 (offset 2048) +DATA ·permuteInt64Table+2048(SB)/8, $0x0000000100000000 +DATA ·permuteInt64Table+2056(SB)/8, $0x0000000100000000 +DATA ·permuteInt64Table+2064(SB)/8, $0x0000000200000001 +DATA ·permuteInt64Table+2072(SB)/8, $0x0000000300000002 +DATA ·permuteInt64Table+2080(SB)/8, $0x0000000400000003 +DATA ·permuteInt64Table+2088(SB)/8, $0x0000000400000003 +DATA ·permuteInt64Table+2096(SB)/8, $0x0000000500000004 +DATA ·permuteInt64Table+2104(SB)/8, $0x0000000600000005 +DATA ·permuteInt64Table+2112(SB)/8, $0x0000000000000000 +DATA ·permuteInt64Table+2120(SB)/8, $0x0000000000000019 +DATA ·permuteInt64Table+2128(SB)/8, $0x0000000000000012 +DATA ·permuteInt64Table+2136(SB)/8, $0x000000000000000b +DATA ·permuteInt64Table+2144(SB)/8, $0x0000000000000004 +DATA ·permuteInt64Table+2152(SB)/8, $0x000000000000001d +DATA ·permuteInt64Table+2160(SB)/8, $0x0000000000000016 +DATA ·permuteInt64Table+2168(SB)/8, $0x000000000000000f + +// BitWidth 26 (offset 2176) +DATA ·permuteInt64Table+2176(SB)/8, $0x0000000100000000 +DATA ·permuteInt64Table+2184(SB)/8, $0x0000000100000000 +DATA ·permuteInt64Table+2192(SB)/8, $0x0000000200000001 +DATA ·permuteInt64Table+2200(SB)/8, $0x0000000300000002 +DATA ·permuteInt64Table+2208(SB)/8, $0x0000000400000003 +DATA ·permuteInt64Table+2216(SB)/8, $0x0000000500000004 +DATA ·permuteInt64Table+2224(SB)/8, $0x0000000500000004 +DATA ·permuteInt64Table+2232(SB)/8, $0x0000000600000005 +DATA ·permuteInt64Table+2240(SB)/8, $0x0000000000000000 +DATA ·permuteInt64Table+2248(SB)/8, $0x000000000000001a +DATA ·permuteInt64Table+2256(SB)/8, $0x0000000000000014 +DATA ·permuteInt64Table+2264(SB)/8, $0x000000000000000e +DATA ·permuteInt64Table+2272(SB)/8, $0x0000000000000008 +DATA ·permuteInt64Table+2280(SB)/8, $0x0000000000000002 +DATA ·permuteInt64Table+2288(SB)/8, $0x000000000000001c +DATA ·permuteInt64Table+2296(SB)/8, $0x0000000000000016 + +// BitWidth 27 (offset 2304) +DATA ·permuteInt64Table+2304(SB)/8, $0x0000000100000000 +DATA ·permuteInt64Table+2312(SB)/8, $0x0000000100000000 +DATA ·permuteInt64Table+2320(SB)/8, $0x0000000200000001 +DATA ·permuteInt64Table+2328(SB)/8, $0x0000000300000002 +DATA ·permuteInt64Table+2336(SB)/8, $0x0000000400000003 +DATA ·permuteInt64Table+2344(SB)/8, $0x0000000500000004 +DATA ·permuteInt64Table+2352(SB)/8, $0x0000000600000005 +DATA ·permuteInt64Table+2360(SB)/8, $0x0000000600000005 +DATA ·permuteInt64Table+2368(SB)/8, $0x0000000000000000 +DATA ·permuteInt64Table+2376(SB)/8, $0x000000000000001b +DATA ·permuteInt64Table+2384(SB)/8, $0x0000000000000016 +DATA ·permuteInt64Table+2392(SB)/8, $0x0000000000000011 +DATA ·permuteInt64Table+2400(SB)/8, $0x000000000000000c +DATA ·permuteInt64Table+2408(SB)/8, $0x0000000000000007 +DATA ·permuteInt64Table+2416(SB)/8, $0x0000000000000002 +DATA ·permuteInt64Table+2424(SB)/8, $0x000000000000001d + +// BitWidth 28 (offset 2432) +DATA ·permuteInt64Table+2432(SB)/8, $0x0000000100000000 +DATA ·permuteInt64Table+2440(SB)/8, $0x0000000100000000 +DATA ·permuteInt64Table+2448(SB)/8, $0x0000000200000001 +DATA ·permuteInt64Table+2456(SB)/8, $0x0000000300000002 +DATA ·permuteInt64Table+2464(SB)/8, $0x0000000400000003 +DATA ·permuteInt64Table+2472(SB)/8, $0x0000000500000004 +DATA ·permuteInt64Table+2480(SB)/8, $0x0000000600000005 +DATA ·permuteInt64Table+2488(SB)/8, $0x0000000700000006 +DATA ·permuteInt64Table+2496(SB)/8, $0x0000000000000000 +DATA ·permuteInt64Table+2504(SB)/8, $0x000000000000001c +DATA ·permuteInt64Table+2512(SB)/8, $0x0000000000000018 +DATA ·permuteInt64Table+2520(SB)/8, $0x0000000000000014 +DATA ·permuteInt64Table+2528(SB)/8, $0x0000000000000010 +DATA ·permuteInt64Table+2536(SB)/8, $0x000000000000000c +DATA ·permuteInt64Table+2544(SB)/8, $0x0000000000000008 +DATA ·permuteInt64Table+2552(SB)/8, $0x0000000000000004 + +// BitWidth 29 (offset 2560) +DATA ·permuteInt64Table+2560(SB)/8, $0x0000000100000000 +DATA ·permuteInt64Table+2568(SB)/8, $0x0000000100000000 +DATA ·permuteInt64Table+2576(SB)/8, $0x0000000200000001 +DATA ·permuteInt64Table+2584(SB)/8, $0x0000000300000002 +DATA ·permuteInt64Table+2592(SB)/8, $0x0000000400000003 +DATA ·permuteInt64Table+2600(SB)/8, $0x0000000500000004 +DATA ·permuteInt64Table+2608(SB)/8, $0x0000000600000005 +DATA ·permuteInt64Table+2616(SB)/8, $0x0000000700000006 +DATA ·permuteInt64Table+2624(SB)/8, $0x0000000000000000 +DATA ·permuteInt64Table+2632(SB)/8, $0x000000000000001d +DATA ·permuteInt64Table+2640(SB)/8, $0x000000000000001a +DATA ·permuteInt64Table+2648(SB)/8, $0x0000000000000017 +DATA ·permuteInt64Table+2656(SB)/8, $0x0000000000000014 +DATA ·permuteInt64Table+2664(SB)/8, $0x0000000000000011 +DATA ·permuteInt64Table+2672(SB)/8, $0x000000000000000e +DATA ·permuteInt64Table+2680(SB)/8, $0x000000000000000b + +// BitWidth 30 (offset 2688) +DATA ·permuteInt64Table+2688(SB)/8, $0x0000000100000000 +DATA ·permuteInt64Table+2696(SB)/8, $0x0000000100000000 +DATA ·permuteInt64Table+2704(SB)/8, $0x0000000200000001 +DATA ·permuteInt64Table+2712(SB)/8, $0x0000000300000002 +DATA ·permuteInt64Table+2720(SB)/8, $0x0000000400000003 +DATA ·permuteInt64Table+2728(SB)/8, $0x0000000500000004 +DATA ·permuteInt64Table+2736(SB)/8, $0x0000000600000005 +DATA ·permuteInt64Table+2744(SB)/8, $0x0000000700000006 +DATA ·permuteInt64Table+2752(SB)/8, $0x0000000000000000 +DATA ·permuteInt64Table+2760(SB)/8, $0x000000000000001e +DATA ·permuteInt64Table+2768(SB)/8, $0x000000000000001c +DATA ·permuteInt64Table+2776(SB)/8, $0x000000000000001a +DATA ·permuteInt64Table+2784(SB)/8, $0x0000000000000018 +DATA ·permuteInt64Table+2792(SB)/8, $0x0000000000000016 +DATA ·permuteInt64Table+2800(SB)/8, $0x0000000000000014 +DATA ·permuteInt64Table+2808(SB)/8, $0x0000000000000012 + +// BitWidth 31 (offset 2816) +DATA ·permuteInt64Table+2816(SB)/8, $0x0000000100000000 +DATA ·permuteInt64Table+2824(SB)/8, $0x0000000100000000 +DATA ·permuteInt64Table+2832(SB)/8, $0x0000000200000001 +DATA ·permuteInt64Table+2840(SB)/8, $0x0000000300000002 +DATA ·permuteInt64Table+2848(SB)/8, $0x0000000400000003 +DATA ·permuteInt64Table+2856(SB)/8, $0x0000000500000004 +DATA ·permuteInt64Table+2864(SB)/8, $0x0000000600000005 +DATA ·permuteInt64Table+2872(SB)/8, $0x0000000700000006 +DATA ·permuteInt64Table+2880(SB)/8, $0x0000000000000000 +DATA ·permuteInt64Table+2888(SB)/8, $0x000000000000001f +DATA ·permuteInt64Table+2896(SB)/8, $0x000000000000001e +DATA ·permuteInt64Table+2904(SB)/8, $0x000000000000001d +DATA ·permuteInt64Table+2912(SB)/8, $0x000000000000001c +DATA ·permuteInt64Table+2920(SB)/8, $0x000000000000001b +DATA ·permuteInt64Table+2928(SB)/8, $0x000000000000001a +DATA ·permuteInt64Table+2936(SB)/8, $0x0000000000000019 + +// Total size: 2944 bytes diff --git a/vendor/github.com/parquet-go/bitpack/pack.go b/vendor/github.com/parquet-go/bitpack/pack.go new file mode 100644 index 000000000..a30bd8d3e --- /dev/null +++ b/vendor/github.com/parquet-go/bitpack/pack.go @@ -0,0 +1,19 @@ +package bitpack + +import ( + "unsafe" + + "github.com/parquet-go/bitpack/unsafecast" +) + +// Pack packs values from src to dst, each value is packed into the given +// bit width regardless of how many bits are needed to represent it. +func Pack[T Int](dst []byte, src []T, bitWidth uint) { + _ = dst[:ByteCount(bitWidth*uint(len(src)))] + switch unsafe.Sizeof(T(0)) { + case 4: + packInt32(dst, unsafecast.Slice[int32](src), bitWidth) + default: + packInt64(dst, unsafecast.Slice[int64](src), bitWidth) + } +} diff --git a/vendor/github.com/parquet-go/bitpack/pack_arm64.go b/vendor/github.com/parquet-go/bitpack/pack_arm64.go new file mode 100644 index 000000000..202950d5d --- /dev/null +++ b/vendor/github.com/parquet-go/bitpack/pack_arm64.go @@ -0,0 +1,31 @@ +//go:build !purego + +package bitpack + +//go:noescape +func packInt32ARM64(dst []byte, src []int32, bitWidth uint) + +//go:noescape +func packInt32NEON(dst []byte, src []int32, bitWidth uint) + +//go:noescape +func packInt64ARM64(dst []byte, src []int64, bitWidth uint) + +//go:noescape +func packInt64NEON(dst []byte, src []int64, bitWidth uint) + +func packInt32(dst []byte, src []int32, bitWidth uint) { + if bitWidth <= 8 { + packInt32NEON(dst, src, bitWidth) + } else { + packInt32ARM64(dst, src, bitWidth) + } +} + +func packInt64(dst []byte, src []int64, bitWidth uint) { + if bitWidth <= 8 { + packInt64NEON(dst, src, bitWidth) + } else { + packInt64ARM64(dst, src, bitWidth) + } +} diff --git a/vendor/github.com/parquet-go/bitpack/pack_int32_arm64.s b/vendor/github.com/parquet-go/bitpack/pack_int32_arm64.s new file mode 100644 index 000000000..5c47327cd --- /dev/null +++ b/vendor/github.com/parquet-go/bitpack/pack_int32_arm64.s @@ -0,0 +1,462 @@ +//go:build !purego + +#include "funcdata.h" +#include "textflag.h" + +// func packInt32ARM64(dst []byte, src []int32, bitWidth uint) +TEXT ·packInt32ARM64(SB), NOSPLIT, $0-56 + MOVD dst_base+0(FP), R0 // R0 = dst pointer + MOVD src_base+24(FP), R1 // R1 = src pointer + MOVD src_len+32(FP), R2 // R2 = src length + MOVD bitWidth+48(FP), R3 // R3 = bitWidth + + // Handle bitWidth == 0 + CBZ R3, done + + // R4 = bitMask = (1 << bitWidth) - 1 + MOVD $1, R4 + LSL R3, R4, R4 + SUB $1, R4, R4 + + // R5 = buffer (64-bit accumulator) + // R6 = bufferedBits + // R7 = byteIndex + // R8 = loop counter (src index) + MOVD $0, R5 + MOVD $0, R6 + MOVD $0, R7 + MOVD $0, R8 + + // Main loop: process each value from src +loop: + CMP R2, R8 + BEQ flush_remaining + + // Load value from src[R8] + LSL $2, R8, R16 // R16 = R8 * 4 + MOVWU (R1)(R16), R9 // R9 = src[R8] + + // Mask the value: R9 = value & bitMask + AND R4, R9, R9 + + // Add to buffer: buffer |= (value << bufferedBits) + LSL R6, R9, R10 // R10 = value << bufferedBits + ORR R10, R5, R5 // buffer |= R10 + + // bufferedBits += bitWidth + ADD R3, R6, R6 + + // Increment source index + ADD $1, R8, R8 + +flush_loop: + // While bufferedBits >= 32, flush 32-bit words + CMP $32, R6 + BLT loop + + // Write 32-bit word to dst[byteIndex] + MOVW R5, (R0)(R7) + + // buffer >>= 32 + LSR $32, R5, R5 + + // bufferedBits -= 32 + SUB $32, R6, R6 + + // byteIndex += 4 + ADD $4, R7, R7 + + B flush_loop + +flush_remaining: + // If no bits remaining, we're done + CBZ R6, done + + // Calculate remaining bytes = (bufferedBits + 7) / 8 + ADD $7, R6, R11 + LSR $3, R11, R11 // R11 = remainingBytes + + MOVD $0, R12 // R12 = i (byte counter) + +flush_byte_loop: + CMP R11, R12 + BEQ done + + // dst[byteIndex] = byte(buffer) + MOVB R5, (R0)(R7) + + // buffer >>= 8 + LSR $8, R5, R5 + + // byteIndex++, i++ + ADD $1, R7, R7 + ADD $1, R12, R12 + + B flush_byte_loop + +done: + RET + +// func packInt32NEON(dst []byte, src []int32, bitWidth uint) +TEXT ·packInt32NEON(SB), NOSPLIT, $0-56 + MOVD dst_base+0(FP), R0 // R0 = dst pointer + MOVD src_base+24(FP), R1 // R1 = src pointer + MOVD src_len+32(FP), R2 // R2 = src length + MOVD bitWidth+48(FP), R3 // R3 = bitWidth + + // Handle bitWidth == 0 + CBZ R3, neon_done + + // Initialize processed count to 0 + MOVD $0, R5 + + // Check if we have at least 4 values to process with NEON paths + CMP $4, R2 + BLT neon_done // Not enough values, return and let Go wrapper handle it + + // Determine which NEON path to use based on bitWidth + CMP $1, R3 + BEQ neon_1bit + CMP $2, R3 + BEQ neon_2bit + CMP $3, R3 + BEQ neon_3bit + CMP $4, R3 + BEQ neon_4bit + CMP $5, R3 + BEQ neon_5bit + CMP $6, R3 + BEQ neon_6bit + CMP $7, R3 + BEQ neon_7bit + CMP $8, R3 + BEQ neon_8bit + + // For other bit widths, return without processing + // The Go wrapper will call the scalar version + RET + +neon_1bit: + // BitWidth 1: Pack 8 int32 values into 1 byte + MOVD R2, R4 + LSR $3, R4, R4 // R4 = len / 8 + LSL $3, R4, R4 // R4 = aligned length + MOVD $0, R5 // R5 = index + CMP $0, R4 + BEQ neon_done + +neon_1bit_loop: + MOVWU (R1), R6 + AND $1, R6, R6 + MOVWU 4(R1), R7 + AND $1, R7, R7 + ORR R7<<1, R6, R6 + MOVWU 8(R1), R7 + AND $1, R7, R7 + ORR R7<<2, R6, R6 + MOVWU 12(R1), R7 + AND $1, R7, R7 + ORR R7<<3, R6, R6 + MOVWU 16(R1), R7 + AND $1, R7, R7 + ORR R7<<4, R6, R6 + MOVWU 20(R1), R7 + AND $1, R7, R7 + ORR R7<<5, R6, R6 + MOVWU 24(R1), R7 + AND $1, R7, R7 + ORR R7<<6, R6, R6 + MOVWU 28(R1), R7 + AND $1, R7, R7 + ORR R7<<7, R6, R6 + MOVB R6, (R0) + ADD $32, R1, R1 + ADD $1, R0, R0 + ADD $8, R5, R5 + CMP R4, R5 + BLT neon_1bit_loop + B neon_done + +neon_2bit: + MOVD R2, R4 + LSR $2, R4, R4 + LSL $2, R4, R4 + MOVD $0, R5 + CMP $0, R4 + BEQ neon_done + +neon_2bit_loop: + MOVWU (R1), R6 + AND $3, R6, R6 + MOVWU 4(R1), R7 + AND $3, R7, R7 + ORR R7<<2, R6, R6 + MOVWU 8(R1), R7 + AND $3, R7, R7 + ORR R7<<4, R6, R6 + MOVWU 12(R1), R7 + AND $3, R7, R7 + ORR R7<<6, R6, R6 + MOVB R6, (R0) + ADD $16, R1, R1 + ADD $1, R0, R0 + ADD $4, R5, R5 + CMP R4, R5 + BLT neon_2bit_loop + B neon_done + +neon_3bit: + MOVD R2, R4 + LSR $3, R4, R4 + LSL $3, R4, R4 + MOVD $0, R5 + CMP $0, R4 + BEQ neon_done + +neon_3bit_loop: + MOVWU (R1), R6 + AND $7, R6, R6 + MOVWU 4(R1), R7 + AND $7, R7, R7 + ORR R7<<3, R6, R6 + MOVWU 8(R1), R7 + AND $7, R7, R7 + ORR R7<<6, R6, R6 + MOVWU 12(R1), R7 + AND $7, R7, R7 + ORR R7<<9, R6, R6 + MOVWU 16(R1), R7 + AND $7, R7, R7 + ORR R7<<12, R6, R6 + MOVWU 20(R1), R7 + AND $7, R7, R7 + ORR R7<<15, R6, R6 + MOVWU 24(R1), R7 + AND $7, R7, R7 + ORR R7<<18, R6, R6 + MOVWU 28(R1), R7 + AND $7, R7, R7 + ORR R7<<21, R6, R6 + MOVB R6, (R0) + LSR $8, R6, R7 + MOVB R7, 1(R0) + LSR $16, R6, R7 + MOVB R7, 2(R0) + ADD $32, R1, R1 + ADD $3, R0, R0 + ADD $8, R5, R5 + CMP R4, R5 + BLT neon_3bit_loop + B neon_done + +neon_4bit: + MOVD R2, R4 + LSR $2, R4, R4 + LSL $2, R4, R4 + MOVD $0, R5 + CMP $0, R4 + BEQ neon_done + +neon_4bit_loop: + MOVWU (R1), R6 + AND $15, R6, R6 + MOVWU 4(R1), R7 + AND $15, R7, R7 + ORR R7<<4, R6, R6 + MOVWU 8(R1), R7 + AND $15, R7, R7 + ORR R7<<8, R6, R6 + MOVWU 12(R1), R7 + AND $15, R7, R7 + ORR R7<<12, R6, R6 + MOVH R6, (R0) + ADD $16, R1, R1 + ADD $2, R0, R0 + ADD $4, R5, R5 + CMP R4, R5 + BLT neon_4bit_loop + B neon_done + +neon_5bit: + MOVD R2, R4 + LSR $3, R4, R4 + LSL $3, R4, R4 + MOVD $0, R5 + CMP $0, R4 + BEQ neon_done + +neon_5bit_loop: + MOVD $0, R6 + MOVWU (R1), R7 + AND $31, R7, R7 + ORR R7, R6, R6 + MOVWU 4(R1), R7 + AND $31, R7, R7 + ORR R7<<5, R6, R6 + MOVWU 8(R1), R7 + AND $31, R7, R7 + ORR R7<<10, R6, R6 + MOVWU 12(R1), R7 + AND $31, R7, R7 + ORR R7<<15, R6, R6 + MOVWU 16(R1), R7 + AND $31, R7, R7 + ORR R7<<20, R6, R6 + MOVWU 20(R1), R7 + AND $31, R7, R7 + ORR R7<<25, R6, R6 + MOVWU 24(R1), R7 + AND $31, R7, R7 + ORR R7<<30, R6, R6 + MOVWU 28(R1), R7 + AND $31, R7, R7 + ORR R7<<35, R6, R6 + MOVB R6, (R0) + LSR $8, R6, R7 + MOVB R7, 1(R0) + LSR $16, R6, R7 + MOVB R7, 2(R0) + LSR $24, R6, R7 + MOVB R7, 3(R0) + LSR $32, R6, R7 + MOVB R7, 4(R0) + ADD $32, R1, R1 + ADD $5, R0, R0 + ADD $8, R5, R5 + CMP R4, R5 + BLT neon_5bit_loop + B neon_done + +neon_6bit: + MOVD R2, R4 + LSR $2, R4, R4 + LSL $2, R4, R4 + MOVD $0, R5 + CMP $0, R4 + BEQ neon_done + +neon_6bit_loop: + MOVWU (R1), R6 + AND $63, R6, R6 + MOVWU 4(R1), R7 + AND $63, R7, R7 + ORR R7<<6, R6, R6 + MOVWU 8(R1), R7 + AND $63, R7, R7 + ORR R7<<12, R6, R6 + MOVWU 12(R1), R7 + AND $63, R7, R7 + ORR R7<<18, R6, R6 + MOVB R6, (R0) + LSR $8, R6, R7 + MOVB R7, 1(R0) + LSR $16, R6, R7 + MOVB R7, 2(R0) + ADD $16, R1, R1 + ADD $3, R0, R0 + ADD $4, R5, R5 + CMP R4, R5 + BLT neon_6bit_loop + B neon_done + +neon_7bit: + MOVD R2, R4 + LSR $3, R4, R4 + LSL $3, R4, R4 + MOVD $0, R5 + CMP $0, R4 + BEQ neon_done + +neon_7bit_loop: + MOVD $0, R6 + MOVWU (R1), R7 + AND $127, R7, R7 + ORR R7, R6, R6 + MOVWU 4(R1), R7 + AND $127, R7, R7 + ORR R7<<7, R6, R6 + MOVWU 8(R1), R7 + AND $127, R7, R7 + ORR R7<<14, R6, R6 + MOVWU 12(R1), R7 + AND $127, R7, R7 + ORR R7<<21, R6, R6 + MOVWU 16(R1), R7 + AND $127, R7, R7 + ORR R7<<28, R6, R6 + MOVWU 20(R1), R7 + AND $127, R7, R7 + ORR R7<<35, R6, R6 + MOVWU 24(R1), R7 + AND $127, R7, R7 + ORR R7<<42, R6, R6 + MOVWU 28(R1), R7 + AND $127, R7, R7 + ORR R7<<49, R6, R6 + MOVB R6, (R0) + LSR $8, R6, R7 + MOVB R7, 1(R0) + LSR $16, R6, R7 + MOVB R7, 2(R0) + LSR $24, R6, R7 + MOVB R7, 3(R0) + LSR $32, R6, R7 + MOVB R7, 4(R0) + LSR $40, R6, R7 + MOVB R7, 5(R0) + LSR $48, R6, R7 + MOVB R7, 6(R0) + ADD $32, R1, R1 + ADD $7, R0, R0 + ADD $8, R5, R5 + CMP R4, R5 + BLT neon_7bit_loop + B neon_done + +neon_8bit: + MOVD R2, R4 + LSR $2, R4, R4 + LSL $2, R4, R4 + MOVD $0, R5 + CMP $0, R4 + BEQ neon_done + +neon_8bit_loop: + MOVWU (R1), R6 + MOVB R6, (R0) + MOVWU 4(R1), R6 + MOVB R6, 1(R0) + MOVWU 8(R1), R6 + MOVB R6, 2(R0) + MOVWU 12(R1), R6 + MOVB R6, 3(R0) + ADD $16, R1, R1 + ADD $4, R0, R0 + ADD $4, R5, R5 + CMP R4, R5 + BLT neon_8bit_loop + +neon_done: + // After NEON processing, handle any remainder with scalar code + // Check if there are remaining values to process + CMP R2, R5 // R5 = processed count, R2 = total length + BGE neon_ret // If processed >= total, we're done + + // Calculate remainder: adjust src/dst pointers and length + // Advance src pointer by (R5 * 4) bytes + LSL $2, R5, R16 + ADD R16, R1, R1 + + // Calculate packed bytes for processed values and advance dst + MUL R3, R5, R16 // R16 = processed * bitWidth (in bits) + LSR $3, R16, R16 // R16 = packed bytes + ADD R16, R0, R0 + + // Update remaining length + SUB R5, R2, R2 + + // Jump to scalar implementation for remainder + B ·packInt32ARM64(SB) + +neon_ret: + RET diff --git a/vendor/github.com/parquet-go/bitpack/pack_int64_arm64.s b/vendor/github.com/parquet-go/bitpack/pack_int64_arm64.s new file mode 100644 index 000000000..9b4137d11 --- /dev/null +++ b/vendor/github.com/parquet-go/bitpack/pack_int64_arm64.s @@ -0,0 +1,514 @@ +//go:build !purego + +#include "funcdata.h" +#include "textflag.h" + +// func packInt64ARM64(dst []byte, src []int64, bitWidth uint) +TEXT ·packInt64ARM64(SB), NOSPLIT, $0-56 + MOVD dst_base+0(FP), R0 // R0 = dst pointer + MOVD src_base+24(FP), R1 // R1 = src pointer + MOVD src_len+32(FP), R2 // R2 = src length + MOVD bitWidth+48(FP), R3 // R3 = bitWidth + + // Handle bitWidth == 0 + CBZ R3, done + + // Special case: bitWidth == 64 (no packing needed) + CMP $64, R3 + BEQ copy_direct + + // R4 = bitMask = (1 << bitWidth) - 1 + MOVD $1, R4 + LSL R3, R4, R4 + SUB $1, R4, R4 + + // R5 = bufferLo (64-bit accumulator) + // R6 = bufferHi (overflow buffer) + // R7 = bufferedBits + // R8 = byteIndex + // R9 = loop counter (src index) + MOVD $0, R5 + MOVD $0, R6 + MOVD $0, R7 + MOVD $0, R8 + MOVD $0, R9 + + // Main loop: process each value from src +loop: + CMP R2, R9 + BEQ flush_remaining + + // Load value from src[R9] + LSL $3, R9, R16 // R16 = R9 * 8 + MOVD (R1)(R16), R10 // R10 = src[R9] + + // Mask the value: R10 = value & bitMask + AND R4, R10, R10 + + // Check if value fits entirely in low buffer + ADD R3, R7, R11 // R11 = bufferedBits + bitWidth + CMP $64, R11 + BGT spans_buffers + + // Value fits in low buffer + LSL R7, R10, R12 // R12 = value << bufferedBits + ORR R12, R5, R5 // bufferLo |= R12 + MOVD R11, R7 // bufferedBits = R11 + B increment_index + +spans_buffers: + // Value spans low and high buffers + // bitsInLo = 64 - bufferedBits + MOVD $64, R12 + SUB R7, R12, R12 // R12 = bitsInLo + + // bufferLo |= value << bufferedBits + LSL R7, R10, R13 + ORR R13, R5, R5 + + // bufferHi = value >> bitsInLo + LSR R12, R10, R6 + + // bufferedBits += bitWidth + MOVD R11, R7 + +increment_index: + // Increment source index + ADD $1, R9, R9 + +flush_loop: + // While bufferedBits >= 64, flush 64-bit words + CMP $64, R7 + BLT loop + + // Write 64-bit word to dst[byteIndex] + MOVD R5, (R0)(R8) + + // bufferLo = bufferHi + MOVD R6, R5 + + // bufferHi = 0 + MOVD $0, R6 + + // bufferedBits -= 64 + SUB $64, R7, R7 + + // byteIndex += 8 + ADD $8, R8, R8 + + B flush_loop + +flush_remaining: + // If no bits remaining, we're done + CBZ R7, done + + // Calculate remaining bytes = (bufferedBits + 7) / 8 + ADD $7, R7, R11 + LSR $3, R11, R11 // R11 = remainingBytes + + MOVD $0, R12 // R12 = i (byte counter) + +flush_byte_loop: + CMP R11, R12 + BEQ done + + // dst[byteIndex] = byte(bufferLo) + MOVB R5, (R0)(R8) + + // bufferLo >>= 8 + LSR $8, R5, R5 + + // byteIndex++, i++ + ADD $1, R8, R8 + ADD $1, R12, R12 + + B flush_byte_loop + +copy_direct: + // bitWidth == 64: direct copy + MOVD $0, R9 // R9 = index + MOVD $0, R10 // R10 = byte offset + +copy_loop: + CMP R2, R9 + BEQ done + + // Load src[i] + LSL $3, R9, R16 + MOVD (R1)(R16), R11 + + // Store to dst[i*8] + MOVD R11, (R0)(R10) + + // i++, offset += 8 + ADD $1, R9, R9 + ADD $8, R10, R10 + + B copy_loop + +done: + RET + +// func packInt64NEON(dst []byte, src []int64, bitWidth uint) +TEXT ·packInt64NEON(SB), NOSPLIT, $0-56 + MOVD dst_base+0(FP), R0 // R0 = dst pointer + MOVD src_base+24(FP), R1 // R1 = src pointer + MOVD src_len+32(FP), R2 // R2 = src length + MOVD bitWidth+48(FP), R3 // R3 = bitWidth + + // Handle bitWidth == 0 + CBZ R3, neon_done + + // Initialize processed count to 0 + MOVD $0, R5 + + // Check if we have at least 4 values to process with NEON paths + CMP $4, R2 + BLT neon_done // Not enough values, return and let Go wrapper handle it + + // Determine which NEON path to use based on bitWidth + CMP $1, R3 + BEQ neon_1bit + CMP $2, R3 + BEQ neon_2bit + CMP $3, R3 + BEQ neon_3bit + CMP $4, R3 + BEQ neon_4bit + CMP $5, R3 + BEQ neon_5bit + CMP $6, R3 + BEQ neon_6bit + CMP $7, R3 + BEQ neon_7bit + CMP $8, R3 + BEQ neon_8bit + + // For other bit widths, return without processing + // The Go wrapper will call the scalar version + RET + +neon_1bit: + // BitWidth 1: Pack 8 int64 values into 1 byte + MOVD R2, R4 + LSR $3, R4, R4 // R4 = len / 8 + LSL $3, R4, R4 // R4 = aligned length + MOVD $0, R5 // R5 = index + CMP $0, R4 + BEQ neon_done + +neon_1bit_loop: + MOVD (R1), R6 + AND $1, R6, R6 + MOVD 8(R1), R7 + AND $1, R7, R7 + ORR R7<<1, R6, R6 + MOVD 16(R1), R7 + AND $1, R7, R7 + ORR R7<<2, R6, R6 + MOVD 24(R1), R7 + AND $1, R7, R7 + ORR R7<<3, R6, R6 + MOVD 32(R1), R7 + AND $1, R7, R7 + ORR R7<<4, R6, R6 + MOVD 40(R1), R7 + AND $1, R7, R7 + ORR R7<<5, R6, R6 + MOVD 48(R1), R7 + AND $1, R7, R7 + ORR R7<<6, R6, R6 + MOVD 56(R1), R7 + AND $1, R7, R7 + ORR R7<<7, R6, R6 + MOVB R6, (R0) + ADD $64, R1, R1 + ADD $1, R0, R0 + ADD $8, R5, R5 + CMP R4, R5 + BLT neon_1bit_loop + B neon_done + +neon_2bit: + MOVD R2, R4 + LSR $2, R4, R4 + LSL $2, R4, R4 + MOVD $0, R5 + CMP $0, R4 + BEQ neon_done + +neon_2bit_loop: + MOVD (R1), R6 + AND $3, R6, R6 + MOVD 8(R1), R7 + AND $3, R7, R7 + ORR R7<<2, R6, R6 + MOVD 16(R1), R7 + AND $3, R7, R7 + ORR R7<<4, R6, R6 + MOVD 24(R1), R7 + AND $3, R7, R7 + ORR R7<<6, R6, R6 + MOVB R6, (R0) + ADD $32, R1, R1 + ADD $1, R0, R0 + ADD $4, R5, R5 + CMP R4, R5 + BLT neon_2bit_loop + B neon_done + +neon_3bit: + MOVD R2, R4 + LSR $3, R4, R4 + LSL $3, R4, R4 + MOVD $0, R5 + CMP $0, R4 + BEQ neon_done + +neon_3bit_loop: + MOVD (R1), R6 + AND $7, R6, R6 + MOVD 8(R1), R7 + AND $7, R7, R7 + ORR R7<<3, R6, R6 + MOVD 16(R1), R7 + AND $7, R7, R7 + ORR R7<<6, R6, R6 + MOVD 24(R1), R7 + AND $7, R7, R7 + ORR R7<<9, R6, R6 + MOVD 32(R1), R7 + AND $7, R7, R7 + ORR R7<<12, R6, R6 + MOVD 40(R1), R7 + AND $7, R7, R7 + ORR R7<<15, R6, R6 + MOVD 48(R1), R7 + AND $7, R7, R7 + ORR R7<<18, R6, R6 + MOVD 56(R1), R7 + AND $7, R7, R7 + ORR R7<<21, R6, R6 + MOVB R6, (R0) + LSR $8, R6, R7 + MOVB R7, 1(R0) + LSR $16, R6, R7 + MOVB R7, 2(R0) + ADD $64, R1, R1 + ADD $3, R0, R0 + ADD $8, R5, R5 + CMP R4, R5 + BLT neon_3bit_loop + B neon_done + +neon_4bit: + MOVD R2, R4 + LSR $2, R4, R4 + LSL $2, R4, R4 + MOVD $0, R5 + CMP $0, R4 + BEQ neon_done + +neon_4bit_loop: + MOVD (R1), R6 + AND $15, R6, R6 + MOVD 8(R1), R7 + AND $15, R7, R7 + ORR R7<<4, R6, R6 + MOVD 16(R1), R7 + AND $15, R7, R7 + ORR R7<<8, R6, R6 + MOVD 24(R1), R7 + AND $15, R7, R7 + ORR R7<<12, R6, R6 + MOVH R6, (R0) + ADD $32, R1, R1 + ADD $2, R0, R0 + ADD $4, R5, R5 + CMP R4, R5 + BLT neon_4bit_loop + B neon_done + +neon_5bit: + MOVD R2, R4 + LSR $3, R4, R4 + LSL $3, R4, R4 + MOVD $0, R5 + CMP $0, R4 + BEQ neon_done + +neon_5bit_loop: + MOVD $0, R6 + MOVD (R1), R7 + AND $31, R7, R7 + ORR R7, R6, R6 + MOVD 8(R1), R7 + AND $31, R7, R7 + ORR R7<<5, R6, R6 + MOVD 16(R1), R7 + AND $31, R7, R7 + ORR R7<<10, R6, R6 + MOVD 24(R1), R7 + AND $31, R7, R7 + ORR R7<<15, R6, R6 + MOVD 32(R1), R7 + AND $31, R7, R7 + ORR R7<<20, R6, R6 + MOVD 40(R1), R7 + AND $31, R7, R7 + ORR R7<<25, R6, R6 + MOVD 48(R1), R7 + AND $31, R7, R7 + ORR R7<<30, R6, R6 + MOVD 56(R1), R7 + AND $31, R7, R7 + ORR R7<<35, R6, R6 + MOVB R6, (R0) + LSR $8, R6, R7 + MOVB R7, 1(R0) + LSR $16, R6, R7 + MOVB R7, 2(R0) + LSR $24, R6, R7 + MOVB R7, 3(R0) + LSR $32, R6, R7 + MOVB R7, 4(R0) + ADD $64, R1, R1 + ADD $5, R0, R0 + ADD $8, R5, R5 + CMP R4, R5 + BLT neon_5bit_loop + B neon_done + +neon_6bit: + MOVD R2, R4 + LSR $2, R4, R4 + LSL $2, R4, R4 + MOVD $0, R5 + CMP $0, R4 + BEQ neon_done + +neon_6bit_loop: + MOVD (R1), R6 + AND $63, R6, R6 + MOVD 8(R1), R7 + AND $63, R7, R7 + ORR R7<<6, R6, R6 + MOVD 16(R1), R7 + AND $63, R7, R7 + ORR R7<<12, R6, R6 + MOVD 24(R1), R7 + AND $63, R7, R7 + ORR R7<<18, R6, R6 + MOVB R6, (R0) + LSR $8, R6, R7 + MOVB R7, 1(R0) + LSR $16, R6, R7 + MOVB R7, 2(R0) + ADD $32, R1, R1 + ADD $3, R0, R0 + ADD $4, R5, R5 + CMP R4, R5 + BLT neon_6bit_loop + B neon_done + +neon_7bit: + MOVD R2, R4 + LSR $3, R4, R4 + LSL $3, R4, R4 + MOVD $0, R5 + CMP $0, R4 + BEQ neon_done + +neon_7bit_loop: + MOVD $0, R6 + MOVD (R1), R7 + AND $127, R7, R7 + ORR R7, R6, R6 + MOVD 8(R1), R7 + AND $127, R7, R7 + ORR R7<<7, R6, R6 + MOVD 16(R1), R7 + AND $127, R7, R7 + ORR R7<<14, R6, R6 + MOVD 24(R1), R7 + AND $127, R7, R7 + ORR R7<<21, R6, R6 + MOVD 32(R1), R7 + AND $127, R7, R7 + ORR R7<<28, R6, R6 + MOVD 40(R1), R7 + AND $127, R7, R7 + ORR R7<<35, R6, R6 + MOVD 48(R1), R7 + AND $127, R7, R7 + ORR R7<<42, R6, R6 + MOVD 56(R1), R7 + AND $127, R7, R7 + ORR R7<<49, R6, R6 + MOVB R6, (R0) + LSR $8, R6, R7 + MOVB R7, 1(R0) + LSR $16, R6, R7 + MOVB R7, 2(R0) + LSR $24, R6, R7 + MOVB R7, 3(R0) + LSR $32, R6, R7 + MOVB R7, 4(R0) + LSR $40, R6, R7 + MOVB R7, 5(R0) + LSR $48, R6, R7 + MOVB R7, 6(R0) + ADD $64, R1, R1 + ADD $7, R0, R0 + ADD $8, R5, R5 + CMP R4, R5 + BLT neon_7bit_loop + B neon_done + +neon_8bit: + MOVD R2, R4 + LSR $2, R4, R4 + LSL $2, R4, R4 + MOVD $0, R5 + CMP $0, R4 + BEQ neon_done + +neon_8bit_loop: + MOVD (R1), R6 + MOVB R6, (R0) + MOVD 8(R1), R6 + MOVB R6, 1(R0) + MOVD 16(R1), R6 + MOVB R6, 2(R0) + MOVD 24(R1), R6 + MOVB R6, 3(R0) + ADD $32, R1, R1 + ADD $4, R0, R0 + ADD $4, R5, R5 + CMP R4, R5 + BLT neon_8bit_loop + +neon_done: + // After NEON processing, handle any remainder with scalar code + // Check if there are remaining values to process + CMP R2, R5 // R5 = processed count, R2 = total length + BGE neon_ret // If processed >= total, we're done + + // Calculate remainder: adjust src/dst pointers and length + // Advance src pointer by (R5 * 8) bytes + LSL $3, R5, R16 + ADD R16, R1, R1 + + // Calculate packed bytes for processed values and advance dst + MUL R3, R5, R16 // R16 = processed * bitWidth (in bits) + LSR $3, R16, R16 // R16 = packed bytes + ADD R16, R0, R0 + + // Update remaining length + SUB R5, R2, R2 + + // Jump to scalar implementation for remainder + B ·packInt64ARM64(SB) + +neon_ret: + RET diff --git a/vendor/github.com/parquet-go/bitpack/pack_purego.go b/vendor/github.com/parquet-go/bitpack/pack_purego.go new file mode 100644 index 000000000..8d02d75ff --- /dev/null +++ b/vendor/github.com/parquet-go/bitpack/pack_purego.go @@ -0,0 +1,94 @@ +//go:build purego || !arm64 + +package bitpack + +import "encoding/binary" + +func packInt32(dst []byte, src []int32, bitWidth uint) { + if bitWidth == 0 { + return + } + + bitMask := uint32(1<= 32 { + binary.LittleEndian.PutUint32(dst[byteIndex:], uint32(buffer)) + buffer >>= 32 + bufferedBits -= 32 + byteIndex += 4 + } + } + + // Flush remaining bits + if bufferedBits > 0 { + // Only write the bytes we need + remainingBytes := (bufferedBits + 7) / 8 + for i := uint(0); i < remainingBytes; i++ { + dst[byteIndex] = byte(buffer) + buffer >>= 8 + byteIndex++ + } + } +} + +func packInt64(dst []byte, src []int64, bitWidth uint) { + if bitWidth == 0 { + return + } + if bitWidth == 64 { + // Special case: no packing needed, direct copy + for i, v := range src { + binary.LittleEndian.PutUint64(dst[i*8:], uint64(v)) + } + return + } + + bitMask := uint64(1<> bitsInLo + bufferedBits += bitWidth + } + + // Flush complete 64-bit words + for bufferedBits >= 64 { + binary.LittleEndian.PutUint64(dst[byteIndex:], bufferLo) + bufferLo = bufferHi + bufferHi = 0 + bufferedBits -= 64 + byteIndex += 8 + } + } + + // Flush remaining bits + if bufferedBits > 0 { + remainingBytes := (bufferedBits + 7) / 8 + for i := uint(0); i < remainingBytes; i++ { + dst[byteIndex] = byte(bufferLo) + bufferLo >>= 8 + byteIndex++ + } + } +} diff --git a/vendor/github.com/parquet-go/bitpack/unpack.go b/vendor/github.com/parquet-go/bitpack/unpack.go new file mode 100644 index 000000000..2396e0dd9 --- /dev/null +++ b/vendor/github.com/parquet-go/bitpack/unpack.go @@ -0,0 +1,29 @@ +package bitpack + +import ( + "unsafe" + + "github.com/parquet-go/bitpack/unsafecast" +) + +// PaddingInt32 is the padding expected to exist after the end of input buffers +// for the UnpackInt32 algorithm to avoid reading beyond the end of the input. +const PaddingInt32 = 16 + +// PaddingInt64 is the padding expected to exist after the end of input buffers +// for the UnpackInt32 algorithm to avoid reading beyond the end of the input. +const PaddingInt64 = 32 + +// Unpack unpacks values from src to dst, each value is unpacked from the given +// bit width regardless of how many bits are needed to represent it. +func Unpack[T Int](dst []T, src []byte, bitWidth uint) { + sizeofT := uint(unsafe.Sizeof(T(0))) + padding := (8 * sizeofT) / 2 // 32 bits => 16, 64 bits => 32 + _ = src[:ByteCount(bitWidth*uint(len(dst))+8*padding)] + switch sizeofT { + case 4: + unpackInt32(unsafecast.Slice[int32](dst), src, bitWidth) + default: + unpackInt64(unsafecast.Slice[int64](dst), src, bitWidth) + } +} diff --git a/vendor/github.com/parquet-go/bitpack/unpack_int32_1bit_arm64.s b/vendor/github.com/parquet-go/bitpack/unpack_int32_1bit_arm64.s new file mode 100644 index 000000000..6f6cb2837 --- /dev/null +++ b/vendor/github.com/parquet-go/bitpack/unpack_int32_1bit_arm64.s @@ -0,0 +1,184 @@ +//go:build !purego + +#include "textflag.h" +#include "unpack_neon_macros_arm64.h" + +// unpackInt32x1bitNEON implements NEON unpacking for bitWidth=1 using direct bit manipulation +// Each byte contains 8 bits: [bit7][bit6][bit5][bit4][bit3][bit2][bit1][bit0] +// +// func unpackInt32x1bitNEON(dst []int32, src []byte, bitWidth uint) +TEXT ·unpackInt32x1bitNEON(SB), NOSPLIT, $0-56 + MOVD dst_base+0(FP), R0 // R0 = dst pointer + MOVD dst_len+8(FP), R1 // R1 = dst length + MOVD src_base+24(FP), R2 // R2 = src pointer + MOVD bitWidth+48(FP), R3 // R3 = bitWidth (should be 1) + + MOVD $0, R5 // R5 = index (initialize early for tail path) + + // Check if we have at least 64 values to process + CMP $64, R1 + BLT neon1_tail + + // Round down to multiple of 64 for NEON processing + MOVD R1, R4 + LSR $6, R4, R4 // R4 = len / 64 + LSL $6, R4, R4 // R4 = aligned length (multiple of 64) + + // Load mask for 1 bit (0x01010101...) + MOVD $0x0101010101010101, R6 + VMOV R6, V31.D[0] + VMOV R6, V31.D[1] // V31 = mask for single bits + +neon1_loop: + // Load 8 bytes (contains 64 x 1-bit values) + VLD1 (R2), [V0.B8] + + // Extract each bit position (8 separate streams) + VAND V31.B16, V0.B16, V1.B16 // V1 = bit 0 + + VUSHR $1, V0.B16, V2.B16 + VAND V31.B16, V2.B16, V2.B16 // V2 = bit 1 + + VUSHR $2, V0.B16, V3.B16 + VAND V31.B16, V3.B16, V3.B16 // V3 = bit 2 + + VUSHR $3, V0.B16, V4.B16 + VAND V31.B16, V4.B16, V4.B16 // V4 = bit 3 + + VUSHR $4, V0.B16, V5.B16 + VAND V31.B16, V5.B16, V5.B16 // V5 = bit 4 + + VUSHR $5, V0.B16, V6.B16 + VAND V31.B16, V6.B16, V6.B16 // V6 = bit 5 + + VUSHR $6, V0.B16, V7.B16 + VAND V31.B16, V7.B16, V7.B16 // V7 = bit 6 + + VUSHR $7, V0.B16, V8.B16 + VAND V31.B16, V8.B16, V8.B16 // V8 = bit 7 + + // Stage 1: ZIP pairs (8 streams → 4 streams of pairs) + VZIP1 V2.B8, V1.B8, V9.B8 // V9 = [bit0,bit1] interleaved + VZIP1 V4.B8, V3.B8, V10.B8 // V10 = [bit2,bit3] interleaved + VZIP1 V6.B8, V5.B8, V11.B8 // V11 = [bit4,bit5] interleaved + VZIP1 V8.B8, V7.B8, V12.B8 // V12 = [bit6,bit7] interleaved + + VZIP2 V2.B8, V1.B8, V13.B8 // V13 = [bit0,bit1] upper half + VZIP2 V4.B8, V3.B8, V14.B8 // V14 = [bit2,bit3] upper half + VZIP2 V6.B8, V5.B8, V15.B8 // V15 = [bit4,bit5] upper half + VZIP2 V8.B8, V7.B8, V16.B8 // V16 = [bit6,bit7] upper half + + // Stage 2: ZIP quads (4 streams → 2 streams of quads) + VZIP1 V10.H4, V9.H4, V17.H4 // V17 = [0,1,2,3] interleaved + VZIP1 V12.H4, V11.H4, V18.H4 // V18 = [4,5,6,7] interleaved + VZIP2 V10.H4, V9.H4, V19.H4 // V19 = [0,1,2,3] next + VZIP2 V12.H4, V11.H4, V20.H4 // V20 = [4,5,6,7] next + + VZIP1 V14.H4, V13.H4, V21.H4 // V21 = upper [0,1,2,3] + VZIP1 V16.H4, V15.H4, V22.H4 // V22 = upper [4,5,6,7] + VZIP2 V14.H4, V13.H4, V23.H4 // V23 = upper [0,1,2,3] next + VZIP2 V16.H4, V15.H4, V24.H4 // V24 = upper [4,5,6,7] next + + // Stage 3: ZIP octets (2 streams → fully sequential) + VZIP1 V18.S2, V17.S2, V25.S2 // V25 = values 0-7 + VZIP2 V18.S2, V17.S2, V26.S2 // V26 = values 8-15 + VZIP1 V20.S2, V19.S2, V27.S2 // V27 = values 16-23 + VZIP2 V20.S2, V19.S2, V28.S2 // V28 = values 24-31 + VZIP1 V22.S2, V21.S2, V1.S2 // V1 = values 32-39 + VZIP2 V22.S2, V21.S2, V2.S2 // V2 = values 40-47 + VZIP1 V24.S2, V23.S2, V3.S2 // V3 = values 48-55 + VZIP2 V24.S2, V23.S2, V4.S2 // V4 = values 56-63 + + // Widen to int32 and store - Process first 32 values + USHLL_8H_8B(5, 25) + USHLL_4S_4H(6, 5) + USHLL2_4S_8H(7, 5) + VST1 [V6.S4, V7.S4], (R0) + ADD $32, R0, R0 + + USHLL_8H_8B(5, 26) + USHLL_4S_4H(6, 5) + USHLL2_4S_8H(7, 5) + VST1 [V6.S4, V7.S4], (R0) + ADD $32, R0, R0 + + USHLL_8H_8B(5, 27) + USHLL_4S_4H(6, 5) + USHLL2_4S_8H(7, 5) + VST1 [V6.S4, V7.S4], (R0) + ADD $32, R0, R0 + + USHLL_8H_8B(5, 28) + USHLL_4S_4H(6, 5) + USHLL2_4S_8H(7, 5) + VST1 [V6.S4, V7.S4], (R0) + ADD $32, R0, R0 + + // Process second 32 values + USHLL_8H_8B(5, 1) + USHLL_4S_4H(6, 5) + USHLL2_4S_8H(7, 5) + VST1 [V6.S4, V7.S4], (R0) + ADD $32, R0, R0 + + USHLL_8H_8B(5, 2) + USHLL_4S_4H(6, 5) + USHLL2_4S_8H(7, 5) + VST1 [V6.S4, V7.S4], (R0) + ADD $32, R0, R0 + + USHLL_8H_8B(5, 3) + USHLL_4S_4H(6, 5) + USHLL2_4S_8H(7, 5) + VST1 [V6.S4, V7.S4], (R0) + ADD $32, R0, R0 + + USHLL_8H_8B(5, 4) + USHLL_4S_4H(6, 5) + USHLL2_4S_8H(7, 5) + VST1 [V6.S4, V7.S4], (R0) + ADD $32, R0, R0 + + // Advance pointers + ADD $8, R2, R2 // src += 8 bytes + ADD $64, R5, R5 // index += 64 + + CMP R4, R5 + BLT neon1_loop + +neon1_tail: + // Handle remaining elements with scalar fallback + CMP R1, R5 + BEQ neon1_done + + // Compute remaining elements + SUB R5, R1, R1 + + // Fall back to scalar unpack for tail + MOVD $1, R4 // bitMask = 1 + MOVD $0, R6 // bitOffset = 0 + MOVD $0, R7 // index = 0 + B neon1_scalar_test + +neon1_scalar_loop: + MOVD R6, R8 + LSR $3, R8, R8 // byte_index = bitOffset / 8 + MOVBU (R2)(R8), R9 // Load byte + + MOVD R6, R10 + AND $7, R10, R10 // bit_offset = bitOffset % 8 + + LSR R10, R9, R9 // Shift right by bit offset + AND $1, R9, R9 // Mask to get bit + MOVW R9, (R0) // Store as int32 + + ADD $4, R0, R0 // dst++ + ADD $1, R6, R6 // bitOffset++ + ADD $1, R7, R7 // index++ + +neon1_scalar_test: + CMP R1, R7 + BLT neon1_scalar_loop + +neon1_done: + RET diff --git a/vendor/github.com/parquet-go/bitpack/unpack_int32_2bit_arm64.s b/vendor/github.com/parquet-go/bitpack/unpack_int32_2bit_arm64.s new file mode 100644 index 000000000..7acea640e --- /dev/null +++ b/vendor/github.com/parquet-go/bitpack/unpack_int32_2bit_arm64.s @@ -0,0 +1,136 @@ +//go:build !purego + +#include "textflag.h" +#include "unpack_neon_macros_arm64.h" + +// unpackInt32x2bitNEON implements NEON unpacking for bitWidth=2 using direct bit manipulation +// Each byte contains 4 values of 2 bits each: [bits 6-7][bits 4-5][bits 2-3][bits 0-1] +// +// func unpackInt32x2bitNEON(dst []int32, src []byte, bitWidth uint) +TEXT ·unpackInt32x2bitNEON(SB), NOSPLIT, $0-56 + MOVD dst_base+0(FP), R0 // R0 = dst pointer + MOVD dst_len+8(FP), R1 // R1 = dst length + MOVD src_base+24(FP), R2 // R2 = src pointer + MOVD bitWidth+48(FP), R3 // R3 = bitWidth (should be 2) + + MOVD $0, R5 // R5 = index (initialize early for tail path) + + // Check if we have at least 32 values to process + CMP $32, R1 + BLT neon2_tail + + // Round down to multiple of 32 for NEON processing + MOVD R1, R4 + LSR $5, R4, R4 // R4 = len / 32 + LSL $5, R4, R4 // R4 = aligned length (multiple of 32) + + // Load mask for 2 bits (0x03030303...) + MOVD $0x0303030303030303, R6 + VMOV R6, V31.D[0] + VMOV R6, V31.D[1] // V31 = mask for 2-bit values + +neon2_loop: + // Load 8 bytes (contains 32 x 2-bit values) + VLD1 (R2), [V0.B8] + + // Extract bits [1:0] from each byte (values at positions 0,4,8,12,...) + VAND V31.B16, V0.B16, V1.B16 + + // Extract bits [3:2] from each byte (values at positions 1,5,9,13,...) + VUSHR $2, V0.B16, V2.B16 + VAND V31.B16, V2.B16, V2.B16 + + // Extract bits [5:4] from each byte (values at positions 2,6,10,14,...) + VUSHR $4, V0.B16, V3.B16 + VAND V31.B16, V3.B16, V3.B16 + + // Extract bits [7:6] from each byte (values at positions 3,7,11,15,...) + VUSHR $6, V0.B16, V4.B16 + VAND V31.B16, V4.B16, V4.B16 + + // Interleave using two stages of ZIP operations + // Stage 1: ZIP pairs at byte level + VZIP1 V2.B8, V1.B8, V5.B8 // V5 = [V1[0],V2[0],V1[1],V2[1],V1[2],V2[2],V1[3],V2[3]] + VZIP1 V4.B8, V3.B8, V6.B8 // V6 = [V3[0],V4[0],V3[1],V4[1],V3[2],V4[2],V3[3],V4[3]] + VZIP2 V2.B8, V1.B8, V7.B8 // V7 = [V1[4],V2[4],V1[5],V2[5],V1[6],V2[6],V1[7],V2[7]] + VZIP2 V4.B8, V3.B8, V8.B8 // V8 = [V3[4],V4[4],V3[5],V4[5],V3[6],V4[6],V3[7],V4[7]] + + // Stage 2: ZIP quads at 16-bit level to get final sequential order + VZIP1 V6.H4, V5.H4, V13.H4 // V13 = [V1[0],V2[0],V3[0],V4[0],V1[1],V2[1],V3[1],V4[1]] = values 0-7 + VZIP2 V6.H4, V5.H4, V14.H4 // V14 = [V1[2],V2[2],V3[2],V4[2],V1[3],V2[3],V3[3],V4[3]] = values 8-15 + VZIP1 V8.H4, V7.H4, V15.H4 // V15 = [V1[4],V2[4],V3[4],V4[4],V1[5],V2[5],V3[5],V4[5]] = values 16-23 + VZIP2 V8.H4, V7.H4, V16.H4 // V16 = [V1[6],V2[6],V3[6],V4[6],V1[7],V2[7],V3[7],V4[7]] = values 24-31 + + // Widen first 8 values (V13) to int32 + USHLL_8H_8B(17, 13) // V17.8H ← V13.8B + USHLL_4S_4H(18, 17) // V18.4S ← V17.4H (values 0-3) + USHLL2_4S_8H(19, 17) // V19.4S ← V17.8H (values 4-7) + + // Widen second 8 values (V14) to int32 + USHLL_8H_8B(20, 14) // V20.8H ← V14.8B + USHLL_4S_4H(21, 20) // V21.4S ← V20.4H (values 8-11) + USHLL2_4S_8H(22, 20) // V22.4S ← V20.8H (values 12-15) + + // Widen third 8 values (V15) to int32 + USHLL_8H_8B(23, 15) // V23.8H ← V15.8B + USHLL_4S_4H(24, 23) // V24.4S ← V23.4H (values 16-19) + USHLL2_4S_8H(25, 23) // V25.4S ← V23.8H (values 20-23) + + // Widen fourth 8 values (V16) to int32 + USHLL_8H_8B(26, 16) // V26.8H ← V16.8B + USHLL_4S_4H(27, 26) // V27.4S ← V26.4H (values 24-27) + USHLL2_4S_8H(28, 26) // V28.4S ← V26.8H (values 28-31) + + // Store 32 int32 values (128 bytes) + VST1 [V18.S4, V19.S4], (R0) + ADD $32, R0, R0 + VST1 [V21.S4, V22.S4], (R0) + ADD $32, R0, R0 + VST1 [V24.S4, V25.S4], (R0) + ADD $32, R0, R0 + VST1 [V27.S4, V28.S4], (R0) + ADD $32, R0, R0 + + // Advance pointers + ADD $8, R2, R2 // src += 8 bytes + ADD $32, R5, R5 // index += 32 + + CMP R4, R5 + BLT neon2_loop + +neon2_tail: + // Handle remaining elements with scalar fallback + CMP R1, R5 + BEQ neon2_done + + // Compute remaining elements + SUB R5, R1, R1 + + // Fall back to scalar unpack for tail + MOVD $3, R4 // bitMask = 3 (0b11 for 2 bits) + MOVD $0, R6 // bitOffset = 0 + MOVD $0, R7 // index = 0 + B neon2_scalar_test + +neon2_scalar_loop: + MOVD R6, R8 + LSR $3, R8, R8 // byte_index = bitOffset / 8 + MOVBU (R2)(R8), R9 // Load byte + + MOVD R6, R10 + AND $7, R10, R10 // bit_offset = bitOffset % 8 + + LSR R10, R9, R9 // Shift right by bit offset + AND $3, R9, R9 // Mask to get 2 bits + MOVW R9, (R0) // Store as int32 + + ADD $4, R0, R0 // dst++ + ADD $2, R6, R6 // bitOffset += 2 + ADD $1, R7, R7 // index++ + +neon2_scalar_test: + CMP R1, R7 + BLT neon2_scalar_loop + +neon2_done: + RET diff --git a/vendor/github.com/parquet-go/bitpack/unpack_int32_4bit_arm64.s b/vendor/github.com/parquet-go/bitpack/unpack_int32_4bit_arm64.s new file mode 100644 index 000000000..05360b4a4 --- /dev/null +++ b/vendor/github.com/parquet-go/bitpack/unpack_int32_4bit_arm64.s @@ -0,0 +1,106 @@ +//go:build !purego + +#include "textflag.h" +#include "unpack_neon_macros_arm64.h" + +// unpackInt32x4bitNEON implements NEON unpacking for bitWidth=4 using direct bit manipulation +// Each byte contains 2 values of 4 bits each +// +// func unpackInt32x4bitNEON(dst []int32, src []byte, bitWidth uint) +TEXT ·unpackInt32x4bitNEON(SB), NOSPLIT, $0-56 + MOVD dst_base+0(FP), R0 // R0 = dst pointer + MOVD dst_len+8(FP), R1 // R1 = dst length + MOVD src_base+24(FP), R2 // R2 = src pointer + MOVD bitWidth+48(FP), R3 // R3 = bitWidth (should be 4) + + MOVD $0, R5 // R5 = index (initialize early for tail path) + + // Check if we have at least 16 values to process + CMP $16, R1 + BLT neon4_tail + + // Round down to multiple of 16 for NEON processing + MOVD R1, R4 + LSR $4, R4, R4 // R4 = len / 16 + LSL $4, R4, R4 // R4 = aligned length (multiple of 16) + + // Load mask for 4 bits (0x0F0F0F0F...) + MOVD $0x0F0F0F0F0F0F0F0F, R6 + VMOV R6, V31.D[0] + VMOV R6, V31.D[1] // V31 = mask for low nibbles + +neon4_loop: + // Load 8 bytes (contains 16 x 4-bit values) + VLD1 (R2), [V0.B8] + + // Extract low nibbles (values at even nibble positions) + VAND V31.B16, V0.B16, V1.B16 // V1 = low nibbles + + // Extract high nibbles (values at odd nibble positions) + VUSHR $4, V0.B16, V2.B16 // V2 = high nibbles (shifted down) + VAND V31.B16, V2.B16, V2.B16 // V2 = high nibbles (masked) + + // Now V1 has values [0,2,4,6,8,10,12,14] and V2 has [1,3,5,7,9,11,13,15] + // We need to interleave them: [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15] + VZIP1 V2.B8, V1.B8, V3.B8 // V3 = interleaved low half + VZIP2 V2.B8, V1.B8, V4.B8 // V4 = interleaved high half + + // Widen first 8 values (V3) to int32 + USHLL_8H_8B(5, 3) // V5.8H ← V3.8B + USHLL_4S_4H(6, 5) // V6.4S ← V5.4H (values 0-3) + USHLL2_4S_8H(7, 5) // V7.4S ← V5.8H (values 4-7) + + // Widen second 8 values (V4) to int32 + USHLL_8H_8B(8, 4) // V8.8H ← V4.8B + USHLL_4S_4H(9, 8) // V9.4S ← V8.4H (values 8-11) + USHLL2_4S_8H(10, 8) // V10.4S ← V8.8H (values 12-15) + + // Store 16 int32 values (64 bytes) + VST1 [V6.S4, V7.S4], (R0) + ADD $32, R0, R0 + VST1 [V9.S4, V10.S4], (R0) + ADD $32, R0, R0 + + // Advance pointers + ADD $8, R2, R2 // src += 8 bytes + ADD $16, R5, R5 // index += 16 + + CMP R4, R5 + BLT neon4_loop + +neon4_tail: + // Handle remaining elements with scalar fallback + CMP R1, R5 + BEQ neon4_done + + // Compute remaining elements + SUB R5, R1, R1 + + // Fall back to scalar unpack for tail + MOVD $0x0F, R4 // bitMask = 0x0F (4 bits) + MOVD $0, R6 // bitOffset = 0 (start from current R2 position) + MOVD $0, R7 // loop counter = 0 + B neon4_scalar_test + +neon4_scalar_loop: + MOVD R6, R8 + LSR $3, R8, R8 // byte_index = bitOffset / 8 + MOVBU (R2)(R8), R9 // Load byte from current position + + MOVD R6, R10 + AND $7, R10, R10 // bit_offset = bitOffset % 8 + + LSR R10, R9, R9 // Shift right by bit offset + AND $0x0F, R9, R9 // Mask to get 4 bits + MOVW R9, (R0) // Store as int32 + + ADD $4, R0, R0 // dst++ + ADD $4, R6, R6 // bitOffset += 4 + ADD $1, R7, R7 // counter++ + +neon4_scalar_test: + CMP R1, R7 + BLT neon4_scalar_loop + +neon4_done: + RET diff --git a/vendor/github.com/parquet-go/bitpack/unpack_int32_8bit_arm64.s b/vendor/github.com/parquet-go/bitpack/unpack_int32_8bit_arm64.s new file mode 100644 index 000000000..40d5bf088 --- /dev/null +++ b/vendor/github.com/parquet-go/bitpack/unpack_int32_8bit_arm64.s @@ -0,0 +1,65 @@ +//go:build !purego + +#include "textflag.h" +#include "unpack_neon_macros_arm64.h" + +// unpackInt32x8bitNEON implements NEON unpacking for bitWidth=8 +// Each byte is already a complete value - just widen to int32 +// Processes 8 values at a time using NEON +// +// func unpackInt32x8bitNEON(dst []int32, src []byte, bitWidth uint) +TEXT ·unpackInt32x8bitNEON(SB), NOSPLIT, $0-56 + MOVD dst_base+0(FP), R0 // R0 = dst pointer + MOVD dst_len+8(FP), R1 // R1 = dst length + MOVD src_base+24(FP), R2 // R2 = src pointer + MOVD bitWidth+48(FP), R3 // R3 = bitWidth (should be 8) + + MOVD $0, R5 // R5 = index + + // Check if we have at least 8 values to process + CMP $8, R1 + BLT tbl8_tail + + // Round down to multiple of 8 for NEON processing + MOVD R1, R4 + LSR $3, R4, R4 // R4 = len / 8 + LSL $3, R4, R4 // R4 = aligned length (multiple of 8) + +tbl8_loop: + // Load 8 bytes (8 x 8-bit values) + VLD1 (R2), [V0.B8] + + // Widen to int32: byte → short → int + USHLL_8H_8B(1, 0) // V1.8H ← V0.8B (8x8-bit → 8x16-bit) + USHLL_4S_4H(2, 1) // V2.4S ← V1.4H (lower 4x16-bit → 4x32-bit) + USHLL2_4S_8H(3, 1) // V3.4S ← V1.8H (upper 4x16-bit → 4x32-bit) + + // Store 8 int32 values + VST1 [V2.S4, V3.S4], (R0) + + // Advance pointers + ADD $8, R2, R2 // src += 8 bytes + ADD $32, R0, R0 // dst += 8 int32 (32 bytes) + ADD $8, R5, R5 // index += 8 + + CMP R4, R5 + BLT tbl8_loop + +tbl8_tail: + // Handle remaining elements (0-7) one by one + CMP R1, R5 + BGE tbl8_done + +tbl8_tail_loop: + MOVBU (R2), R6 // Load byte + MOVW R6, (R0) // Store as int32 (zero-extended) + + ADD $1, R2, R2 // src++ + ADD $4, R0, R0 // dst++ + ADD $1, R5, R5 // index++ + + CMP R1, R5 + BLT tbl8_tail_loop + +tbl8_done: + RET diff --git a/vendor/github.com/parquet-go/bitpack/unpack_int32_amd64.go b/vendor/github.com/parquet-go/bitpack/unpack_int32_amd64.go new file mode 100644 index 000000000..8783f3f82 --- /dev/null +++ b/vendor/github.com/parquet-go/bitpack/unpack_int32_amd64.go @@ -0,0 +1,36 @@ +//go:build !purego + +package bitpack + +import ( + "github.com/parquet-go/bitpack/unsafecast" + "golang.org/x/sys/cpu" +) + +//go:noescape +func unpackInt32Default(dst []int32, src []byte, bitWidth uint) + +//go:noescape +func unpackInt32x1to16bitsAVX2(dst []int32, src []byte, bitWidth uint) + +//go:noescape +func unpackInt32x17to26bitsAVX2(dst []int32, src []byte, bitWidth uint) + +//go:noescape +func unpackInt32x27to31bitsAVX2(dst []int32, src []byte, bitWidth uint) + +func unpackInt32(dst []int32, src []byte, bitWidth uint) { + hasAVX2 := cpu.X86.HasAVX2 + switch { + case hasAVX2 && bitWidth <= 16: + unpackInt32x1to16bitsAVX2(dst, src, bitWidth) + case hasAVX2 && bitWidth <= 26: + unpackInt32x17to26bitsAVX2(dst, src, bitWidth) + case hasAVX2 && bitWidth <= 31: + unpackInt32x27to31bitsAVX2(dst, src, bitWidth) + case bitWidth == 32: + copy(dst, unsafecast.Slice[int32](src)) + default: + unpackInt32Default(dst, src, bitWidth) + } +} diff --git a/vendor/github.com/parquet-go/bitpack/unpack_int32_amd64.s b/vendor/github.com/parquet-go/bitpack/unpack_int32_amd64.s new file mode 100644 index 000000000..e0f2bf4a4 --- /dev/null +++ b/vendor/github.com/parquet-go/bitpack/unpack_int32_amd64.s @@ -0,0 +1,352 @@ +//go:build !purego + +#include "funcdata.h" +#include "textflag.h" + +// func unpackInt32Default(dst []int32, src []byte, bitWidth uint) +TEXT ·unpackInt32Default(SB), NOSPLIT, $0-56 + MOVQ dst_base+0(FP), AX + MOVQ dst_len+8(FP), DX + MOVQ src_base+24(FP), BX + MOVQ bitWidth+48(FP), CX + + MOVQ $1, R8 // bitMask = (1 << bitWidth) - 1 + SHLQ CX, R8 + DECQ R8 + MOVQ CX, R9 // bitWidth + + XORQ DI, DI // bitOffset + XORQ SI, SI // index + JMP test +loop: + MOVQ DI, R10 + MOVQ DI, CX + SHRQ $5, R10 // i = bitOffset / 32 + ANDQ $0b11111, CX // j = bitOffset % 32 + + MOVL (BX)(R10*4), R11 + MOVL R8, R12 // d = bitMask + SHLL CX, R12 // d = d << j + ANDL R12, R11 // d = src[i] & d + SHRL CX, R11 // d = d >> j + + MOVL CX, R13 + ADDL R9, R13 + CMPL R13, $32 + JBE next // j+bitWidth <= 32 ? + + MOVL 4(BX)(R10*4), R14 + MOVL CX, R12 + MOVL $32, CX + SUBL R12, CX // k = 32 - j + MOVL R8, R12 // c = bitMask + SHRL CX, R12 // c = c >> k + ANDL R12, R14 // c = src[i+1] & c + SHLL CX, R14 // c = c << k + ORL R14, R11 // d = d | c +next: + MOVL R11, (AX)(SI*4) // dst[n] = d + ADDQ R9, DI // bitOffset += bitWidth + INCQ SI +test: + CMPQ SI, DX + JNE loop + RET + +// ----------------------------------------------------------------------------- +// The unpack* functions below are adaptations of the algorithms +// described in "Decoding billions of integers per second through vectorization" +// from D. Lemire & L. Boytsov, the following changes were made: +// +// - The paper described two methods for decoding integers called "horizontal" +// and "vertical". The "horizontal" version is the one that applies the best +// to the bit packing done in the Parquet delta encoding; however, it also +// differs in some ways, many compression techniques discussed in the paper +// are not implemented in the Parquet format. +// +// - The paper focuses on implementations based on SSE instructions, which +// describes how to use PMULLD to emulate the lack of variable bit shift +// for packed integers. Our version of the bit unpacking algorithms here +// uses AVX2 and can perform variable bit shifts using VPSRLVD, which yields +// better throughput since the instruction latency is a single CPU cycle, +// vs 10 for VPMULLD. +// +// - The reference implementation at https://github.com/lemire/FastPFor/ uses +// specializations for each bit size, resulting in 32 unique functions. +// Our version here are more generic, we provide 3 variations of the +// algorithm for bit widths 1 to 16, 17 to 26, and 27 to 31 (unpacking 32 +// bits values is a simple copy). In that regard, our implementation is +// somewhat an improvement over the reference, since it uses less code and +// less memory to hold the shuffle masks and shift tables. +// +// Technically, each specialization of our functions could be expressed by the +// algorithm used for unpacking values of 27 to 31 bits. However, multiple steps +// of the main loop can be removed for lower bit widths, providing up to ~35% +// better throughput for smaller sizes. Since we expect delta encoding to often +// result in bit packing values to smaller bit widths, the specializations are +// worth the extra complexity. +// +// For more details, see: https://arxiv.org/pdf/1209.2137v5.pdf +// ----------------------------------------------------------------------------- + +// unpackInt32x1to16bitsAVX2 is the implementation of the bit unpacking +// algorithm for inputs of bit width 1 to 16. +// +// In this version of the algorithm, we can perform a single memory load in each +// loop iteration since we know that 8 values will fit in a single XMM register. +// +// func unpackInt32x1to16bitsAVX2(dst []int32, src []byte, bitWidth uint) +TEXT ·unpackInt32x1to16bitsAVX2(SB), NOSPLIT, $56-56 + NO_LOCAL_POINTERS + MOVQ dst_base+0(FP), AX + MOVQ dst_len+8(FP), DX + MOVQ src_base+24(FP), BX + MOVQ bitWidth+48(FP), CX + + CMPQ DX, $8 + JB tail + + MOVQ DX, DI + SHRQ $3, DI + SHLQ $3, DI + XORQ SI, SI + + MOVQ $1, R8 + SHLQ CX, R8 + DECQ R8 + MOVQ R8, X0 + VPBROADCASTD X0, X0 // bitMask = (1 << bitWidth) - 1 + + MOVQ CX, R9 + DECQ R9 + SHLQ $5, R9 // 32 * (bitWidth - 1) + + MOVQ CX, R10 + DECQ R10 + SHLQ $5, R10 + ANDQ $0xFF, R10 // (32 * (bitWidth - 1)) % 256 + + LEAQ ·shuffleInt32x1to16bits(SB), R11 + VMOVDQA (R11)(R9*1), X1 + VMOVDQA 16(R11)(R9*1), X2 + + LEAQ ·shiftRightInt32(SB), R12 + VMOVDQA (R12)(R10*1), X3 + VMOVDQA 16(R12)(R10*1), X4 +loop: + VMOVDQU (BX), X7 + + VPSHUFB X1, X7, X5 + VPSHUFB X2, X7, X6 + + VPSRLVD X3, X5, X5 + VPSRLVD X4, X6, X6 + + VPAND X0, X5, X5 + VPAND X0, X6, X6 + + VMOVDQU X5, (AX)(SI*4) + VMOVDQU X6, 16(AX)(SI*4) + + ADDQ CX, BX + ADDQ $8, SI + CMPQ SI, DI + JNE loop + VZEROUPPER + + CMPQ SI, DX + JE done + LEAQ (AX)(SI*4), AX + SUBQ SI, DX +tail: + MOVQ AX, dst_base-56(SP) + MOVQ DX, dst_len-48(SP) + MOVQ BX, src_base-32(SP) + MOVQ CX, bitWidth-8(SP) + CALL ·unpackInt32Default(SB) +done: + RET + +// unpackInt32x17to26bitsAVX2 is the implementation of the bit unpacking +// algorithm for inputs of bit width 17 to 26. +// +// In this version of the algorithm, we need to 32 bytes at each loop iteration +// because 8 bit-packed values will span across two XMM registers. +// +// func unpackInt32x17to26bitsAVX2(dst []int32, src []byte, bitWidth uint) +TEXT ·unpackInt32x17to26bitsAVX2(SB), NOSPLIT, $56-56 + NO_LOCAL_POINTERS + MOVQ dst_base+0(FP), AX + MOVQ dst_len+8(FP), DX + MOVQ src_base+24(FP), BX + MOVQ bitWidth+48(FP), CX + + CMPQ DX, $8 + JB tail + + MOVQ DX, DI + SHRQ $3, DI + SHLQ $3, DI + XORQ SI, SI + + MOVQ $1, R8 + SHLQ CX, R8 + DECQ R8 + MOVQ R8, X0 + VPBROADCASTD X0, X0 + + MOVQ CX, R9 + SUBQ $17, R9 + IMULQ $48, R9 // 48 * (bitWidth - 17) + + MOVQ CX, R10 + DECQ R10 + SHLQ $5, R10 + ANDQ $0xFF, R10 // (32 * (bitWidth - 1)) % 256 + + LEAQ ·shuffleInt32x17to26bits(SB), R11 + VMOVDQA (R11)(R9*1), X1 + VMOVDQA 16(R11)(R9*1), X2 + VMOVDQA 32(R11)(R9*1), X3 + + LEAQ ·shiftRightInt32(SB), R12 + VMOVDQA (R12)(R10*1), X4 + VMOVDQA 16(R12)(R10*1), X5 +loop: + VMOVDQU (BX), X6 + VMOVDQU 16(BX), X7 + + VPSHUFB X1, X6, X8 + VPSHUFB X2, X6, X9 + VPSHUFB X3, X7, X10 + VPOR X10, X9, X9 + + VPSRLVD X4, X8, X8 + VPSRLVD X5, X9, X9 + + VPAND X0, X8, X8 + VPAND X0, X9, X9 + + VMOVDQU X8, (AX)(SI*4) + VMOVDQU X9, 16(AX)(SI*4) + + ADDQ CX, BX + ADDQ $8, SI + CMPQ SI, DI + JNE loop + VZEROUPPER + + CMPQ SI, DX + JE done + LEAQ (AX)(SI*4), AX + SUBQ SI, DX +tail: + MOVQ AX, dst_base-56(SP) + MOVQ DX, dst_len-48(SP) + MOVQ BX, src_base-32(SP) + MOVQ CX, bitWidth-8(SP) + CALL ·unpackInt32Default(SB) +done: + RET + +// unpackInt32x27to31bitsAVX2 is the implementation of the bit unpacking +// algorithm for inputs of bit width 27 to 31. +// +// In this version of the algorithm the bit-packed values may span across up to +// 5 bytes. The simpler approach for smaller bit widths where we could perform a +// single shuffle + shift to unpack the values do not work anymore. +// +// Values are unpacked in two steps: the first one extracts lower bits which are +// shifted RIGHT to align on the beginning of 32 bit words, the second extracts +// upper bits which are shifted LEFT to be moved to the end of the 32 bit words. +// +// The amount of LEFT shifts is always "8 minus the amount of RIGHT shift". +// +// func unpackInt32x27to31bitsAVX2(dst []int32, src []byte, bitWidth uint) +TEXT ·unpackInt32x27to31bitsAVX2(SB), NOSPLIT, $56-56 + NO_LOCAL_POINTERS + MOVQ dst_base+0(FP), AX + MOVQ dst_len+8(FP), DX + MOVQ src_base+24(FP), BX + MOVQ bitWidth+48(FP), CX + + CMPQ DX, $8 + JB tail + + MOVQ DX, DI + SHRQ $3, DI + SHLQ $3, DI + XORQ SI, SI + + MOVQ $1, R8 + SHLQ CX, R8 + DECQ R8 + MOVQ R8, X0 + VPBROADCASTD X0, X0 + + MOVQ CX, R9 + SUBQ $27, R9 + IMULQ $80, R9 // (80 * (bitWidth - 27)) + + MOVQ CX, R10 + DECQ R10 + SHLQ $5, R10 + ANDQ $0xFF, R10 // (32 * (bitWidth - 1)) % 256 + + LEAQ ·shuffleInt32x27to31bits(SB), R11 + VMOVDQA (R11)(R9*1), X1 + VMOVDQA 16(R11)(R9*1), X2 + VMOVDQA 32(R11)(R9*1), X3 + VMOVDQA 48(R11)(R9*1), X4 + VMOVDQA 64(R11)(R9*1), X5 + + LEAQ ·shiftRightInt32(SB), R12 + LEAQ ·shiftLeftInt32(SB), R13 + VMOVDQA (R12)(R10*1), X6 + VMOVDQA (R13)(R10*1), X7 + VMOVDQA 16(R12)(R10*1), X8 + VMOVDQA 16(R13)(R10*1), X9 +loop: + VMOVDQU (BX), X10 + VMOVDQU 16(BX), X11 + + VPSHUFB X1, X10, X12 + VPSHUFB X2, X10, X13 + VPSHUFB X3, X10, X14 + VPSHUFB X4, X11, X15 + VPSHUFB X5, X11, X11 + + VPSRLVD X6, X12, X12 + VPSLLVD X7, X13, X13 + VPSRLVD X8, X14, X14 + VPSRLVD X8, X15, X15 + VPSLLVD X9, X11, X11 + + VPOR X13, X12, X12 + VPOR X15, X14, X14 + VPOR X11, X14, X14 + + VPAND X0, X12, X12 + VPAND X0, X14, X14 + + VMOVDQU X12, (AX)(SI*4) + VMOVDQU X14, 16(AX)(SI*4) + + ADDQ CX, BX + ADDQ $8, SI + CMPQ SI, DI + JNE loop + VZEROUPPER + + CMPQ SI, DX + JE done + LEAQ (AX)(SI*4), AX + SUBQ SI, DX +tail: + MOVQ AX, dst_base-56(SP) + MOVQ DX, dst_len-48(SP) + MOVQ BX, src_base-32(SP) + MOVQ CX, bitWidth-8(SP) + CALL ·unpackInt32Default(SB) +done: + RET diff --git a/vendor/github.com/parquet-go/bitpack/unpack_int32_arm64.go b/vendor/github.com/parquet-go/bitpack/unpack_int32_arm64.go new file mode 100644 index 000000000..e73d1fdf7 --- /dev/null +++ b/vendor/github.com/parquet-go/bitpack/unpack_int32_arm64.go @@ -0,0 +1,48 @@ +//go:build !purego + +package bitpack + +import ( + "github.com/parquet-go/bitpack/unsafecast" +) + +//go:noescape +func unpackInt32Default(dst []int32, src []byte, bitWidth uint) + +//go:noescape +func unpackInt32x1to16bitsARM64(dst []int32, src []byte, bitWidth uint) + +//go:noescape +func unpackInt32x1bitNEON(dst []int32, src []byte, bitWidth uint) + +//go:noescape +func unpackInt32x2bitNEON(dst []int32, src []byte, bitWidth uint) + +//go:noescape +func unpackInt32x3bitNEON(dst []int32, src []byte, bitWidth uint) + +//go:noescape +func unpackInt32x4bitNEON(dst []int32, src []byte, bitWidth uint) + +//go:noescape +func unpackInt32x8bitNEON(dst []int32, src []byte, bitWidth uint) + +func unpackInt32(dst []int32, src []byte, bitWidth uint) { + switch { + case bitWidth == 1: + unpackInt32x1bitNEON(dst, src, bitWidth) + case bitWidth == 2: + unpackInt32x2bitNEON(dst, src, bitWidth) + case bitWidth == 4: + unpackInt32x4bitNEON(dst, src, bitWidth) + case bitWidth == 8: + unpackInt32x8bitNEON(dst, src, bitWidth) + // bitWidth == 3,5,6,7: Skip NEON table (don't divide evenly into 8) + case bitWidth <= 16: + unpackInt32x1to16bitsARM64(dst, src, bitWidth) + case bitWidth == 32: + copy(dst, unsafecast.Slice[int32](src)) + default: + unpackInt32Default(dst, src, bitWidth) + } +} diff --git a/vendor/github.com/parquet-go/bitpack/unpack_int32_arm64.s b/vendor/github.com/parquet-go/bitpack/unpack_int32_arm64.s new file mode 100644 index 000000000..d3c0bda54 --- /dev/null +++ b/vendor/github.com/parquet-go/bitpack/unpack_int32_arm64.s @@ -0,0 +1,732 @@ +//go:build !purego + +#include "funcdata.h" +#include "textflag.h" + +// func unpackInt32Default(dst []int32, src []byte, bitWidth uint) +TEXT ·unpackInt32Default(SB), NOSPLIT, $0-56 + MOVD dst_base+0(FP), R0 // R0 = dst pointer + MOVD dst_len+8(FP), R1 // R1 = dst length + MOVD src_base+24(FP), R2 // R2 = src pointer + MOVD bitWidth+48(FP), R3 // R3 = bitWidth + + MOVD $1, R4 // R4 = bitMask = (1 << bitWidth) - 1 + LSL R3, R4, R4 + SUB $1, R4, R4 + + MOVD $0, R5 // R5 = bitOffset + MOVD $0, R6 // R6 = index + B test + +loop: + MOVD R5, R7 // R7 = i = bitOffset / 32 + LSR $5, R7, R7 + + MOVD R5, R8 // R8 = j = bitOffset % 32 + AND $31, R8, R8 + + LSL $2, R7, R16 // R16 = i * 4 + MOVWU (R2)(R16), R9 // R9 = src[i] + MOVW R4, R10 // R10 = bitMask + LSL R8, R10, R10 // R10 = bitMask << j + AND R10, R9, R9 // R9 = src[i] & (bitMask << j) + LSR R8, R9, R9 // R9 = d = (src[i] & (bitMask << j)) >> j + + ADD R3, R8, R11 // R11 = j + bitWidth + CMP $32, R11 + BLE next // if j+bitWidth <= 32, skip to next + + ADD $1, R7, R12 // R12 = i + 1 + LSL $2, R12, R16 // R16 = (i + 1) * 4 + MOVWU (R2)(R16), R13 // R13 = src[i+1] + + MOVD $32, R14 // R14 = k = 32 - j + SUB R8, R14, R14 + + MOVW R4, R15 // R15 = bitMask + LSR R14, R15, R15 // R15 = bitMask >> k + AND R15, R13, R13 // R13 = src[i+1] & (bitMask >> k) + LSL R14, R13, R13 // R13 = (src[i+1] & (bitMask >> k)) << k + ORR R13, R9, R9 // R9 = d | c + +next: + LSL $2, R6, R16 // R16 = index * 4 + MOVW R9, (R0)(R16) // dst[index] = d + ADD R3, R5, R5 // bitOffset += bitWidth + ADD $1, R6, R6 // index++ + +test: + CMP R1, R6 + BNE loop + RET + +// unpackInt32x1to16bitsARM64 implements optimized unpacking for bit widths 1-16 +// Uses optimized scalar ARM64 operations with batched processing +// +// func unpackInt32x1to16bitsARM64(dst []int32, src []byte, bitWidth uint) +TEXT ·unpackInt32x1to16bitsARM64(SB), NOSPLIT, $0-56 + MOVD dst_base+0(FP), R0 // R0 = dst pointer + MOVD dst_len+8(FP), R1 // R1 = dst length + MOVD src_base+24(FP), R2 // R2 = src pointer + MOVD bitWidth+48(FP), R3 // R3 = bitWidth + + // Check if we have at least 4 values to process + CMP $4, R1 + BLT scalar_fallback + + // Determine which NEON path to use based on bitWidth + CMP $1, R3 + BEQ neon_1bit + CMP $2, R3 + BEQ neon_2bit + CMP $3, R3 + BEQ neon_3bit + CMP $4, R3 + BEQ neon_4bit + CMP $5, R3 + BEQ neon_5bit + CMP $6, R3 + BEQ neon_6bit + CMP $7, R3 + BEQ neon_7bit + CMP $8, R3 + BEQ neon_8bit + CMP $16, R3 + BEQ neon_16bit + + // For other bit widths, fall back to scalar + B scalar_fallback + +neon_1bit: + // BitWidth 1: 8 int32 values packed in 1 byte + // Process 8 values at a time using scalar operations + + // Round down to multiple of 8 for processing + MOVD R1, R4 + LSR $3, R4, R4 // R4 = len / 8 + LSL $3, R4, R4 // R4 = aligned length (multiple of 8) + + MOVD $0, R5 // R5 = index + CMP $0, R4 + BEQ scalar_fallback + +neon_1bit_loop: + // Load 1 byte (contains 8 values, 1 bit each) + MOVBU (R2), R6 + + // Extract 8 values manually (bits 0-7) + // Value 0: bit 0 + AND $1, R6, R7 + MOVW R7, (R0) + + // Value 1: bit 1 + LSR $1, R6, R7 + AND $1, R7, R7 + MOVW R7, 4(R0) + + // Value 2: bit 2 + LSR $2, R6, R7 + AND $1, R7, R7 + MOVW R7, 8(R0) + + // Value 3: bit 3 + LSR $3, R6, R7 + AND $1, R7, R7 + MOVW R7, 12(R0) + + // Value 4: bit 4 + LSR $4, R6, R7 + AND $1, R7, R7 + MOVW R7, 16(R0) + + // Value 5: bit 5 + LSR $5, R6, R7 + AND $1, R7, R7 + MOVW R7, 20(R0) + + // Value 6: bit 6 + LSR $6, R6, R7 + AND $1, R7, R7 + MOVW R7, 24(R0) + + // Value 7: bit 7 + LSR $7, R6, R7 + AND $1, R7, R7 + MOVW R7, 28(R0) + + // Advance pointers + ADD $1, R2, R2 // src += 1 byte (8 values) + ADD $32, R0, R0 // dst += 8 int32 (32 bytes) + ADD $8, R5, R5 // index += 8 + + CMP R4, R5 + BLT neon_1bit_loop + + CMP R1, R5 + BEQ neon_done + SUB R5, R1, R1 + B scalar_fallback_entry + +neon_2bit: + // BitWidth 2: 4 int32 values packed in 1 byte + // Process 4 values at a time using scalar operations + + MOVD R1, R4 + LSR $2, R4, R4 // R4 = len / 4 + LSL $2, R4, R4 // R4 = aligned length (multiple of 4) + + MOVD $0, R5 + CMP $0, R4 + BEQ scalar_fallback + +neon_2bit_loop: + // Load 1 byte (contains 4 values, 2 bits each) + MOVBU (R2), R6 + + // Extract 4 values manually (bits 0-1, 2-3, 4-5, 6-7) + // Value 0: bits 0-1 + AND $3, R6, R7 + MOVW R7, (R0) + + // Value 1: bits 2-3 + LSR $2, R6, R7 + AND $3, R7, R7 + MOVW R7, 4(R0) + + // Value 2: bits 4-5 + LSR $4, R6, R7 + AND $3, R7, R7 + MOVW R7, 8(R0) + + // Value 3: bits 6-7 + LSR $6, R6, R7 + AND $3, R7, R7 + MOVW R7, 12(R0) + + // Advance pointers + ADD $1, R2, R2 // src += 1 byte (4 values) + ADD $16, R0, R0 // dst += 4 int32 (16 bytes) + ADD $4, R5, R5 // index += 4 + + CMP R4, R5 + BLT neon_2bit_loop + + CMP R1, R5 + BEQ neon_done + SUB R5, R1, R1 + B scalar_fallback_entry + +neon_3bit: + // BitWidth 3: 8 int32 values packed in 3 bytes + // Process 8 values at a time using scalar operations + + // Round down to multiple of 8 for processing + MOVD R1, R4 + LSR $3, R4, R4 // R4 = len / 8 + LSL $3, R4, R4 // R4 = aligned length (multiple of 8) + + MOVD $0, R5 + CMP $0, R4 + BEQ scalar_fallback + +neon_3bit_loop: + // Load 3 bytes as 32-bit value (4th byte will be ignored) + // Bytes 0-2 contain: [val7:val6:val5:val4:val3:val2:val1:val0] + // Bits layout: [23:21][20:18][17:15][14:12][11:9][8:6][5:3][2:0] + MOVWU (R2), R6 + + // Value 0: bits 0-2 + AND $7, R6, R7 + MOVW R7, (R0) + + // Value 1: bits 3-5 + LSR $3, R6, R7 + AND $7, R7, R7 + MOVW R7, 4(R0) + + // Value 2: bits 6-8 + LSR $6, R6, R7 + AND $7, R7, R7 + MOVW R7, 8(R0) + + // Value 3: bits 9-11 + LSR $9, R6, R7 + AND $7, R7, R7 + MOVW R7, 12(R0) + + // Value 4: bits 12-14 + LSR $12, R6, R7 + AND $7, R7, R7 + MOVW R7, 16(R0) + + // Value 5: bits 15-17 + LSR $15, R6, R7 + AND $7, R7, R7 + MOVW R7, 20(R0) + + // Value 6: bits 18-20 + LSR $18, R6, R7 + AND $7, R7, R7 + MOVW R7, 24(R0) + + // Value 7: bits 21-23 + LSR $21, R6, R7 + AND $7, R7, R7 + MOVW R7, 28(R0) + + // Advance pointers + ADD $3, R2, R2 // src += 3 bytes (8 values) + ADD $32, R0, R0 // dst += 8 int32 (32 bytes) + ADD $8, R5, R5 // index += 8 + + CMP R4, R5 + BLT neon_3bit_loop + + CMP R1, R5 + BEQ neon_done + SUB R5, R1, R1 + B scalar_fallback_entry + +neon_4bit: + // BitWidth 4: 4 int32 values packed in 2 bytes + // Process 4 values at a time using scalar operations + + MOVD R1, R4 + LSR $2, R4, R4 // R4 = len / 4 + LSL $2, R4, R4 // R4 = aligned length (multiple of 4) + + MOVD $0, R5 + CMP $0, R4 + BEQ scalar_fallback + +neon_4bit_loop: + // Load 2 bytes (contains 4 values, 4 bits each) + MOVHU (R2), R6 + + // Extract 4 values manually (nibbles) + // Value 0: bits 0-3 + AND $15, R6, R7 + MOVW R7, (R0) + + // Value 1: bits 4-7 + LSR $4, R6, R7 + AND $15, R7, R7 + MOVW R7, 4(R0) + + // Value 2: bits 8-11 + LSR $8, R6, R7 + AND $15, R7, R7 + MOVW R7, 8(R0) + + // Value 3: bits 12-15 + LSR $12, R6, R7 + AND $15, R7, R7 + MOVW R7, 12(R0) + + // Advance pointers + ADD $2, R2, R2 // src += 2 bytes (4 values) + ADD $16, R0, R0 // dst += 4 int32 (16 bytes) + ADD $4, R5, R5 // index += 4 + + CMP R4, R5 + BLT neon_4bit_loop + + CMP R1, R5 + BEQ neon_done + SUB R5, R1, R1 + B scalar_fallback_entry + +neon_5bit: + // BitWidth 5: 8 int32 values packed in 5 bytes + // Process 8 values at a time using scalar operations + + // Round down to multiple of 8 for processing + MOVD R1, R4 + LSR $3, R4, R4 // R4 = len / 8 + LSL $3, R4, R4 // R4 = aligned length (multiple of 8) + + MOVD $0, R5 + CMP $0, R4 + BEQ scalar_fallback + +neon_5bit_loop: + // Load 5 bytes as 64-bit value (upper bytes will be ignored) + // 8 values × 5 bits = 40 bits = 5 bytes + // Bits layout: [39:35][34:30][29:25][24:20][19:15][14:10][9:5][4:0] + MOVD (R2), R6 + + // Value 0: bits 0-4 + AND $31, R6, R7 + MOVW R7, (R0) + + // Value 1: bits 5-9 + LSR $5, R6, R7 + AND $31, R7, R7 + MOVW R7, 4(R0) + + // Value 2: bits 10-14 + LSR $10, R6, R7 + AND $31, R7, R7 + MOVW R7, 8(R0) + + // Value 3: bits 15-19 + LSR $15, R6, R7 + AND $31, R7, R7 + MOVW R7, 12(R0) + + // Value 4: bits 20-24 + LSR $20, R6, R7 + AND $31, R7, R7 + MOVW R7, 16(R0) + + // Value 5: bits 25-29 + LSR $25, R6, R7 + AND $31, R7, R7 + MOVW R7, 20(R0) + + // Value 6: bits 30-34 + LSR $30, R6, R7 + AND $31, R7, R7 + MOVW R7, 24(R0) + + // Value 7: bits 35-39 + LSR $35, R6, R7 + AND $31, R7, R7 + MOVW R7, 28(R0) + + // Advance pointers + ADD $5, R2, R2 // src += 5 bytes (8 values) + ADD $32, R0, R0 // dst += 8 int32 (32 bytes) + ADD $8, R5, R5 // index += 8 + + CMP R4, R5 + BLT neon_5bit_loop + + CMP R1, R5 + BEQ neon_done + SUB R5, R1, R1 + B scalar_fallback_entry + +neon_6bit: + // BitWidth 6: 4 int32 values packed in 3 bytes + // Process 4 values at a time using scalar operations + + MOVD R1, R4 + LSR $2, R4, R4 // R4 = len / 4 + LSL $2, R4, R4 // R4 = aligned length (multiple of 4) + + MOVD $0, R5 + CMP $0, R4 + BEQ scalar_fallback + +neon_6bit_loop: + // Load 3 bytes as 32-bit value (4th byte will be ignored) + // 4 values × 6 bits = 24 bits = 3 bytes + // Bits layout: [23:18][17:12][11:6][5:0] + MOVWU (R2), R6 + + // Value 0: bits 0-5 + AND $63, R6, R7 + MOVW R7, (R0) + + // Value 1: bits 6-11 + LSR $6, R6, R7 + AND $63, R7, R7 + MOVW R7, 4(R0) + + // Value 2: bits 12-17 + LSR $12, R6, R7 + AND $63, R7, R7 + MOVW R7, 8(R0) + + // Value 3: bits 18-23 + LSR $18, R6, R7 + AND $63, R7, R7 + MOVW R7, 12(R0) + + // Advance pointers + ADD $3, R2, R2 // src += 3 bytes (4 values) + ADD $16, R0, R0 // dst += 4 int32 (16 bytes) + ADD $4, R5, R5 // index += 4 + + CMP R4, R5 + BLT neon_6bit_loop + + CMP R1, R5 + BEQ neon_done + SUB R5, R1, R1 + B scalar_fallback_entry + +neon_7bit: + // BitWidth 7: 8 int32 values packed in 7 bytes + // Process 8 values at a time using scalar operations + + // Round down to multiple of 8 for processing + MOVD R1, R4 + LSR $3, R4, R4 // R4 = len / 8 + LSL $3, R4, R4 // R4 = aligned length (multiple of 8) + + MOVD $0, R5 + CMP $0, R4 + BEQ scalar_fallback + +neon_7bit_loop: + // Load 7 bytes as 64-bit value (8th byte will be ignored) + // 8 values × 7 bits = 56 bits = 7 bytes + // Bits layout: [55:49][48:42][41:35][34:28][27:21][20:14][13:7][6:0] + MOVD (R2), R6 + + // Value 0: bits 0-6 + AND $127, R6, R7 + MOVW R7, (R0) + + // Value 1: bits 7-13 + LSR $7, R6, R7 + AND $127, R7, R7 + MOVW R7, 4(R0) + + // Value 2: bits 14-20 + LSR $14, R6, R7 + AND $127, R7, R7 + MOVW R7, 8(R0) + + // Value 3: bits 21-27 + LSR $21, R6, R7 + AND $127, R7, R7 + MOVW R7, 12(R0) + + // Value 4: bits 28-34 + LSR $28, R6, R7 + AND $127, R7, R7 + MOVW R7, 16(R0) + + // Value 5: bits 35-41 + LSR $35, R6, R7 + AND $127, R7, R7 + MOVW R7, 20(R0) + + // Value 6: bits 42-48 + LSR $42, R6, R7 + AND $127, R7, R7 + MOVW R7, 24(R0) + + // Value 7: bits 49-55 + LSR $49, R6, R7 + AND $127, R7, R7 + MOVW R7, 28(R0) + + // Advance pointers + ADD $7, R2, R2 // src += 7 bytes (8 values) + ADD $32, R0, R0 // dst += 8 int32 (32 bytes) + ADD $8, R5, R5 // index += 8 + + CMP R4, R5 + BLT neon_7bit_loop + + CMP R1, R5 + BEQ neon_done + SUB R5, R1, R1 + B scalar_fallback_entry + +neon_8bit: + // BitWidth 8: 4 int32 values packed in 4 bytes + // Process 4 values at a time using NEON + + // Calculate how many full groups of 4 we can process + MOVD R1, R4 + LSR $2, R4, R4 // R4 = len / 4 + LSL $2, R4, R4 // R4 = (len / 4) * 4 = aligned length + + MOVD $0, R5 // R5 = index + CMP $0, R4 + BEQ scalar_fallback + +neon_8bit_loop: + // Load 4 bytes as 4 uint8 values into lower part of V0 + // We need to load bytes and zero-extend to 32-bit + + // Load 4 bytes to W6 + MOVWU (R2), R6 + + // Extract bytes and write as int32 + // Byte 0 + AND $0xFF, R6, R7 + MOVW R7, (R0) + + // Byte 1 + LSR $8, R6, R7 + AND $0xFF, R7, R7 + MOVW R7, 4(R0) + + // Byte 2 + LSR $16, R6, R7 + AND $0xFF, R7, R7 + MOVW R7, 8(R0) + + // Byte 3 + LSR $24, R6, R7 + MOVW R7, 12(R0) + + // Advance pointers + ADD $4, R2, R2 // src += 4 bytes + ADD $16, R0, R0 // dst += 4 int32 (16 bytes) + ADD $4, R5, R5 // index += 4 + + CMP R4, R5 + BLT neon_8bit_loop + + // Handle tail with scalar + CMP R1, R5 + BEQ neon_done + + // Calculate remaining elements + SUB R5, R1, R1 // R1 = remaining elements + B scalar_fallback_entry + +neon_16bit: + // BitWidth 16: 4 int32 values packed in 8 bytes + // Process 4 values at a time + + MOVD R1, R4 + LSR $2, R4, R4 // R4 = len / 4 + LSL $2, R4, R4 // R4 = (len / 4) * 4 + + MOVD $0, R5 // R5 = index + CMP $0, R4 + BEQ scalar_fallback + +neon_16bit_loop: + // Load 8 bytes as 4 uint16 values + MOVD (R2), R6 // Load 8 bytes into R6 + + // Extract 16-bit values and write as int32 + // Value 0 (bits 0-15) + AND $0xFFFF, R6, R7 + MOVW R7, (R0) + + // Value 1 (bits 16-31) + LSR $16, R6, R7 + AND $0xFFFF, R7, R7 + MOVW R7, 4(R0) + + // Value 2 (bits 32-47) + LSR $32, R6, R7 + AND $0xFFFF, R7, R7 + MOVW R7, 8(R0) + + // Value 3 (bits 48-63) + LSR $48, R6, R7 + MOVW R7, 12(R0) + + // Advance pointers + ADD $8, R2, R2 // src += 8 bytes + ADD $16, R0, R0 // dst += 4 int32 (16 bytes) + ADD $4, R5, R5 // index += 4 + + CMP R4, R5 + BLT neon_16bit_loop + + // Handle tail with scalar + CMP R1, R5 + BEQ neon_done + + SUB R5, R1, R1 + B scalar_fallback_entry + +neon_done: + RET + +scalar_fallback: + MOVD $0, R5 // Start from beginning + // R0, R1, R2, R3 already set from function args + +scalar_fallback_entry: + // R0 = current dst position (already advanced) + // R1 = remaining elements + // R2 = current src position (already advanced) + // R3 = bitWidth + // R5 = elements already processed + + // Fall back to scalar implementation for remaining elements + CMP $0, R1 + BEQ scalar_done // No remaining elements + + MOVD $1, R4 // R4 = bitMask = (1 << bitWidth) - 1 + LSL R3, R4, R4 + SUB $1, R4, R4 + + // bitOffset starts from 0 relative to current R2 position + // (not total offset, since R2 is already advanced) + MOVD $0, R6 // R6 = bitOffset (relative to current R2) + MOVD $0, R7 // R7 = index (within remaining elements) + B scalar_test + +scalar_loop: + MOVD R6, R8 // R8 = i = bitOffset / 32 + LSR $5, R8, R8 + + MOVD R6, R9 // R9 = j = bitOffset % 32 + AND $31, R9, R9 + + LSL $2, R8, R10 // R10 = i * 4 + MOVWU (R2)(R10), R11 // R11 = src[i] (relative to current R2) + MOVW R4, R12 // R12 = bitMask + LSL R9, R12, R12 // R12 = bitMask << j + AND R12, R11, R11 // R11 = src[i] & (bitMask << j) + LSR R9, R11, R11 // R11 = d = (src[i] & (bitMask << j)) >> j + + ADD R3, R9, R12 // R12 = j + bitWidth + CMP $32, R12 + BLE scalar_next // if j+bitWidth <= 32, skip to next + + ADD $1, R8, R13 // R13 = i + 1 + LSL $2, R13, R10 // R10 = (i + 1) * 4 + MOVWU (R2)(R10), R14 // R14 = src[i+1] + + MOVD $32, R15 // R15 = k = 32 - j + SUB R9, R15, R15 + + MOVW R4, R16 // R16 = bitMask + LSR R15, R16, R16 // R16 = bitMask >> k + AND R16, R14, R14 // R14 = src[i+1] & (bitMask >> k) + LSL R15, R14, R14 // R14 = (src[i+1] & (bitMask >> k)) << k + ORR R14, R11, R11 // R11 = d | c + +scalar_next: + LSL $2, R7, R10 // R10 = index * 4 + MOVW R11, (R0)(R10) // dst[index] = d (relative to current R0) + ADD R3, R6, R6 // bitOffset += bitWidth + ADD $1, R7, R7 // index++ + +scalar_test: + CMP R1, R7 + BLT scalar_loop + +scalar_done: + RET + +// Macro definitions for unsupported NEON instructions using WORD encodings +// USHLL Vd.8H, Vn.8B, #0 - widen 8x8-bit to 8x16-bit +#define USHLL_8H_8B(vd, vn) WORD $(0x2f08a400 | (vd) | ((vn)<<5)) + +// USHLL2 Vd.8H, Vn.16B, #0 - widen upper 8x8-bit to 8x16-bit +#define USHLL2_8H_16B(vd, vn) WORD $(0x6f08a400 | (vd) | ((vn)<<5)) + +// USHLL Vd.4S, Vn.4H, #0 - widen 4x16-bit to 4x32-bit +#define USHLL_4S_4H(vd, vn) WORD $(0x2f10a400 | (vd) | ((vn)<<5)) + +// USHLL2 Vd.4S, Vn.8H, #0 - widen upper 4x16-bit to 4x32-bit +#define USHLL2_4S_8H(vd, vn) WORD $(0x6f10a400 | (vd) | ((vn)<<5)) + +// USHLL Vd.2D, Vn.2S, #0 - widen 2x32-bit to 2x64-bit +#define USHLL_2D_2S(vd, vn) WORD $(0x2f20a400 | (vd) | ((vn)<<5)) + +// USHLL2 Vd.2D, Vn.4S, #0 - widen upper 2x32-bit to 2x64-bit +#define USHLL2_2D_4S(vd, vn) WORD $(0x6f20a400 | (vd) | ((vn)<<5)) + +// Bit expansion lookup table defined in bitexpand_table_arm64.s + +// unpackInt32x1bitNEON implements table-based NEON unpacking for bitWidth=1 +// Uses lookup tables for parallel bit expansion +// +// func unpackInt32x1bitNEON(dst []int32, src []byte, bitWidth uint) diff --git a/vendor/github.com/parquet-go/bitpack/unpack_int32_be.go b/vendor/github.com/parquet-go/bitpack/unpack_int32_be.go new file mode 100644 index 000000000..0f4ba054c --- /dev/null +++ b/vendor/github.com/parquet-go/bitpack/unpack_int32_be.go @@ -0,0 +1,15 @@ +//go:build s390x + +package bitpack + +import "encoding/binary" + +func unsafecastBytesToUint32(src []byte) []uint32 { + out := make([]uint32, len(src)/4) + idx := 0 + for k := range out { + out[k] = binary.LittleEndian.Uint32((src)[idx:(4 + idx)]) + idx += 4 + } + return out +} diff --git a/vendor/github.com/parquet-go/bitpack/unpack_int32_le.go b/vendor/github.com/parquet-go/bitpack/unpack_int32_le.go new file mode 100644 index 000000000..035f6341e --- /dev/null +++ b/vendor/github.com/parquet-go/bitpack/unpack_int32_le.go @@ -0,0 +1,9 @@ +//go:build !s390x + +package bitpack + +import "github.com/parquet-go/bitpack/unsafecast" + +func unsafecastBytesToUint32(src []byte) []uint32 { + return unsafecast.Slice[uint32](src) +} diff --git a/vendor/github.com/parquet-go/bitpack/unpack_int32_purego.go b/vendor/github.com/parquet-go/bitpack/unpack_int32_purego.go new file mode 100644 index 000000000..71477f6e7 --- /dev/null +++ b/vendor/github.com/parquet-go/bitpack/unpack_int32_purego.go @@ -0,0 +1,21 @@ +//go:build purego || (!amd64 && !arm64) + +package bitpack + +func unpackInt32(dst []int32, src []byte, bitWidth uint) { + bits := unsafecastBytesToUint32(src) + bitMask := uint32(1<> j + if j+bitWidth > 32 { + k := 32 - j + d |= (bits[i+1] & (bitMask >> k)) << k + } + dst[n] = int32(d) + bitOffset += bitWidth + } +} diff --git a/vendor/github.com/parquet-go/bitpack/unpack_int64_1bit_amd64.s b/vendor/github.com/parquet-go/bitpack/unpack_int64_1bit_amd64.s new file mode 100644 index 000000000..dae464e27 --- /dev/null +++ b/vendor/github.com/parquet-go/bitpack/unpack_int64_1bit_amd64.s @@ -0,0 +1,123 @@ +//go:build !purego + +#include "funcdata.h" +#include "textflag.h" + +// unpackInt64x1bitAVX2 implements optimized unpacking for bitWidth=1 using AVX2 +// Each byte contains 8 bits, processes 8 values at a time +// +// func unpackInt64x1bitAVX2(dst []int64, src []byte, bitWidth uint) +TEXT ·unpackInt64x1bitAVX2(SB), NOSPLIT, $0-56 + MOVQ dst_base+0(FP), AX // AX = dst pointer + MOVQ dst_len+8(FP), DX // DX = dst length + MOVQ src_base+24(FP), BX // BX = src pointer + MOVQ bitWidth+48(FP), CX // CX = bitWidth (should be 1) + + // Check if we have at least 8 values to process + CMPQ DX, $8 + JB avx2_1bit_tail + + // Round down to multiple of 8 for AVX2 processing + MOVQ DX, DI + SHRQ $3, DI // DI = len / 8 + SHLQ $3, DI // DI = aligned length (multiple of 8) + XORQ SI, SI // SI = index + +avx2_1bit_loop: + // Load 1 byte (contains 8 x 1-bit values) + MOVBQZX (BX), R8 + + // Extract each bit and store as int64 + // Bit 0 + MOVQ R8, R9 + ANDQ $1, R9 + MOVQ R9, (AX) + + // Bit 1 + MOVQ R8, R9 + SHRQ $1, R9 + ANDQ $1, R9 + MOVQ R9, 8(AX) + + // Bit 2 + MOVQ R8, R9 + SHRQ $2, R9 + ANDQ $1, R9 + MOVQ R9, 16(AX) + + // Bit 3 + MOVQ R8, R9 + SHRQ $3, R9 + ANDQ $1, R9 + MOVQ R9, 24(AX) + + // Bit 4 + MOVQ R8, R9 + SHRQ $4, R9 + ANDQ $1, R9 + MOVQ R9, 32(AX) + + // Bit 5 + MOVQ R8, R9 + SHRQ $5, R9 + ANDQ $1, R9 + MOVQ R9, 40(AX) + + // Bit 6 + MOVQ R8, R9 + SHRQ $6, R9 + ANDQ $1, R9 + MOVQ R9, 48(AX) + + // Bit 7 + MOVQ R8, R9 + SHRQ $7, R9 + ANDQ $1, R9 + MOVQ R9, 56(AX) + + // Advance pointers + ADDQ $1, BX // src += 1 byte + ADDQ $64, AX // dst += 8 int64 (64 bytes) + ADDQ $8, SI // index += 8 + + CMPQ SI, DI + JNE avx2_1bit_loop + +avx2_1bit_tail: + // Handle remaining elements with scalar fallback + CMPQ SI, DX + JE avx2_1bit_done + + // Compute remaining elements + SUBQ SI, DX + + // Calculate bit offset for remaining elements + // Each processed element consumes 1 bit, so bitOffset = SI * 1 + MOVQ SI, R9 // bitOffset = SI (number of bits already processed) + XORQ R10, R10 // index = 0 (within remaining elements) + JMP avx2_1bit_scalar_test + +avx2_1bit_scalar_loop: + MOVQ R9, R11 + SHRQ $3, R11 // byte_index = bitOffset / 8 + MOVQ src_base+24(FP), R14 // Get original src pointer + MOVBQZX (R14)(R11*1), R12 // Load byte from original src + + MOVQ R9, R13 + ANDQ $7, R13 // bit_offset = bitOffset % 8 + + MOVQ R13, CX // Move bit offset to CX for shift + SHRQ CL, R12 // Shift right by bit offset + ANDQ $1, R12 // Mask to get bit + MOVQ R12, (AX) // Store as int64 + + ADDQ $8, AX // dst++ + ADDQ $1, R9 // bitOffset++ + ADDQ $1, R10 // index++ + +avx2_1bit_scalar_test: + CMPQ R10, DX + JNE avx2_1bit_scalar_loop + +avx2_1bit_done: + RET diff --git a/vendor/github.com/parquet-go/bitpack/unpack_int64_1bit_arm64.s b/vendor/github.com/parquet-go/bitpack/unpack_int64_1bit_arm64.s new file mode 100644 index 000000000..605d6bd3e --- /dev/null +++ b/vendor/github.com/parquet-go/bitpack/unpack_int64_1bit_arm64.s @@ -0,0 +1,239 @@ +//go:build !purego + +#include "textflag.h" +#include "unpack_neon_macros_arm64.h" + +// unpackInt64x1bitNEON implements NEON unpacking for bitWidth=1 using direct bit manipulation +// Each byte contains 8 bits: [bit7][bit6][bit5][bit4][bit3][bit2][bit1][bit0] +// +// func unpackInt64x1bitNEON(dst []int64, src []byte, bitWidth uint) +TEXT ·unpackInt64x1bitNEON(SB), NOSPLIT, $0-56 + MOVD dst_base+0(FP), R0 // R0 = dst pointer + MOVD dst_len+8(FP), R1 // R1 = dst length + MOVD src_base+24(FP), R2 // R2 = src pointer + MOVD bitWidth+48(FP), R3 // R3 = bitWidth (should be 1) + + MOVD $0, R5 // R5 = index (initialize early for tail path) + + // Check if we have at least 64 values to process + CMP $64, R1 + BLT neon1_tail_int64 + + // Round down to multiple of 64 for NEON processing + MOVD R1, R4 + LSR $6, R4, R4 // R4 = len / 64 + LSL $6, R4, R4 // R4 = aligned length (multiple of 64) + + // Load mask for 1 bit (0x01010101...) + MOVD $0x0101010101010101, R6 + VMOV R6, V31.D[0] + VMOV R6, V31.D[1] // V31 = mask for single bits + +neon1_loop_int64: + // Load 8 bytes (contains 64 x 1-bit values) + VLD1 (R2), [V0.B8] + + // Extract each bit position (8 separate streams) + VAND V31.B16, V0.B16, V1.B16 // V1 = bit 0 + + VUSHR $1, V0.B16, V2.B16 + VAND V31.B16, V2.B16, V2.B16 // V2 = bit 1 + + VUSHR $2, V0.B16, V3.B16 + VAND V31.B16, V3.B16, V3.B16 // V3 = bit 2 + + VUSHR $3, V0.B16, V4.B16 + VAND V31.B16, V4.B16, V4.B16 // V4 = bit 3 + + VUSHR $4, V0.B16, V5.B16 + VAND V31.B16, V5.B16, V5.B16 // V5 = bit 4 + + VUSHR $5, V0.B16, V6.B16 + VAND V31.B16, V6.B16, V6.B16 // V6 = bit 5 + + VUSHR $6, V0.B16, V7.B16 + VAND V31.B16, V7.B16, V7.B16 // V7 = bit 6 + + VUSHR $7, V0.B16, V8.B16 + VAND V31.B16, V8.B16, V8.B16 // V8 = bit 7 + + // Stage 1: ZIP pairs (8 streams → 4 streams of pairs) + VZIP1 V2.B8, V1.B8, V9.B8 // V9 = [bit0,bit1] interleaved + VZIP1 V4.B8, V3.B8, V10.B8 // V10 = [bit2,bit3] interleaved + VZIP1 V6.B8, V5.B8, V11.B8 // V11 = [bit4,bit5] interleaved + VZIP1 V8.B8, V7.B8, V12.B8 // V12 = [bit6,bit7] interleaved + + VZIP2 V2.B8, V1.B8, V13.B8 // V13 = [bit0,bit1] upper half + VZIP2 V4.B8, V3.B8, V14.B8 // V14 = [bit2,bit3] upper half + VZIP2 V6.B8, V5.B8, V15.B8 // V15 = [bit4,bit5] upper half + VZIP2 V8.B8, V7.B8, V16.B8 // V16 = [bit6,bit7] upper half + + // Stage 2: ZIP quads (4 streams → 2 streams of quads) + VZIP1 V10.H4, V9.H4, V17.H4 // V17 = [0,1,2,3] interleaved + VZIP1 V12.H4, V11.H4, V18.H4 // V18 = [4,5,6,7] interleaved + VZIP2 V10.H4, V9.H4, V19.H4 // V19 = [0,1,2,3] next + VZIP2 V12.H4, V11.H4, V20.H4 // V20 = [4,5,6,7] next + + VZIP1 V14.H4, V13.H4, V21.H4 // V21 = upper [0,1,2,3] + VZIP1 V16.H4, V15.H4, V22.H4 // V22 = upper [4,5,6,7] + VZIP2 V14.H4, V13.H4, V23.H4 // V23 = upper [0,1,2,3] next + VZIP2 V16.H4, V15.H4, V24.H4 // V24 = upper [4,5,6,7] next + + // Stage 3: ZIP octets (2 streams → fully sequential) + VZIP1 V18.S2, V17.S2, V25.S2 // V25 = values 0-7 + VZIP2 V18.S2, V17.S2, V26.S2 // V26 = values 8-15 + VZIP1 V20.S2, V19.S2, V27.S2 // V27 = values 16-23 + VZIP2 V20.S2, V19.S2, V28.S2 // V28 = values 24-31 + VZIP1 V22.S2, V21.S2, V1.S2 // V1 = values 32-39 + VZIP2 V22.S2, V21.S2, V2.S2 // V2 = values 40-47 + VZIP1 V24.S2, V23.S2, V3.S2 // V3 = values 48-55 + VZIP2 V24.S2, V23.S2, V4.S2 // V4 = values 56-63 + + // Widen to int64 and store - each group of 8 values + // Values 0-7 + USHLL_8H_8B(5, 25) + USHLL_4S_4H(6, 5) + USHLL2_4S_8H(7, 5) + USHLL_2D_2S(8, 6) + USHLL2_2D_4S(9, 6) + USHLL_2D_2S(10, 7) + USHLL2_2D_4S(11, 7) + VST1 [V8.D2, V9.D2], (R0) + ADD $32, R0, R0 + VST1 [V10.D2, V11.D2], (R0) + ADD $32, R0, R0 + + // Values 8-15 + USHLL_8H_8B(5, 26) + USHLL_4S_4H(6, 5) + USHLL2_4S_8H(7, 5) + USHLL_2D_2S(8, 6) + USHLL2_2D_4S(9, 6) + USHLL_2D_2S(10, 7) + USHLL2_2D_4S(11, 7) + VST1 [V8.D2, V9.D2], (R0) + ADD $32, R0, R0 + VST1 [V10.D2, V11.D2], (R0) + ADD $32, R0, R0 + + // Values 16-23 + USHLL_8H_8B(5, 27) + USHLL_4S_4H(6, 5) + USHLL2_4S_8H(7, 5) + USHLL_2D_2S(8, 6) + USHLL2_2D_4S(9, 6) + USHLL_2D_2S(10, 7) + USHLL2_2D_4S(11, 7) + VST1 [V8.D2, V9.D2], (R0) + ADD $32, R0, R0 + VST1 [V10.D2, V11.D2], (R0) + ADD $32, R0, R0 + + // Values 24-31 + USHLL_8H_8B(5, 28) + USHLL_4S_4H(6, 5) + USHLL2_4S_8H(7, 5) + USHLL_2D_2S(8, 6) + USHLL2_2D_4S(9, 6) + USHLL_2D_2S(10, 7) + USHLL2_2D_4S(11, 7) + VST1 [V8.D2, V9.D2], (R0) + ADD $32, R0, R0 + VST1 [V10.D2, V11.D2], (R0) + ADD $32, R0, R0 + + // Values 32-39 + USHLL_8H_8B(5, 1) + USHLL_4S_4H(6, 5) + USHLL2_4S_8H(7, 5) + USHLL_2D_2S(8, 6) + USHLL2_2D_4S(9, 6) + USHLL_2D_2S(10, 7) + USHLL2_2D_4S(11, 7) + VST1 [V8.D2, V9.D2], (R0) + ADD $32, R0, R0 + VST1 [V10.D2, V11.D2], (R0) + ADD $32, R0, R0 + + // Values 40-47 + USHLL_8H_8B(5, 2) + USHLL_4S_4H(6, 5) + USHLL2_4S_8H(7, 5) + USHLL_2D_2S(8, 6) + USHLL2_2D_4S(9, 6) + USHLL_2D_2S(10, 7) + USHLL2_2D_4S(11, 7) + VST1 [V8.D2, V9.D2], (R0) + ADD $32, R0, R0 + VST1 [V10.D2, V11.D2], (R0) + ADD $32, R0, R0 + + // Values 48-55 + USHLL_8H_8B(5, 3) + USHLL_4S_4H(6, 5) + USHLL2_4S_8H(7, 5) + USHLL_2D_2S(8, 6) + USHLL2_2D_4S(9, 6) + USHLL_2D_2S(10, 7) + USHLL2_2D_4S(11, 7) + VST1 [V8.D2, V9.D2], (R0) + ADD $32, R0, R0 + VST1 [V10.D2, V11.D2], (R0) + ADD $32, R0, R0 + + // Values 56-63 + USHLL_8H_8B(5, 4) + USHLL_4S_4H(6, 5) + USHLL2_4S_8H(7, 5) + USHLL_2D_2S(8, 6) + USHLL2_2D_4S(9, 6) + USHLL_2D_2S(10, 7) + USHLL2_2D_4S(11, 7) + VST1 [V8.D2, V9.D2], (R0) + ADD $32, R0, R0 + VST1 [V10.D2, V11.D2], (R0) + ADD $32, R0, R0 + + // Advance pointers + ADD $8, R2, R2 // src += 8 bytes + ADD $64, R5, R5 // index += 64 + + CMP R4, R5 + BLT neon1_loop_int64 + +neon1_tail_int64: + // Handle remaining elements with scalar fallback + CMP R1, R5 + BEQ neon1_done_int64 + + // Compute remaining elements + SUB R5, R1, R1 + + // Fall back to scalar unpack for tail + MOVD $1, R4 // bitMask = 1 + MOVD $0, R6 // bitOffset = 0 + MOVD $0, R7 // index = 0 + B neon1_scalar_test_int64 + +neon1_scalar_loop_int64: + MOVD R6, R8 + LSR $3, R8, R8 // byte_index = bitOffset / 8 + MOVBU (R2)(R8), R9 // Load byte + + MOVD R6, R10 + AND $7, R10, R10 // bit_offset = bitOffset % 8 + + LSR R10, R9, R9 // Shift right by bit offset + AND $1, R9, R9 // Mask to get bit + MOVD R9, (R0) // Store as int64 + + ADD $8, R0, R0 // dst++ + ADD $1, R6, R6 // bitOffset++ + ADD $1, R7, R7 // index++ + +neon1_scalar_test_int64: + CMP R1, R7 + BLT neon1_scalar_loop_int64 + +neon1_done_int64: + RET diff --git a/vendor/github.com/parquet-go/bitpack/unpack_int64_2bit_amd64.s b/vendor/github.com/parquet-go/bitpack/unpack_int64_2bit_amd64.s new file mode 100644 index 000000000..b0204e0e2 --- /dev/null +++ b/vendor/github.com/parquet-go/bitpack/unpack_int64_2bit_amd64.s @@ -0,0 +1,124 @@ +//go:build !purego + +#include "funcdata.h" +#include "textflag.h" + +// unpackInt64x2bitAVX2 implements optimized unpacking for bitWidth=2 using AVX2 +// Each byte contains 4 x 2-bit values, processes 8 values at a time +// +// func unpackInt64x2bitAVX2(dst []int64, src []byte, bitWidth uint) +TEXT ·unpackInt64x2bitAVX2(SB), NOSPLIT, $0-56 + MOVQ dst_base+0(FP), AX // AX = dst pointer + MOVQ dst_len+8(FP), DX // DX = dst length + MOVQ src_base+24(FP), BX // BX = src pointer + MOVQ bitWidth+48(FP), CX // CX = bitWidth (should be 2) + + // Check if we have at least 8 values to process + CMPQ DX, $8 + JB avx2_2bit_tail + + // Round down to multiple of 8 for AVX2 processing + MOVQ DX, DI + SHRQ $3, DI // DI = len / 8 + SHLQ $3, DI // DI = aligned length (multiple of 8) + XORQ SI, SI // SI = index + +avx2_2bit_loop: + // Load 2 bytes (contains 8 x 2-bit values) + MOVWQZX (BX), R8 + + // Extract each 2-bit value and store as int64 + // Value 0 (bits 0-1) + MOVQ R8, R9 + ANDQ $3, R9 + MOVQ R9, (AX) + + // Value 1 (bits 2-3) + MOVQ R8, R9 + SHRQ $2, R9 + ANDQ $3, R9 + MOVQ R9, 8(AX) + + // Value 2 (bits 4-5) + MOVQ R8, R9 + SHRQ $4, R9 + ANDQ $3, R9 + MOVQ R9, 16(AX) + + // Value 3 (bits 6-7) + MOVQ R8, R9 + SHRQ $6, R9 + ANDQ $3, R9 + MOVQ R9, 24(AX) + + // Value 4 (bits 8-9) + MOVQ R8, R9 + SHRQ $8, R9 + ANDQ $3, R9 + MOVQ R9, 32(AX) + + // Value 5 (bits 10-11) + MOVQ R8, R9 + SHRQ $10, R9 + ANDQ $3, R9 + MOVQ R9, 40(AX) + + // Value 6 (bits 12-13) + MOVQ R8, R9 + SHRQ $12, R9 + ANDQ $3, R9 + MOVQ R9, 48(AX) + + // Value 7 (bits 14-15) + MOVQ R8, R9 + SHRQ $14, R9 + ANDQ $3, R9 + MOVQ R9, 56(AX) + + // Advance pointers + ADDQ $2, BX // src += 2 bytes + ADDQ $64, AX // dst += 8 int64 (64 bytes) + ADDQ $8, SI // index += 8 + + CMPQ SI, DI + JNE avx2_2bit_loop + +avx2_2bit_tail: + // Handle remaining elements with scalar fallback + CMPQ SI, DX + JE avx2_2bit_done + + // Compute remaining elements + SUBQ SI, DX + + // Calculate bit offset for remaining elements + // Each processed element consumes 2 bits, so bitOffset = SI * 2 + MOVQ SI, R9 + SHLQ $1, R9 // bitOffset = SI * 2 + XORQ R10, R10 // index = 0 (within remaining elements) + JMP avx2_2bit_scalar_test + +avx2_2bit_scalar_loop: + MOVQ R9, R11 + SHRQ $3, R11 // byte_index = bitOffset / 8 + MOVQ src_base+24(FP), R14 // Get original src pointer + MOVBQZX (R14)(R11*1), R12 // Load byte from original src + + MOVQ R9, R13 + ANDQ $7, R13 // bit_offset = bitOffset % 8 + + MOVQ R13, CX // Move bit offset to CX for shift + SHRQ CL, R12 // Shift right by bit offset + ANDQ $3, R12 // Mask to get 2 bits + MOVQ R12, (AX) // Store as int64 + + ADDQ $8, AX // dst++ + ADDQ $2, R9 // bitOffset += 2 + ADDQ $1, R10 // index++ + +avx2_2bit_scalar_test: + CMPQ R10, DX + JNE avx2_2bit_scalar_loop + +avx2_2bit_done: + RET diff --git a/vendor/github.com/parquet-go/bitpack/unpack_int64_2bit_arm64.s b/vendor/github.com/parquet-go/bitpack/unpack_int64_2bit_arm64.s new file mode 100644 index 000000000..3b7ac7713 --- /dev/null +++ b/vendor/github.com/parquet-go/bitpack/unpack_int64_2bit_arm64.s @@ -0,0 +1,161 @@ +//go:build !purego + +#include "textflag.h" +#include "unpack_neon_macros_arm64.h" + +// unpackInt64x2bitNEON implements NEON unpacking for bitWidth=2 using direct bit manipulation +// Each byte contains 4 values of 2 bits each: [bits 6-7][bits 4-5][bits 2-3][bits 0-1] +// +// func unpackInt64x2bitNEON(dst []int64, src []byte, bitWidth uint) +TEXT ·unpackInt64x2bitNEON(SB), NOSPLIT, $0-56 + MOVD dst_base+0(FP), R0 // R0 = dst pointer + MOVD dst_len+8(FP), R1 // R1 = dst length + MOVD src_base+24(FP), R2 // R2 = src pointer + MOVD bitWidth+48(FP), R3 // R3 = bitWidth (should be 2) + + MOVD $0, R5 // R5 = index (initialize early for tail path) + + // Check if we have at least 32 values to process + CMP $32, R1 + BLT neon2_tail_int64 + + // Round down to multiple of 32 for NEON processing + MOVD R1, R4 + LSR $5, R4, R4 // R4 = len / 32 + LSL $5, R4, R4 // R4 = aligned length (multiple of 32) + + // Load mask for 2 bits (0x03030303...) + MOVD $0x0303030303030303, R6 + VMOV R6, V31.D[0] + VMOV R6, V31.D[1] // V31 = mask for 2-bit values + +neon2_loop_int64: + // Load 8 bytes (contains 32 x 2-bit values) + VLD1 (R2), [V0.B8] + + // Extract bits [1:0] from each byte (values at positions 0,4,8,12,...) + VAND V31.B16, V0.B16, V1.B16 + + // Extract bits [3:2] from each byte (values at positions 1,5,9,13,...) + VUSHR $2, V0.B16, V2.B16 + VAND V31.B16, V2.B16, V2.B16 + + // Extract bits [5:4] from each byte (values at positions 2,6,10,14,...) + VUSHR $4, V0.B16, V3.B16 + VAND V31.B16, V3.B16, V3.B16 + + // Extract bits [7:6] from each byte (values at positions 3,7,11,15,...) + VUSHR $6, V0.B16, V4.B16 + VAND V31.B16, V4.B16, V4.B16 + + // Use multiple ZIP stages to interleave + VZIP1 V2.B8, V1.B8, V5.B8 // V5 = [V1[0],V2[0],V1[1],V2[1],V1[2],V2[2],V1[3],V2[3]] + VZIP1 V4.B8, V3.B8, V6.B8 // V6 = [V3[0],V4[0],V3[1],V4[1],V3[2],V4[2],V3[3],V4[3]] + VZIP2 V2.B8, V1.B8, V7.B8 // V7 = [V1[4],V2[4],V1[5],V2[5],V1[6],V2[6],V1[7],V2[7]] + VZIP2 V4.B8, V3.B8, V8.B8 // V8 = [V3[4],V4[4],V3[5],V4[5],V3[6],V4[6],V3[7],V4[7]] + + // Now ZIP the pairs + VZIP1 V6.H4, V5.H4, V13.H4 // V13 = [V1[0],V2[0],V3[0],V4[0],V1[1],V2[1],V3[1],V4[1]] + VZIP2 V6.H4, V5.H4, V14.H4 // V14 = [V1[2],V2[2],V3[2],V4[2],V1[3],V2[3],V3[3],V4[3]] + VZIP1 V8.H4, V7.H4, V15.H4 // V15 = [V1[4],V2[4],V3[4],V4[4],V1[5],V2[5],V3[5],V4[5]] + VZIP2 V8.H4, V7.H4, V16.H4 // V16 = [V1[6],V2[6],V3[6],V4[6],V1[7],V2[7],V3[7],V4[7]] + + // Widen first 8 values (V13) to int64 + USHLL_8H_8B(17, 13) // V17.8H ← V13.8B + USHLL_4S_4H(18, 17) // V18.4S ← V17.4H + USHLL2_4S_8H(19, 17) // V19.4S ← V17.8H + USHLL_2D_2S(20, 18) // V20.2D ← V18.2S (values 0-1) + USHLL2_2D_4S(21, 18) // V21.2D ← V18.4S (values 2-3) + USHLL_2D_2S(22, 19) // V22.2D ← V19.2S (values 4-5) + USHLL2_2D_4S(23, 19) // V23.2D ← V19.4S (values 6-7) + + // Widen second 8 values (V14) to int64 + USHLL_8H_8B(24, 14) // V24.8H ← V14.8B + USHLL_4S_4H(25, 24) // V25.4S ← V24.4H + USHLL2_4S_8H(26, 24) // V26.4S ← V24.8H + USHLL_2D_2S(27, 25) // V27.2D ← V25.2S (values 8-9) + USHLL2_2D_4S(28, 25) // V28.2D ← V25.4S (values 10-11) + USHLL_2D_2S(29, 26) // V29.2D ← V26.2S (values 12-13) + USHLL2_2D_4S(30, 26) // V30.2D ← V26.4S (values 14-15) + + // Store first 16 int64 values (128 bytes) + VST1 [V20.D2, V21.D2], (R0) + ADD $32, R0, R0 + VST1 [V22.D2, V23.D2], (R0) + ADD $32, R0, R0 + VST1 [V27.D2, V28.D2], (R0) + ADD $32, R0, R0 + VST1 [V29.D2, V30.D2], (R0) + ADD $32, R0, R0 + + // Widen third 8 values (V15) to int64 + USHLL_8H_8B(17, 15) // V17.8H ← V15.8B (reuse V17) + USHLL_4S_4H(18, 17) // V18.4S ← V17.4H + USHLL2_4S_8H(19, 17) // V19.4S ← V17.8H + USHLL_2D_2S(20, 18) // V20.2D ← V18.2S (values 16-17) + USHLL2_2D_4S(21, 18) // V21.2D ← V18.4S (values 18-19) + USHLL_2D_2S(22, 19) // V22.2D ← V19.2S (values 20-21) + USHLL2_2D_4S(23, 19) // V23.2D ← V19.4S (values 22-23) + + // Widen fourth 8 values (V16) to int64 + USHLL_8H_8B(24, 16) // V24.8H ← V16.8B (reuse V24) + USHLL_4S_4H(25, 24) // V25.4S ← V24.4H + USHLL2_4S_8H(26, 24) // V26.4S ← V24.8H + USHLL_2D_2S(27, 25) // V27.2D ← V25.2S (values 24-25) + USHLL2_2D_4S(28, 25) // V28.2D ← V25.4S (values 26-27) + USHLL_2D_2S(29, 26) // V29.2D ← V26.2S (values 28-29) + USHLL2_2D_4S(30, 26) // V30.2D ← V26.4S (values 30-31) + + // Store second 16 int64 values (128 bytes) + VST1 [V20.D2, V21.D2], (R0) + ADD $32, R0, R0 + VST1 [V22.D2, V23.D2], (R0) + ADD $32, R0, R0 + VST1 [V27.D2, V28.D2], (R0) + ADD $32, R0, R0 + VST1 [V29.D2, V30.D2], (R0) + ADD $32, R0, R0 + + // Advance pointers + ADD $8, R2, R2 // src += 8 bytes + ADD $32, R5, R5 // index += 32 + + CMP R4, R5 + BLT neon2_loop_int64 + +neon2_tail_int64: + // Handle remaining elements with scalar fallback + CMP R1, R5 + BEQ neon2_done_int64 + + // Compute remaining elements + SUB R5, R1, R1 + + // Fall back to scalar unpack for tail + MOVD $3, R4 // bitMask = 3 (0b11 for 2 bits) + MOVD $0, R6 // bitOffset = 0 + MOVD $0, R7 // index = 0 + B neon2_scalar_test_int64 + +neon2_scalar_loop_int64: + MOVD R6, R8 + LSR $3, R8, R8 // byte_index = bitOffset / 8 + MOVBU (R2)(R8), R9 // Load byte + + MOVD R6, R10 + AND $7, R10, R10 // bit_offset = bitOffset % 8 + + LSR R10, R9, R9 // Shift right by bit offset + AND $3, R9, R9 // Mask to get 2 bits + MOVD R9, (R0) // Store as int64 + + ADD $8, R0, R0 // dst++ + ADD $2, R6, R6 // bitOffset += 2 + ADD $1, R7, R7 // index++ + +neon2_scalar_test_int64: + CMP R1, R7 + BLT neon2_scalar_loop_int64 + +neon2_done_int64: + RET diff --git a/vendor/github.com/parquet-go/bitpack/unpack_int64_4bit_amd64.s b/vendor/github.com/parquet-go/bitpack/unpack_int64_4bit_amd64.s new file mode 100644 index 000000000..ab6c81cd1 --- /dev/null +++ b/vendor/github.com/parquet-go/bitpack/unpack_int64_4bit_amd64.s @@ -0,0 +1,124 @@ +//go:build !purego + +#include "funcdata.h" +#include "textflag.h" + +// unpackInt64x4bitAVX2 implements optimized unpacking for bitWidth=4 using AVX2 +// Each byte contains 2 x 4-bit values, processes 8 values at a time +// +// func unpackInt64x4bitAVX2(dst []int64, src []byte, bitWidth uint) +TEXT ·unpackInt64x4bitAVX2(SB), NOSPLIT, $0-56 + MOVQ dst_base+0(FP), AX // AX = dst pointer + MOVQ dst_len+8(FP), DX // DX = dst length + MOVQ src_base+24(FP), BX // BX = src pointer + MOVQ bitWidth+48(FP), CX // CX = bitWidth (should be 4) + + // Check if we have at least 8 values to process + CMPQ DX, $8 + JB avx2_4bit_tail + + // Round down to multiple of 8 for AVX2 processing + MOVQ DX, DI + SHRQ $3, DI // DI = len / 8 + SHLQ $3, DI // DI = aligned length (multiple of 8) + XORQ SI, SI // SI = index + +avx2_4bit_loop: + // Load 4 bytes (contains 8 x 4-bit values) + MOVLQZX (BX), R8 + + // Extract each 4-bit value and store as int64 + // Value 0 (bits 0-3) + MOVQ R8, R9 + ANDQ $0xF, R9 + MOVQ R9, (AX) + + // Value 1 (bits 4-7) + MOVQ R8, R9 + SHRQ $4, R9 + ANDQ $0xF, R9 + MOVQ R9, 8(AX) + + // Value 2 (bits 8-11) + MOVQ R8, R9 + SHRQ $8, R9 + ANDQ $0xF, R9 + MOVQ R9, 16(AX) + + // Value 3 (bits 12-15) + MOVQ R8, R9 + SHRQ $12, R9 + ANDQ $0xF, R9 + MOVQ R9, 24(AX) + + // Value 4 (bits 16-19) + MOVQ R8, R9 + SHRQ $16, R9 + ANDQ $0xF, R9 + MOVQ R9, 32(AX) + + // Value 5 (bits 20-23) + MOVQ R8, R9 + SHRQ $20, R9 + ANDQ $0xF, R9 + MOVQ R9, 40(AX) + + // Value 6 (bits 24-27) + MOVQ R8, R9 + SHRQ $24, R9 + ANDQ $0xF, R9 + MOVQ R9, 48(AX) + + // Value 7 (bits 28-31) + MOVQ R8, R9 + SHRQ $28, R9 + ANDQ $0xF, R9 + MOVQ R9, 56(AX) + + // Advance pointers + ADDQ $4, BX // src += 4 bytes + ADDQ $64, AX // dst += 8 int64 (64 bytes) + ADDQ $8, SI // index += 8 + + CMPQ SI, DI + JNE avx2_4bit_loop + +avx2_4bit_tail: + // Handle remaining elements with scalar fallback + CMPQ SI, DX + JE avx2_4bit_done + + // Compute remaining elements + SUBQ SI, DX + + // Calculate bit offset for remaining elements + // Each processed element consumes 4 bits, so bitOffset = SI * 4 + MOVQ SI, R9 + SHLQ $2, R9 // bitOffset = SI * 4 + XORQ R10, R10 // index = 0 (within remaining elements) + JMP avx2_4bit_scalar_test + +avx2_4bit_scalar_loop: + MOVQ R9, R11 + SHRQ $3, R11 // byte_index = bitOffset / 8 + MOVQ src_base+24(FP), R14 // Get original src pointer + MOVBQZX (R14)(R11*1), R12 // Load byte from original src + + MOVQ R9, R13 + ANDQ $7, R13 // bit_offset = bitOffset % 8 + + MOVQ R13, CX // Move bit offset to CX for shift + SHRQ CL, R12 // Shift right by bit offset + ANDQ $0xF, R12 // Mask to get 4 bits + MOVQ R12, (AX) // Store as int64 + + ADDQ $8, AX // dst++ + ADDQ $4, R9 // bitOffset += 4 + ADDQ $1, R10 // index++ + +avx2_4bit_scalar_test: + CMPQ R10, DX + JNE avx2_4bit_scalar_loop + +avx2_4bit_done: + RET diff --git a/vendor/github.com/parquet-go/bitpack/unpack_int64_4bit_arm64.s b/vendor/github.com/parquet-go/bitpack/unpack_int64_4bit_arm64.s new file mode 100644 index 000000000..671b6ea0b --- /dev/null +++ b/vendor/github.com/parquet-go/bitpack/unpack_int64_4bit_arm64.s @@ -0,0 +1,118 @@ +//go:build !purego + +#include "textflag.h" +#include "unpack_neon_macros_arm64.h" + +// unpackInt64x4bitNEON implements NEON unpacking for bitWidth=4 using direct bit manipulation +// Each byte contains 2 values of 4 bits each +// +// func unpackInt64x4bitNEON(dst []int64, src []byte, bitWidth uint) +TEXT ·unpackInt64x4bitNEON(SB), NOSPLIT, $0-56 + MOVD dst_base+0(FP), R0 // R0 = dst pointer + MOVD dst_len+8(FP), R1 // R1 = dst length + MOVD src_base+24(FP), R2 // R2 = src pointer + MOVD bitWidth+48(FP), R3 // R3 = bitWidth (should be 4) + + MOVD $0, R5 // R5 = index (initialize early for tail path) + + // Check if we have at least 16 values to process + CMP $16, R1 + BLT neon4_tail + + // Round down to multiple of 16 for NEON processing + MOVD R1, R4 + LSR $4, R4, R4 // R4 = len / 16 + LSL $4, R4, R4 // R4 = aligned length (multiple of 16) + + // Load mask for 4 bits (0x0F0F0F0F...) + MOVD $0x0F0F0F0F0F0F0F0F, R6 + VMOV R6, V31.D[0] + VMOV R6, V31.D[1] // V31 = mask for low nibbles + +neon4_loop: + // Load 8 bytes (contains 16 x 4-bit values) + VLD1 (R2), [V0.B8] + + // Extract low nibbles (values at even nibble positions) + VAND V31.B16, V0.B16, V1.B16 // V1 = low nibbles + + // Extract high nibbles (values at odd nibble positions) + VUSHR $4, V0.B16, V2.B16 // V2 = high nibbles (shifted down) + VAND V31.B16, V2.B16, V2.B16 // V2 = high nibbles (masked) + + // Now V1 has values [0,2,4,6,8,10,12,14] and V2 has [1,3,5,7,9,11,13,15] + // We need to interleave them: [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15] + VZIP1 V2.B8, V1.B8, V3.B8 // V3 = interleaved low half (values 0-7) + VZIP2 V2.B8, V1.B8, V4.B8 // V4 = interleaved high half (values 8-15) + + // Widen first 8 values (V3) to int64 + USHLL_8H_8B(5, 3) // V5.8H ← V3.8B + USHLL_4S_4H(6, 5) // V6.4S ← V5.4H + USHLL2_4S_8H(7, 5) // V7.4S ← V5.8H + USHLL_2D_2S(8, 6) // V8.2D ← V6.2S (values 0-1) + USHLL2_2D_4S(9, 6) // V9.2D ← V6.4S (values 2-3) + USHLL_2D_2S(10, 7) // V10.2D ← V7.2S (values 4-5) + USHLL2_2D_4S(11, 7) // V11.2D ← V7.4S (values 6-7) + + // Widen second 8 values (V4) to int64 + USHLL_8H_8B(12, 4) // V12.8H ← V4.8B + USHLL_4S_4H(13, 12) // V13.4S ← V12.4H + USHLL2_4S_8H(14, 12) // V14.4S ← V12.8H + USHLL_2D_2S(15, 13) // V15.2D ← V13.2S (values 8-9) + USHLL2_2D_4S(16, 13) // V16.2D ← V13.4S (values 10-11) + USHLL_2D_2S(17, 14) // V17.2D ← V14.2S (values 12-13) + USHLL2_2D_4S(18, 14) // V18.2D ← V14.4S (values 14-15) + + // Store 16 int64 values (128 bytes) + VST1 [V8.D2, V9.D2], (R0) + ADD $32, R0, R0 + VST1 [V10.D2, V11.D2], (R0) + ADD $32, R0, R0 + VST1 [V15.D2, V16.D2], (R0) + ADD $32, R0, R0 + VST1 [V17.D2, V18.D2], (R0) + ADD $32, R0, R0 + + // Advance pointers + ADD $8, R2, R2 // src += 8 bytes + ADD $16, R5, R5 // index += 16 + + CMP R4, R5 + BLT neon4_loop + +neon4_tail: + // Handle remaining elements with scalar fallback + CMP R1, R5 + BEQ neon4_done + + // Compute remaining elements + SUB R5, R1, R1 + + // Fall back to scalar unpack for tail + MOVD $0x0F, R4 // bitMask = 0x0F (4 bits) + MOVD $0, R6 // bitOffset = 0 (start from current R2 position) + MOVD $0, R7 // loop counter = 0 + B neon4_scalar_test + +neon4_scalar_loop: + MOVD R6, R8 + LSR $3, R8, R8 // byte_index = bitOffset / 8 + MOVBU (R2)(R8), R9 // Load byte from current position + + MOVD R6, R10 + AND $7, R10, R10 // bit_offset = bitOffset % 8 + + LSR R10, R9, R9 // Shift right by bit offset + AND $0x0F, R9, R9 // Mask to get 4 bits + MOVD R9, (R0) // Store as int64 + + ADD $8, R0, R0 // dst++ + ADD $4, R6, R6 // bitOffset += 4 + ADD $1, R7, R7 // counter++ + +neon4_scalar_test: + CMP R1, R7 + BLT neon4_scalar_loop + +neon4_done: + RET diff --git a/vendor/github.com/parquet-go/bitpack/unpack_int64_8bit_amd64.s b/vendor/github.com/parquet-go/bitpack/unpack_int64_8bit_amd64.s new file mode 100644 index 000000000..090447f6f --- /dev/null +++ b/vendor/github.com/parquet-go/bitpack/unpack_int64_8bit_amd64.s @@ -0,0 +1,105 @@ +//go:build !purego + +#include "funcdata.h" +#include "textflag.h" + +// unpackInt64x8bitAVX2 implements optimized unpacking for bitWidth=8 using AVX2 +// Each byte is already a complete value, processes 8 values at a time +// +// func unpackInt64x8bitAVX2(dst []int64, src []byte, bitWidth uint) +TEXT ·unpackInt64x8bitAVX2(SB), NOSPLIT, $0-56 + MOVQ dst_base+0(FP), AX // AX = dst pointer + MOVQ dst_len+8(FP), DX // DX = dst length + MOVQ src_base+24(FP), BX // BX = src pointer + MOVQ bitWidth+48(FP), CX // CX = bitWidth (should be 8) + + // Check if we have at least 8 values to process + CMPQ DX, $8 + JB avx2_8bit_tail + + // Round down to multiple of 8 for AVX2 processing + MOVQ DX, DI + SHRQ $3, DI // DI = len / 8 + SHLQ $3, DI // DI = aligned length (multiple of 8) + XORQ SI, SI // SI = index + +avx2_8bit_loop: + // Load 8 bytes (8 x 8-bit values) + MOVQ (BX), R8 + + // Extract each byte and store as int64 + // Value 0 (byte 0) + MOVQ R8, R9 + ANDQ $0xFF, R9 + MOVQ R9, (AX) + + // Value 1 (byte 1) + MOVQ R8, R9 + SHRQ $8, R9 + ANDQ $0xFF, R9 + MOVQ R9, 8(AX) + + // Value 2 (byte 2) + MOVQ R8, R9 + SHRQ $16, R9 + ANDQ $0xFF, R9 + MOVQ R9, 16(AX) + + // Value 3 (byte 3) + MOVQ R8, R9 + SHRQ $24, R9 + ANDQ $0xFF, R9 + MOVQ R9, 24(AX) + + // Value 4 (byte 4) + MOVQ R8, R9 + SHRQ $32, R9 + ANDQ $0xFF, R9 + MOVQ R9, 32(AX) + + // Value 5 (byte 5) + MOVQ R8, R9 + SHRQ $40, R9 + ANDQ $0xFF, R9 + MOVQ R9, 40(AX) + + // Value 6 (byte 6) + MOVQ R8, R9 + SHRQ $48, R9 + ANDQ $0xFF, R9 + MOVQ R9, 48(AX) + + // Value 7 (byte 7) + MOVQ R8, R9 + SHRQ $56, R9 + MOVQ R9, 56(AX) + + // Advance pointers + ADDQ $8, BX // src += 8 bytes + ADDQ $64, AX // dst += 8 int64 (64 bytes) + ADDQ $8, SI // index += 8 + + CMPQ SI, DI + JNE avx2_8bit_loop + +avx2_8bit_tail: + // Handle remaining elements with scalar fallback + CMPQ SI, DX + JE avx2_8bit_done + + // Compute remaining elements + SUBQ SI, DX + +avx2_8bit_tail_loop: + MOVBQZX (BX), R8 // Load byte + MOVQ R8, (AX) // Store as int64 (zero-extended) + + ADDQ $1, BX // src++ + ADDQ $8, AX // dst++ + DECQ DX // remaining-- + + CMPQ DX, $0 + JNE avx2_8bit_tail_loop + +avx2_8bit_done: + RET diff --git a/vendor/github.com/parquet-go/bitpack/unpack_int64_8bit_arm64.s b/vendor/github.com/parquet-go/bitpack/unpack_int64_8bit_arm64.s new file mode 100644 index 000000000..2e8a4e479 --- /dev/null +++ b/vendor/github.com/parquet-go/bitpack/unpack_int64_8bit_arm64.s @@ -0,0 +1,71 @@ +//go:build !purego + +#include "textflag.h" +#include "unpack_neon_macros_arm64.h" + +// unpackInt64x8bitNEON implements NEON unpacking for bitWidth=8 +// Each byte is already a complete value - just widen to int64 +// Processes 8 values at a time using NEON +// +// func unpackInt64x8bitNEON(dst []int64, src []byte, bitWidth uint) +TEXT ·unpackInt64x8bitNEON(SB), NOSPLIT, $0-56 + MOVD dst_base+0(FP), R0 // R0 = dst pointer + MOVD dst_len+8(FP), R1 // R1 = dst length + MOVD src_base+24(FP), R2 // R2 = src pointer + MOVD bitWidth+48(FP), R3 // R3 = bitWidth (should be 8) + + MOVD $0, R5 // R5 = index + + // Check if we have at least 8 values to process + CMP $8, R1 + BLT tbl8_tail + + // Round down to multiple of 8 for NEON processing + MOVD R1, R4 + LSR $3, R4, R4 // R4 = len / 8 + LSL $3, R4, R4 // R4 = aligned length (multiple of 8) + +tbl8_loop: + // Load 8 bytes (8 x 8-bit values) + VLD1 (R2), [V0.B8] + + // Widen to int64: byte → short → int → long + USHLL_8H_8B(1, 0) // V1.8H ← V0.8B (8x8-bit → 8x16-bit) + USHLL_4S_4H(2, 1) // V2.4S ← V1.4H (lower 4x16-bit → 4x32-bit) + USHLL2_4S_8H(3, 1) // V3.4S ← V1.8H (upper 4x16-bit → 4x32-bit) + USHLL_2D_2S(4, 2) // V4.2D ← V2.2S (lower 2x32-bit → 2x64-bit) + USHLL2_2D_4S(5, 2) // V5.2D ← V2.4S (upper 2x32-bit → 2x64-bit) + USHLL_2D_2S(6, 3) // V6.2D ← V3.2S (lower 2x32-bit → 2x64-bit) + USHLL2_2D_4S(7, 3) // V7.2D ← V3.4S (upper 2x32-bit → 2x64-bit) + + // Store 8 int64 values (64 bytes) + VST1 [V4.D2, V5.D2], (R0) + ADD $32, R0, R11 // Temporary pointer for second store + VST1 [V6.D2, V7.D2], (R11) + + // Advance pointers + ADD $8, R2, R2 // src += 8 bytes + ADD $64, R0, R0 // dst += 8 int64 (64 bytes) + ADD $8, R5, R5 // index += 8 + + CMP R4, R5 + BLT tbl8_loop + +tbl8_tail: + // Handle remaining elements (0-7) one by one + CMP R1, R5 + BGE tbl8_done + +tbl8_tail_loop: + MOVBU (R2), R6 // Load byte + MOVD R6, (R0) // Store as int64 (zero-extended) + + ADD $1, R2, R2 // src++ + ADD $8, R0, R0 // dst++ + ADD $1, R5, R5 // index++ + + CMP R1, R5 + BLT tbl8_tail_loop + +tbl8_done: + RET diff --git a/vendor/github.com/parquet-go/bitpack/unpack_int64_amd64.go b/vendor/github.com/parquet-go/bitpack/unpack_int64_amd64.go new file mode 100644 index 000000000..d244f2b42 --- /dev/null +++ b/vendor/github.com/parquet-go/bitpack/unpack_int64_amd64.go @@ -0,0 +1,38 @@ +//go:build !purego + +package bitpack + +import ( + "github.com/parquet-go/bitpack/unsafecast" + "golang.org/x/sys/cpu" +) + +//go:noescape +func unpackInt64Default(dst []int64, src []byte, bitWidth uint) + +//go:noescape +func unpackInt64x1to32bitsAVX2(dst []int64, src []byte, bitWidth uint) + +//go:noescape +func unpackInt64x1bitAVX2(dst []int64, src []byte, bitWidth uint) + +//go:noescape +func unpackInt64x2bitAVX2(dst []int64, src []byte, bitWidth uint) + +//go:noescape +func unpackInt64x4bitAVX2(dst []int64, src []byte, bitWidth uint) + +//go:noescape +func unpackInt64x8bitAVX2(dst []int64, src []byte, bitWidth uint) + +func unpackInt64(dst []int64, src []byte, bitWidth uint) { + hasAVX2 := cpu.X86.HasAVX2 + switch { + case hasAVX2 && bitWidth <= 32: + unpackInt64x1to32bitsAVX2(dst, src, bitWidth) + case bitWidth == 64: + copy(dst, unsafecast.Slice[int64](src)) + default: + unpackInt64Default(dst, src, bitWidth) + } +} diff --git a/vendor/github.com/parquet-go/bitpack/unpack_int64_amd64.s b/vendor/github.com/parquet-go/bitpack/unpack_int64_amd64.s new file mode 100644 index 000000000..6c07ca5c9 --- /dev/null +++ b/vendor/github.com/parquet-go/bitpack/unpack_int64_amd64.s @@ -0,0 +1,824 @@ +//go:build !purego + +#include "funcdata.h" +#include "textflag.h" + +// func unpackInt64Default(dst []int64, src []uint32, bitWidth uint) +TEXT ·unpackInt64Default(SB), NOSPLIT, $0-56 + MOVQ dst_base+0(FP), AX + MOVQ dst_len+8(FP), DX + MOVQ src_base+24(FP), BX + MOVQ bitWidth+48(FP), CX + + // Initialize + XORQ DI, DI // bitOffset + XORQ SI, SI // index + + // Check if length >= 4 for unrolled loop + CMPQ DX, $4 + JB scalar_loop_start + + // Calculate bitMask = (1 << bitWidth) - 1 + MOVQ $1, R8 + SHLQ CX, R8 + DECQ R8 + + // Calculate unrolled iterations: (length / 4) * 4 + MOVQ DX, R15 + SHRQ $2, R15 // R15 = length / 4 + JZ scalar_loop_start + SHLQ $2, R15 // R15 = (length / 4) * 4 + +unrolled_loop: + // Process 4 elements with 64-bit loads + + // === Element 0 === + MOVQ DI, R10 + SHRQ $6, R10 // i = bitOffset / 64 + MOVQ DI, R9 + ANDQ $63, R9 // j = bitOffset % 64 + MOVQ (BX)(R10*8), R11 // load 64-bit word + MOVQ R8, R12 + MOVQ R9, CX + SHLQ CL, R12 + ANDQ R12, R11 + SHRQ CL, R11 + + // Check if spans to next word + MOVQ R9, R13 + ADDQ bitWidth+48(FP), R13 + CMPQ R13, $64 + JBE store0 + MOVQ $64, CX + SUBQ R9, CX + MOVQ 8(BX)(R10*8), R14 + MOVQ R8, R12 + SHRQ CL, R12 + ANDQ R12, R14 + SHLQ CL, R14 + ORQ R14, R11 + +store0: + ADDQ bitWidth+48(FP), DI + MOVQ R11, (AX)(SI*8) + INCQ SI + + // === Element 1 === + MOVQ DI, R10 + SHRQ $6, R10 + MOVQ DI, R9 + ANDQ $63, R9 + MOVQ (BX)(R10*8), R11 + MOVQ R8, R12 + MOVQ R9, CX + SHLQ CL, R12 + ANDQ R12, R11 + SHRQ CL, R11 + + MOVQ R9, R13 + ADDQ bitWidth+48(FP), R13 + CMPQ R13, $64 + JBE store1 + MOVQ $64, CX + SUBQ R9, CX + MOVQ 8(BX)(R10*8), R14 + MOVQ R8, R12 + SHRQ CL, R12 + ANDQ R12, R14 + SHLQ CL, R14 + ORQ R14, R11 + +store1: + ADDQ bitWidth+48(FP), DI + MOVQ R11, (AX)(SI*8) + INCQ SI + + // === Element 2 === + MOVQ DI, R10 + SHRQ $6, R10 + MOVQ DI, R9 + ANDQ $63, R9 + MOVQ (BX)(R10*8), R11 + MOVQ R8, R12 + MOVQ R9, CX + SHLQ CL, R12 + ANDQ R12, R11 + SHRQ CL, R11 + + MOVQ R9, R13 + ADDQ bitWidth+48(FP), R13 + CMPQ R13, $64 + JBE store2 + MOVQ $64, CX + SUBQ R9, CX + MOVQ 8(BX)(R10*8), R14 + MOVQ R8, R12 + SHRQ CL, R12 + ANDQ R12, R14 + SHLQ CL, R14 + ORQ R14, R11 + +store2: + ADDQ bitWidth+48(FP), DI + MOVQ R11, (AX)(SI*8) + INCQ SI + + // === Element 3 === + MOVQ DI, R10 + SHRQ $6, R10 + MOVQ DI, R9 + ANDQ $63, R9 + MOVQ (BX)(R10*8), R11 + MOVQ R8, R12 + MOVQ R9, CX + SHLQ CL, R12 + ANDQ R12, R11 + SHRQ CL, R11 + + MOVQ R9, R13 + ADDQ bitWidth+48(FP), R13 + CMPQ R13, $64 + JBE store3 + MOVQ $64, CX + SUBQ R9, CX + MOVQ 8(BX)(R10*8), R14 + MOVQ R8, R12 + SHRQ CL, R12 + ANDQ R12, R14 + SHLQ CL, R14 + ORQ R14, R11 + +store3: + ADDQ bitWidth+48(FP), DI + MOVQ R11, (AX)(SI*8) + INCQ SI + + CMPQ SI, R15 + JB unrolled_loop + + // Check if done + CMPQ SI, DX + JE done + +scalar_loop_start: + // Fallback scalar loop for remaining elements + // Check if there are any elements to process + CMPQ SI, DX + JE done + + MOVQ bitWidth+48(FP), CX + MOVQ $1, R8 + SHLQ CX, R8 + DECQ R8 + +scalar_loop: + // i = bitOffset / 64 + MOVQ DI, R10 + SHRQ $6, R10 + + // j = bitOffset % 64 + MOVQ DI, R9 + ANDQ $63, R9 + + // Load 64-bit word and extract + MOVQ (BX)(R10*8), R11 + MOVQ R8, R12 + MOVQ R9, CX + SHLQ CL, R12 + ANDQ R12, R11 + SHRQ CL, R11 + + // Check for span + MOVQ R9, R13 + ADDQ bitWidth+48(FP), R13 + CMPQ R13, $64 + JBE scalar_next + + MOVQ $64, CX + SUBQ R9, CX + MOVQ 8(BX)(R10*8), R14 + MOVQ R8, R12 + SHRQ CL, R12 + ANDQ R12, R14 + SHLQ CL, R14 + ORQ R14, R11 + +scalar_next: + MOVQ R11, (AX)(SI*8) + ADDQ bitWidth+48(FP), DI + INCQ SI + CMPQ SI, DX + JNE scalar_loop + JMP done + +zero_fill: + // Fill output with zeros for bitWidth==0 + XORQ SI, SI // Initialize index + XORQ R8, R8 // Zero value +zero_loop: + CMPQ SI, DX + JE done + MOVQ R8, (AX)(SI*8) + INCQ SI + JMP zero_loop + +done: + RET + +// This bit unpacking function was inspired from the 32 bit version, but +// adapted to account for the fact that eight 64 bit values span across +// two YMM registers, and across lanes of YMM registers. +// +// Because of the two lanes of YMM registers, we cannot use the VPSHUFB +// instruction to dispatch bytes of the input to the registers. Instead we use +// the VPERMD instruction, which has higher latency but supports dispatching +// bytes across register lanes. Measurable throughput gains remain despite the +// algorithm running on a few more CPU cycles per loop. +// +// The initialization phase of this algorithm generates masks for +// permutations and shifts used to decode the bit-packed values. +// +// The permutation masks are written to Y7 and Y8, and contain the results +// of this formula: +// +// temp[i] = (bitWidth * i) / 32 +// mask[i] = temp[i] | ((temp[i] + 1) << 32) +// +// Since VPERMQ only supports reading the permutation combination from an +// immediate value, we use VPERMD and generate permutation for pairs of two +// consecutive 32 bit words, which is why we have the upper part of each 64 +// bit word set with (x+1)<<32. +// +// The masks for right shifts are written to Y5 and Y6, and computed with +// this formula: +// +// shift[i] = (bitWidth * i) - (32 * ((bitWidth * i) / 32)) +// +// The amount to shift by is the number of values previously unpacked, offseted +// by the byte count of 32 bit words that we read from first bits from. +// +// Technically the masks could be precomputed and declared in global tables; +// however, declaring masks for all bit width is tedious and makes code +// maintenance more costly for no measurable benefits on production workloads. +// +// unpackInt64x1to32bitsAVX2 implements optimized unpacking for bit widths 1-32 +// Uses specialized kernels for common bit widths with batched processing +// +// func unpackInt64x1to32bitsAVX2(dst []int64, src []byte, bitWidth uint) +TEXT ·unpackInt64x1to32bitsAVX2(SB), NOSPLIT, $56-56 + NO_LOCAL_POINTERS + MOVQ dst_base+0(FP), AX + MOVQ dst_len+8(FP), DX + MOVQ src_base+24(FP), BX + MOVQ bitWidth+48(FP), CX + + // Check if we have enough values for specialized kernels + // 1-bit needs 8, 2-bit needs 8, 4-bit needs 8, 8-bit needs 8 + // 16-bit needs 4, 32-bit needs 2 + CMPQ CX, $1 + JE check_1bit + CMPQ CX, $2 + JE check_2bit + CMPQ CX, $3 + JE check_3bit + CMPQ CX, $4 + JE check_4bit + CMPQ CX, $5 + JE check_5bit + CMPQ CX, $6 + JE check_6bit + CMPQ CX, $7 + JE check_7bit + CMPQ CX, $8 + JE check_8bit + CMPQ CX, $16 + JE check_16bit + CMPQ CX, $32 + JE check_32bit + + // For other bit widths, check if we have at least 8 values + CMPQ DX, $8 + JB tail + JMP generic_avx2 + +check_1bit: + CMPQ DX, $8 + JB tail + JMP int64_1bit + +check_2bit: + CMPQ DX, $8 + JB tail + JMP int64_2bit + +check_3bit: + CMPQ DX, $8 + JB tail + JMP int64_3bit + +check_4bit: + CMPQ DX, $8 + JB tail + JMP int64_4bit + +check_5bit: + CMPQ DX, $8 + JB tail + JMP int64_5bit + +check_6bit: + CMPQ DX, $8 + JB tail + JMP int64_6bit + +check_7bit: + CMPQ DX, $8 + JB tail + JMP int64_7bit + +check_8bit: + CMPQ DX, $8 + JB tail + JMP int64_8bit + +check_16bit: + CMPQ DX, $4 + JB tail + JMP int64_16bit + +check_32bit: + CMPQ DX, $2 + JB tail + JMP int64_32bit + + +int64_1bit: + // Call specialized 1-bit kernel + MOVQ AX, dst_base-56(SP) + MOVQ DX, dst_len-48(SP) + MOVQ BX, src_base-32(SP) + MOVQ CX, bitWidth-8(SP) + CALL ·unpackInt64x1bitAVX2(SB) + RET + +int64_2bit: + // Call specialized 2-bit kernel + MOVQ AX, dst_base-56(SP) + MOVQ DX, dst_len-48(SP) + MOVQ BX, src_base-32(SP) + MOVQ CX, bitWidth-8(SP) + CALL ·unpackInt64x2bitAVX2(SB) + RET + +int64_3bit: + // BitWidth 3: 8 int64 values packed in 3 bytes + CMPQ DX, $8 + JB tail + + MOVQ DX, DI + SHRQ $3, DI + SHLQ $3, DI + XORQ SI, SI + +int64_3bit_loop: + MOVLQZX (BX), R8 + + MOVQ R8, R9 + ANDQ $7, R9 + MOVQ R9, (AX) + + MOVQ R8, R9 + SHRQ $3, R9 + ANDQ $7, R9 + MOVQ R9, 8(AX) + + MOVQ R8, R9 + SHRQ $6, R9 + ANDQ $7, R9 + MOVQ R9, 16(AX) + + MOVQ R8, R9 + SHRQ $9, R9 + ANDQ $7, R9 + MOVQ R9, 24(AX) + + MOVQ R8, R9 + SHRQ $12, R9 + ANDQ $7, R9 + MOVQ R9, 32(AX) + + MOVQ R8, R9 + SHRQ $15, R9 + ANDQ $7, R9 + MOVQ R9, 40(AX) + + MOVQ R8, R9 + SHRQ $18, R9 + ANDQ $7, R9 + MOVQ R9, 48(AX) + + MOVQ R8, R9 + SHRQ $21, R9 + ANDQ $7, R9 + MOVQ R9, 56(AX) + + ADDQ $3, BX + ADDQ $64, AX + ADDQ $8, SI + + CMPQ SI, DI + JNE int64_3bit_loop + + CMPQ SI, DX + JE done + SUBQ SI, DX + JMP tail + +int64_4bit: + // Call specialized 4-bit kernel + MOVQ AX, dst_base-56(SP) + MOVQ DX, dst_len-48(SP) + MOVQ BX, src_base-32(SP) + MOVQ CX, bitWidth-8(SP) + CALL ·unpackInt64x4bitAVX2(SB) + RET + +int64_5bit: + // BitWidth 5: 8 int64 values packed in 5 bytes + CMPQ DX, $8 + JB tail + + MOVQ DX, DI + SHRQ $3, DI + SHLQ $3, DI + XORQ SI, SI + +int64_5bit_loop: + MOVQ (BX), R8 + + MOVQ R8, R9 + ANDQ $31, R9 + MOVQ R9, (AX) + + MOVQ R8, R9 + SHRQ $5, R9 + ANDQ $31, R9 + MOVQ R9, 8(AX) + + MOVQ R8, R9 + SHRQ $10, R9 + ANDQ $31, R9 + MOVQ R9, 16(AX) + + MOVQ R8, R9 + SHRQ $15, R9 + ANDQ $31, R9 + MOVQ R9, 24(AX) + + MOVQ R8, R9 + SHRQ $20, R9 + ANDQ $31, R9 + MOVQ R9, 32(AX) + + MOVQ R8, R9 + SHRQ $25, R9 + ANDQ $31, R9 + MOVQ R9, 40(AX) + + MOVQ R8, R9 + SHRQ $30, R9 + ANDQ $31, R9 + MOVQ R9, 48(AX) + + MOVQ R8, R9 + SHRQ $35, R9 + ANDQ $31, R9 + MOVQ R9, 56(AX) + + ADDQ $5, BX + ADDQ $64, AX + ADDQ $8, SI + + CMPQ SI, DI + JNE int64_5bit_loop + + CMPQ SI, DX + JE done + SUBQ SI, DX + JMP tail + +int64_6bit: + // BitWidth 6: 8 int64 values packed in 6 bytes + CMPQ DX, $8 + JB tail + + MOVQ DX, DI + SHRQ $3, DI + SHLQ $3, DI + XORQ SI, SI + +int64_6bit_loop: + MOVQ (BX), R8 + + MOVQ R8, R9 + ANDQ $63, R9 + MOVQ R9, (AX) + + MOVQ R8, R9 + SHRQ $6, R9 + ANDQ $63, R9 + MOVQ R9, 8(AX) + + MOVQ R8, R9 + SHRQ $12, R9 + ANDQ $63, R9 + MOVQ R9, 16(AX) + + MOVQ R8, R9 + SHRQ $18, R9 + ANDQ $63, R9 + MOVQ R9, 24(AX) + + MOVQ R8, R9 + SHRQ $24, R9 + ANDQ $63, R9 + MOVQ R9, 32(AX) + + MOVQ R8, R9 + SHRQ $30, R9 + ANDQ $63, R9 + MOVQ R9, 40(AX) + + MOVQ R8, R9 + SHRQ $36, R9 + ANDQ $63, R9 + MOVQ R9, 48(AX) + + MOVQ R8, R9 + SHRQ $42, R9 + ANDQ $63, R9 + MOVQ R9, 56(AX) + + ADDQ $6, BX + ADDQ $64, AX + ADDQ $8, SI + + CMPQ SI, DI + JNE int64_6bit_loop + + CMPQ SI, DX + JE done + SUBQ SI, DX + JMP tail + +int64_7bit: + // BitWidth 7: 8 int64 values packed in 7 bytes + CMPQ DX, $8 + JB tail + + MOVQ DX, DI + SHRQ $3, DI + SHLQ $3, DI + XORQ SI, SI + +int64_7bit_loop: + MOVQ (BX), R8 + + MOVQ R8, R9 + ANDQ $127, R9 + MOVQ R9, (AX) + + MOVQ R8, R9 + SHRQ $7, R9 + ANDQ $127, R9 + MOVQ R9, 8(AX) + + MOVQ R8, R9 + SHRQ $14, R9 + ANDQ $127, R9 + MOVQ R9, 16(AX) + + MOVQ R8, R9 + SHRQ $21, R9 + ANDQ $127, R9 + MOVQ R9, 24(AX) + + MOVQ R8, R9 + SHRQ $28, R9 + ANDQ $127, R9 + MOVQ R9, 32(AX) + + MOVQ R8, R9 + SHRQ $35, R9 + ANDQ $127, R9 + MOVQ R9, 40(AX) + + MOVQ R8, R9 + SHRQ $42, R9 + ANDQ $127, R9 + MOVQ R9, 48(AX) + + MOVQ R8, R9 + SHRQ $49, R9 + ANDQ $127, R9 + MOVQ R9, 56(AX) + + ADDQ $7, BX + ADDQ $64, AX + ADDQ $8, SI + + CMPQ SI, DI + JNE int64_7bit_loop + + CMPQ SI, DX + JE done + SUBQ SI, DX + JMP tail + +int64_8bit: + // Call specialized 8-bit kernel + MOVQ AX, dst_base-56(SP) + MOVQ DX, dst_len-48(SP) + MOVQ BX, src_base-32(SP) + MOVQ CX, bitWidth-8(SP) + CALL ·unpackInt64x8bitAVX2(SB) + RET + +int64_16bit: + // BitWidth 16: 4 int64 values packed in 8 bytes + // Process 4 values at a time + + MOVQ DX, DI + SHRQ $2, DI // DI = len / 4 + SHLQ $2, DI // DI = aligned length (multiple of 4) + XORQ SI, SI // SI = index + + CMPQ DI, $0 + JE tail + +int64_16bit_loop: + // Load 8 bytes as 4 uint16 values + MOVQ (BX), R8 + + // Extract 16-bit values and write as int64 + // Value 0 (bits 0-15) + MOVQ R8, R9 + ANDQ $0xFFFF, R9 + MOVQ R9, (AX) + + // Value 1 (bits 16-31) + MOVQ R8, R9 + SHRQ $16, R9 + ANDQ $0xFFFF, R9 + MOVQ R9, 8(AX) + + // Value 2 (bits 32-47) + MOVQ R8, R9 + SHRQ $32, R9 + ANDQ $0xFFFF, R9 + MOVQ R9, 16(AX) + + // Value 3 (bits 48-63) + MOVQ R8, R9 + SHRQ $48, R9 + MOVQ R9, 24(AX) + + // Advance pointers + ADDQ $8, BX // src += 8 bytes (4 values) + ADDQ $32, AX // dst += 4 int64 (32 bytes) + ADDQ $4, SI // index += 4 + + CMPQ SI, DI + JNE int64_16bit_loop + + // Handle tail with scalar + CMPQ SI, DX + JE done + + LEAQ (AX), AX // AX already points to correct position + SUBQ SI, DX + JMP tail + +int64_32bit: + // BitWidth 32: Each value is exactly 4 bytes + // Process values one at a time for simplicity + + MOVQ DX, DI // DI = total length + XORQ SI, SI // SI = index + + CMPQ DI, $0 + JE done + +int64_32bit_loop: + // Load 4 bytes as one uint32 value + MOVLQZX (BX), R8 // Load 32-bit value, zero-extend to 64-bit + MOVQ R8, (AX) // Store as int64 + + // Advance pointers + ADDQ $4, BX // src += 4 bytes (1 value) + ADDQ $8, AX // dst += 1 int64 (8 bytes) + ADDQ $1, SI // index += 1 + + CMPQ SI, DI + JNE int64_32bit_loop + + // All values processed + JMP done + +generic_avx2: + // Optimized AVX2 with reduced setup overhead + CMPQ DX, $8 + JB tail + + MOVQ DX, DI + SHRQ $3, DI + SHLQ $3, DI + XORQ SI, SI + + // Compute bitMask + MOVQ $1, R8 + SHLQ CX, R8 + DECQ R8 + MOVQ R8, X0 + VPBROADCASTQ X0, Y0 + + // Use pre-computed table for bitWidths 9-31 + CMPQ CX, $9 + JB compute_masks + CMPQ CX, $31 + JA compute_masks + + // Calculate table offset: (bitWidth - 9) * 128 + MOVQ CX, R9 + SUBQ $9, R9 + SHLQ $7, R9 // multiply by 128 + + // Load pre-computed permutation and shift masks from table + LEAQ ·permuteInt64Table(SB), R10 + VMOVDQU (R10)(R9*1), Y7 + VMOVDQU 32(R10)(R9*1), Y8 + VMOVDQU 64(R10)(R9*1), Y5 + VMOVDQU 96(R10)(R9*1), Y6 + JMP generic_loop + +compute_masks: + // Fallback: compute masks dynamically (original code) + VPCMPEQQ Y1, Y1, Y1 + VPSRLQ $63, Y1, Y1 + MOVQ CX, X2 + VPBROADCASTQ X2, Y2 + VMOVDQU range0n7<>+0(SB), Y3 + VMOVDQU range0n7<>+32(SB), Y4 + VPMULLD Y2, Y3, Y5 + VPMULLD Y2, Y4, Y6 + VPSRLQ $5, Y5, Y7 + VPSRLQ $5, Y6, Y8 + VPSLLQ $5, Y7, Y9 + VPSLLQ $5, Y8, Y10 + VPADDQ Y1, Y7, Y11 + VPADDQ Y1, Y8, Y12 + VPSLLQ $32, Y11, Y11 + VPSLLQ $32, Y12, Y12 + VPOR Y11, Y7, Y7 + VPOR Y12, Y8, Y8 + VPSUBQ Y9, Y5, Y5 + VPSUBQ Y10, Y6, Y6 + +generic_loop: + VMOVDQU (BX), Y1 + VPERMD Y1, Y7, Y2 + VPERMD Y1, Y8, Y3 + VPSRLVQ Y5, Y2, Y2 + VPSRLVQ Y6, Y3, Y3 + VPAND Y0, Y2, Y2 + VPAND Y0, Y3, Y3 + VMOVDQU Y2, (AX)(SI*8) + VMOVDQU Y3, 32(AX)(SI*8) + ADDQ CX, BX + ADDQ $8, SI + CMPQ SI, DI + JNE generic_loop + VZEROUPPER + + CMPQ SI, DX + JE done + LEAQ (AX)(SI*8), AX + SUBQ SI, DX +tail: + MOVQ AX, dst_base-56(SP) + MOVQ DX, dst_len-48(SP) + MOVQ BX, src_base-32(SP) + MOVQ CX, bitWidth-8(SP) + CALL ·unpackInt64Default(SB) +done: + RET + +GLOBL range0n7<>(SB), RODATA|NOPTR, $64 +DATA range0n7<>+0(SB)/8, $0 +DATA range0n7<>+8(SB)/8, $1 +DATA range0n7<>+16(SB)/8, $2 +DATA range0n7<>+24(SB)/8, $3 +DATA range0n7<>+32(SB)/8, $4 +DATA range0n7<>+40(SB)/8, $5 +DATA range0n7<>+48(SB)/8, $6 +DATA range0n7<>+56(SB)/8, $7 diff --git a/vendor/github.com/parquet-go/bitpack/unpack_int64_arm64.go b/vendor/github.com/parquet-go/bitpack/unpack_int64_arm64.go new file mode 100644 index 000000000..02e6afc99 --- /dev/null +++ b/vendor/github.com/parquet-go/bitpack/unpack_int64_arm64.go @@ -0,0 +1,50 @@ +//go:build !purego + +package bitpack + +import ( + "github.com/parquet-go/bitpack/unsafecast" +) + +//go:noescape +func unpackInt64Default(dst []int64, src []byte, bitWidth uint) + +//go:noescape +func unpackInt64x1to32bitsARM64(dst []int64, src []byte, bitWidth uint) + +//go:noescape +func unpackInt64x1bitNEON(dst []int64, src []byte, bitWidth uint) + +//go:noescape +func unpackInt64x2bitNEON(dst []int64, src []byte, bitWidth uint) + +//go:noescape +func unpackInt64x3bitNEON(dst []int64, src []byte, bitWidth uint) + +//go:noescape +func unpackInt64x4bitNEON(dst []int64, src []byte, bitWidth uint) + +//go:noescape +func unpackInt64x8bitNEON(dst []int64, src []byte, bitWidth uint) + +func unpackInt64(dst []int64, src []byte, bitWidth uint) { + // For ARM64, NEON (Advanced SIMD) is always available + // Use table-based NEON operations for small bit widths + switch { + case bitWidth == 1: + unpackInt64x1bitNEON(dst, src, bitWidth) + case bitWidth == 2: + unpackInt64x2bitNEON(dst, src, bitWidth) + case bitWidth == 4: + unpackInt64x4bitNEON(dst, src, bitWidth) + case bitWidth == 8: + unpackInt64x8bitNEON(dst, src, bitWidth) + // bitWidth == 3,5,6,7: Skip NEON table (don't divide evenly into 8) + case bitWidth <= 32: + unpackInt64x1to32bitsARM64(dst, src, bitWidth) + case bitWidth == 64: + copy(dst, unsafecast.Slice[int64](src)) + default: + unpackInt64Default(dst, src, bitWidth) + } +} diff --git a/vendor/github.com/parquet-go/bitpack/unpack_int64_arm64.s b/vendor/github.com/parquet-go/bitpack/unpack_int64_arm64.s new file mode 100644 index 000000000..2c638fbfe --- /dev/null +++ b/vendor/github.com/parquet-go/bitpack/unpack_int64_arm64.s @@ -0,0 +1,943 @@ +//go:build !purego + +#include "funcdata.h" +#include "textflag.h" + +// func unpackInt64Default(dst []int64, src []byte, bitWidth uint) +TEXT ·unpackInt64Default(SB), NOSPLIT, $0-56 + MOVD dst_base+0(FP), R0 // R0 = dst pointer + MOVD dst_len+8(FP), R1 // R1 = dst length + MOVD src_base+24(FP), R2 // R2 = src pointer + MOVD bitWidth+48(FP), R3 // R3 = bitWidth + + // Initialize registers + MOVD $0, R5 // R5 = bitOffset + MOVD $0, R6 // R6 = index + + // Check if length >= 4 for unrolled loop + CMP $4, R1 + BLT scalar_loop_start + + // Calculate bitMask = (1 << bitWidth) - 1 + MOVD $1, R4 + LSL R3, R4, R4 + SUB $1, R4, R4 // R4 = bitMask + + // Calculate unrolled iterations: (length / 4) * 4 + LSR $2, R1, R16 // R16 = length / 4 + CBZ R16, scalar_loop_start + LSL $2, R16, R16 // R16 = (length / 4) * 4 + +unrolled_loop: + // Process 4 elements with instruction-level parallelism + // Use 64-bit loads for better performance + + // === Element 0 === + LSR $6, R5, R7 // i = bitOffset / 64 + AND $63, R5, R8 // j = bitOffset % 64 + MOVD (R2)(R7<<3), R9 // load 64-bit word from src[i] + LSL R8, R4, R10 + AND R10, R9, R9 + LSR R8, R9, R9 + + // Check if value spans into next word + ADD R8, R3, R11 + CMP $64, R11 + BLE store0 + MOVD $64, R12 + SUB R8, R12, R12 + ADD $1, R7, R13 + MOVD (R2)(R13<<3), R14 + LSR R12, R4, R15 + AND R15, R14, R14 + LSL R12, R14, R14 + ORR R14, R9, R9 + +store0: + ADD R3, R5, R5 // bitOffset += bitWidth + MOVD R9, (R0)(R6<<3) + ADD $1, R6, R6 + + // === Element 1 === + LSR $6, R5, R7 + AND $63, R5, R8 + MOVD (R2)(R7<<3), R9 + LSL R8, R4, R10 + AND R10, R9, R9 + LSR R8, R9, R9 + + ADD R8, R3, R11 + CMP $64, R11 + BLE store1 + MOVD $64, R12 + SUB R8, R12, R12 + ADD $1, R7, R13 + MOVD (R2)(R13<<3), R14 + LSR R12, R4, R15 + AND R15, R14, R14 + LSL R12, R14, R14 + ORR R14, R9, R9 + +store1: + ADD R3, R5, R5 + MOVD R9, (R0)(R6<<3) + ADD $1, R6, R6 + + // === Element 2 === + LSR $6, R5, R7 + AND $63, R5, R8 + MOVD (R2)(R7<<3), R9 + LSL R8, R4, R10 + AND R10, R9, R9 + LSR R8, R9, R9 + + ADD R8, R3, R11 + CMP $64, R11 + BLE store2 + MOVD $64, R12 + SUB R8, R12, R12 + ADD $1, R7, R13 + MOVD (R2)(R13<<3), R14 + LSR R12, R4, R15 + AND R15, R14, R14 + LSL R12, R14, R14 + ORR R14, R9, R9 + +store2: + ADD R3, R5, R5 + MOVD R9, (R0)(R6<<3) + ADD $1, R6, R6 + + // === Element 3 === + LSR $6, R5, R7 + AND $63, R5, R8 + MOVD (R2)(R7<<3), R9 + LSL R8, R4, R10 + AND R10, R9, R9 + LSR R8, R9, R9 + + ADD R8, R3, R11 + CMP $64, R11 + BLE store3 + MOVD $64, R12 + SUB R8, R12, R12 + ADD $1, R7, R13 + MOVD (R2)(R13<<3), R14 + LSR R12, R4, R15 + AND R15, R14, R14 + LSL R12, R14, R14 + ORR R14, R9, R9 + +store3: + ADD R3, R5, R5 + MOVD R9, (R0)(R6<<3) + ADD $1, R6, R6 + + CMP R16, R6 + BLT unrolled_loop + + // Check if done + CMP R1, R6 + BEQ done + +scalar_loop_start: + // Fallback scalar loop for remaining elements + MOVD $1, R4 + LSL R3, R4, R4 + SUB $1, R4, R4 // R4 = bitMask + +scalar_loop: + LSR $6, R5, R7 // i = bitOffset / 64 + AND $63, R5, R8 // j = bitOffset % 64 + MOVD (R2)(R7<<3), R9 // load 64-bit word + LSL R8, R4, R10 // bitMask << j + AND R10, R9, R9 + LSR R8, R9, R9 // extracted value + + // Check for span + ADD R8, R3, R11 + CMP $64, R11 + BLE scalar_next + MOVD $64, R12 + SUB R8, R12, R12 // k = 64 - j + ADD $1, R7, R13 + MOVD (R2)(R13<<3), R14 + LSR R12, R4, R15 + AND R15, R14, R14 + LSL R12, R14, R14 + ORR R14, R9, R9 + +scalar_next: + MOVD R9, (R0)(R6<<3) // dst[index] = d + ADD R3, R5, R5 // bitOffset += bitWidth + ADD $1, R6, R6 // index++ + +scalar_test: + CMP R1, R6 + BNE scalar_loop + +done: + RET + +// unpackInt64x1to32bitsARM64 implements optimized unpacking for bit widths 1-32 +// Uses optimized scalar ARM64 operations with batched processing +// +// func unpackInt64x1to32bitsARM64(dst []int64, src []byte, bitWidth uint) +TEXT ·unpackInt64x1to32bitsARM64(SB), NOSPLIT, $0-56 + MOVD dst_base+0(FP), R0 // R0 = dst pointer + MOVD dst_len+8(FP), R1 // R1 = dst length + MOVD src_base+24(FP), R2 // R2 = src pointer + MOVD bitWidth+48(FP), R3 // R3 = bitWidth + + // Check if we have at least 4 values to process + CMP $4, R1 + BLT scalar_fallback_int64 + + // Determine which path to use based on bitWidth + CMP $1, R3 + BEQ int64_1bit + CMP $2, R3 + BEQ int64_2bit + CMP $3, R3 + BEQ int64_3bit + CMP $4, R3 + BEQ int64_4bit + CMP $5, R3 + BEQ int64_5bit + CMP $6, R3 + BEQ int64_6bit + CMP $7, R3 + BEQ int64_7bit + CMP $8, R3 + BEQ int64_8bit + CMP $16, R3 + BEQ int64_16bit + CMP $32, R3 + BEQ int64_32bit + + // For other bit widths, fall back to scalar + B scalar_fallback_int64 + +int64_1bit: + // BitWidth 1: 8 int64 values packed in 1 byte + // Process 8 values at a time + + // Round down to multiple of 8 for processing + MOVD R1, R4 + LSR $3, R4, R4 // R4 = len / 8 + LSL $3, R4, R4 // R4 = aligned length (multiple of 8) + + MOVD $0, R5 + CMP $0, R4 + BEQ scalar_fallback_int64 + +int64_1bit_loop: + // Load 1 byte (contains 8 values, 1 bit each) + MOVBU (R2), R6 + + // Extract 8 bits + AND $1, R6, R7 + MOVD R7, (R0) + LSR $1, R6, R7 + AND $1, R7, R7 + MOVD R7, 8(R0) + LSR $2, R6, R7 + AND $1, R7, R7 + MOVD R7, 16(R0) + LSR $3, R6, R7 + AND $1, R7, R7 + MOVD R7, 24(R0) + LSR $4, R6, R7 + AND $1, R7, R7 + MOVD R7, 32(R0) + LSR $5, R6, R7 + AND $1, R7, R7 + MOVD R7, 40(R0) + LSR $6, R6, R7 + AND $1, R7, R7 + MOVD R7, 48(R0) + LSR $7, R6, R7 + AND $1, R7, R7 + MOVD R7, 56(R0) + + ADD $1, R2, R2 + ADD $64, R0, R0 + ADD $8, R5, R5 + + CMP R4, R5 + BLT int64_1bit_loop + + CMP R1, R5 + BEQ int64_done + SUB R5, R1, R1 + B scalar_fallback_entry_int64 + +int64_2bit: + // BitWidth 2: 8 int64 values packed in 2 bytes + MOVD R1, R4 + LSR $3, R4, R4 + LSL $3, R4, R4 + + MOVD $0, R5 + CMP $0, R4 + BEQ scalar_fallback_int64 + +int64_2bit_loop: + MOVHU (R2), R6 + + AND $3, R6, R7 + MOVD R7, (R0) + LSR $2, R6, R7 + AND $3, R7, R7 + MOVD R7, 8(R0) + LSR $4, R6, R7 + AND $3, R7, R7 + MOVD R7, 16(R0) + LSR $6, R6, R7 + AND $3, R7, R7 + MOVD R7, 24(R0) + LSR $8, R6, R7 + AND $3, R7, R7 + MOVD R7, 32(R0) + LSR $10, R6, R7 + AND $3, R7, R7 + MOVD R7, 40(R0) + LSR $12, R6, R7 + AND $3, R7, R7 + MOVD R7, 48(R0) + LSR $14, R6, R7 + AND $3, R7, R7 + MOVD R7, 56(R0) + + ADD $2, R2, R2 + ADD $64, R0, R0 + ADD $8, R5, R5 + + CMP R4, R5 + BLT int64_2bit_loop + + CMP R1, R5 + BEQ int64_done + SUB R5, R1, R1 + B scalar_fallback_entry_int64 + +int64_3bit: + // BitWidth 3: 8 int64 values packed in 3 bytes + MOVD R1, R4 + LSR $3, R4, R4 + LSL $3, R4, R4 + + MOVD $0, R5 + CMP $0, R4 + BEQ scalar_fallback_int64 + +int64_3bit_loop: + MOVWU (R2), R6 + + AND $7, R6, R7 + MOVD R7, (R0) + LSR $3, R6, R7 + AND $7, R7, R7 + MOVD R7, 8(R0) + LSR $6, R6, R7 + AND $7, R7, R7 + MOVD R7, 16(R0) + LSR $9, R6, R7 + AND $7, R7, R7 + MOVD R7, 24(R0) + LSR $12, R6, R7 + AND $7, R7, R7 + MOVD R7, 32(R0) + LSR $15, R6, R7 + AND $7, R7, R7 + MOVD R7, 40(R0) + LSR $18, R6, R7 + AND $7, R7, R7 + MOVD R7, 48(R0) + LSR $21, R6, R7 + AND $7, R7, R7 + MOVD R7, 56(R0) + + ADD $3, R2, R2 + ADD $64, R0, R0 + ADD $8, R5, R5 + + CMP R4, R5 + BLT int64_3bit_loop + + CMP R1, R5 + BEQ int64_done + SUB R5, R1, R1 + B scalar_fallback_entry_int64 + +int64_4bit: + // BitWidth 4: 8 int64 values packed in 4 bytes + MOVD R1, R4 + LSR $3, R4, R4 + LSL $3, R4, R4 + + MOVD $0, R5 + CMP $0, R4 + BEQ scalar_fallback_int64 + +int64_4bit_loop: + MOVWU (R2), R6 + + AND $15, R6, R7 + MOVD R7, (R0) + LSR $4, R6, R7 + AND $15, R7, R7 + MOVD R7, 8(R0) + LSR $8, R6, R7 + AND $15, R7, R7 + MOVD R7, 16(R0) + LSR $12, R6, R7 + AND $15, R7, R7 + MOVD R7, 24(R0) + LSR $16, R6, R7 + AND $15, R7, R7 + MOVD R7, 32(R0) + LSR $20, R6, R7 + AND $15, R7, R7 + MOVD R7, 40(R0) + LSR $24, R6, R7 + AND $15, R7, R7 + MOVD R7, 48(R0) + LSR $28, R6, R7 + AND $15, R7, R7 + MOVD R7, 56(R0) + + ADD $4, R2, R2 + ADD $64, R0, R0 + ADD $8, R5, R5 + + CMP R4, R5 + BLT int64_4bit_loop + + CMP R1, R5 + BEQ int64_done + SUB R5, R1, R1 + B scalar_fallback_entry_int64 + +int64_5bit: + // BitWidth 5: 8 int64 values packed in 5 bytes + MOVD R1, R4 + LSR $3, R4, R4 + LSL $3, R4, R4 + + MOVD $0, R5 + CMP $0, R4 + BEQ scalar_fallback_int64 + +int64_5bit_loop: + MOVD (R2), R6 + + AND $31, R6, R7 + MOVD R7, (R0) + LSR $5, R6, R7 + AND $31, R7, R7 + MOVD R7, 8(R0) + LSR $10, R6, R7 + AND $31, R7, R7 + MOVD R7, 16(R0) + LSR $15, R6, R7 + AND $31, R7, R7 + MOVD R7, 24(R0) + LSR $20, R6, R7 + AND $31, R7, R7 + MOVD R7, 32(R0) + LSR $25, R6, R7 + AND $31, R7, R7 + MOVD R7, 40(R0) + LSR $30, R6, R7 + AND $31, R7, R7 + MOVD R7, 48(R0) + LSR $35, R6, R7 + AND $31, R7, R7 + MOVD R7, 56(R0) + + ADD $5, R2, R2 + ADD $64, R0, R0 + ADD $8, R5, R5 + + CMP R4, R5 + BLT int64_5bit_loop + + CMP R1, R5 + BEQ int64_done + SUB R5, R1, R1 + B scalar_fallback_entry_int64 + +int64_6bit: + // BitWidth 6: 8 int64 values packed in 6 bytes + MOVD R1, R4 + LSR $3, R4, R4 + LSL $3, R4, R4 + + MOVD $0, R5 + CMP $0, R4 + BEQ scalar_fallback_int64 + +int64_6bit_loop: + MOVD (R2), R6 + + AND $63, R6, R7 + MOVD R7, (R0) + LSR $6, R6, R7 + AND $63, R7, R7 + MOVD R7, 8(R0) + LSR $12, R6, R7 + AND $63, R7, R7 + MOVD R7, 16(R0) + LSR $18, R6, R7 + AND $63, R7, R7 + MOVD R7, 24(R0) + LSR $24, R6, R7 + AND $63, R7, R7 + MOVD R7, 32(R0) + LSR $30, R6, R7 + AND $63, R7, R7 + MOVD R7, 40(R0) + LSR $36, R6, R7 + AND $63, R7, R7 + MOVD R7, 48(R0) + LSR $42, R6, R7 + AND $63, R7, R7 + MOVD R7, 56(R0) + + ADD $6, R2, R2 + ADD $64, R0, R0 + ADD $8, R5, R5 + + CMP R4, R5 + BLT int64_6bit_loop + + CMP R1, R5 + BEQ int64_done + SUB R5, R1, R1 + B scalar_fallback_entry_int64 + +int64_7bit: + // BitWidth 7: 8 int64 values packed in 7 bytes + MOVD R1, R4 + LSR $3, R4, R4 + LSL $3, R4, R4 + + MOVD $0, R5 + CMP $0, R4 + BEQ scalar_fallback_int64 + +int64_7bit_loop: + MOVD (R2), R6 + + AND $127, R6, R7 + MOVD R7, (R0) + LSR $7, R6, R7 + AND $127, R7, R7 + MOVD R7, 8(R0) + LSR $14, R6, R7 + AND $127, R7, R7 + MOVD R7, 16(R0) + LSR $21, R6, R7 + AND $127, R7, R7 + MOVD R7, 24(R0) + LSR $28, R6, R7 + AND $127, R7, R7 + MOVD R7, 32(R0) + LSR $35, R6, R7 + AND $127, R7, R7 + MOVD R7, 40(R0) + LSR $42, R6, R7 + AND $127, R7, R7 + MOVD R7, 48(R0) + LSR $49, R6, R7 + AND $127, R7, R7 + MOVD R7, 56(R0) + + ADD $7, R2, R2 + ADD $64, R0, R0 + ADD $8, R5, R5 + + CMP R4, R5 + BLT int64_7bit_loop + + CMP R1, R5 + BEQ int64_done + SUB R5, R1, R1 + B scalar_fallback_entry_int64 + +int64_8bit: + // BitWidth 8: 8 int64 values packed in 8 bytes + // Process 8 values at a time + + // Round down to multiple of 8 for processing + MOVD R1, R4 + LSR $3, R4, R4 // R4 = len / 8 + LSL $3, R4, R4 // R4 = aligned length (multiple of 8) + + MOVD $0, R5 // R5 = index + CMP $0, R4 + BEQ scalar_fallback_int64 + +int64_8bit_loop: + // Load 8 bytes (contains 8 values, 1 byte each) + MOVD (R2), R6 + + // Extract 8 bytes and store as int64 + // Value 0: byte 0 + AND $0xFF, R6, R7 + MOVD R7, (R0) + + // Value 1: byte 1 + LSR $8, R6, R7 + AND $0xFF, R7, R7 + MOVD R7, 8(R0) + + // Value 2: byte 2 + LSR $16, R6, R7 + AND $0xFF, R7, R7 + MOVD R7, 16(R0) + + // Value 3: byte 3 + LSR $24, R6, R7 + AND $0xFF, R7, R7 + MOVD R7, 24(R0) + + // Value 4: byte 4 + LSR $32, R6, R7 + AND $0xFF, R7, R7 + MOVD R7, 32(R0) + + // Value 5: byte 5 + LSR $40, R6, R7 + AND $0xFF, R7, R7 + MOVD R7, 40(R0) + + // Value 6: byte 6 + LSR $48, R6, R7 + AND $0xFF, R7, R7 + MOVD R7, 48(R0) + + // Value 7: byte 7 + LSR $56, R6, R7 + MOVD R7, 56(R0) + + // Advance pointers + ADD $8, R2, R2 // src += 8 bytes (8 values) + ADD $64, R0, R0 // dst += 8 int64 (64 bytes) + ADD $8, R5, R5 // index += 8 + + CMP R4, R5 + BLT int64_8bit_loop + + // Handle tail with scalar + CMP R1, R5 + BEQ int64_done + + SUB R5, R1, R1 + B scalar_fallback_entry_int64 + +int64_16bit: + // BitWidth 16: 4 int64 values packed in 8 bytes + // Process 4 values at a time + + MOVD R1, R4 + LSR $2, R4, R4 // R4 = len / 4 + LSL $2, R4, R4 // R4 = aligned length (multiple of 4) + + MOVD $0, R5 // R5 = index + CMP $0, R4 + BEQ scalar_fallback_int64 + +int64_16bit_loop: + // Load 8 bytes as 4 uint16 values + MOVD (R2), R6 + + // Extract 16-bit values and write as int64 + // Value 0 (bits 0-15) + AND $0xFFFF, R6, R7 + MOVD R7, (R0) + + // Value 1 (bits 16-31) + LSR $16, R6, R7 + AND $0xFFFF, R7, R7 + MOVD R7, 8(R0) + + // Value 2 (bits 32-47) + LSR $32, R6, R7 + AND $0xFFFF, R7, R7 + MOVD R7, 16(R0) + + // Value 3 (bits 48-63) + LSR $48, R6, R7 + MOVD R7, 24(R0) + + // Advance pointers + ADD $8, R2, R2 // src += 8 bytes (4 values) + ADD $32, R0, R0 // dst += 4 int64 (32 bytes) + ADD $4, R5, R5 // index += 4 + + CMP R4, R5 + BLT int64_16bit_loop + + // Handle tail with scalar + CMP R1, R5 + BEQ int64_done + + SUB R5, R1, R1 + B scalar_fallback_entry_int64 + +int64_32bit: + // BitWidth 32: 2 int64 values packed in 8 bytes + // Process 2 values at a time + + MOVD R1, R4 + LSR $1, R4, R4 // R4 = len / 2 + LSL $1, R4, R4 // R4 = aligned length (multiple of 2) + + MOVD $0, R5 // R5 = index + CMP $0, R4 + BEQ scalar_fallback_int64 + +int64_32bit_loop: + // Load 8 bytes as 2 uint32 values + MOVD (R2), R6 + + // Extract 32-bit values and write as int64 + // Value 0 (bits 0-31) + AND $0xFFFFFFFF, R6, R7 + MOVD R7, (R0) + + // Value 1 (bits 32-63) + LSR $32, R6, R7 + MOVD R7, 8(R0) + + // Advance pointers + ADD $8, R2, R2 // src += 8 bytes (2 values) + ADD $16, R0, R0 // dst += 2 int64 (16 bytes) + ADD $2, R5, R5 // index += 2 + + CMP R4, R5 + BLT int64_32bit_loop + + // Handle tail with scalar + CMP R1, R5 + BEQ int64_done + + SUB R5, R1, R1 + B scalar_fallback_entry_int64 + +int64_done: + RET + +scalar_fallback_int64: + MOVD $0, R5 // Start from beginning + +scalar_fallback_entry_int64: + // R0 = current dst position (already advanced) + // R1 = remaining elements + // R2 = current src position (already advanced) + // R3 = bitWidth + // R5 = elements already processed + + // Fall back to optimized implementation for remaining elements + CMP $0, R1 + BEQ scalar_done_int64 // No remaining elements + + // Check if we can do 4-way unrolled loop + CMP $4, R1 + BLT scalar_single_int64 + + // Calculate bitMask + MOVD $1, R4 + LSL R3, R4, R4 + SUB $1, R4, R4 // R4 = bitMask + + // Calculate unrolled iterations: (remaining / 4) * 4 + LSR $2, R1, R16 + CBZ R16, scalar_single_int64 + LSL $2, R16, R16 // R16 = (len / 4) * 4 + + MOVD $0, R6 // R6 = bitOffset + MOVD $0, R7 // R7 = index + +scalar_unrolled_loop_int64: + // === Element 0 === + LSR $6, R6, R8 // i = bitOffset / 64 + AND $63, R6, R9 // j = bitOffset % 64 + MOVD (R2)(R8<<3), R11 // load 64-bit word + LSL R9, R4, R12 + AND R12, R11, R11 + LSR R9, R11, R11 + + ADD R9, R3, R12 + CMP $64, R12 + BLE scalar_store0_int64 + MOVD $64, R13 + SUB R9, R13, R13 + ADD $1, R8, R14 + MOVD (R2)(R14<<3), R15 + LSR R13, R4, R10 + AND R10, R15, R15 + LSL R13, R15, R15 + ORR R15, R11, R11 + +scalar_store0_int64: + ADD R3, R6, R6 + LSL $3, R7, R10 + MOVD R11, (R0)(R10) + ADD $1, R7, R7 + + // === Element 1 === + LSR $6, R6, R8 + AND $63, R6, R9 + MOVD (R2)(R8<<3), R11 + LSL R9, R4, R12 + AND R12, R11, R11 + LSR R9, R11, R11 + + ADD R9, R3, R12 + CMP $64, R12 + BLE scalar_store1_int64 + MOVD $64, R13 + SUB R9, R13, R13 + ADD $1, R8, R14 + MOVD (R2)(R14<<3), R15 + LSR R13, R4, R10 + AND R10, R15, R15 + LSL R13, R15, R15 + ORR R15, R11, R11 + +scalar_store1_int64: + ADD R3, R6, R6 + LSL $3, R7, R10 + MOVD R11, (R0)(R10) + ADD $1, R7, R7 + + // === Element 2 === + LSR $6, R6, R8 + AND $63, R6, R9 + MOVD (R2)(R8<<3), R11 + LSL R9, R4, R12 + AND R12, R11, R11 + LSR R9, R11, R11 + + ADD R9, R3, R12 + CMP $64, R12 + BLE scalar_store2_int64 + MOVD $64, R13 + SUB R9, R13, R13 + ADD $1, R8, R14 + MOVD (R2)(R14<<3), R15 + LSR R13, R4, R10 + AND R10, R15, R15 + LSL R13, R15, R15 + ORR R15, R11, R11 + +scalar_store2_int64: + ADD R3, R6, R6 + LSL $3, R7, R10 + MOVD R11, (R0)(R10) + ADD $1, R7, R7 + + // === Element 3 === + LSR $6, R6, R8 + AND $63, R6, R9 + MOVD (R2)(R8<<3), R11 + LSL R9, R4, R12 + AND R12, R11, R11 + LSR R9, R11, R11 + + ADD R9, R3, R12 + CMP $64, R12 + BLE scalar_store3_int64 + MOVD $64, R13 + SUB R9, R13, R13 + ADD $1, R8, R14 + MOVD (R2)(R14<<3), R15 + LSR R13, R4, R10 + AND R10, R15, R15 + LSL R13, R15, R15 + ORR R15, R11, R11 + +scalar_store3_int64: + ADD R3, R6, R6 + LSL $3, R7, R10 + MOVD R11, (R0)(R10) + ADD $1, R7, R7 + + CMP R16, R7 + BLT scalar_unrolled_loop_int64 + + // Check if done + CMP R1, R7 + BEQ scalar_done_int64 + + // Preserve R6 (bitOffset), R4 (bitMask), and R7 (index) for tail processing + // R1 still contains total count, R7 has current index + B scalar_loop_single_int64 + +scalar_single_int64: + // Process remaining elements one at a time + MOVD $1, R4 + LSL R3, R4, R4 + SUB $1, R4, R4 // R4 = bitMask + + MOVD $0, R6 // R6 = bitOffset + MOVD $0, R7 // R7 = index + +scalar_loop_single_int64: + LSR $6, R6, R8 // i = bitOffset / 64 + AND $63, R6, R9 // j = bitOffset % 64 + MOVD (R2)(R8<<3), R11 // load 64-bit word + LSL R9, R4, R12 + AND R12, R11, R11 + LSR R9, R11, R11 + + ADD R9, R3, R12 + CMP $64, R12 + BLE scalar_next_single_int64 + MOVD $64, R13 + SUB R9, R13, R13 + ADD $1, R8, R14 + MOVD (R2)(R14<<3), R15 + LSR R13, R4, R10 + AND R10, R15, R15 + LSL R13, R15, R15 + ORR R15, R11, R11 + +scalar_next_single_int64: + LSL $3, R7, R10 + MOVD R11, (R0)(R10) + ADD R3, R6, R6 + ADD $1, R7, R7 + + CMP R1, R7 + BLT scalar_loop_single_int64 + +scalar_done_int64: + RET + +// Macro definitions for unsupported NEON instructions using WORD encodings +// USHLL Vd.8H, Vn.8B, #0 - widen 8x8-bit to 8x16-bit +#define USHLL_8H_8B(vd, vn) WORD $(0x2f08a400 | (vd) | ((vn)<<5)) + +// USHLL2 Vd.8H, Vn.16B, #0 - widen upper 8x8-bit to 8x16-bit +#define USHLL2_8H_16B(vd, vn) WORD $(0x6f08a400 | (vd) | ((vn)<<5)) + +// USHLL Vd.4S, Vn.4H, #0 - widen 4x16-bit to 4x32-bit +#define USHLL_4S_4H(vd, vn) WORD $(0x2f10a400 | (vd) | ((vn)<<5)) + +// USHLL2 Vd.4S, Vn.8H, #0 - widen upper 4x16-bit to 4x32-bit +#define USHLL2_4S_8H(vd, vn) WORD $(0x6f10a400 | (vd) | ((vn)<<5)) + +// USHLL Vd.2D, Vn.2S, #0 - widen 2x32-bit to 2x64-bit +#define USHLL_2D_2S(vd, vn) WORD $(0x2f20a400 | (vd) | ((vn)<<5)) + +// USHLL2 Vd.2D, Vn.4S, #0 - widen upper 2x32-bit to 2x64-bit +#define USHLL2_2D_4S(vd, vn) WORD $(0x6f20a400 | (vd) | ((vn)<<5)) + +// unpackInt64x1bitNEON implements table-based NEON unpacking for int64 bitWidth=1 +// Similar to int32 version but with additional widening to 64-bit +// +// func unpackInt64x1bitNEON(dst []int64, src []byte, bitWidth uint) diff --git a/vendor/github.com/parquet-go/bitpack/unpack_int64_purego.go b/vendor/github.com/parquet-go/bitpack/unpack_int64_purego.go new file mode 100644 index 000000000..3d7b11832 --- /dev/null +++ b/vendor/github.com/parquet-go/bitpack/unpack_int64_purego.go @@ -0,0 +1,25 @@ +//go:build purego || (!amd64 && !arm64) + +package bitpack + +func unpackInt64(dst []int64, src []byte, bitWidth uint) { + bits := unsafecastBytesToUint32(src) + bitMask := uint64(1<> j + if j+bitWidth > 32 { + k := 32 - j + d |= (uint64(bits[i+1]) & (bitMask >> k)) << k + if j+bitWidth > 64 { + k := 64 - j + d |= (uint64(bits[i+2]) & (bitMask >> k)) << k + } + } + dst[n] = int64(d) + bitOffset += bitWidth + } +} diff --git a/vendor/github.com/parquet-go/bitpack/unpack_neon_macros_arm64.h b/vendor/github.com/parquet-go/bitpack/unpack_neon_macros_arm64.h new file mode 100644 index 000000000..7ba1c8f7b --- /dev/null +++ b/vendor/github.com/parquet-go/bitpack/unpack_neon_macros_arm64.h @@ -0,0 +1,18 @@ +// Macro definitions for unsupported NEON instructions using WORD encodings +// USHLL Vd.8H, Vn.8B, #0 - widen 8x8-bit to 8x16-bit +#define USHLL_8H_8B(vd, vn) WORD $(0x2f08a400 | (vd) | ((vn)<<5)) + +// USHLL2 Vd.8H, Vn.16B, #0 - widen upper 8x8-bit to 8x16-bit +#define USHLL2_8H_16B(vd, vn) WORD $(0x6f08a400 | (vd) | ((vn)<<5)) + +// USHLL Vd.4S, Vn.4H, #0 - widen 4x16-bit to 4x32-bit +#define USHLL_4S_4H(vd, vn) WORD $(0x2f10a400 | (vd) | ((vn)<<5)) + +// USHLL2 Vd.4S, Vn.8H, #0 - widen upper 4x16-bit to 4x32-bit +#define USHLL2_4S_8H(vd, vn) WORD $(0x6f10a400 | (vd) | ((vn)<<5)) + +// USHLL Vd.2D, Vn.2S, #0 - widen 2x32-bit to 2x64-bit +#define USHLL_2D_2S(vd, vn) WORD $(0x2f20a400 | (vd) | ((vn)<<5)) + +// USHLL2 Vd.2D, Vn.4S, #0 - widen upper 2x32-bit to 2x64-bit +#define USHLL2_2D_4S(vd, vn) WORD $(0x6f20a400 | (vd) | ((vn)<<5)) diff --git a/vendor/github.com/parquet-go/bitpack/unsafecast/unsafecast.go b/vendor/github.com/parquet-go/bitpack/unsafecast/unsafecast.go new file mode 100644 index 000000000..0838fd10e --- /dev/null +++ b/vendor/github.com/parquet-go/bitpack/unsafecast/unsafecast.go @@ -0,0 +1,54 @@ +// Package unsafecast exposes functions to bypass the Go type system and perform +// conversions between types that would otherwise not be possible. +// +// The functions of this package are mostly useful as optimizations to avoid +// memory copies when converting between compatible memory layouts; for example, +// casting a [][16]byte to a []byte in order to use functions of the standard +// bytes package on the slices. +// +// With great power comes great responsibility. +package unsafecast + +import "unsafe" + +// The slice type represents the memory layout of slices in Go. It is similar to +// reflect.SliceHeader but uses a unsafe.Pointer instead of uintptr to for the +// backing array to allow the garbage collector to track track the reference. +type slice struct { + ptr unsafe.Pointer + len int + cap int +} + +// Slice converts the data slice of type []From to a slice of type []To sharing +// the same backing array. The length and capacity of the returned slice are +// scaled according to the size difference between the source and destination +// types. +// +// Note that the function does not perform any checks to ensure that the memory +// layouts of the types are compatible, it is possible to cause memory +// corruption if the layouts mismatch (e.g. the pointers in the From are +// different than the pointers in To). +func Slice[To, From any](data []From) []To { + // This function could use unsafe.Slice but it would drop the capacity + // information, so instead we implement the type conversion. + var zf From + var zt To + var s = slice{ + ptr: unsafe.Pointer(unsafe.SliceData(data)), + len: int((uintptr(len(data)) * unsafe.Sizeof(zf)) / unsafe.Sizeof(zt)), + cap: int((uintptr(cap(data)) * unsafe.Sizeof(zf)) / unsafe.Sizeof(zt)), + } + return *(*[]To)(unsafe.Pointer(&s)) +} + +// String converts a byte slice to a string value. The returned string shares +// the backing array of the byte slice. +// +// Programs using this function are responsible for ensuring that the data slice +// is not modified while the returned string is in use, otherwise the guarantee +// of immutability of Go string values will be violated, resulting in undefined +// behavior. +func String(data []byte) string { + return unsafe.String(unsafe.SliceData(data), len(data)) +} diff --git a/vendor/github.com/parquet-go/jsonlite/.gitignore b/vendor/github.com/parquet-go/jsonlite/.gitignore new file mode 100644 index 000000000..b3584c8d4 --- /dev/null +++ b/vendor/github.com/parquet-go/jsonlite/.gitignore @@ -0,0 +1,21 @@ +# Binaries for programs and plugins +*.exe +*.exe~ +*.dll +*.so +*.dylib +*.py + +# Test binary, built with `go test -c` +*.test + +# Output of the go coverage tool, specifically when used with LiteIDE +*.out + +# Dependency directories (remove the comment below to include it) +# vendor/ + +# Emacs +*~ +#*# +.# diff --git a/vendor/github.com/parquet-go/jsonlite/LICENSE b/vendor/github.com/parquet-go/jsonlite/LICENSE new file mode 100644 index 000000000..ef8a76d1d --- /dev/null +++ b/vendor/github.com/parquet-go/jsonlite/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2025 parquet-go + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/vendor/github.com/parquet-go/jsonlite/README.md b/vendor/github.com/parquet-go/jsonlite/README.md new file mode 100644 index 000000000..18c4e911c --- /dev/null +++ b/vendor/github.com/parquet-go/jsonlite/README.md @@ -0,0 +1,54 @@ +# jsonlite + +[![Go Reference](https://pkg.go.dev/badge/github.com/parquet-go/jsonlite.svg)](https://pkg.go.dev/github.com/parquet-go/jsonlite) + +A lightweight JSON parser for Go, optimized for performance through careful +memory management. + +## Motivation + +Go's standard `encoding/json` package is designed for marshaling and +unmarshaling into Go structs. This works well when you know the schema ahead of +time, but becomes awkward when you need to traverse arbitrary JSON +structures—you end up with `map[string]interface{}` and type assertions +everywhere. + +Packages like `github.com/tidwall/gjson` solve this with path-based queries, +but they re-scan the input for each query. If you're recursively walking a JSON +document, you pay the parsing cost repeatedly. + +`jsonlite` takes a different approach: parse once, traverse many times. The +parser builds a lightweight in-memory index of the entire document in a single +pass. Once parsed, you can navigate the structure freely without re-parsing. + +## Design + +The core insight is that most JSON strings don't contain escape sequences. When +a string like `"hello"` appears in the input, we don't need to copy it—we can +just point directly into the original input buffer. Only strings with escapes +(like `"hello\nworld"`) require allocation for the unescaped result. + +Each JSON value is represented by a `Value` struct containing just two machine +words: a pointer and a packed integer. The integer uses its high bits to store +the value's type (null, boolean, number, string, array, or object) and its low +bits for length information. This bit-packing means the overhead per value is +minimal—16 bytes on 64-bit systems regardless of the value type. + +Arrays and objects are stored as contiguous slices, so iteration is +cache-friendly. Object fields are sorted by key during parsing, enabling binary +search for lookups. + +## Trade-offs + +This design optimizes for read-heavy workloads where you parse once and query +multiple times. If you only need to extract a single value from a large +document, a streaming parser or path-based query might be more appropriate. + +The parser assumes the input remains valid for the lifetime of the parsed +values. If you're parsing from a buffer that gets reused, you'll need to copy +strings out before the buffer changes. + +Numeric values are stored as their original string representation and parsed on +demand when you call `Int()`, `Float()`, etc. This avoids precision loss for +large integers and defers parsing cost until needed, but means repeated numeric +access will re-parse each time. diff --git a/vendor/github.com/parquet-go/jsonlite/convert.go b/vendor/github.com/parquet-go/jsonlite/convert.go new file mode 100644 index 000000000..31843da80 --- /dev/null +++ b/vendor/github.com/parquet-go/jsonlite/convert.go @@ -0,0 +1,352 @@ +package jsonlite + +import ( + "encoding/json" + "math" + "strconv" + "time" +) + +// Convertible is the type constraint for the As function, defining all supported +// conversion types. +type Convertible interface { + any | + bool | + int64 | + uint64 | + float64 | + json.Number | + string | + time.Duration | + time.Time | + []any | + []bool | + []int64 | + []uint64 | + []float64 | + []json.Number | + []string | + []time.Duration | + []time.Time | + map[string]any | + map[string]bool | + map[string]int64 | + map[string]uint64 | + map[string]float64 | + map[string]json.Number | + map[string]string | + map[string]time.Duration | + map[string]time.Time +} + +// As converts a JSON value to the specified Go type. +// +// For primitive types (bool, int64, uint64, float64, string, time.Duration, time.Time): +// - Follows the same conversion rules as the corresponding As* function +// - Returns zero value for nil or incompatible types +// +// For json.Number: +// - Returns the number as a json.Number string +// - Returns empty string for non-number values +// +// For slice types ([]T): +// - Converts JSON arrays where each element is converted using the primitive T logic +// - Returns nil for non-array values +// - Returns empty slice for empty arrays +// +// For map types (map[string]T): +// - Converts JSON objects where each value is converted using the primitive T logic +// - Returns nil for non-object values +// - Returns empty map for empty objects +// +// For any types: +// - any: Returns the most natural Go representation (bool, int64, uint64, float64, string, []any, map[string]any) +// - []any: Recursively converts array elements to any +// - map[string]any: Recursively converts object values to any +// +// Examples: +// +// val, _ := Parse(`[1, 2, 3]`) +// nums := As[[]int64](val) // []int64{1, 2, 3} +// +// val, _ := Parse(`{"a": 1, "b": 2}`) +// m := As[map[string]int64](val) // map[string]int64{"a": 1, "b": 2} +func As[T Convertible](v *Value) T { + switch any(*new(T)).(type) { + case bool: + return any(asBool(v)).(T) + case int64: + return any(asInt(v)).(T) + case uint64: + return any(asUint(v)).(T) + case float64: + return any(asFloat(v)).(T) + case json.Number: + return any(asNumber(v)).(T) + case string: + return any(asString(v)).(T) + case time.Duration: + return any(asDuration(v)).(T) + case time.Time: + return any(asTime(v)).(T) + case []any: + return any(asSlice(v, asAny)).(T) + case []bool: + return any(asSlice(v, asBool)).(T) + case []int64: + return any(asSlice(v, asInt)).(T) + case []uint64: + return any(asSlice(v, asUint)).(T) + case []float64: + return any(asSlice(v, asFloat)).(T) + case []json.Number: + return any(asSlice(v, asNumber)).(T) + case []string: + return any(asSlice(v, asString)).(T) + case []time.Duration: + return any(asSlice(v, asDuration)).(T) + case []time.Time: + return any(asSlice(v, asTime)).(T) + case map[string]any: + return any(asMap(v, asAny)).(T) + case map[string]bool: + return any(asMap(v, asBool)).(T) + case map[string]int64: + return any(asMap(v, asInt)).(T) + case map[string]uint64: + return any(asMap(v, asUint)).(T) + case map[string]float64: + return any(asMap(v, asFloat)).(T) + case map[string]json.Number: + return any(asMap(v, asNumber)).(T) + case map[string]string: + return any(asMap(v, asString)).(T) + case map[string]time.Duration: + return any(asMap(v, asDuration)).(T) + case map[string]time.Time: + return any(asMap(v, asTime)).(T) + default: + // Special handling for any to avoid panic when result is nil + r, _ := asAny(v).(T) + return r + } +} + +// asBool coerces the value to a boolean. +func asBool(v *Value) bool { + if v != nil { + switch v.Kind() { + case True: + return true + case Number: + f, err := strconv.ParseFloat(v.json(), 64) + return err == nil && f != 0 + case String, Object, Array: + return v.Len() > 0 + } + } + return false +} + +// asString coerces the value to a string. +func asString(v *Value) string { + if v != nil && v.Kind() != Null { + return v.String() + } + return "" +} + +// asInt coerces the value to a signed 64-bit integer. +func asInt(v *Value) int64 { + if v != nil { + switch v.Kind() { + case True: + return 1 + case Number: + if i, err := strconv.ParseInt(v.json(), 10, 64); err == nil { + return i + } + if f, err := strconv.ParseFloat(v.json(), 64); err == nil { + return int64(f) + } + case String: + // Strip surrounding quotes - no escapes in valid number strings + s := v.json() + s = s[1 : len(s)-1] + if i, err := strconv.ParseInt(s, 10, 64); err == nil { + return i + } + if f, err := strconv.ParseFloat(s, 64); err == nil { + return int64(f) + } + } + } + return 0 +} + +// asUint coerces the value to an unsigned 64-bit integer. +func asUint(v *Value) uint64 { + if v != nil { + switch v.Kind() { + case True: + return 1 + case Number: + if u, err := strconv.ParseUint(v.json(), 10, 64); err == nil { + return u + } + if f, err := strconv.ParseFloat(v.json(), 64); err == nil { + if f >= 0 { + return uint64(f) + } + } + case String: + // Strip surrounding quotes - no escapes in valid number strings + s := v.json() + s = s[1 : len(s)-1] + if u, err := strconv.ParseUint(s, 10, 64); err == nil { + return u + } + if f, err := strconv.ParseFloat(s, 64); err == nil { + if f >= 0 { + return uint64(f) + } + } + } + } + return 0 +} + +// asFloat coerces the value to a 64-bit floating point number. +func asFloat(v *Value) float64 { + if v != nil { + switch v.Kind() { + case True: + return 1 + case Number: + if f, err := strconv.ParseFloat(v.json(), 64); err == nil { + return f + } + case String: + // Strip surrounding quotes - no escapes in valid number strings + s := v.json() + s = s[1 : len(s)-1] + if f, err := strconv.ParseFloat(s, 64); err == nil { + return f + } + } + } + return 0 +} + +// asDuration coerces the value to a time.Duration. +func asDuration(v *Value) time.Duration { + if v != nil { + switch v.Kind() { + case True: + return time.Second + case Number: + if f, err := strconv.ParseFloat(v.json(), 64); err == nil { + return time.Duration(f * float64(time.Second)) + } + case String: + // Strip surrounding quotes - no escapes in valid duration strings + s := v.json() + s = s[1 : len(s)-1] + if d, err := time.ParseDuration(s); err == nil { + return d + } + } + } + return 0 +} + +// asTime coerces the value to a time.Time. +func asTime(v *Value) time.Time { + if v != nil { + switch v.Kind() { + case Number: + if f, err := strconv.ParseFloat(v.json(), 64); err == nil { + sec, frac := math.Modf(f) + return time.Unix(int64(sec), int64(frac*1e9)).UTC() + } + case String: + // Strip surrounding quotes - no escapes in valid RFC3339 time strings + s := v.json() + s = s[1 : len(s)-1] + if t, err := time.ParseInLocation(time.RFC3339, s, time.UTC); err == nil { + return t + } + } + } + return time.Time{} +} + +// asNumber coerces the value to a json.Number. +func asNumber(v *Value) json.Number { + if v != nil && v.Kind() == Number { + return json.Number(v.json()) + } + return "" +} + +// asSlice converts a JSON array to a Go slice by applying the converter +// function to each element. Returns nil for non-array values. +func asSlice[E any](v *Value, converter func(*Value) E) []E { + if v == nil || v.Kind() != Array { + return nil + } + result := make([]E, 0, v.Len()) + for elem := range v.Array { + result = append(result, converter(elem)) + } + return result +} + +// asMap converts a JSON object to a Go map by applying the converter +// function to each value. Returns nil for non-object values. +func asMap[V any](v *Value, converter func(*Value) V) map[string]V { + if v == nil || v.Kind() != Object { + return nil + } + result := make(map[string]V, v.Len()) + for k, val := range v.Object { + result[k] = converter(val) + } + return result +} + +// asAny converts a JSON value to its most natural Go representation. +func asAny(v *Value) any { + if v == nil { + return nil + } + switch v.Kind() { + case Null: + return nil + case True: + return true + case False: + return false + case Number: + switch v.NumberType() { + case Int: + return v.Int() + case Uint: + u := v.Uint() + if u <= math.MaxInt64 { + return int64(u) + } + return u + default: + return v.Float() + } + case String: + return v.String() + case Array: + return asSlice(v, asAny) + case Object: + return asMap(v, asAny) + default: + return nil + } +} diff --git a/vendor/github.com/parquet-go/jsonlite/doc.go b/vendor/github.com/parquet-go/jsonlite/doc.go new file mode 100644 index 000000000..e5fd4e557 --- /dev/null +++ b/vendor/github.com/parquet-go/jsonlite/doc.go @@ -0,0 +1,8 @@ +// Package jsonlite provides a lightweight JSON parser optimized for +// performance through careful memory management. It parses JSON into +// a tree of Value nodes that can be inspected and serialized back to JSON. +// +// The parser handles all standard JSON types: null, booleans, numbers, +// strings, arrays, and objects. It properly handles UTF-16 surrogate +// pairs for emoji and extended Unicode characters. +package jsonlite diff --git a/vendor/github.com/parquet-go/jsonlite/iterator.go b/vendor/github.com/parquet-go/jsonlite/iterator.go new file mode 100644 index 000000000..f2b5d82d7 --- /dev/null +++ b/vendor/github.com/parquet-go/jsonlite/iterator.go @@ -0,0 +1,562 @@ +package jsonlite + +import ( + "fmt" + "math" + "strconv" + "time" +) + +// Iterator provides a streaming interface for traversing JSON values. +// It automatically handles control tokens (braces, brackets, colons, commas) +// and presents only the logical JSON values to the caller. +type Iterator struct { + tokens Tokenizer + json string // Original JSON for computing pre-token positions + token string + kind Kind + key string + err error + state []byte // stack of states: 'a' for array, 'o' for object (expecting key), 'v' for object (expecting value) + bytes [16]byte + consumed bool // whether the current value has been consumed +} + +// Iterate creates a new Iterator for the given JSON string. +func Iterate(json string) *Iterator { + it := &Iterator{ + tokens: Tokenizer{json: json}, + json: json, // Store original JSON + } + it.state = it.bytes[:0] + return it +} + +// Reset resets the iterator to parse a new JSON string. +func (it *Iterator) Reset(json string) { + it.tokens = Tokenizer{json: json} + it.json = json + it.token = "" + it.kind = 0 + it.key = "" + it.err = nil + it.consumed = false +} + +// Next advances the iterator to the next JSON value. +// Returns true if there is a value to process, false when done or on error. +func (it *Iterator) Next() bool { + for { + token, ok := it.tokens.Next() + if !ok { + if len(it.state) > 0 { + if it.top() == 'a' { + it.err = errUnexpectedEndOfArray + } else { + it.err = errUnexpectedEndOfObject + } + } + return false + } + + if len(it.state) > 0 { + s := it.top() + switch s { + case 'a': // in array, expecting value or ] + if token == "]" { + it.pop() + continue + } + if token == "," { + continue + } + case 'o': // in object, expecting key or } + if token == "}" { + it.pop() + continue + } + if token == "," { + continue + } + key, err := Unquote(token) + if err != nil { + it.setErrorf("invalid key: %q: %w", token, err) + return false + } + it.setKey(key) + colon, ok := it.tokens.Next() + if !ok { + it.err = errUnexpectedEndOfObject + return false + } + if colon != ":" { + it.setErrorf("expected ':', got %q", colon) + return false + } + // Change state to expect value + it.set('v') + continue + case 'v': // in object, expecting value + // Change state back to expect key/} + it.set('o') + } + } + + return it.setToken(token) + } +} + +func (it *Iterator) push(state byte) { + it.state = append(it.state, state) +} + +func (it *Iterator) pop() { + it.state = it.state[:len(it.state)-1] +} + +func (it *Iterator) top() byte { + return it.state[len(it.state)-1] +} + +func (it *Iterator) set(state byte) { + it.state[len(it.state)-1] = state +} + +func (it *Iterator) setError(err error) { + it.err = err +} + +func (it *Iterator) setErrorf(msg string, args ...any) { + it.setError(fmt.Errorf(msg, args...)) +} + +func (it *Iterator) setKey(key string) { + it.key = key +} + +func (it *Iterator) setToken(token string) bool { + kind, err := tokenKind(token) + it.token = token + it.kind = kind + it.err = err + it.consumed = false + + if err != nil { + return false + } + + switch kind { + case Array: + it.push('a') + case Object: + it.push('o') + } + + return true +} + +func (it *Iterator) skipArray() { + depth := 1 + for depth > 0 { + token, ok := it.tokens.Next() + if !ok { + it.setError(errUnexpectedEndOfArray) + return + } + switch token { + case "[": + depth++ + case "]": + depth-- + } + } + it.pop() +} + +func (it *Iterator) skipObject() { + depth := 1 + for depth > 0 { + token, ok := it.tokens.Next() + if !ok { + it.setError(errUnexpectedEndOfObject) + return + } + switch token { + case "{": + depth++ + case "}": + depth-- + } + } + it.pop() +} + +func tokenKind(token string) (Kind, error) { + switch token[0] { + case 'n': + if token != "null" { + return Null, fmt.Errorf("invalid token: %q", token) + } + return Null, nil + case 't': + if token != "true" { + return Null, fmt.Errorf("invalid token: %q", token) + } + return True, nil + case 'f': + if token != "false" { + return Null, fmt.Errorf("invalid token: %q", token) + } + return False, nil + case '"': + return String, nil + case '[': + return Array, nil + case '{': + return Object, nil + case '-', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': + if !validNumber(token) { + return Number, fmt.Errorf("invalid number: %q", token) + } + return Number, nil + default: + return Null, fmt.Errorf("invalid token: %q", token) + } +} + +// Kind returns the kind of the current value. +func (it *Iterator) Kind() Kind { return it.kind } + +// Key returns the object key for the current value, if inside an object. +// Returns an empty string if not inside an object or at the top level. +func (it *Iterator) Key() string { return it.key } + +// Err returns any error that occurred during iteration. +func (it *Iterator) Err() error { return it.err } + +// Depth returns the current nesting depth (0 at top level). +func (it *Iterator) Depth() int { return len(it.state) } + +// Value parses and returns the current value. +// For arrays and objects, this consumes all nested tokens and returns the +// complete parsed structure. +func (it *Iterator) Value() (*Value, error) { + val, err := it.value() + return &val, err +} + +func (it *Iterator) value() (Value, error) { + if it.err != nil { + return Value{}, it.err + } + + switch it.kind { + case Null, True, False, Number: + return makeValue(it.kind, it.token), nil + case String: + // Validate the quoted string but store the quoted token + if !validString(it.token) { + return Value{}, fmt.Errorf("invalid string: %q", it.token) + } + return makeStringValue(it.token), nil + case Array: + delimi := len(it.token) + offset := len(it.json) - len(it.tokens.json) - delimi + val, rest, err := parseArray(it.json[offset:], it.tokens.json, DefaultMaxDepth) + it.tokens.json, it.consumed = rest, true + if err != nil { + it.setError(err) + } + it.pop() + return val, err + case Object: + delimi := len(it.token) + offset := len(it.json) - len(it.tokens.json) - delimi + val, rest, err := parseObject(it.json[offset:], it.tokens.json, DefaultMaxDepth) + it.tokens.json, it.consumed = rest, true + if err != nil { + it.setError(err) + } + it.pop() + return val, err + default: + return Value{}, fmt.Errorf("unexpected kind: %v", it.kind) + } +} + +// Null returns true if the current value is null. +func (it *Iterator) Null() bool { return it.kind == Null } + +// Bool returns the current value as a boolean. +// Returns false for null values. +// Returns an error if the value is not a boolean, null, or a string that can be parsed as a boolean. +func (it *Iterator) Bool() (bool, error) { + switch it.kind { + case Null, False: + return false, nil + case True: + return true, nil + case String: + s, err := Unquote(it.token) + if err != nil { + return false, fmt.Errorf("invalid string: %q", it.token) + } + return strconv.ParseBool(s) + default: + return false, fmt.Errorf("cannot convert %v to bool", it.kind) + } +} + +// Int returns the current value as a signed 64-bit integer. +// Returns 0 for null values. +// Returns an error if the value is not a number, null, or a string that can be parsed as an integer. +func (it *Iterator) Int() (int64, error) { + switch it.kind { + case Null: + return 0, nil + case Number: + return strconv.ParseInt(it.token, 10, 64) + case String: + s, err := Unquote(it.token) + if err != nil { + return 0, fmt.Errorf("invalid string: %q", it.token) + } + return strconv.ParseInt(s, 10, 64) + default: + return 0, fmt.Errorf("cannot convert %v to int", it.kind) + } +} + +// Float returns the current value as a 64-bit floating point number. +// Returns 0 for null values. +// Returns an error if the value is not a number, null, or a string that can be parsed as a float. +func (it *Iterator) Float() (float64, error) { + switch it.kind { + case Null: + return 0, nil + case Number: + return strconv.ParseFloat(it.token, 64) + case String: + s, err := Unquote(it.token) + if err != nil { + return 0, fmt.Errorf("invalid string: %q", it.token) + } + return strconv.ParseFloat(s, 64) + default: + return 0, fmt.Errorf("cannot convert %v to float", it.kind) + } +} + +// String returns the current value as a string. +// Returns "" for null values. +// Returns an error if the value is not a string or null. +func (it *Iterator) String() (string, error) { + switch it.kind { + case Null: + return "", nil + case String: + return Unquote(it.token) + default: + return "", fmt.Errorf("cannot convert %v to string", it.kind) + } +} + +// Duration returns the current value as a time.Duration. +// Returns 0 for null values. +// For numbers, the value is interpreted as seconds. +// For strings, the value is parsed using time.ParseDuration. +// Returns an error if the value cannot be converted to a duration. +func (it *Iterator) Duration() (time.Duration, error) { + switch it.kind { + case Null: + return 0, nil + case Number: + f, err := strconv.ParseFloat(it.token, 64) + if err != nil { + return 0, err + } + return time.Duration(f * float64(time.Second)), nil + case String: + s, err := Unquote(it.token) + if err != nil { + return 0, fmt.Errorf("invalid string: %q", it.token) + } + return time.ParseDuration(s) + default: + return 0, fmt.Errorf("cannot convert %v to duration", it.kind) + } +} + +// Time returns the current value as a time.Time. +// Returns the zero time for null values. +// For numbers, the value is interpreted as seconds since Unix epoch. +// For strings, the value is parsed using RFC3339 format. +// Returns an error if the value cannot be converted to a time. +func (it *Iterator) Time() (time.Time, error) { + switch it.kind { + case Null: + return time.Time{}, nil + case Number: + f, err := strconv.ParseFloat(it.token, 64) + if err != nil { + return time.Time{}, err + } + sec, frac := math.Modf(f) + return time.Unix(int64(sec), int64(frac*1e9)).UTC(), nil + case String: + s, err := Unquote(it.token) + if err != nil { + return time.Time{}, fmt.Errorf("invalid string: %q", it.token) + } + return time.Parse(time.RFC3339, s) + default: + return time.Time{}, fmt.Errorf("cannot convert %v to time", it.kind) + } +} + +// Object iterates over the key-value pairs of the current object. +// The iterator yields the key for each field, and the Iterator is positioned +// on the field's value. Call Kind(), Value(), Object(), or Array() to process +// the value. If the value is not consumed before the next iteration, it will +// be automatically skipped. +// For null values, no iterations occur. +// +// Must only be called when Kind() == Object or Kind() == Null. +func (it *Iterator) Object(yield func(string, error) bool) { + if it.kind == Null { + return // null is treated as empty object + } + it.consumed = true // mark the object itself as consumed + for i := 0; ; i++ { + // Auto-consume the previous value if it wasn't consumed + if !it.consumed { + switch it.kind { + case Array: + it.consumed = true + it.skipArray() + case Object: + it.consumed = true + it.skipObject() + } + } + + token, ok := it.tokens.Next() + if !ok { + it.setError(errUnexpectedEndOfObject) + yield("", it.err) + return + } + + if token == "}" { + it.pop() + return + } + + if i != 0 { + if token != "," { + it.setErrorf("expected ',', got %q", token) + yield("", it.err) + return + } + token, ok = it.tokens.Next() + if !ok { + it.setError(errUnexpectedEndOfObject) + yield("", it.err) + return + } + } + + key, err := Unquote(token) + if err != nil { + it.setErrorf("invalid key: %q: %w", token, err) + yield("", it.err) + return + } + + colon, ok := it.tokens.Next() + if !ok { + it.setError(errUnexpectedEndOfObject) + yield("", it.err) + return + } + if colon != ":" { + it.setErrorf("expected ':', got %q", colon) + yield("", it.err) + return + } + + value, ok := it.tokens.Next() + if !ok { + it.setError(errUnexpectedEndOfObject) + yield("", it.err) + return + } + + it.setKey(key) + it.setToken(value) + + if !yield(key, nil) { + return + } + } +} + +// Array iterates over the elements of the current array. +// The iterator yields the index for each element, and the Iterator is +// positioned on the element's value. Call Kind(), Value(), Object(), or +// Array() to process the value. If the value is not consumed before the +// next iteration, it will be automatically skipped. +// For null values, no iterations occur. +// +// Must only be called when Kind() == Array or Kind() == Null. +func (it *Iterator) Array(yield func(int, error) bool) { + if it.kind == Null { + return // null is treated as empty array + } + it.consumed = true // mark the array itself as consumed + for i := 0; ; i++ { + // Auto-consume the previous value if it wasn't consumed + if !it.consumed { + switch it.kind { + case Array: + it.consumed = true + it.skipArray() + case Object: + it.consumed = true + it.skipObject() + } + } + + token, ok := it.tokens.Next() + if !ok { + it.setError(errUnexpectedEndOfArray) + yield(i, it.err) + return + } + + if token == "]" { + it.pop() + return + } + + if i != 0 { + if token != "," { + it.setErrorf("expected ',', got %q", token) + yield(i, it.err) + return + } + token, ok = it.tokens.Next() + if !ok { + it.setError(errUnexpectedEndOfArray) + yield(i, it.err) + return + } + } + + it.setToken(token) + + if !yield(i, nil) { + return + } + } +} diff --git a/vendor/github.com/parquet-go/jsonlite/parse.go b/vendor/github.com/parquet-go/jsonlite/parse.go new file mode 100644 index 000000000..7292cdc1a --- /dev/null +++ b/vendor/github.com/parquet-go/jsonlite/parse.go @@ -0,0 +1,305 @@ +package jsonlite + +import ( + "errors" + "fmt" + "hash/maphash" + "strings" + "unsafe" +) + +const ( + // DefaultMaxDepth is the default maximum depth for parsing JSON objects. + DefaultMaxDepth = 100 +) + +var ( + errEndOfObject = errors.New("}") + errEndOfArray = errors.New("]") + errUnexpectedEndOfObject = errors.New("unexpected end of object") + errUnexpectedEndOfArray = errors.New("unexpected end of array") +) + +// whitespaceMap is a 256-bit lookup table for ASCII whitespace characters. +// Bit i is set if byte i is whitespace (space, tab, newline, carriage return). +var whitespaceMap = func() [4]uint64 { + var m [4]uint64 + for _, c := range []byte{' ', '\t', '\n', '\r'} { + m[c/64] |= 1 << (c % 64) + } + return m +}() + +// isWhitespace returns true if c is a JSON whitespace character. +func isWhitespace(c byte) bool { + return (whitespaceMap[c/64] & (1 << (c % 64))) != 0 +} + +// delimiterMap is a 256-bit lookup table for JSON delimiters and whitespace. +// Used to quickly find the end of numbers/literals. +var delimiterMap = func() [4]uint64 { + var m [4]uint64 + for _, c := range []byte{' ', '\t', '\n', '\r', '[', ']', '{', '}', ':', ',', '"'} { + m[c/64] |= 1 << (c % 64) + } + return m +}() + +// isDelimiter returns true if c is a JSON delimiter or whitespace. +func isDelimiter(c byte) bool { + return (delimiterMap[c/64] & (1 << (c % 64))) != 0 +} + +// Tokenizer is a JSON tokenizer that splits input into tokens. +// It skips whitespace and returns individual JSON tokens one at a time. +type Tokenizer struct { + json string +} + +// Tokenize creates a new Tokenizer for the given JSON string. +func Tokenize(json string) *Tokenizer { + return &Tokenizer{json: json} +} + +// Next returns the next token from the input. +// Returns an empty string and false when there are no more tokens. +func (t *Tokenizer) Next() (token string, ok bool) { + token, t.json, ok = nextToken(t.json) + return token, ok +} + +// nextToken extracts the next JSON token from s. +// Returns the token, the remaining string after the token, and whether a token was found. +// All values are kept in registers - no heap allocation for tokenizer state. +func nextToken(s string) (token, rest string, ok bool) { + // Skip leading whitespace using lookup table + switch { + case len(s) == 0: + return "", "", false + case s[0] <= ' ': + for isWhitespace(s[0]) { + if s = s[1:]; len(s) == 0 { + return "", "", false + } + } + } + + switch s[0] { + case '"': + // Find closing quote, handling escapes + j := 1 + for { + k := strings.IndexByte(s[j:], '"') + if k < 0 { + return s, "", true + } + j += k + 1 + // Count preceding backslashes to check if quote is escaped + n := 0 + for i := j - 2; i > 0 && s[i] == '\\'; i-- { + n++ + } + if n%2 == 0 { + return s[:j], s[j:], true + } + } + case ',', ':', '[', ']', '{', '}': + return s[:1], s[1:], true + default: + // Numbers and literals: scan until delimiter using lookup table + j := 1 + for j < len(s) && !isDelimiter(s[j]) { + j++ + } + return s[:j], s[j:], true + } +} + +// ParseMaxDepth parses JSON data with a maximum nesting depth for objects. +// Objects at maxDepth <= 0 are stored unparsed and will be lazily parsed +// when accessed via Lookup(), Array(), or Object() methods. +// Depth is only decremented for objects, not arrays. +// Returns an error if the JSON is malformed or empty. +func ParseMaxDepth(data string, maxDepth int) (*Value, error) { + v, rest, err := parseValue(data, max(0, maxDepth)) + if err != nil { + return nil, err + } + // Check for trailing content after the root value + if extra, _, ok := nextToken(rest); ok { + return nil, fmt.Errorf("unexpected token after root value: %q", extra) + } + return &v, nil +} + +// Parse parses JSON data and returns a pointer to the root Value. +// Returns an error if the JSON is malformed or empty. +func Parse(data string) (*Value, error) { return ParseMaxDepth(data, DefaultMaxDepth) } + +// parseValue parses a JSON value from s. +// Returns the parsed value, the remaining unparsed string, and any error. +// The string is passed by value to keep it in registers. +func parseValue(s string, maxDepth int) (Value, string, error) { + token, rest, ok := nextToken(s) + if !ok { + return Value{}, rest, errUnexpectedEndOfObject + } + switch token[0] { + case 'n': + if token != "null" { + return Value{}, rest, fmt.Errorf("invalid token: %q", token) + } + return makeNullValue(token[:4]), rest, nil + case 't': + if token != "true" { + return Value{}, rest, fmt.Errorf("invalid token: %q", token) + } + return makeTrueValue(token[:4]), rest, nil + case 'f': + if token != "false" { + return Value{}, rest, fmt.Errorf("invalid token: %q", token) + } + return makeFalseValue(token[:5]), rest, nil + case '"': + // Validate the quoted string but store the quoted token + if !validString(token) { + return Value{}, rest, fmt.Errorf("invalid token: %q", token) + } + return makeStringValue(token), rest, nil + case '[': + return parseArray(s, rest, maxDepth) + case '{': + return parseObject(s, rest, maxDepth) + case ']': + return Value{}, rest, errEndOfArray + case '}': + return Value{}, rest, errEndOfObject + case '-', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': + if !validNumber(token) { + return Value{}, rest, fmt.Errorf("invalid number: %q", token) + } + return makeNumberValue(token), rest, nil + default: + return Value{}, rest, fmt.Errorf("invalid token: %q", token) + } +} + +func parseArray(start, json string, maxDepth int) (Value, string, error) { + elements := make([]Value, 0, 32) + + for i := 0; ; i++ { + if i != 0 { + token, rest, ok := nextToken(json) + if !ok { + return Value{}, json, errUnexpectedEndOfArray + } + if token == "]" { + cached := start[:len(start)-len(rest)] + result := make([]Value, len(elements)+1) + result[0] = makeStringValue(cached) + copy(result[1:], elements) + return makeArrayValue(result), rest, nil + } + if token != "," { + return Value{}, json, fmt.Errorf("expected ',' or ']', got %q", token) + } + json = rest + } + + v, rest, err := parseValue(json, maxDepth) + if err != nil { + if i == 0 && err == errEndOfArray { + cached := start[:len(start)-len(rest)] + result := make([]Value, len(elements)+1) + result[0] = makeStringValue(cached) + copy(result[1:], elements) + return makeArrayValue(result), rest, nil + } + if err == errEndOfArray { + return Value{}, json, fmt.Errorf("unexpected ']' after ','") + } + return Value{}, json, err + } + json = rest + elements = append(elements, v) + } +} + +func parseObject(start, json string, maxDepth int) (Value, string, error) { + if maxDepth == 0 { + depth, remain := 1, json + for depth > 0 { + token, next, ok := nextToken(remain) + if !ok { + return Value{}, remain, errUnexpectedEndOfObject + } + remain = next + switch token { + case "{": + depth++ + case "}": + depth-- + } + } + json := start[:len(start)-len(remain)] + return makeUnparsedObjectValue(json), remain, nil + } + + maxDepth-- + fields := make([]field, 0, 16) + + for i := 0; ; i++ { + token, rest, ok := nextToken(json) + if !ok { + return Value{}, json, errUnexpectedEndOfObject + } + if token == "}" { + cached := start[:len(start)-len(rest)] + result := make([]field, len(fields)+1) + copy(result[1:], fields) + + fields := result[1:] + hashes := make([]byte, len(fields), (len(fields)*8+1)/8) + for i := range fields { + hashes[i] = byte(maphash.String(hashseed, fields[i].k)) + } + + result[0].v = makeStringValue(cached) + result[0].k = unsafe.String(unsafe.SliceData(hashes), cap(hashes)) + return makeObjectValue(result), rest, nil + } + json = rest + + if i != 0 { + if token != "," { + return Value{}, json, fmt.Errorf("expected ',' or '}', got %q", token) + } + token, rest, ok = nextToken(json) + if !ok { + return Value{}, json, errUnexpectedEndOfObject + } + json = rest + } + + key, err := Unquote(token) + if err != nil { + return Value{}, json, fmt.Errorf("invalid key: %q: %w", token, err) + } + + token, rest, ok = nextToken(json) + if !ok { + return Value{}, json, errUnexpectedEndOfObject + } + if token != ":" { + return Value{}, json, fmt.Errorf("%q → expected ':', got %q", key, token) + } + json = rest + + val, rest, err := parseValue(json, maxDepth) + if err != nil { + return Value{}, json, fmt.Errorf("%q → %w", key, err) + } + json = rest + fields = append(fields, field{k: key, v: val}) + } +} diff --git a/vendor/github.com/parquet-go/jsonlite/quote.go b/vendor/github.com/parquet-go/jsonlite/quote.go new file mode 100644 index 000000000..0b849354a --- /dev/null +++ b/vendor/github.com/parquet-go/jsonlite/quote.go @@ -0,0 +1,121 @@ +package jsonlite + +import ( + "math/bits" + "unsafe" +) + +// Quote returns s as a JSON quoted string. +func Quote(s string) string { + return string(AppendQuote(make([]byte, 0, 2+len(s)), s)) +} + +// AppendQuote appends a JSON quoted string to b and returns the result. +func AppendQuote(b []byte, s string) []byte { + b = append(b, '"') + + for { + i := escapeIndex(s) + if i < 0 { + b = append(b, s...) + break + } + + b = append(b, s[:i]...) + switch c := s[i]; c { + case '\\', '"': + b = append(b, '\\', c) + case '\b': + b = append(b, '\\', 'b') + case '\f': + b = append(b, '\\', 'f') + case '\n': + b = append(b, '\\', 'n') + case '\r': + b = append(b, '\\', 'r') + case '\t': + b = append(b, '\\', 't') + default: + const hex = "0123456789abcdef" + b = append(b, '\\', 'u', '0', '0', hex[c>>4], hex[c&0xf]) + } + + s = s[i+1:] + } + + return append(b, '"') +} + +/* +MIT License + +Copyright (c) 2019 Segment.io, Inc. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +const ( + lsb = 0x0101010101010101 + msb = 0x8080808080808080 +) + +// escapeIndex finds the index of the first char in `s` that requires escaping. +// A char requires escaping if it's outside of the range of [0x20, 0x7F] or if +// it includes a double quote or backslash. If no chars in `s` require escaping, +// the return value is -1. +func escapeIndex(s string) int { + var i int + if len(s) >= 8 { + chunks := unsafe.Slice((*uint64)(unsafe.Pointer(unsafe.StringData(s))), len(s)/8) + for j, n := range chunks { + // combine masks before checking for the MSB of each byte. We include + // `n` in the mask to check whether any of the *input* byte MSBs were + // set (i.e. the byte was outside the ASCII range). + mask := n | below(n, 0x20) | contains(n, '"') | contains(n, '\\') + if (mask & msb) != 0 { + return j*8 + bits.TrailingZeros64(mask&msb)/8 + } + } + i = len(chunks) * 8 + } + + for ; i < len(s); i++ { + c := s[i] + if c < 0x20 || c > 0x7f || c == '"' || c == '\\' { + return i + } + } + + return -1 +} + +// below return a mask that can be used to determine if any of the bytes +// in `n` are below `b`. If a byte's MSB is set in the mask then that byte was +// below `b`. The result is only valid if `b`, and each byte in `n`, is below +// 0x80. +func below(n uint64, b byte) uint64 { return n - expand(b) } + +// contains returns a mask that can be used to determine if any of the +// bytes in `n` are equal to `b`. If a byte's MSB is set in the mask then +// that byte is equal to `b`. The result is only valid if `b`, and each +// byte in `n`, is below 0x80. +func contains(n uint64, b byte) uint64 { return (n ^ expand(b)) - lsb } + +// expand puts the specified byte into each of the 8 bytes of a uint64. +func expand(b byte) uint64 { return lsb * uint64(b) } diff --git a/vendor/github.com/parquet-go/jsonlite/unquote.go b/vendor/github.com/parquet-go/jsonlite/unquote.go new file mode 100644 index 000000000..63e3f3298 --- /dev/null +++ b/vendor/github.com/parquet-go/jsonlite/unquote.go @@ -0,0 +1,218 @@ +package jsonlite + +import ( + "fmt" + "math/bits" + "unicode/utf16" + "unicode/utf8" + "unsafe" +) + +const ( + // UTF-16 surrogate pair boundaries (from Unicode standard) + surrogateMin = 0xD800 // Start of high surrogate range + lowSurrogateMin = 0xDC00 // Start of low surrogate range + lowSurrogateMax = 0xDFFF // End of low surrogate range +) + +// Unquote removes quotes from a JSON string and processes escape sequences. +// Returns an error if the string is not properly quoted or contains invalid escapes. +// When the string contains no escape sequences, returns a zero-copy substring. +func Unquote(s string) (string, error) { + if len(s) < 2 || s[0] != '"' || s[len(s)-1] != '"' { + return "", fmt.Errorf("invalid quoted string: %s", s) + } + s = s[1 : len(s)-1] + // Fast path: check if string needs unescaping (has backslash or control chars) + if !escaped(s) { + return s, nil + } + b, err := unquote(make([]byte, 0, len(s)), s) + return string(b), err +} + +// AppendUnquote appends the unquoted string to the buffer. +// Returns an error if the string is not properly quoted or contains invalid escapes. +func AppendUnquote(b []byte, s string) ([]byte, error) { + if len(s) < 2 || s[0] != '"' || s[len(s)-1] != '"' { + return b, fmt.Errorf("invalid quoted string: %s", s) + } + s = s[1 : len(s)-1] + // Fast path: check if string needs unescaping + if !escaped(s) { + return append(b, s...), nil + } + return unquote(b, s) +} + +func escaped(s string) bool { + // SIMD-like scanning for backslash or control characters. + // The bit tricks only work correctly when all bytes are < 0x80, + // so we also check for high bytes and fall back to byte-by-byte. + var i int + if len(s) >= 8 { + chunks := unsafe.Slice((*uint64)(unsafe.Pointer(unsafe.StringData(s))), len(s)/8) + for _, n := range chunks { + // Check for high bytes (>= 0x80), backslash, or control chars + mask := n | below(n, 0x20) | contains(n, '\\') + if (mask & msb) != 0 { + return true + } + } + i = len(chunks) * 8 + } + + for ; i < len(s); i++ { + c := s[i] + if c < 0x20 || c == '\\' { + return true + } + } + + return false +} + +// unescapeIndex checks if the string content needs unescaping. +// Returns -1 if no unescaping needed, or the index of the first problematic byte. +// A string needs unescaping if it contains backslash or control characters (< 0x20). +func unescapeIndex(s string) int { + // SIMD-like scanning for backslash or control characters. + // The bit tricks only work correctly when all bytes are < 0x80, + // so we also check for high bytes and fall back to byte-by-byte. + var i int + if len(s) >= 8 { + chunks := unsafe.Slice((*uint64)(unsafe.Pointer(unsafe.StringData(s))), len(s)/8) + for j, n := range chunks { + // Check for high bytes (>= 0x80), backslash, or control chars + mask := n | below(n, 0x20) | contains(n, '\\') + if (mask & msb) != 0 { + // Found something in this chunk - check byte at the position + k := j*8 + bits.TrailingZeros64(mask&msb)/8 + c := s[k] + switch { + case c < 0x20, c == '\\': + return k + default: + // High byte (>= 0x80) - scan rest of chunk byte by byte + for k++; k < (j+1)*8; k++ { + c := s[k] + if c < 0x20 || c == '\\' { + return k + } + } + } + } + } + i = len(chunks) * 8 + } + + for ; i < len(s); i++ { + c := s[i] + if c < 0x20 || c == '\\' { + return i + } + } + + return -1 +} + +// unquote processes escape sequences in content and appends to b. +// content should not include the surrounding quotes. +func unquote(b []byte, s string) ([]byte, error) { + for len(s) > 0 { + i := unescapeIndex(s) + if i < 0 { + return append(b, s...), nil + } + + b = append(b, s[:i]...) + c := s[i] + if c < 0x20 { + return b, fmt.Errorf("invalid control character in string") + } + if i+1 >= len(s) { + return b, fmt.Errorf("invalid escape sequence at end of string") + } + + switch c := s[i+1]; c { + case '"', '\\', '/': + b = append(b, c) + s = s[i+2:] + case 'b': + b = append(b, '\b') + s = s[i+2:] + case 'f': + b = append(b, '\f') + s = s[i+2:] + case 'n': + b = append(b, '\n') + s = s[i+2:] + case 'r': + b = append(b, '\r') + s = s[i+2:] + case 't': + b = append(b, '\t') + s = s[i+2:] + case 'u': + if i+6 > len(s) { + return b, fmt.Errorf("invalid unicode escape sequence") + } + r1, ok := parseHex4(s[i+2 : i+6]) + if !ok { + return b, fmt.Errorf("invalid unicode escape sequence") + } + + // Check for UTF-16 surrogate pair + if utf16.IsSurrogate(r1) { + // Low surrogate without high surrogate is an error + if r1 >= lowSurrogateMin { + return b, fmt.Errorf("invalid surrogate pair: unexpected low surrogate") + } + // High surrogate, look for low surrogate + if i+12 > len(s) || s[i+6] != '\\' || s[i+7] != 'u' { + return b, fmt.Errorf("invalid surrogate pair: missing low surrogate") + } + r2, ok := parseHex4(s[i+8 : i+12]) + if !ok { + return b, fmt.Errorf("invalid unicode escape sequence in surrogate pair") + } + if r2 < lowSurrogateMin || r2 > lowSurrogateMax { + return b, fmt.Errorf("invalid surrogate pair: low surrogate out of range") + } + // Decode the surrogate pair + b = utf8.AppendRune(b, utf16.DecodeRune(r1, r2)) + s = s[i+12:] + } else { + b = utf8.AppendRune(b, r1) + s = s[i+6:] + } + default: + return b, fmt.Errorf("invalid escape character: %q", c) + } + } + return b, nil +} + +// parseHex4 parses a 4-character hex string into a rune. +// Returns the rune and true on success, or 0 and false on failure. +func parseHex4(s string) (rune, bool) { + if len(s) < 4 { + return 0, false + } + var r rune + for i := range 4 { + r <<= 4 + c := s[i] + switch { + case c >= '0' && c <= '9': + r |= rune(c - '0') + case c >= 'a' && c <= 'f': + r |= rune(c - 'a' + 10) + case c >= 'A' && c <= 'F': + r |= rune(c - 'A' + 10) + default: + return 0, false + } + } + return r, true +} diff --git a/vendor/github.com/parquet-go/jsonlite/valid.go b/vendor/github.com/parquet-go/jsonlite/valid.go new file mode 100644 index 000000000..e16bc9c39 --- /dev/null +++ b/vendor/github.com/parquet-go/jsonlite/valid.go @@ -0,0 +1,244 @@ +package jsonlite + +// Valid reports whether json is a valid JSON string. +// This is similar to encoding/json.Valid but uses the jsonlite tokenizer +// for efficient zero-allocation validation. +func Valid(json string) bool { + tok := Tokenize(json) + if !valid(tok) { + return false + } + // Ensure no trailing content after root value + _, ok := tok.Next() + return !ok +} + +// valid validates a single JSON value and returns true if valid. +func valid(tok *Tokenizer) bool { + token, ok := tok.Next() + if !ok { + return false + } + return validToken(tok, token) +} + +// validToken validates a token and any nested structure it may contain. +func validToken(tok *Tokenizer, token string) bool { + switch token[0] { + case 'n': + return token == "null" + case 't': + return token == "true" + case 'f': + return token == "false" + case '"': + return validString(token) + case '[': + return validArray(tok) + case '{': + return validObject(tok) + default: + return validNumber(token) + } +} + +// validString checks if a token is a valid JSON string. +// The token must start and end with quotes and contain valid escape sequences. +func validString(s string) bool { + if len(s) < 2 || s[0] != '"' || s[len(s)-1] != '"' { + return false + } + // Check for valid escape sequences and no unescaped control characters + content := s[1 : len(s)-1] + + // Fast path: use SIMD-like scanning from unquote.go to check if we need + // detailed validation. If no backslashes or control chars, string is valid. + if !escaped(content) { + return true + } + + // Slow path: validate escape sequences + return validStringEscapes(content) +} + +// validStringEscapes validates escape sequences in a string that contains +// at least one backslash or control character. +func validStringEscapes(content string) bool { + for i := 0; i < len(content); i++ { + c := content[i] + if c < 0x20 { + // Control characters must be escaped + return false + } + if c == '\\' { + i++ + if i >= len(content) { + return false + } + switch content[i] { + case '"', '\\', '/', 'b', 'f', 'n', 'r', 't': + // Valid single-character escape + case 'u': + // Unicode escape: must be followed by 4 hex digits + if i+4 >= len(content) { + return false + } + if !isHexDigit(content[i+1]) || !isHexDigit(content[i+2]) || + !isHexDigit(content[i+3]) || !isHexDigit(content[i+4]) { + return false + } + i += 4 + default: + return false + } + } + } + return true +} + +// isHexDigit returns true if c is a valid hexadecimal digit. +func isHexDigit(c byte) bool { + return (c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F') +} + +// validArray validates a JSON array starting after the '[' token. +func validArray(tok *Tokenizer) bool { + // Check for empty array + token, ok := tok.Next() + if !ok { + return false + } + if token == "]" { + return true + } + + // Parse first element + if !validToken(tok, token) { + return false + } + + // Parse remaining elements + for { + token, ok = tok.Next() + if !ok { + return false + } + if token == "]" { + return true + } + if token != "," { + return false + } + // Expect value after comma + token, ok = tok.Next() + if !ok { + return false + } + if token == "]" { + // Trailing comma is not valid JSON + return false + } + if !validToken(tok, token) { + return false + } + } +} + +// validObject validates a JSON object starting after the '{' token. +func validObject(tok *Tokenizer) bool { + for i := 0; ; i++ { + token, ok := tok.Next() + if !ok { + return false + } + if token == "}" { + return true // Empty object or end of object + } + if i > 0 { + // After first field, expect comma then key + if token != "," { + return false + } + token, ok = tok.Next() + if !ok { + return false + } + if token == "}" { + // Trailing comma is not valid JSON + return false + } + } + // Expect string key + if len(token) == 0 || token[0] != '"' || !validString(token) { + return false + } + // Expect colon + token, ok = tok.Next() + if !ok || token != ":" { + return false + } + // Expect value + if !valid(tok) { + return false + } + } +} + +// validNumber checks if a string is a valid JSON number. +// JSON numbers: -?(0|[1-9][0-9]*)(\.[0-9]+)?([eE][+-]?[0-9]+)? +func validNumber(s string) bool { + if len(s) == 0 { + return false + } + i := 0 + + // Optional minus sign + if s[i] == '-' { + i++ + if i >= len(s) { + return false + } + } + + // Integer part + if s[i] == '0' { + i++ + } else if s[i] >= '1' && s[i] <= '9' { + i++ + for i < len(s) && s[i] >= '0' && s[i] <= '9' { + i++ + } + } else { + return false + } + + // Fractional part + if i < len(s) && s[i] == '.' { + i++ + if i >= len(s) || s[i] < '0' || s[i] > '9' { + return false + } + for i < len(s) && s[i] >= '0' && s[i] <= '9' { + i++ + } + } + + // Exponent part + if i < len(s) && (s[i] == 'e' || s[i] == 'E') { + i++ + if i >= len(s) { + return false + } + if s[i] == '+' || s[i] == '-' { + i++ + } + if i >= len(s) || s[i] < '0' || s[i] > '9' { + return false + } + for i < len(s) && s[i] >= '0' && s[i] <= '9' { + i++ + } + } + + return i == len(s) +} diff --git a/vendor/github.com/parquet-go/jsonlite/value.go b/vendor/github.com/parquet-go/jsonlite/value.go new file mode 100644 index 000000000..5aa5fad8d --- /dev/null +++ b/vendor/github.com/parquet-go/jsonlite/value.go @@ -0,0 +1,415 @@ +package jsonlite + +import ( + "encoding/json" + "fmt" + "hash/maphash" + "strconv" + "strings" + "unsafe" +) + +const ( + // kindShift is calculated based on pointer size to use the high bits + // for the kind field. We have 7 Kind values (0-6), requiring 3 bits. + // On 64-bit systems this is 61 (top 3 bits for kind, bottom 61 for length), + // on 32-bit systems this is 29 (top 3 bits for kind, bottom 29 for length). + kindShift = (unsafe.Sizeof(uintptr(0))*8 - 3) + kindMask = (1 << kindShift) - 1 + // unparsedBit is set for objects/arrays that haven't been parsed yet (lazy parsing). + // On 64-bit systems this is bit 60, on 32-bit systems this is bit 28. + unparsedBit = uintptr(1) << (kindShift - 1) +) + +var ( + // hashseed is the seed used for hashing object keys. + hashseed = maphash.MakeSeed() +) + +// Kind represents the type of a JSON value. +type Kind int + +const ( + // Null represents a JSON null value. + Null Kind = iota + // True represents a JSON true boolean value. + True + // False represents a JSON false boolean value. + False + // Number represents a JSON number value. + Number + // String represents a JSON string value. + String + // Object represents a JSON object value. + Object + // Array represents a JSON array value. + Array +) + +// Value represents a JSON value of any type. +// +// Value instances as immutable, they can be safely accessed from multiple +// goroutines. +// +// The zero-value of Value is invalid, all Value instances must be acquired +// form Parse or from an Iterator. +type Value struct { + p unsafe.Pointer + n uintptr +} + +type field struct { + k string + v Value +} + +// Kind returns the type of the JSON value. +func (v *Value) Kind() Kind { return Kind(v.n >> kindShift) } + +// Len returns the length of the value. +// For strings, it returns the number of bytes. +// For arrays, it returns the number of elements. +// For objects, it returns the number of fields. +// Panics if called on other types. +func (v *Value) Len() int { + switch v.Kind() { + case String: + // String values now store quoted JSON - subtract 2 for quotes + return int(v.n&kindMask) - 2 + case Number: + return int(v.n & kindMask) + case Array, Object: + parsed := v + if v.unparsed() { + parsed = v.parse() + } + // First element/field is always cached JSON + return int(parsed.n&kindMask) - 1 + default: + panic("jsonlite: Len called on non-string/array/object value") + } +} + +// Int returns the value as a signed 64-bit integer. +// Panics if the value is not a number or if parsing fails. +func (v *Value) Int() int64 { + if v.Kind() != Number { + panic("jsonlite: Int called on non-number value") + } + i, err := strconv.ParseInt(v.json(), 10, 64) + if err != nil { + panic(err) + } + return i +} + +// Uint returns the value as an unsigned 64-bit integer. +// Panics if the value is not a number or if parsing fails. +func (v *Value) Uint() uint64 { + if v.Kind() != Number { + panic("jsonlite: Uint called on non-number value") + } + u, err := strconv.ParseUint(v.json(), 10, 64) + if err != nil { + panic(err) + } + return u +} + +// Float returns the value as a 64-bit floating point number. +// Panics if the value is not a number or if parsing fails. +func (v *Value) Float() float64 { + if v.Kind() != Number { + panic("jsonlite: Float called on non-number value") + } + f, err := strconv.ParseFloat(v.json(), 64) + if err != nil { + panic(err) + } + return f +} + +// String returns the value as a string. +// For string and number values, returns the raw value. +// For other types, returns the JSON representation. +func (v *Value) String() string { + switch v.Kind() { + case Null: + return "" + case String: + s, _ := Unquote(v.json()) + return s + case Number, True, False: + return v.json() + case Array: + return (*Value)(v.p).json() + default: + if v.unparsed() { + return v.json() + } + return (*field)(v.p).v.json() + } +} + +// JSON returns the JSON representation of the value. +func (v *Value) JSON() string { + switch v.Kind() { + case String, Number, Null, True, False: + return v.json() + case Array: + return (*Value)(v.p).json() + default: + if v.unparsed() { + return v.json() + } + return (*field)(v.p).v.json() + } +} + +// Array iterates over the array elements. +// Panics if the value is not an array. +func (v *Value) Array(yield func(*Value) bool) { + if v.Kind() != Array { + panic("jsonlite: Array called on non-array value") + } + parsed := v + if v.unparsed() { + parsed = v.parse() + } + elems := unsafe.Slice((*Value)(parsed.p), parsed.len())[1:] + for i := range elems { + if !yield(&elems[i]) { + return + } + } +} + +// Object iterates over the object's key/value pairs. +// Panics if the value is not an object. +func (v *Value) Object(yield func(string, *Value) bool) { + if v.Kind() != Object { + panic("jsonlite: Object called on non-object value") + } + parsed := v + if v.unparsed() { + parsed = v.parse() + } + fields := unsafe.Slice((*field)(parsed.p), parsed.len())[1:] + for i := range fields { + if !yield(fields[i].k, &fields[i].v) { + return + } + } +} + +// Lookup searches for a field by key in an object and returns a pointer to its value. +// Returns nil if the key is not found. +// Panics if the value is not an object. +func (v *Value) Lookup(k string) *Value { + if v.Kind() != Object { + panic("jsonlite: Lookup called on non-object value") + } + parsed := v + if v.unparsed() { + parsed = v.parse() + } + fields := unsafe.Slice((*field)(parsed.p), parsed.len()) + hashes := fields[0].k + refkey := byte(maphash.String(hashseed, k)) + offset := 0 + for { + i := strings.IndexByte(hashes[offset:], refkey) + if i < 0 { + return nil + } + j := offset + i + 1 + f := &fields[j] + if f.k == k { + return &f.v + } + offset = j + } +} + +// LookupPath searches for a nested field by following a path of keys. +// Returns nil if any key in the path is not found. +// Panics if any intermediate value is not an object. +// If path is empty, returns the value itself. +func (v *Value) LookupPath(path ...string) *Value { + for _, key := range path { + if v == nil { + return nil + } + v = v.Lookup(key) + } + return v +} + +// Index returns the value at index i in an array. +// Panics if the value is not an array or if the index is out of range. +func (v *Value) Index(i int) *Value { + if v.Kind() != Array { + panic("jsonlite: Index called on non-array value") + } + parsed := v + if v.unparsed() { + parsed = v.parse() + } + elems := unsafe.Slice((*Value)(parsed.p), parsed.len())[1:] + return &elems[i] +} + +// NumberType returns the classification of the number (int, uint, or float). +// Panics if the value is not a number. +func (v *Value) NumberType() NumberType { + if v.Kind() != Number { + panic("jsonlite: NumberType called on non-number value") + } + return NumberTypeOf(v.json()) +} + +// Number returns the value as a json.Number. +// Panics if the value is not a number. +func (v *Value) Number() json.Number { + if v.Kind() != Number { + panic("jsonlite: Number called on non-number value") + } + return json.Number(v.json()) +} + +func (v *Value) json() string { + return unsafe.String((*byte)(v.p), v.len()) +} + +func (v *Value) len() int { + return int(v.n & (kindMask &^ unparsedBit)) +} + +func (v *Value) unparsed() bool { + return (v.n & unparsedBit) != 0 +} + +func (v *Value) parse() *Value { + parsed, err := Parse(v.JSON()) + if err != nil { + panic(fmt.Errorf("jsonlite: lazy parse failed: %w", err)) + } + return parsed +} + +// NumberType represents the classification of a JSON number. +type NumberType int + +const ( + // Int indicates a signed integer number (has a leading minus sign, no decimal point or exponent). + Int NumberType = iota + // Uint indicates an unsigned integer number (no minus sign, no decimal point or exponent). + Uint + // Float indicates a floating point number (has decimal point or exponent). + Float +) + +// NumberTypeOf returns the classification of a number string. +func NumberTypeOf(s string) NumberType { + if len(s) == 0 { + return Float + } + t := Uint + if s[0] == '-' { + s = s[1:] + t = Int + } + for i := range len(s) { + switch s[i] { + case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': + continue + default: + return Float + } + } + return t +} + +func makeValue(k Kind, s string) Value { + return Value{ + p: unsafe.Pointer(unsafe.StringData(s)), + n: (uintptr(k) << kindShift) | uintptr(len(s)), + } +} + +func makeNullValue(s string) Value { return makeValue(Null, s) } + +func makeTrueValue(s string) Value { return makeValue(True, s) } + +func makeFalseValue(s string) Value { return makeValue(False, s) } + +func makeNumberValue(s string) Value { return makeValue(Number, s) } + +func makeStringValue(s string) Value { return makeValue(String, s) } + +func makeArrayValue(elements []Value) Value { + return Value{ + p: unsafe.Pointer(unsafe.SliceData(elements)), + n: (uintptr(Array) << kindShift) | uintptr(len(elements)), + } +} + +func makeObjectValue(fields []field) Value { + return Value{ + p: unsafe.Pointer(unsafe.SliceData(fields)), + n: (uintptr(Object) << kindShift) | uintptr(len(fields)), + } +} + +func makeUnparsedObjectValue(json string) Value { + return Value{ + p: unsafe.Pointer(unsafe.StringData(json)), + n: (uintptr(Object) << kindShift) | uintptr(len(json)) | unparsedBit, + } +} + +// Append serializes the Value to JSON and appends it to the buffer. +// Returns the extended buffer. +func (v *Value) Append(buf []byte) []byte { return append(buf, v.JSON()...) } + +// Compact appends a compacted JSON representation of the value to buf by recursively +// reconstructing it from the parsed structure. Unlike Append, this method does not +// use cached JSON and always regenerates the output. +func (v *Value) Compact(buf []byte) []byte { + switch v.Kind() { + case String, Null, True, False, Number: + return append(buf, v.json()...) + case Array: + parsed := v + if v.unparsed() { + parsed = v.parse() + } + buf = append(buf, '[') + var count int + for elem := range parsed.Array { + if count > 0 { + buf = append(buf, ',') + } + buf = elem.Compact(buf) + count++ + } + return append(buf, ']') + default: + parsed := v + if v.unparsed() { + parsed = v.parse() + } + buf = append(buf, '{') + var count int + for k, v := range parsed.Object { + if count > 0 { + buf = append(buf, ',') + } + buf = AppendQuote(buf, k) + buf = append(buf, ':') + buf = v.Compact(buf) + count++ + } + return append(buf, '}') + } +} diff --git a/vendor/github.com/parquet-go/parquet-go/.CLAUDE.md b/vendor/github.com/parquet-go/parquet-go/.CLAUDE.md new file mode 100644 index 000000000..37709f4df --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/.CLAUDE.md @@ -0,0 +1,160 @@ +# parquet-go Project Context + +## Overview +High-performance Go library for reading and writing Apache Parquet files. Originally developed by Twilio Segment, now community-maintained at github.com/parquet-go/parquet-go. + +- **Go Version**: 1.22+ +- **Current Version**: v0.26.0+ (pre-v1, breaking changes possible) +- **Codebase Size**: ~71,700 lines across 210 Go files + +## Project Structure + +``` +parquet-go/ +├── Root Package # Core API: Reader/Writer/File/Schema +├── encoding/ # 7 encoding formats (plain, rle, delta, etc.) +├── compress/ # 6 compression codecs (snappy, gzip, zstd, etc.) +├── bloom/ # Split-block Bloom filter (SIMD-optimized) +├── sparse/ # Sparse array utilities with gather ops +├── hashprobe/ # Hash-based dictionary operations +├── format/ # Generated Thrift definitions (parquet spec) +└── internal/ # Memory mgmt, byte algorithms, unsafe casts +``` + +## Key Files by Function + +| Purpose | Files | +|---------|-------| +| Core Types | `parquet.go`, `file.go`, `schema.go`, `node.go`, `type.go` | +| Reading | `reader.go`, `column.go`, `page.go` | +| Writing | `writer.go`, `column_buffer.go`, `buffer.go` | +| Logical Types | `type_boolean.go` through `type_variant.go` (40+ files) | +| Column Buffers | `column_buffer_*.go` (20+ files for each physical type) | +| Page Impl | `page_*.go` (15+ files) | +| Operations | `convert.go`, `merge.go`, `sorting.go` | +| Configuration | `config.go` (900+ lines) | + +## Core Types & Interfaces + +### Schema System +- `Schema` - Immutable, thread-safe parquet schema +- `Node` - Interface for schema nodes +- `Field` - Named schema node interface +- `Column` - Concrete column representation +- `Type` - Logical type interface +- `Kind` - Physical type enum (Boolean, Int32, Int64, Float, Double, ByteArray, FixedLenByteArray) + +### Reader/Writer System +- `GenericReader[T]` - Type-safe reader (preferred) +- `GenericWriter[T]` - Type-safe writer (preferred) +- `SortingWriter[T]` - Writer with integrated sorting +- `GenericBuffer[T]` - In-memory row group buffer (implements sort.Interface) + +### Row Group Abstractions +- `RowGroup` - Interface for row group collections +- `ColumnChunk` - Interface for column data +- `Page` - Interface for page data with stats +- `Pages` - Sequential page reader + +## Common Patterns + +### Writing Data +```go +// One-shot +parquet.WriteFile[T](path, rows, options...) + +// Streaming +writer := parquet.NewGenericWriter[T](w, options...) +writer.Write(rows) +writer.Close() + +// With sorting +sortWriter := parquet.NewSortingWriter[T](w, rowCount, options...) +``` + +### Reading Data +```go +// One-shot +rows, _ := parquet.ReadFile[T](path) + +// Streaming +reader := parquet.NewGenericReader[T](r, options...) +n, _ := reader.Read(rows) + +// Low-level +file, _ := parquet.OpenFile(r, size) +for _, rg := range file.RowGroups() { ... } +``` + +## Build Commands + +```bash +make test # Run tests with -race and coverage +make format # Go fmt + modernize tool +make tools # Install development tools +``` + +## Testing Patterns + +- Unit tests: `*_test.go` throughout (100+ files) +- Examples: `example_test.go` +- Property tests: `internal/quick` package +- Key test files: + - `parquet_test.go` - Core functionality + - `writer_test.go` - Writer specifics (91KB) + - `reader_test.go` - Reader specifics + - `merge_test.go` - Merge operations + - `convert_test.go` - Schema conversion + +## Debugging + +Set environment variable: +```bash +PARQUETGODEBUG=1 +``` + +## Important Implementation Details + +### Performance Optimizations +- SIMD assembly for AMD64 (dictionary ops, page bounds, ordering) +- Zero-copy via interface-based design +- Memory pooling with `BufferPool` +- Async reading with `ReadModeAsync` + +### Schema Tags +Struct field tags control parquet behavior: +```go +type Record struct { + ID int64 `parquet:"id"` + Name string `parquet:"name,optional"` + Data []byte `parquet:"data,snappy"` +} +``` + +### Encoding Options +- plain, rle, delta (binary-packed, length byte array, byte array) +- bytestreamsplit (float optimization) +- dictionary encoding + +### Compression Codecs +snappy, gzip, brotli, zstd, lz4, uncompressed + +## Recent Development Focus +- Bug fixes: panic in Group.GoType(), json.RawMessage handling, repetition levels +- New features: Geometry/Geography types, VARIANT logical type +- Performance: GenericWriter optimizations +- Stability: SortingWriter improvements + +## Common Bug Areas +- Schema conversion edge cases (`convert.go`) +- Nested structure handling (repetition/definition levels) +- Memory management in streaming writes +- Page statistics and index handling +- Dictionary encoding with nulls +- Interface type handling in dynamic value mapping (`value.go`, `row.go`) + +## Dependencies (Runtime) +- Compression: brotli, gzip, lz4, zstd libraries +- Encoding: bitpack, jsonlite +- Types: google/uuid, go-geom (geometry) +- Serialization: protobuf diff --git a/vendor/github.com/parquet-go/parquet-go/.gitattributes b/vendor/github.com/parquet-go/parquet-go/.gitattributes new file mode 100644 index 000000000..75b22ee8e --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/.gitattributes @@ -0,0 +1,2 @@ +internal/gen-go/* linguist-generated=true +testdata/gen/** binary linguist-generated=true \ No newline at end of file diff --git a/vendor/github.com/parquet-go/parquet-go/.gitignore b/vendor/github.com/parquet-go/parquet-go/.gitignore new file mode 100644 index 000000000..b3584c8d4 --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/.gitignore @@ -0,0 +1,21 @@ +# Binaries for programs and plugins +*.exe +*.exe~ +*.dll +*.so +*.dylib +*.py + +# Test binary, built with `go test -c` +*.test + +# Output of the go coverage tool, specifically when used with LiteIDE +*.out + +# Dependency directories (remove the comment below to include it) +# vendor/ + +# Emacs +*~ +#*# +.# diff --git a/vendor/github.com/parquet-go/parquet-go/.mailmap b/vendor/github.com/parquet-go/parquet-go/.mailmap new file mode 100644 index 000000000..09d89739a --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/.mailmap @@ -0,0 +1,2 @@ +Achille Roussel Achille +Thomas Pelletier Thomas Pelletier diff --git a/vendor/github.com/parquet-go/parquet-go/.words b/vendor/github.com/parquet-go/parquet-go/.words new file mode 100644 index 000000000..d7ff36556 --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/.words @@ -0,0 +1,27 @@ + +RowType +Twilio +bottlenecked +decompressors +int96 +millis +nanos +reindexing +repositions +schemas +ColumnPages +PageIndex +Zstandard +xxHash +cardinality +enums +32bit +dic +Blart +Versenwald +purego +stdlib +unscaled +cespare +bitset +checksumming diff --git a/vendor/github.com/parquet-go/parquet-go/AUTHORS.txt b/vendor/github.com/parquet-go/parquet-go/AUTHORS.txt new file mode 100644 index 000000000..26669538c --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/AUTHORS.txt @@ -0,0 +1,5 @@ +Achille Roussel +Frederic Branczyk +Julien Fabre +Kevin Burke +Thomas Pelletier diff --git a/vendor/github.com/parquet-go/parquet-go/CHANGELOG.md b/vendor/github.com/parquet-go/parquet-go/CHANGELOG.md new file mode 100644 index 000000000..987597104 --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/CHANGELOG.md @@ -0,0 +1,16 @@ + # v0.17.0 + + ## Breaking Changes + + - migrate to module github.com/parquet-go/parquet-go [#3](https://github.com/parquet-go/parquet-go/pull/3) @kevinburke + - drop support for `go1.17` [#16](https://github.com/parquet-go/parquet-go/pull/16) @gernest + + ## Bug fixes + + - fix error handling when reading from io.ReaderAt [#18](https://github.com/parquet-go/parquet-go/pull/18) @gernest + - fix zero value of nested field point [#9](https://github.com/parquet-go/parquet-go/pull/9) @gernest + - fix memory corruption in `MergeRowGroups` [#31](https://github.com/parquet-go/parquet-go/pull/31) @gernest + + ## Enhancements + - performance improvement on GenericReader [#17](https://github.com/parquet-go/parquet-go/pull/17) @gernest, @zolstein + - stabilize flakey `TestOpenFile` [#11](https://github.com/parquet-go/parquet-go/pull/11) @gernest diff --git a/vendor/github.com/parquet-go/parquet-go/CODEOWNERS b/vendor/github.com/parquet-go/parquet-go/CODEOWNERS new file mode 100644 index 000000000..45632dfe3 --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/CODEOWNERS @@ -0,0 +1 @@ +* @achille-roussel @fpetkovski @joe-elliott @thorfour diff --git a/vendor/github.com/parquet-go/parquet-go/CODE_OF_CONDUCT.md b/vendor/github.com/parquet-go/parquet-go/CODE_OF_CONDUCT.md new file mode 100644 index 000000000..2f0727ed5 --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/CODE_OF_CONDUCT.md @@ -0,0 +1,73 @@ +# Contributor Covenant Code of Conduct + +## Our Pledge + +In the interest of fostering an open and welcoming environment, we as +contributors and maintainers pledge to making participation in our project and +our community a harassment-free experience for everyone, regardless of age, body +size, disability, ethnicity, sex characteristics, gender identity and expression, +level of experience, education, socio-economic status, nationality, personal +appearance, race, religion, or sexual identity and orientation. + +## Our Standards + +Examples of behavior that contributes to creating a positive environment +include: + +- Using welcoming and inclusive language +- Being respectful of differing viewpoints and experiences +- Gracefully accepting constructive criticism +- Focusing on what is best for the community +- Showing empathy towards other community members + +Examples of unacceptable behavior by participants include: + +- The use of sexualized language or imagery and unwelcome sexual attention or + advances +- Trolling, insulting/derogatory comments, and personal or political attacks +- Public or private harassment +- Publishing others' private information, such as a physical or electronic + address, without explicit permission +- Other conduct which could reasonably be considered inappropriate in a + professional setting + +## Our Responsibilities + +Project maintainers are responsible for clarifying the standards of acceptable +behavior and are expected to take appropriate and fair corrective action in +response to any instances of unacceptable behavior. + +Project maintainers have the right and responsibility to remove, edit, or +reject comments, commits, code, wiki edits, issues, and other contributions +that are not aligned to this Code of Conduct, or to ban temporarily or +permanently any contributor for other behaviors that they deem inappropriate, +threatening, offensive, or harmful. + +## Scope + +This Code of Conduct applies both within project spaces and in public spaces +when an individual is representing the project or its community. Examples of +representing a project or community include using an official project e-mail +address, posting via an official social media account, or acting as an appointed +representative at an online or offline event. Representation of a project may be +further defined and clarified by project maintainers. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be +reported by contacting the project team at open-source@twilio.com. All +complaints will be reviewed and investigated and will result in a response that +is deemed necessary and appropriate to the circumstances. The project team is +obligated to maintain confidentiality with regard to the reporter of an incident. +Further details of specific enforcement policies may be posted separately. + +Project maintainers who do not follow or enforce the Code of Conduct in good +faith may face temporary or permanent repercussions as determined by other +members of the project's leadership. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, +available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html + +[homepage]: https://www.contributor-covenant.org diff --git a/vendor/github.com/parquet-go/parquet-go/CONTRIBUTING.md b/vendor/github.com/parquet-go/parquet-go/CONTRIBUTING.md new file mode 100644 index 000000000..185d95811 --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/CONTRIBUTING.md @@ -0,0 +1,52 @@ +# Contributing to segmentio/parquet + +## Code of Conduct + +Help us keep the project open and inclusive. Please be kind to and +considerate of other developers, as we all have the same goal: make +the project as good as it can be. + +* [Code of Conduct](./CODE_OF_CONDUCT.md) + +## Licensing + +All third party contributors acknowledge that any contributions they provide +will be made under the same open source license that the open source project +is provided under. + +## Contributing + +* Open an Issue to report bugs or discuss non-trivial changes. +* Open a Pull Request to submit a code change for review. + +### Guidelines for code review + +It's good to do code review but we are busy and it's bad to wait for consensus +or opinions that might not arrive. Here are some guidelines + +#### Changes where code review is optional + +- Documentation changes +- Bug fixes where a reproducible test case exists +- Keeping the lights on style work (compat with new Parquet versions, new Go + versions, for example) +- Updates to the CI environment +- Adding additional benchmarks or test cases +- Pull requests that have been open for 30 days, where an attempt has been made + to contact another code owner, and no one has expressly requested changes + +#### Changes that should get at least one code review from an owner + +- Changes that may affect the performance of core library functionality + (serializing, deserializing Parquet data) by more than 2% +- Behavior changes +- New API or changes to existing API + +### Coding Rules + +To ensure consistency throughout the source code, keep these rules in mind +when submitting contributions: + +* All features or bug fixes must be tested by one or more tests. +* All exported types, functions, and symbols must be documented. +* All code must be formatted with `go fmt`. diff --git a/vendor/github.com/parquet-go/parquet-go/LICENSE b/vendor/github.com/parquet-go/parquet-go/LICENSE new file mode 100644 index 000000000..251c7e515 --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/LICENSE @@ -0,0 +1,213 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2023 Twilio, Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +-------------------------------------------------------------------------------- + +This product includes code from Apache Parquet. + +* deprecated/parquet.go is based on Apache Parquet's thrift file +* format/parquet.go is based on Apache Parquet's thrift file + +Copyright: 2014 The Apache Software Foundation. +Home page: https://github.com/apache/parquet-format +License: http://www.apache.org/licenses/LICENSE-2.0 diff --git a/vendor/github.com/parquet-go/parquet-go/Makefile b/vendor/github.com/parquet-go/parquet-go/Makefile new file mode 100644 index 000000000..e3bfd8bc2 --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/Makefile @@ -0,0 +1,15 @@ +.PHONY: format + +AUTHORS.txt: .mailmap + go install github.com/kevinburke/write_mailmap@latest + write_mailmap > AUTHORS.txt + +tools: + go mod tidy -modfile go.tools.mod + +format: tools + go fmt ./... + go tool -modfile go.tools.mod modernize -fix -test ./... + +test: + go test -v -trimpath -race -cover -tags= ./... diff --git a/vendor/github.com/parquet-go/parquet-go/README.md b/vendor/github.com/parquet-go/parquet-go/README.md new file mode 100644 index 000000000..0439ea67b --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/README.md @@ -0,0 +1,688 @@ +
+ +
+

parquet-go/parquet-go

+

+High-performance Go library to manipulate parquet files, initially developed at +Twilio Segment. +

+ +
+ +## Motivation + +Parquet has been established as a powerful solution to represent columnar data +on persistent storage mediums, achieving levels of compression and query +performance that enable managing data sets at scales that reach the petabytes. +In addition, having intensive data applications sharing a common format creates +opportunities for interoperation in our tool kits, providing greater leverage +and value to engineers maintaining and operating those systems. + +The creation and evolution of large scale data management systems, combined with +realtime expectations come with challenging maintenance and performance +requirements, that existing solutions to use parquet with Go were not addressing. + +The `parquet-go/parquet-go` package was designed and developed to respond to those +challenges, offering high level APIs to read and write parquet files, while +keeping a low compute and memory footprint in order to be used in environments +where data volumes and cost constraints require software to achieve high levels +of efficiency. + +## Specification + +Columnar storage allows Parquet to store data more efficiently than, say, +using JSON or Protobuf. For more information, refer to the [Parquet Format Specification](https://github.com/apache/parquet-format). + +## Installation + +The package is distributed as a standard Go module that programs can take a +dependency on and install with the following command: + +```bash +go get github.com/parquet-go/parquet-go +``` + +Go 1.22 or later is required to use the package. + +### Compatibility Guarantees + +The package is currently released as a pre-v1 version, which gives maintainers +the freedom to break backward compatibility to help improve the APIs as we learn +which initial design decisions would need to be revisited to better support the +use cases that the library solves for. These occurrences are expected to be rare +in frequency and documentation will be produce to guide users on how to adapt +their programs to breaking changes. + +## Usage + +The following sections describe how to use APIs exposed by the library, +highlighting the use cases with code examples to demonstrate how they are used +in practice. + +### Struct Tags + +When using Go structs to define the schema of parquet files, struct fields may +include a `parquet` tag to configure properties of the parquet column such as +its name, compression, encoding, and logical type. The first value in the tag +sets the column name, and additional comma-separated values set options. + +```go +type Record struct { + ID int64 `parquet:"id,delta"` + Name string `parquet:"name,dict,zstd"` + Timestamp int64 `parquet:"timestamp,timestamp(microsecond)"` + Score float64 `parquet:"score,split"` + Tags []string `parquet:"tags,list"` + Optional *string `parquet:"optional,optional"` +} +``` + +Map keys and values can be configured with the `parquet-key` and `parquet-value` +tags, and list elements with the `parquet-element` tag. + +For the full reference of supported tags, type constraints, and examples, see +the [`SchemaOf`](https://pkg.go.dev/github.com/parquet-go/parquet-go#SchemaOf) +documentation. + +### Variant Types + +The library supports the [Parquet VARIANT logical type](https://github.com/apache/parquet-format/blob/master/LogicalTypes.md#variant) +for storing semi-structured data (JSON-like values) in a columnar format. + +The simplest way to use variants is with the `variant` struct tag, which +automatically marshals and unmarshals Go values to/from the variant binary +format: + +```go +type Event struct { + ID int64 `parquet:"id"` + Data any `parquet:"data,variant"` +} + +writer := parquet.NewGenericWriter[Event](output) +writer.Write([]Event{ + {ID: 1, Data: "hello"}, + {ID: 2, Data: int32(42)}, + {ID: 3, Data: map[string]any{"key": "value"}}, +}) +writer.Close() +``` + +For shredded variants (which store typed columns alongside the raw value for +faster queries), build a schema with `parquet.ShreddedVariant()`: + +```go +shreddedType, err := parquet.ShreddedVariant(parquet.String()) +schema := parquet.NewSchema("Record", parquet.Group{ + "data": shreddedType, +}) +writer := parquet.NewGenericWriter[Record](output, schema) +``` + +For low-level access to raw variant bytes, use a struct with `Metadata` and +`Value` []byte fields: + +```go +type VariantData struct { + Metadata []byte `parquet:"metadata"` + Value []byte `parquet:"value"` +} +``` + +For more examples, see the `ExampleVariant` and `ExampleShreddedVariant` +functions in `example_variant_test.go`. + +### Writing Parquet Files: [parquet.GenericWriter[T]](https://pkg.go.dev/github.com/parquet-go/parquet-go#GenericWriter) + +A parquet file is a collection of rows sharing the same schema, arranged in +columns to support faster scan operations on subsets of the data set. + +For simple use cases, the `parquet.WriteFile[T]` function allows the creation +of parquet files on the file system from a slice of Go values representing the +rows to write to the file. + +```go +type RowType struct { FirstName, LastName string } + +if err := parquet.WriteFile("file.parquet", []RowType{ + {FirstName: "Bob"}, + {FirstName: "Alice"}, +}); err != nil { + ... +} +``` + +The `parquet.GenericWriter[T]` type denormalizes rows into columns, then encodes +the columns into a parquet file, generating row groups, column chunks, and pages +based on configurable heuristics. + +```go +type RowType struct { FirstName, LastName string } + +writer := parquet.NewGenericWriter[RowType](output) + +_, err := writer.Write([]RowType{ + ... +}) +if err != nil { + ... +} + +// Closing the writer is necessary to flush buffers and write the file footer. +if err := writer.Close(); err != nil { + ... +} +``` + +Explicit declaration of the parquet schema on a writer is useful when the +application needs to ensure that data written to a file adheres to a predefined +schema, which may differ from the schema derived from the writer's type +parameter. The `parquet.Schema` type is a in-memory representation of the schema +of parquet rows, translated from the type of Go values, and can be used for this +purpose. + +```go +schema := parquet.SchemaOf(new(RowType)) +writer := parquet.NewGenericWriter[any](output, schema) +... +``` + +### Reading Parquet Files: [parquet.GenericReader[T]](https://pkg.go.dev/github.com/parquet-go/parquet-go#GenericReader) + +For simple use cases where the data set fits in memory and the program will +read most rows of the file, the `parquet.ReadFile[T]` function returns a slice +of Go values representing the rows read from the file. + +```go +type RowType struct { FirstName, LastName string } + +rows, err := parquet.ReadFile[RowType]("file.parquet") +if err != nil { + ... +} + +for _, c := range rows { + fmt.Printf("%+v\n", c) +} +``` + +The expected schema of rows can be explicitly declared when the reader is +constructed, which is useful to ensure that the program receives rows matching +an specific format; for example, when dealing with files from remote storage +sources that applications cannot trust to have used an expected schema. + +Configuring the schema of a reader is done by passing a `parquet.Schema` +instance as argument when constructing a reader. When the schema is declared, +conversion rules implemented by the package are applied to ensure that rows +read by the application match the desired format (see **Evolving Parquet Schemas**). + +```go +schema := parquet.SchemaOf(new(RowType)) +reader := parquet.NewReader(file, schema) +... +``` + +### Inspecting Parquet Files: [parquet.File](https://pkg.go.dev/github.com/parquet-go/parquet-go#File) + +Sometimes, lower-level APIs can be useful to leverage the columnar layout of +parquet files. The `parquet.File` type is intended to provide such features to +Go applications, by exposing APIs to iterate over the various parts of a +parquet file. + +```go +f, err := parquet.OpenFile(file, size) +if err != nil { + ... +} + +for _, rowGroup := range f.RowGroups() { + for _, columnChunk := range rowGroup.ColumnChunks() { + ... + } +} +``` + +### Evolving Parquet Schemas: [parquet.Convert](https://pkg.go.dev/github.com/parquet-go/parquet-go#Convert) + +Parquet files embed all the metadata necessary to interpret their content, +including a description of the schema of the tables represented by the rows and +columns they contain. + +Parquet files are also immutable; once written, there is not mechanism for +_updating_ a file. If their contents need to be changed, rows must be read, +modified, and written to a new file. + +Because applications evolve, the schema written to parquet files also tend to +evolve over time. Those requirements creating challenges when applications need +to operate on parquet files with heterogenous schemas: algorithms that expect +new columns to exist may have issues dealing with rows that come from files with +mismatching schema versions. + +To help build applications that can handle evolving schemas, `parquet-go/parquet-go` +implements conversion rules that create views of row groups to translate between +schema versions. + +The `parquet.Convert` function is the low-level routine constructing conversion +rules from a source to a target schema. The function is used to build converted +views of `parquet.RowReader` or `parquet.RowGroup`, for example: + +```go +type RowTypeV1 struct { ID int64; FirstName string } +type RowTypeV2 struct { ID int64; FirstName, LastName string } + +source := parquet.SchemaOf(RowTypeV1{}) +target := parquet.SchemaOf(RowTypeV2{}) + +conversion, err := parquet.Convert(target, source) +if err != nil { + ... +} + +targetRowGroup := parquet.ConvertRowGroup(sourceRowGroup, conversion) +... +``` + +Conversion rules are automatically applied by the `parquet.CopyRows` function +when the reader and writers passed to the function also implement the +`parquet.RowReaderWithSchema` and `parquet.RowWriterWithSchema` interfaces. +The copy determines whether the reader and writer schemas can be converted from +one to the other, and automatically applies the conversion rules to facilitate +the translation between schemas. + +At this time, conversion rules only supports adding or removing columns from +the schemas, there are no type conversions performed, nor ways to rename +columns, etc... More advanced conversion rules may be added in the future. + +### Sorting Row Groups: [parquet.GenericBuffer[T]](https://pkg.go.dev/github.com/parquet-go/parquet-go#Buffer) + +The `parquet.GenericWriter[T]` type is optimized for minimal memory usage, +keeping the order of rows unchanged and flushing pages as soon as they are filled. + +Parquet supports expressing columns by which rows are sorted through the +declaration of _sorting columns_ on row groups. Sorting row groups requires +buffering all rows before ordering and writing them to a parquet file. + +To help with those use cases, the `parquet-go/parquet-go` package exposes the +`parquet.GenericBuffer[T]` type which acts as a buffer of rows and implements +`sort.Interface` to allow applications to sort rows prior to writing them +to a file. + +The columns that rows are ordered by are configured when creating +`parquet.GenericBuffer[T]` instances using the `parquet.SortingColumns` function +to construct row group options configuring the buffer. The type of parquet +columns defines how values are compared, see [Parquet Logical Types](https://github.com/apache/parquet-format/blob/master/LogicalTypes.md) +for details. + +When written to a file, the buffer is materialized into a single row group with +the declared sorting columns. After being written, buffers can be reused by +calling their `Reset` method. + +The following example shows how to use a `parquet.GenericBuffer[T]` to order rows +written to a parquet file: + +```go +type RowType struct { FirstName, LastName string } + +buffer := parquet.NewGenericBuffer[RowType]( + parquet.SortingRowGroupConfig( + parquet.SortingColumns( + parquet.Ascending("LastName"), + parquet.Ascending("FistName"), + ), + ), +) + +buffer.Write([]RowType{ + {FirstName: "Luke", LastName: "Skywalker"}, + {FirstName: "Han", LastName: "Solo"}, + {FirstName: "Anakin", LastName: "Skywalker"}, +}) + +sort.Sort(buffer) + +writer := parquet.NewGenericWriter[RowType](output) +_, err := parquet.CopyRows(writer, buffer.Rows()) +if err != nil { + ... +} +if err := writer.Close(); err != nil { + ... +} +``` + +### Merging Row Groups: [parquet.MergeRowGroups](https://pkg.go.dev/github.com/parquet-go/parquet-go#MergeRowGroups) + +Parquet files are often used as part of the underlying engine for data +processing or storage layers, in which cases merging multiple row groups +into one that contains more rows can be a useful operation to improve query +performance; for example, bloom filters in parquet files are stored for each +row group, the larger the row group, the fewer filters need to be stored and +the more effective they become. + +The `parquet-go/parquet-go` package supports creating merged views of row groups, +where the view contains all the rows of the merged groups, maintaining the order +defined by the sorting columns of the groups. + +There are a few constraints when merging row groups: + +- The sorting columns of all the row groups must be the same, or the merge + operation must be explicitly configured a set of sorting columns which are + a prefix of the sorting columns of all merged row groups. + +- The schemas of row groups must all be equal, or the merge operation must + be explicitly configured with a schema that all row groups can be converted + to, in which case the limitations of schema conversions apply. + +Once a merged view is created, it may be written to a new parquet file or buffer +in order to create a larger row group: + +```go +merge, err := parquet.MergeRowGroups(rowGroups) +if err != nil { + ... +} + +writer := parquet.NewGenericWriter[RowType](output) +_, err := parquet.CopyRows(writer, merge.Rows()) +if err != nil { + ... +} +if err := writer.Close(); err != nil { + ... +} +``` + +### Using Bloom Filters: [parquet.BloomFilter](https://pkg.go.dev/github.com/parquet-go/parquet-go#BloomFilter) + +Parquet files can embed bloom filters to help improve the performance of point +lookups in the files. The format of parquet bloom filters is documented in +the parquet specification: [Parquet Bloom Filter](https://github.com/apache/parquet-format/blob/master/BloomFilter.md) + +By default, no bloom filters are created in parquet files, but applications can +configure the list of columns to create filters for using the `parquet.BloomFilters` +option when instantiating writers; for example: + +```go +type RowType struct { + FirstName string `parquet:"first_name"` + LastName string `parquet:"last_name"` +} + +const filterBitsPerValue = 10 +writer := parquet.NewGenericWriter[RowType](output, + parquet.BloomFilters( + // Configures the write to generate split-block bloom filters for the + // "first_name" and "last_name" columns of the parquet schema of rows + // witten by the application. + parquet.SplitBlockFilter(filterBitsPerValue, "first_name"), + parquet.SplitBlockFilter(filterBitsPerValue, "last_name"), + ), +) +... +``` + +Generating bloom filters requires to know how many values exist in a column +chunk in order to properly size the filter, which requires buffering all the +values written to the column in memory. Because of it, the memory footprint +of `parquet.GenericWriter[T]` increases linearly with the number of columns +that the writer needs to generate filters for. This extra cost is optimized +away when rows are copied from a `parquet.GenericBuffer[T]` to a writer, since +in this case the number of values per column in known since the buffer already +holds all the values in memory. + +When reading parquet files, column chunks expose the generated bloom filters +with the `parquet.ColumnChunk.BloomFilter` method, returning a +`parquet.BloomFilter` instance if a filter was available, or `nil` when there +were no filters. + +Using bloom filters in parquet files is useful when performing point-lookups in +parquet files; searching for column rows matching a given value. Programs can +quickly eliminate column chunks that they know does not contain the value they +search for by checking the filter first, which is often multiple orders of +magnitude faster than scanning the column. + +The following code snippet hilights how filters are typically used: + +```go +var candidateChunks []parquet.ColumnChunk + +for _, rowGroup := range file.RowGroups() { + columnChunk := rowGroup.ColumnChunks()[columnIndex] + bloomFilter := columnChunk.BloomFilter() + + if bloomFilter != nil { + if ok, err := bloomFilter.Check(value); err != nil { + ... + } else if !ok { + // Bloom filters may return false positives, but never return false + // negatives, we know this column chunk does not contain the value. + continue + } + } + + candidateChunks = append(candidateChunks, columnChunk) +} +``` + +## Optimizations + +The following sections describe common optimization techniques supported by the +library. + +### Optimizing Reads + +Lower level APIs used to read parquet files offer more efficient ways to access +column values. Consecutive sequences of values are grouped into pages which are +represented by the `parquet.Page` interface. + +A column chunk may contain multiple pages, each holding a section of the column +values. Applications can retrieve the column values either by reading them into +buffers of `parquet.Value`, or type asserting the pages to read arrays of +primitive Go values. The following example demonstrates how to use both +mechanisms to read column values: + +```go +pages := column.Pages() +defer func() { + checkErr(pages.Close()) +}() + +for { + p, err := pages.ReadPage() + if err != nil { + ... // io.EOF when there are no more pages + } + + switch page := p.Values().(type) { + case parquet.Int32Reader: + values := make([]int32, page.NumValues()) + _, err := page.ReadInt32s(values) + ... + case parquet.Int64Reader: + values := make([]int64, page.NumValues()) + _, err := page.ReadInt64s(values) + ... + default: + values := make([]parquet.Value, page.NumValues()) + _, err := page.ReadValues(values) + ... + } +} +``` + +Reading arrays of typed values is often preferable when performing aggregations +on the values as this model offers a more compact representation of the values +in memory, and pairs well with the use of optimizations like SIMD vectorization. + +### Optimizing Writes + +Applications that deal with columnar storage are sometimes designed to work with +columnar data throughout the abstraction layers; it then becomes possible to +write columns of values directly instead of reconstructing rows from the column +values. The package offers two main mechanisms to satisfy those use cases: + +#### A. Writing Columns of Typed Arrays + +The first solution assumes that the program works with in-memory arrays of typed +values, for example slices of primitive Go types like `[]float32`; this would be +the case if the application is built on top of a framework like +[Apache Arrow](https://pkg.go.dev/github.com/apache/arrow/go/arrow). + +`parquet.GenericBuffer[T]` is an implementation of the `parquet.RowGroup` +interface which maintains in-memory buffers of column values. Rows can be +written by either boxing primitive values into arrays of `parquet.Value`, +or type asserting the columns to a access specialized versions of the write +methods accepting arrays of Go primitive types. + +When using either of these models, the application is responsible for ensuring +that the same number of rows are written to each column or the resulting parquet +file will be malformed. + +The following examples demonstrate how to use these two models to write columns +of Go values: + +```go +type RowType struct { FirstName, LastName string } + +func writeColumns(buffer *parquet.GenericBuffer[RowType], firstNames []string) error { + values := make([]parquet.Value, len(firstNames)) + for i := range firstNames { + values[i] = parquet.ValueOf(firstNames[i]) + } + _, err := buffer.ColumnBuffers()[0].WriteValues(values) + return err +} +``` + +```go +type RowType struct { ID int64; Value float32 } + +func writeColumns(buffer *parquet.GenericBuffer[RowType], ids []int64, values []float32) error { + if len(ids) != len(values) { + return fmt.Errorf("number of ids and values mismatch: ids=%d values=%d", len(ids), len(values)) + } + columns := buffer.ColumnBuffers() + if err := columns[0].(parquet.Int64Writer).WriteInt64s(ids); err != nil { + return err + } + if err := columns[1].(parquet.FloatWriter).WriteFloats(values); err != nil { + return err + } + return nil +} +``` + +The latter is more efficient as it does not require boxing the input into an +intermediary array of `parquet.Value`. However, it may not always be the right +model depending on the situation, sometimes the generic abstraction can be a +more expressive model. + +#### B. Implementing parquet.RowGroup + +Programs that need full control over the construction of row groups can choose +to provide their own implementation of the `parquet.RowGroup` interface, which +includes defining implementations of `parquet.ColumnChunk` and `parquet.Page` +to expose column values of the row group. + +This model can be preferable when the underlying storage or in-memory +representation of the data needs to be optimized further than what can be +achieved by using an intermediary buffering layer with `parquet.GenericBuffer[T]`. + +See [parquet.RowGroup](https://pkg.go.dev/github.com/parquet-go/parquet-go#RowGroup) +for the full interface documentation. + +#### C. Using on-disk page buffers + +When generating parquet files, the writer needs to buffer all pages before it +can create the row group. This may require significant amounts of memory as the +entire file content must be buffered prior to generating it. In some cases, the +files might even be larger than the amount of memory available to the program. + +The `parquet.GenericWriter[T]` can be configured to use disk storage instead as +a scratch buffer when generating files, by configuring a different page buffer +pool using the `parquet.ColumnPageBuffers` option and `parquet.PageBufferPool` +interface. + +The `parquet-go/parquet-go` package provides an implementation of the interface +which uses temporary files to store pages while a file is generated, allowing +programs to use local storage as swap space to hold pages and keep memory +utilization to a minimum. The following example demonstrates how to configure +a parquet writer to use on-disk page buffers: + +```go +type RowType struct { ... } + +writer := parquet.NewGenericWriter[RowType](output, + parquet.ColumnPageBuffers( + parquet.NewFileBufferPool("", "buffers.*"), + ), +) +``` + +When a row group is complete, pages buffered to disk need to be copied back to +the output file. This results in doubling I/O operations and storage space +requirements (the system needs to have enough free disk space to hold two copies +of the file). The resulting write amplification can often be optimized away by +the kernel if the file system supports copy-on-write of disk pages since copies +between `os.File` instances are optimized using `copy_file_range(2)` (on linux). + +See [parquet.PageBufferPool](https://pkg.go.dev/github.com/parquet-go/parquet-go#PageBufferPool) +for the full interface documentation. + +#### D. Parallel Column Writes + +For applications that need to maximize throughput when writing large columnar datasets, the library supports writing columns in parallel. This is especially useful when each column can be prepared independently and written concurrently, leveraging multiple CPU cores. + +You can use Go's goroutines to write to each column's `ColumnWriter` in parallel. Each column's values can be written using `WriteRowValues`, and the column must be closed after writing. It is the application's responsibility to ensure that all columns receive the same number of rows, as mismatched row counts will result in malformed files. + +Example: + +```go +columns = writer.ColumnWriters() +var ( + wg sync.WaitGroup + errs = make([]error, len(columns)) + rowCounts = make([]int, len(columns)) +) +for i, col := range columns { + wg.Add(1) + go func(i int, col parquet.ColumnWriter) { + defer wg.Done() + n, err := col.WriteRowValues(values[i]) // values[i] is []parquet.Value for column i + if err != nil { + errs[i] = err + return + } + rowCounts[i] = n + errs[i] = col.Close() + }(i, col) +} +wg.Wait() +// Check errs and rowCounts for consistency +``` + +This approach can significantly reduce the time required to write wide tables or large datasets, especially on multi-core systems. However, you should ensure proper error handling and synchronization, as shown above. + +## Maintenance + +While initial design and development occurred at Twilio Segment, the project is now maintained by the open source community. We welcome external contributors. +to participate in the form of discussions or code changes. Please review to the +[Contribution](./CONTRIBUTING.md) guidelines as well as the [Code of Conduct](./CODE_OF_CONDUCT.md) +before submitting contributions. + +### Continuous Integration + +The project uses [Github Actions](https://github.com/parquet-go/parquet-go/actions) for CI. + +### Debugging + +The package has debugging capabilities built in which can be turned on using the +`PARQUETGODEBUG` environment variable. The value follows a model similar to +`GODEBUG`, it must be formatted as a comma-separated list of `key=value` pairs. + +The following debug flag are currently supported: + +- `tracebuf=1` turns on tracing of internal buffers, which validates that + reference counters are set to zero when buffers are reclaimed by the garbage + collector. When the package detects that a buffer was leaked, it logs an error + message along with the stack trace captured when the buffer was last used. diff --git a/vendor/github.com/parquet-go/parquet-go/allocator.go b/vendor/github.com/parquet-go/parquet-go/allocator.go new file mode 100644 index 000000000..0c1c3ae26 --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/allocator.go @@ -0,0 +1,64 @@ +package parquet + +import ( + "unsafe" + + "github.com/parquet-go/bitpack/unsafecast" +) + +type allocator struct{ buffer []byte } + +func (a *allocator) makeBytes(n int) []byte { + if free := cap(a.buffer) - len(a.buffer); free < n { + newCap := 2 * cap(a.buffer) + if newCap == 0 { + newCap = 4096 + } + for newCap < n { + newCap *= 2 + } + a.buffer = make([]byte, 0, newCap) + } + + i := len(a.buffer) + j := len(a.buffer) + n + a.buffer = a.buffer[:j] + return a.buffer[i:j:j] +} + +func (a *allocator) copyBytes(v []byte) []byte { + b := a.makeBytes(len(v)) + copy(b, v) + return b +} + +func (a *allocator) copyString(v string) string { + b := a.makeBytes(len(v)) + copy(b, v) + return unsafecast.String(b) +} + +func (a *allocator) reset() { + a.buffer = a.buffer[:0] +} + +// rowAllocator is a memory allocator used to make a copy of rows referencing +// memory buffers that parquet-go does not have ownership of. +// +// This type is used in the implementation of various readers and writers that +// need to capture rows passed to the ReadRows/WriteRows methods. Copies to a +// local buffer is necessary in those cases to repect the reader/writer +// contracts that do not allow the implementations to retain the rows they +// are passed as arguments. +// +// See: RowBuffer, NewRowGroupRowReader, NewColumnChunkRowReader +type rowAllocator struct{ allocator } + +func (a *rowAllocator) capture(row Row) { + for i, v := range row { + switch v.Kind() { + case ByteArray, FixedLenByteArray: + row[i].ptr = unsafe.SliceData(a.copyBytes(v.byteArray())) + } + } +} diff --git a/vendor/github.com/parquet-go/parquet-go/array.go b/vendor/github.com/parquet-go/parquet-go/array.go new file mode 100644 index 000000000..4b9dea766 --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/array.go @@ -0,0 +1,27 @@ +package parquet + +import ( + "unsafe" + + "github.com/parquet-go/parquet-go/sparse" +) + +// makeArrayVlaue constructs a sparse.Array from a slice of Value, +// using the offset to locate the field within the Value struct that +// the sparse array is indexing. +func makeArrayValue(values []Value, offset uintptr) sparse.Array { + ptr := unsafe.Pointer(unsafe.SliceData(values)) + return sparse.UnsafeArray(unsafe.Add(ptr, offset), len(values), unsafe.Sizeof(Value{})) +} + +func makeArray(base unsafe.Pointer, length int, offset uintptr) sparse.Array { + return sparse.UnsafeArray(base, length, offset) +} + +func makeArrayFromSlice[T any](s []T) sparse.Array { + return makeArray(unsafe.Pointer(unsafe.SliceData(s)), len(s), unsafe.Sizeof(*new(T))) +} + +func makeArrayFromPointer[T any](v *T) sparse.Array { + return makeArray(unsafe.Pointer(v), 1, unsafe.Sizeof(*v)) +} diff --git a/vendor/github.com/parquet-go/parquet-go/bitmap.go b/vendor/github.com/parquet-go/parquet-go/bitmap.go new file mode 100644 index 000000000..5f2c121b2 --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/bitmap.go @@ -0,0 +1,38 @@ +package parquet + +import "github.com/parquet-go/parquet-go/internal/memory" + +type bitmap struct { + bits []uint64 +} + +func (m *bitmap) reset(size int) { + size = (size + 63) / 64 + if cap(m.bits) < size { + m.bits = make([]uint64, size, 2*size) + } else { + m.bits = m.bits[:size] + m.clear() + } +} + +func (m *bitmap) clear() { + for i := range m.bits { + m.bits[i] = 0 + } +} + +var ( + bitmapPool memory.Pool[bitmap] +) + +func acquireBitmap(n int) *bitmap { + return bitmapPool.Get( + func() *bitmap { return &bitmap{bits: make([]uint64, n, 2*n)} }, + func(b *bitmap) { b.reset(n) }, + ) +} + +func releaseBitmap(b *bitmap) { + bitmapPool.Put(b) +} diff --git a/vendor/github.com/parquet-go/parquet-go/bloom.go b/vendor/github.com/parquet-go/parquet-go/bloom.go new file mode 100644 index 000000000..75dbaee40 --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/bloom.go @@ -0,0 +1,356 @@ +package parquet + +import ( + "bytes" + "fmt" + "io" + "sync" + + "github.com/parquet-go/bitpack/unsafecast" + "github.com/parquet-go/parquet-go/bloom" + "github.com/parquet-go/parquet-go/bloom/xxhash" + "github.com/parquet-go/parquet-go/compress" + "github.com/parquet-go/parquet-go/deprecated" + "github.com/parquet-go/parquet-go/encoding" + "github.com/parquet-go/parquet-go/format" +) + +// BloomFilter is an interface allowing applications to test whether a key +// exists in a bloom filter. +type BloomFilter interface { + // Implement the io.ReaderAt interface as a mechanism to allow reading the + // raw bits of the filter. + io.ReaderAt + + // Returns the size of the bloom filter (in bytes). + Size() int64 + + // Tests whether the given value is present in the filter. + // + // A non-nil error may be returned if reading the filter failed. This may + // happen if the filter was lazily loaded from a storage medium during the + // call to Check for example. Applications that can guarantee that the + // filter was in memory at the time Check was called can safely ignore the + // error, which would always be nil in this case. + Check(value Value) (bool, error) +} + +type errorBloomFilter struct{ err error } + +func (f *errorBloomFilter) Size() int64 { return 0 } +func (f *errorBloomFilter) ReadAt([]byte, int64) (int, error) { return 0, f.err } +func (f *errorBloomFilter) Check(Value) (bool, error) { return false, f.err } + +type FileBloomFilter struct { + io.SectionReader + hash bloom.Hash + check func(io.ReaderAt, int64, uint64) (bool, error) +} + +func (f *FileBloomFilter) Check(v Value) (bool, error) { + return f.check(&f.SectionReader, f.Size(), v.hash(f.hash)) +} + +func (v Value) hash(h bloom.Hash) uint64 { + switch v.Kind() { + case Boolean: + return h.Sum64Uint8(v.byte()) + case Int32, Float: + return h.Sum64Uint32(v.uint32()) + case Int64, Double: + return h.Sum64Uint64(v.uint64()) + default: // Int96, ByteArray, FixedLenByteArray, or null + return h.Sum64(v.byteArray()) + } +} + +func newBloomFilter(file io.ReaderAt, offset int64, header *format.BloomFilterHeader) (*FileBloomFilter, error) { + if header.Algorithm.Block == nil || header.Hash.XxHash == nil { + return nil, nil + } + switch { + case header.Compression.Uncompressed != nil: + return &FileBloomFilter{ + SectionReader: *io.NewSectionReader(file, offset, int64(header.NumBytes)), + hash: bloom.XXH64{}, + check: bloom.CheckSplitBlock, + }, nil + case header.Compression.GZip != nil: + // Decompress lazily on the first Check call so that opening a file with + // PrefetchBloomFilters(false) does not perform any extra I/O at open time. + compressedSize := int64(header.NumBytes) + var ( + once sync.Once + decompressed []byte + decompErr error + ) + lazyCheck := func(_ io.ReaderAt, _ int64, x uint64) (bool, error) { + once.Do(func() { + buf := make([]byte, compressedSize) + if _, err := file.ReadAt(buf, offset); err != nil { + decompErr = fmt.Errorf("reading compressed bloom filter: %w", err) + return + } + decompressed, decompErr = LookupCompressionCodec(format.Gzip).Decode(nil, buf) + if decompErr != nil { + decompErr = fmt.Errorf("decompressing bloom filter: %w", decompErr) + } + }) + if decompErr != nil { + return false, decompErr + } + return bloom.CheckSplitBlock(bytes.NewReader(decompressed), int64(len(decompressed)), x) + } + return &FileBloomFilter{ + SectionReader: *io.NewSectionReader(file, offset, compressedSize), + hash: bloom.XXH64{}, + check: lazyCheck, + }, nil + default: + return nil, nil + } +} + +// newBloomFilterFromBytes constructs a FileBloomFilter backed by an in-memory +// byte slice. Used when the bloom filter was read from an encrypted column and +// the raw bytes were decrypted before constructing the filter. +// +// Bloom filters are an optimization: if the header advertises an algorithm, +// hash, or compression codec we don't support, or if eager decompression +// fails, we return nil so the read silently skips the filter rather than +// failing the whole file open. +func newBloomFilterFromBytes(header *format.BloomFilterHeader, bits []byte) *FileBloomFilter { + if header.Algorithm.Block == nil || header.Hash.XxHash == nil { + return nil + } + switch { + case header.Compression.Uncompressed != nil: + r := bytes.NewReader(bits) + return &FileBloomFilter{ + SectionReader: *io.NewSectionReader(r, 0, int64(len(bits))), + hash: bloom.XXH64{}, + check: bloom.CheckSplitBlock, + } + case header.Compression.GZip != nil: + decompressed, err := LookupCompressionCodec(format.Gzip).Decode(nil, bits) + if err != nil { + return nil + } + r := bytes.NewReader(decompressed) + return &FileBloomFilter{ + SectionReader: *io.NewSectionReader(r, 0, int64(len(decompressed))), + hash: bloom.XXH64{}, + check: bloom.CheckSplitBlock, + } + default: + return nil + } +} + +// The BloomFilterColumn interface is a declarative representation of bloom filters +// used when configuring filters on a parquet writer. +type BloomFilterColumn interface { + // Returns the path of the column that the filter applies to. + Path() []string + + // Returns the hashing algorithm used when inserting values into a bloom + // filter. + Hash() bloom.Hash + + // Returns an encoding which can be used to write columns of values to the + // filter. + Encoding() encoding.Encoding + + // Returns the size of the filter needed to encode values in the filter, + // assuming each value will be encoded with the given number of bits. + Size(numValues int64) int +} + +// SplitBlockFilter constructs a split block bloom filter object for the column +// at the given path, with the given bitsPerValue. +// +// If you are unsure what number of bitsPerValue to use, 10 is a reasonable +// tradeoff between size and error rate for common datasets. +// +// For more information on the tradeoff between size and error rate, consult +// this website: https://hur.st/bloomfilter/?n=4000&p=0.1&m=&k=1 +func SplitBlockFilter(bitsPerValue uint, path ...string) BloomFilterColumn { + return splitBlockFilter{ + bitsPerValue: bitsPerValue, + path: path, + } +} + +type splitBlockFilter struct { + bitsPerValue uint + path []string +} + +func (f splitBlockFilter) Path() []string { return f.path } +func (f splitBlockFilter) Hash() bloom.Hash { return bloom.XXH64{} } +func (f splitBlockFilter) Encoding() encoding.Encoding { return splitBlockEncoding{} } + +func (f splitBlockFilter) Size(numValues int64) int { + return bloom.BlockSize * bloom.NumSplitBlocksOf(numValues, f.bitsPerValue) +} + +// Creates a header from the given bloom filter and compression codec. +// +// For now there is only one type of filter supported, but we provide this +// function to suggest a model for extending the implementation if new filters +// are added to the parquet specs. +func bloomFilterHeader(filter BloomFilterColumn, codec compress.Codec) (header format.BloomFilterHeader) { + switch filter.(type) { + case splitBlockFilter: + header.Algorithm.Block = &format.SplitBlockAlgorithm{} + } + switch filter.Hash().(type) { + case bloom.XXH64: + header.Hash.XxHash = &format.XxHash{} + } + if codec != nil && codec.CompressionCodec() == format.Gzip { + header.Compression.GZip = &format.BloomFilterGzip{} + } else { + header.Compression.Uncompressed = &format.BloomFilterUncompressed{} + } + return header +} + +func searchBloomFilterColumn(filters []BloomFilterColumn, path columnPath) BloomFilterColumn { + for _, f := range filters { + if path.equal(f.Path()) { + return f + } + } + return nil +} + +const ( + // Size of the stack buffer used to perform bulk operations on bloom filters. + // + // This value was determined as being a good default empirically, + // 128 x uint64 makes a 1KiB buffer which amortizes the cost of calling + // methods of bloom filters while not causing too much stack growth either. + filterEncodeBufferSize = 128 +) + +type splitBlockEncoding struct { + encoding.NotSupported +} + +func (splitBlockEncoding) EncodeBoolean(dst []byte, src []byte) ([]byte, error) { + splitBlockEncodeUint8(bloom.MakeSplitBlockFilter(dst), src) + return dst, nil +} + +func (splitBlockEncoding) EncodeInt32(dst []byte, src []int32) ([]byte, error) { + splitBlockEncodeUint32(bloom.MakeSplitBlockFilter(dst), unsafecast.Slice[uint32](src)) + return dst, nil +} + +func (splitBlockEncoding) EncodeInt64(dst []byte, src []int64) ([]byte, error) { + splitBlockEncodeUint64(bloom.MakeSplitBlockFilter(dst), unsafecast.Slice[uint64](src)) + return dst, nil +} + +func (e splitBlockEncoding) EncodeInt96(dst []byte, src []deprecated.Int96) ([]byte, error) { + splitBlockEncodeFixedLenByteArray(bloom.MakeSplitBlockFilter(dst), unsafecastInt96ToBytes(src), 12) + return dst, nil +} + +func (splitBlockEncoding) EncodeFloat(dst []byte, src []float32) ([]byte, error) { + splitBlockEncodeUint32(bloom.MakeSplitBlockFilter(dst), unsafecast.Slice[uint32](src)) + return dst, nil +} + +func (splitBlockEncoding) EncodeDouble(dst []byte, src []float64) ([]byte, error) { + splitBlockEncodeUint64(bloom.MakeSplitBlockFilter(dst), unsafecast.Slice[uint64](src)) + return dst, nil +} + +func (splitBlockEncoding) EncodeByteArray(dst []byte, src []byte, offsets []uint32) ([]byte, error) { + filter := bloom.MakeSplitBlockFilter(dst) + buffer := make([]uint64, 0, filterEncodeBufferSize) + baseOffset := offsets[0] + + for _, endOffset := range offsets[1:] { + value := src[baseOffset:endOffset:endOffset] + baseOffset = endOffset + + if len(buffer) == cap(buffer) { + filter.InsertBulk(buffer) + buffer = buffer[:0] + } + + buffer = append(buffer, xxhash.Sum64(value)) + } + + filter.InsertBulk(buffer) + return dst, nil +} + +func (splitBlockEncoding) EncodeFixedLenByteArray(dst []byte, src []byte, size int) ([]byte, error) { + filter := bloom.MakeSplitBlockFilter(dst) + if size == 16 { + splitBlockEncodeUint128(filter, unsafecast.Slice[[16]byte](src)) + } else { + splitBlockEncodeFixedLenByteArray(filter, src, size) + } + return dst, nil +} + +func splitBlockEncodeFixedLenByteArray(filter bloom.SplitBlockFilter, data []byte, size int) { + buffer := make([]uint64, 0, filterEncodeBufferSize) + + for i, j := 0, size; j <= len(data); { + if len(buffer) == cap(buffer) { + filter.InsertBulk(buffer) + buffer = buffer[:0] + } + buffer = append(buffer, xxhash.Sum64(data[i:j])) + i += size + j += size + } + + filter.InsertBulk(buffer) +} + +func splitBlockEncodeUint8(filter bloom.SplitBlockFilter, values []uint8) { + buffer := make([]uint64, filterEncodeBufferSize) + + for i := 0; i < len(values); { + n := xxhash.MultiSum64Uint8(buffer, values[i:]) + filter.InsertBulk(buffer[:n]) + i += n + } +} + +func splitBlockEncodeUint32(filter bloom.SplitBlockFilter, values []uint32) { + buffer := make([]uint64, filterEncodeBufferSize) + + for i := 0; i < len(values); { + n := xxhash.MultiSum64Uint32(buffer, values[i:]) + filter.InsertBulk(buffer[:n]) + i += n + } +} + +func splitBlockEncodeUint64(filter bloom.SplitBlockFilter, values []uint64) { + buffer := make([]uint64, filterEncodeBufferSize) + + for i := 0; i < len(values); { + n := xxhash.MultiSum64Uint64(buffer, values[i:]) + filter.InsertBulk(buffer[:n]) + i += n + } +} + +func splitBlockEncodeUint128(filter bloom.SplitBlockFilter, values [][16]byte) { + buffer := make([]uint64, filterEncodeBufferSize) + + for i := 0; i < len(values); { + n := xxhash.MultiSum64Uint128(buffer, values[i:]) + filter.InsertBulk(buffer[:n]) + i += n + } +} diff --git a/vendor/github.com/parquet-go/parquet-go/bloom/block.go b/vendor/github.com/parquet-go/parquet-go/bloom/block.go new file mode 100644 index 000000000..193dec721 --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/bloom/block.go @@ -0,0 +1,28 @@ +package bloom + +import "unsafe" + +// Word represents 32 bits words of bloom filter blocks. +type Word uint32 + +// Block represents bloom filter blocks which contain eight 32 bits words. +type Block [8]Word + +// Bytes returns b as a byte slice. +func (b *Block) Bytes() []byte { + return unsafe.Slice((*byte)(unsafe.Pointer(b)), BlockSize) +} + +const ( + // BlockSize is the size of bloom filter blocks in bytes. + BlockSize = 32 + + salt0 = 0x47b6137b + salt1 = 0x44974d91 + salt2 = 0x8824ad5b + salt3 = 0xa2b7289d + salt4 = 0x705495c7 + salt5 = 0x2df1424b + salt6 = 0x9efc4947 + salt7 = 0x5c6bfb31 +) diff --git a/vendor/github.com/parquet-go/parquet-go/bloom/block_amd64.go b/vendor/github.com/parquet-go/parquet-go/bloom/block_amd64.go new file mode 100644 index 000000000..5edf9d5d9 --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/bloom/block_amd64.go @@ -0,0 +1,39 @@ +//go:build !purego + +package bloom + +import "golang.org/x/sys/cpu" + +// The functions in this file are SIMD-optimized versions of the functions +// declared in block_optimized.go for x86 targets. +// +// The optimization yields measurable improvements over the pure Go versions: +// +// goos: darwin +// goarch: amd64 +// pkg: github.com/parquet-go/parquet-go/bloom +// cpu: Intel(R) Core(TM) i9-8950HK CPU @ 2.90GHz +// +// name old time/op new time/op delta +// BlockInsert 11.6ns ± 4% 2.0ns ± 3% -82.37% (p=0.000 n=8+8) +// BlockCheck 12.6ns ±28% 2.1ns ± 4% -83.12% (p=0.000 n=10+8) +// +// name old speed new speed delta +// BlockInsert 2.73GB/s ±13% 15.70GB/s ± 3% +475.96% (p=0.000 n=9+8) +// BlockCheck 2.59GB/s ±23% 15.06GB/s ± 4% +482.25% (p=0.000 n=10+8) +// +// Note that the numbers above are a comparison to the routines implemented in +// block_optimized.go; the delta comparing to functions in block_default.go is +// significantly larger but not very interesting since those functions have no +// practical use cases. +var hasAVX2 = cpu.X86.HasAVX2 + +//go:noescape +func blockInsert(b *Block, x uint32) + +//go:noescape +func blockCheck(b *Block, x uint32) bool + +func (b *Block) Insert(x uint32) { blockInsert(b, x) } + +func (b *Block) Check(x uint32) bool { return blockCheck(b, x) } diff --git a/vendor/github.com/parquet-go/parquet-go/bloom/block_amd64.s b/vendor/github.com/parquet-go/parquet-go/bloom/block_amd64.s new file mode 100644 index 000000000..713aea2cd --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/bloom/block_amd64.s @@ -0,0 +1,129 @@ +//go:build !purego + +#include "textflag.h" + +#define salt0 0x47b6137b +#define salt1 0x44974d91 +#define salt2 0x8824ad5b +#define salt3 0xa2b7289d +#define salt4 0x705495c7 +#define salt5 0x2df1424b +#define salt6 0x9efc4947 +#define salt7 0x5c6bfb31 + +DATA ones+0(SB)/4, $1 +DATA ones+4(SB)/4, $1 +DATA ones+8(SB)/4, $1 +DATA ones+12(SB)/4, $1 +DATA ones+16(SB)/4, $1 +DATA ones+20(SB)/4, $1 +DATA ones+24(SB)/4, $1 +DATA ones+28(SB)/4, $1 +GLOBL ones(SB), RODATA|NOPTR, $32 + +DATA salt+0(SB)/4, $salt0 +DATA salt+4(SB)/4, $salt1 +DATA salt+8(SB)/4, $salt2 +DATA salt+12(SB)/4, $salt3 +DATA salt+16(SB)/4, $salt4 +DATA salt+20(SB)/4, $salt5 +DATA salt+24(SB)/4, $salt6 +DATA salt+28(SB)/4, $salt7 +GLOBL salt(SB), RODATA|NOPTR, $32 + +// This initial block is a SIMD implementation of the mask function declared in +// block_default.go and block_optimized.go. For each of the 8 x 32 bits words of +// the bloom filter block, the operation performed is: +// +// block[i] = 1 << ((x * salt[i]) >> 27) +// +// Arguments +// --------- +// +// * src is a memory location where the value to use when computing the mask is +// located. The memory location is not modified. +// +// * tmp is a YMM register used as scratch space to hold intermediary results in +// the algorithm. +// +// * dst is a YMM register where the final mask is written. +// +#define generateMask(src, tmp, dst) \ + VMOVDQA ones(SB), dst \ + VPBROADCASTD src, tmp \ + VPMULLD salt(SB), tmp, tmp \ + VPSRLD $27, tmp, tmp \ + VPSLLVD tmp, dst, dst + +#define insert(salt, src, dst) \ + MOVL src, CX \ + IMULL salt, CX \ + SHRL $27, CX \ + MOVL $1, DX \ + SHLL CX, DX \ + ORL DX, dst + +#define check(salt, b, x) \ + MOVL b, CX \ + MOVL x, DX \ + IMULL salt, DX \ + SHRL $27, DX \ + BTL DX, CX \ + JAE notfound + +// func blockInsert(b *Block, x uint32) +TEXT ·blockInsert(SB), NOSPLIT, $0-16 + MOVQ b+0(FP), AX + CMPB ·hasAVX2(SB), $0 + JE fallback +avx2: + generateMask(x+8(FP), Y1, Y0) + // Set all 1 bits of the mask in the bloom filter block. + VPOR (AX), Y0, Y0 + VMOVDQU Y0, (AX) + VZEROUPPER + RET +fallback: + MOVL x+8(FP), BX + insert($salt0, BX, 0(AX)) + insert($salt1, BX, 4(AX)) + insert($salt2, BX, 8(AX)) + insert($salt3, BX, 12(AX)) + insert($salt4, BX, 16(AX)) + insert($salt5, BX, 20(AX)) + insert($salt6, BX, 24(AX)) + insert($salt7, BX, 28(AX)) + RET + +// func blockCheck(b *Block, x uint32) bool +TEXT ·blockCheck(SB), NOSPLIT, $0-17 + MOVQ b+0(FP), AX + CMPB ·hasAVX2(SB), $0 + JE fallback +avx2: + generateMask(x+8(FP), Y1, Y0) + // Compare the 1 bits of the mask with the bloom filter block, then compare + // the result with the mask, expecting equality if the value `x` was present + // in the block. + VPAND (AX), Y0, Y1 // Y0 = block & mask + VPTEST Y0, Y1 // if (Y0 & ^Y1) != 0 { CF = 1 } + SETCS ret+16(FP) // return CF == 1 + VZEROUPPER + RET +fallback: + MOVL x+8(FP), BX + check($salt0, 0(AX), BX) + check($salt1, 4(AX), BX) + check($salt2, 8(AX), BX) + check($salt3, 12(AX), BX) + check($salt4, 16(AX), BX) + check($salt5, 20(AX), BX) + check($salt6, 24(AX), BX) + check($salt7, 28(AX), BX) + MOVB $1, CX + JMP done +notfound: + XORB CX, CX +done: + MOVB CX, ret+16(FP) + RET diff --git a/vendor/github.com/parquet-go/parquet-go/bloom/block_default.go b/vendor/github.com/parquet-go/parquet-go/bloom/block_default.go new file mode 100644 index 000000000..016cb73b4 --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/bloom/block_default.go @@ -0,0 +1,65 @@ +//go:build purego && parquet.bloom.no_unroll + +package bloom + +// This file contains direct translation of the algorithms described in the +// parquet bloom filter spec: +// https://github.com/apache/parquet-format/blob/master/BloomFilter.md +// +// There are no practical reasons to eable the parquet.bloom.no_unroll build +// tag, the code is left here as a reference to ensure that the optimized +// implementations of block operations behave the same as the functions in this +// file. + +var salt = [8]uint32{ + 0: salt0, + 1: salt1, + 2: salt2, + 3: salt3, + 4: salt4, + 5: salt5, + 6: salt6, + 7: salt7, +} + +func (w *Word) set(i uint) { + *w |= Word(1 << i) +} + +func (w Word) has(i uint) bool { + return ((w >> Word(i)) & 1) != 0 +} + +func mask(x uint32) Block { + var b Block + for i := uint(0); i < 8; i++ { + y := x * salt[i] + b[i].set(uint(y) >> 27) + } + return b +} + +func (b *Block) Insert(x uint32) { + masked := mask(x) + for i := uint(0); i < 8; i++ { + for j := uint(0); j < 32; j++ { + if masked[i].has(j) { + b[i].set(j) + } + } + } +} + +func (b *Block) Check(x uint32) bool { + masked := mask(x) + for i := uint(0); i < 8; i++ { + for j := uint(0); j < 32; j++ { + if masked[i].has(j) { + if !b[i].has(j) { + return false + } + } + } + } + return true +} diff --git a/vendor/github.com/parquet-go/parquet-go/bloom/block_optimized.go b/vendor/github.com/parquet-go/parquet-go/bloom/block_optimized.go new file mode 100644 index 000000000..20a976812 --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/bloom/block_optimized.go @@ -0,0 +1,53 @@ +//go:build (!amd64 || purego) && !parquet.bloom.no_unroll + +package bloom + +// The functions in this file are optimized versions of the algorithms described +// in https://github.com/apache/parquet-format/blob/master/BloomFilter.md +// +// The functions are manual unrolling of the loops, which yield significant +// performance improvements: +// +// goos: darwin +// goarch: amd64 +// pkg: github.com/parquet-go/parquet-go/bloom +// cpu: Intel(R) Core(TM) i9-8950HK CPU @ 2.90GHz +// +// name old time/op new time/op delta +// BlockInsert 327ns ± 1% 12ns ± 4% -96.47% (p=0.000 n=9+8) +// BlockCheck 240ns ± 4% 13ns ±28% -94.75% (p=0.000 n=8+10) +// +// name old speed new speed delta +// BlockInsert 97.8MB/s ± 1% 2725.0MB/s ±13% +2686.59% (p=0.000 n=9+9) +// BlockCheck 133MB/s ± 4% 2587MB/s ±23% +1838.46% (p=0.000 n=8+10) +// +// The benchmarks measure throughput based on the byte size of a bloom filter +// block. + +func (b *Block) Insert(x uint32) { + b[0] |= 1 << ((x * salt0) >> 27) + b[1] |= 1 << ((x * salt1) >> 27) + b[2] |= 1 << ((x * salt2) >> 27) + b[3] |= 1 << ((x * salt3) >> 27) + b[4] |= 1 << ((x * salt4) >> 27) + b[5] |= 1 << ((x * salt5) >> 27) + b[6] |= 1 << ((x * salt6) >> 27) + b[7] |= 1 << ((x * salt7) >> 27) +} + +func (b *Block) Check(x uint32) bool { + return ((b[0] & (1 << ((x * salt0) >> 27))) != 0) && + ((b[1] & (1 << ((x * salt1) >> 27))) != 0) && + ((b[2] & (1 << ((x * salt2) >> 27))) != 0) && + ((b[3] & (1 << ((x * salt3) >> 27))) != 0) && + ((b[4] & (1 << ((x * salt4) >> 27))) != 0) && + ((b[5] & (1 << ((x * salt5) >> 27))) != 0) && + ((b[6] & (1 << ((x * salt6) >> 27))) != 0) && + ((b[7] & (1 << ((x * salt7) >> 27))) != 0) +} + +func (f SplitBlockFilter) insertBulk(x []uint64) { + for i := range x { + f.Insert(x[i]) + } +} diff --git a/vendor/github.com/parquet-go/parquet-go/bloom/bloom.go b/vendor/github.com/parquet-go/parquet-go/bloom/bloom.go new file mode 100644 index 000000000..337fe0d7d --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/bloom/bloom.go @@ -0,0 +1,13 @@ +// Package bloom implements parquet bloom filters. +package bloom + +func fasthash1x64(value uint64, scale int32) uint64 { + return ((value >> 32) * uint64(scale)) >> 32 +} + +func fasthash4x64(dst, src *[4]uint64, scale int32) { + dst[0] = ((src[0] >> 32) * uint64(scale)) >> 32 + dst[1] = ((src[1] >> 32) * uint64(scale)) >> 32 + dst[2] = ((src[2] >> 32) * uint64(scale)) >> 32 + dst[3] = ((src[3] >> 32) * uint64(scale)) >> 32 +} diff --git a/vendor/github.com/parquet-go/parquet-go/bloom/filter.go b/vendor/github.com/parquet-go/parquet-go/bloom/filter.go new file mode 100644 index 000000000..493428a3d --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/bloom/filter.go @@ -0,0 +1,94 @@ +package bloom + +import ( + "io" + + "github.com/parquet-go/bitpack/unsafecast" + "github.com/parquet-go/parquet-go/internal/memory" +) + +// Filter is an interface representing read-only bloom filters where programs +// can probe for the possible presence of a hash key. +type Filter interface { + Check(uint64) bool +} + +// SplitBlockFilter is an in-memory implementation of the parquet bloom filters. +// +// This type is useful to construct bloom filters that are later serialized +// to a storage medium. +type SplitBlockFilter []Block + +// MakeSplitBlockFilter constructs a SplitBlockFilter value from the data byte +// slice. +func MakeSplitBlockFilter(data []byte) SplitBlockFilter { + return unsafecast.Slice[Block](data) +} + +// NumSplitBlocksOf returns the number of blocks in a filter intended to hold +// the given number of values and bits of filter per value. +// +// This function is useful to determine the number of blocks when creating bloom +// filters in memory, for example: +// +// f := make(bloom.SplitBlockFilter, bloom.NumSplitBlocksOf(n, 10)) +func NumSplitBlocksOf(numValues int64, bitsPerValue uint) int { + numBytes := ((uint(numValues) * bitsPerValue) + 7) / 8 + numBlocks := (numBytes + (BlockSize - 1)) / BlockSize + return int(numBlocks) +} + +// Reset clears the content of the filter f. +func (f SplitBlockFilter) Reset() { + for i := range f { + f[i] = Block{} + } +} + +// Block returns a pointer to the block that the given value hashes to in the +// bloom filter. +func (f SplitBlockFilter) Block(x uint64) *Block { return &f[fasthash1x64(x, int32(len(f)))] } + +// InsertBulk adds all values from x into f. +func (f SplitBlockFilter) InsertBulk(x []uint64) { filterInsertBulk(f, x) } + +// Insert adds x to f. +func (f SplitBlockFilter) Insert(x uint64) { filterInsert(f, x) } + +// Check tests whether x is in f. +func (f SplitBlockFilter) Check(x uint64) bool { return filterCheck(f, x) } + +// Bytes converts f to a byte slice. +// +// The returned slice shares the memory of f. The method is intended to be used +// to serialize the bloom filter to a storage medium. +func (f SplitBlockFilter) Bytes() []byte { + return unsafecast.Slice[byte](f) +} + +// CheckSplitBlock is similar to bloom.SplitBlockFilter.Check but reads the +// bloom filter of n bytes from r. +// +// The size n of the bloom filter is assumed to be a multiple of the block size. +func CheckSplitBlock(r io.ReaderAt, n int64, x uint64) (bool, error) { + block := acquireBlock() + defer releaseBlock(block) + offset := BlockSize * fasthash1x64(x, int32(n/BlockSize)) + _, err := r.ReadAt(block.Bytes(), int64(offset)) + return block.Check(uint32(x)), err +} + +var ( + blockPool memory.Pool[Block] +) + +func acquireBlock() *Block { + return blockPool.Get( + func() *Block { return new(Block) }, + func(b *Block) {}, + ) +} + +func releaseBlock(b *Block) { + blockPool.Put(b) +} diff --git a/vendor/github.com/parquet-go/parquet-go/bloom/filter_amd64.go b/vendor/github.com/parquet-go/parquet-go/bloom/filter_amd64.go new file mode 100644 index 000000000..3649da51c --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/bloom/filter_amd64.go @@ -0,0 +1,33 @@ +//go:build !purego + +package bloom + +// This file contains the signatures for bloom filter algorithms implemented in +// filter_amd64.s. +// +// The assembly code provides significant speedups on filter inserts and checks, +// with the greatest gains seen on the bulk insert operation where the use of +// vectorized code yields great results. +// +// The following sections record the kind of performance improvements we were +// able to measure, comparing with performing the filter block lookups in Go +// and calling to the block insert and check routines: +// +// name old time/op new time/op delta +// FilterInsertBulk 45.1ns ± 2% 17.8ns ± 3% -60.41% (p=0.000 n=10+10) +// FilterInsert 3.48ns ± 2% 2.55ns ± 1% -26.86% (p=0.000 n=10+8) +// FilterCheck 3.64ns ± 3% 2.66ns ± 2% -26.82% (p=0.000 n=10+9) +// +// name old speed new speed delta +// FilterInsertBulk 11.4GB/s ± 2% 28.7GB/s ± 3% +152.61% (p=0.000 n=10+10) +// FilterInsert 9.19GB/s ± 2% 12.56GB/s ± 1% +36.71% (p=0.000 n=10+8) +// FilterCheck 8.80GB/s ± 3% 12.03GB/s ± 2% +36.61% (p=0.000 n=10+9) + +//go:noescape +func filterInsertBulk(f []Block, x []uint64) + +//go:noescape +func filterInsert(f []Block, x uint64) + +//go:noescape +func filterCheck(f []Block, x uint64) bool diff --git a/vendor/github.com/parquet-go/parquet-go/bloom/filter_amd64.s b/vendor/github.com/parquet-go/parquet-go/bloom/filter_amd64.s new file mode 100644 index 000000000..cfa75e69f --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/bloom/filter_amd64.s @@ -0,0 +1,214 @@ +//go:build !purego + +#include "textflag.h" + +#define salt0 0x47b6137b +#define salt1 0x44974d91 +#define salt2 0x8824ad5b +#define salt3 0xa2b7289d +#define salt4 0x705495c7 +#define salt5 0x2df1424b +#define salt6 0x9efc4947 +#define salt7 0x5c6bfb31 + +// See block_amd64.s for a description of this algorithm. +#define generateMask(src, dst) \ + VMOVDQA ones(SB), dst \ + VPMULLD salt(SB), src, src \ + VPSRLD $27, src, src \ + VPSLLVD src, dst, dst + +#define applyMask(src, dst) \ + VPOR dst, src, src \ + VMOVDQU src, dst + +#define fasthash1x64(scale, value) \ + SHRQ $32, value \ + IMULQ scale, value \ + SHRQ $32, value \ + SHLQ $5, value + +#define fasthash4x64(scale, value) \ + VPSRLQ $32, value, value \ + VPMULUDQ scale, value, value \ + VPSRLQ $32, value, value \ + VPSLLQ $5, value, value + +#define extract4x64(srcYMM, srcXMM, tmpXMM, r0, r1, r2, r3) \ + VEXTRACTI128 $1, srcYMM, tmpXMM \ + MOVQ srcXMM, r0 \ + VPEXTRQ $1, srcXMM, r1 \ + MOVQ tmpXMM, r2 \ + VPEXTRQ $1, tmpXMM, r3 + +#define insert(salt, src, dst) \ + MOVL src, CX \ + IMULL salt, CX \ + SHRL $27, CX \ + MOVL $1, DX \ + SHLL CX, DX \ + ORL DX, dst + +#define check(salt, b, x) \ + MOVL b, CX \ + MOVL x, DX \ + IMULL salt, DX \ + SHRL $27, DX \ + BTL DX, CX \ + JAE notfound + +// func filterInsertBulk(f []Block, x []uint64) +TEXT ·filterInsertBulk(SB), NOSPLIT, $0-48 + MOVQ f_base+0(FP), AX + MOVQ f_len+8(FP), CX + MOVQ x_base+24(FP), BX + MOVQ x_len+32(FP), DX + CMPB ·hasAVX2(SB), $0 + JE fallback +avx2: + VPBROADCASTQ f_base+8(FP), Y0 + // Loop initialization, SI holds the current index in `x`, DI is the number + // of elements in `x` rounded down to the nearest multiple of 4. + XORQ SI, SI + MOVQ DX, DI + SHRQ $2, DI + SHLQ $2, DI +avx2loop4x64: + CMPQ SI, DI + JAE avx2loop1x64 + + // The masks and indexes for 4 input hashes are computed in each loop + // iteration. The hashes are loaded in Y1 so we can use vector instructions + // to compute all 4 indexes in parallel. The lower 32 bits of the hashes are + // also broadcasted in 4 YMM registers to compute the 4 masks that will then + // be applied to the filter. + VMOVDQU (BX)(SI*8), Y1 + VPBROADCASTD 0(BX)(SI*8), Y2 + VPBROADCASTD 8(BX)(SI*8), Y3 + VPBROADCASTD 16(BX)(SI*8), Y4 + VPBROADCASTD 24(BX)(SI*8), Y5 + + fasthash4x64(Y0, Y1) + generateMask(Y2, Y6) + generateMask(Y3, Y7) + generateMask(Y4, Y8) + generateMask(Y5, Y9) + + // The next block of instructions move indexes from the vector to general + // purpose registers in order to use them as offsets when applying the mask + // to the filter. + extract4x64(Y1, X1, X10, R8, R9, R10, R11) + + // Apply masks to the filter; this operation is sensitive to aliasing, when + // blocks overlap the, CPU has to serialize the reads and writes, which has + // a measurable impact on throughput. This would be frequent for small bloom + // filters which may have only a few blocks, the probability of seeing + // overlapping blocks on large filters should be small enough to make this + // a non-issue though. + applyMask(Y6, (AX)(R8*1)) + applyMask(Y7, (AX)(R9*1)) + applyMask(Y8, (AX)(R10*1)) + applyMask(Y9, (AX)(R11*1)) + + ADDQ $4, SI + JMP avx2loop4x64 +avx2loop1x64: + // Compute trailing elements in `x` if the length was not a multiple of 4. + // This is the same algorithm as the one in the loop4x64 section, working + // on a single mask/block pair at a time. + CMPQ SI, DX + JE avx2done + MOVQ (BX)(SI*8), R8 + VPBROADCASTD (BX)(SI*8), Y0 + fasthash1x64(CX, R8) + generateMask(Y0, Y1) + applyMask(Y1, (AX)(R8*1)) + INCQ SI + JMP avx2loop1x64 +avx2done: + VZEROUPPER + JMP done +fallback: + XORQ SI, SI + MOVQ DX, DI + MOVQ CX, R10 +loop: + CMPQ SI, DI + JE done + MOVLQZX (BX)(SI*8), R8 + MOVQ (BX)(SI*8), R9 + fasthash1x64(R10, R9) + insert($salt0, R8, 0(AX)(R9*1)) + insert($salt1, R8, 4(AX)(R9*1)) + insert($salt2, R8, 8(AX)(R9*1)) + insert($salt3, R8, 12(AX)(R9*1)) + insert($salt4, R8, 16(AX)(R9*1)) + insert($salt5, R8, 20(AX)(R9*1)) + insert($salt6, R8, 24(AX)(R9*1)) + insert($salt7, R8, 28(AX)(R9*1)) + INCQ SI + JMP loop +done: + RET + +// func filterInsert(f []Block, x uint64) +TEXT ·filterInsert(SB), NOSPLIT, $0-32 + MOVQ f_base+0(FP), AX + MOVQ f_len+8(FP), BX + MOVQ x+24(FP), CX + fasthash1x64(BX, CX) + CMPB ·hasAVX2(SB), $0 + JE fallback +avx2: + VPBROADCASTD x+24(FP), Y1 + generateMask(Y1, Y0) + applyMask(Y0, (AX)(CX*1)) + VZEROUPPER + RET +fallback: + ADDQ CX, AX + MOVL x+24(FP), BX + insert($salt0, BX, 0(AX)) + insert($salt1, BX, 4(AX)) + insert($salt2, BX, 8(AX)) + insert($salt3, BX, 12(AX)) + insert($salt4, BX, 16(AX)) + insert($salt5, BX, 20(AX)) + insert($salt6, BX, 24(AX)) + insert($salt7, BX, 28(AX)) + RET + +// func filterCheck(f []Block, x uint64) bool +TEXT ·filterCheck(SB), NOSPLIT, $0-33 + MOVQ f_base+0(FP), AX + MOVQ f_len+8(FP), BX + MOVQ x+24(FP), CX + fasthash1x64(BX, CX) + CMPB ·hasAVX2(SB), $0 + JE fallback +avx2: + VPBROADCASTD x+24(FP), Y1 + generateMask(Y1, Y0) + VPAND (AX)(CX*1), Y0, Y1 + VPTEST Y0, Y1 + SETCS ret+32(FP) + VZEROUPPER + RET +fallback: + ADDQ CX, AX + MOVL x+24(FP), BX + check($salt0, 0(AX), BX) + check($salt1, 4(AX), BX) + check($salt2, 8(AX), BX) + check($salt3, 12(AX), BX) + check($salt4, 16(AX), BX) + check($salt5, 20(AX), BX) + check($salt6, 24(AX), BX) + check($salt7, 28(AX), BX) + MOVB $1, CX + JMP done +notfound: + XORB CX, CX +done: + MOVB CX, ret+32(FP) + RET diff --git a/vendor/github.com/parquet-go/parquet-go/bloom/filter_default.go b/vendor/github.com/parquet-go/parquet-go/bloom/filter_default.go new file mode 100644 index 000000000..38fadd5be --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/bloom/filter_default.go @@ -0,0 +1,17 @@ +//go:build purego || !amd64 + +package bloom + +func filterInsertBulk(f []Block, x []uint64) { + for i := range x { + filterInsert(f, x[i]) + } +} + +func filterInsert(f []Block, x uint64) { + f[fasthash1x64(x, int32(len(f)))].Insert(uint32(x)) +} + +func filterCheck(f []Block, x uint64) bool { + return f[fasthash1x64(x, int32(len(f)))].Check(uint32(x)) +} diff --git a/vendor/github.com/parquet-go/parquet-go/bloom/hash.go b/vendor/github.com/parquet-go/parquet-go/bloom/hash.go new file mode 100644 index 000000000..9a3b61a30 --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/bloom/hash.go @@ -0,0 +1,77 @@ +package bloom + +import "github.com/parquet-go/parquet-go/bloom/xxhash" + +// Hash is an interface abstracting the hashing algorithm used in bloom filters. +// +// Hash instances must be safe to use concurrently from multiple goroutines. +type Hash interface { + // Returns the 64 bit hash of the value passed as argument. + Sum64(value []byte) uint64 + + // Compute hashes of individual values of primitive types. + Sum64Uint8(value uint8) uint64 + Sum64Uint16(value uint16) uint64 + Sum64Uint32(value uint32) uint64 + Sum64Uint64(value uint64) uint64 + Sum64Uint128(value [16]byte) uint64 + + // Compute hashes of the array of fixed size values passed as arguments, + // returning the number of hashes written to the destination buffer. + MultiSum64Uint8(dst []uint64, src []uint8) int + MultiSum64Uint16(dst []uint64, src []uint16) int + MultiSum64Uint32(dst []uint64, src []uint32) int + MultiSum64Uint64(dst []uint64, src []uint64) int + MultiSum64Uint128(dst []uint64, src [][16]byte) int +} + +// XXH64 is an implementation of the Hash interface using the XXH64 algorithm. +type XXH64 struct{} + +func (XXH64) Sum64(b []byte) uint64 { + return xxhash.Sum64(b) +} + +func (XXH64) Sum64Uint8(v uint8) uint64 { + return xxhash.Sum64Uint8(v) +} + +func (XXH64) Sum64Uint16(v uint16) uint64 { + return xxhash.Sum64Uint16(v) +} + +func (XXH64) Sum64Uint32(v uint32) uint64 { + return xxhash.Sum64Uint32(v) +} + +func (XXH64) Sum64Uint64(v uint64) uint64 { + return xxhash.Sum64Uint64(v) +} + +func (XXH64) Sum64Uint128(v [16]byte) uint64 { + return xxhash.Sum64Uint128(v) +} + +func (XXH64) MultiSum64Uint8(h []uint64, v []uint8) int { + return xxhash.MultiSum64Uint8(h, v) +} + +func (XXH64) MultiSum64Uint16(h []uint64, v []uint16) int { + return xxhash.MultiSum64Uint16(h, v) +} + +func (XXH64) MultiSum64Uint32(h []uint64, v []uint32) int { + return xxhash.MultiSum64Uint32(h, v) +} + +func (XXH64) MultiSum64Uint64(h []uint64, v []uint64) int { + return xxhash.MultiSum64Uint64(h, v) +} + +func (XXH64) MultiSum64Uint128(h []uint64, v [][16]byte) int { + return xxhash.MultiSum64Uint128(h, v) +} + +var ( + _ Hash = XXH64{} +) diff --git a/vendor/github.com/parquet-go/parquet-go/bloom/xxhash/LICENSE b/vendor/github.com/parquet-go/parquet-go/bloom/xxhash/LICENSE new file mode 100644 index 000000000..80bef2ebc --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/bloom/xxhash/LICENSE @@ -0,0 +1,27 @@ +The following files in this directory were derived from the open-source +project at https://github.com/cespare/xxhash. A copy of the original +license is provided below. +------------------------------------------------------------------------ + +Copyright (c) 2016 Caleb Spare + +MIT License + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/vendor/github.com/parquet-go/parquet-go/bloom/xxhash/sum64uint.go b/vendor/github.com/parquet-go/parquet-go/bloom/xxhash/sum64uint.go new file mode 100644 index 000000000..84423d0ef --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/bloom/xxhash/sum64uint.go @@ -0,0 +1,37 @@ +package xxhash + +func Sum64Uint8(v uint8) uint64 { + h := prime5 + 1 + h ^= uint64(v) * prime5 + return avalanche(rol11(h) * prime1) +} + +func Sum64Uint16(v uint16) uint64 { + h := prime5 + 2 + h ^= uint64(v&0xFF) * prime5 + h = rol11(h) * prime1 + h ^= uint64(v>>8) * prime5 + h = rol11(h) * prime1 + return avalanche(h) +} + +func Sum64Uint32(v uint32) uint64 { + h := prime5 + 4 + h ^= uint64(v) * prime1 + return avalanche(rol23(h)*prime2 + prime3) +} + +func Sum64Uint64(v uint64) uint64 { + h := prime5 + 8 + h ^= round(0, v) + return avalanche(rol27(h)*prime1 + prime4) +} + +func Sum64Uint128(v [16]byte) uint64 { + h := prime5 + 16 + h ^= round(0, u64(v[:8])) + h = rol27(h)*prime1 + prime4 + h ^= round(0, u64(v[8:])) + h = rol27(h)*prime1 + prime4 + return avalanche(h) +} diff --git a/vendor/github.com/parquet-go/parquet-go/bloom/xxhash/sum64uint_amd64.go b/vendor/github.com/parquet-go/parquet-go/bloom/xxhash/sum64uint_amd64.go new file mode 100644 index 000000000..03a4d31b1 --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/bloom/xxhash/sum64uint_amd64.go @@ -0,0 +1,49 @@ +//go:build !purego + +package xxhash + +import "golang.org/x/sys/cpu" + +// This file contains the declaration of signatures for the multi hashing +// functions implemented in sum64uint_amd64.s, which provides vectorized +// versions of the algorithms written in sum64uint_purego.go. +// +// The use of SIMD optimization yields measurable throughput increases when +// computing multiple hash values in parallel compared to hashing values +// individually in loops: +// +// name old speed new speed delta +// MultiSum64Uint8/4KB 4.94GB/s ± 2% 6.82GB/s ± 5% +38.00% (p=0.000 n=10+10) +// MultiSum64Uint16/4KB 3.44GB/s ± 2% 4.63GB/s ± 4% +34.56% (p=0.000 n=10+10) +// MultiSum64Uint32/4KB 4.84GB/s ± 2% 6.39GB/s ± 4% +31.94% (p=0.000 n=10+10) +// MultiSum64Uint64/4KB 3.77GB/s ± 2% 4.95GB/s ± 2% +31.14% (p=0.000 n=9+10) +// MultiSum64Uint128/4KB 1.84GB/s ± 2% 3.11GB/s ± 4% +68.70% (p=0.000 n=9+10) +// +// name old hash/s new hash/s delta +// MultiSum64Uint8/4KB 617M ± 2% 852M ± 5% +38.00% (p=0.000 n=10+10) +// MultiSum64Uint16/4KB 431M ± 2% 579M ± 4% +34.56% (p=0.000 n=10+10) +// MultiSum64Uint32/4KB 605M ± 2% 799M ± 4% +31.94% (p=0.000 n=10+10) +// MultiSum64Uint64/4KB 471M ± 2% 618M ± 2% +31.14% (p=0.000 n=9+10) +// MultiSum64Uint128/4KB 231M ± 2% 389M ± 4% +68.70% (p=0.000 n=9+10) +// +// The benchmarks measure the throughput of hashes produced, as a rate of values +// and bytes. + +var hasAVX512 = cpu.X86.HasAVX512 && + cpu.X86.HasAVX512F && + cpu.X86.HasAVX512CD + +//go:noescape +func MultiSum64Uint8(h []uint64, v []uint8) int + +//go:noescape +func MultiSum64Uint16(h []uint64, v []uint16) int + +//go:noescape +func MultiSum64Uint32(h []uint64, v []uint32) int + +//go:noescape +func MultiSum64Uint64(h []uint64, v []uint64) int + +//go:noescape +func MultiSum64Uint128(h []uint64, v [][16]byte) int diff --git a/vendor/github.com/parquet-go/parquet-go/bloom/xxhash/sum64uint_amd64.s b/vendor/github.com/parquet-go/parquet-go/bloom/xxhash/sum64uint_amd64.s new file mode 100644 index 000000000..9d420a9d9 --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/bloom/xxhash/sum64uint_amd64.s @@ -0,0 +1,755 @@ +//go:build !purego + +#include "textflag.h" + +/* +The algorithms in this file are assembly versions of the Go functions in the +sum64uint_default.go file. + +The implementations are mostly direct translations of the Go code to assembly, +leveraging SIMD instructions to process chunks of the input variables in +parallel at each loop iteration. To maximize utilization of the CPU capacity, +some of the functions unroll two steps of the vectorized loop per iteration, +which yields further throughput because the CPU is able to process some of the +instruction from the two steps in parallel due to having no data dependencies +between the inputs and outputs. + +The use of AVX-512 yields a significant increase in throughput on all the +algorithms, in most part thanks to the VPMULLQ instructions which compute +8 x 64 bits multiplication. There were no equivalent instruction in AVX2, which +required emulating vector multiplication with a combination of 32 bits multiply, +additions, shifts, and masks: the amount of instructions and data dependencies +resulted in the AVX2 code yielding equivalent performance characteristics for a +much higher complexity. + +The benchmark results below showcase the improvements that the AVX-512 code +yields on the XXH64 algorithms: + +name old speed new speed delta +MultiSum64Uint8/4KB 4.97GB/s ± 0% 14.59GB/s ± 1% +193.73% (p=0.000 n=10+10) +MultiSum64Uint16/4KB 3.55GB/s ± 0% 9.46GB/s ± 0% +166.20% (p=0.000 n=10+9) +MultiSum64Uint32/4KB 4.48GB/s ± 0% 13.93GB/s ± 1% +210.93% (p=0.000 n=10+10) +MultiSum64Uint64/4KB 3.57GB/s ± 0% 11.12GB/s ± 1% +211.73% (p=0.000 n=9+10) +MultiSum64Uint128/4KB 2.54GB/s ± 0% 6.49GB/s ± 1% +155.69% (p=0.000 n=10+10) + +name old hash/s new hash/s delta +MultiSum64Uint8/4KB 621M ± 0% 1823M ± 1% +193.73% (p=0.000 n=10+10) +MultiSum64Uint16/4KB 444M ± 0% 1182M ± 0% +166.20% (p=0.000 n=10+9) +MultiSum64Uint32/4KB 560M ± 0% 1742M ± 1% +210.93% (p=0.000 n=10+10) +MultiSum64Uint64/4KB 446M ± 0% 1391M ± 1% +211.73% (p=0.000 n=9+10) +MultiSum64Uint128/4KB 317M ± 0% 811M ± 1% +155.69% (p=0.000 n=10+10) + +The functions perform runtime detection of AVX-512 support by testing the value +of the xxhash.hasAVX512 variable declared and initialized in sum64uint_amd64.go. +Branch mispredictions on those tests are very unlikely since the value is never +modified by the application. The cost of the comparisons are also amortized by +the bulk APIs of the MultiSum64* functions (a single test is required per call). + +If a bug is suspected in the vectorized code, compiling the program or running +the tests with -tags=purego can help verify whether the behavior changes when +the program does not use the assembly versions. + +Maintenance of these functions can be complex; however, the XXH64 algorithm is +unlikely to evolve, and the implementations unlikely to change. The tests in +sum64uint_test.go compare the outputs of MultiSum64* functions with the +reference xxhash.Sum64 function, future maintainers can rely on those tests +passing as a guarantee that they have not introduced regressions. +*/ + +#define PRIME1 0x9E3779B185EBCA87 +#define PRIME2 0xC2B2AE3D27D4EB4F +#define PRIME3 0x165667B19E3779F9 +#define PRIME4 0x85EBCA77C2B2AE63 +#define PRIME5 0x27D4EB2F165667C5 + +#define prime1 R12 +#define prime2 R13 +#define prime3 R14 +#define prime4 R11 +#define prime5 R11 // same as prime4 because they are not used together + +#define prime1ZMM Z12 +#define prime2ZMM Z13 +#define prime3ZMM Z14 +#define prime4ZMM Z15 +#define prime5ZMM Z15 + +DATA prime1vec<>+0(SB)/8, $PRIME1 +DATA prime1vec<>+8(SB)/8, $PRIME1 +DATA prime1vec<>+16(SB)/8, $PRIME1 +DATA prime1vec<>+24(SB)/8, $PRIME1 +DATA prime1vec<>+32(SB)/8, $PRIME1 +DATA prime1vec<>+40(SB)/8, $PRIME1 +DATA prime1vec<>+48(SB)/8, $PRIME1 +DATA prime1vec<>+56(SB)/8, $PRIME1 +GLOBL prime1vec<>(SB), RODATA|NOPTR, $64 + +DATA prime2vec<>+0(SB)/8, $PRIME2 +DATA prime2vec<>+8(SB)/8, $PRIME2 +DATA prime2vec<>+16(SB)/8, $PRIME2 +DATA prime2vec<>+24(SB)/8, $PRIME2 +DATA prime2vec<>+32(SB)/8, $PRIME2 +DATA prime2vec<>+40(SB)/8, $PRIME2 +DATA prime2vec<>+48(SB)/8, $PRIME2 +DATA prime2vec<>+56(SB)/8, $PRIME2 +GLOBL prime2vec<>(SB), RODATA|NOPTR, $64 + +DATA prime3vec<>+0(SB)/8, $PRIME3 +DATA prime3vec<>+8(SB)/8, $PRIME3 +DATA prime3vec<>+16(SB)/8, $PRIME3 +DATA prime3vec<>+24(SB)/8, $PRIME3 +DATA prime3vec<>+32(SB)/8, $PRIME3 +DATA prime3vec<>+40(SB)/8, $PRIME3 +DATA prime3vec<>+48(SB)/8, $PRIME3 +DATA prime3vec<>+56(SB)/8, $PRIME3 +GLOBL prime3vec<>(SB), RODATA|NOPTR, $64 + +DATA prime4vec<>+0(SB)/8, $PRIME4 +DATA prime4vec<>+8(SB)/8, $PRIME4 +DATA prime4vec<>+16(SB)/8, $PRIME4 +DATA prime4vec<>+24(SB)/8, $PRIME4 +DATA prime4vec<>+32(SB)/8, $PRIME4 +DATA prime4vec<>+40(SB)/8, $PRIME4 +DATA prime4vec<>+48(SB)/8, $PRIME4 +DATA prime4vec<>+56(SB)/8, $PRIME4 +GLOBL prime4vec<>(SB), RODATA|NOPTR, $64 + +DATA prime5vec<>+0(SB)/8, $PRIME5 +DATA prime5vec<>+8(SB)/8, $PRIME5 +DATA prime5vec<>+16(SB)/8, $PRIME5 +DATA prime5vec<>+24(SB)/8, $PRIME5 +DATA prime5vec<>+32(SB)/8, $PRIME5 +DATA prime5vec<>+40(SB)/8, $PRIME5 +DATA prime5vec<>+48(SB)/8, $PRIME5 +DATA prime5vec<>+56(SB)/8, $PRIME5 +GLOBL prime5vec<>(SB), RODATA|NOPTR, $64 + +DATA prime5vec1<>+0(SB)/8, $PRIME5+1 +DATA prime5vec1<>+8(SB)/8, $PRIME5+1 +DATA prime5vec1<>+16(SB)/8, $PRIME5+1 +DATA prime5vec1<>+24(SB)/8, $PRIME5+1 +DATA prime5vec1<>+32(SB)/8, $PRIME5+1 +DATA prime5vec1<>+40(SB)/8, $PRIME5+1 +DATA prime5vec1<>+48(SB)/8, $PRIME5+1 +DATA prime5vec1<>+56(SB)/8, $PRIME5+1 +GLOBL prime5vec1<>(SB), RODATA|NOPTR, $64 + +DATA prime5vec2<>+0(SB)/8, $PRIME5+2 +DATA prime5vec2<>+8(SB)/8, $PRIME5+2 +DATA prime5vec2<>+16(SB)/8, $PRIME5+2 +DATA prime5vec2<>+24(SB)/8, $PRIME5+2 +DATA prime5vec2<>+32(SB)/8, $PRIME5+2 +DATA prime5vec2<>+40(SB)/8, $PRIME5+2 +DATA prime5vec2<>+48(SB)/8, $PRIME5+2 +DATA prime5vec2<>+56(SB)/8, $PRIME5+2 +GLOBL prime5vec2<>(SB), RODATA|NOPTR, $64 + +DATA prime5vec4<>+0(SB)/8, $PRIME5+4 +DATA prime5vec4<>+8(SB)/8, $PRIME5+4 +DATA prime5vec4<>+16(SB)/8, $PRIME5+4 +DATA prime5vec4<>+24(SB)/8, $PRIME5+4 +DATA prime5vec4<>+32(SB)/8, $PRIME5+4 +DATA prime5vec4<>+40(SB)/8, $PRIME5+4 +DATA prime5vec4<>+48(SB)/8, $PRIME5+4 +DATA prime5vec4<>+56(SB)/8, $PRIME5+4 +GLOBL prime5vec4<>(SB), RODATA|NOPTR, $64 + +DATA prime5vec8<>+0(SB)/8, $PRIME5+8 +DATA prime5vec8<>+8(SB)/8, $PRIME5+8 +DATA prime5vec8<>+16(SB)/8, $PRIME5+8 +DATA prime5vec8<>+24(SB)/8, $PRIME5+8 +DATA prime5vec8<>+32(SB)/8, $PRIME5+8 +DATA prime5vec8<>+40(SB)/8, $PRIME5+8 +DATA prime5vec8<>+48(SB)/8, $PRIME5+8 +DATA prime5vec8<>+56(SB)/8, $PRIME5+8 +GLOBL prime5vec8<>(SB), RODATA|NOPTR, $64 + +DATA prime5vec16<>+0(SB)/8, $PRIME5+16 +DATA prime5vec16<>+8(SB)/8, $PRIME5+16 +DATA prime5vec16<>+16(SB)/8, $PRIME5+16 +DATA prime5vec16<>+24(SB)/8, $PRIME5+16 +DATA prime5vec16<>+32(SB)/8, $PRIME5+16 +DATA prime5vec16<>+40(SB)/8, $PRIME5+16 +DATA prime5vec16<>+48(SB)/8, $PRIME5+16 +DATA prime5vec16<>+56(SB)/8, $PRIME5+16 +GLOBL prime5vec16<>(SB), RODATA|NOPTR, $64 + +DATA lowbytemask<>+0(SB)/8, $0xFF +DATA lowbytemask<>+8(SB)/8, $0xFF +DATA lowbytemask<>+16(SB)/8, $0xFF +DATA lowbytemask<>+24(SB)/8, $0xFF +DATA lowbytemask<>+32(SB)/8, $0xFF +DATA lowbytemask<>+40(SB)/8, $0xFF +DATA lowbytemask<>+48(SB)/8, $0xFF +DATA lowbytemask<>+56(SB)/8, $0xFF +GLOBL lowbytemask<>(SB), RODATA|NOPTR, $64 + +DATA vpermi2qeven<>+0(SB)/8, $0 +DATA vpermi2qeven<>+8(SB)/8, $2 +DATA vpermi2qeven<>+16(SB)/8, $4 +DATA vpermi2qeven<>+24(SB)/8, $6 +DATA vpermi2qeven<>+32(SB)/8, $(1<<3)|0 +DATA vpermi2qeven<>+40(SB)/8, $(1<<3)|2 +DATA vpermi2qeven<>+48(SB)/8, $(1<<3)|4 +DATA vpermi2qeven<>+56(SB)/8, $(1<<3)|6 +GLOBL vpermi2qeven<>(SB), RODATA|NOPTR, $64 + +DATA vpermi2qodd<>+0(SB)/8, $1 +DATA vpermi2qodd<>+8(SB)/8, $3 +DATA vpermi2qodd<>+16(SB)/8, $5 +DATA vpermi2qodd<>+24(SB)/8, $7 +DATA vpermi2qodd<>+32(SB)/8, $(1<<3)|1 +DATA vpermi2qodd<>+40(SB)/8, $(1<<3)|3 +DATA vpermi2qodd<>+48(SB)/8, $(1<<3)|5 +DATA vpermi2qodd<>+56(SB)/8, $(1<<3)|7 +GLOBL vpermi2qodd<>(SB), RODATA|NOPTR, $64 + +#define round(input, acc) \ + IMULQ prime2, input \ + ADDQ input, acc \ + ROLQ $31, acc \ + IMULQ prime1, acc + +#define avalanche(tmp, acc) \ + MOVQ acc, tmp \ + SHRQ $33, tmp \ + XORQ tmp, acc \ + IMULQ prime2, acc \ + MOVQ acc, tmp \ + SHRQ $29, tmp \ + XORQ tmp, acc \ + IMULQ prime3, acc \ + MOVQ acc, tmp \ + SHRQ $32, tmp \ + XORQ tmp, acc + +#define round8x64(input, acc) \ + VPMULLQ prime2ZMM, input, input \ + VPADDQ input, acc, acc \ + VPROLQ $31, acc, acc \ + VPMULLQ prime1ZMM, acc, acc + +#define avalanche8x64(tmp, acc) \ + VPSRLQ $33, acc, tmp \ + VPXORQ tmp, acc, acc \ + VPMULLQ prime2ZMM, acc, acc \ + VPSRLQ $29, acc, tmp \ + VPXORQ tmp, acc, acc \ + VPMULLQ prime3ZMM, acc, acc \ + VPSRLQ $32, acc, tmp \ + VPXORQ tmp, acc, acc + +// func MultiSum64Uint8(h []uint64, v []uint8) int +TEXT ·MultiSum64Uint8(SB), NOSPLIT, $0-54 + MOVQ $PRIME1, prime1 + MOVQ $PRIME2, prime2 + MOVQ $PRIME3, prime3 + MOVQ $PRIME5, prime5 + + MOVQ h_base+0(FP), AX + MOVQ h_len+8(FP), CX + MOVQ v_base+24(FP), BX + MOVQ v_len+32(FP), DX + + CMPQ CX, DX + CMOVQGT DX, CX + MOVQ CX, ret+48(FP) + + XORQ SI, SI + CMPQ CX, $32 + JB loop + CMPB ·hasAVX512(SB), $0 + JE loop + + MOVQ CX, DI + SHRQ $5, DI + SHLQ $5, DI + + VMOVDQU64 prime1vec<>(SB), prime1ZMM + VMOVDQU64 prime2vec<>(SB), prime2ZMM + VMOVDQU64 prime3vec<>(SB), prime3ZMM + VMOVDQU64 prime5vec<>(SB), prime5ZMM + VMOVDQU64 prime5vec1<>(SB), Z6 +loop32x64: + VMOVDQA64 Z6, Z0 + VMOVDQA64 Z6, Z3 + VMOVDQA64 Z6, Z20 + VMOVDQA64 Z6, Z23 + VPMOVZXBQ (BX)(SI*1), Z1 + VPMOVZXBQ 8(BX)(SI*1), Z4 + VPMOVZXBQ 16(BX)(SI*1), Z21 + VPMOVZXBQ 24(BX)(SI*1), Z24 + + VPMULLQ prime5ZMM, Z1, Z1 + VPMULLQ prime5ZMM, Z4, Z4 + VPMULLQ prime5ZMM, Z21, Z21 + VPMULLQ prime5ZMM, Z24, Z24 + VPXORQ Z1, Z0, Z0 + VPXORQ Z4, Z3, Z3 + VPXORQ Z21, Z20, Z20 + VPXORQ Z24, Z23, Z23 + VPROLQ $11, Z0, Z0 + VPROLQ $11, Z3, Z3 + VPROLQ $11, Z20, Z20 + VPROLQ $11, Z23, Z23 + VPMULLQ prime1ZMM, Z0, Z0 + VPMULLQ prime1ZMM, Z3, Z3 + VPMULLQ prime1ZMM, Z20, Z20 + VPMULLQ prime1ZMM, Z23, Z23 + + avalanche8x64(Z1, Z0) + avalanche8x64(Z4, Z3) + avalanche8x64(Z21, Z20) + avalanche8x64(Z24, Z23) + + VMOVDQU64 Z0, (AX)(SI*8) + VMOVDQU64 Z3, 64(AX)(SI*8) + VMOVDQU64 Z20, 128(AX)(SI*8) + VMOVDQU64 Z23, 192(AX)(SI*8) + ADDQ $32, SI + CMPQ SI, DI + JB loop32x64 + VZEROUPPER +loop: + CMPQ SI, CX + JE done + MOVQ $PRIME5+1, R8 + MOVBQZX (BX)(SI*1), R9 + + IMULQ prime5, R9 + XORQ R9, R8 + ROLQ $11, R8 + IMULQ prime1, R8 + avalanche(R9, R8) + + MOVQ R8, (AX)(SI*8) + INCQ SI + JMP loop +done: + RET + +// func MultiSum64Uint16(h []uint64, v []uint16) int +TEXT ·MultiSum64Uint16(SB), NOSPLIT, $0-54 + MOVQ $PRIME1, prime1 + MOVQ $PRIME2, prime2 + MOVQ $PRIME3, prime3 + MOVQ $PRIME5, prime5 + + MOVQ h_base+0(FP), AX + MOVQ h_len+8(FP), CX + MOVQ v_base+24(FP), BX + MOVQ v_len+32(FP), DX + + CMPQ CX, DX + CMOVQGT DX, CX + MOVQ CX, ret+48(FP) + + XORQ SI, SI + CMPQ CX, $16 + JB loop + CMPB ·hasAVX512(SB), $0 + JE loop + + MOVQ CX, DI + SHRQ $4, DI + SHLQ $4, DI + + VMOVDQU64 prime1vec<>(SB), prime1ZMM + VMOVDQU64 prime2vec<>(SB), prime2ZMM + VMOVDQU64 prime3vec<>(SB), prime3ZMM + VMOVDQU64 prime5vec<>(SB), prime5ZMM + VMOVDQU64 prime5vec2<>(SB), Z6 + VMOVDQU64 lowbytemask<>(SB), Z7 +loop16x64: + VMOVDQA64 Z6, Z0 + VMOVDQA64 Z6, Z3 + VPMOVZXWQ (BX)(SI*2), Z1 + VPMOVZXWQ 16(BX)(SI*2), Z4 + + VMOVDQA64 Z1, Z8 + VMOVDQA64 Z4, Z9 + VPSRLQ $8, Z8, Z8 + VPSRLQ $8, Z9, Z9 + VPANDQ Z7, Z1, Z1 + VPANDQ Z7, Z4, Z4 + + VPMULLQ prime5ZMM, Z1, Z1 + VPMULLQ prime5ZMM, Z4, Z4 + VPXORQ Z1, Z0, Z0 + VPXORQ Z4, Z3, Z3 + VPROLQ $11, Z0, Z0 + VPROLQ $11, Z3, Z3 + VPMULLQ prime1ZMM, Z0, Z0 + VPMULLQ prime1ZMM, Z3, Z3 + + VPMULLQ prime5ZMM, Z8, Z8 + VPMULLQ prime5ZMM, Z9, Z9 + VPXORQ Z8, Z0, Z0 + VPXORQ Z9, Z3, Z3 + VPROLQ $11, Z0, Z0 + VPROLQ $11, Z3, Z3 + VPMULLQ prime1ZMM, Z0, Z0 + VPMULLQ prime1ZMM, Z3, Z3 + + avalanche8x64(Z1, Z0) + avalanche8x64(Z4, Z3) + + VMOVDQU64 Z0, (AX)(SI*8) + VMOVDQU64 Z3, 64(AX)(SI*8) + ADDQ $16, SI + CMPQ SI, DI + JB loop16x64 + VZEROUPPER +loop: + CMPQ SI, CX + JE done + MOVQ $PRIME5+2, R8 + MOVWQZX (BX)(SI*2), R9 + + MOVQ R9, R10 + SHRQ $8, R10 + ANDQ $0xFF, R9 + + IMULQ prime5, R9 + XORQ R9, R8 + ROLQ $11, R8 + IMULQ prime1, R8 + + IMULQ prime5, R10 + XORQ R10, R8 + ROLQ $11, R8 + IMULQ prime1, R8 + + avalanche(R9, R8) + + MOVQ R8, (AX)(SI*8) + INCQ SI + JMP loop +done: + RET + +// func MultiSum64Uint32(h []uint64, v []uint32) int +TEXT ·MultiSum64Uint32(SB), NOSPLIT, $0-54 + MOVQ $PRIME1, prime1 + MOVQ $PRIME2, prime2 + MOVQ $PRIME3, prime3 + + MOVQ h_base+0(FP), AX + MOVQ h_len+8(FP), CX + MOVQ v_base+24(FP), BX + MOVQ v_len+32(FP), DX + + CMPQ CX, DX + CMOVQGT DX, CX + MOVQ CX, ret+48(FP) + + XORQ SI, SI + CMPQ CX, $32 + JB loop + CMPB ·hasAVX512(SB), $0 + JE loop + + MOVQ CX, DI + SHRQ $5, DI + SHLQ $5, DI + + VMOVDQU64 prime1vec<>(SB), prime1ZMM + VMOVDQU64 prime2vec<>(SB), prime2ZMM + VMOVDQU64 prime3vec<>(SB), prime3ZMM + VMOVDQU64 prime5vec4<>(SB), Z6 +loop32x64: + VMOVDQA64 Z6, Z0 + VMOVDQA64 Z6, Z3 + VMOVDQA64 Z6, Z20 + VMOVDQA64 Z6, Z23 + VPMOVZXDQ (BX)(SI*4), Z1 + VPMOVZXDQ 32(BX)(SI*4), Z4 + VPMOVZXDQ 64(BX)(SI*4), Z21 + VPMOVZXDQ 96(BX)(SI*4), Z24 + + VPMULLQ prime1ZMM, Z1, Z1 + VPMULLQ prime1ZMM, Z4, Z4 + VPMULLQ prime1ZMM, Z21, Z21 + VPMULLQ prime1ZMM, Z24, Z24 + VPXORQ Z1, Z0, Z0 + VPXORQ Z4, Z3, Z3 + VPXORQ Z21, Z20, Z20 + VPXORQ Z24, Z23, Z23 + VPROLQ $23, Z0, Z0 + VPROLQ $23, Z3, Z3 + VPROLQ $23, Z20, Z20 + VPROLQ $23, Z23, Z23 + VPMULLQ prime2ZMM, Z0, Z0 + VPMULLQ prime2ZMM, Z3, Z3 + VPMULLQ prime2ZMM, Z20, Z20 + VPMULLQ prime2ZMM, Z23, Z23 + VPADDQ prime3ZMM, Z0, Z0 + VPADDQ prime3ZMM, Z3, Z3 + VPADDQ prime3ZMM, Z20, Z20 + VPADDQ prime3ZMM, Z23, Z23 + + avalanche8x64(Z1, Z0) + avalanche8x64(Z4, Z3) + avalanche8x64(Z21, Z20) + avalanche8x64(Z24, Z23) + + VMOVDQU64 Z0, (AX)(SI*8) + VMOVDQU64 Z3, 64(AX)(SI*8) + VMOVDQU64 Z20, 128(AX)(SI*8) + VMOVDQU64 Z23, 192(AX)(SI*8) + ADDQ $32, SI + CMPQ SI, DI + JB loop32x64 + VZEROUPPER +loop: + CMPQ SI, CX + JE done + MOVQ $PRIME5+4, R8 + MOVLQZX (BX)(SI*4), R9 + + IMULQ prime1, R9 + XORQ R9, R8 + ROLQ $23, R8 + IMULQ prime2, R8 + ADDQ prime3, R8 + avalanche(R9, R8) + + MOVQ R8, (AX)(SI*8) + INCQ SI + JMP loop +done: + RET + +// func MultiSum64Uint64(h []uint64, v []uint64) int +TEXT ·MultiSum64Uint64(SB), NOSPLIT, $0-54 + MOVQ $PRIME1, prime1 + MOVQ $PRIME2, prime2 + MOVQ $PRIME3, prime3 + MOVQ $PRIME4, prime4 + + MOVQ h_base+0(FP), AX + MOVQ h_len+8(FP), CX + MOVQ v_base+24(FP), BX + MOVQ v_len+32(FP), DX + + CMPQ CX, DX + CMOVQGT DX, CX + MOVQ CX, ret+48(FP) + + XORQ SI, SI + CMPQ CX, $32 + JB loop + CMPB ·hasAVX512(SB), $0 + JE loop + + MOVQ CX, DI + SHRQ $5, DI + SHLQ $5, DI + + VMOVDQU64 prime1vec<>(SB), prime1ZMM + VMOVDQU64 prime2vec<>(SB), prime2ZMM + VMOVDQU64 prime3vec<>(SB), prime3ZMM + VMOVDQU64 prime4vec<>(SB), prime4ZMM + VMOVDQU64 prime5vec8<>(SB), Z6 +loop32x64: + VMOVDQA64 Z6, Z0 + VMOVDQA64 Z6, Z3 + VMOVDQA64 Z6, Z20 + VMOVDQA64 Z6, Z23 + VMOVDQU64 (BX)(SI*8), Z1 + VMOVDQU64 64(BX)(SI*8), Z4 + VMOVDQU64 128(BX)(SI*8), Z21 + VMOVDQU64 192(BX)(SI*8), Z24 + + VPXORQ Z2, Z2, Z2 + VPXORQ Z5, Z5, Z5 + VPXORQ Z22, Z22, Z22 + VPXORQ Z25, Z25, Z25 + round8x64(Z1, Z2) + round8x64(Z4, Z5) + round8x64(Z21, Z22) + round8x64(Z24, Z25) + + VPXORQ Z2, Z0, Z0 + VPXORQ Z5, Z3, Z3 + VPXORQ Z22, Z20, Z20 + VPXORQ Z25, Z23, Z23 + VPROLQ $27, Z0, Z0 + VPROLQ $27, Z3, Z3 + VPROLQ $27, Z20, Z20 + VPROLQ $27, Z23, Z23 + VPMULLQ prime1ZMM, Z0, Z0 + VPMULLQ prime1ZMM, Z3, Z3 + VPMULLQ prime1ZMM, Z20, Z20 + VPMULLQ prime1ZMM, Z23, Z23 + VPADDQ prime4ZMM, Z0, Z0 + VPADDQ prime4ZMM, Z3, Z3 + VPADDQ prime4ZMM, Z20, Z20 + VPADDQ prime4ZMM, Z23, Z23 + + avalanche8x64(Z1, Z0) + avalanche8x64(Z4, Z3) + avalanche8x64(Z21, Z20) + avalanche8x64(Z24, Z23) + + VMOVDQU64 Z0, (AX)(SI*8) + VMOVDQU64 Z3, 64(AX)(SI*8) + VMOVDQU64 Z20, 128(AX)(SI*8) + VMOVDQU64 Z23, 192(AX)(SI*8) + ADDQ $32, SI + CMPQ SI, DI + JB loop32x64 + VZEROUPPER +loop: + CMPQ SI, CX + JE done + MOVQ $PRIME5+8, R8 + MOVQ (BX)(SI*8), R9 + + XORQ R10, R10 + round(R9, R10) + XORQ R10, R8 + ROLQ $27, R8 + IMULQ prime1, R8 + ADDQ prime4, R8 + avalanche(R9, R8) + + MOVQ R8, (AX)(SI*8) + INCQ SI + JMP loop +done: + RET + +// func MultiSum64Uint128(h []uint64, v [][16]byte) int +TEXT ·MultiSum64Uint128(SB), NOSPLIT, $0-54 + MOVQ $PRIME1, prime1 + MOVQ $PRIME2, prime2 + MOVQ $PRIME3, prime3 + MOVQ $PRIME4, prime4 + + MOVQ h_base+0(FP), AX + MOVQ h_len+8(FP), CX + MOVQ v_base+24(FP), BX + MOVQ v_len+32(FP), DX + + CMPQ CX, DX + CMOVQGT DX, CX + MOVQ CX, ret+48(FP) + + XORQ SI, SI + CMPQ CX, $16 + JB loop + CMPB ·hasAVX512(SB), $0 + JE loop + + MOVQ CX, DI + SHRQ $4, DI + SHLQ $4, DI + + VMOVDQU64 prime1vec<>(SB), prime1ZMM + VMOVDQU64 prime2vec<>(SB), prime2ZMM + VMOVDQU64 prime3vec<>(SB), prime3ZMM + VMOVDQU64 prime4vec<>(SB), prime4ZMM + VMOVDQU64 prime5vec16<>(SB), Z6 + VMOVDQU64 vpermi2qeven<>(SB), Z7 + VMOVDQU64 vpermi2qodd<>(SB), Z8 +loop16x64: + // This algorithm is slightly different from the other ones, because it is + // the only case where the input values are larger than the output (128 bits + // vs 64 bits). + // + // Computing the XXH64 of 128 bits values requires doing two passes over the + // lower and upper 64 bits. The lower and upper quad/ words are split in + // separate vectors, the first pass is applied on the vector holding the + // lower bits of 8 input values, then the second pass is applied with the + // vector holding the upper bits. + // + // Following the model used in the other functions, we unroll the work of + // two consecutive groups of 8 values per loop iteration in order to + // maximize utilization of CPU resources. + CMPQ SI, DI + JE loop + VMOVDQA64 Z6, Z0 + VMOVDQA64 Z6, Z20 + VMOVDQU64 (BX), Z1 + VMOVDQU64 64(BX), Z9 + VMOVDQU64 128(BX), Z21 + VMOVDQU64 192(BX), Z29 + + VMOVDQA64 Z7, Z2 + VMOVDQA64 Z8, Z3 + VMOVDQA64 Z7, Z22 + VMOVDQA64 Z8, Z23 + + VPERMI2Q Z9, Z1, Z2 + VPERMI2Q Z9, Z1, Z3 + VPERMI2Q Z29, Z21, Z22 + VPERMI2Q Z29, Z21, Z23 + + // Compute the rounds on inputs. + VPXORQ Z4, Z4, Z4 + VPXORQ Z5, Z5, Z5 + VPXORQ Z24, Z24, Z24 + VPXORQ Z25, Z25, Z25 + round8x64(Z2, Z4) + round8x64(Z3, Z5) + round8x64(Z22, Z24) + round8x64(Z23, Z25) + + // Lower 64 bits. + VPXORQ Z4, Z0, Z0 + VPXORQ Z24, Z20, Z20 + VPROLQ $27, Z0, Z0 + VPROLQ $27, Z20, Z20 + VPMULLQ prime1ZMM, Z0, Z0 + VPMULLQ prime1ZMM, Z20, Z20 + VPADDQ prime4ZMM, Z0, Z0 + VPADDQ prime4ZMM, Z20, Z20 + + // Upper 64 bits. + VPXORQ Z5, Z0, Z0 + VPXORQ Z25, Z20, Z20 + VPROLQ $27, Z0, Z0 + VPROLQ $27, Z20, Z20 + VPMULLQ prime1ZMM, Z0, Z0 + VPMULLQ prime1ZMM, Z20, Z20 + VPADDQ prime4ZMM, Z0, Z0 + VPADDQ prime4ZMM, Z20, Z20 + + avalanche8x64(Z1, Z0) + avalanche8x64(Z21, Z20) + VMOVDQU64 Z0, (AX)(SI*8) + VMOVDQU64 Z20, 64(AX)(SI*8) + ADDQ $256, BX + ADDQ $16, SI + JMP loop16x64 + VZEROUPPER +loop: + CMPQ SI, CX + JE done + MOVQ $PRIME5+16, R8 + MOVQ (BX), DX + MOVQ 8(BX), DI + + XORQ R9, R9 + XORQ R10, R10 + round(DX, R9) + round(DI, R10) + + XORQ R9, R8 + ROLQ $27, R8 + IMULQ prime1, R8 + ADDQ prime4, R8 + + XORQ R10, R8 + ROLQ $27, R8 + IMULQ prime1, R8 + ADDQ prime4, R8 + + avalanche(R9, R8) + MOVQ R8, (AX)(SI*8) + ADDQ $16, BX + INCQ SI + JMP loop +done: + RET diff --git a/vendor/github.com/parquet-go/parquet-go/bloom/xxhash/sum64uint_purego.go b/vendor/github.com/parquet-go/parquet-go/bloom/xxhash/sum64uint_purego.go new file mode 100644 index 000000000..4b420dc4f --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/bloom/xxhash/sum64uint_purego.go @@ -0,0 +1,53 @@ +//go:build purego || !amd64 + +package xxhash + +func MultiSum64Uint8(h []uint64, v []uint8) int { + n := min(len(h), len(v)) + h = h[:n] + v = v[:n] + for i := range v { + h[i] = Sum64Uint8(v[i]) + } + return n +} + +func MultiSum64Uint16(h []uint64, v []uint16) int { + n := min(len(h), len(v)) + h = h[:n] + v = v[:n] + for i := range v { + h[i] = Sum64Uint16(v[i]) + } + return n +} + +func MultiSum64Uint32(h []uint64, v []uint32) int { + n := min(len(h), len(v)) + h = h[:n] + v = v[:n] + for i := range v { + h[i] = Sum64Uint32(v[i]) + } + return n +} + +func MultiSum64Uint64(h []uint64, v []uint64) int { + n := min(len(h), len(v)) + h = h[:n] + v = v[:n] + for i := range v { + h[i] = Sum64Uint64(v[i]) + } + return n +} + +func MultiSum64Uint128(h []uint64, v [][16]byte) int { + n := min(len(h), len(v)) + h = h[:n] + v = v[:n] + for i := range v { + h[i] = Sum64Uint128(v[i]) + } + return n +} diff --git a/vendor/github.com/parquet-go/parquet-go/bloom/xxhash/xxhash.go b/vendor/github.com/parquet-go/parquet-go/bloom/xxhash/xxhash.go new file mode 100644 index 000000000..0d4b44b2d --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/bloom/xxhash/xxhash.go @@ -0,0 +1,55 @@ +// Package xxhash is an extension of github.com/cespare/xxhash which adds +// routines optimized to hash arrays of fixed size elements. +package xxhash + +import ( + "encoding/binary" + "math/bits" +) + +const ( + prime1 uint64 = 0x9E3779B185EBCA87 + prime2 uint64 = 0xC2B2AE3D27D4EB4F + prime3 uint64 = 0x165667B19E3779F9 + prime4 uint64 = 0x85EBCA77C2B2AE63 + prime5 uint64 = 0x27D4EB2F165667C5 + // Pre-computed operations because the compiler otherwise complains that the + // results overflow 64 bit integers. + prime1plus2 uint64 = 0x60EA27EEADC0B5D6 // prime1 + prime2 + negprime1 uint64 = 0x61C8864E7A143579 // -prime1 +) + +func avalanche(h uint64) uint64 { + h ^= h >> 33 + h *= prime2 + h ^= h >> 29 + h *= prime3 + h ^= h >> 32 + return h +} + +func round(acc, input uint64) uint64 { + acc += input * prime2 + acc = rol31(acc) + acc *= prime1 + return acc +} + +func mergeRound(acc, val uint64) uint64 { + val = round(0, val) + acc ^= val + acc = acc*prime1 + prime4 + return acc +} + +func u64(b []byte) uint64 { return binary.LittleEndian.Uint64(b) } +func u32(b []byte) uint32 { return binary.LittleEndian.Uint32(b) } + +func rol1(x uint64) uint64 { return bits.RotateLeft64(x, 1) } +func rol7(x uint64) uint64 { return bits.RotateLeft64(x, 7) } +func rol11(x uint64) uint64 { return bits.RotateLeft64(x, 11) } +func rol12(x uint64) uint64 { return bits.RotateLeft64(x, 12) } +func rol18(x uint64) uint64 { return bits.RotateLeft64(x, 18) } +func rol23(x uint64) uint64 { return bits.RotateLeft64(x, 23) } +func rol27(x uint64) uint64 { return bits.RotateLeft64(x, 27) } +func rol31(x uint64) uint64 { return bits.RotateLeft64(x, 31) } diff --git a/vendor/github.com/parquet-go/parquet-go/bloom/xxhash/xxhash_amd64.go b/vendor/github.com/parquet-go/parquet-go/bloom/xxhash/xxhash_amd64.go new file mode 100644 index 000000000..4b32923de --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/bloom/xxhash/xxhash_amd64.go @@ -0,0 +1,6 @@ +//go:build !purego + +package xxhash + +// Sum64 computes the 64-bit xxHash digest of b. +func Sum64(b []byte) uint64 diff --git a/vendor/github.com/parquet-go/parquet-go/bloom/xxhash/xxhash_amd64.s b/vendor/github.com/parquet-go/parquet-go/bloom/xxhash/xxhash_amd64.s new file mode 100644 index 000000000..8f88dc1d6 --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/bloom/xxhash/xxhash_amd64.s @@ -0,0 +1,180 @@ +//go:build !purego + +#include "textflag.h" + +#define PRIME1 0x9E3779B185EBCA87 +#define PRIME2 0xC2B2AE3D27D4EB4F +#define PRIME3 0x165667B19E3779F9 +#define PRIME4 0x85EBCA77C2B2AE63 +#define PRIME5 0x27D4EB2F165667C5 + +DATA prime3<>+0(SB)/8, $PRIME3 +GLOBL prime3<>(SB), RODATA|NOPTR, $8 + +DATA prime5<>+0(SB)/8, $PRIME5 +GLOBL prime5<>(SB), RODATA|NOPTR, $8 + +// Register allocation: +// AX h +// SI pointer to advance through b +// DX n +// BX loop end +// R8 v1, k1 +// R9 v2 +// R10 v3 +// R11 v4 +// R12 tmp +// R13 PRIME1 +// R14 PRIME2 +// DI PRIME4 + +// round reads from and advances the buffer pointer in SI. +// It assumes that R13 has PRIME1 and R14 has PRIME2. +#define round(r) \ + MOVQ (SI), R12 \ + ADDQ $8, SI \ + IMULQ R14, R12 \ + ADDQ R12, r \ + ROLQ $31, r \ + IMULQ R13, r + +// mergeRound applies a merge round on the two registers acc and val. +// It assumes that R13 has PRIME1, R14 has PRIME2, and DI has PRIME4. +#define mergeRound(acc, val) \ + IMULQ R14, val \ + ROLQ $31, val \ + IMULQ R13, val \ + XORQ val, acc \ + IMULQ R13, acc \ + ADDQ DI, acc + +// func Sum64(b []byte) uint64 +TEXT ·Sum64(SB), NOSPLIT, $0-32 + // Load fixed primes. + MOVQ $PRIME1, R13 + MOVQ $PRIME2, R14 + MOVQ $PRIME4, DI + + // Load slice. + MOVQ b_base+0(FP), SI + MOVQ b_len+8(FP), DX + LEAQ (SI)(DX*1), BX + + // The first loop limit will be len(b)-32. + SUBQ $32, BX + + // Check whether we have at least one block. + CMPQ DX, $32 + JLT noBlocks + + // Set up initial state (v1, v2, v3, v4). + MOVQ R13, R8 + ADDQ R14, R8 + MOVQ R14, R9 + XORQ R10, R10 + XORQ R11, R11 + SUBQ R13, R11 + + // Loop until SI > BX. +blockLoop: + round(R8) + round(R9) + round(R10) + round(R11) + + CMPQ SI, BX + JLE blockLoop + + MOVQ R8, AX + ROLQ $1, AX + MOVQ R9, R12 + ROLQ $7, R12 + ADDQ R12, AX + MOVQ R10, R12 + ROLQ $12, R12 + ADDQ R12, AX + MOVQ R11, R12 + ROLQ $18, R12 + ADDQ R12, AX + + mergeRound(AX, R8) + mergeRound(AX, R9) + mergeRound(AX, R10) + mergeRound(AX, R11) + + JMP afterBlocks + +noBlocks: + MOVQ $PRIME5, AX + +afterBlocks: + ADDQ DX, AX + + // Right now BX has len(b)-32, and we want to loop until SI > len(b)-8. + ADDQ $24, BX + + CMPQ SI, BX + JG fourByte + +wordLoop: + // Calculate k1. + MOVQ (SI), R8 + ADDQ $8, SI + IMULQ R14, R8 + ROLQ $31, R8 + IMULQ R13, R8 + + XORQ R8, AX + ROLQ $27, AX + IMULQ R13, AX + ADDQ DI, AX + + CMPQ SI, BX + JLE wordLoop + +fourByte: + ADDQ $4, BX + CMPQ SI, BX + JG singles + + MOVL (SI), R8 + ADDQ $4, SI + IMULQ R13, R8 + XORQ R8, AX + + ROLQ $23, AX + IMULQ R14, AX + ADDQ prime3<>(SB), AX + +singles: + ADDQ $4, BX + CMPQ SI, BX + JGE finalize + +singlesLoop: + MOVBQZX (SI), R12 + ADDQ $1, SI + IMULQ prime5<>(SB), R12 + XORQ R12, AX + + ROLQ $11, AX + IMULQ R13, AX + + CMPQ SI, BX + JL singlesLoop + +finalize: + MOVQ AX, R12 + SHRQ $33, R12 + XORQ R12, AX + IMULQ R14, AX + MOVQ AX, R12 + SHRQ $29, R12 + XORQ R12, AX + IMULQ prime3<>(SB), AX + MOVQ AX, R12 + SHRQ $32, R12 + XORQ R12, AX + + MOVQ AX, ret+24(FP) + RET diff --git a/vendor/github.com/parquet-go/parquet-go/bloom/xxhash/xxhash_purego.go b/vendor/github.com/parquet-go/parquet-go/bloom/xxhash/xxhash_purego.go new file mode 100644 index 000000000..896f817a5 --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/bloom/xxhash/xxhash_purego.go @@ -0,0 +1,50 @@ +//go:build purego || !amd64 + +package xxhash + +// Sum64 computes the 64-bit xxHash digest of b. +func Sum64(b []byte) uint64 { + var n = len(b) + var h uint64 + + if n >= 32 { + v1 := prime1plus2 + v2 := prime2 + v3 := uint64(0) + v4 := negprime1 + for len(b) >= 32 { + v1 = round(v1, u64(b[0:8:len(b)])) + v2 = round(v2, u64(b[8:16:len(b)])) + v3 = round(v3, u64(b[16:24:len(b)])) + v4 = round(v4, u64(b[24:32:len(b)])) + b = b[32:len(b):len(b)] + } + h = rol1(v1) + rol7(v2) + rol12(v3) + rol18(v4) + h = mergeRound(h, v1) + h = mergeRound(h, v2) + h = mergeRound(h, v3) + h = mergeRound(h, v4) + } else { + h = prime5 + } + + h += uint64(n) + + i, end := 0, len(b) + for ; i+8 <= end; i += 8 { + k1 := round(0, u64(b[i:i+8:len(b)])) + h ^= k1 + h = rol27(h)*prime1 + prime4 + } + if i+4 <= end { + h ^= uint64(u32(b[i:i+4:len(b)])) * prime1 + h = rol23(h)*prime2 + prime3 + i += 4 + } + for ; i < end; i++ { + h ^= uint64(b[i]) * prime5 + h = rol11(h) * prime1 + } + + return avalanche(h) +} diff --git a/vendor/github.com/parquet-go/parquet-go/bloom_be.go b/vendor/github.com/parquet-go/parquet-go/bloom_be.go new file mode 100644 index 000000000..f7800301a --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/bloom_be.go @@ -0,0 +1,19 @@ +//go:build s390x + +package parquet + +import ( + "encoding/binary" + + "github.com/parquet-go/parquet-go/deprecated" +) + +func unsafecastInt96ToBytes(src []deprecated.Int96) []byte { + out := make([]byte, len(src)*12) + for i := range src { + binary.LittleEndian.PutUint32(out[(i*12):4+(i*12)], uint32(src[i][0])) + binary.LittleEndian.PutUint32(out[4+(i*12):8+(i*12)], uint32(src[i][1])) + binary.LittleEndian.PutUint32(out[8+(i*12):12+(i*12)], uint32(src[i][2])) + } + return out +} diff --git a/vendor/github.com/parquet-go/parquet-go/bloom_le.go b/vendor/github.com/parquet-go/parquet-go/bloom_le.go new file mode 100644 index 000000000..6f8bbdcd8 --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/bloom_le.go @@ -0,0 +1,12 @@ +//go:build !s390x + +package parquet + +import ( + "github.com/parquet-go/bitpack/unsafecast" + "github.com/parquet-go/parquet-go/deprecated" +) + +func unsafecastInt96ToBytes(src []deprecated.Int96) []byte { + return unsafecast.Slice[byte](src) +} diff --git a/vendor/github.com/parquet-go/parquet-go/buf.gen.yaml b/vendor/github.com/parquet-go/parquet-go/buf.gen.yaml new file mode 100644 index 000000000..f2a2315a9 --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/buf.gen.yaml @@ -0,0 +1,6 @@ +version: v2 +plugins: + - remote: buf.build/protocolbuffers/go + out: testdata/gen/go + opt: + - paths=source_relative diff --git a/vendor/github.com/parquet-go/parquet-go/buf.yaml b/vendor/github.com/parquet-go/parquet-go/buf.yaml new file mode 100644 index 000000000..9cf6e83a4 --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/buf.yaml @@ -0,0 +1,9 @@ +version: v2 +modules: + - path: testdata/proto +lint: + use: + - STANDARD +breaking: + use: + - FILE diff --git a/vendor/github.com/parquet-go/parquet-go/buffer.go b/vendor/github.com/parquet-go/parquet-go/buffer.go new file mode 100644 index 000000000..2bb2dea74 --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/buffer.go @@ -0,0 +1,708 @@ +package parquet + +import ( + "log" + "reflect" + "runtime" + "slices" + "sort" + "sync/atomic" + + "github.com/parquet-go/parquet-go/internal/debug" + "github.com/parquet-go/parquet-go/internal/memory" +) + +// GenericBuffer is similar to a Buffer but uses a type parameter to define the +// Go type representing the schema of rows in the buffer. +// +// See GenericWriter for details about the benefits over the classic Buffer API. +type GenericBuffer[T any] struct { + base Buffer + write bufferFunc[T] +} + +// NewGenericBuffer is like NewBuffer but returns a GenericBuffer[T] suited to write +// rows of Go type T. +// +// The type parameter T should be a map, struct, or any. Any other types will +// cause a panic at runtime. Type checking is a lot more effective when the +// generic parameter is a struct type, using map and interface types is somewhat +// similar to using a Writer. If using an interface type for the type parameter, +// then providing a schema at instantiation is required. +// +// If the option list may explicitly declare a schema, it must be compatible +// with the schema generated from T. +func NewGenericBuffer[T any](options ...RowGroupOption) *GenericBuffer[T] { + config, err := NewRowGroupConfig(options...) + if err != nil { + panic(err) + } + + t := typeOf[T]() + if config.Schema == nil && t != nil { + config.Schema = schemaOf(dereference(t)) + } + + if config.Schema == nil { + panic("generic buffer must be instantiated with schema or concrete type.") + } + + buf := &GenericBuffer[T]{ + base: Buffer{config: config}, + } + buf.base.configure(config.Schema) + buf.write = bufferFuncOf[T](t, config.Schema) + return buf +} + +func typeOf[T any]() reflect.Type { + var v T + return reflect.TypeOf(v) +} + +type bufferFunc[T any] func(*GenericBuffer[T], []T) (int, error) + +func bufferFuncOf[T any](t reflect.Type, schema *Schema) bufferFunc[T] { + if t == nil { + return (*GenericBuffer[T]).writeRows + } + switch t.Kind() { + case reflect.Interface, reflect.Map: + return (*GenericBuffer[T]).writeRows + + case reflect.Struct: + return makeBufferFunc[T](t, schema) + + case reflect.Pointer: + if e := t.Elem(); e.Kind() == reflect.Struct { + return makeBufferFunc[T](t, schema) + } + } + panic("cannot create buffer for values of type " + t.String()) +} + +func makeBufferFunc[T any](t reflect.Type, schema *Schema) bufferFunc[T] { + writeRows := writeRowsFuncOf(t, schema, nil, nil) + return func(buf *GenericBuffer[T], rows []T) (n int, err error) { + writeRows(buf.base.columns, columnLevels{}, makeArrayFromSlice(rows)) + return len(rows), nil + } +} + +func (buf *GenericBuffer[T]) Size() int64 { + return buf.base.Size() +} + +func (buf *GenericBuffer[T]) NumRows() int64 { + return buf.base.NumRows() +} + +func (buf *GenericBuffer[T]) ColumnChunks() []ColumnChunk { + return buf.base.ColumnChunks() +} + +func (buf *GenericBuffer[T]) ColumnBuffers() []ColumnBuffer { + return buf.base.ColumnBuffers() +} + +func (buf *GenericBuffer[T]) SortingColumns() []SortingColumn { + return buf.base.SortingColumns() +} + +func (buf *GenericBuffer[T]) Len() int { + return buf.base.Len() +} + +func (buf *GenericBuffer[T]) Less(i, j int) bool { + return buf.base.Less(i, j) +} + +func (buf *GenericBuffer[T]) Swap(i, j int) { + buf.base.Swap(i, j) +} + +func (buf *GenericBuffer[T]) Reset() { + buf.base.Reset() +} + +func (buf *GenericBuffer[T]) Write(rows []T) (int, error) { + if len(rows) == 0 { + return 0, nil + } + return buf.write(buf, rows) +} + +func (buf *GenericBuffer[T]) WriteRows(rows []Row) (int, error) { + return buf.base.WriteRows(rows) +} + +func (buf *GenericBuffer[T]) WriteRowGroup(rowGroup RowGroup) (int64, error) { + return buf.base.WriteRowGroup(rowGroup) +} + +func (buf *GenericBuffer[T]) Rows() Rows { + return buf.base.Rows() +} + +func (buf *GenericBuffer[T]) Schema() *Schema { + return buf.base.Schema() +} + +func (buf *GenericBuffer[T]) writeRows(rows []T) (int, error) { + if cap(buf.base.rowbuf) < len(rows) { + buf.base.rowbuf = make([]Row, len(rows)) + } else { + buf.base.rowbuf = buf.base.rowbuf[:len(rows)] + } + defer clearRows(buf.base.rowbuf) + + schema := buf.base.Schema() + for i := range rows { + buf.base.rowbuf[i] = schema.Deconstruct(buf.base.rowbuf[i], &rows[i]) + } + + return buf.base.WriteRows(buf.base.rowbuf) +} + +var ( + _ RowGroup = (*GenericBuffer[any])(nil) + _ RowGroupWriter = (*GenericBuffer[any])(nil) + _ sort.Interface = (*GenericBuffer[any])(nil) + + _ RowGroup = (*GenericBuffer[struct{}])(nil) + _ RowGroupWriter = (*GenericBuffer[struct{}])(nil) + _ sort.Interface = (*GenericBuffer[struct{}])(nil) + + _ RowGroup = (*GenericBuffer[map[struct{}]struct{}])(nil) + _ RowGroupWriter = (*GenericBuffer[map[struct{}]struct{}])(nil) + _ sort.Interface = (*GenericBuffer[map[struct{}]struct{}])(nil) +) + +// Buffer represents an in-memory group of parquet rows. +// +// The main purpose of the Buffer type is to provide a way to sort rows before +// writing them to a parquet file. Buffer implements sort.Interface as a way +// to support reordering the rows that have been written to it. +type Buffer struct { + config *RowGroupConfig + schema *Schema + rowbuf []Row + colbuf [][]Value + chunks []ColumnChunk + columns []ColumnBuffer + sorted []ColumnBuffer +} + +// NewBuffer constructs a new buffer, using the given list of buffer options +// to configure the buffer returned by the function. +// +// The function panics if the buffer configuration is invalid. Programs that +// cannot guarantee the validity of the options passed to NewBuffer should +// construct the buffer configuration independently prior to calling this +// function: +// +// config, err := parquet.NewRowGroupConfig(options...) +// if err != nil { +// // handle the configuration error +// ... +// } else { +// // this call to create a buffer is guaranteed not to panic +// buffer := parquet.NewBuffer(config) +// ... +// } +func NewBuffer(options ...RowGroupOption) *Buffer { + config, err := NewRowGroupConfig(options...) + if err != nil { + panic(err) + } + buf := &Buffer{ + config: config, + } + if config.Schema != nil { + buf.configure(config.Schema) + } + return buf +} + +// configure sets up the buffer's columns based on the provided schema. +// It also prepares the internal sorting logic by using only the requested sorting columns +// (from buf.config.Sorting.SortingColumns) that are actually found within the schema, +// preserving the requested order but ignoring missing columns. +func (buf *Buffer) configure(schema *Schema) { + if schema == nil { + return + } + sortingColumns := buf.config.Sorting.SortingColumns + buf.sorted = make([]ColumnBuffer, len(sortingColumns)) + + forEachLeafColumnOf(schema, func(leaf leafColumn) { + nullOrdering := nullsGoLast + columnIndex := int(leaf.columnIndex) + columnType := leaf.node.Type() + bufferCap := buf.config.ColumnBufferCapacity + dictionary := (Dictionary)(nil) + encoding := encodingOf(leaf.node, nil) + + if isDictionaryEncoding(encoding) { + estimatedDictBufferSize := columnType.EstimateSize(bufferCap) + dictBuffer := columnType.NewValues( + make([]byte, 0, estimatedDictBufferSize), + nil, + ) + dictionary = columnType.NewDictionary(columnIndex, 0, dictBuffer) + columnType = dictionary.Type() + } + + sortingIndex := searchSortingColumn(sortingColumns, leaf.path) + if sortingIndex < len(sortingColumns) && sortingColumns[sortingIndex].NullsFirst() { + nullOrdering = nullsGoFirst + } + + column := columnType.NewColumnBuffer(columnIndex, bufferCap) + switch { + case leaf.maxRepetitionLevel > 0: + column = newRepeatedColumnBuffer(column, leaf.maxRepetitionLevel, leaf.maxDefinitionLevel, nullOrdering) + case leaf.maxDefinitionLevel > 0: + column = newOptionalColumnBuffer(column, leaf.maxDefinitionLevel, nullOrdering) + } + buf.columns = append(buf.columns, column) + + if sortingIndex < len(sortingColumns) { + if sortingColumns[sortingIndex].Descending() { + column = &reversedColumnBuffer{column} + } + buf.sorted[sortingIndex] = column + } + }) + + buf.sorted = slices.DeleteFunc(buf.sorted, func(cb ColumnBuffer) bool { return cb == nil }) + + buf.schema = schema + buf.rowbuf = make([]Row, 0, 1) + buf.colbuf = make([][]Value, len(buf.columns)) + buf.chunks = make([]ColumnChunk, len(buf.columns)) + + for i, column := range buf.columns { + buf.chunks[i] = column + } +} + +// Size returns the estimated size of the buffer in memory (in bytes). +func (buf *Buffer) Size() int64 { + size := int64(0) + for _, col := range buf.columns { + size += col.Size() + } + return size +} + +// NumRows returns the number of rows written to the buffer. +func (buf *Buffer) NumRows() int64 { return int64(buf.Len()) } + +// ColumnChunks returns the buffer columns. +func (buf *Buffer) ColumnChunks() []ColumnChunk { return buf.chunks } + +// ColumnBuffers returns the buffer columns. +// +// This method is similar to ColumnChunks, but returns a list of ColumnBuffer +// instead of a list of ColumnChunk (the latter being read-only); calling +// ColumnBuffers or ColumnChunks with the same index returns the same underlying +// objects, but with different types, which removes the need for making a type +// assertion if the program needed to write directly to the column buffers. +// The presence of the ColumnChunks method is still required to satisfy the +// RowGroup interface. +func (buf *Buffer) ColumnBuffers() []ColumnBuffer { return buf.columns } + +// Schema returns the schema of the buffer. +// +// The schema is either configured by passing a Schema in the option list when +// constructing the buffer, or lazily discovered when the first row is written. +func (buf *Buffer) Schema() *Schema { return buf.schema } + +// SortingColumns returns the list of columns by which the buffer will be +// sorted. +// +// The sorting order is configured by passing a SortingColumns option when +// constructing the buffer. +func (buf *Buffer) SortingColumns() []SortingColumn { return buf.config.Sorting.SortingColumns } + +// Len returns the number of rows written to the buffer. +func (buf *Buffer) Len() int { + if len(buf.columns) == 0 { + return 0 + } else { + // All columns have the same number of rows. + return buf.columns[0].Len() + } +} + +// Less returns true if row[i] < row[j] in the buffer. +func (buf *Buffer) Less(i, j int) bool { + for _, col := range buf.sorted { + switch { + case col.Less(i, j): + return true + case col.Less(j, i): + return false + } + } + return false +} + +// Swap exchanges the rows at indexes i and j. +func (buf *Buffer) Swap(i, j int) { + for _, col := range buf.columns { + col.Swap(i, j) + } +} + +// Reset clears the content of the buffer, allowing it to be reused. +func (buf *Buffer) Reset() { + for _, col := range buf.columns { + col.Reset() + } +} + +// Write writes a row held in a Go value to the buffer. +func (buf *Buffer) Write(row any) error { + if buf.schema == nil { + buf.configure(SchemaOf(row)) + } + + buf.rowbuf = buf.rowbuf[:1] + defer clearRows(buf.rowbuf) + + buf.rowbuf[0] = buf.schema.Deconstruct(buf.rowbuf[0], row) + _, err := buf.WriteRows(buf.rowbuf) + return err +} + +// WriteRows writes parquet rows to the buffer. +func (buf *Buffer) WriteRows(rows []Row) (int, error) { + defer func() { + for i, colbuf := range buf.colbuf { + clearValues(colbuf) + buf.colbuf[i] = colbuf[:0] + } + }() + + if buf.schema == nil { + return 0, ErrRowGroupSchemaMissing + } + + for _, row := range rows { + for _, value := range row { + columnIndex := value.Column() + buf.colbuf[columnIndex] = append(buf.colbuf[columnIndex], value) + } + } + + for columnIndex, values := range buf.colbuf { + if _, err := buf.columns[columnIndex].WriteValues(values); err != nil { + // TODO: an error at this stage will leave the buffer in an invalid + // state since the row was partially written. Applications are not + // expected to continue using the buffer after getting an error, + // maybe we can enforce it? + return 0, err + } + } + + return len(rows), nil +} + +// WriteRowGroup satisfies the RowGroupWriter interface. +func (buf *Buffer) WriteRowGroup(rowGroup RowGroup) (int64, error) { + rowGroupSchema := rowGroup.Schema() + switch { + case rowGroupSchema == nil: + return 0, ErrRowGroupSchemaMissing + case buf.schema == nil: + buf.configure(rowGroupSchema) + case !EqualNodes(buf.schema, rowGroupSchema): + return 0, ErrRowGroupSchemaMismatch + } + if !sortingColumnsHavePrefix(rowGroup.SortingColumns(), buf.SortingColumns()) { + return 0, ErrRowGroupSortingColumnsMismatch + } + n := buf.NumRows() + r := rowGroup.Rows() + defer r.Close() + _, err := CopyRows(bufferWriter{buf}, r) + return buf.NumRows() - n, err +} + +// Rows returns a reader exposing the current content of the buffer. +// +// The buffer and the returned reader share memory. Mutating the buffer +// concurrently to reading rows may result in non-deterministic behavior. +func (buf *Buffer) Rows() Rows { return NewRowGroupRowReader(buf) } + +// bufferWriter is an adapter for Buffer which implements both RowWriter and +// PageWriter to enable optimizations in CopyRows for types that support writing +// rows by copying whole pages instead of calling WriteRow repeatedly. +type bufferWriter struct{ buf *Buffer } + +func (w bufferWriter) WriteRows(rows []Row) (int, error) { + return w.buf.WriteRows(rows) +} + +func (w bufferWriter) WriteValues(values []Value) (int, error) { + return w.buf.columns[values[0].Column()].WriteValues(values) +} + +func (w bufferWriter) WritePage(page Page) (int64, error) { + return CopyValues(w.buf.columns[page.Column()], page.Values()) +} + +var ( + _ RowGroup = (*Buffer)(nil) + _ RowGroupWriter = (*Buffer)(nil) + _ sort.Interface = (*Buffer)(nil) + + _ RowWriter = (*bufferWriter)(nil) + _ PageWriter = (*bufferWriter)(nil) + _ ValueWriter = (*bufferWriter)(nil) +) + +type buffer[T memory.Datum] struct { + data memory.SliceBuffer[T] + refc atomic.Int32 + pool *bufferPool[T] + stack []byte + id uint64 +} + +func newBuffer[T memory.Datum](data []T) *buffer[T] { + b := &buffer[T]{data: memory.SliceBufferFrom(data)} + b.refc.Store(1) + return b +} + +func (b *buffer[T]) reset() { + b.data.Resize(0) +} + +func (b *buffer[T]) ref() { + if b.refc.Add(1) <= 1 { + panic("BUG: buffer reference count overflow") + } +} + +func (b *buffer[T]) unref() { + switch refc := b.refc.Add(-1); { + case refc < 0: + panic("BUG: buffer reference count underflow") + case refc == 0: + b.data.Reset() + if b.pool != nil { + b.pool.put(b) + } + } +} + +func monitorBufferRelease[T memory.Datum](b *buffer[T]) { + if rc := b.refc.Load(); rc != 0 { + log.Printf("PARQUETGODEBUG: buffer[%d] garbage collected with non-zero reference count (rc=%d)\n%s", b.id, rc, string(b.stack)) + } +} + +type bufferPool[T memory.Datum] struct { + pool memory.Pool[buffer[T]] +} + +var bufferIDCounter atomic.Uint64 + +func (p *bufferPool[T]) get(size int) *buffer[T] { + b := p.pool.Get( + func() *buffer[T] { + b := &buffer[T]{pool: p} + if debug.TRACEBUF > 0 { + b.stack = make([]byte, 4096) + runtime.SetFinalizer(b, monitorBufferRelease[T]) + } + return b + }, + func(*buffer[T]) {}, + ) + + if debug.TRACEBUF > 0 { + b.id = bufferIDCounter.Add(1) + b.stack = b.stack[:runtime.Stack(b.stack[:cap(b.stack)], false)] + } + + b.data.Resize(size) + b.refc.Store(1) + return b +} + +func (p *bufferPool[T]) put(b *buffer[T]) { + if b.pool != p { + panic("BUG: buffer returned to a different pool than the one it was allocated from") + } + if b.refc.Load() != 0 { + panic("BUG: buffer returned to pool with a non-zero reference count") + } + p.pool.Put(b) +} + +var ( + buffers bufferPool[byte] + indexes bufferPool[int32] + offsets bufferPool[uint32] +) + +type bufferedPage struct { + Page + offsets *buffer[uint32] + values *buffer[byte] + repetitionLevels *buffer[byte] + definitionLevels *buffer[byte] +} + +func newBufferedPage(page Page, offsets *buffer[uint32], values *buffer[byte], definitionLevels, repetitionLevels *buffer[byte]) *bufferedPage { + p := &bufferedPage{ + Page: page, + offsets: offsets, + values: values, + definitionLevels: definitionLevels, + repetitionLevels: repetitionLevels, + } + bufferRef(offsets) + bufferRef(values) + bufferRef(definitionLevels) + bufferRef(repetitionLevels) + return p +} + +func (p *bufferedPage) Slice(i, j int64) Page { + return newBufferedPage( + p.Page.Slice(i, j), + p.offsets, + p.values, + p.definitionLevels, + p.repetitionLevels, + ) +} + +func (p *bufferedPage) Retain() { + Retain(p.Page) + bufferRef(p.offsets) + bufferRef(p.values) + bufferRef(p.definitionLevels) + bufferRef(p.repetitionLevels) +} + +func (p *bufferedPage) Release() { + Release(p.Page) + bufferUnref(p.offsets) + bufferUnref(p.values) + bufferUnref(p.definitionLevels) + bufferUnref(p.repetitionLevels) +} + +// ReleaseAndDetachValues releases all underlying buffers except the one backing byte-array contents. This +// allows row and values read from the buffer to continue to be valid, instead relying +// on the garbage collector after it is no longer needed. +func (p *bufferedPage) ReleaseAndDetachValues() { + // We don't return the values buffer to the pool and allow + // standard GC to track it. Remove debug finalizer. + if debug.TRACEBUF > 0 { + runtime.SetFinalizer(p.values, nil) + } + + // Return everything else back to pools. + Release(p.Page) + bufferUnref(p.offsets) + bufferUnref(p.definitionLevels) + bufferUnref(p.repetitionLevels) +} + +func bufferRef[T memory.Datum](buf *buffer[T]) { + if buf != nil { + buf.ref() + } +} + +func bufferUnref[T memory.Datum](buf *buffer[T]) { + if buf != nil { + buf.unref() + } +} + +// Retain is a helper function to increment the reference counter of pages +// backed by memory which can be granularly managed by the application. +// +// Usage of this function is optional and with Release, is intended to allow +// finer grain memory management in the application. Most programs should be +// able to rely on automated memory management provided by the Go garbage +// collector instead. +// +// The function should be called when a page lifetime is about to be shared +// between multiple goroutines or layers of an application, and the program +// wants to express "sharing ownership" of the page. +// +// Calling this function on pages that do not embed a reference counter does +// nothing. +func Retain(page Page) { + if p, _ := page.(retainable); p != nil { + p.Retain() + } +} + +// Release is a helper function to decrement the reference counter of pages +// backed by memory which can be granularly managed by the application. +// +// Usage of this is optional and with Retain, is intended to allow finer grained +// memory management in the application, at the expense of potentially causing +// panics if the page is used after its reference count has reached zero. Most +// programs should be able to rely on automated memory management provided by +// the Go garbage collector instead. +// +// The function should be called to return a page to the internal buffer pool, +// when a goroutine "releases ownership" it acquired either by being the single +// owner (e.g. capturing the return value from a ReadPage call) or having gotten +// shared ownership by calling Retain. +// +// Calling this function on pages that do not embed a reference counter does +// nothing. +func Release(page Page) { + if p, _ := page.(releasable); p != nil { + p.Release() + } +} + +// releaseAndDetachValues is an optional granular memory management method like Release, +// that releases ownership of the page and potentially allows its underlying buffers +// to be reused for new pages acquired from ReadPage. However this method makes the +// additional guarantee that string and byte array values read from the page will +// continue to be valid past the page lifetime. Page-specific implementations do this +// by reusing what buffers they can, while not invaliding the string and byte array values. +// Those are relinquished to the garbage collector and cleaned up when no longer referenced +// by the calling application. +// +// Usage of this is optional and follows the guidelines as Release. +// +// Calling this function on pages that do not embed a reference counter does nothing. +func releaseAndDetachValues(page Page) { + if p, _ := page.(detachable); p != nil { + p.ReleaseAndDetachValues() + } +} + +type retainable interface { + Retain() +} + +type releasable interface { + Release() +} + +type detachable interface { + ReleaseAndDetachValues() +} + +var ( + _ retainable = (*bufferedPage)(nil) + _ releasable = (*bufferedPage)(nil) + _ detachable = (*bufferedPage)(nil) +) diff --git a/vendor/github.com/parquet-go/parquet-go/buffer_pool.go b/vendor/github.com/parquet-go/parquet-go/buffer_pool.go new file mode 100644 index 000000000..d2260a652 --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/buffer_pool.go @@ -0,0 +1,146 @@ +package parquet + +import ( + "io" + "os" + "path/filepath" + + "github.com/parquet-go/parquet-go/internal/memory" +) + +// BufferPool is an interface abstracting the underlying implementation of +// page buffer pools. +// +// The parquet-go package provides two implementations of this interface, one +// backed by in-memory buffers (on the Go heap), and the other using temporary +// files on disk. +// +// Applications which need finer grain control over the allocation and retention +// of page buffers may choose to provide their own implementation and install it +// via the parquet.ColumnPageBuffers writer option. +// +// BufferPool implementations must be safe to use concurrently from multiple +// goroutines. +type BufferPool interface { + // GetBuffer is called when a parquet writer needs to acquire a new + // page buffer from the pool. + GetBuffer() io.ReadWriteSeeker + + // PutBuffer is called when a parquet writer releases a page buffer to + // the pool. + // + // The parquet.Writer type guarantees that the buffers it calls this method + // with were previously acquired by a call to GetBuffer on the same + // pool, and that it will not use them anymore after the call. + PutBuffer(io.ReadWriteSeeker) +} + +const defaultChunkSize = 32 * 1024 // 32 KiB + +// NewBufferPool creates a new in-memory page buffer pool. +// +// The implementation is backed by sync.Pool and allocates memory buffers on the +// Go heap. +func NewBufferPool() BufferPool { return NewChunkBufferPool(defaultChunkSize) } + +// NewChunkBufferPool creates a new in-memory page buffer pool. +// +// The implementation is backed by sync.Pool and allocates memory buffers on the +// Go heap in fixed-size chunks. +func NewChunkBufferPool(chunkSize int) BufferPool { + return &memoryBufferPool{size: chunkSize} +} + +type memoryBufferPool struct { + pool memory.Pool[memory.Buffer] + size int +} + +func (m *memoryBufferPool) GetBuffer() io.ReadWriteSeeker { + return m.pool.Get( + func() *memory.Buffer { return memory.NewBuffer(m.size) }, + (*memory.Buffer).Reset, + ) +} + +func (m *memoryBufferPool) PutBuffer(buf io.ReadWriteSeeker) { + if b, _ := buf.(*memory.Buffer); b != nil { + b.Reset() // release internal buffers + m.pool.Put(b) + } +} + +type fileBufferPool struct { + err error + tempdir string + pattern string +} + +// NewFileBufferPool creates a new on-disk page buffer pool. +func NewFileBufferPool(tempdir, pattern string) BufferPool { + pool := &fileBufferPool{ + tempdir: tempdir, + pattern: pattern, + } + pool.tempdir, pool.err = filepath.Abs(pool.tempdir) + return pool +} + +func (pool *fileBufferPool) GetBuffer() io.ReadWriteSeeker { + if pool.err != nil { + return &errorBuffer{err: pool.err} + } + f, err := os.CreateTemp(pool.tempdir, pool.pattern) + if err != nil { + return &errorBuffer{err: err} + } + return f +} + +func (pool *fileBufferPool) PutBuffer(buf io.ReadWriteSeeker) { + if f, _ := buf.(*os.File); f != nil { + _ = f.Close() + _ = os.Remove(f.Name()) + } +} + +type errorBuffer struct{ err error } + +func (buf *errorBuffer) Read([]byte) (int, error) { return 0, buf.err } +func (buf *errorBuffer) Write([]byte) (int, error) { return 0, buf.err } +func (buf *errorBuffer) ReadFrom(io.Reader) (int64, error) { return 0, buf.err } +func (buf *errorBuffer) WriteTo(io.Writer) (int64, error) { return 0, buf.err } +func (buf *errorBuffer) Seek(int64, int) (int64, error) { return 0, buf.err } + +var ( + defaultColumnBufferPool = memoryBufferPool{size: defaultChunkSize} + defaultSortingBufferPool = memoryBufferPool{size: defaultChunkSize} + + _ io.ReaderFrom = (*errorBuffer)(nil) + _ io.WriterTo = (*errorBuffer)(nil) +) + +type readerAt struct { + reader io.ReadSeeker + offset int64 +} + +func (r *readerAt) ReadAt(b []byte, off int64) (int, error) { + if r.offset < 0 || off != r.offset { + off, err := r.reader.Seek(off, io.SeekStart) + if err != nil { + return 0, err + } + r.offset = off + } + n, err := r.reader.Read(b) + r.offset += int64(n) + return n, err +} + +func newReaderAt(r io.ReadSeeker) io.ReaderAt { + if rr, ok := r.(io.ReaderAt); ok { + return rr + } + return &readerAt{reader: r, offset: -1} +} diff --git a/vendor/github.com/parquet-go/parquet-go/column.go b/vendor/github.com/parquet-go/parquet-go/column.go new file mode 100644 index 000000000..e3ae1714f --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/column.go @@ -0,0 +1,871 @@ +package parquet + +import ( + "encoding/binary" + "fmt" + "io" + "math" + "reflect" + + "github.com/parquet-go/parquet-go/compress" + "github.com/parquet-go/parquet-go/deprecated" + "github.com/parquet-go/parquet-go/encoding" + "github.com/parquet-go/parquet-go/format" + "github.com/parquet-go/parquet-go/internal/memory" +) + +// Column represents a column in a parquet file. +// +// Methods of Column values are safe to call concurrently from multiple +// goroutines. +// +// Column instances satisfy the Node interface. +type Column struct { + typ Type + file *File + schema *format.SchemaElement + order *format.ColumnOrder + path columnPath + fields []Field + columns []*Column + chunks []*format.ColumnChunk + columnIndex []*format.ColumnIndex + offsetIndex []*format.OffsetIndex + encoding encoding.Encoding + compression compress.Codec + + depth byte + maxRepetitionLevel byte + maxDefinitionLevel byte + index uint16 +} + +// Type returns the type of the column. +// +// The returned value is unspecified if c is not a leaf column. +func (c *Column) Type() Type { return c.typ } + +// Optional returns true if the column is optional. +func (c *Column) Optional() bool { return schemaRepetitionTypeOf(c.schema) == format.Optional } + +// Repeated returns true if the column may repeat. +func (c *Column) Repeated() bool { return schemaRepetitionTypeOf(c.schema) == format.Repeated } + +// Required returns true if the column is required. +func (c *Column) Required() bool { return schemaRepetitionTypeOf(c.schema) == format.Required } + +// Leaf returns true if c is a leaf column. +func (c *Column) Leaf() bool { return isLeafSchemaElement(c.schema) } + +// Fields returns the list of fields on the column. +func (c *Column) Fields() []Field { return c.fields } + +// Encoding returns the encodings used by this column. +func (c *Column) Encoding() encoding.Encoding { return c.encoding } + +// Compression returns the compression codecs used by this column. +func (c *Column) Compression() compress.Codec { return c.compression } + +// Path of the column in the parquet schema. +func (c *Column) Path() []string { return c.path[1:] } + +// Name returns the column name. +func (c *Column) Name() string { return c.schema.Name } + +// ID returns column field id +func (c *Column) ID() int { return int(c.schema.FieldID) } + +// Columns returns the list of child columns. +// +// The method returns the same slice across multiple calls, the program must +// treat it as a read-only value. +func (c *Column) Columns() []*Column { return c.columns } + +// Column returns the child column matching the given name. +func (c *Column) Column(name string) *Column { + for _, child := range c.columns { + if child.Name() == name { + return child + } + } + return nil +} + +// Pages returns a reader exposing all pages in this column, across row groups. +func (c *Column) Pages() Pages { + if c.file == nil { + return emptyPages{} + } + return c.PagesFrom(c.file.reader) +} + +func (c *Column) PagesFrom(reader io.ReaderAt) Pages { + if c.index == math.MaxUint16 || c.file == nil { + return emptyPages{} + } + r := &columnPages{ + pages: make([]FilePages, len(c.file.rowGroups)), + } + for i := range r.pages { + r.pages[i].init(c.file.rowGroups[i].(*FileRowGroup).columns[c.index].(*FileColumnChunk), reader) + } + return r +} + +type columnPages struct { + pages []FilePages + index int +} + +func (c *columnPages) ReadPage() (Page, error) { + for { + if c.index >= len(c.pages) { + return nil, io.EOF + } + p, err := c.pages[c.index].ReadPage() + if err == nil || err != io.EOF { + return p, err + } + c.index++ + } +} + +func (c *columnPages) SeekToRow(rowIndex int64) error { + c.index = 0 + + for c.index < len(c.pages) && c.pages[c.index].chunk.rowGroup.NumRows < rowIndex { + rowIndex -= c.pages[c.index].chunk.rowGroup.NumRows + c.index++ + } + + if c.index < len(c.pages) { + if err := c.pages[c.index].SeekToRow(rowIndex); err != nil { + return err + } + for i := c.index + 1; i < len(c.pages); i++ { + p := &c.pages[i] + if err := p.SeekToRow(0); err != nil { + return err + } + } + } + return nil +} + +func (c *columnPages) Close() error { + var lastErr error + + for i := range c.pages { + if err := c.pages[i].Close(); err != nil { + lastErr = err + } + } + + c.pages = nil + c.index = 0 + return lastErr +} + +// Depth returns the position of the column relative to the root. +func (c *Column) Depth() int { return int(c.depth) } + +// MaxRepetitionLevel returns the maximum value of repetition levels on this +// column. +func (c *Column) MaxRepetitionLevel() int { return int(c.maxRepetitionLevel) } + +// MaxDefinitionLevel returns the maximum value of definition levels on this +// column. +func (c *Column) MaxDefinitionLevel() int { return int(c.maxDefinitionLevel) } + +// Index returns the position of the column in a row. Only leaf columns have a +// column index, the method returns -1 when called on non-leaf columns. +func (c *Column) Index() int { + if c.index == math.MaxUint16 { + return -1 + } + return int(c.index) +} + +// GoType returns the Go type that best represents the parquet column. +func (c *Column) GoType() reflect.Type { return goTypeOf(c) } + +// Value returns the sub-value in base for the child column at the given +// index. +func (c *Column) Value(base reflect.Value) reflect.Value { + return base.MapIndex(reflect.ValueOf(&c.schema.Name).Elem()) +} + +// String returns a human-readable string representation of the column. +func (c *Column) String() string { return c.path.String() + ": " + sprint(c.Name(), c) } + +func (c *Column) forEachLeaf(do func(*Column)) { + if isLeafSchemaElement(c.schema) { + do(c) + } else { + for _, child := range c.columns { + child.forEachLeaf(do) + } + } +} + +func openColumns(file *File, metadata *format.FileMetaData, columnIndexes []format.ColumnIndex, offsetIndexes []format.OffsetIndex) (*Column, error) { + cl := columnLoader{} + + c, err := cl.open(file, metadata, columnIndexes, offsetIndexes, nil) + if err != nil { + return nil, err + } + + // Validate that there aren't extra entries in the row group columns, + // which would otherwise indicate that there are dangling data pages + // in the file. + for index, rowGroup := range metadata.RowGroups { + if cl.rowGroupColumnIndex != len(rowGroup.Columns) { + return nil, fmt.Errorf("row group at index %d contains %d columns but %d were referenced by the column schemas", + index, len(rowGroup.Columns), cl.rowGroupColumnIndex) + } + } + + _, err = c.setLevels(0, 0, 0, 0) + return c, err +} + +func (c *Column) setLevels(depth, repetition, definition, index int) (int, error) { + if depth > MaxColumnDepth { + return -1, fmt.Errorf("cannot represent parquet columns with more than %d nested levels: %s", MaxColumnDepth, c.path) + } + if index > MaxColumnIndex { + return -1, fmt.Errorf("cannot represent parquet rows with more than %d columns: %s", MaxColumnIndex, c.path) + } + if repetition > MaxRepetitionLevel { + return -1, fmt.Errorf("cannot represent parquet columns with more than %d repetition levels: %s", MaxRepetitionLevel, c.path) + } + if definition > MaxDefinitionLevel { + return -1, fmt.Errorf("cannot represent parquet columns with more than %d definition levels: %s", MaxDefinitionLevel, c.path) + } + + // Only non-root columns have a well defined repetition_type + if depth > 0 { + switch schemaRepetitionTypeOf(c.schema) { + case format.Optional: + definition++ + case format.Repeated: + repetition++ + definition++ + } + } + + c.depth = byte(depth) + c.maxRepetitionLevel = byte(repetition) + c.maxDefinitionLevel = byte(definition) + depth++ + + // Only leaf columns get a column index. + if isLeafSchemaElement(c.schema) { + c.index = uint16(index) + index++ + } else { + // Groups (including empty groups) don't get a column index + c.index = math.MaxUint16 + } + + var err error + for _, child := range c.columns { + if index, err = child.setLevels(depth, repetition, definition, index); err != nil { + return -1, err + } + } + return index, nil +} + +type columnLoader struct { + schemaIndex int + columnOrderIndex int + rowGroupColumnIndex int +} + +func (cl *columnLoader) open(file *File, metadata *format.FileMetaData, columnIndexes []format.ColumnIndex, offsetIndexes []format.OffsetIndex, path []string) (*Column, error) { + c := &Column{ + file: file, + schema: &metadata.Schema[cl.schemaIndex], + } + c.path = columnPath(path).append(c.schema.Name) + + cl.schemaIndex++ + numChildren := 0 + if c.schema.NumChildren.Valid { + numChildren = int(c.schema.NumChildren.V) + } + + if isLeafSchemaElement(c.schema) { + c.typ = schemaElementTypeOf(c.schema) + + if cl.columnOrderIndex < len(metadata.ColumnOrders) { + c.order = &metadata.ColumnOrders[cl.columnOrderIndex] + cl.columnOrderIndex++ + } + + rowGroups := metadata.RowGroups + rowGroupColumnIndex := cl.rowGroupColumnIndex + cl.rowGroupColumnIndex++ + + c.chunks = make([]*format.ColumnChunk, 0, len(rowGroups)) + c.columnIndex = make([]*format.ColumnIndex, 0, len(rowGroups)) + c.offsetIndex = make([]*format.OffsetIndex, 0, len(rowGroups)) + + for i, rowGroup := range rowGroups { + if rowGroupColumnIndex >= len(rowGroup.Columns) { + return nil, fmt.Errorf("row group at index %d does not have enough columns", i) + } + c.chunks = append(c.chunks, &rowGroup.Columns[rowGroupColumnIndex]) + } + + if len(columnIndexes) > 0 { + for i := range rowGroups { + if rowGroupColumnIndex >= len(columnIndexes) { + return nil, fmt.Errorf("row group at index %d does not have enough column index pages", i) + } + c.columnIndex = append(c.columnIndex, &columnIndexes[rowGroupColumnIndex]) + } + } + + if len(offsetIndexes) > 0 { + for i := range rowGroups { + if rowGroupColumnIndex >= len(offsetIndexes) { + return nil, fmt.Errorf("row group at index %d does not have enough offset index pages", i) + } + c.offsetIndex = append(c.offsetIndex, &offsetIndexes[rowGroupColumnIndex]) + } + } + + if len(c.chunks) > 0 { + // Pick the encoding and compression codec of the first chunk. + // + // Technically each column chunk may use a different compression + // codec, and each page of the column chunk might have a different + // encoding. Exposing these details does not provide a lot of value + // to the end user. + // + // Programs that wish to determine the encoding and compression of + // each page of the column should iterate through the pages and read + // the page headers to determine which compression and encodings are + // applied. + for _, encoding := range c.chunks[0].MetaData.Encoding { + if c.encoding == nil { + c.encoding = LookupEncoding(encoding) + } + if encoding != format.Plain && encoding != format.RLE { + c.encoding = LookupEncoding(encoding) + break + } + } + c.compression = LookupCompressionCodec(c.chunks[0].MetaData.Codec) + } + + return c, nil + } + + c.typ = &groupType{} + if lt := c.schema.LogicalType; lt.Valid && lt.V.Map != nil { + c.typ = &mapType{} + } else if lt.Valid && lt.V.List != nil { + c.typ = &listType{} + } else if lt.Valid && lt.V.Variant != nil { + c.typ = &variantType{} + } else if ct := c.schema.ConvertedType; ct.Valid { + switch ct.V { + case deprecated.Map: + c.typ = &mapType{} + case deprecated.List: + c.typ = &listType{} + } + } + c.columns = make([]*Column, numChildren) + + for i := range c.columns { + if cl.schemaIndex >= len(metadata.Schema) { + return nil, fmt.Errorf("column %q has more children than there are schemas in the file: %d > %d", + c.schema.Name, cl.schemaIndex+1, len(metadata.Schema)) + } + + var err error + c.columns[i], err = cl.open(file, metadata, columnIndexes, offsetIndexes, c.path) + if err != nil { + return nil, fmt.Errorf("%s: %w", c.schema.Name, err) + } + } + + c.fields = make([]Field, len(c.columns)) + for i, column := range c.columns { + c.fields[i] = column + } + return c, nil +} + +// isLeafSchemaElement returns true if the schema element represents a leaf node +// (a column with actual data). According to the Parquet specification, the Type +// field is set for leaf nodes and not set (nil) for group nodes. +// +// This is the authoritative way to distinguish between: +// - Leaf nodes: Type != nil (has column data) +// - Group nodes: Type == nil (including empty groups with NumChildren == 0) +func isLeafSchemaElement(element *format.SchemaElement) bool { + return element.Type.Valid +} + +func schemaElementTypeOf(s *format.SchemaElement) Type { + if s.LogicalType.Valid { + lt := &s.LogicalType.V + // A logical type exists, the Type interface implementations in this + // package are all based on the logical parquet types declared in the + // format sub-package so we can return them directly via a pointer type + // conversion. + switch { + case lt.UTF8 != nil: + return (*stringType)(lt.UTF8) + case lt.Map != nil: + return (*mapType)(lt.Map) + case lt.List != nil: + return (*listType)(lt.List) + case lt.Enum != nil: + return (*enumType)(lt.Enum) + case lt.Decimal != nil: + // A parquet decimal can be one of several different physical types. + if s.Type.Valid { + var typ Type + switch kind := Kind(s.Type.V); kind { + case Int32: + typ = Int32Type + case Int64: + typ = Int64Type + case ByteArray: + typ = ByteArrayType + case FixedLenByteArray: + if !s.TypeLength.Valid { + panic("DECIMAL using FIXED_LEN_BYTE_ARRAY must specify a length") + } + typ = FixedLenByteArrayType(int(s.TypeLength.V)) + default: + panic("DECIMAL must be of type INT32, INT64, BYTE_ARRAY or FIXED_LEN_BYTE_ARRAY but got " + kind.String()) + } + return &decimalType{ + decimal: *lt.Decimal, + Type: typ, + } + } + case lt.Date != nil: + return (*dateType)(lt.Date) + case lt.Time != nil: + return (*timeType)(lt.Time) + case lt.Timestamp != nil: + return (*timestampType)(lt.Timestamp) + case lt.Integer != nil: + return (*intType)(lt.Integer) + case lt.Unknown != nil: + return (*nullType)(lt.Unknown) + case lt.Json != nil: + return (*jsonType)(lt.Json) + case lt.Bson != nil: + return (*bsonType)(lt.Bson) + case lt.UUID != nil: + return (*uuidType)(lt.UUID) + case lt.Geometry != nil: + return (*geometryType)(lt.Geometry) + case lt.Geography != nil: + return (*geographyType)(lt.Geography) + } + } + + if s.ConvertedType.Valid { + ct := s.ConvertedType.V + // This column contains no logical type but has a converted type, it + // was likely created by an older parquet writer. Convert the legacy + // type representation to the equivalent logical parquet type. + switch ct { + case deprecated.UTF8: + return &stringType{} + case deprecated.Map: + return &mapType{} + case deprecated.MapKeyValue: + return &groupType{} + case deprecated.List: + return &listType{} + case deprecated.Enum: + return &enumType{} + case deprecated.Decimal: + if s.Scale.Valid && s.Precision.Valid { + // A parquet decimal can be one of several different physical types. + if s.Type.Valid { + var typ Type + switch kind := Kind(s.Type.V); kind { + case Int32: + typ = Int32Type + case Int64: + typ = Int64Type + case FixedLenByteArray: + if !s.TypeLength.Valid { + panic("DECIMAL using FIXED_LEN_BYTE_ARRAY must specify a length") + } + typ = FixedLenByteArrayType(int(s.TypeLength.V)) + case ByteArray: + typ = ByteArrayType + default: + panic("DECIMAL must be of type INT32, INT64, BYTE_ARRAY or FIXED_LEN_BYTE_ARRAY but got " + kind.String()) + } + return &decimalType{ + decimal: format.DecimalType{ + Scale: s.Scale.V, + Precision: s.Precision.V, + }, + Type: typ, + } + } + } + case deprecated.Date: + return &dateType{} + case deprecated.TimeMillis: + return &timeType{IsAdjustedToUTC: true, Unit: Millisecond.TimeUnit()} + case deprecated.TimeMicros: + return &timeType{IsAdjustedToUTC: true, Unit: Microsecond.TimeUnit()} + case deprecated.TimestampMillis: + return ×tampType{IsAdjustedToUTC: true, Unit: Millisecond.TimeUnit()} + case deprecated.TimestampMicros: + return ×tampType{IsAdjustedToUTC: true, Unit: Microsecond.TimeUnit()} + case deprecated.Uint8: + return &unsignedIntTypes[0] + case deprecated.Uint16: + return &unsignedIntTypes[1] + case deprecated.Uint32: + return &unsignedIntTypes[2] + case deprecated.Uint64: + return &unsignedIntTypes[3] + case deprecated.Int8: + return &signedIntTypes[0] + case deprecated.Int16: + return &signedIntTypes[1] + case deprecated.Int32: + return &signedIntTypes[2] + case deprecated.Int64: + return &signedIntTypes[3] + case deprecated.Json: + return &jsonType{} + case deprecated.Bson: + return &bsonType{} + case deprecated.Interval: + return &intervalType{} + } + } + + if s.Type.Valid { + // The column only has a physical type, convert it to one of the + // primitive types supported by this package. + switch kind := Kind(s.Type.V); kind { + case Boolean: + return BooleanType + case Int32: + return Int32Type + case Int64: + return Int64Type + case Int96: + return Int96Type + case Float: + return FloatType + case Double: + return DoubleType + case ByteArray: + return ByteArrayType + case FixedLenByteArray: + if s.TypeLength.Valid { + return FixedLenByteArrayType(int(s.TypeLength.V)) + } + } + } + + // If we reach this point, we are likely reading a parquet column that was + // written with a non-standard type or is in a newer version of the format + // than this package supports. + return &nullType{} +} + +func schemaRepetitionTypeOf(s *format.SchemaElement) format.FieldRepetitionType { + if s.RepetitionType.Valid { + return s.RepetitionType.V + } + return format.Required +} + +func (c *Column) decompress(compressedPageData []byte, uncompressedPageSize int32) (page *buffer[byte], err error) { + page = buffers.get(int(uncompressedPageSize)) + decoded, err := c.compression.Decode(page.data.Slice(), compressedPageData) + switch { + case err != nil: + page.unref() + page = nil + case len(decoded) < int(uncompressedPageSize): + page.data.Resize(len(decoded)) + case len(decoded) > int(uncompressedPageSize): + page.data = memory.SliceBufferFrom(decoded) + } + return page, err +} + +// DecodeDataPageV1 decodes a data page from the header, compressed data, and +// optional dictionary passed as arguments. +func (c *Column) DecodeDataPageV1(header DataPageHeaderV1, page []byte, dict Dictionary) (Page, error) { + return c.decodeDataPageV1(header, newBuffer(page), dict, -1) +} + +func (c *Column) decodeDataPageV1(header DataPageHeaderV1, page *buffer[byte], dict Dictionary, size int32) (Page, error) { + var ( + pageData = page.data.Slice() + err error + ) + + if isCompressed(c.compression) { + if page, err = c.decompress(pageData, size); err != nil { + return nil, fmt.Errorf("decompressing data page v1: %w", err) + } + defer page.unref() + pageData = page.data.Slice() + } + + var ( + numValues = int(header.NumValues()) + repetitionLevels *buffer[byte] + definitionLevels *buffer[byte] + ) + + if c.maxRepetitionLevel > 0 { + encoding := lookupLevelEncoding(header.RepetitionLevelEncoding(), c.maxRepetitionLevel) + repetitionLevels, pageData, err = decodeLevelsV1(encoding, numValues, pageData) + if err != nil { + return nil, fmt.Errorf("decoding repetition levels of data page v1: %w", err) + } + defer repetitionLevels.unref() + } + + if c.maxDefinitionLevel > 0 { + encoding := lookupLevelEncoding(header.DefinitionLevelEncoding(), c.maxDefinitionLevel) + definitionLevels, pageData, err = decodeLevelsV1(encoding, numValues, pageData) + if err != nil { + return nil, fmt.Errorf("decoding definition levels of data page v1: %w", err) + } + defer definitionLevels.unref() + + // Data pages v1 did not embed the number of null values, + // so we have to compute it from the definition levels. + numValues -= countLevelsNotEqual(definitionLevels.data.Slice(), c.maxDefinitionLevel) + } + + return c.decodeDataPage(header, numValues, repetitionLevels, definitionLevels, page, pageData, dict) +} + +// DecodeDataPageV2 decodes a data page from the header, compressed data, and +// optional dictionary passed as arguments. +func (c *Column) DecodeDataPageV2(header DataPageHeaderV2, page []byte, dict Dictionary) (Page, error) { + return c.decodeDataPageV2(header, newBuffer(page), dict, -1) +} + +func (c *Column) decodeDataPageV2(header DataPageHeaderV2, page *buffer[byte], dict Dictionary, size int32) (Page, error) { + numValues := int(header.NumValues()) + pageData := page.data.Slice() + var err error + + var repetitionLevels *buffer[byte] + var definitionLevels *buffer[byte] + + if length := header.RepetitionLevelsByteLength(); length > 0 { + if c.maxRepetitionLevel == 0 { + // In some cases we've observed files which have a non-zero + // repetition level despite the column not being repeated + // (nor nested within a repeated column). + // + // See https://github.com/apache/parquet-testing/pull/24 + pageData, err = skipLevelsV2(pageData, length) + } else { + encoding := lookupLevelEncoding(header.RepetitionLevelEncoding(), c.maxRepetitionLevel) + repetitionLevels, pageData, err = decodeLevelsV2(encoding, numValues, pageData, length) + } + if err != nil { + return nil, fmt.Errorf("decoding repetition levels of data page v2: %w", io.ErrUnexpectedEOF) + } + if repetitionLevels != nil { + defer repetitionLevels.unref() + + repLevels := repetitionLevels.data.Slice() + if len(repLevels) != 0 && repLevels[0] != 0 { + return nil, fmt.Errorf("%w: first repetition level for column %d (%s) is %d instead of zero, indicating that the page contains trailing values from the previous page (this is forbidden for data pages v2)", + ErrMalformedRepetitionLevel, c.Index(), c.Name(), repLevels[0]) + } + } + } + + if length := header.DefinitionLevelsByteLength(); length > 0 { + if c.maxDefinitionLevel == 0 { + pageData, err = skipLevelsV2(pageData, length) + } else { + encoding := lookupLevelEncoding(header.DefinitionLevelEncoding(), c.maxDefinitionLevel) + definitionLevels, pageData, err = decodeLevelsV2(encoding, numValues, pageData, length) + } + if err != nil { + return nil, fmt.Errorf("decoding definition levels of data page v2: %w", io.ErrUnexpectedEOF) + } + if definitionLevels != nil { + defer definitionLevels.unref() + } + } + + if isCompressed(c.compression) && header.IsCompressed() { + if page, err = c.decompress(pageData, size); err != nil { + return nil, fmt.Errorf("decompressing data page v2: %w", err) + } + defer page.unref() + pageData = page.data.Slice() + } + + numValues -= int(header.NumNulls()) + return c.decodeDataPage(header, numValues, repetitionLevels, definitionLevels, page, pageData, dict) +} + +func (c *Column) decodeDataPage(header DataPageHeader, numValues int, repetitionLevels, definitionLevels, page *buffer[byte], data []byte, dict Dictionary) (Page, error) { + pageEncoding := LookupEncoding(header.Encoding()) + pageType := c.Type() + pageKind := pageType.Kind() + + if isDictionaryEncoding(pageEncoding) { + // In some legacy configurations, the PLAIN_DICTIONARY encoding is used + // on data page headers to indicate that the page contains indexes into + // the dictionary page, but the page is still encoded using the RLE + // encoding in this case, so we convert it to RLE_DICTIONARY. + pageEncoding = &RLEDictionary + pageType = indexedPageType{newIndexedType(pageType, dict)} + } + + var obuf *buffer[uint32] + var vbuf *buffer[byte] + var pageOffsets []uint32 + var pageValues []byte + switch { + case pageEncoding.CanDecodeInPlace(): + vbuf = page + pageValues = data + default: + vbuf = buffers.get(pageType.EstimateDecodeSize(numValues, data, pageEncoding)) + defer vbuf.unref() + pageValues = vbuf.data.Slice() + } + + // Page offsets not needed when dictionary-encoded + if pageKind == ByteArray && !isDictionaryEncoding(pageEncoding) { + obuf = offsets.get(numValues + 1) + defer obuf.unref() + pageOffsets = obuf.data.Slice() + } + + values := pageType.NewValues(pageValues, pageOffsets) + values, err := pageType.Decode(values, data, pageEncoding) + if err != nil { + return nil, err + } + + newPage := pageType.NewPage(c.Index(), numValues, values) + switch { + case c.maxRepetitionLevel > 0: + newPage = newRepeatedPage( + newPage, + c.maxRepetitionLevel, + c.maxDefinitionLevel, + repetitionLevels.data.Slice(), + definitionLevels.data.Slice(), + ) + case c.maxDefinitionLevel > 0: + newPage = newOptionalPage( + newPage, + c.maxDefinitionLevel, + definitionLevels.data.Slice(), + ) + } + + return newBufferedPage(newPage, obuf, vbuf, definitionLevels, repetitionLevels), nil +} + +func decodeLevelsV1(enc encoding.Encoding, numValues int, data []byte) (*buffer[byte], []byte, error) { + if len(data) < 4 { + return nil, data, io.ErrUnexpectedEOF + } + i := 4 + j := 4 + int(binary.LittleEndian.Uint32(data)) + if j > len(data) { + return nil, data, io.ErrUnexpectedEOF + } + levels, err := decodeLevels(enc, numValues, data[i:j]) + return levels, data[j:], err +} + +func decodeLevelsV2(enc encoding.Encoding, numValues int, data []byte, length int64) (*buffer[byte], []byte, error) { + levels, err := decodeLevels(enc, numValues, data[:length]) + return levels, data[length:], err +} + +func decodeLevels(enc encoding.Encoding, numValues int, data []byte) (levels *buffer[byte], err error) { + levels = buffers.get(numValues) + decoded, err := enc.DecodeLevels(levels.data.Slice(), data) + if err != nil { + levels.unref() + levels = nil + } else { + levels.data.Resize(0) + levels.data.Append(decoded...) + switch { + case levels.data.Len() < numValues: + err = fmt.Errorf("decoding level expected %d values but got only %d", numValues, levels.data.Len()) + case levels.data.Len() > numValues: + levels.data.Resize(numValues) + } + } + return levels, err +} + +func skipLevelsV2(data []byte, length int64) ([]byte, error) { + if length >= int64(len(data)) { + return data, io.ErrUnexpectedEOF + } + return data[length:], nil +} + +// DecodeDictionary decodes a data page from the header and compressed data +// passed as arguments. +func (c *Column) DecodeDictionary(header DictionaryPageHeader, page []byte) (Dictionary, error) { + return c.decodeDictionary(header, newBuffer(page), -1) +} + +func (c *Column) decodeDictionary(header DictionaryPageHeader, page *buffer[byte], size int32) (Dictionary, error) { + pageData := page.data.Slice() + + if isCompressed(c.compression) { + var err error + if page, err = c.decompress(pageData, size); err != nil { + return nil, fmt.Errorf("decompressing dictionary page: %w", err) + } + defer page.unref() + pageData = page.data.Slice() + } + + pageType := c.Type() + pageEncoding := header.Encoding() + if pageEncoding == format.PlainDictionary { + pageEncoding = format.Plain + } + + // Dictionaries always have PLAIN encoding, so we need to allocate offsets for the decoded page. + numValues := int(header.NumValues()) + dictBufferSize := pageType.EstimateDecodeSize(numValues, pageData, LookupEncoding(pageEncoding)) + values := pageType.NewValues(make([]byte, 0, dictBufferSize), make([]uint32, 0, numValues)) + values, err := pageType.Decode(values, pageData, LookupEncoding(pageEncoding)) + if err != nil { + return nil, err + } + return pageType.NewDictionary(int(c.index), numValues, values), nil +} + +var _ Node = (*Column)(nil) diff --git a/vendor/github.com/parquet-go/parquet-go/column_buffer.go b/vendor/github.com/parquet-go/parquet-go/column_buffer.go new file mode 100644 index 000000000..fab82866e --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/column_buffer.go @@ -0,0 +1,151 @@ +package parquet + +import ( + "github.com/parquet-go/parquet-go/deprecated" + "github.com/parquet-go/parquet-go/sparse" +) + +// ColumnBuffer is an interface representing columns of a row group. +// +// ColumnBuffer implements sort.Interface as a way to support reordering the +// rows that have been written to it. +// +// The current implementation has a limitation which prevents applications from +// providing custom versions of this interface because it contains unexported +// methods. The only way to create ColumnBuffer values is to call the +// NewColumnBuffer of Type instances. This limitation may be lifted in future +// releases. +type ColumnBuffer interface { + // Exposes a read-only view of the column buffer. + ColumnChunk + + // The column implements ValueReaderAt as a mechanism to read values at + // specific locations within the buffer. + ValueReaderAt + + // The column implements ValueWriter as a mechanism to optimize the copy + // of values into the buffer in contexts where the row information is + // provided by the values because the repetition and definition levels + // are set. + ValueWriter + + // For indexed columns, returns the underlying dictionary holding the column + // values. If the column is not indexed, nil is returned. + Dictionary() Dictionary + + // Returns a copy of the column. The returned copy shares no memory with + // the original, mutations of either column will not modify the other. + Clone() ColumnBuffer + + // Returns the column as a Page. + Page() Page + + // Clears all rows written to the column. + Reset() + + // Returns the current capacity of the column (rows). + Cap() int + + // Returns the number of rows currently written to the column. + Len() int + + // Compares rows at index i and j and reports whether i < j. + Less(i, j int) bool + + // Swaps rows at index i and j. + Swap(i, j int) + + // Returns the size of the column buffer in bytes. + Size() int64 + + // This method is employed to write rows from arrays of Go values into the + // column buffer. The method is currently unexported because it uses unsafe + // APIs which would be difficult for applications to leverage, increasing + // the risk of introducing bugs in the code. As a consequence, applications + // cannot use custom implementations of the ColumnBuffer interface since + // they cannot declare an unexported method that would match this signature. + // It means that in order to create a ColumnBuffer value, programs need to + // go through a call to NewColumnBuffer on a Type instance. We make this + // trade off for now as it is preferrable to optimize for safety over + // extensibility in the public APIs, we might revisit in the future if we + // learn about valid use cases for custom column buffer types. + writeValues(levels columnLevels, rows sparse.Array) + + // Parquet primitive type write methods. Each column buffer implementation + // supports only the Parquet types it can handle and panics for others. + // These methods are unexported for the same reasons as writeValues above. + writeBoolean(levels columnLevels, value bool) + writeInt32(levels columnLevels, value int32) + writeInt64(levels columnLevels, value int64) + writeInt96(levels columnLevels, value deprecated.Int96) + writeFloat(levels columnLevels, value float32) + writeDouble(levels columnLevels, value float64) + writeByteArray(levels columnLevels, value []byte) + writeNull(levels columnLevels) +} + +func columnIndexOfNullable(base ColumnBuffer, maxDefinitionLevel byte, definitionLevels []byte) (ColumnIndex, error) { + index, err := base.ColumnIndex() + if err != nil { + return nil, err + } + return &nullableColumnIndex{ + ColumnIndex: index, + maxDefinitionLevel: maxDefinitionLevel, + definitionLevels: definitionLevels, + }, nil +} + +type nullableColumnIndex struct { + ColumnIndex + maxDefinitionLevel byte + definitionLevels []byte +} + +func (index *nullableColumnIndex) NullPage(i int) bool { + return index.NullCount(i) == int64(len(index.definitionLevels)) +} + +func (index *nullableColumnIndex) NullCount(i int) int64 { + return int64(countLevelsNotEqual(index.definitionLevels, index.maxDefinitionLevel)) +} + +type nullOrdering func(column ColumnBuffer, i, j int, maxDefinitionLevel, definitionLevel1, definitionLevel2 byte) bool + +func nullsGoFirst(column ColumnBuffer, i, j int, maxDefinitionLevel, definitionLevel1, definitionLevel2 byte) bool { + if definitionLevel1 != maxDefinitionLevel { + return definitionLevel2 == maxDefinitionLevel + } else { + return definitionLevel2 == maxDefinitionLevel && column.Less(i, j) + } +} + +func nullsGoLast(column ColumnBuffer, i, j int, maxDefinitionLevel, definitionLevel1, definitionLevel2 byte) bool { + return definitionLevel1 == maxDefinitionLevel && (definitionLevel2 != maxDefinitionLevel || column.Less(i, j)) +} + +// reversedColumnBuffer is an adapter of ColumnBuffer which inverses the order +// in which rows are ordered when the column gets sorted. +// +// This type is used when buffers are constructed with sorting columns ordering +// values in descending order. +type reversedColumnBuffer struct{ ColumnBuffer } + +func (col *reversedColumnBuffer) Less(i, j int) bool { return col.ColumnBuffer.Less(j, i) } + +var ( + _ ColumnBuffer = (*optionalColumnBuffer)(nil) + _ ColumnBuffer = (*repeatedColumnBuffer)(nil) + _ ColumnBuffer = (*nullColumnBuffer)(nil) + _ ColumnBuffer = (*booleanColumnBuffer)(nil) + _ ColumnBuffer = (*int32ColumnBuffer)(nil) + _ ColumnBuffer = (*int64ColumnBuffer)(nil) + _ ColumnBuffer = (*int96ColumnBuffer)(nil) + _ ColumnBuffer = (*floatColumnBuffer)(nil) + _ ColumnBuffer = (*doubleColumnBuffer)(nil) + _ ColumnBuffer = (*byteArrayColumnBuffer)(nil) + _ ColumnBuffer = (*fixedLenByteArrayColumnBuffer)(nil) + _ ColumnBuffer = (*uint32ColumnBuffer)(nil) + _ ColumnBuffer = (*uint64ColumnBuffer)(nil) + _ ColumnBuffer = (*be128ColumnBuffer)(nil) +) diff --git a/vendor/github.com/parquet-go/parquet-go/column_buffer_amd64.go b/vendor/github.com/parquet-go/parquet-go/column_buffer_amd64.go new file mode 100644 index 000000000..8f7e91f65 --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/column_buffer_amd64.go @@ -0,0 +1,30 @@ +//go:build !purego + +package parquet + +import ( + "github.com/parquet-go/bitpack/unsafecast" + "github.com/parquet-go/parquet-go/internal/bytealg" + "github.com/parquet-go/parquet-go/sparse" + "golang.org/x/sys/cpu" +) + +func broadcastValueInt32(dst []int32, src int8) { + bytealg.Broadcast(unsafecast.Slice[byte](dst), byte(src)) +} + +//go:noescape +func broadcastRangeInt32AVX2(dst []int32, base int32) + +func broadcastRangeInt32(dst []int32, base int32) { + if len(dst) >= 8 && cpu.X86.HasAVX2 { + broadcastRangeInt32AVX2(dst, base) + } else { + for i := range dst { + dst[i] = base + int32(i) + } + } +} + +//go:noescape +func writePointersBE128(values [][16]byte, rows sparse.Array) diff --git a/vendor/github.com/parquet-go/parquet-go/column_buffer_amd64.s b/vendor/github.com/parquet-go/parquet-go/column_buffer_amd64.s new file mode 100644 index 000000000..1eef03d1d --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/column_buffer_amd64.s @@ -0,0 +1,67 @@ +//go:build !purego + +#include "textflag.h" + +// func broadcastRangeInt32AVX2(dst []int32, base int32) +TEXT ·broadcastRangeInt32AVX2(SB), NOSPLIT, $0-28 + MOVQ dst_base+0(FP), AX + MOVQ dst_len+8(FP), BX + MOVL base+24(FP), CX + XORQ SI, SI + + CMPQ BX, $8 + JB test1x4 + + VMOVDQU ·range0n8(SB), Y0 // [0,1,2,3,4,5,6,7] + VPBROADCASTD ·range0n8+32(SB), Y1 // [8,8,8,8,8,8,8,8] + VPBROADCASTD base+24(FP), Y2 // [base...] + VPADDD Y2, Y0, Y0 // [base,base+1,...] + + MOVQ BX, DI + SHRQ $3, DI + SHLQ $3, DI + JMP test8x4 +loop8x4: + VMOVDQU Y0, (AX)(SI*4) + VPADDD Y1, Y0, Y0 + ADDQ $8, SI +test8x4: + CMPQ SI, DI + JNE loop8x4 + VZEROUPPER + JMP test1x4 + +loop1x4: + INCQ SI + MOVL CX, DX + IMULL SI, DX + MOVL DX, -4(AX)(SI*4) +test1x4: + CMPQ SI, BX + JNE loop1x4 + RET + +// func writePointersBE128(values [][16]byte, rows sparse.Array) +TEXT ·writePointersBE128(SB), NOSPLIT, $0-48 + MOVQ values_base+0(FP), AX + MOVQ rows_array_ptr+24(FP), BX + MOVQ rows_array_len+32(FP), CX + MOVQ rows_array_off+40(FP), DX + + XORQ SI, SI + JMP test +loop: + PXOR X0, X0 + MOVQ (BX), DI // *[16]byte + CMPQ DI, $0 + JE next + MOVOU (DI), X0 +next: + MOVOU X0, (AX) + ADDQ $16, AX + ADDQ DX, BX + INCQ SI +test: + CMPQ SI, CX + JNE loop + RET diff --git a/vendor/github.com/parquet-go/parquet-go/column_buffer_be128.go b/vendor/github.com/parquet-go/parquet-go/column_buffer_be128.go new file mode 100644 index 000000000..39e66d32d --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/column_buffer_be128.go @@ -0,0 +1,157 @@ +package parquet + +import ( + "encoding/binary" + "fmt" + "io" + "math" + "slices" + + "github.com/parquet-go/parquet-go/deprecated" + "github.com/parquet-go/parquet-go/sparse" +) + +type be128ColumnBuffer struct{ be128Page } + +func newBE128ColumnBuffer(typ Type, columnIndex uint16, numValues int32) *be128ColumnBuffer { + return &be128ColumnBuffer{ + be128Page: be128Page{ + typ: typ, + values: make([][16]byte, 0, numValues), + columnIndex: ^columnIndex, + }, + } +} + +func (col *be128ColumnBuffer) Clone() ColumnBuffer { + return &be128ColumnBuffer{ + be128Page: be128Page{ + typ: col.typ, + values: slices.Clone(col.values), + columnIndex: col.columnIndex, + }, + } +} + +func (col *be128ColumnBuffer) ColumnIndex() (ColumnIndex, error) { + return be128ColumnIndex{&col.be128Page}, nil +} + +func (col *be128ColumnBuffer) OffsetIndex() (OffsetIndex, error) { + return be128OffsetIndex{&col.be128Page}, nil +} + +func (col *be128ColumnBuffer) BloomFilter() BloomFilter { return nil } + +func (col *be128ColumnBuffer) Dictionary() Dictionary { return nil } + +func (col *be128ColumnBuffer) Pages() Pages { return onePage(col.Page()) } + +func (col *be128ColumnBuffer) Page() Page { return &col.be128Page } + +func (col *be128ColumnBuffer) Reset() { col.values = col.values[:0] } + +func (col *be128ColumnBuffer) Cap() int { return cap(col.values) } + +func (col *be128ColumnBuffer) Len() int { return len(col.values) } + +func (col *be128ColumnBuffer) Less(i, j int) bool { + return lessBE128(&col.values[i], &col.values[j]) +} + +func (col *be128ColumnBuffer) Swap(i, j int) { + col.values[i], col.values[j] = col.values[j], col.values[i] +} + +func (col *be128ColumnBuffer) WriteValues(values []Value) (int, error) { + if n := len(col.values) + len(values); n > cap(col.values) { + col.values = append(make([][16]byte, 0, max(n, 2*cap(col.values))), col.values...) + } + n := len(col.values) + col.values = col.values[:n+len(values)] + newValues := col.values[n:] + for i, v := range values { + copy(newValues[i][:], v.byteArray()) + } + return len(values), nil +} + +func (col *be128ColumnBuffer) writeValues(_ columnLevels, rows sparse.Array) { + if n := len(col.values) + rows.Len(); n > cap(col.values) { + col.values = append(make([][16]byte, 0, max(n, 2*cap(col.values))), col.values...) + } + n := len(col.values) + col.values = col.values[:n+rows.Len()] + sparse.GatherUint128(col.values[n:], rows.Uint128Array()) +} + +func (col *be128ColumnBuffer) writeBoolean(levels columnLevels, value bool) { + var be128Value [16]byte + if value { + be128Value[15] = 1 + } + col.values = append(col.values, be128Value) +} + +func (col *be128ColumnBuffer) writeInt32(levels columnLevels, value int32) { + var be128Value [16]byte + binary.BigEndian.PutUint32(be128Value[12:16], uint32(value)) + col.values = append(col.values, be128Value) +} + +func (col *be128ColumnBuffer) writeInt64(levels columnLevels, value int64) { + var be128Value [16]byte + binary.BigEndian.PutUint64(be128Value[8:16], uint64(value)) + col.values = append(col.values, be128Value) +} + +func (col *be128ColumnBuffer) writeInt96(levels columnLevels, value deprecated.Int96) { + var be128Value [16]byte + binary.BigEndian.PutUint32(be128Value[4:8], value[2]) + binary.BigEndian.PutUint32(be128Value[8:12], value[1]) + binary.BigEndian.PutUint32(be128Value[12:16], value[0]) + col.values = append(col.values, be128Value) +} + +func (col *be128ColumnBuffer) writeFloat(levels columnLevels, value float32) { + var be128Value [16]byte + binary.BigEndian.PutUint32(be128Value[12:16], math.Float32bits(value)) + col.values = append(col.values, be128Value) +} + +func (col *be128ColumnBuffer) writeDouble(levels columnLevels, value float64) { + var be128Value [16]byte + binary.BigEndian.PutUint64(be128Value[8:16], math.Float64bits(value)) + col.values = append(col.values, be128Value) +} + +func (col *be128ColumnBuffer) writeByteArray(_ columnLevels, value []byte) { + if len(value) != 16 { + panic(fmt.Sprintf("cannot write %d bytes to [16]byte column", len(value))) + } + col.values = append(col.values, [16]byte(value)) +} + +func (col *be128ColumnBuffer) writeNull(levels columnLevels) { + col.values = append(col.values, [16]byte{}) +} + +func (col *be128ColumnBuffer) ReadValuesAt(values []Value, offset int64) (n int, err error) { + i := int(offset) + switch { + case i < 0: + return 0, errRowIndexOutOfBounds(offset, int64(len(col.values))) + case i >= len(col.values): + return 0, io.EOF + default: + for n < len(values) && i < len(col.values) { + values[n] = col.makeValue(&col.values[i]) + n++ + i++ + } + if n < len(values) { + err = io.EOF + } + return n, err + } +} diff --git a/vendor/github.com/parquet-go/parquet-go/column_buffer_boolean.go b/vendor/github.com/parquet-go/parquet-go/column_buffer_boolean.go new file mode 100644 index 000000000..5d996e6e3 --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/column_buffer_boolean.go @@ -0,0 +1,236 @@ +package parquet + +import ( + "io" + + "github.com/parquet-go/bitpack" + "github.com/parquet-go/parquet-go/deprecated" + "github.com/parquet-go/parquet-go/internal/memory" + "github.com/parquet-go/parquet-go/sparse" +) + +type booleanColumnBuffer struct{ booleanPage } + +func newBooleanColumnBuffer(typ Type, columnIndex uint16, numValues int32) *booleanColumnBuffer { + // Boolean values are bit-packed, we can fit up to 8 values per byte. + bufferSize := (numValues + 7) / 8 + return &booleanColumnBuffer{ + booleanPage: booleanPage{ + typ: typ, + bits: memory.SliceBufferFor[byte](int(bufferSize)), + columnIndex: ^columnIndex, + }, + } +} + +func (col *booleanColumnBuffer) Clone() ColumnBuffer { + return &booleanColumnBuffer{ + booleanPage: booleanPage{ + typ: col.typ, + bits: col.bits.Clone(), + offset: col.offset, + numValues: col.numValues, + columnIndex: col.columnIndex, + }, + } +} + +func (col *booleanColumnBuffer) ColumnIndex() (ColumnIndex, error) { + return booleanColumnIndex{&col.booleanPage}, nil +} + +func (col *booleanColumnBuffer) OffsetIndex() (OffsetIndex, error) { + return booleanOffsetIndex{&col.booleanPage}, nil +} + +func (col *booleanColumnBuffer) BloomFilter() BloomFilter { return nil } + +func (col *booleanColumnBuffer) Dictionary() Dictionary { return nil } + +func (col *booleanColumnBuffer) Pages() Pages { return onePage(col.Page()) } + +func (col *booleanColumnBuffer) Page() Page { return &col.booleanPage } + +func (col *booleanColumnBuffer) Reset() { + col.bits.Reset() + col.offset = 0 + col.numValues = 0 +} + +func (col *booleanColumnBuffer) Cap() int { return 8 * col.bits.Cap() } + +func (col *booleanColumnBuffer) Len() int { return int(col.numValues) } + +func (col *booleanColumnBuffer) Less(i, j int) bool { + a := col.valueAt(i) + b := col.valueAt(j) + return a != b && !a +} + +func (col *booleanColumnBuffer) valueAt(i int) bool { + bits := col.bits.Slice() + j := uint32(i) / 8 + k := uint32(i) % 8 + return ((bits[j] >> k) & 1) != 0 +} + +func (col *booleanColumnBuffer) setValueAt(i int, v bool) { + // `offset` is always zero in the page of a column buffer + bits := col.bits.Slice() + j := uint32(i) / 8 + k := uint32(i) % 8 + x := byte(0) + if v { + x = 1 + } + bits[j] = (bits[j] & ^(1 << k)) | (x << k) +} + +func (col *booleanColumnBuffer) Swap(i, j int) { + a := col.valueAt(i) + b := col.valueAt(j) + col.setValueAt(i, b) + col.setValueAt(j, a) +} + +func (col *booleanColumnBuffer) WriteBooleans(values []bool) (int, error) { + col.writeValues(columnLevels{}, sparse.MakeBoolArray(values).UnsafeArray()) + return len(values), nil +} + +func (col *booleanColumnBuffer) WriteValues(values []Value) (int, error) { + col.writeValues(columnLevels{}, makeArrayValue(values, offsetOfBool)) + return len(values), nil +} + +func (col *booleanColumnBuffer) writeValues(_ columnLevels, rows sparse.Array) { + numBytes := bitpack.ByteCount(uint(col.numValues) + uint(rows.Len())) + if col.bits.Cap() < numBytes { + col.bits.Grow(numBytes - col.bits.Len()) + } + col.bits.Resize(numBytes) + bits := col.bits.Slice() + i := 0 + r := 8 - (int(col.numValues) % 8) + bytes := rows.Uint8Array() + + if r <= bytes.Len() { + // First we attempt to write enough bits to align the number of values + // in the column buffer on 8 bytes. After this step the next bit should + // be written at the zero'th index of a byte of the buffer. + if r < 8 { + var b byte + for i < r { + v := bytes.Index(i) + b |= (v & 1) << uint(i) + i++ + } + x := uint(col.numValues) / 8 + y := uint(col.numValues) % 8 + bits[x] = (b << y) | (bits[x] & ^(0xFF << y)) + col.numValues += int32(i) + } + + if n := ((bytes.Len() - i) / 8) * 8; n > 0 { + // At this stage, we know that that we have at least 8 bits to write + // and the bits will be aligned on the address of a byte in the + // output buffer. We can work on 8 values per loop iteration, + // packing them into a single byte and writing it to the output + // buffer. This effectively reduces by 87.5% the number of memory + // stores that the program needs to perform to generate the values. + i += sparse.GatherBits(bits[col.numValues/8:], bytes.Slice(i, i+n)) + col.numValues += int32(n) + } + } + + for i < bytes.Len() { + x := uint(col.numValues) / 8 + y := uint(col.numValues) % 8 + b := bytes.Index(i) + bits[x] = ((b & 1) << y) | (bits[x] & ^(1 << y)) + col.numValues++ + i++ + } + + col.bits.Resize(bitpack.ByteCount(uint(col.numValues))) + col.clearTrailingBits() +} + +func (col *booleanColumnBuffer) writeBoolean(levels columnLevels, value bool) { + numBytes := bitpack.ByteCount(uint(col.numValues) + 1) + if col.bits.Cap() < numBytes { + col.bits.Grow(numBytes - col.bits.Len()) + } + col.bits.Resize(numBytes) + bits := col.bits.Slice() + x := uint(col.numValues) / 8 + y := uint(col.numValues) % 8 + bit := byte(0) + if value { + bit = 1 + } + bits[x] = (bit << y) | (bits[x] & ^(1 << y)) + col.numValues++ + col.clearTrailingBits() +} + +// clearTrailingBits zeros bit positions beyond numValues in the last byte of +// the bit-packed buffer. SliceBuffer growth does not zero new bytes, so a +// partial trailing byte can otherwise leak stale pool memory and produce +// non-deterministic encoded output (https://github.com/parquet-go/parquet-go/issues/520). +func (col *booleanColumnBuffer) clearTrailingBits() { + extra := uint(col.numValues) % 8 + if extra == 0 { + return + } + bits := col.bits.Slice() + bits[len(bits)-1] &= byte(1<= int(col.numValues): + return 0, io.EOF + default: + for n < len(values) && i < int(col.numValues) { + values[n] = col.makeValue(col.valueAt(i)) + n++ + i++ + } + if n < len(values) { + err = io.EOF + } + return n, err + } +} diff --git a/vendor/github.com/parquet-go/parquet-go/column_buffer_byte_array.go b/vendor/github.com/parquet-go/parquet-go/column_buffer_byte_array.go new file mode 100644 index 000000000..d678f1412 --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/column_buffer_byte_array.go @@ -0,0 +1,271 @@ +package parquet + +import ( + "bytes" + "io" + "strconv" + + "github.com/parquet-go/parquet-go/deprecated" + "github.com/parquet-go/parquet-go/encoding/plain" + "github.com/parquet-go/parquet-go/internal/memory" + "github.com/parquet-go/parquet-go/sparse" +) + +type byteArrayColumnBuffer struct { + byteArrayPage + lengths memory.SliceBuffer[uint32] + scratch memory.SliceBuffer[byte] +} + +func newByteArrayColumnBuffer(typ Type, columnIndex uint16, numValues int32) *byteArrayColumnBuffer { + return &byteArrayColumnBuffer{ + byteArrayPage: byteArrayPage{ + typ: typ, + offsets: memory.SliceBufferFor[uint32](int(numValues)), + columnIndex: ^columnIndex, + }, + lengths: memory.SliceBufferFor[uint32](int(numValues)), + } +} + +func (col *byteArrayColumnBuffer) Clone() ColumnBuffer { + return &byteArrayColumnBuffer{ + byteArrayPage: byteArrayPage{ + typ: col.typ, + values: col.cloneValues(), + offsets: col.cloneOffsets(), + columnIndex: col.columnIndex, + }, + lengths: col.cloneLengths(), + } +} + +func (col *byteArrayColumnBuffer) cloneLengths() memory.SliceBuffer[uint32] { + return col.lengths.Clone() +} + +func (col *byteArrayColumnBuffer) ColumnIndex() (ColumnIndex, error) { + return byteArrayColumnIndex{col.page()}, nil +} + +func (col *byteArrayColumnBuffer) OffsetIndex() (OffsetIndex, error) { + return byteArrayOffsetIndex{col.page()}, nil +} + +func (col *byteArrayColumnBuffer) BloomFilter() BloomFilter { return nil } + +func (col *byteArrayColumnBuffer) Dictionary() Dictionary { return nil } + +func (col *byteArrayColumnBuffer) Pages() Pages { return onePage(col.Page()) } + +func (col *byteArrayColumnBuffer) page() *byteArrayPage { + lengths := col.lengths.Slice() + offsets := col.offsets.Slice() + + if len(lengths) > 0 && orderOfUint32(offsets) < 1 { // unordered? + if col.scratch.Cap() < col.values.Len() { + col.scratch.Grow(col.values.Len()) + } + col.scratch.Resize(0) + + for i := range lengths { + n := col.scratch.Len() + col.scratch.Append(col.index(i)...) + offsets[i] = uint32(n) + } + + col.values, col.scratch = col.scratch, col.values + } + col.offsets.Resize(len(lengths)) + col.offsets.AppendValue(uint32(col.values.Len())) + return &col.byteArrayPage +} + +func (col *byteArrayColumnBuffer) Page() Page { + return col.page() +} + +func (col *byteArrayColumnBuffer) Reset() { + col.values.Reset() + col.offsets.Reset() + col.lengths.Reset() +} + +func (col *byteArrayColumnBuffer) NumRows() int64 { return int64(col.Len()) } + +func (col *byteArrayColumnBuffer) NumValues() int64 { return int64(col.Len()) } + +func (col *byteArrayColumnBuffer) Cap() int { return col.lengths.Cap() } + +func (col *byteArrayColumnBuffer) Len() int { return col.lengths.Len() } + +func (col *byteArrayColumnBuffer) Less(i, j int) bool { + return bytes.Compare(col.index(i), col.index(j)) < 0 +} + +func (col *byteArrayColumnBuffer) Swap(i, j int) { + col.offsets.Swap(i, j) + col.lengths.Swap(i, j) +} + +func (col *byteArrayColumnBuffer) Write(b []byte) (int, error) { + _, n, err := col.writeByteArrays(b) + return n, err +} + +func (col *byteArrayColumnBuffer) WriteByteArrays(values []byte) (int, error) { + n, _, err := col.writeByteArrays(values) + return n, err +} + +func (col *byteArrayColumnBuffer) writeByteArrays(values []byte) (count, bytes int, err error) { + baseCount := col.lengths.Len() + baseBytes := col.values.Len() + (plain.ByteArrayLengthSize * col.lengths.Len()) + + err = plain.RangeByteArray(values, func(value []byte) error { + col.offsets.AppendValue(uint32(col.values.Len())) + col.lengths.AppendValue(uint32(len(value))) + col.values.Append(value...) + return nil + }) + + count = col.lengths.Len() - baseCount + bytes = (col.values.Len() - baseBytes) + (plain.ByteArrayLengthSize * count) + return count, bytes, err +} + +func (col *byteArrayColumnBuffer) WriteValues(values []Value) (int, error) { + col.writeValues(columnLevels{}, makeArrayValue(values, offsetOfPtr)) + return len(values), nil +} + +func (col *byteArrayColumnBuffer) writeValues(levels columnLevels, rows sparse.Array) { + n := rows.Len() + if n == 0 { + return + } + + stringArray := rows.StringArray() + totalBytes := 0 + for i := range n { + totalBytes += len(stringArray.Index(i)) + } + + offsetsStart := col.offsets.Len() + lengthsStart := col.lengths.Len() + valuesStart := col.values.Len() + + col.offsets.Resize(offsetsStart + n) + col.lengths.Resize(lengthsStart + n) + col.values.Resize(valuesStart + totalBytes) + + offsets := col.offsets.Slice()[:offsetsStart+n] + lengths := col.lengths.Slice()[:lengthsStart+n] + values := col.values.Slice()[:valuesStart+totalBytes] + + valueOffset := valuesStart + for i := range n { + s := stringArray.Index(i) + offsets[offsetsStart+i] = uint32(valueOffset) + lengths[lengthsStart+i] = uint32(len(s)) + copy(values[valueOffset:], s) + valueOffset += len(s) + } +} + +func (col *byteArrayColumnBuffer) writeBoolean(levels columnLevels, value bool) { + offset := col.values.Len() + col.values.AppendFunc(func(b []byte) []byte { + return strconv.AppendBool(b, value) + }) + col.offsets.AppendValue(uint32(offset)) + col.lengths.AppendValue(uint32(col.values.Len() - offset)) +} + +func (col *byteArrayColumnBuffer) writeInt32(levels columnLevels, value int32) { + offset := col.values.Len() + col.values.AppendFunc(func(b []byte) []byte { + return strconv.AppendInt(b, int64(value), 10) + }) + col.offsets.AppendValue(uint32(offset)) + col.lengths.AppendValue(uint32(col.values.Len() - offset)) +} + +func (col *byteArrayColumnBuffer) writeInt64(levels columnLevels, value int64) { + offset := col.values.Len() + col.values.AppendFunc(func(b []byte) []byte { + return strconv.AppendInt(b, value, 10) + }) + col.offsets.AppendValue(uint32(offset)) + col.lengths.AppendValue(uint32(col.values.Len() - offset)) +} + +func (col *byteArrayColumnBuffer) writeInt96(levels columnLevels, value deprecated.Int96) { + offset := col.values.Len() + col.values.AppendFunc(func(b []byte) []byte { + result, _ := value.Int().AppendText(b) + return result + }) + col.offsets.AppendValue(uint32(offset)) + col.lengths.AppendValue(uint32(col.values.Len() - offset)) +} + +func (col *byteArrayColumnBuffer) writeFloat(levels columnLevels, value float32) { + offset := col.values.Len() + col.values.AppendFunc(func(b []byte) []byte { + return strconv.AppendFloat(b, float64(value), 'g', -1, 32) + }) + col.offsets.AppendValue(uint32(offset)) + col.lengths.AppendValue(uint32(col.values.Len() - offset)) +} + +func (col *byteArrayColumnBuffer) writeDouble(levels columnLevels, value float64) { + offset := col.values.Len() + col.values.AppendFunc(func(b []byte) []byte { + return strconv.AppendFloat(b, value, 'g', -1, 64) + }) + col.offsets.AppendValue(uint32(offset)) + col.lengths.AppendValue(uint32(col.values.Len() - offset)) +} + +func (col *byteArrayColumnBuffer) writeByteArray(levels columnLevels, value []byte) { + col.offsets.AppendValue(uint32(col.values.Len())) + col.lengths.AppendValue(uint32(len(value))) + col.values.Append(value...) +} + +func (col *byteArrayColumnBuffer) writeNull(levels columnLevels) { + col.offsets.AppendValue(uint32(col.values.Len())) + col.lengths.AppendValue(0) +} + +func (col *byteArrayColumnBuffer) ReadValuesAt(values []Value, offset int64) (n int, err error) { + i := int(offset) + numLengths := col.lengths.Len() + switch { + case i < 0: + return 0, errRowIndexOutOfBounds(offset, int64(numLengths)) + case i >= numLengths: + return 0, io.EOF + default: + for n < len(values) && i < numLengths { + values[n] = col.makeValueBytes(col.index(i)) + n++ + i++ + } + if n < len(values) { + err = io.EOF + } + return n, err + } +} + +func (col *byteArrayColumnBuffer) index(i int) []byte { + offsets := col.offsets.Slice() + lengths := col.lengths.Slice() + values := col.values.Slice() + offset := offsets[i] + length := lengths[i] + end := offset + length + return values[offset:end:end] +} diff --git a/vendor/github.com/parquet-go/parquet-go/column_buffer_double.go b/vendor/github.com/parquet-go/parquet-go/column_buffer_double.go new file mode 100644 index 000000000..9d3f5cdb1 --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/column_buffer_double.go @@ -0,0 +1,145 @@ +package parquet + +import ( + "fmt" + "io" + "strconv" + + "github.com/parquet-go/bitpack/unsafecast" + "github.com/parquet-go/parquet-go/deprecated" + "github.com/parquet-go/parquet-go/internal/memory" + "github.com/parquet-go/parquet-go/sparse" +) + +type doubleColumnBuffer struct{ doublePage } + +func newDoubleColumnBuffer(typ Type, columnIndex uint16, numValues int32) *doubleColumnBuffer { + return &doubleColumnBuffer{ + doublePage: doublePage{ + typ: typ, + values: memory.SliceBufferFor[float64](int(numValues)), + columnIndex: ^columnIndex, + }, + } +} + +func (col *doubleColumnBuffer) Clone() ColumnBuffer { + return &doubleColumnBuffer{ + doublePage: doublePage{ + typ: col.typ, + values: col.values.Clone(), + columnIndex: col.columnIndex, + }, + } +} + +func (col *doubleColumnBuffer) ColumnIndex() (ColumnIndex, error) { + return doubleColumnIndex{&col.doublePage}, nil +} + +func (col *doubleColumnBuffer) OffsetIndex() (OffsetIndex, error) { + return doubleOffsetIndex{&col.doublePage}, nil +} + +func (col *doubleColumnBuffer) BloomFilter() BloomFilter { return nil } + +func (col *doubleColumnBuffer) Dictionary() Dictionary { return nil } + +func (col *doubleColumnBuffer) Pages() Pages { return onePage(col.Page()) } + +func (col *doubleColumnBuffer) Page() Page { return &col.doublePage } + +func (col *doubleColumnBuffer) Reset() { col.values.Reset() } + +func (col *doubleColumnBuffer) Cap() int { return col.values.Cap() } + +func (col *doubleColumnBuffer) Len() int { return col.values.Len() } + +func (col *doubleColumnBuffer) Less(i, j int) bool { return col.values.Less(i, j) } + +func (col *doubleColumnBuffer) Swap(i, j int) { col.values.Swap(i, j) } + +func (col *doubleColumnBuffer) Write(b []byte) (int, error) { + if (len(b) % 8) != 0 { + return 0, fmt.Errorf("cannot write DOUBLE values from input of size %d", len(b)) + } + col.values.Append(unsafecast.Slice[float64](b)...) + return len(b), nil +} + +func (col *doubleColumnBuffer) WriteDoubles(values []float64) (int, error) { + col.values.Append(values...) + return len(values), nil +} + +func (col *doubleColumnBuffer) WriteValues(values []Value) (int, error) { + col.writeValues(columnLevels{}, makeArrayValue(values, offsetOfU64)) + return len(values), nil +} + +func (col *doubleColumnBuffer) writeValues(levels columnLevels, rows sparse.Array) { + offset := col.values.Len() + col.values.Resize(offset + rows.Len()) + sparse.GatherFloat64(col.values.Slice()[offset:], rows.Float64Array()) +} + +func (col *doubleColumnBuffer) writeBoolean(levels columnLevels, value bool) { + var uintValue float64 + if value { + uintValue = 1 + } + col.values.AppendValue(uintValue) +} + +func (col *doubleColumnBuffer) writeInt32(levels columnLevels, value int32) { + col.values.AppendValue(float64(value)) +} + +func (col *doubleColumnBuffer) writeInt64(levels columnLevels, value int64) { + col.values.AppendValue(float64(value)) +} + +func (col *doubleColumnBuffer) writeInt96(levels columnLevels, value deprecated.Int96) { + col.values.AppendValue(float64(value.Int32())) +} + +func (col *doubleColumnBuffer) writeFloat(levels columnLevels, value float32) { + col.values.AppendValue(float64(value)) +} + +func (col *doubleColumnBuffer) writeDouble(levels columnLevels, value float64) { + col.values.AppendValue(float64(value)) +} + +func (col *doubleColumnBuffer) writeByteArray(levels columnLevels, value []byte) { + floatValue, err := strconv.ParseFloat(unsafecast.String(value), 64) + if err != nil { + panic("cannot write byte array to double column: " + err.Error()) + } + col.values.AppendValue(floatValue) +} + +func (col *doubleColumnBuffer) writeNull(levels columnLevels) { + col.values.AppendValue(0) +} + +func (col *doubleColumnBuffer) ReadValuesAt(values []Value, offset int64) (n int, err error) { + i := int(offset) + colValues := col.values.Slice() + switch { + case i < 0: + return 0, errRowIndexOutOfBounds(offset, int64(len(colValues))) + case i >= len(colValues): + return 0, io.EOF + default: + for n < len(values) && i < len(colValues) { + values[n] = col.makeValue(colValues[i]) + n++ + i++ + } + if n < len(values) { + err = io.EOF + } + return n, err + } +} diff --git a/vendor/github.com/parquet-go/parquet-go/column_buffer_fixed_len_byte_array.go b/vendor/github.com/parquet-go/parquet-go/column_buffer_fixed_len_byte_array.go new file mode 100644 index 000000000..1a4ab0406 --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/column_buffer_fixed_len_byte_array.go @@ -0,0 +1,218 @@ +package parquet + +import ( + "bytes" + "encoding/binary" + "fmt" + "io" + "math" + "unsafe" + + "github.com/parquet-go/parquet-go/deprecated" + "github.com/parquet-go/parquet-go/internal/memory" + "github.com/parquet-go/parquet-go/sparse" +) + +type fixedLenByteArrayColumnBuffer struct { + fixedLenByteArrayPage + tmp []byte + buf [32]byte +} + +func newFixedLenByteArrayColumnBuffer(typ Type, columnIndex uint16, numValues int32) *fixedLenByteArrayColumnBuffer { + size := typ.Length() + col := &fixedLenByteArrayColumnBuffer{ + fixedLenByteArrayPage: fixedLenByteArrayPage{ + typ: typ, + size: size, + data: memory.SliceBufferFor[byte](int(numValues) * size), + columnIndex: ^columnIndex, + }, + } + if size <= len(col.buf) { + col.tmp = col.buf[:size] + } else { + col.tmp = make([]byte, size) + } + return col +} + +func (col *fixedLenByteArrayColumnBuffer) Clone() ColumnBuffer { + return &fixedLenByteArrayColumnBuffer{ + fixedLenByteArrayPage: fixedLenByteArrayPage{ + typ: col.typ, + size: col.size, + data: col.data.Clone(), + columnIndex: col.columnIndex, + }, + tmp: make([]byte, col.size), + } +} + +func (col *fixedLenByteArrayColumnBuffer) ColumnIndex() (ColumnIndex, error) { + return fixedLenByteArrayColumnIndex{&col.fixedLenByteArrayPage}, nil +} + +func (col *fixedLenByteArrayColumnBuffer) OffsetIndex() (OffsetIndex, error) { + return fixedLenByteArrayOffsetIndex{&col.fixedLenByteArrayPage}, nil +} + +func (col *fixedLenByteArrayColumnBuffer) BloomFilter() BloomFilter { return nil } + +func (col *fixedLenByteArrayColumnBuffer) Dictionary() Dictionary { return nil } + +func (col *fixedLenByteArrayColumnBuffer) Pages() Pages { return onePage(col.Page()) } + +func (col *fixedLenByteArrayColumnBuffer) Page() Page { return &col.fixedLenByteArrayPage } + +func (col *fixedLenByteArrayColumnBuffer) Reset() { col.data.Reset() } + +func (col *fixedLenByteArrayColumnBuffer) Cap() int { return col.data.Cap() / col.size } + +func (col *fixedLenByteArrayColumnBuffer) Len() int { return col.data.Len() / col.size } + +func (col *fixedLenByteArrayColumnBuffer) Less(i, j int) bool { + return bytes.Compare(col.index(i), col.index(j)) < 0 +} + +func (col *fixedLenByteArrayColumnBuffer) Swap(i, j int) { + t, u, v := col.tmp[:col.size], col.index(i), col.index(j) + copy(t, u) + copy(u, v) + copy(v, t) +} + +func (col *fixedLenByteArrayColumnBuffer) index(i int) []byte { + data := col.data.Slice() + j := (i + 0) * col.size + k := (i + 1) * col.size + return data[j:k:k] +} + +func (col *fixedLenByteArrayColumnBuffer) Write(b []byte) (int, error) { + n, err := col.WriteFixedLenByteArrays(b) + return n * col.size, err +} + +func (col *fixedLenByteArrayColumnBuffer) WriteFixedLenByteArrays(values []byte) (int, error) { + if len(values) == 0 { + return 0, nil + } + d, m := len(values)/col.size, len(values)%col.size + if d == 0 || m != 0 { + return 0, fmt.Errorf("cannot write FIXED_LEN_BYTE_ARRAY values of size %d from input of size %d", col.size, len(values)) + } + col.data.Append(values...) + return d, nil +} + +func (col *fixedLenByteArrayColumnBuffer) WriteValues(values []Value) (int, error) { + for i, v := range values { + if n := len(v.byteArray()); n != col.size { + return i, fmt.Errorf("cannot write FIXED_LEN_BYTE_ARRAY values of size %d from input of size %d", col.size, n) + } + col.data.Append(v.byteArray()...) + } + return len(values), nil +} + +func (col *fixedLenByteArrayColumnBuffer) writeValues(_ columnLevels, rows sparse.Array) { + n := col.size * rows.Len() + i := col.data.Len() + j := col.data.Len() + n + + if col.data.Cap() < j { + col.data.Grow(j - col.data.Len()) + } + + col.data.Resize(j) + data := col.data.Slice() + newData := data[i:] + + for i := range rows.Len() { + p := rows.Index(i) + copy(newData[i*col.size:], unsafe.Slice((*byte)(p), col.size)) + } +} + +func (col *fixedLenByteArrayColumnBuffer) writeBoolean(levels columnLevels, value bool) { + var fixedLenByteArrayValue [1]byte + if value { + fixedLenByteArrayValue[0] = 1 + } + col.writeBigEndian(fixedLenByteArrayValue[:]) +} + +func (col *fixedLenByteArrayColumnBuffer) writeInt32(levels columnLevels, value int32) { + var fixedLenByteArrayValue [4]byte + binary.BigEndian.PutUint32(fixedLenByteArrayValue[:], uint32(value)) + col.writeBigEndian(fixedLenByteArrayValue[:]) +} + +func (col *fixedLenByteArrayColumnBuffer) writeInt64(levels columnLevels, value int64) { + var fixedLenByteArrayValue [8]byte + binary.BigEndian.PutUint64(fixedLenByteArrayValue[:], uint64(value)) + col.writeBigEndian(fixedLenByteArrayValue[:]) +} + +func (col *fixedLenByteArrayColumnBuffer) writeInt96(levels columnLevels, value deprecated.Int96) { + var fixedLenByteArrayValue [12]byte + binary.BigEndian.PutUint32(fixedLenByteArrayValue[0:4], value[2]) + binary.BigEndian.PutUint32(fixedLenByteArrayValue[4:8], value[1]) + binary.BigEndian.PutUint32(fixedLenByteArrayValue[8:12], value[0]) + col.writeBigEndian(fixedLenByteArrayValue[:]) +} + +func (col *fixedLenByteArrayColumnBuffer) writeFloat(levels columnLevels, value float32) { + var fixedLenByteArrayValue [4]byte + binary.BigEndian.PutUint32(fixedLenByteArrayValue[:], math.Float32bits(value)) + col.writeBigEndian(fixedLenByteArrayValue[:]) +} + +func (col *fixedLenByteArrayColumnBuffer) writeDouble(levels columnLevels, value float64) { + var fixedLenByteArrayValue [8]byte + binary.BigEndian.PutUint64(fixedLenByteArrayValue[:], math.Float64bits(value)) + col.writeBigEndian(fixedLenByteArrayValue[:]) +} + +func (col *fixedLenByteArrayColumnBuffer) writeByteArray(levels columnLevels, value []byte) { + if col.size != len(value) { + panic(fmt.Sprintf("cannot write byte array of length %d to fixed length byte array column of size %d", len(value), col.size)) + } + col.data.Append(value...) +} + +func (col *fixedLenByteArrayColumnBuffer) writeNull(levels columnLevels) { + clear(col.tmp) + col.data.Append(col.tmp...) +} + +func (col *fixedLenByteArrayColumnBuffer) writeBigEndian(value []byte) { + if col.size < len(value) { + panic(fmt.Sprintf("cannot write byte array of length %d to fixed length byte array column of size %d", len(value), col.size)) + } + clear(col.tmp) + copy(col.tmp[col.size-len(value):], value) + col.data.Append(col.tmp...) +} + +func (col *fixedLenByteArrayColumnBuffer) ReadValuesAt(values []Value, offset int64) (n int, err error) { + data := col.data.Slice() + i := int(offset) * col.size + switch { + case i < 0: + return 0, errRowIndexOutOfBounds(offset, int64(len(data)/col.size)) + case i >= len(data): + return 0, io.EOF + default: + for n < len(values) && i < len(data) { + values[n] = col.makeValueBytes(data[i : i+col.size]) + n++ + i += col.size + } + if n < len(values) { + err = io.EOF + } + return n, err + } +} diff --git a/vendor/github.com/parquet-go/parquet-go/column_buffer_float.go b/vendor/github.com/parquet-go/parquet-go/column_buffer_float.go new file mode 100644 index 000000000..14355db58 --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/column_buffer_float.go @@ -0,0 +1,145 @@ +package parquet + +import ( + "fmt" + "io" + "strconv" + + "github.com/parquet-go/bitpack/unsafecast" + "github.com/parquet-go/parquet-go/deprecated" + "github.com/parquet-go/parquet-go/internal/memory" + "github.com/parquet-go/parquet-go/sparse" +) + +type floatColumnBuffer struct{ floatPage } + +func newFloatColumnBuffer(typ Type, columnIndex uint16, numValues int32) *floatColumnBuffer { + return &floatColumnBuffer{ + floatPage: floatPage{ + typ: typ, + values: memory.SliceBufferFor[float32](int(numValues)), + columnIndex: ^columnIndex, + }, + } +} + +func (col *floatColumnBuffer) Clone() ColumnBuffer { + return &floatColumnBuffer{ + floatPage: floatPage{ + typ: col.typ, + values: col.values.Clone(), + columnIndex: col.columnIndex, + }, + } +} + +func (col *floatColumnBuffer) ColumnIndex() (ColumnIndex, error) { + return floatColumnIndex{&col.floatPage}, nil +} + +func (col *floatColumnBuffer) OffsetIndex() (OffsetIndex, error) { + return floatOffsetIndex{&col.floatPage}, nil +} + +func (col *floatColumnBuffer) BloomFilter() BloomFilter { return nil } + +func (col *floatColumnBuffer) Dictionary() Dictionary { return nil } + +func (col *floatColumnBuffer) Pages() Pages { return onePage(col.Page()) } + +func (col *floatColumnBuffer) Page() Page { return &col.floatPage } + +func (col *floatColumnBuffer) Reset() { col.values.Reset() } + +func (col *floatColumnBuffer) Cap() int { return col.values.Cap() } + +func (col *floatColumnBuffer) Len() int { return col.values.Len() } + +func (col *floatColumnBuffer) Less(i, j int) bool { return col.values.Less(i, j) } + +func (col *floatColumnBuffer) Swap(i, j int) { col.values.Swap(i, j) } + +func (col *floatColumnBuffer) Write(b []byte) (int, error) { + if (len(b) % 4) != 0 { + return 0, fmt.Errorf("cannot write FLOAT values from input of size %d", len(b)) + } + col.values.Append(unsafecast.Slice[float32](b)...) + return len(b), nil +} + +func (col *floatColumnBuffer) WriteFloats(values []float32) (int, error) { + col.values.Append(values...) + return len(values), nil +} + +func (col *floatColumnBuffer) WriteValues(values []Value) (int, error) { + col.writeValues(columnLevels{}, makeArrayValue(values, offsetOfU32)) + return len(values), nil +} + +func (col *floatColumnBuffer) writeValues(levels columnLevels, rows sparse.Array) { + offset := col.values.Len() + col.values.Resize(offset + rows.Len()) + sparse.GatherFloat32(col.values.Slice()[offset:], rows.Float32Array()) +} + +func (col *floatColumnBuffer) writeBoolean(levels columnLevels, value bool) { + var uintValue float32 + if value { + uintValue = 1 + } + col.values.AppendValue(uintValue) +} + +func (col *floatColumnBuffer) writeInt32(levels columnLevels, value int32) { + col.values.AppendValue(float32(value)) +} + +func (col *floatColumnBuffer) writeInt64(levels columnLevels, value int64) { + col.values.AppendValue(float32(value)) +} + +func (col *floatColumnBuffer) writeInt96(levels columnLevels, value deprecated.Int96) { + col.values.AppendValue(float32(value.Int32())) +} + +func (col *floatColumnBuffer) writeFloat(levels columnLevels, value float32) { + col.values.AppendValue(float32(value)) +} + +func (col *floatColumnBuffer) writeDouble(levels columnLevels, value float64) { + col.values.AppendValue(float32(value)) +} + +func (col *floatColumnBuffer) writeByteArray(levels columnLevels, value []byte) { + floatValue, err := strconv.ParseFloat(unsafecast.String(value), 32) + if err != nil { + panic("cannot write byte array to float column: " + err.Error()) + } + col.values.AppendValue(float32(floatValue)) +} + +func (col *floatColumnBuffer) writeNull(levels columnLevels) { + col.values.AppendValue(0) +} + +func (col *floatColumnBuffer) ReadValuesAt(values []Value, offset int64) (n int, err error) { + i := int(offset) + colValues := col.values.Slice() + switch { + case i < 0: + return 0, errRowIndexOutOfBounds(offset, int64(len(colValues))) + case i >= len(colValues): + return 0, io.EOF + default: + for n < len(values) && i < len(colValues) { + values[n] = col.makeValue(colValues[i]) + n++ + i++ + } + if n < len(values) { + err = io.EOF + } + return n, err + } +} diff --git a/vendor/github.com/parquet-go/parquet-go/column_buffer_geometry.go b/vendor/github.com/parquet-go/parquet-go/column_buffer_geometry.go new file mode 100644 index 000000000..50d83cd00 --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/column_buffer_geometry.go @@ -0,0 +1,33 @@ +package parquet + +import ( + "fmt" + + "github.com/twpayne/go-geom" + "github.com/twpayne/go-geom/encoding/wkb" +) + +func writeGeometry(col ColumnBuffer, levels columnLevels, value geom.T, node Node) { + if value == nil { + col.writeNull(levels) + return + } + + if logicalType := node.Type().LogicalType(); logicalType != nil { + switch { + case logicalType.Geometry != nil: + case logicalType.Geography != nil: + // valid + default: + panic("invalid logical type for value of type geom.T, must be Geometry or Geography") + } + } else { + panic("missing logical type for value of type geom.T, must be Geometry or Geography") + } + + data, err := wkb.Marshal(value, wkb.NDR) + if err != nil { + panic(fmt.Errorf("failed to marshal geometry to WKB: %v", err)) + } + col.writeByteArray(levels, data) +} diff --git a/vendor/github.com/parquet-go/parquet-go/column_buffer_int32.go b/vendor/github.com/parquet-go/parquet-go/column_buffer_int32.go new file mode 100644 index 000000000..3a09f7367 --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/column_buffer_int32.go @@ -0,0 +1,145 @@ +package parquet + +import ( + "fmt" + "io" + "strconv" + + "github.com/parquet-go/bitpack/unsafecast" + "github.com/parquet-go/parquet-go/deprecated" + "github.com/parquet-go/parquet-go/internal/memory" + "github.com/parquet-go/parquet-go/sparse" +) + +type int32ColumnBuffer struct{ int32Page } + +func newInt32ColumnBuffer(typ Type, columnIndex uint16, numValues int32) *int32ColumnBuffer { + return &int32ColumnBuffer{ + int32Page: int32Page{ + typ: typ, + values: memory.SliceBufferFor[int32](int(numValues)), + columnIndex: ^columnIndex, + }, + } +} + +func (col *int32ColumnBuffer) Clone() ColumnBuffer { + return &int32ColumnBuffer{ + int32Page: int32Page{ + typ: col.typ, + values: col.values.Clone(), + columnIndex: col.columnIndex, + }, + } +} + +func (col *int32ColumnBuffer) ColumnIndex() (ColumnIndex, error) { + return int32ColumnIndex{&col.int32Page}, nil +} + +func (col *int32ColumnBuffer) OffsetIndex() (OffsetIndex, error) { + return int32OffsetIndex{&col.int32Page}, nil +} + +func (col *int32ColumnBuffer) BloomFilter() BloomFilter { return nil } + +func (col *int32ColumnBuffer) Dictionary() Dictionary { return nil } + +func (col *int32ColumnBuffer) Pages() Pages { return onePage(col.Page()) } + +func (col *int32ColumnBuffer) Page() Page { return &col.int32Page } + +func (col *int32ColumnBuffer) Reset() { col.values.Reset() } + +func (col *int32ColumnBuffer) Cap() int { return col.values.Cap() } + +func (col *int32ColumnBuffer) Len() int { return col.values.Len() } + +func (col *int32ColumnBuffer) Less(i, j int) bool { return col.values.Less(i, j) } + +func (col *int32ColumnBuffer) Swap(i, j int) { col.values.Swap(i, j) } + +func (col *int32ColumnBuffer) Write(b []byte) (int, error) { + if (len(b) % 4) != 0 { + return 0, fmt.Errorf("cannot write INT32 values from input of size %d", len(b)) + } + col.values.Append(unsafecast.Slice[int32](b)...) + return len(b), nil +} + +func (col *int32ColumnBuffer) WriteInt32s(values []int32) (int, error) { + col.values.Append(values...) + return len(values), nil +} + +func (col *int32ColumnBuffer) WriteValues(values []Value) (int, error) { + col.writeValues(columnLevels{}, makeArrayValue(values, offsetOfU32)) + return len(values), nil +} + +func (col *int32ColumnBuffer) writeValues(levels columnLevels, rows sparse.Array) { + offset := col.values.Len() + col.values.Resize(offset + rows.Len()) + sparse.GatherInt32(col.values.Slice()[offset:], rows.Int32Array()) +} + +func (col *int32ColumnBuffer) writeBoolean(levels columnLevels, value bool) { + var intValue int32 + if value { + intValue = 1 + } + col.values.AppendValue(intValue) +} + +func (col *int32ColumnBuffer) writeInt32(levels columnLevels, value int32) { + col.values.AppendValue(value) +} + +func (col *int32ColumnBuffer) writeInt64(levels columnLevels, value int64) { + col.values.AppendValue(int32(value)) +} + +func (col *int32ColumnBuffer) writeInt96(levels columnLevels, value deprecated.Int96) { + col.values.AppendValue(value.Int32()) +} + +func (col *int32ColumnBuffer) writeFloat(levels columnLevels, value float32) { + col.values.AppendValue(int32(value)) +} + +func (col *int32ColumnBuffer) writeDouble(levels columnLevels, value float64) { + col.values.AppendValue(int32(value)) +} + +func (col *int32ColumnBuffer) writeByteArray(levels columnLevels, value []byte) { + intValue, err := strconv.ParseInt(unsafecast.String(value), 10, 32) + if err != nil { + panic("cannot write byte array to int32 column: " + err.Error()) + } + col.values.AppendValue(int32(intValue)) +} + +func (col *int32ColumnBuffer) writeNull(levels columnLevels) { + col.values.AppendValue(0) +} + +func (col *int32ColumnBuffer) ReadValuesAt(values []Value, offset int64) (n int, err error) { + i := int(offset) + colValues := col.values.Slice() + switch { + case i < 0: + return 0, errRowIndexOutOfBounds(offset, int64(len(colValues))) + case i >= len(colValues): + return 0, io.EOF + default: + for n < len(values) && i < len(colValues) { + values[n] = col.makeValue(colValues[i]) + n++ + i++ + } + if n < len(values) { + err = io.EOF + } + return n, err + } +} diff --git a/vendor/github.com/parquet-go/parquet-go/column_buffer_int64.go b/vendor/github.com/parquet-go/parquet-go/column_buffer_int64.go new file mode 100644 index 000000000..3be0206f6 --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/column_buffer_int64.go @@ -0,0 +1,146 @@ +package parquet + +import ( + "fmt" + "io" + "strconv" + + "github.com/parquet-go/bitpack/unsafecast" + "github.com/parquet-go/parquet-go/deprecated" + "github.com/parquet-go/parquet-go/internal/memory" + "github.com/parquet-go/parquet-go/sparse" +) + +type int64ColumnBuffer struct{ int64Page } + +func newInt64ColumnBuffer(typ Type, columnIndex uint16, numValues int32) *int64ColumnBuffer { + return &int64ColumnBuffer{ + int64Page: int64Page{ + typ: typ, + values: memory.SliceBufferFor[int64](int(numValues)), + columnIndex: ^columnIndex, + }, + } +} + +func (col *int64ColumnBuffer) Clone() ColumnBuffer { + cloned := &int64ColumnBuffer{ + int64Page: int64Page{ + typ: col.typ, + columnIndex: col.columnIndex, + }, + } + cloned.values.Append(col.values.Slice()...) + return cloned +} + +func (col *int64ColumnBuffer) ColumnIndex() (ColumnIndex, error) { + return int64ColumnIndex{&col.int64Page}, nil +} + +func (col *int64ColumnBuffer) OffsetIndex() (OffsetIndex, error) { + return int64OffsetIndex{&col.int64Page}, nil +} + +func (col *int64ColumnBuffer) BloomFilter() BloomFilter { return nil } + +func (col *int64ColumnBuffer) Dictionary() Dictionary { return nil } + +func (col *int64ColumnBuffer) Pages() Pages { return onePage(col.Page()) } + +func (col *int64ColumnBuffer) Page() Page { return &col.int64Page } + +func (col *int64ColumnBuffer) Reset() { col.values.Reset() } + +func (col *int64ColumnBuffer) Cap() int { return col.values.Cap() } + +func (col *int64ColumnBuffer) Len() int { return col.values.Len() } + +func (col *int64ColumnBuffer) Less(i, j int) bool { return col.values.Less(i, j) } + +func (col *int64ColumnBuffer) Swap(i, j int) { col.values.Swap(i, j) } + +func (col *int64ColumnBuffer) Write(b []byte) (int, error) { + if (len(b) % 8) != 0 { + return 0, fmt.Errorf("cannot write INT64 values from input of size %d", len(b)) + } + col.values.Append(unsafecast.Slice[int64](b)...) + return len(b), nil +} + +func (col *int64ColumnBuffer) WriteInt64s(values []int64) (int, error) { + col.values.Append(values...) + return len(values), nil +} + +func (col *int64ColumnBuffer) WriteValues(values []Value) (int, error) { + col.writeValues(columnLevels{}, makeArrayValue(values, offsetOfU64)) + return len(values), nil +} + +func (col *int64ColumnBuffer) writeValues(levels columnLevels, rows sparse.Array) { + offset := col.values.Len() + col.values.Resize(offset + rows.Len()) + sparse.GatherInt64(col.values.Slice()[offset:], rows.Int64Array()) +} + +func (col *int64ColumnBuffer) writeBoolean(levels columnLevels, value bool) { + var intValue int64 + if value { + intValue = 1 + } + col.values.AppendValue(intValue) +} + +func (col *int64ColumnBuffer) writeInt32(levels columnLevels, value int32) { + col.values.AppendValue(int64(value)) +} + +func (col *int64ColumnBuffer) writeInt64(levels columnLevels, value int64) { + col.values.AppendValue(value) +} + +func (col *int64ColumnBuffer) writeInt96(levels columnLevels, value deprecated.Int96) { + col.values.AppendValue(value.Int64()) +} + +func (col *int64ColumnBuffer) writeFloat(levels columnLevels, value float32) { + col.values.AppendValue(int64(value)) +} + +func (col *int64ColumnBuffer) writeDouble(levels columnLevels, value float64) { + col.values.AppendValue(int64(value)) +} + +func (col *int64ColumnBuffer) writeByteArray(levels columnLevels, value []byte) { + intValue, err := strconv.ParseInt(unsafecast.String(value), 10, 64) + if err != nil { + panic("cannot write byte array to int64 column: " + err.Error()) + } + col.values.AppendValue(intValue) +} + +func (col *int64ColumnBuffer) writeNull(levels columnLevels) { + col.values.AppendValue(0) +} + +func (col *int64ColumnBuffer) ReadValuesAt(values []Value, offset int64) (n int, err error) { + i := int(offset) + colValues := col.values.Slice() + switch { + case i < 0: + return 0, errRowIndexOutOfBounds(offset, int64(len(colValues))) + case i >= len(colValues): + return 0, io.EOF + default: + for n < len(values) && i < len(colValues) { + values[n] = col.makeValue(colValues[i]) + n++ + i++ + } + if n < len(values) { + err = io.EOF + } + return n, err + } +} diff --git a/vendor/github.com/parquet-go/parquet-go/column_buffer_int96.go b/vendor/github.com/parquet-go/parquet-go/column_buffer_int96.go new file mode 100644 index 000000000..cb144d767 --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/column_buffer_int96.go @@ -0,0 +1,149 @@ +package parquet + +import ( + "fmt" + "io" + "math/big" + "slices" + + "github.com/parquet-go/bitpack/unsafecast" + "github.com/parquet-go/parquet-go/deprecated" + "github.com/parquet-go/parquet-go/sparse" +) + +type int96ColumnBuffer struct{ int96Page } + +func newInt96ColumnBuffer(typ Type, columnIndex uint16, numValues int32) *int96ColumnBuffer { + return &int96ColumnBuffer{ + int96Page: int96Page{ + typ: typ, + values: make([]deprecated.Int96, 0, numValues), + columnIndex: ^columnIndex, + }, + } +} + +func (col *int96ColumnBuffer) Clone() ColumnBuffer { + return &int96ColumnBuffer{ + int96Page: int96Page{ + typ: col.typ, + values: slices.Clone(col.values), + columnIndex: col.columnIndex, + }, + } +} + +func (col *int96ColumnBuffer) ColumnIndex() (ColumnIndex, error) { + return int96ColumnIndex{&col.int96Page}, nil +} + +func (col *int96ColumnBuffer) OffsetIndex() (OffsetIndex, error) { + return int96OffsetIndex{&col.int96Page}, nil +} + +func (col *int96ColumnBuffer) BloomFilter() BloomFilter { return nil } + +func (col *int96ColumnBuffer) Dictionary() Dictionary { return nil } + +func (col *int96ColumnBuffer) Pages() Pages { return onePage(col.Page()) } + +func (col *int96ColumnBuffer) Page() Page { return &col.int96Page } + +func (col *int96ColumnBuffer) Reset() { col.values = col.values[:0] } + +func (col *int96ColumnBuffer) Cap() int { return cap(col.values) } + +func (col *int96ColumnBuffer) Len() int { return len(col.values) } + +func (col *int96ColumnBuffer) Less(i, j int) bool { return col.values[i].Less(col.values[j]) } + +func (col *int96ColumnBuffer) Swap(i, j int) { + col.values[i], col.values[j] = col.values[j], col.values[i] +} + +func (col *int96ColumnBuffer) Write(b []byte) (int, error) { + if (len(b) % 12) != 0 { + return 0, fmt.Errorf("cannot write INT96 values from input of size %d", len(b)) + } + col.values = append(col.values, unsafecast.Slice[deprecated.Int96](b)...) + return len(b), nil +} + +func (col *int96ColumnBuffer) WriteInt96s(values []deprecated.Int96) (int, error) { + col.values = append(col.values, values...) + return len(values), nil +} + +func (col *int96ColumnBuffer) WriteValues(values []Value) (int, error) { + for _, v := range values { + col.values = append(col.values, v.Int96()) + } + return len(values), nil +} + +func (col *int96ColumnBuffer) writeValues(_ columnLevels, rows sparse.Array) { + for i := range rows.Len() { + p := rows.Index(i) + col.values = append(col.values, *(*deprecated.Int96)(p)) + } +} + +func (col *int96ColumnBuffer) writeBoolean(levels columnLevels, value bool) { + if value { + col.writeInt96(levels, deprecated.Int96{1, 0, 0}) + } else { + col.writeInt96(levels, deprecated.Int96{0, 0, 0}) + } +} + +func (col *int96ColumnBuffer) writeInt32(levels columnLevels, value int32) { + col.writeInt96(levels, deprecated.Int32ToInt96(value)) +} + +func (col *int96ColumnBuffer) writeInt64(levels columnLevels, value int64) { + col.writeInt96(levels, deprecated.Int64ToInt96(value)) +} + +func (col *int96ColumnBuffer) writeInt96(_ columnLevels, value deprecated.Int96) { + col.values = append(col.values, value) +} + +func (col *int96ColumnBuffer) writeFloat(levels columnLevels, value float32) { + col.writeInt96(levels, deprecated.Int64ToInt96(int64(value))) +} + +func (col *int96ColumnBuffer) writeDouble(levels columnLevels, value float64) { + col.writeInt96(levels, deprecated.Int64ToInt96(int64(value))) +} + +func (col *int96ColumnBuffer) writeByteArray(levels columnLevels, value []byte) { + v, ok := new(big.Int).SetString(string(value), 10) + if !ok || v == nil { + panic("invalid byte array for int96: cannot parse") + } + col.writeInt96(levels, deprecated.Int64ToInt96(v.Int64())) +} + +func (col *int96ColumnBuffer) writeNull(_ columnLevels) { + panic("cannot write null to int96 column") +} + +func (col *int96ColumnBuffer) ReadValuesAt(values []Value, offset int64) (n int, err error) { + i := int(offset) + switch { + case i < 0: + return 0, errRowIndexOutOfBounds(offset, int64(len(col.values))) + case i >= len(col.values): + return 0, io.EOF + default: + for n < len(values) && i < len(col.values) { + values[n] = col.makeValue(col.values[i]) + n++ + i++ + } + if n < len(values) { + err = io.EOF + } + return n, err + } +} diff --git a/vendor/github.com/parquet-go/parquet-go/column_buffer_json.go b/vendor/github.com/parquet-go/parquet-go/column_buffer_json.go new file mode 100644 index 000000000..efea249d3 --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/column_buffer_json.go @@ -0,0 +1,226 @@ +package parquet + +import ( + "encoding/json" + "fmt" + "math" + "reflect" + "strconv" + "time" + + "github.com/parquet-go/bitpack/unsafecast" + "github.com/parquet-go/jsonlite" +) + +var jsonNull jsonlite.Value + +func init() { + v, _ := jsonlite.Parse("null") + jsonNull = *v +} + +func jsonParse(data []byte) (*jsonlite.Value, error) { + if len(data) == 0 { + return &jsonNull, nil + } + return jsonlite.Parse(unsafecast.String(data)) +} + +func writeJSONToLeaf(col ColumnBuffer, levels columnLevels, val *jsonlite.Value, node Node) { + typ := node.Type() + if typ.Kind() == ByteArray { + if logicalType := typ.LogicalType(); logicalType != nil && logicalType.Json != nil { + writeJSONToByteArray(col, levels, val, node) + return + } + } + switch val.Kind() { + case jsonlite.Null: + col.writeNull(levels) + case jsonlite.True, jsonlite.False: + col.writeBoolean(levels, val.Kind() == jsonlite.True) + case jsonlite.Number: + writeJSONNumber(col, levels, val.Number(), node) + case jsonlite.String: + writeJSONString(col, levels, val.String(), node) + default: + writeJSONToByteArray(col, levels, val, node) + } +} + +func writeJSONToByteArray(col ColumnBuffer, levels columnLevels, val *jsonlite.Value, node Node) { + if val.Kind() == jsonlite.Null && node.Optional() { + col.writeNull(levels) + } else { + col.writeByteArray(levels, unsafeByteArrayFromString(val.JSON())) + } +} + +func writeJSONToGroup(columns []ColumnBuffer, levels columnLevels, val *jsonlite.Value, node Node, writers []fieldWriter) { + if val.Kind() != jsonlite.Object { + for i := range writers { + w := &writers[i] + w.writeValue(columns, levels, reflect.Value{}) + } + return + } + + for i := range writers { + w := &writers[i] + f := val.Lookup(w.fieldName) + if f == nil { + w.writeValue(columns, levels, reflect.Value{}) + } else { + w.writeValue(columns, levels, reflect.ValueOf(f)) + } + } +} + +func writeJSONToRepeated(columns []ColumnBuffer, levels columnLevels, val *jsonlite.Value, elementWriter writeValueFunc) { + if val.Kind() == jsonlite.Array { + if val.Len() == 0 { + elementWriter(columns, levels, reflect.Value{}) + return + } + + levels.repetitionDepth++ + levels.definitionLevel++ + + for elem := range val.Array { + elementWriter(columns, levels, reflect.ValueOf(elem)) + levels.repetitionLevel = levels.repetitionDepth + } + return + } + + // Auto-wrap scalar to single-element array + levels.repetitionDepth++ + levels.definitionLevel++ + elementWriter(columns, levels, reflect.ValueOf(val)) +} + +func writeJSONString(col ColumnBuffer, levels columnLevels, str string, node Node) { + typ := node.Type() + + if logicalType := typ.LogicalType(); logicalType != nil { + switch { + case logicalType.Timestamp != nil: + t, err := time.Parse(time.RFC3339, str) + if err != nil { + panic(fmt.Errorf("cannot parse JSON string %q as timestamp: %w", str, err)) + } + writeTime(col, levels, t, node) + return + + case logicalType.Date != nil: + t, err := time.Parse("2006-01-02", str) + if err != nil { + panic(fmt.Errorf("cannot parse JSON string %q as date: %w", str, err)) + } + writeTime(col, levels, t, node) + return + + case logicalType.Time != nil: + t, err := time.Parse("15:04:05.000000000", str) + if err != nil { + panic(fmt.Errorf("cannot parse JSON string %q as time: %w", str, err)) + } + d := time.Duration(t.Hour())*time.Hour + + time.Duration(t.Minute())*time.Minute + + time.Duration(t.Second())*time.Second + + time.Duration(t.Nanosecond())*time.Nanosecond + writeDuration(col, levels, d, node) + return + + case logicalType.UUID != nil: + // Only parse UUID strings when writing to binary UUID columns + // (FIXED_LEN_BYTE_ARRAY with 16 bytes). If writing to a STRING + // column with UUID logical type, write the string as-is. + writeUUID(col, levels, str, typ) + return + } + } + + col.writeByteArray(levels, unsafeByteArrayFromString(str)) +} + +func writeJSONNumber(col ColumnBuffer, levels columnLevels, num json.Number, node Node) { + typ := node.Type() + str := num.String() + + if logicalType := typ.LogicalType(); logicalType != nil { + switch { + case logicalType.Timestamp != nil: + // Interpret number as seconds since Unix epoch (with sub-second precision) + f, err := num.Float64() + if err != nil { + panic(fmt.Errorf("cannot convert json.Number %q to float64 for timestamp: %w", num, err)) + } + sec, frac := math.Modf(f) + t := time.Unix(int64(sec), int64(frac*1e9)).UTC() + writeTime(col, levels, t, node) + return + + case logicalType.Date != nil: + // Interpret number as seconds since Unix epoch + i, err := num.Int64() + if err != nil { + panic(fmt.Errorf("cannot convert json.Number %q to int64 for date: %w", num, err)) + } + t := time.Unix(i, 0).UTC() + writeTime(col, levels, t, node) + return + + case logicalType.Time != nil: + // Interpret number as seconds since midnight + f, err := num.Float64() + if err != nil { + panic(fmt.Errorf("cannot convert json.Number %q to float64 for time: %w", num, err)) + } + d := time.Duration(f * float64(time.Second)) + writeDuration(col, levels, d, node) + return + } + } + + switch kind := typ.Kind(); kind { + case Boolean: + f, err := num.Float64() + if err != nil { + panic(fmt.Errorf("cannot convert json.Number %q to float64 for boolean: %w", num, err)) + } + col.writeBoolean(levels, f != 0) + + case Int32, Int64: + switch jsonlite.NumberTypeOf(str) { + case jsonlite.Int: + i, err := num.Int64() + if err != nil { + panic(fmt.Errorf("cannot convert json.Number %q to int: %w", num, err)) + } + col.writeInt64(levels, i) + case jsonlite.Uint: + u, err := strconv.ParseUint(str, 10, 64) + if err != nil { + panic(fmt.Errorf("cannot convert json.Number %q to int: %w", num, err)) + } + col.writeInt64(levels, int64(u)) + case jsonlite.Float: + f, err := num.Float64() + if err != nil { + panic(fmt.Errorf("cannot convert json.Number %q to float: %w", num, err)) + } + col.writeInt64(levels, int64(f)) + } + + case Float, Double: + f, err := num.Float64() + if err != nil { + panic(fmt.Errorf("cannot convert json.Number %q to float64: %w", num, err)) + } + col.writeDouble(levels, f) + + default: + col.writeByteArray(levels, unsafeByteArrayFromString(str)) + } +} diff --git a/vendor/github.com/parquet-go/parquet-go/column_buffer_null.go b/vendor/github.com/parquet-go/parquet-go/column_buffer_null.go new file mode 100644 index 000000000..005221d0f --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/column_buffer_null.go @@ -0,0 +1,114 @@ +package parquet + +import ( + "io" + + "github.com/parquet-go/parquet-go/deprecated" + "github.com/parquet-go/parquet-go/sparse" +) + +type nullColumnBuffer struct{ nullPage } + +func newNullColumnBuffer(typ Type, columnIndex uint16, numValues int32) *nullColumnBuffer { + return &nullColumnBuffer{ + nullPage: nullPage{ + typ: typ, + column: int(columnIndex), + count: int(numValues), + }, + } +} + +func (col *nullColumnBuffer) Clone() ColumnBuffer { + cloned := &nullColumnBuffer{ + nullPage: nullPage{ + typ: col.typ, + column: col.column, + count: col.count, + }, + } + return cloned +} + +func (col *nullColumnBuffer) ColumnIndex() (ColumnIndex, error) { return nil, nil } + +func (col *nullColumnBuffer) OffsetIndex() (OffsetIndex, error) { return nil, nil } + +func (col *nullColumnBuffer) BloomFilter() BloomFilter { return nil } + +func (col *nullColumnBuffer) Pages() Pages { return onePage(col.Page()) } + +func (col *nullColumnBuffer) Page() Page { return &col.nullPage } + +func (col *nullColumnBuffer) Reset() { col.nullPage.count = 0 } + +func (col *nullColumnBuffer) Cap() int { return col.nullPage.count } + +func (col *nullColumnBuffer) Len() int { return col.nullPage.count } + +func (col *nullColumnBuffer) Less(i, j int) bool { return false } + +func (col *nullColumnBuffer) Swap(i, j int) {} + +func (col *nullColumnBuffer) Size() int64 { return 0 } + +func (col *nullColumnBuffer) WriteValues(values []Value) (int, error) { + col.writeValues(columnLevels{}, makeArrayValue(values, 0)) + return len(values), nil +} + +func (col *nullColumnBuffer) writeValues(_ columnLevels, rows sparse.Array) { + col.nullPage.count += rows.Len() +} + +func (col *nullColumnBuffer) writeBoolean(_ columnLevels, _ bool) { + col.nullPage.count++ +} + +func (col *nullColumnBuffer) writeInt32(_ columnLevels, _ int32) { + col.nullPage.count++ +} + +func (col *nullColumnBuffer) writeInt64(_ columnLevels, _ int64) { + col.nullPage.count++ +} + +func (col *nullColumnBuffer) writeInt96(_ columnLevels, _ deprecated.Int96) { + col.nullPage.count++ +} + +func (col *nullColumnBuffer) writeFloat(_ columnLevels, _ float32) { + col.nullPage.count++ +} + +func (col *nullColumnBuffer) writeDouble(_ columnLevels, _ float64) { + col.nullPage.count++ +} + +func (col *nullColumnBuffer) writeByteArray(_ columnLevels, _ []byte) { + col.nullPage.count++ +} + +func (col *nullColumnBuffer) writeNull(_ columnLevels) { + col.nullPage.count++ +} + +func (col *nullColumnBuffer) ReadValuesAt(values []Value, offset int64) (n int, err error) { + i := int(offset) + switch { + case i < 0: + return 0, errRowIndexOutOfBounds(offset, int64(col.nullPage.count)) + case i >= int(col.nullPage.count): + return 0, io.EOF + default: + for n < len(values) && i < int(col.nullPage.count) { + values[n] = Value{columnIndex: ^uint16(col.nullPage.column)} + n++ + i++ + } + if n < len(values) { + err = io.EOF + } + return n, err + } +} diff --git a/vendor/github.com/parquet-go/parquet-go/column_buffer_optional.go b/vendor/github.com/parquet-go/parquet-go/column_buffer_optional.go new file mode 100644 index 000000000..069ccf417 --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/column_buffer_optional.go @@ -0,0 +1,367 @@ +package parquet + +import ( + "io" + + "github.com/parquet-go/parquet-go/deprecated" + "github.com/parquet-go/parquet-go/internal/memory" + "github.com/parquet-go/parquet-go/sparse" +) + +// optionalColumnBuffer is an implementation of the ColumnBuffer interface used +// as a wrapper to an underlying ColumnBuffer to manage the creation of +// definition levels. +// +// Null values are not written to the underlying column; instead, the buffer +// tracks offsets of row values in the column, null row values are represented +// by the value -1 and a definition level less than the max. +// +// This column buffer type is used for all leaf columns that have a non-zero +// max definition level and a zero repetition level, which may be because the +// column or one of its parent(s) are marked optional. +type optionalColumnBuffer struct { + base ColumnBuffer + reordered bool + maxDefinitionLevel byte + rows memory.SliceBuffer[int32] + sortIndex memory.SliceBuffer[int32] + definitionLevels memory.SliceBuffer[byte] + nullOrdering nullOrdering +} + +func newOptionalColumnBuffer(base ColumnBuffer, maxDefinitionLevel byte, nullOrdering nullOrdering) *optionalColumnBuffer { + return &optionalColumnBuffer{ + base: base, + maxDefinitionLevel: maxDefinitionLevel, + nullOrdering: nullOrdering, + } +} + +func (col *optionalColumnBuffer) Clone() ColumnBuffer { + return &optionalColumnBuffer{ + base: col.base.Clone(), + reordered: col.reordered, + maxDefinitionLevel: col.maxDefinitionLevel, + rows: col.rows.Clone(), + definitionLevels: col.definitionLevels.Clone(), + nullOrdering: col.nullOrdering, + } +} + +func (col *optionalColumnBuffer) Type() Type { + return col.base.Type() +} + +func (col *optionalColumnBuffer) NumValues() int64 { + return int64(col.definitionLevels.Len()) +} + +func (col *optionalColumnBuffer) ColumnIndex() (ColumnIndex, error) { + return columnIndexOfNullable(col.base, col.maxDefinitionLevel, col.definitionLevels.Slice()) +} + +func (col *optionalColumnBuffer) OffsetIndex() (OffsetIndex, error) { + return col.base.OffsetIndex() +} + +func (col *optionalColumnBuffer) BloomFilter() BloomFilter { + return col.base.BloomFilter() +} + +func (col *optionalColumnBuffer) Dictionary() Dictionary { + return col.base.Dictionary() +} + +func (col *optionalColumnBuffer) Column() int { + return col.base.Column() +} + +func (col *optionalColumnBuffer) Pages() Pages { + return onePage(col.Page()) +} + +func (col *optionalColumnBuffer) Page() Page { + // No need for any cyclic sorting if the rows have not been reordered. + // This case is also important because the cyclic sorting modifies the + // buffer which makes it unsafe to read the buffer concurrently. + if col.reordered { + numNulls := countLevelsNotEqual(col.definitionLevels.Slice(), col.maxDefinitionLevel) + numValues := col.rows.Len() - numNulls + + if numValues > 0 { + if col.sortIndex.Cap() < numValues { + col.sortIndex = memory.SliceBufferFor[int32](numValues) + } + col.sortIndex.Resize(numValues) + sortIndex := col.sortIndex.Slice() + rows := col.rows.Slice() + i := 0 + for _, j := range rows { + if j >= 0 { + sortIndex[j] = int32(i) + i++ + } + } + + // Cyclic sort: O(N) + for i := range sortIndex { + for j := int(sortIndex[i]); i != j; j = int(sortIndex[i]) { + col.base.Swap(i, j) + sortIndex[i], sortIndex[j] = sortIndex[j], sortIndex[i] + } + } + } + + rows := col.rows.Slice() + i := 0 + for _, r := range rows { + if r >= 0 { + rows[i] = int32(i) + i++ + } + } + + col.reordered = false + } + + return newOptionalPage(col.base.Page(), col.maxDefinitionLevel, col.definitionLevels.Slice()) +} + +func (col *optionalColumnBuffer) Reset() { + col.base.Reset() + col.rows.Resize(0) + col.definitionLevels.Resize(0) +} + +func (col *optionalColumnBuffer) Size() int64 { + return int64(4*col.rows.Len()+4*col.sortIndex.Len()+col.definitionLevels.Len()) + col.base.Size() +} + +func (col *optionalColumnBuffer) Cap() int { return col.rows.Cap() } + +func (col *optionalColumnBuffer) Len() int { return col.rows.Len() } + +func (col *optionalColumnBuffer) Less(i, j int) bool { + rows := col.rows.Slice() + definitionLevels := col.definitionLevels.Slice() + return col.nullOrdering( + col.base, + int(rows[i]), + int(rows[j]), + col.maxDefinitionLevel, + definitionLevels[i], + definitionLevels[j], + ) +} + +func (col *optionalColumnBuffer) Swap(i, j int) { + // Because the underlying column does not contain null values, we cannot + // swap its values at indexes i and j. We swap the row indexes only, then + // reorder the underlying buffer using a cyclic sort when the buffer is + // materialized into a page view. + col.reordered = true + rows := col.rows.Slice() + definitionLevels := col.definitionLevels.Slice() + rows[i], rows[j] = rows[j], rows[i] + definitionLevels[i], definitionLevels[j] = definitionLevels[j], definitionLevels[i] +} + +func (col *optionalColumnBuffer) WriteValues(values []Value) (n int, err error) { + rowIndex := int32(col.base.Len()) + + for n < len(values) { + // Collect index range of contiguous null values, from i to n. If this + // for loop exhausts the values, all remaining if statements and for + // loops will be no-ops and the loop will terminate. + i := n + for n < len(values) && values[n].definitionLevel != col.maxDefinitionLevel { + n++ + } + + // Write the contiguous null values up until the first non-null value + // obtained in the for loop above. + for _, v := range values[i:n] { + col.rows.AppendValue(-1) + col.definitionLevels.AppendValue(v.definitionLevel) + } + + // Collect index range of contiguous non-null values, from i to n. + i = n + for n < len(values) && values[n].definitionLevel == col.maxDefinitionLevel { + n++ + } + + // As long as i < n we have non-null values still to write. It is + // possible that we just exhausted the input values in which case i == n + // and the outer for loop will terminate. + if i < n { + count, err := col.base.WriteValues(values[i:n]) + + for range count { + col.definitionLevels.AppendValue(col.maxDefinitionLevel) + } + + for count > 0 { + col.rows.AppendValue(rowIndex) + rowIndex++ + count-- + } + + if err != nil { + return n, err + } + } + } + return n, nil +} + +func (col *optionalColumnBuffer) writeValues(levels columnLevels, rows sparse.Array) { + // The row count is zero when writing an null optional value, in which case + // we still need to output a row to the buffer to record the definition + // level. + if rows.Len() == 0 { + col.definitionLevels.AppendValue(levels.definitionLevel) + col.rows.AppendValue(-1) + return + } + + baseLen := col.base.Len() + for range rows.Len() { + col.definitionLevels.AppendValue(levels.definitionLevel) + } + + i := col.rows.Len() + j := col.rows.Len() + rows.Len() + + if j <= col.rows.Cap() { + col.rows.Resize(j) + } else { + col.rows.Grow(j - col.rows.Len()) + col.rows.Resize(j) + } + + rowsSlice := col.rows.Slice() + if levels.definitionLevel != col.maxDefinitionLevel { + broadcastValueInt32(rowsSlice[i:], -1) + } else { + broadcastRangeInt32(rowsSlice[i:], int32(baseLen)) + col.base.writeValues(levels, rows) + } +} + +func (col *optionalColumnBuffer) writeBoolean(levels columnLevels, value bool) { + if levels.definitionLevel != col.maxDefinitionLevel { + col.writeNull(levels) + } else { + col.base.writeBoolean(levels, value) + col.writeLevel() + } +} + +func (col *optionalColumnBuffer) writeInt32(levels columnLevels, value int32) { + if levels.definitionLevel != col.maxDefinitionLevel { + col.writeNull(levels) + } else { + col.base.writeInt32(levels, value) + col.writeLevel() + } +} + +func (col *optionalColumnBuffer) writeInt64(levels columnLevels, value int64) { + if levels.definitionLevel != col.maxDefinitionLevel { + col.writeNull(levels) + } else { + col.base.writeInt64(levels, value) + col.writeLevel() + } +} + +func (col *optionalColumnBuffer) writeInt96(levels columnLevels, value deprecated.Int96) { + if levels.definitionLevel != col.maxDefinitionLevel { + col.writeNull(levels) + } else { + col.base.writeInt96(levels, value) + col.writeLevel() + } +} + +func (col *optionalColumnBuffer) writeFloat(levels columnLevels, value float32) { + if levels.definitionLevel != col.maxDefinitionLevel { + col.writeNull(levels) + } else { + col.base.writeFloat(levels, value) + col.writeLevel() + } +} + +func (col *optionalColumnBuffer) writeDouble(levels columnLevels, value float64) { + if levels.definitionLevel != col.maxDefinitionLevel { + col.writeNull(levels) + } else { + col.base.writeDouble(levels, value) + col.writeLevel() + } +} + +func (col *optionalColumnBuffer) writeByteArray(levels columnLevels, value []byte) { + if levels.definitionLevel != col.maxDefinitionLevel { + col.writeNull(levels) + } else { + col.base.writeByteArray(levels, value) + col.writeLevel() + } +} + +func (col *optionalColumnBuffer) writeNull(levels columnLevels) { + col.definitionLevels.AppendValue(levels.definitionLevel) + col.rows.AppendValue(-1) +} + +func (col *optionalColumnBuffer) writeLevel() { + col.definitionLevels.AppendValue(col.maxDefinitionLevel) + col.rows.AppendValue(int32(col.base.Len() - 1)) +} + +func (col *optionalColumnBuffer) ReadValuesAt(values []Value, offset int64) (int, error) { + definitionLevels := col.definitionLevels.Slice() + length := int64(len(definitionLevels)) + if offset < 0 { + return 0, errRowIndexOutOfBounds(offset, length) + } + if offset >= length { + return 0, io.EOF + } + if length -= offset; length < int64(len(values)) { + values = values[:length] + } + + numNulls1 := int64(countLevelsNotEqual(definitionLevels[:offset], col.maxDefinitionLevel)) + numNulls2 := int64(countLevelsNotEqual(definitionLevels[offset:offset+length], col.maxDefinitionLevel)) + + if numNulls2 < length { + n, err := col.base.ReadValuesAt(values[:length-numNulls2], offset-numNulls1) + if err != nil { + return n, err + } + } + + if numNulls2 > 0 { + columnIndex := ^uint16(col.Column()) + i := numNulls2 - 1 + j := length - 1 + definitionLevelsSlice := definitionLevels[offset : offset+length] + maxDefinitionLevel := col.maxDefinitionLevel + + for n := len(definitionLevelsSlice) - 1; n >= 0 && j > i; n-- { + if definitionLevelsSlice[n] != maxDefinitionLevel { + values[j] = Value{definitionLevel: definitionLevelsSlice[n], columnIndex: columnIndex} + } else { + values[j] = values[i] + i-- + } + j-- + } + } + + return int(length), nil +} diff --git a/vendor/github.com/parquet-go/parquet-go/column_buffer_proto.go b/vendor/github.com/parquet-go/parquet-go/column_buffer_proto.go new file mode 100644 index 000000000..31bb7f114 --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/column_buffer_proto.go @@ -0,0 +1,206 @@ +package parquet + +import ( + "fmt" + "slices" + "strconv" + "strings" + "time" + + "github.com/parquet-go/jsonlite" + "github.com/parquet-go/parquet-go/format" + "github.com/parquet-go/parquet-go/internal/memory" + "google.golang.org/protobuf/proto" + "google.golang.org/protobuf/types/known/anypb" + "google.golang.org/protobuf/types/known/durationpb" + "google.golang.org/protobuf/types/known/structpb" + "google.golang.org/protobuf/types/known/timestamppb" +) + +func writeProtoTimestamp(col ColumnBuffer, levels columnLevels, ts *timestamppb.Timestamp, node Node) { + if ts == nil { + col.writeNull(levels) + return + } + var typ = node.Type() + var unit format.TimeUnit + if lt := typ.LogicalType(); lt != nil && lt.Timestamp != nil { + unit = lt.Timestamp.Unit + } else { + unit = Nanosecond.TimeUnit() + } + var t = ts.AsTime() + var value int64 + switch { + case unit.Millis != nil: + value = t.UnixMilli() + case unit.Micros != nil: + value = t.UnixMicro() + default: + value = t.UnixNano() + } + switch kind := typ.Kind(); kind { + case Int32, Int64: + col.writeInt64(levels, value) + case Float, Double: + col.writeDouble(levels, t.Sub(time.Unix(0, 0)).Seconds()) + case ByteArray: + col.writeByteArray(levels, t.AppendFormat(nil, time.RFC3339Nano)) + default: + panic(fmt.Sprintf("unsupported physical type for timestamp: %v", kind)) + } +} + +func writeProtoDuration(col ColumnBuffer, levels columnLevels, dur *durationpb.Duration, node Node) { + if dur == nil { + col.writeNull(levels) + return + } + d := dur.AsDuration() + switch kind := node.Type().Kind(); kind { + case Int32, Int64: + col.writeInt64(levels, d.Nanoseconds()) + case Float, Double: + col.writeDouble(levels, d.Seconds()) + case ByteArray: + col.writeByteArray(levels, unsafeByteArrayFromString(d.String())) + default: + panic(fmt.Sprintf("unsupported physical type for duration: %v", kind)) + } +} + +func writeProtoStruct(col ColumnBuffer, levels columnLevels, s *structpb.Struct, node Node) { + b := memory.SliceBuffer[byte]{} + b.Grow(2 * proto.Size(s)) + writeProtoStructJSON(&b, s) + col.writeByteArray(levels, b.Slice()) + b.Reset() +} + +func writeProtoList(col ColumnBuffer, levels columnLevels, l *structpb.ListValue, node Node) { + b := memory.SliceBuffer[byte]{} + b.Grow(2 * proto.Size(l)) + writeProtoListValueJSON(&b, l) + col.writeByteArray(levels, b.Slice()) + b.Reset() +} + +func writeProtoStructJSON(b *memory.SliceBuffer[byte], s *structpb.Struct) { + if s == nil { + b.Append('n', 'u', 'l', 'l') + return + } + + fields := s.GetFields() + if len(fields) == 0 { + b.Append('{', '}') + return + } + + keys := make([]string, 0, 20) + for key := range fields { + keys = append(keys, key) + } + slices.Sort(keys) + + b.AppendValue('{') + for i, key := range keys { + if i > 0 { + b.AppendValue(',') + } + b.AppendFunc(func(buf []byte) []byte { + return jsonlite.AppendQuote(buf, key) + }) + b.AppendValue(':') + writeProtoValueJSON(b, fields[key]) + } + b.AppendValue('}') +} + +func writeProtoValueJSON(b *memory.SliceBuffer[byte], v *structpb.Value) { + switch k := v.GetKind().(type) { + case *structpb.Value_StringValue: + b.AppendFunc(func(buf []byte) []byte { + return jsonlite.AppendQuote(buf, k.StringValue) + }) + case *structpb.Value_BoolValue: + b.AppendFunc(func(buf []byte) []byte { + return strconv.AppendBool(buf, k.BoolValue) + }) + case *structpb.Value_NumberValue: + b.AppendFunc(func(buf []byte) []byte { + return strconv.AppendFloat(buf, k.NumberValue, 'g', -1, 64) + }) + case *structpb.Value_StructValue: + writeProtoStructJSON(b, k.StructValue) + case *structpb.Value_ListValue: + writeProtoListValueJSON(b, k.ListValue) + default: + b.Append('n', 'u', 'l', 'l') + } +} + +func writeProtoListValueJSON(b *memory.SliceBuffer[byte], l *structpb.ListValue) { + if l == nil { + b.Append('n', 'u', 'l', 'l') + return + } + values := l.GetValues() + b.AppendValue('[') + for i, v := range values { + if i > 0 { + b.AppendValue(',') + } + writeProtoValueJSON(b, v) + } + b.AppendValue(']') +} + +func writeProtoAny(col ColumnBuffer, levels columnLevels, a *anypb.Any, node Node) { + if a == nil { + col.writeNull(levels) + return + } + data, err := proto.Marshal(a) + if err != nil { + panic(fmt.Sprintf("failed to marshal anypb.Any: %v", err)) + } + col.writeByteArray(levels, data) +} + +// makeNestedMap creates a nested map structure from a dot-separated path. +// For example, "testproto.ProtoPayload" with value v creates: +// map["testproto"] = map["ProtoPayload"] = v +func makeNestedMap(path string, value any) any { + components := make([]string, 0, 8) + for component := range strings.SplitSeq(path, ".") { + components = append(components, component) + } + + result := value + for i := len(components) - 1; i >= 0; i-- { + result = map[string]any{ + components[i]: result, + } + } + return result +} + +// navigateToNestedGroup walks through a nested group structure following the given path. +// The path is expected to be a dot-separated string (e.g., "testproto.ProtoPayload"). +// Returns the node at the end of the path, or panics if the path doesn't match the schema. +func navigateToNestedGroup(node Node, path string) Node { + for component := range strings.SplitSeq(path, ".") { + var found bool + for _, field := range node.Fields() { + if field.Name() == component { + node, found = field, true + break + } + } + if !found { + panic(fmt.Sprintf("field %q not found in schema while navigating path %q", component, path)) + } + } + return node +} diff --git a/vendor/github.com/parquet-go/parquet-go/column_buffer_proto_any.go b/vendor/github.com/parquet-go/parquet-go/column_buffer_proto_any.go new file mode 100644 index 000000000..0c1664488 --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/column_buffer_proto_any.go @@ -0,0 +1,60 @@ +//go:build !purego + +package parquet + +import ( + "fmt" + "reflect" + "strings" + + "google.golang.org/protobuf/proto" + "google.golang.org/protobuf/reflect/protoreflect" + "google.golang.org/protobuf/types/known/anypb" +) + +func writeProtoAnyToGroup(msg *anypb.Any, columns []ColumnBuffer, levels columnLevels, writers []fieldWriter, node Node, value *reflect.Value) bool { + if msg == nil { + for i := range writers { + w := &writers[i] + w.writeValue(columns, levels, reflect.Value{}) + } + return true + } + + typeURL := msg.GetTypeUrl() + const prefix = "type.googleapis.com/" + if !strings.HasPrefix(typeURL, prefix) { + panic(fmt.Sprintf("invalid type_url %q: expected %q prefix", typeURL, prefix)) + } + path := typeURL[len(prefix):] + _ = navigateToNestedGroup(node, path) + + unmarshaled, err := msg.UnmarshalNew() + if err != nil { + panic(fmt.Sprintf("failed to unmarshal Any: %v", err)) + } + + *value = reflect.ValueOf(makeNestedMap(path, unmarshaled)) + return false +} + +func writeProtoMessageToGroup(msg proto.Message, columns []ColumnBuffer, levels columnLevels, writers []fieldWriter) { + protoMsg := msg.ProtoReflect() + descriptor := protoMsg.Descriptor() + for i := range writers { + w := &writers[i] + protoField := descriptor.Fields().ByName(protoreflect.Name(w.fieldName)) + if protoField == nil || !protoMsg.Has(protoField) { + w.writeValue(columns, levels, reflect.Value{}) + continue + } + protoValue := protoMsg.Get(protoField) + var fieldValue reflect.Value + if protoField.Kind() == protoreflect.MessageKind { + fieldValue = reflect.ValueOf(protoValue.Message().Interface()) + } else { + fieldValue = reflect.ValueOf(protoValue.Interface()) + } + w.writeValue(columns, levels, fieldValue) + } +} diff --git a/vendor/github.com/parquet-go/parquet-go/column_buffer_proto_purego.go b/vendor/github.com/parquet-go/parquet-go/column_buffer_proto_purego.go new file mode 100644 index 000000000..58e35ab64 --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/column_buffer_proto_purego.go @@ -0,0 +1,84 @@ +//go:build purego + +package parquet + +import ( + "fmt" + "iter" + "reflect" + "strings" + + "google.golang.org/protobuf/proto" + "google.golang.org/protobuf/types/known/anypb" +) + +func writeProtoAnyToGroup(msg *anypb.Any, columns []ColumnBuffer, levels columnLevels, writers []fieldWriter, node Node, value *reflect.Value) bool { + if msg == nil { + for i := range writers { + w := &writers[i] + w.writeValue(columns, levels, reflect.Value{}) + } + return true + } + + typeURL := msg.GetTypeUrl() + const prefix = "type.googleapis.com/" + if !strings.HasPrefix(typeURL, prefix) { + panic(fmt.Sprintf("invalid type_url %q: expected %q prefix", typeURL, prefix)) + } + path := typeURL[len(prefix):] + _ = navigateToNestedGroup(node, path) + + unmarshaled, err := msg.UnmarshalNew() + if err != nil { + panic(fmt.Sprintf("failed to unmarshal Any: %v", err)) + } + + *value = reflect.ValueOf(makeNestedMap(path, unmarshaled)) + return false +} + +func writeProtoMessageToGroup(msg proto.Message, columns []ColumnBuffer, levels columnLevels, writers []fieldWriter) { + msgValue := reflect.ValueOf(msg) + if msgValue.Kind() == reflect.Ptr { + msgValue = msgValue.Elem() + } + for i := range writers { + w := &writers[i] + fieldValue := findFieldByProtoName(msgValue, w.fieldName) + w.writeValue(columns, levels, fieldValue) + } +} + +func findFieldByProtoName(structValue reflect.Value, protoName string) reflect.Value { + structType := structValue.Type() + for i := range structType.NumField() { + f := structType.Field(i) + if tag := f.Tag.Get("protobuf"); tag != "" && tag != "-" { + if name := parseProtoNameFromTag(tag); name == protoName { + return structValue.Field(i) + } + } + } + return reflect.Value{} +} + +func parseProtoNameFromTag(tag string) string { + for name, value := range parseProtoStructTag(tag) { + if name == "name" { + return value + } + } + return "" +} + +func parseProtoStructTag(tag string) iter.Seq2[string, string] { + return func(yield func(string, string) bool) { + for part := range strings.SplitSeq(tag, ",") { + name, value, _ := strings.Cut(part, "=") + if !yield(name, value) { + return + } + } + } +} diff --git a/vendor/github.com/parquet-go/parquet-go/column_buffer_purego.go b/vendor/github.com/parquet-go/parquet-go/column_buffer_purego.go new file mode 100644 index 000000000..6f9996f1c --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/column_buffer_purego.go @@ -0,0 +1,30 @@ +//go:build !amd64 || purego + +package parquet + +import "github.com/parquet-go/parquet-go/sparse" + +func broadcastValueInt32(dst []int32, src int8) { + value := 0x01010101 * int32(src) + for i := range dst { + dst[i] = value + } +} + +func broadcastRangeInt32(dst []int32, base int32) { + for i := range dst { + dst[i] = base + int32(i) + } +} + +func writePointersBE128(values [][16]byte, rows sparse.Array) { + for i := range values { + p := *(**[16]byte)(rows.Index(i)) + + if p != nil { + values[i] = *p + } else { + values[i] = [16]byte{} + } + } +} diff --git a/vendor/github.com/parquet-go/parquet-go/column_buffer_reflect.go b/vendor/github.com/parquet-go/parquet-go/column_buffer_reflect.go new file mode 100644 index 000000000..c472f533c --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/column_buffer_reflect.go @@ -0,0 +1,1055 @@ +package parquet + +import ( + "bytes" + "cmp" + "encoding/binary" + "encoding/json" + "fmt" + "maps" + "math" + "math/big" + "math/bits" + "reflect" + "sort" + "strings" + "sync/atomic" + "time" + "unsafe" + + "github.com/google/uuid" + "github.com/parquet-go/jsonlite" + "github.com/parquet-go/parquet-go/deprecated" + "github.com/parquet-go/parquet-go/internal/memory" + "github.com/parquet-go/parquet-go/sparse" + "github.com/twpayne/go-geom" + "google.golang.org/protobuf/proto" + "google.golang.org/protobuf/reflect/protoreflect" + "google.golang.org/protobuf/types/known/anypb" + "google.golang.org/protobuf/types/known/durationpb" + "google.golang.org/protobuf/types/known/structpb" + "google.golang.org/protobuf/types/known/timestamppb" + "google.golang.org/protobuf/types/known/wrapperspb" +) + +// isNullValue determines if a reflect.Value represents a null value for parquet encoding. +// This handles various types that can represent null including: +// - Invalid reflect values +// - Nil pointers/interfaces/slices/maps +// - json.RawMessage containing "null" +// - *jsonlite.Value with Kind == jsonlite.Null +// - *structpb.Value with NullValue kind +// - Zero values for value types (bool, int, float, string, struct, etc.) +func isNullValue(value reflect.Value) bool { + switch value.Kind() { + case reflect.Invalid: + return true + + case reflect.Pointer, reflect.Interface: + if value.IsNil() { + return true + } + switch v := value.Interface().(type) { + case *jsonlite.Value: + return v.Kind() == jsonlite.Null + case *structpb.Value: + _, isNull := v.GetKind().(*structpb.Value_NullValue) + return isNull + } + return false + + case reflect.Slice: + if value.IsNil() { + return true + } + if value.Type() == reflect.TypeFor[json.RawMessage]() { + return string(value.Bytes()) == "null" + } + return false + + case reflect.Map: + return value.IsNil() + + default: + return value.IsZero() + } +} + +type anymap interface { + entries() (keys, values sparse.Array) +} + +type fieldWriter struct { + fieldName string + writeValue writeValueFunc +} + +type gomap[K cmp.Ordered] struct { + keys []K + vals reflect.Value // slice + swap func(int, int) + size uintptr +} + +func (m *gomap[K]) Len() int { return len(m.keys) } + +func (m *gomap[K]) Less(i, j int) bool { return cmp.Compare(m.keys[i], m.keys[j]) < 0 } + +func (m *gomap[K]) Swap(i, j int) { + m.keys[i], m.keys[j] = m.keys[j], m.keys[i] + m.swap(i, j) +} + +func (m *gomap[K]) entries() (keys, values sparse.Array) { + return makeArrayFromSlice(m.keys), makeArray(m.vals.UnsafePointer(), m.Len(), m.size) +} + +type reflectMap struct { + keys reflect.Value // slice + vals reflect.Value // slice + numKeys int + keySize uintptr + valSize uintptr +} + +func (m *reflectMap) entries() (keys, values sparse.Array) { + return makeArray(m.keys.UnsafePointer(), m.numKeys, m.keySize), makeArray(m.vals.UnsafePointer(), m.numKeys, m.valSize) +} + +func makeMapFuncOf(mapType reflect.Type) func(reflect.Value) anymap { + switch mapType.Key().Kind() { + case reflect.Int: + return makeMapFunc[int](mapType) + case reflect.Int8: + return makeMapFunc[int8](mapType) + case reflect.Int16: + return makeMapFunc[int16](mapType) + case reflect.Int32: + return makeMapFunc[int32](mapType) + case reflect.Int64: + return makeMapFunc[int64](mapType) + case reflect.Uint: + return makeMapFunc[uint](mapType) + case reflect.Uint8: + return makeMapFunc[uint8](mapType) + case reflect.Uint16: + return makeMapFunc[uint16](mapType) + case reflect.Uint32: + return makeMapFunc[uint32](mapType) + case reflect.Uint64: + return makeMapFunc[uint64](mapType) + case reflect.Uintptr: + return makeMapFunc[uintptr](mapType) + case reflect.Float32: + return makeMapFunc[float32](mapType) + case reflect.Float64: + return makeMapFunc[float64](mapType) + case reflect.String: + return makeMapFunc[string](mapType) + } + + keyType := mapType.Key() + valType := mapType.Elem() + + mapBuffer := &reflectMap{ + keySize: keyType.Size(), + valSize: valType.Size(), + } + + keySliceType := reflect.SliceOf(keyType) + valSliceType := reflect.SliceOf(valType) + return func(mapValue reflect.Value) anymap { + length := mapValue.Len() + + if !mapBuffer.keys.IsValid() || mapBuffer.keys.Len() < length { + capacity := 1 << bits.Len(uint(length)) + mapBuffer.keys = reflect.MakeSlice(keySliceType, capacity, capacity) + mapBuffer.vals = reflect.MakeSlice(valSliceType, capacity, capacity) + } + + mapBuffer.numKeys = length + for i, mapIter := 0, mapValue.MapRange(); mapIter.Next(); i++ { + mapBuffer.keys.Index(i).SetIterKey(mapIter) + mapBuffer.vals.Index(i).SetIterValue(mapIter) + } + + return mapBuffer + } +} + +func makeMapFunc[K cmp.Ordered](mapType reflect.Type) func(reflect.Value) anymap { + keyType := mapType.Key() + valType := mapType.Elem() + valSliceType := reflect.SliceOf(valType) + mapBuffer := &gomap[K]{size: valType.Size()} + return func(mapValue reflect.Value) anymap { + length := mapValue.Len() + + if cap(mapBuffer.keys) < length { + capacity := 1 << bits.Len(uint(length)) + mapBuffer.keys = make([]K, capacity) + mapBuffer.vals = reflect.MakeSlice(valSliceType, capacity, capacity) + mapBuffer.swap = reflect.Swapper(mapBuffer.vals.Interface()) + } + + mapBuffer.keys = mapBuffer.keys[:length] + for i, mapIter := 0, mapValue.MapRange(); mapIter.Next(); i++ { + reflect.NewAt(keyType, unsafe.Pointer(&mapBuffer.keys[i])).Elem().SetIterKey(mapIter) + mapBuffer.vals.Index(i).SetIterValue(mapIter) + } + + sort.Sort(mapBuffer) + return mapBuffer + } +} + +// writeValueFunc is a function that writes a single reflect.Value to a set of +// column buffers. +// Panics if the value cannot be written (similar to reflect package behavior). +type writeValueFunc func([]ColumnBuffer, columnLevels, reflect.Value) + +// timeOfDayNanos returns the nanoseconds since midnight for the given time. +func timeOfDayNanos(t time.Time) int64 { + m := nearestMidnightLessThan(t) + return t.Sub(m).Nanoseconds() +} + +func writeTime(col ColumnBuffer, levels columnLevels, t time.Time, node Node) { + typ := node.Type() + + if logicalType := typ.LogicalType(); logicalType != nil { + switch { + case logicalType.Timestamp != nil: + // TIMESTAMP logical type -> write to int64 + unit := logicalType.Timestamp.Unit + var val int64 + switch { + case unit.Millis != nil: + val = t.UnixMilli() + case unit.Micros != nil: + val = t.UnixMicro() + default: + val = t.UnixNano() + } + col.writeInt64(levels, val) + return + + case logicalType.Date != nil: + // DATE logical type -> write to int32 + col.writeInt32(levels, int32(daysSinceUnixEpoch(t))) + return + + case logicalType.Time != nil: + // TIME logical type -> write time of day + unit := logicalType.Time.Unit + nanos := timeOfDayNanos(t) + switch { + case unit.Millis != nil: + col.writeInt32(levels, int32(nanos/1e6)) + case unit.Micros != nil: + col.writeInt64(levels, nanos/1e3) + default: + col.writeInt64(levels, nanos) + } + return + } + } + + // No time logical type - use physical type + switch typ.Kind() { + case Int32: + // int32 without logical type -> days since epoch + col.writeInt32(levels, int32(daysSinceUnixEpoch(t))) + case Int64: + // int64 without logical type -> nanoseconds since epoch + col.writeInt64(levels, t.UnixNano()) + case Float: + // float -> fractional seconds since epoch + col.writeFloat(levels, float32(float64(t.UnixNano())/1e9)) + case Double: + // double -> fractional seconds since epoch + col.writeDouble(levels, float64(t.UnixNano())/1e9) + case ByteArray: + // byte array -> RFC3339Nano + s := t.Format(time.RFC3339Nano) + col.writeByteArray(levels, unsafe.Slice(unsafe.StringData(s), len(s))) + default: + panic(fmt.Sprintf("cannot write time.Time to column with physical type %v", typ)) + } +} + +func writeDuration(col ColumnBuffer, levels columnLevels, d time.Duration, node Node) { + typ := node.Type() + + if logicalType := typ.LogicalType(); logicalType != nil && logicalType.Time != nil { + // TIME logical type + unit := logicalType.Time.Unit + switch { + case unit.Millis != nil: + col.writeInt32(levels, int32(d.Milliseconds())) + case unit.Micros != nil: + col.writeInt64(levels, d.Microseconds()) + default: + col.writeInt64(levels, d.Nanoseconds()) + } + return + } + + // No TIME logical type - use physical type + switch typ.Kind() { + case Int32: + panic("cannot write time.Duration to int32 column without TIME logical type") + case Int64: + // int64 -> nanoseconds + col.writeInt64(levels, d.Nanoseconds()) + case Float: + // float -> seconds + col.writeFloat(levels, float32(d.Seconds())) + case Double: + // double -> seconds + col.writeDouble(levels, d.Seconds()) + case ByteArray: + // byte array -> String() + s := d.String() + col.writeByteArray(levels, unsafe.Slice(unsafe.StringData(s), len(s))) + default: + panic(fmt.Sprintf("cannot write time.Duration to column with physical type %v", typ)) + } +} + +// writeValueFuncOf constructs a function that writes reflect.Values to column buffers. +// It follows the deconstructFuncOf pattern, recursively building functions for the schema tree. +// Returns (nextColumnIndex, writeFunc). +func writeValueFuncOf(columnIndex uint16, node Node) (uint16, writeValueFunc) { + switch { + case node.Optional(): + return writeValueFuncOfOptional(columnIndex, node) + case node.Repeated(): + return writeValueFuncOfRepeated(columnIndex, Required(node)) + case isList(node): + return writeValueFuncOfList(columnIndex, node) + case isMap(node): + return writeValueFuncOfMap(columnIndex, node) + case isVariant(node): + return writeValueFuncOfVariant(columnIndex, node) + default: + return writeValueFuncOfRequired(columnIndex, node) + } +} + +func writeValueFuncOfOptional(columnIndex uint16, node Node) (uint16, writeValueFunc) { + nextColumnIndex, writeValue := writeValueFuncOf(columnIndex, Required(node)) + return nextColumnIndex, func(columns []ColumnBuffer, levels columnLevels, value reflect.Value) { + if isNullValue(value) { + writeValue(columns, levels, value) + } else { + levels.definitionLevel++ + writeValue(columns, levels, value) + } + } +} + +func writeValueFuncOfRepeated(columnIndex uint16, node Node) (uint16, writeValueFunc) { + nextColumnIndex, writeValue := writeValueFuncOf(columnIndex, node) + return nextColumnIndex, func(columns []ColumnBuffer, levels columnLevels, value reflect.Value) { + writeRepatedValue: + if !value.IsValid() { + writeValue(columns, levels, reflect.Value{}) + return + } + + switch msg := value.Interface().(type) { + case *jsonlite.Value: + writeJSONToRepeated(columns, levels, msg, writeValue) + return + + case json.RawMessage: + val, err := jsonParse(msg) + if err != nil { + panic(fmt.Errorf("failed to parse JSON: %w", err)) + } + writeJSONToRepeated(columns, levels, val, writeValue) + return + + case *structpb.Struct: + if msg == nil { + writeValue(columns, levels, reflect.Value{}) + return + } + levels.repetitionDepth++ + levels.definitionLevel++ + writeValue(columns, levels, value) + return + + case *structpb.ListValue: + n := len(msg.GetValues()) + if n == 0 { + writeValue(columns, levels, reflect.Value{}) + return + } + + levels.repetitionDepth++ + levels.definitionLevel++ + + for _, v := range msg.GetValues() { + writeValue(columns, levels, structpbValueToReflectValue(v)) + levels.repetitionLevel = levels.repetitionDepth + } + return + + case protoreflect.List: + n := msg.Len() + if n == 0 { + writeValue(columns, levels, reflect.Value{}) + return + } + + levels.repetitionDepth++ + levels.definitionLevel++ + + for i := range n { + var e = msg.Get(i) + var v reflect.Value + if e.IsValid() { + v = reflect.ValueOf(e.Interface()) + } + writeValue(columns, levels, v) + levels.repetitionLevel = levels.repetitionDepth + } + return + } + + switch value.Kind() { + case reflect.Interface, reflect.Pointer: + if value.IsNil() { + writeValue(columns, levels, reflect.Value{}) + return + } + value = value.Elem() + goto writeRepatedValue + + case reflect.Slice, reflect.Array: + n := value.Len() + if n == 0 { + writeValue(columns, levels, reflect.Value{}) + return + } + + levels.repetitionDepth++ + levels.definitionLevel++ + + for i := range n { + writeValue(columns, levels, value.Index(i)) + levels.repetitionLevel = levels.repetitionDepth + } + + default: + levels.repetitionDepth++ + levels.definitionLevel++ + + // If this is a repeated group with a single field, and the value is a scalar, + // wrap the scalar into a struct with that field name. + if !node.Leaf() && value.IsValid() && value.Kind() != reflect.Struct && value.Kind() != reflect.Map { + fields := Required(node).Fields() + if len(fields) == 1 { + field := fields[0] + fieldType := field.GoType() + fieldName := field.Name() + + if value.Type().AssignableTo(fieldType) || value.Type().ConvertibleTo(fieldType) { + structType := reflect.StructOf([]reflect.StructField{ + {Name: fieldName, Type: fieldType}, + }) + wrappedValue := reflect.New(structType).Elem() + + if value.Type().AssignableTo(fieldType) { + wrappedValue.Field(0).Set(value) + } else { + wrappedValue.Field(0).Set(value.Convert(fieldType)) + } + + value = wrappedValue + } + } + } + + writeValue(columns, levels, value) + } + } +} + +func writeValueFuncOfRequired(columnIndex uint16, node Node) (uint16, writeValueFunc) { + switch { + case node.Leaf(): + return writeValueFuncOfLeaf(columnIndex, node) + default: + return writeValueFuncOfGroup(columnIndex, node) + } +} + +func writeValueFuncOfList(columnIndex uint16, node Node) (uint16, writeValueFunc) { + elem := listElementOf(node) + // Preserve optionality of list elements when writing via reflection paths. + if elem.Optional() { + return writeValueFuncOfRepeated(columnIndex, elem) + } + return writeValueFuncOf(columnIndex, Repeated(elem)) +} + +func writeValueFuncOfMap(columnIndex uint16, node Node) (uint16, writeValueFunc) { + keyValue := mapKeyValueOf(node) + keyValueType := keyValue.GoType() + keyValueElem := keyValueType.Elem() + nextColumnIndex, writeValue := writeValueFuncOf(columnIndex, schemaOf(keyValueElem)) + zeroKeyValue := reflect.Zero(keyValueElem) + + return nextColumnIndex, func(columns []ColumnBuffer, levels columnLevels, mapValue reflect.Value) { + if mapValue.Kind() == reflect.Pointer { + // Return early in the nil case to avoid dealing later with a zero value returned by Elem() + if mapValue.IsNil() { + writeValue(columns, levels, zeroKeyValue) + return + } + mapValue = mapValue.Elem() + } + + // Check for invalid or nil map first to avoid panic on Interface() or Len() + if !mapValue.IsValid() || mapValue.IsNil() { + writeValue(columns, levels, zeroKeyValue) + return + } + + switch m := mapValue.Interface().(type) { + case protoreflect.Map: + n := m.Len() + if n == 0 { + writeValue(columns, levels, zeroKeyValue) + return + } + + levels.repetitionDepth++ + levels.definitionLevel++ + + // Determine the key-value struct type once, using the first + // element to discover the actual key/value Go types. + var kvType reflect.Type + for mapKey, mapVal := range m.Range { + kvType = makeKeyValueType(keyValueElem, reflect.TypeOf(mapKey.Interface()), reflect.TypeOf(mapVal.Interface())) + break + } + if kvType == nil { + kvType = keyValueElem + } + + elem := reflect.New(kvType).Elem() + k := elem.Field(0) + v := elem.Field(1) + + for mapKey, mapVal := range m.Range { + k.Set(reflect.ValueOf(mapKey.Interface()).Convert(k.Type())) + v.Set(reflect.ValueOf(mapVal.Interface()).Convert(v.Type())) + writeValue(columns, levels, elem) + levels.repetitionLevel = levels.repetitionDepth + } + return + } + + if mapValue.Len() == 0 { + writeValue(columns, levels, zeroKeyValue) + return + } + + levels.repetitionDepth++ + levels.definitionLevel++ + + mapType := mapValue.Type() + mapKey := reflect.New(mapType.Key()).Elem() + mapElem := reflect.New(mapType.Elem()).Elem() + + elem := reflect.New(makeKeyValueType(keyValueElem, mapType.Key(), mapType.Elem())).Elem() + k := elem.Field(0) + v := elem.Field(1) + + for it := mapValue.MapRange(); it.Next(); { + mapKey.SetIterKey(it) + mapElem.SetIterValue(it) + k.Set(mapKey.Convert(k.Type())) + v.Set(mapElem.Convert(v.Type())) + writeValue(columns, levels, elem) + levels.repetitionLevel = levels.repetitionDepth + } + } +} + +var structFieldsCache atomic.Value // map[reflect.Type]map[string][]int + +func writeValueFuncOfGroup(columnIndex uint16, node Node) (uint16, writeValueFunc) { + fields := node.Fields() + writers := make([]fieldWriter, len(fields)) + for i, field := range fields { + writers[i].fieldName = field.Name() + columnIndex, writers[i].writeValue = writeValueFuncOf(columnIndex, field) + } + + return columnIndex, func(columns []ColumnBuffer, levels columnLevels, value reflect.Value) { + writeGroupValue: + if !value.IsValid() { + for i := range writers { + w := &writers[i] + w.writeValue(columns, levels, reflect.Value{}) + } + return + } + + switch t := value.Type(); t.Kind() { + case reflect.Map: + switch { + case t.ConvertibleTo(reflect.TypeFor[map[string]string]()): + m := value.Convert(reflect.TypeFor[map[string]string]()).Interface().(map[string]string) + v := new(string) + for i := range writers { + w := &writers[i] + *v = m[w.fieldName] + w.writeValue(columns, levels, reflect.ValueOf(v).Elem()) + } + + case t.ConvertibleTo(reflect.TypeFor[map[string]any]()): + m := value.Convert(reflect.TypeFor[map[string]any]()).Interface().(map[string]any) + for i := range writers { + w := &writers[i] + v := m[w.fieldName] + w.writeValue(columns, levels, reflect.ValueOf(v)) + } + + default: + for i := range writers { + w := &writers[i] + fieldName := reflect.ValueOf(&w.fieldName).Elem() + fieldValue := value.MapIndex(fieldName) + w.writeValue(columns, levels, fieldValue) + } + } + + case reflect.Struct: + cachedFields, _ := structFieldsCache.Load().(map[reflect.Type]map[string][]int) + structFields, ok := cachedFields[t] + if !ok { + visibleStructFields := reflect.VisibleFields(t) + cachedFieldsBefore := cachedFields + structFields = make(map[string][]int, len(visibleStructFields)) + cachedFields = make(map[reflect.Type]map[string][]int, len(cachedFieldsBefore)+1) + cachedFields[t] = structFields + maps.Copy(cachedFields, cachedFieldsBefore) + + for _, visibleStructField := range visibleStructFields { + name := visibleStructField.Name + if tag, ok := visibleStructField.Tag.Lookup("parquet"); ok { + if tagName, _, _ := strings.Cut(tag, ","); tagName != "" { + name = tagName + } + } + structFields[name] = visibleStructField.Index + } + + structFieldsCache.Store(cachedFields) + } + + for i := range writers { + w := &writers[i] + fieldValue := reflect.Value{} + fieldIndex, ok := structFields[w.fieldName] + if ok { + fieldValue = value.FieldByIndex(fieldIndex) + } + w.writeValue(columns, levels, fieldValue) + } + + case reflect.Pointer, reflect.Interface: + if value.IsNil() { + value = reflect.Value{} + goto writeGroupValue + } + + switch msg := value.Interface().(type) { + case *jsonlite.Value: + writeJSONToGroup(columns, levels, msg, node, writers) + case *structpb.Struct: + var fields map[string]*structpb.Value + if msg != nil { + fields = msg.Fields + } + for i := range writers { + w := &writers[i] + v := structpbValueToReflectValue(fields[w.fieldName]) + w.writeValue(columns, levels, v) + } + case *anypb.Any: + if writeProtoAnyToGroup(msg, columns, levels, writers, node, &value) { + return + } + goto writeGroupValue + case proto.Message: + writeProtoMessageToGroup(msg, columns, levels, writers) + default: + value = value.Elem() + goto writeGroupValue + } + + case reflect.Slice: + if t == reflect.TypeFor[json.RawMessage]() { + val, err := jsonParse(value.Bytes()) + if err != nil { + panic(fmt.Errorf("failed to parse JSON: %w", err)) + } + writeJSONToGroup(columns, levels, val, node, writers) + } else { + value = reflect.Value{} + goto writeGroupValue + } + + default: + value = reflect.Value{} + goto writeGroupValue + } + } +} + +func writeValueFuncOfLeaf(columnIndex uint16, node Node) (uint16, writeValueFunc) { + if columnIndex > MaxColumnIndex { + panic("row cannot be written because it has too many columns") + } + return columnIndex + 1, func(columns []ColumnBuffer, levels columnLevels, value reflect.Value) { + col := columns[columnIndex] + writeValue: + switch value.Kind() { + case reflect.Invalid: + col.writeNull(levels) + return + + case reflect.Pointer, reflect.Interface: + if value.IsNil() { + col.writeNull(levels) + return + } + switch msg := value.Interface().(type) { + case *jsonlite.Value: + writeJSONToLeaf(col, levels, msg, node) + case *json.Number: + writeJSONNumber(col, levels, *msg, node) + case *time.Time: + writeTime(col, levels, *msg, node) + case *time.Duration: + writeDuration(col, levels, *msg, node) + case *timestamppb.Timestamp: + writeProtoTimestamp(col, levels, msg, node) + case *durationpb.Duration: + writeProtoDuration(col, levels, msg, node) + case *wrapperspb.BoolValue: + col.writeBoolean(levels, msg.GetValue()) + case *wrapperspb.Int32Value: + col.writeInt32(levels, msg.GetValue()) + case *wrapperspb.Int64Value: + col.writeInt64(levels, msg.GetValue()) + case *wrapperspb.UInt32Value: + col.writeInt32(levels, int32(msg.GetValue())) + case *wrapperspb.UInt64Value: + col.writeInt64(levels, int64(msg.GetValue())) + case *wrapperspb.FloatValue: + col.writeFloat(levels, msg.GetValue()) + case *wrapperspb.DoubleValue: + col.writeDouble(levels, msg.GetValue()) + case *wrapperspb.StringValue: + col.writeByteArray(levels, unsafeByteArrayFromString(msg.GetValue())) + case *wrapperspb.BytesValue: + col.writeByteArray(levels, msg.GetValue()) + case *structpb.Struct: + writeProtoStruct(col, levels, msg, node) + case *structpb.ListValue: + writeProtoList(col, levels, msg, node) + case *anypb.Any: + writeProtoAny(col, levels, msg, node) + case geom.T: + writeGeometry(col, levels, msg, node) + case *big.Float: + writeBigFloat(col, levels, msg, node) + default: + value = value.Elem() + goto writeValue + } + return + + case reflect.Bool: + col.writeBoolean(levels, value.Bool()) + return + + case reflect.Int8, reflect.Int16, reflect.Int32: + col.writeInt32(levels, int32(value.Int())) + return + + case reflect.Int: + col.writeInt64(levels, value.Int()) + return + + case reflect.Int64: + if value.Type() == reflect.TypeFor[time.Duration]() { + writeDuration(col, levels, time.Duration(value.Int()), node) + } else { + col.writeInt64(levels, value.Int()) + } + return + + case reflect.Uint8, reflect.Uint16, reflect.Uint32: + col.writeInt32(levels, int32(value.Uint())) + return + + case reflect.Uint, reflect.Uint64: + col.writeInt64(levels, int64(value.Uint())) + return + + case reflect.Float32: + typ := node.Type() + logicalType := typ.LogicalType() + if logicalType != nil && logicalType.Decimal != nil { + decimalValue(col, levels, typ, value, logicalType.Decimal.Scale) + return + } + col.writeFloat(levels, float32(value.Float())) + return + + case reflect.Float64: + typ := node.Type() + logicalType := typ.LogicalType() + if logicalType != nil && logicalType.Decimal != nil { + decimalValue(col, levels, typ, value, logicalType.Decimal.Scale) + return + } + + col.writeDouble(levels, value.Float()) + return + + case reflect.String: + v := value.String() + switch value.Type() { + case reflect.TypeFor[json.Number](): + writeJSONNumber(col, levels, json.Number(v), node) + default: + typ := node.Type() + logicalType := typ.LogicalType() + if logicalType != nil && logicalType.UUID != nil { + writeUUID(col, levels, v, typ) + return + } + col.writeByteArray(levels, unsafeByteArrayFromString(v)) + } + return + + case reflect.Slice: + if t := value.Type(); t.Elem().Kind() == reflect.Uint8 { + switch t { + case reflect.TypeFor[json.RawMessage](): + val, err := jsonParse(value.Bytes()) + if err != nil { + panic(fmt.Errorf("failed to parse JSON: %w", err)) + } + writeJSONToLeaf(col, levels, val, node) + default: + col.writeByteArray(levels, value.Bytes()) + } + return + } + + case reflect.Array: + col.writeByteArray(levels, value.Bytes()) + return + + case reflect.Struct: + switch v := value.Interface().(type) { + case time.Time: + writeTime(col, levels, v, node) + return + case deprecated.Int96: + col.writeInt96(levels, v) + return + case Interval: + writeInterval(col, levels, v) + return + } + } + + if node.Type().Kind() != ByteArray { + panic(fmt.Sprintf("cannot write value of type %s to leaf column", value.Type())) + } + + if node.Optional() && isNullValue(value) { + col.writeNull(levels) + return + } + + b := memory.SliceBuffer[byte]{} + w := memory.SliceWriter{Buffer: &b} + defer b.Reset() + + enc := json.NewEncoder(w) + enc.SetEscapeHTML(false) + + if err := enc.Encode(value.Interface()); err != nil { + panic(err) + } + + data := b.Slice() + col.writeByteArray(levels, data[:len(data)-1]) + } +} + +func structpbValueToReflectValue(v *structpb.Value) reflect.Value { + switch kind := v.GetKind().(type) { + case nil: + return reflect.Value{} + case *structpb.Value_NullValue: + return reflect.Value{} + case *structpb.Value_NumberValue: + return reflect.ValueOf(&kind.NumberValue) + case *structpb.Value_StringValue: + return reflect.ValueOf(&kind.StringValue) + case *structpb.Value_BoolValue: + return reflect.ValueOf(&kind.BoolValue) + case *structpb.Value_StructValue: + return reflect.ValueOf(kind.StructValue) + case *structpb.Value_ListValue: + return reflect.ValueOf(kind.ListValue) + default: + panic(fmt.Sprintf("unsupported structpb.Value kind: %T", kind)) + } +} + +func unsafeByteArrayFromString(s string) []byte { + return unsafe.Slice(unsafe.StringData(s), len(s)) +} + +func writeUUID(col ColumnBuffer, levels columnLevels, str string, typ Type) { + if typ.Kind() != FixedLenByteArray || typ.Length() != 16 { + panic(fmt.Errorf("cannot write UUID string to non-FIXED_LEN_BYTE_ARRAY(16) column: %q", str)) + } + parsedUUID, err := uuid.Parse(str) + if err != nil { + panic(fmt.Errorf("cannot parse string %q as UUID: %w", str, err)) + } + buf := memory.SliceBuffer[byte]{} + buf.Grow(16) + buf.Append(parsedUUID[:]...) + col.writeByteArray(levels, buf.Slice()) + buf.Reset() +} + +func writeInterval(col ColumnBuffer, levels columnLevels, iv Interval) { + var buf [12]byte + binary.LittleEndian.PutUint32(buf[0:4], iv.Months) + binary.LittleEndian.PutUint32(buf[4:8], iv.Days) + binary.LittleEndian.PutUint32(buf[8:12], iv.Milliseconds) + col.writeByteArray(levels, buf[:]) +} + +func decimalValue(col ColumnBuffer, levels columnLevels, typ Type, value reflect.Value, scale int32) { + val := int64(math.Round(value.Float() * math.Pow10(int(scale)))) + switch typ.Kind() { + case Int32: + col.writeInt32(levels, int32(val)) + case Int64: + col.writeInt64(levels, val) + case ByteArray: + col.writeByteArray(levels, numberToByteArray(val)) + } +} + +func numberToByteArray(data any) []byte { + var buf bytes.Buffer + err := binary.Write(&buf, binary.BigEndian, data) + if err != nil { + panic(err) + } + return buf.Bytes() +} + +func writeBigFloat(col ColumnBuffer, levels columnLevels, f *big.Float, node Node) { + typ := node.Type() + logicalType := typ.LogicalType() + if logicalType == nil || logicalType.Decimal == nil { + panic("writeBigFloat requires a decimal logical type") + } + + scale := int(logicalType.Decimal.Scale) + // Compute minimum precision needed: decimal precision * log2(10) ≈ precision * 3.32 + // We use precision * 4 + 64 for safety margin + minPrec := uint(logicalType.Decimal.Precision)*4 + 64 + prec := max(f.Prec(), minPrec) + scaleFactor := new(big.Float).SetPrec(prec) + scaleFactor.SetInt(new(big.Int).Exp(big.NewInt(10), big.NewInt(int64(scale)), nil)) + scaled := new(big.Float).SetPrec(prec).Mul(f, scaleFactor) + // Round to nearest integer (add 0.5 and truncate for positive, subtract 0.5 for negative) + half := new(big.Float).SetPrec(prec).SetFloat64(0.5) + if scaled.Sign() >= 0 { + scaled.Add(scaled, half) + } else { + scaled.Sub(scaled, half) + } + unscaled, _ := scaled.Int(nil) + + b := bigIntToByteArray(unscaled) + if typ.Kind() == FixedLenByteArray { + b = padToFixedLen(b, typ.Length(), unscaled.Sign() < 0) + } + col.writeByteArray(levels, b) +} + +func bigIntToByteArray(i *big.Int) []byte { + if i.Sign() >= 0 { + b := i.Bytes() + // Add leading zero byte if high bit is set to avoid being interpreted as negative + if len(b) > 0 && b[0]&0x80 != 0 { + b = append([]byte{0}, b...) + } + return b + } + // Negative: convert to two's complement + // Get the absolute value bytes + abs := new(big.Int).Abs(i) + b := abs.Bytes() + // Add a leading zero byte to ensure we have room for sign + if len(b) == 0 || b[0]&0x80 != 0 { + b = append([]byte{0}, b...) + } + // Invert all bits + for j := range b { + b[j] = ^b[j] + } + // Add 1 + carry := byte(1) + for j := len(b) - 1; j >= 0 && carry > 0; j-- { + sum := b[j] + carry + b[j] = sum + if sum != 0 { + carry = 0 + } + } + return b +} + +func padToFixedLen(b []byte, length int, negative bool) []byte { + if len(b) == length { + return b + } + if len(b) > length { + panic(fmt.Sprintf("decimal value requires %d bytes but fixed length is %d", len(b), length)) + } + padByte := byte(0x00) + if negative { + padByte = 0xFF + } + result := make([]byte, length) + padding := length - len(b) + for i := range padding { + result[i] = padByte + } + copy(result[padding:], b) + return result +} diff --git a/vendor/github.com/parquet-go/parquet-go/column_buffer_repeated.go b/vendor/github.com/parquet-go/parquet-go/column_buffer_repeated.go new file mode 100644 index 000000000..9df383703 --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/column_buffer_repeated.go @@ -0,0 +1,451 @@ +package parquet + +import ( + "bytes" + "io" + "slices" + + "github.com/parquet-go/parquet-go/deprecated" + "github.com/parquet-go/parquet-go/internal/bytealg" + "github.com/parquet-go/parquet-go/internal/memory" + "github.com/parquet-go/parquet-go/sparse" +) + +// repeatedColumnBuffer is an implementation of the ColumnBuffer interface used +// as a wrapper to an underlying ColumnBuffer to manage the creation of +// repetition levels, definition levels, and map rows to the region of the +// underlying buffer that contains their sequence of values. +// +// Null values are not written to the underlying column; instead, the buffer +// tracks offsets of row values in the column, null row values are represented +// by the value -1 and a definition level less than the max. +// +// This column buffer type is used for all leaf columns that have a non-zero +// max repetition level, which may be because the column or one of its parent(s) +// are marked repeated. +type repeatedColumnBuffer struct { + base ColumnBuffer + reordered bool + maxRepetitionLevel byte + maxDefinitionLevel byte + rows []offsetMapping + repetitionLevels memory.SliceBuffer[byte] + definitionLevels memory.SliceBuffer[byte] + buffer []Value + reordering *repeatedColumnBuffer + nullOrdering nullOrdering +} + +// The offsetMapping type maps the logical offset of rows within the repetition +// and definition levels, to the base offsets in the underlying column buffers +// where the non-null values have been written. +type offsetMapping struct { + offset uint32 + baseOffset uint32 +} + +func newRepeatedColumnBuffer(base ColumnBuffer, maxRepetitionLevel, maxDefinitionLevel byte, nullOrdering nullOrdering) *repeatedColumnBuffer { + return &repeatedColumnBuffer{ + base: base, + maxRepetitionLevel: maxRepetitionLevel, + maxDefinitionLevel: maxDefinitionLevel, + nullOrdering: nullOrdering, + } +} + +func (col *repeatedColumnBuffer) Clone() ColumnBuffer { + return &repeatedColumnBuffer{ + base: col.base.Clone(), + reordered: col.reordered, + maxRepetitionLevel: col.maxRepetitionLevel, + maxDefinitionLevel: col.maxDefinitionLevel, + rows: slices.Clone(col.rows), + repetitionLevels: col.repetitionLevels.Clone(), + definitionLevels: col.definitionLevels.Clone(), + nullOrdering: col.nullOrdering, + } +} + +func (col *repeatedColumnBuffer) Type() Type { + return col.base.Type() +} + +func (col *repeatedColumnBuffer) NumValues() int64 { + return int64(col.definitionLevels.Len()) +} + +func (col *repeatedColumnBuffer) ColumnIndex() (ColumnIndex, error) { + return columnIndexOfNullable(col.base, col.maxDefinitionLevel, col.definitionLevels.Slice()) +} + +func (col *repeatedColumnBuffer) OffsetIndex() (OffsetIndex, error) { + return col.base.OffsetIndex() +} + +func (col *repeatedColumnBuffer) BloomFilter() BloomFilter { + return col.base.BloomFilter() +} + +func (col *repeatedColumnBuffer) Dictionary() Dictionary { + return col.base.Dictionary() +} + +func (col *repeatedColumnBuffer) Column() int { + return col.base.Column() +} + +func (col *repeatedColumnBuffer) Pages() Pages { + return onePage(col.Page()) +} + +func (col *repeatedColumnBuffer) Page() Page { + if col.reordered { + if col.reordering == nil { + col.reordering = col.Clone().(*repeatedColumnBuffer) + } + + column := col.reordering + column.Reset() + maxNumValues := 0 + defer func() { + clearValues(col.buffer[:maxNumValues]) + }() + + baseOffset := 0 + repetitionLevels := col.repetitionLevels.Slice() + definitionLevels := col.definitionLevels.Slice() + + for _, row := range col.rows { + rowOffset := int(row.offset) + rowLength := repeatedRowLength(repetitionLevels[rowOffset:]) + numNulls := countLevelsNotEqual(definitionLevels[rowOffset:rowOffset+rowLength], col.maxDefinitionLevel) + numValues := rowLength - numNulls + + if numValues > 0 { + if numValues > cap(col.buffer) { + col.buffer = make([]Value, numValues) + } else { + col.buffer = col.buffer[:numValues] + } + n, err := col.base.ReadValuesAt(col.buffer, int64(row.baseOffset)) + if err != nil && n < numValues { + return newErrorPage(col.Type(), col.Column(), "reordering rows of repeated column: %w", err) + } + if _, err := column.base.WriteValues(col.buffer); err != nil { + return newErrorPage(col.Type(), col.Column(), "reordering rows of repeated column: %w", err) + } + if numValues > maxNumValues { + maxNumValues = numValues + } + } + + column.rows = append(column.rows, offsetMapping{ + offset: uint32(column.repetitionLevels.Len()), + baseOffset: uint32(baseOffset), + }) + + column.repetitionLevels.Append(repetitionLevels[rowOffset : rowOffset+rowLength]...) + column.definitionLevels.Append(definitionLevels[rowOffset : rowOffset+rowLength]...) + baseOffset += numValues + } + + col.swapReorderingBuffer(column) + col.reordered = false + } + + return newRepeatedPage( + col.base.Page(), + col.maxRepetitionLevel, + col.maxDefinitionLevel, + col.repetitionLevels.Slice(), + col.definitionLevels.Slice(), + ) +} + +func (col *repeatedColumnBuffer) swapReorderingBuffer(buf *repeatedColumnBuffer) { + col.base, buf.base = buf.base, col.base + col.rows, buf.rows = buf.rows, col.rows + col.repetitionLevels, buf.repetitionLevels = buf.repetitionLevels, col.repetitionLevels + col.definitionLevels, buf.definitionLevels = buf.definitionLevels, col.definitionLevels +} + +func (col *repeatedColumnBuffer) Reset() { + col.base.Reset() + col.rows = col.rows[:0] + col.repetitionLevels.Resize(0) + col.definitionLevels.Resize(0) +} + +func (col *repeatedColumnBuffer) Size() int64 { + return int64(8*len(col.rows)+col.repetitionLevels.Len()+col.definitionLevels.Len()) + col.base.Size() +} + +func (col *repeatedColumnBuffer) Cap() int { return cap(col.rows) } + +func (col *repeatedColumnBuffer) Len() int { return len(col.rows) } + +func (col *repeatedColumnBuffer) Less(i, j int) bool { + row1 := col.rows[i] + row2 := col.rows[j] + less := col.nullOrdering + repetitionLevels := col.repetitionLevels.Slice() + definitionLevels := col.definitionLevels.Slice() + row1Length := repeatedRowLength(repetitionLevels[row1.offset:]) + row2Length := repeatedRowLength(repetitionLevels[row2.offset:]) + + for k := 0; k < row1Length && k < row2Length; k++ { + x := int(row1.baseOffset) + y := int(row2.baseOffset) + definitionLevel1 := definitionLevels[int(row1.offset)+k] + definitionLevel2 := definitionLevels[int(row2.offset)+k] + switch { + case less(col.base, x, y, col.maxDefinitionLevel, definitionLevel1, definitionLevel2): + return true + case less(col.base, y, x, col.maxDefinitionLevel, definitionLevel2, definitionLevel1): + return false + } + } + + return row1Length < row2Length +} + +func (col *repeatedColumnBuffer) Swap(i, j int) { + // Because the underlying column does not contain null values, and may hold + // an arbitrary number of values per row, we cannot swap its values at + // indexes i and j. We swap the row indexes only, then reorder the base + // column buffer when its view is materialized into a page by creating a + // copy and writing rows back to it following the order of rows in the + // repeated column buffer. + col.reordered = true + col.rows[i], col.rows[j] = col.rows[j], col.rows[i] +} + +func (col *repeatedColumnBuffer) WriteValues(values []Value) (numValues int, err error) { + maxRowLen := 0 + defer func() { + clearValues(col.buffer[:maxRowLen]) + }() + + for i := 0; i < len(values); { + j := i + + if values[j].repetitionLevel == 0 { + j++ + } + + for j < len(values) && values[j].repetitionLevel != 0 { + j++ + } + + if err := col.writeRow(values[i:j]); err != nil { + return numValues, err + } + + if len(col.buffer) > maxRowLen { + maxRowLen = len(col.buffer) + } + + numValues += j - i + i = j + } + + return numValues, nil +} + +func (col *repeatedColumnBuffer) writeRow(row []Value) error { + col.buffer = col.buffer[:0] + + for _, v := range row { + if v.definitionLevel == col.maxDefinitionLevel { + col.buffer = append(col.buffer, v) + } + } + + baseOffset := col.base.NumValues() + if len(col.buffer) > 0 { + if _, err := col.base.WriteValues(col.buffer); err != nil { + return err + } + } + + if row[0].repetitionLevel == 0 { + col.rows = append(col.rows, offsetMapping{ + offset: uint32(col.repetitionLevels.Len()), + baseOffset: uint32(baseOffset), + }) + } + + for _, v := range row { + col.repetitionLevels.AppendValue(v.repetitionLevel) + col.definitionLevels.AppendValue(v.definitionLevel) + } + + return nil +} + +func (col *repeatedColumnBuffer) writeValues(levels columnLevels, row sparse.Array) { + if levels.repetitionLevel == 0 { + col.rows = append(col.rows, offsetMapping{ + offset: uint32(col.repetitionLevels.Len()), + baseOffset: uint32(col.base.NumValues()), + }) + } + + if row.Len() == 0 { + col.repetitionLevels.AppendValue(levels.repetitionLevel) + col.definitionLevels.AppendValue(levels.definitionLevel) + return + } + + // Append multiple copies of the level values + count := row.Len() + repStart := col.repetitionLevels.Len() + defStart := col.definitionLevels.Len() + col.repetitionLevels.Resize(repStart + count) + col.definitionLevels.Resize(defStart + count) + bytealg.Broadcast(col.repetitionLevels.Slice()[repStart:], levels.repetitionLevel) + bytealg.Broadcast(col.definitionLevels.Slice()[defStart:], levels.definitionLevel) + + if levels.definitionLevel == col.maxDefinitionLevel { + col.base.writeValues(levels, row) + } +} + +func (col *repeatedColumnBuffer) writeLevel(levels columnLevels) bool { + if levels.repetitionLevel == 0 { + col.rows = append(col.rows, offsetMapping{ + offset: uint32(col.repetitionLevels.Len()), + baseOffset: uint32(col.base.NumValues()), + }) + } + col.repetitionLevels.AppendValue(levels.repetitionLevel) + col.definitionLevels.AppendValue(levels.definitionLevel) + return levels.definitionLevel == col.maxDefinitionLevel +} + +func (col *repeatedColumnBuffer) writeBoolean(levels columnLevels, value bool) { + if col.writeLevel(levels) { + col.base.writeBoolean(levels, value) + } +} + +func (col *repeatedColumnBuffer) writeInt32(levels columnLevels, value int32) { + if col.writeLevel(levels) { + col.base.writeInt32(levels, value) + } +} + +func (col *repeatedColumnBuffer) writeInt64(levels columnLevels, value int64) { + if col.writeLevel(levels) { + col.base.writeInt64(levels, value) + } +} + +func (col *repeatedColumnBuffer) writeInt96(levels columnLevels, value deprecated.Int96) { + if col.writeLevel(levels) { + col.base.writeInt96(levels, value) + } +} + +func (col *repeatedColumnBuffer) writeFloat(levels columnLevels, value float32) { + if col.writeLevel(levels) { + col.base.writeFloat(levels, value) + } +} + +func (col *repeatedColumnBuffer) writeDouble(levels columnLevels, value float64) { + if col.writeLevel(levels) { + col.base.writeDouble(levels, value) + } +} + +func (col *repeatedColumnBuffer) writeByteArray(levels columnLevels, value []byte) { + if col.writeLevel(levels) { + col.base.writeByteArray(levels, value) + } +} + +func (col *repeatedColumnBuffer) writeNull(levels columnLevels) { + col.writeLevel(levels) +} + +func (col *repeatedColumnBuffer) ReadValuesAt(values []Value, offset int64) (int, error) { + length := int64(col.definitionLevels.Len()) + if offset < 0 { + return 0, errRowIndexOutOfBounds(offset, length) + } + if offset >= length { + return 0, io.EOF + } + if length -= offset; length < int64(len(values)) { + values = values[:length] + } + + definitionLevelsSlice := col.definitionLevels.Slice() + repetitionLevelsSlice := col.repetitionLevels.Slice() + + numNulls1 := int64(countLevelsNotEqual(definitionLevelsSlice[:offset], col.maxDefinitionLevel)) + numNulls2 := int64(countLevelsNotEqual(definitionLevelsSlice[offset:offset+length], col.maxDefinitionLevel)) + + if numNulls2 < length { + n, err := col.base.ReadValuesAt(values[:length-numNulls2], offset-numNulls1) + if err != nil { + return n, err + } + } + + definitionLevels := definitionLevelsSlice[offset : offset+length] + repetitionLevels := repetitionLevelsSlice[offset : offset+length] + + if numNulls2 > 0 { + columnIndex := ^uint16(col.Column()) + i := length - numNulls2 - 1 // Last index of non-null values + j := length - 1 // Last index in output values array + maxDefinitionLevel := col.maxDefinitionLevel + + for n := len(definitionLevels) - 1; n >= 0 && j > i; n-- { + if definitionLevels[n] != maxDefinitionLevel { + values[j] = Value{ + repetitionLevel: repetitionLevels[n], + definitionLevel: definitionLevels[n], + columnIndex: columnIndex, + } + } else { + values[j] = values[i] + values[j].repetitionLevel = repetitionLevels[n] + values[j].definitionLevel = maxDefinitionLevel + i-- + } + j-- + } + + // Set levels on remaining non-null values at the beginning + for k := int64(0); k <= i; k++ { + values[k].repetitionLevel = repetitionLevels[k] + values[k].definitionLevel = maxDefinitionLevel + } + } else { + // No nulls, but still need to set levels on all values + for i := range values[:length] { + values[i].repetitionLevel = repetitionLevels[i] + values[i].definitionLevel = col.maxDefinitionLevel + } + } + + return int(length), nil +} + +// repeatedRowLength gives the length of the repeated row starting at the +// beginning of the repetitionLevels slice. +func repeatedRowLength(repetitionLevels []byte) int { + // If a repetition level exists, at least one value is required to represent + // the column. + if len(repetitionLevels) > 0 { + // The subsequent levels will represent the start of a new record when + // they go back to zero. + if i := bytes.IndexByte(repetitionLevels[1:], 0); i >= 0 { + return i + 1 + } + } + return len(repetitionLevels) +} diff --git a/vendor/github.com/parquet-go/parquet-go/column_buffer_uint32.go b/vendor/github.com/parquet-go/parquet-go/column_buffer_uint32.go new file mode 100644 index 000000000..1102ce076 --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/column_buffer_uint32.go @@ -0,0 +1,146 @@ +package parquet + +import ( + "fmt" + "io" + "strconv" + + "github.com/parquet-go/bitpack/unsafecast" + "github.com/parquet-go/parquet-go/deprecated" + "github.com/parquet-go/parquet-go/internal/memory" + "github.com/parquet-go/parquet-go/sparse" +) + +type uint32ColumnBuffer struct{ uint32Page } + +func newUint32ColumnBuffer(typ Type, columnIndex uint16, numValues int32) *uint32ColumnBuffer { + return &uint32ColumnBuffer{ + uint32Page: uint32Page{ + typ: typ, + values: memory.SliceBufferFor[uint32](int(numValues)), + columnIndex: ^columnIndex, + }, + } +} + +func (col *uint32ColumnBuffer) Clone() ColumnBuffer { + cloned := &uint32ColumnBuffer{ + uint32Page: uint32Page{ + typ: col.typ, + columnIndex: col.columnIndex, + }, + } + cloned.values.Append(col.values.Slice()...) + return cloned +} + +func (col *uint32ColumnBuffer) ColumnIndex() (ColumnIndex, error) { + return uint32ColumnIndex{&col.uint32Page}, nil +} + +func (col *uint32ColumnBuffer) OffsetIndex() (OffsetIndex, error) { + return uint32OffsetIndex{&col.uint32Page}, nil +} + +func (col *uint32ColumnBuffer) BloomFilter() BloomFilter { return nil } + +func (col *uint32ColumnBuffer) Dictionary() Dictionary { return nil } + +func (col *uint32ColumnBuffer) Pages() Pages { return onePage(col.Page()) } + +func (col *uint32ColumnBuffer) Page() Page { return &col.uint32Page } + +func (col *uint32ColumnBuffer) Reset() { col.values.Reset() } + +func (col *uint32ColumnBuffer) Cap() int { return col.values.Cap() } + +func (col *uint32ColumnBuffer) Len() int { return col.values.Len() } + +func (col *uint32ColumnBuffer) Less(i, j int) bool { return col.values.Less(i, j) } + +func (col *uint32ColumnBuffer) Swap(i, j int) { col.values.Swap(i, j) } + +func (col *uint32ColumnBuffer) Write(b []byte) (int, error) { + if (len(b) % 4) != 0 { + return 0, fmt.Errorf("cannot write INT32 values from input of size %d", len(b)) + } + col.values.Append(unsafecast.Slice[uint32](b)...) + return len(b), nil +} + +func (col *uint32ColumnBuffer) WriteUint32s(values []uint32) (int, error) { + col.values.Append(values...) + return len(values), nil +} + +func (col *uint32ColumnBuffer) WriteValues(values []Value) (int, error) { + col.writeValues(columnLevels{}, makeArrayValue(values, offsetOfU32)) + return len(values), nil +} + +func (col *uint32ColumnBuffer) writeValues(levels columnLevels, rows sparse.Array) { + offset := col.values.Len() + col.values.Resize(offset + rows.Len()) + sparse.GatherUint32(col.values.Slice()[offset:], rows.Uint32Array()) +} + +func (col *uint32ColumnBuffer) writeBoolean(levels columnLevels, value bool) { + var uintValue uint32 + if value { + uintValue = 1 + } + col.values.AppendValue(uintValue) +} + +func (col *uint32ColumnBuffer) writeInt32(levels columnLevels, value int32) { + col.values.AppendValue(uint32(value)) +} + +func (col *uint32ColumnBuffer) writeInt64(levels columnLevels, value int64) { + col.values.AppendValue(uint32(value)) +} + +func (col *uint32ColumnBuffer) writeInt96(levels columnLevels, value deprecated.Int96) { + col.values.AppendValue(uint32(value.Int32())) +} + +func (col *uint32ColumnBuffer) writeFloat(levels columnLevels, value float32) { + col.values.AppendValue(uint32(value)) +} + +func (col *uint32ColumnBuffer) writeDouble(levels columnLevels, value float64) { + col.values.AppendValue(uint32(value)) +} + +func (col *uint32ColumnBuffer) writeByteArray(levels columnLevels, value []byte) { + uintValue, err := strconv.ParseUint(unsafecast.String(value), 10, 32) + if err != nil { + panic("cannot write byte array to uint32 column: " + err.Error()) + } + col.values.AppendValue(uint32(uintValue)) +} + +func (col *uint32ColumnBuffer) writeNull(levels columnLevels) { + col.values.AppendValue(0) +} + +func (col *uint32ColumnBuffer) ReadValuesAt(values []Value, offset int64) (n int, err error) { + i := int(offset) + colValues := col.values.Slice() + switch { + case i < 0: + return 0, errRowIndexOutOfBounds(offset, int64(len(colValues))) + case i >= len(colValues): + return 0, io.EOF + default: + for n < len(values) && i < len(colValues) { + values[n] = col.makeValue(colValues[i]) + n++ + i++ + } + if n < len(values) { + err = io.EOF + } + return n, err + } +} diff --git a/vendor/github.com/parquet-go/parquet-go/column_buffer_uint64.go b/vendor/github.com/parquet-go/parquet-go/column_buffer_uint64.go new file mode 100644 index 000000000..ce836a2d3 --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/column_buffer_uint64.go @@ -0,0 +1,146 @@ +package parquet + +import ( + "fmt" + "io" + "strconv" + + "github.com/parquet-go/bitpack/unsafecast" + "github.com/parquet-go/parquet-go/deprecated" + "github.com/parquet-go/parquet-go/internal/memory" + "github.com/parquet-go/parquet-go/sparse" +) + +type uint64ColumnBuffer struct{ uint64Page } + +func newUint64ColumnBuffer(typ Type, columnIndex uint16, numValues int32) *uint64ColumnBuffer { + return &uint64ColumnBuffer{ + uint64Page: uint64Page{ + typ: typ, + values: memory.SliceBufferFor[uint64](int(numValues)), + columnIndex: ^columnIndex, + }, + } +} + +func (col *uint64ColumnBuffer) Clone() ColumnBuffer { + cloned := &uint64ColumnBuffer{ + uint64Page: uint64Page{ + typ: col.typ, + columnIndex: col.columnIndex, + }, + } + cloned.values.Append(col.values.Slice()...) + return cloned +} + +func (col *uint64ColumnBuffer) ColumnIndex() (ColumnIndex, error) { + return uint64ColumnIndex{&col.uint64Page}, nil +} + +func (col *uint64ColumnBuffer) OffsetIndex() (OffsetIndex, error) { + return uint64OffsetIndex{&col.uint64Page}, nil +} + +func (col *uint64ColumnBuffer) BloomFilter() BloomFilter { return nil } + +func (col *uint64ColumnBuffer) Dictionary() Dictionary { return nil } + +func (col *uint64ColumnBuffer) Pages() Pages { return onePage(col.Page()) } + +func (col *uint64ColumnBuffer) Page() Page { return &col.uint64Page } + +func (col *uint64ColumnBuffer) Reset() { col.values.Reset() } + +func (col *uint64ColumnBuffer) Cap() int { return col.values.Cap() } + +func (col *uint64ColumnBuffer) Len() int { return col.values.Len() } + +func (col *uint64ColumnBuffer) Less(i, j int) bool { return col.values.Less(i, j) } + +func (col *uint64ColumnBuffer) Swap(i, j int) { col.values.Swap(i, j) } + +func (col *uint64ColumnBuffer) Write(b []byte) (int, error) { + if (len(b) % 8) != 0 { + return 0, fmt.Errorf("cannot write INT64 values from input of size %d", len(b)) + } + col.values.Append(unsafecast.Slice[uint64](b)...) + return len(b), nil +} + +func (col *uint64ColumnBuffer) WriteUint64s(values []uint64) (int, error) { + col.values.Append(values...) + return len(values), nil +} + +func (col *uint64ColumnBuffer) WriteValues(values []Value) (int, error) { + col.writeValues(columnLevels{}, makeArrayValue(values, offsetOfU64)) + return len(values), nil +} + +func (col *uint64ColumnBuffer) writeValues(levels columnLevels, rows sparse.Array) { + offset := col.values.Len() + col.values.Resize(offset + rows.Len()) + sparse.GatherUint64(col.values.Slice()[offset:], rows.Uint64Array()) +} + +func (col *uint64ColumnBuffer) writeBoolean(levels columnLevels, value bool) { + var uintValue uint64 + if value { + uintValue = 1 + } + col.values.AppendValue(uintValue) +} + +func (col *uint64ColumnBuffer) writeInt32(levels columnLevels, value int32) { + col.values.AppendValue(uint64(value)) +} + +func (col *uint64ColumnBuffer) writeInt64(levels columnLevels, value int64) { + col.values.AppendValue(uint64(value)) +} + +func (col *uint64ColumnBuffer) writeInt96(levels columnLevels, value deprecated.Int96) { + col.values.AppendValue(uint64(value.Int32())) +} + +func (col *uint64ColumnBuffer) writeFloat(levels columnLevels, value float32) { + col.values.AppendValue(uint64(value)) +} + +func (col *uint64ColumnBuffer) writeDouble(levels columnLevels, value float64) { + col.values.AppendValue(uint64(value)) +} + +func (col *uint64ColumnBuffer) writeByteArray(levels columnLevels, value []byte) { + uintValue, err := strconv.ParseUint(unsafecast.String(value), 10, 32) + if err != nil { + panic("cannot write byte array to uint64 column: " + err.Error()) + } + col.values.AppendValue(uint64(uintValue)) +} + +func (col *uint64ColumnBuffer) writeNull(levels columnLevels) { + col.values.AppendValue(0) +} + +func (col *uint64ColumnBuffer) ReadValuesAt(values []Value, offset int64) (n int, err error) { + i := int(offset) + colValues := col.values.Slice() + switch { + case i < 0: + return 0, errRowIndexOutOfBounds(offset, int64(len(colValues))) + case i >= len(colValues): + return 0, io.EOF + default: + for n < len(values) && i < len(colValues) { + values[n] = col.makeValue(colValues[i]) + n++ + i++ + } + if n < len(values) { + err = io.EOF + } + return n, err + } +} diff --git a/vendor/github.com/parquet-go/parquet-go/column_buffer_variant.go b/vendor/github.com/parquet-go/parquet-go/column_buffer_variant.go new file mode 100644 index 000000000..2c1b98283 --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/column_buffer_variant.go @@ -0,0 +1,155 @@ +package parquet + +import ( + "fmt" + "reflect" + + "github.com/parquet-go/parquet-go/variant" +) + +// writeValueFuncOfVariant handles writing Go values to variant column buffers. +// Dispatches to unshredded or shredded variant handling. +func writeValueFuncOfVariant(columnIndex uint16, node Node) (uint16, writeValueFunc) { + if isUnshreddedVariant(node) { + return writeValueFuncOfUnshreddedVariant(columnIndex, node) + } + return writeValueFuncOfShreddedVariant(columnIndex, node) +} + +// writeValueFuncOfUnshreddedVariant handles the simple 2-column variant. +func writeValueFuncOfUnshreddedVariant(columnIndex uint16, node Node) (uint16, writeValueFunc) { + metadataColumnIndex := columnIndex + valueColumnIndex := columnIndex + 1 + nextColumnIndex := columnIndex + 2 + + return nextColumnIndex, func(columns []ColumnBuffer, levels columnLevels, value reflect.Value) { + metadata, val := variantMarshalOrNull(value) + columns[metadataColumnIndex].writeByteArray(levels, metadata) + columns[valueColumnIndex].writeByteArray(levels, val) + } +} + +// writeValueFuncOfShreddedVariant handles the 3-field shredded variant. +func writeValueFuncOfShreddedVariant(columnIndex uint16, node Node) (uint16, writeValueFunc) { + // Fields are sorted alphabetically: metadata, typed_value, value + var metadataColumnIndex, valueColumnIndex, typedValueStartColumn uint16 + var typedValueLeafCount uint16 + var typedValueNode Node + + col := columnIndex + for _, f := range node.Fields() { + n := numLeafColumnsOf(f) + switch f.Name() { + case "metadata": + metadataColumnIndex = col + col += n + case "typed_value": + typedValueStartColumn = col + typedValueNode = f + typedValueLeafCount = n + col += n + case "value": + valueColumnIndex = col + col += n + } + } + nextColumnIndex := col + + typedInner := Required(typedValueNode) + matcher := buildShreddedMatcher(typedInner) + + return nextColumnIndex, func(columns []ColumnBuffer, levels columnLevels, value reflect.Value) { + metadata, val := variantMarshalOrNull(value) + columns[metadataColumnIndex].writeByteArray(levels, metadata) + + goVal := extractGoValue(value) + if goVal != nil && matcher.canShred(goVal) { + // Write null to value column + columns[valueColumnIndex].writeNull(levels) + // Write typed value (increment def level for optional wrapper) + typedLevels := levels + typedLevels.definitionLevel++ + writeShredded(columns, typedLevels, typedValueStartColumn, &matcher, goVal) + } else { + // Write variant bytes to value column + valueLevels := levels + valueLevels.definitionLevel++ + columns[valueColumnIndex].writeByteArray(valueLevels, val) + // Write null to all typed_value columns + for col := typedValueStartColumn; col < typedValueStartColumn+typedValueLeafCount; col++ { + columns[col].writeNull(levels) + } + } + } +} + +// writeShredded writes a shredded value to the column buffers. +func writeShredded(columns []ColumnBuffer, levels columnLevels, startColumn uint16, m *shreddedMatcher, v any) { + if m.isPrimitive() { + pv := goValueToParquetValue(v, m.kind()) + col := columns[startColumn] + switch m.kind() { + case Boolean: + col.writeBoolean(levels, pv.Boolean()) + case Int32: + col.writeInt32(levels, pv.Int32()) + case Int64: + col.writeInt64(levels, pv.Int64()) + case Float: + col.writeFloat(levels, pv.Float()) + case Double: + col.writeDouble(levels, pv.Double()) + case ByteArray: + col.writeByteArray(levels, pv.ByteArray()) + default: + col.writeByteArray(levels, pv.ByteArray()) + } + return + } + if m.isGroup() { + writeShreddedObject(columns, levels, startColumn, m, v) + } +} + +func writeShreddedObject(columns []ColumnBuffer, levels columnLevels, startColumn uint16, m *shreddedMatcher, v any) { + obj, ok := v.(map[string]any) + if !ok { + return + } + + col := startColumn + for _, f := range m.fields { + // Within each field's sub-group, fields are sorted alphabetically: + // typed_value comes before value. + typedValueCol := col + typedCount := f.matcher.leafCount + valueCol := col + typedCount + nextFieldCol := valueCol + 1 + + fieldVal, exists := obj[f.name] + if exists && fieldVal != nil && f.matcher.canShred(fieldVal) { + columns[valueCol].writeNull(levels) + typedLevels := levels + typedLevels.definitionLevel++ + writeShredded(columns, typedLevels, typedValueCol, &f.matcher, fieldVal) + } else if exists && fieldVal != nil { + valueLevels := levels + valueLevels.definitionLevel++ + metadata, val, err := variant.Marshal(fieldVal) + if err != nil { + panic(fmt.Sprintf("variant marshal field %q: %v", f.name, err)) + } + combined := encodeFieldVariant(metadata, val) + columns[valueCol].writeByteArray(valueLevels, combined) + for c := typedValueCol; c < typedValueCol+typedCount; c++ { + columns[c].writeNull(levels) + } + } else { + for c := typedValueCol; c < nextFieldCol; c++ { + columns[c].writeNull(levels) + } + } + + col = nextFieldCol + } +} diff --git a/vendor/github.com/parquet-go/parquet-go/column_buffer_write.go b/vendor/github.com/parquet-go/parquet-go/column_buffer_write.go new file mode 100644 index 000000000..dbe1413b4 --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/column_buffer_write.go @@ -0,0 +1,1019 @@ +package parquet + +import ( + "encoding/json" + "fmt" + "math" + "math/bits" + "reflect" + "slices" + "time" + "unsafe" + + "github.com/parquet-go/jsonlite" + "github.com/parquet-go/parquet-go/deprecated" + "github.com/parquet-go/parquet-go/internal/memory" + "github.com/parquet-go/parquet-go/sparse" + "google.golang.org/protobuf/types/known/structpb" +) + +// writeRowsFunc is the type of functions that apply rows to a set of column +// buffers. +// +// - columns is the array of column buffer where the rows are written. +// +// - rows is the array of Go values to write to the column buffers. +// +// - levels is used to track the column index, repetition and definition levels +// of values when writing optional or repeated columns. +type writeRowsFunc func(columns []ColumnBuffer, levels columnLevels, rows sparse.Array) + +// writeRowsFuncOf generates a writeRowsFunc function for the given Go type and +// parquet schema. The column path indicates the column that the function is +// being generated for in the parquet schema. +func writeRowsFuncOf(t reflect.Type, schema *Schema, path columnPath, tagReplacements []StructTagOption) writeRowsFunc { + if leaf, exists := schema.Lookup(path...); exists { + logicalType := leaf.Node.Type().LogicalType() + if logicalType != nil && logicalType.Json != nil { + return writeRowsFuncOfJSON(t, schema, path) + } + } + + switch t { + case reflect.TypeFor[deprecated.Int96](): + return writeRowsFuncOfRequired(t, schema, path) + case reflect.TypeFor[time.Time](): + return writeRowsFuncOfTime(t, schema, path, tagReplacements) + case reflect.TypeFor[json.RawMessage](): + return writeRowsFuncFor[json.RawMessage](schema, path) + case reflect.TypeFor[json.Number](): + return writeRowsFuncFor[json.Number](schema, path) + case reflect.TypeFor[*structpb.Struct](): + return writeRowsFuncFor[*structpb.Struct](schema, path) + case reflect.TypeFor[*jsonlite.Value](): + return writeRowsFuncFor[*jsonlite.Value](schema, path) + } + + switch t.Kind() { + case reflect.Int, reflect.Uint, + reflect.Int32, reflect.Uint32, + reflect.Int64, reflect.Uint64: + return writeRowsFuncOfInt(t, schema, path) + case reflect.Bool, + reflect.Float32, + reflect.Float64, + reflect.String: + return writeRowsFuncOfRequired(t, schema, path) + case reflect.Int8, reflect.Int16, reflect.Uint8, reflect.Uint16: + return writeRowsFuncOfSmallInt(t, schema, path) + case reflect.Slice: + if t.Elem().Kind() == reflect.Uint8 { + // When the column physical type is FIXED_LEN_BYTE_ARRAY (e.g. the + // field carries a decimal(...) tag), the optimized direct-memory + // path would read the slice header bytes instead of the slice + // contents. Dispatch to a dedicated writer that indirects through + // the slice header. See issue #508. + column := schema.lazyLoadState().mapping.lookup(path) + if column.node.Type().Kind() == FixedLenByteArray { + return writeRowsFuncOfByteSlice(t, schema, path) + } + return writeRowsFuncOfRequired(t, schema, path) + } else { + return writeRowsFuncOfSlice(t, schema, path, tagReplacements) + } + case reflect.Array: + if t.Elem().Kind() == reflect.Uint8 { + return writeRowsFuncOfArray(t, schema, path) + } + case reflect.Pointer: + return writeRowsFuncOfPointer(t, schema, path, tagReplacements) + case reflect.Struct: + return writeRowsFuncOfStruct(t, schema, path, tagReplacements) + case reflect.Map: + return writeRowsFuncOfMap(t, schema, path, tagReplacements) + case reflect.Interface: + return writeRowsFuncOfInterface(t, schema, path) + } + panic("cannot convert Go values of type " + typeNameOf(t) + " to parquet value") +} + +func writeRowsFuncOfRequired(t reflect.Type, schema *Schema, path columnPath) writeRowsFunc { + column := schema.lazyLoadState().mapping.lookup(path) + columnIndex := column.columnIndex + if columnIndex == math.MaxUint16 { + panic("parquet: column not found: " + path.String()) + } + return func(columns []ColumnBuffer, levels columnLevels, rows sparse.Array) { + columns[columnIndex].writeValues(levels, rows) + } +} + +func writeRowsFuncOfSmallInt(t reflect.Type, schema *Schema, path columnPath) writeRowsFunc { + column := schema.lazyLoadState().mapping.lookup(path) + columnIndex := column.columnIndex + if columnIndex == math.MaxUint16 { + panic("parquet: column not found: " + path.String()) + } + + kind := t.Kind() + return func(columns []ColumnBuffer, levels columnLevels, rows sparse.Array) { + n := rows.Len() + if n == 0 { + columns[columnIndex].writeValues(levels, rows) + return + } + + buf := smallIntBufPool.Get( + func() *smallIntBuf { return new(smallIntBuf) }, + func(b *smallIntBuf) { b.values = b.values[:0] }, + ) + buf.values = slices.Grow(buf.values, n)[:n] + defer smallIntBufPool.Put(buf) + + switch kind { + case reflect.Int8: + a := rows.Int8Array() + for i := range n { + buf.values[i] = int32(a.Index(i)) + } + case reflect.Int16: + a := rows.Int16Array() + for i := range n { + buf.values[i] = int32(a.Index(i)) + } + case reflect.Uint8: + a := rows.Uint8Array() + for i := range n { + buf.values[i] = int32(a.Index(i)) + } + case reflect.Uint16: + a := rows.Uint16Array() + for i := range n { + buf.values[i] = int32(a.Index(i)) + } + } + + widenedArray := sparse.MakeInt32Array(buf.values).UnsafeArray() + columns[columnIndex].writeValues(levels, widenedArray) + } +} + +type smallIntBuf struct{ values []int32 } + +var smallIntBufPool memory.Pool[smallIntBuf] + +type wideIntBuf struct{ values []int64 } + +var wideIntBufPool memory.Pool[wideIntBuf] + +func writeRowsFuncOfInt(t reflect.Type, schema *Schema, path columnPath) writeRowsFunc { + column := schema.lazyLoadState().mapping.lookup(path) + columnIndex := column.columnIndex + if columnIndex == math.MaxUint16 { + panic("parquet: column not found: " + path.String()) + } + + goSize := t.Size() + colKind := column.node.Type().Kind() + + // When the Go type size doesn't match the column's physical type size, + // we need to convert values element-by-element. This happens when tags + // like int(32) or uint(32) override the default column bit width. + // For example: uint64 with int(32) tag, or int with int(32) on 64-bit. + if goSize > 4 && colKind == Int32 { + // 8-byte Go type → 4-byte column: narrow + signed := t.Kind() == reflect.Int || t.Kind() == reflect.Int64 + return func(columns []ColumnBuffer, levels columnLevels, rows sparse.Array) { + n := rows.Len() + if n == 0 { + columns[columnIndex].writeValues(levels, rows) + return + } + + buf := smallIntBufPool.Get( + func() *smallIntBuf { return new(smallIntBuf) }, + func(b *smallIntBuf) { b.values = b.values[:0] }, + ) + buf.values = slices.Grow(buf.values, n)[:n] + defer smallIntBufPool.Put(buf) + + if signed { + a := rows.Int64Array() + for i := range n { + buf.values[i] = int32(a.Index(i)) + } + } else { + a := rows.Uint64Array() + for i := range n { + buf.values[i] = int32(uint32(a.Index(i))) + } + } + + narrowedArray := sparse.MakeInt32Array(buf.values).UnsafeArray() + columns[columnIndex].writeValues(levels, narrowedArray) + } + } + + if goSize <= 4 && colKind == Int64 { + // 4-byte Go type → 8-byte column: widen + signed := t.Kind() == reflect.Int || t.Kind() == reflect.Int32 + return func(columns []ColumnBuffer, levels columnLevels, rows sparse.Array) { + n := rows.Len() + if n == 0 { + columns[columnIndex].writeValues(levels, rows) + return + } + + buf := wideIntBufPool.Get( + func() *wideIntBuf { return new(wideIntBuf) }, + func(b *wideIntBuf) { b.values = b.values[:0] }, + ) + buf.values = slices.Grow(buf.values, n)[:n] + defer wideIntBufPool.Put(buf) + + if signed { + a := rows.Int32Array() + for i := range n { + buf.values[i] = int64(a.Index(i)) + } + } else { + a := rows.Uint32Array() + for i := range n { + buf.values[i] = int64(a.Index(i)) + } + } + + widenedArray := sparse.MakeInt64Array(buf.values).UnsafeArray() + columns[columnIndex].writeValues(levels, widenedArray) + } + } + + // Default: Go type size matches column physical type size. + return func(columns []ColumnBuffer, levels columnLevels, rows sparse.Array) { + columns[columnIndex].writeValues(levels, rows) + } +} + +func writeRowsFuncOfOptional(t reflect.Type, schema *Schema, path columnPath, writeRows writeRowsFunc) writeRowsFunc { + // For interface types, we just increment the definition level for present + // values without checking for null indexes. + // Interface: handled by writeRowsFuncOfInterface which manages levels internally + writeOptional := func(columns []ColumnBuffer, levels columnLevels, rows sparse.Array) { + if rows.Len() == 0 { + writeRows(columns, levels, rows) + return + } + levels.definitionLevel++ + writeRows(columns, levels, rows) + } + + switch t.Kind() { + case reflect.Interface: + return writeOptional + case reflect.Slice: + // For slices (nested lists), we need to distinguish between nil slices + // and empty slices. Nil slices should not increment the definition level + // (they represent null), while non-nil empty slices should increment it + // (they represent an empty list). + if t.Elem().Kind() != reflect.Uint8 { + type sliceHeader struct { + base unsafe.Pointer + len int + cap int + } + return func(columns []ColumnBuffer, levels columnLevels, rows sparse.Array) { + if rows.Len() == 0 { + writeRows(columns, levels, rows) + return + } + // Process each slice individually to check for nil + for i := range rows.Len() { + p := (*sliceHeader)(rows.Index(i)) + elemLevels := levels + // A nil slice has base=nil, while an empty slice has base!=nil but len=0 + // We need to increment definition level for non-nil slices (including empty ones) + if p.base != nil { + elemLevels.definitionLevel++ + } + writeRows(columns, elemLevels, rows.Slice(i, i+1)) + } + } + } + } + + nullIndex := nullIndexFuncOf(t) + return func(columns []ColumnBuffer, levels columnLevels, rows sparse.Array) { + if rows.Len() == 0 { + writeRows(columns, levels, rows) + return + } + + nulls := acquireBitmap(rows.Len()) + defer releaseBitmap(nulls) + nullIndex(nulls.bits, rows) + + nullLevels := levels + levels.definitionLevel++ + // In this function, we are dealing with optional values which are + // neither pointers nor slices; for example, a int32 field marked + // "optional" in its parent struct. + // + // We need to find zero values, which should be represented as nulls + // in the parquet column. In order to minimize the calls to writeRows + // and maximize throughput, we use the nullIndex and nonNullIndex + // functions, which are type-specific implementations of the algorithm. + // + // Sections of the input that are contiguous nulls or non-nulls can be + // sent to a single call to writeRows to be written to the underlying + // buffer since they share the same definition level. + // + // This optimization is defeated by inputs alternating null and non-null + // sequences of single values, we do not expect this condition to be a + // common case. + for i := 0; i < rows.Len(); { + j := 0 + x := i / 64 + y := i % 64 + + if y != 0 { + if b := nulls.bits[x] >> uint(y); b == 0 { + x++ + y = 0 + } else { + y += bits.TrailingZeros64(b) + goto writeNulls + } + } + + for x < len(nulls.bits) && nulls.bits[x] == 0 { + x++ + } + + if x < len(nulls.bits) { + y = bits.TrailingZeros64(nulls.bits[x]) % 64 + } + + writeNulls: + if j = x*64 + y; j > rows.Len() { + j = rows.Len() + } + + if i < j { + writeRows(columns, nullLevels, rows.Slice(i, j)) + i = j + } + + if y != 0 { + if b := nulls.bits[x] >> uint(y); b == (1< rows.Len() { + j = rows.Len() + } + + if i < j { + writeRows(columns, levels, rows.Slice(i, j)) + i = j + } + } + + } +} + +func writeRowsFuncOfArray(t reflect.Type, schema *Schema, path columnPath) writeRowsFunc { + column := schema.lazyLoadState().mapping.lookup(path) + arrayLen := t.Len() + columnLen := column.node.Type().Length() + if arrayLen != columnLen { + panic(fmt.Sprintf("cannot convert Go values of type "+typeNameOf(t)+" to FIXED_LEN_BYTE_ARRAY(%d)", columnLen)) + } + return writeRowsFuncOfRequired(t, schema, path) +} + +type byteSliceBuf struct{ values []byte } + +var byteSliceBufPool memory.Pool[byteSliceBuf] + +// writeRowsFuncOfByteSlice handles writing []byte Go fields into a +// FIXED_LEN_BYTE_ARRAY column. The optimized path used for other []byte +// columns treats each sparse.Array element as raw bytes, but for a slice +// field that element is a slice header. This function reads each slice via +// rows.StringArray() (string and []byte share the ptr/len header layout), +// copies the referenced bytes into a contiguous scratch buffer, and forwards +// the buffer as a flat sparse.Array so the column buffer's existing write +// path sees real fixed-size elements. +func writeRowsFuncOfByteSlice(t reflect.Type, schema *Schema, path columnPath) writeRowsFunc { + column := schema.lazyLoadState().mapping.lookup(path) + columnIndex := column.columnIndex + if columnIndex == math.MaxUint16 { + panic("parquet: column not found: " + path.String()) + } + size := column.node.Type().Length() + return func(columns []ColumnBuffer, levels columnLevels, rows sparse.Array) { + n := rows.Len() + if n == 0 { + columns[columnIndex].writeValues(levels, rows) + return + } + + buf := byteSliceBufPool.Get( + func() *byteSliceBuf { return new(byteSliceBuf) }, + func(b *byteSliceBuf) { b.values = b.values[:0] }, + ) + buf.values = slices.Grow(buf.values, n*size)[:n*size] + // Zero-fill so empty/nil slice slots emit placeholder bytes; the + // pooled buffer may otherwise carry stale contents from a previous + // caller. The optional wrapper upstream marks those rows null via + // the definition level, matching fixedLenByteArrayColumnBuffer.writeNull. + clear(buf.values) + defer byteSliceBufPool.Put(buf) + + stringArray := rows.StringArray() + for i := range n { + s := stringArray.Index(i) + switch len(s) { + case 0: + case size: + copy(buf.values[i*size:], s) + default: + panic(fmt.Sprintf("cannot write byte slice of length %d to FIXED_LEN_BYTE_ARRAY(%d) column", len(s), size)) + } + } + + flatArray := sparse.UnsafeArray(unsafe.Pointer(&buf.values[0]), n, uintptr(size)) + columns[columnIndex].writeValues(levels, flatArray) + } +} + +func writeRowsFuncOfPointer(t reflect.Type, schema *Schema, path columnPath, tagReplacements []StructTagOption) writeRowsFunc { + elemType := t.Elem() + elemSize := uintptr(elemType.Size()) + writeRows := writeRowsFuncOf(elemType, schema, path, tagReplacements) + + if len(path) == 0 { + // This code path is taken when generating a writeRowsFunc for a pointer + // type. In this case, we do not need to increase the definition level + // since we are not deailng with an optional field but a pointer to the + // row type. + return func(columns []ColumnBuffer, levels columnLevels, rows sparse.Array) { + if rows.Len() == 0 { + writeRows(columns, levels, rows) + return + } + + for i := range rows.Len() { + p := *(*unsafe.Pointer)(rows.Index(i)) + a := sparse.Array{} + if p != nil { + a = makeArray(p, 1, elemSize) + } + writeRows(columns, levels, a) + } + } + } + + // Check if the schema node at this path is optional. If not, the pointer + // is just a Go implementation detail (like proto message types) and we + // should not increment the definition level. + node := findByPath(schema, path) + isOptional := node != nil && node.Optional() + + return func(columns []ColumnBuffer, levels columnLevels, rows sparse.Array) { + if rows.Len() == 0 { + writeRows(columns, levels, rows) + return + } + + for i := range rows.Len() { + p := *(*unsafe.Pointer)(rows.Index(i)) + a := sparse.Array{} + elemLevels := levels + if p != nil { + a = makeArray(p, 1, elemSize) + if isOptional { + elemLevels.definitionLevel++ + } + } + writeRows(columns, elemLevels, a) + } + + } +} + +func writeRowsFuncOfSlice(t reflect.Type, schema *Schema, path columnPath, tagReplacements []StructTagOption) writeRowsFunc { + elemType := t.Elem() + elemSize := uintptr(elemType.Size()) + + // If the current node is a LIST, we need to drill down to the element + // to find the schema node for the slice elements. + if node := findByPath(schema, path); node != nil && isList(node) { + path = path.append("list", "element") + } + + writeRows := writeRowsFuncOf(elemType, schema, path, tagReplacements) + + // Check if the element schema node is optional. + // This handles the case of `parquet-element:",optional"` tag where + // the list elements themselves are optional (non-pointer basic types). + // For pointer types (like []*string), the pointer handling already + // takes care of optionality via writeRowsFuncOfPointer. + if elemType.Kind() != reflect.Ptr { + if node := findByPath(schema, path); node != nil && node.Optional() { + writeRows = writeRowsFuncOfOptional(elemType, schema, path, writeRows) + } + } + + return func(columns []ColumnBuffer, levels columnLevels, rows sparse.Array) { + type sliceHeader struct { + base unsafe.Pointer + len int + cap int + } + + if rows.Len() == 0 { + writeRows(columns, levels, rows) + return + } + + levels.repetitionDepth++ + + for i := range rows.Len() { + p := (*sliceHeader)(rows.Index(i)) + a := makeArray(p.base, p.len, elemSize) + b := sparse.Array{} + + elemLevels := levels + if a.Len() > 0 { + b = a.Slice(0, 1) + elemLevels.definitionLevel++ + } + + writeRows(columns, elemLevels, b) + + if a.Len() > 1 { + elemLevels.repetitionLevel = elemLevels.repetitionDepth + + writeRows(columns, elemLevels, a.Slice(1, a.Len())) + } + } + + } +} + +func writeRowsFuncOfStruct(t reflect.Type, schema *Schema, path columnPath, tagReplacements []StructTagOption) writeRowsFunc { + type column struct { + offset uintptr + writeRows writeRowsFunc + } + + fields := structFieldsOf(path, t, tagReplacements) + columns := make([]column, len(fields)) + + for i, f := range fields { + columnPath := path.append(f.Name) + // Check if the schema node at the field path is optional and/or a list + // Use the schema structure, not the Go struct tags + node := findByPath(schema, columnPath) + list := false + forEachStructTagOption(f, func(_ reflect.Type, option, _ string) { + switch option { + case "list": + list = true + columnPath = columnPath.append("list", "element") + } + }) + writeRows := writeRowsFuncOf(f.Type, schema, columnPath, tagReplacements) + // Check if the schema node is optional (from the schema, not the Go struct tag) + if node.Optional() { + kind := f.Type.Kind() + switch { + case kind == reflect.Pointer: + case kind == reflect.Interface: + // Interface types handle their own definition levels through + // writeRowsFuncOfInterface -> writeValueFuncOf -> writeValueFuncOfOptional + case kind == reflect.Slice && !list && f.Type.Elem().Kind() != reflect.Uint8: + // For slices other than []byte, optional applies + // to the element, not the list. + case f.Type == reflect.TypeFor[json.RawMessage](): + // json.RawMessage handles its own definition levels through + // writeRowsFuncOfJSONRawMessage -> writeValueFuncOf -> writeValueFuncOfOptional + case f.Type == reflect.TypeFor[time.Time](): + // time.Time is a struct but has IsZero() method, + // so it needs special handling. + // Don't use writeRowsFuncOfOptional which relies + // on bitmap batching. + default: + writeRows = writeRowsFuncOfOptional(f.Type, schema, columnPath, writeRows) + } + } + + columns[i] = column{ + offset: f.Offset, + writeRows: writeRows, + } + } + + return func(buffers []ColumnBuffer, levels columnLevels, rows sparse.Array) { + if rows.Len() == 0 { + for _, column := range columns { + column.writeRows(buffers, levels, rows) + } + } else { + for _, column := range columns { + column.writeRows(buffers, levels, rows.Offset(column.offset)) + } + } + } +} + +func writeRowsFuncOfInterface(t reflect.Type, schema *Schema, path columnPath) writeRowsFunc { + node := findByPath(schema, path) + if node == nil { + panic("column not found: " + path.String()) + } + + columnIndex := findColumnIndex(schema, node, path) + if columnIndex == math.MaxUint16 { + // Empty group node (e.g., from interface{} in map[string]any). + // Return a no-op function since there are no columns to write. + return func(columns []ColumnBuffer, levels columnLevels, rows sparse.Array) { + // No-op: nothing to write for empty groups + } + } + + // Get the schema-based write function for this node + _, writeValue := writeValueFuncOf(columnIndex, node) + + return func(columns []ColumnBuffer, levels columnLevels, rows sparse.Array) { + for i := range rows.Len() { + writeValue(columns, levels, reflect.NewAt(t, rows.Index(i)).Elem()) + } + } +} + +// writeRowsFuncOfMapToGroup handles writing a Go map to a Parquet GROUP schema +// (as opposed to a MAP logical type). This allows map[string]T to be written +// to schemas with named optional fields. +func writeRowsFuncOfMapToGroup(t reflect.Type, schema *Schema, path columnPath, groupNode Node, tagReplacements []StructTagOption) writeRowsFunc { + if t.Key().Kind() != reflect.String { + panic("map keys must be strings when writing to GROUP schema") + } + + type fieldWriter struct { + fieldName string + writeRows writeRowsFunc // Writes null/empty value + writeValue writeValueFunc + } + + fields := groupNode.Fields() + if len(fields) == 0 { + // Empty group - return no-op function + return func(columns []ColumnBuffer, levels columnLevels, rows sparse.Array) { + // No-op: empty group has no fields to write + } + } + + writers := make([]fieldWriter, len(fields)) + valueType := t.Elem() + columnIndex := findColumnIndex(schema, findByPath(schema, path), path) + + for i, field := range fields { + fieldPath := path.append(field.Name()) + writeRows := writeRowsFuncOf(valueType, schema, fieldPath, tagReplacements) + if field.Optional() { + writeRows = writeRowsFuncOfOptional(valueType, schema, fieldPath, writeRows) + } + + var writeValue writeValueFunc + columnIndex, writeValue = writeValueFuncOf(columnIndex, field) + + writers[i] = fieldWriter{ + fieldName: field.Name(), + writeRows: writeRows, + writeValue: writeValue, + } + } + + // We make sepcial cases for the common types to avoid paying the cost of + // reflection in calls like MapIndex which force the returned value to be + // allocated on the heap. + var writeMaps writeRowsFunc + switch { + case t.ConvertibleTo(reflect.TypeFor[map[string]string]()): + writeMaps = func(columns []ColumnBuffer, levels columnLevels, rows sparse.Array) { + numRows := rows.Len() + numValues := len(writers) * numRows + buffer := stringArrayPool.Get( + func() *stringArray { return new(stringArray) }, + func(b *stringArray) { b.values = b.values[:0] }, + ) + buffer.values = slices.Grow(buffer.values, numValues)[:numValues] + defer stringArrayPool.Put(buffer) + defer clear(buffer.values) + + for i := range numRows { + m := *(*map[string]string)(reflect.NewAt(t, rows.Index(i)).UnsafePointer()) + for j := range writers { + buffer.values[j*numRows+i] = m[writers[j].fieldName] + } + } + + for j := range writers { + a := sparse.MakeStringArray(buffer.values[j*numRows : (j+1)*numRows]) + writers[j].writeRows(columns, levels, a.UnsafeArray()) + } + } + + case t.ConvertibleTo(reflect.TypeFor[map[string]any]()): + writeMaps = func(columns []ColumnBuffer, levels columnLevels, rows sparse.Array) { + for i := range rows.Len() { + m := *(*map[string]any)(reflect.NewAt(t, rows.Index(i)).UnsafePointer()) + + for j := range writers { + w := &writers[j] + v := m[w.fieldName] + w.writeValue(columns, levels, reflect.ValueOf(v)) + } + } + } + + default: + writeMaps = func(columns []ColumnBuffer, levels columnLevels, rows sparse.Array) { + for i := range rows.Len() { + m := reflect.NewAt(t, rows.Index(i)).Elem() + + for j := range writers { + w := &writers[j] + keyValue := reflect.ValueOf(&w.fieldName).Elem() + mapValue := m.MapIndex(keyValue) + w.writeValue(columns, levels, mapValue) + } + } + } + } + + return func(columns []ColumnBuffer, levels columnLevels, rows sparse.Array) { + if rows.Len() == 0 { + for _, w := range writers { + w.writeRows(columns, levels, sparse.Array{}) + } + } else { + writeMaps(columns, levels, rows) + } + } +} + +type stringArray struct{ values []string } + +var stringArrayPool memory.Pool[stringArray] + +// findColumnIndex finds the column index for a given node and path. +// For leaf nodes, returns the column index directly. +// For group nodes, recursively finds the first leaf column. +// Returns -1 for empty group nodes (groups with no fields), which can occur +// when using interface{} types in maps (e.g., map[string]any). +func findColumnIndex(schema *Schema, node Node, path columnPath) uint16 { + col := schema.lazyLoadState().mapping.lookup(path) + if col.columnIndex <= MaxColumnIndex { + return col.columnIndex + } + if node.Leaf() { + panic("node is a leaf but has no column index: " + path.String()) + } + fields := node.Fields() + if len(fields) == 0 { + // Empty group nodes can occur with interface{} types (e.g., map[string]any). + // Return MaxUint16 to indicate there are no columns to write. + return math.MaxUint16 + } + firstFieldPath := path.append(fields[0].Name()) + return findColumnIndex(schema, fields[0], firstFieldPath) +} + +func writeRowsFuncOfMap(t reflect.Type, schema *Schema, path columnPath, tagReplacements []StructTagOption) writeRowsFunc { + // Check if the schema at this path is a MAP or a GROUP. + node := findByPath(schema, path) + if node != nil && !isMap(node) { + // The schema is a GROUP (not a MAP), so we need to handle it differently. + // Instead of using key_value structure, we iterate through the GROUP's fields + // and look up corresponding map keys. + return writeRowsFuncOfMapToGroup(t, schema, path, node, tagReplacements) + } + + // Standard MAP logical type handling + keyPath := path.append("key_value", "key") + keyType := t.Key() + writeKeys := writeRowsFuncOf(keyType, schema, keyPath, tagReplacements) + + valuePath := path.append("key_value", "value") + valueNode := findByPath(schema, valuePath) + // If the value is a LIST type, adjust the path to include list/element + if valueNode != nil && isList(valueNode) { + valuePath = valuePath.append("list", "element") + } + valueType := t.Elem() + writeValues := writeRowsFuncOf(valueType, schema, valuePath, tagReplacements) + + return func(columns []ColumnBuffer, levels columnLevels, rows sparse.Array) { + if rows.Len() == 0 { + writeKeys(columns, levels, rows) + writeValues(columns, levels, rows) + return + } + + levels.repetitionDepth++ + makeMap := makeMapFuncOf(t) + + for i := range rows.Len() { + m := reflect.NewAt(t, rows.Index(i)).Elem() + n := m.Len() + + if n == 0 { + empty := sparse.Array{} + writeKeys(columns, levels, empty) + writeValues(columns, levels, empty) + continue + } + + elemLevels := levels + elemLevels.definitionLevel++ + + keys, values := makeMap(m).entries() + writeKeys(columns, elemLevels, keys.Slice(0, 1)) + writeValues(columns, elemLevels, values.Slice(0, 1)) + if n > 1 { + elemLevels.repetitionLevel = elemLevels.repetitionDepth + writeKeys(columns, elemLevels, keys.Slice(1, n)) + writeValues(columns, elemLevels, values.Slice(1, n)) + } + } + + } +} + +func writeRowsFuncOfJSON(t reflect.Type, schema *Schema, path columnPath) writeRowsFunc { + // If this is a string or a byte array, write directly. + if t.Kind() == reflect.String || (t.Kind() == reflect.Slice && t.Elem().Kind() == reflect.Uint8) { + column, _ := schema.Lookup(path...) + isOptional := column.Node.Optional() + + // This fast-path avoids the optional checks, so check here and wrap the writer + // if required. + writer := writeRowsFuncOfRequired(t, schema, path) + if isOptional { + writer = writeRowsFuncOfOptional(t, schema, path, writer) + } + return writer + } + + columnIndex := findColumnIndex(schema, schema, path) + if columnIndex == math.MaxUint16 { + // Empty group - return no-op function + return func(columns []ColumnBuffer, levels columnLevels, rows sparse.Array) { + // No-op: empty group has no columns to write + } + } + + return func(columns []ColumnBuffer, levels columnLevels, rows sparse.Array) { + if rows.Len() == 0 { + columns[columnIndex].writeNull(levels) + return + } + + b := memory.SliceBuffer[byte]{} + w := memory.SliceWriter{Buffer: &b} + defer b.Reset() + + for i := range rows.Len() { + v := reflect.NewAt(t, rows.Index(i)) + b.Resize(0) + + enc := json.NewEncoder(w) + enc.SetEscapeHTML(false) + + if err := enc.Encode(v.Interface()); err != nil { + panic(err) + } + + data := b.Slice() + columns[columnIndex].writeByteArray(levels, data[:len(data)-1]) + } + } +} + +func writeRowsFuncOfTime(_ reflect.Type, schema *Schema, path columnPath, tagReplacements []StructTagOption) writeRowsFunc { + t := reflect.TypeFor[int64]() + elemSize := uintptr(t.Size()) + writeRows := writeRowsFuncOf(t, schema, path, tagReplacements) + + col, _ := schema.Lookup(path...) + unit := Nanosecond.TimeUnit() + lt := col.Node.Type().LogicalType() + if lt != nil && lt.Timestamp != nil { + unit = lt.Timestamp.Unit + } + + // Check if the column is optional + isOptional := col.Node.Optional() + + return func(columns []ColumnBuffer, levels columnLevels, rows sparse.Array) { + if rows.Len() == 0 { + writeRows(columns, levels, rows) + return + } + + // If we're optional and the current definition level is already > 0, + // then we're in a pointer/nested context where writeRowsFuncOfPointer + // already handles optionality. + // + // Don't double-handle it here. For simple optional fields, + // definitionLevel starts at 0. + alreadyHandled := isOptional && levels.definitionLevel > 0 + + times := rows.TimeArray() + for i := range times.Len() { + t := times.Index(i) + + // For optional fields, check if the value is zero + // (unless already handled by pointer wrapper). + elemLevels := levels + if isOptional && !alreadyHandled && t.IsZero() { + // Write as NULL (don't increment definition level). + empty := sparse.Array{} + writeRows(columns, elemLevels, empty) + continue + } + + // For optional non-zero values, increment definition level + // (unless already handled). + if isOptional && !alreadyHandled { + elemLevels.definitionLevel++ + } + + var val int64 + switch { + case unit.Millis != nil: + val = t.UnixMilli() + case unit.Micros != nil: + val = t.UnixMicro() + default: + val = t.UnixNano() + } + + a := makeArray(reflectValueData(reflect.ValueOf(val)), 1, elemSize) + writeRows(columns, elemLevels, a) + } + } +} + +// countLeafColumns returns the number of leaf columns in a node +func countLeafColumns(node Node) int16 { + if node.Leaf() { + return 1 + } + count := int16(0) + for _, field := range node.Fields() { + count += countLeafColumns(field) + } + return count +} + +func writeRowsFuncFor[T any](schema *Schema, path columnPath) writeRowsFunc { + node := findByPath(schema, path) + if node == nil { + panic("parquet: column not found: " + path.String()) + } + + columnIndex := findColumnIndex(schema, node, path) + if columnIndex == math.MaxUint16 { + // Empty group - return no-op function + return func(columns []ColumnBuffer, levels columnLevels, rows sparse.Array) { + // No-op: empty group has no columns to write + } + } + + _, writeValue := writeValueFuncOf(columnIndex, node) + + return func(columns []ColumnBuffer, levels columnLevels, rows sparse.Array) { + for i := range rows.Len() { + p := rows.Index(i) + v := reflect.ValueOf((*T)(p)).Elem() + writeValue(columns, levels, v) + } + } +} diff --git a/vendor/github.com/parquet-go/parquet-go/column_chunk.go b/vendor/github.com/parquet-go/parquet-go/column_chunk.go new file mode 100644 index 000000000..ba1e3a59c --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/column_chunk.go @@ -0,0 +1,347 @@ +package parquet + +import ( + "errors" + "io" +) + +var ( + ErrMissingBloomFilter = errors.New("missing bloom filter") + ErrMissingColumnIndex = errors.New("missing column index") + ErrMissingOffsetIndex = errors.New("missing offset index") +) + +// The ColumnChunk interface represents individual columns of a row group. +type ColumnChunk interface { + // Returns the column type. + Type() Type + + // Returns the index of this column in its parent row group. + Column() int + + // Returns a reader exposing the pages of the column. + Pages() Pages + + // Returns the components of the page index for this column chunk, + // containing details about the content and location of pages within the + // chunk. + // + // Note that the returned value may be the same across calls to these + // methods, programs must treat those as read-only. + // + // If the column chunk does not have a column or offset index, the methods return + // ErrMissingColumnIndex or ErrMissingOffsetIndex respectively. + // + // Prior to v0.20, these methods did not return an error because the page index + // for a file was either fully read when the file was opened, or skipped + // completely using the parquet.SkipPageIndex option. Version v0.20 introduced a + // change that the page index can be read on-demand at any time, even if a file + // was opened with the parquet.SkipPageIndex option. Since reading the page index + // can fail, these methods now return an error. + ColumnIndex() (ColumnIndex, error) + OffsetIndex() (OffsetIndex, error) + BloomFilter() BloomFilter + + // Returns the number of values in the column chunk. + // + // This quantity may differ from the number of rows in the parent row group + // because repeated columns may hold zero or more values per row. + NumValues() int64 +} + +// AsyncColumnChunk returns a ColumnChunk that reads pages asynchronously. +func AsyncColumnChunk(columnChunk ColumnChunk) ColumnChunk { + return &asyncColumnChunk{columnChunk} +} + +type asyncColumnChunk struct { + ColumnChunk +} + +func (c *asyncColumnChunk) Pages() Pages { + return AsyncPages(c.ColumnChunk.Pages()) +} + +// NewColumnChunkRowReader creates a new ColumnChunkRowReader for the given +// column chunks. +func NewColumnChunkRowReader(columns []ColumnChunk) RowReadSeekCloser { + return newRowGroupRows(nil, columns, defaultValueBufferSize) +} + +// ColumnChunkValueReader is an interface for reading values from a column chunk. +type ColumnChunkValueReader interface { + ValueReader + RowSeeker + io.Closer +} + +// NewColumnChunkValueReader creates a new ColumnChunkValueReader for the given +// column chunk. +func NewColumnChunkValueReader(column ColumnChunk) ColumnChunkValueReader { + return &columnChunkValueReader{pages: column.Pages()} +} + +type columnChunkValueReader struct { + pages Pages + page Page + values ValueReader + detach bool +} + +func (r *columnChunkValueReader) clear() { + if r.page != nil { + if r.detach { + releaseAndDetachValues(r.page) + } else { + Release(r.page) + } + r.page = nil + r.values = nil + } +} + +func (r *columnChunkValueReader) Reset() { + if r.pages != nil { + // Ignore errors because we are resetting the reader, if the error + // persists we will see it on the next read, and otherwise we can + // read back from the beginning. + r.pages.SeekToRow(0) + } + r.clear() +} + +func (r *columnChunkValueReader) Close() error { + var err error + if r.pages != nil { + err = r.pages.Close() + r.pages = nil + } + r.clear() + return err +} + +func (r *columnChunkValueReader) ReadValues(values []Value) (int, error) { + if r.pages == nil { + return 0, io.EOF + } + + for { + if r.values == nil { + p, err := r.pages.ReadPage() + if err != nil { + return 0, err + } + r.page = p + r.values = p.Values() + } + + n, err := r.values.ReadValues(values) + if n > 0 { + return n, nil + } + if err == nil { + return 0, io.ErrNoProgress + } + if err != io.EOF { + return 0, err + } + r.clear() + } +} + +func (r *columnChunkValueReader) SeekToRow(rowIndex int64) error { + if r.pages == nil { + return io.ErrClosedPipe + } + if err := r.pages.SeekToRow(rowIndex); err != nil { + return err + } + r.clear() + return nil +} + +type pageAndValueWriter interface { + PageWriter + ValueWriter +} + +type readRowsFunc func(*rowGroupRows, []Row, byte) (int, error) + +func readRowsFuncOf(node Node, columnIndex int, repetitionDepth byte) (int, readRowsFunc) { + var read readRowsFunc + + if node.Repeated() { + repetitionDepth++ + } + + if node.Leaf() { + columnIndex, read = readRowsFuncOfLeaf(columnIndex, repetitionDepth) + } else { + columnIndex, read = readRowsFuncOfGroup(node, columnIndex, repetitionDepth) + } + + if node.Repeated() { + read = readRowsFuncOfRepeated(read, repetitionDepth) + } + + return columnIndex, read +} + +//go:noinline +func readRowsFuncOfRepeated(read readRowsFunc, repetitionDepth byte) readRowsFunc { + return func(r *rowGroupRows, rows []Row, repetitionLevel byte) (int, error) { + for i := range rows { + // Repeated columns have variable number of values, we must process + // them one row at a time because we cannot predict how many values + // need to be consumed in each iteration. + row := rows[i : i+1] + + // The first pass looks for values marking the beginning of a row by + // having a repetition level equal to the current level. + n, err := read(r, row, repetitionLevel) + if err != nil { + // The error here may likely be io.EOF, the read function may + // also have successfully read a row, which is indicated by a + // non-zero count. In this case, we increment the index to + // indicate to the caller than rows up to i+1 have been read. + if n > 0 { + i++ + } + return i, err + } + + // The read function may return no errors and also read no rows in + // case where it had more values to read but none corresponded to + // the current repetition level. This is an indication that we will + // not be able to read more rows at this stage, we must return to + // the caller to let it set the repetition level to its current + // depth, which may allow us to read more values when called again. + if n == 0 { + return i, nil + } + + // When we reach this stage, we have successfully read the first + // values of a row of repeated columns. We continue consuming more + // repeated values until we get the indication that we consumed + // them all (the read function returns zero and no errors). + for { + n, err := read(r, row, repetitionDepth) + if err != nil { + return i + 1, err + } + if n == 0 { + break + } + } + } + return len(rows), nil + } +} + +//go:noinline +func readRowsFuncOfGroup(node Node, columnIndex int, repetitionDepth byte) (int, readRowsFunc) { + fields := node.Fields() + + // Empty groups (groups with no fields) are valid structural elements + // that don't contain column data. This function shouldn't be called in + // practice since empty groups have no leaf columns to read from. + if len(fields) == 0 { + return columnIndex, func(r *rowGroupRows, rows []Row, repetitionLevel byte) (int, error) { + // Return 0 since there are no columns to read + return 0, nil + } + } + + if len(fields) == 1 { + // Small optimization for a somewhat common case of groups with a single + // column (like nested list elements for example); there is no need to + // loop over the group of a single element, we can simply skip to calling + // the inner read function. + return readRowsFuncOf(fields[0], columnIndex, repetitionDepth) + } + + group := make([]readRowsFunc, len(fields)) + for i := range group { + columnIndex, group[i] = readRowsFuncOf(fields[i], columnIndex, repetitionDepth) + } + + return columnIndex, func(r *rowGroupRows, rows []Row, repetitionLevel byte) (int, error) { + // When reading a group, we use the first column as an indicator of how + // may rows can be read during this call. + n, err := group[0](r, rows, repetitionLevel) + + if n > 0 { + // Read values for all rows that the group is able to consume. + // Getting io.EOF from calling the read functions indicate that + // we consumed all values of that particular column, but there may + // be more to read in other columns, therefore we must always read + // all columns and cannot stop on the first error. + for _, read := range group[1:] { + _, err2 := read(r, rows[:n], repetitionLevel) + if err2 != nil && err2 != io.EOF { + return 0, err2 + } + } + } + + return n, err + } +} + +//go:noinline +func readRowsFuncOfLeaf(columnIndex int, repetitionDepth byte) (int, readRowsFunc) { + var read readRowsFunc + + if repetitionDepth == 0 { + read = func(r *rowGroupRows, rows []Row, _ byte) (int, error) { + // When the repetition depth is zero, we know that there is exactly + // one value per row for this column, and therefore we can consume + // as many values as there are rows to fill. + col := &r.columns[columnIndex] + buf := r.buffer(columnIndex) + + for i := range rows { + if col.offset == col.length { + n, err := col.reader.ReadValues(buf) + col.offset = 0 + col.length = int32(n) + if n == 0 && err != nil { + return 0, err + } + } + + rows[i] = append(rows[i], buf[col.offset]) + col.offset++ + } + + return len(rows), nil + } + } else { + read = func(r *rowGroupRows, rows []Row, repetitionLevel byte) (int, error) { + // When the repetition depth is not zero, we know that we will be + // called with a single row as input. We attempt to read at most one + // value of a single row and return to the caller. + col := &r.columns[columnIndex] + buf := r.buffer(columnIndex) + + if col.offset == col.length { + n, err := col.reader.ReadValues(buf) + col.offset = 0 + col.length = int32(n) + if n == 0 && err != nil { + return 0, err + } + } + + if buf[col.offset].repetitionLevel != repetitionLevel { + return 0, nil + } + + rows[0] = append(rows[0], buf[col.offset]) + col.offset++ + return 1, nil + } + } + + return columnIndex + 1, read +} diff --git a/vendor/github.com/parquet-go/parquet-go/column_index.go b/vendor/github.com/parquet-go/parquet-go/column_index.go new file mode 100644 index 000000000..4455a98aa --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/column_index.go @@ -0,0 +1,774 @@ +package parquet + +import ( + "github.com/parquet-go/bitpack/unsafecast" + "github.com/parquet-go/parquet-go/deprecated" + "github.com/parquet-go/parquet-go/encoding/plain" + "github.com/parquet-go/parquet-go/format" +) + +type ColumnIndex interface { + // NumPages returns the number of paged in the column index. + NumPages() int + + // Returns the number of null values in the page at the given index. + NullCount(int) int64 + + // Tells whether the page at the given index contains null values only. + NullPage(int) bool + + // PageIndex return min/max bounds for the page at the given index in the + // column. + MinValue(int) Value + MaxValue(int) Value + + // IsAscending returns true if the column index min/max values are sorted + // in ascending order (based on the ordering rules of the column's logical + // type). + IsAscending() bool + + // IsDescending returns true if the column index min/max values are sorted + // in descending order (based on the ordering rules of the column's logical + // type). + IsDescending() bool +} + +// NewColumnIndex constructs a ColumnIndex instance from the given parquet +// format column index. The kind argument configures the type of values +func NewColumnIndex(kind Kind, index *format.ColumnIndex) ColumnIndex { + return &formatColumnIndex{ + kind: kind, + index: index, + } +} + +type formatColumnIndex struct { + kind Kind + index *format.ColumnIndex +} + +func (f *formatColumnIndex) NumPages() int { + return len(f.index.MinValues) +} + +func (f *formatColumnIndex) NullCount(i int) int64 { + if len(f.index.NullCounts) > 0 { + return f.index.NullCounts[i] + } + return 0 +} + +func (f *formatColumnIndex) NullPage(i int) bool { + return len(f.index.NullPages) > 0 && f.index.NullPages[i] +} + +func (f *formatColumnIndex) MinValue(i int) Value { + if f.NullPage(i) { + return Value{} + } + return f.kind.Value(f.index.MinValues[i]) +} + +func (f *formatColumnIndex) MaxValue(i int) Value { + if f.NullPage(i) { + return Value{} + } + return f.kind.Value(f.index.MaxValues[i]) +} + +func (f *formatColumnIndex) IsAscending() bool { + return f.index.BoundaryOrder == format.Ascending +} + +func (f *formatColumnIndex) IsDescending() bool { + return f.index.BoundaryOrder == format.Descending +} + +type FileColumnIndex struct { + index *format.ColumnIndex + kind Kind +} + +func (i *FileColumnIndex) NumPages() int { + return len(i.index.NullPages) +} + +func (i *FileColumnIndex) NullCount(j int) int64 { + if len(i.index.NullCounts) > 0 { + return i.index.NullCounts[j] + } + return 0 +} + +func (i *FileColumnIndex) NullPage(j int) bool { + return isNullPage(j, i.index) +} + +func (i *FileColumnIndex) MinValue(j int) Value { + if i.NullPage(j) { + return Value{} + } + return i.makeValue(i.index.MinValues[j]) +} + +func (i *FileColumnIndex) MaxValue(j int) Value { + if i.NullPage(j) { + return Value{} + } + return i.makeValue(i.index.MaxValues[j]) +} + +func (i *FileColumnIndex) IsAscending() bool { + return i.index.BoundaryOrder == format.Ascending +} + +func (i *FileColumnIndex) IsDescending() bool { + return i.index.BoundaryOrder == format.Descending +} + +func (i *FileColumnIndex) makeValue(b []byte) Value { + return i.kind.Value(b) +} + +func isNullPage(j int, index *format.ColumnIndex) bool { + return len(index.NullPages) > 0 && index.NullPages[j] +} + +type emptyColumnIndex struct{} + +func (emptyColumnIndex) NumPages() int { return 0 } +func (emptyColumnIndex) NullCount(int) int64 { return 0 } +func (emptyColumnIndex) NullPage(int) bool { return false } +func (emptyColumnIndex) MinValue(int) Value { return Value{} } +func (emptyColumnIndex) MaxValue(int) Value { return Value{} } +func (emptyColumnIndex) IsAscending() bool { return false } +func (emptyColumnIndex) IsDescending() bool { return false } + +type booleanColumnIndex struct{ page *booleanPage } + +func (i booleanColumnIndex) NumPages() int { return 1 } +func (i booleanColumnIndex) NullCount(int) int64 { return 0 } +func (i booleanColumnIndex) NullPage(int) bool { return false } +func (i booleanColumnIndex) MinValue(int) Value { return makeValueBoolean(i.page.min()) } +func (i booleanColumnIndex) MaxValue(int) Value { return makeValueBoolean(i.page.max()) } +func (i booleanColumnIndex) IsAscending() bool { return false } +func (i booleanColumnIndex) IsDescending() bool { return false } + +type int32ColumnIndex struct{ page *int32Page } + +func (i int32ColumnIndex) NumPages() int { return 1 } +func (i int32ColumnIndex) NullCount(int) int64 { return 0 } +func (i int32ColumnIndex) NullPage(int) bool { return false } +func (i int32ColumnIndex) MinValue(int) Value { return makeValueInt32(i.page.min()) } +func (i int32ColumnIndex) MaxValue(int) Value { return makeValueInt32(i.page.max()) } +func (i int32ColumnIndex) IsAscending() bool { return false } +func (i int32ColumnIndex) IsDescending() bool { return false } + +type int64ColumnIndex struct{ page *int64Page } + +func (i int64ColumnIndex) NumPages() int { return 1 } +func (i int64ColumnIndex) NullCount(int) int64 { return 0 } +func (i int64ColumnIndex) NullPage(int) bool { return false } +func (i int64ColumnIndex) MinValue(int) Value { return makeValueInt64(i.page.min()) } +func (i int64ColumnIndex) MaxValue(int) Value { return makeValueInt64(i.page.max()) } +func (i int64ColumnIndex) IsAscending() bool { return false } +func (i int64ColumnIndex) IsDescending() bool { return false } + +type int96ColumnIndex struct{ page *int96Page } + +func (i int96ColumnIndex) NumPages() int { return 1 } +func (i int96ColumnIndex) NullCount(int) int64 { return 0 } +func (i int96ColumnIndex) NullPage(int) bool { return false } +func (i int96ColumnIndex) MinValue(int) Value { return makeValueInt96(i.page.min()) } +func (i int96ColumnIndex) MaxValue(int) Value { return makeValueInt96(i.page.max()) } +func (i int96ColumnIndex) IsAscending() bool { return false } +func (i int96ColumnIndex) IsDescending() bool { return false } + +type floatColumnIndex struct{ page *floatPage } + +func (i floatColumnIndex) NumPages() int { return 1 } +func (i floatColumnIndex) NullCount(int) int64 { return 0 } +func (i floatColumnIndex) NullPage(int) bool { return false } +func (i floatColumnIndex) MinValue(int) Value { return makeValueFloat(i.page.min()) } +func (i floatColumnIndex) MaxValue(int) Value { return makeValueFloat(i.page.max()) } +func (i floatColumnIndex) IsAscending() bool { return false } +func (i floatColumnIndex) IsDescending() bool { return false } + +type doubleColumnIndex struct{ page *doublePage } + +func (i doubleColumnIndex) NumPages() int { return 1 } +func (i doubleColumnIndex) NullCount(int) int64 { return 0 } +func (i doubleColumnIndex) NullPage(int) bool { return false } +func (i doubleColumnIndex) MinValue(int) Value { return makeValueDouble(i.page.min()) } +func (i doubleColumnIndex) MaxValue(int) Value { return makeValueDouble(i.page.max()) } +func (i doubleColumnIndex) IsAscending() bool { return false } +func (i doubleColumnIndex) IsDescending() bool { return false } + +type byteArrayColumnIndex struct{ page *byteArrayPage } + +func (i byteArrayColumnIndex) NumPages() int { return 1 } +func (i byteArrayColumnIndex) NullCount(int) int64 { return 0 } +func (i byteArrayColumnIndex) NullPage(int) bool { return false } +func (i byteArrayColumnIndex) MinValue(int) Value { return makeValueBytes(ByteArray, i.page.min()) } +func (i byteArrayColumnIndex) MaxValue(int) Value { return makeValueBytes(ByteArray, i.page.max()) } +func (i byteArrayColumnIndex) IsAscending() bool { return false } +func (i byteArrayColumnIndex) IsDescending() bool { return false } + +type fixedLenByteArrayColumnIndex struct{ page *fixedLenByteArrayPage } + +func (i fixedLenByteArrayColumnIndex) NumPages() int { return 1 } +func (i fixedLenByteArrayColumnIndex) NullCount(int) int64 { return 0 } +func (i fixedLenByteArrayColumnIndex) NullPage(int) bool { return false } +func (i fixedLenByteArrayColumnIndex) MinValue(int) Value { + return makeValueBytes(FixedLenByteArray, i.page.min()) +} +func (i fixedLenByteArrayColumnIndex) MaxValue(int) Value { + return makeValueBytes(FixedLenByteArray, i.page.max()) +} +func (i fixedLenByteArrayColumnIndex) IsAscending() bool { return false } +func (i fixedLenByteArrayColumnIndex) IsDescending() bool { return false } + +type uint32ColumnIndex struct{ page *uint32Page } + +func (i uint32ColumnIndex) NumPages() int { return 1 } +func (i uint32ColumnIndex) NullCount(int) int64 { return 0 } +func (i uint32ColumnIndex) NullPage(int) bool { return false } +func (i uint32ColumnIndex) MinValue(int) Value { return makeValueUint32(i.page.min()) } +func (i uint32ColumnIndex) MaxValue(int) Value { return makeValueUint32(i.page.max()) } +func (i uint32ColumnIndex) IsAscending() bool { return false } +func (i uint32ColumnIndex) IsDescending() bool { return false } + +type uint64ColumnIndex struct{ page *uint64Page } + +func (i uint64ColumnIndex) NumPages() int { return 1 } +func (i uint64ColumnIndex) NullCount(int) int64 { return 0 } +func (i uint64ColumnIndex) NullPage(int) bool { return false } +func (i uint64ColumnIndex) MinValue(int) Value { return makeValueUint64(i.page.min()) } +func (i uint64ColumnIndex) MaxValue(int) Value { return makeValueUint64(i.page.max()) } +func (i uint64ColumnIndex) IsAscending() bool { return false } +func (i uint64ColumnIndex) IsDescending() bool { return false } + +type be128ColumnIndex struct{ page *be128Page } + +func (i be128ColumnIndex) NumPages() int { return 1 } +func (i be128ColumnIndex) NullCount(int) int64 { return 0 } +func (i be128ColumnIndex) NullPage(int) bool { return false } +func (i be128ColumnIndex) MinValue(int) Value { return makeValueBytes(FixedLenByteArray, i.page.min()) } +func (i be128ColumnIndex) MaxValue(int) Value { return makeValueBytes(FixedLenByteArray, i.page.max()) } +func (i be128ColumnIndex) IsAscending() bool { return false } +func (i be128ColumnIndex) IsDescending() bool { return false } + +// The ColumnIndexer interface is implemented by types that support generating +// parquet column indexes. +// +// The package does not export any types that implement this interface, programs +// must call NewColumnIndexer on a Type instance to construct column indexers. +type ColumnIndexer interface { + // Resets the column indexer state. + Reset() + + // Add a page to the column indexer. + IndexPage(numValues, numNulls int64, min, max Value) + + // Generates a format.ColumnIndex value from the current state of the + // column indexer. + // + // The returned value may reference internal buffers, in which case the + // values remain valid until the next call to IndexPage or Reset on the + // column indexer. + ColumnIndex() format.ColumnIndex +} + +type baseColumnIndexer struct { + nullPages []bool + nullCounts []int64 +} + +func (i *baseColumnIndexer) reset() { + i.nullPages = i.nullPages[:0] + i.nullCounts = i.nullCounts[:0] +} + +func (i *baseColumnIndexer) observe(numValues, numNulls int64) { + i.nullPages = append(i.nullPages, numValues == numNulls) + i.nullCounts = append(i.nullCounts, numNulls) +} + +func (i *baseColumnIndexer) columnIndex(minValues, maxValues [][]byte, minOrder, maxOrder int) format.ColumnIndex { + nullPages := make([]bool, len(i.nullPages)) + copy(nullPages, i.nullPages) + nullCounts := make([]int64, len(i.nullCounts)) + copy(nullCounts, i.nullCounts) + return format.ColumnIndex{ + NullPages: nullPages, + NullCounts: nullCounts, + MinValues: minValues, + MaxValues: maxValues, + BoundaryOrder: boundaryOrderOf(minOrder, maxOrder), + } +} + +type booleanColumnIndexer struct { + baseColumnIndexer + minValues []bool + maxValues []bool +} + +func newBooleanColumnIndexer() *booleanColumnIndexer { + return new(booleanColumnIndexer) +} + +func (i *booleanColumnIndexer) Reset() { + i.reset() + i.minValues = i.minValues[:0] + i.maxValues = i.maxValues[:0] +} + +func (i *booleanColumnIndexer) IndexPage(numValues, numNulls int64, min, max Value) { + i.observe(numValues, numNulls) + i.minValues = append(i.minValues, min.boolean()) + i.maxValues = append(i.maxValues, max.boolean()) +} + +func (i *booleanColumnIndexer) ColumnIndex() format.ColumnIndex { + return i.columnIndex( + splitFixedLenByteArrays(unsafecast.Slice[byte](i.minValues), 1), + splitFixedLenByteArrays(unsafecast.Slice[byte](i.maxValues), 1), + orderOfBool(i.minValues), + orderOfBool(i.maxValues), + ) +} + +type int32ColumnIndexer struct { + baseColumnIndexer + minValues []int32 + maxValues []int32 +} + +func newInt32ColumnIndexer() *int32ColumnIndexer { + return new(int32ColumnIndexer) +} + +func (i *int32ColumnIndexer) Reset() { + i.reset() + i.minValues = i.minValues[:0] + i.maxValues = i.maxValues[:0] +} + +func (i *int32ColumnIndexer) IndexPage(numValues, numNulls int64, min, max Value) { + i.observe(numValues, numNulls) + i.minValues = append(i.minValues, min.int32()) + i.maxValues = append(i.maxValues, max.int32()) +} + +func (i *int32ColumnIndexer) ColumnIndex() format.ColumnIndex { + return i.columnIndex( + splitFixedLenByteArrays(columnIndexInt32Values(i.minValues), 4), + splitFixedLenByteArrays(columnIndexInt32Values(i.maxValues), 4), + orderOfInt32(i.minValues), + orderOfInt32(i.maxValues), + ) +} + +type int64ColumnIndexer struct { + baseColumnIndexer + minValues []int64 + maxValues []int64 +} + +func newInt64ColumnIndexer() *int64ColumnIndexer { + return new(int64ColumnIndexer) +} + +func (i *int64ColumnIndexer) Reset() { + i.reset() + i.minValues = i.minValues[:0] + i.maxValues = i.maxValues[:0] +} + +func (i *int64ColumnIndexer) IndexPage(numValues, numNulls int64, min, max Value) { + i.observe(numValues, numNulls) + i.minValues = append(i.minValues, min.int64()) + i.maxValues = append(i.maxValues, max.int64()) +} + +func (i *int64ColumnIndexer) ColumnIndex() format.ColumnIndex { + return i.columnIndex( + splitFixedLenByteArrays(columnIndexInt64Values(i.minValues), 8), + splitFixedLenByteArrays(columnIndexInt64Values(i.maxValues), 8), + orderOfInt64(i.minValues), + orderOfInt64(i.maxValues), + ) +} + +type int96ColumnIndexer struct { + baseColumnIndexer + minValues []deprecated.Int96 + maxValues []deprecated.Int96 +} + +func newInt96ColumnIndexer() *int96ColumnIndexer { + return new(int96ColumnIndexer) +} + +func (i *int96ColumnIndexer) Reset() { + i.reset() + i.minValues = i.minValues[:0] + i.maxValues = i.maxValues[:0] +} + +func (i *int96ColumnIndexer) IndexPage(numValues, numNulls int64, min, max Value) { + i.observe(numValues, numNulls) + i.minValues = append(i.minValues, min.Int96()) + i.maxValues = append(i.maxValues, max.Int96()) +} + +func (i *int96ColumnIndexer) ColumnIndex() format.ColumnIndex { + return i.columnIndex( + splitFixedLenByteArrays(columnIndexInt96Values(i.minValues), 12), + splitFixedLenByteArrays(columnIndexInt96Values(i.maxValues), 12), + deprecated.OrderOfInt96(i.minValues), + deprecated.OrderOfInt96(i.maxValues), + ) +} + +type floatColumnIndexer struct { + baseColumnIndexer + minValues []float32 + maxValues []float32 +} + +func newFloatColumnIndexer() *floatColumnIndexer { + return new(floatColumnIndexer) +} + +func (i *floatColumnIndexer) Reset() { + i.reset() + i.minValues = i.minValues[:0] + i.maxValues = i.maxValues[:0] +} + +func (i *floatColumnIndexer) IndexPage(numValues, numNulls int64, min, max Value) { + i.observe(numValues, numNulls) + i.minValues = append(i.minValues, min.float()) + i.maxValues = append(i.maxValues, max.float()) +} + +func (i *floatColumnIndexer) ColumnIndex() format.ColumnIndex { + return i.columnIndex( + splitFixedLenByteArrays(columnIndexFloatValues(i.minValues), 4), + splitFixedLenByteArrays(columnIndexFloatValues(i.maxValues), 4), + orderOfFloat32(i.minValues), + orderOfFloat32(i.maxValues), + ) +} + +type doubleColumnIndexer struct { + baseColumnIndexer + minValues []float64 + maxValues []float64 +} + +func newDoubleColumnIndexer() *doubleColumnIndexer { + return new(doubleColumnIndexer) +} + +func (i *doubleColumnIndexer) Reset() { + i.reset() + i.minValues = i.minValues[:0] + i.maxValues = i.maxValues[:0] +} + +func (i *doubleColumnIndexer) IndexPage(numValues, numNulls int64, min, max Value) { + i.observe(numValues, numNulls) + i.minValues = append(i.minValues, min.double()) + i.maxValues = append(i.maxValues, max.double()) +} + +func (i *doubleColumnIndexer) ColumnIndex() format.ColumnIndex { + return i.columnIndex( + splitFixedLenByteArrays(columnIndexDoubleValues(i.minValues), 8), + splitFixedLenByteArrays(columnIndexDoubleValues(i.maxValues), 8), + orderOfFloat64(i.minValues), + orderOfFloat64(i.maxValues), + ) +} + +type byteArrayColumnIndexer struct { + baseColumnIndexer + sizeLimit int + minValues []byte + maxValues []byte +} + +func newByteArrayColumnIndexer(sizeLimit int) *byteArrayColumnIndexer { + return &byteArrayColumnIndexer{sizeLimit: sizeLimit} +} + +func (i *byteArrayColumnIndexer) Reset() { + i.reset() + i.minValues = i.minValues[:0] + i.maxValues = i.maxValues[:0] +} + +func (i *byteArrayColumnIndexer) IndexPage(numValues, numNulls int64, min, max Value) { + i.observe(numValues, numNulls) + i.minValues = plain.AppendByteArray(i.minValues, min.byteArray()) + i.maxValues = plain.AppendByteArray(i.maxValues, max.byteArray()) +} + +func (i *byteArrayColumnIndexer) ColumnIndex() format.ColumnIndex { + minValues := splitByteArrays(i.minValues) + maxValues := splitByteArrays(i.maxValues) + if sizeLimit := i.sizeLimit; sizeLimit > 0 { + for i, v := range minValues { + minValues[i] = truncateLargeMinByteArrayValue(v, sizeLimit) + } + for i, v := range maxValues { + maxValues[i] = truncateLargeMaxByteArrayValue(v, sizeLimit) + } + } + return i.columnIndex( + minValues, + maxValues, + orderOfBytes(minValues), + orderOfBytes(maxValues), + ) +} + +type fixedLenByteArrayColumnIndexer struct { + baseColumnIndexer + size int + sizeLimit int + minValues []byte + maxValues []byte +} + +func newFixedLenByteArrayColumnIndexer(size, sizeLimit int) *fixedLenByteArrayColumnIndexer { + return &fixedLenByteArrayColumnIndexer{ + size: size, + sizeLimit: sizeLimit, + } +} + +func (i *fixedLenByteArrayColumnIndexer) Reset() { + i.reset() + i.minValues = i.minValues[:0] + i.maxValues = i.maxValues[:0] +} + +func (i *fixedLenByteArrayColumnIndexer) IndexPage(numValues, numNulls int64, min, max Value) { + i.observe(numValues, numNulls) + i.minValues = append(i.minValues, min.byteArray()...) + i.maxValues = append(i.maxValues, max.byteArray()...) +} + +func (i *fixedLenByteArrayColumnIndexer) ColumnIndex() format.ColumnIndex { + minValues := splitFixedLenByteArrays(i.minValues, i.size) + maxValues := splitFixedLenByteArrays(i.maxValues, i.size) + if sizeLimit := i.sizeLimit; sizeLimit > 0 { + for i, v := range minValues { + minValues[i] = truncateLargeMinByteArrayValue(v, sizeLimit) + } + for i, v := range maxValues { + maxValues[i] = truncateLargeMaxByteArrayValue(v, sizeLimit) + } + } + return i.columnIndex( + minValues, + maxValues, + orderOfBytes(minValues), + orderOfBytes(maxValues), + ) +} + +type uint32ColumnIndexer struct { + baseColumnIndexer + minValues []uint32 + maxValues []uint32 +} + +func newUint32ColumnIndexer() *uint32ColumnIndexer { + return new(uint32ColumnIndexer) +} + +func (i *uint32ColumnIndexer) Reset() { + i.reset() + i.minValues = i.minValues[:0] + i.maxValues = i.maxValues[:0] +} + +func (i *uint32ColumnIndexer) IndexPage(numValues, numNulls int64, min, max Value) { + i.observe(numValues, numNulls) + i.minValues = append(i.minValues, min.uint32()) + i.maxValues = append(i.maxValues, max.uint32()) +} + +func (i *uint32ColumnIndexer) ColumnIndex() format.ColumnIndex { + return i.columnIndex( + splitFixedLenByteArrays(columnIndexUint32Values(i.minValues), 4), + splitFixedLenByteArrays(columnIndexUint32Values(i.maxValues), 4), + orderOfUint32(i.minValues), + orderOfUint32(i.maxValues), + ) +} + +type uint64ColumnIndexer struct { + baseColumnIndexer + minValues []uint64 + maxValues []uint64 +} + +func newUint64ColumnIndexer() *uint64ColumnIndexer { + return new(uint64ColumnIndexer) +} + +func (i *uint64ColumnIndexer) Reset() { + i.reset() + i.minValues = i.minValues[:0] + i.maxValues = i.maxValues[:0] +} + +func (i *uint64ColumnIndexer) IndexPage(numValues, numNulls int64, min, max Value) { + i.observe(numValues, numNulls) + i.minValues = append(i.minValues, min.uint64()) + i.maxValues = append(i.maxValues, max.uint64()) +} + +func (i *uint64ColumnIndexer) ColumnIndex() format.ColumnIndex { + return i.columnIndex( + splitFixedLenByteArrays(columnIndexUint64Values(i.minValues), 8), + splitFixedLenByteArrays(columnIndexUint64Values(i.maxValues), 8), + orderOfUint64(i.minValues), + orderOfUint64(i.maxValues), + ) +} + +type be128ColumnIndexer struct { + baseColumnIndexer + minValues [][16]byte + maxValues [][16]byte +} + +func newBE128ColumnIndexer() *be128ColumnIndexer { + return new(be128ColumnIndexer) +} + +func (i *be128ColumnIndexer) Reset() { + i.reset() + i.minValues = i.minValues[:0] + i.maxValues = i.maxValues[:0] +} + +func (i *be128ColumnIndexer) IndexPage(numValues, numNulls int64, min, max Value) { + i.observe(numValues, numNulls) + if !min.IsNull() { + i.minValues = append(i.minValues, *(*[16]byte)(min.byteArray())) + } + if !max.IsNull() { + i.maxValues = append(i.maxValues, *(*[16]byte)(max.byteArray())) + } +} + +func (i *be128ColumnIndexer) ColumnIndex() format.ColumnIndex { + minValues := splitFixedLenByteArrays(unsafecast.Slice[byte](i.minValues), 16) + maxValues := splitFixedLenByteArrays(unsafecast.Slice[byte](i.maxValues), 16) + return i.columnIndex( + minValues, + maxValues, + orderOfBytes(minValues), + orderOfBytes(maxValues), + ) +} + +func truncateLargeMinByteArrayValue(value []byte, sizeLimit int) []byte { + if len(value) > sizeLimit { + value = value[:sizeLimit] + } + return value +} + +// truncateLargeMaxByteArrayValue truncates the given byte array to the given size limit. +// If the given byte array is truncated, it is incremented by 1 in place. +func truncateLargeMaxByteArrayValue(value []byte, sizeLimit int) []byte { + if len(value) > sizeLimit { + value = value[:sizeLimit] + incrementByteArrayInplace(value) + } + return value +} + +// incrementByteArray increments the given byte array by 1. +// Reference: https://github.com/apache/parquet-java/blob/master/parquet-column/src/main/java/org/apache/parquet/internal/column/columnindex/BinaryTruncator.java#L124 +func incrementByteArrayInplace(value []byte) { + for i := len(value) - 1; i >= 0; i-- { + value[i]++ + if value[i] != 0 { // Did not overflow: 0xFF -> 0x00 + return + } + } + // Fully overflowed, so restore all to 0xFF + for i := range value { + value[i] = 0xFF + } +} + +func splitByteArrays(data []byte) [][]byte { + length := 0 + plain.RangeByteArray(data, func([]byte) error { + length++ + return nil + }) + buffer := make([]byte, 0, len(data)-(4*length)) + values := make([][]byte, 0, length) + plain.RangeByteArray(data, func(value []byte) error { + offset := len(buffer) + buffer = append(buffer, value...) + values = append(values, buffer[offset:]) + return nil + }) + return values +} + +func splitFixedLenByteArrays(data []byte, size int) [][]byte { + data = copyBytes(data) + values := make([][]byte, len(data)/size) + for i := range values { + j := (i + 0) * size + k := (i + 1) * size + values[i] = data[j:k:k] + } + return values +} + +func boundaryOrderOf(minOrder, maxOrder int) format.BoundaryOrder { + if minOrder == maxOrder { + switch { + case minOrder > 0: + return format.Ascending + case minOrder < 0: + return format.Descending + } + } + return format.Unordered +} + +type nullColumnIndexer struct { + baseColumnIndexer +} + +func newNullColumnIndexer() *nullColumnIndexer { + return new(nullColumnIndexer) +} + +func (i *nullColumnIndexer) Reset() { + i.reset() +} + +func (i *nullColumnIndexer) IndexPage(numValues, numNulls int64, min, max Value) { + i.observe(numValues, numNulls) +} + +func (i *nullColumnIndexer) ColumnIndex() format.ColumnIndex { + return i.columnIndex(nil, nil, 0, 0) +} diff --git a/vendor/github.com/parquet-go/parquet-go/column_index_be.go b/vendor/github.com/parquet-go/parquet-go/column_index_be.go new file mode 100644 index 000000000..ace82ae32 --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/column_index_be.go @@ -0,0 +1,84 @@ +// This file gets added on all the big-endian CPU architectures. + +//go:build armbe || arm64be || m68k || mips || mips64 || mips64p32 || ppc || ppc64 || s390 || s390x || shbe || sparc || sparc64 + +package parquet + +import ( + "encoding/binary" + "math" + + "github.com/parquet-go/parquet-go/deprecated" +) + +func columnIndexInt32Values(values []int32) []byte { + buf := make([]byte, len(values)*4) + idx := 0 + for k := range len(values) { + binary.LittleEndian.PutUint32(buf[idx:(4+idx)], uint32(values[k])) + idx += 4 + } + return buf +} + +func columnIndexInt64Values(values []int64) []byte { + buf := make([]byte, len(values)*8) + idx := 0 + for k := range len(values) { + binary.LittleEndian.PutUint64(buf[idx:(8+idx)], uint64(values[k])) + idx += 8 + } + return buf +} + +func columnIndexInt96Values(values []deprecated.Int96) []byte { + buf := make([]byte, len(values)*12) + idx := 0 + for k := range len(values) { + binary.LittleEndian.PutUint32(buf[idx:(4+idx)], uint32(values[k][0])) + binary.LittleEndian.PutUint32(buf[(4+idx):(8+idx)], uint32(values[k][1])) + binary.LittleEndian.PutUint32(buf[(8+idx):(12+idx)], uint32(values[k][2])) + idx += 12 + } + return buf +} + +func columnIndexFloatValues(values []float32) []byte { + buf := make([]byte, len(values)*4) + idx := 0 + for k := range len(values) { + binary.LittleEndian.PutUint32(buf[idx:(4+idx)], math.Float32bits(values[k])) + idx += 4 + } + return buf +} + +func columnIndexDoubleValues(values []float64) []byte { + buf := make([]byte, len(values)*8) + idx := 0 + for k := range len(values) { + binary.LittleEndian.PutUint64(buf[idx:(8+idx)], math.Float64bits(values[k])) + idx += 8 + } + return buf +} + +func columnIndexUint32Values(values []uint32) []byte { + buf := make([]byte, len(values)*4) + idx := 0 + for k := range len(values) { + binary.LittleEndian.PutUint32(buf[idx:(4+idx)], values[k]) + idx += 4 + } + return buf +} + +func columnIndexUint64Values(values []uint64) []byte { + buf := make([]byte, len(values)*8) + idx := 0 + for k := range len(values) { + binary.LittleEndian.PutUint64(buf[idx:(8+idx)], values[k]) + idx += 8 + } + return buf +} diff --git a/vendor/github.com/parquet-go/parquet-go/column_index_le.go b/vendor/github.com/parquet-go/parquet-go/column_index_le.go new file mode 100644 index 000000000..ff842efbb --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/column_index_le.go @@ -0,0 +1,38 @@ +// This file gets added on all the little-endian CPU architectures. + +//go:build 386 || amd64 || amd64p32 || alpha || arm || arm64 || loong64 || mipsle || mips64le || mips64p32le || nios2 || ppc64le || riscv || riscv64 || sh || wasm + +package parquet + +import ( + "github.com/parquet-go/bitpack/unsafecast" + "github.com/parquet-go/parquet-go/deprecated" +) + +func columnIndexInt32Values(values []int32) []byte { + return unsafecast.Slice[byte](values) +} + +func columnIndexInt64Values(values []int64) []byte { + return unsafecast.Slice[byte](values) +} + +func columnIndexInt96Values(values []deprecated.Int96) []byte { + return unsafecast.Slice[byte](values) +} + +func columnIndexFloatValues(values []float32) []byte { + return unsafecast.Slice[byte](values) +} + +func columnIndexDoubleValues(values []float64) []byte { + return unsafecast.Slice[byte](values) +} + +func columnIndexUint32Values(values []uint32) []byte { + return unsafecast.Slice[byte](values) +} + +func columnIndexUint64Values(values []uint64) []byte { + return unsafecast.Slice[byte](values) +} diff --git a/vendor/github.com/parquet-go/parquet-go/column_mapping.go b/vendor/github.com/parquet-go/parquet-go/column_mapping.go new file mode 100644 index 000000000..d1708daa9 --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/column_mapping.go @@ -0,0 +1,90 @@ +package parquet + +import "math" + +// LeafColumn is a struct type representing leaf columns of a parquet schema. +type LeafColumn struct { + Node Node + Path []string + ColumnIndex int + MaxRepetitionLevel int + MaxDefinitionLevel int +} + +func columnMappingOf(schema Node) (mapping columnMappingGroup, columns [][]string) { + mapping = make(columnMappingGroup) + columns = make([][]string, 0, 16) + + forEachLeafColumnOf(schema, func(leaf leafColumn) { + path := make(columnPath, len(leaf.path)) + copy(path, leaf.path) + columns = append(columns, path) + + group := mapping + for len(path) > 1 { + columnName := path[0] + g, ok := group[columnName].(columnMappingGroup) + if !ok { + g = make(columnMappingGroup) + group[columnName] = g + } + group, path = g, path[1:] + } + + leaf.path = path // use the copy + group[path[0]] = &columnMappingLeaf{column: leaf} + }) + + return mapping, columns +} + +type columnMapping interface { + lookup(path columnPath) leafColumn +} + +type columnMappingGroup map[string]columnMapping + +func (group columnMappingGroup) lookup(path columnPath) leafColumn { + if len(path) > 0 { + c, ok := group[path[0]] + if ok { + return c.lookup(path[1:]) + } + } + return leafColumn{columnIndex: math.MaxUint16} +} + +func (group columnMappingGroup) lookupClosest(path columnPath) leafColumn { + for len(path) > 0 { + g, ok := group[path[0]].(columnMappingGroup) + if ok { + group, path = g, path[1:] + } else { + firstName := "" + firstLeaf := (*columnMappingLeaf)(nil) + for name, child := range group { + if leaf, ok := child.(*columnMappingLeaf); ok { + if firstLeaf == nil || name < firstName { + firstName, firstLeaf = name, leaf + } + } + } + if firstLeaf != nil { + return firstLeaf.column + } + break + } + } + return leafColumn{columnIndex: math.MaxUint16} +} + +type columnMappingLeaf struct { + column leafColumn +} + +func (leaf *columnMappingLeaf) lookup(path columnPath) leafColumn { + if len(path) == 0 { + return leaf.column + } + return leafColumn{columnIndex: math.MaxUint16} +} diff --git a/vendor/github.com/parquet-go/parquet-go/column_path.go b/vendor/github.com/parquet-go/parquet-go/column_path.go new file mode 100644 index 000000000..f1fa4271f --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/column_path.go @@ -0,0 +1,108 @@ +package parquet + +import ( + "slices" + "strings" +) + +type columnPath []string + +func (path columnPath) append(names ...string) columnPath { + return slices.Concat(path, names) +} + +func (path columnPath) equal(other columnPath) bool { + return stringsAreEqual(path, other) +} + +func (path columnPath) less(other columnPath) bool { + return stringsAreOrdered(path, other) +} + +func (path columnPath) String() string { + return strings.Join(path, ".") +} + +func stringsAreEqual(strings1, strings2 []string) bool { + if len(strings1) != len(strings2) { + return false + } + + for i := range strings1 { + if strings1[i] != strings2[i] { + return false + } + } + + return true +} + +func stringsAreOrdered(strings1, strings2 []string) bool { + n := min(len(strings1), len(strings2)) + + for i := range n { + if strings1[i] >= strings2[i] { + return false + } + } + + return len(strings1) <= len(strings2) +} + +type leafColumn struct { + node Node + path columnPath + maxRepetitionLevel byte + maxDefinitionLevel byte + columnIndex uint16 +} + +func forEachLeafColumnOf(node Node, do func(leafColumn)) { + forEachLeafColumn(node, nil, 0, 0, 0, do) +} + +func forEachLeafColumn(node Node, path columnPath, columnIndex, maxRepetitionLevel, maxDefinitionLevel int, do func(leafColumn)) int { + switch { + case node.Optional(): + maxDefinitionLevel++ + case node.Repeated(): + maxRepetitionLevel++ + maxDefinitionLevel++ + } + + if node.Leaf() { + do(leafColumn{ + node: node, + path: path, + maxRepetitionLevel: makeRepetitionLevel(maxRepetitionLevel), + maxDefinitionLevel: makeDefinitionLevel(maxDefinitionLevel), + columnIndex: makeColumnIndex(columnIndex), + }) + return columnIndex + 1 + } + + for _, field := range node.Fields() { + columnIndex = forEachLeafColumn( + field, + path.append(field.Name()), + columnIndex, + maxRepetitionLevel, + maxDefinitionLevel, + do, + ) + } + + return columnIndex +} + +func lookupColumnPath(node Node, path columnPath) Node { + for node != nil && len(path) > 0 { + node = fieldByName(node, path[0]) + path = path[1:] + } + return node +} + +func hasColumnPath(node Node, path columnPath) bool { + return lookupColumnPath(node, path) != nil +} diff --git a/vendor/github.com/parquet-go/parquet-go/compare.go b/vendor/github.com/parquet-go/parquet-go/compare.go new file mode 100644 index 000000000..c37f2f7e0 --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/compare.go @@ -0,0 +1,356 @@ +package parquet + +import ( + "encoding/binary" + + "github.com/parquet-go/parquet-go/deprecated" + "github.com/parquet-go/parquet-go/internal/memory" +) + +// CompareDescending constructs a comparison function which inverses the order +// of values. +// +//go:noinline +func CompareDescending(cmp func(Value, Value) int) func(Value, Value) int { + return func(a, b Value) int { return -cmp(a, b) } +} + +// CompareNullsFirst constructs a comparison function which assumes that null +// values are smaller than all other values. +// +//go:noinline +func CompareNullsFirst(cmp func(Value, Value) int) func(Value, Value) int { + return func(a, b Value) int { + switch { + case a.IsNull(): + if b.IsNull() { + return 0 + } + return -1 + case b.IsNull(): + return +1 + default: + return cmp(a, b) + } + } +} + +// CompareNullsLast constructs a comparison function which assumes that null +// values are greater than all other values. +// +//go:noinline +func CompareNullsLast(cmp func(Value, Value) int) func(Value, Value) int { + return func(a, b Value) int { + switch { + case a.IsNull(): + if b.IsNull() { + return 0 + } + return +1 + case b.IsNull(): + return -1 + default: + return cmp(a, b) + } + } +} + +func compareBool(v1, v2 bool) int { + switch { + case !v1 && v2: + return -1 + case v1 && !v2: + return +1 + default: + return 0 + } +} + +func compareInt32(v1, v2 int32) int { + switch { + case v1 < v2: + return -1 + case v1 > v2: + return +1 + default: + return 0 + } +} + +func compareInt64(v1, v2 int64) int { + switch { + case v1 < v2: + return -1 + case v1 > v2: + return +1 + default: + return 0 + } +} + +func compareInt96(v1, v2 deprecated.Int96) int { + switch { + case v1.Less(v2): + return -1 + case v2.Less(v1): + return +1 + default: + return 0 + } +} + +func compareFloat32(v1, v2 float32) int { + switch { + case v1 < v2: + return -1 + case v1 > v2: + return +1 + default: + return 0 + } +} + +func compareFloat64(v1, v2 float64) int { + switch { + case v1 < v2: + return -1 + case v1 > v2: + return +1 + default: + return 0 + } +} + +func compareUint32(v1, v2 uint32) int { + switch { + case v1 < v2: + return -1 + case v1 > v2: + return +1 + default: + return 0 + } +} + +func compareUint64(v1, v2 uint64) int { + switch { + case v1 < v2: + return -1 + case v1 > v2: + return +1 + default: + return 0 + } +} + +func compareBE128(v1, v2 *[16]byte) int { + x := binary.BigEndian.Uint64(v1[:8]) + y := binary.BigEndian.Uint64(v2[:8]) + switch { + case x < y: + return -1 + case x > y: + return +1 + } + x = binary.BigEndian.Uint64(v1[8:]) + y = binary.BigEndian.Uint64(v2[8:]) + switch { + case x < y: + return -1 + case x > y: + return +1 + default: + return 0 + } +} + +func lessBE128(v1, v2 *[16]byte) bool { + x := binary.BigEndian.Uint64(v1[:8]) + y := binary.BigEndian.Uint64(v2[:8]) + switch { + case x < y: + return true + case x > y: + return false + } + x = binary.BigEndian.Uint64(v1[8:]) + y = binary.BigEndian.Uint64(v2[8:]) + return x < y +} + +func compareRowsFuncOf(schema *Schema, sortingColumns []SortingColumn) func(Row, Row) int { + leafColumns := make([]leafColumn, len(sortingColumns)) + canCompareRows := true + + forEachLeafColumnOf(schema, func(leaf leafColumn) { + if leaf.maxRepetitionLevel > 0 { + canCompareRows = false + } + + if sortingIndex := searchSortingColumn(sortingColumns, leaf.path); sortingIndex < len(sortingColumns) { + leafColumns[sortingIndex] = leaf + + if leaf.maxDefinitionLevel > 0 { + canCompareRows = false + } + } + }) + + // This is an optimization for the common case where rows + // are sorted by non-optional, non-repeated columns. + // + // The sort function can make the assumption that it will + // find the column value at the current column index, and + // does not need to scan the rows looking for values with + // a matching column index. + if canCompareRows { + return compareRowsFuncOfColumnIndexes(leafColumns, sortingColumns) + } + + return compareRowsFuncOfColumnValues(leafColumns, sortingColumns) +} + +func compareRowsUnordered(Row, Row) int { return 0 } + +//go:noinline +func compareRowsFuncOfIndexColumns(compareFuncs []func(Row, Row) int) func(Row, Row) int { + return func(row1, row2 Row) int { + for _, compare := range compareFuncs { + if cmp := compare(row1, row2); cmp != 0 { + return cmp + } + } + return 0 + } +} + +//go:noinline +func compareRowsFuncOfIndexAscending(columnIndex uint16, typ Type) func(Row, Row) int { + return func(row1, row2 Row) int { return typ.Compare(row1[columnIndex], row2[columnIndex]) } +} + +//go:noinline +func compareRowsFuncOfIndexDescending(columnIndex uint16, typ Type) func(Row, Row) int { + return func(row1, row2 Row) int { return -typ.Compare(row1[columnIndex], row2[columnIndex]) } +} + +//go:noinline +func compareRowsFuncOfColumnIndexes(leafColumns []leafColumn, sortingColumns []SortingColumn) func(Row, Row) int { + compareFuncs := make([]func(Row, Row) int, len(sortingColumns)) + + for sortingIndex, sortingColumn := range sortingColumns { + leaf := leafColumns[sortingIndex] + typ := leaf.node.Type() + + if sortingColumn.Descending() { + compareFuncs[sortingIndex] = compareRowsFuncOfIndexDescending(leaf.columnIndex, typ) + } else { + compareFuncs[sortingIndex] = compareRowsFuncOfIndexAscending(leaf.columnIndex, typ) + } + } + + switch len(compareFuncs) { + case 0: + return compareRowsUnordered + case 1: + return compareFuncs[0] + default: + return compareRowsFuncOfIndexColumns(compareFuncs) + } +} + +var columnPool memory.Pool[[][2]int32] + +//go:noinline +func compareRowsFuncOfColumnValues(leafColumns []leafColumn, sortingColumns []SortingColumn) func(Row, Row) int { + highestColumnIndex := uint16(0) + columnIndexes := make([]uint16, len(sortingColumns)) + compareFuncs := make([]func(Value, Value) int, len(sortingColumns)) + + for sortingIndex, sortingColumn := range sortingColumns { + leaf := leafColumns[sortingIndex] + compare := leaf.node.Type().Compare + + if sortingColumn.Descending() { + compare = CompareDescending(compare) + } + + if leaf.maxDefinitionLevel > 0 { + if sortingColumn.NullsFirst() { + compare = CompareNullsFirst(compare) + } else { + compare = CompareNullsLast(compare) + } + } + + columnIndexes[sortingIndex] = leaf.columnIndex + compareFuncs[sortingIndex] = compare + + if leaf.columnIndex > highestColumnIndex { + highestColumnIndex = leaf.columnIndex + } + } + + return func(row1, row2 Row) int { + columns1 := columnPool.Get(compareColumnNew, compareColumnReset) + columns2 := columnPool.Get(compareColumnNew, compareColumnReset) + defer columnPool.Put(columns1) + defer columnPool.Put(columns2) + + i1 := 0 + i2 := 0 + + for columnIndex := uint16(0); columnIndex <= highestColumnIndex; columnIndex++ { + j1 := i1 + 1 + j2 := i2 + 1 + + for j1 < len(row1) && row1[j1].columnIndex == ^columnIndex { + j1++ + } + + for j2 < len(row2) && row2[j2].columnIndex == ^columnIndex { + j2++ + } + + *columns1 = append(*columns1, [2]int32{int32(i1), int32(j1)}) + *columns2 = append(*columns2, [2]int32{int32(i2), int32(j2)}) + i1 = j1 + i2 = j2 + } + + for i, compare := range compareFuncs { + columnIndex := columnIndexes[i] + offsets1 := (*columns1)[columnIndex] + offsets2 := (*columns2)[columnIndex] + values1 := row1[offsets1[0]:offsets1[1]:offsets1[1]] + values2 := row2[offsets2[0]:offsets2[1]:offsets2[1]] + i1 := 0 + i2 := 0 + + for i1 < len(values1) && i2 < len(values2) { + if cmp := compare(values1[i1], values2[i2]); cmp != 0 { + return cmp + } + i1++ + i2++ + } + + if i1 < len(values1) { + return +1 + } + if i2 < len(values2) { + return -1 + } + } + return 0 + } +} + +func compareColumnNew() *[][2]int32 { + s := make([][2]int32, 0, 128) + return &s +} + +func compareColumnReset(c *[][2]int32) { + *c = (*c)[:0] +} diff --git a/vendor/github.com/parquet-go/parquet-go/compress.go b/vendor/github.com/parquet-go/parquet-go/compress.go new file mode 100644 index 000000000..c2b2eb130 --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/compress.go @@ -0,0 +1,96 @@ +package parquet + +import ( + "fmt" + + "github.com/parquet-go/parquet-go/compress" + "github.com/parquet-go/parquet-go/compress/brotli" + "github.com/parquet-go/parquet-go/compress/gzip" + "github.com/parquet-go/parquet-go/compress/lz4" + "github.com/parquet-go/parquet-go/compress/snappy" + "github.com/parquet-go/parquet-go/compress/uncompressed" + "github.com/parquet-go/parquet-go/compress/zstd" + "github.com/parquet-go/parquet-go/format" +) + +var ( + // Uncompressed is a parquet compression codec representing uncompressed + // pages. + Uncompressed uncompressed.Codec + + // Snappy is the SNAPPY parquet compression codec. + Snappy snappy.Codec + + // Gzip is the GZIP parquet compression codec. + Gzip = gzip.Codec{ + Level: gzip.DefaultCompression, + } + + // Brotli is the BROTLI parquet compression codec. + Brotli = brotli.Codec{ + Quality: brotli.DefaultQuality, + LGWin: brotli.DefaultLGWin, + } + + // Zstd is the ZSTD parquet compression codec. + Zstd = zstd.Codec{ + Level: zstd.DefaultLevel, + } + + // Lz4Raw is the LZ4_RAW parquet compression codec. + Lz4Raw = lz4.Codec{ + Level: lz4.DefaultLevel, + } + + // Table of compression codecs indexed by their code in the parquet format. + compressionCodecs = [...]compress.Codec{ + format.Uncompressed: &Uncompressed, + format.Snappy: &Snappy, + format.Gzip: &Gzip, + format.Brotli: &Brotli, + format.Zstd: &Zstd, + format.Lz4Raw: &Lz4Raw, + } +) + +// LookupCompressionCodec returns the compression codec associated with the +// given code. +// +// The function never returns nil. If the encoding is not supported, +// an "unsupported" codec is returned. +func LookupCompressionCodec(codec format.CompressionCodec) compress.Codec { + if codec >= 0 && int(codec) < len(compressionCodecs) { + if c := compressionCodecs[codec]; c != nil { + return c + } + } + return &unsupported{codec} +} + +type unsupported struct { + codec format.CompressionCodec +} + +func (u *unsupported) String() string { + return "UNSUPPORTED" +} + +func (u *unsupported) CompressionCodec() format.CompressionCodec { + return u.codec +} + +func (u *unsupported) Encode(dst, src []byte) ([]byte, error) { + return dst[:0], u.error() +} + +func (u *unsupported) Decode(dst, src []byte) ([]byte, error) { + return dst[:0], u.error() +} + +func (u *unsupported) error() error { + return fmt.Errorf("unsupported compression codec: %s", u.codec) +} + +func isCompressed(c compress.Codec) bool { + return c != nil && c.CompressionCodec() != format.Uncompressed +} diff --git a/vendor/github.com/parquet-go/parquet-go/compress/brotli/brotli.go b/vendor/github.com/parquet-go/parquet-go/compress/brotli/brotli.go new file mode 100644 index 000000000..db7c0bfdf --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/compress/brotli/brotli.go @@ -0,0 +1,54 @@ +// Package brotli implements the BROTLI parquet compression codec. +package brotli + +import ( + "io" + + "github.com/andybalholm/brotli" + "github.com/parquet-go/parquet-go/compress" + "github.com/parquet-go/parquet-go/format" +) + +const ( + DefaultQuality = 0 + DefaultLGWin = 0 +) + +type Codec struct { + // Quality controls the compression-speed vs compression-density trade-offs. + // The higher the quality, the slower the compression. Range is 0 to 11. + Quality int + // LGWin is the base 2 logarithm of the sliding window size. + // Range is 10 to 24. 0 indicates automatic configuration based on Quality. + LGWin int + + r compress.Decompressor + w compress.Compressor +} + +func (c *Codec) String() string { + return "BROTLI" +} + +func (c *Codec) CompressionCodec() format.CompressionCodec { + return format.Brotli +} + +func (c *Codec) Encode(dst, src []byte) ([]byte, error) { + return c.w.Encode(dst, src, func(w io.Writer) (compress.Writer, error) { + return brotli.NewWriterOptions(w, brotli.WriterOptions{ + Quality: c.Quality, + LGWin: c.LGWin, + }), nil + }) +} + +func (c *Codec) Decode(dst, src []byte) ([]byte, error) { + return c.r.Decode(dst, src, func(r io.Reader) (compress.Reader, error) { + return reader{brotli.NewReader(r)}, nil + }) +} + +type reader struct{ *brotli.Reader } + +func (reader) Close() error { return nil } diff --git a/vendor/github.com/parquet-go/parquet-go/compress/compress.go b/vendor/github.com/parquet-go/parquet-go/compress/compress.go new file mode 100644 index 000000000..5e22f89be --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/compress/compress.go @@ -0,0 +1,148 @@ +// Package compress provides the generic APIs implemented by parquet compression +// codecs. +// +// https://github.com/apache/parquet-format/blob/master/Compression.md +package compress + +import ( + "bytes" + "io" + + "github.com/parquet-go/parquet-go/format" + "github.com/parquet-go/parquet-go/internal/memory" +) + +// The Codec interface represents parquet compression codecs implemented by the +// compress sub-packages. +// +// Codec instances must be safe to use concurrently from multiple goroutines. +type Codec interface { + // Returns a human-readable name for the codec. + String() string + + // Returns the code of the compression codec in the parquet format. + CompressionCodec() format.CompressionCodec + + // Writes the compressed version of src to dst and returns it. + // + // The method automatically reallocates the output buffer if its capacity + // was too small to hold the compressed data. + Encode(dst, src []byte) ([]byte, error) + + // Writes the uncompressed version of src to dst and returns it. + // + // The method automatically reallocates the output buffer if its capacity + // was too small to hold the uncompressed data. + Decode(dst, src []byte) ([]byte, error) +} + +type Reader interface { + io.ReadCloser + Reset(io.Reader) error +} + +type Writer interface { + io.WriteCloser + Reset(io.Writer) +} + +type Compressor struct { + writers memory.Pool[writer] +} + +type writer struct { + output bytes.Buffer + writer Writer +} + +func (c *Compressor) Encode(dst, src []byte, newWriter func(io.Writer) (Writer, error)) ([]byte, error) { + w := c.writers.Get( + func() *writer { + w := new(writer) + w.output = *bytes.NewBuffer(dst[:0]) + var err error + if w.writer, err = newWriter(&w.output); err != nil { + panic(err) // Will be caught below + } + return w + }, + func(w *writer) { + w.output = *bytes.NewBuffer(dst[:0]) + w.writer.Reset(&w.output) + }, + ) + + defer func() { + w.output = *bytes.NewBuffer(nil) + w.writer.Reset(io.Discard) + c.writers.Put(w) + }() + + if _, err := w.writer.Write(src); err != nil { + return w.output.Bytes(), err + } + if err := w.writer.Close(); err != nil { + return w.output.Bytes(), err + } + return w.output.Bytes(), nil +} + +type Decompressor struct { + readers memory.Pool[reader] +} + +type reader struct { + input bytes.Reader + reader Reader +} + +func (d *Decompressor) Decode(dst, src []byte, newReader func(io.Reader) (Reader, error)) ([]byte, error) { + r := d.readers.Get( + func() *reader { + r := new(reader) + r.input.Reset(src) + var err error + if r.reader, err = newReader(&r.input); err != nil { + panic(err) // Will be caught below + } + return r + }, + func(r *reader) { + r.input.Reset(src) + if err := r.reader.Reset(&r.input); err != nil { + panic(err) // Will be caught below + } + }, + ) + + defer func() { + r.input.Reset(nil) + if err := r.reader.Reset(nil); err == nil { + d.readers.Put(r) + } + }() + + if cap(dst) == 0 { + dst = make([]byte, 0, 2*len(src)) + } else { + dst = dst[:0] + } + + for { + n, err := r.reader.Read(dst[len(dst):cap(dst)]) + dst = dst[:len(dst)+n] + + if err != nil { + if err == io.EOF { + err = nil + } + return dst, err + } + + if len(dst) == cap(dst) { + tmp := make([]byte, len(dst), 2*len(dst)) + copy(tmp, dst) + dst = tmp + } + } +} diff --git a/vendor/github.com/parquet-go/parquet-go/compress/gzip/gzip.go b/vendor/github.com/parquet-go/parquet-go/compress/gzip/gzip.go new file mode 100644 index 000000000..99780a950 --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/compress/gzip/gzip.go @@ -0,0 +1,67 @@ +// Package gzip implements the GZIP parquet compression codec. +package gzip + +import ( + "io" + "strings" + + "github.com/klauspost/compress/gzip" + "github.com/parquet-go/parquet-go/compress" + "github.com/parquet-go/parquet-go/format" +) + +const ( + emptyGzip = "\x1f\x8b\b\x00\x00\x00\x00\x00\x02\xff\x01\x00\x00\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00" +) + +const ( + NoCompression = gzip.NoCompression + BestSpeed = gzip.BestSpeed + BestCompression = gzip.BestCompression + DefaultCompression = gzip.DefaultCompression + HuffmanOnly = gzip.HuffmanOnly +) + +type Codec struct { + Level int + + r compress.Decompressor + w compress.Compressor +} + +func (c *Codec) String() string { + return "GZIP" +} + +func (c *Codec) CompressionCodec() format.CompressionCodec { + return format.Gzip +} + +func (c *Codec) Encode(dst, src []byte) ([]byte, error) { + return c.w.Encode(dst, src, func(w io.Writer) (compress.Writer, error) { + return gzip.NewWriterLevel(w, c.Level) + }) +} + +func (c *Codec) Decode(dst, src []byte) ([]byte, error) { + return c.r.Decode(dst, src, func(r io.Reader) (compress.Reader, error) { + z, err := gzip.NewReader(r) + if err != nil { + return nil, err + } + return &reader{Reader: z}, nil + }) +} + +type reader struct { + *gzip.Reader + emptyGzip strings.Reader +} + +func (r *reader) Reset(rr io.Reader) error { + if rr == nil { + r.emptyGzip.Reset(emptyGzip) + rr = &r.emptyGzip + } + return r.Reader.Reset(rr) +} diff --git a/vendor/github.com/parquet-go/parquet-go/compress/lz4/lz4.go b/vendor/github.com/parquet-go/parquet-go/compress/lz4/lz4.go new file mode 100644 index 000000000..975cfed8c --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/compress/lz4/lz4.go @@ -0,0 +1,87 @@ +// Package lz4 implements the LZ4_RAW parquet compression codec. +package lz4 + +import ( + "github.com/parquet-go/parquet-go/format" + "github.com/pierrec/lz4/v4" +) + +type Level = lz4.CompressionLevel + +const ( + Fastest = lz4.CompressionLevel(99) + Fast = lz4.Fast + Level1 = lz4.Level1 + Level2 = lz4.Level2 + Level3 = lz4.Level3 + Level4 = lz4.Level4 + Level5 = lz4.Level5 + Level6 = lz4.Level6 + Level7 = lz4.Level7 + Level8 = lz4.Level8 + Level9 = lz4.Level9 +) + +const ( + DefaultLevel = Fast +) + +type Codec struct { + Level Level +} + +func (c *Codec) String() string { + return "LZ4_RAW" +} + +func (c *Codec) CompressionCodec() format.CompressionCodec { + return format.Lz4Raw +} + +func (c *Codec) Encode(dst, src []byte) ([]byte, error) { + dst = reserveAtLeast(dst, lz4.CompressBlockBound(len(src))) + + var ( + n int + err error + ) + if c.Level == Fastest { + compressor := lz4.Compressor{} + n, err = compressor.CompressBlock(src, dst) + } else { + compressor := lz4.CompressorHC{Level: c.Level} + n, err = compressor.CompressBlock(src, dst) + } + return dst[:n], err +} + +func (c *Codec) Decode(dst, src []byte) ([]byte, error) { + // 3x seems like a common compression ratio, so we optimistically size the + // output buffer to that size. Feel free to change the value if you observe + // different behaviors. + dst = reserveAtLeast(dst, 3*len(src)) + + for { + n, err := lz4.UncompressBlock(src, dst) + // The lz4 package does not expose the error values, they are declared + // in internal/lz4errors. Based on what I read of the implementation, + // the only condition where this function errors is if the output buffer + // was too short. + // + // https://github.com/pierrec/lz4/blob/a5532e5996ee86d17f8ce2694c08fb5bf3c6b471/internal/lz4block/block.go#L45-L53 + if err != nil { + dst = make([]byte, 2*len(dst)) + } else { + return dst[:n], nil + } + } +} + +func reserveAtLeast(b []byte, n int) []byte { + if cap(b) < n { + b = make([]byte, n) + } else { + b = b[:cap(b)] + } + return b +} diff --git a/vendor/github.com/parquet-go/parquet-go/compress/snappy/snappy.go b/vendor/github.com/parquet-go/parquet-go/compress/snappy/snappy.go new file mode 100644 index 000000000..eb3febda4 --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/compress/snappy/snappy.go @@ -0,0 +1,31 @@ +// Package snappy implements the SNAPPY parquet compression codec. +package snappy + +import ( + "github.com/klauspost/compress/snappy" + "github.com/parquet-go/parquet-go/format" +) + +type Codec struct { +} + +// The snappy.Reader and snappy.Writer implement snappy encoding/decoding with +// a framing protocol, but snappy requires the implementation to use the raw +// snappy block encoding. This is why we need to use snappy.Encode/snappy.Decode +// and have to ship custom implementations of the compressed reader and writer. + +func (c *Codec) String() string { + return "SNAPPY" +} + +func (c *Codec) CompressionCodec() format.CompressionCodec { + return format.Snappy +} + +func (c *Codec) Encode(dst, src []byte) ([]byte, error) { + return snappy.Encode(dst, src), nil +} + +func (c *Codec) Decode(dst, src []byte) ([]byte, error) { + return snappy.Decode(dst, src) +} diff --git a/vendor/github.com/parquet-go/parquet-go/compress/uncompressed/uncompressed.go b/vendor/github.com/parquet-go/parquet-go/compress/uncompressed/uncompressed.go new file mode 100644 index 000000000..2aa0b3538 --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/compress/uncompressed/uncompressed.go @@ -0,0 +1,27 @@ +// Package uncompressed provides implementations of the compression codec +// interfaces as pass-through without applying any compression nor +// decompression. +package uncompressed + +import ( + "github.com/parquet-go/parquet-go/format" +) + +type Codec struct { +} + +func (c *Codec) String() string { + return "UNCOMPRESSED" +} + +func (c *Codec) CompressionCodec() format.CompressionCodec { + return format.Uncompressed +} + +func (c *Codec) Encode(dst, src []byte) ([]byte, error) { + return append(dst[:0], src...), nil +} + +func (c *Codec) Decode(dst, src []byte) ([]byte, error) { + return append(dst[:0], src...), nil +} diff --git a/vendor/github.com/parquet-go/parquet-go/compress/zstd/zstd.go b/vendor/github.com/parquet-go/parquet-go/compress/zstd/zstd.go new file mode 100644 index 000000000..82105fb6e --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/compress/zstd/zstd.go @@ -0,0 +1,105 @@ +// Package zstd implements the ZSTD parquet compression codec. +package zstd + +import ( + "github.com/klauspost/compress/zstd" + "github.com/parquet-go/parquet-go/format" + "github.com/parquet-go/parquet-go/internal/memory" +) + +type Level = zstd.EncoderLevel + +const ( + // SpeedFastest will choose the fastest reasonable compression. + // This is roughly equivalent to the fastest Zstandard mode. + SpeedFastest = zstd.SpeedFastest + + // SpeedDefault is the default "pretty fast" compression option. + // This is roughly equivalent to the default Zstandard mode (level 3). + SpeedDefault = zstd.SpeedDefault + + // SpeedBetterCompression will yield better compression than the default. + // Currently it is about zstd level 7-8 with ~ 2x-3x the default CPU usage. + // By using this, notice that CPU usage may go up in the future. + SpeedBetterCompression = zstd.SpeedBetterCompression + + // SpeedBestCompression will choose the best available compression option. + // This will offer the best compression no matter the CPU cost. + SpeedBestCompression = zstd.SpeedBestCompression +) + +const ( + DefaultLevel = SpeedDefault + + DefaultConcurrency = 1 +) + +type Codec struct { + Level Level + + // Concurrency is the number of CPU cores to use for encoding and decoding. + // If Concurrency is 0, it will use DefaultConcurrency. + Concurrency uint + + encoders memory.Pool[zstd.Encoder] + decoders memory.Pool[zstd.Decoder] +} + +func (c *Codec) String() string { + return "ZSTD" +} + +func (c *Codec) CompressionCodec() format.CompressionCodec { + return format.Zstd +} + +func (c *Codec) Encode(dst, src []byte) ([]byte, error) { + e := c.encoders.Get( + func() *zstd.Encoder { + e, err := zstd.NewWriter(nil, + zstd.WithEncoderConcurrency(c.concurrency()), + zstd.WithEncoderLevel(c.level()), + zstd.WithZeroFrames(true), + zstd.WithEncoderCRC(false), + ) + if err != nil { + panic(err) + } + return e + }, + func(e *zstd.Encoder) {}, + ) + defer c.encoders.Put(e) + return e.EncodeAll(src, dst[:0]), nil +} + +func (c *Codec) Decode(dst, src []byte) ([]byte, error) { + d := c.decoders.Get( + func() *zstd.Decoder { + d, err := zstd.NewReader(nil, + zstd.WithDecoderConcurrency(c.concurrency()), + ) + if err != nil { + panic(err) + } + return d + }, + func(d *zstd.Decoder) {}, + ) + defer c.decoders.Put(d) + return d.DecodeAll(src, dst[:0]) +} + +func (c *Codec) level() Level { + if c.Level != 0 { + return c.Level + } + return DefaultLevel +} + +func (c *Codec) concurrency() int { + if c.Concurrency != 0 { + return int(c.Concurrency) + } + return DefaultConcurrency +} diff --git a/vendor/github.com/parquet-go/parquet-go/config.go b/vendor/github.com/parquet-go/parquet-go/config.go new file mode 100644 index 000000000..6492d07c7 --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/config.go @@ -0,0 +1,1052 @@ +package parquet + +import ( + "cmp" + "fmt" + "maps" + "math" + "reflect" + "runtime/debug" + "slices" + "strings" + "sync" + + "github.com/parquet-go/parquet-go/compress" + "github.com/parquet-go/parquet-go/encoding" +) + +// ReadMode is an enum that is used to configure the way that a File reads pages. +type ReadMode int + +const ( + ReadModeSync ReadMode = iota // ReadModeSync reads pages synchronously on demand (Default). + ReadModeAsync // ReadModeAsync reads pages asynchronously in the background. +) + +const ( + DefaultColumnIndexSizeLimit = 16 + DefaultColumnBufferCapacity = 16 * 1024 + DefaultPageBufferSize = 256 * 1024 + DefaultWriteBufferSize = 32 * 1024 + DefaultDataPageVersion = 2 + DefaultDataPageStatistics = true + DefaultSkipMagicBytes = false + DefaultSkipPageIndex = false + DefaultSkipBloomFilters = false + DefaultPrefetchBloomFilters = false + DefaultMaxRowsPerRowGroup = math.MaxInt64 + DefaultReadMode = ReadModeSync +) + +const ( + parquetGoModulePath = "github.com/parquet-go/parquet-go" +) + +var ( + defaultCreatedByInfo string + defaultCreatedByOnce sync.Once +) + +func defaultCreatedBy() string { + defaultCreatedByOnce.Do(func() { + createdBy := parquetGoModulePath + build, ok := debug.ReadBuildInfo() + if ok { + for _, mod := range build.Deps { + if mod.Replace == nil && mod.Path == parquetGoModulePath { + semver, _, buildsha := parseModuleVersion(mod.Version) + createdBy = formatCreatedBy(createdBy, semver, buildsha) + break + } + } + } + defaultCreatedByInfo = createdBy + }) + return defaultCreatedByInfo +} + +func parseModuleVersion(version string) (semver, datetime, buildsha string) { + semver, version = splitModuleVersion(version) + datetime, version = splitModuleVersion(version) + buildsha, _ = splitModuleVersion(version) + semver = strings.TrimPrefix(semver, "v") + return +} + +func splitModuleVersion(s string) (head, tail string) { + if i := strings.IndexByte(s, '-'); i < 0 { + head = s + } else { + head, tail = s[:i], s[i+1:] + } + return +} + +func formatCreatedBy(application, version, build string) string { + return application + " version " + version + "(build " + build + ")" +} + +// The FileConfig type carries configuration options for parquet files. +// +// FileConfig implements the FileOption interface so it can be used directly +// as argument to the OpenFile function when needed, for example: +// +// f, err := parquet.OpenFile(reader, size, &parquet.FileConfig{ +// SkipPageIndex: true, +// SkipBloomFilters: true, +// ReadMode: ReadModeAsync, +// }) +type FileConfig struct { + SkipMagicBytes bool + SkipPageIndex bool + SkipBloomFilters bool + PrefetchBloomFilters bool + OptimisticRead bool + ReadBufferSize int + ReadMode ReadMode + Schema *Schema + Decryption *DecryptionConfig +} + +// DefaultFileConfig returns a new FileConfig value initialized with the +// default file configuration. +func DefaultFileConfig() *FileConfig { + return &FileConfig{ + SkipMagicBytes: DefaultSkipMagicBytes, + SkipPageIndex: DefaultSkipPageIndex, + SkipBloomFilters: DefaultSkipBloomFilters, + PrefetchBloomFilters: DefaultPrefetchBloomFilters, + ReadBufferSize: defaultReadBufferSize, + ReadMode: DefaultReadMode, + Schema: nil, + } +} + +// NewFileConfig constructs a new file configuration applying the options passed +// as arguments. +// +// The function returns an non-nil error if some of the options carried invalid +// configuration values. +func NewFileConfig(options ...FileOption) (*FileConfig, error) { + config := DefaultFileConfig() + config.Apply(options...) + return config, config.Validate() +} + +// Apply applies the given list of options to c. +func (c *FileConfig) Apply(options ...FileOption) { + for _, opt := range options { + opt.ConfigureFile(c) + } +} + +// ConfigureFile applies configuration options from c to config. +func (c *FileConfig) ConfigureFile(config *FileConfig) { + *config = FileConfig{ + SkipMagicBytes: c.SkipMagicBytes, + SkipPageIndex: c.SkipPageIndex, + SkipBloomFilters: c.SkipBloomFilters, + PrefetchBloomFilters: c.PrefetchBloomFilters, + ReadBufferSize: cmp.Or(c.ReadBufferSize, config.ReadBufferSize), + ReadMode: ReadMode(cmp.Or(int(c.ReadMode), int(config.ReadMode))), + Schema: cmp.Or(c.Schema, config.Schema), + Decryption: cmp.Or(c.Decryption, config.Decryption), + } +} + +// Validate returns a non-nil error if the configuration of c is invalid. +func (c *FileConfig) Validate() error { + return nil +} + +// The ReaderConfig type carries configuration options for parquet readers. +// +// ReaderConfig implements the ReaderOption interface so it can be used directly +// as argument to the NewReader function when needed, for example: +// +// reader := parquet.NewReader(output, schema, &parquet.ReaderConfig{ +// // ... +// }) +type ReaderConfig struct { + Schema *Schema + SchemaConfig *SchemaConfig +} + +// DefaultReaderConfig returns a new ReaderConfig value initialized with the +// default reader configuration. +func DefaultReaderConfig() *ReaderConfig { + return &ReaderConfig{ + SchemaConfig: DefaultSchemaConfig(), + } +} + +// NewReaderConfig constructs a new reader configuration applying the options +// passed as arguments. +// +// The function returns an non-nil error if some of the options carried invalid +// configuration values. +func NewReaderConfig(options ...ReaderOption) (*ReaderConfig, error) { + config := DefaultReaderConfig() + config.Apply(options...) + return config, config.Validate() +} + +// Apply applies the given list of options to c. +func (c *ReaderConfig) Apply(options ...ReaderOption) { + for _, opt := range options { + opt.ConfigureReader(c) + } +} + +// ConfigureReader applies configuration options from c to config. +func (c *ReaderConfig) ConfigureReader(config *ReaderConfig) { + *config = ReaderConfig{ + Schema: cmp.Or(c.Schema, config.Schema), + SchemaConfig: cmp.Or(c.SchemaConfig, config.SchemaConfig), + } +} + +// Validate returns a non-nil error if the configuration of c is invalid. +func (c *ReaderConfig) Validate() error { + return nil +} + +// The WriterConfig type carries configuration options for parquet writers. +// +// WriterConfig implements the WriterOption interface so it can be used directly +// as argument to the NewWriter function when needed, for example: +// +// writer := parquet.NewWriter(output, schema, &parquet.WriterConfig{ +// CreatedBy: "my test program", +// }) +type WriterConfig struct { + CreatedBy string + ColumnPageBuffers BufferPool + ColumnIndexSizeLimit func(path []string) int + PageBufferSize int + WriteBufferSize int + DataPageVersion int + DataPageStatistics bool + DeprecatedDataPageStatistics bool + MaxRowsPerRowGroup int64 + KeyValueMetadata map[string]string + Schema *Schema + BloomFilters []BloomFilterColumn + DeferredBloomFiltersBuffers BufferPool + BloomFilterCompression compress.Codec + Compression compress.Codec + Sorting SortingConfig + SkipPageBounds [][]string + SkipPageStatistics [][]string + Encodings map[Kind]encoding.Encoding + DictionaryMaxBytes int64 + SchemaConfig *SchemaConfig + Encryption *EncryptionConfig +} + +// DefaultWriterConfig returns a new WriterConfig value initialized with the +// default writer configuration. +func DefaultWriterConfig() *WriterConfig { + return &WriterConfig{ + CreatedBy: defaultCreatedBy(), + ColumnPageBuffers: &defaultColumnBufferPool, + ColumnIndexSizeLimit: func(path []string) int { return DefaultColumnIndexSizeLimit }, + PageBufferSize: DefaultPageBufferSize, + WriteBufferSize: DefaultWriteBufferSize, + DataPageVersion: DefaultDataPageVersion, + DataPageStatistics: DefaultDataPageStatistics, + MaxRowsPerRowGroup: DefaultMaxRowsPerRowGroup, + SchemaConfig: DefaultSchemaConfig(), + Sorting: SortingConfig{ + SortingBuffers: &defaultSortingBufferPool, + }, + } +} + +// NewWriterConfig constructs a new writer configuration applying the options +// passed as arguments. +// +// The function returns an non-nil error if some of the options carried invalid +// configuration values. +func NewWriterConfig(options ...WriterOption) (*WriterConfig, error) { + config := DefaultWriterConfig() + config.Apply(options...) + return config, config.Validate() +} + +// Apply applies the given list of options to c. +func (c *WriterConfig) Apply(options ...WriterOption) { + for _, opt := range options { + opt.ConfigureWriter(c) + } +} + +// ConfigureWriter applies configuration options from c to config. +func (c *WriterConfig) ConfigureWriter(config *WriterConfig) { + keyValueMetadata := config.KeyValueMetadata + if len(c.KeyValueMetadata) > 0 { + if keyValueMetadata == nil { + keyValueMetadata = make(map[string]string, len(c.KeyValueMetadata)) + } + maps.Copy(keyValueMetadata, c.KeyValueMetadata) + } + + encodings := config.Encodings + if len(c.Encodings) > 0 { + if encodings == nil { + encodings = make(map[Kind]encoding.Encoding, len(c.Encodings)) + } + maps.Copy(encodings, c.Encodings) + } + + *config = WriterConfig{ + CreatedBy: cmp.Or(c.CreatedBy, config.CreatedBy), + ColumnPageBuffers: cmp.Or(c.ColumnPageBuffers, config.ColumnPageBuffers), + ColumnIndexSizeLimit: coalesceFunc(c.ColumnIndexSizeLimit, config.ColumnIndexSizeLimit), + PageBufferSize: cmp.Or(c.PageBufferSize, config.PageBufferSize), + WriteBufferSize: cmp.Or(c.WriteBufferSize, config.WriteBufferSize), + DataPageVersion: cmp.Or(c.DataPageVersion, config.DataPageVersion), + DataPageStatistics: c.DataPageStatistics || config.DataPageStatistics, + DeprecatedDataPageStatistics: c.DeprecatedDataPageStatistics || config.DeprecatedDataPageStatistics, + MaxRowsPerRowGroup: cmp.Or(c.MaxRowsPerRowGroup, config.MaxRowsPerRowGroup), + KeyValueMetadata: keyValueMetadata, + Schema: cmp.Or(c.Schema, config.Schema), + BloomFilters: coalesceSlices(c.BloomFilters, config.BloomFilters), + DeferredBloomFiltersBuffers: cmp.Or(c.DeferredBloomFiltersBuffers, config.DeferredBloomFiltersBuffers), + BloomFilterCompression: cmp.Or(c.BloomFilterCompression, config.BloomFilterCompression), + Compression: cmp.Or(c.Compression, config.Compression), + Sorting: coalesceSortingConfig(c.Sorting, config.Sorting), + SkipPageBounds: coalesceSlices(c.SkipPageBounds, config.SkipPageBounds), + SkipPageStatistics: coalesceSlices(c.SkipPageStatistics, config.SkipPageStatistics), + Encodings: encodings, + SchemaConfig: cmp.Or(c.SchemaConfig, config.SchemaConfig), + Encryption: cmp.Or(c.Encryption, config.Encryption), + } +} + +// Validate returns a non-nil error if the configuration of c is invalid. +func (c *WriterConfig) Validate() error { + const baseName = "parquet.(*WriterConfig)." + return errorInvalidConfiguration( + validateNotNil(baseName+"ColumnPageBuffers", c.ColumnPageBuffers), + validatePositiveInt(baseName+"PageBufferSize", c.PageBufferSize), + validateOneOfInt(baseName+"DataPageVersion", c.DataPageVersion, 1, 2), + c.Sorting.Validate(), + ) +} + +// The RowGroupConfig type carries configuration options for parquet row groups. +// +// RowGroupConfig implements the RowGroupOption interface so it can be used +// directly as argument to the NewBuffer function when needed, for example: +// +// buffer := parquet.NewBuffer(&parquet.RowGroupConfig{ +// ColumnBufferCapacity: 10_000, +// }) +type RowGroupConfig struct { + ColumnBufferCapacity int + Schema *Schema + Sorting SortingConfig +} + +// DefaultRowGroupConfig returns a new RowGroupConfig value initialized with the +// default row group configuration. +func DefaultRowGroupConfig() *RowGroupConfig { + return &RowGroupConfig{ + ColumnBufferCapacity: DefaultColumnBufferCapacity, + Sorting: SortingConfig{ + SortingBuffers: &defaultSortingBufferPool, + }, + } +} + +// NewRowGroupConfig constructs a new row group configuration applying the +// options passed as arguments. +// +// The function returns an non-nil error if some of the options carried invalid +// configuration values. +func NewRowGroupConfig(options ...RowGroupOption) (*RowGroupConfig, error) { + config := DefaultRowGroupConfig() + config.Apply(options...) + return config, config.Validate() +} + +// Validate returns a non-nil error if the configuration of c is invalid. +func (c *RowGroupConfig) Validate() error { + const baseName = "parquet.(*RowGroupConfig)." + return errorInvalidConfiguration( + validatePositiveInt(baseName+"ColumnBufferCapacity", c.ColumnBufferCapacity), + c.Sorting.Validate(), + ) +} + +func (c *RowGroupConfig) Apply(options ...RowGroupOption) { + for _, opt := range options { + opt.ConfigureRowGroup(c) + } +} + +func (c *RowGroupConfig) ConfigureRowGroup(config *RowGroupConfig) { + *config = RowGroupConfig{ + ColumnBufferCapacity: cmp.Or(c.ColumnBufferCapacity, config.ColumnBufferCapacity), + Schema: cmp.Or(c.Schema, config.Schema), + Sorting: coalesceSortingConfig(c.Sorting, config.Sorting), + } +} + +// The SortingConfig type carries configuration options for parquet row groups. +// +// SortingConfig implements the SortingOption interface so it can be used +// directly as argument to the NewSortingWriter function when needed, +// for example: +// +// buffer := parquet.NewSortingWriter[Row]( +// parquet.SortingWriterConfig( +// parquet.DropDuplicatedRows(true), +// ), +// }) +type SortingConfig struct { + SortingBuffers BufferPool + SortingColumns []SortingColumn + DropDuplicatedRows bool +} + +// DefaultSortingConfig returns a new SortingConfig value initialized with the +// default row group configuration. +func DefaultSortingConfig() *SortingConfig { + return &SortingConfig{ + SortingBuffers: &defaultSortingBufferPool, + } +} + +// NewSortingConfig constructs a new sorting configuration applying the +// options passed as arguments. +// +// The function returns an non-nil error if some of the options carried invalid +// configuration values. +func NewSortingConfig(options ...SortingOption) (*SortingConfig, error) { + config := DefaultSortingConfig() + config.Apply(options...) + return config, config.Validate() +} + +func (c *SortingConfig) Validate() error { + const baseName = "parquet.(*SortingConfig)." + return errorInvalidConfiguration( + validateNotNil(baseName+"SortingBuffers", c.SortingBuffers), + ) +} + +func (c *SortingConfig) Apply(options ...SortingOption) { + for _, opt := range options { + opt.ConfigureSorting(c) + } +} + +func (c *SortingConfig) ConfigureSorting(config *SortingConfig) { + *config = coalesceSortingConfig(*c, *config) +} + +// SchemaOption is an interface implemented by types that carry configuration +// options for parquet schemas. SchemaOption also implements ReaderOption and WriterOption +// and may be used to configure the way NewGenericReader and NewGenericWriter derive schemas from the arguments. +type SchemaOption interface { + ReaderOption + WriterOption + + ConfigureSchema(*SchemaConfig) +} + +// FileOption is an interface implemented by types that carry configuration +// options for parquet files. +type FileOption interface { + ConfigureFile(*FileConfig) +} + +// ReaderOption is an interface implemented by types that carry configuration +// options for parquet readers. +type ReaderOption interface { + ConfigureReader(*ReaderConfig) +} + +// WriterOption is an interface implemented by types that carry configuration +// options for parquet writers. +type WriterOption interface { + ConfigureWriter(*WriterConfig) +} + +// RowGroupOption is an interface implemented by types that carry configuration +// options for parquet row groups. +type RowGroupOption interface { + ConfigureRowGroup(*RowGroupConfig) +} + +// SortingOption is an interface implemented by types that carry configuration +// options for parquet sorting writers. +type SortingOption interface { + ConfigureSorting(*SortingConfig) +} + +// SkipMagicBytes is a file configuration option which prevents automatically +// reading the magic bytes when opening a parquet file, when set to true. This +// is useful as an optimization when programs can trust that they are dealing +// with parquet files and do not need to verify the first 4 bytes. +func SkipMagicBytes(skip bool) FileOption { + return fileOption(func(config *FileConfig) { config.SkipMagicBytes = skip }) +} + +// SkipPageIndex is a file configuration option which prevents automatically +// reading the page index when opening a parquet file, when set to true. This is +// useful as an optimization when programs know that they will not need to +// consume the page index. +// +// Defaults to false. +func SkipPageIndex(skip bool) FileOption { + return fileOption(func(config *FileConfig) { config.SkipPageIndex = skip }) +} + +// SkipBloomFilters is a file configuration option which prevents automatically +// reading the bloom filter headers when opening a parquet file, when set to +// true. This is useful as an optimization when programs know that they will not +// need to consume the bloom filters. +// +// Defaults to false. +func SkipBloomFilters(skip bool) FileOption { + return fileOption(func(config *FileConfig) { config.SkipBloomFilters = skip }) +} + +// PrefetchBloomFilters is a file configuration option that controls whether the +// bloom filter contents are loaded into memory when a file is opened. By +// default, only the headers are parsed, requiring further reads to the file to +// probe the filter. Using this option with OptimisticRead can be useful when +// reading from remote storage, reducing network round trips. +// +// Defaults to false. +func PrefetchBloomFilters(prefetch bool) FileOption { + return fileOption(func(config *FileConfig) { config.PrefetchBloomFilters = prefetch }) +} + +// OptimisticRead configures a file to optimistically perform larger buffered +// reads to improve performance. This is useful when reading from remote storage +// and amortize the cost of network round trips. +// +// This is an option instead of enabled by default because dependents of this +// package have historically relied on the read patterns to provide external +// caches and achieve similar results (e.g., Tempo). +func OptimisticRead(enabled bool) FileOption { + return fileOption(func(config *FileConfig) { config.OptimisticRead = enabled }) +} + +// FileReadMode is a file configuration option which controls the way pages +// are read. Currently the only two options are ReadModeAsync and ReadModeSync +// which control whether or not pages are loaded asynchronously. It can be +// advantageous to use ReadModeAsync if your reader is backed by network +// storage. +// +// Defaults to ReadModeSync. +func FileReadMode(mode ReadMode) FileOption { + return fileOption(func(config *FileConfig) { config.ReadMode = mode }) +} + +// ReadBufferSize is a file configuration option which controls the default +// buffer sizes for reads made to the provided io.Reader. The default of 4096 +// is appropriate for disk based access but if your reader is backed by network +// storage it can be advantageous to increase this value to something more like +// 4 MiB. +// +// Defaults to 4096. +func ReadBufferSize(size int) FileOption { + return fileOption(func(config *FileConfig) { config.ReadBufferSize = size }) +} + +// FileSchema is used to pass a known schema in while opening a Parquet file. +// This optimization is only useful if your application is currently opening +// an extremely large number of parquet files with the same, known schema. +// +// Defaults to nil. +func FileSchema(schema *Schema) FileOption { + return fileOption(func(config *FileConfig) { config.Schema = schema }) +} + +// PageBufferSize configures the size of column page buffers on parquet writers. +// +// Note that the page buffer size refers to the in-memory buffers where pages +// are generated, not the size of pages after encoding and compression. +// This design choice was made to help control the amount of memory needed to +// read and write pages rather than controlling the space used by the encoded +// representation on disk. +// +// Defaults to 256KiB. +func PageBufferSize(size int) WriterOption { + return writerOption(func(config *WriterConfig) { config.PageBufferSize = size }) +} + +// WriteBufferSize configures the size of the write buffer. +// +// Setting the writer buffer size to zero deactivates buffering, all writes are +// immediately sent to the output io.Writer. +// +// Defaults to 32KiB. +func WriteBufferSize(size int) WriterOption { + return writerOption(func(config *WriterConfig) { config.WriteBufferSize = size }) +} + +// MaxRowsPerRowGroup configures the maximum number of rows that a writer will +// produce in each row group. +// +// This limit is useful to control size of row groups in both number of rows and +// byte size. While controlling the byte size of a row group is difficult to +// achieve with parquet due to column encoding and compression, the number of +// rows remains a useful proxy. +// +// Defaults to unlimited. +func MaxRowsPerRowGroup(numRows int64) WriterOption { + if numRows <= 0 { + numRows = DefaultMaxRowsPerRowGroup + } + return writerOption(func(config *WriterConfig) { config.MaxRowsPerRowGroup = numRows }) +} + +// CreatedBy creates a configuration option which sets the name of the +// application that created a parquet file. +// +// The option formats the "CreatedBy" file metadata according to the convention +// described by the parquet spec: +// +// " version (build )" +// +// By default, the option is set to the parquet-go module name, version, and +// build hash. +func CreatedBy(application, version, build string) WriterOption { + createdBy := formatCreatedBy(application, version, build) + return writerOption(func(config *WriterConfig) { config.CreatedBy = createdBy }) +} + +// ColumnPageBuffers creates a configuration option to customize the buffer pool +// used when constructing row groups. This can be used to provide on-disk buffers +// as swap space to ensure that the parquet file creation will no be bottlenecked +// on the amount of memory available. +// +// Defaults to using in-memory buffers. +func ColumnPageBuffers(buffers BufferPool) WriterOption { + return writerOption(func(config *WriterConfig) { config.ColumnPageBuffers = buffers }) +} + +// ColumnIndexSizeLimit creates a configuration option to customize the size +// limit of page boundaries recorded in column indexes. The result of the provided +// function must be larger then 0. +// +// Defaults to the function that returns 16 for all paths. +func ColumnIndexSizeLimit(f func(path []string) int) WriterOption { + return writerOption(func(config *WriterConfig) { config.ColumnIndexSizeLimit = f }) +} + +// DataPageVersion creates a configuration option which configures the version of +// data pages used when creating a parquet file. +// +// Defaults to version 2. +func DataPageVersion(version int) WriterOption { + return writerOption(func(config *WriterConfig) { config.DataPageVersion = version }) +} + +// DataPageStatistics creates a configuration option which defines whether data +// page statistics are emitted. This option is useful when generating parquet +// files that intend to be backward compatible with older readers which may not +// have the ability to load page statistics from the column index. +// +// This used to be disabled by default, but it was switched from false to true +// after v0.26.0, because the computation of page statistics is cheap and query +// engines do better the more statistics they have available, enabling is a more +// sensible default. +// +// Defaults to true. +func DataPageStatistics(enabled bool) WriterOption { + return writerOption(func(config *WriterConfig) { config.DataPageStatistics = enabled }) +} + +// DeprecatedDataPageStatistics creates a configuration option which defines +// whether to also write the deprecated Min/Max fields in column chunk +// statistics, in addition to the standard MinValue/MaxValue fields. This option +// is useful for backward compatibility with older readers that only understand +// the deprecated fields. +// +// The deprecated Min/Max fields use signed comparison, while MinValue/MaxValue +// respect the column's logical type ordering. For columns where these orderings +// differ (e.g., unsigned integers), enabling this option may produce incorrect +// statistics for older readers. +// +// Defaults to false. +func DeprecatedDataPageStatistics(enabled bool) WriterOption { + return writerOption(func(config *WriterConfig) { config.DeprecatedDataPageStatistics = enabled }) +} + +// KeyValueMetadata creates a configuration option which adds key/value metadata +// to add to the metadata of parquet files. +// +// This option is additive, it may be used multiple times to add more than one +// key/value pair. +// +// Keys are assumed to be unique, if the same key is repeated multiple times the +// last value is retained. While the parquet format does not require unique keys, +// this design decision was made to optimize for the most common use case where +// applications leverage this extension mechanism to associate single values to +// keys. This may create incompatibilities with other parquet libraries, or may +// cause some key/value pairs to be lost when open parquet files written with +// repeated keys. We can revisit this decision if it ever becomes a blocker. +func KeyValueMetadata(key, value string) WriterOption { + return writerOption(func(config *WriterConfig) { + if config.KeyValueMetadata == nil { + config.KeyValueMetadata = map[string]string{key: value} + } else { + config.KeyValueMetadata[key] = value + } + }) +} + +// BloomFilters creates a configuration option which defines the bloom filters +// that parquet writers should generate. +// +// The compute and memory footprint of generating bloom filters for all columns +// of a parquet schema can be significant, so by default no filters are created +// and applications need to explicitly declare the columns that they want to +// create filters for. +func BloomFilters(filters ...BloomFilterColumn) WriterOption { + filters = slices.Clone(filters) + return writerOption(func(config *WriterConfig) { config.BloomFilters = filters }) +} + +// DeferBloomFiltersWithBuffers creates a configuration option which delays the +// writing of bloom filters until the end of the file. This can be beneficial for +// files read from remote storage with a custom reader, as an optimistic read can +// capture the file footer along with the bloom filters in a single request. +// +// When this option is enabled, the accumulated bloom filters need to be retained +// until the file is closed; it is therefore required to provide a buffer when +// using this option. +// +// Defaults to nil; bloom filters are written immediately after each row group. +func DeferBloomFiltersWithBuffers(buffer BufferPool) WriterOption { + return writerOption(func(config *WriterConfig) { config.DeferredBloomFiltersBuffers = buffer }) +} + +// BloomFilterCompression creates a configuration option which sets the +// compression codec used when writing bloom filters. The default is +// uncompressed for backward compatibility. +func BloomFilterCompression(codec compress.Codec) WriterOption { + return writerOption(func(config *WriterConfig) { config.BloomFilterCompression = codec }) +} + +// Compression creates a configuration option which sets the default compression +// codec used by a writer for columns where none were defined. +func Compression(codec compress.Codec) WriterOption { + return writerOption(func(config *WriterConfig) { config.Compression = codec }) +} + +// SortingWriterConfig is a writer option which applies configuration specific +// to sorting writers. +func SortingWriterConfig(options ...SortingOption) WriterOption { + options = slices.Clone(options) + return writerOption(func(config *WriterConfig) { config.Sorting.Apply(options...) }) +} + +// SkipPageBounds lists the path to a column that shouldn't have bounds written to the +// footer of the parquet file. This is useful for data blobs, like a raw html file, +// where the bounds are not meaningful. +// +// This option is additive, it may be used multiple times to skip multiple columns. +func SkipPageBounds(path ...string) WriterOption { + return writerOption(func(config *WriterConfig) { config.SkipPageBounds = append(config.SkipPageBounds, path) }) +} + +// SkipPageStatistics lists the path to a column that shouldn't have statistics +// written for pages. This is useful for data blobs, like a raw html file, +// where the bounds are not meaningful. +// +// This option has no effect if DataPageStatistics(false) is used. +// +// This option is additive, it may be used multiple times to skip multiple columns. +func SkipPageStatistics(path ...string) WriterOption { + return writerOption(func(config *WriterConfig) { config.SkipPageStatistics = append(config.SkipPageStatistics, path) }) +} + +// DefaultEncodingFor creates a configuration option which sets the default encoding +// used by a writer for columns with the specified primitive type where none were defined. +// +// It will fail if the specified enconding isn't compatible with the specified primitive type. +func DefaultEncodingFor(kind Kind, enc encoding.Encoding) WriterOption { + return writerOption(func(config *WriterConfig) { defaultEncodingFor(config, kind, enc) }) +} + +func defaultEncodingFor(config *WriterConfig, kind Kind, enc encoding.Encoding) { + if !canEncode(enc, kind) { + panic("cannot use encoding " + enc.Encoding().String() + " for kind " + kind.String()) + } + if config.Encodings == nil { + config.Encodings = map[Kind]encoding.Encoding{kind: enc} + } else { + config.Encodings[kind] = enc + } +} + +// DefaultEncoding creates a configuration option which sets the default encoding +// used by a writer for columns where none were defined. +// +// It will fail if the specified enconding isn't compatible with any of the primitive types. +func DefaultEncoding(enc encoding.Encoding) WriterOption { + return writerOption(func(config *WriterConfig) { + defaultEncodingFor(config, Boolean, enc) + defaultEncodingFor(config, Int32, enc) + defaultEncodingFor(config, Int64, enc) + defaultEncodingFor(config, Int96, enc) + defaultEncodingFor(config, Float, enc) + defaultEncodingFor(config, Double, enc) + defaultEncodingFor(config, ByteArray, enc) + defaultEncodingFor(config, FixedLenByteArray, enc) + }) +} + +// DictionaryMaxBytes creates a configuration option which sets the maximum +// size in bytes for each column's dictionary. +// +// When a column's dictionary exceeds this limit, that column will switch from +// dictionary encoding to PLAIN encoding for the remainder of the row group. +// Pages written before the limit was reached remain dictionary-encoded, while +// subsequent pages use PLAIN encoding. +// +// A value of 0 (the default) means unlimited dictionary size. +func DictionaryMaxBytes(size int64) WriterOption { + return writerOption(func(config *WriterConfig) { config.DictionaryMaxBytes = size }) +} + +// ColumnBufferCapacity creates a configuration option which defines the size of +// row group column buffers. +// +// Defaults to 16384. +func ColumnBufferCapacity(size int) RowGroupOption { + return rowGroupOption(func(config *RowGroupConfig) { config.ColumnBufferCapacity = size }) +} + +// SortingRowGroupConfig is a row group option which applies configuration +// specific sorting row groups. +func SortingRowGroupConfig(options ...SortingOption) RowGroupOption { + options = slices.Clone(options) + return rowGroupOption(func(config *RowGroupConfig) { config.Sorting.Apply(options...) }) +} + +// SortingColumns creates a configuration option which defines the sorting order +// of columns in a row group. +// +// The order of sorting columns passed as argument defines the ordering +// hierarchy; when elements are equal in the first column, the second column is +// used to order rows, etc... +func SortingColumns(columns ...SortingColumn) SortingOption { + // Make a copy so that we do not retain the input slice generated implicitly + // for the variable argument list, and also avoid having a nil slice when + // the option is passed with no sorting columns, so we can differentiate it + // from it not being passed. + columns = slices.Clone(columns) + return sortingOption(func(config *SortingConfig) { config.SortingColumns = columns }) +} + +// SortingBuffers creates a configuration option which sets the pool of buffers +// used to hold intermediary state when sorting parquet rows. +// +// Defaults to using in-memory buffers. +func SortingBuffers(buffers BufferPool) SortingOption { + return sortingOption(func(config *SortingConfig) { config.SortingBuffers = buffers }) +} + +// DropDuplicatedRows configures whether a sorting writer will keep or remove +// duplicated rows. +// +// Two rows are considered duplicates if the values of their all their sorting +// columns are equal. +// +// Defaults to false +func DropDuplicatedRows(drop bool) SortingOption { + return sortingOption(func(config *SortingConfig) { config.DropDuplicatedRows = drop }) +} + +// The SchemaConfig type carries configuration options for parquet schemas. +// +// SchemaConfig implements the SchemaOption interface so it can be used directly +// as argument to the SchemaOf function when needed, for example: +// +// schema := parquet.SchemaOf(obj, &parquet.SchemaConfig{ +// ... +// }) +type SchemaConfig struct { + StructTags []StructTagOption +} + +func (c *SchemaConfig) ConfigureSchema(config *SchemaConfig) { + config.StructTags = coalesceSlices(c.StructTags, config.StructTags) +} + +func (c *SchemaConfig) ConfigureReader(config *ReaderConfig) { + c.ConfigureSchema(config.SchemaConfig) +} + +func (c *SchemaConfig) ConfigureWriter(config *WriterConfig) { + c.ConfigureSchema(config.SchemaConfig) +} + +func DefaultSchemaConfig() *SchemaConfig { + return &SchemaConfig{} +} + +// StructTagOption performs runtime replacement of "parquet..." struct tags. This +// option can be used anywhere a schema is derived from a Go struct including +// SchemaOf, NewGenericReader, and NewGenericWriter. +type StructTagOption struct { + ColumnPath []string + StructTag reflect.StructTag +} + +var ( + _ SchemaOption = (*StructTagOption)(nil) + _ ReaderOption = (*StructTagOption)(nil) + _ WriterOption = (*StructTagOption)(nil) +) + +// StructTag performs runtime replacement of struct tags when deriving a schema from +// a Go struct for the column at the given path. This option can be used anywhere a schema is +// derived from a Go struct including SchemaOf, NewGenericReader, and NewGenericWriter. +// +// This option is additive, it may be used multiple times to affect multiple columns. +// +// When renaming a column, configure the option by its original name. +func StructTag(tag reflect.StructTag, path ...string) SchemaOption { + return &StructTagOption{StructTag: tag, ColumnPath: path} +} + +func (f *StructTagOption) ConfigureSchema(config *SchemaConfig) { + config.StructTags = append(config.StructTags, *f) +} + +func (f *StructTagOption) ConfigureWriter(config *WriterConfig) { + f.ConfigureSchema(config.SchemaConfig) +} + +func (f *StructTagOption) ConfigureReader(config *ReaderConfig) { + f.ConfigureSchema(config.SchemaConfig) +} + +type fileOption func(*FileConfig) + +func (opt fileOption) ConfigureFile(config *FileConfig) { opt(config) } + +type readerOption func(*ReaderConfig) + +func (opt readerOption) ConfigureReader(config *ReaderConfig) { opt(config) } + +type writerOption func(*WriterConfig) + +func (opt writerOption) ConfigureWriter(config *WriterConfig) { opt(config) } + +type rowGroupOption func(*RowGroupConfig) + +func (opt rowGroupOption) ConfigureRowGroup(config *RowGroupConfig) { opt(config) } + +type sortingOption func(*SortingConfig) + +func (opt sortingOption) ConfigureSorting(config *SortingConfig) { opt(config) } + +func coalesceSlices[T any](s1, s2 []T) []T { + if s1 != nil { + return s1 + } + return s2 +} + +func coalesceFunc[F ~func(I) O, I, O any](f1, f2 F) F { + if f1 != nil { + return f1 + } + return f2 +} + +func coalesceSortingConfig(c1, c2 SortingConfig) SortingConfig { + return SortingConfig{ + SortingBuffers: cmp.Or(c1.SortingBuffers, c2.SortingBuffers), + SortingColumns: coalesceSlices(c1.SortingColumns, c2.SortingColumns), + DropDuplicatedRows: c1.DropDuplicatedRows, + } +} + +func validatePositiveInt(optionName string, optionValue int) error { + if optionValue > 0 { + return nil + } + return errorInvalidOptionValue(optionName, optionValue) +} + +func validatePositiveInt64(optionName string, optionValue int64) error { + if optionValue > 0 { + return nil + } + return errorInvalidOptionValue(optionName, optionValue) +} + +func validateOneOfInt(optionName string, optionValue int, supportedValues ...int) error { + if slices.Contains(supportedValues, optionValue) { + return nil + } + return errorInvalidOptionValue(optionName, optionValue) +} + +func validateNotNil(optionName string, optionValue any) error { + if optionValue != nil { + return nil + } + return errorInvalidOptionValue(optionName, optionValue) +} + +func errorInvalidOptionValue(optionName string, optionValue any) error { + return fmt.Errorf("invalid option value: %s: %v", optionName, optionValue) +} + +func errorInvalidConfiguration(reasons ...error) error { + var err *invalidConfiguration + + for _, reason := range reasons { + if reason != nil { + if err == nil { + err = new(invalidConfiguration) + } + err.reasons = append(err.reasons, reason) + } + } + + if err != nil { + return err + } + + return nil +} + +type invalidConfiguration struct { + reasons []error +} + +func (err *invalidConfiguration) Error() string { + errorMessage := new(strings.Builder) + for _, reason := range err.reasons { + errorMessage.WriteString(reason.Error()) + errorMessage.WriteString("\n") + } + errorString := errorMessage.String() + if errorString != "" { + errorString = errorString[:len(errorString)-1] + } + return errorString +} + +var ( + _ FileOption = (*FileConfig)(nil) + _ ReaderOption = (*ReaderConfig)(nil) + _ WriterOption = (*WriterConfig)(nil) + _ RowGroupOption = (*RowGroupConfig)(nil) + _ SortingOption = (*SortingConfig)(nil) + _ SchemaOption = (*SchemaConfig)(nil) +) diff --git a/vendor/github.com/parquet-go/parquet-go/convert.go b/vendor/github.com/parquet-go/parquet-go/convert.go new file mode 100644 index 000000000..a1d043994 --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/convert.go @@ -0,0 +1,1590 @@ +package parquet + +import ( + "encoding/binary" + "encoding/hex" + "fmt" + "io" + "math" + "math/big" + "strconv" + "time" + + "github.com/parquet-go/parquet-go/internal/memory" + "golang.org/x/sys/cpu" + + "github.com/parquet-go/bitpack/unsafecast" + "github.com/parquet-go/parquet-go/deprecated" + "github.com/parquet-go/parquet-go/encoding" + "github.com/parquet-go/parquet-go/format" +) + +// ConvertError is an error type returned by calls to Convert when the conversion +// of parquet schemas is impossible or the input row for the conversion is +// malformed. +type ConvertError struct { + Path []string + From Node + To Node +} + +// Error satisfies the error interface. +func (e *ConvertError) Error() string { + sourceType := e.From.Type() + targetType := e.To.Type() + + sourceRepetition := fieldRepetitionTypeOf(e.From) + targetRepetition := fieldRepetitionTypeOf(e.To) + + return fmt.Sprintf("cannot convert parquet column %q from %s %s to %s %s", + columnPath(e.Path), + sourceRepetition, + sourceType, + targetRepetition, + targetType, + ) +} + +// Conversion is an interface implemented by types that provide conversion of +// parquet rows from one schema to another. +// +// Conversion instances must be safe to use concurrently from multiple goroutines. +type Conversion interface { + // Applies the conversion logic on the src row, returning the result + // appended to dst. + Convert(rows []Row) (int, error) + // Converts the given column index in the target schema to the original + // column index in the source schema of the conversion. + Column(int) int + // Returns the target schema of the conversion. + Schema() *Schema +} + +type conversion struct { + columns []conversionColumn + schema *Schema + buffers memory.Pool[conversionBuffer] + // This field is used to size the column buffers held in the memory.Pool since + // they are intended to store the source rows being converted from. + numberOfSourceColumns int +} + +type conversionBuffer struct { + columns [][]Value +} + +type conversionColumn struct { + sourceIndex int + convertValues conversionFunc + targetKind Kind // Target column kind for creating proper null values + isOptional bool // Whether the target column is optional (for null handling) +} + +type conversionFunc func([]Value) error + +func convertToSelf(column []Value) error { return nil } + +func convertToType(targetType, sourceType Type) conversionFunc { + return func(column []Value) error { + for i, v := range column { + v, err := targetType.ConvertValue(v, sourceType) + if err != nil { + return err + } + column[i].ptr = v.ptr + column[i].u64 = v.u64 + column[i].kind = v.kind + } + return nil + } +} + +func convertToZero(kind Kind) conversionFunc { + switch kind { + case Boolean: + return convertToZeroBoolean + case Int32: + return convertToZeroInt32 + case Int64: + return convertToZeroInt64 + case Int96: + return convertToZeroInt96 + case Float: + return convertToZeroFloat + case Double: + return convertToZeroDouble + case ByteArray: + return convertToZeroByteArray + case FixedLenByteArray: + return convertToZeroFixedLenByteArray + default: + return convertToSelf + } +} + +func convertToZeroKind(column []Value, kind int8) error { + for i := range column { + column[i].ptr = nil + column[i].u64 = 0 + column[i].kind = kind + } + return nil +} + +func convertToZeroBoolean(column []Value) error { + return convertToZeroKind(column, ^int8(Boolean)) +} + +func convertToZeroInt32(column []Value) error { + return convertToZeroKind(column, ^int8(Int32)) +} + +func convertToZeroInt64(column []Value) error { + return convertToZeroKind(column, ^int8(Int64)) +} + +func convertToZeroInt96(column []Value) error { + return convertToZeroKind(column, ^int8(Int96)) +} + +func convertToZeroFloat(column []Value) error { + return convertToZeroKind(column, ^int8(Float)) +} + +func convertToZeroDouble(column []Value) error { + return convertToZeroKind(column, ^int8(Double)) +} + +func convertToZeroByteArray(column []Value) error { + return convertToZeroKind(column, ^int8(ByteArray)) +} + +func convertToZeroFixedLenByteArray(column []Value) error { + return convertToZeroKind(column, ^int8(FixedLenByteArray)) +} + +func convertToNull(column []Value) error { + return convertToZeroKind(column, 0) // kind = 0 indicates null +} + +func convertToNullOptional(maxDefinitionLevel byte) conversionFunc { + return func(column []Value) error { + for i := range column { + column[i].ptr = nil + column[i].u64 = 0 + column[i].kind = 0 // kind = 0 indicates null + // For optional fields, if the source value is present (defLevel == max), + // we need to set defLevel to max-1 to indicate null at the leaf level + if column[i].definitionLevel == maxDefinitionLevel { + column[i].definitionLevel-- + } + // If source is already null (defLevel < max), keep the same defLevel + } + return nil + } +} + +func convertToLevels(repetitionLevels, definitionLevels []byte) conversionFunc { + return func(column []Value) error { + for i := range column { + r := column[i].repetitionLevel + d := column[i].definitionLevel + column[i].repetitionLevel = repetitionLevels[r] + column[i].definitionLevel = definitionLevels[d] + } + return nil + } +} + +func multiConversionFunc(conversions []conversionFunc) conversionFunc { + switch len(conversions) { + case 0: + return convertToSelf + case 1: + return conversions[0] + default: + return func(column []Value) error { + for _, conv := range conversions { + if err := conv(column); err != nil { + return err + } + } + return nil + } + } +} + +func (c *conversion) getBuffer() *conversionBuffer { + return c.buffers.Get( + func() *conversionBuffer { + b := &conversionBuffer{ + columns: make([][]Value, c.numberOfSourceColumns), + } + values := make([]Value, c.numberOfSourceColumns) + for i := range b.columns { + b.columns[i] = values[i : i : i+1] + } + return b + }, + func(b *conversionBuffer) {}, + ) +} + +func (c *conversion) putBuffer(b *conversionBuffer) { + c.buffers.Put(b) +} + +// Convert here satisfies the Conversion interface, and does the actual work +// to convert between the source and target Rows. +func (c *conversion) Convert(rows []Row) (int, error) { + source := c.getBuffer() + defer c.putBuffer(source) + + for n, row := range rows { + for i, values := range source.columns { + source.columns[i] = values[:0] + } + row.Range(func(columnIndex int, columnValues []Value) bool { + source.columns[columnIndex] = append(source.columns[columnIndex], columnValues...) + return true + }) + row = row[:0] + + for columnIndex, conv := range c.columns { + columnOffset := len(row) + + // Get source values if available + var sourceValues []Value + if conv.sourceIndex >= 0 { + sourceValues = source.columns[conv.sourceIndex] + } + + if len(sourceValues) > 0 { + row = append(row, sourceValues...) + } else { + // No source values - add placeholder to maintain sequential column order. + // This happens when the column doesn't exist in source or has no values for this row. + if conv.isOptional { + row = append(row, NullValue()) + } else { + row = append(row, ZeroValue(conv.targetKind)) + } + } + columnValues := row[columnOffset:] + + if err := conv.convertValues(columnValues); err != nil { + return n, err + } + + // Since the column index may have changed between the source and + // taget columns we ensure that the right value is always written + // to the output row. + for i := range columnValues { + // Fix: If we have a zero Value{}, convert it to a properly typed value + // For optional fields, keep as null (kind = 0) + // For required fields, convert to typed zero value + if columnValues[i].IsNull() && !conv.isOptional { + columnValues[i] = ZeroValue(conv.targetKind) + } + + columnValues[i].columnIndex = ^uint16(columnIndex) + } + } + + rows[n] = row + } + + return len(rows), nil +} + +func (c *conversion) Column(i int) int { + return c.columns[i].sourceIndex +} + +func (c *conversion) Schema() *Schema { + return c.schema +} + +type identity struct{ schema *Schema } + +func (id identity) Convert(rows []Row) (int, error) { return len(rows), nil } +func (id identity) Column(i int) int { return i } +func (id identity) Schema() *Schema { return id.schema } + +// Convert constructs a conversion function from one parquet schema to another. +// +// The function supports converting between schemas where the source or target +// have extra columns; if there are more columns in the source, they will be +// stripped out of the rows. Extra columns in the target schema will be set to +// null or zero values. +// +// The returned function is intended to be used to append the converted source +// row to the destination buffer. +func Convert(to, from Node) (conv Conversion, err error) { + schema, _ := to.(*Schema) + if schema == nil { + schema = NewSchema("", to) + } + + if EqualNodes(to, from) { + return identity{schema}, nil + } + + targetMapping, targetColumns := columnMappingOf(to) + sourceMapping, sourceColumns := columnMappingOf(from) + columns := make([]conversionColumn, len(targetColumns)) + + targetFieldCache := make(map[Node]map[string]Field) + sourceFieldCache := make(map[Node]map[string]Field) + + // Helper function to get or build field map for a node + getFieldMap := func(cache map[Node]map[string]Field, node Node) map[string]Field { + if fieldMap, ok := cache[node]; ok { + return fieldMap + } + fields := node.Fields() + fieldMap := make(map[string]Field, len(fields)) + for _, f := range fields { + fieldMap[f.Name()] = f + } + cache[node] = fieldMap + return fieldMap + } + + for i, path := range targetColumns { + targetColumn := targetMapping.lookup(path) + sourceColumn := sourceMapping.lookup(path) + + // Track whether this column exists in source BEFORE any closestColumn lookup + isMissingColumn := sourceColumn.node == nil + + conversions := []conversionFunc{} + if sourceColumn.node != nil { + targetType := targetColumn.node.Type() + sourceType := sourceColumn.node.Type() + if !EqualTypes(targetType, sourceType) { + conversions = append(conversions, convertToType(targetType, sourceType)) + } + + repetitionLevels := make([]byte, len(path)+1) + definitionLevels := make([]byte, len(path)+1) + targetRepetitionLevel := byte(0) + targetDefinitionLevel := byte(0) + sourceRepetitionLevel := byte(0) + sourceDefinitionLevel := byte(0) + targetNode := to + sourceNode := from + + for j := range path { + targetNode = getFieldMap(targetFieldCache, targetNode)[path[j]] + sourceNode = getFieldMap(sourceFieldCache, sourceNode)[path[j]] + + targetRepetitionLevel, targetDefinitionLevel = applyFieldRepetitionType( + fieldRepetitionTypeOf(targetNode), + targetRepetitionLevel, + targetDefinitionLevel, + ) + sourceRepetitionLevel, sourceDefinitionLevel = applyFieldRepetitionType( + fieldRepetitionTypeOf(sourceNode), + sourceRepetitionLevel, + sourceDefinitionLevel, + ) + + repetitionLevels[sourceRepetitionLevel] = targetRepetitionLevel + definitionLevels[sourceDefinitionLevel] = targetDefinitionLevel + } + + repetitionLevels = repetitionLevels[:sourceRepetitionLevel+1] + definitionLevels = definitionLevels[:sourceDefinitionLevel+1] + + if !isDirectLevelMapping(repetitionLevels) || !isDirectLevelMapping(definitionLevels) { + conversions = append(conversions, convertToLevels(repetitionLevels, definitionLevels)) + } + + } else { + // Column doesn't exist in source - this is a missing column + targetType := targetColumn.node.Type() + targetKind := targetType.Kind() + + // Check if the leaf field itself is optional (not just nested in optional/repeated structure) + isOptionalField := targetColumn.node.Optional() + + closestColumn := sourceMapping.lookupClosest(path) + if closestColumn.node != nil { + // There's a sibling column we can use as a template for structure. + // The sibling's values provide the repetition count for repeated groups. + // The conversion functions transform them to typed zeros or nulls. + if isOptionalField { + // Optional field: convert to null values while mirroring structure + conversions = append(conversions, convertToNullOptional(targetColumn.maxDefinitionLevel)) + } else { + // Required field: convert to typed zero values + conversions = append(conversions, convertToZero(targetKind)) + } + // Use the sibling column as the source for structural information + sourceColumn = closestColumn + } else { + // No sibling columns exist + if !isOptionalField { + // Required field: create typed zero value + conversions = append(conversions, convertToZero(targetKind)) + } + // Keep sourceColumn with columnIndex -1 + // For optional fields without siblings, we'll create a single null value per row + } + } + + // Store target column type for creating proper null values + targetType := targetColumn.node.Type() + + // Determine sourceIndex: use the source column index (which may be a + // sibling's index if the column is missing but has a sibling). + // For missing columns without siblings, sourceColumn.columnIndex will + // be math.MaxUint16 (from the initial lookup returning an empty leafColumn). + sourceIndex := int(sourceColumn.columnIndex) + // Only set to -1 if truly missing AND no sibling was found + if isMissingColumn && sourceColumn.node == nil { + sourceIndex = -1 + } + + // Determine if target column is optional + isOptional := targetColumn.maxDefinitionLevel > 0 + + columns[i] = conversionColumn{ + sourceIndex: sourceIndex, + convertValues: multiConversionFunc(conversions), + targetKind: targetType.Kind(), // Store target kind for null value creation + isOptional: isOptional, + } + } + + c := &conversion{ + columns: columns, + schema: schema, + numberOfSourceColumns: len(sourceColumns), + } + return c, nil +} + +func isDirectLevelMapping(levels []byte) bool { + for i, level := range levels { + if level != byte(i) { + return false + } + } + return true +} + +// findAdjacentColumnChunk finds a sibling column at the same repetition depth +// Returns nil if no suitable adjacent column exists +func findAdjacentColumnChunk(schema *Schema, targetColumnIndex uint16, columns []ColumnChunk, sourceMapping columnMapping) ColumnChunk { + var targetLeaf leafColumn + targetFound := false + + forEachLeafColumnOf(schema, func(leaf leafColumn) { + if leaf.columnIndex == targetColumnIndex { + targetLeaf = leaf + targetFound = true + } + }) + + if !targetFound { + return nil + } + + // Find a sibling: same parent path and same max repetition level + targetParentPath := targetLeaf.path + if len(targetParentPath) > 0 { + targetParentPath = targetParentPath[:len(targetParentPath)-1] + } + + var adjacentChunk ColumnChunk + forEachLeafColumnOf(schema, func(leaf leafColumn) { + if leaf.columnIndex == targetColumnIndex { + return // Skip self + } + + // Check if this is a sibling + if len(leaf.path) > 0 { + leafParentPath := leaf.path[:len(leaf.path)-1] + if targetParentPath.equal(leafParentPath) && + leaf.maxRepetitionLevel == targetLeaf.maxRepetitionLevel { + // Check if this column exists in the converted row group + if int(leaf.columnIndex) < len(columns) && columns[leaf.columnIndex] != nil { + // Make sure it's not another missing column + if _, ok := columns[leaf.columnIndex].(*missingColumnChunk); !ok { + adjacentChunk = columns[leaf.columnIndex] + return // Found a suitable adjacent column + } + } + } + } + }) + + return adjacentChunk +} + +// ConvertRowGroup constructs a wrapper of the given row group which applies +// the given schema conversion to its rows. +func ConvertRowGroup(rowGroup RowGroup, conv Conversion) RowGroup { + if EqualNodes(rowGroup.Schema(), conv.Schema()) { + return rowGroup + } + schema := conv.Schema() + numRows := rowGroup.NumRows() + rowGroupColumns := rowGroup.ColumnChunks() + sourceSchema := rowGroup.Schema() + + // Build a mapping to detect missing columns + sourceMapping, _ := columnMappingOf(sourceSchema) + + columns := make([]ColumnChunk, numLeafColumnsOf(schema)) + + // First pass: create all non-missing columns + forEachLeafColumnOf(schema, func(leaf leafColumn) { + i := leaf.columnIndex + j := conv.Column(int(leaf.columnIndex)) + + // Check if this column actually exists in the source schema + sourceColumn := sourceMapping.lookup(leaf.path) + isMissing := sourceColumn.node == nil + + if !isMissing { + if i == uint16(j) { + columns[i] = rowGroupColumns[j] + } else { + columns[i] = &convertedColumnChunk{ + chunk: rowGroupColumns[j], + targetColumnIndex: ^i, + } + } + } + }) + + // Second pass: create missing columns with references to adjacent columns + forEachLeafColumnOf(schema, func(leaf leafColumn) { + i := leaf.columnIndex + + // Check if this column actually exists in the source schema + sourceColumn := sourceMapping.lookup(leaf.path) + isMissing := sourceColumn.node == nil + + if isMissing { + var adjacentChunk ColumnChunk + if leaf.maxRepetitionLevel > 0 { + // Find adjacent column for mirroring levels + adjacentChunk = findAdjacentColumnChunk(schema, i, columns, sourceMapping) + } + + columns[i] = &missingColumnChunk{ + typ: leaf.node.Type(), + column: i, + numRows: numRows, + numValues: numRows, // May be adjusted when reading + numNulls: numRows, // Depends on required vs optional + maxRepetitionLevel: leaf.maxRepetitionLevel, + maxDefinitionLevel: leaf.maxDefinitionLevel, + adjacentChunk: adjacentChunk, + } + } + }) + + // Sorting columns must exist on the conversion schema in order to be + // advertised on the converted row group otherwise the resulting rows + // would not be in the right order. + sorting := []SortingColumn{} + for _, col := range rowGroup.SortingColumns() { + if !hasColumnPath(schema, col.Path()) { + break + } + sorting = append(sorting, col) + } + + return &convertedRowGroup{ + // The pair of rowGroup+conv is retained to construct a converted row + // reader by wrapping the underlying row reader of the row group because + // it allows proper reconstruction of the repetition and definition + // levels. + // + // Columns of the source row group which do not exist in the target are + // masked to prevent loading unneeded pages when reading rows from the + // converted row group. + rowGroup: maskMissingRowGroupColumns(rowGroup, len(columns), conv), + columns: columns, + sorting: sorting, + conv: conv, + } +} + +func maskMissingRowGroupColumns(r RowGroup, numColumns int, conv Conversion) RowGroup { + rowGroupColumns := r.ColumnChunks() + columns := make([]ColumnChunk, len(rowGroupColumns)) + missing := make([]missingColumnChunk, len(columns)) + numRows := r.NumRows() + + // Compute max levels for each column in the source schema + sourceSchema := r.Schema() + forEachLeafColumnOf(sourceSchema, func(leaf leafColumn) { + i := leaf.columnIndex + missing[i] = missingColumnChunk{ + typ: rowGroupColumns[i].Type(), + column: i, + numRows: numRows, + numValues: numRows, + numNulls: numRows, + maxRepetitionLevel: leaf.maxRepetitionLevel, + maxDefinitionLevel: leaf.maxDefinitionLevel, + } + }) + + for i := range columns { + columns[i] = &missing[i] + } + + for i := range numColumns { + j := conv.Column(i) + if j >= 0 && j < len(columns) { + columns[j] = rowGroupColumns[j] + } + } + + return &rowGroup{ + schema: r.Schema(), + numRows: numRows, + columns: columns, + } +} + +type missingColumnChunk struct { + typ Type + column uint16 + numRows int64 + numValues int64 + numNulls int64 + maxRepetitionLevel byte // Maximum repetition level for this column + maxDefinitionLevel byte // Maximum definition level for this column + adjacentChunk ColumnChunk // Adjacent column chunk to mirror levels from (nil if none) +} + +func (c *missingColumnChunk) Type() Type { return c.typ } +func (c *missingColumnChunk) Column() int { return int(c.column) } +func (c *missingColumnChunk) Pages() Pages { + var adjacentPages Pages + if c.adjacentChunk != nil { + adjacentPages = c.adjacentChunk.Pages() + } + return onePage(missingPage{ + missingColumnChunk: c, + adjacentPages: adjacentPages, + }) +} +func (c *missingColumnChunk) ColumnIndex() (ColumnIndex, error) { return missingColumnIndex{c}, nil } +func (c *missingColumnChunk) OffsetIndex() (OffsetIndex, error) { return missingOffsetIndex{}, nil } +func (c *missingColumnChunk) BloomFilter() BloomFilter { return missingBloomFilter{} } +func (c *missingColumnChunk) NumValues() int64 { return c.numValues } + +type missingColumnIndex struct{ *missingColumnChunk } + +func (i missingColumnIndex) NumPages() int { return 1 } +func (i missingColumnIndex) NullCount(int) int64 { return i.numNulls } +func (i missingColumnIndex) NullPage(int) bool { return true } +func (i missingColumnIndex) MinValue(int) Value { return Value{} } +func (i missingColumnIndex) MaxValue(int) Value { return Value{} } +func (i missingColumnIndex) IsAscending() bool { return true } +func (i missingColumnIndex) IsDescending() bool { return false } + +type missingOffsetIndex struct{} + +func (missingOffsetIndex) NumPages() int { return 1 } +func (missingOffsetIndex) Offset(int) int64 { return 0 } +func (missingOffsetIndex) CompressedPageSize(int) int64 { return 0 } +func (missingOffsetIndex) FirstRowIndex(int) int64 { return 0 } + +type missingBloomFilter struct{} + +func (missingBloomFilter) ReadAt([]byte, int64) (int, error) { return 0, io.EOF } +func (missingBloomFilter) Size() int64 { return 0 } +func (missingBloomFilter) Check(Value) (bool, error) { return false, nil } + +type missingPage struct { + *missingColumnChunk + adjacentPages Pages // Pages from adjacent column for level mirroring +} + +func (p missingPage) Column() int { return int(p.column) } +func (p missingPage) Dictionary() Dictionary { return nil } +func (p missingPage) NumRows() int64 { return p.numRows } +func (p missingPage) NumValues() int64 { return p.numValues } +func (p missingPage) NumNulls() int64 { return p.numNulls } +func (p missingPage) Bounds() (min, max Value, ok bool) { return } +func (p missingPage) Slice(i, j int64) Page { + return missingPage{ + missingColumnChunk: &missingColumnChunk{ + typ: p.typ, + column: p.column, + numRows: j - i, + numValues: j - i, + numNulls: j - i, + maxRepetitionLevel: p.maxRepetitionLevel, + maxDefinitionLevel: p.maxDefinitionLevel, + adjacentChunk: p.adjacentChunk, + }, + adjacentPages: p.adjacentPages, + } +} +func (p missingPage) Size() int64 { return 0 } +func (p missingPage) RepetitionLevels() []byte { return nil } +func (p missingPage) DefinitionLevels() []byte { return nil } +func (p missingPage) Data() encoding.Values { return p.typ.NewValues(nil, nil) } +func (p missingPage) Values() ValueReader { + return &missingPageValues{ + page: p, + adjacentPages: p.adjacentPages, // Store the Pages reader to read multiple pages + adjacentBuffer: make([]Value, 1024), // Reasonable buffer size + } +} + +type missingPageValues struct { + page missingPage + read int64 + adjacentPages Pages // Pages reader for adjacent column (to read multiple pages) + adjacentReader ValueReader // Current page's reader for adjacent column to mirror levels + adjacentBuffer []Value // Buffer for reading adjacent values +} + +func (r *missingPageValues) ReadValues(values []Value) (int, error) { + remain := r.page.numValues - r.read + if remain == 0 { + return 0, io.EOF + } + if int64(len(values)) > remain { + values = values[:remain] + } + + typ := r.page.typ + columnIndex := ^r.page.column + + var n int + var err error + // Case 1: No adjacent column (root-level field, no siblings) + if r.adjacentPages == nil { + n, err = r.readWithoutAdjacent(values, typ, columnIndex) + } else { + // Case 2: Has adjacent column - mirror its repetition/definition levels + n, err = r.readWithAdjacent(values, typ, columnIndex) + } + + return n, err +} + +func (r *missingPageValues) readWithoutAdjacent(values []Value, typ Type, columnIndex uint16) (int, error) { + // For fields without siblings, assume one value per row + // Definition level depends on whether field is required or optional + isRequired := r.page.maxDefinitionLevel == 0 + + if isRequired { + // Required field: produce zero/default values + for i := range values { + values[i] = ZeroValue(typ.Kind()) + values[i].repetitionLevel = 0 + values[i].definitionLevel = r.page.maxDefinitionLevel // Present value + values[i].columnIndex = columnIndex + } + } else { + // Optional field: produce nulls + definitionLevel := byte(0) + if r.page.maxDefinitionLevel > 0 { + definitionLevel = r.page.maxDefinitionLevel - 1 + } + + for i := range values { + values[i] = Value{ + repetitionLevel: 0, + definitionLevel: definitionLevel, + columnIndex: columnIndex, + } + } + } + + r.read += int64(len(values)) + if r.read == r.page.numValues { + return len(values), io.EOF + } + return len(values), nil +} + +func (r *missingPageValues) readWithAdjacent(values []Value, typ Type, columnIndex uint16) (int, error) { + // Determine if this missing column is required or optional + isRequired := r.page.maxDefinitionLevel == 0 + totalRead := 0 + + for totalRead < len(values) { + // Initialize or get next page from adjacent column if needed + if r.adjacentReader == nil { + adjacentPage, err := r.adjacentPages.ReadPage() + if err == io.EOF { + // No more adjacent pages - we're done + if totalRead > 0 { + r.read += int64(totalRead) + return totalRead, nil + } + r.read += int64(totalRead) + return totalRead, io.EOF + } + if err != nil { + return totalRead, err + } + r.adjacentReader = adjacentPage.Values() + } + + // Read values from adjacent column to get its levels + remaining := values[totalRead:] + n, err := r.adjacentReader.ReadValues(r.adjacentBuffer[:len(remaining)]) + + for i := range n { + repLevel := r.adjacentBuffer[i].repetitionLevel + adjacentDefLevel := r.adjacentBuffer[i].definitionLevel + + var defLevel byte + var value Value + + if isRequired { + // Required field: produce zero/default values + value = ZeroValue(typ.Kind()) + // Mirror adjacent definition level structure + defLevel = adjacentDefLevel + } else { + // Optional field: produce nulls + // Definition level indicates null at appropriate nesting + if adjacentDefLevel < r.page.maxDefinitionLevel { + // Adjacent is null at some level, follow its definition + defLevel = adjacentDefLevel + } else { + // Adjacent is present, but we are null at leaf level + defLevel = r.page.maxDefinitionLevel - 1 + } + } + + value.repetitionLevel = repLevel + value.definitionLevel = defLevel + value.columnIndex = columnIndex + remaining[i] = value + } + + totalRead += n + + if err == io.EOF { + // Current adjacent page exhausted, try next page + r.adjacentReader = nil + continue + } + if err != nil { + return totalRead, err + } + } + + r.read += int64(totalRead) + return totalRead, nil +} + +func (r *missingPageValues) Close() error { + r.read = r.page.numValues + if r.adjacentPages != nil { + return r.adjacentPages.Close() + } + return nil +} + +type convertedRowGroup struct { + rowGroup RowGroup + columns []ColumnChunk + sorting []SortingColumn + conv Conversion +} + +func (c *convertedRowGroup) NumRows() int64 { return c.rowGroup.NumRows() } +func (c *convertedRowGroup) ColumnChunks() []ColumnChunk { return c.columns } +func (c *convertedRowGroup) Schema() *Schema { return c.conv.Schema() } +func (c *convertedRowGroup) SortingColumns() []SortingColumn { return c.sorting } +func (c *convertedRowGroup) Rows() Rows { + rows := c.rowGroup.Rows() + return &convertedRows{ + Closer: rows, + rows: rows, + conv: c.conv, + } +} + +// ConvertRowReader constructs a wrapper of the given row reader which applies +// the given schema conversion to the rows. +func ConvertRowReader(rows RowReader, conv Conversion) RowReaderWithSchema { + return &convertedRows{rows: &forwardRowSeeker{rows: rows}, conv: conv} +} + +type convertedRows struct { + io.Closer + rows RowReadSeeker + conv Conversion +} + +func (c *convertedRows) ReadRows(rows []Row) (int, error) { + n, err := c.rows.ReadRows(rows) + if n > 0 { + var convErr error + n, convErr = c.conv.Convert(rows[:n]) + if convErr != nil { + err = convErr + } + } + return n, err +} + +func (c *convertedRows) Schema() *Schema { + return c.conv.Schema() +} + +func (c *convertedRows) SeekToRow(rowIndex int64) error { + return c.rows.SeekToRow(rowIndex) +} + +// convertedColumnChunk wraps a ColumnChunk to fix the column index after reordering. +// When ConvertRowGroup reorders columns, the underlying chunk's Column() method +// returns the original position. This wrapper fixes both Column() and the +// columnIndex in values read from the chunk. +type convertedColumnChunk struct { + chunk ColumnChunk + targetColumnIndex uint16 // XOR-encoded column index (^uint16(columnIndex)) +} + +func (c *convertedColumnChunk) Type() Type { + return c.chunk.Type() +} + +func (c *convertedColumnChunk) Column() int { + return int(^c.targetColumnIndex) +} + +func (c *convertedColumnChunk) NumValues() int64 { + return c.chunk.NumValues() +} + +func (c *convertedColumnChunk) Pages() Pages { + return &convertedPages{ + pages: c.chunk.Pages(), + targetColumnIndex: c.targetColumnIndex, + } +} + +func (c *convertedColumnChunk) ColumnIndex() (ColumnIndex, error) { + return c.chunk.ColumnIndex() +} + +func (c *convertedColumnChunk) OffsetIndex() (OffsetIndex, error) { + return c.chunk.OffsetIndex() +} + +func (c *convertedColumnChunk) BloomFilter() BloomFilter { + return c.chunk.BloomFilter() +} + +// convertedPages wraps Pages to return convertedPage instances. +type convertedPages struct { + pages Pages + targetColumnIndex uint16 +} + +func (p *convertedPages) ReadPage() (Page, error) { + page, err := p.pages.ReadPage() + if err != nil { + return nil, err + } + return &convertedPage{ + page: page, + targetColumnIndex: p.targetColumnIndex, + }, nil +} + +func (p *convertedPages) SeekToRow(rowIndex int64) error { + return p.pages.SeekToRow(rowIndex) +} + +func (p *convertedPages) Close() error { + return p.pages.Close() +} + +// convertedPage wraps a Page to return a convertedValueReader. +type convertedPage struct { + page Page + targetColumnIndex uint16 +} + +func (p *convertedPage) Type() Type { + return p.page.Type() +} + +func (p *convertedPage) Column() int { + return int(^p.targetColumnIndex) +} + +func (p *convertedPage) Dictionary() Dictionary { + return p.page.Dictionary() +} + +func (p *convertedPage) NumRows() int64 { + return p.page.NumRows() +} + +func (p *convertedPage) NumValues() int64 { + return p.page.NumValues() +} + +func (p *convertedPage) NumNulls() int64 { + return p.page.NumNulls() +} + +func (p *convertedPage) Bounds() (min, max Value, ok bool) { + return p.page.Bounds() +} + +func (p *convertedPage) Size() int64 { + return p.page.Size() +} + +func (p *convertedPage) RepetitionLevels() []byte { + return p.page.RepetitionLevels() +} + +func (p *convertedPage) DefinitionLevels() []byte { + return p.page.DefinitionLevels() +} + +func (p *convertedPage) Data() encoding.Values { + return p.page.Data() +} + +func (p *convertedPage) Values() ValueReader { + return &convertedValueReader{ + reader: p.page.Values(), + targetColumnIndex: p.targetColumnIndex, + } +} + +func (p *convertedPage) Slice(i, j int64) Page { + return &convertedPage{ + page: p.page.Slice(i, j), + targetColumnIndex: p.targetColumnIndex, + } +} + +func (p *convertedPage) Retain() { + Retain(p.page) +} + +func (p *convertedPage) Release() { + Release(p.page) +} + +func (p *convertedPage) ReleaseAndDetachValues() { + releaseAndDetachValues(p.page) +} + +var ( + _ retainable = (*convertedPage)(nil) + _ releasable = (*convertedPage)(nil) + _ detachable = (*convertedPage)(nil) +) + +// convertedValueReader wraps a ValueReader to rewrite columnIndex in values. +type convertedValueReader struct { + reader ValueReader + targetColumnIndex uint16 +} + +func (r *convertedValueReader) ReadValues(values []Value) (int, error) { + n, err := r.reader.ReadValues(values) + // Rewrite columnIndex for all values to match the target column position + for i := range n { + values[i].columnIndex = r.targetColumnIndex + } + return n, err +} + +var ( + trueBytes = []byte(`true`) + falseBytes = []byte(`false`) + unixEpoch = time.Date(1970, time.January, 1, 0, 0, 0, 0, time.UTC) +) + +func convertBooleanToInt32(v Value) (Value, error) { + return v.convertToInt32(int32(v.byte())), nil +} + +func convertBooleanToInt64(v Value) (Value, error) { + return v.convertToInt64(int64(v.byte())), nil +} + +func convertBooleanToInt96(v Value) (Value, error) { + return v.convertToInt96(deprecated.Int96{0: uint32(v.byte())}), nil +} + +func convertBooleanToFloat(v Value) (Value, error) { + return v.convertToFloat(float32(v.byte())), nil +} + +func convertBooleanToDouble(v Value) (Value, error) { + return v.convertToDouble(float64(v.byte())), nil +} + +func convertBooleanToByteArray(v Value) (Value, error) { + return v.convertToByteArray([]byte{v.byte()}), nil +} + +func convertBooleanToFixedLenByteArray(v Value, size int) (Value, error) { + b := []byte{v.byte()} + c := make([]byte, size) + copy(c, b) + return v.convertToFixedLenByteArray(c), nil +} + +func convertBooleanToString(v Value) (Value, error) { + b := ([]byte)(nil) + if v.boolean() { + b = trueBytes + } else { + b = falseBytes + } + return v.convertToByteArray(b), nil +} + +func convertInt32ToBoolean(v Value) (Value, error) { + return v.convertToBoolean(v.int32() != 0), nil +} + +func convertInt32ToInt64(v Value) (Value, error) { + return v.convertToInt64(int64(v.int32())), nil +} + +func convertInt32ToInt96(v Value) (Value, error) { + return v.convertToInt96(deprecated.Int32ToInt96(v.int32())), nil +} + +func convertInt32ToFloat(v Value) (Value, error) { + return v.convertToFloat(float32(v.int32())), nil +} + +func convertInt32ToDouble(v Value) (Value, error) { + return v.convertToDouble(float64(v.int32())), nil +} + +func convertInt32ToByteArray(v Value) (Value, error) { + b := make([]byte, 4) + binary.LittleEndian.PutUint32(b, v.uint32()) + return v.convertToByteArray(b), nil +} + +func convertInt32ToFixedLenByteArray(v Value, size int) (Value, error) { + b := make([]byte, 4) + c := make([]byte, size) + binary.LittleEndian.PutUint32(b, v.uint32()) + copy(c, b) + return v.convertToFixedLenByteArray(c), nil +} + +func convertInt32ToString(v Value) (Value, error) { + return v.convertToByteArray(strconv.AppendInt(nil, int64(v.int32()), 10)), nil +} + +func convertInt64ToBoolean(v Value) (Value, error) { + return v.convertToBoolean(v.int64() != 0), nil +} + +func convertInt64ToInt32(v Value) (Value, error) { + return v.convertToInt32(int32(v.int64())), nil +} + +func convertInt64ToInt96(v Value) (Value, error) { + return v.convertToInt96(deprecated.Int64ToInt96(v.int64())), nil +} + +func convertInt64ToFloat(v Value) (Value, error) { + return v.convertToFloat(float32(v.int64())), nil +} + +func convertInt64ToDouble(v Value) (Value, error) { + return v.convertToDouble(float64(v.int64())), nil +} + +func convertInt64ToByteArray(v Value) (Value, error) { + b := make([]byte, 8) + binary.LittleEndian.PutUint64(b, v.uint64()) + return v.convertToByteArray(b), nil +} + +func convertInt64ToFixedLenByteArray(v Value, size int) (Value, error) { + b := make([]byte, 8) + c := make([]byte, size) + binary.LittleEndian.PutUint64(b, v.uint64()) + copy(c, b) + return v.convertToFixedLenByteArray(c), nil +} + +func convertInt64ToString(v Value) (Value, error) { + return v.convertToByteArray(strconv.AppendInt(nil, v.int64(), 10)), nil +} + +func convertInt96ToBoolean(v Value) (Value, error) { + return v.convertToBoolean(!v.int96().IsZero()), nil +} + +func convertInt96ToInt32(v Value) (Value, error) { + return v.convertToInt32(v.int96().Int32()), nil +} + +func convertInt96ToInt64(v Value) (Value, error) { + return v.convertToInt64(v.int96().Int64()), nil +} + +func convertInt96ToFloat(v Value) (Value, error) { + return v, invalidConversion(v, "INT96", "FLOAT") +} + +func convertInt96ToDouble(v Value) (Value, error) { + return v, invalidConversion(v, "INT96", "DOUBLE") +} + +func convertInt96ToByteArray(v Value) (Value, error) { + return v.convertToByteArray(v.byteArray()), nil +} + +func convertInt96ToFixedLenByteArray(v Value, size int) (Value, error) { + b := v.byteArray() + if len(b) < size { + c := make([]byte, size) + copy(c, b) + b = c + } else { + b = b[:size] + } + return v.convertToFixedLenByteArray(b), nil +} + +func convertInt96ToString(v Value) (Value, error) { + return v.convertToByteArray([]byte(v.String())), nil +} + +func convertFloatToBoolean(v Value) (Value, error) { + return v.convertToBoolean(v.float() != 0), nil +} + +func convertFloatToInt32(v Value) (Value, error) { + return v.convertToInt32(int32(v.float())), nil +} + +func convertFloatToInt64(v Value) (Value, error) { + return v.convertToInt64(int64(v.float())), nil +} + +func convertFloatToInt96(v Value) (Value, error) { + return v, invalidConversion(v, "FLOAT", "INT96") +} + +func convertFloatToDouble(v Value) (Value, error) { + return v.convertToDouble(float64(v.float())), nil +} + +func convertFloatToByteArray(v Value) (Value, error) { + b := make([]byte, 4) + binary.LittleEndian.PutUint32(b, v.uint32()) + return v.convertToByteArray(b), nil +} + +func convertFloatToFixedLenByteArray(v Value, size int) (Value, error) { + b := make([]byte, 4) + c := make([]byte, size) + binary.LittleEndian.PutUint32(b, v.uint32()) + copy(c, b) + return v.convertToFixedLenByteArray(c), nil +} + +func convertFloatToString(v Value) (Value, error) { + return v.convertToByteArray(strconv.AppendFloat(nil, float64(v.float()), 'g', -1, 32)), nil +} + +func convertDoubleToBoolean(v Value) (Value, error) { + return v.convertToBoolean(v.double() != 0), nil +} + +func convertDoubleToInt32(v Value) (Value, error) { + return v.convertToInt32(int32(v.double())), nil +} + +func convertDoubleToInt64(v Value) (Value, error) { + return v.convertToInt64(int64(v.double())), nil +} + +func convertDoubleToInt96(v Value) (Value, error) { + return v, invalidConversion(v, "FLOAT", "INT96") +} + +func convertDoubleToFloat(v Value) (Value, error) { + return v.convertToFloat(float32(v.double())), nil +} + +func convertDoubleToByteArray(v Value) (Value, error) { + b := make([]byte, 8) + binary.LittleEndian.PutUint64(b, v.uint64()) + return v.convertToByteArray(b), nil +} + +func convertDoubleToFixedLenByteArray(v Value, size int) (Value, error) { + b := make([]byte, 8) + c := make([]byte, size) + binary.LittleEndian.PutUint64(b, v.uint64()) + copy(c, b) + return v.convertToFixedLenByteArray(c), nil +} + +func convertDoubleToString(v Value) (Value, error) { + return v.convertToByteArray(strconv.AppendFloat(nil, v.double(), 'g', -1, 64)), nil +} + +func convertByteArrayToBoolean(v Value) (Value, error) { + return v.convertToBoolean(!isZero(v.byteArray())), nil +} + +func convertByteArrayToInt32(v Value) (Value, error) { + b := make([]byte, 4) + copy(b, v.byteArray()) + return v.convertToInt32(int32(binary.LittleEndian.Uint32(b))), nil +} + +func convertByteArrayToInt64(v Value) (Value, error) { + b := make([]byte, 8) + copy(b, v.byteArray()) + return v.convertToInt64(int64(binary.LittleEndian.Uint64(b))), nil +} + +func convertByteArrayToInt96(v Value) (Value, error) { + b := make([]byte, 12) + copy(b, v.byteArray()) + return v.convertToInt96(deprecated.Int96{ + 0: binary.LittleEndian.Uint32(b[0:4]), + 1: binary.LittleEndian.Uint32(b[4:8]), + 2: binary.LittleEndian.Uint32(b[8:12]), + }), nil +} + +func convertByteArrayToFloat(v Value) (Value, error) { + b := make([]byte, 4) + copy(b, v.byteArray()) + return v.convertToFloat(math.Float32frombits(binary.LittleEndian.Uint32(b))), nil +} + +func convertByteArrayToDouble(v Value) (Value, error) { + b := make([]byte, 8) + copy(b, v.byteArray()) + return v.convertToDouble(math.Float64frombits(binary.LittleEndian.Uint64(b))), nil +} + +func convertByteArrayToFixedLenByteArray(v Value, size int) (Value, error) { + b := v.byteArray() + if len(b) < size { + c := make([]byte, size) + copy(c, b) + b = c + } else { + b = b[:size] + } + return v.convertToFixedLenByteArray(b), nil +} + +func convertFixedLenByteArrayToString(v Value) (Value, error) { + b := v.byteArray() + c := make([]byte, hex.EncodedLen(len(b))) + hex.Encode(c, b) + return v.convertToByteArray(c), nil +} + +func convertStringToBoolean(v Value) (Value, error) { + b, err := strconv.ParseBool(v.string()) + if err != nil { + return v, conversionError(v, "STRING", "BOOLEAN", err) + } + return v.convertToBoolean(b), nil +} + +func convertStringToInt32(v Value) (Value, error) { + i, err := strconv.ParseInt(v.string(), 10, 32) + if err != nil { + return v, conversionError(v, "STRING", "INT32", err) + } + return v.convertToInt32(int32(i)), nil +} + +func convertStringToInt64(v Value) (Value, error) { + i, err := strconv.ParseInt(v.string(), 10, 64) + if err != nil { + return v, conversionError(v, "STRING", "INT64", err) + } + return v.convertToInt64(i), nil +} + +func convertStringToInt96(v Value) (Value, error) { + i, ok := new(big.Int).SetString(v.string(), 10) + if !ok { + return v, conversionError(v, "STRING", "INT96", strconv.ErrSyntax) + } + b := i.Bytes() + c := make([]byte, 12) + copy(c, b) + if cpu.IsBigEndian { + bufLen := len(c) + for idx := 0; idx < bufLen; idx = idx + 4 { + for m, n := (idx + 0), (idx + 3); m < n; m, n = m+1, n-1 { + c[m], c[n] = c[n], c[m] + } + } + } + i96 := unsafecast.Slice[deprecated.Int96](c) + return v.convertToInt96(i96[0]), nil +} + +func convertStringToFloat(v Value) (Value, error) { + f, err := strconv.ParseFloat(v.string(), 32) + if err != nil { + return v, conversionError(v, "STRING", "FLOAT", err) + } + return v.convertToFloat(float32(f)), nil +} + +func convertStringToDouble(v Value) (Value, error) { + f, err := strconv.ParseFloat(v.string(), 64) + if err != nil { + return v, conversionError(v, "STRING", "DOUBLE", err) + } + return v.convertToDouble(f), nil +} + +func convertStringToFixedLenByteArray(v Value, size int) (Value, error) { + b := v.byteArray() + c := make([]byte, size) + _, err := hex.Decode(c, b) + if err != nil { + return v, conversionError(v, "STRING", "BYTE_ARRAY", err) + } + return v.convertToFixedLenByteArray(c), nil +} + +func convertStringToDate(v Value, tz *time.Location) (Value, error) { + t, err := time.ParseInLocation("2006-01-02", v.string(), tz) + if err != nil { + return v, conversionError(v, "STRING", "DATE", err) + } + d := daysSinceUnixEpoch(t) + return v.convertToInt32(int32(d)), nil +} + +func convertStringToTimeMillis(v Value, tz *time.Location) (Value, error) { + t, err := time.ParseInLocation("15:04:05.999", v.string(), tz) + if err != nil { + return v, conversionError(v, "STRING", "TIME", err) + } + m := nearestMidnightLessThan(t) + milliseconds := t.Sub(m).Milliseconds() + return v.convertToInt32(int32(milliseconds)), nil +} + +func convertStringToTimeMicros(v Value, tz *time.Location) (Value, error) { + t, err := time.ParseInLocation("15:04:05.999999", v.string(), tz) + if err != nil { + return v, conversionError(v, "STRING", "TIME", err) + } + m := nearestMidnightLessThan(t) + microseconds := t.Sub(m).Microseconds() + return v.convertToInt64(microseconds), nil +} + +func convertDateToTimestamp(v Value, u format.TimeUnit, tz *time.Location) (Value, error) { + t := unixEpoch.AddDate(0, 0, int(v.int32())) + d := timeUnitDuration(u) + return v.convertToInt64(int64(t.In(tz).Sub(unixEpoch) / d)), nil +} + +func convertDateToString(v Value) (Value, error) { + t := unixEpoch.AddDate(0, 0, int(v.int32())) + b := t.AppendFormat(make([]byte, 0, 10), "2006-01-02") + return v.convertToByteArray(b), nil +} + +func convertTimeMillisToString(v Value, tz *time.Location) (Value, error) { + t := time.UnixMilli(int64(v.int32())).In(tz) + b := t.AppendFormat(make([]byte, 0, 12), "15:04:05.999") + return v.convertToByteArray(b), nil +} + +func convertTimeMicrosToString(v Value, tz *time.Location) (Value, error) { + t := time.UnixMicro(v.int64()).In(tz) + b := t.AppendFormat(make([]byte, 0, 15), "15:04:05.999999") + return v.convertToByteArray(b), nil +} + +func convertTimestampToDate(v Value, u format.TimeUnit, tz *time.Location) (Value, error) { + t := timestamp(v, u, tz) + d := daysSinceUnixEpoch(t) + return v.convertToInt32(int32(d)), nil +} + +func convertTimestampToTimeMillis(v Value, u format.TimeUnit, sourceZone, targetZone *time.Location) (Value, error) { + t := timestamp(v, u, sourceZone) + m := nearestMidnightLessThan(t) + milliseconds := t.In(targetZone).Sub(m).Milliseconds() + return v.convertToInt32(int32(milliseconds)), nil +} + +func convertTimestampToTimeMicros(v Value, u format.TimeUnit, sourceZone, targetZone *time.Location) (Value, error) { + t := timestamp(v, u, sourceZone) + m := nearestMidnightLessThan(t) + microseconds := t.In(targetZone).Sub(m).Microseconds() + return v.convertToInt64(int64(microseconds)), nil +} + +func convertTimestampToTimestamp(v Value, sourceUnit, targetUnit format.TimeUnit) (Value, error) { + sourceScale := timeUnitDuration(sourceUnit).Nanoseconds() + targetScale := timeUnitDuration(targetUnit).Nanoseconds() + targetValue := (v.int64() * sourceScale) / targetScale + return v.convertToInt64(targetValue), nil +} + +const nanosecondsPerDay = 24 * 60 * 60 * 1e9 + +func daysSinceUnixEpoch(t time.Time) int { + return int(t.Sub(unixEpoch).Hours()) / 24 +} + +func nearestMidnightLessThan(t time.Time) time.Time { + y, m, d := t.Date() + return time.Date(y, m, d, 0, 0, 0, 0, t.Location()) +} + +func timestamp(v Value, u format.TimeUnit, tz *time.Location) time.Time { + return unixEpoch.In(tz).Add(time.Duration(v.int64()) * timeUnitDuration(u)) +} + +func timeUnitDuration(unit format.TimeUnit) time.Duration { + switch { + case unit.Millis != nil: + return time.Millisecond + case unit.Micros != nil: + return time.Microsecond + default: + return time.Nanosecond + } +} + +func invalidConversion(value Value, from, to string) error { + return fmt.Errorf("%s to %s: %s: %w", from, to, value, ErrInvalidConversion) +} + +func conversionError(value Value, from, to string, err error) error { + return fmt.Errorf("%s to %s: %q: %s: %w", from, to, value.string(), err, ErrInvalidConversion) +} diff --git a/vendor/github.com/parquet-go/parquet-go/dedupe.go b/vendor/github.com/parquet-go/parquet-go/dedupe.go new file mode 100644 index 000000000..0f4343967 --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/dedupe.go @@ -0,0 +1,107 @@ +package parquet + +// DedupeRowReader constructs a row reader which drops duplicated consecutive +// rows, according to the comparator function passed as argument. +// +// If the underlying reader produces a sequence of rows sorted by the same +// comparison predicate, the output is guaranteed to produce unique rows only. +func DedupeRowReader(reader RowReader, compare func(Row, Row) int) RowReader { + return &dedupeRowReader{reader: reader, compare: compare} +} + +type dedupeRowReader struct { + reader RowReader + compare func(Row, Row) int + dedupe +} + +func (d *dedupeRowReader) ReadRows(rows []Row) (int, error) { + for { + n, err := d.reader.ReadRows(rows) + n = d.deduplicate(rows[:n], d.compare) + + if n > 0 || err != nil { + return n, err + } + } +} + +// DedupeRowWriter constructs a row writer which drops duplicated consecutive +// rows, according to the comparator function passed as argument. +// +// If the writer is given a sequence of rows sorted by the same comparison +// predicate, the output is guaranteed to contain unique rows only. +func DedupeRowWriter(writer RowWriter, compare func(Row, Row) int) RowWriter { + return &dedupeRowWriter{writer: writer, compare: compare} +} + +type dedupeRowWriter struct { + writer RowWriter + compare func(Row, Row) int + dedupe + rows []Row +} + +func (d *dedupeRowWriter) WriteRows(rows []Row) (int, error) { + // We need to make a copy because we cannot modify the rows slice received + // as argument to respect the RowWriter contract. + d.rows = append(d.rows[:0], rows...) + defer func() { + for i := range d.rows { + d.rows[i] = Row{} + } + }() + + if n := d.deduplicate(d.rows, d.compare); n > 0 { + w, err := d.writer.WriteRows(d.rows[:n]) + if err != nil { + return w, err + } + } + + // Return the number of rows received instead of the number of deduplicated + // rows actually written to the underlying writer because we have to repsect + // the RowWriter contract. + return len(rows), nil +} + +type dedupe struct { + lastRow Row + uniq []Row + dupe []Row +} + +func (d *dedupe) reset() { + d.lastRow = d.lastRow[:0] +} + +func (d *dedupe) deduplicate(rows []Row, compare func(Row, Row) int) int { + defer func() { + for i := range d.uniq { + d.uniq[i] = Row{} + } + for i := range d.dupe { + d.dupe[i] = Row{} + } + d.uniq = d.uniq[:0] + d.dupe = d.dupe[:0] + }() + + lastRow := d.lastRow + + for _, row := range rows { + if len(lastRow) != 0 && compare(row, lastRow) == 0 { + d.dupe = append(d.dupe, row) + } else { + lastRow = row + d.uniq = append(d.uniq, row) + } + } + + rows = rows[:0] + rows = append(rows, d.uniq...) + rows = append(rows, d.dupe...) + + d.lastRow = append(d.lastRow[:0], lastRow...) + return len(d.uniq) +} diff --git a/vendor/github.com/parquet-go/parquet-go/deprecated/int96.go b/vendor/github.com/parquet-go/parquet-go/deprecated/int96.go new file mode 100644 index 000000000..fc6d40648 --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/deprecated/int96.go @@ -0,0 +1,179 @@ +package deprecated + +import ( + "math/big" + "math/bits" +) + +// Int96 is an implementation of the deprecated INT96 parquet type. +type Int96 [3]uint32 + +// Int32ToInt96 converts a int32 value to a Int96. +func Int32ToInt96(value int32) (i96 Int96) { + if value < 0 { + i96[2] = 0xFFFFFFFF + i96[1] = 0xFFFFFFFF + } + i96[0] = uint32(value) + return +} + +// Int64ToInt96 converts a int64 value to Int96. +func Int64ToInt96(value int64) (i96 Int96) { + if value < 0 { + i96[2] = 0xFFFFFFFF + } + i96[1] = uint32(value >> 32) + i96[0] = uint32(value) + return +} + +// IsZero returns true if i is the zero-value. +func (i Int96) IsZero() bool { return i == Int96{} } + +// Negative returns true if i is a negative value. +func (i Int96) Negative() bool { + return (i[2] >> 31) != 0 +} + +// Less returns true if i < j. +// +// The method implements a signed comparison between the two operands. +func (i Int96) Less(j Int96) bool { + if i.Negative() { + if !j.Negative() { + return true + } + } else { + if j.Negative() { + return false + } + } + for k := 2; k >= 0; k-- { + a, b := i[k], j[k] + switch { + case a < b: + return true + case a > b: + return false + } + } + return false +} + +// Int converts i to a big.Int representation. +func (i Int96) Int() *big.Int { + z := new(big.Int) + z.Or(z, big.NewInt(int64(i[2])<<32|int64(i[1]))) + z.Lsh(z, 32) + z.Or(z, big.NewInt(int64(i[0]))) + return z +} + +// Int32 converts i to a int32, potentially truncating the value. +func (i Int96) Int32() int32 { + return int32(i[0]) +} + +// Int64 converts i to a int64, potentially truncating the value. +func (i Int96) Int64() int64 { + return int64(i[1])<<32 | int64(i[0]) +} + +// String returns a string representation of i. +func (i Int96) String() string { + return i.Int().String() +} + +// Len returns the minimum length in bits required to store the value of i. +func (i Int96) Len() int { + switch { + case i[2] != 0: + return 64 + bits.Len32(i[2]) + case i[1] != 0: + return 32 + bits.Len32(i[1]) + default: + return bits.Len32(i[0]) + } +} + +func MaxLenInt96(data []Int96) int { + max := 0 + for i := range data { + n := data[i].Len() + if n > max { + max = n + } + } + return max +} + +func MinInt96(data []Int96) (min Int96) { + if len(data) > 0 { + min = data[0] + for _, v := range data[1:] { + if v.Less(min) { + min = v + } + } + } + return min +} + +func MaxInt96(data []Int96) (max Int96) { + if len(data) > 0 { + max = data[0] + for _, v := range data[1:] { + if max.Less(v) { + max = v + } + } + } + return max +} + +func MinMaxInt96(data []Int96) (min, max Int96) { + if len(data) > 0 { + min = data[0] + max = data[0] + for _, v := range data[1:] { + if v.Less(min) { + min = v + } + if max.Less(v) { + max = v + } + } + } + return min, max +} + +func OrderOfInt96(data []Int96) int { + if len(data) > 1 { + if int96AreInAscendingOrder(data) { + return +1 + } + if int96AreInDescendingOrder(data) { + return -1 + } + } + return 0 +} + +func int96AreInAscendingOrder(data []Int96) bool { + for i := len(data) - 1; i > 0; i-- { + if data[i].Less(data[i-1]) { + return false + } + } + return true +} + +func int96AreInDescendingOrder(data []Int96) bool { + for i := len(data) - 1; i > 0; i-- { + if data[i-1].Less(data[i]) { + return false + } + } + return true +} diff --git a/vendor/github.com/parquet-go/parquet-go/deprecated/parquet.go b/vendor/github.com/parquet-go/parquet-go/deprecated/parquet.go new file mode 100644 index 000000000..b2c60072c --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/deprecated/parquet.go @@ -0,0 +1,112 @@ +package deprecated + +// DEPRECATED: Common types used by frameworks(e.g. hive, pig) using parquet. +// ConvertedType is superseded by LogicalType. This enum should not be extended. +// +// See LogicalTypes.md for conversion between ConvertedType and LogicalType. +type ConvertedType int32 + +const ( + // a BYTE_ARRAY actually contains UTF8 encoded chars + UTF8 ConvertedType = 0 + + // a map is converted as an optional field containing a repeated key/value pair + Map ConvertedType = 1 + + // a key/value pair is converted into a group of two fields + MapKeyValue ConvertedType = 2 + + // a list is converted into an optional field containing a repeated field for its + // values + List ConvertedType = 3 + + // an enum is converted into a binary field + Enum ConvertedType = 4 + + // A decimal value. + // + // This may be used to annotate binary or fixed primitive types. The + // underlying byte array stores the unscaled value encoded as two's + // complement using big-endian byte order (the most significant byte is the + // zeroth element). The value of the decimal is the value * 10^{-scale}. + // + // This must be accompanied by a (maximum) precision and a scale in the + // SchemaElement. The precision specifies the number of digits in the decimal + // and the scale stores the location of the decimal point. For example 1.23 + // would have precision 3 (3 total digits) and scale 2 (the decimal point is + // 2 digits over). + Decimal ConvertedType = 5 + + // A Date + // + // Stored as days since Unix epoch, encoded as the INT32 physical type. + Date ConvertedType = 6 + + // A time + // + // The total number of milliseconds since midnight. The value is stored + // as an INT32 physical type. + TimeMillis ConvertedType = 7 + + // A time. + // + // The total number of microseconds since midnight. The value is stored as + // an INT64 physical type. + TimeMicros ConvertedType = 8 + + // A date/time combination + // + // Date and time recorded as milliseconds since the Unix epoch. Recorded as + // a physical type of INT64. + TimestampMillis ConvertedType = 9 + + // A date/time combination + // + // Date and time recorded as microseconds since the Unix epoch. The value is + // stored as an INT64 physical type. + TimestampMicros ConvertedType = 10 + + // An unsigned integer value. + // + // The number describes the maximum number of meaningful data bits in + // the stored value. 8, 16 and 32 bit values are stored using the + // INT32 physical type. 64 bit values are stored using the INT64 + // physical type. + Uint8 ConvertedType = 11 + Uint16 ConvertedType = 12 + Uint32 ConvertedType = 13 + Uint64 ConvertedType = 14 + + // A signed integer value. + // + // The number describes the maximum number of meaningful data bits in + // the stored value. 8, 16 and 32 bit values are stored using the + // INT32 physical type. 64 bit values are stored using the INT64 + // physical type. + Int8 ConvertedType = 15 + Int16 ConvertedType = 16 + Int32 ConvertedType = 17 + Int64 ConvertedType = 18 + + // An embedded JSON document + // + // A JSON document embedded within a single UTF8 column. + Json ConvertedType = 19 + + // An embedded BSON document + // + // A BSON document embedded within a single BINARY column. + Bson ConvertedType = 20 + + // An interval of time + // + // This type annotates data stored as a FIXED_LEN_BYTE_ARRAY of length 12 + // This data is composed of three separate little endian unsigned + // integers. Each stores a component of a duration of time. The first + // integer identifies the number of months associated with the duration, + // the second identifies the number of days associated with the duration + // and the third identifies the number of milliseconds associated with + // the provided duration. This duration of time is independent of any + // particular timezone or date. + Interval ConvertedType = 21 +) diff --git a/vendor/github.com/parquet-go/parquet-go/dictionary.go b/vendor/github.com/parquet-go/parquet-go/dictionary.go new file mode 100644 index 000000000..b3addbddc --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/dictionary.go @@ -0,0 +1,427 @@ +package parquet + +import ( + "io" + + "slices" + + "github.com/parquet-go/parquet-go/deprecated" + "github.com/parquet-go/parquet-go/encoding" + "github.com/parquet-go/parquet-go/sparse" +) + +const ( + // Maximum load of probing tables. This parameter configures the balance + // between memory density and compute time of probing operations. Valid + // values are floating point numbers between 0 and 1. + // + // Smaller values result in lower collision probability when inserting + // values in probing tables, but also increase memory utilization. + // + // TODO: make this configurable by the application? + hashprobeTableMaxLoad = 0.85 + + // An estimate of the CPU cache footprint used by insert operations. + // + // This constant is used to determine a useful chunk size depending on the + // size of values being inserted in dictionaries. More values of small size + // can fit in CPU caches, so the inserts can operate on larger chunks. + insertsTargetCacheFootprint = 8192 +) + +// The Dictionary interface represents type-specific implementations of parquet +// dictionaries. +// +// Programs can instantiate dictionaries by call the NewDictionary method of a +// Type object. +// +// The current implementation has a limitation which prevents applications from +// providing custom versions of this interface because it contains unexported +// methods. The only way to create Dictionary values is to call the +// NewDictionary of Type instances. This limitation may be lifted in future +// releases. +type Dictionary interface { + // Returns the type that the dictionary was created from. + Type() Type + + // Returns the number of value indexed in the dictionary. + Len() int + + // Returns the total size in bytes of all values stored in the dictionary. + // This is used for tracking dictionary memory usage and enforcing size limits. + Size() int64 + + // Returns the dictionary value at the given index. + Index(index int32) Value + + // Inserts values from the second slice to the dictionary and writes the + // indexes at which each value was inserted to the first slice. + // + // The method panics if the length of the indexes slice is smaller than the + // length of the values slice. + Insert(indexes []int32, values []Value) + + // Given an array of dictionary indexes, lookup the values into the array + // of values passed as second argument. + // + // The method panics if len(indexes) > len(values), or one of the indexes + // is negative or greater than the highest index in the dictionary. + Lookup(indexes []int32, values []Value) + + // Returns the min and max values found in the given indexes. + Bounds(indexes []int32) (min, max Value) + + // Resets the dictionary to its initial state, removing all values. + Reset() + + // Returns a Page representing the content of the dictionary. + // + // The returned page shares the underlying memory of the buffer, it remains + // valid to use until the dictionary's Reset method is called. + Page() Page + + // See ColumnBuffer.writeValues for details on the use of unexported methods + // on interfaces. + insert(indexes []int32, rows sparse.Array) + + // Parquet primitive type insert methods. Each dictionary implementation + // supports only the Parquet types it can handle and panics for others. + // Returns the index at which the value was inserted (or already existed). + insertBoolean(value bool) int32 + insertInt32(value int32) int32 + insertInt64(value int64) int32 + insertInt96(value deprecated.Int96) int32 + insertFloat(value float32) int32 + insertDouble(value float64) int32 + insertByteArray(value []byte) int32 + + //lookup(indexes []int32, rows sparse.Array) +} + +func checkLookupIndexBounds(indexes []int32, rows sparse.Array) { + if rows.Len() < len(indexes) { + panic("dictionary lookup with more indexes than values") + } +} + +// indexedType is a wrapper around a Type value which overrides object +// constructors to use indexed versions referencing values in the dictionary +// instead of storing plain values. +type indexedType struct { + Type + dict Dictionary +} + +func newIndexedType(typ Type, dict Dictionary) *indexedType { + return &indexedType{Type: typ, dict: dict} +} + +func (t *indexedType) NewColumnBuffer(columnIndex, numValues int) ColumnBuffer { + return newIndexedColumnBuffer(t, makeColumnIndex(columnIndex), makeNumValues(numValues)) +} + +func (t *indexedType) NewPage(columnIndex, numValues int, data encoding.Values) Page { + return newIndexedPage(t, makeColumnIndex(columnIndex), makeNumValues(numValues), data) +} + +// indexedPage is an implementation of the Page interface which stores +// indexes instead of plain value. The indexes reference the values in a +// dictionary that the page was created for. +type indexedPage struct { + typ *indexedType + values []int32 + columnIndex uint16 +} + +func newIndexedPage(typ *indexedType, columnIndex uint16, numValues int32, data encoding.Values) *indexedPage { + // RLE encoded values that contain dictionary indexes in data pages are + // sometimes truncated when they contain only zeros. We account for this + // special case here and extend the values buffer if it is shorter than + // needed to hold `numValues`. + size := int(numValues) + values := data.Int32() + + if len(values) < size { + if cap(values) < size { + tmp := make([]int32, size) + copy(tmp, values) + values = tmp + } else { + clear := values[len(values):size] + for i := range clear { + clear[i] = 0 + } + } + } + + return &indexedPage{ + typ: typ, + values: values[:size], + columnIndex: ^columnIndex, + } +} + +func (page *indexedPage) Type() Type { return indexedPageType{page.typ} } + +func (page *indexedPage) Column() int { return int(^page.columnIndex) } + +func (page *indexedPage) Dictionary() Dictionary { return page.typ.dict } + +func (page *indexedPage) NumRows() int64 { return int64(len(page.values)) } + +func (page *indexedPage) NumValues() int64 { return int64(len(page.values)) } + +func (page *indexedPage) NumNulls() int64 { return 0 } + +func (page *indexedPage) Size() int64 { return 4 * int64(len(page.values)) } + +func (page *indexedPage) RepetitionLevels() []byte { return nil } + +func (page *indexedPage) DefinitionLevels() []byte { return nil } + +func (page *indexedPage) Data() encoding.Values { return encoding.Int32Values(page.values) } + +func (page *indexedPage) Values() ValueReader { return &indexedPageValues{page: page} } + +func (page *indexedPage) Bounds() (min, max Value, ok bool) { + if ok = len(page.values) > 0; ok { + min, max = page.typ.dict.Bounds(page.values) + min.columnIndex = page.columnIndex + max.columnIndex = page.columnIndex + } + return min, max, ok +} + +func (page *indexedPage) Slice(i, j int64) Page { + return &indexedPage{ + typ: page.typ, + values: page.values[i:j], + columnIndex: page.columnIndex, + } +} + +// indexedPageType is an adapter for the indexedType returned when accessing +// the type of an indexedPage value. It overrides the Encode/Decode methods to +// account for the fact that an indexed page is holding indexes of values into +// its dictionary instead of plain values. +type indexedPageType struct{ *indexedType } + +func (t indexedPageType) NewValues(values []byte, _ []uint32) encoding.Values { + return encoding.Int32ValuesFromBytes(values) +} + +func (t indexedPageType) Encode(dst []byte, src encoding.Values, enc encoding.Encoding) ([]byte, error) { + return encoding.EncodeInt32(dst, src, enc) +} + +func (t indexedPageType) Decode(dst encoding.Values, src []byte, enc encoding.Encoding) (encoding.Values, error) { + return encoding.DecodeInt32(dst, src, enc) +} + +func (t indexedPageType) EstimateDecodeSize(numValues int, src []byte, enc encoding.Encoding) int { + return Int32Type.EstimateDecodeSize(numValues, src, enc) +} + +type indexedPageValues struct { + page *indexedPage + offset int +} + +func (r *indexedPageValues) ReadValues(values []Value) (n int, err error) { + if n = len(r.page.values) - r.offset; n == 0 { + return 0, io.EOF + } + if n > len(values) { + n = len(values) + } + r.page.typ.dict.Lookup(r.page.values[r.offset:r.offset+n], values[:n]) + r.offset += n + if r.offset == len(r.page.values) { + err = io.EOF + } + return n, err +} + +// indexedColumnBuffer is an implementation of the ColumnBuffer interface which +// builds a page of indexes into a parent dictionary when values are written. +type indexedColumnBuffer struct{ indexedPage } + +func newIndexedColumnBuffer(typ *indexedType, columnIndex uint16, numValues int32) *indexedColumnBuffer { + return &indexedColumnBuffer{ + indexedPage: indexedPage{ + typ: typ, + values: make([]int32, 0, numValues), + columnIndex: ^columnIndex, + }, + } +} + +func (col *indexedColumnBuffer) Clone() ColumnBuffer { + return &indexedColumnBuffer{ + indexedPage: indexedPage{ + typ: col.typ, + values: slices.Clone(col.values), + columnIndex: col.columnIndex, + }, + } +} + +func (col *indexedColumnBuffer) Type() Type { return col.typ.Type } + +func (col *indexedColumnBuffer) ColumnIndex() (ColumnIndex, error) { + return indexedColumnIndex{col}, nil +} + +func (col *indexedColumnBuffer) OffsetIndex() (OffsetIndex, error) { + return indexedOffsetIndex{col}, nil +} + +func (col *indexedColumnBuffer) BloomFilter() BloomFilter { return nil } + +func (col *indexedColumnBuffer) Dictionary() Dictionary { return col.typ.dict } + +func (col *indexedColumnBuffer) Pages() Pages { return onePage(col.Page()) } + +func (col *indexedColumnBuffer) Page() Page { return &col.indexedPage } + +func (col *indexedColumnBuffer) Reset() { col.values = col.values[:0] } + +func (col *indexedColumnBuffer) Cap() int { return cap(col.values) } + +func (col *indexedColumnBuffer) Len() int { return len(col.values) } + +func (col *indexedColumnBuffer) Less(i, j int) bool { + u := col.typ.dict.Index(col.values[i]) + v := col.typ.dict.Index(col.values[j]) + return col.typ.Compare(u, v) < 0 +} + +func (col *indexedColumnBuffer) Swap(i, j int) { + col.values[i], col.values[j] = col.values[j], col.values[i] +} + +func (col *indexedColumnBuffer) WriteValues(values []Value) (int, error) { + i := len(col.values) + j := len(col.values) + len(values) + + if j <= cap(col.values) { + col.values = col.values[:j] + } else { + tmp := make([]int32, j, 2*j) + copy(tmp, col.values) + col.values = tmp + } + + col.typ.dict.Insert(col.values[i:], values) + return len(values), nil +} + +func (col *indexedColumnBuffer) writeValues(_ columnLevels, rows sparse.Array) { + i := len(col.values) + j := len(col.values) + rows.Len() + + if j <= cap(col.values) { + col.values = col.values[:j] + } else { + tmp := make([]int32, j, 2*j) + copy(tmp, col.values) + col.values = tmp + } + + col.typ.dict.insert(col.values[i:], rows) +} + +func (col *indexedColumnBuffer) writeBoolean(_ columnLevels, value bool) { + col.values = append(col.values, col.typ.dict.insertBoolean(value)) +} + +func (col *indexedColumnBuffer) writeInt32(_ columnLevels, value int32) { + col.values = append(col.values, col.typ.dict.insertInt32(value)) +} + +func (col *indexedColumnBuffer) writeInt64(_ columnLevels, value int64) { + col.values = append(col.values, col.typ.dict.insertInt64(value)) +} + +func (col *indexedColumnBuffer) writeInt96(_ columnLevels, value deprecated.Int96) { + col.values = append(col.values, col.typ.dict.insertInt96(value)) +} + +func (col *indexedColumnBuffer) writeFloat(_ columnLevels, value float32) { + col.values = append(col.values, col.typ.dict.insertFloat(value)) +} + +func (col *indexedColumnBuffer) writeDouble(_ columnLevels, value float64) { + col.values = append(col.values, col.typ.dict.insertDouble(value)) +} + +func (col *indexedColumnBuffer) writeByteArray(_ columnLevels, value []byte) { + col.values = append(col.values, col.typ.dict.insertByteArray(value)) +} + +func (col *indexedColumnBuffer) writeNull(_ columnLevels) { + panic("cannot write null to indexed column") +} + +func (col *indexedColumnBuffer) ReadValuesAt(values []Value, offset int64) (n int, err error) { + i := int(offset) + switch { + case i < 0: + return 0, errRowIndexOutOfBounds(offset, int64(len(col.values))) + case i >= len(col.values): + return 0, io.EOF + default: + for n < len(values) && i < len(col.values) { + values[n] = col.typ.dict.Index(col.values[i]) + values[n].columnIndex = col.columnIndex + n++ + i++ + } + if n < len(values) { + err = io.EOF + } + return n, err + } +} + +func (col *indexedColumnBuffer) ReadRowAt(row Row, index int64) (Row, error) { + switch { + case index < 0: + return row, errRowIndexOutOfBounds(index, int64(len(col.values))) + case index >= int64(len(col.values)): + return row, io.EOF + default: + v := col.typ.dict.Index(col.values[index]) + v.columnIndex = col.columnIndex + return append(row, v), nil + } +} + +type indexedColumnIndex struct{ col *indexedColumnBuffer } + +func (index indexedColumnIndex) NumPages() int { return 1 } +func (index indexedColumnIndex) NullCount(int) int64 { return 0 } +func (index indexedColumnIndex) NullPage(int) bool { return false } +func (index indexedColumnIndex) MinValue(int) Value { + min, _, _ := index.col.Bounds() + return min +} +func (index indexedColumnIndex) MaxValue(int) Value { + _, max, _ := index.col.Bounds() + return max +} +func (index indexedColumnIndex) IsAscending() bool { + min, max, _ := index.col.Bounds() + return index.col.typ.Compare(min, max) <= 0 +} +func (index indexedColumnIndex) IsDescending() bool { + min, max, _ := index.col.Bounds() + return index.col.typ.Compare(min, max) > 0 +} + +type indexedOffsetIndex struct{ col *indexedColumnBuffer } + +func (index indexedOffsetIndex) NumPages() int { return 1 } +func (index indexedOffsetIndex) Offset(int) int64 { return 0 } +func (index indexedOffsetIndex) CompressedPageSize(int) int64 { return index.col.Size() } +func (index indexedOffsetIndex) FirstRowIndex(int) int64 { return 0 } diff --git a/vendor/github.com/parquet-go/parquet-go/dictionary_amd64.go b/vendor/github.com/parquet-go/parquet-go/dictionary_amd64.go new file mode 100644 index 000000000..56002c7e5 --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/dictionary_amd64.go @@ -0,0 +1,168 @@ +//go:build !purego + +package parquet + +import ( + "unsafe" + + "github.com/parquet-go/bitpack/unsafecast" + "github.com/parquet-go/parquet-go/sparse" +) + +//go:noescape +func dictionaryBoundsInt32(dict []int32, indexes []int32) (min, max int32, err errno) + +//go:noescape +func dictionaryBoundsInt64(dict []int64, indexes []int32) (min, max int64, err errno) + +//go:noescape +func dictionaryBoundsFloat32(dict []float32, indexes []int32) (min, max float32, err errno) + +//go:noescape +func dictionaryBoundsFloat64(dict []float64, indexes []int32) (min, max float64, err errno) + +//go:noescape +func dictionaryBoundsUint32(dict []uint32, indexes []int32) (min, max uint32, err errno) + +//go:noescape +func dictionaryBoundsUint64(dict []uint64, indexes []int32) (min, max uint64, err errno) + +//go:noescape +func dictionaryBoundsBE128(dict [][16]byte, indexes []int32) (min, max *[16]byte, err errno) + +//go:noescape +func dictionaryLookup32(dict []uint32, indexes []int32, rows sparse.Array) errno + +//go:noescape +func dictionaryLookup64(dict []uint64, indexes []int32, rows sparse.Array) errno + +//go:noescape +func dictionaryLookupByteArrayString(dict []uint32, page []byte, indexes []int32, rows sparse.Array) errno + +//go:noescape +func dictionaryLookupFixedLenByteArrayString(dict []byte, len int, indexes []int32, rows sparse.Array) errno + +//go:noescape +func dictionaryLookupFixedLenByteArrayPointer(dict []byte, len int, indexes []int32, rows sparse.Array) errno + +func (d *int32Dictionary) lookup(indexes []int32, rows sparse.Array) { + checkLookupIndexBounds(indexes, rows) + dict := unsafecast.Slice[uint32](d.values.Slice()) + dictionaryLookup32(dict, indexes, rows).check() +} + +func (d *int64Dictionary) lookup(indexes []int32, rows sparse.Array) { + checkLookupIndexBounds(indexes, rows) + dict := unsafecast.Slice[uint64](d.values.Slice()) + dictionaryLookup64(dict, indexes, rows).check() +} + +func (d *floatDictionary) lookup(indexes []int32, rows sparse.Array) { + checkLookupIndexBounds(indexes, rows) + dict := unsafecast.Slice[uint32](d.values.Slice()) + dictionaryLookup32(dict, indexes, rows).check() +} + +func (d *doubleDictionary) lookup(indexes []int32, rows sparse.Array) { + checkLookupIndexBounds(indexes, rows) + dict := unsafecast.Slice[uint64](d.values.Slice()) + dictionaryLookup64(dict, indexes, rows).check() +} + +func (d *byteArrayDictionary) lookupString(indexes []int32, rows sparse.Array) { + checkLookupIndexBounds(indexes, rows) + // TODO: this optimization is disabled for now because it appears to race + // with the garbage collector and result in writing pointers to free objects + // to the output. + // + // This command was used to trigger the problem: + // + // GOMAXPROCS=8 go test -run TestIssueSegmentio368 -count 10 + // + // https://github.com/segmentio/parquet-go/issues/368 + // + //dictionaryLookupByteArrayString(d.offsets, d.values, indexes, rows).check() + for i, j := range indexes { + *(*string)(rows.Index(i)) = unsafecast.String(d.index(int(j))) + } +} + +func (d *fixedLenByteArrayDictionary) lookupString(indexes []int32, rows sparse.Array) { + checkLookupIndexBounds(indexes, rows) + //dictionaryLookupFixedLenByteArrayString(d.data, d.size, indexes, rows).check() + for i, j := range indexes { + *(*string)(rows.Index(i)) = unsafecast.String(d.index(j)) + } +} + +func (d *uint32Dictionary) lookup(indexes []int32, rows sparse.Array) { + checkLookupIndexBounds(indexes, rows) + dictionaryLookup32(d.values.Slice(), indexes, rows).check() +} + +func (d *uint64Dictionary) lookup(indexes []int32, rows sparse.Array) { + checkLookupIndexBounds(indexes, rows) + dictionaryLookup64(d.values.Slice(), indexes, rows).check() +} + +func (d *be128Dictionary) lookupString(indexes []int32, rows sparse.Array) { + checkLookupIndexBounds(indexes, rows) + //dict := unsafecast.Slice[byte](d.values) + //dictionaryLookupFixedLenByteArrayString(dict, 16, indexes, rows).check() + s := "0123456789ABCDEF" + for i, j := range indexes { + *(**[16]byte)(unsafe.Pointer(&s)) = d.index(j) + *(*string)(rows.Index(i)) = s + } +} + +func (d *be128Dictionary) lookupPointer(indexes []int32, rows sparse.Array) { + checkLookupIndexBounds(indexes, rows) + //dict := unsafecast.Slice[byte](d.values) + //dictionaryLookupFixedLenByteArrayPointer(dict, 16, indexes, rows).check() + for i, j := range indexes { + *(**[16]byte)(rows.Index(i)) = d.index(j) + } +} + +func (d *int32Dictionary) bounds(indexes []int32) (min, max int32) { + min, max, err := dictionaryBoundsInt32(d.values.Slice(), indexes) + err.check() + return min, max +} + +func (d *int64Dictionary) bounds(indexes []int32) (min, max int64) { + min, max, err := dictionaryBoundsInt64(d.values.Slice(), indexes) + err.check() + return min, max +} + +func (d *floatDictionary) bounds(indexes []int32) (min, max float32) { + min, max, err := dictionaryBoundsFloat32(d.values.Slice(), indexes) + err.check() + return min, max +} + +func (d *doubleDictionary) bounds(indexes []int32) (min, max float64) { + min, max, err := dictionaryBoundsFloat64(d.values.Slice(), indexes) + err.check() + return min, max +} + +func (d *uint32Dictionary) bounds(indexes []int32) (min, max uint32) { + min, max, err := dictionaryBoundsUint32(d.values.Slice(), indexes) + err.check() + return min, max +} + +func (d *uint64Dictionary) bounds(indexes []int32) (min, max uint64) { + min, max, err := dictionaryBoundsUint64(d.values.Slice(), indexes) + err.check() + return min, max +} + +func (d *be128Dictionary) bounds(indexes []int32) (min, max *[16]byte) { + min, max, err := dictionaryBoundsBE128(d.values, indexes) + err.check() + return min, max +} diff --git a/vendor/github.com/parquet-go/parquet-go/dictionary_amd64.s b/vendor/github.com/parquet-go/parquet-go/dictionary_amd64.s new file mode 100644 index 000000000..9372ffbfc --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/dictionary_amd64.s @@ -0,0 +1,941 @@ +//go:build !purego + +#include "textflag.h" + +#define errnoIndexOutOfBounds 1 + +// func dictionaryBoundsInt32(dict []int32, indexes []int32) (min, max int32, err errno) +TEXT ·dictionaryBoundsInt32(SB), NOSPLIT, $0-64 + MOVQ dict_base+0(FP), AX + MOVQ dict_len+8(FP), BX + + MOVQ indexes_base+24(FP), CX + MOVQ indexes_len+32(FP), DX + + XORQ R10, R10 // min + XORQ R11, R11 // max + XORQ R12, R12 // err + XORQ SI, SI + + CMPQ DX, $0 + JE return + + MOVL (CX), DI + CMPL DI, BX + JAE indexOutOfBounds + MOVL (AX)(DI*4), R10 + MOVL R10, R11 + + CMPQ DX, $8 + JB test + + CMPB ·hasAVX512VL(SB), $0 + JE test + + MOVQ DX, DI + SHRQ $3, DI + SHLQ $3, DI + + MOVQ $0xFFFF, R8 + KMOVW R8, K1 + + VPBROADCASTD BX, Y2 // [len(dict)...] + VPBROADCASTD R10, Y3 // [min...] + VMOVDQU32 Y3, Y4 // [max...] +loopAVX512: + VMOVDQU32 (CX)(SI*4), Y0 + VPCMPUD $1, Y2, Y0, K2 + KMOVW K2, R9 + CMPB R9, $0xFF + JNE indexOutOfBounds + VPGATHERDD (AX)(Y0*4), K1, Y1 + VPMINSD Y1, Y3, Y3 + VPMAXSD Y1, Y4, Y4 + KMOVW R8, K1 + ADDQ $8, SI + CMPQ SI, DI + JNE loopAVX512 + + VPERM2I128 $1, Y3, Y3, Y0 + VPERM2I128 $1, Y4, Y4, Y1 + VPMINSD Y0, Y3, Y3 + VPMAXSD Y1, Y4, Y4 + + VPSHUFD $0b1110, Y3, Y0 + VPSHUFD $0b1110, Y4, Y1 + VPMINSD Y0, Y3, Y3 + VPMAXSD Y1, Y4, Y4 + + VPSHUFD $1, Y3, Y0 + VPSHUFD $1, Y4, Y1 + VPMINSD Y0, Y3, Y3 + VPMAXSD Y1, Y4, Y4 + + MOVQ X3, R10 + MOVQ X4, R11 + ANDQ $0xFFFFFFFF, R10 + ANDQ $0xFFFFFFFF, R11 + + VZEROUPPER + JMP test +loop: + MOVL (CX)(SI*4), DI + CMPL DI, BX + JAE indexOutOfBounds + MOVL (AX)(DI*4), DI + CMPL DI, R10 + CMOVLLT DI, R10 + CMPL DI, R11 + CMOVLGT DI, R11 + INCQ SI +test: + CMPQ SI, DX + JNE loop +return: + MOVL R10, min+48(FP) + MOVL R11, max+52(FP) + MOVQ R12, err+56(FP) + RET +indexOutOfBounds: + MOVQ $errnoIndexOutOfBounds, R12 + JMP return + +// func dictionaryBoundsInt64(dict []int64, indexes []int32) (min, max int64, err errno) +TEXT ·dictionaryBoundsInt64(SB), NOSPLIT, $0-72 + MOVQ dict_base+0(FP), AX + MOVQ dict_len+8(FP), BX + + MOVQ indexes_base+24(FP), CX + MOVQ indexes_len+32(FP), DX + + XORQ R10, R10 // min + XORQ R11, R11 // max + XORQ R12, R12 // err + XORQ SI, SI + + CMPQ DX, $0 + JE return + + MOVL (CX), DI + CMPL DI, BX + JAE indexOutOfBounds + MOVQ (AX)(DI*8), R10 + MOVQ R10, R11 + + CMPQ DX, $8 + JB test + + CMPB ·hasAVX512VL(SB), $0 + JE test + + MOVQ DX, DI + SHRQ $3, DI + SHLQ $3, DI + + MOVQ $0xFFFF, R8 + KMOVW R8, K1 + + VPBROADCASTD BX, Y2 // [len(dict)...] + VPBROADCASTQ R10, Z3 // [min...] + VMOVDQU64 Z3, Z4 // [max...] +loopAVX512: + VMOVDQU32 (CX)(SI*4), Y0 + VPCMPUD $1, Y2, Y0, K2 + KMOVW K2, R9 + CMPB R9, $0xFF + JNE indexOutOfBounds + VPGATHERDQ (AX)(Y0*8), K1, Z1 + VPMINSQ Z1, Z3, Z3 + VPMAXSQ Z1, Z4, Z4 + KMOVW R8, K1 + ADDQ $8, SI + CMPQ SI, DI + JNE loopAVX512 + + VPERMQ $0b1110, Z3, Z0 + VPERMQ $0b1110, Z4, Z1 + VPMINSQ Z0, Z3, Z3 + VPMAXSQ Z1, Z4, Z4 + + VPERMQ $1, Z3, Z0 + VPERMQ $1, Z4, Z1 + VPMINSQ Z0, Z3, Z3 + VPMAXSQ Z1, Z4, Z4 + + VSHUFF64X2 $2, Z3, Z3, Z0 + VSHUFF64X2 $2, Z4, Z4, Z1 + VPMINSQ Z0, Z3, Z3 + VPMAXSQ Z1, Z4, Z4 + + MOVQ X3, R10 + MOVQ X4, R11 + + VZEROUPPER + JMP test +loop: + MOVL (CX)(SI*4), DI + CMPL DI, BX + JAE indexOutOfBounds + MOVQ (AX)(DI*8), DI + CMPQ DI, R10 + CMOVQLT DI, R10 + CMPQ DI, R11 + CMOVQGT DI, R11 + INCQ SI +test: + CMPQ SI, DX + JNE loop +return: + MOVQ R10, min+48(FP) + MOVQ R11, max+56(FP) + MOVQ R12, err+64(FP) + RET +indexOutOfBounds: + MOVQ $errnoIndexOutOfBounds, R12 + JMP return + +// func dictionaryBoundsFloat32(dict []float32, indexes []int32) (min, max float32, err errno) +TEXT ·dictionaryBoundsFloat32(SB), NOSPLIT, $0-64 + MOVQ dict_base+0(FP), AX + MOVQ dict_len+8(FP), BX + + MOVQ indexes_base+24(FP), CX + MOVQ indexes_len+32(FP), DX + + PXOR X3, X3 // min + PXOR X4, X4 // max + XORQ R12, R12 // err + XORQ SI, SI + + CMPQ DX, $0 + JE return + + MOVL (CX), DI + CMPL DI, BX + JAE indexOutOfBounds + MOVSS (AX)(DI*4), X3 + MOVAPS X3, X4 + + CMPQ DX, $8 + JB test + + CMPB ·hasAVX512VL(SB), $0 + JE test + + MOVQ DX, DI + SHRQ $3, DI + SHLQ $3, DI + + MOVQ $0xFFFF, R8 + KMOVW R8, K1 + + VPBROADCASTD BX, Y2 // [len(dict)...] + VPBROADCASTD X3, Y3 // [min...] + VMOVDQU32 Y3, Y4 // [max...] +loopAVX512: + VMOVDQU32 (CX)(SI*4), Y0 + VPCMPUD $1, Y2, Y0, K2 + KMOVW K2, R9 + CMPB R9, $0xFF + JNE indexOutOfBounds + VPGATHERDD (AX)(Y0*4), K1, Y1 + VMINPS Y1, Y3, Y3 + VMAXPS Y1, Y4, Y4 + KMOVW R8, K1 + ADDQ $8, SI + CMPQ SI, DI + JNE loopAVX512 + + VPERM2I128 $1, Y3, Y3, Y0 + VPERM2I128 $1, Y4, Y4, Y1 + VMINPS Y0, Y3, Y3 + VMAXPS Y1, Y4, Y4 + + VPSHUFD $0b1110, Y3, Y0 + VPSHUFD $0b1110, Y4, Y1 + VMINPS Y0, Y3, Y3 + VMAXPS Y1, Y4, Y4 + + VPSHUFD $1, Y3, Y0 + VPSHUFD $1, Y4, Y1 + VMINPS Y0, Y3, Y3 + VMAXPS Y1, Y4, Y4 + + VZEROUPPER + JMP test +loop: + MOVL (CX)(SI*4), DI + CMPL DI, BX + JAE indexOutOfBounds + MOVSS (AX)(DI*4), X1 + UCOMISS X3, X1 + JAE skipAssignMin + MOVAPS X1, X3 +skipAssignMin: + UCOMISS X4, X1 + JBE skipAssignMax + MOVAPS X1, X4 +skipAssignMax: + INCQ SI +test: + CMPQ SI, DX + JNE loop +return: + MOVSS X3, min+48(FP) + MOVSS X4, max+52(FP) + MOVQ R12, err+56(FP) + RET +indexOutOfBounds: + MOVQ $errnoIndexOutOfBounds, R12 + JMP return + +// func dictionaryBoundsFloat64(dict []float64, indexes []int32) (min, max float64, err errno) +TEXT ·dictionaryBoundsFloat64(SB), NOSPLIT, $0-72 + MOVQ dict_base+0(FP), AX + MOVQ dict_len+8(FP), BX + + MOVQ indexes_base+24(FP), CX + MOVQ indexes_len+32(FP), DX + + PXOR X3, X3 // min + PXOR X4, X4 // max + XORQ R12, R12 // err + XORQ SI, SI + + CMPQ DX, $0 + JE return + + MOVL (CX), DI + CMPL DI, BX + JAE indexOutOfBounds + MOVSD (AX)(DI*8), X3 + MOVAPS X3, X4 + + CMPQ DX, $8 + JB test + + CMPB ·hasAVX512VL(SB), $0 + JE test + + MOVQ DX, DI + SHRQ $3, DI + SHLQ $3, DI + + MOVQ $0xFFFF, R8 + KMOVW R8, K1 + + VPBROADCASTD BX, Y2 // [len(dict)...] + VPBROADCASTQ X3, Z3 // [min...] + VMOVDQU64 Z3, Z4 // [max...] +loopAVX512: + VMOVDQU32 (CX)(SI*4), Y0 + VPCMPUD $1, Y2, Y0, K2 + KMOVW K2, R9 + CMPB R9, $0xFF + JNE indexOutOfBounds + VPGATHERDQ (AX)(Y0*8), K1, Z1 + VMINPD Z1, Z3, Z3 + VMAXPD Z1, Z4, Z4 + KMOVW R8, K1 + ADDQ $8, SI + CMPQ SI, DI + JNE loopAVX512 + + VPERMQ $0b1110, Z3, Z0 + VPERMQ $0b1110, Z4, Z1 + VMINPD Z0, Z3, Z3 + VMAXPD Z1, Z4, Z4 + + VPERMQ $1, Z3, Z0 + VPERMQ $1, Z4, Z1 + VMINPD Z0, Z3, Z3 + VMAXPD Z1, Z4, Z4 + + VSHUFF64X2 $2, Z3, Z3, Z0 + VSHUFF64X2 $2, Z4, Z4, Z1 + VMINPD Z0, Z3, Z3 + VMAXPD Z1, Z4, Z4 + + VZEROUPPER + JMP test +loop: + MOVL (CX)(SI*4), DI + CMPL DI, BX + JAE indexOutOfBounds + MOVSD (AX)(DI*8), X1 + UCOMISD X3, X1 + JAE skipAssignMin + MOVAPD X1, X3 +skipAssignMin: + UCOMISD X4, X1 + JBE skipAssignMax + MOVAPD X1, X4 +skipAssignMax: + INCQ SI +test: + CMPQ SI, DX + JNE loop +return: + MOVSD X3, min+48(FP) + MOVSD X4, max+56(FP) + MOVQ R12, err+64(FP) + RET +indexOutOfBounds: + MOVQ $errnoIndexOutOfBounds, R12 + JMP return + +// func dictionaryBoundsUint32(dict []uint32, indexes []int32) (min, max uint32, err errno) +TEXT ·dictionaryBoundsUint32(SB), NOSPLIT, $0-64 + MOVQ dict_base+0(FP), AX + MOVQ dict_len+8(FP), BX + + MOVQ indexes_base+24(FP), CX + MOVQ indexes_len+32(FP), DX + + XORQ R10, R10 // min + XORQ R11, R11 // max + XORQ R12, R12 // err + XORQ SI, SI + + CMPQ DX, $0 + JE return + + MOVL (CX), DI + CMPL DI, BX + JAE indexOutOfBounds + MOVL (AX)(DI*4), R10 + MOVL R10, R11 + + CMPQ DX, $8 + JB test + + CMPB ·hasAVX512VL(SB), $0 + JE test + + MOVQ DX, DI + SHRQ $3, DI + SHLQ $3, DI + + MOVQ $0xFFFF, R8 + KMOVW R8, K1 + + VPBROADCASTD BX, Y2 // [len(dict)...] + VPBROADCASTD R10, Y3 // [min...] + VMOVDQU32 Y3, Y4 // [max...] +loopAVX512: + VMOVDQU32 (CX)(SI*4), Y0 + VPCMPUD $1, Y2, Y0, K2 + KMOVW K2, R9 + CMPB R9, $0xFF + JNE indexOutOfBounds + VPGATHERDD (AX)(Y0*4), K1, Y1 + VPMINUD Y1, Y3, Y3 + VPMAXUD Y1, Y4, Y4 + KMOVW R8, K1 + ADDQ $8, SI + CMPQ SI, DI + JNE loopAVX512 + + VPERM2I128 $1, Y3, Y3, Y0 + VPERM2I128 $1, Y4, Y4, Y1 + VPMINUD Y0, Y3, Y3 + VPMAXUD Y1, Y4, Y4 + + VPSHUFD $0b1110, Y3, Y0 + VPSHUFD $0b1110, Y4, Y1 + VPMINUD Y0, Y3, Y3 + VPMAXUD Y1, Y4, Y4 + + VPSHUFD $1, Y3, Y0 + VPSHUFD $1, Y4, Y1 + VPMINUD Y0, Y3, Y3 + VPMAXUD Y1, Y4, Y4 + + MOVQ X3, R10 + MOVQ X4, R11 + ANDQ $0xFFFFFFFF, R10 + ANDQ $0xFFFFFFFF, R11 + + VZEROUPPER + JMP test +loop: + MOVL (CX)(SI*4), DI + CMPL DI, BX + JAE indexOutOfBounds + MOVL (AX)(DI*4), DI + CMPL DI, R10 + CMOVLCS DI, R10 + CMPL DI, R11 + CMOVLHI DI, R11 + INCQ SI +test: + CMPQ SI, DX + JNE loop +return: + MOVL R10, min+48(FP) + MOVL R11, max+52(FP) + MOVQ R12, err+56(FP) + RET +indexOutOfBounds: + MOVQ $errnoIndexOutOfBounds, R12 + JMP return + +// func dictionaryBoundsUint64(dict []uint64, indexes []int32) (min, max uint64, err errno) +TEXT ·dictionaryBoundsUint64(SB), NOSPLIT, $0-72 + MOVQ dict_base+0(FP), AX + MOVQ dict_len+8(FP), BX + + MOVQ indexes_base+24(FP), CX + MOVQ indexes_len+32(FP), DX + + XORQ R10, R10 // min + XORQ R11, R11 // max + XORQ R12, R12 // err + XORQ SI, SI + + CMPQ DX, $0 + JE return + + MOVL (CX)(SI*4), DI + CMPL DI, BX + JAE indexOutOfBounds + MOVQ (AX)(DI*8), R10 + MOVQ R10, R11 + + CMPQ DX, $8 + JB test + + CMPB ·hasAVX512VL(SB), $0 + JE test + + MOVQ DX, DI + SHRQ $3, DI + SHLQ $3, DI + + MOVQ $0xFFFF, R8 + KMOVW R8, K1 + + VPBROADCASTD BX, Y2 // [len(dict)...] + VPBROADCASTQ R10, Z3 // [min...] + VMOVDQU64 Z3, Z4 // [max...] +loopAVX512: + VMOVDQU32 (CX)(SI*4), Y0 + VPCMPUD $1, Y2, Y0, K2 + KMOVW K2, R9 + CMPB R9, $0xFF + JNE indexOutOfBounds + VPGATHERDQ (AX)(Y0*8), K1, Z1 + VPMINUQ Z1, Z3, Z3 + VPMAXUQ Z1, Z4, Z4 + KMOVW R8, K1 + ADDQ $8, SI + CMPQ SI, DI + JNE loopAVX512 + + VPERMQ $0b1110, Z3, Z0 + VPERMQ $0b1110, Z4, Z1 + VPMINUQ Z0, Z3, Z3 + VPMAXUQ Z1, Z4, Z4 + + VPERMQ $1, Z3, Z0 + VPERMQ $1, Z4, Z1 + VPMINUQ Z0, Z3, Z3 + VPMAXUQ Z1, Z4, Z4 + + VSHUFF64X2 $2, Z3, Z3, Z0 + VSHUFF64X2 $2, Z4, Z4, Z1 + VPMINUQ Z0, Z3, Z3 + VPMAXUQ Z1, Z4, Z4 + + MOVQ X3, R10 + MOVQ X4, R11 + + VZEROUPPER + JMP test +loop: + MOVL (CX)(SI*4), DI + CMPL DI, BX + JAE indexOutOfBounds + MOVQ (AX)(DI*8), DI + CMPQ DI, R10 + CMOVQCS DI, R10 + CMPQ DI, R11 + CMOVQHI DI, R11 + INCQ SI +test: + CMPQ SI, DX + JNE loop +return: + MOVQ R10, min+48(FP) + MOVQ R11, max+56(FP) + MOVQ R12, err+64(FP) + RET +indexOutOfBounds: + MOVQ $errnoIndexOutOfBounds, R12 + JMP return + +// func dictionaryBoundsBE128(dict [][16]byte, indexes []int32) (min, max *[16]byte, err errno) +TEXT ·dictionaryBoundsBE128(SB), NOSPLIT, $0-72 + MOVQ dict_base+0(FP), AX + MOVQ dict_len+8(FP), BX + + MOVQ indexes_base+24(FP), CX + MOVQ indexes_len+32(FP), DX + SHLQ $2, DX // x 4 + ADDQ CX, DX // end + + XORQ R8, R8 // min (pointer) + XORQ R9, R9 // max (pointer) + XORQ SI, SI // err + XORQ DI, DI + + CMPQ DX, $0 + JE return + + MOVL (CX), DI + CMPL DI, BX + JAE indexOutOfBounds + SHLQ $4, DI // the dictionary contains 16 byte words + LEAQ (AX)(DI*1), R8 + MOVQ R8, R9 + MOVQ 0(AX)(DI*1), R10 // min (high) + MOVQ 8(AX)(DI*1), R11 // min (low) + BSWAPQ R10 + BSWAPQ R11 + MOVQ R10, R12 // max (high) + MOVQ R11, R13 // max (low) + + JMP next +loop: + MOVL (CX), DI + CMPL DI, BX + JAE indexOutOfBounds + SHLQ $4, DI + MOVQ 0(AX)(DI*1), R14 + MOVQ 8(AX)(DI*1), R15 + BSWAPQ R14 + BSWAPQ R15 +testLessThan: + CMPQ R14, R10 + JA testGreaterThan + JB lessThan + CMPQ R15, R11 + JAE testGreaterThan +lessThan: + LEAQ (AX)(DI*1), R8 + MOVQ R14, R10 + MOVQ R15, R11 + JMP next +testGreaterThan: + CMPQ R14, R12 + JB next + JA greaterThan + CMPQ R15, R13 + JBE next +greaterThan: + LEAQ (AX)(DI*1), R9 + MOVQ R14, R12 + MOVQ R15, R13 +next: + ADDQ $4, CX + CMPQ CX, DX + JNE loop +return: + MOVQ R8, min+48(FP) + MOVQ R9, max+56(FP) + MOVQ SI, err+64(FP) + RET +indexOutOfBounds: + MOVQ $errnoIndexOutOfBounds, SI + JMP return + +// The lookup functions provide optimized versions of the dictionary index +// lookup logic. +// +// When AVX512 is available, the AVX512 versions of the functions are used +// which use the VPGATHER* instructions to perform 8 parallel lookups of the +// values in the dictionary, then VPSCATTER* to do 8 parallel writes to the +// sparse output buffer. + +// func dictionaryLookup32(dict []uint32, indexes []int32, rows sparse.Array) errno +TEXT ·dictionaryLookup32(SB), NOSPLIT, $0-80 + MOVQ dict_base+0(FP), AX + MOVQ dict_len+8(FP), BX + + MOVQ indexes_base+24(FP), CX + MOVQ indexes_len+32(FP), DX + + MOVQ rows_array_ptr+48(FP), R8 + MOVQ rows_array_off+64(FP), R9 + + XORQ SI, SI + + CMPQ DX, $8 + JB test + + CMPB ·hasAVX512VL(SB), $0 + JE test + + MOVQ DX, DI + SHRQ $3, DI + SHLQ $3, DI + + MOVQ R9, R10 + SHLQ $3, R10 // 8 * size + + MOVW $0xFFFF, R11 + KMOVW R11, K1 + KMOVW R11, K2 + + VPBROADCASTD R9, Y2 // [size...] + VPMULLD ·range0n8(SB), Y2, Y2 // [0*size,1*size,...] + VPBROADCASTD BX, Y3 // [len(dict)...] +loopAVX512: + VMOVDQU32 (CX)(SI*4), Y0 + VPCMPUD $1, Y3, Y0, K3 + KMOVW K3, R11 + CMPB R11, $0xFF + JNE indexOutOfBounds + VPGATHERDD (AX)(Y0*4), K1, Y1 + VPSCATTERDD Y1, K2, (R8)(Y2*1) + KMOVW R11, K1 + KMOVW R11, K2 + ADDQ R10, R8 + ADDQ $8, SI + CMPQ SI, DI + JNE loopAVX512 + VZEROUPPER + JMP test +loop: + MOVL (CX)(SI*4), DI + CMPL DI, BX + JAE indexOutOfBounds + MOVL (AX)(DI*4), DI + MOVL DI, (R8) + ADDQ R9, R8 + INCQ SI +test: + CMPQ SI, DX + JNE loop + XORQ AX, AX +return: + MOVQ AX, ret+72(FP) + RET +indexOutOfBounds: + MOVQ $errnoIndexOutOfBounds, AX + JMP return + +// func dictionaryLookup64(dict []uint64, indexes []int32, rows sparse.Array) errno +TEXT ·dictionaryLookup64(SB), NOSPLIT, $0-80 + MOVQ dict_base+0(FP), AX + MOVQ dict_len+8(FP), BX + + MOVQ indexes_base+24(FP), CX + MOVQ indexes_len+32(FP), DX + + MOVQ rows_array_ptr+48(FP), R8 + MOVQ rows_array_off+64(FP), R9 + + XORQ SI, SI + + CMPQ DX, $8 + JB test + + CMPB ·hasAVX512VL(SB), $0 + JE test + + MOVQ DX, DI + SHRQ $3, DI + SHLQ $3, DI + + MOVQ R9, R10 + SHLQ $3, R10 // 8 * size + + MOVW $0xFFFF, R11 + KMOVW R11, K1 + KMOVW R11, K2 + + VPBROADCASTD R9, Y2 // [size...] + VPMULLD ·range0n8(SB), Y2, Y2 // [0*size,1*size,...] + VPBROADCASTD BX, Y3 // [len(dict)...] +loopAVX512: + VMOVDQU32 (CX)(SI*4), Y0 + VPCMPUD $1, Y3, Y0, K3 + KMOVW K3, R11 + CMPB R11, $0xFF + JNE indexOutOfBounds + VPGATHERDQ (AX)(Y0*8), K1, Z1 + VPSCATTERDQ Z1, K2, (R8)(Y2*1) + KMOVW R11, K1 + KMOVW R11, K2 + ADDQ R10, R8 + ADDQ $8, SI + CMPQ SI, DI + JNE loopAVX512 + VZEROUPPER + JMP test +loop: + MOVL (CX)(SI*4), DI + CMPL DI, BX + JAE indexOutOfBounds + MOVQ (AX)(DI*8), DI + MOVQ DI, (R8) + ADDQ R9, R8 + INCQ SI +test: + CMPQ SI, DX + JNE loop + XORQ AX, AX +return: + MOVQ AX, ret+72(FP) + RET +indexOutOfBounds: + MOVQ $errnoIndexOutOfBounds, AX + JMP return + +// func dictionaryLookupByteArrayString(dict []uint32, page []byte, indexes []int32, rows sparse.Array) errno +TEXT ·dictionaryLookupByteArrayString(SB), NOSPLIT, $0-104 + MOVQ dict_base+0(FP), AX + MOVQ dict_len+8(FP), BX + DECQ BX // the offsets have the total length as last element + + MOVQ page_base+24(FP), CX + + MOVQ indexes_base+48(FP), R8 + MOVQ indexes_len+56(FP), R9 + + MOVQ rows_array_ptr+72(FP), R10 + MOVQ rows_array_off+88(FP), R11 + + XORQ DI, DI + XORQ SI, SI +loop: + // Load the index that we want to read the value from. This may come from + // user input so we must validate that the indexes are within the bounds of + // the dictionary. + MOVL (R8)(SI*4), DI + CMPL DI, BX + JAE indexOutOfBounds + + // Load the offsets within the dictionary page where the value is stored. + // We trust the offsets to be correct since they are generated internally by + // the dictionary code, there is no need to check that they are within the + // bounds of the dictionary page. + MOVL 0(AX)(DI*4), DX + MOVL 4(AX)(DI*4), DI + + // Compute the length of the value (the difference between two consecutive + // offsets), and the pointer to the first byte of the string value. + SUBL DX, DI + LEAQ (CX)(DX*1), DX + + // Store the length and pointer to the value into the output location. + // The memory layout is expected to hold a pointer and length, which are + // both 64 bits words. This is the layout used by parquet.Value and the Go + // string value type. + MOVQ DX, (R10) + MOVQ DI, 8(R10) + + ADDQ R11, R10 + INCQ SI +test: + CMPQ SI, R9 + JNE loop + XORQ AX, AX +return: + MOVQ AX, ret+96(FP) + RET +indexOutOfBounds: + MOVQ $errnoIndexOutOfBounds, AX + JMP return + +// func dictionaryLookupFixedLenByteArrayString(dict []byte, len int, indexes []int32, rows sparse.Array) errno +TEXT ·dictionaryLookupFixedLenByteArrayString(SB), NOSPLIT, $0-88 + MOVQ dict_base+0(FP), AX + MOVQ dict_len+8(FP), BX + + MOVQ len+24(FP), CX + + MOVQ indexes_base+32(FP), DX + MOVQ indexes_len+40(FP), R8 + + MOVQ rows_array_ptr+56(FP), R9 + MOVQ rows_array_off+72(FP), R10 + + XORQ DI, DI + XORQ SI, SI +loop: + MOVL (DX)(SI*4), DI + IMULQ CX, DI + CMPL DI, BX + JAE indexOutOfBounds + + ADDQ AX, DI + MOVQ DI, (R9) + MOVQ CX, 8(R9) + + ADDQ R10, R9 + INCQ SI +test: + CMPQ SI, R8 + JNE loop + XORQ AX, AX +return: + MOVQ AX, ret+80(FP) + RET +indexOutOfBounds: + MOVQ $errnoIndexOutOfBounds, AX + JMP return + +// This is the same algorithm as dictionaryLookupFixedLenByteArrayString but we +// only store the pointer to the location holding the value instead of storing +// the pair of pointer and length. Since the length is fixed for this dictionary +// type, the application can assume it at the call site. +// +// func dictionaryLookupFixedLenByteArrayPointer(dict []byte, len int, indexes []int32, rows sparse.Array) errno +TEXT ·dictionaryLookupFixedLenByteArrayPointer(SB), NOSPLIT, $0-88 + MOVQ dict_base+0(FP), AX + MOVQ dict_len+8(FP), BX + + MOVQ len+24(FP), CX + + MOVQ indexes_base+32(FP), DX + MOVQ indexes_len+40(FP), R8 + + MOVQ rows_array_ptr+56(FP), R9 + MOVQ rows_array_off+72(FP), R10 + + XORQ DI, DI + XORQ SI, SI +loop: + MOVL (DX)(SI*4), DI + IMULQ CX, DI + CMPL DI, BX + JAE indexOutOfBounds + + ADDQ AX, DI + MOVQ DI, (R9) + + ADDQ R10, R9 + INCQ SI +test: + CMPQ SI, R8 + JNE loop + XORQ AX, AX +return: + MOVQ AX, ret+80(FP) + RET +indexOutOfBounds: + MOVQ $errnoIndexOutOfBounds, AX + JMP return + +GLOBL ·range0n8(SB), RODATA|NOPTR, $40 +DATA ·range0n8+0(SB)/4, $0 +DATA ·range0n8+4(SB)/4, $1 +DATA ·range0n8+8(SB)/4, $2 +DATA ·range0n8+12(SB)/4, $3 +DATA ·range0n8+16(SB)/4, $4 +DATA ·range0n8+20(SB)/4, $5 +DATA ·range0n8+24(SB)/4, $6 +DATA ·range0n8+28(SB)/4, $7 +DATA ·range0n8+32(SB)/4, $8 diff --git a/vendor/github.com/parquet-go/parquet-go/dictionary_be128.go b/vendor/github.com/parquet-go/parquet-go/dictionary_be128.go new file mode 100644 index 000000000..3d7d5bb62 --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/dictionary_be128.go @@ -0,0 +1,184 @@ +package parquet + +import ( + "encoding/binary" + "fmt" + "math" + "unsafe" + + "github.com/parquet-go/parquet-go/deprecated" + "github.com/parquet-go/parquet-go/encoding" + "github.com/parquet-go/parquet-go/hashprobe" + "github.com/parquet-go/parquet-go/sparse" +) + +type be128Dictionary struct { + be128Page + table *hashprobe.Uint128Table +} + +func newBE128Dictionary(typ Type, columnIndex uint16, numValues int32, data encoding.Values) *be128Dictionary { + return &be128Dictionary{ + be128Page: be128Page{ + typ: typ, + values: data.Uint128()[:numValues], + columnIndex: ^columnIndex, + }, + } +} + +func (d *be128Dictionary) Type() Type { return newIndexedType(d.typ, d) } + +func (d *be128Dictionary) Len() int { return len(d.values) } + +func (d *be128Dictionary) Size() int64 { return int64(len(d.values) * 16) } + +func (d *be128Dictionary) Index(i int32) Value { return d.makeValue(d.index(i)) } + +func (d *be128Dictionary) index(i int32) *[16]byte { return &d.values[i] } + +func (d *be128Dictionary) Insert(indexes []int32, values []Value) { + _ = indexes[:len(values)] + + for _, v := range values { + if v.kind != ^int8(FixedLenByteArray) { + panic("values inserted in BE128 dictionary must be of type BYTE_ARRAY") + } + if v.u64 != 16 { + panic("values inserted in BE128 dictionary must be of length 16") + } + } + + if d.table == nil { + d.init(indexes) + } + + const chunkSize = insertsTargetCacheFootprint / 16 + var buffer [chunkSize][16]byte + + for i := 0; i < len(values); i += chunkSize { + j := min(chunkSize+i, len(values)) + n := min(chunkSize, len(values)-i) + + probe := buffer[:n:n] + writePointersBE128(probe, makeArrayValue(values[i:j], unsafe.Offsetof(values[i].ptr))) + + if d.table.Probe(probe, indexes[i:j:j]) > 0 { + for k, v := range probe { + if indexes[i+k] == int32(len(d.values)) { + d.values = append(d.values, v) + } + } + } + } +} + +func (d *be128Dictionary) init(indexes []int32) { + d.table = hashprobe.NewUint128Table(len(d.values), 0.75) + + n := min(len(d.values), len(indexes)) + + for i := 0; i < len(d.values); i += n { + j := min(i+n, len(d.values)) + d.table.Probe(d.values[i:j:j], indexes[:n:n]) + } +} + +func (d *be128Dictionary) insert(indexes []int32, rows sparse.Array) { + const chunkSize = insertsTargetCacheFootprint / 16 + + if d.table == nil { + d.init(indexes) + } + + values := rows.Uint128Array() + + for i := 0; i < values.Len(); i += chunkSize { + j := min(i+chunkSize, values.Len()) + + if d.table.ProbeArray(values.Slice(i, j), indexes[i:j:j]) > 0 { + for k, index := range indexes[i:j] { + if index == int32(len(d.values)) { + d.values = append(d.values, values.Index(i+k)) + } + } + } + } +} + +func (d *be128Dictionary) Lookup(indexes []int32, values []Value) { + model := d.makeValueString("") + memsetValues(values, model) + d.lookupString(indexes, makeArrayValue(values, offsetOfPtr)) +} + +func (d *be128Dictionary) Bounds(indexes []int32) (min, max Value) { + if len(indexes) > 0 { + minValue, maxValue := d.bounds(indexes) + min = d.makeValue(minValue) + max = d.makeValue(maxValue) + } + return min, max +} + +func (d *be128Dictionary) Reset() { + d.values = d.values[:0] + if d.table != nil { + d.table.Reset() + } +} + +func (d *be128Dictionary) Page() Page { + return &d.be128Page +} + +func (d *be128Dictionary) insertBoolean(value bool) int32 { + var buf [16]byte + if value { + buf[15] = 1 + } + return d.insertByteArray(buf[:]) +} + +func (d *be128Dictionary) insertInt32(value int32) int32 { + var buf [16]byte + binary.BigEndian.PutUint32(buf[12:16], uint32(value)) + return d.insertByteArray(buf[:]) +} + +func (d *be128Dictionary) insertInt64(value int64) int32 { + var buf [16]byte + binary.BigEndian.PutUint64(buf[8:16], uint64(value)) + return d.insertByteArray(buf[:]) +} + +func (d *be128Dictionary) insertInt96(value deprecated.Int96) int32 { + var buf [16]byte + binary.BigEndian.PutUint32(buf[4:8], value[2]) + binary.BigEndian.PutUint32(buf[8:12], value[1]) + binary.BigEndian.PutUint32(buf[12:16], value[0]) + return d.insertByteArray(buf[:]) +} + +func (d *be128Dictionary) insertFloat(value float32) int32 { + var buf [16]byte + binary.BigEndian.PutUint32(buf[12:16], math.Float32bits(value)) + return d.insertByteArray(buf[:]) +} + +func (d *be128Dictionary) insertDouble(value float64) int32 { + var buf [16]byte + binary.BigEndian.PutUint64(buf[8:16], math.Float64bits(value)) + return d.insertByteArray(buf[:]) +} + +func (d *be128Dictionary) insertByteArray(value []byte) int32 { + if len(value) != 16 { + panic(fmt.Sprintf("byte array length %d does not match required length 16 for be128", len(value))) + } + + b := ([16]byte)(value) + i := [1]int32{} + d.insert(i[:], makeArrayFromPointer(&b)) + return i[0] +} diff --git a/vendor/github.com/parquet-go/parquet-go/dictionary_boolean.go b/vendor/github.com/parquet-go/parquet-go/dictionary_boolean.go new file mode 100644 index 000000000..a7240d7df --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/dictionary_boolean.go @@ -0,0 +1,181 @@ +package parquet + +import ( + "math/bits" + + "github.com/parquet-go/bitpack" + "github.com/parquet-go/parquet-go/deprecated" + "github.com/parquet-go/parquet-go/encoding" + "github.com/parquet-go/parquet-go/encoding/plain" + "github.com/parquet-go/parquet-go/internal/memory" + "github.com/parquet-go/parquet-go/sparse" +) + +// The boolean dictionary always contains two values for true and false. +type booleanDictionary struct { + booleanPage + // There are only two possible values for booleans, false and true. + // Rather than using a Go map, we track the indexes of each values + // in an array of two 32 bits integers. When inserting values in the + // dictionary, we ensure that an index exist for each boolean value, + // then use the value 0 or 1 (false or true) to perform a lookup in + // the dictionary's map. + table [2]int32 +} + +func newBooleanDictionary(typ Type, columnIndex uint16, numValues int32, data encoding.Values) *booleanDictionary { + indexOfFalse, indexOfTrue, values := int32(-1), int32(-1), data.Boolean() + + for i := int32(0); i < numValues && indexOfFalse < 0 && indexOfTrue < 0; i += 8 { + v := values[i] + if v != 0x00 { + indexOfTrue = i + int32(bits.TrailingZeros8(v)) + } + if v != 0xFF { + indexOfFalse = i + int32(bits.TrailingZeros8(^v)) + } + } + + return &booleanDictionary{ + booleanPage: booleanPage{ + typ: typ, + bits: memory.SliceBufferFrom(values[:bitpack.ByteCount(uint(numValues))]), + numValues: numValues, + columnIndex: ^columnIndex, + }, + table: [2]int32{ + 0: indexOfFalse, + 1: indexOfTrue, + }, + } +} + +func (d *booleanDictionary) Type() Type { return newIndexedType(d.typ, d) } + +func (d *booleanDictionary) Len() int { return int(d.numValues) } + +func (d *booleanDictionary) Size() int64 { return int64(d.bits.Len()) } + +func (d *booleanDictionary) Index(i int32) Value { return d.makeValue(d.index(i)) } + +func (d *booleanDictionary) index(i int32) bool { return d.valueAt(int(i)) } + +func (d *booleanDictionary) Insert(indexes []int32, values []Value) { + d.insert(indexes, makeArrayValue(values, offsetOfBool)) +} + +func (d *booleanDictionary) insert(indexes []int32, rows sparse.Array) { + _ = indexes[:rows.Len()] + + if d.table[0] < 0 { + d.table[0] = d.numValues + d.numValues++ + bits := plain.AppendBoolean(d.bits.Slice(), int(d.table[0]), false) + d.bits = memory.SliceBufferFrom(bits) + } + + if d.table[1] < 0 { + d.table[1] = d.numValues + d.numValues++ + bits := plain.AppendBoolean(d.bits.Slice(), int(d.table[1]), true) + d.bits = memory.SliceBufferFrom(bits) + } + + values := rows.Uint8Array() + dict := d.table + + for i := range rows.Len() { + v := values.Index(i) & 1 + indexes[i] = dict[v] + } +} + +func (d *booleanDictionary) Lookup(indexes []int32, values []Value) { + model := d.makeValue(false) + memsetValues(values, model) + d.lookup(indexes, makeArrayValue(values, offsetOfBool)) +} + +func (d *booleanDictionary) lookup(indexes []int32, rows sparse.Array) { + checkLookupIndexBounds(indexes, rows) + for i, j := range indexes { + *(*bool)(rows.Index(i)) = d.index(j) + } +} + +func (d *booleanDictionary) Bounds(indexes []int32) (min, max Value) { + if len(indexes) > 0 { + hasFalse, hasTrue := false, false + + for _, i := range indexes { + v := d.index(i) + if v { + hasTrue = true + } else { + hasFalse = true + } + if hasTrue && hasFalse { + break + } + } + + min = d.makeValue(!hasFalse) + max = d.makeValue(hasTrue) + } + return min, max +} + +func (d *booleanDictionary) Reset() { + d.bits.Reset() + d.offset = 0 + d.numValues = 0 + d.table = [2]int32{-1, -1} +} + +func (d *booleanDictionary) Page() Page { + return &d.booleanPage +} + +func (d *booleanDictionary) insertBoolean(value bool) int32 { + // Ensure both indexes are initialized + if d.table[0] < 0 { + d.table[0] = d.numValues + d.numValues++ + bits := plain.AppendBoolean(d.bits.Slice(), int(d.table[0]), false) + d.bits = memory.SliceBufferFrom(bits) + } + if d.table[1] < 0 { + d.table[1] = d.numValues + d.numValues++ + bits := plain.AppendBoolean(d.bits.Slice(), int(d.table[1]), true) + d.bits = memory.SliceBufferFrom(bits) + } + if value { + return d.table[1] + } + return d.table[0] +} + +func (d *booleanDictionary) insertInt32(value int32) int32 { + return d.insertBoolean(value != 0) +} + +func (d *booleanDictionary) insertInt64(value int64) int32 { + return d.insertBoolean(value != 0) +} + +func (d *booleanDictionary) insertInt96(value deprecated.Int96) int32 { + return d.insertBoolean(!value.IsZero()) +} + +func (d *booleanDictionary) insertFloat(value float32) int32 { + return d.insertBoolean(value != 0) +} + +func (d *booleanDictionary) insertDouble(value float64) int32 { + return d.insertBoolean(value != 0) +} + +func (d *booleanDictionary) insertByteArray(value []byte) int32 { + return d.insertBoolean(len(value) != 0) +} diff --git a/vendor/github.com/parquet-go/parquet-go/dictionary_byte_array.go b/vendor/github.com/parquet-go/parquet-go/dictionary_byte_array.go new file mode 100644 index 000000000..c12be586d --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/dictionary_byte_array.go @@ -0,0 +1,173 @@ +package parquet + +import ( + "math" + "strconv" + + "github.com/parquet-go/bitpack/unsafecast" + "github.com/parquet-go/parquet-go/deprecated" + "github.com/parquet-go/parquet-go/encoding" + "github.com/parquet-go/parquet-go/internal/memory" + "github.com/parquet-go/parquet-go/sparse" +) + +type byteArrayDictionary struct { + byteArrayPage + table map[string]int32 + alloc allocator +} + +func newByteArrayDictionary(typ Type, columnIndex uint16, numValues int32, data encoding.Values) *byteArrayDictionary { + values, offsets := data.ByteArray() + // The first offset must always be zero, and the last offset is the length + // of the values in bytes. + // + // As an optimization we make the assumption that the backing array of the + // offsets slice belongs to the dictionary. + switch { + case cap(offsets) == 0: + offsets = make([]uint32, 1, 8) + case len(offsets) == 0: + offsets = append(offsets[:0], 0) + } + return &byteArrayDictionary{ + byteArrayPage: byteArrayPage{ + typ: typ, + values: memory.SliceBufferFrom(values), + offsets: memory.SliceBufferFrom(offsets), + columnIndex: ^columnIndex, + }, + } +} + +func (d *byteArrayDictionary) Type() Type { return newIndexedType(d.typ, d) } + +func (d *byteArrayDictionary) Len() int { return d.len() } + +func (d *byteArrayDictionary) Size() int64 { return int64(d.values.Len()) } + +func (d *byteArrayDictionary) Index(i int32) Value { return d.makeValueBytes(d.index(int(i))) } + +func (d *byteArrayDictionary) Insert(indexes []int32, values []Value) { + d.insert(indexes, makeArrayValue(values, offsetOfPtr)) +} + +func (d *byteArrayDictionary) init() { + numValues := d.len() + d.table = make(map[string]int32, numValues) + + for i := range numValues { + d.table[string(d.index(i))] = int32(len(d.table)) + } +} + +func (d *byteArrayDictionary) insert(indexes []int32, rows sparse.Array) { + if d.table == nil { + d.init() + } + + values := rows.StringArray() + + for i := range indexes { + value := values.Index(i) + + index, exists := d.table[value] + if !exists { + // Check if adding this value would cause uint32 overflow in offsets. + // The offsets are stored as uint32, so the total size of all values + // cannot exceed math.MaxUint32 bytes. + newLen := int64(d.values.Len()) + int64(len(value)) + if newLen > math.MaxUint32 { + panic("parquet: byte array dictionary size exceeds maximum (4GB); use DictionaryMaxBytes writer option to limit dictionary size") + } + value = d.alloc.copyString(value) + index = int32(len(d.table)) + d.table[value] = index + d.values.Append([]byte(value)...) + d.offsets.AppendValue(uint32(d.values.Len())) + } + + indexes[i] = index + } +} + +func (d *byteArrayDictionary) Lookup(indexes []int32, values []Value) { + model := d.makeValueString("") + memsetValues(values, model) + d.lookupString(indexes, makeArrayValue(values, offsetOfPtr)) +} + +func (d *byteArrayDictionary) Bounds(indexes []int32) (min, max Value) { + if len(indexes) > 0 { + base := d.index(int(indexes[0])) + minValue := unsafecast.String(base) + maxValue := minValue + values := [64]string{} + + for i := 1; i < len(indexes); i += len(values) { + n := len(indexes) - i + if n > len(values) { + n = len(values) + } + j := i + n + d.lookupString(indexes[i:j:j], makeArrayFromSlice(values[:n:n])) + + for _, value := range values[:n:n] { + switch { + case value < minValue: + minValue = value + case value > maxValue: + maxValue = value + } + } + } + + min = d.makeValueString(minValue) + max = d.makeValueString(maxValue) + } + return min, max +} + +func (d *byteArrayDictionary) Reset() { + d.offsets.Resize(1) + d.values.Resize(0) + for k := range d.table { + delete(d.table, k) + } + d.alloc.reset() +} + +func (d *byteArrayDictionary) Page() Page { + return &d.byteArrayPage +} + +func (d *byteArrayDictionary) insertBoolean(value bool) int32 { + return d.insertByteArray(strconv.AppendBool(make([]byte, 0, 8), value)) +} + +func (d *byteArrayDictionary) insertInt32(value int32) int32 { + return d.insertByteArray(strconv.AppendInt(make([]byte, 0, 16), int64(value), 10)) +} + +func (d *byteArrayDictionary) insertInt64(value int64) int32 { + return d.insertByteArray(strconv.AppendInt(make([]byte, 0, 24), value, 10)) +} + +func (d *byteArrayDictionary) insertInt96(value deprecated.Int96) int32 { + return d.insertByteArray([]byte(value.String())) +} + +func (d *byteArrayDictionary) insertFloat(value float32) int32 { + return d.insertByteArray(strconv.AppendFloat(make([]byte, 0, 24), float64(value), 'g', -1, 32)) +} + +func (d *byteArrayDictionary) insertDouble(value float64) int32 { + return d.insertByteArray(strconv.AppendFloat(make([]byte, 0, 24), value, 'g', -1, 64)) +} + +func (d *byteArrayDictionary) insertByteArray(value []byte) int32 { + s := unsafecast.String(value) + var indexes [1]int32 + d.insert(indexes[:], makeArrayFromPointer(&s)) + return indexes[0] +} diff --git a/vendor/github.com/parquet-go/parquet-go/dictionary_double.go b/vendor/github.com/parquet-go/parquet-go/dictionary_double.go new file mode 100644 index 000000000..4cd815aac --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/dictionary_double.go @@ -0,0 +1,137 @@ +package parquet + +import ( + "strconv" + + "github.com/parquet-go/parquet-go/deprecated" + "github.com/parquet-go/parquet-go/encoding" + "github.com/parquet-go/parquet-go/hashprobe" + "github.com/parquet-go/parquet-go/internal/memory" + "github.com/parquet-go/parquet-go/sparse" +) + +type doubleDictionary struct { + doublePage + table *hashprobe.Float64Table +} + +func newDoubleDictionary(typ Type, columnIndex uint16, numValues int32, data encoding.Values) *doubleDictionary { + return &doubleDictionary{ + doublePage: doublePage{ + typ: typ, + values: memory.SliceBufferFrom(data.Double()[:numValues]), + columnIndex: ^columnIndex, + }, + } +} + +func (d *doubleDictionary) Type() Type { return newIndexedType(d.typ, d) } + +func (d *doubleDictionary) Len() int { return d.values.Len() } + +func (d *doubleDictionary) Size() int64 { return int64(d.values.Len() * 8) } + +func (d *doubleDictionary) Index(i int32) Value { return d.makeValue(d.index(i)) } + +func (d *doubleDictionary) index(i int32) float64 { return d.values.Slice()[i] } + +func (d *doubleDictionary) Insert(indexes []int32, values []Value) { + d.insert(indexes, makeArrayValue(values, offsetOfU64)) +} + +func (d *doubleDictionary) init(indexes []int32) { + values := d.values.Slice() + d.table = hashprobe.NewFloat64Table(len(values), hashprobeTableMaxLoad) + + n := min(len(values), len(indexes)) + + for i := 0; i < len(values); i += n { + j := min(i+n, len(values)) + d.table.Probe(values[i:j:j], indexes[:n:n]) + } +} + +func (d *doubleDictionary) insert(indexes []int32, rows sparse.Array) { + const chunkSize = insertsTargetCacheFootprint / 8 + + if d.table == nil { + d.init(indexes) + } + + values := rows.Float64Array() + + for i := 0; i < values.Len(); i += chunkSize { + j := min(i+chunkSize, values.Len()) + + if d.table.ProbeArray(values.Slice(i, j), indexes[i:j:j]) > 0 { + for k, index := range indexes[i:j] { + if index == int32(d.values.Len()) { + d.values.Append(values.Index(i + k)) + } + } + } + } +} + +func (d *doubleDictionary) Lookup(indexes []int32, values []Value) { + model := d.makeValue(0) + memsetValues(values, model) + d.lookup(indexes, makeArrayValue(values, offsetOfU64)) +} + +func (d *doubleDictionary) Bounds(indexes []int32) (min, max Value) { + if len(indexes) > 0 { + minValue, maxValue := d.bounds(indexes) + min = d.makeValue(minValue) + max = d.makeValue(maxValue) + } + return min, max +} + +func (d *doubleDictionary) Reset() { + d.values.Reset() + if d.table != nil { + d.table.Reset() + } +} + +func (d *doubleDictionary) Page() Page { + return &d.doublePage +} + +func (d *doubleDictionary) insertBoolean(value bool) int32 { + if value { + return d.insertDouble(1) + } + return d.insertDouble(0) +} + +func (d *doubleDictionary) insertInt32(value int32) int32 { + return d.insertDouble(float64(value)) +} + +func (d *doubleDictionary) insertInt64(value int64) int32 { + return d.insertDouble(float64(value)) +} + +func (d *doubleDictionary) insertInt96(value deprecated.Int96) int32 { + return d.insertDouble(float64(value.Int64())) +} + +func (d *doubleDictionary) insertFloat(value float32) int32 { + return d.insertDouble(float64(value)) +} + +func (d *doubleDictionary) insertDouble(value float64) int32 { + var indexes [1]int32 + d.insert(indexes[:], makeArrayFromPointer(&value)) + return indexes[0] +} + +func (d *doubleDictionary) insertByteArray(value []byte) int32 { + v, err := strconv.ParseUint(string(value), 10, 32) + if err != nil { + panic(err) + } + return d.insertDouble(float64(v)) +} diff --git a/vendor/github.com/parquet-go/parquet-go/dictionary_fixed_len_byte_array.go b/vendor/github.com/parquet-go/parquet-go/dictionary_fixed_len_byte_array.go new file mode 100644 index 000000000..6cd71ec87 --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/dictionary_fixed_len_byte_array.go @@ -0,0 +1,198 @@ +package parquet + +import ( + "encoding/binary" + "fmt" + "math" + "unsafe" + + "github.com/parquet-go/bitpack/unsafecast" + "github.com/parquet-go/parquet-go/deprecated" + "github.com/parquet-go/parquet-go/encoding" + "github.com/parquet-go/parquet-go/internal/memory" + "github.com/parquet-go/parquet-go/sparse" +) + +type fixedLenByteArrayDictionary struct { + fixedLenByteArrayPage + hashmap map[string]int32 +} + +func newFixedLenByteArrayDictionary(typ Type, columnIndex uint16, numValues int32, values encoding.Values) *fixedLenByteArrayDictionary { + data, size := values.FixedLenByteArray() + return &fixedLenByteArrayDictionary{ + fixedLenByteArrayPage: fixedLenByteArrayPage{ + typ: typ, + size: size, + data: memory.SliceBufferFrom(data[:int(numValues)*size]), + columnIndex: ^columnIndex, + }, + } +} + +func (d *fixedLenByteArrayDictionary) Type() Type { return newIndexedType(d.typ, d) } + +func (d *fixedLenByteArrayDictionary) Len() int { return d.data.Len() / d.size } + +func (d *fixedLenByteArrayDictionary) Size() int64 { return int64(d.data.Len()) } + +func (d *fixedLenByteArrayDictionary) Index(i int32) Value { + return d.makeValueBytes(d.index(i)) +} + +func (d *fixedLenByteArrayDictionary) index(i int32) []byte { + data := d.data.Slice() + j := (int(i) + 0) * d.size + k := (int(i) + 1) * d.size + return data[j:k:k] +} + +func (d *fixedLenByteArrayDictionary) Insert(indexes []int32, values []Value) { + d.insertValues(indexes, len(values), func(i int) *byte { + return values[i].ptr + }) +} + +func (d *fixedLenByteArrayDictionary) insert(indexes []int32, rows sparse.Array) { + d.insertValues(indexes, rows.Len(), func(i int) *byte { + return (*byte)(rows.Index(i)) + }) +} + +func (d *fixedLenByteArrayDictionary) insertValues(indexes []int32, count int, valueAt func(int) *byte) { + _ = indexes[:count] + + if d.hashmap == nil { + data := d.data.Slice() + d.hashmap = make(map[string]int32, d.data.Cap()/d.size) + for i, j := 0, int32(0); i < len(data); i += d.size { + d.hashmap[string(data[i:i+d.size])] = j + j++ + } + } + + for i := range count { + value := unsafe.Slice(valueAt(i), d.size) + + index, exists := d.hashmap[string(value)] + if !exists { + index = int32(d.Len()) + start := d.data.Len() + d.data.Append(value...) + data := d.data.Slice() + d.hashmap[string(data[start:])] = index + } + + indexes[i] = index + } +} + +func (d *fixedLenByteArrayDictionary) Lookup(indexes []int32, values []Value) { + model := d.makeValueString("") + memsetValues(values, model) + d.lookupString(indexes, makeArrayValue(values, offsetOfPtr)) +} + +func (d *fixedLenByteArrayDictionary) Bounds(indexes []int32) (min, max Value) { + if len(indexes) > 0 { + base := d.index(indexes[0]) + minValue := unsafecast.String(base) + maxValue := minValue + values := [64]string{} + + for i := 1; i < len(indexes); i += len(values) { + n := len(indexes) - i + if n > len(values) { + n = len(values) + } + j := i + n + d.lookupString(indexes[i:j:j], makeArrayFromSlice(values[:n:n])) + + for _, value := range values[:n:n] { + switch { + case value < minValue: + minValue = value + case value > maxValue: + maxValue = value + } + } + } + + min = d.makeValueString(minValue) + max = d.makeValueString(maxValue) + } + return min, max +} + +func (d *fixedLenByteArrayDictionary) Reset() { + d.data.Resize(0) + d.hashmap = nil +} + +func (d *fixedLenByteArrayDictionary) Page() Page { + return &d.fixedLenByteArrayPage +} + +func (d *fixedLenByteArrayDictionary) insertBoolean(value bool) int32 { + buf := make([]byte, d.size) + if value { + buf[d.size-1] = 1 + } + return d.insertByteArray(buf) +} + +func (d *fixedLenByteArrayDictionary) insertInt32(value int32) int32 { + if d.size < 4 { + panic(fmt.Sprintf("cannot write 4-byte int32 to fixed length byte array of size %d", d.size)) + } + buf := make([]byte, d.size) + binary.BigEndian.PutUint32(buf[d.size-4:], uint32(value)) + return d.insertByteArray(buf) +} + +func (d *fixedLenByteArrayDictionary) insertInt64(value int64) int32 { + if d.size < 8 { + panic(fmt.Sprintf("cannot write 8-byte int64 to fixed length byte array of size %d", d.size)) + } + buf := make([]byte, d.size) + binary.BigEndian.PutUint64(buf[d.size-8:], uint64(value)) + return d.insertByteArray(buf) +} + +func (d *fixedLenByteArrayDictionary) insertInt96(value deprecated.Int96) int32 { + if d.size < 12 { + panic(fmt.Sprintf("cannot write 12-byte int96 to fixed length byte array of size %d", d.size)) + } + buf := make([]byte, d.size) + binary.BigEndian.PutUint32(buf[d.size-12:d.size-8], value[2]) + binary.BigEndian.PutUint32(buf[d.size-8:d.size-4], value[1]) + binary.BigEndian.PutUint32(buf[d.size-4:], value[0]) + return d.insertByteArray(buf) +} + +func (d *fixedLenByteArrayDictionary) insertFloat(value float32) int32 { + if d.size < 4 { + panic(fmt.Sprintf("cannot write 4-byte float to fixed length byte array of size %d", d.size)) + } + buf := make([]byte, d.size) + binary.BigEndian.PutUint32(buf[d.size-4:], math.Float32bits(value)) + return d.insertByteArray(buf) +} + +func (d *fixedLenByteArrayDictionary) insertDouble(value float64) int32 { + if d.size < 8 { + panic(fmt.Sprintf("cannot write 8-byte double to fixed length byte array of size %d", d.size)) + } + buf := make([]byte, d.size) + binary.BigEndian.PutUint64(buf[d.size-8:], math.Float64bits(value)) + return d.insertByteArray(buf) +} + +func (d *fixedLenByteArrayDictionary) insertByteArray(value []byte) int32 { + if len(value) != d.size { + panic(fmt.Sprintf("byte array length %d does not match fixed length %d", len(value), d.size)) + } + indexes := [1]int32{0} + d.insertValues(indexes[:], 1, func(i int) *byte { return &value[0] }) + return indexes[0] +} diff --git a/vendor/github.com/parquet-go/parquet-go/dictionary_float.go b/vendor/github.com/parquet-go/parquet-go/dictionary_float.go new file mode 100644 index 000000000..59e2e7c13 --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/dictionary_float.go @@ -0,0 +1,137 @@ +package parquet + +import ( + "strconv" + + "github.com/parquet-go/parquet-go/deprecated" + "github.com/parquet-go/parquet-go/encoding" + "github.com/parquet-go/parquet-go/hashprobe" + "github.com/parquet-go/parquet-go/internal/memory" + "github.com/parquet-go/parquet-go/sparse" +) + +type floatDictionary struct { + floatPage + table *hashprobe.Float32Table +} + +func newFloatDictionary(typ Type, columnIndex uint16, numValues int32, data encoding.Values) *floatDictionary { + return &floatDictionary{ + floatPage: floatPage{ + typ: typ, + values: memory.SliceBufferFrom(data.Float()[:numValues]), + columnIndex: ^columnIndex, + }, + } +} + +func (d *floatDictionary) Type() Type { return newIndexedType(d.typ, d) } + +func (d *floatDictionary) Len() int { return d.values.Len() } + +func (d *floatDictionary) Size() int64 { return int64(d.values.Len() * 4) } + +func (d *floatDictionary) Index(i int32) Value { return d.makeValue(d.index(i)) } + +func (d *floatDictionary) index(i int32) float32 { return d.values.Slice()[i] } + +func (d *floatDictionary) Insert(indexes []int32, values []Value) { + d.insert(indexes, makeArrayValue(values, offsetOfU32)) +} + +func (d *floatDictionary) init(indexes []int32) { + values := d.values.Slice() + d.table = hashprobe.NewFloat32Table(len(values), hashprobeTableMaxLoad) + + n := min(len(values), len(indexes)) + + for i := 0; i < len(values); i += n { + j := min(i+n, len(values)) + d.table.Probe(values[i:j:j], indexes[:n:n]) + } +} + +func (d *floatDictionary) insert(indexes []int32, rows sparse.Array) { + const chunkSize = insertsTargetCacheFootprint / 4 + + if d.table == nil { + d.init(indexes) + } + + values := rows.Float32Array() + + for i := 0; i < values.Len(); i += chunkSize { + j := min(i+chunkSize, values.Len()) + + if d.table.ProbeArray(values.Slice(i, j), indexes[i:j:j]) > 0 { + for k, index := range indexes[i:j] { + if index == int32(d.values.Len()) { + d.values.Append(values.Index(i + k)) + } + } + } + } +} + +func (d *floatDictionary) Lookup(indexes []int32, values []Value) { + model := d.makeValue(0) + memsetValues(values, model) + d.lookup(indexes, makeArrayValue(values, offsetOfU32)) +} + +func (d *floatDictionary) Bounds(indexes []int32) (min, max Value) { + if len(indexes) > 0 { + minValue, maxValue := d.bounds(indexes) + min = d.makeValue(minValue) + max = d.makeValue(maxValue) + } + return min, max +} + +func (d *floatDictionary) Reset() { + d.values.Reset() + if d.table != nil { + d.table.Reset() + } +} + +func (d *floatDictionary) Page() Page { + return &d.floatPage +} + +func (d *floatDictionary) insertBoolean(value bool) int32 { + if value { + return d.insertFloat(1) + } + return d.insertFloat(0) +} + +func (d *floatDictionary) insertInt32(value int32) int32 { + return d.insertFloat(float32(value)) +} + +func (d *floatDictionary) insertInt64(value int64) int32 { + return d.insertFloat(float32(value)) +} + +func (d *floatDictionary) insertInt96(value deprecated.Int96) int32 { + return d.insertFloat(float32(value.Int32())) +} + +func (d *floatDictionary) insertFloat(value float32) int32 { + var indexes [1]int32 + d.insert(indexes[:], makeArrayFromPointer(&value)) + return indexes[0] +} + +func (d *floatDictionary) insertDouble(value float64) int32 { + return d.insertFloat(float32(value)) +} + +func (d *floatDictionary) insertByteArray(value []byte) int32 { + v, err := strconv.ParseUint(string(value), 10, 32) + if err != nil { + panic(err) + } + return d.insertFloat(float32(v)) +} diff --git a/vendor/github.com/parquet-go/parquet-go/dictionary_int32.go b/vendor/github.com/parquet-go/parquet-go/dictionary_int32.go new file mode 100644 index 000000000..96892730c --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/dictionary_int32.go @@ -0,0 +1,150 @@ +package parquet + +import ( + "strconv" + + "github.com/parquet-go/parquet-go/deprecated" + "github.com/parquet-go/parquet-go/encoding" + "github.com/parquet-go/parquet-go/hashprobe" + "github.com/parquet-go/parquet-go/internal/memory" + "github.com/parquet-go/parquet-go/sparse" +) + +type int32Dictionary struct { + int32Page + table *hashprobe.Int32Table +} + +func newInt32Dictionary(typ Type, columnIndex uint16, numValues int32, data encoding.Values) *int32Dictionary { + return &int32Dictionary{ + int32Page: int32Page{ + typ: typ, + values: memory.SliceBufferFrom(data.Int32()[:numValues]), + columnIndex: ^columnIndex, + }, + } +} + +func (d *int32Dictionary) Type() Type { return newIndexedType(d.typ, d) } + +func (d *int32Dictionary) Len() int { return d.values.Len() } + +func (d *int32Dictionary) Size() int64 { return int64(d.values.Len() * 4) } + +func (d *int32Dictionary) Index(i int32) Value { return d.makeValue(d.index(i)) } + +func (d *int32Dictionary) index(i int32) int32 { return d.values.Slice()[i] } + +func (d *int32Dictionary) Insert(indexes []int32, values []Value) { + d.insert(indexes, makeArrayValue(values, offsetOfU32)) +} + +func (d *int32Dictionary) init(indexes []int32) { + values := d.values.Slice() + d.table = hashprobe.NewInt32Table(len(values), hashprobeTableMaxLoad) + + n := min(len(values), len(indexes)) + + for i := 0; i < len(values); i += n { + j := min(i+n, len(values)) + d.table.Probe(values[i:j:j], indexes[:n:n]) + } +} + +func (d *int32Dictionary) insert(indexes []int32, rows sparse.Array) { + // Iterating over the input in chunks helps keep relevant data in CPU + // caches when a large number of values are inserted into the dictionary with + // a single method call. + // + // Without this chunking, memory areas from the head of the indexes and + // values arrays end up being evicted from CPU caches as the probing + // operation iterates through the array. The subsequent scan of the indexes + // required to determine which values must be inserted into the page then + // stalls on retrieving data from main memory. + // + // We measured as much as ~37% drop in throughput when disabling the + // chunking, and did not observe any penalties from having it on smaller + // inserts. + const chunkSize = insertsTargetCacheFootprint / 4 + + if d.table == nil { + d.init(indexes) + } + + values := rows.Int32Array() + + for i := 0; i < values.Len(); i += chunkSize { + j := min(i+chunkSize, values.Len()) + + if d.table.ProbeArray(values.Slice(i, j), indexes[i:j:j]) > 0 { + for k, index := range indexes[i:j] { + if index == int32(d.values.Len()) { + d.values.Append(values.Index(i + k)) + } + } + } + } +} + +func (d *int32Dictionary) Lookup(indexes []int32, values []Value) { + model := d.makeValue(0) + memsetValues(values, model) + d.lookup(indexes, makeArrayValue(values, offsetOfU32)) +} + +func (d *int32Dictionary) Bounds(indexes []int32) (min, max Value) { + if len(indexes) > 0 { + minValue, maxValue := d.bounds(indexes) + min = d.makeValue(minValue) + max = d.makeValue(maxValue) + } + return min, max +} + +func (d *int32Dictionary) Reset() { + d.values.Reset() + if d.table != nil { + d.table.Reset() + } +} + +func (d *int32Dictionary) Page() Page { + return &d.int32Page +} + +func (d *int32Dictionary) insertBoolean(value bool) int32 { + if value { + return d.insertInt32(1) + } + return d.insertInt32(0) +} + +func (d *int32Dictionary) insertInt32(value int32) int32 { + var indexes [1]int32 + d.insert(indexes[:], makeArrayFromPointer(&value)) + return indexes[0] +} + +func (d *int32Dictionary) insertInt64(value int64) int32 { + return d.insertInt32(int32(value)) +} + +func (d *int32Dictionary) insertInt96(value deprecated.Int96) int32 { + return d.insertInt32(int32(value[0])) +} + +func (d *int32Dictionary) insertFloat(value float32) int32 { + return d.insertInt32(int32(value)) +} + +func (d *int32Dictionary) insertDouble(value float64) int32 { + return d.insertInt32(int32(value)) +} + +func (d *int32Dictionary) insertByteArray(value []byte) int32 { + v, err := strconv.ParseInt(string(value), 10, 32) + if err != nil { + panic(err) + } + return d.insertInt32(int32(v)) +} diff --git a/vendor/github.com/parquet-go/parquet-go/dictionary_int64.go b/vendor/github.com/parquet-go/parquet-go/dictionary_int64.go new file mode 100644 index 000000000..0c7ff7330 --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/dictionary_int64.go @@ -0,0 +1,134 @@ +package parquet + +import ( + "strconv" + + "github.com/parquet-go/parquet-go/deprecated" + "github.com/parquet-go/parquet-go/encoding" + "github.com/parquet-go/parquet-go/hashprobe" + "github.com/parquet-go/parquet-go/internal/memory" + "github.com/parquet-go/parquet-go/sparse" +) + +type int64Dictionary struct { + int64Page + table *hashprobe.Int64Table +} + +func newInt64Dictionary(typ Type, columnIndex uint16, numValues int32, data encoding.Values) *int64Dictionary { + return &int64Dictionary{ + int64Page: int64Page{ + typ: typ, + values: memory.SliceBufferFrom(data.Int64()[:numValues]), + columnIndex: ^columnIndex, + }, + } +} + +func (d *int64Dictionary) Type() Type { return newIndexedType(d.typ, d) } + +func (d *int64Dictionary) Len() int { return d.values.Len() } + +func (d *int64Dictionary) Size() int64 { return int64(d.values.Len() * 8) } + +func (d *int64Dictionary) Index(i int32) Value { return d.makeValue(d.index(i)) } + +func (d *int64Dictionary) index(i int32) int64 { return d.values.Slice()[i] } + +func (d *int64Dictionary) Insert(indexes []int32, values []Value) { + d.insert(indexes, makeArrayValue(values, offsetOfU64)) +} + +func (d *int64Dictionary) init(indexes []int32) { + values := d.values.Slice() + d.table = hashprobe.NewInt64Table(len(values), hashprobeTableMaxLoad) + + n := min(len(values), len(indexes)) + + for i := 0; i < len(values); i += n { + j := min(i+n, len(values)) + d.table.Probe(values[i:j:j], indexes[:n:n]) + } +} + +func (d *int64Dictionary) insert(indexes []int32, rows sparse.Array) { + const chunkSize = insertsTargetCacheFootprint / 8 + + if d.table == nil { + d.init(indexes) + } + + values := rows.Int64Array() + + for i := 0; i < values.Len(); i += chunkSize { + j := min(i+chunkSize, values.Len()) + + if d.table.ProbeArray(values.Slice(i, j), indexes[i:j:j]) > 0 { + for k, index := range indexes[i:j] { + if index == int32(d.values.Len()) { + d.values.Append(values.Index(i + k)) + } + } + } + } +} + +func (d *int64Dictionary) Lookup(indexes []int32, values []Value) { + model := d.makeValue(0) + memsetValues(values, model) + d.lookup(indexes, makeArrayValue(values, offsetOfU64)) +} + +func (d *int64Dictionary) Bounds(indexes []int32) (min, max Value) { + if len(indexes) > 0 { + minValue, maxValue := d.bounds(indexes) + min = d.makeValue(minValue) + max = d.makeValue(maxValue) + } + return min, max +} + +func (d *int64Dictionary) Reset() { + d.values.Reset() + if d.table != nil { + d.table.Reset() + } +} + +func (d *int64Dictionary) Page() Page { + return &d.int64Page +} + +func (d *int64Dictionary) insertBoolean(value bool) int32 { + panic("cannot insert boolean value into int64 dictionary") +} + +func (d *int64Dictionary) insertInt32(value int32) int32 { + return d.insertInt64(int64(value)) +} + +func (d *int64Dictionary) insertInt64(value int64) int32 { + var indexes [1]int32 + d.insert(indexes[:], makeArrayFromPointer(&value)) + return indexes[0] +} + +func (d *int64Dictionary) insertInt96(value deprecated.Int96) int32 { + return d.insertInt64(value.Int64()) +} + +func (d *int64Dictionary) insertFloat(value float32) int32 { + return d.insertInt64(int64(value)) +} + +func (d *int64Dictionary) insertDouble(value float64) int32 { + return d.insertInt64(int64(value)) +} + +func (d *int64Dictionary) insertByteArray(value []byte) int32 { + v, err := strconv.ParseInt(string(value), 10, 64) + if err != nil { + panic(err) + } + return d.insertInt64(v) +} diff --git a/vendor/github.com/parquet-go/parquet-go/dictionary_int96.go b/vendor/github.com/parquet-go/parquet-go/dictionary_int96.go new file mode 100644 index 000000000..a68120f87 --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/dictionary_int96.go @@ -0,0 +1,143 @@ +package parquet + +import ( + "math/big" + + "github.com/parquet-go/parquet-go/deprecated" + "github.com/parquet-go/parquet-go/encoding" + "github.com/parquet-go/parquet-go/sparse" +) + +type int96Dictionary struct { + int96Page + hashmap map[deprecated.Int96]int32 +} + +func newInt96Dictionary(typ Type, columnIndex uint16, numValues int32, data encoding.Values) *int96Dictionary { + return &int96Dictionary{ + int96Page: int96Page{ + typ: typ, + values: data.Int96()[:numValues], + columnIndex: ^columnIndex, + }, + } +} + +func (d *int96Dictionary) Type() Type { return newIndexedType(d.typ, d) } + +func (d *int96Dictionary) Len() int { return len(d.values) } + +func (d *int96Dictionary) Size() int64 { return int64(len(d.values) * 12) } + +func (d *int96Dictionary) Index(i int32) Value { return d.makeValue(d.index(i)) } + +func (d *int96Dictionary) index(i int32) deprecated.Int96 { return d.values[i] } + +func (d *int96Dictionary) Insert(indexes []int32, values []Value) { + d.insertValues(indexes, len(values), func(i int) deprecated.Int96 { + return values[i].Int96() + }) +} + +func (d *int96Dictionary) insert(indexes []int32, rows sparse.Array) { + d.insertValues(indexes, rows.Len(), func(i int) deprecated.Int96 { + return *(*deprecated.Int96)(rows.Index(i)) + }) +} + +func (d *int96Dictionary) insertValues(indexes []int32, count int, valueAt func(int) deprecated.Int96) { + _ = indexes[:count] + + if d.hashmap == nil { + d.hashmap = make(map[deprecated.Int96]int32, len(d.values)) + for i, v := range d.values { + d.hashmap[v] = int32(i) + } + } + + for i := range count { + value := valueAt(i) + + index, exists := d.hashmap[value] + if !exists { + index = int32(len(d.values)) + d.values = append(d.values, value) + d.hashmap[value] = index + } + + indexes[i] = index + } +} + +func (d *int96Dictionary) Lookup(indexes []int32, values []Value) { + for i, j := range indexes { + values[i] = d.Index(j) + } +} + +func (d *int96Dictionary) Bounds(indexes []int32) (min, max Value) { + if len(indexes) > 0 { + minValue := d.index(indexes[0]) + maxValue := minValue + + for _, i := range indexes[1:] { + value := d.index(i) + switch { + case value.Less(minValue): + minValue = value + case maxValue.Less(value): + maxValue = value + } + } + + min = d.makeValue(minValue) + max = d.makeValue(maxValue) + } + return min, max +} + +func (d *int96Dictionary) Reset() { + d.values = d.values[:0] + d.hashmap = nil +} + +func (d *int96Dictionary) Page() Page { + return &d.int96Page +} + +func (d *int96Dictionary) insertBoolean(value bool) int32 { + if value { + return d.insertInt96(deprecated.Int96{1, 0, 0}) + } + return d.insertInt96(deprecated.Int96{0, 0, 0}) +} + +func (d *int96Dictionary) insertInt32(value int32) int32 { + return d.insertInt96(deprecated.Int96{uint32(value), 0, 0}) +} + +func (d *int96Dictionary) insertInt64(value int64) int32 { + return d.insertInt96(deprecated.Int64ToInt96(value)) +} + +func (d *int96Dictionary) insertInt96(value deprecated.Int96) int32 { + indexes := [1]int32{0} + d.insertValues(indexes[:], 1, func(i int) deprecated.Int96 { return value }) + return indexes[0] +} + +func (d *int96Dictionary) insertFloat(value float32) int32 { + return d.insertInt96(deprecated.Int64ToInt96(int64(value))) +} + +func (d *int96Dictionary) insertDouble(value float64) int32 { + return d.insertInt96(deprecated.Int64ToInt96(int64(value))) +} + +func (d *int96Dictionary) insertByteArray(value []byte) int32 { + v, ok := new(big.Int).SetString(string(value), 10) + if !ok || v == nil { + panic("invalid byte array for int96: cannot parse") + } + return d.insertInt96(deprecated.Int64ToInt96(v.Int64())) +} diff --git a/vendor/github.com/parquet-go/parquet-go/dictionary_null.go b/vendor/github.com/parquet-go/parquet-go/dictionary_null.go new file mode 100644 index 000000000..6f7f9d7d4 --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/dictionary_null.go @@ -0,0 +1,75 @@ +package parquet + +import ( + "github.com/parquet-go/parquet-go/deprecated" + "github.com/parquet-go/parquet-go/encoding" + "github.com/parquet-go/parquet-go/sparse" +) + +// nullDictionary is a dictionary for NULL type columns where all operations are no-ops. +type nullDictionary struct { + nullPage +} + +func newNullDictionary(typ Type, columnIndex uint16, numValues int32, _ encoding.Values) *nullDictionary { + return &nullDictionary{ + nullPage: *newNullPage(typ, columnIndex, numValues), + } +} + +func (d *nullDictionary) Type() Type { return d.nullPage.Type() } + +func (d *nullDictionary) Len() int { return int(d.nullPage.count) } + +func (d *nullDictionary) Size() int64 { return 0 } + +func (d *nullDictionary) Index(i int32) Value { return NullValue() } + +func (d *nullDictionary) Lookup(indexes []int32, values []Value) { + checkLookupIndexBounds(indexes, makeArrayValue(values, 0)) + for i := range indexes { + values[i] = NullValue() + } +} + +func (d *nullDictionary) Insert(indexes []int32, values []Value) {} + +func (d *nullDictionary) Bounds(indexes []int32) (min, max Value) { + return NullValue(), NullValue() +} + +func (d *nullDictionary) Reset() { + d.nullPage.count = 0 +} + +func (d *nullDictionary) Page() Page { return &d.nullPage } + +func (d *nullDictionary) insert(indexes []int32, rows sparse.Array) {} + +func (d *nullDictionary) insertBoolean(value bool) int32 { + panic("cannot insert boolean value into null dictionary") +} + +func (d *nullDictionary) insertInt32(value int32) int32 { + panic("cannot insert int32 value into null dictionary") +} + +func (d *nullDictionary) insertInt64(value int64) int32 { + panic("cannot insert int64 value into null dictionary") +} + +func (d *nullDictionary) insertInt96(value deprecated.Int96) int32 { + panic("cannot insert int96 value into null dictionary") +} + +func (d *nullDictionary) insertFloat(value float32) int32 { + panic("cannot insert float value into null dictionary") +} + +func (d *nullDictionary) insertDouble(value float64) int32 { + panic("cannot insert double value into null dictionary") +} + +func (d *nullDictionary) insertByteArray(value []byte) int32 { + panic("cannot insert byte array value into null dictionary") +} diff --git a/vendor/github.com/parquet-go/parquet-go/dictionary_purego.go b/vendor/github.com/parquet-go/parquet-go/dictionary_purego.go new file mode 100644 index 000000000..8d71b9827 --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/dictionary_purego.go @@ -0,0 +1,210 @@ +//go:build purego || !amd64 + +package parquet + +import ( + "unsafe" + + "github.com/parquet-go/bitpack/unsafecast" + "github.com/parquet-go/parquet-go/sparse" +) + +func (d *int32Dictionary) lookup(indexes []int32, rows sparse.Array) { + checkLookupIndexBounds(indexes, rows) + for i, j := range indexes { + *(*int32)(rows.Index(i)) = d.index(j) + } +} + +func (d *int64Dictionary) lookup(indexes []int32, rows sparse.Array) { + checkLookupIndexBounds(indexes, rows) + for i, j := range indexes { + *(*int64)(rows.Index(i)) = d.index(j) + } +} + +func (d *floatDictionary) lookup(indexes []int32, rows sparse.Array) { + checkLookupIndexBounds(indexes, rows) + for i, j := range indexes { + *(*float32)(rows.Index(i)) = d.index(j) + } +} + +func (d *doubleDictionary) lookup(indexes []int32, rows sparse.Array) { + checkLookupIndexBounds(indexes, rows) + for i, j := range indexes { + *(*float64)(rows.Index(i)) = d.index(j) + } +} + +func (d *byteArrayDictionary) lookupString(indexes []int32, rows sparse.Array) { + checkLookupIndexBounds(indexes, rows) + for i, j := range indexes { + *(*string)(rows.Index(i)) = unsafecast.String(d.index(int(j))) + } +} + +func (d *fixedLenByteArrayDictionary) lookupString(indexes []int32, rows sparse.Array) { + checkLookupIndexBounds(indexes, rows) + for i, j := range indexes { + *(*string)(rows.Index(i)) = unsafecast.String(d.index(j)) + } +} + +func (d *uint32Dictionary) lookup(indexes []int32, rows sparse.Array) { + checkLookupIndexBounds(indexes, rows) + for i, j := range indexes { + *(*uint32)(rows.Index(i)) = d.index(j) + } +} + +func (d *uint64Dictionary) lookup(indexes []int32, rows sparse.Array) { + checkLookupIndexBounds(indexes, rows) + for i, j := range indexes { + *(*uint64)(rows.Index(i)) = d.index(j) + } +} + +func (d *be128Dictionary) lookupString(indexes []int32, rows sparse.Array) { + checkLookupIndexBounds(indexes, rows) + s := "0123456789ABCDEF" + for i, j := range indexes { + *(**[16]byte)(unsafe.Pointer(&s)) = d.index(j) + *(*string)(rows.Index(i)) = s + } +} + +func (d *be128Dictionary) lookupPointer(indexes []int32, rows sparse.Array) { + checkLookupIndexBounds(indexes, rows) + for i, j := range indexes { + *(**[16]byte)(rows.Index(i)) = d.index(j) + } +} + +func (d *int32Dictionary) bounds(indexes []int32) (min, max int32) { + min = d.index(indexes[0]) + max = min + + for _, i := range indexes[1:] { + value := d.index(i) + if value < min { + min = value + } + if value > max { + max = value + } + } + + return min, max +} + +func (d *int64Dictionary) bounds(indexes []int32) (min, max int64) { + min = d.index(indexes[0]) + max = min + + for _, i := range indexes[1:] { + value := d.index(i) + if value < min { + min = value + } + if value > max { + max = value + } + } + + return min, max +} + +func (d *floatDictionary) bounds(indexes []int32) (min, max float32) { + min = d.index(indexes[0]) + max = min + + for _, i := range indexes[1:] { + value := d.index(i) + if value < min { + min = value + } + if value > max { + max = value + } + } + + return min, max +} + +func (d *doubleDictionary) bounds(indexes []int32) (min, max float64) { + min = d.index(indexes[0]) + max = min + + for _, i := range indexes[1:] { + value := d.index(i) + if value < min { + min = value + } + if value > max { + max = value + } + } + + return min, max +} + +func (d *uint32Dictionary) bounds(indexes []int32) (min, max uint32) { + min = d.index(indexes[0]) + max = min + + for _, i := range indexes[1:] { + value := d.index(i) + if value < min { + min = value + } + if value > max { + max = value + } + } + + return min, max +} + +func (d *uint64Dictionary) bounds(indexes []int32) (min, max uint64) { + min = d.index(indexes[0]) + max = min + + for _, i := range indexes[1:] { + value := d.index(i) + if value < min { + min = value + } + if value > max { + max = value + } + } + + return min, max +} + +func (d *be128Dictionary) bounds(indexes []int32) (min, max *[16]byte) { + values := [64]*[16]byte{} + min = d.index(indexes[0]) + max = min + + for i := 1; i < len(indexes); i += len(values) { + n := len(indexes) - i + if n > len(values) { + n = len(values) + } + j := i + n + d.lookupPointer(indexes[i:j:j], makeArrayFromSlice(values[:n:n])) + + for _, value := range values[:n:n] { + switch { + case lessBE128(value, min): + min = value + case lessBE128(max, value): + max = value + } + } + } + + return min, max +} diff --git a/vendor/github.com/parquet-go/parquet-go/dictionary_uint32.go b/vendor/github.com/parquet-go/parquet-go/dictionary_uint32.go new file mode 100644 index 000000000..92a4fe933 --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/dictionary_uint32.go @@ -0,0 +1,141 @@ +package parquet + +import ( + "strconv" + + "github.com/parquet-go/parquet-go/deprecated" + "github.com/parquet-go/parquet-go/encoding" + "github.com/parquet-go/parquet-go/hashprobe" + "github.com/parquet-go/parquet-go/internal/memory" + "github.com/parquet-go/parquet-go/sparse" +) + +type uint32Dictionary struct { + uint32Page + table *hashprobe.Uint32Table +} + +func newUint32Dictionary(typ Type, columnIndex uint16, numValues int32, data encoding.Values) *uint32Dictionary { + return &uint32Dictionary{ + uint32Page: uint32Page{ + typ: typ, + values: memory.SliceBufferFrom(data.Uint32()[:numValues]), + columnIndex: ^columnIndex, + }, + } +} + +func (d *uint32Dictionary) Type() Type { return newIndexedType(d.typ, d) } + +func (d *uint32Dictionary) Len() int { return d.values.Len() } + +func (d *uint32Dictionary) Size() int64 { return int64(d.values.Len() * 4) } + +func (d *uint32Dictionary) Index(i int32) Value { return d.makeValue(d.index(i)) } + +func (d *uint32Dictionary) index(i int32) uint32 { return d.values.Slice()[i] } + +func (d *uint32Dictionary) Insert(indexes []int32, values []Value) { + d.insert(indexes, makeArrayValue(values, offsetOfU32)) +} + +func (d *uint32Dictionary) init(indexes []int32) { + values := d.values.Slice() + d.table = hashprobe.NewUint32Table(len(values), hashprobeTableMaxLoad) + + n := min(len(values), len(indexes)) + + for i := 0; i < len(values); i += n { + j := min(i+n, len(values)) + d.table.Probe(values[i:j:j], indexes[:n:n]) + } +} + +func (d *uint32Dictionary) insert(indexes []int32, rows sparse.Array) { + const chunkSize = insertsTargetCacheFootprint / 4 + + if d.table == nil { + d.init(indexes) + } + + values := rows.Uint32Array() + + for i := 0; i < values.Len(); i += chunkSize { + j := min(i+chunkSize, values.Len()) + + if d.table.ProbeArray(values.Slice(i, j), indexes[i:j:j]) > 0 { + for k, index := range indexes[i:j] { + if index == int32(d.values.Len()) { + d.values.Append(values.Index(i + k)) + } + } + } + } +} + +func (d *uint32Dictionary) Lookup(indexes []int32, values []Value) { + model := d.makeValue(0) + memsetValues(values, model) + d.lookup(indexes, makeArrayValue(values, offsetOfU32)) +} + +func (d *uint32Dictionary) Bounds(indexes []int32) (min, max Value) { + if len(indexes) > 0 { + minValue, maxValue := d.bounds(indexes) + min = d.makeValue(minValue) + max = d.makeValue(maxValue) + } + return min, max +} + +func (d *uint32Dictionary) Reset() { + d.values.Reset() + if d.table != nil { + d.table.Reset() + } +} + +func (d *uint32Dictionary) Page() Page { + return &d.uint32Page +} + +func (d *uint32Dictionary) insertBoolean(value bool) int32 { + if value { + return d.insertUint32(1) + } + return d.insertUint32(0) +} + +func (d *uint32Dictionary) insertInt32(value int32) int32 { + return d.insertUint32(uint32(value)) +} + +func (d *uint32Dictionary) insertInt64(value int64) int32 { + return d.insertUint32(uint32(value)) +} + +func (d *uint32Dictionary) insertInt96(value deprecated.Int96) int32 { + return d.insertUint32(value[0]) +} + +func (d *uint32Dictionary) insertFloat(value float32) int32 { + return d.insertUint32(uint32(value)) +} + +func (d *uint32Dictionary) insertDouble(value float64) int32 { + return d.insertUint32(uint32(value)) +} + +func (d *uint32Dictionary) insertByteArray(value []byte) int32 { + v, err := strconv.ParseUint(string(value), 10, 32) + if err != nil { + panic(err) + } + return d.insertUint32(uint32(v)) +} + +func (d *uint32Dictionary) insertUint32(value uint32) int32 { + var indexes [1]int32 + d.insert(indexes[:], makeArrayFromPointer(&value)) + return indexes[0] +} diff --git a/vendor/github.com/parquet-go/parquet-go/dictionary_uint64.go b/vendor/github.com/parquet-go/parquet-go/dictionary_uint64.go new file mode 100644 index 000000000..188ecc1cb --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/dictionary_uint64.go @@ -0,0 +1,141 @@ +package parquet + +import ( + "strconv" + + "github.com/parquet-go/parquet-go/deprecated" + "github.com/parquet-go/parquet-go/encoding" + "github.com/parquet-go/parquet-go/hashprobe" + "github.com/parquet-go/parquet-go/internal/memory" + "github.com/parquet-go/parquet-go/sparse" +) + +type uint64Dictionary struct { + uint64Page + table *hashprobe.Uint64Table +} + +func newUint64Dictionary(typ Type, columnIndex uint16, numValues int32, data encoding.Values) *uint64Dictionary { + return &uint64Dictionary{ + uint64Page: uint64Page{ + typ: typ, + values: memory.SliceBufferFrom(data.Uint64()[:numValues]), + columnIndex: ^columnIndex, + }, + } +} + +func (d *uint64Dictionary) Type() Type { return newIndexedType(d.typ, d) } + +func (d *uint64Dictionary) Len() int { return d.values.Len() } + +func (d *uint64Dictionary) Size() int64 { return int64(d.values.Len() * 8) } + +func (d *uint64Dictionary) Index(i int32) Value { return d.makeValue(d.index(i)) } + +func (d *uint64Dictionary) index(i int32) uint64 { return d.values.Slice()[i] } + +func (d *uint64Dictionary) Insert(indexes []int32, values []Value) { + d.insert(indexes, makeArrayValue(values, offsetOfU64)) +} + +func (d *uint64Dictionary) init(indexes []int32) { + values := d.values.Slice() + d.table = hashprobe.NewUint64Table(len(values), hashprobeTableMaxLoad) + + n := min(len(values), len(indexes)) + + for i := 0; i < len(values); i += n { + j := min(i+n, len(values)) + d.table.Probe(values[i:j:j], indexes[:n:n]) + } +} + +func (d *uint64Dictionary) insert(indexes []int32, rows sparse.Array) { + const chunkSize = insertsTargetCacheFootprint / 8 + + if d.table == nil { + d.init(indexes) + } + + values := rows.Uint64Array() + + for i := 0; i < values.Len(); i += chunkSize { + j := min(i+chunkSize, values.Len()) + + if d.table.ProbeArray(values.Slice(i, j), indexes[i:j:j]) > 0 { + for k, index := range indexes[i:j] { + if index == int32(d.values.Len()) { + d.values.Append(values.Index(i + k)) + } + } + } + } +} + +func (d *uint64Dictionary) Lookup(indexes []int32, values []Value) { + model := d.makeValue(0) + memsetValues(values, model) + d.lookup(indexes, makeArrayValue(values, offsetOfU64)) +} + +func (d *uint64Dictionary) Bounds(indexes []int32) (min, max Value) { + if len(indexes) > 0 { + minValue, maxValue := d.bounds(indexes) + min = d.makeValue(minValue) + max = d.makeValue(maxValue) + } + return min, max +} + +func (d *uint64Dictionary) Reset() { + d.values.Reset() + if d.table != nil { + d.table.Reset() + } +} + +func (d *uint64Dictionary) Page() Page { + return &d.uint64Page +} + +func (d *uint64Dictionary) insertBoolean(value bool) int32 { + if value { + return d.insertUint64(1) + } + return d.insertUint64(0) +} + +func (d *uint64Dictionary) insertInt32(value int32) int32 { + return d.insertUint64(uint64(value)) +} + +func (d *uint64Dictionary) insertInt64(value int64) int32 { + return d.insertUint64(uint64(value)) +} + +func (d *uint64Dictionary) insertInt96(value deprecated.Int96) int32 { + return d.insertUint64(uint64(value.Int64())) +} + +func (d *uint64Dictionary) insertFloat(value float32) int32 { + return d.insertUint64(uint64(value)) +} + +func (d *uint64Dictionary) insertDouble(value float64) int32 { + return d.insertUint64(uint64(value)) +} + +func (d *uint64Dictionary) insertByteArray(value []byte) int32 { + v, err := strconv.ParseUint(string(value), 10, 32) + if err != nil { + panic(err) + } + return d.insertUint64(uint64(v)) +} + +func (d *uint64Dictionary) insertUint64(value uint64) int32 { + var indexes [1]int32 + d.insert(indexes[:], makeArrayFromPointer(&value)) + return indexes[0] +} diff --git a/vendor/github.com/parquet-go/parquet-go/encoding.go b/vendor/github.com/parquet-go/parquet-go/encoding.go new file mode 100644 index 000000000..f5c98c93b --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/encoding.go @@ -0,0 +1,160 @@ +package parquet + +import ( + "math/bits" + "sync" + + "github.com/parquet-go/parquet-go/encoding" + "github.com/parquet-go/parquet-go/encoding/bitpacked" + "github.com/parquet-go/parquet-go/encoding/bytestreamsplit" + "github.com/parquet-go/parquet-go/encoding/delta" + "github.com/parquet-go/parquet-go/encoding/plain" + "github.com/parquet-go/parquet-go/encoding/rle" + "github.com/parquet-go/parquet-go/format" +) + +var ( + // Plain is the default parquet encoding. + Plain plain.Encoding + + // RLE is the hybrid bit-pack/run-length parquet encoding. + RLE rle.Encoding + + // BitPacked is the deprecated bit-packed encoding for repetition and + // definition levels. + BitPacked bitpacked.Encoding + + // PlainDictionary is the plain dictionary parquet encoding. + // + // This encoding should not be used anymore in parquet 2.0 and later, + // it is implemented for backwards compatibility to support reading + // files that were encoded with older parquet libraries. + PlainDictionary plain.DictionaryEncoding + + // RLEDictionary is the RLE dictionary parquet encoding. + RLEDictionary rle.DictionaryEncoding + + // DeltaBinaryPacked is the delta binary packed parquet encoding. + DeltaBinaryPacked delta.BinaryPackedEncoding + + // DeltaLengthByteArray is the delta length byte array parquet encoding. + DeltaLengthByteArray delta.LengthByteArrayEncoding + + // DeltaByteArray is the delta byte array parquet encoding. + DeltaByteArray delta.ByteArrayEncoding + + // ByteStreamSplit is an encoding for numeric and fixed-length binary data. + ByteStreamSplit bytestreamsplit.Encoding + + // Table indexing the encodings supported by this package. + encodings = [...]encoding.Encoding{ + format.Plain: &Plain, + format.PlainDictionary: &PlainDictionary, + format.BitPacked: &BitPacked, + format.RLE: &RLE, + format.RLEDictionary: &RLEDictionary, + format.DeltaBinaryPacked: &DeltaBinaryPacked, + format.DeltaLengthByteArray: &DeltaLengthByteArray, + format.DeltaByteArray: &DeltaByteArray, + format.ByteStreamSplit: &ByteStreamSplit, + } + + // Table indexing RLE encodings for repetition and definition levels of + // all supported bit widths. + levelEncodingsRLE = [...]rle.Encoding{ + 0: {BitWidth: 1}, + 1: {BitWidth: 2}, + 2: {BitWidth: 3}, + 3: {BitWidth: 4}, + 4: {BitWidth: 5}, + 5: {BitWidth: 6}, + 6: {BitWidth: 7}, + 7: {BitWidth: 8}, + } + + levelEncodingsBitPacked = [...]bitpacked.Encoding{ + 0: {BitWidth: 1}, + 1: {BitWidth: 2}, + 2: {BitWidth: 3}, + 3: {BitWidth: 4}, + 4: {BitWidth: 5}, + 5: {BitWidth: 6}, + 6: {BitWidth: 7}, + 7: {BitWidth: 8}, + } +) + +var extraEncodings sync.Map + +func isDictionaryEncoding(encoding encoding.Encoding) bool { + return isDictionaryFormat(encoding.Encoding()) +} + +func isDictionaryFormat(encoding format.Encoding) bool { + return encoding == format.PlainDictionary || encoding == format.RLEDictionary +} + +func RegisterEncoding(enc encoding.Encoding) { + ns := encoding.NotSupported{} + if enc == ns { + panic("cannot register parquet encoding as not-supported") + } + if LookupEncoding(enc.Encoding()) != ns { + panic("cannot register parquet encoding that overrides the standard specification") + } + extraEncodings.Store(enc.Encoding(), enc) +} + +// LookupEncoding returns the parquet encoding associated with the given code. +// +// The function never returns nil. If the encoding is not supported, +// encoding.NotSupported is returned. +func LookupEncoding(enc format.Encoding) encoding.Encoding { + if enc >= 0 && int(enc) < len(encodings) { + if e := encodings[enc]; e != nil { + return e + } + } + if enc, ok := extraEncodings.Load(enc); ok { + return enc.(encoding.Encoding) + } + return encoding.NotSupported{} +} + +func lookupLevelEncoding(enc format.Encoding, max byte) encoding.Encoding { + i := bits.Len8(max) - 1 + switch enc { + case format.RLE: + return &levelEncodingsRLE[i] + case format.BitPacked: + return &levelEncodingsBitPacked[i] + default: + return encoding.NotSupported{} + } +} + +func canEncode(e encoding.Encoding, k Kind) bool { + if isDictionaryEncoding(e) { + return true + } + switch k { + case Boolean: + return encoding.CanEncodeBoolean(e) + case Int32: + return encoding.CanEncodeInt32(e) + case Int64: + return encoding.CanEncodeInt64(e) + case Int96: + return encoding.CanEncodeInt96(e) + case Float: + return encoding.CanEncodeFloat(e) + case Double: + return encoding.CanEncodeDouble(e) + case ByteArray: + return encoding.CanEncodeByteArray(e) + case FixedLenByteArray: + return encoding.CanEncodeFixedLenByteArray(e) + default: + return false + } +} diff --git a/vendor/github.com/parquet-go/parquet-go/encoding/bitpacked/bitpacked.go b/vendor/github.com/parquet-go/parquet-go/encoding/bitpacked/bitpacked.go new file mode 100644 index 000000000..7e59a9d5b --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/encoding/bitpacked/bitpacked.go @@ -0,0 +1,119 @@ +package bitpacked + +import ( + "github.com/parquet-go/parquet-go/encoding" + "github.com/parquet-go/parquet-go/format" +) + +type Encoding struct { + encoding.NotSupported + BitWidth int +} + +func (e *Encoding) String() string { + return "BIT_PACKED" +} + +func (e *Encoding) Encoding() format.Encoding { + return format.BitPacked +} + +func (e *Encoding) EncodeLevels(dst []byte, src []uint8) ([]byte, error) { + dst, err := encodeLevels(dst[:0], src, uint(e.BitWidth)) + return dst, e.wrap(err) +} + +func (e *Encoding) DecodeLevels(dst []uint8, src []byte) ([]uint8, error) { + dst, err := decodeLevels(dst[:0], src, uint(e.BitWidth)) + return dst, e.wrap(err) +} + +func (e *Encoding) wrap(err error) error { + if err != nil { + err = encoding.Error(e, err) + } + return err +} + +func encodeLevels(dst, src []byte, bitWidth uint) ([]byte, error) { + if bitWidth == 0 || len(src) == 0 { + return append(dst[:0], 0), nil + } + + n := ((int(bitWidth) * len(src)) + 7) / 8 + c := n + 1 + + if cap(dst) < c { + dst = make([]byte, c, 2*c) + } else { + dst = dst[:c] + for i := range dst { + dst[i] = 0 + } + } + + bitOffset := uint(0) + + for _, value := range src { + i := bitOffset / 8 + j := bitOffset % 8 + available := 8 - j + if bitWidth <= available { + // Value fits entirely in byte i; align to the right within available bits. + dst[i] |= value << (available - bitWidth) + } else { + // Split across byte i and byte i+1. + dst[i] |= value >> (bitWidth - available) + dst[i+1] |= value << (8 - (bitWidth - available)) + } + bitOffset += bitWidth + } + + return dst[:n], nil +} + +func decodeLevels(dst, src []byte, bitWidth uint) ([]byte, error) { + if bitWidth == 0 || len(src) == 0 { + return append(dst[:0], 0), nil + } + + numBits := 8 * uint(len(src)) + numValues := int(numBits / bitWidth) + if (numBits % bitWidth) != 0 { + numValues++ + } + + if cap(dst) < numValues { + dst = make([]byte, numValues, 2*numValues) + } else { + dst = dst[:numValues] + for i := range dst { + dst[i] = 0 + } + } + + bitMask := byte(1<> (available - bitWidth)) & bitMask + } else { + remaining := bitWidth - available + topBits := src[i] & ((1 << available) - 1) + var bottomBits byte + if int(i+1) < len(src) { + bottomBits = src[i+1] >> (8 - remaining) + } + v = (topBits << remaining) | bottomBits + } + dst[k] = v + bitOffset += bitWidth + } + + return dst, nil +} diff --git a/vendor/github.com/parquet-go/parquet-go/encoding/bytestreamsplit/bytestreamsplit.go b/vendor/github.com/parquet-go/parquet-go/encoding/bytestreamsplit/bytestreamsplit.go new file mode 100644 index 000000000..235d5a1fa --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/encoding/bytestreamsplit/bytestreamsplit.go @@ -0,0 +1,114 @@ +package bytestreamsplit + +import ( + "github.com/parquet-go/bitpack/unsafecast" + "github.com/parquet-go/parquet-go/encoding" + "github.com/parquet-go/parquet-go/format" +) + +// This encoder implements a version of the Byte Stream Split encoding as described +// in https://github.com/apache/parquet-format/blob/master/Encodings.md#byte-stream-split-byte_stream_split--9 +type Encoding struct { + encoding.NotSupported +} + +func (e *Encoding) String() string { + return "BYTE_STREAM_SPLIT" +} + +func (e *Encoding) Encoding() format.Encoding { + return format.ByteStreamSplit +} + +func (e *Encoding) EncodeFloat(dst []byte, src []float32) ([]byte, error) { + dst = resize(dst, 4*len(src)) + encodeFloat(dst, unsafecast.Slice[byte](src)) + return dst, nil +} + +func (e *Encoding) EncodeDouble(dst []byte, src []float64) ([]byte, error) { + dst = resize(dst, 8*len(src)) + encodeDouble(dst, unsafecast.Slice[byte](src)) + return dst, nil +} + +func (e *Encoding) DecodeFloat(dst []float32, src []byte) ([]float32, error) { + if (len(src) % 4) != 0 { + return dst, encoding.ErrDecodeInvalidInputSize(e, "FLOAT", len(src)) + } + buf := resize(unsafecast.Slice[byte](dst), len(src)) + decodeFloat(buf, src) + return unsafecast.Slice[float32](buf), nil +} + +func (e *Encoding) DecodeDouble(dst []float64, src []byte) ([]float64, error) { + if (len(src) % 8) != 0 { + return dst, encoding.ErrDecodeInvalidInputSize(e, "DOUBLE", len(src)) + } + buf := resize(unsafecast.Slice[byte](dst), len(src)) + decodeDouble(buf, src) + return unsafecast.Slice[float64](buf), nil +} + +func (e *Encoding) EncodeInt32(dst []byte, src []int32) ([]byte, error) { + dst = resize(dst, 4*len(src)) + encodeInt32(dst, unsafecast.Slice[byte](src)) + return dst, nil +} + +func (e *Encoding) DecodeInt32(dst []int32, src []byte) ([]int32, error) { + if (len(src) % 4) != 0 { + return dst, encoding.ErrDecodeInvalidInputSize(e, "INT32", len(src)) + } + buf := resize(unsafecast.Slice[byte](dst), len(src)) + decodeInt32(buf, src) + return unsafecast.Slice[int32](buf), nil +} + +func (e *Encoding) EncodeInt64(dst []byte, src []int64) ([]byte, error) { + dst = resize(dst, 8*len(src)) + encodeInt64(dst, unsafecast.Slice[byte](src)) + return dst, nil +} + +func (e *Encoding) DecodeInt64(dst []int64, src []byte) ([]int64, error) { + if (len(src) % 8) != 0 { + return dst, encoding.ErrDecodeInvalidInputSize(e, "INT64", len(src)) + } + buf := resize(unsafecast.Slice[byte](dst), len(src)) + decodeInt64(buf, src) + return unsafecast.Slice[int64](buf), nil +} + +func (e *Encoding) EncodeFixedLenByteArray(dst []byte, src []byte, size int) ([]byte, error) { + if size <= 0 || size > encoding.MaxFixedLenByteArraySize { + return dst[:0], encoding.Error(e, encoding.ErrInvalidArgument) + } + if (len(src) % size) != 0 { + return dst[:0], encoding.ErrEncodeInvalidInputSize(e, "FIXED_LEN_BYTE_ARRAY", len(src)) + } + dst = resize(dst, len(src)) + encodeFixedLenByteArray(dst, src, size) + return dst, nil +} + +func (e *Encoding) DecodeFixedLenByteArray(dst []byte, src []byte, size int) ([]byte, error) { + if size <= 0 || size > encoding.MaxFixedLenByteArraySize { + return dst, encoding.Error(e, encoding.ErrInvalidArgument) + } + if (len(src) % size) != 0 { + return dst, encoding.ErrDecodeInvalidInputSize(e, "FIXED_LEN_BYTE_ARRAY", len(src)) + } + dst = resize(dst, len(src)) + decodeFixedLenByteArray(dst, src, size) + return dst, nil +} + +func resize(buf []byte, size int) []byte { + if cap(buf) < size { + buf = make([]byte, size, 2*size) + } else { + buf = buf[:size] + } + return buf +} diff --git a/vendor/github.com/parquet-go/parquet-go/encoding/bytestreamsplit/bytestreamsplit_amd64.go b/vendor/github.com/parquet-go/parquet-go/encoding/bytestreamsplit/bytestreamsplit_amd64.go new file mode 100644 index 000000000..f8fdf51f8 --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/encoding/bytestreamsplit/bytestreamsplit_amd64.go @@ -0,0 +1,43 @@ +//go:build !purego + +package bytestreamsplit + +import ( + "golang.org/x/sys/cpu" +) + +var encodeFloatHasAVX512 = cpu.X86.HasAVX512 && + cpu.X86.HasAVX512F && + cpu.X86.HasAVX512VL + +var encodeDoubleHasAVX512 = cpu.X86.HasAVX512 && + cpu.X86.HasAVX512F && + cpu.X86.HasAVX512VL && + cpu.X86.HasAVX512VBMI // VPERMB + +var decodeFloatHasAVX2 = cpu.X86.HasAVX2 + +var decodeDoubleHasAVX512 = cpu.X86.HasAVX512 && + cpu.X86.HasAVX512F && + cpu.X86.HasAVX512VL && + cpu.X86.HasAVX512VBMI // VPERMB + +//go:noescape +func encodeFloat(dst, src []byte) + +//go:noescape +func encodeDouble(dst, src []byte) + +//go:noescape +func decodeFloat(dst, src []byte) + +//go:noescape +func decodeDouble(dst, src []byte) + +func encodeInt32(dst, src []byte) { encodeFloat(dst, src) } + +func decodeInt32(dst, src []byte) { decodeFloat(dst, src) } + +func encodeInt64(dst, src []byte) { encodeDouble(dst, src) } + +func decodeInt64(dst, src []byte) { decodeDouble(dst, src) } diff --git a/vendor/github.com/parquet-go/parquet-go/encoding/bytestreamsplit/bytestreamsplit_amd64.s b/vendor/github.com/parquet-go/parquet-go/encoding/bytestreamsplit/bytestreamsplit_amd64.s new file mode 100644 index 000000000..b0c7622db --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/encoding/bytestreamsplit/bytestreamsplit_amd64.s @@ -0,0 +1,426 @@ + //go:build !purego + +#include "textflag.h" + +// This file contains optimizations of the BYTE_STREAM_SPLIT encoding using AVX2 +// and AVX512 (when available). +// +// The AVX2/512 instruction set comes with instructions to load memory from, or +// store memory at sparse locations called VPGATHER and VPSCATTER. VPGATHER was +// available in the AVX2 instruction set, VPSCATTER was introduced in AVX512 +// (when the AVX512_VBMI extension is supported). Gathering bytes are sparse +// memory locations is useful during the decoding process since we are +// recomposing 32 or 64 bit floating point values from 4 or 8 bytes dispatched +// in the input byte array. +// +// To either deconstruct or reconstruct floating point values, we need to +// reorder the bytes of each value. If we have 4 32 bit floats, we can permute +// their bytes so that the first one contains all the first bytes, the second +// contains all the second bytes, etc... The VPSHUFB instruction is used to +// perform the byte permutation, or the VPERMB instruction for 64 bit floats. +// +// We use different instructions because the VPSHUFB instruction works on two +// lanes of 16 bytes when used on YMM registers. 4 32 bit floats take 16 bytes, +// so a a YMM register can hold two lanes of 4 32 bit floats and the VPSHUFB +// can permute the two sets of values in a single invocation. For 64 bit floats +// we need to permute 8 values, which take 64 bytes and therefore need to be +// held in a ZMM register and apply permutations across the entire register, +// which is only possible using VPERMB. +// +// Technically we could use ZMM registers when working on 32 bit floats to work +// on 16 values per iteration. However, measurements indicated that the latency +// of VPGATHERDD/VPSCATTERDD on ZMM registers did not provide any improvements +// to the throughput of the algorithms, but working on more values increased the +// code complexity. Using YMM registers offered the best balance between +// performance and maintainability. +// +// At a high level the vectorized algorithms are the following: +// +// encoding +// -------- +// * Load a vector of data from the input buffer +// * Permute bytes, grouping bytes by index +// * Scatter bytes of the register to the output buffer +// +// decoding +// -------- +// * Gather sparse bytes from the input buffer +// * Permute bytes, reconstructing the original values +// * Store the vector in the output buffer +// +// When AVX instructions are not available, the functions fallback to scalar +// implementations of the algorithms. These yield much lower throughput, but +// performed 20-30% better than the code generated by the Go compiler. + +// func encodeFloat(dst, src []byte) +TEXT ·encodeFloat(SB), NOSPLIT, $0-48 + MOVQ src_base+24(FP), AX + MOVQ src_len+32(FP), BX + MOVQ dst_base+0(FP), DX + + MOVQ AX, CX + ADDQ BX, CX // end + SHRQ $2, BX // len + + CMPQ BX, $0 + JE done + + CMPB ·encodeFloatHasAVX512(SB), $0 + JE loop1x4 + + CMPQ BX, $8 + JB loop1x4 + + MOVQ CX, DI + SUBQ AX, DI + SHRQ $5, DI + SHLQ $5, DI + ADDQ AX, DI + + VMOVDQU32 shuffle8x4<>(SB), Y0 + VPBROADCASTD BX, Y2 + VPMULLD scale8x4<>(SB), Y2, Y2 + VPADDD offset8x4<>(SB), Y2, Y2 +loop8x4: + KXORQ K1, K1, K1 + KNOTQ K1, K1 + + VMOVDQU32 (AX), Y1 + VPSHUFB Y0, Y1, Y1 + VPSCATTERDD Y1, K1, (DX)(Y2*1) + + ADDQ $32, AX + ADDQ $8, DX + CMPQ AX, DI + JNE loop8x4 + VZEROUPPER + + CMPQ AX, CX + JE done +loop1x4: + MOVL (AX), SI + MOVQ DX, DI + + MOVB SI, (DI) + SHRL $8, SI + ADDQ BX, DI + + MOVB SI, (DI) + SHRL $8, SI + ADDQ BX, DI + + MOVB SI, (DI) + SHRL $8, SI + ADDQ BX, DI + + MOVB SI, (DI) + + ADDQ $4, AX + INCQ DX + CMPQ AX, CX + JB loop1x4 +done: + RET + +// func encodeDouble(dst, src []byte) +TEXT ·encodeDouble(SB), NOSPLIT, $0-48 + MOVQ src_base+24(FP), AX + MOVQ src_len+32(FP), BX + MOVQ dst_base+0(FP), DX + + MOVQ AX, CX + ADDQ BX, CX + SHRQ $3, BX + + CMPQ BX, $0 + JE done + + CMPB ·encodeDoubleHasAVX512(SB), $0 + JE loop1x8 + + CMPQ BX, $8 + JB loop1x8 + + MOVQ CX, DI + SUBQ AX, DI + SHRQ $6, DI + SHLQ $6, DI + ADDQ AX, DI + + VMOVDQU64 shuffle8x8<>(SB), Z0 + VPBROADCASTQ BX, Z2 + VPMULLQ scale8x8<>(SB), Z2, Z2 +loop8x8: + KXORQ K1, K1, K1 + KNOTQ K1, K1 + + VMOVDQU64 (AX), Z1 + VPERMB Z1, Z0, Z1 + VPSCATTERQQ Z1, K1, (DX)(Z2*1) + + ADDQ $64, AX + ADDQ $8, DX + CMPQ AX, DI + JNE loop8x8 + VZEROUPPER + + CMPQ AX, CX + JE done +loop1x8: + MOVQ (AX), SI + MOVQ DX, DI + + MOVB SI, (DI) + SHRQ $8, SI + ADDQ BX, DI + + MOVB SI, (DI) + SHRQ $8, SI + ADDQ BX, DI + + MOVB SI, (DI) + SHRQ $8, SI + ADDQ BX, DI + + MOVB SI, (DI) + SHRQ $8, SI + ADDQ BX, DI + + MOVB SI, (DI) + SHRQ $8, SI + ADDQ BX, DI + + MOVB SI, (DI) + SHRQ $8, SI + ADDQ BX, DI + + MOVB SI, (DI) + SHRQ $8, SI + ADDQ BX, DI + + MOVB SI, (DI) + + ADDQ $8, AX + INCQ DX + CMPQ AX, CX + JB loop1x8 +done: + RET + +// func decodeFloat(dst, src []byte) +TEXT ·decodeFloat(SB), NOSPLIT, $0-48 + MOVQ dst_base+0(FP), AX + MOVQ dst_len+8(FP), BX + MOVQ src_base+24(FP), DX + + MOVQ AX, CX + ADDQ BX, CX // end + SHRQ $2, BX // len + + CMPQ BX, $0 + JE done + + CMPB ·decodeFloatHasAVX2(SB), $0 + JE loop1x4 + + CMPQ BX, $8 + JB loop1x4 + + MOVQ CX, DI + SUBQ AX, DI + SHRQ $5, DI + SHLQ $5, DI + ADDQ AX, DI + + MOVQ $0xFFFFFFFF, SI + MOVQ BX, X5 + MOVQ SI, X6 + VMOVDQU shuffle8x4<>(SB), Y0 + VPBROADCASTD X5, Y2 + VPBROADCASTD X6, Y3 + VPMULLD scale8x4<>(SB), Y2, Y2 + VPADDD offset8x4<>(SB), Y2, Y2 + VMOVDQU Y3, Y4 +loop8x4: + VPGATHERDD Y4, (DX)(Y2*1), Y1 + VPSHUFB Y0, Y1, Y1 + VMOVDQU Y1, (AX) + VMOVDQU Y3, Y4 + + ADDQ $32, AX + ADDQ $8, DX + CMPQ AX, DI + JNE loop8x4 + VZEROUPPER + + CMPQ AX, CX + JE done +loop1x4: + MOVQ DX, DI + MOVBLZX (DI), R8 + ADDQ BX, DI + MOVBLZX (DI), R9 + ADDQ BX, DI + MOVBLZX (DI), R10 + ADDQ BX, DI + MOVBLZX (DI), R11 + + SHLL $8, R9 + SHLL $16, R10 + SHLL $24, R11 + + ORL R9, R8 + ORL R10, R8 + ORL R11, R8 + + MOVL R8, (AX) + + ADDQ $4, AX + INCQ DX + CMPQ AX, CX + JB loop1x4 +done: + RET + +// func decodeDouble(dst, src []byte) +TEXT ·decodeDouble(SB), NOSPLIT, $0-48 + MOVQ dst_base+0(FP), AX + MOVQ dst_len+8(FP), BX + MOVQ src_base+24(FP), DX + + MOVQ AX, CX + ADDQ BX, CX + SHRQ $3, BX + + CMPQ BX, $0 + JE done + + CMPB ·decodeDoubleHasAVX512(SB), $0 + JE loop1x8 + + CMPQ BX, $8 + JB loop1x8 + + MOVQ CX, DI + SUBQ AX, DI + SHRQ $6, DI + SHLQ $6, DI + ADDQ AX, DI + + VMOVDQU64 shuffle8x8<>(SB), Z0 + VPBROADCASTQ BX, Z2 + VPMULLQ scale8x8<>(SB), Z2, Z2 +loop8x8: + KXORQ K1, K1, K1 + KNOTQ K1, K1 + + VPGATHERQQ (DX)(Z2*1), K1, Z1 + VPERMB Z1, Z0, Z1 + VMOVDQU64 Z1, (AX) + + ADDQ $64, AX + ADDQ $8, DX + CMPQ AX, DI + JNE loop8x8 + VZEROUPPER + + CMPQ AX, CX + JE done +loop1x8: + MOVQ DX, DI + XORQ R12, R12 + + MOVBQZX (DI), R8 + ADDQ BX, DI + MOVBQZX (DI), R9 + ADDQ BX, DI + MOVBQZX (DI), R10 + ADDQ BX, DI + MOVBQZX (DI), R11 + ADDQ BX, DI + + SHLQ $8, R9 + SHLQ $16, R10 + SHLQ $24, R11 + + ORQ R8, R12 + ORQ R9, R12 + ORQ R10, R12 + ORQ R11, R12 + + MOVBQZX (DI), R8 + ADDQ BX, DI + MOVBQZX (DI), R9 + ADDQ BX, DI + MOVBQZX (DI), R10 + ADDQ BX, DI + MOVBQZX (DI), R11 + + SHLQ $32, R8 + SHLQ $40, R9 + SHLQ $48, R10 + SHLQ $56, R11 + + ORQ R8, R12 + ORQ R9, R12 + ORQ R10, R12 + ORQ R11, R12 + + MOVQ R12, (AX) + + ADDQ $8, AX + INCQ DX + CMPQ AX, CX + JB loop1x8 +done: + RET + +GLOBL scale8x4<>(SB), RODATA|NOPTR, $32 +DATA scale8x4<>+0(SB)/4, $0 +DATA scale8x4<>+4(SB)/4, $1 +DATA scale8x4<>+8(SB)/4, $2 +DATA scale8x4<>+12(SB)/4, $3 +DATA scale8x4<>+16(SB)/4, $0 +DATA scale8x4<>+20(SB)/4, $1 +DATA scale8x4<>+24(SB)/4, $2 +DATA scale8x4<>+28(SB)/4, $3 + +GLOBL offset8x4<>(SB), RODATA|NOPTR, $32 +DATA offset8x4<>+0(SB)/4, $0 +DATA offset8x4<>+4(SB)/4, $0 +DATA offset8x4<>+8(SB)/4, $0 +DATA offset8x4<>+12(SB)/4, $0 +DATA offset8x4<>+16(SB)/4, $4 +DATA offset8x4<>+20(SB)/4, $4 +DATA offset8x4<>+24(SB)/4, $4 +DATA offset8x4<>+28(SB)/4, $4 + +GLOBL shuffle8x4<>(SB), RODATA|NOPTR, $32 +DATA shuffle8x4<>+0(SB)/4, $0x0C080400 +DATA shuffle8x4<>+4(SB)/4, $0x0D090501 +DATA shuffle8x4<>+8(SB)/4, $0x0E0A0602 +DATA shuffle8x4<>+12(SB)/4, $0x0F0B0703 +DATA shuffle8x4<>+16(SB)/4, $0x0C080400 +DATA shuffle8x4<>+20(SB)/4, $0x0D090501 +DATA shuffle8x4<>+24(SB)/4, $0x0E0A0602 +DATA shuffle8x4<>+28(SB)/4, $0x0F0B0703 + +GLOBL scale8x8<>(SB), RODATA|NOPTR, $64 +DATA scale8x8<>+0(SB)/8, $0 +DATA scale8x8<>+8(SB)/8, $1 +DATA scale8x8<>+16(SB)/8, $2 +DATA scale8x8<>+24(SB)/8, $3 +DATA scale8x8<>+32(SB)/8, $4 +DATA scale8x8<>+40(SB)/8, $5 +DATA scale8x8<>+48(SB)/8, $6 +DATA scale8x8<>+56(SB)/8, $7 + +GLOBL shuffle8x8<>(SB), RODATA|NOPTR, $64 +DATA shuffle8x8<>+0(SB)/8, $0x3830282018100800 +DATA shuffle8x8<>+8(SB)/8, $0x3931292119110901 +DATA shuffle8x8<>+16(SB)/8, $0x3A322A221A120A02 +DATA shuffle8x8<>+24(SB)/8, $0x3B332B231B130B03 +DATA shuffle8x8<>+32(SB)/8, $0x3C342C241C140C04 +DATA shuffle8x8<>+40(SB)/8, $0x3D352D251D150D05 +DATA shuffle8x8<>+48(SB)/8, $0x3E362E261E160E06 +DATA shuffle8x8<>+56(SB)/8, $0x3F372F271F170F07 diff --git a/vendor/github.com/parquet-go/parquet-go/encoding/bytestreamsplit/bytestreamsplit_fixedlen.go b/vendor/github.com/parquet-go/parquet-go/encoding/bytestreamsplit/bytestreamsplit_fixedlen.go new file mode 100644 index 000000000..528180f66 --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/encoding/bytestreamsplit/bytestreamsplit_fixedlen.go @@ -0,0 +1,21 @@ +package bytestreamsplit + +func encodeFixedLenByteArray(dst, src []byte, size int) { + n := len(src) / size + for s := range size { + stream := dst[s*n : (s+1)*n] + for i := range n { + stream[i] = src[i*size+s] + } + } +} + +func decodeFixedLenByteArray(dst, src []byte, size int) { + n := len(src) / size + for s := range size { + stream := src[s*n : (s+1)*n] + for i := range n { + dst[i*size+s] = stream[i] + } + } +} diff --git a/vendor/github.com/parquet-go/parquet-go/encoding/bytestreamsplit/bytestreamsplit_purego.go b/vendor/github.com/parquet-go/parquet-go/encoding/bytestreamsplit/bytestreamsplit_purego.go new file mode 100644 index 000000000..62e595a5e --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/encoding/bytestreamsplit/bytestreamsplit_purego.go @@ -0,0 +1,99 @@ +//go:build purego || !amd64 + +package bytestreamsplit + +import "github.com/parquet-go/bitpack/unsafecast" + +func encodeFloat(dst, src []byte) { + n := len(src) / 4 + b0 := dst[0*n : 1*n] + b1 := dst[1*n : 2*n] + b2 := dst[2*n : 3*n] + b3 := dst[3*n : 4*n] + + for i, v := range unsafecast.Slice[uint32](src) { + b0[i] = byte(v >> 0) + b1[i] = byte(v >> 8) + b2[i] = byte(v >> 16) + b3[i] = byte(v >> 24) + } +} + +func encodeDouble(dst, src []byte) { + n := len(src) / 8 + b0 := dst[0*n : 1*n] + b1 := dst[1*n : 2*n] + b2 := dst[2*n : 3*n] + b3 := dst[3*n : 4*n] + b4 := dst[4*n : 5*n] + b5 := dst[5*n : 6*n] + b6 := dst[6*n : 7*n] + b7 := dst[7*n : 8*n] + + for i, v := range unsafecast.Slice[uint64](src) { + b0[i] = byte(v >> 0) + b1[i] = byte(v >> 8) + b2[i] = byte(v >> 16) + b3[i] = byte(v >> 24) + b4[i] = byte(v >> 32) + b5[i] = byte(v >> 40) + b6[i] = byte(v >> 48) + b7[i] = byte(v >> 56) + } +} + +func decodeFloat(dst, src []byte) { + n := len(src) / 4 + b0 := src[0*n : 1*n] + b1 := src[1*n : 2*n] + b2 := src[2*n : 3*n] + b3 := src[3*n : 4*n] + + dst32 := unsafecast.Slice[uint32](dst) + for i := range dst32 { + dst32[i] = uint32(b0[i]) | + uint32(b1[i])<<8 | + uint32(b2[i])<<16 | + uint32(b3[i])<<24 + } +} + +func decodeDouble(dst, src []byte) { + n := len(src) / 8 + b0 := src[0*n : 1*n] + b1 := src[1*n : 2*n] + b2 := src[2*n : 3*n] + b3 := src[3*n : 4*n] + b4 := src[4*n : 5*n] + b5 := src[5*n : 6*n] + b6 := src[6*n : 7*n] + b7 := src[7*n : 8*n] + + dst64 := unsafecast.Slice[uint64](dst) + for i := range dst64 { + dst64[i] = uint64(b0[i]) | + uint64(b1[i])<<8 | + uint64(b2[i])<<16 | + uint64(b3[i])<<24 | + uint64(b4[i])<<32 | + uint64(b5[i])<<40 | + uint64(b6[i])<<48 | + uint64(b7[i])<<56 + } +} + +func encodeInt32(dst, src []byte) { + encodeFloat(dst, src) +} + +func decodeInt32(dst, src []byte) { + decodeFloat(dst, src) +} + +func encodeInt64(dst, src []byte) { + encodeDouble(dst, src) +} + +func decodeInt64(dst, src []byte) { + decodeDouble(dst, src) +} diff --git a/vendor/github.com/parquet-go/parquet-go/encoding/delta/binary_packed.go b/vendor/github.com/parquet-go/parquet-go/encoding/delta/binary_packed.go new file mode 100644 index 000000000..9e6f8c9b2 --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/encoding/delta/binary_packed.go @@ -0,0 +1,488 @@ +package delta + +import ( + "encoding/binary" + "fmt" + "io" + "math" + "math/bits" + + "github.com/parquet-go/bitpack" + "github.com/parquet-go/bitpack/unsafecast" + "github.com/parquet-go/parquet-go/encoding" + "github.com/parquet-go/parquet-go/format" +) + +type BinaryPackedEncoding struct { + encoding.NotSupported +} + +func (e *BinaryPackedEncoding) String() string { + return "DELTA_BINARY_PACKED" +} + +func (e *BinaryPackedEncoding) Encoding() format.Encoding { + return format.DeltaBinaryPacked +} + +func (e *BinaryPackedEncoding) EncodeInt32(dst []byte, src []int32) ([]byte, error) { + return encodeInt32(dst[:0], src), nil +} + +func (e *BinaryPackedEncoding) EncodeInt64(dst []byte, src []int64) ([]byte, error) { + return encodeInt64(dst[:0], src), nil +} + +func (e *BinaryPackedEncoding) DecodeInt32(dst []int32, src []byte) ([]int32, error) { + buf := unsafecast.Slice[byte](dst) + buf, _, err := decodeInt32(buf[:0], src) + return unsafecast.Slice[int32](buf), e.wrap(err) +} + +func (e *BinaryPackedEncoding) DecodeInt64(dst []int64, src []byte) ([]int64, error) { + buf := unsafecast.Slice[byte](dst) + buf, _, err := decodeInt64(buf[:0], src) + return unsafecast.Slice[int64](buf), e.wrap(err) +} + +func (e *BinaryPackedEncoding) wrap(err error) error { + if err != nil { + err = encoding.Error(e, err) + } + return err +} + +const ( + blockSize = 128 + numMiniBlocks = 4 + miniBlockSize = blockSize / numMiniBlocks + // The parquet spec does not enforce a limit to the block size, but we need + // one otherwise invalid inputs may result in unbounded memory allocations. + // + // 65K+ values should be enough for any valid use case. + maxSupportedBlockSize = 65536 + + maxHeaderLength32 = 4 * binary.MaxVarintLen64 + maxMiniBlockLength32 = binary.MaxVarintLen64 + numMiniBlocks + (4 * blockSize) + + maxHeaderLength64 = 8 * binary.MaxVarintLen64 + maxMiniBlockLength64 = binary.MaxVarintLen64 + numMiniBlocks + (8 * blockSize) +) + +var ( + encodeInt32 = encodeInt32Default + encodeInt64 = encodeInt64Default +) + +func encodeInt32Default(dst []byte, src []int32) []byte { + totalValues := len(src) + firstValue := int32(0) + if totalValues > 0 { + firstValue = src[0] + } + + n := len(dst) + dst = resize(dst, n+maxHeaderLength32) + dst = dst[:n+encodeBinaryPackedHeader(dst[n:], blockSize, numMiniBlocks, totalValues, int64(firstValue))] + + if totalValues < 2 { + return dst + } + + lastValue := firstValue + for i := 1; i < len(src); i += blockSize { + block := [blockSize]int32{} + blockLength := copy(block[:], src[i:]) + + lastValue = blockDeltaInt32(&block, lastValue) + minDelta := blockMinInt32(&block) + blockSubInt32(&block, minDelta) + blockClearInt32(&block, blockLength) + + bitWidths := [numMiniBlocks]byte{} + blockBitWidthsInt32(&bitWidths, &block) + + n := len(dst) + dst = resize(dst, n+maxMiniBlockLength32+4) + n += encodeBlockHeader(dst[n:], int64(minDelta), bitWidths) + + for i, bitWidth := range bitWidths { + if bitWidth != 0 { + miniBlock := (*[miniBlockSize]int32)(block[i*miniBlockSize:]) + encodeMiniBlockInt32(dst[n:], miniBlock, uint(bitWidth)) + n += (miniBlockSize * int(bitWidth)) / 8 + } + } + + dst = dst[:n] + } + + return dst +} + +func encodeInt64Default(dst []byte, src []int64) []byte { + totalValues := len(src) + firstValue := int64(0) + if totalValues > 0 { + firstValue = src[0] + } + + n := len(dst) + dst = resize(dst, n+maxHeaderLength64) + dst = dst[:n+encodeBinaryPackedHeader(dst[n:], blockSize, numMiniBlocks, totalValues, firstValue)] + + if totalValues < 2 { + return dst + } + + lastValue := firstValue + for i := 1; i < len(src); i += blockSize { + block := [blockSize]int64{} + blockLength := copy(block[:], src[i:]) + + lastValue = blockDeltaInt64(&block, lastValue) + minDelta := blockMinInt64(&block) + blockSubInt64(&block, minDelta) + blockClearInt64(&block, blockLength) + + bitWidths := [numMiniBlocks]byte{} + blockBitWidthsInt64(&bitWidths, &block) + + n := len(dst) + dst = resize(dst, n+maxMiniBlockLength64+8) + n += encodeBlockHeader(dst[n:], minDelta, bitWidths) + + for i, bitWidth := range bitWidths { + if bitWidth != 0 { + miniBlock := (*[miniBlockSize]int64)(block[i*miniBlockSize:]) + encodeMiniBlockInt64(dst[n:], miniBlock, uint(bitWidth)) + n += (miniBlockSize * int(bitWidth)) / 8 + } + } + + dst = dst[:n] + } + + return dst +} + +func encodeBinaryPackedHeader(dst []byte, blockSize, numMiniBlocks, totalValues int, firstValue int64) (n int) { + n += binary.PutUvarint(dst[n:], uint64(blockSize)) + n += binary.PutUvarint(dst[n:], uint64(numMiniBlocks)) + n += binary.PutUvarint(dst[n:], uint64(totalValues)) + n += binary.PutVarint(dst[n:], firstValue) + return n +} + +func encodeBlockHeader(dst []byte, minDelta int64, bitWidths [numMiniBlocks]byte) (n int) { + n += binary.PutVarint(dst, int64(minDelta)) + n += copy(dst[n:], bitWidths[:]) + return n +} + +func blockClearInt32(block *[blockSize]int32, blockLength int) { + if blockLength < blockSize { + clear := block[blockLength:] + for i := range clear { + clear[i] = 0 + } + } +} + +func blockDeltaInt32(block *[blockSize]int32, lastValue int32) int32 { + for i, v := range block { + block[i], lastValue = v-lastValue, v + } + return lastValue +} + +func blockMinInt32(block *[blockSize]int32) int32 { + min := block[0] + for _, v := range block[1:] { + if v < min { + min = v + } + } + return min +} + +func blockSubInt32(block *[blockSize]int32, value int32) { + for i := range block { + block[i] -= value + } +} + +func blockBitWidthsInt32(bitWidths *[numMiniBlocks]byte, block *[blockSize]int32) { + for i := range bitWidths { + j := (i + 0) * miniBlockSize + k := (i + 1) * miniBlockSize + bitWidth := 0 + + for _, v := range block[j:k] { + if n := bits.Len32(uint32(v)); n > bitWidth { + bitWidth = n + } + } + + bitWidths[i] = byte(bitWidth) + } +} + +func blockClearInt64(block *[blockSize]int64, blockLength int) { + if blockLength < blockSize { + clear := block[blockLength:] + for i := range clear { + clear[i] = 0 + } + } +} + +func blockDeltaInt64(block *[blockSize]int64, lastValue int64) int64 { + for i, v := range block { + block[i], lastValue = v-lastValue, v + } + return lastValue +} + +func blockMinInt64(block *[blockSize]int64) int64 { + min := block[0] + for _, v := range block[1:] { + if v < min { + min = v + } + } + return min +} + +func blockSubInt64(block *[blockSize]int64, value int64) { + for i := range block { + block[i] -= value + } +} + +func blockBitWidthsInt64(bitWidths *[numMiniBlocks]byte, block *[blockSize]int64) { + for i := range bitWidths { + j := (i + 0) * miniBlockSize + k := (i + 1) * miniBlockSize + bitWidth := 0 + + for _, v := range block[j:k] { + if n := bits.Len64(uint64(v)); n > bitWidth { + bitWidth = n + } + } + + bitWidths[i] = byte(bitWidth) + } +} + +func decodeInt32(dst, src []byte) ([]byte, []byte, error) { + blockSize, numMiniBlocks, totalValues, firstValue, src, err := decodeBinaryPackedHeader(src) + if err != nil { + return dst, src, err + } + if totalValues == 0 { + return dst, src, nil + } + if firstValue < math.MinInt32 || firstValue > math.MaxInt32 { + return dst, src, fmt.Errorf("first value out of range: %d", firstValue) + } + + writeOffset := len(dst) + dst = resize(dst, len(dst)+4*totalValues) + out := unsafecast.Slice[int32](dst) + out[writeOffset] = int32(firstValue) + writeOffset++ + totalValues-- + lastValue := int32(firstValue) + numValuesInMiniBlock := blockSize / numMiniBlocks + + const padding = 16 + miniBlockTemp := make([]byte, 256+padding) + + for totalValues > 0 && len(src) > 0 { + var minDelta int64 + var bitWidths []byte + minDelta, bitWidths, src, err = decodeBinaryPackedBlock(src, numMiniBlocks) + if err != nil { + return dst, src, err + } + + blockOffset := writeOffset + + for _, bitWidth := range bitWidths { + n := min(numValuesInMiniBlock, totalValues) + if bitWidth != 0 { + miniBlockSize := (numValuesInMiniBlock * int(bitWidth)) / 8 + miniBlockData := src + if miniBlockSize <= len(src) { + miniBlockData = miniBlockData[:miniBlockSize] + } + src = src[len(miniBlockData):] + if cap(miniBlockData) < miniBlockSize+bitpack.PaddingInt32 { + miniBlockTemp = resize(miniBlockTemp[:0], miniBlockSize+bitpack.PaddingInt32) + miniBlockData = miniBlockTemp[:copy(miniBlockTemp, miniBlockData)] + } + miniBlockData = miniBlockData[:miniBlockSize] + bitpack.Unpack(out[writeOffset:writeOffset+n], miniBlockData, uint(bitWidth)) + } + writeOffset += n + totalValues -= n + if totalValues == 0 { + break + } + } + + lastValue = decodeBlockInt32(out[blockOffset:writeOffset], int32(minDelta), lastValue) + } + + if totalValues > 0 { + return dst, src, fmt.Errorf("%d missing values: %w", totalValues, io.ErrUnexpectedEOF) + } + + return dst, src, nil +} + +func decodeInt64(dst, src []byte) ([]byte, []byte, error) { + blockSize, numMiniBlocks, totalValues, firstValue, src, err := decodeBinaryPackedHeader(src) + if err != nil { + return dst, src, err + } + if totalValues == 0 { + return dst, src, nil + } + + writeOffset := len(dst) + dst = resize(dst, len(dst)+8*totalValues) + out := unsafecast.Slice[int64](dst) + out[writeOffset] = firstValue + writeOffset++ + totalValues-- + lastValue := firstValue + numValuesInMiniBlock := blockSize / numMiniBlocks + + const padding = 16 + miniBlockTemp := make([]byte, 512+padding) + + for totalValues > 0 && len(src) > 0 { + var minDelta int64 + var bitWidths []byte + minDelta, bitWidths, src, err = decodeBinaryPackedBlock(src, numMiniBlocks) + if err != nil { + return dst, src, err + } + blockOffset := writeOffset + + for _, bitWidth := range bitWidths { + n := min(numValuesInMiniBlock, totalValues) + if bitWidth != 0 { + miniBlockSize := (numValuesInMiniBlock * int(bitWidth)) / 8 + miniBlockData := src + if miniBlockSize <= len(src) { + miniBlockData = src[:miniBlockSize] + } + src = src[len(miniBlockData):] + if len(miniBlockData) < miniBlockSize+bitpack.PaddingInt64 { + miniBlockTemp = resize(miniBlockTemp[:0], miniBlockSize+bitpack.PaddingInt64) + miniBlockData = miniBlockTemp[:copy(miniBlockTemp, miniBlockData)] + } + miniBlockData = miniBlockData[:miniBlockSize] + bitpack.Unpack(out[writeOffset:writeOffset+n], miniBlockData, uint(bitWidth)) + } + writeOffset += n + totalValues -= n + if totalValues == 0 { + break + } + } + + lastValue = decodeBlockInt64(out[blockOffset:writeOffset], minDelta, lastValue) + } + + if totalValues > 0 { + return dst, src, fmt.Errorf("%d missing values: %w", totalValues, io.ErrUnexpectedEOF) + } + + return dst, src, nil +} + +func decodeBinaryPackedHeader(src []byte) (blockSize, numMiniBlocks, totalValues int, firstValue int64, next []byte, err error) { + u := uint64(0) + n := 0 + i := 0 + + if u, n, err = decodeUvarint(src[i:], "block size"); err != nil { + return + } + i += n + blockSize = int(u) + + if u, n, err = decodeUvarint(src[i:], "number of mini-blocks"); err != nil { + return + } + i += n + numMiniBlocks = int(u) + + if u, n, err = decodeUvarint(src[i:], "total values"); err != nil { + return + } + i += n + totalValues = int(u) + + if firstValue, n, err = decodeVarint(src[i:], "first value"); err != nil { + return + } + i += n + + if numMiniBlocks == 0 { + err = fmt.Errorf("invalid number of mini block (%d)", numMiniBlocks) + } else if (blockSize <= 0) || (blockSize%128) != 0 { + err = fmt.Errorf("invalid block size is not a multiple of 128 (%d)", blockSize) + } else if blockSize > maxSupportedBlockSize { + err = fmt.Errorf("invalid block size is too large (%d)", blockSize) + } else if miniBlockSize := blockSize / numMiniBlocks; (numMiniBlocks <= 0) || (miniBlockSize%32) != 0 { + err = fmt.Errorf("invalid mini block size is not a multiple of 32 (%d)", miniBlockSize) + } else if totalValues < 0 { + err = fmt.Errorf("invalid total number of values is negative (%d)", totalValues) + } else if totalValues > math.MaxInt32 { + err = fmt.Errorf("too many values: %d", totalValues) + } + + return blockSize, numMiniBlocks, totalValues, firstValue, src[i:], err +} + +func decodeBinaryPackedBlock(src []byte, numMiniBlocks int) (minDelta int64, bitWidths, next []byte, err error) { + minDelta, n, err := decodeVarint(src, "min delta") + if err != nil { + return 0, nil, src, err + } + src = src[n:] + if len(src) < numMiniBlocks { + bitWidths, next = src, nil + } else { + bitWidths, next = src[:numMiniBlocks], src[numMiniBlocks:] + } + return minDelta, bitWidths, next, nil +} + +func decodeUvarint(buf []byte, what string) (u uint64, n int, err error) { + u, n = binary.Uvarint(buf) + if n == 0 { + return 0, 0, fmt.Errorf("decoding %s: %w", what, io.ErrUnexpectedEOF) + } + if n < 0 { + return 0, 0, fmt.Errorf("overflow decoding %s (read %d/%d bytes)", what, -n, len(buf)) + } + return u, n, nil +} + +func decodeVarint(buf []byte, what string) (v int64, n int, err error) { + v, n = binary.Varint(buf) + if n == 0 { + return 0, 0, fmt.Errorf("decoding %s: %w", what, io.ErrUnexpectedEOF) + } + if n < 0 { + return 0, 0, fmt.Errorf("overflow decoding %s (read %d/%d bytes)", what, -n, len(buf)) + } + return v, n, nil +} diff --git a/vendor/github.com/parquet-go/parquet-go/encoding/delta/binary_packed_amd64.go b/vendor/github.com/parquet-go/parquet-go/encoding/delta/binary_packed_amd64.go new file mode 100644 index 000000000..a466e5b9a --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/encoding/delta/binary_packed_amd64.go @@ -0,0 +1,256 @@ +//go:build !purego + +package delta + +import ( + "github.com/parquet-go/bitpack/unsafecast" + "golang.org/x/sys/cpu" +) + +func init() { + if cpu.X86.HasAVX2 { + encodeInt32 = encodeInt32AVX2 + encodeInt64 = encodeInt64AVX2 + } +} + +//go:noescape +func blockDeltaInt32AVX2(block *[blockSize]int32, lastValue int32) int32 + +//go:noescape +func blockMinInt32AVX2(block *[blockSize]int32) int32 + +//go:noescape +func blockSubInt32AVX2(block *[blockSize]int32, value int32) + +//go:noescape +func blockBitWidthsInt32AVX2(bitWidths *[numMiniBlocks]byte, block *[blockSize]int32) + +//go:noescape +func encodeMiniBlockInt32Default(dst *byte, src *[miniBlockSize]int32, bitWidth uint) + +//go:noescape +func encodeMiniBlockInt32x1bitAVX2(dst *byte, src *[miniBlockSize]int32) + +//go:noescape +func encodeMiniBlockInt32x2bitsAVX2(dst *byte, src *[miniBlockSize]int32) + +//go:noescape +func encodeMiniBlockInt32x3to16bitsAVX2(dst *byte, src *[miniBlockSize]int32, bitWidth uint) + +//go:noescape +func encodeMiniBlockInt32x32bitsAVX2(dst *byte, src *[miniBlockSize]int32) + +func encodeMiniBlockInt32(dst []byte, src *[miniBlockSize]int32, bitWidth uint) { + encodeMiniBlockInt32Default(&dst[0], src, bitWidth) +} + +func encodeMiniBlockInt32AVX2(dst *byte, src *[miniBlockSize]int32, bitWidth uint) { + switch { + case bitWidth == 1: + encodeMiniBlockInt32x1bitAVX2(dst, src) + case bitWidth == 2: + encodeMiniBlockInt32x2bitsAVX2(dst, src) + case bitWidth == 32: + encodeMiniBlockInt32x32bitsAVX2(dst, src) + case bitWidth <= 16: + encodeMiniBlockInt32x3to16bitsAVX2(dst, src, bitWidth) + default: + encodeMiniBlockInt32Default(dst, src, bitWidth) + } +} + +func encodeInt32AVX2(dst []byte, src []int32) []byte { + totalValues := len(src) + firstValue := int32(0) + if totalValues > 0 { + firstValue = src[0] + } + + n := len(dst) + dst = resize(dst, n+maxHeaderLength32) + dst = dst[:n+encodeBinaryPackedHeader(dst[n:], blockSize, numMiniBlocks, totalValues, int64(firstValue))] + + if totalValues < 2 { + return dst + } + + lastValue := firstValue + for i := 1; i < len(src); i += blockSize { + block := [blockSize]int32{} + blockLength := copy(block[:], src[i:]) + + lastValue = blockDeltaInt32AVX2(&block, lastValue) + minDelta := blockMinInt32AVX2(&block) + blockSubInt32AVX2(&block, minDelta) + blockClearInt32(&block, blockLength) + + bitWidths := [numMiniBlocks]byte{} + blockBitWidthsInt32AVX2(&bitWidths, &block) + + n := len(dst) + dst = resize(dst, n+maxMiniBlockLength32+16) + n += encodeBlockHeader(dst[n:], int64(minDelta), bitWidths) + + for i, bitWidth := range bitWidths { + if bitWidth != 0 { + miniBlock := (*[miniBlockSize]int32)(block[i*miniBlockSize:]) + encodeMiniBlockInt32AVX2(&dst[n], miniBlock, uint(bitWidth)) + n += (miniBlockSize * int(bitWidth)) / 8 + } + } + + dst = dst[:n] + } + + return dst +} + +//go:noescape +func blockDeltaInt64AVX2(block *[blockSize]int64, lastValue int64) int64 + +//go:noescape +func blockMinInt64AVX2(block *[blockSize]int64) int64 + +//go:noescape +func blockSubInt64AVX2(block *[blockSize]int64, value int64) + +//go:noescape +func blockBitWidthsInt64AVX2(bitWidths *[numMiniBlocks]byte, block *[blockSize]int64) + +//go:noescape +func encodeMiniBlockInt64Default(dst *byte, src *[miniBlockSize]int64, bitWidth uint) + +//go:noescape +func encodeMiniBlockInt64x1bitAVX2(dst *byte, src *[miniBlockSize]int64) + +//go:noescape +func encodeMiniBlockInt64x2bitsAVX2(dst *byte, src *[miniBlockSize]int64) + +//go:noescape +func encodeMiniBlockInt64x64bitsAVX2(dst *byte, src *[miniBlockSize]int64) + +func encodeMiniBlockInt64(dst []byte, src *[miniBlockSize]int64, bitWidth uint) { + encodeMiniBlockInt64Default(&dst[0], src, bitWidth) +} + +func encodeMiniBlockInt64AVX2(dst *byte, src *[miniBlockSize]int64, bitWidth uint) { + switch { + case bitWidth == 1: + encodeMiniBlockInt64x1bitAVX2(dst, src) + case bitWidth == 2: + encodeMiniBlockInt64x2bitsAVX2(dst, src) + case bitWidth == 64: + encodeMiniBlockInt64x64bitsAVX2(dst, src) + default: + encodeMiniBlockInt64Default(dst, src, bitWidth) + } +} + +func encodeInt64AVX2(dst []byte, src []int64) []byte { + totalValues := len(src) + firstValue := int64(0) + if totalValues > 0 { + firstValue = src[0] + } + + n := len(dst) + dst = resize(dst, n+maxHeaderLength64) + dst = dst[:n+encodeBinaryPackedHeader(dst[n:], blockSize, numMiniBlocks, totalValues, int64(firstValue))] + + if totalValues < 2 { + return dst + } + + lastValue := firstValue + for i := 1; i < len(src); i += blockSize { + block := [blockSize]int64{} + blockLength := copy(block[:], src[i:]) + + lastValue = blockDeltaInt64AVX2(&block, lastValue) + minDelta := blockMinInt64AVX2(&block) + blockSubInt64AVX2(&block, minDelta) + blockClearInt64(&block, blockLength) + + bitWidths := [numMiniBlocks]byte{} + blockBitWidthsInt64AVX2(&bitWidths, &block) + + n := len(dst) + dst = resize(dst, n+maxMiniBlockLength64+16) + n += encodeBlockHeader(dst[n:], int64(minDelta), bitWidths) + + for i, bitWidth := range bitWidths { + if bitWidth != 0 { + miniBlock := (*[miniBlockSize]int64)(block[i*miniBlockSize:]) + encodeMiniBlockInt64AVX2(&dst[n], miniBlock, uint(bitWidth)) + n += (miniBlockSize * int(bitWidth)) / 8 + } + } + + dst = dst[:n] + } + + return dst +} + +//go:noescape +func decodeBlockInt32Default(dst []int32, minDelta, lastValue int32) int32 + +//go:noescape +func decodeBlockInt32AVX2(dst []int32, minDelta, lastValue int32) int32 + +func decodeBlockInt32(dst []int32, minDelta, lastValue int32) int32 { + switch { + case cpu.X86.HasAVX2: + return decodeBlockInt32AVX2(dst, minDelta, lastValue) + default: + return decodeBlockInt32Default(dst, minDelta, lastValue) + } +} + +//go:noescape +func decodeMiniBlockInt32Default(dst []int32, src []uint32, bitWidth uint) + +//go:noescape +func decodeMiniBlockInt32x1to16bitsAVX2(dst []int32, src []uint32, bitWidth uint) + +//go:noescape +func decodeMiniBlockInt32x17to26bitsAVX2(dst []int32, src []uint32, bitWidth uint) + +//go:noescape +func decodeMiniBlockInt32x27to31bitsAVX2(dst []int32, src []uint32, bitWidth uint) + +func decodeMiniBlockInt32(dst []int32, src []uint32, bitWidth uint) { + hasAVX2 := cpu.X86.HasAVX2 + switch { + case hasAVX2 && bitWidth <= 16: + decodeMiniBlockInt32x1to16bitsAVX2(dst, src, bitWidth) + case hasAVX2 && bitWidth <= 26: + decodeMiniBlockInt32x17to26bitsAVX2(dst, src, bitWidth) + case hasAVX2 && bitWidth <= 31: + decodeMiniBlockInt32x27to31bitsAVX2(dst, src, bitWidth) + case bitWidth == 32: + copy(dst, unsafecast.Slice[int32](src)) + default: + decodeMiniBlockInt32Default(dst, src, bitWidth) + } +} + +//go:noescape +func decodeBlockInt64Default(dst []int64, minDelta, lastValue int64) int64 + +func decodeBlockInt64(dst []int64, minDelta, lastValue int64) int64 { + return decodeBlockInt64Default(dst, minDelta, lastValue) +} + +//go:noescape +func decodeMiniBlockInt64Default(dst []int64, src []uint32, bitWidth uint) + +func decodeMiniBlockInt64(dst []int64, src []uint32, bitWidth uint) { + switch { + case bitWidth == 64: + copy(dst, unsafecast.Slice[int64](src)) + default: + decodeMiniBlockInt64Default(dst, src, bitWidth) + } +} diff --git a/vendor/github.com/parquet-go/parquet-go/encoding/delta/binary_packed_amd64.s b/vendor/github.com/parquet-go/parquet-go/encoding/delta/binary_packed_amd64.s new file mode 100644 index 000000000..08f80f090 --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/encoding/delta/binary_packed_amd64.s @@ -0,0 +1,920 @@ +//go:build !purego + +#include "textflag.h" + +#define blockSize 128 +#define numMiniBlocks 4 +#define miniBlockSize 32 + +// ----------------------------------------------------------------------------- +// 32 bits +// ----------------------------------------------------------------------------- + +#define deltaInt32AVX2x8(baseAddr) \ + VMOVDQU baseAddr, Y1 \ // [0,1,2,3,4,5,6,7] + VPERMD Y1, Y3, Y2 \ // [7,0,1,2,3,4,5,6] + VPBLENDD $1, Y0, Y2, Y2 \ // [x,0,1,2,3,4,5,6] + VPSUBD Y2, Y1, Y2 \ // [0,1,2,...] - [x,0,1,...] + VMOVDQU Y2, baseAddr \ + VPERMD Y1, Y3, Y0 + +// func blockDeltaInt32AVX2(block *[blockSize]int32, lastValue int32) int32 +TEXT ·blockDeltaInt32AVX2(SB), NOSPLIT, $0-20 + MOVQ block+0(FP), AX + MOVL 4*blockSize-4(AX), CX + MOVL CX, ret+16(FP) + + VPBROADCASTD lastValue+8(FP), Y0 + VMOVDQU ·rotateLeft32(SB), Y3 + + XORQ SI, SI +loop: + deltaInt32AVX2x8(0(AX)(SI*4)) + deltaInt32AVX2x8(32(AX)(SI*4)) + deltaInt32AVX2x8(64(AX)(SI*4)) + deltaInt32AVX2x8(96(AX)(SI*4)) + ADDQ $32, SI + CMPQ SI, $blockSize + JNE loop + VZEROUPPER + RET + +// func blockMinInt32AVX2(block *[blockSize]int32) int32 +TEXT ·blockMinInt32AVX2(SB), NOSPLIT, $0-12 + MOVQ block+0(FP), AX + VPBROADCASTD (AX), Y15 + + VPMINSD 0(AX), Y15, Y0 + VPMINSD 32(AX), Y15, Y1 + VPMINSD 64(AX), Y15, Y2 + VPMINSD 96(AX), Y15, Y3 + VPMINSD 128(AX), Y15, Y4 + VPMINSD 160(AX), Y15, Y5 + VPMINSD 192(AX), Y15, Y6 + VPMINSD 224(AX), Y15, Y7 + VPMINSD 256(AX), Y15, Y8 + VPMINSD 288(AX), Y15, Y9 + VPMINSD 320(AX), Y15, Y10 + VPMINSD 352(AX), Y15, Y11 + VPMINSD 384(AX), Y15, Y12 + VPMINSD 416(AX), Y15, Y13 + VPMINSD 448(AX), Y15, Y14 + VPMINSD 480(AX), Y15, Y15 + + VPMINSD Y1, Y0, Y0 + VPMINSD Y3, Y2, Y2 + VPMINSD Y5, Y4, Y4 + VPMINSD Y7, Y6, Y6 + VPMINSD Y9, Y8, Y8 + VPMINSD Y11, Y10, Y10 + VPMINSD Y13, Y12, Y12 + VPMINSD Y15, Y14, Y14 + + VPMINSD Y2, Y0, Y0 + VPMINSD Y6, Y4, Y4 + VPMINSD Y10, Y8, Y8 + VPMINSD Y14, Y12, Y12 + + VPMINSD Y4, Y0, Y0 + VPMINSD Y12, Y8, Y8 + + VPMINSD Y8, Y0, Y0 + + VPERM2I128 $1, Y0, Y0, Y1 + VPMINSD Y1, Y0, Y0 + + VPSHUFD $0b00011011, Y0, Y1 + VPMINSD Y1, Y0, Y0 + VZEROUPPER + + MOVQ X0, CX + MOVL CX, BX + SHRQ $32, CX + CMPL CX, BX + CMOVLLT CX, BX + MOVL BX, ret+8(FP) + RET + +#define subInt32AVX2x32(baseAddr, offset) \ + VMOVDQU offset+0(baseAddr), Y1 \ + VMOVDQU offset+32(baseAddr), Y2 \ + VMOVDQU offset+64(baseAddr), Y3 \ + VMOVDQU offset+96(baseAddr), Y4 \ + VPSUBD Y0, Y1, Y1 \ + VPSUBD Y0, Y2, Y2 \ + VPSUBD Y0, Y3, Y3 \ + VPSUBD Y0, Y4, Y4 \ + VMOVDQU Y1, offset+0(baseAddr) \ + VMOVDQU Y2, offset+32(baseAddr) \ + VMOVDQU Y3, offset+64(baseAddr) \ + VMOVDQU Y4, offset+96(baseAddr) + +// func blockSubInt32AVX2(block *[blockSize]int32, value int32) +TEXT ·blockSubInt32AVX2(SB), NOSPLIT, $0-12 + MOVQ block+0(FP), AX + VPBROADCASTD value+8(FP), Y0 + subInt32AVX2x32(AX, 0) + subInt32AVX2x32(AX, 128) + subInt32AVX2x32(AX, 256) + subInt32AVX2x32(AX, 384) + VZEROUPPER + RET + +// func blockBitWidthsInt32AVX2(bitWidths *[numMiniBlocks]byte, block *[blockSize]int32) +TEXT ·blockBitWidthsInt32AVX2(SB), NOSPLIT, $0-16 + MOVQ bitWidths+0(FP), AX + MOVQ block+8(FP), BX + + // AVX2 only has signed comparisons (and min/max), we emulate working on + // unsigned values by adding -2^31 to the values. Y5 is a vector of -2^31 + // used to offset 8 packed 32 bits integers in other YMM registers where + // the block data are loaded. + VPCMPEQD Y5, Y5, Y5 + VPSLLD $31, Y5, Y5 + + XORQ DI, DI +loop: + VPBROADCASTD (BX), Y0 // max + VPADDD Y5, Y0, Y0 + + VMOVDQU (BX), Y1 + VMOVDQU 32(BX), Y2 + VMOVDQU 64(BX), Y3 + VMOVDQU 96(BX), Y4 + + VPADDD Y5, Y1, Y1 + VPADDD Y5, Y2, Y2 + VPADDD Y5, Y3, Y3 + VPADDD Y5, Y4, Y4 + + VPMAXSD Y2, Y1, Y1 + VPMAXSD Y4, Y3, Y3 + VPMAXSD Y3, Y1, Y1 + VPMAXSD Y1, Y0, Y0 + + VPERM2I128 $1, Y0, Y0, Y1 + VPMAXSD Y1, Y0, Y0 + + VPSHUFD $0b00011011, Y0, Y1 + VPMAXSD Y1, Y0, Y0 + VPSUBD Y5, Y0, Y0 + + MOVQ X0, CX + MOVL CX, DX + SHRQ $32, CX + CMPL CX, DX + CMOVLHI CX, DX + + LZCNTL DX, DX + NEGL DX + ADDL $32, DX + MOVB DX, (AX)(DI*1) + + ADDQ $128, BX + INCQ DI + CMPQ DI, $numMiniBlocks + JNE loop + VZEROUPPER + RET + +// encodeMiniBlockInt32Default is the generic implementation of the algorithm to +// pack 32 bit integers into values of a given bit width (<=32). +// +// This algorithm is much slower than the vectorized versions, but is useful +// as a reference implementation to run the tests against, and as fallback when +// the code runs on a CPU which does not support the AVX2 instruction set. +// +// func encodeMiniBlockInt32Default(dst *byte, src *[miniBlockSize]int32, bitWidth uint) +TEXT ·encodeMiniBlockInt32Default(SB), NOSPLIT, $0-24 + MOVQ dst+0(FP), AX + MOVQ src+8(FP), BX + MOVQ bitWidth+16(FP), R9 + + XORQ DI, DI // bitOffset + XORQ SI, SI +loop: + MOVQ DI, CX + MOVQ DI, DX + + ANDQ $0b11111, CX // bitOffset % 32 + SHRQ $5, DX // bitOffset / 32 + + MOVLQZX (BX)(SI*4), R8 + SHLQ CX, R8 + ORQ R8, (AX)(DX*4) + + ADDQ R9, DI + INCQ SI + CMPQ SI, $miniBlockSize + JNE loop + RET + +// encodeMiniBlockInt32x1bitAVX2 packs 32 bit integers into 1 bit values in the +// the output buffer. +// +// The algorithm uses MOVMSKPS to extract the 8 relevant bits from the 8 values +// packed in YMM registers, then combines 4 of these into a 32 bit word which +// then gets written to the output. The result is 32 bits because each mini +// block has 32 values (the block size is 128 and there are 4 mini blocks per +// block). +// +// func encodeMiniBlockInt32x1bitAVX2(dst *byte, src *[miniBlockSize]int32) +TEXT ·encodeMiniBlockInt32x1bitAVX2(SB), NOSPLIT, $0-16 + MOVQ dst+0(FP), AX + MOVQ src+8(FP), BX + + VMOVDQU 0(BX), Y0 + VMOVDQU 32(BX), Y1 + VMOVDQU 64(BX), Y2 + VMOVDQU 96(BX), Y3 + + VPSLLD $31, Y0, Y0 + VPSLLD $31, Y1, Y1 + VPSLLD $31, Y2, Y2 + VPSLLD $31, Y3, Y3 + + VMOVMSKPS Y0, R8 + VMOVMSKPS Y1, R9 + VMOVMSKPS Y2, R10 + VMOVMSKPS Y3, R11 + + SHLL $8, R9 + SHLL $16, R10 + SHLL $24, R11 + + ORL R9, R8 + ORL R10, R8 + ORL R11, R8 + MOVL R8, (AX) + VZEROUPPER + RET + +// encodeMiniBlockInt32x2bitsAVX2 implements an algorithm for packing 32 bit +// integers into 2 bit values. +// +// The algorithm is derived from the one employed in encodeMiniBlockInt32x1bitAVX2 +// but needs to perform a bit extra work since MOVMSKPS can only extract one bit +// per packed integer of each YMM vector. We run two passes to extract the two +// bits needed to compose each item of the result, and merge the values by +// interleaving the first and second bits with PDEP. +// +// func encodeMiniBlockInt32x2bitsAVX2(dst *byte, src *[miniBlockSize]int32) +TEXT ·encodeMiniBlockInt32x2bitsAVX2(SB), NOSPLIT, $0-16 + MOVQ dst+0(FP), AX + MOVQ src+8(FP), BX + + VMOVDQU 0(BX), Y0 + VMOVDQU 32(BX), Y1 + VMOVDQU 64(BX), Y2 + VMOVDQU 96(BX), Y3 + + VPSLLD $31, Y0, Y4 + VPSLLD $31, Y1, Y5 + VPSLLD $31, Y2, Y6 + VPSLLD $31, Y3, Y7 + + VMOVMSKPS Y4, R8 + VMOVMSKPS Y5, R9 + VMOVMSKPS Y6, R10 + VMOVMSKPS Y7, R11 + + SHLQ $8, R9 + SHLQ $16, R10 + SHLQ $24, R11 + ORQ R9, R8 + ORQ R10, R8 + ORQ R11, R8 + + MOVQ $0x5555555555555555, DX // 0b010101... + PDEPQ DX, R8, R8 + + VPSLLD $30, Y0, Y8 + VPSLLD $30, Y1, Y9 + VPSLLD $30, Y2, Y10 + VPSLLD $30, Y3, Y11 + + VMOVMSKPS Y8, R12 + VMOVMSKPS Y9, R13 + VMOVMSKPS Y10, R14 + VMOVMSKPS Y11, R15 + + SHLQ $8, R13 + SHLQ $16, R14 + SHLQ $24, R15 + ORQ R13, R12 + ORQ R14, R12 + ORQ R15, R12 + + MOVQ $0xAAAAAAAAAAAAAAAA, DI // 0b101010... + PDEPQ DI, R12, R12 + + ORQ R12, R8 + MOVQ R8, (AX) + VZEROUPPER + RET + +// encodeMiniBlockInt32x32bitsAVX2 is a specialization of the bit packing logic +// for 32 bit integers when the output bit width is also 32, in which case a +// simple copy of the mini block to the output buffer produces the result. +// +// func encodeMiniBlockInt32x32bitsAVX2(dst *byte, src *[miniBlockSize]int32) +TEXT ·encodeMiniBlockInt32x32bitsAVX2(SB), NOSPLIT, $0-16 + MOVQ dst+0(FP), AX + MOVQ src+8(FP), BX + VMOVDQU 0(BX), Y0 + VMOVDQU 32(BX), Y1 + VMOVDQU 64(BX), Y2 + VMOVDQU 96(BX), Y3 + VMOVDQU Y0, 0(AX) + VMOVDQU Y1, 32(AX) + VMOVDQU Y2, 64(AX) + VMOVDQU Y3, 96(AX) + VZEROUPPER + RET + +// encodeMiniBlockInt32x3to16bitsAVX2 is the algorithm used to bit-pack 32 bit +// integers into values of width 3 to 16 bits. +// +// This function is a small overhead due to having to initialize registers with +// values that depend on the bit width. We measured this cost at ~10% throughput +// in synthetic benchmarks compared to generating constant shifts and offsets +// using a macro. Using a single function rather than generating one for each +// bit width has the benefit of reducing the code size, which in practice can +// also yield benefits like reducing CPU cache misses. Not using a macro also +// has other advantages like providing accurate line number of stack traces and +// enabling the use of breakpoints when debugging. Overall, this approach seemed +// to be the right trade off between performance and maintainability. +// +// The algorithm treats chunks of 8 values in 4 iterations to process all 32 +// values of the mini block. Writes to the output buffer are aligned on 128 bits +// since we may write up to 128 bits (8 x 16 bits). Padding is therefore +// required in the output buffer to avoid triggering a segfault. +// The encodeInt32AVX2 method adds enough padding when sizing the output buffer +// to account for this requirement. +// +// We leverage the two lanes of YMM registers to work on two sets of 4 values +// (in the sequence of VMOVDQU/VPSHUFD, VPAND, VPSLLQ, VPOR), resulting in having +// two sets of bit-packed values in the lower 64 bits of each YMM lane. +// The upper lane is then permuted into a lower lane to merge the two results, +// which may not be aligned on byte boundaries so we shift the lower and upper +// bits and compose two sets of 128 bits sequences (VPSLLQ, VPSRLQ, VBLENDPD), +// merge them and write the 16 bytes result to the output buffer. +TEXT ·encodeMiniBlockInt32x3to16bitsAVX2(SB), NOSPLIT, $0-24 + MOVQ dst+0(FP), AX + MOVQ src+8(FP), BX + MOVQ bitWidth+16(FP), CX + + VPBROADCASTQ bitWidth+16(FP), Y6 // [1*bitWidth...] + VPSLLQ $1, Y6, Y7 // [2*bitWidth...] + VPADDQ Y6, Y7, Y8 // [3*bitWidth...] + VPSLLQ $2, Y6, Y9 // [4*bitWidth...] + + VPBROADCASTQ sixtyfour<>(SB), Y10 + VPSUBQ Y6, Y10, Y11 // [64-1*bitWidth...] + VPSUBQ Y9, Y10, Y12 // [64-4*bitWidth...] + VPCMPEQQ Y4, Y4, Y4 + VPSRLVQ Y11, Y4, Y4 + + VPXOR Y5, Y5, Y5 + XORQ SI, SI +loop: + VMOVDQU (BX)(SI*4), Y0 + VPSHUFD $0b01010101, Y0, Y1 + VPSHUFD $0b10101010, Y0, Y2 + VPSHUFD $0b11111111, Y0, Y3 + + VPAND Y4, Y0, Y0 + VPAND Y4, Y1, Y1 + VPAND Y4, Y2, Y2 + VPAND Y4, Y3, Y3 + + VPSLLVQ Y6, Y1, Y1 + VPSLLVQ Y7, Y2, Y2 + VPSLLVQ Y8, Y3, Y3 + + VPOR Y1, Y0, Y0 + VPOR Y3, Y2, Y2 + VPOR Y2, Y0, Y0 + + VPERMQ $0b00001010, Y0, Y1 + + VPSLLVQ X9, X1, X2 + VPSRLQ X12, X1, X3 + VBLENDPD $0b10, X3, X2, X1 + VBLENDPD $0b10, X5, X0, X0 + VPOR X1, X0, X0 + + VMOVDQU X0, (AX) + + ADDQ CX, AX + ADDQ $8, SI + CMPQ SI, $miniBlockSize + JNE loop + VZEROUPPER + RET + +GLOBL sixtyfour<>(SB), RODATA|NOPTR, $32 +DATA sixtyfour<>+0(SB)/8, $64 +DATA sixtyfour<>+8(SB)/8, $64 +DATA sixtyfour<>+16(SB)/8, $64 +DATA sixtyfour<>+24(SB)/8, $64 + +// func decodeBlockInt32Default(dst []int32, minDelta, lastValue int32) int32 +TEXT ·decodeBlockInt32Default(SB), NOSPLIT, $0-36 + MOVQ dst_base+0(FP), AX + MOVQ dst_len+8(FP), BX + MOVLQZX minDelta+24(FP), CX + MOVLQZX lastValue+28(FP), DX + XORQ SI, SI + JMP test +loop: + MOVL (AX)(SI*4), DI + ADDL CX, DI + ADDL DI, DX + MOVL DX, (AX)(SI*4) + INCQ SI +test: + CMPQ SI, BX + JNE loop +done: + MOVL DX, ret+32(FP) + RET + +// func decodeBlockInt32AVX2(dst []int32, minDelta, lastValue int32) int32 +TEXT ·decodeBlockInt32AVX2(SB), NOSPLIT, $0-36 + MOVQ dst_base+0(FP), AX + MOVQ dst_len+8(FP), BX + MOVLQZX minDelta+24(FP), CX + MOVLQZX lastValue+28(FP), DX + XORQ SI, SI + + CMPQ BX, $8 + JB test + + MOVQ BX, DI + SHRQ $3, DI + SHLQ $3, DI + + VPXOR X1, X1, X1 + MOVQ CX, X0 + MOVQ DX, X1 + VPBROADCASTD X0, Y0 +loopAVX2: + VMOVDQU (AX)(SI*4), Y2 + VPADDD Y0, Y2, Y2 // Y2[:] += minDelta + VPADDD Y1, Y2, Y2 // Y2[0] += lastValue + + VPSLLDQ $4, Y2, Y3 + VPADDD Y3, Y2, Y2 + + VPSLLDQ $8, Y2, Y3 + VPADDD Y3, Y2, Y2 + + VPSHUFD $0xFF, X2, X1 + VPERM2I128 $1, Y2, Y2, Y3 + VPADDD X1, X3, X3 + + VMOVDQU X2, (AX)(SI*4) + VMOVDQU X3, 16(AX)(SI*4) + VPSRLDQ $12, X3, X1 // lastValue + + ADDQ $8, SI + CMPQ SI, DI + JNE loopAVX2 + VZEROUPPER + MOVQ X1, DX + JMP test +loop: + MOVL (AX)(SI*4), DI + ADDL CX, DI + ADDL DI, DX + MOVL DX, (AX)(SI*4) + INCQ SI +test: + CMPQ SI, BX + JNE loop +done: + MOVL DX, ret+32(FP) + RET + +// ----------------------------------------------------------------------------- +// 64 bits +// ----------------------------------------------------------------------------- + +#define deltaInt64AVX2x4(baseAddr) \ + VMOVDQU baseAddr, Y1 \ // [0,1,2,3] + VPERMQ $0b10010011, Y1, Y2 \ // [3,0,1,2] + VPBLENDD $3, Y0, Y2, Y2 \ // [x,0,1,2] + VPSUBQ Y2, Y1, Y2 \ // [0,1,2,3] - [x,0,1,2] + VMOVDQU Y2, baseAddr \ + VPERMQ $0b10010011, Y1, Y0 + +// func blockDeltaInt64AVX2(block *[blockSize]int64, lastValue int64) int64 +TEXT ·blockDeltaInt64AVX2(SB), NOSPLIT, $0-24 + MOVQ block+0(FP), AX + MOVQ 8*blockSize-8(AX), CX + MOVQ CX, ret+16(FP) + + VPBROADCASTQ lastValue+8(FP), Y0 + XORQ SI, SI +loop: + deltaInt64AVX2x4((AX)(SI*8)) + deltaInt64AVX2x4(32(AX)(SI*8)) + deltaInt64AVX2x4(64(AX)(SI*8)) + deltaInt64AVX2x4(96(AX)(SI*8)) + ADDQ $16, SI + CMPQ SI, $blockSize + JNE loop + VZEROUPPER + RET + +// vpminsq is an emulation of the AVX-512 VPMINSQ instruction with AVX2. +#define vpminsq(ones, tmp, arg2, arg1, ret) \ + VPCMPGTQ arg1, arg2, tmp \ + VPBLENDVB tmp, arg1, arg2, ret + +// func blockMinInt64AVX2(block *[blockSize]int64) int64 +TEXT ·blockMinInt64AVX2(SB), NOSPLIT, $0-16 + MOVQ block+0(FP), AX + XORQ SI, SI + VPCMPEQQ Y9, Y9, Y9 // ones + VPBROADCASTQ (AX), Y0 +loop: + VMOVDQU 0(AX)(SI*8), Y1 + VMOVDQU 32(AX)(SI*8), Y2 + VMOVDQU 64(AX)(SI*8), Y3 + VMOVDQU 96(AX)(SI*8), Y4 + VMOVDQU 128(AX)(SI*8), Y5 + VMOVDQU 160(AX)(SI*8), Y6 + VMOVDQU 192(AX)(SI*8), Y7 + VMOVDQU 224(AX)(SI*8), Y8 + + vpminsq(Y9, Y10, Y0, Y1, Y1) + vpminsq(Y9, Y11, Y0, Y2, Y2) + vpminsq(Y9, Y12, Y0, Y3, Y3) + vpminsq(Y9, Y13, Y0, Y4, Y4) + vpminsq(Y9, Y14, Y0, Y5, Y5) + vpminsq(Y9, Y15, Y0, Y6, Y6) + vpminsq(Y9, Y10, Y0, Y7, Y7) + vpminsq(Y9, Y11, Y0, Y8, Y8) + + vpminsq(Y9, Y12, Y2, Y1, Y1) + vpminsq(Y9, Y13, Y4, Y3, Y3) + vpminsq(Y9, Y14, Y6, Y5, Y5) + vpminsq(Y9, Y15, Y8, Y7, Y7) + + vpminsq(Y9, Y10, Y3, Y1, Y1) + vpminsq(Y9, Y11, Y7, Y5, Y5) + vpminsq(Y9, Y12, Y5, Y1, Y0) + + ADDQ $32, SI + CMPQ SI, $blockSize + JNE loop + + VPERM2I128 $1, Y0, Y0, Y1 + vpminsq(Y9, Y10, Y1, Y0, Y0) + + MOVQ X0, CX + VPEXTRQ $1, X0, BX + CMPQ CX, BX + CMOVQLT CX, BX + MOVQ BX, ret+8(FP) + VZEROUPPER + RET + +#define subInt64AVX2x32(baseAddr, offset) \ + VMOVDQU offset+0(baseAddr), Y1 \ + VMOVDQU offset+32(baseAddr), Y2 \ + VMOVDQU offset+64(baseAddr), Y3 \ + VMOVDQU offset+96(baseAddr), Y4 \ + VMOVDQU offset+128(baseAddr), Y5 \ + VMOVDQU offset+160(baseAddr), Y6 \ + VMOVDQU offset+192(baseAddr), Y7 \ + VMOVDQU offset+224(baseAddr), Y8 \ + VPSUBQ Y0, Y1, Y1 \ + VPSUBQ Y0, Y2, Y2 \ + VPSUBQ Y0, Y3, Y3 \ + VPSUBQ Y0, Y4, Y4 \ + VPSUBQ Y0, Y5, Y5 \ + VPSUBQ Y0, Y6, Y6 \ + VPSUBQ Y0, Y7, Y7 \ + VPSUBQ Y0, Y8, Y8 \ + VMOVDQU Y1, offset+0(baseAddr) \ + VMOVDQU Y2, offset+32(baseAddr) \ + VMOVDQU Y3, offset+64(baseAddr) \ + VMOVDQU Y4, offset+96(baseAddr) \ + VMOVDQU Y5, offset+128(baseAddr) \ + VMOVDQU Y6, offset+160(baseAddr) \ + VMOVDQU Y7, offset+192(baseAddr) \ + VMOVDQU Y8, offset+224(baseAddr) + +// func blockSubInt64AVX2(block *[blockSize]int64, value int64) +TEXT ·blockSubInt64AVX2(SB), NOSPLIT, $0-16 + MOVQ block+0(FP), AX + VPBROADCASTQ value+8(FP), Y0 + subInt64AVX2x32(AX, 0) + subInt64AVX2x32(AX, 256) + subInt64AVX2x32(AX, 512) + subInt64AVX2x32(AX, 768) + VZEROUPPER + RET + +// vpmaxsq is an emulation of the AVX-512 VPMAXSQ instruction with AVX2. +#define vpmaxsq(tmp, arg2, arg1, ret) \ + VPCMPGTQ arg2, arg1, tmp \ + VPBLENDVB tmp, arg1, arg2, ret + +// func blockBitWidthsInt64AVX2(bitWidths *[numMiniBlocks]byte, block *[blockSize]int64) +TEXT ·blockBitWidthsInt64AVX2(SB), NOSPLIT, $0-16 + MOVQ bitWidths+0(FP), AX + MOVQ block+8(FP), BX + + // AVX2 only has signed comparisons (and min/max), we emulate working on + // unsigned values by adding -2^64 to the values. Y9 is a vector of -2^64 + // used to offset 4 packed 64 bits integers in other YMM registers where + // the block data are loaded. + VPCMPEQQ Y9, Y9, Y9 + VPSLLQ $63, Y9, Y9 + + XORQ DI, DI +loop: + VPBROADCASTQ (BX), Y0 // max + VPADDQ Y9, Y0, Y0 + + VMOVDQU (BX), Y1 + VMOVDQU 32(BX), Y2 + VMOVDQU 64(BX), Y3 + VMOVDQU 96(BX), Y4 + VMOVDQU 128(BX), Y5 + VMOVDQU 160(BX), Y6 + VMOVDQU 192(BX), Y7 + VMOVDQU 224(BX), Y8 + + VPADDQ Y9, Y1, Y1 + VPADDQ Y9, Y2, Y2 + VPADDQ Y9, Y3, Y3 + VPADDQ Y9, Y4, Y4 + VPADDQ Y9, Y5, Y5 + VPADDQ Y9, Y6, Y6 + VPADDQ Y9, Y7, Y7 + VPADDQ Y9, Y8, Y8 + + vpmaxsq(Y10, Y2, Y1, Y1) + vpmaxsq(Y11, Y4, Y3, Y3) + vpmaxsq(Y12, Y6, Y5, Y5) + vpmaxsq(Y13, Y8, Y7, Y7) + + vpmaxsq(Y10, Y3, Y1, Y1) + vpmaxsq(Y11, Y7, Y5, Y5) + vpmaxsq(Y12, Y5, Y1, Y1) + vpmaxsq(Y13, Y1, Y0, Y0) + + VPERM2I128 $1, Y0, Y0, Y1 + vpmaxsq(Y10, Y1, Y0, Y0) + VPSUBQ Y9, Y0, Y0 + + MOVQ X0, CX + VPEXTRQ $1, X0, DX + CMPQ CX, DX + CMOVQHI CX, DX + + LZCNTQ DX, DX + NEGQ DX + ADDQ $64, DX + MOVB DX, (AX)(DI*1) + + ADDQ $256, BX + INCQ DI + CMPQ DI, $numMiniBlocks + JNE loop + VZEROUPPER + RET + +// encodeMiniBlockInt64Default is the generic implementation of the algorithm to +// pack 64 bit integers into values of a given bit width (<=64). +// +// This algorithm is much slower than the vectorized versions, but is useful +// as a reference implementation to run the tests against, and as fallback when +// the code runs on a CPU which does not support the AVX2 instruction set. +// +// func encodeMiniBlockInt64Default(dst *byte, src *[miniBlockSize]int64, bitWidth uint) +TEXT ·encodeMiniBlockInt64Default(SB), NOSPLIT, $0-24 + MOVQ dst+0(FP), AX + MOVQ src+8(FP), BX + MOVQ bitWidth+16(FP), R10 + + XORQ R11, R11 // zero + XORQ DI, DI // bitOffset + XORQ SI, SI +loop: + MOVQ DI, CX + MOVQ DI, DX + + ANDQ $0b111111, CX // bitOffset % 64 + SHRQ $6, DX // bitOffset / 64 + + MOVQ (BX)(SI*8), R8 + MOVQ R8, R9 + SHLQ CX, R8 + NEGQ CX + ADDQ $64, CX + SHRQ CX, R9 + CMPQ CX, $64 + CMOVQEQ R11, R9 // needed because shifting by more than 63 is undefined + + ORQ R8, 0(AX)(DX*8) + ORQ R9, 8(AX)(DX*8) + + ADDQ R10, DI + INCQ SI + CMPQ SI, $miniBlockSize + JNE loop + RET + +// func encodeMiniBlockInt64x1bitAVX2(dst *byte, src *[miniBlockSize]int64) +TEXT ·encodeMiniBlockInt64x1bitAVX2(SB), NOSPLIT, $0-16 + MOVQ dst+0(FP), AX + MOVQ src+8(FP), BX + + VMOVDQU 0(BX), Y0 + VMOVDQU 32(BX), Y1 + VMOVDQU 64(BX), Y2 + VMOVDQU 96(BX), Y3 + VMOVDQU 128(BX), Y4 + VMOVDQU 160(BX), Y5 + VMOVDQU 192(BX), Y6 + VMOVDQU 224(BX), Y7 + + VPSLLQ $63, Y0, Y0 + VPSLLQ $63, Y1, Y1 + VPSLLQ $63, Y2, Y2 + VPSLLQ $63, Y3, Y3 + VPSLLQ $63, Y4, Y4 + VPSLLQ $63, Y5, Y5 + VPSLLQ $63, Y6, Y6 + VPSLLQ $63, Y7, Y7 + + VMOVMSKPD Y0, R8 + VMOVMSKPD Y1, R9 + VMOVMSKPD Y2, R10 + VMOVMSKPD Y3, R11 + VMOVMSKPD Y4, R12 + VMOVMSKPD Y5, R13 + VMOVMSKPD Y6, R14 + VMOVMSKPD Y7, R15 + + SHLL $4, R9 + SHLL $8, R10 + SHLL $12, R11 + SHLL $16, R12 + SHLL $20, R13 + SHLL $24, R14 + SHLL $28, R15 + + ORL R9, R8 + ORL R11, R10 + ORL R13, R12 + ORL R15, R14 + ORL R10, R8 + ORL R14, R12 + ORL R12, R8 + + MOVL R8, (AX) + VZEROUPPER + RET + +// func encodeMiniBlockInt64x2bitsAVX2(dst *byte, src *[miniBlockSize]int64) +TEXT ·encodeMiniBlockInt64x2bitsAVX2(SB), NOSPLIT, $0-16 + MOVQ dst+0(FP), AX + MOVQ src+8(FP), BX + + VMOVDQU 0(BX), Y8 + VMOVDQU 32(BX), Y9 + VMOVDQU 64(BX), Y10 + VMOVDQU 96(BX), Y11 + VMOVDQU 128(BX), Y12 + VMOVDQU 160(BX), Y13 + VMOVDQU 192(BX), Y14 + VMOVDQU 224(BX), Y15 + + VPSLLQ $63, Y8, Y0 + VPSLLQ $63, Y9, Y1 + VPSLLQ $63, Y10, Y2 + VPSLLQ $63, Y11, Y3 + VPSLLQ $63, Y12, Y4 + VPSLLQ $63, Y13, Y5 + VPSLLQ $63, Y14, Y6 + VPSLLQ $63, Y15, Y7 + + VMOVMSKPD Y0, R8 + VMOVMSKPD Y1, R9 + VMOVMSKPD Y2, R10 + VMOVMSKPD Y3, R11 + VMOVMSKPD Y4, R12 + VMOVMSKPD Y5, R13 + VMOVMSKPD Y6, R14 + VMOVMSKPD Y7, R15 + + SHLQ $4, R9 + SHLQ $8, R10 + SHLQ $12, R11 + SHLQ $16, R12 + SHLQ $20, R13 + SHLQ $24, R14 + SHLQ $28, R15 + + ORQ R9, R8 + ORQ R11, R10 + ORQ R13, R12 + ORQ R15, R14 + ORQ R10, R8 + ORQ R14, R12 + ORQ R12, R8 + + MOVQ $0x5555555555555555, CX // 0b010101... + PDEPQ CX, R8, CX + + VPSLLQ $62, Y8, Y8 + VPSLLQ $62, Y9, Y9 + VPSLLQ $62, Y10, Y10 + VPSLLQ $62, Y11, Y11 + VPSLLQ $62, Y12, Y12 + VPSLLQ $62, Y13, Y13 + VPSLLQ $62, Y14, Y14 + VPSLLQ $62, Y15, Y15 + + VMOVMSKPD Y8, R8 + VMOVMSKPD Y9, R9 + VMOVMSKPD Y10, R10 + VMOVMSKPD Y11, R11 + VMOVMSKPD Y12, R12 + VMOVMSKPD Y13, R13 + VMOVMSKPD Y14, R14 + VMOVMSKPD Y15, R15 + + SHLQ $4, R9 + SHLQ $8, R10 + SHLQ $12, R11 + SHLQ $16, R12 + SHLQ $20, R13 + SHLQ $24, R14 + SHLQ $28, R15 + + ORQ R9, R8 + ORQ R11, R10 + ORQ R13, R12 + ORQ R15, R14 + ORQ R10, R8 + ORQ R14, R12 + ORQ R12, R8 + + MOVQ $0xAAAAAAAAAAAAAAAA, DX // 0b101010... + PDEPQ DX, R8, DX + ORQ DX, CX + MOVQ CX, (AX) + VZEROUPPER + RET + +// func encodeMiniBlockInt64x64bitsAVX2(dst *byte, src *[miniBlockSize]int64) +TEXT ·encodeMiniBlockInt64x64bitsAVX2(SB), NOSPLIT, $0-16 + MOVQ dst+0(FP), AX + MOVQ src+8(FP), BX + VMOVDQU 0(BX), Y0 + VMOVDQU 32(BX), Y1 + VMOVDQU 64(BX), Y2 + VMOVDQU 96(BX), Y3 + VMOVDQU 128(BX), Y4 + VMOVDQU 160(BX), Y5 + VMOVDQU 192(BX), Y6 + VMOVDQU 224(BX), Y7 + VMOVDQU Y0, 0(AX) + VMOVDQU Y1, 32(AX) + VMOVDQU Y2, 64(AX) + VMOVDQU Y3, 96(AX) + VMOVDQU Y4, 128(AX) + VMOVDQU Y5, 160(AX) + VMOVDQU Y6, 192(AX) + VMOVDQU Y7, 224(AX) + VZEROUPPER + RET + +// func decodeBlockInt64Default(dst []int64, minDelta, lastValue int64) int64 +TEXT ·decodeBlockInt64Default(SB), NOSPLIT, $0-48 + MOVQ dst_base+0(FP), AX + MOVQ dst_len+8(FP), BX + MOVQ minDelta+24(FP), CX + MOVQ lastValue+32(FP), DX + XORQ SI, SI + JMP test +loop: + MOVQ (AX)(SI*8), DI + ADDQ CX, DI + ADDQ DI, DX + MOVQ DX, (AX)(SI*8) + INCQ SI +test: + CMPQ SI, BX + JNE loop +done: + MOVQ DX, ret+40(FP) + RET diff --git a/vendor/github.com/parquet-go/parquet-go/encoding/delta/binary_packed_purego.go b/vendor/github.com/parquet-go/parquet-go/encoding/delta/binary_packed_purego.go new file mode 100644 index 000000000..aa1f62367 --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/encoding/delta/binary_packed_purego.go @@ -0,0 +1,105 @@ +//go:build purego || !amd64 + +package delta + +import ( + "encoding/binary" +) + +func encodeMiniBlockInt32(dst []byte, src *[miniBlockSize]int32, bitWidth uint) { + bitMask := uint32(1<> (32 - j)) + + binary.LittleEndian.PutUint32(dst[(i+0)*4:], lo) + binary.LittleEndian.PutUint32(dst[(i+1)*4:], hi) + + bitOffset += bitWidth + } +} + +func encodeMiniBlockInt64(dst []byte, src *[miniBlockSize]int64, bitWidth uint) { + bitMask := uint64(1<> (64 - j)) + + binary.LittleEndian.PutUint64(dst[(i+0)*8:], lo) + binary.LittleEndian.PutUint64(dst[(i+1)*8:], hi) + + bitOffset += bitWidth + } +} + +func decodeBlockInt32(block []int32, minDelta, lastValue int32) int32 { + for i := range block { + block[i] += minDelta + block[i] += lastValue + lastValue = block[i] + } + return lastValue +} + +func decodeBlockInt64(block []int64, minDelta, lastValue int64) int64 { + for i := range block { + block[i] += minDelta + block[i] += lastValue + lastValue = block[i] + } + return lastValue +} + +func decodeMiniBlockInt32(dst []int32, src []uint32, bitWidth uint) { + bitMask := uint32(1<> j + if j+bitWidth > 32 { + k := 32 - j + d |= (src[i+1] & (bitMask >> k)) << k + } + dst[n] = int32(d) + bitOffset += bitWidth + } +} + +func decodeMiniBlockInt64(dst []int64, src []uint32, bitWidth uint) { + bitMask := uint64(1<> j + if j+bitWidth > 32 { + k := 32 - j + d |= (uint64(src[i+1]) & (bitMask >> k)) << k + if j+bitWidth > 64 { + k := 64 - j + d |= (uint64(src[i+2]) & (bitMask >> k)) << k + } + } + dst[n] = int64(d) + bitOffset += bitWidth + } +} diff --git a/vendor/github.com/parquet-go/parquet-go/encoding/delta/byte_array.go b/vendor/github.com/parquet-go/parquet-go/encoding/delta/byte_array.go new file mode 100644 index 000000000..7033dc95b --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/encoding/delta/byte_array.go @@ -0,0 +1,212 @@ +package delta + +import ( + "bytes" + "sort" + + "github.com/parquet-go/parquet-go/encoding" + "github.com/parquet-go/parquet-go/format" +) + +const ( + maxLinearSearchPrefixLength = 64 // arbitrary +) + +type ByteArrayEncoding struct { + encoding.NotSupported +} + +func (e *ByteArrayEncoding) String() string { + return "DELTA_BYTE_ARRAY" +} + +func (e *ByteArrayEncoding) Encoding() format.Encoding { + return format.DeltaByteArray +} + +func (e *ByteArrayEncoding) EncodeByteArray(dst []byte, src []byte, offsets []uint32) ([]byte, error) { + prefix := getInt32Buffer() + defer putInt32Buffer(prefix) + + length := getInt32Buffer() + defer putInt32Buffer(length) + + totalSize := 0 + if len(offsets) > 0 { + lastValue := ([]byte)(nil) + baseOffset := offsets[0] + + for _, endOffset := range offsets[1:] { + v := src[baseOffset:endOffset:endOffset] + n := int(endOffset - baseOffset) + p := 0 + baseOffset = endOffset + + if len(v) <= maxLinearSearchPrefixLength { + p = linearSearchPrefixLength(lastValue, v) + } else { + p = binarySearchPrefixLength(lastValue, v) + } + + prefix.values = append(prefix.values, int32(p)) + length.values = append(length.values, int32(n-p)) + lastValue = v + totalSize += n - p + } + } + + dst = dst[:0] + dst = encodeInt32(dst, prefix.values) + dst = encodeInt32(dst, length.values) + dst = resize(dst, len(dst)+totalSize) + + if len(offsets) > 0 { + b := dst[len(dst)-totalSize:] + i := int(offsets[0]) + j := 0 + + _ = length.values[:len(prefix.values)] + + for k, p := range prefix.values { + n := p + length.values[k] + j += copy(b[j:], src[i+int(p):i+int(n)]) + i += int(n) + } + } + + return dst, nil +} + +func (e *ByteArrayEncoding) EncodeFixedLenByteArray(dst []byte, src []byte, size int) ([]byte, error) { + // The parquet specs say that this encoding is only supported for BYTE_ARRAY + // values, but the reference Java implementation appears to support + // FIXED_LEN_BYTE_ARRAY as well: + // https://github.com/apache/parquet-java/blob/5608695f5777de1eb0899d9075ec9411cfdf31d3/parquet-column/src/main/java/org/apache/parquet/column/Encoding.java#L211 + if size < 0 || size > encoding.MaxFixedLenByteArraySize { + return dst[:0], encoding.Error(e, encoding.ErrInvalidArgument) + } + if (len(src) % size) != 0 { + return dst[:0], encoding.ErrEncodeInvalidInputSize(e, "FIXED_LEN_BYTE_ARRAY", len(src)) + } + + prefix := getInt32Buffer() + defer putInt32Buffer(prefix) + + length := getInt32Buffer() + defer putInt32Buffer(length) + + totalSize := 0 + lastValue := ([]byte)(nil) + + for i := size; i <= len(src); i += size { + v := src[i-size : i : i] + p := linearSearchPrefixLength(lastValue, v) + n := size - p + prefix.values = append(prefix.values, int32(p)) + length.values = append(length.values, int32(n)) + lastValue = v + totalSize += n + } + + dst = dst[:0] + dst = encodeInt32(dst, prefix.values) + dst = encodeInt32(dst, length.values) + dst = resize(dst, len(dst)+totalSize) + + b := dst[len(dst)-totalSize:] + i := 0 + j := 0 + + for _, p := range prefix.values { + j += copy(b[j:], src[i+int(p):i+size]) + i += size + } + + return dst, nil +} + +func (e *ByteArrayEncoding) DecodeByteArray(dst []byte, src []byte, offsets []uint32) ([]byte, []uint32, error) { + dst, offsets = dst[:0], offsets[:0] + + prefix := getInt32Buffer() + defer putInt32Buffer(prefix) + + suffix := getInt32Buffer() + defer putInt32Buffer(suffix) + + var err error + src, err = prefix.decode(src) + if err != nil { + return dst, offsets, e.wrapf("decoding prefix lengths: %w", err) + } + src, err = suffix.decode(src) + if err != nil { + return dst, offsets, e.wrapf("decoding suffix lengths: %w", err) + } + if len(prefix.values) != len(suffix.values) { + return dst, offsets, e.wrap(errPrefixAndSuffixLengthMismatch(len(prefix.values), len(suffix.values))) + } + return decodeByteArray(dst, src, prefix.values, suffix.values, offsets) +} + +func (e *ByteArrayEncoding) DecodeFixedLenByteArray(dst []byte, src []byte, size int) ([]byte, error) { + dst = dst[:0] + + if size < 0 || size > encoding.MaxFixedLenByteArraySize { + return dst, e.wrap(encoding.ErrInvalidArgument) + } + + prefix := getInt32Buffer() + defer putInt32Buffer(prefix) + + suffix := getInt32Buffer() + defer putInt32Buffer(suffix) + + var err error + src, err = prefix.decode(src) + if err != nil { + return dst, e.wrapf("decoding prefix lengths: %w", err) + } + src, err = suffix.decode(src) + if err != nil { + return dst, e.wrapf("decoding suffix lengths: %w", err) + } + if len(prefix.values) != len(suffix.values) { + return dst, e.wrap(errPrefixAndSuffixLengthMismatch(len(prefix.values), len(suffix.values))) + } + return decodeFixedLenByteArray(dst[:0], src, size, prefix.values, suffix.values) +} + +func (e *ByteArrayEncoding) EstimateDecodeByteArraySize(src []byte) int { + length := getInt32Buffer() + defer putInt32Buffer(length) + src, _ = length.decode(src) + sum := int(length.sum()) + length.decode(src) + return sum + int(length.sum()) +} + +func (e *ByteArrayEncoding) wrap(err error) error { + if err != nil { + err = encoding.Error(e, err) + } + return err +} + +func (e *ByteArrayEncoding) wrapf(msg string, args ...any) error { + return encoding.Errorf(e, msg, args...) +} + +func linearSearchPrefixLength(base, data []byte) (n int) { + for n < len(base) && n < len(data) && base[n] == data[n] { + n++ + } + return n +} + +func binarySearchPrefixLength(base, data []byte) int { + n := min(len(base), len(data)) + return sort.Search(n, func(i int) bool { + return !bytes.Equal(base[:i+1], data[:i+1]) + }) +} diff --git a/vendor/github.com/parquet-go/parquet-go/encoding/delta/byte_array_amd64.go b/vendor/github.com/parquet-go/parquet-go/encoding/delta/byte_array_amd64.go new file mode 100644 index 000000000..71a077ee8 --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/encoding/delta/byte_array_amd64.go @@ -0,0 +1,164 @@ +//go:build !purego + +package delta + +import ( + "golang.org/x/sys/cpu" +) + +//go:noescape +func validatePrefixAndSuffixLengthValuesAVX2(prefix, suffix []int32, maxLength int) (totalPrefixLength, totalSuffixLength int, ok bool) + +func validatePrefixAndSuffixLengthValues(prefix, suffix []int32, maxLength int) (totalPrefixLength, totalSuffixLength int, err error) { + if cpu.X86.HasAVX2 { + totalPrefixLength, totalSuffixLength, ok := validatePrefixAndSuffixLengthValuesAVX2(prefix, suffix, maxLength) + if ok { + return totalPrefixLength, totalSuffixLength, nil + } + } + + lastValueLength := 0 + + for i := range prefix { + p := int(prefix[i]) + n := int(suffix[i]) + if p < 0 { + err = errInvalidNegativePrefixLength(p) + return + } + if n < 0 { + err = errInvalidNegativeValueLength(n) + return + } + if p > lastValueLength { + err = errPrefixLengthOutOfBounds(p, lastValueLength) + return + } + totalPrefixLength += p + totalSuffixLength += n + lastValueLength = p + n + } + + if totalSuffixLength > maxLength { + err = errValueLengthOutOfBounds(totalSuffixLength, maxLength) + return + } + + return totalPrefixLength, totalSuffixLength, nil +} + +//go:noescape +func decodeByteArrayOffsets(offsets []uint32, prefix, suffix []int32) + +//go:noescape +func decodeByteArrayAVX2(dst, src []byte, prefix, suffix []int32) int + +func decodeByteArray(dst, src []byte, prefix, suffix []int32, offsets []uint32) ([]byte, []uint32, error) { + totalPrefixLength, totalSuffixLength, err := validatePrefixAndSuffixLengthValues(prefix, suffix, len(src)) + if err != nil { + return dst, offsets, err + } + + totalLength := totalPrefixLength + totalSuffixLength + dst = resizeNoMemclr(dst, totalLength+padding) + + if size := len(prefix) + 1; cap(offsets) < size { + offsets = make([]uint32, size) + } else { + offsets = offsets[:size] + } + + _ = prefix[:len(suffix)] + _ = suffix[:len(prefix)] + decodeByteArrayOffsets(offsets, prefix, suffix) + + var lastValue []byte + var i int + var j int + + if cpu.X86.HasAVX2 && len(src) > padding { + k := len(suffix) + n := 0 + + for k > 0 && n < padding { + k-- + n += int(suffix[k]) + } + + if k > 0 && n >= padding { + i = decodeByteArrayAVX2(dst, src, prefix[:k], suffix[:k]) + j = len(src) - n + lastValue = dst[i-(int(prefix[k-1])+int(suffix[k-1])):] + prefix = prefix[k:] + suffix = suffix[k:] + } + } + + for k := range prefix { + p := int(prefix[k]) + n := int(suffix[k]) + lastValueOffset := i + i += copy(dst[i:], lastValue[:p]) + i += copy(dst[i:], src[j:j+n]) + j += n + lastValue = dst[lastValueOffset:] + } + + return dst[:totalLength], offsets, nil +} + +//go:noescape +func decodeByteArrayAVX2x128bits(dst, src []byte, prefix, suffix []int32) int + +func decodeFixedLenByteArray(dst, src []byte, size int, prefix, suffix []int32) ([]byte, error) { + totalPrefixLength, totalSuffixLength, err := validatePrefixAndSuffixLengthValues(prefix, suffix, len(src)) + if err != nil { + return dst, err + } + + totalLength := totalPrefixLength + totalSuffixLength + dst = resizeNoMemclr(dst, totalLength+padding) + + _ = prefix[:len(suffix)] + _ = suffix[:len(prefix)] + + var lastValue []byte + var i int + var j int + + if cpu.X86.HasAVX2 && len(src) > padding { + k := len(suffix) + n := 0 + + for k > 0 && n < padding { + k-- + n += int(suffix[k]) + } + + if k > 0 && n >= padding { + if size == 16 { + i = decodeByteArrayAVX2x128bits(dst, src, prefix[:k], suffix[:k]) + } else { + i = decodeByteArrayAVX2(dst, src, prefix[:k], suffix[:k]) + } + j = len(src) - n + prefix = prefix[k:] + suffix = suffix[k:] + if i >= size { + lastValue = dst[i-size:] + } + } + } + + for k := range prefix { + p := int(prefix[k]) + n := int(suffix[k]) + k := i + i += copy(dst[i:], lastValue[:p]) + i += copy(dst[i:], src[j:j+n]) + j += n + lastValue = dst[k:] + } + + return dst[:totalLength], nil +} diff --git a/vendor/github.com/parquet-go/parquet-go/encoding/delta/byte_array_amd64.s b/vendor/github.com/parquet-go/parquet-go/encoding/delta/byte_array_amd64.s new file mode 100644 index 000000000..b8b709832 --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/encoding/delta/byte_array_amd64.s @@ -0,0 +1,243 @@ +//go:build !purego + +#include "funcdata.h" +#include "textflag.h" + +// func validatePrefixAndSuffixLengthValuesAVX2(prefix, suffix []int32, maxLength int) (totalPrefixLength, totalSuffixLength int, ok bool) +TEXT ·validatePrefixAndSuffixLengthValuesAVX2(SB), NOSPLIT, $0-73 + MOVQ prefix_base+0(FP), AX + MOVQ suffix_base+24(FP), BX + MOVQ suffix_len+32(FP), CX + MOVQ maxLength+48(FP), DX + + XORQ SI, SI + XORQ DI, DI // lastValueLength + XORQ R8, R8 + XORQ R9, R9 + XORQ R10, R10 // totalPrefixLength + XORQ R11, R11 // totalSuffixLength + XORQ R12, R12 // ok + + CMPQ CX, $8 + JB test + + MOVQ CX, R13 + SHRQ $3, R13 + SHLQ $3, R13 + + VPXOR X0, X0, X0 // lastValueLengths + VPXOR X1, X1, X1 // totalPrefixLengths + VPXOR X2, X2, X2 // totalSuffixLengths + VPXOR X3, X3, X3 // negative prefix length sentinels + VPXOR X4, X4, X4 // negative suffix length sentinels + VPXOR X5, X5, X5 // prefix length overflow sentinels + VMOVDQU ·rotateLeft32(SB), Y6 + +loopAVX2: + VMOVDQU (AX)(SI*4), Y7 // p + VMOVDQU (BX)(SI*4), Y8 // n + + VPADDD Y7, Y1, Y1 + VPADDD Y8, Y2, Y2 + + VPOR Y7, Y3, Y3 + VPOR Y8, Y4, Y4 + + VPADDD Y7, Y8, Y9 // p + n + VPERMD Y0, Y6, Y10 + VPBLENDD $1, Y10, Y9, Y10 + VPCMPGTD Y10, Y7, Y10 + VPOR Y10, Y5, Y5 + + VMOVDQU Y9, Y0 + ADDQ $8, SI + CMPQ SI, R13 + JNE loopAVX2 + + // If any of the sentinel values has its most significant bit set then one + // of the values was negative or one of the prefixes was greater than the + // length of the previous value, return false. + VPOR Y4, Y3, Y3 + VPOR Y5, Y3, Y3 + VMOVMSKPS Y3, R13 + CMPQ R13, $0 + JNE done + + // We computed 8 sums in parallel for the prefix and suffix arrays, they + // need to be accumulated into single values, which is what these reduction + // steps do. + VPSRLDQ $4, Y1, Y5 + VPSRLDQ $8, Y1, Y6 + VPSRLDQ $12, Y1, Y7 + VPADDD Y5, Y1, Y1 + VPADDD Y6, Y1, Y1 + VPADDD Y7, Y1, Y1 + VPERM2I128 $1, Y1, Y1, Y0 + VPADDD Y0, Y1, Y1 + MOVQ X1, R10 + ANDQ $0x7FFFFFFF, R10 + + VPSRLDQ $4, Y2, Y5 + VPSRLDQ $8, Y2, Y6 + VPSRLDQ $12, Y2, Y7 + VPADDD Y5, Y2, Y2 + VPADDD Y6, Y2, Y2 + VPADDD Y7, Y2, Y2 + VPERM2I128 $1, Y2, Y2, Y0 + VPADDD Y0, Y2, Y2 + MOVQ X2, R11 + ANDQ $0x7FFFFFFF, R11 + + JMP test +loop: + MOVLQSX (AX)(SI*4), R8 + MOVLQSX (BX)(SI*4), R9 + + CMPQ R8, $0 // p < 0 ? + JL done + + CMPQ R9, $0 // n < 0 ? + JL done + + CMPQ R8, DI // p > lastValueLength ? + JG done + + ADDQ R8, R10 + ADDQ R9, R11 + ADDQ R8, DI + ADDQ R9, DI + + INCQ SI +test: + CMPQ SI, CX + JNE loop + + CMPQ R11, DX // totalSuffixLength > maxLength ? + JG done + + MOVB $1, R12 +done: + MOVQ R10, totalPrefixLength+56(FP) + MOVQ R11, totalSuffixLength+64(FP) + MOVB R12, ok+72(FP) + RET + +// func decodeByteArrayOffsets(offsets []uint32, prefix, suffix []int32) +TEXT ·decodeByteArrayOffsets(SB), NOSPLIT, $0-72 + MOVQ offsets_base+0(FP), AX + MOVQ prefix_base+24(FP), BX + MOVQ suffix_base+48(FP), CX + MOVQ suffix_len+56(FP), DX + + XORQ SI, SI + XORQ R10, R10 + JMP test +loop: + MOVL (BX)(SI*4), R8 + MOVL (CX)(SI*4), R9 + MOVL R10, (AX)(SI*4) + ADDL R8, R10 + ADDL R9, R10 + INCQ SI +test: + CMPQ SI, DX + JNE loop + MOVL R10, (AX)(SI*4) + RET + +// func decodeByteArrayAVX2(dst, src []byte, prefix, suffix []int32) int +TEXT ·decodeByteArrayAVX2(SB), NOSPLIT, $0-104 + MOVQ dst_base+0(FP), AX + MOVQ src_base+24(FP), BX + MOVQ prefix_base+48(FP), CX + MOVQ suffix_base+72(FP), DX + MOVQ suffix_len+80(FP), DI + + XORQ SI, SI + XORQ R8, R8 + XORQ R9, R9 + MOVQ AX, R10 // last value + + JMP test +loop: + MOVLQZX (CX)(SI*4), R8 // prefix length + MOVLQZX (DX)(SI*4), R9 // suffix length +prefix: + VMOVDQU (R10), Y0 + VMOVDQU Y0, (AX) + CMPQ R8, $32 + JA copyPrefix +suffix: + VMOVDQU (BX), Y1 + VMOVDQU Y1, (AX)(R8*1) + CMPQ R9, $32 + JA copySuffix +next: + MOVQ AX, R10 + ADDQ R9, R8 + LEAQ (AX)(R8*1), AX + LEAQ (BX)(R9*1), BX + INCQ SI +test: + CMPQ SI, DI + JNE loop + MOVQ dst_base+0(FP), BX + SUBQ BX, AX + MOVQ AX, ret+96(FP) + VZEROUPPER + RET +copyPrefix: + MOVQ $32, R12 +copyPrefixLoop: + VMOVDQU (R10)(R12*1), Y0 + VMOVDQU Y0, (AX)(R12*1) + ADDQ $32, R12 + CMPQ R12, R8 + JB copyPrefixLoop + JMP suffix +copySuffix: + MOVQ $32, R12 + LEAQ (AX)(R8*1), R13 +copySuffixLoop: + VMOVDQU (BX)(R12*1), Y1 + VMOVDQU Y1, (R13)(R12*1) + ADDQ $32, R12 + CMPQ R12, R9 + JB copySuffixLoop + JMP next + +// func decodeByteArrayAVX2x128bits(dst, src []byte, prefix, suffix []int32) int +TEXT ·decodeByteArrayAVX2x128bits(SB), NOSPLIT, $0-104 + MOVQ dst_base+0(FP), AX + MOVQ src_base+24(FP), BX + MOVQ prefix_base+48(FP), CX + MOVQ suffix_base+72(FP), DX + MOVQ suffix_len+80(FP), DI + + XORQ SI, SI + XORQ R8, R8 + XORQ R9, R9 + VPXOR X0, X0, X0 + + JMP test +loop: + MOVLQZX (CX)(SI*4), R8 // prefix length + MOVLQZX (DX)(SI*4), R9 // suffix length + + VMOVDQU (BX), X1 + VMOVDQU X0, (AX) + VMOVDQU X1, (AX)(R8*1) + VMOVDQU (AX), X0 + + ADDQ R9, R8 + LEAQ (AX)(R8*1), AX + LEAQ (BX)(R9*1), BX + INCQ SI +test: + CMPQ SI, DI + JNE loop + MOVQ dst_base+0(FP), BX + SUBQ BX, AX + MOVQ AX, ret+96(FP) + VZEROUPPER + RET diff --git a/vendor/github.com/parquet-go/parquet-go/encoding/delta/byte_array_purego.go b/vendor/github.com/parquet-go/parquet-go/encoding/delta/byte_array_purego.go new file mode 100644 index 000000000..972c1feca --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/encoding/delta/byte_array_purego.go @@ -0,0 +1,63 @@ +//go:build purego || !amd64 + +package delta + +func decodeByteArray(dst, src []byte, prefix, suffix []int32, offsets []uint32) ([]byte, []uint32, error) { + _ = prefix[:len(suffix)] + _ = suffix[:len(prefix)] + + var lastValue []byte + for i := range suffix { + n := int(suffix[i]) + p := int(prefix[i]) + if n < 0 { + return dst, offsets, errInvalidNegativeValueLength(n) + } + if n > len(src) { + return dst, offsets, errValueLengthOutOfBounds(n, len(src)) + } + if p < 0 { + return dst, offsets, errInvalidNegativePrefixLength(p) + } + if p > len(lastValue) { + return dst, offsets, errPrefixLengthOutOfBounds(p, len(lastValue)) + } + j := len(dst) + offsets = append(offsets, uint32(j)) + dst = append(dst, lastValue[:p]...) + dst = append(dst, src[:n]...) + lastValue = dst[j:] + src = src[n:] + } + + return dst, append(offsets, uint32(len(dst))), nil +} + +func decodeFixedLenByteArray(dst, src []byte, size int, prefix, suffix []int32) ([]byte, error) { + _ = prefix[:len(suffix)] + _ = suffix[:len(prefix)] + + var lastValue []byte + for i := range suffix { + n := int(suffix[i]) + p := int(prefix[i]) + if n < 0 { + return dst, errInvalidNegativeValueLength(n) + } + if n > len(src) { + return dst, errValueLengthOutOfBounds(n, len(src)) + } + if p < 0 { + return dst, errInvalidNegativePrefixLength(p) + } + if p > len(lastValue) { + return dst, errPrefixLengthOutOfBounds(p, len(lastValue)) + } + j := len(dst) + dst = append(dst, lastValue[:p]...) + dst = append(dst, src[:n]...) + lastValue = dst[j:] + src = src[n:] + } + return dst, nil +} diff --git a/vendor/github.com/parquet-go/parquet-go/encoding/delta/delta.go b/vendor/github.com/parquet-go/parquet-go/encoding/delta/delta.go new file mode 100644 index 000000000..75a20c891 --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/encoding/delta/delta.go @@ -0,0 +1,99 @@ +package delta + +import ( + "fmt" + + "github.com/parquet-go/bitpack/unsafecast" + "github.com/parquet-go/parquet-go/internal/memory" +) + +type int32Buffer struct { + values []int32 +} + +func (buf *int32Buffer) resize(size int) { + if cap(buf.values) < size { + buf.values = make([]int32, size, 2*size) + } else { + buf.values = buf.values[:size] + } +} + +func (buf *int32Buffer) decode(src []byte) ([]byte, error) { + values, remain, err := decodeInt32(unsafecast.Slice[byte](buf.values[:0]), src) + buf.values = unsafecast.Slice[int32](values) + return remain, err +} + +func (buf *int32Buffer) sum() (sum int32) { + for _, v := range buf.values { + sum += v + } + return sum +} + +var ( + int32BufferPool memory.Pool[int32Buffer] +) + +func getInt32Buffer() *int32Buffer { + return int32BufferPool.Get( + func() *int32Buffer { + return &int32Buffer{ + values: make([]int32, 0, 1024), + } + }, + func(b *int32Buffer) { b.values = b.values[:0] }, + ) +} + +func putInt32Buffer(b *int32Buffer) { + int32BufferPool.Put(b) +} + +func resizeNoMemclr(buf []byte, size int) []byte { + if cap(buf) < size { + return grow(buf, size) + } + return buf[:size] +} + +func resize(buf []byte, size int) []byte { + if cap(buf) < size { + return grow(buf, size) + } + if size > len(buf) { + clear := buf[len(buf):size] + for i := range clear { + clear[i] = 0 + } + } + return buf[:size] +} + +func grow(buf []byte, size int) []byte { + newCap := max(2*cap(buf), size) + newBuf := make([]byte, size, newCap) + copy(newBuf, buf) + return newBuf +} + +func errPrefixAndSuffixLengthMismatch(prefixLength, suffixLength int) error { + return fmt.Errorf("length of prefix and suffix mismatch: %d != %d", prefixLength, suffixLength) +} + +func errInvalidNegativeValueLength(length int) error { + return fmt.Errorf("invalid negative value length: %d", length) +} + +func errInvalidNegativePrefixLength(length int) error { + return fmt.Errorf("invalid negative prefix length: %d", length) +} + +func errValueLengthOutOfBounds(length, maxLength int) error { + return fmt.Errorf("value length is larger than the input size: %d > %d", length, maxLength) +} + +func errPrefixLengthOutOfBounds(length, maxLength int) error { + return fmt.Errorf("prefix length %d is larger than the last value of size %d", length, maxLength) +} diff --git a/vendor/github.com/parquet-go/parquet-go/encoding/delta/delta_amd64.go b/vendor/github.com/parquet-go/parquet-go/encoding/delta/delta_amd64.go new file mode 100644 index 000000000..864aeac13 --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/encoding/delta/delta_amd64.go @@ -0,0 +1,16 @@ +//go:build !purego + +package delta + +const ( + padding = 64 +) + +func findNegativeLength(lengths []int32) int { + for _, n := range lengths { + if n < 0 { + return int(n) + } + } + return -1 +} diff --git a/vendor/github.com/parquet-go/parquet-go/encoding/delta/delta_amd64.s b/vendor/github.com/parquet-go/parquet-go/encoding/delta/delta_amd64.s new file mode 100644 index 000000000..e8748a263 --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/encoding/delta/delta_amd64.s @@ -0,0 +1,13 @@ +//go:build !purego + +#include "textflag.h" + +GLOBL ·rotateLeft32(SB), RODATA|NOPTR, $32 +DATA ·rotateLeft32+0(SB)/4, $7 +DATA ·rotateLeft32+4(SB)/4, $0 +DATA ·rotateLeft32+8(SB)/4, $1 +DATA ·rotateLeft32+12(SB)/4, $2 +DATA ·rotateLeft32+16(SB)/4, $3 +DATA ·rotateLeft32+20(SB)/4, $4 +DATA ·rotateLeft32+24(SB)/4, $5 +DATA ·rotateLeft32+28(SB)/4, $6 diff --git a/vendor/github.com/parquet-go/parquet-go/encoding/delta/length_byte_array.go b/vendor/github.com/parquet-go/parquet-go/encoding/delta/length_byte_array.go new file mode 100644 index 000000000..65ed6f79b --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/encoding/delta/length_byte_array.go @@ -0,0 +1,81 @@ +package delta + +import ( + "github.com/parquet-go/parquet-go/encoding" + "github.com/parquet-go/parquet-go/format" +) + +type LengthByteArrayEncoding struct { + encoding.NotSupported +} + +func (e *LengthByteArrayEncoding) String() string { + return "DELTA_LENGTH_BYTE_ARRAY" +} + +func (e *LengthByteArrayEncoding) Encoding() format.Encoding { + return format.DeltaLengthByteArray +} + +func (e *LengthByteArrayEncoding) EncodeByteArray(dst []byte, src []byte, offsets []uint32) ([]byte, error) { + if len(offsets) == 0 { + return dst[:0], nil + } + + length := getInt32Buffer() + defer putInt32Buffer(length) + + length.resize(len(offsets) - 1) + encodeByteArrayLengths(length.values, offsets) + + dst = dst[:0] + dst = encodeInt32(dst, length.values) + dst = append(dst, src...) + return dst, nil +} + +func (e *LengthByteArrayEncoding) DecodeByteArray(dst []byte, src []byte, offsets []uint32) ([]byte, []uint32, error) { + dst, offsets = dst[:0], offsets[:0] + + length := getInt32Buffer() + defer putInt32Buffer(length) + + src, err := length.decode(src) + if err != nil { + return dst, offsets, e.wrap(err) + } + + if size := len(length.values) + 1; cap(offsets) < size { + offsets = make([]uint32, size, 2*size) + } else { + offsets = offsets[:size] + } + + lastOffset, invalidLength := decodeByteArrayLengths(offsets, length.values) + if invalidLength != 0 { + return dst, offsets, e.wrap(errInvalidNegativeValueLength(int(invalidLength))) + } + if int(lastOffset) > len(src) { + return dst, offsets, e.wrap(errValueLengthOutOfBounds(int(lastOffset), len(src))) + } + + return append(dst, src[:lastOffset]...), offsets, nil +} + +func (e *LengthByteArrayEncoding) EstimateDecodeByteArraySize(src []byte) int { + length := getInt32Buffer() + defer putInt32Buffer(length) + length.decode(src) + return int(length.sum()) +} + +func (e *LengthByteArrayEncoding) CanDecodeInPlace() bool { + return true +} + +func (e *LengthByteArrayEncoding) wrap(err error) error { + if err != nil { + err = encoding.Error(e, err) + } + return err +} diff --git a/vendor/github.com/parquet-go/parquet-go/encoding/delta/length_byte_array_amd64.go b/vendor/github.com/parquet-go/parquet-go/encoding/delta/length_byte_array_amd64.go new file mode 100644 index 000000000..905e8516e --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/encoding/delta/length_byte_array_amd64.go @@ -0,0 +1,9 @@ +//go:build !purego + +package delta + +//go:noescape +func encodeByteArrayLengths(lengths []int32, offsets []uint32) + +//go:noescape +func decodeByteArrayLengths(offsets []uint32, lengths []int32) (lastOffset uint32, invalidLength int32) diff --git a/vendor/github.com/parquet-go/parquet-go/encoding/delta/length_byte_array_amd64.s b/vendor/github.com/parquet-go/parquet-go/encoding/delta/length_byte_array_amd64.s new file mode 100644 index 000000000..bc6292e2a --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/encoding/delta/length_byte_array_amd64.s @@ -0,0 +1,122 @@ +//go:build !purego + +#include "textflag.h" + +// func encodeByteArrayLengths(lengths []int32, offsets []uint32) +TEXT ·encodeByteArrayLengths(SB), NOSPLIT, $0-48 + MOVQ lengths_base+0(FP), AX + MOVQ lengths_len+8(FP), CX + MOVQ offsets_base+24(FP), BX + XORQ SI, SI + + CMPQ CX, $4 + JB test + + MOVQ CX, DX + SHRQ $2, DX + SHLQ $2, DX + +loopSSE2: + MOVOU 0(BX)(SI*4), X0 + MOVOU 4(BX)(SI*4), X1 + PSUBL X0, X1 + MOVOU X1, (AX)(SI*4) + ADDQ $4, SI + CMPQ SI, DX + JNE loopSSE2 + JMP test +loop: + MOVL 0(BX)(SI*4), R8 + MOVL 4(BX)(SI*4), R9 + SUBL R8, R9 + MOVL R9, (AX)(SI*4) + INCQ SI +test: + CMPQ SI, CX + JNE loop + RET + +// func decodeByteArrayLengths(offsets []uint32, length []int32) (lastOffset uint32, invalidLength int32) +TEXT ·decodeByteArrayLengths(SB), NOSPLIT, $0-56 + MOVQ offsets_base+0(FP), AX + MOVQ lengths_base+24(FP), BX + MOVQ lengths_len+32(FP), CX + + XORQ DX, DX // lastOffset + XORQ DI, DI // invalidLength + XORQ SI, SI + + CMPQ CX, $4 + JL test + + MOVQ CX, R8 + SHRQ $2, R8 + SHLQ $2, R8 + + MOVL $0, (AX) + PXOR X0, X0 + PXOR X3, X3 + // This loop computes the prefix sum of the lengths array in order to + // generate values of the offsets array. + // + // We stick to SSE2 to keep the code simple (the Go compiler appears to + // assume that SSE2 must be supported on AMD64) which already yields most + // of the performance that we could get on this subroutine if we were using + // AVX2. + // + // The X3 register also accumulates a mask of all length values, which is + // checked after the loop to determine whether any of the lengths were + // negative. + // + // The following article contains a description of the prefix sum algorithm + // used in this function: https://en.algorithmica.org/hpc/algorithms/prefix/ +loopSSE2: + MOVOU (BX)(SI*4), X1 + POR X1, X3 + + MOVOA X1, X2 + PSLLDQ $4, X2 + PADDD X2, X1 + + MOVOA X1, X2 + PSLLDQ $8, X2 + PADDD X2, X1 + + PADDD X1, X0 + MOVOU X0, 4(AX)(SI*4) + + PSHUFD $0b11111111, X0, X0 + + ADDQ $4, SI + CMPQ SI, R8 + JNE loopSSE2 + + // If any of the most significant bits of double words in the X3 register + // are set to 1, it indicates that one of the lengths was negative and + // therefore the prefix sum is invalid. + // + // TODO: we report the invalid length as -1, effectively losing the original + // value due to the aggregation within X3. This is something that we might + // want to address in the future to provide better error reporting. + MOVMSKPS X3, R8 + MOVL $-1, R9 + CMPL R8, $0 + CMOVLNE R9, DI + + MOVQ X0, DX + JMP test +loop: + MOVL (BX)(SI*4), R8 + MOVL DX, (AX)(SI*4) + ADDL R8, DX + CMPL R8, $0 + CMOVLLT R8, DI + INCQ SI +test: + CMPQ SI, CX + JNE loop + + MOVL DX, (AX)(SI*4) + MOVL DX, lastOffset+48(FP) + MOVL DI, invalidLength+52(FP) + RET diff --git a/vendor/github.com/parquet-go/parquet-go/encoding/delta/length_byte_array_purego.go b/vendor/github.com/parquet-go/parquet-go/encoding/delta/length_byte_array_purego.go new file mode 100644 index 000000000..0c0fb6bae --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/encoding/delta/length_byte_array_purego.go @@ -0,0 +1,24 @@ +//go:build purego || !amd64 + +package delta + +func encodeByteArrayLengths(lengths []int32, offsets []uint32) { + for i := range lengths { + lengths[i] = int32(offsets[i+1] - offsets[i]) + } +} + +func decodeByteArrayLengths(offsets []uint32, lengths []int32) (uint32, int32) { + lastOffset := uint32(0) + + for i, n := range lengths { + if n < 0 { + return lastOffset, n + } + offsets[i] = lastOffset + lastOffset += uint32(n) + } + + offsets[len(lengths)] = lastOffset + return lastOffset, 0 +} diff --git a/vendor/github.com/parquet-go/parquet-go/encoding/encoding.go b/vendor/github.com/parquet-go/parquet-go/encoding/encoding.go new file mode 100644 index 000000000..a919f5917 --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/encoding/encoding.go @@ -0,0 +1,72 @@ +// Package encoding provides the generic APIs implemented by parquet encodings +// in its sub-packages. +package encoding + +import ( + "math" + + "github.com/parquet-go/parquet-go/deprecated" + "github.com/parquet-go/parquet-go/format" +) + +const ( + MaxFixedLenByteArraySize = math.MaxInt16 +) + +// The Encoding interface is implemented by types representing parquet column +// encodings. +// +// Encoding instances must be safe to use concurrently from multiple goroutines. +type Encoding interface { + // Returns a human-readable name for the encoding. + String() string + + // Returns the parquet code representing the encoding. + Encoding() format.Encoding + + // Encode methods serialize the source sequence of values into the + // destination buffer, potentially reallocating it if it was too short to + // contain the output. + // + // The methods panic if the type of src values differ from the type of + // values being encoded. + EncodeLevels(dst []byte, src []uint8) ([]byte, error) + EncodeBoolean(dst []byte, src []byte) ([]byte, error) + EncodeInt32(dst []byte, src []int32) ([]byte, error) + EncodeInt64(dst []byte, src []int64) ([]byte, error) + EncodeInt96(dst []byte, src []deprecated.Int96) ([]byte, error) + EncodeFloat(dst []byte, src []float32) ([]byte, error) + EncodeDouble(dst []byte, src []float64) ([]byte, error) + EncodeByteArray(dst []byte, src []byte, offsets []uint32) ([]byte, error) + EncodeFixedLenByteArray(dst []byte, src []byte, size int) ([]byte, error) + + // Decode methods deserialize from the source buffer into the destination + // slice, potentially growing it if it was too short to contain the result. + // + // The methods panic if the type of dst values differ from the type of + // values being decoded. + DecodeLevels(dst []uint8, src []byte) ([]uint8, error) + DecodeBoolean(dst []byte, src []byte) ([]byte, error) + DecodeInt32(dst []int32, src []byte) ([]int32, error) + DecodeInt64(dst []int64, src []byte) ([]int64, error) + DecodeInt96(dst []deprecated.Int96, src []byte) ([]deprecated.Int96, error) + DecodeFloat(dst []float32, src []byte) ([]float32, error) + DecodeDouble(dst []float64, src []byte) ([]float64, error) + DecodeByteArray(dst []byte, src []byte, offsets []uint32) ([]byte, []uint32, error) + DecodeFixedLenByteArray(dst []byte, src []byte, size int) ([]byte, error) + + // Computes an estimation of the output size of decoding the encoded page + // of values passed as argument. + // + // Note that this is an estimate, it is useful to preallocate the output + // buffer that will be passed to the decode method, but the actual output + // size may be different. + // + // The estimate never errors since it is not intended to be used as an + // input validation method. + EstimateDecodeByteArraySize(src []byte) int + + // When this method returns true, the encoding supports receiving the same + // buffer as source and destination. + CanDecodeInPlace() bool +} diff --git a/vendor/github.com/parquet-go/parquet-go/encoding/notsupported.go b/vendor/github.com/parquet-go/parquet-go/encoding/notsupported.go new file mode 100644 index 000000000..59215330d --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/encoding/notsupported.go @@ -0,0 +1,213 @@ +package encoding + +import ( + "errors" + "fmt" + + "github.com/parquet-go/parquet-go/deprecated" + "github.com/parquet-go/parquet-go/format" +) + +var ( + // ErrNotSupported is an error returned when the underlying encoding does + // not support the type of values being encoded or decoded. + // + // This error may be wrapped with type information, applications must use + // errors.Is rather than equality comparisons to test the error values + // returned by encoders and decoders. + ErrNotSupported = errors.New("encoding not supported") + + // ErrInvalidArgument is an error returned one or more arguments passed to + // the encoding functions are incorrect. + // + // As with ErrNotSupported, this error may be wrapped with specific + // information about the problem and applications are expected to use + // errors.Is for comparisons. + ErrInvalidArgument = errors.New("invalid argument") +) + +// Error constructs an error which wraps err and indicates that it originated +// from the given encoding. +func Error(e Encoding, err error) error { + return fmt.Errorf("%s: %w", e, err) +} + +// Errorf is like Error but constructs the error message from the given format +// and arguments. +func Errorf(e Encoding, msg string, args ...any) error { + return Error(e, fmt.Errorf(msg, args...)) +} + +// ErrEncodeInvalidInputSize constructs an error indicating that encoding failed +// due to the size of the input. +func ErrEncodeInvalidInputSize(e Encoding, typ string, size int) error { + return errInvalidInputSize(e, "encode", typ, size) +} + +// ErrDecodeInvalidInputSize constructs an error indicating that decoding failed +// due to the size of the input. +func ErrDecodeInvalidInputSize(e Encoding, typ string, size int) error { + return errInvalidInputSize(e, "decode", typ, size) +} + +func errInvalidInputSize(e Encoding, op, typ string, size int) error { + return Errorf(e, "cannot %s %s from input of size %d: %w", op, typ, size, ErrInvalidArgument) +} + +// CanEncodeInt8 reports whether e can encode LEVELS values. +func CanEncodeLevels(e Encoding) bool { + _, err := e.EncodeLevels(nil, nil) + return !errors.Is(err, ErrNotSupported) +} + +// CanEncodeBoolean reports whether e can encode BOOLEAN values. +func CanEncodeBoolean(e Encoding) bool { + _, err := e.EncodeBoolean(nil, nil) + return !errors.Is(err, ErrNotSupported) +} + +// CanEncodeInt32 reports whether e can encode INT32 values. +func CanEncodeInt32(e Encoding) bool { + _, err := e.EncodeInt32(nil, nil) + return !errors.Is(err, ErrNotSupported) +} + +// CanEncodeInt64 reports whether e can encode INT64 values. +func CanEncodeInt64(e Encoding) bool { + _, err := e.EncodeInt64(nil, nil) + return !errors.Is(err, ErrNotSupported) +} + +// CanEncodeInt96 reports whether e can encode INT96 values. +func CanEncodeInt96(e Encoding) bool { + _, err := e.EncodeInt96(nil, nil) + return !errors.Is(err, ErrNotSupported) +} + +// CanEncodeFloat reports whether e can encode FLOAT values. +func CanEncodeFloat(e Encoding) bool { + _, err := e.EncodeFloat(nil, nil) + return !errors.Is(err, ErrNotSupported) +} + +// CanEncodeDouble reports whether e can encode DOUBLE values. +func CanEncodeDouble(e Encoding) bool { + _, err := e.EncodeDouble(nil, nil) + return !errors.Is(err, ErrNotSupported) +} + +// CanEncodeByteArray reports whether e can encode BYTE_ARRAY values. +func CanEncodeByteArray(e Encoding) bool { + _, err := e.EncodeByteArray(nil, nil, zeroOffsets[:]) + return !errors.Is(err, ErrNotSupported) +} + +// CanEncodeFixedLenByteArray reports whether e can encode +// FIXED_LEN_BYTE_ARRAY values. +func CanEncodeFixedLenByteArray(e Encoding) bool { + _, err := e.EncodeFixedLenByteArray(nil, nil, 1) + return !errors.Is(err, ErrNotSupported) +} + +var zeroOffsets [1]uint32 + +// NotSupported is a type satisfying the Encoding interface which does not +// support encoding nor decoding any value types. +type NotSupported struct { +} + +func (NotSupported) String() string { + return "NOT_SUPPORTED" +} + +func (NotSupported) Encoding() format.Encoding { + return -1 +} + +func (NotSupported) EncodeLevels(dst []byte, src []uint8) ([]byte, error) { + return dst[:0], errNotSupported("LEVELS") +} + +func (NotSupported) EncodeBoolean(dst []byte, src []byte) ([]byte, error) { + return dst[:0], errNotSupported("BOOLEAN") +} + +func (NotSupported) EncodeInt32(dst []byte, src []int32) ([]byte, error) { + return dst[:0], errNotSupported("INT32") +} + +func (NotSupported) EncodeInt64(dst []byte, src []int64) ([]byte, error) { + return dst[:0], errNotSupported("INT64") +} + +func (NotSupported) EncodeInt96(dst []byte, src []deprecated.Int96) ([]byte, error) { + return dst[:0], errNotSupported("INT96") +} + +func (NotSupported) EncodeFloat(dst []byte, src []float32) ([]byte, error) { + return dst[:0], errNotSupported("FLOAT") +} + +func (NotSupported) EncodeDouble(dst []byte, src []float64) ([]byte, error) { + return dst[:0], errNotSupported("DOUBLE") +} + +func (NotSupported) EncodeByteArray(dst []byte, src []byte, offsets []uint32) ([]byte, error) { + return dst[:0], errNotSupported("BYTE_ARRAY") +} + +func (NotSupported) EncodeFixedLenByteArray(dst []byte, src []byte, size int) ([]byte, error) { + return dst[:0], errNotSupported("FIXED_LEN_BYTE_ARRAY") +} + +func (NotSupported) DecodeLevels(dst []uint8, src []byte) ([]uint8, error) { + return dst, errNotSupported("LEVELS") +} + +func (NotSupported) DecodeBoolean(dst []byte, src []byte) ([]byte, error) { + return dst, errNotSupported("BOOLEAN") +} + +func (NotSupported) DecodeInt32(dst []int32, src []byte) ([]int32, error) { + return dst, errNotSupported("INT32") +} + +func (NotSupported) DecodeInt64(dst []int64, src []byte) ([]int64, error) { + return dst, errNotSupported("INT64") +} + +func (NotSupported) DecodeInt96(dst []deprecated.Int96, src []byte) ([]deprecated.Int96, error) { + return dst, errNotSupported("INT96") +} + +func (NotSupported) DecodeFloat(dst []float32, src []byte) ([]float32, error) { + return dst, errNotSupported("FLOAT") +} + +func (NotSupported) DecodeDouble(dst []float64, src []byte) ([]float64, error) { + return dst, errNotSupported("DOUBLE") +} + +func (NotSupported) DecodeByteArray(dst []byte, src []byte, offsets []uint32) ([]byte, []uint32, error) { + return dst, offsets, errNotSupported("BYTE_ARRAY") +} + +func (NotSupported) DecodeFixedLenByteArray(dst []byte, src []byte, size int) ([]byte, error) { + return dst, errNotSupported("FIXED_LEN_BYTE_ARRAY") +} + +func (NotSupported) EstimateDecodeByteArraySize(src []byte) int { + return 0 +} + +func (NotSupported) CanDecodeInPlace() bool { + return false +} + +func errNotSupported(typ string) error { + return fmt.Errorf("%w for type %s", ErrNotSupported, typ) +} + +var ( + _ Encoding = NotSupported{} +) diff --git a/vendor/github.com/parquet-go/parquet-go/encoding/plain/dictionary.go b/vendor/github.com/parquet-go/parquet-go/encoding/plain/dictionary.go new file mode 100644 index 000000000..4946a7929 --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/encoding/plain/dictionary.go @@ -0,0 +1,27 @@ +package plain + +import ( + "github.com/parquet-go/parquet-go/encoding" + "github.com/parquet-go/parquet-go/format" +) + +type DictionaryEncoding struct { + encoding.NotSupported + plain Encoding +} + +func (e *DictionaryEncoding) String() string { + return "PLAIN_DICTIONARY" +} + +func (e *DictionaryEncoding) Encoding() format.Encoding { + return format.PlainDictionary +} + +func (e *DictionaryEncoding) EncodeInt32(dst []byte, src []int32) ([]byte, error) { + return e.plain.EncodeInt32(dst, src) +} + +func (e *DictionaryEncoding) DecodeInt32(dst []int32, src []byte) ([]int32, error) { + return e.plain.DecodeInt32(dst, src) +} diff --git a/vendor/github.com/parquet-go/parquet-go/encoding/plain/plain.go b/vendor/github.com/parquet-go/parquet-go/encoding/plain/plain.go new file mode 100644 index 000000000..c16aa2b7a --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/encoding/plain/plain.go @@ -0,0 +1,246 @@ +// Package plain implements the PLAIN parquet encoding. +// +// https://github.com/apache/parquet-format/blob/master/Encodings.md#plain-plain--0 +package plain + +import ( + "encoding/binary" + "fmt" + "io" + "math" + + "github.com/parquet-go/bitpack/unsafecast" + "github.com/parquet-go/parquet-go/deprecated" + "github.com/parquet-go/parquet-go/encoding" + "github.com/parquet-go/parquet-go/format" +) + +const ( + ByteArrayLengthSize = 4 + MaxByteArrayLength = math.MaxInt32 +) + +type Encoding struct { + encoding.NotSupported +} + +func (e *Encoding) String() string { + return "PLAIN" +} + +func (e *Encoding) Encoding() format.Encoding { + return format.Plain +} + +func (e *Encoding) EncodeBoolean(dst []byte, src []byte) ([]byte, error) { + return append(dst[:0], src...), nil +} + +func (e *Encoding) EncodeInt96(dst []byte, src []deprecated.Int96) ([]byte, error) { + return append(dst[:0], unsafecast.Slice[byte](src)...), nil +} + +func (e *Encoding) EncodeByteArray(dst []byte, src []byte, offsets []uint32) ([]byte, error) { + dst = dst[:0] + + if len(offsets) > 0 { + baseOffset := offsets[0] + + for _, endOffset := range offsets[1:] { + dst = AppendByteArray(dst, src[baseOffset:endOffset:endOffset]) + baseOffset = endOffset + } + } + + return dst, nil +} + +func (e *Encoding) EncodeFixedLenByteArray(dst []byte, src []byte, size int) ([]byte, error) { + if size < 0 || size > encoding.MaxFixedLenByteArraySize { + return dst[:0], encoding.Error(e, encoding.ErrInvalidArgument) + } + return append(dst[:0], src...), nil +} + +func (e *Encoding) DecodeBoolean(dst []byte, src []byte) ([]byte, error) { + return append(dst[:0], src...), nil +} + +func (e *Encoding) DecodeInt96(dst []deprecated.Int96, src []byte) ([]deprecated.Int96, error) { + if (len(src) % 12) != 0 { + return dst, encoding.ErrDecodeInvalidInputSize(e, "INT96", len(src)) + } + return append(dst[:0], unsafecast.Slice[deprecated.Int96](src)...), nil +} + +func (e *Encoding) DecodeByteArray(dst []byte, src []byte, offsets []uint32) ([]byte, []uint32, error) { + dst, offsets = dst[:0], offsets[:0] + + for i := 0; i < len(src); { + if (len(src) - i) < ByteArrayLengthSize { + return dst, offsets, ErrTooShort(len(src)) + } + n := ByteArrayLength(src[i:]) + if n > (len(src) - ByteArrayLengthSize) { + return dst, offsets, ErrTooShort(len(src)) + } + i += ByteArrayLengthSize + offsets = append(offsets, uint32(len(dst))) + dst = append(dst, src[i:i+n]...) + i += n + } + + return dst, append(offsets, uint32(len(dst))), nil +} + +func (e *Encoding) DecodeFixedLenByteArray(dst []byte, src []byte, size int) ([]byte, error) { + if size < 0 || size > encoding.MaxFixedLenByteArraySize { + return dst, encoding.Error(e, encoding.ErrInvalidArgument) + } + if (len(src) % size) != 0 { + return dst, encoding.ErrDecodeInvalidInputSize(e, "FIXED_LEN_BYTE_ARRAY", len(src)) + } + return append(dst[:0], src...), nil +} + +func (e *Encoding) EstimateDecodeByteArraySize(src []byte) int { + return len(src) +} + +func (e *Encoding) CanDecodeInPlace() bool { + return true +} + +func Boolean(v bool) []byte { return AppendBoolean(nil, 0, v) } + +func Int32(v int32) []byte { return AppendInt32(nil, v) } + +func Int64(v int64) []byte { return AppendInt64(nil, v) } + +func Int96(v deprecated.Int96) []byte { return AppendInt96(nil, v) } + +func Float(v float32) []byte { return AppendFloat(nil, v) } + +func Double(v float64) []byte { return AppendDouble(nil, v) } + +func ByteArray(v []byte) []byte { return AppendByteArray(nil, v) } + +func AppendBoolean(b []byte, n int, v bool) []byte { + i := n / 8 + j := n % 8 + + if cap(b) > i { + b = b[:i+1] + } else { + tmp := make([]byte, i+1, 2*(i+1)) + copy(tmp, b) + b = tmp + } + + k := uint(j) + x := byte(0) + if v { + x = 1 + } + + b[i] = (b[i] & ^(1 << k)) | (x << k) + return b +} + +func AppendInt32(b []byte, v int32) []byte { + x := [4]byte{} + binary.LittleEndian.PutUint32(x[:], uint32(v)) + return append(b, x[:]...) +} + +func AppendInt64(b []byte, v int64) []byte { + x := [8]byte{} + binary.LittleEndian.PutUint64(x[:], uint64(v)) + return append(b, x[:]...) +} + +func AppendInt96(b []byte, v deprecated.Int96) []byte { + x := [12]byte{} + binary.LittleEndian.PutUint32(x[0:4], v[0]) + binary.LittleEndian.PutUint32(x[4:8], v[1]) + binary.LittleEndian.PutUint32(x[8:12], v[2]) + return append(b, x[:]...) +} + +func AppendFloat(b []byte, v float32) []byte { + x := [4]byte{} + binary.LittleEndian.PutUint32(x[:], math.Float32bits(v)) + return append(b, x[:]...) +} + +func AppendDouble(b []byte, v float64) []byte { + x := [8]byte{} + binary.LittleEndian.PutUint64(x[:], math.Float64bits(v)) + return append(b, x[:]...) +} + +func AppendByteArray(b, v []byte) []byte { + length := [ByteArrayLengthSize]byte{} + PutByteArrayLength(length[:], len(v)) + b = append(b, length[:]...) + b = append(b, v...) + return b +} + +func AppendByteArrayString(b []byte, v string) []byte { + length := [ByteArrayLengthSize]byte{} + PutByteArrayLength(length[:], len(v)) + b = append(b, length[:]...) + b = append(b, v...) + return b +} + +func AppendByteArrayLength(b []byte, n int) []byte { + length := [ByteArrayLengthSize]byte{} + PutByteArrayLength(length[:], n) + return append(b, length[:]...) +} + +func ByteArrayLength(b []byte) int { + return int(binary.LittleEndian.Uint32(b)) +} + +func PutByteArrayLength(b []byte, n int) { + binary.LittleEndian.PutUint32(b, uint32(n)) +} + +func RangeByteArray(b []byte, do func([]byte) error) (err error) { + for len(b) > 0 { + var v []byte + if v, b, err = NextByteArray(b); err != nil { + return err + } + if err = do(v); err != nil { + return err + } + } + return nil +} + +func NextByteArray(b []byte) (v, r []byte, err error) { + if len(b) < ByteArrayLengthSize { + return nil, b, ErrTooShort(len(b)) + } + n := ByteArrayLength(b) + if n > (len(b) - ByteArrayLengthSize) { + return nil, b, ErrTooShort(len(b)) + } + if n > MaxByteArrayLength { + return nil, b, ErrTooLarge(n) + } + n += ByteArrayLengthSize + return b[ByteArrayLengthSize:n:n], b[n:len(b):len(b)], nil +} + +func ErrTooShort(length int) error { + return fmt.Errorf("input of length %d is too short to contain a PLAIN encoded byte array value: %w", length, io.ErrUnexpectedEOF) +} + +func ErrTooLarge(length int) error { + return fmt.Errorf("byte array of length %d is too large to be encoded", length) +} diff --git a/vendor/github.com/parquet-go/parquet-go/encoding/plain/plain_be.go b/vendor/github.com/parquet-go/parquet-go/encoding/plain/plain_be.go new file mode 100644 index 000000000..6c8c9000b --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/encoding/plain/plain_be.go @@ -0,0 +1,113 @@ +//go:build s390x + +package plain + +import ( + "encoding/binary" + "math" + + "github.com/parquet-go/parquet-go/encoding" +) + +// TODO: optimize by doing the byte swap in the output slice instead of +// allocating a temporay buffer. + +func (e *Encoding) EncodeInt32(dst []byte, src []int32) ([]byte, error) { + srcLen := len(src) + byteEnc := make([]byte, (srcLen * 4)) + idx := 0 + for k := range srcLen { + binary.LittleEndian.PutUint32(byteEnc[idx:(4+idx)], uint32((src)[k])) + idx += 4 + } + return append(dst[:0], (byteEnc)...), nil +} + +func (e *Encoding) EncodeInt64(dst []byte, src []int64) ([]byte, error) { + srcLen := len(src) + byteEnc := make([]byte, (srcLen * 8)) + idx := 0 + for k := range srcLen { + binary.LittleEndian.PutUint64(byteEnc[idx:(8+idx)], uint64((src)[k])) + idx += 8 + } + return append(dst[:0], (byteEnc)...), nil +} + +func (e *Encoding) EncodeFloat(dst []byte, src []float32) ([]byte, error) { + srcLen := len(src) + byteEnc := make([]byte, (srcLen * 4)) + idx := 0 + for k := range srcLen { + binary.LittleEndian.PutUint32(byteEnc[idx:(4+idx)], math.Float32bits((src)[k])) + idx += 4 + } + return append(dst[:0], (byteEnc)...), nil +} + +func (e *Encoding) EncodeDouble(dst []byte, src []float64) ([]byte, error) { + srcLen := len(src) + byteEnc := make([]byte, (srcLen * 8)) + idx := 0 + for k := range srcLen { + binary.LittleEndian.PutUint64(byteEnc[idx:(8+idx)], math.Float64bits((src)[k])) + idx += 8 + } + return append(dst[:0], (byteEnc)...), nil +} + +func (e *Encoding) DecodeInt32(dst []int32, src []byte) ([]int32, error) { + if (len(src) % 4) != 0 { + return dst, encoding.ErrDecodeInvalidInputSize(e, "INT32", len(src)) + } + srcLen := (len(src) / 4) + byteDec := make([]int32, srcLen) + idx := 0 + for k := range srcLen { + byteDec[k] = int32(binary.LittleEndian.Uint32((src)[idx:(4 + idx)])) + idx += 4 + } + return append(dst[:0], (byteDec)...), nil +} + +func (e *Encoding) DecodeInt64(dst []int64, src []byte) ([]int64, error) { + if (len(src) % 8) != 0 { + return dst, encoding.ErrDecodeInvalidInputSize(e, "INT64", len(src)) + } + srcLen := (len(src) / 8) + byteDec := make([]int64, srcLen) + idx := 0 + for k := range srcLen { + byteDec[k] = int64(binary.LittleEndian.Uint64((src)[idx:(8 + idx)])) + idx += 8 + } + return append(dst[:0], (byteDec)...), nil +} + +func (e *Encoding) DecodeFloat(dst []float32, src []byte) ([]float32, error) { + if (len(src) % 4) != 0 { + return dst, encoding.ErrDecodeInvalidInputSize(e, "FLOAT", len(src)) + } + srcLen := (len(src) / 4) + byteDec := make([]float32, srcLen) + idx := 0 + for k := range srcLen { + byteDec[k] = float32(math.Float32frombits(binary.LittleEndian.Uint32((src)[idx:(4 + idx)]))) + idx += 4 + } + return append(dst[:0], (byteDec)...), nil +} + +func (e *Encoding) DecodeDouble(dst []float64, src []byte) ([]float64, error) { + if (len(src) % 8) != 0 { + return dst, encoding.ErrDecodeInvalidInputSize(e, "DOUBLE", len(src)) + } + srcLen := (len(src) / 8) + byteDec := make([]float64, srcLen) + idx := 0 + for k := range srcLen { + byteDec[k] = float64(math.Float64frombits(binary.LittleEndian.Uint64((src)[idx:(8 + idx)]))) + idx += 8 + } + return append(dst[:0], (byteDec)...), nil +} diff --git a/vendor/github.com/parquet-go/parquet-go/encoding/plain/plain_le.go b/vendor/github.com/parquet-go/parquet-go/encoding/plain/plain_le.go new file mode 100644 index 000000000..97772a1cd --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/encoding/plain/plain_le.go @@ -0,0 +1,52 @@ +//go:build !s390x + +package plain + +import ( + "github.com/parquet-go/bitpack/unsafecast" + "github.com/parquet-go/parquet-go/encoding" +) + +func (e *Encoding) EncodeInt32(dst []byte, src []int32) ([]byte, error) { + return append(dst[:0], unsafecast.Slice[byte](src)...), nil +} + +func (e *Encoding) EncodeInt64(dst []byte, src []int64) ([]byte, error) { + return append(dst[:0], unsafecast.Slice[byte](src)...), nil +} + +func (e *Encoding) EncodeFloat(dst []byte, src []float32) ([]byte, error) { + return append(dst[:0], unsafecast.Slice[byte](src)...), nil +} + +func (e *Encoding) EncodeDouble(dst []byte, src []float64) ([]byte, error) { + return append(dst[:0], unsafecast.Slice[byte](src)...), nil +} + +func (e *Encoding) DecodeInt32(dst []int32, src []byte) ([]int32, error) { + if (len(src) % 4) != 0 { + return dst, encoding.ErrDecodeInvalidInputSize(e, "INT32", len(src)) + } + return append(dst[:0], unsafecast.Slice[int32](src)...), nil +} + +func (e *Encoding) DecodeInt64(dst []int64, src []byte) ([]int64, error) { + if (len(src) % 8) != 0 { + return dst, encoding.ErrDecodeInvalidInputSize(e, "INT64", len(src)) + } + return append(dst[:0], unsafecast.Slice[int64](src)...), nil +} + +func (e *Encoding) DecodeFloat(dst []float32, src []byte) ([]float32, error) { + if (len(src) % 4) != 0 { + return dst, encoding.ErrDecodeInvalidInputSize(e, "FLOAT", len(src)) + } + return append(dst[:0], unsafecast.Slice[float32](src)...), nil +} + +func (e *Encoding) DecodeDouble(dst []float64, src []byte) ([]float64, error) { + if (len(src) % 8) != 0 { + return dst, encoding.ErrDecodeInvalidInputSize(e, "DOUBLE", len(src)) + } + return append(dst[:0], unsafecast.Slice[float64](src)...), nil +} diff --git a/vendor/github.com/parquet-go/parquet-go/encoding/rle/dictionary.go b/vendor/github.com/parquet-go/parquet-go/encoding/rle/dictionary.go new file mode 100644 index 000000000..b21fda912 --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/encoding/rle/dictionary.go @@ -0,0 +1,59 @@ +package rle + +import ( + "math/bits" + + "github.com/parquet-go/bitpack/unsafecast" + "github.com/parquet-go/parquet-go/encoding" + "github.com/parquet-go/parquet-go/format" +) + +type DictionaryEncoding struct { + encoding.NotSupported +} + +func (e *DictionaryEncoding) String() string { + return "RLE_DICTIONARY" +} + +func (e *DictionaryEncoding) Encoding() format.Encoding { + return format.RLEDictionary +} + +func (e *DictionaryEncoding) EncodeInt32(dst []byte, src []int32) ([]byte, error) { + bitWidth := maxLenInt32(src) + dst = append(dst[:0], byte(bitWidth)) + dst, err := encodeInt32(dst, src, uint(bitWidth)) + return dst, e.wrap(err) +} + +func (e *DictionaryEncoding) DecodeInt32(dst []int32, src []byte) ([]int32, error) { + if len(src) == 0 { + return dst[:0], nil + } + buf := unsafecast.Slice[byte](dst) + buf, err := decodeInt32(buf[:0], src[1:], uint(src[0])) + return unsafecast.Slice[int32](buf), e.wrap(err) +} + +func (e *DictionaryEncoding) wrap(err error) error { + if err != nil { + err = encoding.Error(e, err) + } + return err +} + +func clearInt32(data []int32) { + for i := range data { + data[i] = 0 + } +} + +func maxLenInt32(data []int32) (max int) { + for _, v := range data { + if n := bits.Len32(uint32(v)); n > max { + max = n + } + } + return max +} diff --git a/vendor/github.com/parquet-go/parquet-go/encoding/rle/rle.go b/vendor/github.com/parquet-go/parquet-go/encoding/rle/rle.go new file mode 100644 index 000000000..6aef0ae32 --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/encoding/rle/rle.go @@ -0,0 +1,567 @@ +// Package rle implements the hybrid RLE/Bit-Packed encoding employed in +// repetition and definition levels, dictionary indexed data pages, and +// boolean values in the PLAIN encoding. +// +// https://github.com/apache/parquet-format/blob/master/Encodings.md#run-length-encoding--bit-packing-hybrid-rle--3 +package rle + +import ( + "encoding/binary" + "fmt" + "io" + "math" + "unsafe" + + "golang.org/x/sys/cpu" + + "github.com/parquet-go/bitpack" + "github.com/parquet-go/bitpack/unsafecast" + "github.com/parquet-go/parquet-go/encoding" + "github.com/parquet-go/parquet-go/format" + "github.com/parquet-go/parquet-go/internal/bytealg" +) + +const ( + // This limit prevents unbounded memory allocations when decoding runs. + // It is set to the largest run length the Parquet specification permits in an RLE header. + maxSupportedValueCount = math.MaxInt32 +) + +type Encoding struct { + encoding.NotSupported + BitWidth int +} + +func (e *Encoding) String() string { + return "RLE" +} + +func (e *Encoding) Encoding() format.Encoding { + return format.RLE +} + +func (e *Encoding) EncodeLevels(dst []byte, src []uint8) ([]byte, error) { + dst, err := encodeBytes(dst[:0], src, uint(e.BitWidth)) + return dst, e.wrap(err) +} + +func (e *Encoding) EncodeBoolean(dst []byte, src []byte) ([]byte, error) { + // In the case of encoding a boolean values, the 4 bytes length of the + // output is expected by the parquet format. We add the bytes as placeholder + // before appending the encoded data. + dst = append(dst[:0], 0, 0, 0, 0) + dst, err := encodeBits(dst, src) + binary.LittleEndian.PutUint32(dst, uint32(len(dst))-4) + return dst, e.wrap(err) +} + +func (e *Encoding) EncodeInt32(dst []byte, src []int32) ([]byte, error) { + dst, err := encodeInt32(dst[:0], src, uint(e.BitWidth)) + return dst, e.wrap(err) +} + +func (e *Encoding) DecodeLevels(dst []uint8, src []byte) ([]uint8, error) { + dst, err := decodeBytes(dst[:0], src, uint(e.BitWidth)) + return dst, e.wrap(err) +} + +func (e *Encoding) DecodeBoolean(dst []byte, src []byte) ([]byte, error) { + if len(src) == 4 { + return dst[:0], nil + } + if len(src) < 4 { + return dst[:0], fmt.Errorf("input shorter than 4 bytes: %w", io.ErrUnexpectedEOF) + } + n := int(binary.LittleEndian.Uint32(src)) + src = src[4:] + if n > len(src) { + return dst[:0], fmt.Errorf("input shorter than length prefix: %d < %d: %w", len(src), n, io.ErrUnexpectedEOF) + } + dst, err := decodeBits(dst[:0], src[:n]) + return dst, e.wrap(err) +} + +func (e *Encoding) DecodeInt32(dst []int32, src []byte) ([]int32, error) { + buf := unsafecast.Slice[byte](dst) + buf, err := decodeInt32(buf[:0], src, uint(e.BitWidth)) + return unsafecast.Slice[int32](buf), e.wrap(err) +} + +func (e *Encoding) wrap(err error) error { + if err != nil { + err = encoding.Error(e, err) + } + return err +} + +func encodeBits(dst, src []byte) ([]byte, error) { + if len(src) == 0 || isZero(src) || isOnes(src) { + dst = appendUvarint(dst, uint64(8*len(src))<<1) + if len(src) > 0 { + dst = append(dst, src[0]) + } + return dst, nil + } + + for i := 0; i < len(src); { + j := i + 1 + + // Look for contiguous sections of 8 bits, all zeros or ones; these + // are run-length encoded as it only takes 2 or 3 bytes to store these + // sequences. + if src[i] == 0 || src[i] == 0xFF { + for j < len(src) && src[i] == src[j] { + j++ + } + + if n := j - i; n > 1 { + dst = appendRunLengthBits(dst, 8*n, src[i]) + i = j + continue + } + } + + // Sequences of bits that are neither all zeroes or ones are bit-packed, + // which is a simple copy of the input to the output preceded with the + // bit-pack header. + for j < len(src) && (src[j-1] != src[j] || (src[j] != 0 && src[j] == 0xFF)) { + j++ + } + + if (j-i) > 1 && j < len(src) { + j-- + } + + dst = appendBitPackedBits(dst, src[i:j]) + i = j + } + return dst, nil +} + +func encodeBytes(dst, src []byte, bitWidth uint) ([]byte, error) { + if bitWidth > 8 { + return dst, errEncodeInvalidBitWidth("INT8", bitWidth) + } + if bitWidth == 0 { + if !isZero(src) { + return dst, errEncodeInvalidBitWidth("INT8", bitWidth) + } + return appendUvarint(dst, uint64(len(src))<<1), nil + } + + if len(src) >= 8 { + words := unsafecast.Slice[uint64](src) + if cpu.IsBigEndian { + srcLen := (len(src) / 8) + idx := 0 + for k := range srcLen { + words[k] = binary.LittleEndian.Uint64((src)[idx:(8 + idx)]) + idx += 8 + } + } else { + words = unsafe.Slice((*uint64)(unsafe.Pointer(&src[0])), len(src)/8) + } + + for i := 0; i < len(words); { + j := i + pattern := broadcast8x1(words[i]) + + for j < len(words) && words[j] == pattern { + j++ + } + + if i < j { + dst = appendRunLengthBytes(dst, 8*(j-i), byte(pattern)) + } else { + j++ + + for j < len(words) && words[j] != broadcast8x1(words[j-1]) { + j++ + } + + dst = appendBitPackedBytes(dst, words[i:j], bitWidth) + } + + i = j + } + } + + for i := (len(src) / 8) * 8; i < len(src); { + j := i + 1 + + for j < len(src) && src[i] == src[j] { + j++ + } + + dst = appendRunLengthBytes(dst, j-i, src[i]) + i = j + } + + return dst, nil +} + +func encodeInt32(dst []byte, src []int32, bitWidth uint) ([]byte, error) { + if bitWidth > 32 { + return dst, errEncodeInvalidBitWidth("INT32", bitWidth) + } + if bitWidth == 0 { + if !isZero(unsafecast.Slice[byte](src)) { + return dst, errEncodeInvalidBitWidth("INT32", bitWidth) + } + return appendUvarint(dst, uint64(len(src))<<1), nil + } + + if len(src) >= 8 { + words := unsafecast.Slice[[8]int32](src) + + for i := 0; i < len(words); { + j := i + pattern := broadcast8x4(words[i][0]) + + for j < len(words) && words[j] == pattern { + j++ + } + + if i < j { + dst = appendRunLengthInt32(dst, 8*(j-i), pattern[0], bitWidth) + } else { + j += 1 + j += encodeInt32IndexEqual8Contiguous(words[j:]) + dst = appendBitPackedInt32(dst, words[i:j], bitWidth) + } + + i = j + } + } + + for i := (len(src) / 8) * 8; i < len(src); { + j := i + 1 + + for j < len(src) && src[i] == src[j] { + j++ + } + + dst = appendRunLengthInt32(dst, j-i, src[i], bitWidth) + i = j + } + + return dst, nil +} + +func decodeBits(dst, src []byte) ([]byte, error) { + for i := 0; i < len(src); { + u, n := binary.Uvarint(src[i:]) + if n == 0 { + return dst, fmt.Errorf("decoding run-length block header: %w", io.ErrUnexpectedEOF) + } + if n < 0 { + return dst, fmt.Errorf("overflow after decoding %d/%d bytes of run-length block header", -n+i, len(src)) + } + i += n + + count, bitpacked := uint(u>>1), (u&1) != 0 + if count > maxSupportedValueCount { + return dst, fmt.Errorf("decoded run-length block cannot have more than %d values", maxSupportedValueCount) + } + if bitpacked { + n := int(count) + j := i + n + + if j > len(src) { + return dst, fmt.Errorf("decoding bit-packed block of %d values: %w", n, io.ErrUnexpectedEOF) + } + + dst = append(dst, src[i:j]...) + i = j + } else { + word := byte(0) + if i < len(src) { + word = src[i] + i++ + } + + offset := len(dst) + length := bitpack.ByteCount(count) + dst = resize(dst, offset+length) + bytealg.Broadcast(dst[offset:], word) + } + } + return dst, nil +} + +func decodeBytes(dst, src []byte, bitWidth uint) ([]byte, error) { + if bitWidth > 8 { + return dst, errDecodeInvalidBitWidth("INT8", bitWidth) + } + + for i := 0; i < len(src); { + u, n := binary.Uvarint(src[i:]) + if n == 0 { + return dst, fmt.Errorf("decoding run-length block header: %w", io.ErrUnexpectedEOF) + } + if n < 0 { + return dst, fmt.Errorf("overflow after decoding %d/%d bytes of run-length block header", -n+i, len(src)) + } + i += n + + count, bitpacked := uint(u>>1), (u&1) != 0 + if count > maxSupportedValueCount { + return dst, fmt.Errorf("decoded run-length block cannot have more than %d values", maxSupportedValueCount) + } + if bitpacked { + count *= 8 + j := i + bitpack.ByteCount(count*bitWidth) + + if j > len(src) { + return dst, fmt.Errorf("decoding bit-packed block of %d values: %w", 8*count, io.ErrUnexpectedEOF) + } + + offset := len(dst) + length := int(count) + dst = resize(dst, offset+length) + decodeBytesBitpack(dst[offset:], src[i:j], count, bitWidth) + + i = j + } else { + if bitWidth != 0 && (i+1) > len(src) { + return dst, fmt.Errorf("decoding run-length block of %d values: %w", count, io.ErrUnexpectedEOF) + } + + word := byte(0) + if bitWidth != 0 { + word = src[i] + i++ + } + + offset := len(dst) + length := int(count) + dst = resize(dst, offset+length) + bytealg.Broadcast(dst[offset:], word) + } + } + + return dst, nil +} + +func decodeInt32(dst, src []byte, bitWidth uint) ([]byte, error) { + if bitWidth > 32 { + return dst, errDecodeInvalidBitWidth("INT32", bitWidth) + } + + buf := make([]byte, 2*bitpack.PaddingInt32) + + for i := 0; i < len(src); { + u, n := binary.Uvarint(src[i:]) + if n == 0 { + return dst, fmt.Errorf("decoding run-length block header: %w", io.ErrUnexpectedEOF) + } + if n < 0 { + return dst, fmt.Errorf("overflow after decoding %d/%d bytes of run-length block header", -n+i, len(src)) + } + i += n + + count, bitpacked := uint(u>>1), (u&1) != 0 + if count > maxSupportedValueCount { + return dst, fmt.Errorf("decoded run-length block cannot have more than %d values", maxSupportedValueCount) + } + if bitpacked { + offset := len(dst) + length := int(count * bitWidth) + dst = resize(dst, offset+4*8*int(count)) + + // The bitpack.UnpackInt32 function requires the input to be padded + // or the function panics. If there is enough room in the input + // buffer we can use it, otherwise we have to copy it to a larger + // location (which should rarely happen). + in := src[i : i+length] + if (cap(in) - len(in)) >= bitpack.PaddingInt32 { + in = in[:cap(in)] + } else { + buf = resize(buf, len(in)+bitpack.PaddingInt32) + copy(buf, in) + in = buf + } + + out := unsafecast.Slice[int32](dst[offset:]) + bitpack.Unpack(out, in, bitWidth) + i += length + } else { + j := i + bitpack.ByteCount(bitWidth) + + if j > len(src) { + return dst, fmt.Errorf("decoding run-length block of %d values: %w", count, io.ErrUnexpectedEOF) + } + + bits := [4]byte{} + copy(bits[:], src[i:j]) + + //swap the bytes in the "bits" array to take care of big endian arch + if cpu.IsBigEndian { + for m, n := 0, 3; m < n; m, n = m+1, n-1 { + bits[m], bits[n] = bits[n], bits[m] + } + } + dst = appendRepeat(dst, bits[:], count) + i = j + } + } + + return dst, nil +} + +func errEncodeInvalidBitWidth(typ string, bitWidth uint) error { + return errInvalidBitWidth("encode", typ, bitWidth) +} + +func errDecodeInvalidBitWidth(typ string, bitWidth uint) error { + return errInvalidBitWidth("decode", typ, bitWidth) +} + +func errInvalidBitWidth(op, typ string, bitWidth uint) error { + return fmt.Errorf("cannot %s %s with invalid bit-width=%d", op, typ, bitWidth) +} + +func appendRepeat(dst, pattern []byte, count uint) []byte { + offset := len(dst) + length := int(count) * len(pattern) + dst = resize(dst, offset+length) + i := offset + copy(dst[offset:], pattern) + for i < len(dst) { + i += copy(dst[i:], dst[offset:i]) + } + return dst +} + +func appendUvarint(dst []byte, u uint64) []byte { + var b [binary.MaxVarintLen64]byte + var n = binary.PutUvarint(b[:], u) + return append(dst, b[:n]...) +} + +func appendRunLengthBits(dst []byte, count int, value byte) []byte { + return appendRunLengthBytes(dst, count, value) +} + +func appendBitPackedBits(dst []byte, words []byte) []byte { + n := len(dst) + dst = resize(dst, n+binary.MaxVarintLen64+len(words)) + n += binary.PutUvarint(dst[n:], uint64(len(words)<<1)|1) + n += copy(dst[n:], words) + return dst[:n] +} + +func appendRunLengthBytes(dst []byte, count int, value byte) []byte { + n := len(dst) + dst = resize(dst, n+binary.MaxVarintLen64+1) + n += binary.PutUvarint(dst[n:], uint64(count)<<1) + dst[n] = value + return dst[:n+1] +} + +func appendBitPackedBytes(dst []byte, words []uint64, bitWidth uint) []byte { + n := len(dst) + dst = resize(dst, n+binary.MaxVarintLen64+(len(words)*int(bitWidth))+8) + n += binary.PutUvarint(dst[n:], uint64(len(words)<<1)|1) + n += encodeBytesBitpack(dst[n:], words, bitWidth) + return dst[:n] +} + +func appendRunLengthInt32(dst []byte, count int, value int32, bitWidth uint) []byte { + n := len(dst) + dst = resize(dst, n+binary.MaxVarintLen64+4) + n += binary.PutUvarint(dst[n:], uint64(count)<<1) + binary.LittleEndian.PutUint32(dst[n:], uint32(value)) + return dst[:n+bitpack.ByteCount(bitWidth)] +} + +func appendBitPackedInt32(dst []byte, words [][8]int32, bitWidth uint) []byte { + n := len(dst) + dst = resize(dst, n+binary.MaxVarintLen64+(len(words)*int(bitWidth))+32) + n += binary.PutUvarint(dst[n:], uint64(len(words))<<1|1) + n += encodeInt32Bitpack(dst[n:], words, bitWidth) + return dst[:n] +} + +func broadcast8x1(v uint64) uint64 { + return (v & 0xFF) * 0x0101010101010101 +} + +func broadcast8x4(v int32) [8]int32 { + return [8]int32{v, v, v, v, v, v, v, v} +} + +func isZero(data []byte) bool { + return bytealg.Count(data, 0x00) == len(data) +} + +func isOnes(data []byte) bool { + return bytealg.Count(data, 0xFF) == len(data) +} + +func resize(buf []byte, size int) []byte { + if cap(buf) < size { + return grow(buf, size) + } + return buf[:size] +} + +func grow(buf []byte, size int) []byte { + newCap := max(2*cap(buf), size) + newBuf := make([]byte, size, newCap) + copy(newBuf, buf) + return newBuf +} + +func encodeInt32BitpackDefault(dst []byte, src [][8]int32, bitWidth uint) int { + bits := unsafecast.Slice[int32](src) + bitpack.Pack(dst, bits, bitWidth) + return bitpack.ByteCount(uint(len(src)*8) * bitWidth) +} + +func encodeBytesBitpackDefault(dst []byte, src []uint64, bitWidth uint) int { + bitMask := uint64(1<> 8) & bitMask) << (1 * bitWidth)) | + (((word >> 16) & bitMask) << (2 * bitWidth)) | + (((word >> 24) & bitMask) << (3 * bitWidth)) | + (((word >> 32) & bitMask) << (4 * bitWidth)) | + (((word >> 40) & bitMask) << (5 * bitWidth)) | + (((word >> 48) & bitMask) << (6 * bitWidth)) | + (((word >> 56) & bitMask) << (7 * bitWidth)) + binary.LittleEndian.PutUint64(dst[n:], word) + n += int(bitWidth) + } + + return n +} + +func decodeBytesBitpackDefault(dst, src []byte, count, bitWidth uint) { + dst = dst[:0] + + bitMask := uint64(1< 0; count -= 8 { + j := i + byteCount + + bits := [8]byte{} + copy(bits[:], src[i:j]) + word := binary.LittleEndian.Uint64(bits[:]) + + dst = append(dst, + byte((word>>(0*bitWidth))&bitMask), + byte((word>>(1*bitWidth))&bitMask), + byte((word>>(2*bitWidth))&bitMask), + byte((word>>(3*bitWidth))&bitMask), + byte((word>>(4*bitWidth))&bitMask), + byte((word>>(5*bitWidth))&bitMask), + byte((word>>(6*bitWidth))&bitMask), + byte((word>>(7*bitWidth))&bitMask), + ) + + i = j + } +} diff --git a/vendor/github.com/parquet-go/parquet-go/encoding/rle/rle_amd64.go b/vendor/github.com/parquet-go/parquet-go/encoding/rle/rle_amd64.go new file mode 100644 index 000000000..960a2b3e1 --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/encoding/rle/rle_amd64.go @@ -0,0 +1,60 @@ +//go:build !purego + +package rle + +import ( + "golang.org/x/sys/cpu" +) + +var ( + encodeInt32IndexEqual8Contiguous func(words [][8]int32) int + encodeInt32Bitpack func(dst []byte, src [][8]int32, bitWidth uint) int + encodeBytesBitpack func(dst []byte, src []uint64, bitWidth uint) int + decodeBytesBitpack func(dst, src []byte, count, bitWidth uint) +) + +func init() { + switch { + case cpu.X86.HasAVX2: + encodeInt32IndexEqual8Contiguous = encodeInt32IndexEqual8ContiguousAVX2 + encodeInt32Bitpack = encodeInt32BitpackAVX2 + default: + encodeInt32IndexEqual8Contiguous = encodeInt32IndexEqual8ContiguousSSE + encodeInt32Bitpack = encodeInt32BitpackDefault + } + + switch { + case cpu.X86.HasBMI2: + encodeBytesBitpack = encodeBytesBitpackBMI2 + decodeBytesBitpack = decodeBytesBitpackBMI2 + default: + encodeBytesBitpack = encodeBytesBitpackDefault + decodeBytesBitpack = decodeBytesBitpackDefault + } +} + +//go:noescape +func encodeBytesBitpackBMI2(dst []byte, src []uint64, bitWidth uint) int + +//go:noescape +func encodeInt32IndexEqual8ContiguousAVX2(words [][8]int32) int + +//go:noescape +func encodeInt32IndexEqual8ContiguousSSE(words [][8]int32) int + +//go:noescape +func encodeInt32Bitpack1to16bitsAVX2(dst []byte, src [][8]int32, bitWidth uint) int + +func encodeInt32BitpackAVX2(dst []byte, src [][8]int32, bitWidth uint) int { + switch { + case bitWidth == 0: + return 0 + case bitWidth <= 16: + return encodeInt32Bitpack1to16bitsAVX2(dst, src, bitWidth) + default: + return encodeInt32BitpackDefault(dst, src, bitWidth) + } +} + +//go:noescape +func decodeBytesBitpackBMI2(dst, src []byte, count, bitWidth uint) diff --git a/vendor/github.com/parquet-go/parquet-go/encoding/rle/rle_amd64.s b/vendor/github.com/parquet-go/parquet-go/encoding/rle/rle_amd64.s new file mode 100644 index 000000000..a03a559d6 --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/encoding/rle/rle_amd64.s @@ -0,0 +1,174 @@ +//go:build !purego + +#include "textflag.h" + +GLOBL bitMasks<>(SB), RODATA|NOPTR, $64 +DATA bitMasks<>+0(SB)/8, $0b0000000100000001000000010000000100000001000000010000000100000001 +DATA bitMasks<>+8(SB)/8, $0b0000001100000011000000110000001100000011000000110000001100000011 +DATA bitMasks<>+16(SB)/8, $0b0000011100000111000001110000011100000111000001110000011100000111 +DATA bitMasks<>+24(SB)/8, $0b0000111100001111000011110000111100001111000011110000111100001111 +DATA bitMasks<>+32(SB)/8, $0b0001111100011111000111110001111100011111000111110001111100011111 +DATA bitMasks<>+40(SB)/8, $0b0011111100111111001111110011111100111111001111110011111100111111 +DATA bitMasks<>+48(SB)/8, $0b0111111101111111011111110111111101111111011111110111111101111111 +DATA bitMasks<>+56(SB)/8, $0b1111111111111111111111111111111111111111111111111111111111111111 + +// func decodeBytesBitpackBMI2(dst, src []byte, count, bitWidth uint) +TEXT ·decodeBytesBitpackBMI2(SB), NOSPLIT, $0-64 + MOVQ dst_base+0(FP), AX + MOVQ src_base+24(FP), BX + MOVQ count+48(FP), CX + MOVQ bitWidth+56(FP), DX + LEAQ bitMasks<>(SB), DI + MOVQ -8(DI)(DX*8), DI + XORQ SI, SI + SHRQ $3, CX + JMP test +loop: + MOVQ (BX), R8 + PDEPQ DI, R8, R8 + MOVQ R8, (AX)(SI*8) + ADDQ DX, BX + INCQ SI +test: + CMPQ SI, CX + JNE loop + RET + +// func encodeBytesBitpackBMI2(dst []byte, src []uint64, bitWidth uint) int +TEXT ·encodeBytesBitpackBMI2(SB), NOSPLIT, $0-64 + MOVQ dst_base+0(FP), AX + MOVQ src_base+24(FP), BX + MOVQ src_len+32(FP), CX + MOVQ bitWidth+48(FP), DX + LEAQ bitMasks<>(SB), DI + MOVQ -8(DI)(DX*8), DI + XORQ SI, SI + JMP test +loop: + MOVQ (BX)(SI*8), R8 + PEXTQ DI, R8, R8 + MOVQ R8, (AX) + ADDQ DX, AX + INCQ SI +test: + CMPQ SI, CX + JNE loop +done: + SUBQ dst+0(FP), AX + MOVQ AX, ret+56(FP) + RET + +// func encodeInt32IndexEqual8ContiguousAVX2(words [][8]int32) int +TEXT ·encodeInt32IndexEqual8ContiguousAVX2(SB), NOSPLIT, $0-32 + MOVQ words_base+0(FP), AX + MOVQ words_len+8(FP), BX + XORQ SI, SI + SHLQ $5, BX + JMP test +loop: + VMOVDQU (AX)(SI*1), Y0 + VPSHUFD $0, Y0, Y1 + VPCMPEQD Y1, Y0, Y0 + VMOVMSKPS Y0, CX + CMPL CX, $0xFF + JE done + ADDQ $32, SI +test: + CMPQ SI, BX + JNE loop +done: + VZEROUPPER + SHRQ $5, SI + MOVQ SI, ret+24(FP) + RET + +// func encodeInt32IndexEqual8ContiguousSSE(words [][8]int32) int +TEXT ·encodeInt32IndexEqual8ContiguousSSE(SB), NOSPLIT, $0-32 + MOVQ words_base+0(FP), AX + MOVQ words_len+8(FP), BX + XORQ SI, SI + SHLQ $5, BX + JMP test +loop: + MOVOU (AX)(SI*1), X0 + MOVOU 16(AX)(SI*1), X1 + PSHUFD $0, X0, X2 + PCMPEQL X2, X0 + PCMPEQL X2, X1 + MOVMSKPS X0, CX + MOVMSKPS X1, DX + ANDL DX, CX + CMPL CX, $0xF + JE done + ADDQ $32, SI +test: + CMPQ SI, BX + JNE loop +done: + SHRQ $5, SI + MOVQ SI, ret+24(FP) + RET + +// func encodeInt32Bitpack1to16bitsAVX2(dst []byte, src [][8]int32, bitWidth uint) int +TEXT ·encodeInt32Bitpack1to16bitsAVX2(SB), NOSPLIT, $0-64 + MOVQ dst_base+0(FP), AX + MOVQ src_base+24(FP), BX + MOVQ src_len+32(FP), CX + MOVQ bitWidth+48(FP), DX + + MOVQ DX, X0 + VPBROADCASTQ X0, Y6 // [1*bitWidth...] + VPSLLQ $1, Y6, Y7 // [2*bitWidth...] + VPADDQ Y6, Y7, Y8 // [3*bitWidth...] + VPSLLQ $2, Y6, Y9 // [4*bitWidth...] + + MOVQ $64, DI + MOVQ DI, X1 + VPBROADCASTQ X1, Y10 + VPSUBQ Y6, Y10, Y11 // [64-1*bitWidth...] + VPSUBQ Y9, Y10, Y12 // [64-4*bitWidth...] + VPCMPEQQ Y4, Y4, Y4 + VPSRLVQ Y11, Y4, Y4 + + VPXOR Y5, Y5, Y5 + XORQ SI, SI + SHLQ $5, CX + JMP test +loop: + VMOVDQU (BX)(SI*1), Y0 + VPSHUFD $0b01010101, Y0, Y1 + VPSHUFD $0b10101010, Y0, Y2 + VPSHUFD $0b11111111, Y0, Y3 + + VPAND Y4, Y0, Y0 + VPAND Y4, Y1, Y1 + VPAND Y4, Y2, Y2 + VPAND Y4, Y3, Y3 + + VPSLLVQ Y6, Y1, Y1 + VPSLLVQ Y7, Y2, Y2 + VPSLLVQ Y8, Y3, Y3 + + VPOR Y1, Y0, Y0 + VPOR Y3, Y2, Y2 + VPOR Y2, Y0, Y0 + + VPERMQ $0b00001010, Y0, Y1 + + VPSLLVQ X9, X1, X2 + VPSRLQ X12, X1, X3 + VBLENDPD $0b10, X3, X2, X1 + VBLENDPD $0b10, X5, X0, X0 + VPOR X1, X0, X0 + + VMOVDQU X0, (AX) + + ADDQ DX, AX + ADDQ $32, SI +test: + CMPQ SI, CX + JNE loop + VZEROUPPER + SUBQ dst+0(FP), AX + MOVQ AX, ret+56(FP) + RET diff --git a/vendor/github.com/parquet-go/parquet-go/encoding/rle/rle_purego.go b/vendor/github.com/parquet-go/parquet-go/encoding/rle/rle_purego.go new file mode 100644 index 000000000..8f3462d14 --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/encoding/rle/rle_purego.go @@ -0,0 +1,22 @@ +//go:build purego || !amd64 + +package rle + +func encodeBytesBitpack(dst []byte, src []uint64, bitWidth uint) int { + return encodeBytesBitpackDefault(dst, src, bitWidth) +} + +func encodeInt32IndexEqual8Contiguous(words [][8]int32) (n int) { + for n < len(words) && words[n] != broadcast8x4(words[n][0]) { + n++ + } + return n +} + +func encodeInt32Bitpack(dst []byte, src [][8]int32, bitWidth uint) int { + return encodeInt32BitpackDefault(dst, src, bitWidth) +} + +func decodeBytesBitpack(dst, src []byte, count, bitWidth uint) { + decodeBytesBitpackDefault(dst, src, count, bitWidth) +} diff --git a/vendor/github.com/parquet-go/parquet-go/encoding/thrift/LICENSE b/vendor/github.com/parquet-go/parquet-go/encoding/thrift/LICENSE new file mode 100644 index 000000000..1fbffdf72 --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/encoding/thrift/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2019 Segment.io, Inc. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/vendor/github.com/parquet-go/parquet-go/encoding/thrift/binary.go b/vendor/github.com/parquet-go/parquet-go/encoding/thrift/binary.go new file mode 100644 index 000000000..f9798be5a --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/encoding/thrift/binary.go @@ -0,0 +1,589 @@ +package thrift + +import ( + "bufio" + "bytes" + "encoding/binary" + "fmt" + "io" + "math" + + "github.com/parquet-go/bitpack/unsafecast" +) + +// BinaryProtocol is a Protocol implementation for the binary thrift protocol. +// +// https://github.com/apache/thrift/blob/master/doc/specs/thrift-binary-protocol.md +type BinaryProtocol struct { + NonStrict bool +} + +func (p *BinaryProtocol) NewReaderFromBytes(b []byte) Reader { + return &binaryBytesReader{p: p, data: b} +} + +func (p *BinaryProtocol) NewReader(r io.Reader) Reader { + return &binaryReader{p: p, r: r} +} + +func (p *BinaryProtocol) NewWriter(w io.Writer) Writer { + return &binaryWriter{p: p, w: w} +} + +func (p *BinaryProtocol) Features() Features { + return 0 +} + +type binaryReader struct { + p *BinaryProtocol + r io.Reader + b [8]byte + n int +} + +func (r *binaryReader) Protocol() Protocol { + return r.p +} + +func (r *binaryReader) Reader() io.Reader { + return r.r +} + +func (r *binaryReader) ReadBool() (bool, error) { + v, err := r.ReadByte() + // Thrift protocol treats both 0 and 2 as false. + return v != 0 && v != 2, err +} + +func (r *binaryReader) ReadInt8() (int8, error) { + b, err := r.ReadByte() + return int8(b), err +} + +func (r *binaryReader) ReadInt16() (int16, error) { + b, err := r.read(2) + if len(b) < 2 { + return 0, err + } + return int16(binary.BigEndian.Uint16(b)), nil +} + +func (r *binaryReader) ReadInt32() (int32, error) { + b, err := r.read(4) + if len(b) < 4 { + return 0, err + } + return int32(binary.BigEndian.Uint32(b)), nil +} + +func (r *binaryReader) ReadInt64() (int64, error) { + b, err := r.read(8) + if len(b) < 8 { + return 0, err + } + return int64(binary.BigEndian.Uint64(b)), nil +} + +func (r *binaryReader) ReadFloat64() (float64, error) { + b, err := r.read(8) + if len(b) < 8 { + return 0, err + } + return math.Float64frombits(binary.BigEndian.Uint64(b)), nil +} + +func (r *binaryReader) ReadBytes() ([]byte, error) { + n, err := r.ReadLength() + if err != nil { + return nil, err + } + b := make([]byte, n) + _, err = io.ReadFull(r.r, b) + if err == nil { + r.n += n + } + return b, err +} + +func (r *binaryReader) ReadString() (string, error) { + b, err := r.ReadBytes() + return unsafecast.String(b), err +} + +func (r *binaryReader) ReadLength() (int, error) { + b, err := r.read(4) + if len(b) < 4 { + return 0, err + } + n := binary.BigEndian.Uint32(b) + if n > math.MaxInt32 { + return 0, fmt.Errorf("length out of range: %d", n) + } + return int(n), nil +} + +func (r *binaryReader) ReadMessage() (Message, error) { + m := Message{} + + b, err := r.read(4) + if len(b) < 4 { + return m, err + } + + if (b[0] >> 7) == 0 { // non-strict + n := int(binary.BigEndian.Uint32(b)) + s := make([]byte, n) + _, err := io.ReadFull(r.r, s) + if err != nil { + return m, dontExpectEOF(err) + } + r.n += n + m.Name = unsafecast.String(s) + + t, err := r.ReadInt8() + if err != nil { + return m, dontExpectEOF(err) + } + + m.Type = MessageType(t & 0x7) + } else { + m.Type = MessageType(b[3] & 0x7) + + if m.Name, err = r.ReadString(); err != nil { + return m, dontExpectEOF(err) + } + } + + m.SeqID, err = r.ReadInt32() + return m, err +} + +func (r *binaryReader) ReadField() (Field, error) { + t, err := r.ReadInt8() + if err != nil { + return Field{}, err + } + i, err := r.ReadInt16() + if err != nil { + return Field{}, err + } + return Field{ID: i, Type: Type(t)}, nil +} + +func (r *binaryReader) ReadList() (List, error) { + t, err := r.ReadInt8() + if err != nil { + return List{}, err + } + n, err := r.ReadInt32() + if err != nil { + return List{}, dontExpectEOF(err) + } + return List{Size: n, Type: Type(t)}, nil +} + +func (r *binaryReader) ReadSet() (Set, error) { + l, err := r.ReadList() + return Set(l), err +} + +func (r *binaryReader) ReadMap() (Map, error) { + k, err := r.ReadByte() + if err != nil { + return Map{}, err + } + v, err := r.ReadByte() + if err != nil { + return Map{}, dontExpectEOF(err) + } + n, err := r.ReadInt32() + if err != nil { + return Map{}, dontExpectEOF(err) + } + return Map{Size: n, Key: Type(k), Value: Type(v)}, nil +} + +func (r *binaryReader) ReadByte() (byte, error) { + var b byte + var err error + switch x := r.r.(type) { + case *bytes.Buffer: + b, err = x.ReadByte() + case *bytes.Reader: + b, err = x.ReadByte() + case *bufio.Reader: + b, err = x.ReadByte() + case io.ByteReader: + b, err = x.ReadByte() + default: + buf, err := r.read(1) + if err != nil { + return 0, err + } + return buf[0], nil + } + if err == nil { + r.n++ + } + return b, err +} + +func (r *binaryReader) read(n int) ([]byte, error) { + _, err := io.ReadFull(r.r, r.b[:n]) + if err == nil { + r.n += n + } + return r.b[:n], err +} + +func (r *binaryReader) BytesRead() int { + return r.n +} + +type binaryWriter struct { + p *BinaryProtocol + b [8]byte + w io.Writer +} + +func (w *binaryWriter) Protocol() Protocol { + return w.p +} + +func (w *binaryWriter) Writer() io.Writer { + return w.w +} + +func (w *binaryWriter) WriteBool(v bool) error { + var b byte + if v { + b = 1 + } + return w.writeByte(b) +} + +func (w *binaryWriter) WriteInt8(v int8) error { + return w.writeByte(byte(v)) +} + +func (w *binaryWriter) WriteInt16(v int16) error { + binary.BigEndian.PutUint16(w.b[:2], uint16(v)) + return w.write(w.b[:2]) +} + +func (w *binaryWriter) WriteInt32(v int32) error { + binary.BigEndian.PutUint32(w.b[:4], uint32(v)) + return w.write(w.b[:4]) +} + +func (w *binaryWriter) WriteInt64(v int64) error { + binary.BigEndian.PutUint64(w.b[:8], uint64(v)) + return w.write(w.b[:8]) +} + +func (w *binaryWriter) WriteFloat64(v float64) error { + binary.BigEndian.PutUint64(w.b[:8], math.Float64bits(v)) + return w.write(w.b[:8]) +} + +func (w *binaryWriter) WriteBytes(v []byte) error { + if err := w.WriteLength(len(v)); err != nil { + return err + } + return w.write(v) +} + +func (w *binaryWriter) WriteString(v string) error { + if err := w.WriteLength(len(v)); err != nil { + return err + } + return w.writeString(v) +} + +func (w *binaryWriter) WriteLength(n int) error { + if n < 0 { + return fmt.Errorf("negative length cannot be encoded in thrift: %d", n) + } + if n > math.MaxInt32 { + return fmt.Errorf("length is too large to be encoded in thrift: %d", n) + } + return w.WriteInt32(int32(n)) +} + +func (w *binaryWriter) WriteMessage(m Message) error { + if w.p.NonStrict { + if err := w.WriteString(m.Name); err != nil { + return err + } + if err := w.writeByte(byte(m.Type)); err != nil { + return err + } + } else { + w.b[0] = 1 << 7 + w.b[1] = 0 + w.b[2] = 0 + w.b[3] = byte(m.Type) & 0x7 + binary.BigEndian.PutUint32(w.b[4:], uint32(len(m.Name))) + + if err := w.write(w.b[:8]); err != nil { + return err + } + if err := w.writeString(m.Name); err != nil { + return err + } + } + return w.WriteInt32(m.SeqID) +} + +func (w *binaryWriter) WriteField(f Field) error { + if err := w.writeByte(byte(f.Type)); err != nil { + return err + } + return w.WriteInt16(f.ID) +} + +func (w *binaryWriter) WriteList(l List) error { + if err := w.writeByte(byte(l.Type)); err != nil { + return err + } + return w.WriteInt32(l.Size) +} + +func (w *binaryWriter) WriteSet(s Set) error { + return w.WriteList(List(s)) +} + +func (w *binaryWriter) WriteMap(m Map) error { + if err := w.writeByte(byte(m.Key)); err != nil { + return err + } + if err := w.writeByte(byte(m.Value)); err != nil { + return err + } + return w.WriteInt32(m.Size) +} + +func (w *binaryWriter) write(b []byte) error { + _, err := w.w.Write(b) + return err +} + +func (w *binaryWriter) writeString(s string) error { + _, err := io.WriteString(w.w, s) + return err +} + +func (w *binaryWriter) writeByte(b byte) error { + // The special cases are intended to reduce the runtime overheadof testing + // for the io.ByteWriter interface for common types. Type assertions on a + // concrete type is just a pointer comparison, instead of requiring a + // complex lookup in the type metadata. + switch x := w.w.(type) { + case *bytes.Buffer: + return x.WriteByte(b) + case *bufio.Writer: + return x.WriteByte(b) + case io.ByteWriter: + return x.WriteByte(b) + default: + w.b[0] = b + return w.write(w.b[:1]) + } +} + +// binaryBytesReader is a zero-allocation reader that reads directly from a byte slice. +// Strings and byte slices returned by this reader point into the original buffer, +// so the buffer must outlive any usage of the decoded values. +type binaryBytesReader struct { + p *BinaryProtocol + data []byte + offset int +} + +func (r *binaryBytesReader) Protocol() Protocol { + return r.p +} + +func (r *binaryBytesReader) Reader() io.Reader { + return bytes.NewReader(r.data[r.offset:]) +} + +func (r *binaryBytesReader) ReadBool() (bool, error) { + b, err := r.ReadByte() + // Thrift protocol treats both 0 and 2 as false. + return b != 0 && b != 2, err +} + +func (r *binaryBytesReader) ReadInt8() (int8, error) { + b, err := r.ReadByte() + return int8(b), err +} + +func (r *binaryBytesReader) ReadInt16() (int16, error) { + if r.offset+2 > len(r.data) { + return 0, io.ErrUnexpectedEOF + } + v := int16(binary.BigEndian.Uint16(r.data[r.offset:])) + r.offset += 2 + return v, nil +} + +func (r *binaryBytesReader) ReadInt32() (int32, error) { + if r.offset+4 > len(r.data) { + return 0, io.ErrUnexpectedEOF + } + v := int32(binary.BigEndian.Uint32(r.data[r.offset:])) + r.offset += 4 + return v, nil +} + +func (r *binaryBytesReader) ReadInt64() (int64, error) { + if r.offset+8 > len(r.data) { + return 0, io.ErrUnexpectedEOF + } + v := int64(binary.BigEndian.Uint64(r.data[r.offset:])) + r.offset += 8 + return v, nil +} + +func (r *binaryBytesReader) ReadFloat64() (float64, error) { + if r.offset+8 > len(r.data) { + return 0, io.ErrUnexpectedEOF + } + v := math.Float64frombits(binary.BigEndian.Uint64(r.data[r.offset:])) + r.offset += 8 + return v, nil +} + +func (r *binaryBytesReader) ReadBytes() ([]byte, error) { + n, err := r.ReadLength() + if err != nil { + return nil, err + } + if r.offset+n > len(r.data) { + return nil, io.ErrUnexpectedEOF + } + b := r.data[r.offset : r.offset+n] + r.offset += n + return b, nil +} + +func (r *binaryBytesReader) ReadString() (string, error) { + n, err := r.ReadLength() + if err != nil { + return "", err + } + if r.offset+n > len(r.data) { + return "", io.ErrUnexpectedEOF + } + s := unsafecast.String(r.data[r.offset : r.offset+n]) + r.offset += n + return s, nil +} + +func (r *binaryBytesReader) ReadLength() (int, error) { + if r.offset+4 > len(r.data) { + return 0, io.ErrUnexpectedEOF + } + n := binary.BigEndian.Uint32(r.data[r.offset:]) + r.offset += 4 + if n > math.MaxInt32 { + return 0, fmt.Errorf("length out of range: %d", n) + } + return int(n), nil +} + +func (r *binaryBytesReader) ReadMessage() (Message, error) { + m := Message{} + + if r.offset+4 > len(r.data) { + return m, io.ErrUnexpectedEOF + } + b := r.data[r.offset : r.offset+4] + r.offset += 4 + + if (b[0] >> 7) == 0 { // non-strict + n := int(binary.BigEndian.Uint32(b)) + if r.offset+n > len(r.data) { + return m, io.ErrUnexpectedEOF + } + m.Name = unsafecast.String(r.data[r.offset : r.offset+n]) + r.offset += n + + t, err := r.ReadInt8() + if err != nil { + return m, dontExpectEOF(err) + } + + m.Type = MessageType(t & 0x7) + } else { + m.Type = MessageType(b[3] & 0x7) + + var err error + if m.Name, err = r.ReadString(); err != nil { + return m, dontExpectEOF(err) + } + } + + var err error + m.SeqID, err = r.ReadInt32() + return m, err +} + +func (r *binaryBytesReader) ReadField() (Field, error) { + t, err := r.ReadInt8() + if err != nil { + return Field{}, err + } + i, err := r.ReadInt16() + if err != nil { + return Field{}, err + } + return Field{ID: i, Type: Type(t)}, nil +} + +func (r *binaryBytesReader) ReadList() (List, error) { + t, err := r.ReadInt8() + if err != nil { + return List{}, err + } + n, err := r.ReadInt32() + if err != nil { + return List{}, dontExpectEOF(err) + } + return List{Size: n, Type: Type(t)}, nil +} + +func (r *binaryBytesReader) ReadSet() (Set, error) { + l, err := r.ReadList() + return Set(l), err +} + +func (r *binaryBytesReader) ReadMap() (Map, error) { + k, err := r.ReadByte() + if err != nil { + return Map{}, err + } + v, err := r.ReadByte() + if err != nil { + return Map{}, dontExpectEOF(err) + } + n, err := r.ReadInt32() + if err != nil { + return Map{}, dontExpectEOF(err) + } + return Map{Size: n, Key: Type(k), Value: Type(v)}, nil +} + +func (r *binaryBytesReader) ReadByte() (byte, error) { + if r.offset >= len(r.data) { + return 0, io.EOF + } + b := r.data[r.offset] + r.offset++ + return b, nil +} + +func (r *binaryBytesReader) BytesRead() int { + return r.offset +} diff --git a/vendor/github.com/parquet-go/parquet-go/encoding/thrift/compact.go b/vendor/github.com/parquet-go/parquet-go/encoding/thrift/compact.go new file mode 100644 index 000000000..249b3d8b9 --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/encoding/thrift/compact.go @@ -0,0 +1,576 @@ +package thrift + +import ( + "bufio" + "bytes" + "encoding/binary" + "fmt" + "io" + "math" + + "github.com/parquet-go/bitpack/unsafecast" +) + +// CompactProtocol is a Protocol implementation for the compact thrift protocol. +// +// https://github.com/apache/thrift/blob/master/doc/specs/thrift-compact-protocol.md#integer-encoding +type CompactProtocol struct{} + +func (p *CompactProtocol) NewReaderFromBytes(b []byte) Reader { + return &compactBytesReader{protocol: p, data: b} +} + +func (p *CompactProtocol) NewReader(r io.Reader) Reader { + return &compactReader{protocol: p, binary: binaryReader{r: r}} +} + +func (p *CompactProtocol) NewWriter(w io.Writer) Writer { + return &compactWriter{protocol: p, binary: binaryWriter{w: w}} +} + +func (p *CompactProtocol) Features() Features { + return UseDeltaEncoding | CoalesceBoolFields +} + +type compactReader struct { + protocol *CompactProtocol + binary binaryReader +} + +func (r *compactReader) Protocol() Protocol { + return r.protocol +} + +func (r *compactReader) Reader() io.Reader { + return r.binary.Reader() +} + +func (r *compactReader) BytesRead() int { + return r.binary.BytesRead() +} + +func (r *compactReader) ReadBool() (bool, error) { + return r.binary.ReadBool() +} + +func (r *compactReader) ReadInt8() (int8, error) { + return r.binary.ReadInt8() +} + +func (r *compactReader) ReadInt16() (int16, error) { + v, err := r.readVarint("int16", math.MinInt16, math.MaxInt16) + return int16(v), err +} + +func (r *compactReader) ReadInt32() (int32, error) { + v, err := r.readVarint("int32", math.MinInt32, math.MaxInt32) + return int32(v), err +} + +func (r *compactReader) ReadInt64() (int64, error) { + return r.readVarint("int64", math.MinInt64, math.MaxInt64) +} + +func (r *compactReader) ReadFloat64() (float64, error) { + b, err := r.binary.read(8) + if len(b) < 8 { + return 0, err + } + return math.Float64frombits(binary.LittleEndian.Uint64(b)), nil +} + +func (r *compactReader) ReadBytes() ([]byte, error) { + n, err := r.ReadLength() + if err != nil { + return nil, err + } + b := make([]byte, n) + _, err = io.ReadFull(r.Reader(), b) + if err == nil { + r.binary.n += n + } + return b, err +} + +func (r *compactReader) ReadString() (string, error) { + b, err := r.ReadBytes() + return unsafecast.String(b), err +} + +func (r *compactReader) ReadLength() (int, error) { + n, err := r.readUvarint("length", math.MaxInt32) + return int(n), err +} + +func (r *compactReader) ReadMessage() (Message, error) { + m := Message{} + + b0, err := r.ReadByte() + if err != nil { + return m, err + } + if b0 != 0x82 { + return m, fmt.Errorf("invalid protocol id found when reading thrift message: %#x", b0) + } + + b1, err := r.ReadByte() + if err != nil { + return m, dontExpectEOF(err) + } + + seqID, err := r.readUvarint("seq id", math.MaxInt32) + if err != nil { + return m, dontExpectEOF(err) + } + + m.Type = MessageType(b1) & 0x7 + m.SeqID = int32(seqID) + m.Name, err = r.ReadString() + return m, dontExpectEOF(err) +} + +func (r *compactReader) ReadField() (Field, error) { + f := Field{} + + b, err := r.ReadByte() + if err != nil { + return f, err + } + + if Type(b) == STOP { + return f, nil + } + + if (b >> 4) != 0 { + f = Field{ID: int16(b >> 4), Type: Type(b & 0xF), Delta: true} + } else { + i, err := r.ReadInt16() + if err != nil { + return f, dontExpectEOF(err) + } + f = Field{ID: i, Type: Type(b)} + } + + return f, nil +} + +func (r *compactReader) ReadList() (List, error) { + b, err := r.ReadByte() + if err != nil { + return List{}, err + } + if (b >> 4) != 0xF { + return List{Size: int32(b >> 4), Type: Type(b & 0xF)}, nil + } + n, err := r.readUvarint("list size", math.MaxInt32) + if err != nil { + return List{}, dontExpectEOF(err) + } + return List{Size: int32(n), Type: Type(b & 0xF)}, nil +} + +func (r *compactReader) ReadSet() (Set, error) { + l, err := r.ReadList() + return Set(l), err +} + +func (r *compactReader) ReadMap() (Map, error) { + n, err := r.readUvarint("map size", math.MaxInt32) + if err != nil { + return Map{}, err + } + if n == 0 { // empty map + return Map{}, nil + } + b, err := r.ReadByte() + if err != nil { + return Map{}, dontExpectEOF(err) + } + return Map{Size: int32(n), Key: Type(b >> 4), Value: Type(b & 0xF)}, nil +} + +func (r *compactReader) ReadByte() (byte, error) { + return r.binary.ReadByte() +} + +func (r *compactReader) readUvarint(typ string, max uint64) (uint64, error) { + var br io.ByteReader + + switch x := r.Reader().(type) { + case *bytes.Buffer: + br = x + case *bytes.Reader: + br = x + case *bufio.Reader: + br = x + case io.ByteReader: + br = x + default: + br = &r.binary + } + + u, err := binary.ReadUvarint(br) + if err == nil { + if u > max { + err = fmt.Errorf("%s varint out of range: %d > %d", typ, u, max) + } + } + return u, err +} + +func (r *compactReader) readVarint(typ string, min, max int64) (int64, error) { + var br io.ByteReader + + switch x := r.Reader().(type) { + case *bytes.Buffer: + br = x + case *bytes.Reader: + br = x + case *bufio.Reader: + br = x + case io.ByteReader: + br = x + default: + br = &r.binary + } + + v, err := binary.ReadVarint(br) + if err == nil { + if v < min || v > max { + err = fmt.Errorf("%s varint out of range: %d not in [%d;%d]", typ, v, min, max) + } + } + return v, err +} + +type compactWriter struct { + protocol *CompactProtocol + binary binaryWriter + varint [binary.MaxVarintLen64]byte +} + +func (w *compactWriter) Protocol() Protocol { + return w.protocol +} + +func (w *compactWriter) Writer() io.Writer { + return w.binary.Writer() +} + +func (w *compactWriter) WriteBool(v bool) error { + return w.binary.WriteBool(v) +} + +func (w *compactWriter) WriteInt8(v int8) error { + return w.binary.WriteInt8(v) +} + +func (w *compactWriter) WriteInt16(v int16) error { + return w.writeVarint(int64(v)) +} + +func (w *compactWriter) WriteInt32(v int32) error { + return w.writeVarint(int64(v)) +} + +func (w *compactWriter) WriteInt64(v int64) error { + return w.writeVarint(v) +} + +func (w *compactWriter) WriteFloat64(v float64) error { + binary.LittleEndian.PutUint64(w.binary.b[:8], math.Float64bits(v)) + return w.binary.write(w.binary.b[:8]) +} + +func (w *compactWriter) WriteBytes(v []byte) error { + if err := w.WriteLength(len(v)); err != nil { + return err + } + return w.binary.write(v) +} + +func (w *compactWriter) WriteString(v string) error { + if err := w.WriteLength(len(v)); err != nil { + return err + } + return w.binary.writeString(v) +} + +func (w *compactWriter) WriteLength(n int) error { + if n < 0 { + return fmt.Errorf("negative length cannot be encoded in thrift: %d", n) + } + if n > math.MaxInt32 { + return fmt.Errorf("length is too large to be encoded in thrift: %d", n) + } + return w.writeUvarint(uint64(n)) +} + +func (w *compactWriter) WriteMessage(m Message) error { + if err := w.binary.writeByte(0x82); err != nil { + return err + } + if err := w.binary.writeByte(byte(m.Type)); err != nil { + return err + } + if err := w.writeUvarint(uint64(m.SeqID)); err != nil { + return err + } + return w.WriteString(m.Name) +} + +func (w *compactWriter) WriteField(f Field) error { + if f.Type == STOP { + return w.binary.writeByte(0) + } + if f.ID <= 15 { + return w.binary.writeByte(byte(f.ID<<4) | byte(f.Type)) + } + if err := w.binary.writeByte(byte(f.Type)); err != nil { + return err + } + return w.WriteInt16(f.ID) +} + +func (w *compactWriter) WriteList(l List) error { + if l.Size <= 14 { + return w.binary.writeByte(byte(l.Size<<4) | byte(l.Type)) + } + if err := w.binary.writeByte(0xF0 | byte(l.Type)); err != nil { + return err + } + return w.writeUvarint(uint64(l.Size)) +} + +func (w *compactWriter) WriteSet(s Set) error { + return w.WriteList(List(s)) +} + +func (w *compactWriter) WriteMap(m Map) error { + if err := w.writeUvarint(uint64(m.Size)); err != nil || m.Size == 0 { + return err + } + return w.binary.writeByte((byte(m.Key) << 4) | byte(m.Value)) +} + +func (w *compactWriter) writeUvarint(v uint64) error { + n := binary.PutUvarint(w.varint[:], v) + return w.binary.write(w.varint[:n]) +} + +func (w *compactWriter) writeVarint(v int64) error { + n := binary.PutVarint(w.varint[:], v) + return w.binary.write(w.varint[:n]) +} + +// compactBytesReader is a zero-allocation reader that reads directly from a byte slice. +// Strings and byte slices returned by this reader point into the original buffer, +// so the buffer must outlive any usage of the decoded values. +type compactBytesReader struct { + protocol *CompactProtocol + data []byte + offset int +} + +func (r *compactBytesReader) Protocol() Protocol { + return r.protocol +} + +func (r *compactBytesReader) Reader() io.Reader { + return bytes.NewReader(r.data[r.offset:]) +} + +func (r *compactBytesReader) ReadBool() (bool, error) { + b, err := r.ReadByte() + // Thrift protocol treats both 0 and 2 as false. + return b != 0 && b != 2, err +} + +func (r *compactBytesReader) ReadInt8() (int8, error) { + b, err := r.ReadByte() + return int8(b), err +} + +func (r *compactBytesReader) ReadInt16() (int16, error) { + v, err := r.readVarint("int16", math.MinInt16, math.MaxInt16) + return int16(v), err +} + +func (r *compactBytesReader) ReadInt32() (int32, error) { + v, err := r.readVarint("int32", math.MinInt32, math.MaxInt32) + return int32(v), err +} + +func (r *compactBytesReader) ReadInt64() (int64, error) { + return r.readVarint("int64", math.MinInt64, math.MaxInt64) +} + +func (r *compactBytesReader) ReadFloat64() (float64, error) { + if r.offset+8 > len(r.data) { + return 0, io.ErrUnexpectedEOF + } + v := math.Float64frombits(binary.LittleEndian.Uint64(r.data[r.offset:])) + r.offset += 8 + return v, nil +} + +func (r *compactBytesReader) ReadBytes() ([]byte, error) { + n, err := r.ReadLength() + if err != nil { + return nil, err + } + if r.offset+n > len(r.data) { + return nil, io.ErrUnexpectedEOF + } + b := r.data[r.offset : r.offset+n] + r.offset += n + return b, nil +} + +func (r *compactBytesReader) ReadString() (string, error) { + n, err := r.ReadLength() + if err != nil { + return "", err + } + if r.offset+n > len(r.data) { + return "", io.ErrUnexpectedEOF + } + s := unsafecast.String(r.data[r.offset : r.offset+n]) + r.offset += n + return s, nil +} + +func (r *compactBytesReader) ReadLength() (int, error) { + n, err := r.readUvarint("length", math.MaxInt32) + return int(n), err +} + +func (r *compactBytesReader) ReadMessage() (Message, error) { + m := Message{} + + b0, err := r.ReadByte() + if err != nil { + return m, err + } + if b0 != 0x82 { + return m, fmt.Errorf("invalid protocol id found when reading thrift message: %#x", b0) + } + + b1, err := r.ReadByte() + if err != nil { + return m, dontExpectEOF(err) + } + + seqID, err := r.readUvarint("seq id", math.MaxInt32) + if err != nil { + return m, dontExpectEOF(err) + } + + m.Type = MessageType(b1) & 0x7 + m.SeqID = int32(seqID) + m.Name, err = r.ReadString() + return m, dontExpectEOF(err) +} + +func (r *compactBytesReader) ReadField() (Field, error) { + f := Field{} + + b, err := r.ReadByte() + if err != nil { + return f, err + } + + if Type(b) == STOP { + return f, nil + } + + if (b >> 4) != 0 { + f = Field{ID: int16(b >> 4), Type: Type(b & 0xF), Delta: true} + } else { + i, err := r.ReadInt16() + if err != nil { + return f, dontExpectEOF(err) + } + f = Field{ID: i, Type: Type(b)} + } + + return f, nil +} + +func (r *compactBytesReader) ReadList() (List, error) { + b, err := r.ReadByte() + if err != nil { + return List{}, err + } + if (b >> 4) != 0xF { + return List{Size: int32(b >> 4), Type: Type(b & 0xF)}, nil + } + n, err := r.readUvarint("list size", math.MaxInt32) + if err != nil { + return List{}, dontExpectEOF(err) + } + return List{Size: int32(n), Type: Type(b & 0xF)}, nil +} + +func (r *compactBytesReader) ReadSet() (Set, error) { + l, err := r.ReadList() + return Set(l), err +} + +func (r *compactBytesReader) ReadMap() (Map, error) { + n, err := r.readUvarint("map size", math.MaxInt32) + if err != nil { + return Map{}, err + } + if n == 0 { // empty map + return Map{}, nil + } + b, err := r.ReadByte() + if err != nil { + return Map{}, dontExpectEOF(err) + } + return Map{Size: int32(n), Key: Type(b >> 4), Value: Type(b & 0xF)}, nil +} + +func (r *compactBytesReader) ReadByte() (byte, error) { + if r.offset >= len(r.data) { + return 0, io.EOF + } + b := r.data[r.offset] + r.offset++ + return b, nil +} + +func (r *compactBytesReader) BytesRead() int { + return r.offset +} + +func (r *compactBytesReader) readUvarint(typ string, max uint64) (uint64, error) { + u, n := binary.Uvarint(r.data[r.offset:]) + if n == 0 { + return 0, io.ErrUnexpectedEOF + } + if n < 0 { + return 0, fmt.Errorf("%s varint overflow", typ) + } + r.offset += n + if u > max { + return 0, fmt.Errorf("%s varint out of range: %d > %d", typ, u, max) + } + return u, nil +} + +func (r *compactBytesReader) readVarint(typ string, min, max int64) (int64, error) { + v, n := binary.Varint(r.data[r.offset:]) + if n == 0 { + return 0, io.ErrUnexpectedEOF + } + if n < 0 { + return 0, fmt.Errorf("%s varint overflow", typ) + } + r.offset += n + if v < min || v > max { + return 0, fmt.Errorf("%s varint out of range: %d not in [%d;%d]", typ, v, min, max) + } + return v, nil +} diff --git a/vendor/github.com/parquet-go/parquet-go/encoding/thrift/debug.go b/vendor/github.com/parquet-go/parquet-go/encoding/thrift/debug.go new file mode 100644 index 000000000..e8bf5763b --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/encoding/thrift/debug.go @@ -0,0 +1,234 @@ +package thrift + +import ( + "io" + "log" +) + +func NewDebugReader(r Reader, l *log.Logger) Reader { + return &debugReader{ + r: r, + l: l, + } +} + +func NewDebugWriter(w Writer, l *log.Logger) Writer { + return &debugWriter{ + w: w, + l: l, + } +} + +type debugReader struct { + r Reader + l *log.Logger +} + +func (d *debugReader) log(method string, res any, err error) { + if err != nil { + d.l.Printf("(%T).%s() → ERROR: %v", d.r, method, err) + } else { + d.l.Printf("(%T).%s() → %#v", d.r, method, res) + } +} + +func (d *debugReader) Protocol() Protocol { + return d.r.Protocol() +} + +func (d *debugReader) Reader() io.Reader { + return d.r.Reader() +} + +func (d *debugReader) ReadBool() (bool, error) { + v, err := d.r.ReadBool() + d.log("ReadBool", v, err) + return v, err +} + +func (d *debugReader) ReadInt8() (int8, error) { + v, err := d.r.ReadInt8() + d.log("ReadInt8", v, err) + return v, err +} + +func (d *debugReader) ReadInt16() (int16, error) { + v, err := d.r.ReadInt16() + d.log("ReadInt16", v, err) + return v, err +} + +func (d *debugReader) ReadInt32() (int32, error) { + v, err := d.r.ReadInt32() + d.log("ReadInt32", v, err) + return v, err +} + +func (d *debugReader) ReadInt64() (int64, error) { + v, err := d.r.ReadInt64() + d.log("ReadInt64", v, err) + return v, err +} + +func (d *debugReader) ReadFloat64() (float64, error) { + v, err := d.r.ReadFloat64() + d.log("ReadFloat64", v, err) + return v, err +} + +func (d *debugReader) ReadBytes() ([]byte, error) { + v, err := d.r.ReadBytes() + d.log("ReadBytes", v, err) + return v, err +} + +func (d *debugReader) ReadString() (string, error) { + v, err := d.r.ReadString() + d.log("ReadString", v, err) + return v, err +} + +func (d *debugReader) ReadLength() (int, error) { + v, err := d.r.ReadLength() + d.log("ReadLength", v, err) + return v, err +} + +func (d *debugReader) ReadMessage() (Message, error) { + v, err := d.r.ReadMessage() + d.log("ReadMessage", v, err) + return v, err +} + +func (d *debugReader) ReadField() (Field, error) { + v, err := d.r.ReadField() + d.log("ReadField", v, err) + return v, err +} + +func (d *debugReader) ReadList() (List, error) { + v, err := d.r.ReadList() + d.log("ReadList", v, err) + return v, err +} + +func (d *debugReader) ReadSet() (Set, error) { + v, err := d.r.ReadSet() + d.log("ReadSet", v, err) + return v, err +} + +func (d *debugReader) ReadMap() (Map, error) { + v, err := d.r.ReadMap() + d.log("ReadMap", v, err) + return v, err +} + +func (d *debugReader) BytesRead() int { + return d.r.BytesRead() +} + +type debugWriter struct { + w Writer + l *log.Logger +} + +func (d *debugWriter) log(method string, arg any, err error) { + if err != nil { + d.l.Printf("(%T).%s(%#v) → ERROR: %v", d.w, method, arg, err) + } else { + d.l.Printf("(%T).%s(%#v)", d.w, method, arg) + } +} + +func (d *debugWriter) Protocol() Protocol { + return d.w.Protocol() +} + +func (d *debugWriter) Writer() io.Writer { + return d.w.Writer() +} + +func (d *debugWriter) WriteBool(v bool) error { + err := d.w.WriteBool(v) + d.log("WriteBool", v, err) + return err +} + +func (d *debugWriter) WriteInt8(v int8) error { + err := d.w.WriteInt8(v) + d.log("WriteInt8", v, err) + return err +} + +func (d *debugWriter) WriteInt16(v int16) error { + err := d.w.WriteInt16(v) + d.log("WriteInt16", v, err) + return err +} + +func (d *debugWriter) WriteInt32(v int32) error { + err := d.w.WriteInt32(v) + d.log("WriteInt32", v, err) + return err +} + +func (d *debugWriter) WriteInt64(v int64) error { + err := d.w.WriteInt64(v) + d.log("WriteInt64", v, err) + return err +} + +func (d *debugWriter) WriteFloat64(v float64) error { + err := d.w.WriteFloat64(v) + d.log("WriteFloat64", v, err) + return err +} + +func (d *debugWriter) WriteBytes(v []byte) error { + err := d.w.WriteBytes(v) + d.log("WriteBytes", v, err) + return err +} + +func (d *debugWriter) WriteString(v string) error { + err := d.w.WriteString(v) + d.log("WriteString", v, err) + return err +} + +func (d *debugWriter) WriteLength(n int) error { + err := d.w.WriteLength(n) + d.log("WriteLength", n, err) + return err +} + +func (d *debugWriter) WriteMessage(m Message) error { + err := d.w.WriteMessage(m) + d.log("WriteMessage", m, err) + return err +} + +func (d *debugWriter) WriteField(f Field) error { + err := d.w.WriteField(f) + d.log("WriteField", f, err) + return err +} + +func (d *debugWriter) WriteList(l List) error { + err := d.w.WriteList(l) + d.log("WriteList", l, err) + return err +} + +func (d *debugWriter) WriteSet(s Set) error { + err := d.w.WriteSet(s) + d.log("WriteSet", s, err) + return err +} + +func (d *debugWriter) WriteMap(m Map) error { + err := d.w.WriteMap(m) + d.log("WriteMap", m, err) + return err +} diff --git a/vendor/github.com/parquet-go/parquet-go/encoding/thrift/decode.go b/vendor/github.com/parquet-go/parquet-go/encoding/thrift/decode.go new file mode 100644 index 000000000..b1e318929 --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/encoding/thrift/decode.go @@ -0,0 +1,737 @@ +package thrift + +import ( + "bufio" + "fmt" + "io" + "maps" + "reflect" + "slices" + "sync/atomic" +) + +// Unmarshal deserializes the thrift data from b to v using to the protocol p. +// +// The function errors if the data in b does not match the type of v. +// +// The function panics if v cannot be converted to a thrift representation. +// +// As an optimization, the value passed in v may be reused across multiple calls +// to Unmarshal, allowing the function to reuse objects referenced by pointer +// fields of struct values. When reusing objects, the application is responsible +// for resetting the state of v before calling Unmarshal again. +func Unmarshal(p Protocol, b []byte, v any) error { + pr := p.NewReaderFromBytes(slices.Clone(b)) + + if err := NewDecoder(pr).Decode(v); err != nil { + return err + } + + if n := len(b) - pr.BytesRead(); n != 0 { + return fmt.Errorf("unexpected trailing bytes at the end of thrift input: %d", n) + } + + return nil +} + +type Decoder struct { + r Reader + f Flags +} + +func NewDecoder(r Reader) *Decoder { + return &Decoder{r: r, f: decoderFlags(r)} +} + +func (d *Decoder) Decode(v any) error { + t := reflect.TypeOf(v) + p := reflect.ValueOf(v) + + if t.Kind() != reflect.Ptr { + panic("thrift.(*Decoder).Decode: expected pointer type but got " + t.String()) + } + + t = t.Elem() + p = p.Elem() + + cache, _ := decoderCache.Load().(map[typeID]DecodeFunc) + decode, _ := cache[makeTypeID(t)] + + if decode == nil { + decode = DecodeFuncOf(t, make(DecodeFuncCache)) + + newCache := make(map[typeID]DecodeFunc, len(cache)+1) + newCache[makeTypeID(t)] = decode + maps.Copy(newCache, cache) + + decoderCache.Store(newCache) + } + + return decode(d.r, p, d.f) +} + +func (d *Decoder) Reset(r Reader) { + d.r = r + d.f = d.f.Without(protocolFlags).With(decoderFlags(r)) +} + +func (d *Decoder) SetStrict(enabled bool) { + if enabled { + d.f = d.f.With(Strict) + } else { + d.f = d.f.Without(Strict) + } +} + +func decoderFlags(r Reader) Flags { + return Flags(r.Protocol().Features() << featuresBitOffset) +} + +var decoderCache atomic.Value // map[typeID]DecodeFunc + +// DecodeFunc is a function that decodes a value from a thrift reader. +type DecodeFunc func(Reader, reflect.Value, Flags) error + +// DecodeFuncCache is a cache for decode functions. +type DecodeFuncCache map[reflect.Type]DecodeFunc + +// DecodeFuncFor returns the decode function for type T. +// This is a convenience wrapper around DecodeFuncOf using reflect.TypeFor. +func DecodeFuncFor[T any](cache DecodeFuncCache) DecodeFunc { + return DecodeFuncOf(reflect.TypeFor[T](), cache) +} + +// DecodeFuncOf returns the decode function for the given type. +func DecodeFuncOf(t reflect.Type, seen DecodeFuncCache) DecodeFunc { + f := seen[t] + if f != nil { + return f + } + + // Check if type implements Value interface first + if t.Implements(valueType) { + zv := reflect.Zero(t).Interface().(Value) + f = zv.DecodeFunc(seen) + seen[t] = f + return f + } + + switch t.Kind() { + case reflect.Bool: + f = decodeBool + case reflect.Int8: + f = decodeInt8 + case reflect.Int16: + f = decodeInt16 + case reflect.Int32: + f = decodeInt32 + case reflect.Int64, reflect.Int: + f = decodeInt64 + case reflect.Float32, reflect.Float64: + f = decodeFloat64 + case reflect.String: + f = decodeString + case reflect.Slice: + if t.Elem().Kind() == reflect.Uint8 { // []byte + f = decodeBytes + } else { + f = decodeFuncSliceOf(t, seen) + } + case reflect.Map: + f = decodeFuncMapOf(t, seen) + case reflect.Struct: + f = decodeFuncStructOf(t, seen) + case reflect.Ptr: + f = decodeFuncPtrOf(t, seen) + default: + panic("type cannot be decoded in thrift: " + t.String()) + } + seen[t] = f + return f +} + +func decodeBool(r Reader, v reflect.Value, _ Flags) error { + b, err := r.ReadBool() + if err != nil { + return err + } + v.SetBool(b) + return nil +} + +func decodeInt8(r Reader, v reflect.Value, _ Flags) error { + i, err := r.ReadInt8() + if err != nil { + return err + } + v.SetInt(int64(i)) + return nil +} + +func decodeInt16(r Reader, v reflect.Value, _ Flags) error { + i, err := r.ReadInt16() + if err != nil { + return err + } + v.SetInt(int64(i)) + return nil +} + +func decodeInt32(r Reader, v reflect.Value, _ Flags) error { + i, err := r.ReadInt32() + if err != nil { + return err + } + v.SetInt(int64(i)) + return nil +} + +func decodeInt64(r Reader, v reflect.Value, _ Flags) error { + i, err := r.ReadInt64() + if err != nil { + return err + } + v.SetInt(int64(i)) + return nil +} + +func decodeFloat64(r Reader, v reflect.Value, _ Flags) error { + f, err := r.ReadFloat64() + if err != nil { + return err + } + v.SetFloat(f) + return nil +} + +func decodeString(r Reader, v reflect.Value, _ Flags) error { + s, err := r.ReadString() + if err != nil { + return err + } + v.SetString(s) + return nil +} + +func decodeBytes(r Reader, v reflect.Value, _ Flags) error { + b, err := r.ReadBytes() + if err != nil { + return err + } + v.SetBytes(b) + return nil +} + +func decodeFuncSliceOf(t reflect.Type, seen DecodeFuncCache) DecodeFunc { + elem := t.Elem() + typ := TypeOf(elem) + dec := DecodeFuncOf(elem, seen) + + return func(r Reader, v reflect.Value, flags Flags) error { + l, err := r.ReadList() + if err != nil { + return err + } + + // Sometimes the list type is set to TRUE when the list contains only + // TRUE values. Thrift does not seem to optimize the encoding by + // omitting the boolean values that are known to all be TRUE, we still + // need to decode them. + switch l.Type { + case TRUE: + l.Type = BOOL + } + + // TODO: implement type conversions? + if typ != l.Type { + if flags.Have(Strict) { + return &TypeMismatch{item: "list item", Expect: typ, Found: l.Type} + } + return nil + } + + size := int(l.Size) + if v.IsNil() { + v.Set(reflect.MakeSlice(t, size, size)) + } else if v.Cap() >= size { + v.SetLen(size) + } else { + v.Grow(size - v.Len()) + v.SetLen(size) + } + flags = flags.Only(decodeFlags) + + for i := range size { + if err := dec(r, v.Index(i), flags); err != nil { + return with(dontExpectEOF(err), &decodeErrorList{cause: l, index: i}) + } + } + + return nil + } +} + +func decodeFuncMapOf(t reflect.Type, seen DecodeFuncCache) DecodeFunc { + key, elem := t.Key(), t.Elem() + if elem.Size() == 0 { // map[?]struct{} + return decodeFuncMapAsSetOf(t, seen) + } + + mapType := reflect.MapOf(key, elem) + keyZero := reflect.Zero(key) + elemZero := reflect.Zero(elem) + keyType := TypeOf(key) + elemType := TypeOf(elem) + decodeKey := DecodeFuncOf(key, seen) + decodeElem := DecodeFuncOf(elem, seen) + + return func(r Reader, v reflect.Value, flags Flags) error { + m, err := r.ReadMap() + if err != nil { + return err + } + + v.Set(reflect.MakeMapWithSize(mapType, int(m.Size))) + + if m.Size == 0 { // empty map + return nil + } + + // TODO: implement type conversions? + if keyType != m.Key { + if flags.Have(Strict) { + return &TypeMismatch{item: "map key", Expect: keyType, Found: m.Key} + } + return nil + } + + if elemType != m.Value { + if flags.Have(Strict) { + return &TypeMismatch{item: "map value", Expect: elemType, Found: m.Value} + } + return nil + } + + tmpKey := reflect.New(key).Elem() + tmpElem := reflect.New(elem).Elem() + flags = flags.Only(decodeFlags) + + for i := range int(m.Size) { + if err := decodeKey(r, tmpKey, flags); err != nil { + return with(dontExpectEOF(err), &decodeErrorMap{cause: m, index: i}) + } + if err := decodeElem(r, tmpElem, flags); err != nil { + return with(dontExpectEOF(err), &decodeErrorMap{cause: m, index: i}) + } + v.SetMapIndex(tmpKey, tmpElem) + tmpKey.Set(keyZero) + tmpElem.Set(elemZero) + } + + return nil + } +} + +func decodeFuncMapAsSetOf(t reflect.Type, seen DecodeFuncCache) DecodeFunc { + key, elem := t.Key(), t.Elem() + keyZero := reflect.Zero(key) + elemZero := reflect.Zero(elem) + typ := TypeOf(key) + dec := DecodeFuncOf(key, seen) + + return func(r Reader, v reflect.Value, flags Flags) error { + s, err := r.ReadSet() + if err != nil { + return err + } + + // See decodeFuncSliceOf for details about why this type conversion + // needs to be done. + switch s.Type { + case TRUE: + s.Type = BOOL + } + + v.Set(reflect.MakeMapWithSize(t, int(s.Size))) + + if s.Size == 0 { + return nil + } + + // TODO: implement type conversions? + if typ != s.Type { + if flags.Have(Strict) { + return &TypeMismatch{item: "list item", Expect: typ, Found: s.Type} + } + return nil + } + + tmp := reflect.New(key).Elem() + flags = flags.Only(decodeFlags) + + for i := range int(s.Size) { + if err := dec(r, tmp, flags); err != nil { + return with(dontExpectEOF(err), &decodeErrorSet{cause: s, index: i}) + } + v.SetMapIndex(tmp, elemZero) + tmp.Set(keyZero) + } + + return nil + } +} + +type structDecoder struct { + fields []structDecoderField + union []int + minID int16 + zero reflect.Value + required []uint64 +} + +func (dec *structDecoder) decode(r Reader, v reflect.Value, flags Flags) error { + flags = flags.Only(decodeFlags) + coalesceBool := flags.Have(coalesceBoolFields) + + lastField := reflect.Value{} + union := len(dec.union) > 0 + seen := make([]uint64, 1) + if len(dec.required) > len(seen) { + seen = make([]uint64, len(dec.required)) + } + + // Inlined readStruct loop to avoid closure overhead + lastFieldID := int16(0) + numFields := 0 + +decodeFields: + for { + f, err := r.ReadField() + if err != nil { + if numFields > 0 { + err = dontExpectEOF(err) + } + return err + } + + if f.Type == STOP { + break + } + + if f.Delta { + f.ID += lastFieldID + f.Delta = false + } + + i := int(f.ID) - int(dec.minID) + if i < 0 || i >= len(dec.fields) || dec.fields[i].decode == nil { + if err := skip(r, f.Type); err != nil { + return with(dontExpectEOF(err), &decodeErrorField{cause: f}) + } + lastFieldID = f.ID + numFields++ + continue decodeFields + } + field := &dec.fields[i] + seen[i/64] |= 1 << (i % 64) + + // TODO: implement type conversions? + if f.Type != field.typ && !(f.Type == TRUE && field.typ == BOOL) { + if flags.Have(Strict) { + return &TypeMismatch{item: "field value", Expect: field.typ, Found: f.Type} + } + lastFieldID = f.ID + numFields++ + continue decodeFields + } + + x := v + for _, i := range field.index { + if x.Kind() == reflect.Ptr { + x = x.Elem() + } + if x = x.Field(i); x.Kind() == reflect.Ptr { + if x.IsNil() { + x.Set(reflect.New(x.Type().Elem())) + } + } + } + + if union { + v.Set(dec.zero) + } + + lastField = x + + if coalesceBool && (f.Type == TRUE || f.Type == FALSE) { + for x.Kind() == reflect.Ptr { + if x.IsNil() { + x.Set(reflect.New(x.Type().Elem())) + } + x = x.Elem() + } + // Handle BoolValue types (like Null[bool]) + if x.Type().Implements(boolValueType) { + x.FieldByName("V").SetBool(f.Type == TRUE) + x.FieldByName("Valid").SetBool(true) + } else { + x.SetBool(f.Type == TRUE) + } + lastFieldID = f.ID + numFields++ + continue decodeFields + } + + if err := field.decode(r, x, flags.With(field.flags)); err != nil { + return with(dontExpectEOF(err), &decodeErrorField{cause: f}) + } + + lastFieldID = f.ID + numFields++ + } + + for i, required := range dec.required { + if mask := required & seen[i]; mask != required { + missing := required &^ seen[i] + i *= 64 + for (missing & 1) == 0 { + missing >>= 1 + i++ + } + field := &dec.fields[i] + return &MissingField{Field: Field{ID: field.id, Type: field.typ}} + } + } + + if union && lastField.IsValid() { + v.FieldByIndex(dec.union).Set(lastField.Addr()) + } + + return nil +} + +type structDecoderField struct { + index []int + id int16 + flags Flags + typ Type + decode DecodeFunc +} + +func decodeFuncStructOf(t reflect.Type, seen DecodeFuncCache) DecodeFunc { + dec := &structDecoder{ + zero: reflect.Zero(t), + } + decode := dec.decode + seen[t] = decode + + fields := make([]structDecoderField, 0, t.NumField()) + forEachStructField(t, nil, func(f structField) { + if f.flags.Have(Union) { + dec.union = f.index + } else { + fields = append(fields, structDecoderField{ + index: f.index, + id: f.id, + flags: f.flags, + typ: TypeOf(f.typ), + decode: decodeFuncStructFieldOf(f, seen), + }) + } + }) + + minID := int16(0) + maxID := int16(0) + + for _, f := range fields { + if f.id < minID || minID == 0 { + minID = f.id + } + if f.id > maxID { + maxID = f.id + } + } + + dec.fields = make([]structDecoderField, (maxID-minID)+1) + dec.minID = minID + dec.required = make([]uint64, len(fields)/64+1) + + for _, f := range fields { + i := f.id - minID + p := dec.fields[i] + if p.decode != nil { + panic(fmt.Errorf("thrift struct field id %d is present multiple times in %s with types %s and %s", f.id, t, p.typ, f.typ)) + } + dec.fields[i] = f + if f.flags.Have(Required) { + dec.required[i/64] |= 1 << (i % 64) + } + } + + return decode +} + +func decodeFuncStructFieldOf(f structField, seen DecodeFuncCache) DecodeFunc { + if f.flags.Have(Enum) { + switch f.typ.Kind() { + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + return decodeInt32 + } + } + return DecodeFuncOf(f.typ, seen) +} + +func decodeFuncPtrOf(t reflect.Type, seen DecodeFuncCache) DecodeFunc { + elem := t.Elem() + decode := DecodeFuncOf(t.Elem(), seen) + return func(r Reader, v reflect.Value, f Flags) error { + if v.IsNil() { + v.Set(reflect.New(elem)) + } + return decode(r, v.Elem(), f) + } +} + +func readBinary(r Reader, f func(io.Reader) error) error { + n, err := r.ReadLength() + if err != nil { + return err + } + return dontExpectEOF(f(io.LimitReader(r.Reader(), int64(n)))) +} + +func skip(r Reader, t Type) error { + var err error + switch t { + case TRUE, FALSE: + // When CoalesceBoolFields is advertised, boolean field values are encoded in the field + // type (TRUE/FALSE) with no additional data bytes. ReadBool() would + // consume a byte that belongs to the next field, misaligning the reader. + // So, only read a byte for protocols that don't coalesce bool fields. + // Related test file: binary_min_val_exact.parquet + if r.Protocol().Features()&CoalesceBoolFields == 0 { + _, err = r.ReadBool() + } + case I8: + _, err = r.ReadInt8() + case I16: + _, err = r.ReadInt16() + case I32: + _, err = r.ReadInt32() + case I64: + _, err = r.ReadInt64() + case DOUBLE: + _, err = r.ReadFloat64() + case BINARY: + err = skipBinary(r) + case LIST: + err = skipList(r) + case SET: + err = skipSet(r) + case MAP: + err = skipMap(r) + case STRUCT: + err = skipStruct(r) + case UUID: + // UUID is 16 bytes (fixed size) in compact protocol. ReadFloat64 is + // used because it reads exactly 8 raw bytes in compact encoding, + // unlike ReadInt64 which reads a variable-length zigzag varint. + _, err = r.ReadFloat64() + if err == nil { + _, err = r.ReadFloat64() + } + default: + return fmt.Errorf("skipping unsupported thrift type %d", t) + } + return err +} + +func skipBinary(r Reader) error { + n, err := r.ReadLength() + if err != nil { + return err + } + if n == 0 { + return nil + } + switch x := r.Reader().(type) { + case *bufio.Reader: + _, err = x.Discard(int(n)) + default: + _, err = io.CopyN(io.Discard, x, int64(n)) + } + return dontExpectEOF(err) +} + +func skipList(r Reader) error { + l, err := r.ReadList() + if err != nil { + return err + } + for i := range int(l.Size) { + if err := skip(r, l.Type); err != nil { + return with(dontExpectEOF(err), &decodeErrorList{cause: l, index: i}) + } + } + return nil +} + +func skipSet(r Reader) error { + s, err := r.ReadSet() + if err != nil { + return err + } + for i := range int(s.Size) { + if err := skip(r, s.Type); err != nil { + return with(dontExpectEOF(err), &decodeErrorSet{cause: s, index: i}) + } + } + return nil +} + +func skipMap(r Reader) error { + m, err := r.ReadMap() + if err != nil { + return err + } + for i := range int(m.Size) { + if err := skip(r, m.Key); err != nil { + return with(dontExpectEOF(err), &decodeErrorMap{cause: m, index: i}) + } + if err := skip(r, m.Value); err != nil { + return with(dontExpectEOF(err), &decodeErrorMap{cause: m, index: i}) + } + } + return nil +} + +func skipStruct(r Reader) error { + lastFieldID := int16(0) + numFields := 0 + + for { + f, err := r.ReadField() + if err != nil { + if numFields > 0 { + err = dontExpectEOF(err) + } + return err + } + + if f.Type == STOP { + return nil + } + + if f.Delta { + f.ID += lastFieldID + f.Delta = false + } + + if err := skip(r, f.Type); err != nil { + return with(dontExpectEOF(err), &decodeErrorField{cause: f}) + } + + lastFieldID = f.ID + numFields++ + } +} diff --git a/vendor/github.com/parquet-go/parquet-go/encoding/thrift/encode.go b/vendor/github.com/parquet-go/parquet-go/encoding/thrift/encode.go new file mode 100644 index 000000000..f9899312b --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/encoding/thrift/encode.go @@ -0,0 +1,449 @@ +package thrift + +import ( + "bytes" + "cmp" + "fmt" + "maps" + "math" + "reflect" + "slices" + "sync/atomic" +) + +// Marshal serializes v into a thrift representation according to the the +// protocol p. +// +// The function panics if v cannot be converted to a thrift representation. +func Marshal(p Protocol, v any) ([]byte, error) { + buf := new(bytes.Buffer) + enc := NewEncoder(p.NewWriter(buf)) + err := enc.Encode(v) + return buf.Bytes(), err +} + +type Encoder struct { + w Writer + f Flags +} + +func NewEncoder(w Writer) *Encoder { + return &Encoder{w: w, f: encoderFlags(w)} +} + +func (e *Encoder) Encode(v any) error { + t := reflect.TypeOf(v) + cache, _ := encoderCache.Load().(map[typeID]EncodeFunc) + encode, _ := cache[makeTypeID(t)] + + if encode == nil { + encode = EncodeFuncOf(t, make(EncodeFuncCache)) + + newCache := make(map[typeID]EncodeFunc, len(cache)+1) + newCache[makeTypeID(t)] = encode + maps.Copy(newCache, cache) + + encoderCache.Store(newCache) + } + + return encode(e.w, reflect.ValueOf(v), e.f) +} + +func (e *Encoder) Reset(w Writer) { + e.w = w + e.f = e.f.Without(protocolFlags).With(encoderFlags(w)) +} + +func encoderFlags(w Writer) Flags { + return Flags(w.Protocol().Features() << featuresBitOffset) +} + +var encoderCache atomic.Value // map[typeID]EncodeFunc + +// EncodeFunc is a function that encodes a value to a thrift writer. +type EncodeFunc func(Writer, reflect.Value, Flags) error + +// EncodeFuncCache is a cache for encode functions. +type EncodeFuncCache map[reflect.Type]EncodeFunc + +// EncodeFuncFor returns the encode function for type T. +// This is a convenience wrapper around EncodeFuncOf using reflect.TypeFor. +func EncodeFuncFor[T any](cache EncodeFuncCache) EncodeFunc { + return EncodeFuncOf(reflect.TypeFor[T](), cache) +} + +// EncodeFuncOf returns the encode function for the given type. +func EncodeFuncOf(t reflect.Type, seen EncodeFuncCache) EncodeFunc { + f := seen[t] + if f != nil { + return f + } + + // Check if type implements Value interface first + if t.Implements(valueType) { + zv := reflect.Zero(t).Interface().(Value) + f = zv.EncodeFunc(seen) + seen[t] = f + return f + } + + switch t.Kind() { + case reflect.Bool: + f = encodeBool + case reflect.Int8: + f = encodeInt8 + case reflect.Int16: + f = encodeInt16 + case reflect.Int32: + f = encodeInt32 + case reflect.Int64, reflect.Int: + f = encodeInt64 + case reflect.Float32, reflect.Float64: + f = encodeFloat64 + case reflect.String: + f = encodeString + case reflect.Slice: + if t.Elem().Kind() == reflect.Uint8 { + f = encodeBytes + } else { + f = encodeFuncSliceOf(t, seen) + } + case reflect.Map: + f = encodeFuncMapOf(t, seen) + case reflect.Struct: + f = encodeFuncStructOf(t, seen) + case reflect.Ptr: + f = encodeFuncPtrOf(t, seen) + default: + panic("type cannot be encoded in thrift: " + t.String()) + } + seen[t] = f + return f +} + +func encodeBool(w Writer, v reflect.Value, _ Flags) error { + return w.WriteBool(v.Bool()) +} + +func encodeInt8(w Writer, v reflect.Value, _ Flags) error { + return w.WriteInt8(int8(v.Int())) +} + +func encodeInt16(w Writer, v reflect.Value, _ Flags) error { + return w.WriteInt16(int16(v.Int())) +} + +func encodeInt32(w Writer, v reflect.Value, _ Flags) error { + return w.WriteInt32(int32(v.Int())) +} + +func encodeInt64(w Writer, v reflect.Value, _ Flags) error { + return w.WriteInt64(v.Int()) +} + +func encodeFloat64(w Writer, v reflect.Value, _ Flags) error { + return w.WriteFloat64(v.Float()) +} + +func encodeString(w Writer, v reflect.Value, _ Flags) error { + return w.WriteString(v.String()) +} + +func encodeBytes(w Writer, v reflect.Value, _ Flags) error { + return w.WriteBytes(v.Bytes()) +} + +func encodeFuncSliceOf(t reflect.Type, seen EncodeFuncCache) EncodeFunc { + elem := t.Elem() + typ := TypeOf(elem) + enc := EncodeFuncOf(elem, seen) + + return func(w Writer, v reflect.Value, flags Flags) error { + n := v.Len() + if n > math.MaxInt32 { + return fmt.Errorf("slice length is too large to be represented in thrift: %d > max(int32)", n) + } + + err := w.WriteList(List{ + Size: int32(n), + Type: typ, + }) + if err != nil { + return err + } + + for i := range n { + if err := enc(w, v.Index(i), flags); err != nil { + return err + } + } + + return nil + } +} + +func encodeFuncMapOf(t reflect.Type, seen EncodeFuncCache) EncodeFunc { + key, elem := t.Key(), t.Elem() + if elem.Size() == 0 { // map[?]struct{} + return encodeFuncMapAsSetOf(t, seen) + } + + keyType := TypeOf(key) + elemType := TypeOf(elem) + encodeKey := EncodeFuncOf(key, seen) + encodeElem := EncodeFuncOf(elem, seen) + + return func(w Writer, v reflect.Value, flags Flags) error { + n := v.Len() + if n > math.MaxInt32 { + return fmt.Errorf("map length is too large to be represented in thrift: %d > max(int32)", n) + } + + err := w.WriteMap(Map{ + Size: int32(n), + Key: keyType, + Value: elemType, + }) + if err != nil { + return err + } + if n == 0 { // empty map + return nil + } + + for i, iter := 0, v.MapRange(); iter.Next(); i++ { + if err := encodeKey(w, iter.Key(), flags); err != nil { + return err + } + if err := encodeElem(w, iter.Value(), flags); err != nil { + return err + } + } + + return nil + } +} + +func encodeFuncMapAsSetOf(t reflect.Type, seen EncodeFuncCache) EncodeFunc { + key := t.Key() + typ := TypeOf(key) + enc := EncodeFuncOf(key, seen) + + return func(w Writer, v reflect.Value, flags Flags) error { + n := v.Len() + if n > math.MaxInt32 { + return fmt.Errorf("map length is too large to be represented in thrift: %d > max(int32)", n) + } + + err := w.WriteSet(Set{ + Size: int32(n), + Type: typ, + }) + if err != nil { + return err + } + if n == 0 { // empty map + return nil + } + + for i, iter := 0, v.MapRange(); iter.Next(); i++ { + if err := enc(w, iter.Key(), flags); err != nil { + return err + } + } + + return nil + } +} + +type structEncoder struct { + fields []structEncoderField + union bool +} + +func dereference(v reflect.Value) reflect.Value { + for v.Kind() == reflect.Ptr { + if v.IsNil() { + return v + } + v = v.Elem() + } + return v +} + +func isTrue(v reflect.Value) bool { + v = dereference(v) + if !v.IsValid() { + return false + } + // Handle BoolValue types (like Null[bool]) + if v.Type().Implements(boolValueType) { + return v.Interface().(BoolValue).Bool() + } + return v.Kind() == reflect.Bool && v.Bool() +} + +func (enc *structEncoder) encode(w Writer, v reflect.Value, flags Flags) error { + deltaEnc := flags.Have(useDeltaEncoding) + coalesceBool := flags.Have(coalesceBoolFields) + numFields := int16(0) + lastFieldID := int16(0) + +encodeFields: + for _, f := range enc.fields { + x := v + for _, i := range f.index { + if x.Kind() == reflect.Ptr { + x = x.Elem() + } + if x = x.Field(i); x.Kind() == reflect.Ptr { + if x.IsNil() { + continue encodeFields + } + } + } + + // Check if Value types should be skipped (e.g., Null[T] with Valid=false, nil Slice[T]) + if f.null(x) { + continue encodeFields + } + + if !f.flags.Have(Required) && !f.flags.Have(WriteZero) && x.IsZero() { + continue encodeFields + } + + field := Field{ + ID: f.id, + Type: f.typ, + } + + if deltaEnc { + if delta := field.ID - lastFieldID; delta <= 15 { + field.ID = delta + field.Delta = true + } + } + + skipValue := coalesceBool && field.Type == BOOL + if skipValue && isTrue(x) == true { + field.Type = TRUE + } + + if err := w.WriteField(field); err != nil { + return err + } + + if !skipValue { + if err := f.encode(w, x, flags); err != nil { + return err + } + } + + numFields++ + lastFieldID = f.id + } + + if err := w.WriteField(Field{Type: STOP}); err != nil { + return err + } + + if numFields > 1 && enc.union { + return fmt.Errorf("thrift union had more than one field with a non-zero value (%d)", numFields) + } + + return nil +} + +func (enc *structEncoder) String() string { + if enc.union { + return "union" + } + return "struct" +} + +type structEncoderField struct { + index []int + id int16 + flags Flags + typ Type + encode EncodeFunc + null NullFunc +} + +func encodeFuncStructOf(t reflect.Type, seen EncodeFuncCache) EncodeFunc { + enc := &structEncoder{ + fields: make([]structEncoderField, 0, t.NumField()), + } + encode := enc.encode + seen[t] = encode + + forEachStructField(t, nil, func(f structField) { + if f.flags.Have(Union) { + enc.union = true + } else { + enc.fields = append(enc.fields, structEncoderField{ + index: f.index, + id: f.id, + flags: f.flags, + typ: TypeOf(f.typ), + encode: encodeFuncStructFieldOf(f, seen), + null: nullFuncOf(f), + }) + } + }) + + slices.SortStableFunc(enc.fields, func(a, b structEncoderField) int { + return cmp.Compare(a.id, b.id) + }) + + for i := len(enc.fields) - 1; i > 0; i-- { + if enc.fields[i-1].id == enc.fields[i].id { + panic(fmt.Errorf("thrift struct field id %d is present multiple times", enc.fields[i].id)) + } + } + + return encode +} + +func encodeFuncStructFieldOf(f structField, seen EncodeFuncCache) EncodeFunc { + if f.flags.Have(Enum) { + switch f.typ.Kind() { + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + return encodeInt32 + } + } + return EncodeFuncOf(f.typ, seen) +} + +// neverNull is used for required fields and non-Value types. +func neverNull(reflect.Value) bool { return false } + +func nullFuncOf(f structField) NullFunc { + // Required fields are never skipped + if f.flags.Have(Required) { + return neverNull + } + // Value types: use their NullFunc + if f.flags.Have(ValueType) { + zv := reflect.Zero(f.typ).Interface().(Value) + return zv.NullFunc() + } + // Pointer fields are handled by the loop (nil check during traversal) + // All other types use neverNull + return neverNull +} + +func encodeFuncPtrOf(t reflect.Type, seen EncodeFuncCache) EncodeFunc { + typ := t.Elem() + enc := EncodeFuncOf(typ, seen) + zero := reflect.Zero(typ) + + return func(w Writer, v reflect.Value, f Flags) error { + if v.IsNil() { + v = zero + } else { + v = v.Elem() + } + return enc(w, v, f) + } +} diff --git a/vendor/github.com/parquet-go/parquet-go/encoding/thrift/error.go b/vendor/github.com/parquet-go/parquet-go/encoding/thrift/error.go new file mode 100644 index 000000000..ceeb1ba09 --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/encoding/thrift/error.go @@ -0,0 +1,111 @@ +package thrift + +import ( + "errors" + "fmt" + "io" + "strings" +) + +type MissingField struct { + Field Field +} + +func (e *MissingField) Error() string { + return fmt.Sprintf("missing required field: %s", e.Field) +} + +type TypeMismatch struct { + Expect Type + Found Type + item string +} + +func (e *TypeMismatch) Error() string { + return fmt.Sprintf("%s type mismatch: expected %s but found %s", e.item, e.Expect, e.Found) +} + +type decodeError struct { + base error + path []error +} + +func (e *decodeError) Error() string { + s := strings.Builder{} + s.Grow(256) + s.WriteString("decoding thrift payload: ") + + if len(e.path) != 0 { + n := len(e.path) - 1 + for i := n; i >= 0; i-- { + if i < n { + s.WriteString(" → ") + } + s.WriteString(e.path[i].Error()) + } + s.WriteString(": ") + } + + s.WriteString(e.base.Error()) + return s.String() +} + +func (e *decodeError) Unwrap() error { return e.base } + +func with(base, elem error) error { + if errors.Is(base, io.EOF) { + return base + } + e, _ := base.(*decodeError) + if e == nil { + e = &decodeError{base: base} + } + e.path = append(e.path, elem) + return e +} + +type decodeErrorField struct { + cause Field +} + +func (d *decodeErrorField) Error() string { + return d.cause.String() +} + +type decodeErrorList struct { + cause List + index int +} + +func (d *decodeErrorList) Error() string { + return fmt.Sprintf("%d/%d:%s", d.index, d.cause.Size, d.cause) +} + +type decodeErrorSet struct { + cause Set + index int +} + +func (d *decodeErrorSet) Error() string { + return fmt.Sprintf("%d/%d:%s", d.index, d.cause.Size, d.cause) +} + +type decodeErrorMap struct { + cause Map + index int +} + +func (d *decodeErrorMap) Error() string { + return fmt.Sprintf("%d/%d:%s", d.index, d.cause.Size, d.cause) +} + +func dontExpectEOF(err error) error { + switch err { + case nil: + return nil + case io.EOF: + return io.ErrUnexpectedEOF + default: + return err + } +} diff --git a/vendor/github.com/parquet-go/parquet-go/encoding/thrift/list.go b/vendor/github.com/parquet-go/parquet-go/encoding/thrift/list.go new file mode 100644 index 000000000..0fb66c023 --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/encoding/thrift/list.go @@ -0,0 +1,96 @@ +package thrift + +import ( + "reflect" + "slices" +) + +// Slice[T] is a slice type that optimizes thrift decoding allocations. +// Unlike bare slices, Slice[T] implements the Value interface to use +// typed allocation instead of reflect.MakeSlice, avoiding one heap +// allocation per slice decode. +// +// Slice[T] is designed as a drop-in replacement for slice fields in +// thrift structs. It works transparently with standard slice operations. +type Slice[T any] []T + +// Type returns the thrift type LIST. +func (l Slice[T]) Type() Type { + return LIST +} + +// EncodeFunc returns an encode function that encodes the slice. +func (l Slice[T]) EncodeFunc(cache EncodeFuncCache) EncodeFunc { + elemEnc := EncodeFuncFor[T](cache) + elemType := TypeOf(reflect.TypeFor[T]()) + + return func(w Writer, v reflect.Value, flags Flags) error { + n := v.Len() + + if err := w.WriteList(List{Size: int32(n), Type: elemType}); err != nil { + return err + } + + for i := range n { + if err := elemEnc(w, v.Index(i), flags); err != nil { + return err + } + } + return nil + } +} + +// NullFunc returns a function that checks if the slice is nil. +// A nil slice is considered unset; an empty slice is a valid empty list. +func (l Slice[T]) NullFunc() NullFunc { + return func(v reflect.Value) bool { + return v.IsNil() + } +} + +// DecodeFunc returns a decode function that decodes into the slice +// using typed allocation to avoid reflect.MakeSlice overhead. +func (l Slice[T]) DecodeFunc(cache DecodeFuncCache) DecodeFunc { + elemDec := DecodeFuncFor[T](cache) + elemType := TypeOf(reflect.TypeFor[T]()) + + return func(r Reader, v reflect.Value, flags Flags) error { + listHeader, err := r.ReadList() + if err != nil { + return err + } + + // Handle TRUE -> BOOL type conversion + if listHeader.Type == TRUE { + listHeader.Type = BOOL + } + + if elemType != listHeader.Type { + if flags.Have(Strict) { + return &TypeMismatch{item: "list item", Expect: elemType, Found: listHeader.Type} + } + return nil + } + + size := int(listHeader.Size) + + // Zero-alloc typed allocation via interface-to-pointer conversion + list := v.Addr().Interface().(*Slice[T]) + if *list == nil { + *list = make(Slice[T], size) + } else if cap(*list) >= size { + *list = (*list)[:size] + } else { + *list = slices.Grow((*list)[:0], size)[:size] + } + + // Decode elements + flags = flags.Only(decodeFlags) + for i := range size { + if err := elemDec(r, v.Index(i), flags); err != nil { + return with(dontExpectEOF(err), &decodeErrorList{cause: listHeader, index: i}) + } + } + return nil + } +} diff --git a/vendor/github.com/parquet-go/parquet-go/encoding/thrift/null.go b/vendor/github.com/parquet-go/parquet-go/encoding/thrift/null.go new file mode 100644 index 000000000..1189a5dc7 --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/encoding/thrift/null.go @@ -0,0 +1,83 @@ +package thrift + +import "reflect" + +// Null represents an optional value of type T. +// Unlike pointers, Null[T] is a value type that stores the value inline, +// avoiding heap allocations during thrift decoding. +// +// Null[T] is designed to be a drop-in replacement for pointer fields in +// thrift structs where the pointer is used to distinguish between "not set" +// and "zero value". +// +// Null[T] implements the Value interface for custom thrift encoding/decoding. +type Null[T any] struct { + V T + Valid bool +} + +// New creates a valid Null[T] with the given value. +func New[T any](v T) Null[T] { + return Null[T]{V: v, Valid: true} +} + +// Get returns the value and whether it is valid. +func (n Null[T]) Get() (T, bool) { + return n.V, n.Valid +} + +// Set sets the value and marks it as valid. +func (n *Null[T]) Set(v T) { + n.V = v + n.Valid = true +} + +// Reset clears the value and marks it as invalid. +func (n *Null[T]) Reset() { + var zero T + n.V = zero + n.Valid = false +} + +// Type returns the thrift type of the inner value T. +func (n Null[T]) Type() Type { + return TypeOf(reflect.TypeFor[T]()) +} + +// EncodeFunc returns an encode function that encodes the V field. +func (n Null[T]) EncodeFunc(cache EncodeFuncCache) EncodeFunc { + enc := EncodeFuncFor[T](cache) + return func(w Writer, v reflect.Value, f Flags) error { + return enc(w, v.Field(0), f) // Field 0 is V + } +} + +// DecodeFunc returns a decode function that decodes into V and sets Valid. +func (n Null[T]) DecodeFunc(cache DecodeFuncCache) DecodeFunc { + dec := DecodeFuncFor[T](cache) + return func(r Reader, v reflect.Value, f Flags) error { + if err := dec(r, v.Field(0), f); err != nil { // Field 0 is V + return err + } + v.Field(1).SetBool(true) // Field 1 is Valid + return nil + } +} + +// Bool returns the inner bool value. +// This method is only meaningful for Null[bool] and implements BoolValue. +func (n Null[T]) Bool() bool { + // Use reflection to check if T is bool and return the value + v := reflect.ValueOf(n.V) + if v.Kind() == reflect.Bool { + return v.Bool() + } + return false +} + +// NullFunc returns a function that checks if the value is not set. +func (n Null[T]) NullFunc() NullFunc { + return func(v reflect.Value) bool { + return !v.Field(1).Bool() // Field 1 is Valid + } +} diff --git a/vendor/github.com/parquet-go/parquet-go/encoding/thrift/protocol.go b/vendor/github.com/parquet-go/parquet-go/encoding/thrift/protocol.go new file mode 100644 index 000000000..1640398a8 --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/encoding/thrift/protocol.go @@ -0,0 +1,75 @@ +package thrift + +import ( + "io" +) + +// Features is a bitset describing the thrift encoding features supported by +// protocol implementations. +type Features uint + +const ( + // DeltaEncoding is advertised by protocols that allow encoders to apply + // delta encoding on struct fields. + UseDeltaEncoding Features = 1 << iota + + // CoalesceBoolFields is advertised by protocols that allow encoders to + // coalesce boolean values into field types. + CoalesceBoolFields +) + +// The Protocol interface abstracts the creation of low-level thrift readers and +// writers implementing the various protocols that the encoding supports. +// +// Protocol instances must be safe to use concurrently from multiple gourintes. +// However, the readers and writer that they instantiates are intended to be +// used by a single goroutine. +type Protocol interface { + NewReaderFromBytes(b []byte) Reader + NewReader(r io.Reader) Reader + NewWriter(w io.Writer) Writer + Features() Features +} + +// Reader represents a low-level reader of values encoded according to one of +// the thrift protocols. +type Reader interface { + Protocol() Protocol + Reader() io.Reader + ReadBool() (bool, error) + ReadInt8() (int8, error) + ReadInt16() (int16, error) + ReadInt32() (int32, error) + ReadInt64() (int64, error) + ReadFloat64() (float64, error) + ReadBytes() ([]byte, error) + ReadString() (string, error) + ReadLength() (int, error) + ReadMessage() (Message, error) + ReadField() (Field, error) + ReadList() (List, error) + ReadSet() (Set, error) + ReadMap() (Map, error) + BytesRead() int +} + +// Writer represents a low-level writer of values encoded according to one of +// the thrift protocols. +type Writer interface { + Protocol() Protocol + Writer() io.Writer + WriteBool(bool) error + WriteInt8(int8) error + WriteInt16(int16) error + WriteInt32(int32) error + WriteInt64(int64) error + WriteFloat64(float64) error + WriteBytes([]byte) error + WriteString(string) error + WriteLength(int) error + WriteMessage(Message) error + WriteField(Field) error + WriteList(List) error + WriteSet(Set) error + WriteMap(Map) error +} diff --git a/vendor/github.com/parquet-go/parquet-go/encoding/thrift/struct.go b/vendor/github.com/parquet-go/parquet-go/encoding/thrift/struct.go new file mode 100644 index 000000000..81f0fb2e6 --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/encoding/thrift/struct.go @@ -0,0 +1,154 @@ +package thrift + +import ( + "fmt" + "reflect" + "strconv" + "strings" +) + +// Flags controls encoding/decoding behavior. +type Flags int16 + +// Flag constants for struct field handling. +const ( + Enum Flags = 1 << 0 + Union Flags = 1 << 1 + Required Flags = 1 << 2 + Optional Flags = 1 << 3 + Strict Flags = 1 << 4 + WriteZero Flags = 1 << 5 + ValueType Flags = 1 << 6 // indicates field implements Value interface + + // Internal protocol feature flags + featuresBitOffset = 8 + useDeltaEncoding = Flags(UseDeltaEncoding) << featuresBitOffset + coalesceBoolFields = Flags(CoalesceBoolFields) << featuresBitOffset + + decodeFlags Flags = Strict | protocolFlags + protocolFlags Flags = useDeltaEncoding | coalesceBoolFields +) + +// Have returns true if f has all the bits in x. +func (f Flags) Have(x Flags) bool { + return (f & x) == x +} + +// Only returns only the bits in f that are also in x. +func (f Flags) Only(x Flags) Flags { + return f & x +} + +// With returns f with the bits in x set. +func (f Flags) With(x Flags) Flags { + return f | x +} + +// Without returns f with the bits in x cleared. +func (f Flags) Without(x Flags) Flags { + return f & ^x +} + +type structField struct { + typ reflect.Type + index []int + id int16 + flags Flags +} + +func forEachStructField(t reflect.Type, index []int, do func(structField)) { + for i, n := 0, t.NumField(); i < n; i++ { + f := t.Field(i) + + if f.PkgPath != "" && !f.Anonymous { // unexported + continue + } + + fieldIndex := append(index, i) + fieldIndex = fieldIndex[:len(fieldIndex):len(fieldIndex)] + + if f.Anonymous { + fieldType := f.Type + + for fieldType.Kind() == reflect.Ptr { + fieldType = fieldType.Elem() + } + + if fieldType.Kind() == reflect.Struct { + forEachStructField(fieldType, fieldIndex, do) + continue + } + } + + tag := f.Tag.Get("thrift") + if tag == "" { + continue + } + tags := strings.Split(tag, ",") + flags := Flags(0) + + for _, opt := range tags[1:] { + switch opt { + case "enum": + flags = flags.With(Enum) + case "union": + flags = flags.With(Union) + case "required": + flags = flags.With(Required) + case "optional": + flags = flags.With(Optional) + case "writezero": + flags = flags.With(WriteZero) + default: + panic(fmt.Errorf("thrift struct field contains an unknown tag option %q in `thrift:\"%s\"`", opt, tag)) + } + } + + if flags.Have(Optional | Required) { + panic(fmt.Errorf("thrift struct field cannot be both optional and required in `thrift:\"%s\"`", tag)) + } + + if flags.Have(Union) { + if f.Type.Kind() != reflect.Interface { + panic(fmt.Errorf("thrift union tag found on a field which is not an interface type `thrift:\"%s\"`", tag)) + } + + if tags[0] != "" { + panic(fmt.Errorf("invalid thrift field id on union field `thrift:\"%s\"`", tag)) + } + + do(structField{ + typ: f.Type, + index: fieldIndex, + flags: flags, + }) + } else { + if flags.Have(Enum) { + switch f.Type.Kind() { + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + default: + panic(fmt.Errorf("thrift enum tag found on a field which is not an integer type `thrift:\"%s\"`", tag)) + } + } + + // Detect types implementing Value interface + if f.Type.Implements(valueType) { + flags = flags.With(ValueType) + } + + if id, err := strconv.ParseInt(tags[0], 10, 16); err != nil { + panic(fmt.Errorf("invalid thrift field id found in struct tag `thrift:\"%s\"`: %w", tag, err)) + } else if id <= 0 { + panic(fmt.Errorf("invalid thrift field id found in struct tag `thrift:\"%s\"`: %d <= 0", tag, id)) + } else { + do(structField{ + typ: f.Type, + index: fieldIndex, + id: int16(id), + flags: flags, + }) + } + } + } +} diff --git a/vendor/github.com/parquet-go/parquet-go/encoding/thrift/thrift.go b/vendor/github.com/parquet-go/parquet-go/encoding/thrift/thrift.go new file mode 100644 index 000000000..861329efd --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/encoding/thrift/thrift.go @@ -0,0 +1,173 @@ +package thrift + +import ( + "fmt" + "reflect" +) + +type Message struct { + Type MessageType + Name string + SeqID int32 +} + +type MessageType int8 + +const ( + Call MessageType = iota + Reply + Exception + Oneway +) + +func (m MessageType) String() string { + switch m { + case Call: + return "Call" + case Reply: + return "Reply" + case Exception: + return "Exception" + case Oneway: + return "Oneway" + default: + return "?" + } +} + +type Field struct { + ID int16 + Type Type + Delta bool // whether the field id is a delta +} + +func (f Field) String() string { + return fmt.Sprintf("%d:FIELD<%s>", f.ID, f.Type) +} + +type Type int8 + +const ( + STOP Type = iota + TRUE + FALSE + I8 + I16 + I32 + I64 + DOUBLE + BINARY + LIST + SET + MAP + STRUCT + UUID + BOOL = FALSE +) + +func (t Type) String() string { + switch t { + case STOP: + return "STOP" + case TRUE: + return "TRUE" + case BOOL: + return "BOOL" + case I8: + return "I8" + case I16: + return "I16" + case I32: + return "I32" + case I64: + return "I64" + case DOUBLE: + return "DOUBLE" + case BINARY: + return "BINARY" + case LIST: + return "LIST" + case SET: + return "SET" + case MAP: + return "MAP" + case STRUCT: + return "STRUCT" + case UUID: + return "UUID" + default: + return "?" + } +} + +func (t Type) GoString() string { + return "thrift." + t.String() +} + +type List struct { + Size int32 + Type Type +} + +func (l List) String() string { + return fmt.Sprintf("LIST<%s>", l.Type) +} + +type Set List + +func (s Set) String() string { + return fmt.Sprintf("SET<%s>", s.Type) +} + +type Map struct { + Size int32 + Key Type + Value Type +} + +func (m Map) String() string { + return fmt.Sprintf("MAP<%s,%s>", m.Key, m.Value) +} + +func TypeOf(t reflect.Type) Type { + // Check if type implements Value interface first + if t.Implements(valueType) { + zv := reflect.Zero(t).Interface().(Value) + return zv.Type() + } + + switch t.Kind() { + case reflect.Bool: + return BOOL + case reflect.Int8, reflect.Uint8: + return I8 + case reflect.Int16, reflect.Uint16: + return I16 + case reflect.Int32, reflect.Uint32: + return I32 + case reflect.Int64, reflect.Uint64, reflect.Int, reflect.Uint, reflect.Uintptr: + return I64 + case reflect.Float32, reflect.Float64: + return DOUBLE + case reflect.String: + return BINARY + case reflect.Slice: + if t.Elem().Kind() == reflect.Uint8 { // []byte + return BINARY + } else { + return LIST + } + case reflect.Map: + if t.Elem().Size() == 0 { + return SET + } else { + return MAP + } + case reflect.Struct: + return STRUCT + case reflect.Ptr: + return TypeOf(t.Elem()) + default: + panic("type cannot be represented in thrift: " + t.String()) + } +} diff --git a/vendor/github.com/parquet-go/parquet-go/encoding/thrift/unsafe.go b/vendor/github.com/parquet-go/parquet-go/encoding/thrift/unsafe.go new file mode 100644 index 000000000..b27c6489d --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/encoding/thrift/unsafe.go @@ -0,0 +1,20 @@ +package thrift + +import ( + "reflect" + "unsafe" +) + +// typeID is used as key in encoder and decoder caches to enable using +// the optimize runtime.mapaccess2_fast64 function instead of the more +// expensive lookup if we were to use reflect.Type as map key. +// +// typeID holds the pointer to the reflect.Type value, which is unique +// in the program. +type typeID struct{ ptr unsafe.Pointer } + +func makeTypeID(t reflect.Type) typeID { + return typeID{ + ptr: (*[2]unsafe.Pointer)(unsafe.Pointer(&t))[1], + } +} diff --git a/vendor/github.com/parquet-go/parquet-go/encoding/thrift/value.go b/vendor/github.com/parquet-go/parquet-go/encoding/thrift/value.go new file mode 100644 index 000000000..56a332b1b --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/encoding/thrift/value.go @@ -0,0 +1,45 @@ +package thrift + +import "reflect" + +// NullFunc checks if a value should be skipped during encoding. +type NullFunc func(reflect.Value) bool + +// Value is implemented by types that provide custom thrift encoding/decoding. +// Types implementing this interface control how they are serialized. +// +// The NullFunc method returns a function that checks whether a value should +// be skipped during encoding. Optional types return true when not set; +// collection types return true when nil. +type Value interface { + // Type returns the thrift type for this value. + Type() Type + + // EncodeFunc returns the encode function for this type. + // The cache parameter allows recursive encoding of nested types. + EncodeFunc(cache EncodeFuncCache) EncodeFunc + + // DecodeFunc returns the decode function for this type. + DecodeFunc(cache DecodeFuncCache) DecodeFunc + + // NullFunc returns a function that checks if the value should be skipped + // during encoding. For optional types like Null[T], this checks if the + // value is not set. For collection types like Slice[T], this checks if + // the slice is nil. + NullFunc() NullFunc +} + +// BoolValue extends Value for types that wrap a boolean value. +// This is used for coalesced bool field encoding where the bool +// value is encoded in the field type (TRUE/FALSE) rather than +// as a separate value. +type BoolValue interface { + Value + Bool() bool +} + +// Cached interface types for reflection checks +var ( + valueType = reflect.TypeFor[Value]() + boolValueType = reflect.TypeFor[BoolValue]() +) diff --git a/vendor/github.com/parquet-go/parquet-go/encoding/values.go b/vendor/github.com/parquet-go/parquet-go/encoding/values.go new file mode 100644 index 000000000..9e82eb7cc --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/encoding/values.go @@ -0,0 +1,276 @@ +package encoding + +import ( + "fmt" + + "github.com/parquet-go/bitpack/unsafecast" + "github.com/parquet-go/parquet-go/deprecated" +) + +type Kind int32 + +const ( + Undefined Kind = iota + Boolean + Int32 + Int64 + Int96 + Float + Double + ByteArray + FixedLenByteArray +) + +func (kind Kind) String() string { + switch kind { + case Boolean: + return "BOOLEAN" + case Int32: + return "INT32" + case Int64: + return "INT64" + case Int96: + return "INT96" + case Float: + return "FLOAT" + case Double: + return "DOUBLE" + case ByteArray: + return "BYTE_ARRAY" + case FixedLenByteArray: + return "FIXED_LEN_BYTE_ARRAY" + default: + return "UNDEFINED" + } +} + +type Values struct { + kind Kind + size int32 + data []byte + offsets []uint32 +} + +func (v *Values) assertKind(kind Kind) { + if kind != v.kind { + panic(fmt.Sprintf("cannot convert values of type %s to type %s", v.kind, kind)) + } +} + +func (v *Values) assertSize(size int) { + if size != int(v.size) { + panic(fmt.Sprintf("cannot convert values of size %d to size %d", v.size, size)) + } +} + +func (v *Values) Size() int64 { + return int64(len(v.data)) +} + +func (v *Values) Kind() Kind { + return v.kind +} + +func (v *Values) Data() (data []byte, offsets []uint32) { + return v.data, v.offsets +} + +func (v *Values) Boolean() []byte { + v.assertKind(Boolean) + return v.data +} + +func (v *Values) Int32() []int32 { + v.assertKind(Int32) + return unsafecast.Slice[int32](v.data) +} + +func (v *Values) Int64() []int64 { + v.assertKind(Int64) + return unsafecast.Slice[int64](v.data) +} + +func (v *Values) Int96() []deprecated.Int96 { + v.assertKind(Int96) + return unsafecast.Slice[deprecated.Int96](v.data) +} + +func (v *Values) Float() []float32 { + v.assertKind(Float) + return unsafecast.Slice[float32](v.data) +} + +func (v *Values) Double() []float64 { + v.assertKind(Double) + return unsafecast.Slice[float64](v.data) +} + +func (v *Values) ByteArray() (data []byte, offsets []uint32) { + v.assertKind(ByteArray) + return v.data, v.offsets +} + +func (v *Values) FixedLenByteArray() (data []byte, size int) { + v.assertKind(FixedLenByteArray) + return v.data, int(v.size) +} + +func (v *Values) Uint32() []uint32 { + v.assertKind(Int32) + return unsafecast.Slice[uint32](v.data) +} + +func (v *Values) Uint64() []uint64 { + v.assertKind(Int64) + return unsafecast.Slice[uint64](v.data) +} + +func (v *Values) Uint128() [][16]byte { + v.assertKind(FixedLenByteArray) + v.assertSize(16) + return unsafecast.Slice[[16]byte](v.data) +} + +func makeValues[T any](kind Kind, values []T) Values { + return Values{kind: kind, data: unsafecast.Slice[byte](values)} +} + +func BooleanValues(values []byte) Values { + return makeValues(Boolean, values) +} + +func Int32Values(values []int32) Values { + return makeValues(Int32, values) +} + +func Int64Values(values []int64) Values { + return makeValues(Int64, values) +} + +func Int96Values(values []deprecated.Int96) Values { + return makeValues(Int96, values) +} + +func FloatValues(values []float32) Values { + return makeValues(Float, values) +} + +func DoubleValues(values []float64) Values { + return makeValues(Double, values) +} + +func ByteArrayValues(values []byte, offsets []uint32) Values { + return Values{kind: ByteArray, data: values, offsets: offsets} +} + +func FixedLenByteArrayValues(values []byte, size int) Values { + return Values{kind: FixedLenByteArray, size: int32(size), data: values} +} + +func Uint32Values(values []uint32) Values { + return Int32Values(unsafecast.Slice[int32](values)) +} + +func Uint64Values(values []uint64) Values { + return Int64Values(unsafecast.Slice[int64](values)) +} + +func Uint128Values(values [][16]byte) Values { + return FixedLenByteArrayValues(unsafecast.Slice[byte](values), 16) +} + +func Int32ValuesFromBytes(values []byte) Values { + return Values{kind: Int32, data: values} +} + +func Int64ValuesFromBytes(values []byte) Values { + return Values{kind: Int64, data: values} +} + +func Int96ValuesFromBytes(values []byte) Values { + return Values{kind: Int96, data: values} +} + +func FloatValuesFromBytes(values []byte) Values { + return Values{kind: Float, data: values} +} + +func DoubleValuesFromBytes(values []byte) Values { + return Values{kind: Double, data: values} +} + +func EncodeBoolean(dst []byte, src Values, enc Encoding) ([]byte, error) { + return enc.EncodeBoolean(dst, src.Boolean()) +} + +func EncodeInt32(dst []byte, src Values, enc Encoding) ([]byte, error) { + return enc.EncodeInt32(dst, src.Int32()) +} + +func EncodeInt64(dst []byte, src Values, enc Encoding) ([]byte, error) { + return enc.EncodeInt64(dst, src.Int64()) +} + +func EncodeInt96(dst []byte, src Values, enc Encoding) ([]byte, error) { + return enc.EncodeInt96(dst, src.Int96()) +} + +func EncodeFloat(dst []byte, src Values, enc Encoding) ([]byte, error) { + return enc.EncodeFloat(dst, src.Float()) +} + +func EncodeDouble(dst []byte, src Values, enc Encoding) ([]byte, error) { + return enc.EncodeDouble(dst, src.Double()) +} + +func EncodeByteArray(dst []byte, src Values, enc Encoding) ([]byte, error) { + values, offsets := src.ByteArray() + return enc.EncodeByteArray(dst, values, offsets) +} + +func EncodeFixedLenByteArray(dst []byte, src Values, enc Encoding) ([]byte, error) { + data, size := src.FixedLenByteArray() + return enc.EncodeFixedLenByteArray(dst, data, size) +} + +func DecodeBoolean(dst Values, src []byte, enc Encoding) (Values, error) { + values, err := enc.DecodeBoolean(dst.Boolean(), src) + return BooleanValues(values), err +} + +func DecodeInt32(dst Values, src []byte, enc Encoding) (Values, error) { + values, err := enc.DecodeInt32(dst.Int32(), src) + return Int32Values(values), err +} + +func DecodeInt64(dst Values, src []byte, enc Encoding) (Values, error) { + values, err := enc.DecodeInt64(dst.Int64(), src) + return Int64Values(values), err +} + +func DecodeInt96(dst Values, src []byte, enc Encoding) (Values, error) { + values, err := enc.DecodeInt96(dst.Int96(), src) + return Int96Values(values), err +} + +func DecodeFloat(dst Values, src []byte, enc Encoding) (Values, error) { + values, err := enc.DecodeFloat(dst.Float(), src) + return FloatValues(values), err +} + +func DecodeDouble(dst Values, src []byte, enc Encoding) (Values, error) { + values, err := enc.DecodeDouble(dst.Double(), src) + return DoubleValues(values), err +} + +func DecodeByteArray(dst Values, src []byte, enc Encoding) (Values, error) { + values, offsets := dst.ByteArray() + values, offsets, err := enc.DecodeByteArray(values, src, offsets) + return ByteArrayValues(values, offsets), err +} + +func DecodeFixedLenByteArray(dst Values, src []byte, enc Encoding) (Values, error) { + data, size := dst.FixedLenByteArray() + values, err := enc.DecodeFixedLenByteArray(data, src, size) + return FixedLenByteArrayValues(values, size), err +} diff --git a/vendor/github.com/parquet-go/parquet-go/encrypt.go b/vendor/github.com/parquet-go/parquet-go/encrypt.go new file mode 100644 index 000000000..645885250 --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/encrypt.go @@ -0,0 +1,301 @@ +package parquet + +import ( + "crypto/aes" + "crypto/cipher" + "crypto/rand" + "encoding/binary" + "errors" + "fmt" + "io" + "strings" +) + +// Module type constants per the Apache Parquet encryption spec. +const ( + footerModule byte = 0 + columnMetaDataModule byte = 1 + dataPageBodyModule byte = 2 + dataPageHeaderModule byte = 3 + dictPageBodyModule byte = 4 + dictPageHeaderModule byte = 5 + bloomFilterHdrModule byte = 6 + bloomFilterBitsModule byte = 7 + columnIndexModule byte = 8 + offsetIndexModule byte = 9 +) + +const ( + encNonceSize = 12 + encTagSize = 16 + // encOverhead is the total per-module overhead: 4-byte length + nonce + GCM tag. + encOverhead = 4 + encNonceSize + encTagSize +) + +// EncryptionAlgorithmType selects the Parquet encryption algorithm. +type EncryptionAlgorithmType int + +const ( + // AES_GCM_V1 is the default algorithm (AES-GCM authenticated encryption). + AES_GCM_V1 EncryptionAlgorithmType = iota + // AES_GCM_CTR_V1 is the CTR variant (deprecated, included for spec completeness). + AES_GCM_CTR_V1 +) + +// EncryptionConfig holds the write-side encryption configuration. +type EncryptionConfig struct { + // FooterKey is the AES key used to encrypt the footer and any columns + // that do not have a per-column key. Must be 16, 24, or 32 bytes. + FooterKey []byte + + // ColumnKeys maps a dot-joined column path (e.g. "a.b.c") to its AES key. + // Columns not listed here use FooterKey. + ColumnKeys map[string][]byte + + // Algorithm selects AES_GCM_V1 (default) or AES_GCM_CTR_V1. + Algorithm EncryptionAlgorithmType + + // EncryptedFooter controls the file layout: + // true → encrypted footer, file uses "PARE" magic. + // false → plaintext footer with GCM signature appended, file uses "PAR1" magic. + EncryptedFooter bool + + // AadPrefix is an optional byte prefix that is prepended to every AAD. + AadPrefix []byte + + // FileIdentifier is an 8-byte per-file unique value embedded in every AAD. + // If nil, 8 random bytes are generated when the writer is created. + FileIdentifier []byte +} + +// ErrKeyNotFound is the sentinel error that a KeyRetriever should return (or +// wrap with %w) when the caller intentionally does not have access to a +// particular column key. OpenFile treats this as a non-fatal signal and +// leaves that column inaccessible rather than aborting the open. Any other +// error from ColumnKey is treated as a hard failure and propagated to the +// caller. +var ErrKeyNotFound = errors.New("parquet: encryption key not found") + +// KeyRetriever resolves AES keys from the metadata bytes stored in the file. +// Implement this interface to supply keys when opening an encrypted parquet file. +type KeyRetriever interface { + // FooterKey returns the AES key for the file footer. + // keyMetadata is the optional bytes stored in FileCryptoMetaData.KeyMetadata + // (may be nil). + FooterKey(keyMetadata []byte) ([]byte, error) + + // ColumnKey returns the AES key for an encrypted column. + // path is the column's path in the schema; keyMetadata is the optional bytes + // stored in EncryptionWithColumnKey.KeyMetadata (may be nil). + // + // To signal that a particular column's key is intentionally unavailable + // (e.g. the caller only holds keys for a subset of columns), return an + // error that wraps ErrKeyNotFound: + // + // return nil, fmt.Errorf("no key for %v: %w", path, parquet.ErrKeyNotFound) + // + // OpenFile treats ErrKeyNotFound as non-fatal and leaves that column + // inaccessible; any other non-nil error is propagated as a hard failure. + ColumnKey(path []string, keyMetadata []byte) ([]byte, error) +} + +// DecryptionConfig holds the read-side decryption configuration. +type DecryptionConfig struct { + Keys KeyRetriever +} + +// WithDecryption returns a FileOption that configures decryption with the given KeyRetriever. +func WithDecryption(keys KeyRetriever) FileOption { + return &fileDecryptionOption{cfg: &DecryptionConfig{Keys: keys}} +} + +type fileDecryptionOption struct{ cfg *DecryptionConfig } + +func (o *fileDecryptionOption) ConfigureFile(c *FileConfig) { c.Decryption = o.cfg } + +// WithEncryption returns a WriterOption that configures encryption. +func WithEncryption(cfg *EncryptionConfig) WriterOption { + return &writerEncryptionOption{cfg: cfg} +} + +type writerEncryptionOption struct{ cfg *EncryptionConfig } + +func (o *writerEncryptionOption) ConfigureWriter(c *WriterConfig) { c.Encryption = o.cfg } + +// ----------------------------------------------------------------------- +// Internal state +// ----------------------------------------------------------------------- + +// fileEncryptionState is the per-file encryption context used during writing. +type fileEncryptionState struct { + cfg *EncryptionConfig + fileUnique []byte // 8-byte random per-file identifier stored in AesGcmV1.AadFileUnique +} + +func newFileEncryptionState(cfg *EncryptionConfig) (*fileEncryptionState, error) { + if cfg.Algorithm == AES_GCM_CTR_V1 { + return nil, fmt.Errorf("parquet encryption: AES_GCM_CTR_V1 is not yet implemented; use AES_GCM_V1") + } + s := &fileEncryptionState{cfg: cfg} + if len(cfg.FileIdentifier) > 0 { + s.fileUnique = cfg.FileIdentifier + } else { + s.fileUnique = make([]byte, 8) + if _, err := io.ReadFull(rand.Reader, s.fileUnique); err != nil { + return nil, fmt.Errorf("parquet encryption: generating file identifier: %w", err) + } + } + return s, nil +} + +// columnKeyFor returns the AES key for the given dot-joined column path, +// falling back to FooterKey when no per-column key is configured. +func (s *fileEncryptionState) columnKeyFor(path string) []byte { + if s.cfg.ColumnKeys != nil { + if key, ok := s.cfg.ColumnKeys[path]; ok { + return key + } + } + return s.cfg.FooterKey +} + +// ----------------------------------------------------------------------- +// Core cryptographic primitives +// ----------------------------------------------------------------------- + +// encryptModule encrypts plaintext as a Parquet module envelope. +// +// Wire format: 4-byte LE length | 12-byte nonce | AES-GCM(plaintext) | 16-byte tag +// The length field equals len(plaintext) + encNonceSize + encTagSize. +func encryptModule(key, aad, plaintext []byte) ([]byte, error) { + block, err := aes.NewCipher(key) + if err != nil { + return nil, fmt.Errorf("parquet encryption: creating AES cipher: %w", err) + } + gcm, err := cipher.NewGCM(block) + if err != nil { + return nil, fmt.Errorf("parquet encryption: creating GCM: %w", err) + } + + var nonce [encNonceSize]byte + if _, err := io.ReadFull(rand.Reader, nonce[:]); err != nil { + return nil, fmt.Errorf("parquet encryption: generating nonce: %w", err) + } + + // cipherLen = len(plaintext) + gcm.Overhead() (= tag size = 16) + cipherLen := len(plaintext) + gcm.Overhead() + // moduleLen = nonce + ciphertext+tag (= what the 4-byte length field encodes) + moduleLen := encNonceSize + cipherLen + + out := make([]byte, 4+moduleLen) + binary.LittleEndian.PutUint32(out[:4], uint32(moduleLen)) + copy(out[4:4+encNonceSize], nonce[:]) + gcm.Seal(out[4+encNonceSize:4+encNonceSize], nonce[:], plaintext, aad) + return out, nil +} + +// decryptModule decrypts a Parquet module envelope. +// +// envelope must start with the 4-byte length field. +// Returns the decrypted plaintext. +func decryptModule(key, aad, envelope []byte) ([]byte, error) { + if len(envelope) < 4 { + return nil, fmt.Errorf("parquet decryption: module envelope too short (%d bytes)", len(envelope)) + } + moduleLen := int(binary.LittleEndian.Uint32(envelope[:4])) + if len(envelope) < 4+moduleLen { + return nil, fmt.Errorf("parquet decryption: envelope truncated: need %d bytes, have %d", 4+moduleLen, len(envelope)) + } + if moduleLen < encNonceSize+encTagSize { + return nil, fmt.Errorf("parquet decryption: module too short to contain nonce+tag (%d bytes)", moduleLen) + } + + block, err := aes.NewCipher(key) + if err != nil { + return nil, fmt.Errorf("parquet decryption: creating AES cipher: %w", err) + } + gcm, err := cipher.NewGCM(block) + if err != nil { + return nil, fmt.Errorf("parquet decryption: creating GCM: %w", err) + } + + nonce := envelope[4 : 4+encNonceSize] + ciphertext := envelope[4+encNonceSize : 4+moduleLen] + + plaintext, err := gcm.Open(nil, nonce, ciphertext, aad) + if err != nil { + return nil, fmt.Errorf("parquet decryption: AES-GCM authentication failed: %w", err) + } + return plaintext, nil +} + +// makeAAD constructs the Additional Authenticated Data for a module. +// +// Layout: aadPrefix || fileUnique || moduleType [ || ordinals... (int16 LE each) ] +func makeAAD(aadPrefix, fileUnique []byte, moduleType byte, ordinals ...int16) []byte { + buf := make([]byte, 0, len(aadPrefix)+len(fileUnique)+1+len(ordinals)*2) + buf = append(buf, aadPrefix...) + buf = append(buf, fileUnique...) + buf = append(buf, moduleType) + for _, ord := range ordinals { + buf = append(buf, byte(ord), byte(ord>>8)) + } + return buf +} + +// signFooter produces the 28-byte footer signature (nonce || GCM tag) used in +// plaintext-footer mode. The footer bytes are authenticated (but not encrypted) +// as additional data with an empty plaintext. +func signFooter(key, aad, footerBytes []byte) ([]byte, error) { + block, err := aes.NewCipher(key) + if err != nil { + return nil, fmt.Errorf("parquet encryption: footer sign: creating cipher: %w", err) + } + gcm, err := cipher.NewGCM(block) + if err != nil { + return nil, fmt.Errorf("parquet encryption: footer sign: creating GCM: %w", err) + } + + var nonce [encNonceSize]byte + if _, err := io.ReadFull(rand.Reader, nonce[:]); err != nil { + return nil, fmt.Errorf("parquet encryption: footer sign: generating nonce: %w", err) + } + + // Encrypt empty plaintext; footerBytes become the AAD so the tag authenticates them. + combined := append(aad, footerBytes...) + tag := gcm.Seal(nil, nonce[:], nil, combined) + + sig := make([]byte, encNonceSize+encTagSize) + copy(sig[:encNonceSize], nonce[:]) + copy(sig[encNonceSize:], tag) + return sig, nil +} + +// verifyFooterSignature checks the 28-byte signature appended to the footer. +func verifyFooterSignature(key, aad, footerBytes, sig []byte) error { + if len(sig) != encNonceSize+encTagSize { + return fmt.Errorf("parquet decryption: footer signature has wrong length %d (want %d)", len(sig), encNonceSize+encTagSize) + } + block, err := aes.NewCipher(key) + if err != nil { + return fmt.Errorf("parquet decryption: footer verify: creating cipher: %w", err) + } + gcm, err := cipher.NewGCM(block) + if err != nil { + return fmt.Errorf("parquet decryption: footer verify: creating GCM: %w", err) + } + + nonce := sig[:encNonceSize] + tag := sig[encNonceSize:] + combined := append(aad, footerBytes...) + _, err = gcm.Open(nil, nonce, tag, combined) + if err != nil { + return fmt.Errorf("parquet decryption: footer signature verification failed: %w", err) + } + return nil +} + +// columnPathString returns the dot-joined string for a column path. +func columnPathString(path []string) string { + return strings.Join(path, ".") +} diff --git a/vendor/github.com/parquet-go/parquet-go/errors.go b/vendor/github.com/parquet-go/parquet-go/errors.go new file mode 100644 index 000000000..651fe740c --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/errors.go @@ -0,0 +1,87 @@ +package parquet + +import ( + "errors" + "fmt" +) + +var ( + // ErrCorrupted is an error returned by the Err method of ColumnPages + // instances when they encountered a mismatch between the CRC checksum + // recorded in a page header and the one computed while reading the page + // data. + ErrCorrupted = errors.New("corrupted parquet page") + + // ErrMissingRootColumn is an error returned when opening an invalid parquet + // file which does not have a root column. + ErrMissingRootColumn = errors.New("parquet file is missing a root column") + + // ErrRowGroupSchemaMissing is an error returned when attempting to write a + // row group but the source has no schema. + ErrRowGroupSchemaMissing = errors.New("cannot write rows to a row group which has no schema") + + // ErrRowGroupSchemaMismatch is an error returned when attempting to write a + // row group but the source and destination schemas differ. + ErrRowGroupSchemaMismatch = errors.New("cannot write row groups with mismatching schemas") + + // ErrRowGroupSortingColumnsMismatch is an error returned when attempting to + // write a row group but the sorting columns differ in the source and + // destination. + ErrRowGroupSortingColumnsMismatch = errors.New("cannot write row groups with mismatching sorting columns") + + // ErrSeekOutOfRange is an error returned when seeking to a row index which + // is less than the first row of a page. + ErrSeekOutOfRange = errors.New("seek to row index out of page range") + + // ErrUnexpectedDictionaryPage is an error returned when a page reader + // encounters a dictionary page after the first page, or in a column + // which does not use a dictionary encoding. + ErrUnexpectedDictionaryPage = errors.New("unexpected dictionary page") + + // ErrMissingPageHeader is an error returned when a page reader encounters + // a malformed page header which is missing page-type-specific information. + ErrMissingPageHeader = errors.New("missing page header") + + // ErrUnexpectedRepetitionLevels is an error returned when attempting to + // decode repetition levels into a page which is not part of a repeated + // column. + ErrUnexpectedRepetitionLevels = errors.New("unexpected repetition levels") + + // ErrUnexpectedDefinitionLevels is an error returned when attempting to + // decode definition levels into a page which is part of a required column. + ErrUnexpectedDefinitionLevels = errors.New("unexpected definition levels") + + // ErrTooManyRowGroups is returned when attempting to generate a parquet + // file with more than MaxRowGroups row groups. + ErrTooManyRowGroups = errors.New("the limit of 32767 row groups has been reached") + + // ErrConversion is used to indicate that a conversion betwen two values + // cannot be done because there are no rules to translate between their + // physical types. + ErrInvalidConversion = errors.New("invalid conversion between parquet values") + + // ErrMalformedRepetitionLevel is returned when a page reader encounters + // a repetition level which does not start at the beginning of a row. + ErrMalformedRepetitionLevel = errors.New("parquet-go encountered a malformed data page which does not start at the beginning of a row") +) + +type errno int + +const ( + ok errno = iota + indexOutOfBounds +) + +func (e errno) check() { + switch e { + case ok: + case indexOutOfBounds: + panic("index out of bounds") + default: + panic("BUG: unknown error code") + } +} + +func errRowIndexOutOfBounds(rowIndex, rowCount int64) error { + return fmt.Errorf("row index out of bounds: %d/%d", rowIndex, rowCount) +} diff --git a/vendor/github.com/parquet-go/parquet-go/file.go b/vendor/github.com/parquet-go/parquet-go/file.go new file mode 100644 index 000000000..11c697b04 --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/file.go @@ -0,0 +1,1658 @@ +package parquet + +import ( + "bufio" + "bytes" + "encoding/binary" + "errors" + "fmt" + "hash/crc32" + "io" + "slices" + "sort" + "strings" + "sync" + "sync/atomic" + + "github.com/parquet-go/parquet-go/encoding/thrift" + "github.com/parquet-go/parquet-go/format" + "github.com/parquet-go/parquet-go/internal/memory" +) + +const ( + defaultDictBufferSize = 8192 + defaultReadBufferSize = 4096 +) + +// File represents a parquet file. The layout of a Parquet file can be found +// here: https://github.com/apache/parquet-format#file-format +type File struct { + metadata format.FileMetaData + protocol thrift.CompactProtocol + reader io.ReaderAt + size int64 + schema *Schema + root *Column + columnIndexes []format.ColumnIndex + offsetIndexes []format.OffsetIndex + rowGroups []RowGroup + config *FileConfig + + // Decryption state; non-nil when the file has encryption metadata. + decryption *DecryptionConfig + fileUnique []byte // AadFileUnique from encryption algorithm + aadPrefix []byte // AadPrefix from encryption algorithm +} + +type FileView interface { + Metadata() *format.FileMetaData + Schema() *Schema + NumRows() int64 + Lookup(key string) (string, bool) + Size() int64 + Root() *Column + RowGroups() []RowGroup + ColumnIndexes() []format.ColumnIndex + OffsetIndexes() []format.OffsetIndex +} + +// OpenFile opens a parquet file and reads the content between offset 0 and the given +// size in r. +// +// Only the parquet magic bytes and footer are read, column chunks and other +// parts of the file are left untouched; this means that successfully opening +// a file does not validate that the pages have valid checksums. +func OpenFile(r io.ReaderAt, size int64, options ...FileOption) (*File, error) { + c, err := NewFileConfig(options...) + if err != nil { + return nil, err + } + f := &File{reader: r, size: size, config: c} + + var headerMagic [4]byte + if !c.SkipMagicBytes { + if _, err := readAt(r, headerMagic[:], 0); err != nil { + return nil, fmt.Errorf("reading magic header of parquet file: %w", err) + } + switch string(headerMagic[:]) { + case "PAR1": + // plain or plaintext-footer-with-signature + case "PARE": + // encrypted footer + if c.Decryption == nil { + return nil, fmt.Errorf("parquet file has encrypted footer (magic \"PARE\") but no DecryptionConfig was provided") + } + default: + return nil, fmt.Errorf("invalid magic header of parquet file: %q", headerMagic[:]) + } + } + + if cast, ok := f.reader.(interface{ SetMagicFooterSection(offset, length int64) }); ok { + cast.SetMagicFooterSection(size-8, 8) + } + + optimisticRead := c.OptimisticRead + optimisticFooterSize := min(int64(c.ReadBufferSize), size) + if !optimisticRead || optimisticFooterSize < 8 { + optimisticFooterSize = 8 + } + optimisticFooterData := make([]byte, optimisticFooterSize) + if optimisticRead { + f.reader = &optimisticFileReaderAt{ + reader: f.reader, + offset: size - optimisticFooterSize, + footer: optimisticFooterData, + } + } + + if n, err := readAt(r, optimisticFooterData, size-optimisticFooterSize); n != len(optimisticFooterData) { + return nil, fmt.Errorf("reading magic footer of parquet file: %w (read: %d)", err, n) + } + optimisticFooterSize -= 8 + b := optimisticFooterData[optimisticFooterSize:] + footerMagic := string(b[4:]) + if footerMagic != "PAR1" && footerMagic != "PARE" { + return nil, fmt.Errorf("invalid magic footer of parquet file: %q", b[4:]) + } + + footerSize := int64(binary.LittleEndian.Uint32(b[:4])) + footerData := []byte(nil) + + if footerSize <= optimisticFooterSize { + footerData = optimisticFooterData[optimisticFooterSize-footerSize : optimisticFooterSize] + } else { + footerData = make([]byte, footerSize) + if cast, ok := f.reader.(interface{ SetFooterSection(offset, length int64) }); ok { + cast.SetFooterSection(size-(footerSize+8), footerSize) + } + if _, err := f.readAt(footerData, size-(footerSize+8)); err != nil { + return nil, fmt.Errorf("reading footer of parquet file: %w", err) + } + } + + if footerMagic == "PARE" { + // Encrypted footer: footerData = FileCryptoMetaData (thrift) || encrypted footer module. + // Decode FileCryptoMetaData using a bytes-backed reader to count bytes consumed. + pr := f.protocol.NewReaderFromBytes(footerData) + var cryptoMeta format.FileCryptoMetaData + if err := thrift.NewDecoder(pr).Decode(&cryptoMeta); err != nil { + return nil, fmt.Errorf("reading FileCryptoMetaData: %w", err) + } + encFooterEnvelope := footerData[pr.BytesRead():] + + // Extract AAD parameters from the encryption algorithm. + var fileUnique, aadPrefix []byte + switch { + case cryptoMeta.EncryptionAlgorithm.AesGcmV1 != nil: + fileUnique = cryptoMeta.EncryptionAlgorithm.AesGcmV1.AadFileUnique + aadPrefix = cryptoMeta.EncryptionAlgorithm.AesGcmV1.AadPrefix + case cryptoMeta.EncryptionAlgorithm.AesGcmCtrV1 != nil: + fileUnique = cryptoMeta.EncryptionAlgorithm.AesGcmCtrV1.AadFileUnique + aadPrefix = cryptoMeta.EncryptionAlgorithm.AesGcmCtrV1.AadPrefix + } + + footerKey, err := c.Decryption.Keys.FooterKey(cryptoMeta.KeyMetadata) + if err != nil { + return nil, fmt.Errorf("retrieving footer key: %w", err) + } + footerAAD := makeAAD(aadPrefix, fileUnique, footerModule) + plainFooter, err := decryptModule(footerKey, footerAAD, encFooterEnvelope) + if err != nil { + return nil, fmt.Errorf("decrypting footer: %w", err) + } + if err := thrift.Unmarshal(&f.protocol, plainFooter, &f.metadata); err != nil { + return nil, fmt.Errorf("reading parquet file metadata from decrypted footer: %w", err) + } + f.decryption = c.Decryption + f.fileUnique = fileUnique + f.aadPrefix = aadPrefix + } else { + // Decode the footer metadata using a reader that tracks bytes consumed, + // so that we can detect a trailing 28-byte AES-GCM signature without + // tripping over the "unexpected trailing bytes" check in thrift.Unmarshal. + pr := f.protocol.NewReaderFromBytes(bytes.Clone(footerData)) + if err := thrift.NewDecoder(pr).Decode(&f.metadata); err != nil { + return nil, fmt.Errorf("reading parquet file metadata: %w", err) + } + trailing := len(footerData) - pr.BytesRead() + const sigLen = encNonceSize + encTagSize + switch { + case trailing == 0: + // Plain, unsigned footer — nothing to do. + case trailing == sigLen: + // Plaintext footer with AES-GCM signature appended. + if c.Decryption == nil { + return nil, fmt.Errorf("parquet file has a signed footer but no DecryptionConfig was provided") + } + algo := &f.metadata.EncryptionAlgorithm + var fileUnique, aadPrefix []byte + switch { + case algo.AesGcmV1 != nil: + fileUnique = algo.AesGcmV1.AadFileUnique + aadPrefix = algo.AesGcmV1.AadPrefix + case algo.AesGcmCtrV1 != nil: + fileUnique = algo.AesGcmCtrV1.AadFileUnique + aadPrefix = algo.AesGcmCtrV1.AadPrefix + } + plainFooterBytes := footerData[:pr.BytesRead()] + sig := footerData[pr.BytesRead():] + + footerKey, err := c.Decryption.Keys.FooterKey(f.metadata.FooterSigningKeyMetadata) + if err != nil { + return nil, fmt.Errorf("retrieving footer signing key: %w", err) + } + footerAAD := makeAAD(aadPrefix, fileUnique, footerModule) + if err := verifyFooterSignature(footerKey, footerAAD, plainFooterBytes, sig); err != nil { + return nil, err + } + f.decryption = c.Decryption + f.fileUnique = fileUnique + f.aadPrefix = aadPrefix + default: + return nil, fmt.Errorf("reading parquet file metadata: unexpected trailing bytes at the end of thrift input: %d", trailing) + } + } + + if len(f.metadata.Schema) == 0 { + return nil, ErrMissingRootColumn + } + + // In plaintext-footer mode with encryption, column metadata is stored as + // EncryptedColumnMetadata. Decrypt it into MetaData before reading the page + // index — column index/offset index offsets live in MetaData and would + // otherwise be zero, silently losing the page index for every encrypted + // plaintext-footer file. + if f.decryption != nil { + if err := f.decryptAllColumnMetadata(); err != nil { + return nil, err + } + } + + if !c.SkipPageIndex { + if f.columnIndexes, f.offsetIndexes, err = f.ReadPageIndex(); err != nil { + return nil, fmt.Errorf("reading page index of parquet file: %w", err) + } + } + + if f.root, err = openColumns(f, &f.metadata, f.columnIndexes, f.offsetIndexes); err != nil { + return nil, fmt.Errorf("opening columns of parquet file: %w", err) + } + + if c.Schema != nil { + f.schema = c.Schema + } else { + f.schema = NewSchema(f.root.Name(), f.root) + } + columns := makeLeafColumns(f.root) + rowGroups, err := makeFileRowGroups(f, columns) + if err != nil { + return nil, err + } + f.rowGroups = makeRowGroups(rowGroups) + + if !c.SkipBloomFilters { + section := io.NewSectionReader(f.reader, 0, size) + rbuf, rbufpool := getBufioReader(section, c.ReadBufferSize) + defer putBufioReader(rbuf, rbufpool) + + header := format.BloomFilterHeader{} + compact := thrift.CompactProtocol{} + decoder := thrift.NewDecoder(compact.NewReader(rbuf)) + + for i := range rowGroups { + g := &rowGroups[i] + + for j := range g.columns { + cc := g.columns[j].(*FileColumnChunk) + + if offset := cc.chunk.MetaData.BloomFilterOffset; offset > 0 { + // Encrypted columns cannot be decoded here because the bloom + // filter bytes on disk are AES-GCM envelopes, not plain + // thrift. Defer loading to the lazy readBloomFilter path. + if cc.decryptionKey != nil { + continue + } + + section.Seek(offset, io.SeekStart) + rbuf.Reset(section) + + header = format.BloomFilterHeader{} + if err := decoder.Decode(&header); err != nil { + return nil, fmt.Errorf("decoding bloom filter header: %w", err) + } + + offset, _ = section.Seek(0, io.SeekCurrent) + offset -= int64(rbuf.Buffered()) + + if cast, ok := r.(interface{ SetBloomFilterSection(offset, length int64) }); ok { + bloomFilterOffset := cc.chunk.MetaData.BloomFilterOffset + bloomFilterLength := (offset - bloomFilterOffset) + int64(header.NumBytes) + cast.SetBloomFilterSection(bloomFilterOffset, bloomFilterLength) + } + + if c.PrefetchBloomFilters { + bloomData := make([]byte, header.NumBytes) + if _, err := io.ReadFull(rbuf, bloomData); err != nil { + return nil, fmt.Errorf("reading bloom filter data: %w", err) + } + // Create bloom filter using in-memory data. + bf, err := newBloomFilter(bytes.NewReader(bloomData), 0, &header) + if err != nil { + return nil, fmt.Errorf("reading bloom filter: %w", err) + } + cc.bloomFilter.Store(bf) + } else { + bf, err := newBloomFilter(f, offset, &header) + if err != nil { + return nil, fmt.Errorf("reading bloom filter: %w", err) + } + cc.bloomFilter.Store(bf) + } + } + } + } + } + + sortKeyValueMetadata(f.metadata.KeyValueMetadata) + f.reader = r // restore in case an optimistic reader was used + return f, nil +} + +// ReadPageIndex reads the page index section of the parquet file f. +// +// If the file did not contain a page index, the method returns two empty slices +// and a nil error. +// +// Only leaf columns have indexes, the returned indexes are arranged using the +// following layout: +// +// ------------------ +// | col 0: chunk 0 | +// ------------------ +// | col 1: chunk 0 | +// ------------------ +// | ... | +// ------------------ +// | col 0: chunk 1 | +// ------------------ +// | col 1: chunk 1 | +// ------------------ +// | ... | +// ------------------ +// +// This method is useful in combination with the SkipPageIndex option to delay +// reading the page index section until after the file was opened. Note that in +// this case the page index is not cached within the file, programs are expected +// to make use of independently from the parquet package. +func (f *File) ReadPageIndex() ([]format.ColumnIndex, []format.OffsetIndex, error) { + if len(f.metadata.RowGroups) == 0 { + return nil, nil, nil + } + + columnIndexOffset := f.metadata.RowGroups[0].Columns[0].ColumnIndexOffset + offsetIndexOffset := f.metadata.RowGroups[0].Columns[0].OffsetIndexOffset + columnIndexLength := int64(0) + offsetIndexLength := int64(0) + + forEachColumnChunk := func(do func(int, int, *format.ColumnChunk) error) error { + for i := range f.metadata.RowGroups { + for j := range f.metadata.RowGroups[i].Columns { + c := &f.metadata.RowGroups[i].Columns[j] + if err := do(i, j, c); err != nil { + return err + } + } + } + return nil + } + + forEachColumnChunk(func(_, _ int, c *format.ColumnChunk) error { + columnIndexLength += int64(c.ColumnIndexLength) + offsetIndexLength += int64(c.OffsetIndexLength) + return nil + }) + + if columnIndexLength == 0 && offsetIndexLength == 0 { + return nil, nil, nil + } + + numRowGroups := len(f.metadata.RowGroups) + numColumns := len(f.metadata.RowGroups[0].Columns) + numColumnChunks := numRowGroups * numColumns + + columnIndexes := make([]format.ColumnIndex, numColumnChunks) + offsetIndexes := make([]format.OffsetIndex, numColumnChunks) + indexBuffer := make([]byte, max(int(columnIndexLength), int(offsetIndexLength))) + + // pageIndexKey returns the AES key used to decrypt this column's + // page-index modules, or nil for plaintext columns. We resolve keys here + // (instead of relying on FileRowGroup.init) because ReadPageIndex runs + // before makeFileRowGroups during OpenFile. + pageIndexKey := func(c *format.ColumnChunk) ([]byte, error) { + if f.decryption == nil { + return nil, nil + } + switch { + case c.CryptoMetadata.EncryptionWithFooterKey != nil: + return f.decryption.Keys.FooterKey(nil) + case c.CryptoMetadata.EncryptionWithColumnKey != nil: + colKey := c.CryptoMetadata.EncryptionWithColumnKey + return f.decryption.Keys.ColumnKey(colKey.PathInSchema, colKey.KeyMetadata) + default: + return nil, nil + } + } + + if columnIndexOffset > 0 { + columnIndexData := indexBuffer[:columnIndexLength] + + if cast, ok := f.reader.(interface{ SetColumnIndexSection(offset, length int64) }); ok { + cast.SetColumnIndexSection(columnIndexOffset, columnIndexLength) + } + if _, err := f.readAt(columnIndexData, columnIndexOffset); err != nil { + return nil, nil, fmt.Errorf("reading %d bytes column index at offset %d: %w", columnIndexLength, columnIndexOffset, err) + } + + err := forEachColumnChunk(func(i, j int, c *format.ColumnChunk) error { + // Some parquet files are missing the column index on some columns. + // + // An example of this file is testdata/alltypes_tiny_pages_plain.parquet + // which was added in https://github.com/apache/parquet-testing/pull/24. + if c.ColumnIndexOffset > 0 { + offset := c.ColumnIndexOffset - columnIndexOffset + length := int64(c.ColumnIndexLength) + buffer := columnIndexData[offset : offset+length] + key, err := pageIndexKey(c) + if err != nil { + if errors.Is(err, ErrKeyNotFound) { + return nil + } + return fmt.Errorf("resolving column index key: rowGroup=%d col=%d: %w", i, j, err) + } + if key != nil { + aad := makeAAD(f.aadPrefix, f.fileUnique, columnIndexModule, int16(i), int16(j)) + plain, err := decryptModule(key, aad, buffer) + if err != nil { + return fmt.Errorf("decrypting column index: rowGroup=%d col=%d: %w", i, j, err) + } + buffer = plain + } + if err := thrift.Unmarshal(&f.protocol, buffer, &columnIndexes[(i*numColumns)+j]); err != nil { + return fmt.Errorf("decoding column index: rowGroup=%d columnChunk=%d/%d: %w", i, j, numColumns, err) + } + } + return nil + }) + if err != nil { + return nil, nil, err + } + } + + if offsetIndexOffset > 0 { + offsetIndexData := indexBuffer[:offsetIndexLength] + + if cast, ok := f.reader.(interface{ SetOffsetIndexSection(offset, length int64) }); ok { + cast.SetOffsetIndexSection(offsetIndexOffset, offsetIndexLength) + } + if _, err := f.readAt(offsetIndexData, offsetIndexOffset); err != nil { + return nil, nil, fmt.Errorf("reading %d bytes offset index at offset %d: %w", offsetIndexLength, offsetIndexOffset, err) + } + + err := forEachColumnChunk(func(i, j int, c *format.ColumnChunk) error { + if c.OffsetIndexOffset > 0 { + offset := c.OffsetIndexOffset - offsetIndexOffset + length := int64(c.OffsetIndexLength) + buffer := offsetIndexData[offset : offset+length] + key, err := pageIndexKey(c) + if err != nil { + if errors.Is(err, ErrKeyNotFound) { + return nil + } + return fmt.Errorf("resolving offset index key: rowGroup=%d col=%d: %w", i, j, err) + } + if key != nil { + aad := makeAAD(f.aadPrefix, f.fileUnique, offsetIndexModule, int16(i), int16(j)) + plain, err := decryptModule(key, aad, buffer) + if err != nil { + return fmt.Errorf("decrypting offset index: rowGroup=%d col=%d: %w", i, j, err) + } + buffer = plain + } + if err := thrift.Unmarshal(&f.protocol, buffer, &offsetIndexes[(i*numColumns)+j]); err != nil { + return fmt.Errorf("decoding offset index: rowGroup=%d columnChunk=%d/%d: %w", i, j, numColumns, err) + } + } + return nil + }) + if err != nil { + return nil, nil, err + } + } + + return columnIndexes, offsetIndexes, nil +} + +// decryptAllColumnMetadata decrypts EncryptedColumnMetadata for every column +// chunk in every row group, restoring ColumnChunk.MetaData so that +// openColumns and schema construction see the full encoding/compression info. +// This must be called before openColumns. +func (f *File) decryptAllColumnMetadata() error { + // Normalize ordinals before using them in AADs. Files from writers that + // omit the optional Ordinal field have all zeros; validateRowGroupOrdinals + // back-fills sequential values so rg.Ordinal is guaranteed to equal the + // slice index — matching exactly what the writer embedded in every AAD. + if err := validateRowGroupOrdinals(f.metadata.RowGroups); err != nil { + return err + } + + for rgIdx := range f.metadata.RowGroups { + rg := &f.metadata.RowGroups[rgIdx] + for colIdx := range rg.Columns { + chunk := &rg.Columns[colIdx] + if len(chunk.EncryptedColumnMetadata) == 0 { + continue + } + var key []byte + switch { + case chunk.CryptoMetadata.EncryptionWithFooterKey != nil: + var err error + key, err = f.decryption.Keys.FooterKey(nil) + if err != nil { + return fmt.Errorf("resolving footer key for column metadata: %w", err) + } + case chunk.CryptoMetadata.EncryptionWithColumnKey != nil: + colKey := chunk.CryptoMetadata.EncryptionWithColumnKey + var err error + key, err = f.decryption.Keys.ColumnKey(colKey.PathInSchema, colKey.KeyMetadata) + if err != nil { + // Only treat an explicit ErrKeyNotFound as non-fatal: the + // caller intentionally omitted this column's key. Any other + // error (KMS failure, bad metadata, …) is propagated so the + // caller sees the real problem instead of silent zero data. + if errors.Is(err, ErrKeyNotFound) { + continue + } + return fmt.Errorf("resolving column key for column metadata: %w", err) + } + default: + continue + } + // rg.Ordinal is reliable here because validateRowGroupOrdinals ran above. + aad := makeAAD(f.aadPrefix, f.fileUnique, columnMetaDataModule, rg.Ordinal, int16(colIdx)) + plain, err := decryptModule(key, aad, chunk.EncryptedColumnMetadata) + if err != nil { + return fmt.Errorf("decrypting column metadata: rowGroup=%d col=%d: %w", rg.Ordinal, colIdx, err) + } + compact := thrift.CompactProtocol{} + if err := thrift.Unmarshal(&compact, plain, &chunk.MetaData); err != nil { + return fmt.Errorf("decoding column metadata: rowGroup=%d col=%d: %w", rg.Ordinal, colIdx, err) + } + } + } + return nil +} + +// NumRows returns the number of rows in the file. +func (f *File) NumRows() int64 { return f.metadata.NumRows } + +// RowGroups returns the list of row groups in the file. +// +// Elements of the returned slice are guaranteed to be of type *FileRowGroup. +func (f *File) RowGroups() []RowGroup { return f.rowGroups } + +// Root returns the root column of f. +func (f *File) Root() *Column { return f.root } + +// Schema returns the schema of f. +func (f *File) Schema() *Schema { return f.schema } + +// Metadata returns the metadata of f. +func (f *File) Metadata() *format.FileMetaData { return &f.metadata } + +// Size returns the size of f (in bytes). +func (f *File) Size() int64 { return f.size } + +// ReadAt reads bytes into b from f at the given offset. +// +// The method satisfies the io.ReaderAt interface. +func (f *File) ReadAt(b []byte, off int64) (int, error) { + if off < 0 || off >= f.size { + return 0, io.EOF + } + + if limit := f.size - off; limit < int64(len(b)) { + n, err := f.readAt(b[:limit], off) + if err == nil { + err = io.EOF + } + return n, err + } + + return f.readAt(b, off) +} + +// ColumnIndexes returns the page index of the parquet file f. +// +// If the file did not contain a column index, the method returns an empty slice. +func (f *File) ColumnIndexes() []format.ColumnIndex { return f.columnIndexes } + +// OffsetIndexes returns the page index of the parquet file f. +// +// If the file did not contain an offset index, the method returns an empty +// slice. +func (f *File) OffsetIndexes() []format.OffsetIndex { return f.offsetIndexes } + +// Lookup returns the value associated with the given key in the file key/value +// metadata. +// +// The ok boolean will be true if the key was found, false otherwise. +func (f *File) Lookup(key string) (value string, ok bool) { + return lookupKeyValueMetadata(f.metadata.KeyValueMetadata, key) +} + +func (f *File) hasIndexes() bool { + return f.columnIndexes != nil && f.offsetIndexes != nil +} + +var _ io.ReaderAt = (*File)(nil) + +func sortKeyValueMetadata(keyValueMetadata []format.KeyValue) { + slices.SortFunc(keyValueMetadata, func(a, b format.KeyValue) int { + if cmp := strings.Compare(a.Key, b.Key); cmp != 0 { + return cmp + } + return strings.Compare(a.Value, b.Value) + }) +} + +func lookupKeyValueMetadata(keyValueMetadata []format.KeyValue, key string) (value string, ok bool) { + i, found := slices.BinarySearchFunc(keyValueMetadata, key, func(kv format.KeyValue, key string) int { + return strings.Compare(kv.Key, key) + }) + if found { + return keyValueMetadata[i].Value, true + } + return "", false +} + +// FileRowGroup is an implementation of the RowGroup interface on parquet files +// returned by OpenFile. +type FileRowGroup struct { + file *File + rowGroup *format.RowGroup + columns []ColumnChunk + sorting []SortingColumn +} + +func (g *FileRowGroup) init(file *File, columns []*Column, rowGroup *format.RowGroup) error { + g.file = file + g.rowGroup = rowGroup + g.columns = make([]ColumnChunk, len(rowGroup.Columns)) + g.sorting = make([]SortingColumn, len(rowGroup.SortingColumns)) + fileColumnChunks := make([]FileColumnChunk, len(rowGroup.Columns)) + fileColumnIndexes := make([]FileColumnIndex, len(rowGroup.Columns)) + fileOffsetIndexes := make([]FileOffsetIndex, len(rowGroup.Columns)) + + for i := range g.columns { + fileColumnChunks[i] = FileColumnChunk{ + file: file, + column: columns[i], + rowGroup: rowGroup, + chunk: &rowGroup.Columns[i], + columnOrdinal: int16(i), + rowGroupOrdinal: rowGroup.Ordinal, + } + if file.decryption != nil { + chunk := &rowGroup.Columns[i] + switch { + case chunk.CryptoMetadata.EncryptionWithFooterKey != nil: + key, err := file.decryption.Keys.FooterKey(nil) + if err != nil { + return fmt.Errorf("resolving footer key for column %q: %w", columnPathString(columns[i].Path()), err) + } + fileColumnChunks[i].decryptionKey = key + case chunk.CryptoMetadata.EncryptionWithColumnKey != nil: + colKey := chunk.CryptoMetadata.EncryptionWithColumnKey + key, err := file.decryption.Keys.ColumnKey(colKey.PathInSchema, colKey.KeyMetadata) + switch { + case err == nil: + fileColumnChunks[i].decryptionKey = key + case errors.Is(err, ErrKeyNotFound): + // Caller intentionally omitted this column's key. Leave + // decryptionKey nil so the chunk fails only if the data is + // actually accessed. + default: + // Real error (KMS failure, bad metadata, …) must surface so + // the caller learns the cause instead of seeing later + // "decode page header" errors masking the missing key. + return fmt.Errorf("resolving column key for %q: %w", columnPathString(colKey.PathInSchema), err) + } + } + } + + if file.hasIndexes() { + j := (int(rowGroup.Ordinal) * len(columns)) + i + + fileColumnIndexes[i] = FileColumnIndex{index: &file.columnIndexes[j], kind: columns[i].Type().Kind()} + fileOffsetIndexes[i] = FileOffsetIndex{index: &file.offsetIndexes[j]} + + fileColumnChunks[i].columnIndex.Store(&fileColumnIndexes[i]) + fileColumnChunks[i].offsetIndex.Store(&fileOffsetIndexes[i]) + } + + g.columns[i] = &fileColumnChunks[i] + } + + for i := range g.sorting { + g.sorting[i] = &fileSortingColumn{ + column: columns[rowGroup.SortingColumns[i].ColumnIdx], + descending: rowGroup.SortingColumns[i].Descending, + nullsFirst: rowGroup.SortingColumns[i].NullsFirst, + } + } + return nil +} + +// File returns the file that this row group belongs to. +func (g *FileRowGroup) File() *File { return g.file } + +// Schema returns the schema of the row group. +func (g *FileRowGroup) Schema() *Schema { return g.file.schema } + +// NumRows returns the number of rows in the row group. +func (g *FileRowGroup) NumRows() int64 { return g.rowGroup.NumRows } + +// ColumnChunks returns the list of column chunks in the row group. +// +// Elements of the returned slice are guaranteed to be of type *FileColumnChunk. +func (g *FileRowGroup) ColumnChunks() []ColumnChunk { return g.columns } + +// SortingColumns returns the list of sorting columns in the row group. +func (g *FileRowGroup) SortingColumns() []SortingColumn { return g.sorting } + +// Rows returns a row reader for the row group. +func (g *FileRowGroup) Rows() Rows { + rowGroup := RowGroup(g) + if g.file.config.ReadMode == ReadModeAsync { + rowGroup = AsyncRowGroup(rowGroup) + } + return NewRowGroupRowReader(rowGroup) +} + +type fileSortingColumn struct { + column *Column + descending bool + nullsFirst bool +} + +func (s *fileSortingColumn) Path() []string { return s.column.Path() } +func (s *fileSortingColumn) Descending() bool { return s.descending } +func (s *fileSortingColumn) NullsFirst() bool { return s.nullsFirst } +func (s *fileSortingColumn) String() string { + b := new(strings.Builder) + if s.nullsFirst { + b.WriteString("nulls_first+") + } + if s.descending { + b.WriteString("descending(") + } else { + b.WriteString("ascending(") + } + b.WriteString(columnPath(s.Path()).String()) + b.WriteString(")") + return b.String() +} + +// FileColumnChunk is an implementation of the ColumnChunk interface on parquet +// files returned by OpenFile. +type FileColumnChunk struct { + file *File + column *Column + rowGroup *format.RowGroup + chunk *format.ColumnChunk + columnIndex atomic.Pointer[FileColumnIndex] + offsetIndex atomic.Pointer[FileOffsetIndex] + bloomFilter atomic.Pointer[FileBloomFilter] + + // Decryption state; decryptionKey is nil when the column is not encrypted. + decryptionKey []byte + columnOrdinal int16 + rowGroupOrdinal int16 +} + +// File returns the file that this column chunk belongs to. +func (c *FileColumnChunk) File() *File { return c.file } + +// Node returns the node that this column chunk belongs to in the parquet schema. +func (c *FileColumnChunk) Node() Node { return c.column } + +// Type returns the type of the column chunk. +func (c *FileColumnChunk) Type() Type { return c.column.Type() } + +// Column returns the column index of this chunk in its parent row group. +func (c *FileColumnChunk) Column() int { return int(c.column.Index()) } + +// Bounds returns the min and max values found in the column chunk. +func (c *FileColumnChunk) Bounds() (min, max Value, ok bool) { + stats := &c.chunk.MetaData.Statistics + columnKind := c.Type().Kind() + hasMinValue := stats.MinValue != nil + hasMaxValue := stats.MaxValue != nil + if hasMinValue { + min = columnKind.Value(stats.MinValue) + } + if hasMaxValue { + max = columnKind.Value(stats.MaxValue) + } + return min, max, hasMinValue && hasMaxValue +} + +// Pages returns a page reader for the column chunk. +func (c *FileColumnChunk) Pages() Pages { + pages := Pages(c.PagesFrom(c.file.reader)) + if c.file.config.ReadMode == ReadModeAsync { + pages = AsyncPages(pages) + } + return pages +} + +// PagesFrom returns a page reader for the column chunk, using the reader passed +// as argument instead of the one that the file was originally opened from. +// +// Note that unlike when calling Pages, the returned reader is not wrapped in an +// AsyncPages reader if the file was opened in async mode. +func (c *FileColumnChunk) PagesFrom(reader io.ReaderAt) *FilePages { + pages := new(FilePages) + pages.init(c, reader) + return pages +} + +// ColumnIndex returns the column index of the column chunk, or an error if it +// didn't exist or couldn't be read. +func (c *FileColumnChunk) ColumnIndex() (ColumnIndex, error) { + index, err := c.ColumnIndexFrom(c.file.reader) + if err != nil { + return nil, err + } + return index, nil +} + +// ColumnIndexFrom is like ColumnIndex but uses the reader passed as argument to +// read the column index. +func (c *FileColumnChunk) ColumnIndexFrom(reader io.ReaderAt) (*FileColumnIndex, error) { + index, err := c.readColumnIndexFrom(reader) + if err != nil { + return nil, err + } + if index == nil || c.chunk.ColumnIndexOffset == 0 { + return nil, ErrMissingColumnIndex + } + return index, nil +} + +// OffsetIndex returns the offset index of the column chunk, or an error if it +// didn't exist or couldn't be read. +func (c *FileColumnChunk) OffsetIndex() (OffsetIndex, error) { + index, err := c.OffsetIndexFrom(c.file.reader) + if err != nil { + return nil, err + } + return index, nil +} + +// OffsetIndexFrom is like OffsetIndex but uses the reader passed as argument to +// read the offset index. +func (c *FileColumnChunk) OffsetIndexFrom(reader io.ReaderAt) (*FileOffsetIndex, error) { + index, err := c.readOffsetIndex(reader) + if err != nil { + return nil, err + } + if index == nil || c.chunk.OffsetIndexOffset == 0 { + return nil, ErrMissingOffsetIndex + } + return index, nil +} + +// BloomFilter returns the bloom filter of the column chunk, or nil if it didn't +// have one. +func (c *FileColumnChunk) BloomFilter() BloomFilter { + filter, err := c.BloomFilterFrom(c.file.reader) + switch err { + case nil: + return filter + case ErrMissingBloomFilter: + return nil + default: + return &errorBloomFilter{err: err} + } +} + +// BloomFilterFrom is like BloomFilter but uses the reader passed as argument to +// read the bloom filter. +func (c *FileColumnChunk) BloomFilterFrom(reader io.ReaderAt) (*FileBloomFilter, error) { + filter, err := c.readBloomFilter(reader) + if err != nil { + return nil, err + } + if filter == nil || c.chunk.MetaData.BloomFilterOffset == 0 { + return nil, ErrMissingBloomFilter + } + return filter, nil +} + +// NumValues returns the number of values in the column chunk. +func (c *FileColumnChunk) NumValues() int64 { + return c.chunk.MetaData.NumValues +} + +// NullCount returns the number of null values in the column chunk. +// +// This value is extracted from the column chunk statistics, parquet writers are +// not required to populate it. +func (c *FileColumnChunk) NullCount() int64 { + return c.chunk.MetaData.Statistics.NullCount +} + +func (c *FileColumnChunk) readColumnIndex() (*FileColumnIndex, error) { + return c.readColumnIndexFrom(c.file.reader) +} + +func (c *FileColumnChunk) readColumnIndexFrom(reader io.ReaderAt) (*FileColumnIndex, error) { + if index := c.columnIndex.Load(); index != nil { + return index, nil + } + columnChunk := &c.file.metadata.RowGroups[c.rowGroup.Ordinal].Columns[c.Column()] + offset, length := columnChunk.ColumnIndexOffset, columnChunk.ColumnIndexLength + if offset == 0 { + return nil, nil + } + + indexData := make([]byte, int(length)) + var columnIndex format.ColumnIndex + if _, err := readAt(reader, indexData, offset); err != nil { + return nil, fmt.Errorf("read %d bytes column index at offset %d: %w", length, offset, err) + } + plain := indexData + if c.decryptionKey != nil { + aad := makeAAD(c.file.aadPrefix, c.file.fileUnique, columnIndexModule, c.rowGroupOrdinal, c.columnOrdinal) + decrypted, err := decryptModule(c.decryptionKey, aad, indexData) + if err != nil { + return nil, fmt.Errorf("decrypting column index: rowGroup=%d col=%d: %w", c.rowGroupOrdinal, c.columnOrdinal, err) + } + plain = decrypted + } + if err := thrift.Unmarshal(&c.file.protocol, plain, &columnIndex); err != nil { + return nil, fmt.Errorf("decode column index: rowGroup=%d columnChunk=%d/%d: %w", c.rowGroup.Ordinal, c.Column(), len(c.rowGroup.Columns), err) + } + index := &FileColumnIndex{index: &columnIndex, kind: c.column.Type().Kind()} + // We do a CAS (and Load on CAS failure) instead of a simple Store for + // the nice property that concurrent calling goroutines will only ever + // observe a single pointer value for the result. + if !c.columnIndex.CompareAndSwap(nil, index) { + // another goroutine populated it since we last read the pointer + return c.columnIndex.Load(), nil + } + return index, nil +} + +func (c *FileColumnChunk) readOffsetIndex(reader io.ReaderAt) (*FileOffsetIndex, error) { + if index := c.offsetIndex.Load(); index != nil { + return index, nil + } + columnChunk := &c.file.metadata.RowGroups[c.rowGroup.Ordinal].Columns[c.Column()] + offset, length := columnChunk.OffsetIndexOffset, columnChunk.OffsetIndexLength + if offset == 0 { + return nil, nil + } + + indexData := make([]byte, int(length)) + var offsetIndex format.OffsetIndex + if _, err := readAt(reader, indexData, offset); err != nil { + return nil, fmt.Errorf("read %d bytes offset index at offset %d: %w", length, offset, err) + } + plain := indexData + if c.decryptionKey != nil { + aad := makeAAD(c.file.aadPrefix, c.file.fileUnique, offsetIndexModule, c.rowGroupOrdinal, c.columnOrdinal) + decrypted, err := decryptModule(c.decryptionKey, aad, indexData) + if err != nil { + return nil, fmt.Errorf("decrypting offset index: rowGroup=%d col=%d: %w", c.rowGroupOrdinal, c.columnOrdinal, err) + } + plain = decrypted + } + if err := thrift.Unmarshal(&c.file.protocol, plain, &offsetIndex); err != nil { + return nil, fmt.Errorf("decode offset index: rowGroup=%d columnChunk=%d/%d: %w", c.rowGroup.Ordinal, c.Column(), len(c.rowGroup.Columns), err) + } + index := &FileOffsetIndex{index: &offsetIndex} + if !c.offsetIndex.CompareAndSwap(nil, index) { + // another goroutine populated it since we last read the pointer + return c.offsetIndex.Load(), nil + } + return index, nil +} + +func (c *FileColumnChunk) readBloomFilter(reader io.ReaderAt) (*FileBloomFilter, error) { + if filter := c.bloomFilter.Load(); filter != nil { + return filter, nil + } + columnChunkMetaData := &c.file.metadata.RowGroups[c.rowGroup.Ordinal].Columns[c.Column()].MetaData + offset := columnChunkMetaData.BloomFilterOffset + length := c.file.size - offset + if offset == 0 { + return nil, nil + } + + section := io.NewSectionReader(reader, offset, length) + rbuf, rbufpool := getBufioReader(section, 1024) + defer putBufioReader(rbuf, rbufpool) + + var header format.BloomFilterHeader + var filter *FileBloomFilter + + if c.decryptionKey != nil { + hdrAAD := makeAAD(c.file.aadPrefix, c.file.fileUnique, bloomFilterHdrModule, c.rowGroupOrdinal, c.columnOrdinal) + hdrPlain, err := readDecryptedEnvelopeFrom(rbuf, c.decryptionKey, hdrAAD) + if err != nil { + return nil, fmt.Errorf("decrypting bloom filter header: %w", err) + } + compact := thrift.CompactProtocol{} + if err := thrift.NewDecoder(compact.NewReaderFromBytes(hdrPlain)).Decode(&header); err != nil { + return nil, fmt.Errorf("decoding encrypted bloom filter header: %w", err) + } + bitsAAD := makeAAD(c.file.aadPrefix, c.file.fileUnique, bloomFilterBitsModule, c.rowGroupOrdinal, c.columnOrdinal) + bitsPlain, err := readDecryptedEnvelopeFrom(rbuf, c.decryptionKey, bitsAAD) + if err != nil { + return nil, fmt.Errorf("decrypting bloom filter bitset: %w", err) + } + filter = newBloomFilterFromBytes(&header, bitsPlain) + } else { + compact := thrift.CompactProtocol{} + decoder := thrift.NewDecoder(compact.NewReader(rbuf)) + if err := decoder.Decode(&header); err != nil { + return nil, fmt.Errorf("decoding bloom filter header: %w", err) + } + offset, _ = section.Seek(0, io.SeekCurrent) + var err error + filter, err = newBloomFilter(reader, offset, &header) + if err != nil { + return nil, fmt.Errorf("reading bloom filter: %w", err) + } + } + + if !c.bloomFilter.CompareAndSwap(nil, filter) { + return c.bloomFilter.Load(), nil + } + return filter, nil +} + +type FilePages struct { + chunk *FileColumnChunk + rbuf *bufio.Reader + rbufpool *memory.Pool[bufio.Reader] + section io.SectionReader + + protocol thrift.CompactProtocol + decoder thrift.Decoder + + baseOffset int64 + dataOffset int64 + dictOffset int64 + index int + skip int64 + dictionary Dictionary + + // track the last page to prevent re-reading the same page + lastPageIndex int + lastPage Page + serveLastPage bool + + bufferSize int + + // dec is non-nil only when the column is encrypted. Keeping it behind a + // pointer avoids bloating FilePages for the common (non-encrypted) case. + dec *filePagesDecryptionState +} + +// filePagesDecryptionState holds per-column decryption context for FilePages. +type filePagesDecryptionState struct { + key []byte + fileUnique []byte + aadPrefix []byte + rowGroupOrdinal int16 + columnOrdinal int16 + dataPageOrd int16 + dictPagePending bool // true when next page in the stream is the encrypted dict page +} + +func (f *FilePages) init(c *FileColumnChunk, reader io.ReaderAt) { + f.chunk = c + f.baseOffset = c.chunk.MetaData.DataPageOffset + f.dataOffset = f.baseOffset + f.bufferSize = c.file.config.ReadBufferSize + + if c.chunk.MetaData.DictionaryPageOffset != 0 { + f.baseOffset = c.chunk.MetaData.DictionaryPageOffset + f.dictOffset = f.baseOffset + } + + f.section = *io.NewSectionReader(reader, f.baseOffset, c.chunk.MetaData.TotalCompressedSize) + f.rbuf, f.rbufpool = getBufioReader(&f.section, f.bufferSize) + f.decoder.Reset(f.protocol.NewReader(f.rbuf)) + f.index = 0 + Release(f.lastPage) + f.lastPage = nil + f.lastPageIndex = -1 + f.serveLastPage = false + + // Populate decryption state from the column chunk (only when encrypted). + if c.decryptionKey != nil { + f.dec = &filePagesDecryptionState{ + key: c.decryptionKey, + fileUnique: c.file.fileUnique, + aadPrefix: c.file.aadPrefix, + rowGroupOrdinal: c.rowGroupOrdinal, + columnOrdinal: c.columnOrdinal, + dataPageOrd: 0, + dictPagePending: f.dictOffset > 0, + } + } else { + f.dec = nil + } +} + +// ReadDictionary returns the dictionary of the column chunk, or nil if the +// column chunk did not have one. +// +// The program is not required to call this method before calling ReadPage, +// the dictionary is read automatically when needed. It is exposed to allow +// programs to access the dictionary without reading the first page. +func (f *FilePages) ReadDictionary() (Dictionary, error) { + if f.dictionary == nil && f.dictOffset > 0 { + if err := f.readDictionary(); err != nil { + return nil, err + } + } + return f.dictionary, nil +} + +// ReadPages reads the next from from f. +func (f *FilePages) ReadPage() (Page, error) { + if f.chunk == nil { + return nil, io.EOF + } + + // seekToRowStart indicates whether we are in the process of seeking to the start + // of requested row to read, as opposed to reading sequentially values and moving through pages + seekToRowStart := f.skip > 0 + + // serve the last page if SeekToRow targeted the same page as last returned + if f.serveLastPage && f.lastPage != nil { + f.serveLastPage = false + f.index = f.lastPageIndex + 1 + + numRows := f.lastPage.NumRows() + if f.skip < numRows { + tail := f.lastPage.Slice(f.skip, numRows) + f.skip = 0 + return tail, nil + } + + f.skip -= numRows // fall through to reading the next page + } + + for { + var ( + header *format.PageHeader + data *buffer[byte] + err error + ) + + if f.dec != nil { + header, data, err = f.readEncryptedPage() + if err != nil { + return nil, err + } + // If we've already loaded the dictionary, skip a duplicate dict page. + if header.Type == format.DictionaryPage && f.dictionary != nil { + data.unref() + continue + } + } else { + // Instantiate a new format.PageHeader for each page. + // + // A previous implementation reused page headers to save allocations. + // https://github.com/segmentio/parquet-go/pull/484 + // The optimization turned out to be less effective than expected, + // because all the values referenced by pointers in the page header + // are lost when the header is reset and put back in the pool. + // https://github.com/parquet-go/parquet-go/pull/11 + // + // Even after being reset, reusing page headers still produced instability + // issues. + // https://github.com/parquet-go/parquet-go/issues/70 + header = new(format.PageHeader) + if err = f.decoder.Decode(header); err != nil { + return nil, err + } + + // if this is a dictionary page and we've already read and decoded the dictionary we can skip past it. + // call f.rbuf.Discard to skip the page data and realign f.rbuf with the next page header + if header.Type == format.DictionaryPage && f.dictionary != nil { + f.rbuf.Discard(int(header.CompressedPageSize)) + continue + } + + data, err = f.readPage(header, f.rbuf) + if err != nil { + return nil, err + } + } + + var page Page + switch header.Type { + case format.DataPageV2: + page, err = f.readDataPageV2(header, data) + case format.DataPage: + page, err = f.readDataPageV1(header, data) + case format.DictionaryPage: + // Sometimes parquet files do not have the dictionary page offset + // recorded in the column metadata. We account for this by lazily + // reading dictionary pages when we encounter them. + err = f.readDictionaryPage(header, data) + default: + err = fmt.Errorf("cannot read values of type %s from page", header.Type) + } + + data.unref() + + if err != nil { + return nil, fmt.Errorf("decoding page %d of column %q: %w", f.index, f.columnPath(), err) + } + + if page == nil { + continue + } + + if f.lastPage != nil { + Release(f.lastPage) // in case we cached a valid last page, release it now + } + + // track last page + f.lastPage = page + Retain(page) + f.lastPageIndex = f.index + + f.index++ + if f.skip == 0 { + // f.skip==0 can be true: + // (1) while reading a row of a column which has multiple values (ie. X.list.element) and values continue + // across pages. In that case we just want to keep reading without skipping any values. + // (2) when seeking to a specific row and trying to reach the start offset of the first + // row in a new page. + if !seekToRowStart || header.Type != format.DataPage { + // keep reading values from beginning of new page + return page, nil + } + // We need to seek to beginning of row. + // V1 data pages do not necessarily start at a row boundary. + if page.NumRows() == 0 { + // if current page does not have any rows, continue until a page with at least 1 row is reached + Release(page) + continue + } + repLvls := page.RepetitionLevels() + if len(repLvls) > 0 && repLvls[0] == 0 { + // avoid page slice if page starts at a row boundary + return page, nil + } + tail := page.Slice(0, page.NumRows()) + Release(page) + return tail, nil + } + + // TODO: what about pages that don't embed the number of rows? + // (data page v1 with no offset index in the column chunk). + numRows := page.NumRows() + + if numRows <= f.skip { + Release(page) + } else { + tail := page.Slice(f.skip, numRows) + Release(page) + f.skip = 0 + return tail, nil + } + + f.skip -= numRows + } +} + +func (f *FilePages) readDictionary() error { + chunk := io.NewSectionReader(f.section.Outer()) + rbuf, pool := getBufioReader(chunk, f.bufferSize) + defer putBufioReader(rbuf, pool) + + header := new(format.PageHeader) + var page *buffer[byte] + + if f.dec != nil { + d := f.dec + hdrAAD := makeAAD(d.aadPrefix, d.fileUnique, dictPageHeaderModule, d.rowGroupOrdinal, d.columnOrdinal, 0) + hdrPlain, err := readDecryptedEnvelopeFrom(rbuf, d.key, hdrAAD) + if err != nil { + return err + } + pr := f.protocol.NewReaderFromBytes(hdrPlain) + if err := thrift.NewDecoder(pr).Decode(header); err != nil { + return fmt.Errorf("parquet decryption: decoding dict page header: %w", err) + } + bodyAAD := makeAAD(d.aadPrefix, d.fileUnique, dictPageBodyModule, d.rowGroupOrdinal, d.columnOrdinal, 0) + bodyPlain, err := readDecryptedEnvelopeFrom(rbuf, d.key, bodyAAD) + if err != nil { + return err + } + page = buffers.get(len(bodyPlain)) + copy(page.data.Slice(), bodyPlain) + page.ref() + } else { + decoder := thrift.NewDecoder(f.protocol.NewReader(rbuf)) + if err := decoder.Decode(header); err != nil { + return err + } + page = buffers.get(int(header.CompressedPageSize)) + page.ref() + if _, err := io.ReadFull(rbuf, page.data.Slice()); err != nil { + page.unref() + return err + } + } + defer page.unref() + return f.readDictionaryPage(header, page) +} + +func (f *FilePages) readDictionaryPage(header *format.PageHeader, page *buffer[byte]) error { + if !header.DictionaryPageHeader.Valid { + return ErrMissingPageHeader + } + d, err := f.chunk.column.decodeDictionary(DictionaryPageHeader{&header.DictionaryPageHeader.V}, page, header.UncompressedPageSize) + if err != nil { + return err + } + f.dictionary = d + return nil +} + +func (f *FilePages) readDataPageV1(header *format.PageHeader, page *buffer[byte]) (Page, error) { + if !header.DataPageHeader.Valid { + return nil, ErrMissingPageHeader + } + if isDictionaryFormat(header.DataPageHeader.V.Encoding) && f.dictionary == nil { + if err := f.readDictionary(); err != nil { + return nil, err + } + } + return f.chunk.column.decodeDataPageV1(DataPageHeaderV1{&header.DataPageHeader.V}, page, f.dictionary, header.UncompressedPageSize) +} + +func (f *FilePages) readDataPageV2(header *format.PageHeader, page *buffer[byte]) (Page, error) { + if !header.DataPageHeaderV2.Valid { + return nil, ErrMissingPageHeader + } + if isDictionaryFormat(header.DataPageHeaderV2.V.Encoding) && f.dictionary == nil { + // If the program seeked to a row passed the first page, the dictionary + // page may not have been seen, in which case we have to lazily load it + // from the beginning of column chunk. + if err := f.readDictionary(); err != nil { + return nil, err + } + } + return f.chunk.column.decodeDataPageV2(DataPageHeaderV2{&header.DataPageHeaderV2.V}, page, f.dictionary, header.UncompressedPageSize) +} + +func (f *FilePages) readPage(header *format.PageHeader, reader *bufio.Reader) (*buffer[byte], error) { + page := buffers.get(int(header.CompressedPageSize)) + defer page.unref() + + if _, err := io.ReadFull(reader, page.data.Slice()); err != nil { + return nil, err + } + + if header.CRC != 0 { + headerChecksum := uint32(header.CRC) + bufferChecksum := crc32.ChecksumIEEE(page.data.Slice()) + + if headerChecksum != bufferChecksum { + // The parquet specs indicate that corruption errors could be + // handled gracefully by skipping pages, tho this may not always + // be practical. Depending on how the pages are consumed, + // missing rows may cause unpredictable behaviors in algorithms. + // + // For now, we assume these errors to be fatal, but we may + // revisit later and improve error handling to be more resilient + // to data corruption. + return nil, fmt.Errorf("crc32 checksum mismatch in page of column %q: want=0x%08X got=0x%08X: %w", + f.columnPath(), + headerChecksum, + bufferChecksum, + ErrCorrupted, + ) + } + } + + page.ref() + return page, nil +} + +// readDecryptedEnvelopeFrom reads a Parquet module envelope from r, decrypts it, and +// returns the plaintext. The caller supplies the key and pre-built AAD bytes. +func readDecryptedEnvelopeFrom(r io.Reader, key, aad []byte) ([]byte, error) { + var lenBuf [4]byte + if _, err := io.ReadFull(r, lenBuf[:]); err != nil { + return nil, err + } + moduleLen := int(binary.LittleEndian.Uint32(lenBuf[:])) + envelope := make([]byte, 4+moduleLen) + copy(envelope[:4], lenBuf[:]) + if _, err := io.ReadFull(r, envelope[4:]); err != nil { + return nil, err + } + return decryptModule(key, aad, envelope) +} + +// readEncryptedPage reads one page (header + body) from the encrypted stream. +// It handles both dictionary pages and data pages; the caller must not call it +// when the stream is exhausted. +func (f *FilePages) readEncryptedPage() (*format.PageHeader, *buffer[byte], error) { + d := f.dec + isDictPage := d.dictPagePending + + var hdrModuleType, bodyModuleType byte + var pageOrd int16 + if isDictPage { + hdrModuleType = dictPageHeaderModule + bodyModuleType = dictPageBodyModule + pageOrd = 0 + } else { + hdrModuleType = dataPageHeaderModule + bodyModuleType = dataPageBodyModule + pageOrd = d.dataPageOrd + } + + hdrAAD := makeAAD(d.aadPrefix, d.fileUnique, hdrModuleType, d.rowGroupOrdinal, d.columnOrdinal, pageOrd) + hdrPlain, err := readDecryptedEnvelopeFrom(f.rbuf, d.key, hdrAAD) + if err != nil { + return nil, nil, err + } + + header := new(format.PageHeader) + pr := f.protocol.NewReaderFromBytes(hdrPlain) + if err := thrift.NewDecoder(pr).Decode(header); err != nil { + return nil, nil, fmt.Errorf("parquet decryption: decoding page header: %w", err) + } + + bodyAAD := makeAAD(d.aadPrefix, d.fileUnique, bodyModuleType, d.rowGroupOrdinal, d.columnOrdinal, pageOrd) + bodyPlain, err := readDecryptedEnvelopeFrom(f.rbuf, d.key, bodyAAD) + if err != nil { + return nil, nil, err + } + + page := buffers.get(len(bodyPlain)) + copy(page.data.Slice(), bodyPlain) + page.ref() + + if isDictPage { + d.dictPagePending = false + } else { + d.dataPageOrd++ + } + return header, page, nil +} + +// SeekToRow seeks to the given row index in the column chunk. +func (f *FilePages) SeekToRow(rowIndex int64) error { + if f.chunk == nil { + return io.ErrClosedPipe + } + + if index := f.chunk.offsetIndex.Load(); index == nil { + _, err := f.section.Seek(f.dataOffset-f.baseOffset, io.SeekStart) + if err != nil { + return err + } + + f.skip = rowIndex + f.index = 0 + if f.dictOffset > 0 { + f.index = 1 + } + if f.dec != nil { + f.dec.dataPageOrd = 0 + f.dec.dictPagePending = false + } + } else { + pages := index.index.PageLocations + if len(pages) == 0 { + // A 0-row row group may still carry an OffsetIndex with no + // page locations. Seeking to row 0 is valid in this case; + // the subsequent ReadPage will return io.EOF. + if rowIndex == 0 { + f.skip = 0 + return nil + } + return ErrSeekOutOfRange + } + target := sort.Search(len(pages), func(i int) bool { + return pages[i].FirstRowIndex > rowIndex + }) - 1 + if target < 0 { + return ErrSeekOutOfRange + } + + f.skip = rowIndex - pages[target].FirstRowIndex + + // positioned at the last returned page: serve it + if f.lastPage != nil && target == f.lastPageIndex { + f.serveLastPage = true + return nil + } + + // already positioned at the target page + if f.index == target { + return nil + } + + f.index = target + // Sync decryption ordinal so the next readEncryptedPage uses the correct AAD. + if f.dec != nil { + f.dec.dataPageOrd = int16(target) + f.dec.dictPagePending = false + } + + // if the target page is within the unread portion of the current buffer, just skip/discard some bytes + var pos int64 + pos, err := f.section.Seek(0, io.SeekCurrent) // no-op seek to retrieve position + if err != nil { + return err + } + + unread := int64(f.rbuf.Buffered()) + currOffset := pos - unread // section relative offset + targetOffset := pages[target].Offset - f.baseOffset // section relative target offset + skipBytes := targetOffset - currOffset + if skipBytes == 0 { + return nil + } + if skipBytes > 0 && skipBytes <= unread { + _, err = f.rbuf.Discard(int(skipBytes)) + return err + } + + _, err = f.section.Seek(pages[target].Offset-f.baseOffset, io.SeekStart) + if err != nil { + return err + } + } + + f.rbuf.Reset(&f.section) + return nil +} + +// Close closes the page reader. +func (f *FilePages) Close() error { + putBufioReader(f.rbuf, f.rbufpool) + f.chunk = nil + f.section = io.SectionReader{} + f.rbuf = nil + f.rbufpool = nil + f.baseOffset = 0 + f.dataOffset = 0 + f.dictOffset = 0 + f.index = 0 + Release(f.lastPage) + f.lastPage = nil + f.lastPageIndex = -1 + f.serveLastPage = false + f.skip = 0 + f.dictionary = nil + return nil +} + +func (f *FilePages) columnPath() columnPath { + return columnPath(f.chunk.column.Path()) +} + +type putBufioReaderFunc func() + +var ( + bufioReaderPoolLock sync.Mutex + bufioReaderPool = map[int]*memory.Pool[bufio.Reader]{} +) + +func getBufioReader(r io.Reader, bufferSize int) (*bufio.Reader, *memory.Pool[bufio.Reader]) { + pool := getBufioReaderPool(bufferSize) + rbuf := pool.Get( + func() *bufio.Reader { return bufio.NewReaderSize(r, bufferSize) }, + func(rbuf *bufio.Reader) { rbuf.Reset(r) }, + ) + return rbuf, pool +} + +func putBufioReader(rbuf *bufio.Reader, pool *memory.Pool[bufio.Reader]) { + if pool != nil { + rbuf.Reset(nil) + pool.Put(rbuf) + } +} + +func getBufioReaderPool(size int) *memory.Pool[bufio.Reader] { + bufioReaderPoolLock.Lock() + defer bufioReaderPoolLock.Unlock() + + if pool := bufioReaderPool[size]; pool != nil { + return pool + } + + pool := &memory.Pool[bufio.Reader]{} + bufioReaderPool[size] = pool + return pool +} + +func (f *File) readAt(p []byte, off int64) (int, error) { + return readAt(f.reader, p, off) +} + +func readAt(r io.ReaderAt, p []byte, off int64) (n int, err error) { + n, err = r.ReadAt(p, off) + if n == len(p) { + err = nil + // p was fully read.There is no further need to check for errors. This + // operation is a success in principle. + return + } + return +} + +type optimisticFileReaderAt struct { + reader io.ReaderAt + offset int64 + footer []byte +} + +func (r *optimisticFileReaderAt) ReadAt(p []byte, off int64) (n int, err error) { + length := r.offset + int64(len(r.footer)) + + if off >= length { + return 0, io.EOF + } + + if off >= r.offset { + n = copy(p, r.footer[off-r.offset:]) + p = p[n:] + off += int64(n) + if len(p) == 0 { + return n, nil + } + } + + rn, err := r.reader.ReadAt(p, off) + return n + rn, err +} diff --git a/vendor/github.com/parquet-go/parquet-go/filter.go b/vendor/github.com/parquet-go/parquet-go/filter.go new file mode 100644 index 000000000..26e0f3f45 --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/filter.go @@ -0,0 +1,82 @@ +package parquet + +// FilterRowReader constructs a RowReader which exposes rows from reader for +// which the predicate has returned true. +func FilterRowReader(reader RowReader, predicate func(Row) bool) RowReader { + f := &filterRowReader{reader: reader, predicate: predicate} + for i := range f.rows { + f.rows[i] = f.values[i : i : i+1] + } + return f +} + +type filterRowReader struct { + reader RowReader + predicate func(Row) bool + rows [defaultRowBufferSize]Row + values [defaultRowBufferSize]Value +} + +func (f *filterRowReader) ReadRows(rows []Row) (n int, err error) { + for n < len(rows) { + r := min(len(rows)-n, len(f.rows)) + + r, err = f.reader.ReadRows(f.rows[:r]) + + for i := range r { + if f.predicate(f.rows[i]) { + rows[n] = append(rows[n][:0], f.rows[i]...) + n++ + } + } + + if err != nil { + break + } + } + return n, err +} + +// FilterRowWriter constructs a RowWriter which writes rows to writer for which +// the predicate has returned true. +func FilterRowWriter(writer RowWriter, predicate func(Row) bool) RowWriter { + return &filterRowWriter{writer: writer, predicate: predicate} +} + +type filterRowWriter struct { + writer RowWriter + predicate func(Row) bool + rows [defaultRowBufferSize]Row +} + +func (f *filterRowWriter) WriteRows(rows []Row) (n int, err error) { + defer func() { + clear := f.rows[:] + for i := range clear { + clearValues(clear[i]) + } + }() + + for n < len(rows) { + i := 0 + j := min(len(rows)-n, len(f.rows)) + + for _, row := range rows[n : n+j] { + if f.predicate(row) { + f.rows[i] = row + i++ + } + } + + if i > 0 { + _, err := f.writer.WriteRows(f.rows[:i]) + if err != nil { + break + } + } + + n += j + } + + return n, err +} diff --git a/vendor/github.com/parquet-go/parquet-go/format/parquet.go b/vendor/github.com/parquet-go/parquet-go/format/parquet.go new file mode 100644 index 000000000..26a5de5d2 --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/format/parquet.go @@ -0,0 +1,1324 @@ +package format + +import ( + "fmt" + "strings" + + "github.com/parquet-go/parquet-go/deprecated" + "github.com/parquet-go/parquet-go/encoding/thrift" +) + +// Types supported by Parquet. These types are intended to be used in combination +// with the encodings to control the on disk storage format. For example INT16 +// is not included as a type since a good encoding of INT32 would handle this. +type Type int32 + +const ( + Boolean Type = 0 + Int32 Type = 1 + Int64 Type = 2 + Int96 Type = 3 // deprecated, only used by legacy implementations. + Float Type = 4 + Double Type = 5 + ByteArray Type = 6 + FixedLenByteArray Type = 7 +) + +func (t Type) String() string { + switch t { + case Boolean: + return "BOOLEAN" + case Int32: + return "INT32" + case Int64: + return "INT64" + case Int96: + return "INT96" + case Float: + return "FLOAT" + case Double: + return "DOUBLE" + case ByteArray: + return "BYTE_ARRAY" + case FixedLenByteArray: + return "FIXED_LEN_BYTE_ARRAY" + default: + return "Type(?)" + } +} + +// Representation of Schemas. +type FieldRepetitionType int32 + +const ( + // The field is required (can not be null) and each record has exactly 1 value. + Required FieldRepetitionType = 0 + // The field is optional (can be null) and each record has 0 or 1 values. + Optional FieldRepetitionType = 1 + // The field is repeated and can contain 0 or more values. + Repeated FieldRepetitionType = 2 +) + +func (t FieldRepetitionType) String() string { + switch t { + case Required: + return "REQUIRED" + case Optional: + return "OPTIONAL" + case Repeated: + return "REPEATED" + default: + return "FieldRepeationaType(?)" + } +} + +// A structure for capturing metadata for estimating the unencoded, +// uncompressed size of data written. This is useful for readers to estimate +// how much memory is needed to reconstruct data in their memory model and for +// fine grained filter pushdown on nested structures (the histograms contained +// in this structure can help determine the number of nulls at a particular +// nesting level and maximum length of lists). +type SizeStatistics struct { + // The number of physical bytes stored for BYTE_ARRAY data values assuming + // no encoding. This is exclusive of the bytes needed to store the length of + // each byte array. In other words, this field is equivalent to the `(size + // of PLAIN-ENCODING the byte array values) - (4 bytes * number of values + // written)`. To determine unencoded sizes of other types readers can use + // schema information multiplied by the number of non-null and null values. + // The number of null/non-null values can be inferred from the histograms + // below. + // + // For example, if a column chunk is dictionary-encoded with dictionary + // ["a", "bc", "cde"], and a data page contains the indices [0, 0, 1, 2], + // then this value for that data page should be 7 (1 + 1 + 2 + 3). + // + // This field should only be set for types that use BYTE_ARRAY as their + // physical type. + UnencodedByteArrayDataBytes int64 `thrift:"1,optional"` + + // When present, there is expected to be one element corresponding to each + // repetition (i.e. size=max repetition_level+1) where each element + // represents the number of times the repetition level was observed in the + // data. + // + // This field may be omitted if max_repetition_level is 0 without loss + // of information. + RepetitionLevelHistogram []int64 `thrift:"2,optional"` + + // Same as repetition_level_histogram except for definition levels. + // + // This field may be omitted if max_definition_level is 0 or 1 without + // loss of information. + DefinitionLevelHistogram []int64 `thrift:"3,optional"` +} + +// Bounding box for GEOMETRY or GEOGRAPHY type in the representation of min/max +// value pair of coordinates from each axis. +type BoundingBox struct { + XMin float64 `thrift:"1,required"` + XMax float64 `thrift:"2,required"` + YMin float64 `thrift:"3,required"` + YMax float64 `thrift:"4,required"` + ZMin thrift.Null[float64] `thrift:"5,optional"` + ZMax thrift.Null[float64] `thrift:"6,optional"` + MMin thrift.Null[float64] `thrift:"7,optional"` + MMax thrift.Null[float64] `thrift:"8,optional"` +} + +// Statistics specific to Geometry and Geography logical types +type GeospatialStatistics struct { + // A bounding box of geospatial instances + BBox BoundingBox `thrift:"1,optional"` + // Geospatial type codes of all instances, or an empty list if not known + GeoSpatialTypes []int32 `thrift:"2,optional"` +} + +// Statistics per row group and per page. +// All fields are optional. +type Statistics struct { + // DEPRECATED: min and max value of the column. Use min_value and max_value. + // + // Values are encoded using PLAIN encoding, except that variable-length byte + // arrays do not include a length prefix. + // + // These fields encode min and max values determined by signed comparison + // only. New files should use the correct order for a column's logical type + // and store the values in the min_value and max_value fields. + // + // To support older readers, these may be set when the column order is + // signed. + Max []byte `thrift:"1"` + Min []byte `thrift:"2"` + // Count of null value in the column. + // The writezero tag satisfies spec: + // "Writers SHOULD always write this field even if it is zero (i.e. no null value) or the column is not nullable." + // https://github.com/apache/parquet-format/blob/apache-parquet-format-2.12.0/src/main/thrift/parquet.thrift#L283-L291 + NullCount int64 `thrift:"3,writezero"` + // Count of distinct values occurring. + DistinctCount int64 `thrift:"4"` + // Min and max values for the column, determined by its ColumnOrder. + // + // Values are encoded using PLAIN encoding, except that variable-length byte + // arrays do not include a length prefix. + MaxValue []byte `thrift:"5"` + MinValue []byte `thrift:"6"` +} + +// Empty structs to use as logical type annotations. +type StringType struct{} // allowed for BINARY, must be encoded with UTF-8 +type UUIDType struct{} // allowed for FIXED[16], must encode raw UUID bytes +type MapType struct{} // see LogicalTypes.md +type ListType struct{} // see LogicalTypes.md +type EnumType struct{} // allowed for BINARY, must be encoded with UTF-8 +type DateType struct{} // allowed for INT32 +type Float16Type struct{} // allowed for FIXED[2], must encoded raw FLOAT16 bytes + +func (*StringType) String() string { return "STRING" } +func (*UUIDType) String() string { return "UUID" } +func (*MapType) String() string { return "MAP" } +func (*ListType) String() string { return "LIST" } +func (*EnumType) String() string { return "ENUM" } +func (*DateType) String() string { return "DATE" } +func (*Float16Type) String() string { return "FLOAT16" } + +// Logical type to annotate a column that is always null. +// +// Sometimes when discovering the schema of existing data, values are always +// null and the physical type can't be determined. This annotation signals +// the case where the physical type was guessed from all null values. +type NullType struct{} + +func (*NullType) String() string { return "NULL" } + +// Decimal logical type annotation +// +// To maintain forward-compatibility in v1, implementations using this logical +// type must also set scale and precision on the annotated SchemaElement. +// +// Allowed for physical types: INT32, INT64, FIXED, and BINARY +type DecimalType struct { + Scale int32 `thrift:"1,required"` + Precision int32 `thrift:"2,required"` +} + +func (t *DecimalType) String() string { + // Matching parquet-cli's decimal string format: https://github.com/apache/parquet-java/blob/d057b39d93014fe40f5067ee4a33621e65c91552/parquet-column/src/test/java/org/apache/parquet/parser/TestParquetParser.java#L249-L265 + return fmt.Sprintf("DECIMAL(%d,%d)", t.Precision, t.Scale) +} + +// Time units for logical types. +type MilliSeconds struct{} +type MicroSeconds struct{} +type NanoSeconds struct{} + +func (*MilliSeconds) String() string { return "MILLIS" } +func (*MicroSeconds) String() string { return "MICROS" } +func (*NanoSeconds) String() string { return "NANOS" } + +type TimeUnit struct { // union + Millis *MilliSeconds `thrift:"1"` + Micros *MicroSeconds `thrift:"2"` + Nanos *NanoSeconds `thrift:"3"` +} + +func (u *TimeUnit) String() string { + switch { + case u.Millis != nil: + return u.Millis.String() + case u.Micros != nil: + return u.Micros.String() + case u.Nanos != nil: + return u.Nanos.String() + default: + return "" + } +} + +// Timestamp logical type annotation +// +// Allowed for physical types: INT64 +type TimestampType struct { + IsAdjustedToUTC bool `thrift:"1,required"` + Unit TimeUnit `thrift:"2,required"` +} + +func (t *TimestampType) String() string { + return fmt.Sprintf("TIMESTAMP(isAdjustedToUTC=%t,unit=%s)", t.IsAdjustedToUTC, &t.Unit) +} + +// Time logical type annotation +// +// Allowed for physical types: INT32 (millis), INT64 (micros, nanos) +type TimeType struct { + IsAdjustedToUTC bool `thrift:"1,required"` + Unit TimeUnit `thrift:"2,required"` +} + +func (t *TimeType) String() string { + return fmt.Sprintf("TIME(isAdjustedToUTC=%t,unit=%s)", t.IsAdjustedToUTC, &t.Unit) +} + +// Integer logical type annotation +// +// bitWidth must be 8, 16, 32, or 64. +// +// Allowed for physical types: INT32, INT64 +type IntType struct { + BitWidth int8 `thrift:"1,required"` + IsSigned bool `thrift:"2,required"` +} + +func (t *IntType) String() string { + return fmt.Sprintf("INT(%d,%t)", t.BitWidth, t.IsSigned) +} + +// Embedded JSON logical type annotation +// +// Allowed for physical types: BINARY +type JsonType struct{} + +func (t *JsonType) String() string { return "JSON" } + +// Embedded BSON logical type annotation +// +// Allowed for physical types: BINARY +type BsonType struct{} + +func (t *BsonType) String() string { return "BSON" } + +// Embedded Variant logical type annotation +// +// Allowed for physical types: BYTE_ARRAY (with embedded Variant binary). +// +// The canonical parquet-format thrift IDL declares an optional +// `specification_version` (i8) field on VariantType. Upstream parquet-go +// v0.29.0 modeled this as an empty struct, which works for *reading* (the +// unknown field id is silently skipped) but means the value is dropped on +// round-trip. By declaring the field explicitly we (a) decode it into a +// typed Go value, (b) preserve compatibility on round-trip writes, and +// (c) eliminate one source of "unknown thrift field" warnings when reading +// files written by newer parquet-cpp / parquet-mr / Arrow toolchains. +type VariantType struct { + // SpecificationVersion records the version of the variant specification + // the value was written with. Optional; absent in older files. + SpecificationVersion int8 `thrift:"1,optional"` +} + +func (*VariantType) String() string { return "VARIANT" } + +// Edge interpolation algorithm for Geography logical type +type EdgeInterpolationAlgorithm int32 + +const ( + Spherical EdgeInterpolationAlgorithm = 0 + Vincenty EdgeInterpolationAlgorithm = 1 + Thomas EdgeInterpolationAlgorithm = 2 + Andoyer EdgeInterpolationAlgorithm = 3 + Karney EdgeInterpolationAlgorithm = 4 +) + +const ( + SphericalName = "SPHERICAL" + VincentyName = "VINCENTY" + ThomasName = "THOMAS" + AndoyerName = "ANDOYER" + KarneyName = "KARNEY" +) + +func (e EdgeInterpolationAlgorithm) String() string { + switch e { + case Spherical: + return SphericalName + case Vincenty: + return VincentyName + case Thomas: + return ThomasName + case Andoyer: + return AndoyerName + case Karney: + return KarneyName + default: + return "EdgeInterpolationAlgorithm(?)" + } +} + +func (e *EdgeInterpolationAlgorithm) FromString(s string) error { + switch strings.ToUpper(s) { + case SphericalName: + *e = Spherical + case VincentyName: + *e = Vincenty + case ThomasName: + *e = Thomas + case AndoyerName: + *e = Andoyer + case KarneyName: + *e = Karney + default: + return fmt.Errorf("invalid EdgeInterpolationAlgorithm: %q", s) + } + return nil +} + +const ( + defaultCRS = "OGC:CRS84" + GeometryDefaultCRS = defaultCRS +) + +// Embedded Geometry logical type annotation +// +// Geospatial features in the Well-Known Binary (WKB) format and edges interpolation +// is always linear/planar. +// +// A custom CRS can be set by the crs field. If unset, it defaults to "OGC:CRS84", +// which means that the geometries must be stored in longitude, latitude based on +// the WGS84 datum. +// +// Allowed for physical type: BYTE_ARRAY. +// +// See Geospatial.md for details. +type GeometryType struct { + CRS string `thrift:"1,optional"` +} + +func (t *GeometryType) String() string { + crs := t.CRS + if crs == "" { + crs = GeometryDefaultCRS + } + return fmt.Sprintf("GEOMETRY(%q)", crs) +} + +const GeographyDefaultCRS = defaultCRS + +// Embedded Geography logical type annotation +// +// Geospatial features in the WKB format with an explicit (non-linear/non-planar) +// edges interpolation algorithm. +// +// A custom geographic CRS can be set by the crs field, where longitudes are +// bound by [-180, 180] and latitudes are bound by [-90, 90]. If unset, the CRS +// defaults to "OGC:CRS84". +// +// An optional algorithm can be set to correctly interpret edges interpolation +// of the geometries. If unset, the algorithm defaults to SPHERICAL. +// +// Allowed for physical type: BYTE_ARRAY. +// +// See Geospatial.md for details. +type GeographyType struct { + CRS string `thrift:"1,optional"` + Algorithm EdgeInterpolationAlgorithm `thrift:"2,optional"` +} + +func (t *GeographyType) String() string { + crs := t.CRS + if crs == "" { + crs = GeographyDefaultCRS + } + return fmt.Sprintf("GEOGRAPHY(%q, %s)", crs, t.Algorithm) +} + +// LogicalType annotations to replace ConvertedType. +// +// To maintain compatibility, implementations using LogicalType for a +// SchemaElement must also set the corresponding ConvertedType (if any) +// from the following table. +type LogicalType struct { // union + UTF8 *StringType `thrift:"1"` // use ConvertedType UTF8 + Map *MapType `thrift:"2"` // use ConvertedType Map + List *ListType `thrift:"3"` // use ConvertedType List + Enum *EnumType `thrift:"4"` // use ConvertedType Enum + Decimal *DecimalType `thrift:"5"` // use ConvertedType Decimal + SchemaElement.{Scale, Precision} + Date *DateType `thrift:"6"` // use ConvertedType Date + + // use ConvertedType TimeMicros for Time{IsAdjustedToUTC: *, Unit: Micros} + // use ConvertedType TimeMillis for Time{IsAdjustedToUTC: *, Unit: Millis} + Time *TimeType `thrift:"7"` + + // use ConvertedType TimestampMicros for Timestamp{IsAdjustedToUTC: *, Unit: Micros} + // use ConvertedType TimestampMillis for Timestamp{IsAdjustedToUTC: *, Unit: Millis} + Timestamp *TimestampType `thrift:"8"` + + // 9: reserved for Interval + Integer *IntType `thrift:"10"` // use ConvertedType Int* or Uint* + Unknown *NullType `thrift:"11"` // no compatible ConvertedType + Json *JsonType `thrift:"12"` // use ConvertedType JSON + Bson *BsonType `thrift:"13"` // use ConvertedType BSON + UUID *UUIDType `thrift:"14"` // no compatible ConvertedType + Float16 *Float16Type `thrift:"15"` // no compatible ConvertedType + Variant *VariantType `thrift:"16"` // no compatible ConvertedType + Geometry *GeometryType `thrift:"17"` // no compatible ConvertedType + Geography *GeographyType `thrift:"18"` // no compatible ConvertedType +} + +func (t *LogicalType) String() string { + switch { + case t.UTF8 != nil: + return t.UTF8.String() + case t.Map != nil: + return t.Map.String() + case t.List != nil: + return t.List.String() + case t.Enum != nil: + return t.Enum.String() + case t.Decimal != nil: + return t.Decimal.String() + case t.Date != nil: + return t.Date.String() + case t.Time != nil: + return t.Time.String() + case t.Timestamp != nil: + return t.Timestamp.String() + case t.Integer != nil: + return t.Integer.String() + case t.Unknown != nil: + return t.Unknown.String() + case t.Json != nil: + return t.Json.String() + case t.Bson != nil: + return t.Bson.String() + case t.UUID != nil: + return t.UUID.String() + case t.Float16 != nil: + return t.Float16.String() + case t.Variant != nil: + return t.Variant.String() + case t.Geometry != nil: + return t.Geometry.String() + case t.Geography != nil: + return t.Geography.String() + default: + return "" + } +} + +// Represents a element inside a schema definition. +// +// - if it is a group (inner node) then type is undefined and num_children is +// defined +// +// - if it is a primitive type (leaf) then type is defined and num_children is +// undefined +// +// The nodes are listed in depth first traversal order. +type SchemaElement struct { + // Data type for this field. Not set if the current element is a non-leaf node. + Type thrift.Null[Type] `thrift:"1,optional"` + + // If type is FixedLenByteArray, this is the byte length of the values. + // Otherwise, if specified, this is the maximum bit length to store any of the values. + // (e.g. a low cardinality INT col could have this set to 3). Note that this is + // in the schema, and therefore fixed for the entire file. + TypeLength thrift.Null[int32] `thrift:"2,optional"` + + // repetition of the field. The root of the schema does not have a repetition_type. + // All other nodes must have one. + RepetitionType thrift.Null[FieldRepetitionType] `thrift:"3,optional"` + + // Name of the field in the schema. + Name string `thrift:"4,required"` + + // Nested fields. Since thrift does not support nested fields, + // the nesting is flattened to a single list by a depth-first traversal. + // The children count is used to construct the nested relationship. + // This field is not set when the element is a primitive type + NumChildren thrift.Null[int32] `thrift:"5,optional"` + + // DEPRECATED: When the schema is the result of a conversion from another model. + // Used to record the original type to help with cross conversion. + // + // This is superseded by logicalType. + ConvertedType thrift.Null[deprecated.ConvertedType] `thrift:"6,optional"` + + // DEPRECATED: Used when this column contains decimal data. + // See the DECIMAL converted type for more details. + // + // This is superseded by using the DecimalType annotation in logicalType. + Scale thrift.Null[int32] `thrift:"7,optional"` + Precision thrift.Null[int32] `thrift:"8,optional"` + + // When the original schema supports field ids, this will save the + // original field id in the parquet schema. + FieldID int32 `thrift:"9,optional"` + + // The logical type of this SchemaElement + // + // LogicalType replaces ConvertedType, but ConvertedType is still required + // for some logical types to ensure forward-compatibility in format v1. + LogicalType thrift.Null[LogicalType] `thrift:"10,optional"` +} + +// Encodings supported by Parquet. Not all encodings are valid for all types. +// These enums are also used to specify the encoding of definition and +// repetition levels. See the accompanying doc for the details of the more +// complicated encodings. +type Encoding int32 + +const ( + // Default encoding. + // Boolean - 1 bit per value. 0 is false; 1 is true. + // Int32 - 4 bytes per value. Stored as little-endian. + // Int64 - 8 bytes per value. Stored as little-endian. + // Float - 4 bytes per value. IEEE. Stored as little-endian. + // Double - 8 bytes per value. IEEE. Stored as little-endian. + // ByteArray - 4 byte length stored as little endian, followed by bytes. + // FixedLenByteArray - Just the bytes. + Plain Encoding = 0 + + // Group VarInt encoding for Int32/Int64. + // This encoding is deprecated. It was never used. + // GroupVarInt Encoding = 1 + + // Deprecated: Dictionary encoding. The values in the dictionary are encoded + // in the plain type. + // In a data page use RLEDictionary instead. + // In a Dictionary page use Plain instead. + PlainDictionary Encoding = 2 + + // Group packed run length encoding. Usable for definition/repetition levels + // encoding and Booleans (on one bit: 0 is false 1 is true.) + RLE Encoding = 3 + + // Bit packed encoding. This can only be used if the data has a known max + // width. Usable for definition/repetition levels encoding. + BitPacked Encoding = 4 + + // Delta encoding for integers. This can be used for int columns and works best + // on sorted data. + DeltaBinaryPacked Encoding = 5 + + // Encoding for byte arrays to separate the length values and the data. + // The lengths are encoded using DeltaBinaryPacked. + DeltaLengthByteArray Encoding = 6 + + // Incremental-encoded byte array. Prefix lengths are encoded using DELTA_BINARY_PACKED. + // Suffixes are stored as delta length byte arrays. + DeltaByteArray Encoding = 7 + + // Dictionary encoding: the ids are encoded using the RLE encoding + RLEDictionary Encoding = 8 + + // Encoding for floating-point data. + // K byte-streams are created where K is the size in bytes of the data type. + // The individual bytes of an FP value are scattered to the corresponding stream and + // the streams are concatenated. + // This itself does not reduce the size of the data but can lead to better compression + // afterwards. + ByteStreamSplit Encoding = 9 +) + +func (e Encoding) String() string { + switch e { + case Plain: + return "PLAIN" + case PlainDictionary: + return "PLAIN_DICTIONARY" + case RLE: + return "RLE" + case BitPacked: + return "BIT_PACKED" + case DeltaBinaryPacked: + return "DELTA_BINARY_PACKED" + case DeltaLengthByteArray: + return "DELTA_LENGTH_BYTE_ARRAY" + case DeltaByteArray: + return "DELTA_BYTE_ARRAY" + case RLEDictionary: + return "RLE_DICTIONARY" + case ByteStreamSplit: + return "BYTE_STREAM_SPLIT" + default: + return "Encoding(?)" + } +} + +// Supported compression algorithms. +// +// Codecs added in format version X.Y can be read by readers based on X.Y and later. +// Codec support may vary between readers based on the format version and +// libraries available at runtime. +// +// See Compression.md for a detailed specification of these algorithms. +type CompressionCodec int32 + +const ( + Uncompressed CompressionCodec = 0 + Snappy CompressionCodec = 1 + Gzip CompressionCodec = 2 + LZO CompressionCodec = 3 + Brotli CompressionCodec = 4 // Added in 2.4 + Lz4 CompressionCodec = 5 // DEPRECATED (Added in 2.4) + Zstd CompressionCodec = 6 // Added in 2.4 + Lz4Raw CompressionCodec = 7 // Added in 2.9 +) + +func (c CompressionCodec) String() string { + switch c { + case Uncompressed: + return "UNCOMPRESSED" + case Snappy: + return "SNAPPY" + case Gzip: + return "GZIP" + case LZO: + return "LZO" + case Brotli: + return "BROTLI" + case Lz4: + return "LZ4" + case Zstd: + return "ZSTD" + case Lz4Raw: + return "LZ4_RAW" + default: + return "CompressionCodec(?)" + } +} + +type PageType int32 + +const ( + DataPage PageType = 0 + IndexPage PageType = 1 + DictionaryPage PageType = 2 + // Version 2 is indicated in the PageHeader and the use of DataPageHeaderV2, + // and allows you to read repetition and definition level data without + // decompressing the Page. + DataPageV2 PageType = 3 +) + +func (p PageType) String() string { + switch p { + case DataPage: + return "DATA_PAGE" + case IndexPage: + return "INDEX_PAGE" + case DictionaryPage: + return "DICTIONARY_PAGE" + case DataPageV2: + return "DATA_PAGE_V2" + default: + return "PageType(?)" + } +} + +// Enum to annotate whether lists of min/max elements inside ColumnIndex +// are ordered and if so, in which direction. +type BoundaryOrder int32 + +const ( + Unordered BoundaryOrder = 0 + Ascending BoundaryOrder = 1 + Descending BoundaryOrder = 2 +) + +func (b BoundaryOrder) String() string { + switch b { + case Unordered: + return "UNORDERED" + case Ascending: + return "ASCENDING" + case Descending: + return "DESCENDING" + default: + return "BoundaryOrder(?)" + } +} + +// Data page header. +type DataPageHeader struct { + // Number of values, including NULLs, in this data page. + NumValues int32 `thrift:"1,required"` + + // Encoding used for this data page. + Encoding Encoding `thrift:"2,required"` + + // Encoding used for definition levels. + DefinitionLevelEncoding Encoding `thrift:"3,required"` + + // Encoding used for repetition levels. + RepetitionLevelEncoding Encoding `thrift:"4,required"` + + // Optional statistics for the data in this page. + // The writezero tag supports writezero fields of Statistics. + Statistics Statistics `thrift:"5,optional,writezero"` +} + +type IndexPageHeader struct { + // TODO +} + +// The dictionary page must be placed at the first position of the column chunk +// if it is partly or completely dictionary encoded. At most one dictionary page +// can be placed in a column chunk. +type DictionaryPageHeader struct { + // Number of values in the dictionary. + NumValues int32 `thrift:"1,required"` + + // Encoding using this dictionary page. + Encoding Encoding `thrift:"2,required"` + + // If true, the entries in the dictionary are sorted in ascending order. + IsSorted bool `thrift:"3,optional"` +} + +// New page format allowing reading levels without decompressing the data +// Repetition and definition levels are uncompressed +// The remaining section containing the data is compressed if is_compressed is +// true. +type DataPageHeaderV2 struct { + // Number of values, including NULLs, in this data page. + NumValues int32 `thrift:"1,required"` + // Number of NULL values, in this data page. + // Number of non-null = num_values - num_nulls which is also the number of + // values in the data section. + NumNulls int32 `thrift:"2,required"` + // Number of rows in this data page. which means pages change on record boundaries (r = 0). + NumRows int32 `thrift:"3,required"` + // Encoding used for data in this page. + Encoding Encoding `thrift:"4,required"` + + // Repetition levels and definition levels are always using RLE (without size in it). + + // Length of the definition levels. + DefinitionLevelsByteLength int32 `thrift:"5,required"` + // Length of the repetition levels. + RepetitionLevelsByteLength int32 `thrift:"6,required"` + + // Whether the values are compressed. + // Which means the section of the page between + // definition_levels_byte_length + repetition_levels_byte_length + 1 and compressed_page_size (included) + // is compressed with the compression_codec. + // If missing it is considered compressed. + IsCompressed thrift.Null[bool] `thrift:"7,optional"` + + // Optional statistics for the data in this page. + // The writezero tag supports writezero fields of Statistics. + Statistics Statistics `thrift:"8,optional,writezero"` +} + +// Block-based algorithm type annotation. +type SplitBlockAlgorithm struct{} + +// The algorithm used in Bloom filter. +type BloomFilterAlgorithm struct { // union + Block *SplitBlockAlgorithm `thrift:"1"` +} + +// Hash strategy type annotation. xxHash is an extremely fast non-cryptographic +// hash algorithm. It uses 64 bits version of xxHash. +type XxHash struct{} + +// The hash function used in Bloom filter. This function takes the hash of a +// column value using plain encoding. +type BloomFilterHash struct { // union + XxHash *XxHash `thrift:"1"` +} + +// The compression used in the Bloom filter. +type BloomFilterUncompressed struct{} +type BloomFilterGzip struct{} +type BloomFilterCompression struct { // union + Uncompressed *BloomFilterUncompressed `thrift:"1"` + GZip *BloomFilterGzip `thrift:"2"` +} + +// Bloom filter header is stored at beginning of Bloom filter data of each column +// and followed by its bitset. +type BloomFilterHeader struct { + // The size of bitset in bytes. + NumBytes int32 `thrift:"1,required"` + // The algorithm for setting bits. + Algorithm BloomFilterAlgorithm `thrift:"2,required"` + // The hash function used for Bloom filter. + Hash BloomFilterHash `thrift:"3,required"` + // The compression used in the Bloom filter. + Compression BloomFilterCompression `thrift:"4,required"` +} + +type PageHeader struct { + // The type of the page indicates which of the *Header fields below is set. + Type PageType `thrift:"1,required"` + + // Uncompressed page size in bytes (not including this header). + UncompressedPageSize int32 `thrift:"2,required"` + + // Compressed (and potentially encrypted) page size in bytes, not including + // this header. + CompressedPageSize int32 `thrift:"3,required"` + + // The 32bit CRC for the page, to be be calculated as follows: + // - Using the standard CRC32 algorithm + // - On the data only, i.e. this header should not be included. 'Data' + // hereby refers to the concatenation of the repetition levels, the + // definition levels and the column value, in this exact order. + // - On the encoded versions of the repetition levels, definition levels and + // column values. + // - On the compressed versions of the repetition levels, definition levels + // and column values where possible; + // - For v1 data pages, the repetition levels, definition levels and column + // values are always compressed together. If a compression scheme is + // specified, the CRC shall be calculated on the compressed version of + // this concatenation. If no compression scheme is specified, the CRC + // shall be calculated on the uncompressed version of this concatenation. + // - For v2 data pages, the repetition levels and definition levels are + // handled separately from the data and are never compressed (only + // encoded). If a compression scheme is specified, the CRC shall be + // calculated on the concatenation of the uncompressed repetition levels, + // uncompressed definition levels and the compressed column values. + // If no compression scheme is specified, the CRC shall be calculated on + // the uncompressed concatenation. + // - In encrypted columns, CRC is calculated after page encryption; the + // encryption itself is performed after page compression (if compressed) + // If enabled, this allows for disabling checksumming in HDFS if only a few + // pages need to be read. + CRC int32 `thrift:"4,optional"` + + // Headers for page specific data. One only will be set. + DataPageHeader thrift.Null[DataPageHeader] `thrift:"5,optional"` + IndexPageHeader thrift.Null[IndexPageHeader] `thrift:"6,optional"` + DictionaryPageHeader thrift.Null[DictionaryPageHeader] `thrift:"7,optional"` + DataPageHeaderV2 thrift.Null[DataPageHeaderV2] `thrift:"8,optional"` +} + +// Wrapper struct to store key values. +type KeyValue struct { + Key string `thrift:"1,required"` + Value string `thrift:"2,required"` +} + +// Wrapper struct to specify sort order. +type SortingColumn struct { + // The column index (in this row group) + ColumnIdx int32 `thrift:"1,required"` + + // If true, indicates this column is sorted in descending order. + Descending bool `thrift:"2,required"` + + // If true, nulls will come before non-null values, otherwise, + // nulls go at the end. + NullsFirst bool `thrift:"3,required"` +} + +// Statistics of a given page type and encoding. +type PageEncodingStats struct { + // The page type (data/dic/...). + PageType PageType `thrift:"1,required"` + + // Encoding of the page. + Encoding Encoding `thrift:"2,required"` + + // Number of pages of this type with this encoding. + Count int32 `thrift:"3,required"` +} + +// Description for column metadata. +type ColumnMetaData struct { + // Type of this column. + Type Type `thrift:"1,required"` + + // Set of all encodings used for this column. The purpose is to validate + // whether we can decode those pages. + Encoding thrift.Slice[Encoding] `thrift:"2,required"` + + // Path in schema. + PathInSchema thrift.Slice[string] `thrift:"3,required"` + + // Compression codec. + Codec CompressionCodec `thrift:"4,required"` + + // Number of values in this column. + NumValues int64 `thrift:"5,required"` + + // Total byte size of all uncompressed pages in this column chunk (including the headers). + TotalUncompressedSize int64 `thrift:"6,required"` + + // Total byte size of all compressed, and potentially encrypted, pages + // in this column chunk (including the headers). + TotalCompressedSize int64 `thrift:"7,required"` + + // Optional key/value metadata. + KeyValueMetadata []KeyValue `thrift:"8,optional"` + + // Byte offset from beginning of file to first data page. + DataPageOffset int64 `thrift:"9,required"` + + // Byte offset from beginning of file to root index page. + IndexPageOffset int64 `thrift:"10,optional"` + + // Byte offset from the beginning of file to first (only) dictionary page. + DictionaryPageOffset int64 `thrift:"11,optional"` + + // optional statistics for this column chunk. + // The writezero tag supports writezero fields of Statistics. + Statistics Statistics `thrift:"12,optional,writezero"` + + // Set of all encodings used for pages in this column chunk. + // This information can be used to determine if all data pages are + // dictionary encoded for example. + EncodingStats []PageEncodingStats `thrift:"13,optional"` + + // Byte offset from beginning of file to Bloom filter data. + BloomFilterOffset int64 `thrift:"14,optional"` + + // Size of Bloom filter data including the serialized header, in bytes. + // Added in 2.10 so readers may not read this field from old files and + // it can be obtained after the BloomFilterHeader has been deserialized. + // Writers should write this field so readers can read the bloom filter + // in a single I/O. + BloomFilterLength int32 `thrift:"15,optional"` + + // Optional statistics to help estimate total memory when converted to in-memory + // representations. The histograms contained in these statistics can + // also be useful in some cases for more fine-grained nullability/list length + // filter pushdown. + SizeStatistics SizeStatistics `thrift:"16,optional"` + + // Optional statistics specific for Geometry and Geography logical types + GeospatialStatistics GeospatialStatistics `thrift:"17,optional"` +} + +type EncryptionWithFooterKey struct{} + +type EncryptionWithColumnKey struct { + // Column path in schema. + PathInSchema []string `thrift:"1,required"` + + // Retrieval metadata of column encryption key. + KeyMetadata []byte `thrift:"2,optional"` +} + +type ColumnCryptoMetaData struct { + EncryptionWithFooterKey *EncryptionWithFooterKey `thrift:"1"` + EncryptionWithColumnKey *EncryptionWithColumnKey `thrift:"2"` +} + +type ColumnChunk struct { + // File where column data is stored. If not set, assumed to be same file as + // metadata. This path is relative to the current file. + FilePath string `thrift:"1,optional"` + + // Byte offset in file_path to the ColumnMetaData. + FileOffset int64 `thrift:"2,required"` + + // Column metadata for this chunk. This is the same content as what is at + // file_path/file_offset. Having it here has it replicated in the file + // metadata. + MetaData ColumnMetaData `thrift:"3,optional"` + + // File offset of ColumnChunk's OffsetIndex. + OffsetIndexOffset int64 `thrift:"4,optional"` + + // Size of ColumnChunk's OffsetIndex, in bytes. + OffsetIndexLength int32 `thrift:"5,optional"` + + // File offset of ColumnChunk's ColumnIndex. + ColumnIndexOffset int64 `thrift:"6,optional"` + + // Size of ColumnChunk's ColumnIndex, in bytes. + ColumnIndexLength int32 `thrift:"7,optional"` + + // Crypto metadata of encrypted columns. + CryptoMetadata ColumnCryptoMetaData `thrift:"8,optional"` + + // Encrypted column metadata for this chunk. + EncryptedColumnMetadata []byte `thrift:"9,optional"` +} + +type RowGroup struct { + // Metadata for each column chunk in this row group. + // This list must have the same order as the SchemaElement list in FileMetaData. + Columns thrift.Slice[ColumnChunk] `thrift:"1,required"` + + // Total byte size of all the uncompressed column data in this row group. + TotalByteSize int64 `thrift:"2,required"` + + // Number of rows in this row group. + NumRows int64 `thrift:"3,required"` + + // If set, specifies a sort ordering of the rows in this RowGroup. + // The sorting columns can be a subset of all the columns. + SortingColumns []SortingColumn `thrift:"4,optional"` + + // Byte offset from beginning of file to first page (data or dictionary) + // in this row group + FileOffset int64 `thrift:"5,optional"` + + // Total byte size of all compressed (and potentially encrypted) column data + // in this row group. + TotalCompressedSize int64 `thrift:"6,optional"` + + // Row group ordinal in the file. + // The writezero tag ensures ordinal 0 is serialized; without it, Ordinal=0 + // would be omitted while Ordinal=1,2,... would be written, which breaks + // readers with strict validation for row group ordinals. + Ordinal int16 `thrift:"7,optional,writezero"` +} + +func (r *RowGroup) Reset() { + r.FileOffset = 0 + r.NumRows = 0 + r.TotalByteSize = 0 + r.SortingColumns = r.SortingColumns[:0] + r.Columns = r.Columns[:0] + r.Ordinal = 0 + r.TotalByteSize = 0 + r.TotalCompressedSize = 0 +} + +// Empty struct to signal the order defined by the physical or logical type. +type TypeDefinedOrder struct{} + +// Union to specify the order used for the min_value and max_value fields for a +// column. This union takes the role of an enhanced enum that allows rich +// elements (which will be needed for a collation-based ordering in the future). +// +// Possible values are: +// +// TypeDefinedOrder - the column uses the order defined by its logical or +// physical type (if there is no logical type). +// +// If the reader does not support the value of this union, min and max stats +// for this column should be ignored. +type ColumnOrder struct { // union + // The sort orders for logical types are: + // UTF8 - unsigned byte-wise comparison + // INT8 - signed comparison + // INT16 - signed comparison + // INT32 - signed comparison + // INT64 - signed comparison + // UINT8 - unsigned comparison + // UINT16 - unsigned comparison + // UINT32 - unsigned comparison + // UINT64 - unsigned comparison + // DECIMAL - signed comparison of the represented value + // DATE - signed comparison + // TIME_MILLIS - signed comparison + // TIME_MICROS - signed comparison + // TIMESTAMP_MILLIS - signed comparison + // TIMESTAMP_MICROS - signed comparison + // INTERVAL - unsigned comparison + // JSON - unsigned byte-wise comparison + // BSON - unsigned byte-wise comparison + // ENUM - unsigned byte-wise comparison + // LIST - undefined + // MAP - undefined + // VARIANT - undefined + // GEOMETRY - undefined + // GEOGRAPHY - undefined + // + // In the absence of logical types, the sort order is determined by the physical type: + // BOOLEAN - false, true + // INT32 - signed comparison + // INT64 - signed comparison + // INT96 (only used for legacy timestamps) - undefined + // FLOAT - signed comparison of the represented value (*) + // DOUBLE - signed comparison of the represented value (*) + // BYTE_ARRAY - unsigned byte-wise comparison + // FIXED_LEN_BYTE_ARRAY - unsigned byte-wise comparison + // + // (*) Because the sorting order is not specified properly for floating + // point values (relations vs. total ordering) the following + // compatibility rules should be applied when reading statistics: + // - If the min is a NaN, it should be ignored. + // - If the max is a NaN, it should be ignored. + // - If the min is +0, the row group may contain -0 values as well. + // - If the max is -0, the row group may contain +0 values as well. + // - When looking for NaN values, min and max should be ignored. + TypeOrder *TypeDefinedOrder `thrift:"1"` +} + +type PageLocation struct { + // Offset of the page in the file. + Offset int64 `thrift:"1,required"` + + // Size of the page, including header. Sum of compressed_page_size and + // header length. + CompressedPageSize int32 `thrift:"2,required"` + + // Index within the RowGroup of the first row of the page; this means + // pages change on record boundaries (r = 0). + FirstRowIndex int64 `thrift:"3,required"` +} + +type OffsetIndex struct { + // PageLocations, ordered by increasing PageLocation.offset. It is required + // that page_locations[i].first_row_index < page_locations[i+1].first_row_index. + PageLocations []PageLocation `thrift:"1,required"` + + // Unencoded/uncompressed size for BYTE_ARRAY types. + // + // See documention for unencoded_byte_array_data_bytes in SizeStatistics for + // more details on this field. + UnencodedByteArrayDataBytes []int64 `thrift:"2,optional"` +} + +func (o *OffsetIndex) Reset() { + for k := range o.PageLocations { + o.PageLocations[k].Offset = 0 + o.PageLocations[k].CompressedPageSize = 0 + o.PageLocations[k].FirstRowIndex = 0 + } + o.PageLocations = o.PageLocations[:0] + o.UnencodedByteArrayDataBytes = o.UnencodedByteArrayDataBytes[:0] +} + +// Description for ColumnIndex. +// Each [i] refers to the page at OffsetIndex.PageLocations[i] +type ColumnIndex struct { + // A list of Boolean values to determine the validity of the corresponding + // min and max values. If true, a page contains only null values, and writers + // have to set the corresponding entries in min_values and max_values to + // byte[0], so that all lists have the same length. If false, the + // corresponding entries in min_values and max_values must be valid. + NullPages []bool `thrift:"1,required"` + + // Two lists containing lower and upper bounds for the values of each page + // determined by the ColumnOrder of the column. These may be the actual + // minimum and maximum values found on a page, but can also be (more compact) + // values that do not exist on a page. For example, instead of storing ""Blart + // Versenwald III", a writer may set min_values[i]="B", max_values[i]="C". + // Such more compact values must still be valid values within the column's + // logical type. Readers must make sure that list entries are populated before + // using them by inspecting null_pages. + MinValues [][]byte `thrift:"2,required"` + MaxValues [][]byte `thrift:"3,required"` + + // Stores whether both min_values and max_values are ordered and if so, in + // which direction. This allows readers to perform binary searches in both + // lists. Readers cannot assume that max_values[i] <= min_values[i+1], even + // if the lists are ordered. + BoundaryOrder BoundaryOrder `thrift:"4,required"` + + // A list containing the number of null values for each page. + // The writezero tag satisfies spec: + // "Writers SHOULD always write this field even if no null values are present or the column is not nullable." + // https://github.com/apache/parquet-format/blob/apache-parquet-format-2.12.0/src/main/thrift/parquet.thrift#L1197-L1198 + NullCounts []int64 `thrift:"5,optional,writezero"` + + // Contains repetition level histograms for each page + // concatenated together. The repetition_level_histogram field on + // SizeStatistics contains more details. + // + // When present the length should always be (number of pages * + // (max_repetition_level + 1)) elements. + // + // Element 0 is the first element of the histogram for the first page. + // Element (max_repetition_level + 1) is the first element of the histogram + // for the second page. + RepetitionLevelHistogram []int64 `thrift:"6,optional"` + + // Same as repetition_level_histograms except for definitions levels. + DefinitionLevelHistogram []int64 `thrift:"7,optional"` +} + +func (c *ColumnIndex) Reset() { + c.DefinitionLevelHistogram = c.DefinitionLevelHistogram[:0] + c.RepetitionLevelHistogram = c.RepetitionLevelHistogram[:0] + c.NullCounts = c.NullCounts[:0] + c.NullPages = c.NullPages[:0] + c.BoundaryOrder = 0 + for k := range c.MaxValues { + c.MaxValues[k] = c.MaxValues[k][:0] + } + c.MaxValues = c.MaxValues[:0] + for k := range c.MinValues { + c.MinValues[k] = c.MinValues[k][:0] + } + c.MinValues = c.MinValues[:0] +} + +type AesGcmV1 struct { + // AAD prefix. + AadPrefix []byte `thrift:"1,optional"` + + // Unique file identifier part of AAD suffix. + AadFileUnique []byte `thrift:"2,optional"` + + // In files encrypted with AAD prefix without storing it, + // readers must supply the prefix. + SupplyAadPrefix bool `thrift:"3,optional"` +} + +type AesGcmCtrV1 struct { + // AAD prefix. + AadPrefix []byte `thrift:"1,optional"` + + // Unique file identifier part of AAD suffix. + AadFileUnique []byte `thrift:"2,optional"` + + // In files encrypted with AAD prefix without storing it, + // readers must supply the prefix. + SupplyAadPrefix bool `thrift:"3,optional"` +} + +type EncryptionAlgorithm struct { // union + AesGcmV1 *AesGcmV1 `thrift:"1"` + AesGcmCtrV1 *AesGcmCtrV1 `thrift:"2"` +} + +// Description for file metadata. +type FileMetaData struct { + // Version of this file. + Version int32 `thrift:"1,required"` + + // Parquet schema for this file. This schema contains metadata for all the columns. + // The schema is represented as a tree with a single root. The nodes of the tree + // are flattened to a list by doing a depth-first traversal. + // The column metadata contains the path in the schema for that column which can be + // used to map columns to nodes in the schema. + // The first element is the root. + Schema thrift.Slice[SchemaElement] `thrift:"2,required"` + + // Number of rows in this file. + NumRows int64 `thrift:"3,required"` + + // Row groups in this file. + RowGroups thrift.Slice[RowGroup] `thrift:"4,required"` + + // Optional key/value metadata. + KeyValueMetadata []KeyValue `thrift:"5,optional"` + + // String for application that wrote this file. This should be in the format + // version (build ). + // e.g. impala version 1.0 (build 6cf94d29b2b7115df4de2c06e2ab4326d721eb55) + CreatedBy string `thrift:"6,optional"` + + // Sort order used for the min_value and max_value fields in the Statistics + // objects and the min_values and max_values fields in the ColumnIndex + // objects of each column in this file. Sort orders are listed in the order + // matching the columns in the schema. The indexes are not necessary the same + // though, because only leaf nodes of the schema are represented in the list + // of sort orders. + // + // Without column_orders, the meaning of the min_value and max_value fields + // in the Statistics object and the ColumnIndex object is undefined. To ensure + // well-defined behavior, if these fields are written to a Parquet file, + // column_orders must be written as well. + // + // The obsolete min and max fields in the Statistics object are always sorted + // by signed comparison regardless of column_orders. + ColumnOrders []ColumnOrder `thrift:"7,optional"` + + // Encryption algorithm. This field is set only in encrypted files + // with plaintext footer. Files with encrypted footer store algorithm id + // in FileCryptoMetaData structure. + EncryptionAlgorithm EncryptionAlgorithm `thrift:"8,optional"` + + // Retrieval metadata of key used for signing the footer. + // Used only in encrypted files with plaintext footer. + FooterSigningKeyMetadata []byte `thrift:"9,optional"` +} + +// Crypto metadata for files with encrypted footer. +type FileCryptoMetaData struct { + // Encryption algorithm. This field is only used for files + // with encrypted footer. Files with plaintext footer store algorithm id + // inside footer (FileMetaData structure). + EncryptionAlgorithm EncryptionAlgorithm `thrift:"1,required"` + + // Retrieval metadata of key used for encryption of footer, + // and (possibly) columns. + KeyMetadata []byte `thrift:"2,optional"` +} diff --git a/vendor/github.com/parquet-go/parquet-go/geospatial_statistics.go b/vendor/github.com/parquet-go/parquet-go/geospatial_statistics.go new file mode 100644 index 000000000..8e7eae1ce --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/geospatial_statistics.go @@ -0,0 +1,213 @@ +package parquet + +import ( + "math" + "slices" + + ethrift "github.com/parquet-go/parquet-go/encoding/thrift" + "github.com/parquet-go/parquet-go/format" + geom "github.com/twpayne/go-geom" + "github.com/twpayne/go-geom/encoding/wkb" +) + +// geospatialBBoxAccumulator accumulates a bounding box across all non-null WKB +// values written to a GEOMETRY or GEOGRAPHY column within one row group. +type geospatialBBoxAccumulator struct { + hasValues bool // true once a non-empty geometry extends the bbox + hasGeomTypes bool // true once any non-null geometry (including empty) is seen + parseError bool // true if any WKB value failed to parse; suppresses stats output + xMin, xMax float64 + yMin, yMax float64 + hasZ bool + zMin, zMax float64 + hasM bool + mMin, mMax float64 + geomTypeSet map[int32]struct{} +} + +func newGeospatialBBoxAccumulator() *geospatialBBoxAccumulator { + a := &geospatialBBoxAccumulator{} + a.reset() + return a +} + +func (a *geospatialBBoxAccumulator) reset() { + a.hasValues = false + a.hasGeomTypes = false + a.parseError = false + a.xMin, a.xMax = math.Inf(1), math.Inf(-1) + a.yMin, a.yMax = math.Inf(1), math.Inf(-1) + a.hasZ = false + a.zMin, a.zMax = math.Inf(1), math.Inf(-1) + a.hasM = false + a.mMin, a.mMax = math.Inf(1), math.Inf(-1) + a.geomTypeSet = make(map[int32]struct{}) +} + +func (a *geospatialBBoxAccumulator) updateFromGeom(g geom.T) { + if g == nil { + return + } + + // Always record the type code: empty geometries are still non-null values + // with a defined WKB type and must appear in GeoSpatialTypes. + if code := wkbGeomTypeCode(g); code != 0 { + a.geomTypeSet[code] = struct{}{} + a.hasGeomTypes = true + } + + bounds := g.Bounds() + if bounds == nil || bounds.IsEmpty() { + return + } + + xMin := bounds.Min(0) + xMax := bounds.Max(0) + yMin := bounds.Min(1) + yMax := bounds.Max(1) + + if !math.IsNaN(xMin) && !math.IsNaN(xMax) { + if !a.hasValues || xMin < a.xMin { + a.xMin = xMin + } + if !a.hasValues || xMax > a.xMax { + a.xMax = xMax + } + } + if !math.IsNaN(yMin) && !math.IsNaN(yMax) { + if !a.hasValues || yMin < a.yMin { + a.yMin = yMin + } + if !a.hasValues || yMax > a.yMax { + a.yMax = yMax + } + } + + layout := g.Layout() + if zIdx := layout.ZIndex(); zIdx >= 0 { + zMin := bounds.Min(zIdx) + zMax := bounds.Max(zIdx) + if !math.IsNaN(zMin) && !math.IsNaN(zMax) { + if !a.hasZ || zMin < a.zMin { + a.zMin = zMin + } + if !a.hasZ || zMax > a.zMax { + a.zMax = zMax + } + a.hasZ = true + } + } + if mIdx := layout.MIndex(); mIdx >= 0 { + mMin := bounds.Min(mIdx) + mMax := bounds.Max(mIdx) + if !math.IsNaN(mMin) && !math.IsNaN(mMax) { + if !a.hasM || mMin < a.mMin { + a.mMin = mMin + } + if !a.hasM || mMax > a.mMax { + a.mMax = mMax + } + a.hasM = true + } + } + + a.hasValues = true +} + +func (a *geospatialBBoxAccumulator) accumulatePage(page Page) { + reader := page.Values() + var buf [64]Value + for { + n, err := reader.ReadValues(buf[:]) + for _, v := range buf[:n] { + if v.IsNull() { + continue + } + g, parseErr := wkb.Unmarshal(v.ByteArray()) + if parseErr != nil { + // A non-null value that cannot be parsed makes the stats + // unreliable for this chunk; suppress them entirely. + a.parseError = true + return + } + a.updateFromGeom(g) + } + if err != nil { + break + } + } +} + +func (a *geospatialBBoxAccumulator) toGeospatialStatistics() format.GeospatialStatistics { + // Suppress stats entirely if a WKB parse error occurred: the accumulated + // data would describe only a subset of the column chunk values. + if a.parseError || !a.hasGeomTypes { + return format.GeospatialStatistics{} + } + + geomTypes := make([]int32, 0, len(a.geomTypeSet)) + for t := range a.geomTypeSet { + geomTypes = append(geomTypes, t) + } + slices.Sort(geomTypes) + + stats := format.GeospatialStatistics{ + GeoSpatialTypes: geomTypes, + } + + // BBox is only meaningful when at least one non-empty geometry was seen. + if a.hasValues { + bbox := format.BoundingBox{ + XMin: a.xMin, + XMax: a.xMax, + YMin: a.yMin, + YMax: a.yMax, + } + if a.hasZ { + bbox.ZMin = ethrift.New(a.zMin) + bbox.ZMax = ethrift.New(a.zMax) + } + if a.hasM { + bbox.MMin = ethrift.New(a.mMin) + bbox.MMax = ethrift.New(a.mMax) + } + stats.BBox = bbox + } + + return stats +} + +// wkbGeomTypeCode returns the OGC WKB geometry type code for g. +// Base codes: 1=Point, 2=LineString, 3=Polygon, 4=MultiPoint, +// 5=MultiLineString, 6=MultiPolygon, 7=GeometryCollection. +// Z variants add 1000, M variants add 2000, ZM variants add 3000. +func wkbGeomTypeCode(g geom.T) int32 { + var base int32 + switch g.(type) { + case *geom.Point: + base = 1 + case *geom.LineString: + base = 2 + case *geom.Polygon: + base = 3 + case *geom.MultiPoint: + base = 4 + case *geom.MultiLineString: + base = 5 + case *geom.MultiPolygon: + base = 6 + case *geom.GeometryCollection: + base = 7 + default: + return 0 + } + switch g.Layout() { + case geom.XYZ: + return base + 1000 + case geom.XYM: + return base + 2000 + case geom.XYZM: + return base + 3000 + } + return base +} diff --git a/vendor/github.com/parquet-go/parquet-go/go.tools.mod b/vendor/github.com/parquet-go/parquet-go/go.tools.mod new file mode 100644 index 000000000..b4bf5c3fd --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/go.tools.mod @@ -0,0 +1,25 @@ +module github.com/parquet-go/parquet-go + +go 1.25.1 + +tool golang.org/x/tools/gopls/internal/analysis/modernize/cmd/modernize + +require ( + github.com/andybalholm/brotli v1.1.1 + github.com/google/uuid v1.6.0 + github.com/hexops/gotextdiff v1.0.3 + github.com/klauspost/compress v1.18.0 + github.com/parquet-go/bitpack v0.0.0-20251026130316-7709569977d0 + github.com/parquet-go/jsonlite v0.1.0 + github.com/pierrec/lz4/v4 v4.1.22 + github.com/twpayne/go-geom v1.6.1 + golang.org/x/sys v0.37.0 + google.golang.org/protobuf v1.36.5 +) + +require ( + golang.org/x/mod v0.23.0 // indirect + golang.org/x/sync v0.11.0 // indirect + golang.org/x/tools v0.30.1-0.20250221230316-5055f70f240c // indirect + golang.org/x/tools/gopls v0.18.1 // indirect +) diff --git a/vendor/github.com/parquet-go/parquet-go/go.tools.sum b/vendor/github.com/parquet-go/parquet-go/go.tools.sum new file mode 100644 index 000000000..58a4cf2df --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/go.tools.sum @@ -0,0 +1,38 @@ +github.com/DATA-DOG/go-sqlmock v1.5.2 h1:OcvFkGmslmlZibjAjaHm3L//6LiuBgolP7OputlJIzU= +github.com/DATA-DOG/go-sqlmock v1.5.2/go.mod h1:88MAG/4G7SMwSE3CeA0ZKzrT5CiOU3OJ+JlNzwDqpNU= +github.com/alecthomas/assert/v2 v2.10.0 h1:jjRCHsj6hBJhkmhznrCzoNpbA3zqy0fYiUcYZP/GkPY= +github.com/alecthomas/assert/v2 v2.10.0/go.mod h1:Bze95FyfUr7x34QZrjL+XP+0qgp/zg8yS+TtBj1WA3k= +github.com/alecthomas/repr v0.4.0 h1:GhI2A8MACjfegCPVq9f1FLvIBS+DrQ2KQBFZP1iFzXc= +github.com/alecthomas/repr v0.4.0/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4= +github.com/andybalholm/brotli v1.1.1 h1:PR2pgnyFznKEugtsUo0xLdDop5SKXd5Qf5ysW+7XdTA= +github.com/andybalholm/brotli v1.1.1/go.mod h1:05ib4cKhjx3OQYUY22hTVd34Bc8upXjOLL2rKwwZBoA= +github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM= +github.com/hexops/gotextdiff v1.0.3/go.mod h1:pSWU5MAI3yDq+fZBTazCSJysOMbxWL1BSow5/V2vxeg= +github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo= +github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ= +github.com/parquet-go/bitpack v0.0.0-20251026130316-7709569977d0 h1:WDJJwDZk8KYIFVkyvQJE7q6UGwNSre4pzw+bpJ0osIY= +github.com/parquet-go/bitpack v0.0.0-20251026130316-7709569977d0/go.mod h1:Ef5FEKAqlyHECov7Z3RKOq8Ud9Vr8cbGNoCF56KEKaE= +github.com/parquet-go/jsonlite v0.1.0 h1:hVHIMaDBeKkruXcrP0K90vu528z7+tCCYpKWUrR/m9Y= +github.com/parquet-go/jsonlite v0.1.0/go.mod h1:SPIV4YIXv+4eKxONWRe4mVJCw/D/7v0SJju8wCyDnOc= +github.com/pierrec/lz4/v4 v4.1.22 h1:cKFw6uJDK+/gfw5BcDL0JL5aBsAFdsIT18eRtLj7VIU= +github.com/pierrec/lz4/v4 v4.1.22/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= +github.com/twpayne/go-geom v1.6.1 h1:iLE+Opv0Ihm/ABIcvQFGIiFBXd76oBIar9drAwHFhR4= +github.com/twpayne/go-geom v1.6.1/go.mod h1:Kr+Nly6BswFsKM5sd31YaoWS5PeDDH2NftJTK7Gd028= +github.com/xyproto/randomstring v1.0.5 h1:YtlWPoRdgMu3NZtP45drfy1GKoojuR7hmRcnhZqKjWU= +github.com/xyproto/randomstring v1.0.5/go.mod h1:rgmS5DeNXLivK7YprL0pY+lTuhNQW3iGxZ18UQApw/E= +golang.org/x/mod v0.23.0 h1:Zb7khfcRGKk+kqfxFaP5tZqCnDZMjC5VtUBs87Hr6QM= +golang.org/x/mod v0.23.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY= +golang.org/x/sync v0.11.0 h1:GGz8+XQP4FvTTrjZPzNKTMFtSXH80RAzG+5ghFPgK9w= +golang.org/x/sync v0.11.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sys v0.37.0 h1:fdNQudmxPjkdUTPnLn5mdQv7Zwvbvpaxqs831goi9kQ= +golang.org/x/sys v0.37.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= +golang.org/x/tools v0.30.1-0.20250221230316-5055f70f240c h1:Ja/5gV5a9Vvho3p2NC/T2TtxhHjrWS/2DvCKMvA0a+Y= +golang.org/x/tools v0.30.1-0.20250221230316-5055f70f240c/go.mod h1:c347cR/OJfw5TI+GfX7RUPNMdDRRbjvYTS0jPyvsVtY= +golang.org/x/tools/gopls v0.18.1 h1:2xJBNzdImS5u/kV/ZzqDLSvlBSeZX+pWY9uKVP7Pask= +golang.org/x/tools/gopls v0.18.1/go.mod h1:UdNu0zeGjkmjL9L20QDszXu9tP2798pUIHC980kOBrI= +google.golang.org/protobuf v1.36.5 h1:tPhr+woSbjfYvY6/GPufUoYizxw1cF/yFoxJ2fmpwlM= +google.golang.org/protobuf v1.36.5/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= diff --git a/vendor/github.com/parquet-go/parquet-go/hashprobe/aeshash/aeshash.go b/vendor/github.com/parquet-go/parquet-go/hashprobe/aeshash/aeshash.go new file mode 100644 index 000000000..4e1018a01 --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/hashprobe/aeshash/aeshash.go @@ -0,0 +1,21 @@ +// Package aeshash implements hashing functions derived from the Go runtime's +// internal hashing based on the support of AES encryption in CPU instructions. +// +// On architecture where the CPU does not provide instructions for AES +// encryption, the aeshash.Enabled function always returns false, and attempting +// to call any other function will trigger a panic. +package aeshash + +import "github.com/parquet-go/parquet-go/sparse" + +func MultiHash32(hashes []uintptr, values []uint32, seed uintptr) { + MultiHashUint32Array(hashes, sparse.MakeUint32Array(values), seed) +} + +func MultiHash64(hashes []uintptr, values []uint64, seed uintptr) { + MultiHashUint64Array(hashes, sparse.MakeUint64Array(values), seed) +} + +func MultiHash128(hashes []uintptr, values [][16]byte, seed uintptr) { + MultiHashUint128Array(hashes, sparse.MakeUint128Array(values), seed) +} diff --git a/vendor/github.com/parquet-go/parquet-go/hashprobe/aeshash/aeshash_amd64.go b/vendor/github.com/parquet-go/parquet-go/hashprobe/aeshash/aeshash_amd64.go new file mode 100644 index 000000000..8b6ee2129 --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/hashprobe/aeshash/aeshash_amd64.go @@ -0,0 +1,60 @@ +//go:build !purego + +package aeshash + +import ( + "math/rand" + "unsafe" + + "golang.org/x/sys/cpu" + + "github.com/parquet-go/parquet-go/sparse" +) + +// hashRandomBytes is 48 since this is what the assembly code depends on. +const hashRandomBytes = 48 + +var aeskeysched [hashRandomBytes]byte + +func init() { + for _, v := range aeskeysched { + if v != 0 { + // aeskeysched was initialized somewhere else (e.g. tests), so we + // can skip initialization. No synchronization is needed since init + // functions are called sequentially in a single goroutine (see + // https://go.dev/ref/spec#Package_initialization). + return + } + } + + key := (*[hashRandomBytes / 8]uint64)(unsafe.Pointer(&aeskeysched)) + for i := range key { + key[i] = rand.Uint64() + } +} + +// Enabled returns true if AES hash is available on the system. +// +// The function uses the same logic than the Go runtime since we depend on +// the AES hash state being initialized. +// +// See https://go.dev/src/runtime/alg.go +func Enabled() bool { return cpu.X86.HasAES && cpu.X86.HasSSSE3 && cpu.X86.HasSSE41 } + +//go:noescape +func Hash32(value uint32, seed uintptr) uintptr + +//go:noescape +func Hash64(value uint64, seed uintptr) uintptr + +//go:noescape +func Hash128(value [16]byte, seed uintptr) uintptr + +//go:noescape +func MultiHashUint32Array(hashes []uintptr, values sparse.Uint32Array, seed uintptr) + +//go:noescape +func MultiHashUint64Array(hashes []uintptr, values sparse.Uint64Array, seed uintptr) + +//go:noescape +func MultiHashUint128Array(hashes []uintptr, values sparse.Uint128Array, seed uintptr) diff --git a/vendor/github.com/parquet-go/parquet-go/hashprobe/aeshash/aeshash_amd64.s b/vendor/github.com/parquet-go/parquet-go/hashprobe/aeshash/aeshash_amd64.s new file mode 100644 index 000000000..ad6812aeb --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/hashprobe/aeshash/aeshash_amd64.s @@ -0,0 +1,155 @@ +//go:build !purego + +#include "textflag.h" + +// func Hash32(value uint32, seed uintptr) uintptr +TEXT ·Hash32(SB), NOSPLIT, $0-24 + MOVL value+0(FP), AX + MOVQ seed+8(FP), BX + + MOVOU ·aeskeysched+0(SB), X1 + MOVOU ·aeskeysched+16(SB), X2 + MOVOU ·aeskeysched+32(SB), X3 + + MOVQ BX, X0 + PINSRD $2, AX, X0 + + AESENC X1, X0 + AESENC X2, X0 + AESENC X3, X0 + + MOVQ X0, ret+16(FP) + RET + +// func Hash64(value uint64, seed uintptr) uintptr +TEXT ·Hash64(SB), NOSPLIT, $0-24 + MOVQ value+0(FP), AX + MOVQ seed+8(FP), BX + + MOVOU ·aeskeysched+0(SB), X1 + MOVOU ·aeskeysched+16(SB), X2 + MOVOU ·aeskeysched+32(SB), X3 + + MOVQ BX, X0 + PINSRQ $1, AX, X0 + + AESENC X1, X0 + AESENC X2, X0 + AESENC X3, X0 + + MOVQ X0, ret+16(FP) + RET + +// func Hash128(value [16]byte, seed uintptr) uintptr +TEXT ·Hash128(SB), NOSPLIT, $0-32 + LEAQ value+0(FP), AX + MOVQ seed+16(FP), BX + MOVQ $16, CX + + MOVQ BX, X0 // 64 bits of per-table hash seed + PINSRW $4, CX, X0 // 16 bits of length + PSHUFHW $0, X0, X0 // repeat length 4 times total + PXOR ·aeskeysched(SB), X0 // xor in per-process seed + AESENC X0, X0 // scramble seed + + MOVOU (AX), X1 + PXOR X0, X1 + AESENC X1, X1 + AESENC X1, X1 + AESENC X1, X1 + + MOVQ X1, ret+24(FP) + RET + +// func MultiHashUint32Array(hashes []uintptr, values sparse.Uint32Array, seed uintptr) +TEXT ·MultiHashUint32Array(SB), NOSPLIT, $0-56 + MOVQ hashes_base+0(FP), AX + MOVQ values_array_ptr+24(FP), BX + MOVQ values_array_len+32(FP), CX + MOVQ values_array_off+40(FP), R8 + MOVQ seed+48(FP), DX + + MOVOU ·aeskeysched+0(SB), X1 + MOVOU ·aeskeysched+16(SB), X2 + MOVOU ·aeskeysched+32(SB), X3 + + XORQ SI, SI + JMP test +loop: + MOVQ DX, X0 + PINSRD $2, (BX), X0 + + AESENC X1, X0 + AESENC X2, X0 + AESENC X3, X0 + + MOVQ X0, (AX)(SI*8) + INCQ SI + ADDQ R8, BX +test: + CMPQ SI, CX + JNE loop + RET + +// func MultiHashUint64Array(hashes []uintptr, values sparse.Uint64Array, seed uintptr) +TEXT ·MultiHashUint64Array(SB), NOSPLIT, $0-56 + MOVQ hashes_base+0(FP), AX + MOVQ values_array_ptr+24(FP), BX + MOVQ values_array_len+32(FP), CX + MOVQ values_array_off+40(FP), R8 + MOVQ seed+48(FP), DX + + MOVOU ·aeskeysched+0(SB), X1 + MOVOU ·aeskeysched+16(SB), X2 + MOVOU ·aeskeysched+32(SB), X3 + + XORQ SI, SI + JMP test +loop: + MOVQ DX, X0 + PINSRQ $1, (BX), X0 + + AESENC X1, X0 + AESENC X2, X0 + AESENC X3, X0 + + MOVQ X0, (AX)(SI*8) + INCQ SI + ADDQ R8, BX +test: + CMPQ SI, CX + JNE loop + RET + +// func MultiHashUint128Array(hashes []uintptr, values sparse.Uint128Array, seed uintptr) +TEXT ·MultiHashUint128Array(SB), NOSPLIT, $0-56 + MOVQ hashes_base+0(FP), AX + MOVQ values_array_ptr+24(FP), BX + MOVQ values_array_len+32(FP), CX + MOVQ values_array_off+40(FP), R8 + MOVQ seed+48(FP), DX + MOVQ $16, DI + + MOVQ DX, X0 + PINSRW $4, DI, X0 + PSHUFHW $0, X0, X0 + PXOR ·aeskeysched(SB), X0 + AESENC X0, X0 + + XORQ SI, SI + JMP test +loop: + MOVOU (BX), X1 + + PXOR X0, X1 + AESENC X1, X1 + AESENC X1, X1 + AESENC X1, X1 + + MOVQ X1, (AX)(SI*8) + INCQ SI + ADDQ R8, BX +test: + CMPQ SI, CX + JNE loop + RET diff --git a/vendor/github.com/parquet-go/parquet-go/hashprobe/aeshash/aeshash_purego.go b/vendor/github.com/parquet-go/parquet-go/hashprobe/aeshash/aeshash_purego.go new file mode 100644 index 000000000..42d367a0d --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/hashprobe/aeshash/aeshash_purego.go @@ -0,0 +1,29 @@ +//go:build purego || !amd64 + +package aeshash + +import "github.com/parquet-go/parquet-go/sparse" + +// Enabled always returns false since we assume that AES instructions are not +// available by default. +func Enabled() bool { return false } + +const unsupported = "BUG: AES hash is not available on this platform" + +func Hash32(value uint32, seed uintptr) uintptr { panic(unsupported) } + +func Hash64(value uint64, seed uintptr) uintptr { panic(unsupported) } + +func Hash128(value [16]byte, seed uintptr) uintptr { panic(unsupported) } + +func MultiHashUint32Array(hashes []uintptr, values sparse.Uint32Array, seed uintptr) { + panic(unsupported) +} + +func MultiHashUint64Array(hashes []uintptr, values sparse.Uint64Array, seed uintptr) { + panic(unsupported) +} + +func MultiHashUint128Array(hashes []uintptr, values sparse.Uint128Array, seed uintptr) { + panic(unsupported) +} diff --git a/vendor/github.com/parquet-go/parquet-go/hashprobe/hashprobe.go b/vendor/github.com/parquet-go/parquet-go/hashprobe/hashprobe.go new file mode 100644 index 000000000..1c81b3285 --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/hashprobe/hashprobe.go @@ -0,0 +1,783 @@ +// Package hashprobe provides implementations of probing tables for various +// data types. +// +// Probing tables are specialized hash tables supporting only a single +// "probing" operation which behave like a "lookup or insert". When a key +// is probed, either its value is retrieved if it already existed in the table, +// or it is inserted and assigned its index in the insert sequence as value. +// +// Values are represented as signed 32 bits integers, which means that probing +// tables defined in this package may contain at most 2^31-1 entries. +// +// Probing tables have a method named Probe with the following signature: +// +// func (t *Int64Table) Probe(keys []int64, values []int32) int { +// ... +// } +// +// The method takes an array of keys to probe as first argument, an array of +// values where the indexes of each key will be written as second argument, and +// returns the number of keys that were inserted during the call. +// +// Applications that need to determine which keys were inserted can capture the +// length of the probing table prior to the call, and scan the list of values +// looking for indexes greater or equal to the length of the table before the +// call. +package hashprobe + +import ( + cryptoRand "crypto/rand" + "encoding/binary" + "math" + "math/bits" + "math/rand" + "sync" + + "github.com/parquet-go/bitpack/unsafecast" + "github.com/parquet-go/parquet-go/hashprobe/aeshash" + "github.com/parquet-go/parquet-go/hashprobe/wyhash" + "github.com/parquet-go/parquet-go/sparse" +) + +const ( + // Number of probes tested per iteration. This parameter balances between + // the amount of memory allocated on the stack to hold the computed hashes + // of the keys being probed, and amortizing the baseline cost of the probing + // algorithm. + // + // The larger the value, the more memory is required, but lower the baseline + // cost will be. + // + // We chose a value that is somewhat large, resulting in reserving 2KiB of + // stack but mostly erasing the baseline cost. + probesPerLoop = 256 +) + +var ( + prngSeed [8]byte + prngMutex sync.Mutex + prngSource rand.Source64 +) + +func init() { + _, err := cryptoRand.Read(prngSeed[:]) + if err != nil { + panic("cannot seed random number generator from system source: " + err.Error()) + } + seed := int64(binary.LittleEndian.Uint64(prngSeed[:])) + prngSource = rand.NewSource(seed).(rand.Source64) +} + +func tableSizeAndMaxLen(groupSize, numValues int, maxLoad float64) (size, maxLen int) { + n := int(math.Ceil((1 / maxLoad) * float64(numValues))) + size = nextPowerOf2((n + (groupSize - 1)) / groupSize) + maxLen = int(math.Ceil(maxLoad * float64(groupSize*size))) + return +} + +func nextPowerOf2(n int) int { + return 1 << (64 - bits.LeadingZeros64(uint64(n-1))) +} + +func randSeed() uintptr { + prngMutex.Lock() + defer prngMutex.Unlock() + return uintptr(prngSource.Uint64()) +} + +type Int32Table struct{ table32 } + +func NewInt32Table(cap int, maxLoad float64) *Int32Table { + return &Int32Table{makeTable32(cap, maxLoad)} +} + +func (t *Int32Table) Reset() { t.reset() } + +func (t *Int32Table) Len() int { return t.len } + +func (t *Int32Table) Cap() int { return t.size() } + +func (t *Int32Table) Probe(keys, values []int32) int { + return t.probe(unsafecast.Slice[uint32](keys), values) +} + +func (t *Int32Table) ProbeArray(keys sparse.Int32Array, values []int32) int { + return t.probeArray(keys.Uint32Array(), values) +} + +type Float32Table struct{ table32 } + +func NewFloat32Table(cap int, maxLoad float64) *Float32Table { + return &Float32Table{makeTable32(cap, maxLoad)} +} + +func (t *Float32Table) Reset() { t.reset() } + +func (t *Float32Table) Len() int { return t.len } + +func (t *Float32Table) Cap() int { return t.size() } + +func (t *Float32Table) Probe(keys []float32, values []int32) int { + return t.probe(unsafecast.Slice[uint32](keys), values) +} + +func (t *Float32Table) ProbeArray(keys sparse.Float32Array, values []int32) int { + return t.probeArray(keys.Uint32Array(), values) +} + +type Uint32Table struct{ table32 } + +func NewUint32Table(cap int, maxLoad float64) *Uint32Table { + return &Uint32Table{makeTable32(cap, maxLoad)} +} + +func (t *Uint32Table) Reset() { t.reset() } + +func (t *Uint32Table) Len() int { return t.len } + +func (t *Uint32Table) Cap() int { return t.size() } + +func (t *Uint32Table) Probe(keys []uint32, values []int32) int { + return t.probe(keys, values) +} + +func (t *Uint32Table) ProbeArray(keys sparse.Uint32Array, values []int32) int { + return t.probeArray(keys, values) +} + +// table32 is the generic implementation of probing tables for 32 bit types. +// +// The table uses the following memory layout: +// +// [group 0][group 1][...][group N] +// +// Each group contains up to 7 key/value pairs, and is exactly 64 bytes in size, +// which allows it to fit within a single cache line, and ensures that probes +// can be performed with a single memory load per key. +// +// Groups fill up by appending new entries to the keys and values arrays. When a +// group is full, the probe checks the next group. +// +// https://en.wikipedia.org/wiki/Linear_probing +type table32 struct { + len int + maxLen int + maxLoad float64 + seed uintptr + table []table32Group +} + +const table32GroupSize = 7 + +type table32Group struct { + keys [table32GroupSize]uint32 + values [table32GroupSize]uint32 + bits uint32 + _ uint32 +} + +func makeTable32(cap int, maxLoad float64) (t table32) { + if maxLoad < 0 || maxLoad > 1 { + panic("max load of probing table must be a value between 0 and 1") + } + if cap < table32GroupSize { + cap = table32GroupSize + } + t.init(cap, maxLoad) + return t +} + +func (t *table32) size() int { + return table32GroupSize * len(t.table) +} + +func (t *table32) init(cap int, maxLoad float64) { + size, maxLen := tableSizeAndMaxLen(table32GroupSize, cap, maxLoad) + *t = table32{ + maxLen: maxLen, + maxLoad: maxLoad, + seed: randSeed(), + table: make([]table32Group, size), + } +} + +func (t *table32) grow(totalValues int) { + tmp := table32{} + tmp.init(totalValues, t.maxLoad) + tmp.len = t.len + + hashes := make([]uintptr, table32GroupSize) + modulo := uintptr(len(tmp.table)) - 1 + + for i := range t.table { + g := &t.table[i] + n := bits.OnesCount32(g.bits) + + if aeshash.Enabled() { + aeshash.MultiHash32(hashes[:n], g.keys[:n], tmp.seed) + } else { + wyhash.MultiHash32(hashes[:n], g.keys[:n], tmp.seed) + } + + for j, hash := range hashes[:n] { + for { + group := &tmp.table[hash&modulo] + + if n := bits.OnesCount32(group.bits); n < table32GroupSize { + group.bits = (group.bits << 1) | 1 + group.keys[n] = g.keys[j] + group.values[n] = g.values[j] + break + } + + hash++ + } + } + } + + *t = tmp +} + +func (t *table32) reset() { + t.len = 0 + + for i := range t.table { + t.table[i] = table32Group{} + } +} + +func (t *table32) probe(keys []uint32, values []int32) int { + return t.probeArray(sparse.MakeUint32Array(keys), values) +} + +func (t *table32) probeArray(keys sparse.Uint32Array, values []int32) int { + numKeys := keys.Len() + + if totalValues := t.len + numKeys; totalValues > t.maxLen { + t.grow(totalValues) + } + + var hashes [probesPerLoop]uintptr + var baseLength = t.len + var useAesHash = aeshash.Enabled() + + _ = values[:numKeys] + + for i := 0; i < numKeys; { + j := len(hashes) + i + n := len(hashes) + + if j > numKeys { + j = numKeys + n = numKeys - i + } + + k := keys.Slice(i, j) + v := values[i:j:j] + h := hashes[:n:n] + + if useAesHash { + aeshash.MultiHashUint32Array(h, k, t.seed) + } else { + wyhash.MultiHashUint32Array(h, k, t.seed) + } + + t.len = multiProbe32(t.table, t.len, h, k, v) + i = j + } + + return t.len - baseLength +} + +func multiProbe32Default(table []table32Group, numKeys int, hashes []uintptr, keys sparse.Uint32Array, values []int32) int { + modulo := uintptr(len(table)) - 1 + + for i, hash := range hashes { + key := keys.Index(i) + for { + group := &table[hash&modulo] + index := table32GroupSize + value := int32(0) + + for j, k := range group.keys { + if k == key { + index = j + break + } + } + + if n := bits.OnesCount32(group.bits); index < n { + value = int32(group.values[index]) + } else { + if n == table32GroupSize { + hash++ + continue + } + + value = int32(numKeys) + group.bits = (group.bits << 1) | 1 + group.keys[n] = key + group.values[n] = uint32(value) + numKeys++ + } + + values[i] = value + break + } + } + + return numKeys +} + +type Int64Table struct{ table64 } + +func NewInt64Table(cap int, maxLoad float64) *Int64Table { + return &Int64Table{makeTable64(cap, maxLoad)} +} + +func (t *Int64Table) Reset() { t.reset() } + +func (t *Int64Table) Len() int { return t.len } + +func (t *Int64Table) Cap() int { return t.size() } + +func (t *Int64Table) Probe(keys []int64, values []int32) int { + return t.probe(unsafecast.Slice[uint64](keys), values) +} + +func (t *Int64Table) ProbeArray(keys sparse.Int64Array, values []int32) int { + return t.probeArray(keys.Uint64Array(), values) +} + +type Float64Table struct{ table64 } + +func NewFloat64Table(cap int, maxLoad float64) *Float64Table { + return &Float64Table{makeTable64(cap, maxLoad)} +} + +func (t *Float64Table) Reset() { t.reset() } + +func (t *Float64Table) Len() int { return t.len } + +func (t *Float64Table) Cap() int { return t.size() } + +func (t *Float64Table) Probe(keys []float64, values []int32) int { + return t.probe(unsafecast.Slice[uint64](keys), values) +} + +func (t *Float64Table) ProbeArray(keys sparse.Float64Array, values []int32) int { + return t.probeArray(keys.Uint64Array(), values) +} + +type Uint64Table struct{ table64 } + +func NewUint64Table(cap int, maxLoad float64) *Uint64Table { + return &Uint64Table{makeTable64(cap, maxLoad)} +} + +func (t *Uint64Table) Reset() { t.reset() } + +func (t *Uint64Table) Len() int { return t.len } + +func (t *Uint64Table) Cap() int { return t.size() } + +func (t *Uint64Table) Probe(keys []uint64, values []int32) int { + return t.probe(keys, values) +} + +func (t *Uint64Table) ProbeArray(keys sparse.Uint64Array, values []int32) int { + return t.probeArray(keys, values) +} + +// table64 is the generic implementation of probing tables for 64 bit types. +// +// The table uses a layout similar to the one documented on the table for 32 bit +// keys (see table32). Each group holds up to 4 key/value pairs (instead of 7 +// like table32) so that each group fits in a single CPU cache line. This table +// version has a bit lower memory density, with ~23% of table memory being used +// for padding. +// +// Technically we could hold up to 5 entries per group and still fit within the +// 64 bytes of a CPU cache line; on x86 platforms, AVX2 registers can only hold +// four 64 bit values, we would need twice as many instructions per probe if the +// groups were holding 5 values. The trade off of memory for compute efficiency +// appeared to be the right choice at the time. +type table64 struct { + len int + maxLen int + maxLoad float64 + seed uintptr + table []table64Group +} + +const table64GroupSize = 4 + +type table64Group struct { + keys [table64GroupSize]uint64 + values [table64GroupSize]uint32 + bits uint32 + _ uint32 + _ uint32 + _ uint32 +} + +func makeTable64(cap int, maxLoad float64) (t table64) { + if maxLoad < 0 || maxLoad > 1 { + panic("max load of probing table must be a value between 0 and 1") + } + if cap < table64GroupSize { + cap = table64GroupSize + } + t.init(cap, maxLoad) + return t +} + +func (t *table64) size() int { + return table64GroupSize * len(t.table) +} + +func (t *table64) init(cap int, maxLoad float64) { + size, maxLen := tableSizeAndMaxLen(table64GroupSize, cap, maxLoad) + *t = table64{ + maxLen: maxLen, + maxLoad: maxLoad, + seed: randSeed(), + table: make([]table64Group, size), + } +} + +func (t *table64) grow(totalValues int) { + tmp := table64{} + tmp.init(totalValues, t.maxLoad) + tmp.len = t.len + + hashes := make([]uintptr, table64GroupSize) + modulo := uintptr(len(tmp.table)) - 1 + + for i := range t.table { + g := &t.table[i] + n := bits.OnesCount32(g.bits) + + if aeshash.Enabled() { + aeshash.MultiHash64(hashes[:n], g.keys[:n], tmp.seed) + } else { + wyhash.MultiHash64(hashes[:n], g.keys[:n], tmp.seed) + } + + for j, hash := range hashes[:n] { + for { + group := &tmp.table[hash&modulo] + + if n := bits.OnesCount32(group.bits); n < table64GroupSize { + group.bits = (group.bits << 1) | 1 + group.keys[n] = g.keys[j] + group.values[n] = g.values[j] + break + } + + hash++ + } + } + } + + *t = tmp +} + +func (t *table64) reset() { + t.len = 0 + + for i := range t.table { + t.table[i] = table64Group{} + } +} + +func (t *table64) probe(keys []uint64, values []int32) int { + return t.probeArray(sparse.MakeUint64Array(keys), values) +} + +func (t *table64) probeArray(keys sparse.Uint64Array, values []int32) int { + numKeys := keys.Len() + + if totalValues := t.len + numKeys; totalValues > t.maxLen { + t.grow(totalValues) + } + + var hashes [probesPerLoop]uintptr + var baseLength = t.len + var useAesHash = aeshash.Enabled() + + _ = values[:numKeys] + + for i := 0; i < numKeys; { + j := len(hashes) + i + n := len(hashes) + + if j > numKeys { + j = numKeys + n = numKeys - i + } + + k := keys.Slice(i, j) + v := values[i:j:j] + h := hashes[:n:n] + + if useAesHash { + aeshash.MultiHashUint64Array(h, k, t.seed) + } else { + wyhash.MultiHashUint64Array(h, k, t.seed) + } + + t.len = multiProbe64(t.table, t.len, h, k, v) + i = j + } + + return t.len - baseLength +} + +func multiProbe64Default(table []table64Group, numKeys int, hashes []uintptr, keys sparse.Uint64Array, values []int32) int { + modulo := uintptr(len(table)) - 1 + + for i, hash := range hashes { + key := keys.Index(i) + for { + group := &table[hash&modulo] + index := table64GroupSize + value := int32(0) + + for i, k := range group.keys { + if k == key { + index = i + break + } + } + + if n := bits.OnesCount32(group.bits); index < n { + value = int32(group.values[index]) + } else { + if n == table64GroupSize { + hash++ + continue + } + + value = int32(numKeys) + group.bits = (group.bits << 1) | 1 + group.keys[n] = key + group.values[n] = uint32(value) + numKeys++ + } + + values[i] = value + break + } + } + + return numKeys +} + +type Uint128Table struct{ table128 } + +func NewUint128Table(cap int, maxLoad float64) *Uint128Table { + return &Uint128Table{makeTable128(cap, maxLoad)} +} + +func (t *Uint128Table) Reset() { t.reset() } + +func (t *Uint128Table) Len() int { return t.len } + +func (t *Uint128Table) Cap() int { return t.cap } + +func (t *Uint128Table) Probe(keys [][16]byte, values []int32) int { + return t.probe(keys, values) +} + +func (t *Uint128Table) ProbeArray(keys sparse.Uint128Array, values []int32) int { + return t.probeArray(keys, values) +} + +// table128 is the generic implementation of probing tables for 128 bit types. +// +// This table uses the following memory layout: +// +// [key A][key B][...][value A][value B][...] +// +// The table stores values as their actual value plus one, and uses zero as a +// sentinel to determine whether a slot is occupied. A linear probing strategy +// is used to resolve conflicts. This approach results in at most two memory +// loads for every four keys being tested, since the location of a key and its +// corresponding value will not be contiguous on the same CPU cache line, but +// a cache line can hold four 16 byte keys. +type table128 struct { + len int + cap int + maxLen int + maxLoad float64 + seed uintptr + table []byte +} + +func makeTable128(cap int, maxLoad float64) (t table128) { + if maxLoad < 0 || maxLoad > 1 { + panic("max load of probing table must be a value between 0 and 1") + } + if cap < 8 { + cap = 8 + } + t.init(cap, maxLoad) + return t +} + +func (t *table128) init(cap int, maxLoad float64) { + size, maxLen := tableSizeAndMaxLen(1, cap, maxLoad) + *t = table128{ + cap: size, + maxLen: maxLen, + maxLoad: maxLoad, + seed: randSeed(), + table: make([]byte, 16*size+4*size), + } +} + +func (t *table128) kv() (keys [][16]byte, values []int32) { + i := t.cap * 16 + return unsafecast.Slice[[16]byte](t.table[:i]), unsafecast.Slice[int32](t.table[i:]) +} + +func (t *table128) grow(totalValues int) { + tmp := table128{} + tmp.init(totalValues, t.maxLoad) + tmp.len = t.len + + keys, values := t.kv() + hashes := make([]uintptr, probesPerLoop) + useAesHash := aeshash.Enabled() + + _ = values[:len(keys)] + + for i := 0; i < len(keys); { + j := len(hashes) + i + n := len(hashes) + + if j > len(keys) { + j = len(keys) + n = len(keys) - i + } + + h := hashes[:n:n] + k := keys[i:j:j] + v := values[i:j:j] + + if useAesHash { + aeshash.MultiHash128(h, k, tmp.seed) + } else { + wyhash.MultiHash128(h, k, tmp.seed) + } + + tmp.insert(h, k, v) + i = j + } + + *t = tmp +} + +func (t *table128) insert(hashes []uintptr, keys [][16]byte, values []int32) { + tableKeys, tableValues := t.kv() + modulo := uintptr(t.cap) - 1 + + for i, hash := range hashes { + for { + j := hash & modulo + v := tableValues[j] + + if v == 0 { + tableKeys[j] = keys[i] + tableValues[j] = values[i] + break + } + + hash++ + } + } +} + +func (t *table128) reset() { + t.len = 0 + + for i := range t.table { + t.table[i] = 0 + } +} + +func (t *table128) probe(keys [][16]byte, values []int32) int { + return t.probeArray(sparse.MakeUint128Array(keys), values) +} + +func (t *table128) probeArray(keys sparse.Uint128Array, values []int32) int { + numKeys := keys.Len() + + if totalValues := t.len + numKeys; totalValues > t.maxLen { + t.grow(totalValues) + } + + var hashes [probesPerLoop]uintptr + var baseLength = t.len + var useAesHash = aeshash.Enabled() + + _ = values[:numKeys] + + for i := 0; i < numKeys; { + j := len(hashes) + i + n := len(hashes) + + if j > numKeys { + j = numKeys + n = numKeys - i + } + + k := keys.Slice(i, j) + v := values[i:j:j] + h := hashes[:n:n] + + if useAesHash { + aeshash.MultiHashUint128Array(h, k, t.seed) + } else { + wyhash.MultiHashUint128Array(h, k, t.seed) + } + + t.len = multiProbe128(t.table, t.cap, t.len, h, k, v) + i = j + } + + return t.len - baseLength +} + +func multiProbe128Default(table []byte, tableCap, tableLen int, hashes []uintptr, keys sparse.Uint128Array, values []int32) int { + modulo := uintptr(tableCap) - 1 + offset := uintptr(tableCap) * 16 + tableKeys := unsafecast.Slice[[16]byte](table[:offset]) + tableValues := unsafecast.Slice[int32](table[offset:]) + + for i, hash := range hashes { + key := keys.Index(i) + for { + j := hash & modulo + v := tableValues[j] + + if v == 0 { + values[i] = int32(tableLen) + tableLen++ + tableKeys[j] = key + tableValues[j] = int32(tableLen) + break + } + + if key == tableKeys[j] { + values[i] = v - 1 + break + } + + hash++ + } + } + + return tableLen +} diff --git a/vendor/github.com/parquet-go/parquet-go/hashprobe/hashprobe_amd64.go b/vendor/github.com/parquet-go/parquet-go/hashprobe/hashprobe_amd64.go new file mode 100644 index 000000000..8802d151a --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/hashprobe/hashprobe_amd64.go @@ -0,0 +1,38 @@ +//go:build !purego + +package hashprobe + +import ( + "github.com/parquet-go/parquet-go/sparse" + "golang.org/x/sys/cpu" +) + +//go:noescape +func multiProbe32AVX2(table []table32Group, numKeys int, hashes []uintptr, keys sparse.Uint32Array, values []int32) int + +//go:noescape +func multiProbe64AVX2(table []table64Group, numKeys int, hashes []uintptr, keys sparse.Uint64Array, values []int32) int + +//go:noescape +func multiProbe128SSE2(table []byte, tableCap, tableLen int, hashes []uintptr, keys sparse.Uint128Array, values []int32) int + +func multiProbe32(table []table32Group, numKeys int, hashes []uintptr, keys sparse.Uint32Array, values []int32) int { + if cpu.X86.HasAVX2 { + return multiProbe32AVX2(table, numKeys, hashes, keys, values) + } + return multiProbe32Default(table, numKeys, hashes, keys, values) +} + +func multiProbe64(table []table64Group, numKeys int, hashes []uintptr, keys sparse.Uint64Array, values []int32) int { + if cpu.X86.HasAVX2 { + return multiProbe64AVX2(table, numKeys, hashes, keys, values) + } + return multiProbe64Default(table, numKeys, hashes, keys, values) +} + +func multiProbe128(table []byte, tableCap, tableLen int, hashes []uintptr, keys sparse.Uint128Array, values []int32) int { + if cpu.X86.HasSSE2 { + return multiProbe128SSE2(table, tableCap, tableLen, hashes, keys, values) + } + return multiProbe128Default(table, tableCap, tableLen, hashes, keys, values) +} diff --git a/vendor/github.com/parquet-go/parquet-go/hashprobe/hashprobe_amd64.s b/vendor/github.com/parquet-go/parquet-go/hashprobe/hashprobe_amd64.s new file mode 100644 index 000000000..5c5c0c02f --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/hashprobe/hashprobe_amd64.s @@ -0,0 +1,197 @@ +//go:build !purego + +#include "textflag.h" + +// This version of the probing algorithm for 32 bit keys takes advantage of +// the memory layout of table groups and SIMD instructions to accelerate the +// probing operations. +// +// The first 32 bytes of a table group contain the bit mask indicating which +// slots are in use, and the array of keys, which fits into a single vector +// register (YMM) and can be loaded and tested with a single instruction. +// +// A first version of the table group used the number of keys held in the +// group instead of a bit mask, which required the probing operation to +// reconstruct the bit mask during the lookup operation in order to identify +// which elements of the VPCMPEQD result should be retained. The extra CPU +// instructions used to reconstruct the bit mask had a measurable overhead. +// By holding the bit mask in the data structure, we can determine the number +// of keys in a group using the POPCNT instruction, and avoid recomputing the +// mask during lookups. +// +// func multiProbe32AVX2(table []table32Group, numKeys int, hashes []uintptr, keys sparse.Uint32Array, values []int32) int +TEXT ·multiProbe32AVX2(SB), NOSPLIT, $0-112 + MOVQ table_base+0(FP), AX + MOVQ table_len+8(FP), BX + MOVQ numKeys+24(FP), CX + MOVQ hashes_base+32(FP), DX + MOVQ hashes_len+40(FP), DI + MOVQ keys_array_ptr+56(FP), R8 + MOVQ keys_array_off+72(FP), R15 + MOVQ values_base+80(FP), R9 + DECQ BX // modulo = len(table) - 1 + + XORQ SI, SI + JMP test +loop: + MOVQ (DX)(SI*8), R10 // hash + VPBROADCASTD (R8), Y0 // [key] +probe: + MOVQ R10, R11 + ANDQ BX, R11 // hash & modulo + SHLQ $6, R11 // x 64 (size of table32Group) + LEAQ (AX)(R11*1), R12 + + VMOVDQU (R12), Y1 + VPCMPEQD Y0, Y1, Y2 + VMOVMSKPS Y2, R11 + MOVL 56(R12), R13 + TESTL R11, R13 + JZ insert + + TZCNTL R11, R13 + MOVL 28(R12)(R13*4), R14 +next: + MOVL R14, (R9)(SI*4) + INCQ SI + ADDQ R15, R8 +test: + CMPQ SI, DI + JNE loop + MOVQ CX, ret+104(FP) + VZEROUPPER + RET +insert: + CMPL R13, $0b1111111 + JE probeNextGroup + + MOVL R13, R11 + POPCNTL R13, R13 + MOVQ X0, R14 // key + SHLL $1, R11 + ORL $1, R11 + MOVL R11, 56(R12) // group.len = (group.len << 1) | 1 + MOVL R14, (R12)(R13*4) // group.keys[i] = key + MOVL CX, 28(R12)(R13*4) // group.values[i] = value + MOVL CX, R14 + INCL CX + JMP next +probeNextGroup: + INCQ R10 + JMP probe + +// func multiProbe64AVX2(table []table64Group, numKeys int, hashes []uintptr, keys sparse.Uint64Array, values []int32) int +TEXT ·multiProbe64AVX2(SB), NOSPLIT, $0-112 + MOVQ table_base+0(FP), AX + MOVQ table_len+8(FP), BX + MOVQ numKeys+24(FP), CX + MOVQ hashes_base+32(FP), DX + MOVQ hashes_len+40(FP), DI + MOVQ keys_array_ptr+56(FP), R8 + MOVQ keys_array_off+72(FP), R15 + MOVQ values_base+80(FP), R9 + DECQ BX // modulo = len(table) - 1 + + XORQ SI, SI + JMP test +loop: + MOVQ (DX)(SI*8), R10 // hash + VPBROADCASTQ (R8), Y0 // [key] +probe: + MOVQ R10, R11 + ANDQ BX, R11 // hash & modulo + SHLQ $6, R11 // x 64 (size of table64Group) + LEAQ (AX)(R11*1), R12 + + VMOVDQU (R12), Y1 + VPCMPEQQ Y0, Y1, Y2 + VMOVMSKPD Y2, R11 + MOVL 48(R12), R13 + TESTL R11, R13 + JZ insert + + TZCNTL R11, R13 + MOVL 32(R12)(R13*4), R14 +next: + MOVL R14, (R9)(SI*4) + INCQ SI + ADDQ R15, R8 +test: + CMPQ SI, DI + JNE loop + MOVQ CX, ret+104(FP) + VZEROUPPER + RET +insert: + CMPL R13, $0b1111 + JE probeNextGroup + + MOVL R13, R11 + POPCNTL R13, R13 + SHLL $1, R11 + ORL $1, R11 + MOVL R11, 48(R12) // group.len = (group.len << 1) | 1 + MOVQ X0, (R12)(R13*8) // group.keys[i] = key + MOVL CX, 32(R12)(R13*4) // group.values[i] = value + MOVL CX, R14 + INCL CX + JMP next +probeNextGroup: + INCQ R10 + JMP probe + +// func multiProbe128SSE2(table []byte, tableCap, tableLen int, hashes []uintptr, keys sparse.Uint128Array, values []int32) int +TEXT ·multiProbe128SSE2(SB), NOSPLIT, $0-120 + MOVQ table_base+0(FP), AX + MOVQ tableCap+24(FP), BX + MOVQ tableLen+32(FP), CX + MOVQ hashes_base+40(FP), DX + MOVQ hashes_len+48(FP), DI + MOVQ keys_array_ptr+64(FP), R8 + MOVQ keys_array_off+80(FP), R15 + MOVQ values_base+88(FP), R9 + + MOVQ BX, R10 + SHLQ $4, R10 + LEAQ (AX)(R10*1), R10 + DECQ BX // modulo = tableCap - 1 + + XORQ SI, SI + JMP test +loop: + MOVQ (DX)(SI*8), R11 // hash + MOVOU (R8), X0 // key +probe: + MOVQ R11, R12 + ANDQ BX, R12 + + MOVL (R10)(R12*4), R14 + CMPL R14, $0 + JE insert + + SHLQ $4, R12 + MOVOU (AX)(R12*1), X1 + PCMPEQL X0, X1 + MOVMSKPS X1, R13 + CMPL R13, $0b1111 + JE next + + INCQ R11 + JMP probe +next: + DECL R14 + MOVL R14, (R9)(SI*4) + INCQ SI + ADDQ R15, R8 +test: + CMPQ SI, DI + JNE loop + MOVQ CX, ret+112(FP) + RET +insert: + INCL CX + MOVL CX, (R10)(R12*4) + MOVL CX, R14 + SHLQ $4, R12 + MOVOU X0, (AX)(R12*1) + JMP next diff --git a/vendor/github.com/parquet-go/parquet-go/hashprobe/hashprobe_purego.go b/vendor/github.com/parquet-go/parquet-go/hashprobe/hashprobe_purego.go new file mode 100644 index 000000000..5afb40c74 --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/hashprobe/hashprobe_purego.go @@ -0,0 +1,19 @@ +//go:build purego || !amd64 + +package hashprobe + +import ( + "github.com/parquet-go/parquet-go/sparse" +) + +func multiProbe32(table []table32Group, numKeys int, hashes []uintptr, keys sparse.Uint32Array, values []int32) int { + return multiProbe32Default(table, numKeys, hashes, keys, values) +} + +func multiProbe64(table []table64Group, numKeys int, hashes []uintptr, keys sparse.Uint64Array, values []int32) int { + return multiProbe64Default(table, numKeys, hashes, keys, values) +} + +func multiProbe128(table []byte, tableCap, tableLen int, hashes []uintptr, keys sparse.Uint128Array, values []int32) int { + return multiProbe128Default(table, tableCap, tableLen, hashes, keys, values) +} diff --git a/vendor/github.com/parquet-go/parquet-go/hashprobe/wyhash/wyhash.go b/vendor/github.com/parquet-go/parquet-go/hashprobe/wyhash/wyhash.go new file mode 100644 index 000000000..457bd8e11 --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/hashprobe/wyhash/wyhash.go @@ -0,0 +1,49 @@ +// Package wyhash implements a hashing algorithm derived from the Go runtime's +// internal hashing fallback, which uses a variation of the wyhash algorithm. +package wyhash + +import ( + "encoding/binary" + "math/bits" + + "github.com/parquet-go/parquet-go/sparse" +) + +const ( + m1 = 0xa0761d6478bd642f + m2 = 0xe7037ed1a0b428db + m3 = 0x8ebc6af09c88c6e3 + m4 = 0x589965cc75374cc3 + m5 = 0x1d8e4e27c47d124f +) + +func mix(a, b uint64) uint64 { + hi, lo := bits.Mul64(a, b) + return hi ^ lo +} + +func Hash32(value uint32, seed uintptr) uintptr { + return uintptr(mix(m5^4, mix(uint64(value)^m2, uint64(value)^uint64(seed)^m1))) +} + +func Hash64(value uint64, seed uintptr) uintptr { + return uintptr(mix(m5^8, mix(value^m2, value^uint64(seed)^m1))) +} + +func Hash128(value [16]byte, seed uintptr) uintptr { + a := binary.LittleEndian.Uint64(value[:8]) + b := binary.LittleEndian.Uint64(value[8:]) + return uintptr(mix(m5^16, mix(a^m2, b^uint64(seed)^m1))) +} + +func MultiHash32(hashes []uintptr, values []uint32, seed uintptr) { + MultiHashUint32Array(hashes, sparse.MakeUint32Array(values), seed) +} + +func MultiHash64(hashes []uintptr, values []uint64, seed uintptr) { + MultiHashUint64Array(hashes, sparse.MakeUint64Array(values), seed) +} + +func MultiHash128(hashes []uintptr, values [][16]byte, seed uintptr) { + MultiHashUint128Array(hashes, sparse.MakeUint128Array(values), seed) +} diff --git a/vendor/github.com/parquet-go/parquet-go/hashprobe/wyhash/wyhash_amd64.go b/vendor/github.com/parquet-go/parquet-go/hashprobe/wyhash/wyhash_amd64.go new file mode 100644 index 000000000..55f499deb --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/hashprobe/wyhash/wyhash_amd64.go @@ -0,0 +1,14 @@ +//go:build !purego + +package wyhash + +import "github.com/parquet-go/parquet-go/sparse" + +//go:noescape +func MultiHashUint32Array(hashes []uintptr, values sparse.Uint32Array, seed uintptr) + +//go:noescape +func MultiHashUint64Array(hashes []uintptr, values sparse.Uint64Array, seed uintptr) + +//go:noescape +func MultiHashUint128Array(hashes []uintptr, values sparse.Uint128Array, seed uintptr) diff --git a/vendor/github.com/parquet-go/parquet-go/hashprobe/wyhash/wyhash_amd64.s b/vendor/github.com/parquet-go/parquet-go/hashprobe/wyhash/wyhash_amd64.s new file mode 100644 index 000000000..7b99879f0 --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/hashprobe/wyhash/wyhash_amd64.s @@ -0,0 +1,118 @@ +//go:build !purego + +#include "textflag.h" + +#define m1 0xa0761d6478bd642f +#define m2 0xe7037ed1a0b428db +#define m3 0x8ebc6af09c88c6e3 +#define m4 0x589965cc75374cc3 +#define m5 0x1d8e4e27c47d124f + +// func MultiHashUint32Array(hashes []uintptr, values sparse.Uint32Array, seed uintptr) +TEXT ·MultiHashUint32Array(SB), NOSPLIT, $0-56 + MOVQ hashes_base+0(FP), R12 + MOVQ values_array_ptr+24(FP), R13 + MOVQ values_array_len+32(FP), R14 + MOVQ values_array_off+40(FP), R15 + MOVQ seed+48(FP), R11 + + MOVQ $m1, R8 + MOVQ $m2, R9 + MOVQ $m5^4, R10 + XORQ R11, R8 + + XORQ SI, SI + JMP test +loop: + MOVL (R13), AX + MOVQ R8, BX + + XORQ AX, BX + XORQ R9, AX + + MULQ BX + XORQ DX, AX + + MULQ R10 + XORQ DX, AX + + MOVQ AX, (R12)(SI*8) + INCQ SI + ADDQ R15, R13 +test: + CMPQ SI, R14 + JNE loop + RET + +// func MultiHashUint64Array(hashes []uintptr, values sparse.Uint64Array, seed uintptr) +TEXT ·MultiHashUint64Array(SB), NOSPLIT, $0-56 + MOVQ hashes_base+0(FP), R12 + MOVQ values_array_ptr+24(FP), R13 + MOVQ values_array_len+32(FP), R14 + MOVQ values_array_off+40(FP), R15 + MOVQ seed+48(FP), R11 + + MOVQ $m1, R8 + MOVQ $m2, R9 + MOVQ $m5^8, R10 + XORQ R11, R8 + + XORQ SI, SI + JMP test +loop: + MOVQ (R13), AX + MOVQ R8, BX + + XORQ AX, BX + XORQ R9, AX + + MULQ BX + XORQ DX, AX + + MULQ R10 + XORQ DX, AX + + MOVQ AX, (R12)(SI*8) + INCQ SI + ADDQ R15, R13 +test: + CMPQ SI, R14 + JNE loop + RET + +// func MultiHashUint128Array(hashes []uintptr, values sparse.Uint128Array, seed uintptr) +TEXT ·MultiHashUint128Array(SB), NOSPLIT, $0-56 + MOVQ hashes_base+0(FP), R12 + MOVQ values_array_ptr+24(FP), R13 + MOVQ values_array_len+32(FP), R14 + MOVQ values_array_off+40(FP), R15 + MOVQ seed+48(FP), R11 + + MOVQ $m1, R8 + MOVQ $m2, R9 + MOVQ $m5^16, R10 + XORQ R11, R8 + + XORQ SI, SI + JMP test +loop: + MOVQ 0(R13), AX + MOVQ 8(R13), DX + MOVQ R8, BX + + XORQ DX, BX + XORQ R9, AX + + MULQ BX + XORQ DX, AX + + MULQ R10 + XORQ DX, AX + + MOVQ AX, (R12)(SI*8) + INCQ SI + ADDQ R15, R13 +test: + CMPQ SI, R14 + JNE loop + RET diff --git a/vendor/github.com/parquet-go/parquet-go/hashprobe/wyhash/wyhash_purego.go b/vendor/github.com/parquet-go/parquet-go/hashprobe/wyhash/wyhash_purego.go new file mode 100644 index 000000000..b5760f7ec --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/hashprobe/wyhash/wyhash_purego.go @@ -0,0 +1,23 @@ +//go:build purego || !amd64 + +package wyhash + +import "github.com/parquet-go/parquet-go/sparse" + +func MultiHashUint32Array(hashes []uintptr, values sparse.Uint32Array, seed uintptr) { + for i := range hashes { + hashes[i] = Hash32(values.Index(i), seed) + } +} + +func MultiHashUint64Array(hashes []uintptr, values sparse.Uint64Array, seed uintptr) { + for i := range hashes { + hashes[i] = Hash64(values.Index(i), seed) + } +} + +func MultiHashUint128Array(hashes []uintptr, values sparse.Uint128Array, seed uintptr) { + for i := range hashes { + hashes[i] = Hash128(values.Index(i), seed) + } +} diff --git a/vendor/github.com/parquet-go/parquet-go/internal/bytealg/broadcast_amd64.go b/vendor/github.com/parquet-go/parquet-go/internal/bytealg/broadcast_amd64.go new file mode 100644 index 000000000..407f1cfc5 --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/internal/bytealg/broadcast_amd64.go @@ -0,0 +1,17 @@ +//go:build !purego + +package bytealg + +//go:noescape +func broadcastAVX2(dst []byte, src byte) + +// Broadcast writes the src value to all bytes of dst. +func Broadcast(dst []byte, src byte) { + if len(dst) >= 8 && hasAVX2 { + broadcastAVX2(dst, src) + } else { + for i := range dst { + dst[i] = src + } + } +} diff --git a/vendor/github.com/parquet-go/parquet-go/internal/bytealg/broadcast_amd64.s b/vendor/github.com/parquet-go/parquet-go/internal/bytealg/broadcast_amd64.s new file mode 100644 index 000000000..241a658e3 --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/internal/bytealg/broadcast_amd64.s @@ -0,0 +1,51 @@ +//go:build !purego + +#include "textflag.h" + +// func broadcastAVX2(dst []byte, src byte) +TEXT ·broadcastAVX2(SB), NOSPLIT, $0-25 + MOVQ dst_base+0(FP), AX + MOVQ dst_len+8(FP), BX + MOVBQZX src+24(FP), CX + + CMPQ BX, $8 + JBE test + + CMPQ BX, $64 + JB init8 + + XORQ SI, SI + MOVQ BX, DX + SHRQ $6, DX + SHLQ $6, DX + MOVQ CX, X0 + VPBROADCASTB X0, Y0 +loop64: + VMOVDQU Y0, (AX)(SI*1) + VMOVDQU Y0, 32(AX)(SI*1) + ADDQ $64, SI + CMPQ SI, DX + JNE loop64 + VMOVDQU Y0, -64(AX)(BX*1) + VMOVDQU Y0, -32(AX)(BX*1) + VZEROUPPER + RET + +init8: + MOVQ $0x0101010101010101, R8 + IMULQ R8, CX +loop8: + MOVQ CX, -8(AX)(BX*1) + SUBQ $8, BX + CMPQ BX, $8 + JAE loop8 + MOVQ CX, (AX) + RET + +loop: + MOVB CX, -1(AX)(BX*1) + DECQ BX +test: + CMPQ BX, $0 + JNE loop + RET diff --git a/vendor/github.com/parquet-go/parquet-go/internal/bytealg/broadcast_purego.go b/vendor/github.com/parquet-go/parquet-go/internal/bytealg/broadcast_purego.go new file mode 100644 index 000000000..fd858e0a2 --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/internal/bytealg/broadcast_purego.go @@ -0,0 +1,9 @@ +//go:build purego || !amd64 + +package bytealg + +func Broadcast(dst []byte, src byte) { + for i := range dst { + dst[i] = src + } +} diff --git a/vendor/github.com/parquet-go/parquet-go/internal/bytealg/bytealg.go b/vendor/github.com/parquet-go/parquet-go/internal/bytealg/bytealg.go new file mode 100644 index 000000000..0698df22f --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/internal/bytealg/bytealg.go @@ -0,0 +1,2 @@ +// Package bytealg contains optimized algorithms operating on byte slices. +package bytealg diff --git a/vendor/github.com/parquet-go/parquet-go/internal/bytealg/bytealg_amd64.go b/vendor/github.com/parquet-go/parquet-go/internal/bytealg/bytealg_amd64.go new file mode 100644 index 000000000..edfcb64c7 --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/internal/bytealg/bytealg_amd64.go @@ -0,0 +1,17 @@ +//go:build !purego + +package bytealg + +import "golang.org/x/sys/cpu" + +var ( + hasAVX2 = cpu.X86.HasAVX2 + // These use AVX-512 instructions in the countByte algorithm relies + // operations that are available in the AVX512BW extension: + // * VPCMPUB + // * KMOVQ + // + // Note that the function will fallback to an AVX2 version if those + // instructions are not available. + hasAVX512Count = cpu.X86.HasAVX512VL && cpu.X86.HasAVX512BW +) diff --git a/vendor/github.com/parquet-go/parquet-go/internal/bytealg/count_amd64.go b/vendor/github.com/parquet-go/parquet-go/internal/bytealg/count_amd64.go new file mode 100644 index 000000000..b41d3d8d9 --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/internal/bytealg/count_amd64.go @@ -0,0 +1,26 @@ +//go:build !purego + +package bytealg + +// This function is similar to using the standard bytes.Count function with a +// one-byte separator. However, the implementation makes use of AVX-512 when +// possible, which yields measurable throughput improvements: +// +// name old time/op new time/op delta +// CountByte 82.5ns ± 0% 43.9ns ± 0% -46.74% (p=0.000 n=10+10) +// +// name old speed new speed delta +// CountByte 49.6GB/s ± 0% 93.2GB/s ± 0% +87.74% (p=0.000 n=10+10) +// +// On systems that do not have AVX-512, the AVX2 version of the code is also +// optimized to make use of multiple register lanes, which gives a bit better +// throughput than the standard library function: +// +// name old time/op new time/op delta +// CountByte 82.5ns ± 0% 61.0ns ± 0% -26.04% (p=0.000 n=10+10) +// +// name old speed new speed delta +// CountByte 49.6GB/s ± 0% 67.1GB/s ± 0% +35.21% (p=0.000 n=10+10) +// +//go:noescape +func Count(data []byte, value byte) int diff --git a/vendor/github.com/parquet-go/parquet-go/internal/bytealg/count_amd64.s b/vendor/github.com/parquet-go/parquet-go/internal/bytealg/count_amd64.s new file mode 100644 index 000000000..1a75a0f79 --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/internal/bytealg/count_amd64.s @@ -0,0 +1,100 @@ +//go:build !purego + +#include "textflag.h" + +// func Count(data []byte, value byte) int +TEXT ·Count(SB), NOSPLIT, $0-40 + MOVQ data_base+0(FP), AX + MOVQ data_len+8(FP), CX + MOVB value+24(FP), BX + MOVQ CX, DX // len + ADDQ AX, CX // end + XORQ SI, SI // count + + CMPQ DX, $256 + JB test + + CMPB ·hasAVX2(SB), $0 + JE test + + XORQ R12, R12 + XORQ R13, R13 + XORQ R14, R14 + XORQ DI, DI + + CMPB ·hasAVX512Count(SB), $0 + JE initAVX2 + + SHRQ $8, DX + SHLQ $8, DX + ADDQ AX, DX + VPBROADCASTB BX, Z0 +loopAVX512: + VMOVDQU64 (AX), Z1 + VMOVDQU64 64(AX), Z2 + VMOVDQU64 128(AX), Z3 + VMOVDQU64 192(AX), Z4 + VPCMPUB $0, Z0, Z1, K1 + VPCMPUB $0, Z0, Z2, K2 + VPCMPUB $0, Z0, Z3, K3 + VPCMPUB $0, Z0, Z4, K4 + KMOVQ K1, R8 + KMOVQ K2, R9 + KMOVQ K3, R10 + KMOVQ K4, R11 + POPCNTQ R8, R8 + POPCNTQ R9, R9 + POPCNTQ R10, R10 + POPCNTQ R11, R11 + ADDQ R8, R12 + ADDQ R9, R13 + ADDQ R10, R14 + ADDQ R11, DI + ADDQ $256, AX + CMPQ AX, DX + JNE loopAVX512 + ADDQ R12, R13 + ADDQ R14, DI + ADDQ R13, SI + ADDQ DI, SI + JMP doneAVX + +initAVX2: + SHRQ $6, DX + SHLQ $6, DX + ADDQ AX, DX + VPBROADCASTB value+24(FP), Y0 +loopAVX2: + VMOVDQU (AX), Y1 + VMOVDQU 32(AX), Y2 + VPCMPEQB Y0, Y1, Y1 + VPCMPEQB Y0, Y2, Y2 + VPMOVMSKB Y1, R12 + VPMOVMSKB Y2, R13 + POPCNTL R12, R12 + POPCNTL R13, R13 + ADDQ R12, R14 + ADDQ R13, DI + ADDQ $64, AX + CMPQ AX, DX + JNE loopAVX2 + ADDQ R14, SI + ADDQ DI, SI + +doneAVX: + VZEROUPPER + JMP test + +loop: + MOVQ SI, DI + INCQ DI + MOVB (AX), R8 + CMPB BX, R8 + CMOVQEQ DI, SI + INCQ AX +test: + CMPQ AX, CX + JNE loop +done: + MOVQ SI, ret+32(FP) + RET diff --git a/vendor/github.com/parquet-go/parquet-go/internal/bytealg/count_purego.go b/vendor/github.com/parquet-go/parquet-go/internal/bytealg/count_purego.go new file mode 100644 index 000000000..de35ee44a --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/internal/bytealg/count_purego.go @@ -0,0 +1,9 @@ +//go:build purego || !amd64 + +package bytealg + +import "bytes" + +func Count(data []byte, value byte) int { + return bytes.Count(data, []byte{value}) +} diff --git a/vendor/github.com/parquet-go/parquet-go/internal/debug/debug.go b/vendor/github.com/parquet-go/parquet-go/internal/debug/debug.go new file mode 100644 index 000000000..8acc28c4c --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/internal/debug/debug.go @@ -0,0 +1,95 @@ +package debug + +import ( + "encoding/hex" + "fmt" + "io" + "log" + "os" + "strconv" + "strings" +) + +func ReaderAt(reader io.ReaderAt, prefix string) io.ReaderAt { + return &ioReaderAt{ + reader: reader, + prefix: prefix, + } +} + +type ioReaderAt struct { + reader io.ReaderAt + prefix string +} + +func (d *ioReaderAt) ReadAt(b []byte, off int64) (int, error) { + n, err := d.reader.ReadAt(b, off) + fmt.Printf("%s: Read(%d) @%d => %d %v \n%s\n", d.prefix, len(b), off, n, err, hex.Dump(b[:n])) + return n, err +} + +func Reader(reader io.Reader, prefix string) io.Reader { + return &ioReader{ + reader: reader, + prefix: prefix, + } +} + +type ioReader struct { + reader io.Reader + prefix string + offset int64 +} + +func (d *ioReader) Read(b []byte) (int, error) { + n, err := d.reader.Read(b) + fmt.Printf("%s: Read(%d) @%d => %d %v \n%s\n", d.prefix, len(b), d.offset, n, err, hex.Dump(b[:n])) + d.offset += int64(n) + return n, err +} + +func Writer(writer io.Writer, prefix string) io.Writer { + return &ioWriter{ + writer: writer, + prefix: prefix, + } +} + +type ioWriter struct { + writer io.Writer + prefix string + offset int64 +} + +func (d *ioWriter) Write(b []byte) (int, error) { + n, err := d.writer.Write(b) + fmt.Printf("%s: Write(%d) @%d => %d %v \n %q\n", d.prefix, len(b), d.offset, n, err, b[:n]) + d.offset += int64(n) + return n, err +} + +var ( + TRACEBUF int +) + +func init() { + for arg := range strings.SplitSeq(os.Getenv("PARQUETGODEBUG"), ",") { + k := arg + v := "" + i := strings.IndexByte(arg, '=') + if i >= 0 { + k, v = arg[:i], arg[i+1:] + } + var err error + switch k { + case "": + // ignore empty entries + case "tracebuf": + if TRACEBUF, err = strconv.Atoi(v); err != nil { + log.Printf("PARQUETGODEBUG: invalid value for tracebuf: %q", v) + } + default: + log.Printf("PARQUETGODEBUG: unrecognized debug option: %q", k) + } + } +} diff --git a/vendor/github.com/parquet-go/parquet-go/internal/debug/finalizer_off.go b/vendor/github.com/parquet-go/parquet-go/internal/debug/finalizer_off.go new file mode 100644 index 000000000..acabe9cdf --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/internal/debug/finalizer_off.go @@ -0,0 +1,6 @@ +//go:build debug + +package debug + +// SetFinalizer is a no-op when the debug tag is specified. +func SetFinalizer(interface{}, interface{}) {} diff --git a/vendor/github.com/parquet-go/parquet-go/internal/debug/finalizer_on.go b/vendor/github.com/parquet-go/parquet-go/internal/debug/finalizer_on.go new file mode 100644 index 000000000..40edbe481 --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/internal/debug/finalizer_on.go @@ -0,0 +1,7 @@ +//go:build !debug + +package debug + +import "runtime" + +func SetFinalizer(obj, finalizer any) { runtime.SetFinalizer(obj, finalizer) } diff --git a/vendor/github.com/parquet-go/parquet-go/internal/memory/buffer.go b/vendor/github.com/parquet-go/parquet-go/internal/memory/buffer.go new file mode 100644 index 000000000..d44981792 --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/internal/memory/buffer.go @@ -0,0 +1,128 @@ +package memory + +import ( + "fmt" + "io" +) + +// Buffer is a buffer that stores bytes in fixed-size chunks and implements +// io.ReadWriteSeeker. +// +// It uses ChunkBuffer[byte] internally for chunk management. +type Buffer struct { + data ChunkBuffer[byte] + seek int64 // absolute offset for read/write operations +} + +func NewBuffer(chunkSize int) *Buffer { + return &Buffer{data: ChunkBufferFor[byte](chunkSize)} +} + +func (b *Buffer) Reset() { + b.data.Reset() + b.seek = 0 +} + +func (b *Buffer) Read(p []byte) (n int, err error) { + if len(p) == 0 { + return 0, nil + } + + if b.seek >= int64(b.data.Len()) { + return 0, io.EOF + } + + chunkSize := b.data.chunkSize + i := int(b.seek) / chunkSize + offset := int(b.seek) % chunkSize + + chunk := b.data.Chunk(i) + n = copy(p, chunk[offset:]) + b.seek += int64(n) + + return n, nil +} + +func (b *Buffer) Write(p []byte) (int, error) { + n := len(p) + if n == 0 { + return 0, nil + } + + chunkSize := b.data.chunkSize + for len(p) > 0 { + i := int(b.seek) / chunkSize + offset := int(b.seek) % chunkSize + + if i == b.data.NumChunks() { + // Append dummy byte to allocate chunk, we'll overwrite it + b.data.Append(0) + } + + chunk := b.data.Chunk(i) + chunk = chunk[:cap(chunk)] + written := copy(chunk[offset:], p) + + newSeek := b.seek + int64(written) + if int(newSeek) > b.data.length { + b.data.length = int(newSeek) + } + + b.seek = newSeek + p = p[written:] + } + + return n, nil +} + +func (b *Buffer) WriteTo(w io.Writer) (int64, error) { + var written int64 + var err error + chunkSize := b.data.chunkSize + + for err == nil && b.seek < int64(b.data.Len()) { + i := int(b.seek) / chunkSize + offset := int(b.seek) % chunkSize + + chunk := b.data.Chunk(i) + n, e := w.Write(chunk[offset:]) + written += int64(n) + b.seek += int64(n) + err = e + } + return written, err +} + +func (b *Buffer) Seek(offset int64, whence int) (int64, error) { + var position int64 + + switch whence { + case io.SeekStart: + position = offset + case io.SeekCurrent: + position = b.seek + offset + case io.SeekEnd: + position = int64(b.data.Len()) + offset + default: + return 0, fmt.Errorf("seek: invalid whence: %d", whence) + } + + if position < 0 { + return 0, fmt.Errorf("seek: negative offset: %d<0", position) + } + + end := int64(b.data.Len()) + if position > end { + position = end + } + + b.seek = position + return position, nil +} + +var ( + _ io.Writer = (*Buffer)(nil) + _ io.Reader = (*Buffer)(nil) + _ io.Seeker = (*Buffer)(nil) + _ io.WriterTo = (*Buffer)(nil) +) diff --git a/vendor/github.com/parquet-go/parquet-go/internal/memory/chunk_buffer.go b/vendor/github.com/parquet-go/parquet-go/internal/memory/chunk_buffer.go new file mode 100644 index 000000000..c11a38f8c --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/internal/memory/chunk_buffer.go @@ -0,0 +1,116 @@ +package memory + +import ( + "math/bits" + "unsafe" +) + +// ChunkBuffer is a buffer that stores data in fixed-size chunks. +// Chunks are allocated lazily on demand and reused via slice pools. +// The chunk size is rounded up to the nearest power of two to utilize slice buffers fully. +// This design minimizes memory fragmentation and provides predictable memory usage. +type ChunkBuffer[T Datum] struct { + chunks []*slice[byte] + chunkSize int // in bytes, always a power of 2 + length int +} + +// ChunkBufferFor creates a new ChunkBuffer with the given chunk size (in bytes). +// The chunk size will be rounded up to the nearest power of two. +func ChunkBufferFor[T Datum](chunkSize int) ChunkBuffer[T] { + size := nextPowerOfTwo(uint(chunkSize)) + // Clamp to bucket system range + minSize := uint(minBucketSize) + maxSize := uint(bucketSize(numBuckets - 1)) + size = max(minSize, min(size, maxSize)) + return ChunkBuffer[T]{chunkSize: int(size)} +} + +func nextPowerOfTwo(n uint) uint { + if n == 0 { + return 1 + } + // bits.Len(x) returns the minimum number of bits required to represent x + return 1 << bits.Len(n-1) +} + +// Append adds data to the buffer, allocating new chunks as needed. +func (b *ChunkBuffer[T]) Append(data ...T) { + elemSize := int(unsafe.Sizeof(*new(T))) + capacity := b.chunkSize / elemSize + + for len(data) > 0 { + if len(b.chunks) == 0 || (b.length&(capacity-1)) == 0 { + bucketIndex := bucketIndexOfGet(b.chunkSize) + chunk := slicePools[bucketIndex].Get( + func() *slice[byte] { + return newSlice[byte](b.chunkSize) + }, + func(s *slice[byte]) { s.data = s.data[:0] }, + ) + b.chunks = append(b.chunks, chunk) + } + + currentChunk := b.chunks[len(b.chunks)-1] + chunkData := (*T)(unsafe.Pointer(unsafe.SliceData(currentChunk.data))) + typeChunk := unsafe.Slice(chunkData, capacity) + offset := b.length & (capacity - 1) + available := capacity - offset + toWrite := min(len(data), available) + + copy(typeChunk[offset:], data[:toWrite]) + b.length += toWrite + data = data[toWrite:] + } +} + +// Reset returns all chunks to the pool and resets the buffer to empty. +func (b *ChunkBuffer[T]) Reset() { + bucketIndex := bucketIndexOfGet(b.chunkSize) + for i := range b.chunks { + slicePools[bucketIndex].Put(b.chunks[i]) + b.chunks[i] = nil + } + b.chunks = b.chunks[:0] + b.length = 0 +} + +// Len returns the number of elements currently in the buffer. +func (b *ChunkBuffer[T]) Len() int { return b.length } + +// NumChunks returns the number of chunks currently allocated. +func (b *ChunkBuffer[T]) NumChunks() int { return len(b.chunks) } + +// Chunk returns the data for the chunk at the given index. +// The caller must ensure i < NumChunks(). +func (b *ChunkBuffer[T]) Chunk(i int) []T { + elemSize := int(unsafe.Sizeof(*new(T))) + capacity := b.chunkSize / elemSize + chunk := b.chunks[i] + chunkData := (*T)(unsafe.Pointer(unsafe.SliceData(chunk.data))) + // Return slice with valid length but full capacity + remaining := b.length - i*capacity + length := min(remaining, capacity) + s := unsafe.Slice(chunkData, capacity) + return s[:length] +} + +// Chunks returns an iterator over the chunks in the buffer. +// Each chunk is yielded as a slice containing the valid data. +func (b *ChunkBuffer[T]) Chunks(yield func([]T) bool) { + elemSize := int(unsafe.Sizeof(*new(T))) + capacity := b.chunkSize / elemSize + remaining := b.length + for _, c := range b.chunks { + if remaining == 0 { + break + } + chunkData := (*T)(unsafe.Pointer(unsafe.SliceData(c.data))) + chunkSize := min(remaining, capacity) + typeChunk := unsafe.Slice(chunkData, chunkSize) + if !yield(typeChunk) { + return + } + remaining -= chunkSize + } +} diff --git a/vendor/github.com/parquet-go/parquet-go/internal/memory/memory.go b/vendor/github.com/parquet-go/parquet-go/internal/memory/memory.go new file mode 100644 index 000000000..8256d11dc --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/internal/memory/memory.go @@ -0,0 +1,7 @@ +package memory + +// Datum is a constraint for types that can be stored in chunk and slice buffers. +// It includes the common numeric types used in parquet files. +type Datum interface { + ~byte | ~int32 | ~int64 | ~uint32 | ~uint64 | ~float32 | ~float64 +} diff --git a/vendor/github.com/parquet-go/parquet-go/internal/memory/pool.go b/vendor/github.com/parquet-go/parquet-go/internal/memory/pool.go new file mode 100644 index 000000000..7046c724b --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/internal/memory/pool.go @@ -0,0 +1,25 @@ +package memory + +import "sync" + +// Pool is a generic wrapper around sync.Pool that provides type-safe pooling +// of pointers to values of type T. +type Pool[T any] struct { + pool sync.Pool // *T +} + +func (p *Pool[T]) Get(newT func() *T, resetT func(*T)) *T { + v, _ := p.pool.Get().(*T) + if v == nil { + v = newT() + } else { + resetT(v) + } + return v +} + +func (p *Pool[T]) Put(v *T) { + if v != nil { + p.pool.Put(v) + } +} diff --git a/vendor/github.com/parquet-go/parquet-go/internal/memory/slice_buffer.go b/vendor/github.com/parquet-go/parquet-go/internal/memory/slice_buffer.go new file mode 100644 index 000000000..ba035fb26 --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/internal/memory/slice_buffer.go @@ -0,0 +1,352 @@ +package memory + +import ( + "unsafe" + + "github.com/parquet-go/parquet-go/internal/unsafecast" +) + +// slice is a wrapper around a slice to enable pooling. +type slice[T Datum] struct{ data []T } + +func newSlice[T Datum](cap int) *slice[T] { + return &slice[T]{data: make([]T, 0, cap)} +} + +// SliceBuffer is a buffer that stores data in a single contiguous slice. +// The slice grows by moving to larger size buckets from pools as needed. +// This design provides efficient sequential access and minimal overhead for small datasets. +type SliceBuffer[T Datum] struct { + slice *slice[T] // non-nil if data came from pool (used to return to pool on Reset) + data []T // the active slice (always used for access) +} + +const ( + numBuckets = 32 + minBucketSize = 4096 // 4 KiB (smallest bucket) + lastShortBucketSize = 262144 // 256 KiB (transition point for growth strategy) +) + +var slicePools [numBuckets]Pool[slice[byte]] + +// nextBucketSize computes the next bucket size using the hybrid growth strategy: +// - Below 256KB: grow by 2x (4K, 8K, 16K, 32K, 64K, 128K, 256K) +// - Above 256KB: grow by 1.5x (384K, 576K, 864K, ...) +func nextBucketSize(size int) int { + if size < lastShortBucketSize { + return size * 2 + } else { + return size + (size / 2) + } +} + +// SliceBufferFrom creates a SliceBuffer that wraps an existing slice without copying. +// The buffer takes ownership of the slice and will not return it to any pool. +func SliceBufferFrom[T Datum](data []T) SliceBuffer[T] { + return SliceBuffer[T]{data: data} +} + +// SliceBufferFor creates a SliceBuffer with pre-allocated capacity for the given number of elements. +// The buffer will be backed by a pooled slice large enough to hold cap elements. +func SliceBufferFor[T Datum](cap int) SliceBuffer[T] { + var buf SliceBuffer[T] + buf.Grow(cap) + return buf +} + +// reserveMore ensures the buffer has capacity for at least one more element. +// +// This is moved to a separate function to keep the complexity cost of AppendValue +// within the inlining budget. +// +//go:noinline +func (b *SliceBuffer[T]) reserveMore() { b.reserve(1) } + +// reserve ensures the buffer has capacity for at least count more elements. +// It handles transitioning from external data to pooled storage and growing when needed. +// Caller must check that len(b.data)+count > cap(b.data) before calling. +func (b *SliceBuffer[T]) reserve(count int) { + elemSize := int(unsafe.Sizeof(*new(T))) + requiredBytes := (len(b.data) + count) * elemSize + bucketIndex := bucketIndexOfGet(requiredBytes) + + if bucketIndex < 0 { + // Size exceeds all buckets, allocate directly without pooling + newCap := len(b.data) + count + newData := make([]T, len(b.data), newCap) + copy(newData, b.data) + if b.slice != nil { + putSliceToPool(b.slice, elemSize) + b.slice = nil + } + b.data = newData + return + } + + if b.slice == nil { + // Either empty or using external data + b.slice = getSliceFromPool[T](bucketIndex, elemSize) + if b.data != nil { + // Transition from external data to pooled storage + b.slice.data = append(b.slice.data, b.data...) + } + b.data = b.slice.data + } else { + // Already using pooled storage, grow to new bucket + oldSlice := b.slice + b.slice = getSliceFromPool[T](bucketIndex, elemSize) + b.slice.data = append(b.slice.data, b.data...) + oldSlice.data = b.data // Sync before returning to pool + putSliceToPool(oldSlice, elemSize) + b.data = b.slice.data + } +} + +// Append adds data to the buffer, growing the slice as needed by promoting to +// larger pool buckets. +func (b *SliceBuffer[T]) Append(data ...T) { + if len(b.data)+len(data) > cap(b.data) { + b.reserve(len(data)) + } + b.data = append(b.data, data...) +} + +// AppendFunc calls fn with the internal buffer and updates the buffer with the +// returned slice. This is useful for functions that follow the append pattern +// (e.g., strconv.AppendInt, jsonlite.AppendQuote) where the function may +// reallocate the slice if capacity is insufficient. +// +// If fn reallocates the slice (returns a different backing array), the buffer +// transitions to using external data and releases any pooled storage. +func (b *SliceBuffer[T]) AppendFunc(fn func([]T) []T) { + const reserveSize = 1024 + + if (cap(b.data) - len(b.data)) < reserveSize { + // Ensure there's some extra capacity to reduce reallocations + // when fn appends small amounts of data. + b.reserve(reserveSize) + } + + oldData := b.data + newData := fn(b.data) + + if unsafe.SliceData(oldData) != unsafe.SliceData(newData) { + if b.slice != nil { + // Release pooled storage since fn allocated new memory + b.slice.data = oldData + putSliceToPool(b.slice, int(unsafe.Sizeof(*new(T)))) + b.slice = nil + } + b.slice = &slice[T]{data: newData} + } + + b.data = newData +} + +// AppendValue appends a single value to the buffer. +func (b *SliceBuffer[T]) AppendValue(value T) { + if len(b.data) == cap(b.data)-1 { + b.reserveMore() + } + b.data = append(b.data, value) +} + +// SliceWriter wraps a SliceBuffer[byte] to provide io.Writer and other +// byte-specific write methods without generics type parameter shadowing issues. +type SliceWriter struct { + Buffer *SliceBuffer[byte] +} + +// Write appends data to the buffer and returns the number of bytes written. +// Implements io.Writer. +func (w SliceWriter) Write(p []byte) (n int, err error) { + w.Buffer.Append(p...) + return len(p), nil +} + +// WriteString appends a string to the buffer by copying its bytes. +func (w SliceWriter) WriteString(s string) (n int, err error) { + if len(s) == 0 { + return 0, nil + } + oldLen := len(w.Buffer.data) + if oldLen+len(s) > cap(w.Buffer.data) { + w.Buffer.reserve(len(s)) + } + w.Buffer.data = w.Buffer.data[:oldLen+len(s)] + copy(w.Buffer.data[oldLen:], s) + return len(s), nil +} + +// WriteByte appends a single byte to the buffer. +func (w SliceWriter) WriteByte(c byte) error { + w.Buffer.AppendValue(c) + return nil +} + +// Reset returns the slice to its pool and resets the buffer to empty. +func (b *SliceBuffer[T]) Reset() { + if b.slice != nil { + b.slice.data = b.data // Sync before returning to pool + elemSize := int(unsafe.Sizeof(*new(T))) + putSliceToPool(b.slice, elemSize) + b.slice = nil + } + b.data = nil +} + +// Cap returns the current capacity. +func (b *SliceBuffer[T]) Cap() int { return cap(b.data) } + +// Len returns the number of elements currently in the buffer. +func (b *SliceBuffer[T]) Len() int { return len(b.data) } + +// Slice returns a view of the current data. +// The returned slice is only valid until the next call to Append or Reset. +func (b *SliceBuffer[T]) Slice() []T { return b.data } + +// Swap swaps the elements at indices i and j. +func (b *SliceBuffer[T]) Swap(i, j int) { + b.data[i], b.data[j] = b.data[j], b.data[i] +} + +// Less reports whether the element at index i is less than the element at index j. +func (b *SliceBuffer[T]) Less(i, j int) bool { + s := b.data + return s[i] < s[j] +} + +// Grow ensures the buffer has capacity for at least n more elements. +func (b *SliceBuffer[T]) Grow(n int) { + if n > 0 && len(b.data)+n > cap(b.data) { + b.reserve(n) + } +} + +// Clone creates a copy of the buffer with its own pooled allocation. +// The cloned buffer is allocated from the pool with exactly the right size. +func (b *SliceBuffer[T]) Clone() SliceBuffer[T] { + if len(b.data) == 0 { + return SliceBuffer[T]{} + } + + elemSize := int(unsafe.Sizeof(*new(T))) + requiredBytes := len(b.data) * elemSize + bucketIndex := bucketIndexOfGet(requiredBytes) + + if bucketIndex < 0 { + // Size exceeds all buckets, allocate directly without pooling + newData := make([]T, len(b.data)) + copy(newData, b.data) + return SliceBuffer[T]{data: newData} + } + + cloned := SliceBuffer[T]{ + slice: getSliceFromPool[T](bucketIndex, elemSize), + } + cloned.slice.data = append(cloned.slice.data, b.data...) + cloned.data = cloned.slice.data + return cloned +} + +// Resize changes the length of the buffer to size, growing capacity if needed. +// If size is larger than the current length, the new elements contain uninitialized data. +// If size is smaller, the buffer is truncated. +func (b *SliceBuffer[T]) Resize(size int) { + if size <= len(b.data) { + b.data = b.data[:size] + } else { + if size > cap(b.data) { + b.reserve(size - len(b.data)) + } + b.data = b.data[:size] + } +} + +func bucketIndexOfGet(requiredBytes int) int { + if requiredBytes <= 0 { + return 0 + } + + // Find the smallest bucket that can hold requiredBytes + size := minBucketSize + for i := range numBuckets { + if requiredBytes <= size { + return i + } + size = nextBucketSize(size) + } + + // If requiredBytes exceeds all buckets, return -1 to indicate + // the allocation should not use pooling + return -1 +} + +func bucketIndexOfPut(capacityBytes int) int { + // When releasing buffers, some may have a capacity that is not one of the + // bucket sizes (due to the use of append for example). In this case, we + // return the buffer to the highest bucket with a size less or equal + // to the buffer capacity. + if capacityBytes < minBucketSize { + return -1 + } + + size := minBucketSize + for i := range numBuckets { + nextSize := nextBucketSize(size) + if capacityBytes < nextSize { + return i + } + size = nextSize + } + + // If we've gone through all buckets, return the last bucket + return numBuckets - 1 +} + +func bucketSize(bucketIndex int) int { + if bucketIndex < 0 || bucketIndex >= numBuckets { + return 0 + } + + size := minBucketSize + for range bucketIndex { + size = nextBucketSize(size) + } + return size +} + +func getSliceFromPool[T Datum](bucketIndex int, elemSize int) *slice[T] { + byteSlice := slicePools[bucketIndex].Get( + func() *slice[byte] { + return newSlice[byte](bucketSize(bucketIndex)) + }, + func(s *slice[byte]) { + s.data = s.data[:0] + }, + ) + + typeSlice := (*slice[T])(unsafe.Pointer(byteSlice)) + typeSlice.data = unsafecast.Slice[T](byteSlice.data) + return typeSlice +} + +func putSliceToPool[T Datum](s *slice[T], elemSize int) { + if s == nil || s.data == nil { + return + } + + byteLen := cap(s.data) * elemSize + bucketIndex := bucketIndexOfPut(byteLen) + + // If bucket index is -1, the buffer is too small to pool + if bucketIndex < 0 { + return + } + + byteSlice := (*slice[byte])(unsafe.Pointer(s)) + byteSlice.data = unsafecast.Slice[byte](s.data) + slicePools[bucketIndex].Put(byteSlice) +} + +var _ SliceBuffer[byte] diff --git a/vendor/github.com/parquet-go/parquet-go/internal/unsafecast/unsafecast.go b/vendor/github.com/parquet-go/parquet-go/internal/unsafecast/unsafecast.go new file mode 100644 index 000000000..0838fd10e --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/internal/unsafecast/unsafecast.go @@ -0,0 +1,54 @@ +// Package unsafecast exposes functions to bypass the Go type system and perform +// conversions between types that would otherwise not be possible. +// +// The functions of this package are mostly useful as optimizations to avoid +// memory copies when converting between compatible memory layouts; for example, +// casting a [][16]byte to a []byte in order to use functions of the standard +// bytes package on the slices. +// +// With great power comes great responsibility. +package unsafecast + +import "unsafe" + +// The slice type represents the memory layout of slices in Go. It is similar to +// reflect.SliceHeader but uses a unsafe.Pointer instead of uintptr to for the +// backing array to allow the garbage collector to track track the reference. +type slice struct { + ptr unsafe.Pointer + len int + cap int +} + +// Slice converts the data slice of type []From to a slice of type []To sharing +// the same backing array. The length and capacity of the returned slice are +// scaled according to the size difference between the source and destination +// types. +// +// Note that the function does not perform any checks to ensure that the memory +// layouts of the types are compatible, it is possible to cause memory +// corruption if the layouts mismatch (e.g. the pointers in the From are +// different than the pointers in To). +func Slice[To, From any](data []From) []To { + // This function could use unsafe.Slice but it would drop the capacity + // information, so instead we implement the type conversion. + var zf From + var zt To + var s = slice{ + ptr: unsafe.Pointer(unsafe.SliceData(data)), + len: int((uintptr(len(data)) * unsafe.Sizeof(zf)) / unsafe.Sizeof(zt)), + cap: int((uintptr(cap(data)) * unsafe.Sizeof(zf)) / unsafe.Sizeof(zt)), + } + return *(*[]To)(unsafe.Pointer(&s)) +} + +// String converts a byte slice to a string value. The returned string shares +// the backing array of the byte slice. +// +// Programs using this function are responsible for ensuring that the data slice +// is not modified while the returned string is in use, otherwise the guarantee +// of immutability of Go string values will be violated, resulting in undefined +// behavior. +func String(data []byte) string { + return unsafe.String(unsafe.SliceData(data), len(data)) +} diff --git a/vendor/github.com/parquet-go/parquet-go/level.go b/vendor/github.com/parquet-go/parquet-go/level.go new file mode 100644 index 000000000..43b76ae30 --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/level.go @@ -0,0 +1,33 @@ +package parquet + +import "github.com/parquet-go/parquet-go/internal/bytealg" + +type columnLevels struct { + repetitionDepth byte + repetitionLevel byte + definitionLevel byte +} + +func countLevelsEqual(levels []byte, value byte) int { + return bytealg.Count(levels, value) +} + +func countLevelsNotEqual(levels []byte, value byte) int { + return len(levels) - countLevelsEqual(levels, value) +} + +func appendLevel(levels []byte, value byte, count int) []byte { + i := len(levels) + n := len(levels) + count + + if cap(levels) < n { + newLevels := make([]byte, n, 2*n) + copy(newLevels, levels) + levels = newLevels + } else { + levels = levels[:n] + } + + bytealg.Broadcast(levels[i:], value) + return levels +} diff --git a/vendor/github.com/parquet-go/parquet-go/limits.go b/vendor/github.com/parquet-go/parquet-go/limits.go new file mode 100644 index 000000000..be1a08ff7 --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/limits.go @@ -0,0 +1,64 @@ +package parquet + +import ( + "fmt" + "math" +) + +const ( + // MaxColumnDepth is the maximum column depth supported by this package. + MaxColumnDepth = math.MaxUint8 + + // MaxColumnIndex is the maximum column index supported by this package. + MaxColumnIndex = math.MaxUint16 - 1 + + // MaxRepetitionLevel is the maximum repetition level supported by this + // package. + MaxRepetitionLevel = math.MaxUint8 + + // MaxDefinitionLevel is the maximum definition level supported by this + // package. + MaxDefinitionLevel = math.MaxUint8 + + // MaxRowGroups is the maximum number of row groups which can be contained + // in a single parquet file. + // + // This limit is enforced by the use of 16 bits signed integers in the file + // metadata footer of parquet files. It is part of the parquet specification + // and therefore cannot be changed. + MaxRowGroups = math.MaxInt16 +) + +const ( + estimatedSizeOfByteArrayValues = 20 +) + +func makeRepetitionLevel(i int) byte { + checkIndexRange("repetition level", i, 0, MaxRepetitionLevel) + return byte(i) +} + +func makeDefinitionLevel(i int) byte { + checkIndexRange("definition level", i, 0, MaxDefinitionLevel) + return byte(i) +} + +func makeColumnIndex(i int) uint16 { + checkIndexRange("column index", i, 0, MaxColumnIndex) + return uint16(i) +} + +func makeNumValues(i int) int32 { + checkIndexRange("number of values", i, 0, math.MaxInt32) + return int32(i) +} + +func checkIndexRange(typ string, i, min, max int) { + if i < min || i > max { + panic(errIndexOutOfRange(typ, i, min, max)) + } +} + +func errIndexOutOfRange(typ string, i, min, max int) error { + return fmt.Errorf("%s out of range: %d not in [%d:%d]", typ, i, min, max) +} diff --git a/vendor/github.com/parquet-go/parquet-go/merge.go b/vendor/github.com/parquet-go/parquet-go/merge.go new file mode 100644 index 000000000..0c47386b3 --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/merge.go @@ -0,0 +1,958 @@ +package parquet + +import ( + "cmp" + "fmt" + "io" + "iter" + "slices" + + "github.com/parquet-go/parquet-go/encoding" + "github.com/parquet-go/parquet-go/format" +) + +// MergeRowGroups constructs a row group which is a merged view of rowGroups. If +// rowGroups are sorted and the passed options include sorting, the merged row +// group will also be sorted. +// +// The function validates the input to ensure that the merge operation is +// possible, ensuring that the schemas match or can be converted to an +// optionally configured target schema passed as argument in the option list. +// +// The sorting columns of each row group are also consulted to determine whether +// the output can be represented. If sorting columns are configured on the merge +// they must be a prefix of sorting columns of all row groups being merged. +func MergeRowGroups(rowGroups []RowGroup, options ...RowGroupOption) (RowGroup, error) { + config, err := NewRowGroupConfig(options...) + if err != nil { + return nil, err + } + + schema := config.Schema + if len(rowGroups) == 0 { + if schema == nil { + return nil, fmt.Errorf("cannot merge empty row groups without a schema") + } + return newEmptyRowGroup(schema), nil + } + if schema == nil { + schemas := make([]Node, len(rowGroups)) + for i, rowGroup := range rowGroups { + schemas[i] = rowGroup.Schema() + } + schema = NewSchema(rowGroups[0].Schema().Name(), MergeNodes(schemas...)) + } + + mergedRowGroups := slices.Clone(rowGroups) + for i, rowGroup := range mergedRowGroups { + rowGroupSchema := rowGroup.Schema() + // Always apply conversion when merging multiple row groups to ensure + // column indices match the merged schema layout. The merge process can + // reorder fields even when schemas are otherwise identical. + conv, err := Convert(schema, rowGroupSchema) + if err != nil { + return nil, fmt.Errorf("cannot merge row groups: %w", err) + } + mergedRowGroups[i] = ConvertRowGroup(rowGroup, conv) + } + + // Determine the effective sorting columns for the merge + mergedSortingColumns := slices.Clone(config.Sorting.SortingColumns) + if len(mergedSortingColumns) == 0 { + // Auto-detect common sorting columns from input row groups + sortingColumns := make([][]SortingColumn, len(mergedRowGroups)) + for i, rowGroup := range mergedRowGroups { + sortingColumns[i] = rowGroup.SortingColumns() + } + mergedSortingColumns = MergeSortingColumns(sortingColumns...) + } + + if len(mergedSortingColumns) == 0 { + // When there are no effective sorting columns, use a simpler version of the + // merger which simply concatenates rows from each of the row groups. + // This is preferable because it makes the output deterministic, the + // heap merge may otherwise reorder rows across groups. + // + // IMPORTANT: We need to ensure conversions are applied even in the simple + // concatenation path. Instead of returning the multiRowGroup directly + // (which bypasses row-level conversion), we create a simple concatenating + // row reader that preserves the conversion logic. + return newMultiRowGroup(schema, nil, mergedRowGroups), nil + } + + mergedCompare := compareRowsFuncOf(schema, mergedSortingColumns) + dropDuplicatedRows := config.Sorting.DropDuplicatedRows + + // Optimization: detect non-overlapping row groups and create segments + rowGroupSegments := make([]RowGroup, 0) + for segment := range overlappingRowGroups(mergedRowGroups, schema, mergedSortingColumns, mergedCompare) { + if len(segment) == 1 { + rowGroupSegments = append(rowGroupSegments, segment[0]) + } else { + merged := &mergedRowGroup{ + compare: mergedCompare, + dropDuplicatedRows: dropDuplicatedRows, + } + merged.init(schema, mergedSortingColumns, segment) + rowGroupSegments = append(rowGroupSegments, merged) + } + } + + if len(rowGroupSegments) == 1 { + rg := rowGroupSegments[0] + if dropDuplicatedRows { + if _, isMerged := rg.(*mergedRowGroup); !isMerged { + return &dedupRowGroup{ + RowGroup: rg, + compare: mergedCompare, + }, nil + } + } + return rg, nil + } + + m := newMultiRowGroup(schema, mergedSortingColumns, rowGroupSegments) + return &sortedSegmentRowGroup{ + multiRowGroup: *m, + segments: rowGroupSegments, + compare: mergedCompare, + dropDuplicatedRows: dropDuplicatedRows, + }, nil +} + +// overlappingRowGroups analyzes row groups to find non-overlapping segments +// Returns groups of row groups where each group either: +// 1. Contains a single non-overlapping row group (can be concatenated) +// 2. Contains multiple overlapping row groups (need to be merged) +func overlappingRowGroups(rowGroups []RowGroup, schema *Schema, sorting []SortingColumn, compare func(Row, Row) int) iter.Seq[[]RowGroup] { + return func(yield func([]RowGroup) bool) { + type rowGroupRange struct { + rowGroup RowGroup + minRow Row + maxRow Row + } + + rowGroupRanges := make([]rowGroupRange, 0, len(rowGroups)) + for _, rg := range rowGroups { + if rg.NumRows() == 0 { + continue + } + minRow, maxRow, err := rowGroupRangeOfSortedColumns(rg, schema, sorting) + if err != nil { + yield(rowGroups) + return + } + rowGroupRanges = append(rowGroupRanges, rowGroupRange{ + rowGroup: rg, + minRow: minRow, + maxRow: maxRow, + }) + } + if len(rowGroupRanges) == 0 { + return + } + if len(rowGroupRanges) == 1 { + yield([]RowGroup{rowGroupRanges[0].rowGroup}) + return + } + + // Sort row groups by their minimum values + slices.SortFunc(rowGroupRanges, func(a, b rowGroupRange) int { + return compare(a.minRow, b.minRow) + }) + + // Detect overlapping segments + currentSegment := []RowGroup{rowGroupRanges[0].rowGroup} + currentMax := rowGroupRanges[0].maxRow + + for _, rr := range rowGroupRanges[1:] { + if compare(rr.minRow, currentMax) <= 0 { + // Overlapping - add to current segment and extend max if necessary + currentSegment = append(currentSegment, rr.rowGroup) + if compare(rr.maxRow, currentMax) > 0 { + currentMax = rr.maxRow + } + } else { + // Non-overlapping - yield current segment + if !yield(currentSegment) { + return + } + currentSegment = []RowGroup{rr.rowGroup} + currentMax = rr.maxRow + } + } + + if len(currentSegment) > 0 { + yield(currentSegment) + } + } +} + +func rowGroupRangeOfSortedColumns(rg RowGroup, schema *Schema, sorting []SortingColumn) (minRow, maxRow Row, err error) { + // Extract min/max values from column indices + columnChunks := rg.ColumnChunks() + columns := schema.Columns() + minValues := make([]Value, len(columns)) + maxValues := make([]Value, len(columns)) + + // Fill in default null values for non-sorting columns + for i := range columns { + minValues[i] = Value{}.Level(0, 0, i) + maxValues[i] = Value{}.Level(0, 0, i) + } + + for _, sortingColumn := range sorting { + // Find column index + sortingColumnIndex := -1 + sortingColumnPath := columnPath(sortingColumn.Path()) + for columnIndex, columnPath := range columns { + if slices.Equal(columnPath, sortingColumnPath) { + sortingColumnIndex = columnIndex + break + } + } + if sortingColumnIndex < 0 { + return nil, nil, fmt.Errorf("sorting column %v not found in schema", sortingColumnPath) + } + + columnChunk := columnChunks[sortingColumnIndex] + columnIndex, err := columnChunk.ColumnIndex() + if err != nil || columnIndex == nil || columnIndex.NumPages() == 0 { + // No column index available - fall back to merging + return nil, nil, fmt.Errorf("column index not available for sorting column %s", sortingColumnPath) + } + + // Since data is sorted by sorting columns, we can efficiently get min/max: + // - Min value = min of first non-null page + // - Max value = max of last non-null page + numPages := columnIndex.NumPages() + + // Find first non-null page for min value + var globalMin Value + var found bool + for pageIdx := range numPages { + if !columnIndex.NullPage(pageIdx) { + if minValue := columnIndex.MinValue(pageIdx); !minValue.IsNull() { + globalMin, found = minValue, true + break + } + } + } + if !found { + return nil, nil, fmt.Errorf("no valid pages found in column index for column %s", sortingColumnPath) + } + + // Find last non-null page for max value + var globalMax Value + for pageIdx := numPages - 1; pageIdx >= 0; pageIdx-- { + if !columnIndex.NullPage(pageIdx) { + if maxValue := columnIndex.MaxValue(pageIdx); !maxValue.IsNull() { + globalMax = maxValue + break + } + } + } + + // Set the min/max values with proper levels + minValues[sortingColumnIndex] = globalMin.Level(0, 1, sortingColumnIndex) + maxValues[sortingColumnIndex] = globalMax.Level(0, 1, sortingColumnIndex) + } + + minRow = Row(minValues) + maxRow = Row(maxValues) + return +} + +// compareValues compares two parquet values, taking into account the descending flag +func compareValues(a, b Value, columnType Type, descending bool) int { + cmp := columnType.Compare(a, b) + if descending { + return -cmp + } + return cmp +} + +type mergedRowGroup struct { + multiRowGroup + compare func(Row, Row) int + dropDuplicatedRows bool +} + +func (m *mergedRowGroup) Rows() Rows { + // The row group needs to respect a sorting order; the merged row reader + // uses a heap to merge rows from the row groups. + rows := make([]Rows, len(m.rowGroups)) + for i := range rows { + rows[i] = m.rowGroups[i].Rows() + } + var merge RowReader = mergeRowReaders(rows, m.compare) + if m.dropDuplicatedRows { + merge = DedupeRowReader(merge, m.compare) + } + return &mergedRowGroupRows{ + merge: merge, + rows: rows, + schema: m.schema, + } +} + +// sortedSegmentRowGroup wraps a multiRowGroup but overrides Rows() to +// concatenate from each segment's Rows() reader in sequence. This preserves +// the heap merge ordering within mergedRowGroup segments, which would be +// bypassed if multiRowGroup.Rows() read column pages directly. +type sortedSegmentRowGroup struct { + multiRowGroup + segments []RowGroup + compare func(Row, Row) int + dropDuplicatedRows bool +} + +func (s *sortedSegmentRowGroup) Rows() Rows { + readers := make([]Rows, len(s.segments)) + for i, seg := range s.segments { + readers[i] = seg.Rows() + } + var reader RowReader = &concatenatingRows{ + readers: readers, + schema: s.schema, + } + if s.dropDuplicatedRows { + reader = DedupeRowReader(reader, s.compare) + } + return &concatenatingRowsWrapper{ + reader: reader, + readers: readers, + schema: s.schema, + } +} + +// dedupRowGroup wraps a single RowGroup and applies deduplication to its Rows(). +type dedupRowGroup struct { + RowGroup + compare func(Row, Row) int +} + +func (d *dedupRowGroup) Rows() Rows { + rows := d.RowGroup.Rows() + reader := DedupeRowReader(rows, d.compare) + return &concatenatingRowsWrapper{ + reader: reader, + readers: []Rows{rows}, + schema: d.RowGroup.Schema(), + } +} + +// concatenatingRows reads from a sequence of Rows readers, advancing to the +// next reader when the current one returns io.EOF. +type concatenatingRows struct { + readers []Rows + index int + schema *Schema +} + +func (c *concatenatingRows) ReadRows(rows []Row) (int, error) { + for c.index < len(c.readers) { + n, err := c.readers[c.index].ReadRows(rows) + if err == io.EOF { + c.index++ + if n > 0 { + return n, nil + } + continue + } + return n, err + } + return 0, io.EOF +} + +// concatenatingRowsWrapper implements the Rows interface by wrapping a +// RowReader (which may include dedup) and managing the underlying Rows +// readers for Close and SeekToRow. +type concatenatingRowsWrapper struct { + reader RowReader + readers []Rows + schema *Schema + rowIndex int64 +} + +func (c *concatenatingRowsWrapper) ReadRows(rows []Row) (int, error) { + n, err := c.reader.ReadRows(rows) + c.rowIndex += int64(n) + return n, err +} + +func (c *concatenatingRowsWrapper) Close() (lastErr error) { + for _, r := range c.readers { + if err := r.Close(); err != nil { + lastErr = err + } + } + return lastErr +} + +func (c *concatenatingRowsWrapper) SeekToRow(rowIndex int64) error { + if rowIndex < c.rowIndex { + return fmt.Errorf("SeekToRow: concatenating row reader cannot seek backward from row %d to %d", c.rowIndex, rowIndex) + } + // Forward seek by reading and discarding rows + discard := make([]Row, 64) + for c.rowIndex < rowIndex { + n := min(int(rowIndex-c.rowIndex), len(discard)) + n, err := c.reader.ReadRows(discard[:n]) + c.rowIndex += int64(n) + if err != nil { + return err + } + } + return nil +} + +func (c *concatenatingRowsWrapper) Schema() *Schema { + return c.schema +} + +type mergedRowGroupRows struct { + merge RowReader + rowIndex int64 + seekToRow int64 + rows []Rows + schema *Schema +} + +func (r *mergedRowGroupRows) Close() (lastErr error) { + r.rowIndex = -1 + r.seekToRow = 0 + + for _, rows := range r.rows { + if err := rows.Close(); err != nil { + lastErr = err + } + } + + return lastErr +} + +func (r *mergedRowGroupRows) ReadRows(rows []Row) (int, error) { + if r.rowIndex < 0 { + return 0, io.EOF + } + + for r.rowIndex < r.seekToRow { + n := min(int(r.seekToRow-r.rowIndex), len(rows)) + n, err := r.merge.ReadRows(rows[:n]) + if err != nil { + return 0, err + } + rows = rows[n:] + r.rowIndex += int64(n) + } + + n, err := r.merge.ReadRows(rows) + r.rowIndex += int64(n) + return n, err +} + +func (r *mergedRowGroupRows) SeekToRow(rowIndex int64) error { + if r.rowIndex < 0 { + return fmt.Errorf("SeekToRow: cannot seek to %d on closed merged row group rows", rowIndex) + } + if rowIndex >= r.rowIndex { + r.seekToRow = rowIndex + return nil + } + return fmt.Errorf("SeekToRow: merged row reader cannot seek backward from row %d to %d", r.rowIndex, rowIndex) +} + +func (r *mergedRowGroupRows) Schema() *Schema { + return r.schema +} + +// MergeRowReader constructs a RowReader which creates an ordered sequence of +// all the readers using the given compare function as the ordering predicate. +func MergeRowReaders(rows []RowReader, compare func(Row, Row) int) RowReader { + return mergeRowReaders(rows, compare) +} + +func mergeRowReaders[T RowReader](rows []T, compare func(Row, Row) int) RowReader { + switch len(rows) { + case 0: + return emptyRows{} + case 1: + return rows[0] + case 2: + return &mergedRowReader2{ + compare: compare, + buffers: [2]bufferedRowReader{ + {rows: rows[0]}, + {rows: rows[1]}, + }, + } + default: + buffers := make([]bufferedRowReader, len(rows)) + readers := make([]*bufferedRowReader, len(rows)) + for i, r := range rows { + buffers[i].rows = r + readers[i] = &buffers[i] + } + return &mergedRowReader{ + compare: compare, + readers: readers, + } + } +} + +// mergedRowReader2 is a specialized implementation for merging exactly 2 readers +// that avoids heap overhead by doing direct comparisons +type mergedRowReader2 struct { + compare func(Row, Row) int + readers [2]*bufferedRowReader + buffers [2]bufferedRowReader + initialized bool +} + +func (m *mergedRowReader2) initialize() error { + for i := range m.buffers { + r := &m.buffers[i] + switch err := r.read(); err { + case nil: + m.readers[i] = r + case io.EOF: + m.readers[i] = nil + default: + return err + } + } + return nil +} + +func (m *mergedRowReader2) ReadRows(rows []Row) (n int, err error) { + if !m.initialized { + m.initialized = true + if err := m.initialize(); err != nil { + return 0, err + } + } + + r0 := m.readers[0] + r1 := m.readers[1] + + if r0 != nil && r0.empty() { + if err := r0.read(); err != nil { + if err != io.EOF { + return 0, err + } + r0, m.readers[0] = nil, nil + } + } + + if r1 != nil && r1.empty() { + if err := r1.read(); err != nil { + if err != io.EOF { + return 0, err + } + r1, m.readers[1] = nil, nil + } + } + + if r0 == nil && r1 == nil { + return 0, io.EOF + } + + switch { + case r0 == nil: + for n < len(rows) { + rows[n] = append(rows[n][:0], r1.head()...) + n++ + if !r1.next() { + break + } + } + + case r1 == nil: + for n < len(rows) { + rows[n] = append(rows[n][:0], r0.head()...) + n++ + if !r0.next() { + break + } + } + + default: + var hasNext0 bool + var hasNext1 bool + + for n < len(rows) { + switch cmp := m.compare(r0.head(), r1.head()); { + case cmp < 0: + rows[n] = append(rows[n][:0], r0.head()...) + n++ + hasNext0 = r0.next() + hasNext1 = true + case cmp > 0: + rows[n] = append(rows[n][:0], r1.head()...) + n++ + hasNext0 = true + hasNext1 = r1.next() + default: + rows[n] = append(rows[n][:0], r0.head()...) + n++ + hasNext0 = r0.next() + if n < len(rows) { + rows[n] = append(rows[n][:0], r1.head()...) + n++ + hasNext1 = r1.next() + } + } + if !hasNext0 || !hasNext1 { + break + } + } + } + + return n, nil +} + +type mergedRowReader struct { + compare func(Row, Row) int + readers []*bufferedRowReader + initialized bool +} + +func (m *mergedRowReader) initialize() error { + for i, r := range m.readers { + switch err := r.read(); err { + case nil: + case io.EOF: + m.readers[i] = nil + default: + m.readers = nil + return err + } + } + + n := 0 + for _, r := range m.readers { + if r != nil { + m.readers[n] = r + n++ + } + } + + clear := m.readers[n:] + for i := range clear { + clear[i] = nil + } + + m.readers = m.readers[:n] + m.heapInit() + return nil +} + +func (m *mergedRowReader) ReadRows(rows []Row) (n int, err error) { + if !m.initialized { + m.initialized = true + + if err := m.initialize(); err != nil { + return 0, err + } + } + + for n < len(rows) && len(m.readers) != 0 { + r := m.readers[0] + if r.empty() { // This readers buffer has been exhausted, repopulate it. + if err := r.read(); err != nil { + if err == io.EOF { + m.heapPop() + continue + } + return n, err + } else { + if !m.heapDown(0, len(m.readers)) { // heap.Fix + m.heapUp(0) + } + continue + } + } + + rows[n] = append(rows[n][:0], r.head()...) + n++ + + if !r.next() { + return n, nil + } + if !m.heapDown(0, len(m.readers)) { // heap.Fix + m.heapUp(0) + } + } + + if len(m.readers) == 0 { + err = io.EOF + } + + return n, err +} + +func (m *mergedRowReader) heapInit() { + n := len(m.readers) + for i := n/2 - 1; i >= 0; i-- { + m.heapDown(i, n) + } +} + +func (m *mergedRowReader) heapPop() { + n := len(m.readers) - 1 + m.heapSwap(0, n) + m.heapDown(0, n) + m.readers = m.readers[:n] +} + +func (m *mergedRowReader) heapUp(j int) { + for { + i := (j - 1) / 2 // parent + if i == j || !(m.compare(m.readers[j].head(), m.readers[i].head()) < 0) { + break + } + m.heapSwap(i, j) + j = i + } +} + +func (m *mergedRowReader) heapDown(i0, n int) bool { + i := i0 + for { + j1 := 2*i + 1 + if j1 >= n || j1 < 0 { // j1 < 0 after int overflow + break + } + j := j1 // left child + if j2 := j1 + 1; j2 < n && m.compare(m.readers[j2].head(), m.readers[j1].head()) < 0 { + j = j2 // = 2*i + 2 // right child + } + if !(m.compare(m.readers[j].head(), m.readers[i].head()) < 0) { + break + } + m.heapSwap(i, j) + i = j + } + return i > i0 +} + +func (m *mergedRowReader) heapSwap(i, j int) { + m.readers[i], m.readers[j] = m.readers[j], m.readers[i] +} + +type bufferedRowReader struct { + rows RowReader + off int32 + end int32 + buf [24]Row +} + +func (r *bufferedRowReader) empty() bool { + return r.end == r.off +} + +func (r *bufferedRowReader) head() Row { + return r.buf[r.off] +} + +func (r *bufferedRowReader) next() bool { + r.off++ + hasNext := r.off < r.end + if !hasNext { + // We need to read more rows, however it is unsafe to do so here because we haven't + // returned the current rows to the caller yet which may cause buffer corruption. + r.off = 0 + r.end = 0 + } + return hasNext +} + +func (r *bufferedRowReader) read() error { + n, err := r.rows.ReadRows(r.buf[r.end:]) + if err != nil && n == 0 { + return err + } + r.end += int32(n) + return nil +} + +var ( + _ RowReaderWithSchema = (*mergedRowGroupRows)(nil) +) + +// MergeNodes takes a list of nodes and greedily retains properties of the schemas: +// - keeps last compression that is not nil +// - keeps last non-plain encoding that is not nil +// - keeps last non-zero field id +// - union of all columns for group nodes +// - retains the most permissive repetition (required < optional < repeated) +func MergeNodes(nodes ...Node) Node { + switch len(nodes) { + case 0: + return nil + case 1: + return nodes[0] + default: + merged := nodes[0] + for _, node := range nodes[1:] { + merged = mergeTwoNodes(merged, node) + } + return merged + } +} + +// mergeTwoNodes merges two nodes using greedy property retention +func mergeTwoNodes(a, b Node) Node { + leaf1 := a.Leaf() + leaf2 := b.Leaf() + // Both must be either leaf or group nodes + if leaf1 != leaf2 { + // Cannot merge leaf with group - return the last one + return b + } + + var merged Node + if leaf1 { + // Prefer the type with a logical type annotation if one exists. + // This ensures that logical types like JSON are preserved when merging + // a typed node (from an authoritative schema) with a plain node (from + // reflection-based schema generation). + merged = Leaf(selectLogicalType(b.Type(), a.Type())) + + // Apply compression (keep last non-nil) + compression1 := a.Compression() + compression2 := b.Compression() + compression := cmp.Or(compression2, compression1) + if compression != nil { + merged = Compressed(merged, compression) + } + + // Apply encoding (keep last non-plain, non-nil) + encoding := encoding.Encoding(&Plain) + encoding1 := a.Encoding() + encoding2 := b.Encoding() + if !isPlainEncoding(encoding1) { + encoding = encoding1 + } + if !isPlainEncoding(encoding2) { + encoding = encoding2 + } + if encoding != nil && canEncode(encoding, merged.Type().Kind()) { + merged = Encoded(merged, encoding) + } + } else { + fields1 := slices.Clone(a.Fields()) + fields2 := slices.Clone(b.Fields()) + sortFields(fields1) + sortFields(fields2) + + group := make(Group, len(fields1)) + i1 := 0 + i2 := 0 + for i1 < len(fields1) && i2 < len(fields2) { + name1 := fields1[i1].Name() + name2 := fields2[i2].Name() + switch { + case name1 < name2: + group[name1] = nullable(fields1[i1]) + i1++ + case name1 > name2: + group[name2] = nullable(fields2[i2]) + i2++ + default: + group[name1] = mergeTwoNodes(fields1[i1], fields2[i2]) + i1++ + i2++ + } + } + + for _, field := range fields1[i1:] { + group[field.Name()] = nullable(field) + } + + for _, field := range fields2[i2:] { + group[field.Name()] = nullable(field) + } + + merged = group + + if logicalType := b.Type().LogicalType(); logicalType != nil { + switch { + case logicalType.List != nil: + merged = &listNode{group} + case logicalType.Map != nil: + merged = &mapNode{group} + case logicalType.Variant != nil: + merged = &variantNode{group} + } + } + } + + // Apply repetition (most permissive: required < optional < repeated) + if a.Repeated() || b.Repeated() { + merged = Repeated(merged) + } else if a.Optional() || b.Optional() { + merged = Optional(merged) + } else { + merged = Required(merged) + } + + // Apply field ID (keep last non-zero) + return FieldID(merged, cmp.Or(b.ID(), a.ID())) +} + +// isPlainEncoding checks if the encoding is plain encoding +func isPlainEncoding(enc encoding.Encoding) bool { + return enc == nil || enc.Encoding() == format.Plain +} + +func nullable(n Node) Node { + if !n.Repeated() { + return Optional(n) + } + return n +} + +func selectLogicalType(t1, t2 Type) Type { + if t1.LogicalType() != nil { + return t1 + } + return t2 +} + +// MergeSortingColumns returns the common prefix of all sorting columns passed as arguments. +// This function is used to determine the resulting sorting columns when merging multiple +// row groups that each have their own sorting columns. +// +// The function returns the longest common prefix where all sorting columns match exactly +// (same path, same descending flag, same nulls first flag). If any row group has no +// sorting columns, or if there's no common prefix, an empty slice is returned. +// +// Example: +// +// columns1 := []SortingColumn{Ascending("A"), Ascending("B"), Descending("C")} +// columns2 := []SortingColumn{Ascending("A"), Ascending("B"), Ascending("D")} +// result := MergeSortingColumns(columns1, columns2) +// // result will be []SortingColumn{Ascending("A"), Ascending("B")} +func MergeSortingColumns(sortingColumns ...[]SortingColumn) []SortingColumn { + if len(sortingColumns) == 0 { + return nil + } + merged := slices.Clone(sortingColumns[0]) + for _, columns := range sortingColumns[1:] { + merged = commonSortingPrefix(merged, columns) + } + return merged +} + +// commonSortingPrefix returns the common prefix of two sorting column slices +func commonSortingPrefix(a, b []SortingColumn) []SortingColumn { + minLen := min(len(a), len(b)) + for i := range minLen { + if !equalSortingColumn(a[i], b[i]) { + return a[:i] + } + } + return a[:minLen] +} diff --git a/vendor/github.com/parquet-go/parquet-go/multi_row_group.go b/vendor/github.com/parquet-go/parquet-go/multi_row_group.go new file mode 100644 index 000000000..65bf920d0 --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/multi_row_group.go @@ -0,0 +1,565 @@ +package parquet + +import ( + "io" + "slices" +) + +// MultiRowGroup wraps multiple row groups to appear as if it was a single +// RowGroup. RowGroups must have the same schema or it will error. +func MultiRowGroup(rowGroups ...RowGroup) RowGroup { + if len(rowGroups) == 0 { + return &emptyRowGroup{} + } + if len(rowGroups) == 1 { + return rowGroups[0] + } + schema, err := compatibleSchemaOf(rowGroups) + if err != nil { + panic(err) + } + return newMultiRowGroup(schema, nil, slices.Clone(rowGroups)) +} + +func newMultiRowGroup(schema *Schema, sorting []SortingColumn, rowGroups []RowGroup) *multiRowGroup { + m := new(multiRowGroup) + m.init(schema, sorting, rowGroups) + return m +} + +func (m *multiRowGroup) init(schema *Schema, sorting []SortingColumn, rowGroups []RowGroup) *multiRowGroup { + columns := make([]multiColumnChunk, len(schema.Columns())) + columnChunks := make([][]ColumnChunk, len(rowGroups)) + + for i, rowGroup := range rowGroups { + columnChunks[i] = rowGroup.ColumnChunks() + } + + for i := range columns { + columns[i].rowGroup = m + columns[i].column = i + // Don't pre-allocate - we need to flatten nested multiColumnChunks + columns[i].chunks = nil + columns[i].rowCounts = nil + + for j, chunks := range columnChunks { + chunk := chunks[i] + // Flatten nested multiColumnChunks to avoid nesting issues + // when merging already-merged row groups + if mcc, ok := chunk.(*multiColumnChunk); ok { + columns[i].chunks = append(columns[i].chunks, mcc.chunks...) + // Copy row counts from nested multiColumnChunk if available, + // otherwise use the nested rowGroup's row counts + if len(mcc.rowCounts) > 0 { + columns[i].rowCounts = append(columns[i].rowCounts, mcc.rowCounts...) + } else { + // Fall back to computing from the nested rowGroup + for _, rg := range mcc.rowGroup.rowGroups { + columns[i].rowCounts = append(columns[i].rowCounts, rg.NumRows()) + } + } + } else { + columns[i].chunks = append(columns[i].chunks, chunk) + columns[i].rowCounts = append(columns[i].rowCounts, rowGroups[j].NumRows()) + } + } + } + + m.schema = schema + m.sorting = sorting + m.rowGroups = rowGroups + m.columns = make([]ColumnChunk, len(columns)) + + for i := range columns { + m.columns[i] = &columns[i] + } + + return m +} + +func compatibleSchemaOf(rowGroups []RowGroup) (*Schema, error) { + schema := rowGroups[0].Schema() + + // Fast path: Many times all row groups have the exact same schema so a + // pointer comparison is cheaper. + samePointer := true + for _, rowGroup := range rowGroups[1:] { + if rowGroup.Schema() != schema { + samePointer = false + break + } + } + if samePointer { + return schema, nil + } + + // Slow path: The schema pointers are not the same, but they still have to + // be compatible. + for _, rowGroup := range rowGroups[1:] { + if !EqualNodes(schema, rowGroup.Schema()) { + return nil, ErrRowGroupSchemaMismatch + } + } + + return schema, nil +} + +type multiRowGroup struct { + schema *Schema + rowGroups []RowGroup + columns []ColumnChunk + sorting []SortingColumn +} + +func (m *multiRowGroup) NumRows() (numRows int64) { + for _, rowGroup := range m.rowGroups { + numRows += rowGroup.NumRows() + } + return numRows +} + +func (m *multiRowGroup) ColumnChunks() []ColumnChunk { return m.columns } + +func (m *multiRowGroup) SortingColumns() []SortingColumn { return m.sorting } + +func (m *multiRowGroup) Schema() *Schema { return m.schema } + +func (m *multiRowGroup) Rows() Rows { return NewRowGroupRowReader(m) } + +type multiColumnChunk struct { + rowGroup *multiRowGroup + column int + chunks []ColumnChunk + rowCounts []int64 // row count for each chunk (used when chunks are flattened) +} + +func (c *multiColumnChunk) Type() Type { + if len(c.chunks) != 0 { + return c.chunks[0].Type() // all chunks should be of the same type + } + return nil +} + +func (c *multiColumnChunk) NumValues() int64 { + n := int64(0) + for i := range c.chunks { + n += c.chunks[i].NumValues() + } + return n +} + +func (c *multiColumnChunk) Column() int { + return c.column +} + +func (c *multiColumnChunk) Pages() Pages { + return &multiPages{column: c} +} + +func (c *multiColumnChunk) ColumnIndex() (ColumnIndex, error) { + if len(c.chunks) == 0 { + return emptyColumnIndex{}, nil + } + + // Collect indexes from all chunks + indexes := make([]ColumnIndex, len(c.chunks)) + numPages := 0 + + for i, chunk := range c.chunks { + index, err := chunk.ColumnIndex() + if err != nil { + return nil, err + } + indexes[i] = index + numPages += index.NumPages() + } + + // Build cumulative offsets for page index mapping + offsets := make([]int, len(indexes)+1) + for i, index := range indexes { + offsets[i+1] = offsets[i] + index.NumPages() + } + + return &multiColumnIndex{ + column: c, + indexes: indexes, + offsets: offsets, + numPages: numPages, + typ: c.Type(), + }, nil +} + +func (c *multiColumnChunk) OffsetIndex() (OffsetIndex, error) { + if len(c.chunks) == 0 { + return emptyOffsetIndex{}, nil + } + + // Collect indexes from all chunks + indexes := make([]OffsetIndex, len(c.chunks)) + numPages := 0 + + for i, chunk := range c.chunks { + index, err := chunk.OffsetIndex() + if err != nil { + return nil, err + } + indexes[i] = index + numPages += index.NumPages() + } + + // Build cumulative page offsets + offsets := make([]int, len(indexes)+1) + for i, index := range indexes { + offsets[i+1] = offsets[i] + index.NumPages() + } + + // Build cumulative row offsets for each chunk + rowOffsets := make([]int64, len(c.chunks)+1) + for i := range c.chunks { + rowOffsets[i+1] = rowOffsets[i] + c.rowGroup.rowGroups[i].NumRows() + } + + return &multiOffsetIndex{ + column: c, + indexes: indexes, + offsets: offsets, + rowOffsets: rowOffsets, + numPages: numPages, + }, nil +} + +func (c *multiColumnChunk) BloomFilter() BloomFilter { + return multiBloomFilter{c} +} + +type multiBloomFilter struct{ *multiColumnChunk } + +func (f multiBloomFilter) ReadAt(b []byte, off int64) (int, error) { + i := 0 + + for i < len(f.chunks) { + if r := f.chunks[i].BloomFilter(); r != nil { + size := r.Size() + if off < size { + break + } + off -= size + } + i++ + } + + if i == len(f.chunks) { + return 0, io.EOF + } + + rn := int(0) + for len(b) > 0 { + if i >= len(f.chunks) { + return rn, io.EOF + } + if r := f.chunks[i].BloomFilter(); r != nil { + n, err := r.ReadAt(b, off) + rn += n + if err != nil && err != io.EOF { + return rn, err + } + if b = b[n:]; len(b) == 0 { + return rn, nil + } + // When moving to next chunk, reset offset to 0 + off = 0 + } + i++ + } + + return rn, nil +} + +func (f multiBloomFilter) Size() int64 { + size := int64(0) + for _, c := range f.chunks { + if b := c.BloomFilter(); b != nil { + size += b.Size() + } + } + return size +} + +func (f multiBloomFilter) Check(v Value) (bool, error) { + for _, c := range f.chunks { + if b := c.BloomFilter(); b != nil { + if ok, err := b.Check(v); ok || err != nil { + return ok, err + } + } + } + return false, nil +} + +type multiColumnIndex struct { + column *multiColumnChunk + indexes []ColumnIndex + offsets []int + numPages int + typ Type +} + +func (m *multiColumnIndex) NumPages() int { + return m.numPages +} + +func (m *multiColumnIndex) mapPageIndex(pageIndex int) (chunkIndex, localIndex int) { + for i := range len(m.offsets) - 1 { + if pageIndex >= m.offsets[i] && pageIndex < m.offsets[i+1] { + return i, pageIndex - m.offsets[i] + } + } + // Out of bounds - return last valid position + if len(m.indexes) > 0 { + lastIndex := len(m.indexes) - 1 + lastNumPages := m.indexes[lastIndex].NumPages() + if lastNumPages > 0 { + return lastIndex, lastNumPages - 1 + } + } + return 0, 0 +} + +func (m *multiColumnIndex) NullCount(pageIndex int) int64 { + chunkIndex, localIndex := m.mapPageIndex(pageIndex) + return m.indexes[chunkIndex].NullCount(localIndex) +} + +func (m *multiColumnIndex) NullPage(pageIndex int) bool { + chunkIndex, localIndex := m.mapPageIndex(pageIndex) + return m.indexes[chunkIndex].NullPage(localIndex) +} + +func (m *multiColumnIndex) MinValue(pageIndex int) Value { + chunkIndex, localIndex := m.mapPageIndex(pageIndex) + return m.indexes[chunkIndex].MinValue(localIndex) +} + +func (m *multiColumnIndex) MaxValue(pageIndex int) Value { + chunkIndex, localIndex := m.mapPageIndex(pageIndex) + return m.indexes[chunkIndex].MaxValue(localIndex) +} + +func (m *multiColumnIndex) IsAscending() bool { + if len(m.indexes) == 0 { + return false + } + + // All indexes must be ascending + for _, index := range m.indexes { + if !index.IsAscending() { + return false + } + } + + // Additionally, check boundary between chunks: + // max of chunk[i] must be <= min of chunk[i+1] + if m.typ == nil { + return true + } + cmp := m.typ.Compare + for i := range len(m.indexes) - 1 { + currIndex := m.indexes[i] + nextIndex := m.indexes[i+1] + + // Find last non-null page in current chunk + lastPage := currIndex.NumPages() - 1 + for lastPage >= 0 && currIndex.NullPage(lastPage) { + lastPage-- + } + + // Find first non-null page in next chunk + firstPage := 0 + numPages := nextIndex.NumPages() + for firstPage < numPages && nextIndex.NullPage(firstPage) { + firstPage++ + } + + // If both have valid pages, compare max of current to min of next + if lastPage >= 0 && firstPage < numPages { + currMax := currIndex.MaxValue(lastPage) + nextMin := nextIndex.MinValue(firstPage) + if cmp(currMax, nextMin) > 0 { + return false // Not ascending across chunk boundary + } + } + } + + return true +} + +func (m *multiColumnIndex) IsDescending() bool { + if len(m.indexes) == 0 { + return false + } + + // All indexes must be descending + for _, index := range m.indexes { + if !index.IsDescending() { + return false + } + } + + // Additionally, check boundary between chunks: + // min of chunk[i] must be >= max of chunk[i+1] + if m.typ == nil { + return true + } + cmp := m.typ.Compare + for i := range len(m.indexes) - 1 { + currIndex := m.indexes[i] + nextIndex := m.indexes[i+1] + + // Find first non-null page in current chunk + firstPage := 0 + numPages := currIndex.NumPages() + for firstPage < numPages && currIndex.NullPage(firstPage) { + firstPage++ + } + + // Find last non-null page in next chunk + lastPage := nextIndex.NumPages() - 1 + for lastPage >= 0 && nextIndex.NullPage(lastPage) { + lastPage-- + } + + // If both have valid pages, compare min of current to max of next + if firstPage < numPages && lastPage >= 0 { + currMin := currIndex.MinValue(firstPage) + nextMax := nextIndex.MaxValue(lastPage) + if cmp(currMin, nextMax) < 0 { + return false // Not descending across chunk boundary + } + } + } + + return true +} + +type multiOffsetIndex struct { + column *multiColumnChunk + indexes []OffsetIndex + offsets []int + rowOffsets []int64 + numPages int +} + +func (m *multiOffsetIndex) NumPages() int { + return m.numPages +} + +func (m *multiOffsetIndex) mapPageIndex(pageIndex int) (chunkIndex int, localIndex int) { + for i := range len(m.offsets) - 1 { + if pageIndex >= m.offsets[i] && pageIndex < m.offsets[i+1] { + return i, pageIndex - m.offsets[i] + } + } + // Out of bounds - return last valid position + if len(m.indexes) > 0 { + lastIndex := len(m.indexes) - 1 + lastNumPages := m.indexes[lastIndex].NumPages() + if lastNumPages > 0 { + return lastIndex, lastNumPages - 1 + } + } + return 0, 0 +} + +func (m *multiOffsetIndex) Offset(pageIndex int) int64 { + chunkIndex, localIndex := m.mapPageIndex(pageIndex) + return m.indexes[chunkIndex].Offset(localIndex) +} + +func (m *multiOffsetIndex) CompressedPageSize(pageIndex int) int64 { + chunkIndex, localIndex := m.mapPageIndex(pageIndex) + return m.indexes[chunkIndex].CompressedPageSize(localIndex) +} + +func (m *multiOffsetIndex) FirstRowIndex(pageIndex int) int64 { + chunkIndex, localIndex := m.mapPageIndex(pageIndex) + localRowIndex := m.indexes[chunkIndex].FirstRowIndex(localIndex) + return m.rowOffsets[chunkIndex] + localRowIndex +} + +type multiPages struct { + pages Pages + index int + column *multiColumnChunk +} + +func (m *multiPages) ReadPage() (Page, error) { + for { + if m.pages != nil { + p, err := m.pages.ReadPage() + if err == nil || err != io.EOF { + return p, err + } + if err := m.pages.Close(); err != nil { + return nil, err + } + m.pages = nil + } + + if m.column == nil || m.index == len(m.column.chunks) { + return nil, io.EOF + } + + m.pages = m.column.chunks[m.index].Pages() + m.index++ + } +} + +func (m *multiPages) SeekToRow(rowIndex int64) error { + if m.column == nil { + return io.ErrClosedPipe + } + + if m.pages != nil { + if err := m.pages.Close(); err != nil { + return err + } + } + + // Use rowCounts if available (flattened chunks), otherwise use rowGroups + rowCounts := m.column.rowCounts + numChunks := len(m.column.chunks) + m.pages = nil + m.index = 0 + + for m.index < numChunks { + var numRows int64 + if len(rowCounts) > m.index { + numRows = rowCounts[m.index] + } else { + // Fall back to rowGroups if rowCounts not populated + numRows = m.column.rowGroup.rowGroups[m.index].NumRows() + } + if rowIndex < numRows { + break + } + rowIndex -= numRows + m.index++ + } + + if m.index < numChunks { + m.pages = m.column.chunks[m.index].Pages() + m.index++ + return m.pages.SeekToRow(rowIndex) + } + return nil +} + +func (m *multiPages) Close() (err error) { + if m.pages != nil { + err = m.pages.Close() + } + m.pages = nil + m.index = 0 + m.column = nil + return err +} diff --git a/vendor/github.com/parquet-go/parquet-go/node.go b/vendor/github.com/parquet-go/parquet-go/node.go new file mode 100644 index 000000000..e92489f3e --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/node.go @@ -0,0 +1,705 @@ +package parquet + +import ( + "fmt" + "reflect" + "slices" + "strconv" + "strings" + "unicode" + "unicode/utf8" + + "github.com/parquet-go/parquet-go/compress" + "github.com/parquet-go/parquet-go/deprecated" + "github.com/parquet-go/parquet-go/encoding" + "github.com/parquet-go/parquet-go/encoding/thrift" + "github.com/parquet-go/parquet-go/format" +) + +// Node values represent nodes of a parquet schema. +// +// Nodes carry the type of values, as well as properties like whether the values +// are optional or repeat. Nodes with one or more children represent parquet +// groups and therefore do not have a logical type. +// +// Nodes are immutable values and therefore safe to use concurrently from +// multiple goroutines. +type Node interface { + // The id of this node in its parent node. Zero value is treated as id is not + // set. ID only needs to be unique within its parent context. + // + // This is the same as parquet field_id + ID() int + + // Returns a human-readable representation of the parquet node. + String() string + + // For leaf nodes, returns the type of values of the parquet column. + // + // Calling this method on non-leaf nodes will panic. + Type() Type + + // Returns whether the parquet column is optional. + Optional() bool + + // Returns whether the parquet column is repeated. + Repeated() bool + + // Returns whether the parquet column is required. + Required() bool + + // Returns true if this a leaf node. + Leaf() bool + + // Returns a mapping of the node's fields. + // + // As an optimization, the same slices may be returned by multiple calls to + // this method, programs must treat the returned values as immutable. + // + // This method returns an empty mapping when called on leaf nodes. + Fields() []Field + + // Returns the encoding used by the node. + // + // The method may return nil to indicate that no specific encoding was + // configured on the node, in which case a default encoding might be used. + Encoding() encoding.Encoding + + // Returns compression codec used by the node. + // + // The method may return nil to indicate that no specific compression codec + // was configured on the node, in which case a default compression might be + // used. + Compression() compress.Codec + + // Returns the Go type that best represents the parquet node. + // + // For leaf nodes, this will be one of bool, int32, int64, deprecated.Int96, + // float32, float64, string, []byte, or [N]byte. + // + // For groups, the method returns a struct type. + // + // If the method is called on a repeated node, the method returns a slice of + // the underlying type. + // + // For optional nodes, the method returns a pointer of the underlying type. + // + // For nodes that were constructed from Go values (e.g. using SchemaOf), the + // method returns the original Go type. + GoType() reflect.Type +} + +// Field instances represent fields of a parquet node, which associate a node to +// their name in their parent node. +type Field interface { + Node + + // Returns the name of this field in its parent node. + Name() string + + // Given a reference to the Go value matching the structure of the parent + // node, returns the Go value of the field. + Value(base reflect.Value) reflect.Value +} + +// Encoded wraps the node passed as argument to use the given encoding. +// +// The function panics if it is called on a non-leaf node, or if the +// encoding does not support the node type. +func Encoded(node Node, encoding encoding.Encoding) Node { + if !node.Leaf() { + panic("cannot add encoding to a non-leaf node") + } + if encoding != nil { + kind := node.Type().Kind() + if !canEncode(encoding, kind) { + panic("cannot apply " + encoding.Encoding().String() + " to node of type " + kind.String()) + } + } + return &encodedNode{ + Node: node, + encoding: encoding, + } +} + +type encodedNode struct { + Node + encoding encoding.Encoding +} + +func (n *encodedNode) Encoding() encoding.Encoding { + return n.encoding +} + +// Compressed wraps the node passed as argument to use the given compression +// codec. +// +// If the codec is nil, the node's compression is left unchanged. +// +// The function panics if it is called on a non-leaf node. +func Compressed(node Node, codec compress.Codec) Node { + if !node.Leaf() { + panic("cannot add compression codec to a non-leaf node") + } + return &compressedNode{ + Node: node, + codec: codec, + } +} + +type compressedNode struct { + Node + codec compress.Codec +} + +func (n *compressedNode) Compression() compress.Codec { + return n.codec +} + +// Optional wraps the given node to make it optional. +func Optional(node Node) Node { return &optionalNode{node} } + +type optionalNode struct{ Node } + +func (opt *optionalNode) Optional() bool { return true } +func (opt *optionalNode) Repeated() bool { return false } +func (opt *optionalNode) Required() bool { return false } +func (opt *optionalNode) GoType() reflect.Type { return reflect.PtrTo(opt.Node.GoType()) } + +// FieldID wraps a node to provide node field id +func FieldID(node Node, id int) Node { return &fieldIDNode{Node: node, id: id} } + +type fieldIDNode struct { + Node + id int +} + +func (f *fieldIDNode) ID() int { return f.id } + +// Repeated wraps the given node to make it repeated. +func Repeated(node Node) Node { return &repeatedNode{node} } + +type repeatedNode struct{ Node } + +func (rep *repeatedNode) Optional() bool { return false } +func (rep *repeatedNode) Repeated() bool { return true } +func (rep *repeatedNode) Required() bool { return false } +func (rep *repeatedNode) GoType() reflect.Type { return reflect.SliceOf(rep.Node.GoType()) } + +// Required wraps the given node to make it required. +func Required(node Node) Node { return &requiredNode{node} } + +type requiredNode struct{ Node } + +func (req *requiredNode) Optional() bool { return false } +func (req *requiredNode) Repeated() bool { return false } +func (req *requiredNode) Required() bool { return true } +func (req *requiredNode) GoType() reflect.Type { return req.Node.GoType() } + +type node struct{} + +// Leaf returns a leaf node of the given type. +func Leaf(typ Type) Node { + return &leafNode{typ: typ} +} + +type leafNode struct{ typ Type } + +func (n *leafNode) ID() int { return 0 } + +func (n *leafNode) String() string { return sprint("", n) } + +func (n *leafNode) Type() Type { return n.typ } + +func (n *leafNode) Optional() bool { return false } + +func (n *leafNode) Repeated() bool { return false } + +func (n *leafNode) Required() bool { return true } + +func (n *leafNode) Leaf() bool { return true } + +func (n *leafNode) Fields() []Field { return nil } + +func (n *leafNode) Encoding() encoding.Encoding { return nil } + +func (n *leafNode) Compression() compress.Codec { return nil } + +func (n *leafNode) GoType() reflect.Type { return goTypeOfLeaf(n) } + +func fieldRepetitionTypeNullOf(node Node) thrift.Null[format.FieldRepetitionType] { + switch { + case node.Required(): + return thrift.New(format.Required) + case node.Optional(): + return thrift.New(format.Optional) + case node.Repeated(): + return thrift.New(format.Repeated) + default: + return thrift.Null[format.FieldRepetitionType]{} + } +} + +func fieldRepetitionTypeOf(node Node) format.FieldRepetitionType { + switch { + case node.Optional(): + return format.Optional + case node.Repeated(): + return format.Repeated + default: + return format.Required + } +} + +func applyFieldRepetitionType(t format.FieldRepetitionType, repetitionLevel, definitionLevel byte) (byte, byte) { + switch t { + case format.Optional: + definitionLevel++ + case format.Repeated: + repetitionLevel++ + definitionLevel++ + } + return repetitionLevel, definitionLevel +} + +type Group map[string]Node + +func (g Group) ID() int { return 0 } + +func (g Group) String() string { return sprint("", g) } + +func (g Group) Type() Type { return groupType{} } + +func (g Group) Optional() bool { return false } + +func (g Group) Repeated() bool { return false } + +func (g Group) Required() bool { return true } + +func (g Group) Leaf() bool { return false } + +func (g Group) Fields() []Field { + groupFields := make([]groupField, 0, len(g)) + for name, node := range g { + groupFields = append(groupFields, groupField{ + Node: node, + name: name, + }) + } + slices.SortFunc(groupFields, func(a, b groupField) int { + return strings.Compare(a.name, b.name) + }) + fields := make([]Field, len(groupFields)) + for i := range groupFields { + fields[i] = &groupFields[i] + } + return fields +} + +func (g Group) Encoding() encoding.Encoding { return nil } + +func (g Group) Compression() compress.Codec { return nil } + +func (g Group) GoType() reflect.Type { return goTypeOfGroup(g) } + +type groupField struct { + Node + name string +} + +func (f *groupField) Name() string { return f.name } + +func (f *groupField) Value(base reflect.Value) reflect.Value { + if base.Kind() == reflect.Interface { + if base.IsNil() { + return reflect.ValueOf(nil) + } + if base = base.Elem(); base.Kind() == reflect.Pointer && base.IsNil() { + return reflect.ValueOf(nil) + } + } + switch base.Kind() { + case reflect.Struct: + return groupFieldByName(base, f.name) + case reflect.Ptr: + if base.IsNil() { + base.Set(reflect.New(base.Type().Elem())) + } + return groupFieldByName(base.Elem(), f.name) + default: + return base.MapIndex(reflect.ValueOf(&f.name).Elem()) + } +} + +// groupFieldByName looks up a struct field by its parquet tag name, +// falling back to matching the Go field name directly. +func groupFieldByName(base reflect.Value, name string) reflect.Value { + t := base.Type() + for i := range t.NumField() { + f := t.Field(i) + if tag := f.Tag.Get("parquet"); tag != "" { + fieldName, _ := split(tag) + if fieldName == name { + return base.Field(i) + } + } + if f.Name == name { + return base.Field(i) + } + } + return reflect.Value{} +} + +func goTypeOf(node Node) reflect.Type { + switch { + case node.Optional(): + return goTypeOfOptional(node) + case node.Repeated(): + return goTypeOfRepeated(node) + default: + return goTypeOfRequired(node) + } +} + +func goTypeOfOptional(node Node) reflect.Type { + return reflect.PtrTo(goTypeOfRequired(node)) +} + +func goTypeOfRepeated(node Node) reflect.Type { + return reflect.SliceOf(goTypeOfRequired(node)) +} + +func goTypeOfRequired(node Node) reflect.Type { + if node.Leaf() { + return goTypeOfLeaf(node) + } else { + return goTypeOfGroup(node) + } +} + +func goTypeOfLeaf(node Node) reflect.Type { + t := node.Type() + if convertibleType, ok := t.(interface{ GoType() reflect.Type }); ok { + return convertibleType.GoType() + } + switch t.Kind() { + case Boolean: + return reflect.TypeOf(false) + case Int32: + return reflect.TypeOf(int32(0)) + case Int64: + return reflect.TypeOf(int64(0)) + case Int96: + return reflect.TypeOf(deprecated.Int96{}) + case Float: + return reflect.TypeOf(float32(0)) + case Double: + return reflect.TypeOf(float64(0)) + case ByteArray: + return reflect.TypeOf(([]byte)(nil)) + case FixedLenByteArray: + return reflect.ArrayOf(t.Length(), reflect.TypeOf(byte(0))) + default: + panic("BUG: parquet type returned an unsupported kind") + } +} + +func goTypeOfGroup(node Node) reflect.Type { + fields := node.Fields() + structFields := make([]reflect.StructField, len(fields)) + names := make(map[string]struct{}, len(fields)) + for i, field := range fields { + if strings.IndexByte(field.Name(), ',') != -1 { + // Ruh-roh! We cannot create a valid Go identifier, but neither can we + // create a valid Go struct tag for mapping a synthetic field name. + panic(fmt.Sprintf("schema node contains an invalid name %q: fields must not have commas in their name", field.Name())) + } + name := exportedStructFieldName(field.Name()) + for { + if _, alreadyUsed := names[name]; !alreadyUsed { + break + } + name += "_" // add suffix to fix collision + } + names[name] = struct{}{} + structFields[i].Name = name + structFields[i].Type = field.GoType() + structFields[i].Tag = reflect.StructTag(`parquet:` + strconv.Quote(field.Name())) + } + return reflect.StructOf(structFields) +} + +func exportedStructFieldName(name string) string { + firstRune, size := utf8.DecodeRuneInString(name) + if unicode.IsUpper(firstRune) { + return sanitize(name) + } + upperFirstRune := unicode.ToUpper(firstRune) + if upperFirstRune == firstRune { + // First character was not a letter, so just trying to upper-case the first + // character won't export the field. We need to add an upper-case prefix instead. + return "X" + sanitize(name) + } + return string([]rune{upperFirstRune}) + sanitize(name[size:]) +} + +// sanitize replaces any character that are invalid in a Go identifier with "_". +func sanitize(name string) string { + if isSanitized(name) { + // Fast path: name is fine, no need to allocate or compute anything. + return name + } + var newName strings.Builder + for _, r := range name { + if isValidInGoIdent(r) { + newName.WriteRune(r) + } else { + newName.WriteByte('_') + } + } + return newName.String() +} + +func isSanitized(name string) bool { + for _, r := range name { + if !isValidInGoIdent(r) { + return false + } + } + return true +} + +func isValidInGoIdent(r rune) bool { + return r == '_' || unicode.IsLetter(r) || unicode.IsDigit(r) +} + +func isList(node Node) bool { + logicalType := node.Type().LogicalType() + return logicalType != nil && logicalType.List != nil +} + +func isMap(node Node) bool { + logicalType := node.Type().LogicalType() + return logicalType != nil && logicalType.Map != nil +} + +func isVariant(node Node) bool { + logicalType := node.Type().LogicalType() + return logicalType != nil && logicalType.Variant != nil +} + +func numLeafColumnsOf(node Node) uint16 { + return makeColumnIndex(numLeafColumns(node, 0)) +} + +func numLeafColumns(node Node, columnIndex int) int { + if node.Leaf() { + return columnIndex + 1 + } + for _, field := range node.Fields() { + columnIndex = numLeafColumns(field, columnIndex) + } + return columnIndex +} + +func listElementOf(node Node) Node { + if !node.Leaf() { + // The spec says the outer group should be named "list" and the inner + // element should be named "element", but many implementations use + // different names (e.g. "bag"/"array_element", "list"/"item"). + // Per the spec's backward compatibility rules, we should not enforce + // specific names when reading. + // + // A LIST node should have exactly one child (the repeated group), + // and that group should have exactly one child (the element). + if fields := node.Fields(); len(fields) == 1 { + repeatedGroup := fields[0] + if !repeatedGroup.Leaf() && repeatedGroup.Repeated() { + if elems := repeatedGroup.Fields(); len(elems) == 1 { + return elems[0] + } + } + } + } + panic("node with logical type LIST is not composed of a repeated group with a single element") +} + +func mapKeyValueOf(node Node) Node { + if !node.Leaf() && (node.Required() || node.Optional()) { + for _, kv_name := range []string{"key_value", "map"} { + if keyValue := fieldByName(node, kv_name); keyValue != nil && !keyValue.Leaf() && keyValue.Repeated() { + k := fieldByName(keyValue, "key") + v := fieldByName(keyValue, "value") + if k != nil && v != nil && k.Required() { + return keyValue + } + } + } + } + panic("node with logical type MAP is not composed of a repeated .key_value group (or .map group) with key and value fields") +} + +func encodingOf(node Node, defaultEncodings map[Kind]encoding.Encoding) encoding.Encoding { + encoding := node.Encoding() + kind := node.Type().Kind() + if encoding == nil && defaultEncodings != nil { + encoding = defaultEncodings[kind] + } + // The parquet-format documentation states that the + // DELTA_LENGTH_BYTE_ARRAY is always preferred to PLAIN when + // encoding BYTE_ARRAY values. We apply it as a default if + // none were explicitly specified, which gives the application + // the opportunity to override this behavior if needed. + // + // https://github.com/apache/parquet-format/blob/master/Encodings.md#delta-length-byte-array-delta_length_byte_array--6 + if kind == ByteArray && encoding == nil { + encoding = &DeltaLengthByteArray + } + if encoding == nil { + encoding = &Plain + } + return encoding +} + +func forEachNodeOf(name string, node Node, do func(string, Node)) { + do(name, node) + + for _, f := range node.Fields() { + forEachNodeOf(f.Name(), f, do) + } +} + +func fieldByName(node Node, name string) Field { + for _, f := range node.Fields() { + if f.Name() == name { + return f + } + } + return nil +} + +// findByPath navigates the node tree to find the node at the given path. +// Returns nil if the path doesn't exist. +// The path is a sequence of field names to traverse. +func findByPath(node Node, path []string) Node { + for _, name := range path { + field := fieldByName(node, name) + if field == nil { + return nil + } + node = field + } + return node +} + +// EqualNodes returns true if node1 and node2 are equal. +// +// Nodes that are not of the same repetition type (optional, required, repeated) +// or of the same hierarchical type (leaf, group) are considered not equal. +// Leaf nodes are considered equal if they are of the same data type. +// +// Groups are compared recursively, taking the order of fields into account +// (because it influences the column index of each leaf node), and comparing +// their logical types: for example, a MAP node is not equal to a GROUP node +// with the same fields, because MAP nodes have a specific logical type. +// +// Note that the encoding and compression of the nodes are not considered by this +// function. +func EqualNodes(node1, node2 Node) bool { + if node1.Leaf() { + return node2.Leaf() && leafNodesAreEqual(node1, node2) + } else { + return !node2.Leaf() && groupNodesAreEqual(node1, node2) + } +} + +// SameNodes returns true if node1 and node2 are equivalent, ignoring field order. +// +// Unlike EqualNodes, this function considers nodes with the same fields in different +// orders as equivalent. This is useful when comparing schemas that may have been +// reordered by operations like MergeNodes. +// +// For leaf nodes, this behaves identically to EqualNodes. +// For group nodes, this compares fields by name rather than position. +func SameNodes(node1, node2 Node) bool { + if node1.Leaf() { + return node2.Leaf() && leafNodesAreEqual(node1, node2) + } else { + return !node2.Leaf() && groupNodesAreSame(node1, node2) + } +} + +func repetitionsAreEqual(node1, node2 Node) bool { + return node1.Optional() == node2.Optional() && node1.Repeated() == node2.Repeated() +} + +func leafNodesAreEqual(node1, node2 Node) bool { + return EqualTypes(node1.Type(), node2.Type()) && repetitionsAreEqual(node1, node2) +} + +func groupNodesAreEqual(node1, node2 Node) bool { + fields1 := node1.Fields() + fields2 := node2.Fields() + if len(fields1) != len(fields2) { + return false + } + if !repetitionsAreEqual(node1, node2) { + return false + } + if !fieldsAreEqual(fields1, fields2, EqualNodes) { + return false + } + return equalLogicalTypes(node1.Type(), node2.Type()) +} + +func groupNodesAreSame(node1, node2 Node) bool { + fields1 := node1.Fields() + fields2 := node2.Fields() + if len(fields1) != len(fields2) { + return false + } + if !repetitionsAreEqual(node1, node2) { + return false + } + if !fieldsAreSorted(fields1) { + fields1 = slices.Clone(fields1) + sortFields(fields1) + } + if !fieldsAreSorted(fields2) { + fields2 = slices.Clone(fields2) + sortFields(fields2) + } + if !fieldsAreEqual(fields1, fields2, SameNodes) { + return false + } + return equalLogicalTypes(node1.Type(), node2.Type()) +} + +func fieldsAreEqual(fields1, fields2 []Field, equal func(Node, Node) bool) bool { + if len(fields1) != len(fields2) { + return false + } + for i := range fields1 { + if fields1[i].Name() != fields2[i].Name() { + return false + } + } + for i := range fields1 { + if !equal(fields1[i], fields2[i]) { + return false + } + } + return true +} + +func fieldsAreSorted(fields []Field) bool { + return slices.IsSortedFunc(fields, compareFields) +} + +func sortFields(fields []Field) { + slices.SortFunc(fields, compareFields) +} + +func compareFields(a, b Field) int { + return strings.Compare(a.Name(), b.Name()) +} diff --git a/vendor/github.com/parquet-go/parquet-go/null.go b/vendor/github.com/parquet-go/parquet-go/null.go new file mode 100644 index 000000000..9af7af1c4 --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/null.go @@ -0,0 +1,139 @@ +package parquet + +import ( + "reflect" + "time" + "unsafe" + + "github.com/parquet-go/bitpack/unsafecast" + "github.com/parquet-go/parquet-go/deprecated" + "github.com/parquet-go/parquet-go/internal/bytealg" + "github.com/parquet-go/parquet-go/sparse" +) + +// nullIndexFunc is the type of functions used to detect null values in rows. +// +// For each value of the rows array, the bitmap passed as first argument is +// populated to indicate whether the values were null (0) or not (1). +// +// The function writes one bit to the output buffer for each row in the input, +// the buffer must be sized accordingly. +type nullIndexFunc func(bits []uint64, rows sparse.Array) + +func nullIndex[T comparable](bits []uint64, rows sparse.Array) { + var zero T + for i := range rows.Len() { + v := *(*T)(rows.Index(i)) + if v != zero { + x := uint(i) / 64 + y := uint(i) % 64 + bits[x] |= 1 << y + } + } +} + +func nullIndexStruct(bits []uint64, rows sparse.Array) { + bytealg.Broadcast(unsafecast.Slice[byte](bits), 0xFF) +} + +func nullIndexTime(bits []uint64, rows sparse.Array) { + for i := range rows.Len() { + t := (*time.Time)(rows.Index(i)) + if !t.IsZero() { + x := uint(i) / 64 + y := uint(i) % 64 + bits[x] |= 1 << y + } + } +} + +func nullIndexFuncOf(t reflect.Type) nullIndexFunc { + switch t { + case reflect.TypeOf(deprecated.Int96{}): + return nullIndex[deprecated.Int96] + case reflect.TypeOf(time.Time{}): + return nullIndexTime + } + + switch t.Kind() { + case reflect.Bool: + return nullIndexBool + + case reflect.Int: + return nullIndexInt + + case reflect.Int8: + return nullIndexInt8 + + case reflect.Int16: + return nullIndexInt16 + + case reflect.Int32: + return nullIndexInt32 + + case reflect.Int64: + return nullIndexInt64 + + case reflect.Uint: + return nullIndexUint + + case reflect.Uint8: + return nullIndexUint8 + + case reflect.Uint16: + return nullIndexUint16 + + case reflect.Uint32: + return nullIndexUint32 + + case reflect.Uint64: + return nullIndexUint64 + + case reflect.Float32: + return nullIndexFloat32 + + case reflect.Float64: + return nullIndexFloat64 + + case reflect.String: + return nullIndexString + + case reflect.Slice: + return nullIndexSlice + + case reflect.Map: + return nullIndexPointer + + case reflect.Array: + if t.Elem().Kind() == reflect.Uint8 { + switch size := t.Len(); size { + case 16: + return nullIndexUint128 + default: + return nullIndexFuncOfByteArray(size) + } + } + + case reflect.Pointer: + return nullIndexPointer + + case reflect.Struct: + return nullIndexStruct + } + + panic("cannot convert Go values of type " + typeNameOf(t) + " to parquet value") +} + +func nullIndexFuncOfByteArray(n int) nullIndexFunc { + return func(bits []uint64, rows sparse.Array) { + for i := range rows.Len() { + p := (*byte)(rows.Index(i)) + b := unsafe.Slice(p, n) + if !isZero(b) { + x := uint(i) / 64 + y := uint(i) % 64 + bits[x] |= 1 << y + } + } + } +} diff --git a/vendor/github.com/parquet-go/parquet-go/null_amd64.go b/vendor/github.com/parquet-go/parquet-go/null_amd64.go new file mode 100644 index 000000000..a3d295db1 --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/null_amd64.go @@ -0,0 +1,90 @@ +//go:build !purego + +package parquet + +import "github.com/parquet-go/parquet-go/sparse" + +//go:noescape +func nullIndex8(bits *uint64, rows sparse.Array) + +//go:noescape +func nullIndex32(bits *uint64, rows sparse.Array) + +//go:noescape +func nullIndex64(bits *uint64, rows sparse.Array) + +//go:noescape +func nullIndex128(bits *uint64, rows sparse.Array) + +func nullIndexBool(bits []uint64, rows sparse.Array) { + nullIndex8(&bits[0], rows) +} + +func nullIndexInt(bits []uint64, rows sparse.Array) { + nullIndex64(&bits[0], rows) +} + +func nullIndexInt8(bits []uint64, rows sparse.Array) { + nullIndex8(&bits[0], rows) +} + +func nullIndexInt16(bits []uint64, rows sparse.Array) { + nullIndex[int16](bits, rows) +} + +func nullIndexInt32(bits []uint64, rows sparse.Array) { + nullIndex32(&bits[0], rows) +} + +func nullIndexInt64(bits []uint64, rows sparse.Array) { + nullIndex64(&bits[0], rows) +} + +func nullIndexUint(bits []uint64, rows sparse.Array) { + nullIndex64(&bits[0], rows) +} + +func nullIndexUint8(bits []uint64, rows sparse.Array) { + nullIndex8(&bits[0], rows) +} + +func nullIndexUint16(bits []uint64, rows sparse.Array) { + nullIndex[uint16](bits, rows) +} + +func nullIndexUint32(bits []uint64, rows sparse.Array) { + nullIndex32(&bits[0], rows) +} + +func nullIndexUint64(bits []uint64, rows sparse.Array) { + nullIndex64(&bits[0], rows) +} + +func nullIndexUint128(bits []uint64, rows sparse.Array) { + nullIndex128(&bits[0], rows) +} + +func nullIndexFloat32(bits []uint64, rows sparse.Array) { + nullIndex32(&bits[0], rows) +} + +func nullIndexFloat64(bits []uint64, rows sparse.Array) { + nullIndex64(&bits[0], rows) +} + +func nullIndexString(bits []uint64, rows sparse.Array) { + // We offset by an extra 8 bytes to test the lengths of string values where + // the first field is the pointer and the second is the length which we want + // to test. + nullIndex64(&bits[0], rows.Offset(8)) +} + +func nullIndexSlice(bits []uint64, rows sparse.Array) { + // Slice values are null if their pointer is nil, which is held in the first + // 8 bytes of the object so we can simply test 64 bits words. + nullIndex64(&bits[0], rows) +} + +func nullIndexPointer(bits []uint64, rows sparse.Array) { + nullIndex64(&bits[0], rows) +} diff --git a/vendor/github.com/parquet-go/parquet-go/null_amd64.s b/vendor/github.com/parquet-go/parquet-go/null_amd64.s new file mode 100644 index 000000000..52804d3f1 --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/null_amd64.s @@ -0,0 +1,227 @@ +//go:build !purego + +#include "textflag.h" + +// func nullIndex8(bits *uint64, rows sparse.Array) +TEXT ·nullIndex8(SB), NOSPLIT, $0-32 + MOVQ bits+0(FP), AX + MOVQ rows_array_ptr+8(FP), BX + MOVQ rows_array_len+16(FP), DI + MOVQ rows_array_off+24(FP), DX + + MOVQ $1, CX + XORQ SI, SI + + CMPQ DI, $0 + JE done +loop1x1: + XORQ R8, R8 + MOVB (BX), R9 + CMPB R9, $0 + JE next1x1 + + MOVQ SI, R10 + SHRQ $6, R10 + ORQ CX, (AX)(R10*8) +next1x1: + ADDQ DX, BX + ROLQ $1, CX + INCQ SI + CMPQ SI, DI + JNE loop1x1 +done: + RET + +// func nullIndex32(bits *uint64, rows sparse.Array) +TEXT ·nullIndex32(SB), NOSPLIT, $0-32 + MOVQ bits+0(FP), AX + MOVQ rows_array_ptr+8(FP), BX + MOVQ rows_array_len+16(FP), DI + MOVQ rows_array_off+24(FP), DX + + MOVQ $1, CX + XORQ SI, SI + + CMPQ DI, $0 + JE done + + CMPQ DI, $8 + JB loop1x4 + + CMPB ·hasAVX2(SB), $0 + JE loop1x4 + + MOVQ DI, R8 + SHRQ $3, R8 + SHLQ $3, R8 + + VPBROADCASTD rows_array_off+24(FP), Y0 + VPMULLD ·range0n8(SB), Y0, Y0 + VPCMPEQD Y1, Y1, Y1 + VPCMPEQD Y2, Y2, Y2 + VPXOR Y3, Y3, Y3 +loop8x4: + VPGATHERDD Y1, (BX)(Y0*1), Y4 + VPCMPEQD Y3, Y4, Y4 + VMOVMSKPS Y4, R9 + VMOVDQU Y2, Y1 + + NOTQ R9 + ANDQ $0b11111111, R9 + + MOVQ SI, CX + ANDQ $0b111111, CX + + MOVQ SI, R10 + SHRQ $6, R10 + + SHLQ CX, R9 + ORQ R9, (AX)(R10*8) + + LEAQ (BX)(DX*8), BX + ADDQ $8, SI + CMPQ SI, R8 + JNE loop8x4 + VZEROUPPER + + CMPQ SI, DI + JE done + + MOVQ $1, R8 + MOVQ SI, CX + ANDQ $0b111111, R8 + SHLQ CX, R8 + MOVQ R8, CX + +loop1x4: + MOVL (BX), R8 + CMPL R8, $0 + JE next1x4 + + MOVQ SI, R9 + SHRQ $6, R9 + ORQ CX, (AX)(R9*8) +next1x4: + ADDQ DX, BX + ROLQ $1, CX + INCQ SI + CMPQ SI, DI + JNE loop1x4 +done: + RET + +// func nullIndex64(bits *uint64, rows sparse.Array) +TEXT ·nullIndex64(SB), NOSPLIT, $0-32 + MOVQ bits+0(FP), AX + MOVQ rows_array_ptr+8(FP), BX + MOVQ rows_array_len+16(FP), DI + MOVQ rows_array_off+24(FP), DX + + MOVQ $1, CX + XORQ SI, SI + + CMPQ DI, $0 + JE done + + CMPQ DI, $4 + JB loop1x8 + + CMPB ·hasAVX2(SB), $0 + JE loop1x8 + + MOVQ DI, R8 + SHRQ $2, R8 + SHLQ $2, R8 + + VPBROADCASTQ rows_array_off+24(FP), Y0 + VPMULLD scale4x8<>(SB), Y0, Y0 + VPCMPEQQ Y1, Y1, Y1 + VPCMPEQQ Y2, Y2, Y2 + VPXOR Y3, Y3, Y3 +loop4x8: + VPGATHERQQ Y1, (BX)(Y0*1), Y4 + VPCMPEQQ Y3, Y4, Y4 + VMOVMSKPD Y4, R9 + VMOVDQU Y2, Y1 + + NOTQ R9 + ANDQ $0b1111, R9 + + MOVQ SI, CX + ANDQ $0b111111, CX + + MOVQ SI, R10 + SHRQ $6, R10 + + SHLQ CX, R9 + ORQ R9, (AX)(R10*8) + + LEAQ (BX)(DX*4), BX + ADDQ $4, SI + CMPQ SI, R8 + JNE loop4x8 + VZEROUPPER + + CMPQ SI, DI + JE done + + MOVQ $1, R8 + MOVQ SI, CX + ANDQ $0b111111, R8 + SHLQ CX, R8 + MOVQ R8, CX + +loop1x8: + MOVQ (BX), R8 + CMPQ R8, $0 + JE next1x8 + + MOVQ SI, R9 + SHRQ $6, R9 + ORQ CX, (AX)(R9*8) +next1x8: + ADDQ DX, BX + ROLQ $1, CX + INCQ SI + CMPQ SI, DI + JNE loop1x8 +done: + RET + +GLOBL scale4x8<>(SB), RODATA|NOPTR, $32 +DATA scale4x8<>+0(SB)/8, $0 +DATA scale4x8<>+8(SB)/8, $1 +DATA scale4x8<>+16(SB)/8, $2 +DATA scale4x8<>+24(SB)/8, $3 + +// func nullIndex128(bits *uint64, rows sparse.Array) +TEXT ·nullIndex128(SB), NOSPLIT, $0-32 + MOVQ bits+0(FP), AX + MOVQ rows_array_ptr+8(FP), BX + MOVQ rows_array_len+16(FP), DI + MOVQ rows_array_off+24(FP), DX + + CMPQ DI, $0 + JE done + + MOVQ $1, CX + XORQ SI, SI + PXOR X0, X0 +loop1x16: + MOVOU (BX), X1 + PCMPEQQ X0, X1 + MOVMSKPD X1, R8 + CMPB R8, $0b11 + JE next1x16 + + MOVQ SI, R9 + SHRQ $6, R9 + ORQ CX, (AX)(R9*8) +next1x16: + ADDQ DX, BX + ROLQ $1, CX + INCQ SI + CMPQ SI, DI + JNE loop1x16 +done: + RET diff --git a/vendor/github.com/parquet-go/parquet-go/null_purego.go b/vendor/github.com/parquet-go/parquet-go/null_purego.go new file mode 100644 index 000000000..47298a485 --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/null_purego.go @@ -0,0 +1,80 @@ +//go:build purego || !amd64 + +package parquet + +import "github.com/parquet-go/parquet-go/sparse" + +func nullIndexBool(bits []uint64, rows sparse.Array) { + nullIndex[bool](bits, rows) +} + +func nullIndexInt(bits []uint64, rows sparse.Array) { + nullIndex[int](bits, rows) +} + +func nullIndexInt8(bits []uint64, rows sparse.Array) { + nullIndex[int8](bits, rows) +} + +func nullIndexInt16(bits []uint64, rows sparse.Array) { + nullIndex[int16](bits, rows) +} + +func nullIndexInt32(bits []uint64, rows sparse.Array) { + nullIndex[int32](bits, rows) +} + +func nullIndexInt64(bits []uint64, rows sparse.Array) { + nullIndex[int64](bits, rows) +} + +func nullIndexUint(bits []uint64, rows sparse.Array) { + nullIndex[uint](bits, rows) +} + +func nullIndexUint8(bits []uint64, rows sparse.Array) { + nullIndex[uint8](bits, rows) +} + +func nullIndexUint16(bits []uint64, rows sparse.Array) { + nullIndex[uint16](bits, rows) +} + +func nullIndexUint32(bits []uint64, rows sparse.Array) { + nullIndex[uint32](bits, rows) +} + +func nullIndexUint64(bits []uint64, rows sparse.Array) { + nullIndex[uint64](bits, rows) +} + +func nullIndexUint128(bits []uint64, rows sparse.Array) { + nullIndex[[16]byte](bits, rows) +} + +func nullIndexFloat32(bits []uint64, rows sparse.Array) { + nullIndex[float32](bits, rows) +} + +func nullIndexFloat64(bits []uint64, rows sparse.Array) { + nullIndex[float64](bits, rows) +} + +func nullIndexString(bits []uint64, rows sparse.Array) { + nullIndex[string](bits, rows) +} + +func nullIndexSlice(bits []uint64, rows sparse.Array) { + for i := range rows.Len() { + p := *(**struct{})(rows.Index(i)) + b := uint64(0) + if p != nil { + b = 1 + } + bits[uint(i)/64] |= b << (uint(i) % 64) + } +} + +func nullIndexPointer(bits []uint64, rows sparse.Array) { + nullIndex[*struct{}](bits, rows) +} diff --git a/vendor/github.com/parquet-go/parquet-go/offset_index.go b/vendor/github.com/parquet-go/parquet-go/offset_index.go new file mode 100644 index 000000000..b499e427c --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/offset_index.go @@ -0,0 +1,128 @@ +package parquet + +import ( + "github.com/parquet-go/parquet-go/format" +) + +type OffsetIndex interface { + // NumPages returns the number of pages in the offset index. + NumPages() int + + // Offset returns the offset starting from the beginning of the file for the + // page at the given index. + Offset(int) int64 + + // CompressedPageSize returns the size of the page at the given index + // (in bytes). + CompressedPageSize(int) int64 + + // FirstRowIndex returns the the first row in the page at the given index. + // + // The returned row index is based on the row group that the page belongs + // to, the first row has index zero. + FirstRowIndex(int) int64 +} + +type FileOffsetIndex struct { + index *format.OffsetIndex +} + +func (i *FileOffsetIndex) NumPages() int { + return len(i.index.PageLocations) +} + +func (i *FileOffsetIndex) Offset(j int) int64 { + return i.index.PageLocations[j].Offset +} + +func (i *FileOffsetIndex) CompressedPageSize(j int) int64 { + return int64(i.index.PageLocations[j].CompressedPageSize) +} + +func (i *FileOffsetIndex) FirstRowIndex(j int) int64 { + return i.index.PageLocations[j].FirstRowIndex +} + +type emptyOffsetIndex struct{} + +func (emptyOffsetIndex) NumPages() int { return 0 } +func (emptyOffsetIndex) Offset(int) int64 { return 0 } +func (emptyOffsetIndex) CompressedPageSize(int) int64 { return 0 } +func (emptyOffsetIndex) FirstRowIndex(int) int64 { return 0 } + +type booleanOffsetIndex struct{ page *booleanPage } + +func (i booleanOffsetIndex) NumPages() int { return 1 } +func (i booleanOffsetIndex) Offset(int) int64 { return 0 } +func (i booleanOffsetIndex) CompressedPageSize(int) int64 { return i.page.Size() } +func (i booleanOffsetIndex) FirstRowIndex(int) int64 { return 0 } + +type int32OffsetIndex struct{ page *int32Page } + +func (i int32OffsetIndex) NumPages() int { return 1 } +func (i int32OffsetIndex) Offset(int) int64 { return 0 } +func (i int32OffsetIndex) CompressedPageSize(int) int64 { return i.page.Size() } +func (i int32OffsetIndex) FirstRowIndex(int) int64 { return 0 } + +type int64OffsetIndex struct{ page *int64Page } + +func (i int64OffsetIndex) NumPages() int { return 1 } +func (i int64OffsetIndex) Offset(int) int64 { return 0 } +func (i int64OffsetIndex) CompressedPageSize(int) int64 { return i.page.Size() } +func (i int64OffsetIndex) FirstRowIndex(int) int64 { return 0 } + +type int96OffsetIndex struct{ page *int96Page } + +func (i int96OffsetIndex) NumPages() int { return 1 } +func (i int96OffsetIndex) Offset(int) int64 { return 0 } +func (i int96OffsetIndex) CompressedPageSize(int) int64 { return i.page.Size() } +func (i int96OffsetIndex) FirstRowIndex(int) int64 { return 0 } + +type floatOffsetIndex struct{ page *floatPage } + +func (i floatOffsetIndex) NumPages() int { return 1 } +func (i floatOffsetIndex) Offset(int) int64 { return 0 } +func (i floatOffsetIndex) CompressedPageSize(int) int64 { return i.page.Size() } +func (i floatOffsetIndex) FirstRowIndex(int) int64 { return 0 } + +type doubleOffsetIndex struct{ page *doublePage } + +func (i doubleOffsetIndex) NumPages() int { return 1 } +func (i doubleOffsetIndex) Offset(int) int64 { return 0 } +func (i doubleOffsetIndex) CompressedPageSize(int) int64 { return i.page.Size() } +func (i doubleOffsetIndex) FirstRowIndex(int) int64 { return 0 } + +type byteArrayOffsetIndex struct{ page *byteArrayPage } + +func (i byteArrayOffsetIndex) NumPages() int { return 1 } +func (i byteArrayOffsetIndex) Offset(int) int64 { return 0 } +func (i byteArrayOffsetIndex) CompressedPageSize(int) int64 { return i.page.Size() } +func (i byteArrayOffsetIndex) FirstRowIndex(int) int64 { return 0 } + +type fixedLenByteArrayOffsetIndex struct{ page *fixedLenByteArrayPage } + +func (i fixedLenByteArrayOffsetIndex) NumPages() int { return 1 } +func (i fixedLenByteArrayOffsetIndex) Offset(int) int64 { return 0 } +func (i fixedLenByteArrayOffsetIndex) CompressedPageSize(int) int64 { return i.page.Size() } +func (i fixedLenByteArrayOffsetIndex) FirstRowIndex(int) int64 { return 0 } + +type uint32OffsetIndex struct{ page *uint32Page } + +func (i uint32OffsetIndex) NumPages() int { return 1 } +func (i uint32OffsetIndex) Offset(int) int64 { return 0 } +func (i uint32OffsetIndex) CompressedPageSize(int) int64 { return i.page.Size() } +func (i uint32OffsetIndex) FirstRowIndex(int) int64 { return 0 } + +type uint64OffsetIndex struct{ page *uint64Page } + +func (i uint64OffsetIndex) NumPages() int { return 1 } +func (i uint64OffsetIndex) Offset(int) int64 { return 0 } +func (i uint64OffsetIndex) CompressedPageSize(int) int64 { return i.page.Size() } +func (i uint64OffsetIndex) FirstRowIndex(int) int64 { return 0 } + +type be128OffsetIndex struct{ page *be128Page } + +func (i be128OffsetIndex) NumPages() int { return 1 } +func (i be128OffsetIndex) Offset(int) int64 { return 0 } +func (i be128OffsetIndex) CompressedPageSize(int) int64 { return i.page.Size() } +func (i be128OffsetIndex) FirstRowIndex(int) int64 { return 0 } diff --git a/vendor/github.com/parquet-go/parquet-go/order.go b/vendor/github.com/parquet-go/parquet-go/order.go new file mode 100644 index 000000000..6fef7bba2 --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/order.go @@ -0,0 +1,102 @@ +package parquet + +import ( + "bytes" + + "github.com/parquet-go/bitpack/unsafecast" +) + +func orderOfBool(data []bool) int { + switch len(data) { + case 0, 1: + return 0 + default: + k := 0 + i := 0 + + if data[0] { // true => false: descending + k = -1 + i = streakOfTrue(data) + if i == len(data) { + k = +1 + } else { + i += streakOfFalse(data[i:]) + } + } else { // false => true: ascending + k = +1 + i = streakOfFalse(data) + i += streakOfTrue(data[i:]) + } + + if i != len(data) { + k = 0 + } + return k + } +} + +func streakOfTrue(data []bool) int { + if i := bytes.IndexByte(unsafecast.Slice[byte](data), 0); i >= 0 { + return i + } + return len(data) +} + +func streakOfFalse(data []bool) int { + if i := bytes.IndexByte(unsafecast.Slice[byte](data), 1); i >= 0 { + return i + } + return len(data) +} + +func orderOfBytes(data [][]byte) int { + switch len(data) { + case 0, 1: + return 0 + } + data = skipBytesStreak(data) + if len(data) < 2 { + return 1 + } + ordering := bytes.Compare(data[0], data[1]) + switch { + case ordering < 0: + if bytesAreInAscendingOrder(data[1:]) { + return +1 + } + case ordering > 0: + if bytesAreInDescendingOrder(data[1:]) { + return -1 + } + } + return 0 +} + +func skipBytesStreak(data [][]byte) [][]byte { + for i := 1; i < len(data); i++ { + if !bytes.Equal(data[i], data[0]) { + return data[i-1:] + } + } + return data[len(data)-1:] +} + +func bytesAreInAscendingOrder(data [][]byte) bool { + for i := len(data) - 1; i > 0; i-- { + k := bytes.Compare(data[i-1], data[i]) + if k > 0 { + return false + } + } + return true +} + +func bytesAreInDescendingOrder(data [][]byte) bool { + for i := len(data) - 1; i > 0; i-- { + k := bytes.Compare(data[i-1], data[i]) + if k < 0 { + return false + } + } + return true +} diff --git a/vendor/github.com/parquet-go/parquet-go/order_amd64.go b/vendor/github.com/parquet-go/parquet-go/order_amd64.go new file mode 100644 index 000000000..8ac597d62 --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/order_amd64.go @@ -0,0 +1,21 @@ +//go:build !purego + +package parquet + +//go:noescape +func orderOfInt32(data []int32) int + +//go:noescape +func orderOfInt64(data []int64) int + +//go:noescape +func orderOfUint32(data []uint32) int + +//go:noescape +func orderOfUint64(data []uint64) int + +//go:noescape +func orderOfFloat32(data []float32) int + +//go:noescape +func orderOfFloat64(data []float64) int diff --git a/vendor/github.com/parquet-go/parquet-go/order_amd64.s b/vendor/github.com/parquet-go/parquet-go/order_amd64.s new file mode 100644 index 000000000..651d2510b --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/order_amd64.s @@ -0,0 +1,547 @@ +//go:build !purego + +#include "textflag.h" + +#define UNDEFINED 0 +#define ASCENDING 1 +#define DESCENDING -1 + +DATA shift1x32<>+0(SB)/4, $1 +DATA shift1x32<>+4(SB)/4, $2 +DATA shift1x32<>+8(SB)/4, $3 +DATA shift1x32<>+12(SB)/4, $4 +DATA shift1x32<>+16(SB)/4, $5 +DATA shift1x32<>+20(SB)/4, $6 +DATA shift1x32<>+24(SB)/4, $7 +DATA shift1x32<>+28(SB)/4, $8 +DATA shift1x32<>+32(SB)/4, $9 +DATA shift1x32<>+36(SB)/4, $10 +DATA shift1x32<>+40(SB)/4, $11 +DATA shift1x32<>+44(SB)/4, $12 +DATA shift1x32<>+48(SB)/4, $13 +DATA shift1x32<>+52(SB)/4, $14 +DATA shift1x32<>+56(SB)/4, $15 +DATA shift1x32<>+60(SB)/4, $15 +GLOBL shift1x32<>(SB), RODATA|NOPTR, $64 + +DATA shift1x64<>+0(SB)/4, $1 +DATA shift1x64<>+8(SB)/4, $2 +DATA shift1x64<>+16(SB)/4, $3 +DATA shift1x64<>+24(SB)/4, $4 +DATA shift1x64<>+32(SB)/4, $5 +DATA shift1x64<>+40(SB)/4, $6 +DATA shift1x64<>+48(SB)/4, $7 +DATA shift1x64<>+56(SB)/4, $7 +GLOBL shift1x64<>(SB), RODATA|NOPTR, $64 + +// func orderOfInt32(data []int32) int +TEXT ·orderOfInt32(SB), NOSPLIT, $-32 + MOVQ data_base+0(FP), R8 + MOVQ data_len+8(FP), R9 + XORQ SI, SI + XORQ DI, DI + + CMPQ R9, $2 + JB undefined + + CMPB ·hasAVX512VL(SB), $0 + JE test + + CMPQ R9, $16 + JB test + + XORQ DX, DX + MOVQ R9, AX + SHRQ $4, AX + SHLQ $4, AX + MOVQ $15, CX + IDIVQ CX + IMULQ $15, AX + DECQ R9 + + VMOVDQU32 shift1x32<>(SB), Z2 + KXORW K2, K2, K2 +testAscending15: + VMOVDQU32 (R8)(SI*4), Z0 + VMOVDQU32 Z2, Z1 + VPERMI2D Z0, Z0, Z1 + VPCMPD $2, Z1, Z0, K1 + KORTESTW K2, K1 + JNC testDescending15 + ADDQ $15, SI + CMPQ SI, AX + JNE testAscending15 + VZEROUPPER + JMP testAscending +testDescending15: + VMOVDQU32 (R8)(DI*4), Z0 + VMOVDQU32 Z2, Z1 + VPERMI2D Z0, Z0, Z1 + VPCMPD $5, Z1, Z0, K1 + KORTESTW K2, K1 + JNC undefined15 + ADDQ $15, DI + CMPQ DI, AX + JNE testDescending15 + VZEROUPPER + JMP testDescending + +test: + DECQ R9 +testAscending: + CMPQ SI, R9 + JAE ascending + MOVL (R8)(SI*4), BX + MOVL 4(R8)(SI*4), DX + INCQ SI + CMPL BX, DX + JLE testAscending + JMP testDescending +ascending: + MOVQ $ASCENDING, ret+24(FP) + RET +testDescending: + CMPQ DI, R9 + JAE descending + MOVL (R8)(DI*4), BX + MOVL 4(R8)(DI*4), DX + INCQ DI + CMPL BX, DX + JGE testDescending + JMP undefined +descending: + MOVQ $DESCENDING, ret+24(FP) + RET +undefined15: + VZEROUPPER +undefined: + MOVQ $UNDEFINED, ret+24(FP) + RET + +// func orderOfInt64(data []int64) int +TEXT ·orderOfInt64(SB), NOSPLIT, $-32 + MOVQ data_base+0(FP), R8 + MOVQ data_len+8(FP), R9 + XORQ SI, SI + XORQ DI, DI + + CMPQ R9, $2 + JB undefined + + CMPB ·hasAVX512VL(SB), $0 + JE test + + CMPQ R9, $8 + JB test + + XORQ DX, DX + MOVQ R9, AX + SHRQ $3, AX + SHLQ $3, AX + MOVQ $7, CX + IDIVQ CX + IMULQ $7, AX + DECQ R9 + + VMOVDQU64 shift1x64<>(SB), Z2 + KXORB K2, K2, K2 +testAscending7: + VMOVDQU64 (R8)(SI*8), Z0 + VMOVDQU64 Z2, Z1 + VPERMI2Q Z0, Z0, Z1 + VPCMPQ $2, Z1, Z0, K1 + KORTESTB K2, K1 + JNC testDescending7 + ADDQ $7, SI + CMPQ SI, AX + JNE testAscending7 + VZEROUPPER + JMP testAscending +testDescending7: + VMOVDQU64 (R8)(DI*8), Z0 + VMOVDQU64 Z2, Z1 + VPERMI2Q Z0, Z0, Z1 + VPCMPQ $5, Z1, Z0, K1 + KORTESTB K2, K1 + JNC undefined7 + ADDQ $7, DI + CMPQ DI, AX + JNE testDescending7 + VZEROUPPER + JMP testDescending + +test: + DECQ R9 +testAscending: + CMPQ SI, R9 + JAE ascending + MOVQ (R8)(SI*8), BX + MOVQ 8(R8)(SI*8), DX + INCQ SI + CMPQ BX, DX + JLE testAscending + JMP testDescending +ascending: + MOVQ $ASCENDING, ret+24(FP) + RET +testDescending: + CMPQ DI, R9 + JAE descending + MOVQ (R8)(DI*8), BX + MOVQ 8(R8)(DI*8), DX + INCQ DI + CMPQ BX, DX + JGE testDescending + JMP undefined +descending: + MOVQ $DESCENDING, ret+24(FP) + RET +undefined7: + VZEROUPPER +undefined: + MOVQ $UNDEFINED, ret+24(FP) + RET + +// func orderOfUint32(data []uint32) int +TEXT ·orderOfUint32(SB), NOSPLIT, $-32 + MOVQ data_base+0(FP), R8 + MOVQ data_len+8(FP), R9 + XORQ SI, SI + XORQ DI, DI + + CMPQ R9, $2 + JB undefined + + CMPB ·hasAVX512VL(SB), $0 + JE test + + CMPQ R9, $16 + JB test + + XORQ DX, DX + MOVQ R9, AX + SHRQ $4, AX + SHLQ $4, AX + MOVQ $15, CX + IDIVQ CX + IMULQ $15, AX + DECQ R9 + + VMOVDQU32 shift1x32<>(SB), Z2 + KXORW K2, K2, K2 +testAscending15: + VMOVDQU32 (R8)(SI*4), Z0 + VMOVDQU32 Z2, Z1 + VPERMI2D Z0, Z0, Z1 + VPCMPUD $2, Z1, Z0, K1 + KORTESTW K2, K1 + JNC testDescending15 + ADDQ $15, SI + CMPQ SI, AX + JNE testAscending15 + VZEROUPPER + JMP testAscending +testDescending15: + VMOVDQU32 (R8)(DI*4), Z0 + VMOVDQU32 Z2, Z1 + VPERMI2D Z0, Z0, Z1 + VPCMPUD $5, Z1, Z0, K1 + KORTESTW K2, K1 + JNC undefined15 + ADDQ $15, DI + CMPQ DI, AX + JNE testDescending15 + VZEROUPPER + JMP testDescending + +test: + DECQ R9 +testAscending: + CMPQ SI, R9 + JAE ascending + MOVL (R8)(SI*4), BX + MOVL 4(R8)(SI*4), DX + INCQ SI + CMPL BX, DX + JBE testAscending + JMP testDescending +ascending: + MOVQ $ASCENDING, ret+24(FP) + RET +testDescending: + CMPQ DI, R9 + JAE descending + MOVL (R8)(DI*4), BX + MOVL 4(R8)(DI*4), DX + INCQ DI + CMPL BX, DX + JAE testDescending + JMP undefined +descending: + MOVQ $DESCENDING, ret+24(FP) + RET +undefined15: + VZEROUPPER +undefined: + MOVQ $UNDEFINED, ret+24(FP) + RET + +// func orderOfUint64(data []uint64) int +TEXT ·orderOfUint64(SB), NOSPLIT, $-32 + MOVQ data_base+0(FP), R8 + MOVQ data_len+8(FP), R9 + XORQ SI, SI + XORQ DI, DI + + CMPQ R9, $2 + JB undefined + + CMPB ·hasAVX512VL(SB), $0 + JE test + + CMPQ R9, $8 + JB test + + XORQ DX, DX + MOVQ R9, AX + SHRQ $3, AX + SHLQ $3, AX + MOVQ $7, CX + IDIVQ CX + IMULQ $7, AX + DECQ R9 + + VMOVDQU64 shift1x64<>(SB), Z2 + KXORB K2, K2, K2 +testAscending7: + VMOVDQU64 (R8)(SI*8), Z0 + VMOVDQU64 Z2, Z1 + VPERMI2Q Z0, Z0, Z1 + VPCMPUQ $2, Z1, Z0, K1 + KORTESTB K2, K1 + JNC testDescending7 + ADDQ $7, SI + CMPQ SI, AX + JNE testAscending7 + VZEROUPPER + JMP testAscending +testDescending7: + VMOVDQU64 (R8)(DI*8), Z0 + VMOVDQU64 Z2, Z1 + VPERMI2Q Z0, Z0, Z1 + VPCMPUQ $5, Z1, Z0, K1 + KORTESTB K2, K1 + JNC undefined7 + ADDQ $7, DI + CMPQ DI, AX + JNE testDescending7 + VZEROUPPER + JMP testDescending + +test: + DECQ R9 +testAscending: + CMPQ SI, R9 + JAE ascending + MOVQ (R8)(SI*8), BX + MOVQ 8(R8)(SI*8), DX + INCQ SI + CMPQ BX, DX + JBE testAscending + JMP testDescending +ascending: + MOVQ $ASCENDING, ret+24(FP) + RET +testDescending: + CMPQ DI, R9 + JAE descending + MOVQ (R8)(DI*8), BX + MOVQ 8(R8)(DI*8), DX + INCQ DI + CMPQ BX, DX + JAE testDescending + JMP undefined +descending: + MOVQ $DESCENDING, ret+24(FP) + RET +undefined7: + VZEROUPPER +undefined: + MOVQ $UNDEFINED, ret+24(FP) + RET + +// func orderOfFloat32(data []float32) int +TEXT ·orderOfFloat32(SB), NOSPLIT, $-32 + MOVQ data_base+0(FP), R8 + MOVQ data_len+8(FP), R9 + XORQ SI, SI + XORQ DI, DI + + CMPQ R9, $2 + JB undefined + + CMPB ·hasAVX512VL(SB), $0 + JE test + + CMPQ R9, $16 + JB test + + XORQ DX, DX + MOVQ R9, AX + SHRQ $4, AX + SHLQ $4, AX + MOVQ $15, CX + IDIVQ CX + IMULQ $15, AX + DECQ R9 + + VMOVDQU32 shift1x32<>(SB), Z2 + KXORW K2, K2, K2 +testAscending15: + VMOVDQU32 (R8)(SI*4), Z0 + VMOVDQU32 Z2, Z1 + VPERMI2D Z0, Z0, Z1 + VCMPPS $2, Z1, Z0, K1 + KORTESTW K2, K1 + JNC testDescending15 + ADDQ $15, SI + CMPQ SI, AX + JNE testAscending15 + VZEROUPPER + JMP testAscending +testDescending15: + VMOVDQU32 (R8)(DI*4), Z0 + VMOVDQU32 Z2, Z1 + VPERMI2D Z0, Z0, Z1 + VCMPPS $5, Z1, Z0, K1 + KORTESTW K2, K1 + JNC undefined15 + ADDQ $15, DI + CMPQ DI, AX + JNE testDescending15 + VZEROUPPER + JMP testDescending + +test: + DECQ R9 +testAscending: + CMPQ SI, R9 + JAE ascending + MOVLQZX (R8)(SI*4), BX + MOVLQZX 4(R8)(SI*4), DX + INCQ SI + MOVQ BX, X0 + MOVQ DX, X1 + UCOMISS X1, X0 + JBE testAscending + JMP testDescending +ascending: + MOVQ $ASCENDING, ret+24(FP) + RET +testDescending: + CMPQ DI, R9 + JAE descending + MOVLQZX (R8)(DI*4), BX + MOVLQZX 4(R8)(DI*4), DX + INCQ DI + MOVQ BX, X0 + MOVQ DX, X1 + UCOMISS X1, X0 + JAE testDescending + JMP undefined +descending: + MOVQ $DESCENDING, ret+24(FP) + RET +undefined15: + VZEROUPPER +undefined: + MOVQ $UNDEFINED, ret+24(FP) + RET + +// func orderOfFloat64(data []uint64) int +TEXT ·orderOfFloat64(SB), NOSPLIT, $-32 + MOVQ data_base+0(FP), R8 + MOVQ data_len+8(FP), R9 + XORQ SI, SI + XORQ DI, DI + + CMPQ R9, $2 + JB undefined + + CMPB ·hasAVX512VL(SB), $0 + JE test + + CMPQ R9, $8 + JB test + + XORQ DX, DX + MOVQ R9, AX + SHRQ $3, AX + SHLQ $3, AX + MOVQ $7, CX + IDIVQ CX + IMULQ $7, AX + DECQ R9 + + VMOVDQU64 shift1x64<>(SB), Z2 + KXORB K2, K2, K2 +testAscending7: + VMOVDQU64 (R8)(SI*8), Z0 + VMOVDQU64 Z2, Z1 + VPERMI2Q Z0, Z0, Z1 + VCMPPD $2, Z1, Z0, K1 + KORTESTB K2, K1 + JNC testDescending7 + ADDQ $7, SI + CMPQ SI, AX + JNE testAscending7 + VZEROUPPER + JMP testAscending +testDescending7: + VMOVDQU64 (R8)(DI*8), Z0 + VMOVDQU64 Z2, Z1 + VPERMI2Q Z0, Z0, Z1 + VCMPPD $5, Z1, Z0, K1 + KORTESTB K2, K1 + JNC undefined7 + ADDQ $7, DI + CMPQ DI, AX + JNE testDescending7 + VZEROUPPER + JMP testDescending + +test: + DECQ R9 +testAscending: + CMPQ SI, R9 + JAE ascending + MOVQ (R8)(SI*8), BX + MOVQ 8(R8)(SI*8), DX + INCQ SI + MOVQ BX, X0 + MOVQ DX, X1 + UCOMISD X1, X0 + JBE testAscending + JMP testDescending +ascending: + MOVQ $ASCENDING, ret+24(FP) + RET +testDescending: + CMPQ DI, R9 + JAE descending + MOVQ (R8)(DI*8), BX + MOVQ 8(R8)(DI*8), DX + INCQ DI + MOVQ BX, X0 + MOVQ DX, X1 + UCOMISD X1, X0 + JAE testDescending + JMP undefined +descending: + MOVQ $DESCENDING, ret+24(FP) + RET +undefined7: + VZEROUPPER +undefined: + MOVQ $UNDEFINED, ret+24(FP) + RET diff --git a/vendor/github.com/parquet-go/parquet-go/order_purego.go b/vendor/github.com/parquet-go/parquet-go/order_purego.go new file mode 100644 index 000000000..44c4d7905 --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/order_purego.go @@ -0,0 +1,42 @@ +//go:build purego || !amd64 + +package parquet + +import "cmp" + +func orderOfInt32(data []int32) int { return orderOf(data) } +func orderOfInt64(data []int64) int { return orderOf(data) } +func orderOfUint32(data []uint32) int { return orderOf(data) } +func orderOfUint64(data []uint64) int { return orderOf(data) } +func orderOfFloat32(data []float32) int { return orderOf(data) } +func orderOfFloat64(data []float64) int { return orderOf(data) } + +func orderOf[T cmp.Ordered](data []T) int { + if len(data) > 1 { + if orderIsAscending(data) { + return +1 + } + if orderIsDescending(data) { + return -1 + } + } + return 0 +} + +func orderIsAscending[T cmp.Ordered](data []T) bool { + for i := len(data) - 1; i > 0; i-- { + if data[i-1] > data[i] { + return false + } + } + return true +} + +func orderIsDescending[T cmp.Ordered](data []T) bool { + for i := len(data) - 1; i > 0; i-- { + if data[i-1] < data[i] { + return false + } + } + return true +} diff --git a/vendor/github.com/parquet-go/parquet-go/page.go b/vendor/github.com/parquet-go/parquet-go/page.go new file mode 100644 index 000000000..605fcfb33 --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/page.go @@ -0,0 +1,414 @@ +package parquet + +import ( + "errors" + "fmt" + "io" + + "github.com/parquet-go/parquet-go/encoding" + "github.com/parquet-go/parquet-go/internal/debug" +) + +// Page values represent sequences of parquet values. From the Parquet +// documentation: "Column chunks are a chunk of the data for a particular +// column. They live in a particular row group and are guaranteed to be +// contiguous in the file. Column chunks are divided up into pages. A page is +// conceptually an indivisible unit (in terms of compression and encoding). +// There can be multiple page types which are interleaved in a column chunk." +// +// https://github.com/apache/parquet-format#glossary +type Page interface { + // Returns the type of values read from this page. + // + // The returned type can be used to encode the page data, in the case of + // an indexed page (which has a dictionary), the type is configured to + // encode the indexes stored in the page rather than the plain values. + Type() Type + + // Returns the column index that this page belongs to. + Column() int + + // If the page contains indexed values, calling this method returns the + // dictionary in which the values are looked up. Otherwise, the method + // returns nil. + Dictionary() Dictionary + + // Returns the number of rows, values, and nulls in the page. The number of + // rows may be less than the number of values in the page if the page is + // part of a repeated column. + NumRows() int64 + NumValues() int64 + NumNulls() int64 + + // Returns the page's min and max values. + // + // The third value is a boolean indicating whether the page bounds were + // available. Page bounds may not be known if the page contained no values + // or only nulls, or if they were read from a parquet file which had neither + // page statistics nor a page index. + Bounds() (min, max Value, ok bool) + + // Returns the size of the page in bytes (uncompressed). + Size() int64 + + // Returns a reader exposing the values contained in the page. + // + // Depending on the underlying implementation, the returned reader may + // support reading an array of typed Go values by implementing interfaces + // like parquet.Int32Reader. Applications should use type assertions on + // the returned reader to determine whether those optimizations are + // available. + // + // In the data page format version 1, it wasn't specified whether pages + // must start with a new row. Legacy writers have produced parquet files + // where row values were overlapping between two consecutive pages. + // As a result, the values read must not be assumed to start at the + // beginning of a row, unless the program knows that it is only working + // with parquet files that used the data page format version 2 (which is + // the default behavior for parquet-go). + Values() ValueReader + + // Returns a new page which is as slice of the receiver between row indexes + // i and j. + Slice(i, j int64) Page + + // Expose the lists of repetition and definition levels of the page. + // + // The returned slices may be empty when the page has no repetition or + // definition levels. + RepetitionLevels() []byte + DefinitionLevels() []byte + + // Returns the in-memory buffer holding the page values. + // + // The intent is for the returned value to be used as input parameter when + // calling the Encode method of the associated Type. + // + // The slices referenced by the encoding.Values may be the same across + // multiple calls to this method, applications must treat the content as + // immutable. + Data() encoding.Values +} + +// PageReader is an interface implemented by types that support producing a +// sequence of pages. +type PageReader interface { + // Reads and returns the next page from the sequence. When all pages have + // been read, or if the sequence was closed, the method returns io.EOF. + ReadPage() (Page, error) +} + +// PageWriter is an interface implemented by types that support writing pages +// to an underlying storage medium. +type PageWriter interface { + WritePage(Page) (int64, error) +} + +// Pages is an interface implemented by page readers returned by calling the +// Pages method of ColumnChunk instances. +type Pages interface { + PageReader + RowSeeker + io.Closer +} + +// AsyncPages wraps the given Pages instance to perform page reads +// asynchronously in a separate goroutine. +// +// Performing page reads asynchronously is important when the application may +// be reading pages from a high latency backend, and the last +// page read may be processed while initiating reading of the next page. +func AsyncPages(pages Pages) Pages { + read := make(chan asyncPage) + seek := make(chan asyncSeek, 1) + init := make(chan struct{}) + done := make(chan struct{}) + + go readPages(pages, read, seek, init, done) + + p := &asyncPages{ + read: read, + seek: seek, + init: init, + done: done, + } + + // If the pages object gets garbage collected without Close being called, + // this finalizer would ensure that the goroutine is stopped and doesn't + // leak. + debug.SetFinalizer(p, func(p *asyncPages) { p.Close() }) + return p +} + +type asyncPages struct { + read chan asyncPage + seek chan asyncSeek + init chan struct{} + done chan struct{} + version int64 +} + +type asyncPage struct { + page Page + err error + version int64 +} + +type asyncSeek struct { + rowIndex int64 + version int64 +} + +func (pages *asyncPages) Close() (err error) { + if pages.init != nil { + close(pages.init) + pages.init = nil + } + if pages.done != nil { + close(pages.done) + pages.done = nil + } + for p := range pages.read { + Release(p.page) + + // Capture the last error, which is the value returned from closing the + // underlying Pages instance. + err = p.err + } + pages.seek = nil + return err +} + +func (pages *asyncPages) ReadPage() (Page, error) { + pages.start() + for { + p, ok := <-pages.read + if !ok { + return nil, io.EOF + } + // Because calls to SeekToRow might be made concurrently to reading + // pages, it is possible for ReadPage to see pages that were read before + // the last SeekToRow call. + // + // A version number is attached to each page read asynchronously to + // discard outdated pages and ensure that we maintain a consistent view + // of the sequence of pages read. + if p.version == pages.version { + return p.page, p.err + } + + // the page is being dropped here b/c it was the wrong version + Release(p.page) + } +} + +func (pages *asyncPages) SeekToRow(rowIndex int64) error { + if pages.seek == nil { + return io.ErrClosedPipe + } + // First flush the channel in case SeekToRow is called twice or more in a + // row, otherwise we would block if readPages had already exited. + select { + case <-pages.seek: + default: + pages.version++ + } + // The seek channel has a capacity of 1 to allow the first SeekToRow call to + // be non-blocking. + // + // If SeekToRow calls are performed faster than they can be handled by the + // goroutine reading pages, this path might become a contention point. + pages.seek <- asyncSeek{rowIndex: rowIndex, version: pages.version} + pages.start() + return nil +} + +func (pages *asyncPages) start() { + if pages.init != nil { + close(pages.init) + pages.init = nil + } +} + +func readPages(pages Pages, read chan<- asyncPage, seek <-chan asyncSeek, init, done <-chan struct{}) { + defer func() { + read <- asyncPage{err: pages.Close(), version: -1} + close(read) + }() + + // To avoid reading pages before the first SeekToRow call, we wait for the + // reader to be initialized, which means it either received a call to + // ReadPage, SeekToRow, or Close. + select { + case <-init: + case <-done: + return + } + + // If SeekToRow was invoked before ReadPage, the seek channel contains the + // new position of the reader. + // + // Note that we have a default case in this select because we don't want to + // block if the first call was ReadPage and no values were ever produced to + // the seek channel. + var seekTo asyncSeek + select { + case seekTo = <-seek: + default: + seekTo.rowIndex = -1 + } + + var err error + + for { + var page Page + + // if err is not fatal we consider the underlying pages object to be in an unknown state + // and we only repeatedly return that error + if !isFatalError(err) { + if seekTo.rowIndex >= 0 { + err = pages.SeekToRow(seekTo.rowIndex) + if err == nil { + seekTo.rowIndex = -1 + continue + } + } else { + page, err = pages.ReadPage() + } + } + + select { + case read <- asyncPage{ + page: page, + err: err, + version: seekTo.version, + }: + case seekTo = <-seek: + Release(page) + case <-done: + Release(page) + return + } + } +} + +func isFatalError(err error) bool { + return err != nil && err != io.EOF && !errors.Is(err, ErrSeekOutOfRange) // ErrSeekOutOfRange can be returned from FilePages but is recoverable +} + +type singlePage struct { + page Page + seek int64 + numRows int64 +} + +func (r *singlePage) ReadPage() (Page, error) { + if r.page != nil { + if r.seek < r.numRows { + seek := r.seek + r.seek = r.numRows + if seek > 0 { + return r.page.Slice(seek, r.numRows), nil + } + return r.page, nil + } + } + return nil, io.EOF +} + +func (r *singlePage) SeekToRow(rowIndex int64) error { + r.seek = rowIndex + return nil +} + +func (r *singlePage) Close() error { + r.page = nil + r.seek = 0 + return nil +} + +func onePage(page Page) Pages { + return &singlePage{page: page, numRows: page.NumRows()} +} + +// CopyPages copies pages from src to dst, returning the number of values that +// were copied. +// +// The function returns any error it encounters reading or writing pages, except +// for io.EOF from the reader which indicates that there were no more pages to +// read. +func CopyPages(dst PageWriter, src PageReader) (numValues int64, err error) { + for { + p, err := src.ReadPage() + if err != nil { + if err == io.EOF { + err = nil + } + return numValues, err + } + n, err := dst.WritePage(p) + numValues += n + Release(p) + if err != nil { + return numValues, err + } + } +} + +// errorPage is an implementation of the Page interface which always errors when +// values are read from it. +type errorPage struct { + typ Type + err error + columnIndex int +} + +func newErrorPage(typ Type, columnIndex int, msg string, args ...any) *errorPage { + return &errorPage{ + typ: typ, + err: fmt.Errorf(msg, args...), + columnIndex: columnIndex, + } +} + +func (page *errorPage) Type() Type { return page.typ } +func (page *errorPage) Column() int { return page.columnIndex } +func (page *errorPage) Dictionary() Dictionary { return nil } +func (page *errorPage) NumRows() int64 { return 1 } +func (page *errorPage) NumValues() int64 { return 1 } +func (page *errorPage) NumNulls() int64 { return 0 } +func (page *errorPage) Bounds() (min, max Value, ok bool) { return } +func (page *errorPage) Slice(i, j int64) Page { return page } +func (page *errorPage) Size() int64 { return 1 } +func (page *errorPage) RepetitionLevels() []byte { return nil } +func (page *errorPage) DefinitionLevels() []byte { return nil } +func (page *errorPage) Data() encoding.Values { return encoding.Values{} } +func (page *errorPage) Values() ValueReader { return errorPageValues{page: page} } + +type errorPageValues struct{ page *errorPage } + +func (r errorPageValues) ReadValues([]Value) (int, error) { return 0, r.page.err } +func (r errorPageValues) Close() error { return nil } + +func errPageBoundsOutOfRange(i, j, n int64) error { + return fmt.Errorf("page bounds out of range [%d:%d]: with length %d", i, j, n) +} + +var ( + _ Page = (*optionalPage)(nil) + _ Page = (*repeatedPage)(nil) + _ Page = (*booleanPage)(nil) + _ Page = (*int32Page)(nil) + _ Page = (*int64Page)(nil) + _ Page = (*int96Page)(nil) + _ Page = (*floatPage)(nil) + _ Page = (*doublePage)(nil) + _ Page = (*byteArrayPage)(nil) + _ Page = (*fixedLenByteArrayPage)(nil) + _ Page = (*uint32Page)(nil) + _ Page = (*uint64Page)(nil) + _ Page = (*be128Page)(nil) + _ Page = (*nullPage)(nil) + _ Pages = (*singlePage)(nil) + _ PageReader = (*singlePage)(nil) +) diff --git a/vendor/github.com/parquet-go/parquet-go/page_be128.go b/vendor/github.com/parquet-go/parquet-go/page_be128.go new file mode 100644 index 000000000..30f62d0f9 --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/page_be128.go @@ -0,0 +1,99 @@ +package parquet + +import ( + "io" + + "github.com/parquet-go/parquet-go/encoding" +) + +type be128Page struct { + typ Type + values [][16]byte + columnIndex uint16 +} + +func newBE128Page(typ Type, columnIndex uint16, numValues int32, values encoding.Values) *be128Page { + return &be128Page{ + typ: typ, + values: values.Uint128()[:numValues], + columnIndex: ^columnIndex, + } +} + +func (page *be128Page) Type() Type { return page.typ } + +func (page *be128Page) Column() int { return int(^page.columnIndex) } + +func (page *be128Page) Dictionary() Dictionary { return nil } + +func (page *be128Page) NumRows() int64 { return int64(len(page.values)) } + +func (page *be128Page) NumValues() int64 { return int64(len(page.values)) } + +func (page *be128Page) NumNulls() int64 { return 0 } + +func (page *be128Page) Size() int64 { return 16 * int64(len(page.values)) } + +func (page *be128Page) RepetitionLevels() []byte { return nil } + +func (page *be128Page) DefinitionLevels() []byte { return nil } + +func (page *be128Page) Data() encoding.Values { return encoding.Uint128Values(page.values) } + +func (page *be128Page) Values() ValueReader { return &be128PageValues{page: page} } + +func (page *be128Page) min() []byte { return minBE128(page.values) } + +func (page *be128Page) max() []byte { return maxBE128(page.values) } + +func (page *be128Page) bounds() (min, max []byte) { return boundsBE128(page.values) } + +func (page *be128Page) Bounds() (min, max Value, ok bool) { + if ok = len(page.values) > 0; ok { + minBytes, maxBytes := page.bounds() + min = page.makeValueBytes(minBytes) + max = page.makeValueBytes(maxBytes) + } + return min, max, ok +} + +func (page *be128Page) Slice(i, j int64) Page { + return &be128Page{ + typ: page.typ, + values: page.values[i:j], + columnIndex: page.columnIndex, + } +} + +func (page *be128Page) makeValue(v *[16]byte) Value { + return page.makeValueBytes(v[:]) +} + +func (page *be128Page) makeValueBytes(v []byte) Value { + value := makeValueBytes(FixedLenByteArray, v) + value.columnIndex = page.columnIndex + return value +} + +func (page *be128Page) makeValueString(v string) Value { + value := makeValueString(FixedLenByteArray, v) + value.columnIndex = page.columnIndex + return value +} + +type be128PageValues struct { + page *be128Page + offset int +} + +func (r *be128PageValues) ReadValues(values []Value) (n int, err error) { + for n < len(values) && r.offset < len(r.page.values) { + values[n] = r.page.makeValue(&r.page.values[r.offset]) + r.offset++ + n++ + } + if r.offset == len(r.page.values) { + err = io.EOF + } + return n, err +} diff --git a/vendor/github.com/parquet-go/parquet-go/page_boolean.go b/vendor/github.com/parquet-go/parquet-go/page_boolean.go new file mode 100644 index 000000000..27418e8c9 --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/page_boolean.go @@ -0,0 +1,157 @@ +package parquet + +import ( + "io" + + "github.com/parquet-go/bitpack" + "github.com/parquet-go/parquet-go/encoding" + "github.com/parquet-go/parquet-go/internal/memory" +) + +type booleanPage struct { + typ Type + bits memory.SliceBuffer[byte] + offset int32 + numValues int32 + columnIndex uint16 +} + +func newBooleanPage(typ Type, columnIndex uint16, numValues int32, values encoding.Values) *booleanPage { + return &booleanPage{ + typ: typ, + bits: memory.SliceBufferFrom(values.Boolean()[:bitpack.ByteCount(uint(numValues))]), + numValues: numValues, + columnIndex: ^columnIndex, + } +} + +func (page *booleanPage) Type() Type { return page.typ } + +func (page *booleanPage) Column() int { return int(^page.columnIndex) } + +func (page *booleanPage) Dictionary() Dictionary { return nil } + +func (page *booleanPage) NumRows() int64 { return int64(page.numValues) } + +func (page *booleanPage) NumValues() int64 { return int64(page.numValues) } + +func (page *booleanPage) NumNulls() int64 { return 0 } + +func (page *booleanPage) Size() int64 { return int64(page.bits.Len()) } + +func (page *booleanPage) RepetitionLevels() []byte { return nil } + +func (page *booleanPage) DefinitionLevels() []byte { return nil } + +func (page *booleanPage) Data() encoding.Values { return encoding.BooleanValues(page.bits.Slice()) } + +func (page *booleanPage) Values() ValueReader { return &booleanPageValues{page: page} } + +func (page *booleanPage) valueAt(i int) bool { + bits := page.bits.Slice() + j := uint32(int(page.offset)+i) / 8 + k := uint32(int(page.offset)+i) % 8 + return ((bits[j] >> k) & 1) != 0 +} + +func (page *booleanPage) min() bool { + for i := range int(page.numValues) { + if !page.valueAt(i) { + return false + } + } + return page.numValues > 0 +} + +func (page *booleanPage) max() bool { + for i := range int(page.numValues) { + if page.valueAt(i) { + return true + } + } + return false +} + +func (page *booleanPage) bounds() (min, max bool) { + hasFalse, hasTrue := false, false + + for i := range int(page.numValues) { + v := page.valueAt(i) + if v { + hasTrue = true + } else { + hasFalse = true + } + if hasTrue && hasFalse { + break + } + } + + min = !hasFalse + max = hasTrue + return min, max +} + +func (page *booleanPage) Bounds() (min, max Value, ok bool) { + if ok = page.numValues > 0; ok { + minBool, maxBool := page.bounds() + min = page.makeValue(minBool) + max = page.makeValue(maxBool) + } + return min, max, ok +} + +func (page *booleanPage) Slice(i, j int64) Page { + lowWithOffset := i + int64(page.offset) + highWithOffset := j + int64(page.offset) + + off := lowWithOffset / 8 + end := highWithOffset / 8 + + if (highWithOffset % 8) != 0 { + end++ + } + + return &booleanPage{ + typ: page.typ, + bits: memory.SliceBufferFrom(page.bits.Slice()[off:end]), + offset: int32(lowWithOffset % 8), + numValues: int32(j - i), + columnIndex: page.columnIndex, + } +} + +func (page *booleanPage) makeValue(v bool) Value { + value := makeValueBoolean(v) + value.columnIndex = page.columnIndex + return value +} + +type booleanPageValues struct { + page *booleanPage + offset int +} + +func (r *booleanPageValues) ReadBooleans(values []bool) (n int, err error) { + for n < len(values) && r.offset < int(r.page.numValues) { + values[n] = r.page.valueAt(r.offset) + r.offset++ + n++ + } + if r.offset == int(r.page.numValues) { + err = io.EOF + } + return n, err +} + +func (r *booleanPageValues) ReadValues(values []Value) (n int, err error) { + for n < len(values) && r.offset < int(r.page.numValues) { + values[n] = r.page.makeValue(r.page.valueAt(r.offset)) + r.offset++ + n++ + } + if r.offset == int(r.page.numValues) { + err = io.EOF + } + return n, err +} diff --git a/vendor/github.com/parquet-go/parquet-go/page_bounds.go b/vendor/github.com/parquet-go/parquet-go/page_bounds.go new file mode 100644 index 000000000..8278e25ce --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/page_bounds.go @@ -0,0 +1,25 @@ +package parquet + +import "bytes" + +func boundsFixedLenByteArray(data []byte, size int) (min, max []byte) { + if len(data) > 0 { + min = data[:size] + max = data[:size] + + for i, j := size, 2*size; j <= len(data); { + item := data[i:j] + + if bytes.Compare(item, min) < 0 { + min = item + } + if bytes.Compare(item, max) > 0 { + max = item + } + + i += size + j += size + } + } + return min, max +} diff --git a/vendor/github.com/parquet-go/parquet-go/page_bounds_amd64.go b/vendor/github.com/parquet-go/parquet-go/page_bounds_amd64.go new file mode 100644 index 000000000..9cb513b40 --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/page_bounds_amd64.go @@ -0,0 +1,129 @@ +//go:build !purego + +package parquet + +// The min-max algorithms combine looking for the min and max values in a single +// pass over the data. While the behavior is the same as calling functions to +// look for the min and max values independently, doing both operations at the +// same time means that we only load the data from memory once. When working on +// large arrays the algorithms are limited by memory bandwidth, computing both +// the min and max together shrinks by half the amount of data read from memory. +// +// The following benchmarks results were highlighting the benefits of combining +// the min-max search, compared to calling the min and max functions separately: +// +// name old time/op new time/op delta +// BoundsInt64/10240KiB 590µs ±15% 330µs ±10% -44.01% (p=0.000 n=10+10) +// +// name old speed new speed delta +// BoundsInt64/10240KiB 17.9GB/s ±13% 31.8GB/s ±11% +78.13% (p=0.000 n=10+10) +// +// As expected, since the functions are memory-bound in those cases, and load +// half as much data, we see significant improvements. The gains are not 2x because +// running more AVX-512 instructions in the tight loops causes more contention +// on CPU ports. +// +// Optimizations being trade offs, using min/max functions independently appears +// to yield better throughput when the data resides in CPU caches: +// +// name old time/op new time/op delta +// BoundsInt64/4KiB 52.1ns ± 0% 46.2ns ± 1% -12.65% (p=0.000 n=10+10) +// +// name old speed new speed delta +// BoundsInt64/4KiB 78.6GB/s ± 0% 88.6GB/s ± 1% +11.23% (p=0.000 n=10+10) +// +// The probable explanation is that in those cases the algorithms are not +// memory-bound anymore, but limited by contention on CPU ports, and the +// individual min/max functions are able to better parallelize the work due +// to running less instructions per loop. The performance starts to equalize +// around 256KiB, and degrade beyond 1MiB, so we use this threshold to determine +// which approach to prefer. +const combinedBoundsThreshold = 1 * 1024 * 1024 + +//go:noescape +func combinedBoundsBool(data []bool) (min, max bool) + +//go:noescape +func combinedBoundsInt32(data []int32) (min, max int32) + +//go:noescape +func combinedBoundsInt64(data []int64) (min, max int64) + +//go:noescape +func combinedBoundsUint32(data []uint32) (min, max uint32) + +//go:noescape +func combinedBoundsUint64(data []uint64) (min, max uint64) + +//go:noescape +func combinedBoundsFloat32(data []float32) (min, max float32) + +//go:noescape +func combinedBoundsFloat64(data []float64) (min, max float64) + +//go:noescape +func combinedBoundsBE128(data [][16]byte) (min, max []byte) + +func boundsInt32(data []int32) (min, max int32) { + if 4*len(data) >= combinedBoundsThreshold { + return combinedBoundsInt32(data) + } + min = minInt32(data) + max = maxInt32(data) + return +} + +func boundsInt64(data []int64) (min, max int64) { + if 8*len(data) >= combinedBoundsThreshold { + return combinedBoundsInt64(data) + } + min = minInt64(data) + max = maxInt64(data) + return +} + +func boundsUint32(data []uint32) (min, max uint32) { + if 4*len(data) >= combinedBoundsThreshold { + return combinedBoundsUint32(data) + } + min = minUint32(data) + max = maxUint32(data) + return +} + +func boundsUint64(data []uint64) (min, max uint64) { + if 8*len(data) >= combinedBoundsThreshold { + return combinedBoundsUint64(data) + } + min = minUint64(data) + max = maxUint64(data) + return +} + +func boundsFloat32(data []float32) (min, max float32) { + if 4*len(data) >= combinedBoundsThreshold { + return combinedBoundsFloat32(data) + } + min = minFloat32(data) + max = maxFloat32(data) + return +} + +func boundsFloat64(data []float64) (min, max float64) { + if 8*len(data) >= combinedBoundsThreshold { + return combinedBoundsFloat64(data) + } + min = minFloat64(data) + max = maxFloat64(data) + return +} + +func boundsBE128(data [][16]byte) (min, max []byte) { + // TODO: min/max BE128 is really complex to vectorize, and the returns + // were barely better than doing the min and max independently, for all + // input sizes. We should revisit if we find ways to improve the min or + // max algorithms which can be transposed to the combined version. + min = minBE128(data) + max = maxBE128(data) + return +} diff --git a/vendor/github.com/parquet-go/parquet-go/page_bounds_amd64.s b/vendor/github.com/parquet-go/parquet-go/page_bounds_amd64.s new file mode 100644 index 000000000..c6d172a5e --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/page_bounds_amd64.s @@ -0,0 +1,551 @@ +//go:build !purego + +#include "textflag.h" + +#define bswap128lo 0x08080A0B0C0D0E0F +#define bswap128hi 0x0001020304050607 + +DATA bswap128+0(SB)/8, $bswap128lo +DATA bswap128+8(SB)/8, $bswap128hi +DATA bswap128+16(SB)/8, $bswap128lo +DATA bswap128+24(SB)/8, $bswap128hi +DATA bswap128+32(SB)/8, $bswap128lo +DATA bswap128+40(SB)/8, $bswap128hi +DATA bswap128+48(SB)/8, $bswap128lo +DATA bswap128+56(SB)/8, $bswap128hi +GLOBL bswap128(SB), RODATA|NOPTR, $64 + +DATA indexes128+0(SB)/8, $0 +DATA indexes128+8(SB)/8, $0 +DATA indexes128+16(SB)/8, $1 +DATA indexes128+24(SB)/8, $1 +DATA indexes128+32(SB)/8, $2 +DATA indexes128+40(SB)/8, $2 +DATA indexes128+48(SB)/8, $3 +DATA indexes128+56(SB)/8, $3 +GLOBL indexes128(SB), RODATA|NOPTR, $64 + +DATA swap64+0(SB)/8, $4 +DATA swap64+8(SB)/8, $5 +DATA swap64+16(SB)/8, $6 +DATA swap64+24(SB)/8, $7 +DATA swap64+32(SB)/8, $2 +DATA swap64+40(SB)/8, $3 +DATA swap64+48(SB)/8, $0 +DATA swap64+56(SB)/8, $1 +GLOBL swap64(SB), RODATA|NOPTR, $64 + +DATA swap32+0(SB)/4, $8 +DATA swap32+4(SB)/4, $9 +DATA swap32+8(SB)/4, $10 +DATA swap32+12(SB)/4, $11 +DATA swap32+16(SB)/4, $12 +DATA swap32+20(SB)/4, $13 +DATA swap32+24(SB)/4, $14 +DATA swap32+28(SB)/4, $15 +DATA swap32+32(SB)/4, $4 +DATA swap32+36(SB)/4, $5 +DATA swap32+40(SB)/4, $6 +DATA swap32+44(SB)/4, $7 +DATA swap32+48(SB)/4, $2 +DATA swap32+52(SB)/4, $3 +DATA swap32+56(SB)/4, $0 +DATA swap32+60(SB)/4, $1 +GLOBL swap32(SB), RODATA|NOPTR, $64 + +// func combinedBoundsInt32(data []int32) (min, max int32) +TEXT ·combinedBoundsInt32(SB), NOSPLIT, $-32 + MOVQ data_base+0(FP), AX + MOVQ data_len+8(FP), CX + XORQ R8, R8 + XORQ R9, R9 + + CMPQ CX, $0 + JE done + XORQ SI, SI + MOVLQZX (AX), R8 // min + MOVLQZX (AX), R9 // max + + CMPB ·hasAVX512VL(SB), $0 + JE loop + + CMPQ CX, $32 + JB loop + + MOVQ CX, DI + SHRQ $5, DI + SHLQ $5, DI + VPBROADCASTD (AX), Z0 + VPBROADCASTD (AX), Z3 +loop32: + VMOVDQU32 (AX)(SI*4), Z1 + VMOVDQU32 64(AX)(SI*4), Z2 + VPMINSD Z1, Z0, Z0 + VPMINSD Z2, Z0, Z0 + VPMAXSD Z1, Z3, Z3 + VPMAXSD Z2, Z3, Z3 + ADDQ $32, SI + CMPQ SI, DI + JNE loop32 + + VMOVDQU32 swap32+0(SB), Z1 + VMOVDQU32 swap32+0(SB), Z2 + VPERMI2D Z0, Z0, Z1 + VPERMI2D Z3, Z3, Z2 + VPMINSD Y1, Y0, Y0 + VPMAXSD Y2, Y3, Y3 + + VMOVDQU32 swap32+32(SB), Y1 + VMOVDQU32 swap32+32(SB), Y2 + VPERMI2D Y0, Y0, Y1 + VPERMI2D Y3, Y3, Y2 + VPMINSD X1, X0, X0 + VPMAXSD X2, X3, X3 + + VMOVDQU32 swap32+48(SB), X1 + VMOVDQU32 swap32+48(SB), X2 + VPERMI2D X0, X0, X1 + VPERMI2D X3, X3, X2 + VPMINSD X1, X0, X0 + VPMAXSD X2, X3, X3 + VZEROUPPER + + MOVQ X0, BX + MOVQ X3, DX + MOVL BX, R8 + MOVL DX, R9 + SHRQ $32, BX + SHRQ $32, DX + CMPL BX, R8 + CMOVLLT BX, R8 + CMPL DX, R9 + CMOVLGT DX, R9 + + CMPQ SI, CX + JE done +loop: + MOVLQZX (AX)(SI*4), DX + CMPL DX, R8 + CMOVLLT DX, R8 + CMPL DX, R9 + CMOVLGT DX, R9 + INCQ SI + CMPQ SI, CX + JNE loop +done: + MOVL R8, min+24(FP) + MOVL R9, max+28(FP) + RET + +// func combinedBoundsInt64(data []int64) (min, max int64) +TEXT ·combinedBoundsInt64(SB), NOSPLIT, $-40 + MOVQ data_base+0(FP), AX + MOVQ data_len+8(FP), CX + XORQ R8, R8 + XORQ R9, R9 + + CMPQ CX, $0 + JE done + XORQ SI, SI + MOVQ (AX), R8 // min + MOVQ (AX), R9 // max + + CMPB ·hasAVX512VL(SB), $0 + JE loop + + CMPQ CX, $16 + JB loop + + MOVQ CX, DI + SHRQ $4, DI + SHLQ $4, DI + VPBROADCASTQ (AX), Z0 + VPBROADCASTQ (AX), Z3 +loop16: + VMOVDQU64 (AX)(SI*8), Z1 + VMOVDQU64 64(AX)(SI*8), Z2 + VPMINSQ Z1, Z0, Z0 + VPMINSQ Z2, Z0, Z0 + VPMAXSQ Z1, Z3, Z3 + VPMAXSQ Z2, Z3, Z3 + ADDQ $16, SI + CMPQ SI, DI + JNE loop16 + + VMOVDQU32 swap32+0(SB), Z1 + VMOVDQU32 swap32+0(SB), Z2 + VPERMI2D Z0, Z0, Z1 + VPERMI2D Z3, Z3, Z2 + VPMINSQ Y1, Y0, Y0 + VPMAXSQ Y2, Y3, Y3 + + VMOVDQU32 swap32+32(SB), Y1 + VMOVDQU32 swap32+32(SB), Y2 + VPERMI2D Y0, Y0, Y1 + VPERMI2D Y3, Y3, Y2 + VPMINSQ X1, X0, X0 + VPMAXSQ X2, X3, X3 + + VMOVDQU32 swap32+48(SB), X1 + VMOVDQU32 swap32+48(SB), X2 + VPERMI2D X0, X0, X1 + VPERMI2D X3, X3, X2 + VPMINSQ X1, X0, X0 + VPMAXSQ X2, X3, X3 + VZEROUPPER + + MOVQ X0, R8 + MOVQ X3, R9 + CMPQ SI, CX + JE done +loop: + MOVQ (AX)(SI*8), DX + CMPQ DX, R8 + CMOVQLT DX, R8 + CMPQ DX, R9 + CMOVQGT DX, R9 + INCQ SI + CMPQ SI, CX + JNE loop +done: + MOVQ R8, min+24(FP) + MOVQ R9, max+32(FP) + RET + +// func combinedBoundsUint32(data []uint32) (min, max uint32) +TEXT ·combinedBoundsUint32(SB), NOSPLIT, $-32 + MOVQ data_base+0(FP), AX + MOVQ data_len+8(FP), CX + XORQ R8, R8 + XORQ R9, R9 + + CMPQ CX, $0 + JE done + XORQ SI, SI + MOVLQZX (AX), R8 // min + MOVLQZX (AX), R9 // max + + CMPB ·hasAVX512VL(SB), $0 + JE loop + + CMPQ CX, $32 + JB loop + + MOVQ CX, DI + SHRQ $5, DI + SHLQ $5, DI + VPBROADCASTD (AX), Z0 + VPBROADCASTD (AX), Z3 +loop32: + VMOVDQU32 (AX)(SI*4), Z1 + VMOVDQU32 64(AX)(SI*4), Z2 + VPMINUD Z1, Z0, Z0 + VPMINUD Z2, Z0, Z0 + VPMAXUD Z1, Z3, Z3 + VPMAXUD Z2, Z3, Z3 + ADDQ $32, SI + CMPQ SI, DI + JNE loop32 + + VMOVDQU32 swap32+0(SB), Z1 + VMOVDQU32 swap32+0(SB), Z2 + VPERMI2D Z0, Z0, Z1 + VPERMI2D Z3, Z3, Z2 + VPMINUD Y1, Y0, Y0 + VPMAXUD Y2, Y3, Y3 + + VMOVDQU32 swap32+32(SB), Y1 + VMOVDQU32 swap32+32(SB), Y2 + VPERMI2D Y0, Y0, Y1 + VPERMI2D Y3, Y3, Y2 + VPMINUD X1, X0, X0 + VPMAXUD X2, X3, X3 + + VMOVDQU32 swap32+48(SB), X1 + VMOVDQU32 swap32+48(SB), X2 + VPERMI2D X0, X0, X1 + VPERMI2D X3, X3, X2 + VPMINUD X1, X0, X0 + VPMAXUD X2, X3, X3 + VZEROUPPER + + MOVQ X0, BX + MOVQ X3, DX + MOVL BX, R8 + MOVL DX, R9 + SHRQ $32, BX + SHRQ $32, DX + CMPL BX, R8 + CMOVLCS BX, R8 + CMPL DX, R9 + CMOVLHI DX, R9 + + CMPQ SI, CX + JE done +loop: + MOVLQZX (AX)(SI*4), DX + CMPL DX, R8 + CMOVLCS DX, R8 + CMPL DX, R9 + CMOVLHI DX, R9 + INCQ SI + CMPQ SI, CX + JNE loop +done: + MOVL R8, min+24(FP) + MOVL R9, max+28(FP) + RET + +// func combinedBoundsUint64(data []uint64) (min, max uint64) +TEXT ·combinedBoundsUint64(SB), NOSPLIT, $-40 + MOVQ data_base+0(FP), AX + MOVQ data_len+8(FP), CX + XORQ R8, R8 + XORQ R9, R9 + + CMPQ CX, $0 + JE done + XORQ SI, SI + MOVQ (AX), R8 // min + MOVQ (AX), R9 // max + + CMPB ·hasAVX512VL(SB), $0 + JE loop + + CMPQ CX, $16 + JB loop + + MOVQ CX, DI + SHRQ $4, DI + SHLQ $4, DI + VPBROADCASTQ (AX), Z0 + VPBROADCASTQ (AX), Z3 +loop16: + VMOVDQU64 (AX)(SI*8), Z1 + VMOVDQU64 64(AX)(SI*8), Z2 + VPMINUQ Z1, Z0, Z0 + VPMINUQ Z2, Z0, Z0 + VPMAXUQ Z1, Z3, Z3 + VPMAXUQ Z2, Z3, Z3 + ADDQ $16, SI + CMPQ SI, DI + JNE loop16 + + VMOVDQU32 swap32+0(SB), Z1 + VMOVDQU32 swap32+0(SB), Z2 + VPERMI2D Z0, Z0, Z1 + VPERMI2D Z3, Z3, Z2 + VPMINUQ Y1, Y0, Y0 + VPMAXUQ Y2, Y3, Y3 + + VMOVDQU32 swap32+32(SB), Y1 + VMOVDQU32 swap32+32(SB), Y2 + VPERMI2D Y0, Y0, Y1 + VPERMI2D Y3, Y3, Y2 + VPMINUQ X1, X0, X0 + VPMAXUQ X2, X3, X3 + + VMOVDQU32 swap32+48(SB), X1 + VMOVDQU32 swap32+48(SB), X2 + VPERMI2D X0, X0, X1 + VPERMI2D X3, X3, X2 + VPMINUQ X1, X0, X0 + VPMAXUQ X2, X3, X3 + VZEROUPPER + + MOVQ X0, R8 + MOVQ X3, R9 + CMPQ SI, CX + JE done +loop: + MOVQ (AX)(SI*8), DX + CMPQ DX, R8 + CMOVQCS DX, R8 + CMPQ DX, R9 + CMOVQHI DX, R9 + INCQ SI + CMPQ SI, CX + JNE loop +done: + MOVQ R8, min+24(FP) + MOVQ R9, max+32(FP) + RET + +// func combinedBoundsFloat32(data []float32) (min, max float32) +TEXT ·combinedBoundsFloat32(SB), NOSPLIT, $-32 + MOVQ data_base+0(FP), AX + MOVQ data_len+8(FP), CX + XORQ R8, R8 + XORQ R9, R9 + + CMPQ CX, $0 + JE done + XORPS X0, X0 + XORPS X1, X1 + XORQ SI, SI + MOVLQZX (AX), R8 // min + MOVLQZX (AX), R9 // max + MOVQ R8, X0 + MOVQ R9, X1 + + CMPB ·hasAVX512VL(SB), $0 + JE loop + + CMPQ CX, $32 + JB loop + + MOVQ CX, DI + SHRQ $5, DI + SHLQ $5, DI + VPBROADCASTD (AX), Z0 + VPBROADCASTD (AX), Z3 +loop32: + VMOVDQU32 (AX)(SI*4), Z1 + VMOVDQU32 64(AX)(SI*4), Z2 + VMINPS Z1, Z0, Z0 + VMINPS Z2, Z0, Z0 + VMAXPS Z1, Z3, Z3 + VMAXPS Z2, Z3, Z3 + ADDQ $32, SI + CMPQ SI, DI + JNE loop32 + + VMOVDQU32 swap32+0(SB), Z1 + VMOVDQU32 swap32+0(SB), Z2 + VPERMI2D Z0, Z0, Z1 + VPERMI2D Z3, Z3, Z2 + VMINPS Y1, Y0, Y0 + VMAXPS Y2, Y3, Y3 + + VMOVDQU32 swap32+32(SB), Y1 + VMOVDQU32 swap32+32(SB), Y2 + VPERMI2D Y0, Y0, Y1 + VPERMI2D Y3, Y3, Y2 + VMINPS X1, X0, X0 + VMAXPS X2, X3, X3 + + VMOVDQU32 swap32+48(SB), X1 + VMOVDQU32 swap32+48(SB), X2 + VPERMI2D X0, X0, X1 + VPERMI2D X3, X3, X2 + VMINPS X1, X0, X0 + VMAXPS X2, X3, X3 + VZEROUPPER + + MOVAPS X0, X1 + MOVAPS X3, X2 + + PSRLQ $32, X1 + MOVQ X0, R8 + MOVQ X1, R10 + UCOMISS X0, X1 + CMOVLCS R10, R8 + + PSRLQ $32, X2 + MOVQ X3, R9 + MOVQ X2, R11 + UCOMISS X3, X2 + CMOVLHI R11, R9 + + CMPQ SI, CX + JE done + MOVQ R8, X0 + MOVQ R9, X1 +loop: + MOVLQZX (AX)(SI*4), DX + MOVQ DX, X2 + UCOMISS X0, X2 + CMOVLCS DX, R8 + UCOMISS X1, X2 + CMOVLHI DX, R9 + MOVQ R8, X0 + MOVQ R9, X1 + INCQ SI + CMPQ SI, CX + JNE loop +done: + MOVL R8, min+24(FP) + MOVL R9, max+28(FP) + RET + +// func combinedBoundsFloat64(data []float64) (min, max float64) +TEXT ·combinedBoundsFloat64(SB), NOSPLIT, $-40 + MOVQ data_base+0(FP), AX + MOVQ data_len+8(FP), CX + XORQ R8, R8 + XORQ R9, R9 + + CMPQ CX, $0 + JE done + XORPD X0, X0 + XORPD X1, X1 + XORQ SI, SI + MOVQ (AX), R8 // min + MOVQ (AX), R9 // max + MOVQ R8, X0 + MOVQ R9, X1 + + CMPB ·hasAVX512VL(SB), $0 + JE loop + + CMPQ CX, $16 + JB loop + + MOVQ CX, DI + SHRQ $4, DI + SHLQ $4, DI + VPBROADCASTQ (AX), Z0 + VPBROADCASTQ (AX), Z3 +loop16: + VMOVDQU64 (AX)(SI*8), Z1 + VMOVDQU64 64(AX)(SI*8), Z2 + VMINPD Z1, Z0, Z0 + VMINPD Z2, Z0, Z0 + VMAXPD Z1, Z3, Z3 + VMAXPD Z2, Z3, Z3 + ADDQ $16, SI + CMPQ SI, DI + JNE loop16 + + VMOVDQU64 swap32+0(SB), Z1 + VMOVDQU64 swap32+0(SB), Z2 + VPERMI2D Z0, Z0, Z1 + VPERMI2D Z3, Z3, Z2 + VMINPD Y1, Y0, Y0 + VMAXPD Y2, Y3, Y3 + + VMOVDQU64 swap32+32(SB), Y1 + VMOVDQU64 swap32+32(SB), Y2 + VPERMI2D Y0, Y0, Y1 + VPERMI2D Y3, Y3, Y2 + VMINPD X1, X0, X0 + VMAXPD X2, X3, X3 + + VMOVDQU64 swap32+48(SB), X1 + VMOVDQU64 swap32+48(SB), X2 + VPERMI2D X0, X0, X1 + VPERMI2D X3, X3, X2 + VMINPD X1, X0, X0 + VMAXPD X2, X3, X1 + VZEROUPPER + + MOVQ X0, R8 + MOVQ X1, R9 + CMPQ SI, CX + JE done +loop: + MOVQ (AX)(SI*8), DX + MOVQ DX, X2 + UCOMISD X0, X2 + CMOVQCS DX, R8 + UCOMISD X1, X2 + CMOVQHI DX, R9 + MOVQ R8, X0 + MOVQ R9, X1 + INCQ SI + CMPQ SI, CX + JNE loop +done: + MOVQ R8, min+24(FP) + MOVQ R9, max+32(FP) + RET diff --git a/vendor/github.com/parquet-go/parquet-go/page_bounds_purego.go b/vendor/github.com/parquet-go/parquet-go/page_bounds_purego.go new file mode 100644 index 000000000..0b5a2474f --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/page_bounds_purego.go @@ -0,0 +1,143 @@ +//go:build purego || !amd64 + +package parquet + +import ( + "encoding/binary" +) + +func boundsInt32(data []int32) (min, max int32) { + if len(data) > 0 { + min = data[0] + max = data[0] + + for _, v := range data[1:] { + if v < min { + min = v + } + if v > max { + max = v + } + } + } + return min, max +} + +func boundsInt64(data []int64) (min, max int64) { + if len(data) > 0 { + min = data[0] + max = data[0] + + for _, v := range data[1:] { + if v < min { + min = v + } + if v > max { + max = v + } + } + } + return min, max +} + +func boundsUint32(data []uint32) (min, max uint32) { + if len(data) > 0 { + min = data[0] + max = data[0] + + for _, v := range data[1:] { + if v < min { + min = v + } + if v > max { + max = v + } + } + } + return min, max +} + +func boundsUint64(data []uint64) (min, max uint64) { + if len(data) > 0 { + min = data[0] + max = data[0] + + for _, v := range data[1:] { + if v < min { + min = v + } + if v > max { + max = v + } + } + } + return min, max +} + +func boundsFloat32(data []float32) (min, max float32) { + if len(data) > 0 { + min = data[0] + max = data[0] + + for _, v := range data[1:] { + if v < min { + min = v + } + if v > max { + max = v + } + } + } + return min, max +} + +func boundsFloat64(data []float64) (min, max float64) { + if len(data) > 0 { + min = data[0] + max = data[0] + + for _, v := range data[1:] { + if v < min { + min = v + } + if v > max { + max = v + } + } + } + return min, max +} + +func boundsBE128(data [][16]byte) (min, max []byte) { + if len(data) > 0 { + minHi := binary.BigEndian.Uint64(data[0][:8]) + maxHi := minHi + minIndex := 0 + maxIndex := 0 + for i := 1; i < len(data); i++ { + hi := binary.BigEndian.Uint64(data[i][:8]) + lo := binary.BigEndian.Uint64(data[i][8:]) + switch { + case hi < minHi: + minHi, minIndex = hi, i + case hi == minHi: + minLo := binary.BigEndian.Uint64(data[minIndex][8:]) + if lo < minLo { + minHi, minIndex = hi, i + } + } + switch { + case hi > maxHi: + maxHi, maxIndex = hi, i + case hi == maxHi: + maxLo := binary.BigEndian.Uint64(data[maxIndex][8:]) + if lo > maxLo { + maxHi, maxIndex = hi, i + } + } + } + min = data[minIndex][:] + max = data[maxIndex][:] + } + return min, max +} diff --git a/vendor/github.com/parquet-go/parquet-go/page_byte_array.go b/vendor/github.com/parquet-go/parquet-go/page_byte_array.go new file mode 100644 index 000000000..786b81707 --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/page_byte_array.go @@ -0,0 +1,210 @@ +package parquet + +import ( + "bytes" + "io" + + "github.com/parquet-go/parquet-go/encoding" + "github.com/parquet-go/parquet-go/encoding/plain" + "github.com/parquet-go/parquet-go/internal/memory" +) + +type byteArrayPage struct { + typ Type + values memory.SliceBuffer[byte] + offsets memory.SliceBuffer[uint32] + columnIndex uint16 +} + +func newByteArrayPage(typ Type, columnIndex uint16, numValues int32, values encoding.Values) *byteArrayPage { + data, offsets := values.ByteArray() + if len(offsets) != int(numValues)+1 { + println("parquet: warning: column", columnIndex, "byte array page has", len(offsets), "offsets but numValues is", numValues, "(expected", numValues+1, "offsets)") + } + if int(numValues)+1 <= len(offsets) { + offsets = offsets[:numValues+1] + } + return &byteArrayPage{ + typ: typ, + values: memory.SliceBufferFrom(data), + offsets: memory.SliceBufferFrom(offsets), + columnIndex: ^columnIndex, + } +} + +func (page *byteArrayPage) Type() Type { return page.typ } + +func (page *byteArrayPage) Column() int { return int(^page.columnIndex) } + +func (page *byteArrayPage) Dictionary() Dictionary { return nil } + +func (page *byteArrayPage) NumRows() int64 { return int64(page.len()) } + +func (page *byteArrayPage) NumValues() int64 { return int64(page.len()) } + +func (page *byteArrayPage) NumNulls() int64 { return 0 } + +func (page *byteArrayPage) Size() int64 { + return int64(page.values.Len()) + 4*int64(page.offsets.Len()) +} + +func (page *byteArrayPage) RepetitionLevels() []byte { return nil } + +func (page *byteArrayPage) DefinitionLevels() []byte { return nil } + +func (page *byteArrayPage) Data() encoding.Values { + return encoding.ByteArrayValues(page.values.Slice(), page.offsets.Slice()) +} + +func (page *byteArrayPage) Values() ValueReader { return &byteArrayPageValues{page: page} } + +func (page *byteArrayPage) len() int { return page.offsets.Len() - 1 } + +func (page *byteArrayPage) index(i int) []byte { + offsets := page.offsets.Slice() + values := page.values.Slice() + j := offsets[i+0] + k := offsets[i+1] + return values[j:k:k] +} + +func (page *byteArrayPage) min() (min []byte) { + if n := page.len(); n > 0 { + min = page.index(0) + + for i := 1; i < n; i++ { + v := page.index(i) + + if bytes.Compare(v, min) < 0 { + min = v + } + } + } + return min +} + +func (page *byteArrayPage) max() (max []byte) { + if n := page.len(); n > 0 { + max = page.index(0) + + for i := 1; i < n; i++ { + v := page.index(i) + + if bytes.Compare(v, max) > 0 { + max = v + } + } + } + return max +} + +func (page *byteArrayPage) bounds() (min, max []byte) { + if n := page.len(); n > 0 { + min = page.index(0) + max = min + + for i := 1; i < n; i++ { + v := page.index(i) + + switch { + case bytes.Compare(v, min) < 0: + min = v + case bytes.Compare(v, max) > 0: + max = v + } + } + } + return min, max +} + +func (page *byteArrayPage) Bounds() (min, max Value, ok bool) { + if ok = page.offsets.Len() > 1; ok { + minBytes, maxBytes := page.bounds() + min = page.makeValueBytes(minBytes) + max = page.makeValueBytes(maxBytes) + } + return min, max, ok +} + +func (page *byteArrayPage) cloneValues() memory.SliceBuffer[byte] { + return page.values.Clone() +} + +func (page *byteArrayPage) cloneOffsets() memory.SliceBuffer[uint32] { + return page.offsets.Clone() +} + +func (page *byteArrayPage) Slice(i, j int64) Page { + return &byteArrayPage{ + typ: page.typ, + values: page.values, + offsets: memory.SliceBufferFrom(page.offsets.Slice()[i : j+1]), + columnIndex: page.columnIndex, + } +} + +func (page *byteArrayPage) makeValueBytes(v []byte) Value { + value := makeValueBytes(ByteArray, v) + value.columnIndex = page.columnIndex + return value +} + +func (page *byteArrayPage) makeValueString(v string) Value { + value := makeValueString(ByteArray, v) + value.columnIndex = page.columnIndex + return value +} + +type byteArrayPageValues struct { + page *byteArrayPage + offset int +} + +func (r *byteArrayPageValues) Read(b []byte) (int, error) { + _, n, err := r.readByteArrays(b) + return n, err +} + +func (r *byteArrayPageValues) ReadRequired(values []byte) (int, error) { + return r.ReadByteArrays(values) +} + +func (r *byteArrayPageValues) ReadByteArrays(values []byte) (int, error) { + n, _, err := r.readByteArrays(values) + return n, err +} + +func (r *byteArrayPageValues) readByteArrays(values []byte) (c, n int, err error) { + numValues := r.page.len() + for r.offset < numValues { + b := r.page.index(r.offset) + k := plain.ByteArrayLengthSize + len(b) + if k > (len(values) - n) { + break + } + plain.PutByteArrayLength(values[n:], len(b)) + n += plain.ByteArrayLengthSize + n += copy(values[n:], b) + r.offset++ + c++ + } + if r.offset == numValues { + err = io.EOF + } else if n == 0 && len(values) > 0 { + err = io.ErrShortBuffer + } + return c, n, err +} + +func (r *byteArrayPageValues) ReadValues(values []Value) (n int, err error) { + numValues := r.page.len() + for n < len(values) && r.offset < numValues { + values[n] = r.page.makeValueBytes(r.page.index(r.offset)) + r.offset++ + n++ + } + if r.offset == numValues { + err = io.EOF + } + return n, err +} diff --git a/vendor/github.com/parquet-go/parquet-go/page_double.go b/vendor/github.com/parquet-go/parquet-go/page_double.go new file mode 100644 index 000000000..390cb2bd2 --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/page_double.go @@ -0,0 +1,136 @@ +package parquet + +import ( + "io" + "math" + + "github.com/parquet-go/bitpack/unsafecast" + "github.com/parquet-go/parquet-go/encoding" + "github.com/parquet-go/parquet-go/internal/memory" +) + +type doublePage struct { + typ Type + values memory.SliceBuffer[float64] + columnIndex uint16 +} + +func newDoublePage(typ Type, columnIndex uint16, numValues int32, values encoding.Values) *doublePage { + return &doublePage{ + typ: typ, + values: memory.SliceBufferFrom(values.Double()[:numValues]), + columnIndex: ^columnIndex, + } +} + +func (page *doublePage) Type() Type { return page.typ } + +func (page *doublePage) Column() int { return int(^page.columnIndex) } + +func (page *doublePage) Dictionary() Dictionary { return nil } + +func (page *doublePage) NumRows() int64 { return int64(page.values.Len()) } + +func (page *doublePage) NumValues() int64 { return int64(page.values.Len()) } + +func (page *doublePage) NumNulls() int64 { return 0 } + +func (page *doublePage) Size() int64 { return 8 * int64(page.values.Len()) } + +func (page *doublePage) RepetitionLevels() []byte { return nil } + +func (page *doublePage) DefinitionLevels() []byte { return nil } + +func (page *doublePage) Data() encoding.Values { return encoding.DoubleValues(page.values.Slice()) } + +func (page *doublePage) Values() ValueReader { return &doublePageValues{page: page} } + +func (page *doublePage) min() float64 { return minFloat64(page.values.Slice()) } + +func (page *doublePage) max() float64 { return maxFloat64(page.values.Slice()) } + +func (page *doublePage) bounds() (min, max float64) { return boundsFloat64(page.values.Slice()) } + +// Bounds returns the min and max values in the page. NaN values are excluded +// from the result when non-NaN values exist so that query engines can rely on +// min/max for predicate pushdown and row-group/page skipping. This matches the +// behavior of Apache parquet-mr (PARQUET-1246), Apache Arrow, and the Apache +// Iceberg spec (which states lower/upper bounds apply to non-null, non-NaN +// values only). If all values are NaN, the bounds are reported as NaN so that +// readers know the page had data. +func (page *doublePage) Bounds() (min, max Value, ok bool) { + if ok = page.values.Len() > 0; ok { + data := page.values.Slice() + i := 0 + for i < len(data) && math.IsNaN(data[i]) { + i++ + } + if i >= len(data) { + min = page.makeValue(data[0]) + max = page.makeValue(data[0]) + return min, max, ok + } + lo, hi := data[i], data[i] + for _, v := range data[i+1:] { + if math.IsNaN(v) { + continue + } + if v < lo { + lo = v + } + if v > hi { + hi = v + } + } + min = page.makeValue(lo) + max = page.makeValue(hi) + } + return min, max, ok +} + +func (page *doublePage) Slice(i, j int64) Page { + return &doublePage{ + typ: page.typ, + values: memory.SliceBufferFrom(page.values.Slice()[i:j]), + columnIndex: page.columnIndex, + } +} + +func (page *doublePage) makeValue(v float64) Value { + value := makeValueDouble(v) + value.columnIndex = page.columnIndex + return value +} + +type doublePageValues struct { + page *doublePage + offset int +} + +func (r *doublePageValues) Read(b []byte) (n int, err error) { + n, err = r.ReadDoubles(unsafecast.Slice[float64](b)) + return 8 * n, err +} + +func (r *doublePageValues) ReadDoubles(values []float64) (n int, err error) { + pageValues := r.page.values.Slice() + n = copy(values, pageValues[r.offset:]) + r.offset += n + if r.offset == len(pageValues) { + err = io.EOF + } + return n, err +} + +func (r *doublePageValues) ReadValues(values []Value) (n int, err error) { + pageValues := r.page.values.Slice() + for n < len(values) && r.offset < len(pageValues) { + values[n] = r.page.makeValue(pageValues[r.offset]) + r.offset++ + n++ + } + if r.offset == len(pageValues) { + err = io.EOF + } + return n, err +} diff --git a/vendor/github.com/parquet-go/parquet-go/page_fixed_len_byte_array.go b/vendor/github.com/parquet-go/parquet-go/page_fixed_len_byte_array.go new file mode 100644 index 000000000..33863ee3b --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/page_fixed_len_byte_array.go @@ -0,0 +1,133 @@ +package parquet + +import ( + "io" + + "github.com/parquet-go/parquet-go/encoding" + "github.com/parquet-go/parquet-go/internal/memory" +) + +type fixedLenByteArrayPage struct { + typ Type + data memory.SliceBuffer[byte] + size int + columnIndex uint16 +} + +func newFixedLenByteArrayPage(typ Type, columnIndex uint16, numValues int32, values encoding.Values) *fixedLenByteArrayPage { + data, size := values.FixedLenByteArray() + return &fixedLenByteArrayPage{ + typ: typ, + data: memory.SliceBufferFrom(data[:int(numValues)*size]), + size: size, + columnIndex: ^columnIndex, + } +} + +func (page *fixedLenByteArrayPage) Type() Type { return page.typ } + +func (page *fixedLenByteArrayPage) Column() int { return int(^page.columnIndex) } + +func (page *fixedLenByteArrayPage) Dictionary() Dictionary { return nil } + +func (page *fixedLenByteArrayPage) NumRows() int64 { return int64(page.data.Len() / page.size) } + +func (page *fixedLenByteArrayPage) NumValues() int64 { return int64(page.data.Len() / page.size) } + +func (page *fixedLenByteArrayPage) NumNulls() int64 { return 0 } + +func (page *fixedLenByteArrayPage) Size() int64 { return int64(page.data.Len()) } + +func (page *fixedLenByteArrayPage) RepetitionLevels() []byte { return nil } + +func (page *fixedLenByteArrayPage) DefinitionLevels() []byte { return nil } + +func (page *fixedLenByteArrayPage) Data() encoding.Values { + return encoding.FixedLenByteArrayValues(page.data.Slice(), page.size) +} + +func (page *fixedLenByteArrayPage) Values() ValueReader { + return &fixedLenByteArrayPageValues{page: page} +} + +func (page *fixedLenByteArrayPage) min() []byte { + return minFixedLenByteArray(page.data.Slice(), page.size) +} + +func (page *fixedLenByteArrayPage) max() []byte { + return maxFixedLenByteArray(page.data.Slice(), page.size) +} + +func (page *fixedLenByteArrayPage) bounds() (min, max []byte) { + return boundsFixedLenByteArray(page.data.Slice(), page.size) +} + +func (page *fixedLenByteArrayPage) Bounds() (min, max Value, ok bool) { + if ok = page.data.Len() > 0; ok { + minBytes, maxBytes := page.bounds() + min = page.makeValueBytes(minBytes) + max = page.makeValueBytes(maxBytes) + } + return min, max, ok +} + +func (page *fixedLenByteArrayPage) Slice(i, j int64) Page { + data := page.data.Slice() + return &fixedLenByteArrayPage{ + typ: page.typ, + data: memory.SliceBufferFrom(data[i*int64(page.size) : j*int64(page.size)]), + size: page.size, + columnIndex: page.columnIndex, + } +} + +func (page *fixedLenByteArrayPage) makeValueBytes(v []byte) Value { + value := makeValueBytes(FixedLenByteArray, v) + value.columnIndex = page.columnIndex + return value +} + +func (page *fixedLenByteArrayPage) makeValueString(v string) Value { + value := makeValueString(FixedLenByteArray, v) + value.columnIndex = page.columnIndex + return value +} + +type fixedLenByteArrayPageValues struct { + page *fixedLenByteArrayPage + offset int +} + +func (r *fixedLenByteArrayPageValues) Read(b []byte) (n int, err error) { + n, err = r.ReadFixedLenByteArrays(b) + return n * r.page.size, err +} + +func (r *fixedLenByteArrayPageValues) ReadRequired(values []byte) (int, error) { + return r.ReadFixedLenByteArrays(values) +} + +func (r *fixedLenByteArrayPageValues) ReadFixedLenByteArrays(values []byte) (n int, err error) { + data := r.page.data.Slice() + n = copy(values, data[r.offset:]) / r.page.size + r.offset += n * r.page.size + if r.offset == len(data) { + err = io.EOF + } else if n == 0 && len(values) > 0 { + err = io.ErrShortBuffer + } + return n, err +} + +func (r *fixedLenByteArrayPageValues) ReadValues(values []Value) (n int, err error) { + data := r.page.data.Slice() + for n < len(values) && r.offset < len(data) { + values[n] = r.page.makeValueBytes(data[r.offset : r.offset+r.page.size]) + r.offset += r.page.size + n++ + } + if r.offset == len(data) { + err = io.EOF + } + return n, err +} diff --git a/vendor/github.com/parquet-go/parquet-go/page_float.go b/vendor/github.com/parquet-go/parquet-go/page_float.go new file mode 100644 index 000000000..8e3efbaef --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/page_float.go @@ -0,0 +1,137 @@ +package parquet + +import ( + "io" + "math" + + "github.com/parquet-go/bitpack/unsafecast" + "github.com/parquet-go/parquet-go/encoding" + "github.com/parquet-go/parquet-go/internal/memory" +) + +type floatPage struct { + typ Type + values memory.SliceBuffer[float32] + columnIndex uint16 +} + +func newFloatPage(typ Type, columnIndex uint16, numValues int32, values encoding.Values) *floatPage { + return &floatPage{ + typ: typ, + values: memory.SliceBufferFrom(values.Float()[:numValues]), + columnIndex: ^columnIndex, + } +} + +func (page *floatPage) Type() Type { return page.typ } + +func (page *floatPage) Column() int { return int(^page.columnIndex) } + +func (page *floatPage) Dictionary() Dictionary { return nil } + +func (page *floatPage) NumRows() int64 { return int64(page.values.Len()) } + +func (page *floatPage) NumValues() int64 { return int64(page.values.Len()) } + +func (page *floatPage) NumNulls() int64 { return 0 } + +func (page *floatPage) Size() int64 { return 4 * int64(page.values.Len()) } + +func (page *floatPage) RepetitionLevels() []byte { return nil } + +func (page *floatPage) DefinitionLevels() []byte { return nil } + +func (page *floatPage) Data() encoding.Values { return encoding.FloatValues(page.values.Slice()) } + +func (page *floatPage) Values() ValueReader { return &floatPageValues{page: page} } + +func (page *floatPage) min() float32 { return minFloat32(page.values.Slice()) } + +func (page *floatPage) max() float32 { return maxFloat32(page.values.Slice()) } + +func (page *floatPage) bounds() (min, max float32) { return boundsFloat32(page.values.Slice()) } + +// Bounds returns the min and max values in the page. NaN values are excluded +// from the result when non-NaN values exist so that query engines can rely on +// min/max for predicate pushdown and row-group/page skipping. This matches the +// behavior of Apache parquet-mr (PARQUET-1246), Apache Arrow, and the Apache +// Iceberg spec (which states lower/upper bounds apply to non-null, non-NaN +// values only). If all values are NaN, the bounds are reported as NaN so that +// readers know the page had data. +func (page *floatPage) Bounds() (min, max Value, ok bool) { + if ok = page.values.Len() > 0; ok { + data := page.values.Slice() + i := 0 + for i < len(data) && math.IsNaN(float64(data[i])) { + i++ + } + if i >= len(data) { + // All values are NaN. + min = page.makeValue(data[0]) + max = page.makeValue(data[0]) + return min, max, ok + } + lo, hi := data[i], data[i] + for _, v := range data[i+1:] { + if math.IsNaN(float64(v)) { + continue + } + if v < lo { + lo = v + } + if v > hi { + hi = v + } + } + min = page.makeValue(lo) + max = page.makeValue(hi) + } + return min, max, ok +} + +func (page *floatPage) Slice(i, j int64) Page { + return &floatPage{ + typ: page.typ, + values: memory.SliceBufferFrom(page.values.Slice()[i:j]), + columnIndex: page.columnIndex, + } +} + +func (page *floatPage) makeValue(v float32) Value { + value := makeValueFloat(v) + value.columnIndex = page.columnIndex + return value +} + +type floatPageValues struct { + page *floatPage + offset int +} + +func (r *floatPageValues) Read(b []byte) (n int, err error) { + n, err = r.ReadFloats(unsafecast.Slice[float32](b)) + return 4 * n, err +} + +func (r *floatPageValues) ReadFloats(values []float32) (n int, err error) { + pageValues := r.page.values.Slice() + n = copy(values, pageValues[r.offset:]) + r.offset += n + if r.offset == len(pageValues) { + err = io.EOF + } + return n, err +} + +func (r *floatPageValues) ReadValues(values []Value) (n int, err error) { + pageValues := r.page.values.Slice() + for n < len(values) && r.offset < len(pageValues) { + values[n] = r.page.makeValue(pageValues[r.offset]) + r.offset++ + n++ + } + if r.offset == len(pageValues) { + err = io.EOF + } + return n, err +} diff --git a/vendor/github.com/parquet-go/parquet-go/page_header.go b/vendor/github.com/parquet-go/parquet-go/page_header.go new file mode 100644 index 000000000..13a44ed88 --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/page_header.go @@ -0,0 +1,221 @@ +package parquet + +import ( + "fmt" + + "github.com/parquet-go/parquet-go/format" +) + +// PageHeader is an interface implemented by parquet page headers. +type PageHeader interface { + // Returns the number of values in the page (including nulls). + NumValues() int64 + + // Returns the page encoding. + Encoding() format.Encoding + + // Returns the parquet format page type. + PageType() format.PageType +} + +// DataPageHeader is a specialization of the PageHeader interface implemented by +// data pages. +type DataPageHeader interface { + PageHeader + + // Returns the encoding of the repetition level section. + RepetitionLevelEncoding() format.Encoding + + // Returns the encoding of the definition level section. + DefinitionLevelEncoding() format.Encoding + + // Returns the number of null values in the page. + NullCount() int64 + + // Returns the minimum value in the page based on the ordering rules of the + // column's logical type. + // + // As an optimization, the method may return the same slice across multiple + // calls. Programs must treat the returned value as immutable to prevent + // unpredictable behaviors. + // + // If the page only contains only null values, an empty slice is returned. + MinValue() []byte + + // Returns the maximum value in the page based on the ordering rules of the + // column's logical type. + // + // As an optimization, the method may return the same slice across multiple + // calls. Programs must treat the returned value as immutable to prevent + // unpredictable behaviors. + // + // If the page only contains only null values, an empty slice is returned. + MaxValue() []byte +} + +// DictionaryPageHeader is an implementation of the PageHeader interface +// representing dictionary pages. +type DictionaryPageHeader struct { + header *format.DictionaryPageHeader +} + +func (dict DictionaryPageHeader) NumValues() int64 { + return int64(dict.header.NumValues) +} + +func (dict DictionaryPageHeader) Encoding() format.Encoding { + return dict.header.Encoding +} + +func (dict DictionaryPageHeader) PageType() format.PageType { + return format.DictionaryPage +} + +func (dict DictionaryPageHeader) IsSorted() bool { + return dict.header.IsSorted +} + +func (dict DictionaryPageHeader) String() string { + return fmt.Sprintf("DICTIONARY_PAGE_HEADER{NumValues=%d,Encoding=%s,IsSorted=%t}", + dict.header.NumValues, + dict.header.Encoding, + dict.header.IsSorted) +} + +// DataPageHeaderV1 is an implementation of the DataPageHeader interface +// representing data pages version 1. +type DataPageHeaderV1 struct { + header *format.DataPageHeader +} + +func (v1 DataPageHeaderV1) NumValues() int64 { + return int64(v1.header.NumValues) +} + +func (v1 DataPageHeaderV1) RepetitionLevelEncoding() format.Encoding { + return v1.header.RepetitionLevelEncoding +} + +func (v1 DataPageHeaderV1) DefinitionLevelEncoding() format.Encoding { + return v1.header.DefinitionLevelEncoding +} + +func (v1 DataPageHeaderV1) Encoding() format.Encoding { + return v1.header.Encoding +} + +func (v1 DataPageHeaderV1) PageType() format.PageType { + return format.DataPage +} + +func (v1 DataPageHeaderV1) NullCount() int64 { + return v1.header.Statistics.NullCount +} + +func (v1 DataPageHeaderV1) MinValue() []byte { + return v1.header.Statistics.MinValue +} + +func (v1 DataPageHeaderV1) MaxValue() []byte { + return v1.header.Statistics.MaxValue +} + +func (v1 DataPageHeaderV1) String() string { + return fmt.Sprintf("DATA_PAGE_HEADER{NumValues=%d,Encoding=%s}", + v1.header.NumValues, + v1.header.Encoding) +} + +// DataPageHeaderV2 is an implementation of the DataPageHeader interface +// representing data pages version 2. +type DataPageHeaderV2 struct { + header *format.DataPageHeaderV2 +} + +func (v2 DataPageHeaderV2) NumValues() int64 { + return int64(v2.header.NumValues) +} + +func (v2 DataPageHeaderV2) NumNulls() int64 { + return int64(v2.header.NumNulls) +} + +func (v2 DataPageHeaderV2) NumRows() int64 { + return int64(v2.header.NumRows) +} + +func (v2 DataPageHeaderV2) RepetitionLevelsByteLength() int64 { + return int64(v2.header.RepetitionLevelsByteLength) +} + +func (v2 DataPageHeaderV2) DefinitionLevelsByteLength() int64 { + return int64(v2.header.DefinitionLevelsByteLength) +} + +func (v2 DataPageHeaderV2) RepetitionLevelEncoding() format.Encoding { + return format.RLE +} + +func (v2 DataPageHeaderV2) DefinitionLevelEncoding() format.Encoding { + return format.RLE +} + +func (v2 DataPageHeaderV2) Encoding() format.Encoding { + return v2.header.Encoding +} + +func (v2 DataPageHeaderV2) PageType() format.PageType { + return format.DataPageV2 +} + +func (v2 DataPageHeaderV2) NullCount() int64 { + return v2.header.Statistics.NullCount +} + +func (v2 DataPageHeaderV2) MinValue() []byte { + return v2.header.Statistics.MinValue +} + +func (v2 DataPageHeaderV2) MaxValue() []byte { + return v2.header.Statistics.MaxValue +} + +func (v2 DataPageHeaderV2) IsCompressed() bool { + return !v2.header.IsCompressed.Valid || v2.header.IsCompressed.V +} + +func (v2 DataPageHeaderV2) String() string { + return fmt.Sprintf("DATA_PAGE_HEADER_V2{NumValues=%d,NumNulls=%d,NumRows=%d,Encoding=%s,IsCompressed=%t}", + v2.header.NumValues, + v2.header.NumNulls, + v2.header.NumRows, + v2.header.Encoding, + v2.IsCompressed()) +} + +type unknownPageHeader struct { + header *format.PageHeader +} + +func (u unknownPageHeader) NumValues() int64 { + return 0 +} + +func (u unknownPageHeader) Encoding() format.Encoding { + return -1 +} + +func (u unknownPageHeader) PageType() format.PageType { + return u.header.Type +} + +func (u unknownPageHeader) String() string { + return fmt.Sprintf("UNKNOWN_PAGE_HEADER{Type=%d}", u.header.Type) +} + +var ( + _ PageHeader = DictionaryPageHeader{} + _ DataPageHeader = DataPageHeaderV1{} + _ DataPageHeader = DataPageHeaderV2{} + _ PageHeader = unknownPageHeader{} +) diff --git a/vendor/github.com/parquet-go/parquet-go/page_int32.go b/vendor/github.com/parquet-go/parquet-go/page_int32.go new file mode 100644 index 000000000..11bb4fb0e --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/page_int32.go @@ -0,0 +1,108 @@ +package parquet + +import ( + "io" + + "github.com/parquet-go/bitpack/unsafecast" + "github.com/parquet-go/parquet-go/encoding" + "github.com/parquet-go/parquet-go/internal/memory" +) + +type int32Page struct { + typ Type + values memory.SliceBuffer[int32] + columnIndex uint16 +} + +func newInt32Page(typ Type, columnIndex uint16, numValues int32, values encoding.Values) *int32Page { + return &int32Page{ + typ: typ, + values: memory.SliceBufferFrom(values.Int32()[:numValues]), + columnIndex: ^columnIndex, + } +} + +func (page *int32Page) Type() Type { return page.typ } + +func (page *int32Page) Column() int { return int(^page.columnIndex) } + +func (page *int32Page) Dictionary() Dictionary { return nil } + +func (page *int32Page) NumRows() int64 { return int64(page.values.Len()) } + +func (page *int32Page) NumValues() int64 { return int64(page.values.Len()) } + +func (page *int32Page) NumNulls() int64 { return 0 } + +func (page *int32Page) Size() int64 { return 4 * int64(page.values.Len()) } + +func (page *int32Page) RepetitionLevels() []byte { return nil } + +func (page *int32Page) DefinitionLevels() []byte { return nil } + +func (page *int32Page) Data() encoding.Values { return encoding.Int32Values(page.values.Slice()) } + +func (page *int32Page) Values() ValueReader { return &int32PageValues{page: page} } + +func (page *int32Page) min() int32 { return minInt32(page.values.Slice()) } + +func (page *int32Page) max() int32 { return maxInt32(page.values.Slice()) } + +func (page *int32Page) bounds() (min, max int32) { return boundsInt32(page.values.Slice()) } + +func (page *int32Page) Bounds() (min, max Value, ok bool) { + if ok = page.values.Len() > 0; ok { + minInt32, maxInt32 := page.bounds() + min = page.makeValue(minInt32) + max = page.makeValue(maxInt32) + } + return min, max, ok +} + +func (page *int32Page) Slice(i, j int64) Page { + sliced := &int32Page{ + typ: page.typ, + columnIndex: page.columnIndex, + } + sliced.values.Append(page.values.Slice()[i:j]...) + return sliced +} + +func (page *int32Page) makeValue(v int32) Value { + value := makeValueInt32(v) + value.columnIndex = page.columnIndex + return value +} + +type int32PageValues struct { + page *int32Page + offset int +} + +func (r *int32PageValues) Read(b []byte) (n int, err error) { + n, err = r.ReadInt32s(unsafecast.Slice[int32](b)) + return 4 * n, err +} + +func (r *int32PageValues) ReadInt32s(values []int32) (n int, err error) { + pageValues := r.page.values.Slice() + n = copy(values, pageValues[r.offset:]) + r.offset += n + if r.offset == len(pageValues) { + err = io.EOF + } + return n, err +} + +func (r *int32PageValues) ReadValues(values []Value) (n int, err error) { + pageValues := r.page.values.Slice() + for n < len(values) && r.offset < len(pageValues) { + values[n] = r.page.makeValue(pageValues[r.offset]) + r.offset++ + n++ + } + if r.offset == len(pageValues) { + err = io.EOF + } + return n, err +} diff --git a/vendor/github.com/parquet-go/parquet-go/page_int64.go b/vendor/github.com/parquet-go/parquet-go/page_int64.go new file mode 100644 index 000000000..555700ebe --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/page_int64.go @@ -0,0 +1,108 @@ +package parquet + +import ( + "io" + + "github.com/parquet-go/bitpack/unsafecast" + "github.com/parquet-go/parquet-go/encoding" + "github.com/parquet-go/parquet-go/internal/memory" +) + +type int64Page struct { + typ Type + values memory.SliceBuffer[int64] + columnIndex uint16 +} + +func newInt64Page(typ Type, columnIndex uint16, numValues int32, values encoding.Values) *int64Page { + return &int64Page{ + typ: typ, + values: memory.SliceBufferFrom(values.Int64()[:numValues]), + columnIndex: ^columnIndex, + } +} + +func (page *int64Page) Type() Type { return page.typ } + +func (page *int64Page) Column() int { return int(^page.columnIndex) } + +func (page *int64Page) Dictionary() Dictionary { return nil } + +func (page *int64Page) NumRows() int64 { return int64(page.values.Len()) } + +func (page *int64Page) NumValues() int64 { return int64(page.values.Len()) } + +func (page *int64Page) NumNulls() int64 { return 0 } + +func (page *int64Page) Size() int64 { return 8 * int64(page.values.Len()) } + +func (page *int64Page) RepetitionLevels() []byte { return nil } + +func (page *int64Page) DefinitionLevels() []byte { return nil } + +func (page *int64Page) Data() encoding.Values { return encoding.Int64Values(page.values.Slice()) } + +func (page *int64Page) Values() ValueReader { return &int64PageValues{page: page} } + +func (page *int64Page) min() int64 { return minInt64(page.values.Slice()) } + +func (page *int64Page) max() int64 { return maxInt64(page.values.Slice()) } + +func (page *int64Page) bounds() (min, max int64) { return boundsInt64(page.values.Slice()) } + +func (page *int64Page) Bounds() (min, max Value, ok bool) { + if ok = page.values.Len() > 0; ok { + minInt64, maxInt64 := page.bounds() + min = page.makeValue(minInt64) + max = page.makeValue(maxInt64) + } + return min, max, ok +} + +func (page *int64Page) Slice(i, j int64) Page { + sliced := &int64Page{ + typ: page.typ, + columnIndex: page.columnIndex, + } + sliced.values.Append(page.values.Slice()[i:j]...) + return sliced +} + +func (page *int64Page) makeValue(v int64) Value { + value := makeValueInt64(v) + value.columnIndex = page.columnIndex + return value +} + +type int64PageValues struct { + page *int64Page + offset int +} + +func (r *int64PageValues) Read(b []byte) (n int, err error) { + n, err = r.ReadInt64s(unsafecast.Slice[int64](b)) + return 8 * n, err +} + +func (r *int64PageValues) ReadInt64s(values []int64) (n int, err error) { + pageValues := r.page.values.Slice() + n = copy(values, pageValues[r.offset:]) + r.offset += n + if r.offset == len(pageValues) { + err = io.EOF + } + return n, err +} + +func (r *int64PageValues) ReadValues(values []Value) (n int, err error) { + pageValues := r.page.values.Slice() + for n < len(values) && r.offset < len(pageValues) { + values[n] = r.page.makeValue(pageValues[r.offset]) + r.offset++ + n++ + } + if r.offset == len(pageValues) { + err = io.EOF + } + return n, err +} diff --git a/vendor/github.com/parquet-go/parquet-go/page_int96.go b/vendor/github.com/parquet-go/parquet-go/page_int96.go new file mode 100644 index 000000000..2946dff67 --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/page_int96.go @@ -0,0 +1,107 @@ +package parquet + +import ( + "io" + + "github.com/parquet-go/bitpack/unsafecast" + "github.com/parquet-go/parquet-go/deprecated" + "github.com/parquet-go/parquet-go/encoding" +) + +type int96Page struct { + typ Type + values []deprecated.Int96 + columnIndex uint16 +} + +func newInt96Page(typ Type, columnIndex uint16, numValues int32, values encoding.Values) *int96Page { + return &int96Page{ + typ: typ, + values: values.Int96()[:numValues], + columnIndex: ^columnIndex, + } +} + +func (page *int96Page) Type() Type { return page.typ } + +func (page *int96Page) Column() int { return int(^page.columnIndex) } + +func (page *int96Page) Dictionary() Dictionary { return nil } + +func (page *int96Page) NumRows() int64 { return int64(len(page.values)) } + +func (page *int96Page) NumValues() int64 { return int64(len(page.values)) } + +func (page *int96Page) NumNulls() int64 { return 0 } + +func (page *int96Page) Size() int64 { return 12 * int64(len(page.values)) } + +func (page *int96Page) RepetitionLevels() []byte { return nil } + +func (page *int96Page) DefinitionLevels() []byte { return nil } + +func (page *int96Page) Data() encoding.Values { return encoding.Int96Values(page.values) } + +func (page *int96Page) Values() ValueReader { return &int96PageValues{page: page} } + +func (page *int96Page) min() deprecated.Int96 { return deprecated.MinInt96(page.values) } + +func (page *int96Page) max() deprecated.Int96 { return deprecated.MaxInt96(page.values) } + +func (page *int96Page) bounds() (min, max deprecated.Int96) { + return deprecated.MinMaxInt96(page.values) +} + +func (page *int96Page) Bounds() (min, max Value, ok bool) { + if ok = len(page.values) > 0; ok { + minInt96, maxInt96 := page.bounds() + min = page.makeValue(minInt96) + max = page.makeValue(maxInt96) + } + return min, max, ok +} + +func (page *int96Page) Slice(i, j int64) Page { + return &int96Page{ + typ: page.typ, + values: page.values[i:j], + columnIndex: page.columnIndex, + } +} + +func (page *int96Page) makeValue(v deprecated.Int96) Value { + value := makeValueInt96(v) + value.columnIndex = page.columnIndex + return value +} + +type int96PageValues struct { + page *int96Page + offset int +} + +func (r *int96PageValues) Read(b []byte) (n int, err error) { + n, err = r.ReadInt96s(unsafecast.Slice[deprecated.Int96](b)) + return 12 * n, err +} + +func (r *int96PageValues) ReadInt96s(values []deprecated.Int96) (n int, err error) { + n = copy(values, r.page.values[r.offset:]) + r.offset += n + if r.offset == len(r.page.values) { + err = io.EOF + } + return n, err +} + +func (r *int96PageValues) ReadValues(values []Value) (n int, err error) { + for n < len(values) && r.offset < len(r.page.values) { + values[n] = r.page.makeValue(r.page.values[r.offset]) + r.offset++ + n++ + } + if r.offset == len(r.page.values) { + err = io.EOF + } + return n, err +} diff --git a/vendor/github.com/parquet-go/parquet-go/page_max.go b/vendor/github.com/parquet-go/parquet-go/page_max.go new file mode 100644 index 000000000..f6afecf6a --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/page_max.go @@ -0,0 +1,23 @@ +package parquet + +import ( + "bytes" +) + +func maxFixedLenByteArray(data []byte, size int) (max []byte) { + if len(data) > 0 { + max = data[:size] + + for i, j := size, 2*size; j <= len(data); { + item := data[i:j] + + if bytes.Compare(item, max) > 0 { + max = item + } + + i += size + j += size + } + } + return max +} diff --git a/vendor/github.com/parquet-go/parquet-go/page_max_amd64.go b/vendor/github.com/parquet-go/parquet-go/page_max_amd64.go new file mode 100644 index 000000000..2ac1de2f0 --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/page_max_amd64.go @@ -0,0 +1,24 @@ +//go:build !purego + +package parquet + +//go:noescape +func maxInt32(data []int32) int32 + +//go:noescape +func maxInt64(data []int64) int64 + +//go:noescape +func maxUint32(data []uint32) uint32 + +//go:noescape +func maxUint64(data []uint64) uint64 + +//go:noescape +func maxFloat32(data []float32) float32 + +//go:noescape +func maxFloat64(data []float64) float64 + +//go:noescape +func maxBE128(data [][16]byte) []byte diff --git a/vendor/github.com/parquet-go/parquet-go/page_max_amd64.s b/vendor/github.com/parquet-go/parquet-go/page_max_amd64.s new file mode 100644 index 000000000..8159583d1 --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/page_max_amd64.s @@ -0,0 +1,598 @@ +//go:build !purego + +#include "textflag.h" + +// func maxInt32(data []int32) int32 +TEXT ·maxInt32(SB), NOSPLIT, $-28 + MOVQ data_base+0(FP), AX + MOVQ data_len+8(FP), CX + XORQ BX, BX + + CMPQ CX, $0 + JE done + XORQ SI, SI + MOVLQZX (AX), BX + + CMPB ·hasAVX512VL(SB), $0 + JE loop + + CMPQ CX, $32 + JB loop + + MOVQ CX, DI + SHRQ $5, DI + SHLQ $5, DI + VPBROADCASTD (AX), Z0 +loop32: + VMOVDQU32 (AX)(SI*4), Z1 + VMOVDQU32 64(AX)(SI*4), Z2 + VPMAXSD Z1, Z0, Z0 + VPMAXSD Z2, Z0, Z0 + ADDQ $32, SI + CMPQ SI, DI + JNE loop32 + + VMOVDQU32 swap32+0(SB), Z1 + VPERMI2D Z0, Z0, Z1 + VPMAXSD Y1, Y0, Y0 + + VMOVDQU32 swap32+32(SB), Y1 + VPERMI2D Y0, Y0, Y1 + VPMAXSD X1, X0, X0 + + VMOVDQU32 swap32+48(SB), X1 + VPERMI2D X0, X0, X1 + VPMAXSD X1, X0, X0 + VZEROUPPER + + MOVQ X0, DX + MOVL DX, BX + SHRQ $32, DX + CMPL DX, BX + CMOVLGT DX, BX + + CMPQ SI, CX + JE done +loop: + MOVLQZX (AX)(SI*4), DX + CMPL DX, BX + CMOVLGT DX, BX + INCQ SI + CMPQ SI, CX + JNE loop +done: + MOVL BX, ret+24(FP) + RET + +// func maxInt64(data []int64) int64 +TEXT ·maxInt64(SB), NOSPLIT, $-32 + MOVQ data_base+0(FP), AX + MOVQ data_len+8(FP), CX + XORQ BX, BX + + CMPQ CX, $0 + JE done + XORQ SI, SI + MOVQ (AX), BX + + CMPB ·hasAVX512VL(SB), $0 + JE loop + + CMPQ CX, $32 + JB loop + + MOVQ CX, DI + SHRQ $5, DI + SHLQ $5, DI + VPBROADCASTQ (AX), Z0 +loop32: + VMOVDQU64 (AX)(SI*8), Z1 + VMOVDQU64 64(AX)(SI*8), Z2 + VMOVDQU64 128(AX)(SI*8), Z3 + VMOVDQU64 192(AX)(SI*8), Z4 + VPMAXSQ Z1, Z2, Z5 + VPMAXSQ Z3, Z4, Z6 + VPMAXSQ Z5, Z6, Z1 + VPMAXSQ Z1, Z0, Z0 + ADDQ $32, SI + CMPQ SI, DI + JNE loop32 + + VMOVDQU32 swap32+0(SB), Z1 + VPERMI2D Z0, Z0, Z1 + VPMAXSQ Y1, Y0, Y0 + + VMOVDQU32 swap32+32(SB), Y1 + VPERMI2D Y0, Y0, Y1 + VPMAXSQ X1, X0, X0 + + VMOVDQU32 swap32+48(SB), X1 + VPERMI2D X0, X0, X1 + VPMAXSQ X1, X0, X0 + VZEROUPPER + + MOVQ X0, BX + CMPQ SI, CX + JE done +loop: + MOVQ (AX)(SI*8), DX + CMPQ DX, BX + CMOVQGT DX, BX + INCQ SI + CMPQ SI, CX + JNE loop +done: + MOVQ BX, ret+24(FP) + RET + +// func maxUint32(data []int32) int32 +TEXT ·maxUint32(SB), NOSPLIT, $-28 + MOVQ data_base+0(FP), AX + MOVQ data_len+8(FP), CX + XORQ BX, BX + + CMPQ CX, $0 + JE done + XORQ SI, SI + MOVLQZX (AX), BX + + CMPB ·hasAVX512VL(SB), $0 + JE loop + + CMPQ CX, $32 + JB loop + + MOVQ CX, DI + SHRQ $5, DI + SHLQ $5, DI + VPBROADCASTD (AX), Z0 +loop32: + VMOVDQU32 (AX)(SI*4), Z1 + VMOVDQU32 64(AX)(SI*4), Z2 + VPMAXUD Z1, Z0, Z0 + VPMAXUD Z2, Z0, Z0 + ADDQ $32, SI + CMPQ SI, DI + JNE loop32 + + VMOVDQU32 swap32+0(SB), Z1 + VPERMI2D Z0, Z0, Z1 + VPMAXUD Y1, Y0, Y0 + + VMOVDQU32 swap32+32(SB), Y1 + VPERMI2D Y0, Y0, Y1 + VPMAXUD X1, X0, X0 + + VMOVDQU32 swap32+48(SB), X1 + VPERMI2D X0, X0, X1 + VPMAXUD X1, X0, X0 + VZEROUPPER + + MOVQ X0, DX + MOVL DX, BX + SHRQ $32, DX + CMPL DX, BX + CMOVLHI DX, BX + + CMPQ SI, CX + JE done +loop: + MOVLQZX (AX)(SI*4), DX + CMPL DX, BX + CMOVLHI DX, BX + INCQ SI + CMPQ SI, CX + JNE loop +done: + MOVL BX, ret+24(FP) + RET + +// func maxUint64(data []uint64) uint64 +TEXT ·maxUint64(SB), NOSPLIT, $-32 + MOVQ data_base+0(FP), AX + MOVQ data_len+8(FP), CX + XORQ BX, BX + + CMPQ CX, $0 + JE done + XORQ SI, SI + MOVQ (AX), BX + + CMPB ·hasAVX512VL(SB), $0 + JE loop + + CMPQ CX, $32 + JB loop + + MOVQ CX, DI + SHRQ $5, DI + SHLQ $5, DI + VPBROADCASTQ (AX), Z0 +loop32: + VMOVDQU64 (AX)(SI*8), Z1 + VMOVDQU64 64(AX)(SI*8), Z2 + VMOVDQU64 128(AX)(SI*8), Z3 + VMOVDQU64 192(AX)(SI*8), Z4 + VPMAXUQ Z1, Z2, Z5 + VPMAXUQ Z3, Z4, Z6 + VPMAXUQ Z5, Z6, Z1 + VPMAXUQ Z1, Z0, Z0 + ADDQ $32, SI + CMPQ SI, DI + JNE loop32 + + VMOVDQU32 swap32+0(SB), Z1 + VPERMI2D Z0, Z0, Z1 + VPMAXUQ Y1, Y0, Y0 + + VMOVDQU32 swap32+32(SB), Y1 + VPERMI2D Y0, Y0, Y1 + VPMAXUQ X1, X0, X0 + + VMOVDQU32 swap32+48(SB), X1 + VPERMI2D X0, X0, X1 + VPMAXUQ X1, X0, X0 + VZEROUPPER + + MOVQ X0, BX + CMPQ SI, CX + JE done +loop: + MOVQ (AX)(SI*8), DX + CMPQ DX, BX + CMOVQHI DX, BX + INCQ SI + CMPQ SI, CX + JNE loop +done: + MOVQ BX, ret+24(FP) + RET + +// func maxFloat32(data []float32) float32 +TEXT ·maxFloat32(SB), NOSPLIT, $-28 + MOVQ data_base+0(FP), AX + MOVQ data_len+8(FP), CX + XORQ BX, BX + + CMPQ CX, $0 + JE done + XORPS X0, X0 + XORPS X1, X1 + XORQ SI, SI + MOVLQZX (AX), BX + MOVQ BX, X0 + + CMPB ·hasAVX512VL(SB), $0 + JE loop + + CMPQ CX, $64 + JB loop + + MOVQ CX, DI + SHRQ $6, DI + SHLQ $6, DI + VPBROADCASTD (AX), Z0 +loop64: + VMOVDQU32 (AX)(SI*4), Z1 + VMOVDQU32 64(AX)(SI*4), Z2 + VMOVDQU32 128(AX)(SI*4), Z3 + VMOVDQU32 192(AX)(SI*4), Z4 + VMAXPS Z1, Z2, Z5 + VMAXPS Z3, Z4, Z6 + VMAXPS Z5, Z6, Z1 + VMAXPS Z1, Z0, Z0 + ADDQ $64, SI + CMPQ SI, DI + JNE loop64 + + VMOVDQU32 swap32+0(SB), Z1 + VPERMI2D Z0, Z0, Z1 + VMAXPS Y1, Y0, Y0 + + VMOVDQU32 swap32+32(SB), Y1 + VPERMI2D Y0, Y0, Y1 + VMAXPS X1, X0, X0 + + VMOVDQU32 swap32+48(SB), X1 + VPERMI2D X0, X0, X1 + VMAXPS X1, X0, X0 + VZEROUPPER + + MOVAPS X0, X1 + PSRLQ $32, X1 + MOVQ X0, BX + MOVQ X1, DX + UCOMISS X0, X1 + CMOVLHI DX, BX + + CMPQ SI, CX + JE done + MOVQ BX, X0 +loop: + MOVLQZX (AX)(SI*4), DX + MOVQ DX, X1 + UCOMISS X0, X1 + CMOVLHI DX, BX + MOVQ BX, X0 + INCQ SI + CMPQ SI, CX + JNE loop +done: + MOVL BX, ret+24(FP) + RET + +// func maxFloat64(data []float64) float64 +TEXT ·maxFloat64(SB), NOSPLIT, $-32 + MOVQ data_base+0(FP), AX + MOVQ data_len+8(FP), CX + XORQ BX, BX + + CMPQ CX, $0 + JE done + XORPD X0, X0 + XORPD X1, X1 + XORQ SI, SI + MOVQ (AX), BX + MOVQ BX, X0 + + CMPB ·hasAVX512VL(SB), $0 + JE loop + + CMPQ CX, $32 + JB loop + + MOVQ CX, DI + SHRQ $5, DI + SHLQ $5, DI + VPBROADCASTQ (AX), Z0 +loop32: + VMOVDQU64 (AX)(SI*8), Z1 + VMOVDQU64 64(AX)(SI*8), Z2 + VMOVDQU64 128(AX)(SI*8), Z3 + VMOVDQU64 192(AX)(SI*8), Z4 + VMAXPD Z1, Z2, Z5 + VMAXPD Z3, Z4, Z6 + VMAXPD Z5, Z6, Z1 + VMAXPD Z1, Z0, Z0 + ADDQ $32, SI + CMPQ SI, DI + JNE loop32 + + VMOVDQU64 swap32+0(SB), Z1 + VPERMI2D Z0, Z0, Z1 + VMAXPD Y1, Y0, Y0 + + VMOVDQU64 swap32+32(SB), Y1 + VPERMI2D Y0, Y0, Y1 + VMAXPD X1, X0, X0 + + VMOVDQU64 swap32+48(SB), X1 + VPERMI2D X0, X0, X1 + VMAXPD X1, X0, X0 + VZEROUPPER + + MOVQ X0, BX + CMPQ SI, CX + JE done +loop: + MOVQ (AX)(SI*8), DX + MOVQ DX, X1 + UCOMISD X0, X1 + CMOVQHI DX, BX + MOVQ BX, X0 + INCQ SI + CMPQ SI, CX + JNE loop +done: + MOVQ BX, ret+24(FP) + RET + +// vpmaxu128 is a macro comparing unsigned 128 bits values held in the +// `srcValues` and `maxValues` vectors. The `srcIndexes` and `maxIndexes` +// vectors contain the indexes of elements in the value vectors. Remaining +// K and R arguments are mask and general purpose registers needed to hold +// temporary values during the computation. The last M argument is a mask +// generated by vpmaxu128mask. +// +// The routine uses AVX-512 instructions (VPCMPUQ, VPBLENDMQ) to implement +// the comparison of 128 bits values. The values are expected to be stored +// in the vectors as a little-endian pair of two consecutive quad words. +// +// The results are written to the `maxValues` and `maxIndexes` vectors, +// overwriting the inputs. `srcValues` and `srcIndexes` are read-only +// parameters. +// +// At a high level, for two pairs of quad words formaxg two 128 bits values +// A and B, the test implemented by this macro is: +// +// A[1] > B[1] || (A[1] == B[1] && A[0] > B[0]) +// +// Values in the source vector that evaluate to true on this expression are +// written to the vector of maximum values, and their indexes are written to +// the vector of indexes. +#define vpmaxu128(srcValues, srcIndexes, maxValues, maxIndexes, K1, K2, R1, R2, R3, M) \ + VPCMPUQ $0, maxValues, srcValues, K1 \ + VPCMPUQ $6, maxValues, srcValues, K2 \ + KMOVB K1, R1 \ + KMOVB K2, R2 \ + MOVB R2, R3 \ + SHLB $1, R3 \ + ANDB R3, R1 \ + ORB R2, R1 \ + ANDB M, R1 \ + MOVB R1, R2 \ + SHRB $1, R2 \ + ORB R2, R1 \ + KMOVB R1, K1 \ + VPBLENDMQ srcValues, maxValues, K1, maxValues \ + VPBLENDMQ srcIndexes, maxIndexes, K1, maxIndexes + +// vpmaxu128mask is a macro used to initialize the mask passed as last argument +// to vpmaxu128. The argument M is intended to be a general purpose register. +// +// The bit mask is used to merge the results of the "greater than" and "equal" +// comparison that are performed on each lane of maximum vectors. The upper bits +// are used to compute results of the operation to determine which of the pairs +// of quad words representing the 128 bits elements are the maximums. +#define vpmaxu128mask(M) MOVB $0b10101010, M + +// func maxBE128(data [][16]byte) []byte +TEXT ·maxBE128(SB), NOSPLIT, $-48 + MOVQ data_base+0(FP), AX + MOVQ data_len+8(FP), CX + CMPQ CX, $0 + JE null + + SHLQ $4, CX + MOVQ CX, DX // len + MOVQ AX, BX // max + ADDQ AX, CX // end + + CMPQ DX, $256 + JB loop + + CMPB ·hasAVX512MinMaxBE128(SB), $0 + JE loop + + // Z19 holds a vector of the count by which we increment the vectors of + // swap at each loop iteration. + MOVQ $16, DI + VPBROADCASTQ DI, Z19 + + // Z31 holds the shuffle mask used to convert 128 bits elements from big to + // little endian so we can apply vectorized comparison instructions. + VMOVDQU64 bswap128(SB), Z31 + + // These vectors hold four lanes of maximum values found in the input. + VBROADCASTI64X2 (AX), Z0 + VPSHUFB Z31, Z0, Z0 + VMOVDQU64 Z0, Z5 + VMOVDQU64 Z0, Z10 + VMOVDQU64 Z0, Z15 + + // These vectors hold four lanes of swap of maximum values. + // + // We initialize them at zero because we broadcast the first value of the + // input in the vectors that track the maximums of each lane; in other + // words, we assume the maximum value is at the first offset and work our + // way up from there. + VPXORQ Z2, Z2, Z2 + VPXORQ Z7, Z7, Z7 + VPXORQ Z12, Z12, Z12 + VPXORQ Z17, Z17, Z17 + + // These vectors are used to compute the swap of maximum values held + // in [Z1, Z5, Z10, Z15]. Each vector holds a contiguous sequence of + // swap; for example, Z3 is initialized with [0, 1, 2, 3]. At each + // loop iteration, the swap are incremented by the number of elements + // consumed from the input (4x4=16). + VMOVDQU64 indexes128(SB), Z3 + VPXORQ Z8, Z8, Z8 + VPXORQ Z13, Z13, Z13 + VPXORQ Z18, Z18, Z18 + MOVQ $4, DI + VPBROADCASTQ DI, Z1 + VPADDQ Z1, Z3, Z8 + VPADDQ Z1, Z8, Z13 + VPADDQ Z1, Z13, Z18 + + // This bit mask is used to merge the results of the "less than" and "equal" + // comparison that we perform on each lane of maximum vectors. We use the + // upper bits to compute four results of the operation which determines + // which of the pair of quad words representing the 128 bits elements is the + // maximum. + vpmaxu128mask(DI) + SHRQ $8, DX + SHLQ $8, DX + ADDQ AX, DX +loop16: + // Compute 4x4 maximum values in vector registers, along with their swap + // in the input array. + VMOVDQU64 (AX), Z1 + VMOVDQU64 64(AX), Z6 + VMOVDQU64 128(AX), Z11 + VMOVDQU64 192(AX), Z16 + VPSHUFB Z31, Z1, Z1 + VPSHUFB Z31, Z6, Z6 + VPSHUFB Z31, Z11, Z11 + VPSHUFB Z31, Z16, Z16 + vpmaxu128(Z1, Z3, Z0, Z2, K1, K2, R8, R9, R10, DI) + vpmaxu128(Z6, Z8, Z5, Z7, K3, K4, R11, R12, R13, DI) + vpmaxu128(Z11, Z13, Z10, Z12, K1, K2, R8, R9, R10, DI) + vpmaxu128(Z16, Z18, Z15, Z17, K3, K4, R11, R12, R13, DI) + VPADDQ Z19, Z3, Z3 + VPADDQ Z19, Z8, Z8 + VPADDQ Z19, Z13, Z13 + VPADDQ Z19, Z18, Z18 + ADDQ $256, AX + CMPQ AX, DX + JB loop16 + + // After the loop completed, we need to merge the lanes that each contain + // 4 maximum values (so 16 total candidate at this stage). The results are + // reduced into 4 candidates in Z0, with their swap in Z2. + vpmaxu128(Z10, Z12, Z0, Z2, K1, K2, R8, R9, R10, DI) + vpmaxu128(Z15, Z17, Z5, Z7, K3, K4, R11, R12, R13, DI) + vpmaxu128(Z5, Z7, Z0, Z2, K1, K2, R8, R9, R10, DI) + + // Further reduce the results by swapping the upper and lower parts of the + // vector registers, and comparing them to determaxe which values are the + // smallest. We compare 2x2 values at this step, then 2x1 values at the next + // to find the index of the maximum. + VMOVDQU64 swap64+0(SB), Z1 + VMOVDQU64 swap64+0(SB), Z3 + VPERMI2Q Z0, Z0, Z1 + VPERMI2Q Z2, Z2, Z3 + vpmaxu128(Y1, Y3, Y0, Y2, K1, K2, R8, R9, R10, DI) + + VMOVDQU64 swap64+32(SB), Y1 + VMOVDQU64 swap64+32(SB), Y3 + VPERMI2Q Y0, Y0, Y1 + VPERMI2Q Y2, Y2, Y3 + vpmaxu128(X1, X3, X0, X2, K1, K2, R8, R9, R10, DI) + VZEROUPPER + + // Extract the index of the maximum value computed in the lower 64 bits of + // X2 and position the BX pointer at the index of the maximum value. + MOVQ X2, DX + SHLQ $4, DX + ADDQ DX, BX + CMPQ AX, CX + JE done + + // Unless the input was aligned on 256 bytes, we need to perform a few more + // iterations on the remaining elements. + // + // This loop is also taken if the CPU has no support for AVX-512. +loop: + MOVQ (AX), R8 + MOVQ (BX), R9 + BSWAPQ R8 + BSWAPQ R9 + CMPQ R8, R9 + JA more + JB next + MOVQ 8(AX), R8 + MOVQ 8(BX), R9 + BSWAPQ R8 + BSWAPQ R9 + CMPQ R8, R9 + JBE next +more: + MOVQ AX, BX +next: + ADDQ $16, AX + CMPQ AX, CX + JB loop +done: + MOVQ BX, ret_base+24(FP) + MOVQ $16, ret_len+32(FP) + MOVQ $16, ret_cap+40(FP) + RET +null: + XORQ BX, BX + MOVQ BX, ret_base+24(FP) + MOVQ BX, ret_len+32(FP) + MOVQ BX, ret_cap+40(FP) + RET + diff --git a/vendor/github.com/parquet-go/parquet-go/page_max_purego.go b/vendor/github.com/parquet-go/parquet-go/page_max_purego.go new file mode 100644 index 000000000..0d23d12f6 --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/page_max_purego.go @@ -0,0 +1,72 @@ +//go:build purego || !amd64 + +package parquet + +import ( + "encoding/binary" + "slices" +) + +func maxInt32(data []int32) int32 { + if len(data) == 0 { + return 0 + } + return slices.Max(data) +} + +func maxInt64(data []int64) int64 { + if len(data) == 0 { + return 0 + } + return slices.Max(data) +} + +func maxUint32(data []uint32) uint32 { + if len(data) == 0 { + return 0 + } + return slices.Max(data) +} + +func maxUint64(data []uint64) uint64 { + if len(data) == 0 { + return 0 + } + return slices.Max(data) +} + +func maxFloat32(data []float32) float32 { + if len(data) == 0 { + return 0 + } + return slices.Max(data) +} + +func maxFloat64(data []float64) float64 { + if len(data) == 0 { + return 0 + } + return slices.Max(data) +} + +func maxBE128(data [][16]byte) (min []byte) { + if len(data) > 0 { + m := binary.BigEndian.Uint64(data[0][:8]) + j := 0 + for i := 1; i < len(data); i++ { + x := binary.BigEndian.Uint64(data[i][:8]) + switch { + case x > m: + m, j = x, i + case x == m: + y := binary.BigEndian.Uint64(data[i][8:]) + n := binary.BigEndian.Uint64(data[j][8:]) + if y > n { + m, j = x, i + } + } + } + min = data[j][:] + } + return min +} diff --git a/vendor/github.com/parquet-go/parquet-go/page_min.go b/vendor/github.com/parquet-go/parquet-go/page_min.go new file mode 100644 index 000000000..2bc185ac2 --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/page_min.go @@ -0,0 +1,23 @@ +package parquet + +import ( + "bytes" +) + +func minFixedLenByteArray(data []byte, size int) (min []byte) { + if len(data) > 0 { + min = data[:size] + + for i, j := size, 2*size; j <= len(data); { + item := data[i:j] + + if bytes.Compare(item, min) < 0 { + min = item + } + + i += size + j += size + } + } + return min +} diff --git a/vendor/github.com/parquet-go/parquet-go/page_min_amd64.go b/vendor/github.com/parquet-go/parquet-go/page_min_amd64.go new file mode 100644 index 000000000..c9fd65475 --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/page_min_amd64.go @@ -0,0 +1,24 @@ +//go:build !purego + +package parquet + +//go:noescape +func minInt32(data []int32) int32 + +//go:noescape +func minInt64(data []int64) int64 + +//go:noescape +func minUint32(data []uint32) uint32 + +//go:noescape +func minUint64(data []uint64) uint64 + +//go:noescape +func minFloat32(data []float32) float32 + +//go:noescape +func minFloat64(data []float64) float64 + +//go:noescape +func minBE128(data [][16]byte) []byte diff --git a/vendor/github.com/parquet-go/parquet-go/page_min_amd64.s b/vendor/github.com/parquet-go/parquet-go/page_min_amd64.s new file mode 100644 index 000000000..7bd2e3a87 --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/page_min_amd64.s @@ -0,0 +1,592 @@ +//go:build !purego + +#include "textflag.h" + +// func minInt32(data []int32) int32 +TEXT ·minInt32(SB), NOSPLIT, $-28 + MOVQ data_base+0(FP), AX + MOVQ data_len+8(FP), CX + XORQ BX, BX + + CMPQ CX, $0 + JE done + XORQ SI, SI + MOVLQZX (AX), BX + + CMPB ·hasAVX512VL(SB), $0 + JE loop + + CMPQ CX, $32 + JB loop + + MOVQ CX, DI + SHRQ $5, DI + SHLQ $5, DI + VPBROADCASTD (AX), Z0 +loop32: + VMOVDQU32 (AX)(SI*4), Z1 + VMOVDQU32 64(AX)(SI*4), Z2 + VPMINSD Z1, Z0, Z0 + VPMINSD Z2, Z0, Z0 + ADDQ $32, SI + CMPQ SI, DI + JNE loop32 + + VMOVDQU32 swap32+0(SB), Z1 + VPERMI2D Z0, Z0, Z1 + VPMINSD Y1, Y0, Y0 + + VMOVDQU32 swap32+32(SB), Y1 + VPERMI2D Y0, Y0, Y1 + VPMINSD X1, X0, X0 + + VMOVDQU32 swap32+48(SB), X1 + VPERMI2D X0, X0, X1 + VPMINSD X1, X0, X0 + VZEROUPPER + + MOVQ X0, DX + MOVL DX, BX + SHRQ $32, DX + CMPL DX, BX + CMOVLLT DX, BX + + CMPQ SI, CX + JE done +loop: + MOVLQZX (AX)(SI*4), DX + CMPL DX, BX + CMOVLLT DX, BX + INCQ SI + CMPQ SI, CX + JNE loop +done: + MOVL BX, ret+24(FP) + RET + +// func minInt64(data []int64) int64 +TEXT ·minInt64(SB), NOSPLIT, $-32 + MOVQ data_base+0(FP), AX + MOVQ data_len+8(FP), CX + XORQ BX, BX + + CMPQ CX, $0 + JE done + XORQ SI, SI + MOVQ (AX), BX + + CMPB ·hasAVX512VL(SB), $0 + JE loop + + CMPQ CX, $32 + JB loop + + MOVQ CX, DI + SHRQ $5, DI + SHLQ $5, DI + VPBROADCASTQ (AX), Z0 +loop32: + VMOVDQU64 (AX)(SI*8), Z1 + VMOVDQU64 64(AX)(SI*8), Z2 + VMOVDQU64 128(AX)(SI*8), Z3 + VMOVDQU64 192(AX)(SI*8), Z4 + VPMINSQ Z1, Z2, Z5 + VPMINSQ Z3, Z4, Z6 + VPMINSQ Z5, Z6, Z1 + VPMINSQ Z1, Z0, Z0 + ADDQ $32, SI + CMPQ SI, DI + JNE loop32 + + VMOVDQU32 swap32+0(SB), Z1 + VPERMI2D Z0, Z0, Z1 + VPMINSQ Y1, Y0, Y0 + + VMOVDQU32 swap32+32(SB), Y1 + VPERMI2D Y0, Y0, Y1 + VPMINSQ X1, X0, X0 + + VMOVDQU32 swap32+48(SB), X1 + VPERMI2D X0, X0, X1 + VPMINSQ X1, X0, X0 + VZEROUPPER + + MOVQ X0, BX + CMPQ SI, CX + JE done +loop: + MOVQ (AX)(SI*8), DX + CMPQ DX, BX + CMOVQLT DX, BX + INCQ SI + CMPQ SI, CX + JNE loop +done: + MOVQ BX, ret+24(FP) + RET + +// func minUint32(data []int32) int32 +TEXT ·minUint32(SB), NOSPLIT, $-28 + MOVQ data_base+0(FP), AX + MOVQ data_len+8(FP), CX + XORQ BX, BX + + CMPQ CX, $0 + JE done + XORQ SI, SI + MOVLQZX (AX), BX + + CMPB ·hasAVX512VL(SB), $0 + JE loop + + CMPQ CX, $32 + JB loop + + MOVQ CX, DI + SHRQ $5, DI + SHLQ $5, DI + VPBROADCASTD (AX), Z0 +loop32: + VMOVDQU32 (AX)(SI*4), Z1 + VMOVDQU32 64(AX)(SI*4), Z2 + VPMINUD Z1, Z0, Z0 + VPMINUD Z2, Z0, Z0 + ADDQ $32, SI + CMPQ SI, DI + JNE loop32 + + VMOVDQU32 swap32+0(SB), Z1 + VPERMI2D Z0, Z0, Z1 + VPMINUD Y1, Y0, Y0 + + VMOVDQU32 swap32+32(SB), Y1 + VPERMI2D Y0, Y0, Y1 + VPMINUD X1, X0, X0 + + VMOVDQU32 swap32+48(SB), X1 + VPERMI2D X0, X0, X1 + VPMINUD X1, X0, X0 + VZEROUPPER + + MOVQ X0, DX + MOVL DX, BX + SHRQ $32, DX + CMPL DX, BX + CMOVLCS DX, BX + + CMPQ SI, CX + JE done +loop: + MOVLQZX (AX)(SI*4), DX + CMPL DX, BX + CMOVLCS DX, BX + INCQ SI + CMPQ SI, CX + JNE loop +done: + MOVL BX, ret+24(FP) + RET + +// func minUint64(data []uint64) uint64 +TEXT ·minUint64(SB), NOSPLIT, $-32 + MOVQ data_base+0(FP), AX + MOVQ data_len+8(FP), CX + XORQ BX, BX + + CMPQ CX, $0 + JE done + XORQ SI, SI + MOVQ (AX), BX + + CMPB ·hasAVX512VL(SB), $0 + JE loop + + CMPQ CX, $32 + JB loop + + MOVQ CX, DI + SHRQ $5, DI + SHLQ $5, DI + VPBROADCASTQ (AX), Z0 +loop32: + VMOVDQU64 (AX)(SI*8), Z1 + VMOVDQU64 64(AX)(SI*8), Z2 + VMOVDQU64 128(AX)(SI*8), Z3 + VMOVDQU64 192(AX)(SI*8), Z4 + VPMINUQ Z1, Z2, Z5 + VPMINUQ Z3, Z4, Z6 + VPMINUQ Z5, Z6, Z1 + VPMINUQ Z1, Z0, Z0 + ADDQ $32, SI + CMPQ SI, DI + JNE loop32 + + VMOVDQU32 swap32+0(SB), Z1 + VPERMI2D Z0, Z0, Z1 + VPMINUQ Y1, Y0, Y0 + + VMOVDQU32 swap32+32(SB), Y1 + VPERMI2D Y0, Y0, Y1 + VPMINUQ X1, X0, X0 + + VMOVDQU32 swap32+48(SB), X1 + VPERMI2D X0, X0, X1 + VPMINUQ X1, X0, X0 + VZEROUPPER + + MOVQ X0, BX + CMPQ SI, CX + JE done +loop: + MOVQ (AX)(SI*8), DX + CMPQ DX, BX + CMOVQCS DX, BX + INCQ SI + CMPQ SI, CX + JNE loop +done: + MOVQ BX, ret+24(FP) + RET + +// func minFloat32(data []float32) float32 +TEXT ·minFloat32(SB), NOSPLIT, $-28 + MOVQ data_base+0(FP), AX + MOVQ data_len+8(FP), CX + XORQ BX, BX + + CMPQ CX, $0 + JE done + XORPS X0, X0 + XORPS X1, X1 + XORQ SI, SI + MOVLQZX (AX), BX + MOVQ BX, X0 + + CMPB ·hasAVX512VL(SB), $0 + JE loop + + CMPQ CX, $64 + JB loop + + MOVQ CX, DI + SHRQ $6, DI + SHLQ $6, DI + VPBROADCASTD (AX), Z0 +loop64: + VMOVDQU32 (AX)(SI*4), Z1 + VMOVDQU32 64(AX)(SI*4), Z2 + VMOVDQU32 128(AX)(SI*4), Z3 + VMOVDQU32 192(AX)(SI*4), Z4 + VMINPS Z1, Z2, Z5 + VMINPS Z3, Z4, Z6 + VMINPS Z5, Z6, Z1 + VMINPS Z1, Z0, Z0 + ADDQ $64, SI + CMPQ SI, DI + JNE loop64 + + VMOVDQU32 swap32+0(SB), Z1 + VPERMI2D Z0, Z0, Z1 + VMINPS Y1, Y0, Y0 + + VMOVDQU32 swap32+32(SB), Y1 + VPERMI2D Y0, Y0, Y1 + VMINPS X1, X0, X0 + + VMOVDQU32 swap32+48(SB), X1 + VPERMI2D X0, X0, X1 + VMINPS X1, X0, X0 + VZEROUPPER + + MOVAPS X0, X1 + PSRLQ $32, X1 + MOVQ X0, BX + MOVQ X1, DX + UCOMISS X0, X1 + CMOVLCS DX, BX + + CMPQ SI, CX + JE done + MOVQ BX, X0 +loop: + MOVLQZX (AX)(SI*4), DX + MOVQ DX, X1 + UCOMISS X0, X1 + CMOVLCS DX, BX + MOVQ BX, X0 + INCQ SI + CMPQ SI, CX + JNE loop +done: + MOVL BX, ret+24(FP) + RET + +// func minFloat64(data []float64) float64 +TEXT ·minFloat64(SB), NOSPLIT, $-32 + MOVQ data_base+0(FP), AX + MOVQ data_len+8(FP), CX + XORQ BX, BX + + CMPQ CX, $0 + JE done + XORPD X0, X0 + XORPD X1, X1 + XORQ SI, SI + MOVQ (AX), BX + MOVQ BX, X0 + + CMPB ·hasAVX512VL(SB), $0 + JE loop + + CMPQ CX, $32 + JB loop + + MOVQ CX, DI + SHRQ $5, DI + SHLQ $5, DI + VPBROADCASTQ (AX), Z0 +loop32: + VMOVDQU64 (AX)(SI*8), Z1 + VMOVDQU64 64(AX)(SI*8), Z2 + VMOVDQU64 128(AX)(SI*8), Z3 + VMOVDQU64 192(AX)(SI*8), Z4 + VMINPD Z1, Z2, Z5 + VMINPD Z3, Z4, Z6 + VMINPD Z5, Z6, Z1 + VMINPD Z1, Z0, Z0 + ADDQ $32, SI + CMPQ SI, DI + JNE loop32 + + VMOVDQU64 swap32+0(SB), Z1 + VPERMI2D Z0, Z0, Z1 + VMINPD Y1, Y0, Y0 + + VMOVDQU64 swap32+32(SB), Y1 + VPERMI2D Y0, Y0, Y1 + VMINPD X1, X0, X0 + + VMOVDQU64 swap32+48(SB), X1 + VPERMI2D X0, X0, X1 + VMINPD X1, X0, X0 + VZEROUPPER + + MOVQ X0, BX + CMPQ SI, CX + JE done +loop: + MOVQ (AX)(SI*8), DX + MOVQ DX, X1 + UCOMISD X0, X1 + CMOVQCS DX, BX + MOVQ BX, X0 + INCQ SI + CMPQ SI, CX + JNE loop +done: + MOVQ BX, ret+24(FP) + RET + +// vpminu128 is a macro comparing unsigned 128 bits values held in the +// `srcValues` and `minValues` vectors. The `srcIndexes` and `minIndexes` +// vectors contain the indexes of elements in the value vectors. Remaining +// K and R arguments are mask and general purpose registers needed to hold +// temporary values during the computation. The last M argument is a mask +// generated by vpminu128mask. +// +// The routine uses AVX-512 instructions (VPCMPUQ, VPBLENDMQ) to implement +// the comparison of 128 bits values. The values are expected to be stored +// in the vectors as a little-endian pair of two consecutive quad words. +// +// The results are written to the `minValues` and `minIndexes` vectors, +// overwriting the inputs. `srcValues` and `srcIndexes` are read-only +// parameters. +// +// At a high level, for two pairs of quad words forming two 128 bits values +// A and B, the test implemented by this macro is: +// +// A[1] < B[1] || (A[1] == B[1] && A[0] < B[0]) +// +// Values in the source vector that evalute to true on this expression are +// written to the vector of minimum values, and their indexes are written to +// the vector of indexes. +#define vpminu128(srcValues, srcIndexes, minValues, minIndexes, K1, K2, R1, R2, R3, M) \ + VPCMPUQ $0, minValues, srcValues, K1 \ + VPCMPUQ $1, minValues, srcValues, K2 \ + KMOVB K1, R1 \ + KMOVB K2, R2 \ + MOVB R2, R3 \ + SHLB $1, R3 \ + ANDB R3, R1 \ + ORB R2, R1 \ + ANDB M, R1 \ + MOVB R1, R2 \ + SHRB $1, R2 \ + ORB R2, R1 \ + KMOVB R1, K1 \ + VPBLENDMQ srcValues, minValues, K1, minValues \ + VPBLENDMQ srcIndexes, minIndexes, K1, minIndexes + +// vpminu128mask is a macro used to initialize the mask passed as last argument +// to vpminu128. The argument M is intended to be a general purpose register. +// +// The bit mask is used to merge the results of the "less than" and "equal" +// comparison that are performed on each lane of minimum vectors. The upper bits +// are used to compute results of the operation to determines which of the pairs +// of quad words representing the 128 bits elements are the minimums. +#define vpminu128mask(M) MOVB $0b10101010, M + +// func minBE128(data [][16]byte) []byte +TEXT ·minBE128(SB), NOSPLIT, $-48 + MOVQ data_base+0(FP), AX + MOVQ data_len+8(FP), CX + CMPQ CX, $0 + JE null + + SHLQ $4, CX + MOVQ CX, DX // len + MOVQ AX, BX // min + ADDQ AX, CX // end + + CMPQ DX, $256 + JB loop + + CMPB ·hasAVX512MinMaxBE128(SB), $0 + JE loop + + // Z19 holds a vector of the count by which we increment the vectors of + // swap at each loop iteration. + MOVQ $16, DI + VPBROADCASTQ DI, Z19 + + // Z31 holds the shuffle mask used to convert 128 bits elements from big to + // little endian so we can apply vectorized comparison instructions. + VMOVDQU64 bswap128(SB), Z31 + + // These vectors hold four lanes of minimum values found in the input. + VBROADCASTI64X2 (AX), Z0 + VPSHUFB Z31, Z0, Z0 + VMOVDQU64 Z0, Z5 + VMOVDQU64 Z0, Z10 + VMOVDQU64 Z0, Z15 + + // These vectors hold four lanes of swap of minimum values. + // + // We initialize them at zero because we broadcast the first value of the + // input in the vectors that track the minimums of each lane; in other + // words, we assume the minimum value is at the first offset and work our + // way up from there. + VPXORQ Z2, Z2, Z2 + VPXORQ Z7, Z7, Z7 + VPXORQ Z12, Z12, Z12 + VPXORQ Z17, Z17, Z17 + + // These vectors are used to compute the swap of minimum values held + // in [Z1, Z5, Z10, Z15]. Each vector holds a contiguous sequence of + // swap; for example, Z3 is initialized with [0, 1, 2, 3]. At each + // loop iteration, the swap are incremented by the number of elements + // consumed from the input (4x4=16). + VMOVDQU64 indexes128(SB), Z3 + VPXORQ Z8, Z8, Z8 + VPXORQ Z13, Z13, Z13 + VPXORQ Z18, Z18, Z18 + MOVQ $4, DI + VPBROADCASTQ DI, Z1 + VPADDQ Z1, Z3, Z8 + VPADDQ Z1, Z8, Z13 + VPADDQ Z1, Z13, Z18 + + vpminu128mask(DI) + SHRQ $8, DX + SHLQ $8, DX + ADDQ AX, DX +loop16: + // Compute 4x4 minimum values in vector registers, along with their swap + // in the input array. + VMOVDQU64 (AX), Z1 + VMOVDQU64 64(AX), Z6 + VMOVDQU64 128(AX), Z11 + VMOVDQU64 192(AX), Z16 + VPSHUFB Z31, Z1, Z1 + VPSHUFB Z31, Z6, Z6 + VPSHUFB Z31, Z11, Z11 + VPSHUFB Z31, Z16, Z16 + vpminu128(Z1, Z3, Z0, Z2, K1, K2, R8, R9, R10, DI) + vpminu128(Z6, Z8, Z5, Z7, K3, K4, R11, R12, R13, DI) + vpminu128(Z11, Z13, Z10, Z12, K1, K2, R8, R9, R10, DI) + vpminu128(Z16, Z18, Z15, Z17, K3, K4, R11, R12, R13, DI) + VPADDQ Z19, Z3, Z3 + VPADDQ Z19, Z8, Z8 + VPADDQ Z19, Z13, Z13 + VPADDQ Z19, Z18, Z18 + ADDQ $256, AX + CMPQ AX, DX + JB loop16 + + // After the loop completed, we need to merge the lanes that each contain + // 4 minimum values (so 16 total candidate at this stage). The results are + // reduced into 4 candidates in Z0, with their swap in Z2. + vpminu128(Z10, Z12, Z0, Z2, K1, K2, R8, R9, R10, DI) + vpminu128(Z15, Z17, Z5, Z7, K3, K4, R11, R12, R13, DI) + vpminu128(Z5, Z7, Z0, Z2, K1, K2, R8, R9, R10, DI) + + // Further reduce the results by swapping the upper and lower parts of the + // vector registers, and comparing them to determine which values are the + // smallest. We compare 2x2 values at this step, then 2x1 values at the next + // to find the index of the minimum. + VMOVDQU64 swap64+0(SB), Z1 + VMOVDQU64 swap64+0(SB), Z3 + VPERMI2Q Z0, Z0, Z1 + VPERMI2Q Z2, Z2, Z3 + vpminu128(Y1, Y3, Y0, Y2, K1, K2, R8, R9, R10, DI) + + VMOVDQU64 swap64+32(SB), Y1 + VMOVDQU64 swap64+32(SB), Y3 + VPERMI2Q Y0, Y0, Y1 + VPERMI2Q Y2, Y2, Y3 + vpminu128(X1, X3, X0, X2, K1, K2, R8, R9, R10, DI) + VZEROUPPER + + // Extract the index of the minimum value computed in the lower 64 bits of + // X2 and position the BX pointer at the index of the minimum value. + MOVQ X2, DX + SHLQ $4, DX + ADDQ DX, BX + CMPQ AX, CX + JE done + + // Unless the input was aligned on 256 bytes, we need to perform a few more + // iterations on the remaining elements. + // + // This loop is also taken if the CPU has no support for AVX-512. +loop: + MOVQ (AX), R8 + MOVQ (BX), R9 + BSWAPQ R8 + BSWAPQ R9 + CMPQ R8, R9 + JB less + JA next + MOVQ 8(AX), R8 + MOVQ 8(BX), R9 + BSWAPQ R8 + BSWAPQ R9 + CMPQ R8, R9 + JAE next +less: + MOVQ AX, BX +next: + ADDQ $16, AX + CMPQ AX, CX + JB loop +done: + MOVQ BX, ret_base+24(FP) + MOVQ $16, ret_len+32(FP) + MOVQ $16, ret_cap+40(FP) + RET +null: + XORQ BX, BX + MOVQ BX, ret_base+24(FP) + MOVQ BX, ret_len+32(FP) + MOVQ BX, ret_cap+40(FP) + RET diff --git a/vendor/github.com/parquet-go/parquet-go/page_min_purego.go b/vendor/github.com/parquet-go/parquet-go/page_min_purego.go new file mode 100644 index 000000000..e4fad096f --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/page_min_purego.go @@ -0,0 +1,72 @@ +//go:build purego || !amd64 + +package parquet + +import ( + "encoding/binary" + "slices" +) + +func minInt32(data []int32) int32 { + if len(data) == 0 { + return 0 + } + return slices.Min(data) +} + +func minInt64(data []int64) int64 { + if len(data) == 0 { + return 0 + } + return slices.Min(data) +} + +func minUint32(data []uint32) uint32 { + if len(data) == 0 { + return 0 + } + return slices.Min(data) +} + +func minUint64(data []uint64) uint64 { + if len(data) == 0 { + return 0 + } + return slices.Min(data) +} + +func minFloat32(data []float32) float32 { + if len(data) == 0 { + return 0 + } + return slices.Min(data) +} + +func minFloat64(data []float64) float64 { + if len(data) == 0 { + return 0 + } + return slices.Min(data) +} + +func minBE128(data [][16]byte) (min []byte) { + if len(data) > 0 { + m := binary.BigEndian.Uint64(data[0][:8]) + j := 0 + for i := 1; i < len(data); i++ { + x := binary.BigEndian.Uint64(data[i][:8]) + switch { + case x < m: + m, j = x, i + case x == m: + y := binary.BigEndian.Uint64(data[i][8:]) + n := binary.BigEndian.Uint64(data[j][8:]) + if y < n { + m, j = x, i + } + } + } + min = data[j][:] + } + return min +} diff --git a/vendor/github.com/parquet-go/parquet-go/page_null.go b/vendor/github.com/parquet-go/parquet-go/page_null.go new file mode 100644 index 000000000..e2b5de1d4 --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/page_null.go @@ -0,0 +1,57 @@ +package parquet + +import ( + "io" + + "github.com/parquet-go/parquet-go/encoding" +) + +type nullPage struct { + typ Type + column int + count int +} + +func newNullPage(typ Type, columnIndex uint16, numValues int32) *nullPage { + return &nullPage{ + typ: typ, + column: int(columnIndex), + count: int(numValues), + } +} + +func (page *nullPage) Type() Type { return page.typ } +func (page *nullPage) Column() int { return page.column } +func (page *nullPage) Dictionary() Dictionary { return nil } +func (page *nullPage) NumRows() int64 { return int64(page.count) } +func (page *nullPage) NumValues() int64 { return int64(page.count) } +func (page *nullPage) NumNulls() int64 { return int64(page.count) } +func (page *nullPage) Bounds() (min, max Value, ok bool) { return } +func (page *nullPage) Size() int64 { return 1 } +func (page *nullPage) Values() ValueReader { + return &nullPageValues{column: page.column, remain: page.count} +} +func (page *nullPage) Slice(i, j int64) Page { + return &nullPage{column: page.column, count: page.count - int(j-i)} +} +func (page *nullPage) RepetitionLevels() []byte { return nil } +func (page *nullPage) DefinitionLevels() []byte { return nil } +func (page *nullPage) Data() encoding.Values { return encoding.Values{} } + +type nullPageValues struct { + column int + remain int +} + +func (r *nullPageValues) ReadValues(values []Value) (n int, err error) { + columnIndex := ^uint16(r.column) + values = values[:min(r.remain, len(values))] + for i := range values { + values[i] = Value{columnIndex: columnIndex} + } + r.remain -= len(values) + if r.remain == 0 { + err = io.EOF + } + return len(values), err +} diff --git a/vendor/github.com/parquet-go/parquet-go/page_optional.go b/vendor/github.com/parquet-go/parquet-go/page_optional.go new file mode 100644 index 000000000..1bf6bbaaf --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/page_optional.go @@ -0,0 +1,117 @@ +package parquet + +import ( + "io" + + "github.com/parquet-go/parquet-go/encoding" +) + +type optionalPage struct { + base Page + maxDefinitionLevel byte + definitionLevels []byte +} + +func newOptionalPage(base Page, maxDefinitionLevel byte, definitionLevels []byte) *optionalPage { + return &optionalPage{ + base: base, + maxDefinitionLevel: maxDefinitionLevel, + definitionLevels: definitionLevels, + } +} + +func (page *optionalPage) Type() Type { return page.base.Type() } + +func (page *optionalPage) Column() int { return page.base.Column() } + +func (page *optionalPage) Dictionary() Dictionary { return page.base.Dictionary() } + +func (page *optionalPage) NumRows() int64 { return int64(len(page.definitionLevels)) } + +func (page *optionalPage) NumValues() int64 { return int64(len(page.definitionLevels)) } + +func (page *optionalPage) NumNulls() int64 { + return int64(countLevelsNotEqual(page.definitionLevels, page.maxDefinitionLevel)) +} + +func (page *optionalPage) Bounds() (min, max Value, ok bool) { return page.base.Bounds() } + +func (page *optionalPage) Size() int64 { return int64(len(page.definitionLevels)) + page.base.Size() } + +func (page *optionalPage) RepetitionLevels() []byte { return nil } + +func (page *optionalPage) DefinitionLevels() []byte { return page.definitionLevels } + +func (page *optionalPage) Data() encoding.Values { return page.base.Data() } + +func (page *optionalPage) Values() ValueReader { + return &optionalPageValues{ + page: page, + values: page.base.Values(), + } +} + +func (page *optionalPage) Slice(i, j int64) Page { + maxDefinitionLevel := page.maxDefinitionLevel + definitionLevels := page.definitionLevels + numNulls1 := int64(countLevelsNotEqual(definitionLevels[:i], maxDefinitionLevel)) + numNulls2 := int64(countLevelsNotEqual(definitionLevels[i:j], maxDefinitionLevel)) + return newOptionalPage( + page.base.Slice(i-numNulls1, j-(numNulls1+numNulls2)), + maxDefinitionLevel, + definitionLevels[i:j:j], + ) +} + +type optionalPageValues struct { + page *optionalPage + values ValueReader + offset int +} + +func (r *optionalPageValues) ReadValues(values []Value) (n int, err error) { + maxDefinitionLevel := r.page.maxDefinitionLevel + definitionLevels := r.page.definitionLevels + columnIndex := ^uint16(r.page.Column()) + + for n < len(values) && r.offset < len(definitionLevels) { + for n < len(values) && r.offset < len(definitionLevels) && definitionLevels[r.offset] != maxDefinitionLevel { + values[n] = Value{ + definitionLevel: definitionLevels[r.offset], + columnIndex: columnIndex, + } + r.offset++ + n++ + } + + i := n + j := r.offset + for i < len(values) && j < len(definitionLevels) && definitionLevels[j] == maxDefinitionLevel { + i++ + j++ + } + + if n < i { + j, err := r.values.ReadValues(values[n:i]) + if j == 0 && err == io.EOF { + // Underlying page exhausted before definitionLevels - corrupted file + return n, io.EOF + } + for ; j > 0; j-- { + values[n].definitionLevel = maxDefinitionLevel + r.offset++ + n++ + } + // Do not return on an io.EOF here as we may still have null values to read. + if err != nil && err != io.EOF { + return n, err + } + err = nil + } + } + + if r.offset == len(definitionLevels) { + err = io.EOF + } + return n, err +} diff --git a/vendor/github.com/parquet-go/parquet-go/page_repeated.go b/vendor/github.com/parquet-go/parquet-go/page_repeated.go new file mode 100644 index 000000000..f5db89202 --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/page_repeated.go @@ -0,0 +1,177 @@ +package parquet + +import ( + "io" + + "github.com/parquet-go/parquet-go/encoding" +) + +type repeatedPage struct { + base Page + maxRepetitionLevel byte + maxDefinitionLevel byte + definitionLevels []byte + repetitionLevels []byte +} + +func newRepeatedPage(base Page, maxRepetitionLevel, maxDefinitionLevel byte, repetitionLevels, definitionLevels []byte) *repeatedPage { + return &repeatedPage{ + base: base, + maxRepetitionLevel: maxRepetitionLevel, + maxDefinitionLevel: maxDefinitionLevel, + definitionLevels: definitionLevels, + repetitionLevels: repetitionLevels, + } +} + +func (page *repeatedPage) Type() Type { return page.base.Type() } + +func (page *repeatedPage) Column() int { return page.base.Column() } + +func (page *repeatedPage) Dictionary() Dictionary { return page.base.Dictionary() } + +func (page *repeatedPage) NumRows() int64 { return int64(countLevelsEqual(page.repetitionLevels, 0)) } + +func (page *repeatedPage) NumValues() int64 { return int64(len(page.definitionLevels)) } + +func (page *repeatedPage) NumNulls() int64 { + return int64(countLevelsNotEqual(page.definitionLevels, page.maxDefinitionLevel)) +} + +func (page *repeatedPage) Bounds() (min, max Value, ok bool) { return page.base.Bounds() } + +func (page *repeatedPage) Size() int64 { + return int64(len(page.repetitionLevels)) + int64(len(page.definitionLevels)) + page.base.Size() +} + +func (page *repeatedPage) RepetitionLevels() []byte { return page.repetitionLevels } + +func (page *repeatedPage) DefinitionLevels() []byte { return page.definitionLevels } + +func (page *repeatedPage) Data() encoding.Values { return page.base.Data() } + +func (page *repeatedPage) Values() ValueReader { + return &repeatedPageValues{ + page: page, + values: page.base.Values(), + } +} + +func (page *repeatedPage) Slice(i, j int64) Page { + numRows := page.NumRows() + if i < 0 || i > numRows { + panic(errPageBoundsOutOfRange(i, j, numRows)) + } + if j < 0 || j > numRows { + panic(errPageBoundsOutOfRange(i, j, numRows)) + } + if i > j { + panic(errPageBoundsOutOfRange(i, j, numRows)) + } + + maxRepetitionLevel := page.maxRepetitionLevel + maxDefinitionLevel := page.maxDefinitionLevel + repetitionLevels := page.repetitionLevels + definitionLevels := page.definitionLevels + + rowIndex0 := 0 + rowIndex1 := len(repetitionLevels) + rowIndex2 := len(repetitionLevels) + + for k, def := range repetitionLevels { + if def == 0 { + if rowIndex0 == int(i) { + rowIndex1 = k + break + } + rowIndex0++ + } + } + + for k, def := range repetitionLevels[rowIndex1:] { + if def == 0 { + if rowIndex0 == int(j) { + rowIndex2 = rowIndex1 + k + break + } + rowIndex0++ + } + } + + numNulls1 := countLevelsNotEqual(definitionLevels[:rowIndex1], maxDefinitionLevel) + numNulls2 := countLevelsNotEqual(definitionLevels[rowIndex1:rowIndex2], maxDefinitionLevel) + + i = int64(rowIndex1 - numNulls1) + j = int64(rowIndex2 - (numNulls1 + numNulls2)) + + return newRepeatedPage( + page.base.Slice(i, j), + maxRepetitionLevel, + maxDefinitionLevel, + repetitionLevels[rowIndex1:rowIndex2:rowIndex2], + definitionLevels[rowIndex1:rowIndex2:rowIndex2], + ) +} + +type repeatedPageValues struct { + page *repeatedPage + values ValueReader + offset int +} + +func (r *repeatedPageValues) ReadValues(values []Value) (n int, err error) { + maxDefinitionLevel := r.page.maxDefinitionLevel + definitionLevels := r.page.definitionLevels + repetitionLevels := r.page.repetitionLevels + columnIndex := ^uint16(r.page.Column()) + + // While we haven't exceeded the output buffer and we haven't exceeded the page size. + for n < len(values) && r.offset < len(definitionLevels) { + + // While we haven't exceeded the output buffer and we haven't exceeded the + // page size AND the current element's definitionLevel is not the + // maxDefinitionLevel (this is a null value), Create the zero values to be + // returned in this run. + for n < len(values) && r.offset < len(definitionLevels) && definitionLevels[r.offset] != maxDefinitionLevel { + values[n] = Value{ + repetitionLevel: repetitionLevels[r.offset], + definitionLevel: definitionLevels[r.offset], + columnIndex: columnIndex, + } + r.offset++ + n++ + } + + i := n + j := r.offset + // Get the length of the run of non-zero values to be copied. + for i < len(values) && j < len(definitionLevels) && definitionLevels[j] == maxDefinitionLevel { + i++ + j++ + } + + // Copy all the non-zero values in this run. + if n < i { + j, err := r.values.ReadValues(values[n:i]) + if j == 0 && err == io.EOF { + // Underlying page exhausted before definitionLevels - corrupted file + return n, io.EOF + } + for ; j > 0; j-- { + values[n].repetitionLevel = repetitionLevels[r.offset] + values[n].definitionLevel = maxDefinitionLevel + r.offset++ + n++ + } + if err != nil && err != io.EOF { + return n, err + } + err = nil + } + } + + if r.offset == len(definitionLevels) { + err = io.EOF + } + return n, err +} diff --git a/vendor/github.com/parquet-go/parquet-go/page_uint32.go b/vendor/github.com/parquet-go/parquet-go/page_uint32.go new file mode 100644 index 000000000..fe49fe65d --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/page_uint32.go @@ -0,0 +1,108 @@ +package parquet + +import ( + "io" + + "github.com/parquet-go/bitpack/unsafecast" + "github.com/parquet-go/parquet-go/encoding" + "github.com/parquet-go/parquet-go/internal/memory" +) + +type uint32Page struct { + typ Type + values memory.SliceBuffer[uint32] + columnIndex uint16 +} + +func newUint32Page(typ Type, columnIndex uint16, numValues int32, values encoding.Values) *uint32Page { + return &uint32Page{ + typ: typ, + values: memory.SliceBufferFrom(values.Uint32()[:numValues]), + columnIndex: ^columnIndex, + } +} + +func (page *uint32Page) Type() Type { return page.typ } + +func (page *uint32Page) Column() int { return int(^page.columnIndex) } + +func (page *uint32Page) Dictionary() Dictionary { return nil } + +func (page *uint32Page) NumRows() int64 { return int64(page.values.Len()) } + +func (page *uint32Page) NumValues() int64 { return int64(page.values.Len()) } + +func (page *uint32Page) NumNulls() int64 { return 0 } + +func (page *uint32Page) Size() int64 { return 4 * int64(page.values.Len()) } + +func (page *uint32Page) RepetitionLevels() []byte { return nil } + +func (page *uint32Page) DefinitionLevels() []byte { return nil } + +func (page *uint32Page) Data() encoding.Values { return encoding.Uint32Values(page.values.Slice()) } + +func (page *uint32Page) Values() ValueReader { return &uint32PageValues{page: page} } + +func (page *uint32Page) min() uint32 { return minUint32(page.values.Slice()) } + +func (page *uint32Page) max() uint32 { return maxUint32(page.values.Slice()) } + +func (page *uint32Page) bounds() (min, max uint32) { return boundsUint32(page.values.Slice()) } + +func (page *uint32Page) Bounds() (min, max Value, ok bool) { + if ok = page.values.Len() > 0; ok { + minUint32, maxUint32 := page.bounds() + min = page.makeValue(minUint32) + max = page.makeValue(maxUint32) + } + return min, max, ok +} + +func (page *uint32Page) Slice(i, j int64) Page { + sliced := &uint32Page{ + typ: page.typ, + columnIndex: page.columnIndex, + } + sliced.values.Append(page.values.Slice()[i:j]...) + return sliced +} + +func (page *uint32Page) makeValue(v uint32) Value { + value := makeValueUint32(v) + value.columnIndex = page.columnIndex + return value +} + +type uint32PageValues struct { + page *uint32Page + offset int +} + +func (r *uint32PageValues) Read(b []byte) (n int, err error) { + n, err = r.ReadUint32s(unsafecast.Slice[uint32](b)) + return 4 * n, err +} + +func (r *uint32PageValues) ReadUint32s(values []uint32) (n int, err error) { + pageValues := r.page.values.Slice() + n = copy(values, pageValues[r.offset:]) + r.offset += n + if r.offset == len(pageValues) { + err = io.EOF + } + return n, err +} + +func (r *uint32PageValues) ReadValues(values []Value) (n int, err error) { + pageValues := r.page.values.Slice() + for n < len(values) && r.offset < len(pageValues) { + values[n] = r.page.makeValue(pageValues[r.offset]) + r.offset++ + n++ + } + if r.offset == len(pageValues) { + err = io.EOF + } + return n, err +} diff --git a/vendor/github.com/parquet-go/parquet-go/page_uint64.go b/vendor/github.com/parquet-go/parquet-go/page_uint64.go new file mode 100644 index 000000000..342744a84 --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/page_uint64.go @@ -0,0 +1,108 @@ +package parquet + +import ( + "io" + + "github.com/parquet-go/bitpack/unsafecast" + "github.com/parquet-go/parquet-go/encoding" + "github.com/parquet-go/parquet-go/internal/memory" +) + +type uint64Page struct { + typ Type + values memory.SliceBuffer[uint64] + columnIndex uint16 +} + +func newUint64Page(typ Type, columnIndex uint16, numValues int32, values encoding.Values) *uint64Page { + return &uint64Page{ + typ: typ, + values: memory.SliceBufferFrom(values.Uint64()[:numValues]), + columnIndex: ^columnIndex, + } +} + +func (page *uint64Page) Type() Type { return page.typ } + +func (page *uint64Page) Column() int { return int(^page.columnIndex) } + +func (page *uint64Page) Dictionary() Dictionary { return nil } + +func (page *uint64Page) NumRows() int64 { return int64(page.values.Len()) } + +func (page *uint64Page) NumValues() int64 { return int64(page.values.Len()) } + +func (page *uint64Page) NumNulls() int64 { return 0 } + +func (page *uint64Page) Size() int64 { return 8 * int64(page.values.Len()) } + +func (page *uint64Page) RepetitionLevels() []byte { return nil } + +func (page *uint64Page) DefinitionLevels() []byte { return nil } + +func (page *uint64Page) Data() encoding.Values { return encoding.Uint64Values(page.values.Slice()) } + +func (page *uint64Page) Values() ValueReader { return &uint64PageValues{page: page} } + +func (page *uint64Page) min() uint64 { return minUint64(page.values.Slice()) } + +func (page *uint64Page) max() uint64 { return maxUint64(page.values.Slice()) } + +func (page *uint64Page) bounds() (min, max uint64) { return boundsUint64(page.values.Slice()) } + +func (page *uint64Page) Bounds() (min, max Value, ok bool) { + if ok = page.values.Len() > 0; ok { + minUint64, maxUint64 := page.bounds() + min = page.makeValue(minUint64) + max = page.makeValue(maxUint64) + } + return min, max, ok +} + +func (page *uint64Page) Slice(i, j int64) Page { + sliced := &uint64Page{ + typ: page.typ, + columnIndex: page.columnIndex, + } + sliced.values.Append(page.values.Slice()[i:j]...) + return sliced +} + +func (page *uint64Page) makeValue(v uint64) Value { + value := makeValueUint64(v) + value.columnIndex = page.columnIndex + return value +} + +type uint64PageValues struct { + page *uint64Page + offset int +} + +func (r *uint64PageValues) Read(b []byte) (n int, err error) { + n, err = r.ReadUint64s(unsafecast.Slice[uint64](b)) + return 8 * n, err +} + +func (r *uint64PageValues) ReadUint64s(values []uint64) (n int, err error) { + pageValues := r.page.values.Slice() + n = copy(values, pageValues[r.offset:]) + r.offset += n + if r.offset == len(pageValues) { + err = io.EOF + } + return n, err +} + +func (r *uint64PageValues) ReadValues(values []Value) (n int, err error) { + pageValues := r.page.values.Slice() + for n < len(values) && r.offset < len(pageValues) { + values[n] = r.page.makeValue(pageValues[r.offset]) + r.offset++ + n++ + } + if r.offset == len(pageValues) { + err = io.EOF + } + return n, err +} diff --git a/vendor/github.com/parquet-go/parquet-go/parquet.go b/vendor/github.com/parquet-go/parquet-go/parquet.go new file mode 100644 index 000000000..666b16eb1 --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/parquet.go @@ -0,0 +1,122 @@ +// Copyright 2022 Twilio Inc. + +// Package parquet is a library for working with parquet files. For an overview +// of Parquet's qualities as a storage format, see this blog post: +// https://blog.twitter.com/engineering/en_us/a/2013/dremel-made-simple-with-parquet +// +// Or see the Parquet documentation: https://parquet.apache.org/docs/ +package parquet + +import ( + "io" + "os" + "reflect" +) + +// Read reads and returns rows from the parquet file in the given reader. +// +// The type T defines the type of rows read from r. T must be compatible with +// the file's schema or an error will be returned. The row type might represent +// a subset of the full schema, in which case only a subset of the columns will +// be loaded from r. +// +// This function is provided for convenience to facilitate reading of parquet +// files from arbitrary locations in cases where the data set fit in memory. +func Read[T any](r io.ReaderAt, size int64, options ...ReaderOption) (rows []T, err error) { + config, err := NewReaderConfig(options...) + if err != nil { + return nil, err + } + file, err := OpenFile(r, size) + if err != nil { + return nil, err + } + rows = make([]T, file.NumRows()) + reader := NewGenericReader[T](file, config) + n, err := reader.Read(rows) + if err == io.EOF { + err = nil + } + reader.Close() + return rows[:n], err +} + +// ReadFile reads rows of the parquet file at the given path. +// +// The type T defines the type of rows read from r. T must be compatible with +// the file's schema or an error will be returned. The row type might represent +// a subset of the full schema, in which case only a subset of the columns will +// be loaded from the file. +// +// This function is provided for convenience to facilitate reading of parquet +// files from the file system in cases where the data set fit in memory. +func ReadFile[T any](path string, options ...ReaderOption) (rows []T, err error) { + f, err := os.Open(path) + if err != nil { + return nil, err + } + defer f.Close() + s, err := f.Stat() + if err != nil { + return nil, err + } + return Read[T](f, s.Size()) +} + +// Write writes the given list of rows to a parquet file written to w. +// +// This function is provided for convenience to facilitate the creation of +// parquet files. +func Write[T any](w io.Writer, rows []T, options ...WriterOption) error { + config, err := NewWriterConfig(options...) + if err != nil { + return err + } + writer := NewGenericWriter[T](w, config) + if _, err := writer.Write(rows); err != nil { + return err + } + return writer.Close() +} + +// Write writes the given list of rows to a parquet file written to w. +// +// This function is provided for convenience to facilitate writing parquet +// files to the file system. +func WriteFile[T any](path string, rows []T, options ...WriterOption) error { + f, err := os.Create(path) + if err != nil { + return err + } + defer f.Close() + return Write(f, rows, options...) +} + +func atLeastOne(size int) int { + return atLeast(size, 1) +} + +func atLeast(size, least int) int { + if size < least { + return least + } + return size +} + +func typeNameOf(t reflect.Type) string { + s1 := t.String() + s2 := t.Kind().String() + if s1 == s2 { + return s1 + } + return s1 + " (" + s2 + ")" +} + +func isZero(b []byte) bool { + for _, c := range b { + if c != 0 { + return false + } + } + return true +} diff --git a/vendor/github.com/parquet-go/parquet-go/parquet_amd64.go b/vendor/github.com/parquet-go/parquet-go/parquet_amd64.go new file mode 100644 index 000000000..64165571d --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/parquet_amd64.go @@ -0,0 +1,18 @@ +//go:build !purego + +package parquet + +import "golang.org/x/sys/cpu" + +var ( + // This variable is used in x86 assembly source files to gate the use of + // AVX2 instructions depending on whether the CPU supports it. + hasAVX2 = cpu.X86.HasAVX2 + hasAVX512F = cpu.X86.HasAVX512F + hasAVX512VL = cpu.X86.HasAVX512F && cpu.X86.HasAVX512VL + // For min/max functions over big-endian 128 bits values, we need the + // follwing instructions from the DQ set: + // * VPBROADCASTQ (with 64 bits source register) + // * VBROADCASTI64X2 + hasAVX512MinMaxBE128 = cpu.X86.HasAVX512F && cpu.X86.HasAVX512DQ +) diff --git a/vendor/github.com/parquet-go/parquet-go/print.go b/vendor/github.com/parquet-go/parquet-go/print.go new file mode 100644 index 000000000..0de9eeb83 --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/print.go @@ -0,0 +1,363 @@ +package parquet + +import ( + "errors" + "fmt" + "io" + "strconv" + "strings" + "text/tabwriter" +) + +func PrintSchema(w io.Writer, name string, node Node) error { + return PrintSchemaIndent(w, name, node, "\t", "\n") +} + +func PrintSchemaIndent(w io.Writer, name string, node Node, pattern, newline string) error { + pw := &printWriter{writer: w} + pi := &printIndent{} + + if node.Leaf() { + printSchemaWithIndent(pw, "", node, pi) + } else { + pw.WriteString("message ") + + if name == "" { + pw.WriteString("{") + } else { + pw.WriteString(name) + pw.WriteString(" {") + } + + pi.pattern = pattern + pi.newline = newline + pi.repeat = 1 + pi.writeNewLine(pw) + + for _, field := range node.Fields() { + printSchemaWithIndent(pw, field.Name(), field, pi) + pi.writeNewLine(pw) + } + + pw.WriteString("}") + } + + return pw.err +} + +func printSchemaWithIndent(w io.StringWriter, name string, node Node, indent *printIndent) { + indent.writeTo(w) + + switch { + case node.Optional(): + w.WriteString("optional ") + case node.Repeated(): + w.WriteString("repeated ") + default: + w.WriteString("required ") + } + + if node.Leaf() { + t := node.Type() + switch t.Kind() { + case Boolean: + w.WriteString("boolean") + case Int32: + w.WriteString("int32") + case Int64: + w.WriteString("int64") + case Int96: + w.WriteString("int96") + case Float: + w.WriteString("float") + case Double: + w.WriteString("double") + case ByteArray: + w.WriteString("binary") + case FixedLenByteArray: + w.WriteString("fixed_len_byte_array(") + w.WriteString(strconv.Itoa(t.Length())) + w.WriteString(")") + default: + w.WriteString("") + } + + if name != "" { + w.WriteString(" ") + w.WriteString(name) + } + + if annotation := annotationOf(node); annotation != "" { + w.WriteString(" (") + w.WriteString(annotation) + w.WriteString(")") + } + + if id := node.ID(); id != 0 { + w.WriteString(" = ") + w.WriteString(strconv.Itoa(id)) + } + + w.WriteString(";") + } else { + w.WriteString("group") + + if name != "" { + w.WriteString(" ") + w.WriteString(name) + } + + if annotation := annotationOf(node); annotation != "" { + w.WriteString(" (") + w.WriteString(annotation) + w.WriteString(")") + } + + if id := node.ID(); id != 0 { + w.WriteString(" = ") + w.WriteString(strconv.Itoa(id)) + } + + w.WriteString(" {") + indent.writeNewLine(w) + indent.push() + + for _, field := range node.Fields() { + printSchemaWithIndent(w, field.Name(), field, indent) + indent.writeNewLine(w) + } + + indent.pop() + indent.writeTo(w) + w.WriteString("}") + } +} + +func annotationOf(node Node) string { + if logicalType := node.Type().LogicalType(); logicalType != nil { + return logicalType.String() + } + return "" +} + +type printIndent struct { + pattern string + newline string + repeat int +} + +func (i *printIndent) push() { + i.repeat++ +} + +func (i *printIndent) pop() { + i.repeat-- +} + +func (i *printIndent) writeTo(w io.StringWriter) { + if i.pattern != "" { + for n := i.repeat; n > 0; n-- { + w.WriteString(i.pattern) + } + } +} + +func (i *printIndent) writeNewLine(w io.StringWriter) { + if i.newline != "" { + w.WriteString(i.newline) + } +} + +type printWriter struct { + writer io.Writer + err error +} + +func (w *printWriter) Write(b []byte) (int, error) { + if w.err != nil { + return 0, w.err + } + n, err := w.writer.Write(b) + if err != nil { + w.err = err + } + return n, err +} + +func (w *printWriter) WriteString(s string) (int, error) { + if w.err != nil { + return 0, w.err + } + n, err := io.WriteString(w.writer, s) + if err != nil { + w.err = err + } + return n, err +} + +var ( + _ io.StringWriter = (*printWriter)(nil) +) + +func sprint(name string, node Node) string { + s := new(strings.Builder) + PrintSchema(s, name, node) + return s.String() +} + +func PrintRowGroup(w io.Writer, rowGroup RowGroup) error { + schema := rowGroup.Schema() + pw := &printWriter{writer: w} + tw := tabwriter.NewWriter(pw, 0, 0, 2, ' ', 0) + + columns := schema.Columns() + header := make([]string, len(columns)) + footer := make([]string, len(columns)) + + for i, column := range columns { + leaf, _ := schema.Lookup(column...) + columnType := leaf.Node.Type() + + header[i] = strings.Join(column, ".") + footer[i] = columnType.String() + } + + // Print header + for i, h := range header { + if i > 0 { + pw.WriteString("\t") + } + pw.WriteString(h) + } + pw.WriteString("\n") + + // Print separator line + for i := range header { + if i > 0 { + pw.WriteString("\t") + } + pw.WriteString(strings.Repeat("-", len(header[i]))) + } + pw.WriteString("\n") + + rowbuf := make([]Row, defaultRowBufferSize) + cells := make([]string, 0, len(columns)) + rows := rowGroup.Rows() + defer rows.Close() + + for { + n, err := rows.ReadRows(rowbuf) + + for _, row := range rowbuf[:n] { + cells = cells[:0] + + for _, value := range row { + columnIndex := value.Column() + + for len(cells) <= columnIndex { + cells = append(cells, "") + } + + if cells[columnIndex] == "" { + cells[columnIndex] = value.String() + } else { + cells[columnIndex] += "," + value.String() + } + } + + // Print row + for i, cell := range cells { + if i > 0 { + tw.Write([]byte("\t")) + } + tw.Write([]byte(cell)) + } + tw.Write([]byte("\n")) + } + + if err != nil { + if errors.Is(err, io.EOF) { + break + } + return err + } + } + + // Print footer + for i := range header { + if i > 0 { + pw.WriteString("\t") + } + pw.WriteString(strings.Repeat("-", len(header[i]))) + } + pw.WriteString("\n") + for i, f := range footer { + if i > 0 { + pw.WriteString("\t") + } + pw.WriteString(f) + } + pw.WriteString("\n") + + tw.Flush() + fmt.Fprintf(pw, "%d rows\n\n", rowGroup.NumRows()) + return pw.err +} + +func PrintColumnChunk(w io.Writer, columnChunk ColumnChunk) error { + pw := &printWriter{writer: w} + pw.WriteString(columnChunk.Type().String()) + pw.WriteString("\n--------------------------------------------------------------------------------\n") + + values := [42]Value{} + pages := columnChunk.Pages() + numPages, numValues := int64(0), int64(0) + + defer pages.Close() + for { + p, err := pages.ReadPage() + if err != nil { + if !errors.Is(err, io.EOF) { + return err + } + break + } + + numPages++ + n := p.NumValues() + if n == 0 { + fmt.Fprintf(pw, "*** page %d, no values ***\n", numPages) + } else { + fmt.Fprintf(pw, "*** page %d, values %d to %d ***\n", numPages, numValues+1, numValues+n) + printPage(w, p, values[:], numValues+1) + numValues += n + } + + pw.WriteString("\n") + } + + return pw.err +} + +func PrintPage(w io.Writer, page Page) error { + return printPage(w, page, make([]Value, 42), 0) +} + +func printPage(w io.Writer, page Page, values []Value, numValues int64) error { + r := page.Values() + for { + n, err := r.ReadValues(values[:]) + for i, v := range values[:n] { + _, err := fmt.Fprintf(w, "value %d: %+v\n", numValues+int64(i), v) + if err != nil { + return err + } + } + if err != nil { + if errors.Is(err, io.EOF) { + err = nil + } + return err + } + } +} diff --git a/vendor/github.com/parquet-go/parquet-go/reader.go b/vendor/github.com/parquet-go/parquet-go/reader.go new file mode 100644 index 000000000..29c020892 --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/reader.go @@ -0,0 +1,714 @@ +package parquet + +import ( + "errors" + "fmt" + "io" + "reflect" + + "github.com/parquet-go/parquet-go/format" +) + +// GenericReader is similar to a Reader but uses a type parameter to define the +// Go type representing the schema of rows being read. +// +// See GenericWriter for details about the benefits over the classic Reader API. +type GenericReader[T any] struct { + base Reader + read readFunc[T] +} + +// NewGenericReader is like NewReader but returns GenericReader[T] suited to write +// rows of Go type T. +// +// The type parameter T should be a map, struct, or any. Any other types will +// cause a panic at runtime. Type checking is a lot more effective when the +// generic parameter is a struct type, using map and interface types is somewhat +// similar to using a Writer. +// +// If the option list may explicitly declare a schema, it must be compatible +// with the schema generated from T. +func NewGenericReader[T any](input io.ReaderAt, options ...ReaderOption) *GenericReader[T] { + c, err := NewReaderConfig(options...) + if err != nil { + panic(err) + } + + f, err := openFile(input) + if err != nil { + panic(err) + } + + rowGroup := fileRowGroupOf(f) + + t := typeOf[T]() + if c.Schema == nil { + if t == nil { + c.Schema = rowGroup.Schema() + } else { + c.Schema = schemaOf(dereference(t), c.SchemaConfig.StructTags...) + } + } + + r := &GenericReader[T]{ + base: Reader{ + file: reader{ + file: f, + schema: c.Schema, + rowGroup: rowGroup, + }, + }, + } + + if !EqualNodes(c.Schema, f.schema) { + r.base.file.rowGroup = convertRowGroupTo(r.base.file.rowGroup, c.Schema) + } + + r.base.read.init(r.base.file.schema, r.base.file.rowGroup) + r.read = readFuncOf[T](t, r.base.file.schema) + return r +} + +func NewGenericRowGroupReader[T any](rowGroup RowGroup, options ...ReaderOption) *GenericReader[T] { + c, err := NewReaderConfig(options...) + if err != nil { + panic(err) + } + + t := typeOf[T]() + if c.Schema == nil { + if t == nil { + c.Schema = rowGroup.Schema() + } else { + c.Schema = schemaOf(dereference(t), c.SchemaConfig.StructTags...) + } + } + + r := &GenericReader[T]{ + base: Reader{ + file: reader{ + schema: c.Schema, + rowGroup: rowGroup, + }, + }, + } + + if !EqualNodes(c.Schema, rowGroup.Schema()) { + r.base.file.rowGroup = convertRowGroupTo(r.base.file.rowGroup, c.Schema) + } + + r.base.read.init(r.base.file.schema, r.base.file.rowGroup) + r.read = readFuncOf[T](t, r.base.file.schema) + return r +} + +func (r *GenericReader[T]) Reset() { + r.base.Reset() +} + +// Read reads the next rows from the reader into the given rows slice up to len(rows). +// +// The returned values are safe to reuse across Read calls and do not share +// memory with the reader's underlying page buffers. +// +// The method returns the number of rows read and io.EOF when no more rows +// can be read from the reader. +func (r *GenericReader[T]) Read(rows []T) (int, error) { + return r.read(r, rows) +} + +func (r *GenericReader[T]) ReadRows(rows []Row) (int, error) { + return r.base.ReadRows(rows) +} + +func (r *GenericReader[T]) Schema() *Schema { + return r.base.Schema() +} + +func (r *GenericReader[T]) NumRows() int64 { + return r.base.NumRows() +} + +func (r *GenericReader[T]) SeekToRow(rowIndex int64) error { + return r.base.SeekToRow(rowIndex) +} + +func (r *GenericReader[T]) Close() error { + return r.base.Close() +} + +// File returns a FileView of the underlying parquet file. +func (r *GenericReader[T]) File() FileView { + return r.base.File() +} + +// readRows reads the next rows from the reader into the given rows slice up to len(rows). +// +// The returned values are safe to reuse across readRows calls and do not share +// memory with the reader's underlying page buffers. +// +// The method returns the number of rows read and io.EOF when no more rows +// can be read from the reader. +func (r *GenericReader[T]) readRows(rows []T) (int, error) { + nRequest := len(rows) + if cap(r.base.rowbuf) < nRequest { + r.base.rowbuf = make([]Row, nRequest) + } else { + r.base.rowbuf = r.base.rowbuf[:nRequest] + } + + var n, nTotal int + var err error + for { + // ReadRows reads the minimum remaining rows in a column page across all columns + // of the underlying reader, unless the length of the slice passed to it is smaller. + // In that case, ReadRows will read the number of rows equal to the length of the + // given slice argument. We limit that length to never be more than requested + // because sequential reads can cross page boundaries. + n, err = r.base.ReadRows(r.base.rowbuf[:nRequest-nTotal]) + if n > 0 { + schema := r.base.Schema() + + for i, row := range r.base.rowbuf[:n] { + if err2 := schema.Reconstruct(&rows[nTotal+i], row); err2 != nil { + return nTotal + i, err2 + } + } + } + nTotal += n + if n == 0 || nTotal == nRequest || err != nil { + break + } + } + + return nTotal, err +} + +var ( + _ Rows = (*GenericReader[any])(nil) + _ RowReaderWithSchema = (*Reader)(nil) + + _ Rows = (*GenericReader[struct{}])(nil) + _ RowReaderWithSchema = (*GenericReader[struct{}])(nil) + + _ Rows = (*GenericReader[map[struct{}]struct{}])(nil) + _ RowReaderWithSchema = (*GenericReader[map[struct{}]struct{}])(nil) +) + +type readFunc[T any] func(*GenericReader[T], []T) (int, error) + +func readFuncOf[T any](t reflect.Type, schema *Schema) readFunc[T] { + if t == nil { + return (*GenericReader[T]).readRows + } + switch t.Kind() { + case reflect.Interface, reflect.Map: + return (*GenericReader[T]).readRows + + case reflect.Struct: + return (*GenericReader[T]).readRows + + case reflect.Pointer: + if e := t.Elem(); e.Kind() == reflect.Struct { + return (*GenericReader[T]).readRows + } + } + panic("cannot create reader for values of type " + t.String()) +} + +// Deprecated: A Reader reads Go values from parquet files. +// +// This example showcases a typical use of parquet readers: +// +// reader := parquet.NewReader(file) +// rows := []RowType{} +// for { +// row := RowType{} +// err := reader.Read(&row) +// if err != nil { +// if err == io.EOF { +// break +// } +// ... +// } +// rows = append(rows, row) +// } +// if err := reader.Close(); err != nil { +// ... +// } +// +// For programs building with Go 1.18 or later, the GenericReader[T] type +// supersedes this one. +type Reader struct { + seen reflect.Type + file reader + read reader + rowIndex int64 + rowbuf []Row +} + +// NewReader constructs a parquet reader reading rows from the given +// io.ReaderAt. +// +// In order to read parquet rows, the io.ReaderAt must be converted to a +// parquet.File. If r is already a parquet.File it is used directly; otherwise, +// the io.ReaderAt value is expected to either have a `Size() int64` method or +// implement io.Seeker in order to determine its size. +// +// The function panics if the reader configuration is invalid. Programs that +// cannot guarantee the validity of the options passed to NewReader should +// construct the reader configuration independently prior to calling this +// function: +// +// config, err := parquet.NewReaderConfig(options...) +// if err != nil { +// // handle the configuration error +// ... +// } else { +// // this call to create a reader is guaranteed not to panic +// reader := parquet.NewReader(input, config) +// ... +// } +func NewReader(input io.ReaderAt, options ...ReaderOption) *Reader { + c, err := NewReaderConfig(options...) + if err != nil { + panic(err) + } + + f, err := openFile(input) + if err != nil { + panic(err) + } + + r := &Reader{ + file: reader{ + file: f, + schema: f.schema, + rowGroup: fileRowGroupOf(f), + }, + } + + if c.Schema != nil { + r.file.schema = c.Schema + r.file.rowGroup = convertRowGroupTo(r.file.rowGroup, c.Schema) + } + + r.read.init(r.file.schema, r.file.rowGroup) + return r +} + +func openFile(input io.ReaderAt) (*File, error) { + f, _ := input.(*File) + if f != nil { + return f, nil + } + n, err := sizeOf(input) + if err != nil { + return nil, err + } + return OpenFile(input, n) +} + +func fileRowGroupOf(f *File) RowGroup { + switch rowGroups := f.RowGroups(); len(rowGroups) { + case 0: + return newEmptyRowGroup(f.Schema()) + case 1: + return rowGroups[0] + default: + // TODO: should we attempt to merge the row groups via MergeRowGroups + // to preserve the global order of sorting columns within the file? + return MultiRowGroup(rowGroups...) + } +} + +// NewRowGroupReader constructs a new Reader which reads rows from the RowGroup +// passed as argument. +func NewRowGroupReader(rowGroup RowGroup, options ...ReaderOption) *Reader { + c, err := NewReaderConfig(options...) + if err != nil { + panic(err) + } + + if c.Schema != nil { + rowGroup = convertRowGroupTo(rowGroup, c.Schema) + } + + r := &Reader{ + file: reader{ + schema: rowGroup.Schema(), + rowGroup: rowGroup, + }, + } + + r.read.init(r.file.schema, r.file.rowGroup) + return r +} + +func convertRowGroupTo(rowGroup RowGroup, schema *Schema) RowGroup { + if rowGroupSchema := rowGroup.Schema(); !EqualNodes(schema, rowGroupSchema) { + conv, err := Convert(schema, rowGroupSchema) + if err != nil { + // TODO: this looks like something we should not be panicking on, + // but the current NewReader API does not offer a mechanism to + // report errors. + panic(err) + } + rowGroup = ConvertRowGroup(rowGroup, conv) + } + return rowGroup +} + +func sizeOf(r io.ReaderAt) (int64, error) { + switch f := r.(type) { + case interface{ Size() int64 }: + return f.Size(), nil + case io.Seeker: + off, err := f.Seek(0, io.SeekCurrent) + if err != nil { + return 0, err + } + end, err := f.Seek(0, io.SeekEnd) + if err != nil { + return 0, err + } + _, err = f.Seek(off, io.SeekStart) + return end, err + default: + return 0, fmt.Errorf("cannot determine length of %T", r) + } +} + +// Reset repositions the reader at the beginning of the underlying parquet file. +func (r *Reader) Reset() { + r.file.Reset() + r.read.Reset() + r.rowIndex = 0 + clearRows(r.rowbuf) +} + +// Read reads the next row from r. The type of the row must match the schema +// of the underlying parquet file or an error will be returned. +// +// The method returns io.EOF when no more rows can be read from r. +func (r *Reader) Read(row any) error { + if rowType := dereference(reflect.TypeOf(row)); rowType.Kind() == reflect.Struct { + if r.seen != rowType { + if err := r.updateReadSchema(rowType); err != nil { + return fmt.Errorf("cannot read parquet row into go value of type %T: %w", row, err) + } + } + } + + if err := r.read.SeekToRow(r.rowIndex); err != nil { + if errors.Is(err, io.ErrClosedPipe) { + return io.EOF + } + return fmt.Errorf("seeking reader to row %d: %w", r.rowIndex, err) + } + + if cap(r.rowbuf) == 0 { + r.rowbuf = make([]Row, 1) + } else { + r.rowbuf = r.rowbuf[:1] + } + + n, err := r.read.ReadRows(r.rowbuf[:]) + if n == 0 { + return err + } + + r.rowIndex++ + return r.read.schema.Reconstruct(row, r.rowbuf[0]) +} + +func (r *Reader) updateReadSchema(rowType reflect.Type) error { + schema := schemaOf(rowType) + + if EqualNodes(schema, r.file.schema) { + r.read.init(schema, r.file.rowGroup) + } else { + conv, err := Convert(schema, r.file.schema) + if err != nil { + return err + } + r.read.init(schema, ConvertRowGroup(r.file.rowGroup, conv)) + } + + r.seen = rowType + return nil +} + +// ReadRows reads the next rows from r into the given Row buffer. +// +// The returned values are laid out in the order expected by the +// parquet.(*Schema).Reconstruct method. +// +// The method returns io.EOF when no more rows can be read from r. +func (r *Reader) ReadRows(rows []Row) (int, error) { + if err := r.file.SeekToRow(r.rowIndex); err != nil { + return 0, err + } + n, err := r.file.ReadRows(rows) + r.rowIndex += int64(n) + return n, err +} + +// Schema returns the schema of rows read by r. +func (r *Reader) Schema() *Schema { return r.file.schema } + +// NumRows returns the number of rows that can be read from r. +func (r *Reader) NumRows() int64 { return r.file.rowGroup.NumRows() } + +// SeekToRow positions r at the given row index. +func (r *Reader) SeekToRow(rowIndex int64) error { + if err := r.file.SeekToRow(rowIndex); err != nil { + return err + } + r.rowIndex = rowIndex + return nil +} + +// Close closes the reader, preventing more rows from being read. +func (r *Reader) Close() error { + if err := r.read.Close(); err != nil { + return err + } + if err := r.file.Close(); err != nil { + return err + } + return nil +} + +// reader is a subtype used in the implementation of Reader to support the two +// use cases of either reading rows calling the ReadRow method (where full rows +// are read from the underlying parquet file), or calling the Read method to +// read rows into Go values, potentially doing partial reads on a subset of the +// columns due to using a converted row group view. +type reader struct { + file *File + schema *Schema + rowGroup RowGroup + rows Rows + rowIndex int64 +} + +func (r *reader) init(schema *Schema, rowGroup RowGroup) { + r.schema = schema + r.rowGroup = rowGroup + r.Reset() +} + +func (r *reader) Reset() { + r.rowIndex = 0 + + if rows, ok := r.rows.(interface{ Reset() }); ok { + // This optimization works for the common case where the underlying type + // of the Rows instance is rowGroupRows, which should be true in most + // cases since even external implementations of the RowGroup interface + // can construct values of this type via the NewRowGroupRowReader + // function. + // + // Foreign implementations of the Rows interface may also define a Reset + // method in order to participate in this optimization. + rows.Reset() + return + } + + if r.rows != nil { + r.rows.Close() + r.rows = nil + } +} + +func (r *reader) ReadRows(rows []Row) (int, error) { + if r.rowGroup == nil { + return 0, io.EOF + } + if r.rows == nil { + r.rows = r.rowGroup.Rows() + if r.rowIndex > 0 { + if err := r.rows.SeekToRow(r.rowIndex); err != nil { + return 0, err + } + } + } + n, err := r.rows.ReadRows(rows) + r.rowIndex += int64(n) + return n, err +} + +func (r *reader) SeekToRow(rowIndex int64) error { + if r.rowGroup == nil { + return io.ErrClosedPipe + } + if rowIndex != r.rowIndex { + if r.rows != nil { + if err := r.rows.SeekToRow(rowIndex); err != nil { + return err + } + } + r.rowIndex = rowIndex + } + return nil +} + +func (r *reader) Close() (err error) { + r.rowGroup = nil + if r.rows != nil { + err = r.rows.Close() + } + return err +} + +var ( + _ Rows = (*Reader)(nil) + _ RowReaderWithSchema = (*Reader)(nil) + + _ RowReader = (*reader)(nil) + _ RowSeeker = (*reader)(nil) +) + +type readerFileView struct { + reader *reader + schema *Schema +} + +// File returns a FileView of the parquet file being read. +// Only available if Reader was created with a File. +func (r *Reader) File() FileView { + if r.file.schema == nil || r.file.file == nil { + return nil + } + return &readerFileView{ + &r.file, + r.file.schema, + } +} + +func (r *readerFileView) Metadata() *format.FileMetaData { + if r.reader.file != nil { + return r.reader.file.Metadata() + } + return nil +} + +func (r *readerFileView) Schema() *Schema { + return r.schema +} + +func (r *readerFileView) NumRows() int64 { + return r.reader.rowGroup.NumRows() +} + +func (r *readerFileView) Lookup(key string) (string, bool) { + if meta := r.Metadata(); meta != nil { + return lookupKeyValueMetadata(meta.KeyValueMetadata, key) + } + return "", false +} + +func (r *readerFileView) Size() int64 { + if r.reader.file != nil { + return r.reader.file.Size() + } + return 0 +} + +func (r *readerFileView) ColumnIndexes() []format.ColumnIndex { + if r.reader.file != nil { + return r.reader.file.ColumnIndexes() + } + return nil +} + +func (r *readerFileView) OffsetIndexes() []format.OffsetIndex { + if r.reader.file != nil { + return r.reader.file.OffsetIndexes() + } + return nil +} + +func (r *readerFileView) Root() *Column { + if meta := r.Metadata(); meta != nil { + root, _ := openColumns(nil, meta, r.ColumnIndexes(), r.OffsetIndexes()) + return root + } + return nil +} + +func (r *readerFileView) RowGroups() []RowGroup { + file := r.reader.file + if file == nil { + return nil + } + columns := makeLeafColumns(r.Root()) + fileRowGroups, err := makeFileRowGroups(file, columns) + if err != nil { + return nil + } + return makeRowGroups(fileRowGroups) +} + +func makeLeafColumns(root *Column) []*Column { + columns := make([]*Column, 0, numLeafColumnsOf(root)) + root.forEachLeaf(func(c *Column) { columns = append(columns, c) }) + return columns +} + +func makeFileRowGroups(file *File, columns []*Column) ([]FileRowGroup, error) { + rowGroups := file.metadata.RowGroups + + if err := validateRowGroupOrdinals(rowGroups); err != nil { + return nil, err + } + + fileRowGroups := make([]FileRowGroup, len(rowGroups)) + for i := range fileRowGroups { + if err := fileRowGroups[i].init(file, columns, &rowGroups[i]); err != nil { + return nil, err + } + } + return fileRowGroups, nil +} + +func makeRowGroups(fileRowGroups []FileRowGroup) []RowGroup { + rowGroups := make([]RowGroup, len(fileRowGroups)) + for i := range fileRowGroups { + rowGroups[i] = &fileRowGroups[i] + } + return rowGroups +} + +func validateRowGroupOrdinals(rowGroups []format.RowGroup) error { + allZero := true + for _, rg := range rowGroups { + if rg.Ordinal != 0 { + allZero = false + break + } + } + + // If all ordinals are zero, the writer omitted the optional Ordinal field + // (or there is a single row group at index 0). Back-fill sequential values + // so callers — including AAD construction for encryption — can rely on + // rg.Ordinal == slice index. + if allZero { + for i := range rowGroups { + rowGroups[i].Ordinal = int16(i) + } + return nil + } + + // Otherwise the file embeds explicit ordinals; require them to match the + // slice index. The Parquet spec defines RowGroup.Ordinal as the row group + // position, and downstream code (page-index lookup, AAD) assumes that + // equality. Files with non-sequential or out-of-order ordinals would + // silently produce wrong AADs or out-of-range index lookups. + for i, rg := range rowGroups { + if int(rg.Ordinal) != i { + return fmt.Errorf("row group ordinal %d at index %d does not match its position", rg.Ordinal, i) + } + } + return nil +} diff --git a/vendor/github.com/parquet-go/parquet-go/row.go b/vendor/github.com/parquet-go/parquet-go/row.go new file mode 100644 index 000000000..190480832 --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/row.go @@ -0,0 +1,935 @@ +package parquet + +import ( + "errors" + "fmt" + "io" + "reflect" +) + +const ( + defaultRowBufferSize = 42 +) + +// Row represents a parquet row as a slice of values. +// +// Each value should embed a column index, repetition level, and definition +// level allowing the program to determine how to reconstruct the original +// object from the row. +type Row []Value + +// MakeRow constructs a Row from a list of column values. +// +// The function panics if the column indexes of values in each column do not +// match their position in the argument list. +func MakeRow(columns ...[]Value) Row { return AppendRow(nil, columns...) } + +// AppendRow appends to row the given list of column values. +// +// AppendRow can be used to construct a Row value from columns, while retaining +// the underlying memory buffer to avoid reallocation; for example: +// +// The function panics if the column indexes of values in each column do not +// match their position in the argument list. +func AppendRow(row Row, columns ...[]Value) Row { + numValues := 0 + + for expectedColumnIndex, column := range columns { + numValues += len(column) + + for _, value := range column { + if value.columnIndex != ^uint16(expectedColumnIndex) { + panic(fmt.Sprintf("value of column %d has column index %d", expectedColumnIndex, value.Column())) + } + } + } + + if capacity := cap(row) - len(row); capacity < numValues { + row = append(make(Row, 0, len(row)+numValues), row...) + } + + return appendRow(row, columns) +} + +func appendRow(row Row, columns [][]Value) Row { + for _, column := range columns { + row = append(row, column...) + } + return row +} + +// Clone creates a copy of the row which shares no pointers. +// +// This method is useful to capture rows after a call to RowReader.ReadRows when +// values need to be retained before the next call to ReadRows or after the lifespan +// of the reader. +func (row Row) Clone() Row { + clone := make(Row, len(row)) + for i := range row { + clone[i] = row[i].Clone() + } + return clone +} + +// Equal returns true if row and other contain the same sequence of values. +func (row Row) Equal(other Row) bool { + if len(row) != len(other) { + return false + } + for i := range row { + if !Equal(row[i], other[i]) { + return false + } + if row[i].repetitionLevel != other[i].repetitionLevel { + return false + } + if row[i].definitionLevel != other[i].definitionLevel { + return false + } + if row[i].columnIndex != other[i].columnIndex { + return false + } + } + return true +} + +// Range calls f for each column of row. +func (row Row) Range(f func(columnIndex int, columnValues []Value) bool) { + columnIndex := 0 + + for i := 0; i < len(row); { + j := i + 1 + + for j < len(row) && row[j].columnIndex == ^uint16(columnIndex) { + j++ + } + + if !f(columnIndex, row[i:j:j]) { + break + } + + columnIndex++ + i = j + } +} + +// RowSeeker is an interface implemented by readers of parquet rows which can be +// positioned at a specific row index. +type RowSeeker interface { + // Positions the stream on the given row index. + // + // Some implementations of the interface may only allow seeking forward. + // + // The method returns io.ErrClosedPipe if the stream had already been closed. + SeekToRow(int64) error +} + +// RowReader reads a sequence of parquet rows. +type RowReader interface { + // ReadRows reads rows from the reader, returning the number of rows read + // into the buffer, and any error that occurred. + // + // When all rows have been read, the reader returns io.EOF to indicate the + // end of the sequence. It is valid for the reader to return both a non-zero + // number of rows and a non-nil error (including io.EOF). + // + // The buffer of rows passed as argument will be used to store values of + // each row read from the reader. If the rows are not nil, the backing array + // of the slices will be used as an optimization to avoid re-allocating new + // arrays. + // + // The application is expected to handle the case where ReadRows returns + // less rows than requested and no error, by looking at the first returned + // value from ReadRows, which is the number of rows that were read. + ReadRows([]Row) (int, error) +} + +// RowReaderFrom reads parquet rows from reader. +type RowReaderFrom interface { + ReadRowsFrom(RowReader) (int64, error) +} + +// RowReaderWithSchema is an extension of the RowReader interface which +// advertises the schema of rows returned by ReadRow calls. +type RowReaderWithSchema interface { + RowReader + Schema() *Schema +} + +// RowReadSeeker is an interface implemented by row readers which support +// seeking to arbitrary row positions. +type RowReadSeeker interface { + RowReader + RowSeeker +} + +// RowReadCloser is an interface implemented by row readers which require +// closing when done. +type RowReadCloser interface { + RowReader + io.Closer +} + +// RowReadSeekCloser is an interface implemented by row readers which support +// seeking to arbitrary row positions and required closing the reader when done. +type RowReadSeekCloser interface { + RowReader + RowSeeker + io.Closer +} + +// RowWriter writes parquet rows to an underlying medium. +type RowWriter interface { + // Writes rows to the writer, returning the number of rows written and any + // error that occurred. + // + // Because columnar operations operate on independent columns of values, + // writes of rows may not be atomic operations, and could result in some + // rows being partially written. The method returns the number of rows that + // were successfully written, but if an error occurs, values of the row(s) + // that failed to be written may have been partially committed to their + // columns. For that reason, applications should consider a write error as + // fatal and assume that they need to discard the state, they cannot retry + // the write nor recover the underlying file. + WriteRows([]Row) (int, error) +} + +// RowWriterTo writes parquet rows to a writer. +type RowWriterTo interface { + WriteRowsTo(RowWriter) (int64, error) +} + +// RowWriterWithSchema is an extension of the RowWriter interface which +// advertises the schema of rows expected to be passed to WriteRow calls. +type RowWriterWithSchema interface { + RowWriter + Schema() *Schema +} + +// RowReaderFunc is a function type implementing the RowReader interface. +type RowReaderFunc func([]Row) (int, error) + +func (f RowReaderFunc) ReadRows(rows []Row) (int, error) { return f(rows) } + +// RowWriterFunc is a function type implementing the RowWriter interface. +type RowWriterFunc func([]Row) (int, error) + +func (f RowWriterFunc) WriteRows(rows []Row) (int, error) { return f(rows) } + +// MultiRowWriter constructs a RowWriter which dispatches writes to all the +// writers passed as arguments. +// +// When writing rows, if any of the writers returns an error, the operation is +// aborted and the error returned. If one of the writers did not error, but did +// not write all the rows, the operation is aborted and io.ErrShortWrite is +// returned. +// +// Rows are written sequentially to each writer in the order they are given to +// this function. +func MultiRowWriter(writers ...RowWriter) RowWriter { + m := &multiRowWriter{writers: make([]RowWriter, len(writers))} + copy(m.writers, writers) + return m +} + +type multiRowWriter struct{ writers []RowWriter } + +func (m *multiRowWriter) WriteRows(rows []Row) (int, error) { + for _, w := range m.writers { + n, err := w.WriteRows(rows) + if err != nil { + return n, err + } + if n != len(rows) { + return n, io.ErrShortWrite + } + } + return len(rows), nil +} + +type forwardRowSeeker struct { + rows RowReader + seek int64 + index int64 +} + +func (r *forwardRowSeeker) ReadRows(rows []Row) (int, error) { + for { + n, err := r.rows.ReadRows(rows) + + if n > 0 && r.index < r.seek { + skip := r.seek - r.index + r.index += int64(n) + if skip >= int64(n) { + continue + } + + for i, j := 0, int(skip); j < n; i++ { + rows[i] = append(rows[i][:0], rows[j]...) + } + + n -= int(skip) + } + + return n, err + } +} + +func (r *forwardRowSeeker) SeekToRow(rowIndex int64) error { + if rowIndex >= r.index { + r.seek = rowIndex + return nil + } + return fmt.Errorf( + "SeekToRow: %T does not implement parquet.RowSeeker: cannot seek backward from row %d to %d", + r.rows, + r.index, + rowIndex, + ) +} + +// CopyRows copies rows from src to dst. +// +// The underlying types of src and dst are tested to determine if they expose +// information about the schema of rows that are read and expected to be +// written. If the schema information are available but do not match, the +// function will attempt to automatically convert the rows from the source +// schema to the destination. +// +// As an optimization, the src argument may implement RowWriterTo to bypass +// the default row copy logic and provide its own. The dst argument may also +// implement RowReaderFrom for the same purpose. +// +// The function returns the number of rows written, or any error encountered +// other than io.EOF. +func CopyRows(dst RowWriter, src RowReader) (int64, error) { + return copyRows(dst, src, nil) +} + +func copyRows(dst RowWriter, src RowReader, buf []Row) (written int64, err error) { + targetSchema := targetSchemaOf(dst) + sourceSchema := sourceSchemaOf(src) + + if targetSchema != nil && sourceSchema != nil { + if !EqualNodes(targetSchema, sourceSchema) { + conv, err := Convert(targetSchema, sourceSchema) + if err != nil { + return 0, err + } + // The conversion effectively disables a potential optimization + // if the source reader implemented RowWriterTo. It is a trade off + // we are making to optimize for safety rather than performance. + // + // Entering this code path should not be the common case tho, it is + // most often used when parquet schemas are evolving, but we expect + // that the majority of files of an application to be sharing a + // common schema. + src = ConvertRowReader(src, conv) + } + } + + if wt, ok := src.(RowWriterTo); ok { + return wt.WriteRowsTo(dst) + } + + if rf, ok := dst.(RowReaderFrom); ok { + return rf.ReadRowsFrom(src) + } + + if len(buf) == 0 { + buf = make([]Row, defaultRowBufferSize) + } + + defer clearRows(buf) + + for { + rn, err := src.ReadRows(buf) + + if rn > 0 { + wn, err := dst.WriteRows(buf[:rn]) + if err != nil { + return written, err + } + + written += int64(wn) + } + + if err != nil { + if errors.Is(err, io.EOF) { + err = nil + } + return written, err + } + + if rn == 0 { + return written, io.ErrNoProgress + } + } +} + +func makeRows(n int) []Row { + buf := make([]Value, n) + row := make([]Row, n) + for i := range row { + row[i] = buf[i : i : i+1] + } + return row +} + +func clearRows(rows []Row) { + for i, values := range rows { + clearValues(values) + rows[i] = values[:0] + } +} + +func sourceSchemaOf(r RowReader) *Schema { + if rrs, ok := r.(RowReaderWithSchema); ok { + return rrs.Schema() + } + return nil +} + +func targetSchemaOf(w RowWriter) *Schema { + if rws, ok := w.(RowWriterWithSchema); ok { + return rws.Schema() + } + return nil +} + +// ============================================================================= +// Functions returning closures are marked with "go:noinline" below to prevent +// losing naming information of the closure in stack traces. +// +// Because some of the functions are very short (simply return a closure), the +// compiler inlines when at their call site, which result in the closure being +// named something like parquet.deconstructFuncOf.func2 instead of the original +// parquet.deconstructFuncOfLeaf.func1; the latter being much more meaningful +// when reading CPU or memory profiles. +// ============================================================================= + +// deconstructFunc accepts a row, the current levels, the value to deserialize +// the current column onto, and returns the row minus the deserialied value(s) +// It recurses until it hits a leaf node, then deserializes that value +// individually as the base case. +type deconstructFunc func([][]Value, columnLevels, reflect.Value) + +func deconstructFuncOf(columnIndex uint16, node Node) (uint16, deconstructFunc) { + switch { + case node.Optional(): + return deconstructFuncOfOptional(columnIndex, node) + case node.Repeated(): + return deconstructFuncOfRepeated(columnIndex, node) + case isList(node): + return deconstructFuncOfList(columnIndex, node) + case isMap(node): + return deconstructFuncOfMap(columnIndex, node) + case isVariant(node): + return deconstructFuncOfVariant(columnIndex, node) + default: + return deconstructFuncOfRequired(columnIndex, node) + } +} + +//go:noinline +func deconstructFuncOfOptional(columnIndex uint16, node Node) (uint16, deconstructFunc) { + columnIndex, deconstruct := deconstructFuncOf(columnIndex, Required(node)) + return columnIndex, func(columns [][]Value, levels columnLevels, value reflect.Value) { + if value.IsValid() { + if isNullValue(value) { + value = reflect.Value{} + } else { + if value.Kind() == reflect.Ptr { + value = value.Elem() + } + levels.definitionLevel++ + } + } + deconstruct(columns, levels, value) + } +} + +//go:noinline +func deconstructFuncOfRepeated(columnIndex uint16, node Node) (uint16, deconstructFunc) { + columnIndex, deconstruct := deconstructFuncOf(columnIndex, Required(node)) + return columnIndex, func(columns [][]Value, levels columnLevels, value reflect.Value) { + if value.Kind() == reflect.Interface { + value = value.Elem() + } + + if !value.IsValid() || value.Len() == 0 { + deconstruct(columns, levels, reflect.Value{}) + return + } + + levels.repetitionDepth++ + levels.definitionLevel++ + + for i, n := 0, value.Len(); i < n; i++ { + deconstruct(columns, levels, value.Index(i)) + levels.repetitionLevel = levels.repetitionDepth + } + } +} + +func deconstructFuncOfRequired(columnIndex uint16, node Node) (uint16, deconstructFunc) { + switch { + case node.Leaf(): + return deconstructFuncOfLeaf(columnIndex, node) + default: + return deconstructFuncOfGroup(columnIndex, node) + } +} + +func deconstructFuncOfList(columnIndex uint16, node Node) (uint16, deconstructFunc) { + return deconstructFuncOf(columnIndex, Repeated(listElementOf(node))) +} + +// makeKeyValueType returns a key-value struct type for use with actual map +// key/value types. If the schema's synthetic value type is already convertible +// to actualVal, keyValueElem is returned unchanged. +func makeKeyValueType(keyValueElem reflect.Type, actualKey, actualVal reflect.Type) reflect.Type { + schemaVal := keyValueElem.Field(1).Type + if actualVal == schemaVal || actualVal.ConvertibleTo(schemaVal) { + return keyValueElem + } + return reflect.StructOf([]reflect.StructField{ + {Name: keyValueElem.Field(0).Name, Type: actualKey, Tag: keyValueElem.Field(0).Tag}, + {Name: keyValueElem.Field(1).Name, Type: actualVal, Tag: keyValueElem.Field(1).Tag}, + }) +} + +//go:noinline +func deconstructFuncOfMap(columnIndex uint16, node Node) (uint16, deconstructFunc) { + keyValue := mapKeyValueOf(node) + keyValueType := keyValue.GoType() + keyValueElem := keyValueType.Elem() + keyType := keyValueElem.Field(0).Type + valueType := keyValueElem.Field(1).Type + nextColumnIndex, deconstruct := deconstructFuncOf(columnIndex, schemaOf(keyValueElem)) + return nextColumnIndex, func(columns [][]Value, levels columnLevels, mapValue reflect.Value) { + if !mapValue.IsValid() || mapValue.Len() == 0 { + deconstruct(columns, levels, reflect.Value{}) + return + } + + levels.repetitionDepth++ + levels.definitionLevel++ + + elem := reflect.New(makeKeyValueType(keyValueElem, mapValue.Type().Key(), mapValue.Type().Elem())).Elem() + k := elem.Field(0) + v := elem.Field(1) + + for _, key := range mapValue.MapKeys() { + k.Set(key.Convert(keyType)) + v.Set(mapValue.MapIndex(key).Convert(valueType)) + deconstruct(columns, levels, elem) + levels.repetitionLevel = levels.repetitionDepth + } + } +} + +//go:noinline +func deconstructFuncOfGroup(columnIndex uint16, node Node) (uint16, deconstructFunc) { + fields := node.Fields() + funcs := make([]deconstructFunc, len(fields)) + for i, field := range fields { + columnIndex, funcs[i] = deconstructFuncOf(columnIndex, field) + } + return columnIndex, func(columns [][]Value, levels columnLevels, value reflect.Value) { + if value.IsValid() { + for i, f := range funcs { + f(columns, levels, fields[i].Value(value)) + } + } else { + for _, f := range funcs { + f(columns, levels, value) + } + } + } +} + +//go:noinline +func deconstructFuncOfLeaf(columnIndex uint16, node Node) (uint16, deconstructFunc) { + if columnIndex > MaxColumnIndex { + panic("row cannot be deconstructed because it has too many columns") + } + typ := node.Type() + kind := typ.Kind() + lt := typ.LogicalType() + valueColumnIndex := ^columnIndex + return columnIndex + 1, func(columns [][]Value, levels columnLevels, value reflect.Value) { + v := Value{} + + if value.IsValid() { + v = makeValue(kind, lt, value) + } + + v.repetitionLevel = levels.repetitionLevel + v.definitionLevel = levels.definitionLevel + v.columnIndex = valueColumnIndex + + columns[columnIndex] = append(columns[columnIndex], v) + } +} + +// "reconstructX" turns a Go value into a Go representation of a Parquet series +// of values + +type reconstructFunc func(reflect.Value, columnLevels, [][]Value) error + +func reconstructFuncOf(columnIndex uint16, node Node) (uint16, reconstructFunc) { + switch { + case node.Optional(): + return reconstructFuncOfOptional(columnIndex, node) + case node.Repeated(): + return reconstructFuncOfRepeated(columnIndex, Required(node)) + case isList(node): + return reconstructFuncOfList(columnIndex, node) + case isMap(node): + return reconstructFuncOfMap(columnIndex, node) + case isVariant(node): + return reconstructFuncOfVariant(columnIndex, node) + default: + return reconstructFuncOfRequired(columnIndex, node) + } +} + +//go:noinline +func reconstructFuncOfOptional(columnIndex uint16, node Node) (uint16, reconstructFunc) { + // We convert the optional func to required so that we eventually reach the + // leaf base-case. We're still using the heuristics of optional in the + // returned closure (see levels.definitionLevel++), but we don't actually do + // deserialization here, that happens in the leaf function, hence this line. + nextColumnIndex, reconstruct := reconstructFuncOf(columnIndex, Required(node)) + + return nextColumnIndex, func(value reflect.Value, levels columnLevels, columns [][]Value) error { + levels.definitionLevel++ + + // For empty groups (no columns), we can't check definition levels. + // Treat them as always present (non-null). + if len(columns) > 0 && len(columns[0]) > 0 { + if columns[0][0].definitionLevel < levels.definitionLevel { + value.SetZero() + return nil + } + } + + if value.Kind() == reflect.Ptr { + // Always allocate a fresh pointer. Reusing the caller's + // previous-call allocation would let downstream AssignValue + // mutate bytes behind any pointer the caller has retained from + // an earlier Read (issue #522). + value.Set(reflect.New(value.Type().Elem())) + value = value.Elem() + } + + return reconstruct(value, levels, columns) + } +} + +func setMakeSlice(v reflect.Value, n int) reflect.Value { + t := v.Type() + if t.Kind() == reflect.Interface { + t = reflect.TypeOf(([]any)(nil)) + } + s := reflect.MakeSlice(t, n, n) + v.Set(s) + return s +} + +func setNullSlice(v reflect.Value) reflect.Value { + t := v.Type() + if t.Kind() == reflect.Interface { + t = reflect.TypeOf(([]any)(nil)) + } + s := reflect.Zero(t) + v.Set(s) + return s +} + +//go:noinline +func reconstructFuncOfRepeated(columnIndex uint16, node Node) (uint16, reconstructFunc) { + nextColumnIndex, reconstruct := reconstructFuncOf(columnIndex, node) + return nextColumnIndex, func(value reflect.Value, levels columnLevels, columns [][]Value) error { + levels.repetitionDepth++ + levels.definitionLevel++ + + // Handle empty groups (no columns) + if len(columns) == 0 || len(columns[0]) == 0 { + setMakeSlice(value, 0) + return nil + } + + if columns[0][0].definitionLevel < levels.definitionLevel { + setMakeSlice(value, 0) + return nil + } + + values := make([][]Value, len(columns)) + column := columns[0] + n := 0 + + for i, column := range columns { + values[i] = column[0:0:len(column)] + } + + for i := 0; i < len(column); { + i++ + n++ + + for i < len(column) && column[i].repetitionLevel > levels.repetitionDepth { + i++ + } + } + + value = setMakeSlice(value, n) + + for i := range n { + for j, column := range values { + column = column[:cap(column)] + if len(column) == 0 { + continue + } + + k := 1 + for k < len(column) && column[k].repetitionLevel > levels.repetitionDepth { + k++ + } + + values[j] = column[:k] + } + + if err := reconstruct(value.Index(i), levels, values); err != nil { + return err + } + + for j, column := range values { + values[j] = column[len(column):len(column):cap(column)] + } + + levels.repetitionLevel = levels.repetitionDepth + } + + return nil + } +} + +func reconstructFuncOfRequired(columnIndex uint16, node Node) (uint16, reconstructFunc) { + switch { + case node.Leaf(): + return reconstructFuncOfLeaf(columnIndex, node) + default: + return reconstructFuncOfGroup(columnIndex, node) + } +} + +func reconstructFuncOfList(columnIndex uint16, node Node) (uint16, reconstructFunc) { + elem := listElementOf(node) + // If the list element is optional (e.g., from `parquet-element:",optional"`), + // we need to handle it specially because the normal path through + // reconstructFuncOfRepeated would wrap the node with Required() which + // hides the Optional property. + if elem.Optional() { + return reconstructFuncOfRepeated(columnIndex, elem) + } + return reconstructFuncOf(columnIndex, Repeated(elem)) +} + +//go:noinline +func reconstructFuncOfMap(columnIndex uint16, node Node) (uint16, reconstructFunc) { + keyValue := mapKeyValueOf(node) + keyValueType := keyValue.GoType() + keyValueElem := keyValueType.Elem() + nextColumnIndex, reconstruct := reconstructFuncOf(columnIndex, schemaOf(keyValueElem)) + + // Check if the value field of the map is a LIST type + valueNode := fieldByName(keyValue, "value") + valueIsList := valueNode != nil && isList(valueNode) + + return nextColumnIndex, func(value reflect.Value, levels columnLevels, columns [][]Value) error { + levels.repetitionDepth++ + levels.definitionLevel++ + + if columns[0][0].definitionLevel < levels.definitionLevel { + valueType := value.Type() + if valueType.Kind() == reflect.Interface { + value.Set(reflect.ValueOf(map[string]any{})) + } else { + value.Set(reflect.MakeMap(valueType)) + } + return nil + } + + values := make([][]Value, len(columns)) + column := columns[0] + t := value.Type() + if t.Kind() == reflect.Interface { + t = reflect.TypeOf((map[string]any)(nil)) + } + k := t.Key() + v := t.Elem() + n := 0 + + for i, column := range columns { + values[i] = column[0:0:len(column)] + } + + for i := 0; i < len(column); { + i++ + n++ + + for i < len(column) && column[i].repetitionLevel > levels.repetitionDepth { + i++ + } + } + + if value.IsNil() { + m := reflect.MakeMapWithSize(t, n) + value.Set(m) + value = m // track map instead of any for read[any]() + } + + actualVal := v + if valueIsList || v.Kind() == reflect.Interface { + actualVal = keyValueElem.Field(1).Type + } + elem := reflect.New(makeKeyValueType(keyValueElem, k, actualVal)).Elem() + for range n { + for j, column := range values { + column = column[:cap(column)] + k := 1 + + for k < len(column) && column[k].repetitionLevel > levels.repetitionDepth { + k++ + } + + values[j] = column[:k] + } + + if err := reconstruct(elem, levels, values); err != nil { + return err + } + + for j, column := range values { + values[j] = column[len(column):len(column):cap(column)] + } + + mapKey := elem.Field(0).Convert(k) + mapValue := elem.Field(1) + + // If the value is a LIST type, we need to extract the elements from the + // wrapper struct. The wrapper struct has a "List" field containing + // []struct { Element T }, and we need to convert it to []T. + if valueIsList && v.Kind() == reflect.Slice { + mapValue = convertListWrapperToSlice(mapValue, v) + } else { + mapValue = mapValue.Convert(v) + } + + value.SetMapIndex(mapKey, mapValue) + elem.SetZero() + levels.repetitionLevel = levels.repetitionDepth + } + + return nil + } +} + +// convertListWrapperToSlice converts a LIST wrapper struct to a slice. +// The wrapper struct has the form: struct { List []struct { Element T } } +// and this function returns a reflect.Value of type []T containing the elements. +func convertListWrapperToSlice(wrapper reflect.Value, targetSliceType reflect.Type) reflect.Value { + // Get the "List" field from the wrapper struct + listField := wrapper.FieldByName("List") + if !listField.IsValid() { + // If there's no List field, try direct conversion as fallback + return wrapper.Convert(targetSliceType) + } + + // Create the target slice with the same length + n := listField.Len() + result := reflect.MakeSlice(targetSliceType, n, n) + elemType := targetSliceType.Elem() + + // Copy elements from the wrapper to the result slice + for i := range n { + listElem := listField.Index(i) + // Get the "Element" field from each list element struct + elementField := listElem.FieldByName("Element") + if elementField.IsValid() { + result.Index(i).Set(elementField.Convert(elemType)) + } + } + + return result +} + +//go:noinline +func reconstructFuncOfGroup(columnIndex uint16, node Node) (uint16, reconstructFunc) { + fields := node.Fields() + funcs := make([]reconstructFunc, len(fields)) + columnOffsets := make([]uint16, len(fields)) + firstColumnIndex := columnIndex + + for i, field := range fields { + columnIndex, funcs[i] = reconstructFuncOf(columnIndex, field) + columnOffsets[i] = columnIndex - firstColumnIndex + } + + return columnIndex, func(value reflect.Value, levels columnLevels, columns [][]Value) error { + if value.Kind() == reflect.Interface { + value.Set(reflect.MakeMap(reflect.TypeOf((map[string]any)(nil)))) + value = value.Elem() + } + + if value.Kind() == reflect.Map { + elemType := value.Type().Elem() + name := reflect.New(reflect.TypeOf("")).Elem() + elem := reflect.New(elemType).Elem() + + if value.Len() > 0 { + value.Set(reflect.MakeMap(value.Type())) + } + + off := uint16(0) + + for i, f := range funcs { + name.SetString(fields[i].Name()) + end := columnOffsets[i] + err := f(elem, levels, columns[off:end:end]) + if err != nil { + return fmt.Errorf("%s → %w", name, err) + } + off = end + value.SetMapIndex(name, elem) + elem.SetZero() + } + } else { + off := uint16(0) + + for i, f := range funcs { + end := columnOffsets[i] + err := f(fields[i].Value(value), levels, columns[off:end:end]) + if err != nil { + return fmt.Errorf("%s → %w", fields[i].Name(), err) + } + off = end + } + } + + return nil + } +} + +//go:noinline +func reconstructFuncOfLeaf(columnIndex uint16, node Node) (uint16, reconstructFunc) { + typ := node.Type() + return columnIndex + 1, func(value reflect.Value, _ columnLevels, columns [][]Value) error { + column := columns[0] + if len(column) == 0 { + return fmt.Errorf("no values found in parquet row for column %d", columnIndex) + } + return typ.AssignValue(value, column[0]) + } +} diff --git a/vendor/github.com/parquet-go/parquet-go/row_buffer.go b/vendor/github.com/parquet-go/parquet-go/row_buffer.go new file mode 100644 index 000000000..a904e88f3 --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/row_buffer.go @@ -0,0 +1,491 @@ +package parquet + +import ( + "io" + "math" + "sort" + + "github.com/parquet-go/parquet-go/deprecated" + "github.com/parquet-go/parquet-go/encoding" +) + +// RowBuffer is an implementation of the RowGroup interface which stores parquet +// rows in memory. +// +// Unlike GenericBuffer which uses a column layout to store values in memory +// buffers, RowBuffer uses a row layout. The use of row layout provides greater +// efficiency when sorting the buffer, which is the primary use case for the +// RowBuffer type. Applications which intend to sort rows prior to writing them +// to a parquet file will often see lower CPU utilization from using a RowBuffer +// than a GenericBuffer. +// +// RowBuffer values are not safe to use concurrently from multiple goroutines. +type RowBuffer[T any] struct { + alloc rowAllocator + schema *Schema + sorting []SortingColumn + rows []Row + values []Value + compare func(Row, Row) int +} + +// NewRowBuffer constructs a new row buffer. +func NewRowBuffer[T any](options ...RowGroupOption) *RowBuffer[T] { + config := DefaultRowGroupConfig() + config.Apply(options...) + if err := config.Validate(); err != nil { + panic(err) + } + + t := typeOf[T]() + if config.Schema == nil && t != nil { + config.Schema = schemaOf(dereference(t)) + } + + if config.Schema == nil { + panic("row buffer must be instantiated with schema or concrete type.") + } + + return &RowBuffer[T]{ + schema: config.Schema, + sorting: config.Sorting.SortingColumns, + compare: config.Schema.Comparator(config.Sorting.SortingColumns...), + } +} + +// Reset clears the content of the buffer without releasing its memory. +func (buf *RowBuffer[T]) Reset() { + for i := range buf.rows { + buf.rows[i] = nil + } + for i := range buf.values { + buf.values[i] = Value{} + } + buf.rows = buf.rows[:0] + buf.values = buf.values[:0] + buf.alloc.reset() +} + +// NumRows returns the number of rows currently written to the buffer. +func (buf *RowBuffer[T]) NumRows() int64 { return int64(len(buf.rows)) } + +// ColumnChunks returns a view of the buffer's columns. +// +// Note that reading columns of a RowBuffer will be less efficient than reading +// columns of a GenericBuffer since the latter uses a column layout. This method +// is mainly exposed to satisfy the RowGroup interface, applications which need +// compute-efficient column scans on in-memory buffers should likely use a +// GenericBuffer instead. +// +// The returned column chunks are snapshots at the time the method is called, +// they remain valid until the next call to Reset on the buffer. +func (buf *RowBuffer[T]) ColumnChunks() []ColumnChunk { + columns := buf.schema.Columns() + chunks := make([]rowBufferColumnChunk, len(columns)) + + for i, column := range columns { + leafColumn, _ := buf.schema.Lookup(column...) + chunks[i] = rowBufferColumnChunk{ + page: rowBufferPage{ + rows: buf.rows, + typ: leafColumn.Node.Type(), + column: leafColumn.ColumnIndex, + maxRepetitionLevel: byte(leafColumn.MaxRepetitionLevel), + maxDefinitionLevel: byte(leafColumn.MaxDefinitionLevel), + }, + } + } + + columnChunks := make([]ColumnChunk, len(chunks)) + for i := range chunks { + columnChunks[i] = &chunks[i] + } + return columnChunks +} + +// SortingColumns returns the list of columns that rows are expected to be +// sorted by. +// +// The list of sorting columns is configured when the buffer is created and used +// when it is sorted. +// +// Note that unless the buffer is explicitly sorted, there are no guarantees +// that the rows it contains will be in the order specified by the sorting +// columns. +func (buf *RowBuffer[T]) SortingColumns() []SortingColumn { return buf.sorting } + +// Schema returns the schema of rows in the buffer. +func (buf *RowBuffer[T]) Schema() *Schema { return buf.schema } + +// Len returns the number of rows in the buffer. +// +// The method contributes to satisfying sort.Interface. +func (buf *RowBuffer[T]) Len() int { return len(buf.rows) } + +// Less compares the rows at index i and j according to the sorting columns +// configured on the buffer. +// +// The method contributes to satisfying sort.Interface. +func (buf *RowBuffer[T]) Less(i, j int) bool { + return buf.compare(buf.rows[i], buf.rows[j]) < 0 +} + +// Swap exchanges the rows at index i and j in the buffer. +// +// The method contributes to satisfying sort.Interface. +func (buf *RowBuffer[T]) Swap(i, j int) { + buf.rows[i], buf.rows[j] = buf.rows[j], buf.rows[i] +} + +// Rows returns a Rows instance exposing rows stored in the buffer. +// +// The rows returned are a snapshot at the time the method is called. +// The returned rows and values read from it remain valid until the next call +// to Reset on the buffer. +func (buf *RowBuffer[T]) Rows() Rows { + return &rowBufferRows{rows: buf.rows, schema: buf.schema} +} + +// Write writes rows to the buffer, returning the number of rows written. +func (buf *RowBuffer[T]) Write(rows []T) (int, error) { + for i := range rows { + off := len(buf.values) + buf.values = buf.schema.Deconstruct(buf.values, &rows[i]) + end := len(buf.values) + row := buf.values[off:end:end] + buf.alloc.capture(row) + buf.rows = append(buf.rows, row) + } + return len(rows), nil +} + +// WriteRows writes parquet rows to the buffer, returing the number of rows +// written. +func (buf *RowBuffer[T]) WriteRows(rows []Row) (int, error) { + for i := range rows { + off := len(buf.values) + buf.values = append(buf.values, rows[i]...) + end := len(buf.values) + row := buf.values[off:end:end] + buf.alloc.capture(row) + buf.rows = append(buf.rows, row) + } + return len(rows), nil +} + +type rowBufferColumnChunk struct{ page rowBufferPage } + +func (c *rowBufferColumnChunk) Type() Type { return c.page.Type() } + +func (c *rowBufferColumnChunk) Column() int { return c.page.Column() } + +func (c *rowBufferColumnChunk) Pages() Pages { return onePage(&c.page) } + +func (c *rowBufferColumnChunk) ColumnIndex() (ColumnIndex, error) { return nil, nil } + +func (c *rowBufferColumnChunk) OffsetIndex() (OffsetIndex, error) { return nil, nil } + +func (c *rowBufferColumnChunk) BloomFilter() BloomFilter { return nil } + +func (c *rowBufferColumnChunk) NumValues() int64 { return c.page.NumValues() } + +type rowBufferPage struct { + rows []Row + typ Type + column int + maxRepetitionLevel byte + maxDefinitionLevel byte +} + +func (p *rowBufferPage) Type() Type { return p.typ } + +func (p *rowBufferPage) Column() int { return p.column } + +func (p *rowBufferPage) Dictionary() Dictionary { return nil } + +func (p *rowBufferPage) NumRows() int64 { return int64(len(p.rows)) } + +func (p *rowBufferPage) NumValues() int64 { + numValues := int64(0) + p.scan(func(value Value) { + if !value.isNull() { + numValues++ + } + }) + return numValues +} + +func (p *rowBufferPage) NumNulls() int64 { + numNulls := int64(0) + p.scan(func(value Value) { + if value.isNull() { + numNulls++ + } + }) + return numNulls +} + +func (p *rowBufferPage) Bounds() (min, max Value, ok bool) { + var nanValue Value + hasNaN := false + + p.scan(func(value Value) { + if value.IsNull() { + return + } + // NaN is excluded from min/max so that query engines can rely on + // statistics for predicate pushdown. See floatPage.Bounds for the + // full rationale. + if isNaN(value) { + nanValue = value + hasNaN = true + return + } + switch { + case !ok: + min, max, ok = value, value, true + case p.typ.Compare(value, min) < 0: + min = value + case p.typ.Compare(value, max) > 0: + max = value + } + }) + + if !ok && hasNaN { + min, max, ok = nanValue, nanValue, true + } + return min, max, ok +} + +func isNaN(v Value) bool { + switch v.Kind() { + case Float: + return math.IsNaN(float64(v.Float())) + case Double: + return math.IsNaN(v.Double()) + default: + return false + } +} + +func (p *rowBufferPage) Size() int64 { return 0 } + +func (p *rowBufferPage) Values() ValueReader { + return &rowBufferPageValueReader{ + page: p, + columnIndex: ^uint16(p.column), + } +} + +func (p *rowBufferPage) Clone() Page { + rows := make([]Row, len(p.rows)) + for i := range rows { + rows[i] = p.rows[i].Clone() + } + return &rowBufferPage{ + rows: rows, + typ: p.typ, + column: p.column, + } +} + +func (p *rowBufferPage) Slice(i, j int64) Page { + return &rowBufferPage{ + rows: p.rows[i:j], + typ: p.typ, + column: p.column, + } +} + +func (p *rowBufferPage) RepetitionLevels() (repetitionLevels []byte) { + if p.maxRepetitionLevel != 0 { + repetitionLevels = make([]byte, 0, len(p.rows)) + p.scan(func(value Value) { + repetitionLevels = append(repetitionLevels, value.repetitionLevel) + }) + } + return repetitionLevels +} + +func (p *rowBufferPage) DefinitionLevels() (definitionLevels []byte) { + if p.maxDefinitionLevel != 0 { + definitionLevels = make([]byte, 0, len(p.rows)) + p.scan(func(value Value) { + definitionLevels = append(definitionLevels, value.definitionLevel) + }) + } + return definitionLevels +} + +func (p *rowBufferPage) Data() encoding.Values { + switch p.typ.Kind() { + case Boolean: + values := make([]byte, (len(p.rows)+7)/8) + numValues := 0 + p.scanNonNull(func(value Value) { + if value.boolean() { + i := uint(numValues) / 8 + j := uint(numValues) % 8 + values[i] |= 1 << j + } + numValues++ + }) + return encoding.BooleanValues(values[:(numValues+7)/8]) + + case Int32: + values := make([]int32, 0, len(p.rows)) + p.scanNonNull(func(value Value) { values = append(values, value.int32()) }) + return encoding.Int32Values(values) + + case Int64: + values := make([]int64, 0, len(p.rows)) + p.scanNonNull(func(value Value) { values = append(values, value.int64()) }) + return encoding.Int64Values(values) + + case Int96: + values := make([]deprecated.Int96, 0, len(p.rows)) + p.scanNonNull(func(value Value) { values = append(values, value.int96()) }) + return encoding.Int96Values(values) + + case Float: + values := make([]float32, 0, len(p.rows)) + p.scanNonNull(func(value Value) { values = append(values, value.float()) }) + return encoding.FloatValues(values) + + case Double: + values := make([]float64, 0, len(p.rows)) + p.scanNonNull(func(value Value) { values = append(values, value.double()) }) + return encoding.DoubleValues(values) + + case ByteArray: + values := make([]byte, 0, p.typ.EstimateSize(len(p.rows))) + offsets := make([]uint32, 0, len(p.rows)) + p.scanNonNull(func(value Value) { + offsets = append(offsets, uint32(len(values))) + values = append(values, value.byteArray()...) + }) + offsets = append(offsets, uint32(len(values))) + return encoding.ByteArrayValues(values, offsets) + + case FixedLenByteArray: + length := p.typ.Length() + values := make([]byte, 0, length*len(p.rows)) + p.scanNonNull(func(value Value) { values = append(values, value.byteArray()...) }) + return encoding.FixedLenByteArrayValues(values, length) + + default: + return encoding.Values{} + } +} + +func (p *rowBufferPage) scan(f func(Value)) { + columnIndex := ^uint16(p.column) + + for _, row := range p.rows { + for _, value := range row { + if value.columnIndex == columnIndex { + f(value) + } + } + } +} + +func (p *rowBufferPage) scanNonNull(f func(Value)) { + p.scan(func(value Value) { + if !value.isNull() { + f(value) + } + }) +} + +type rowBufferPageValueReader struct { + page *rowBufferPage + rowIndex int + valueIndex int + columnIndex uint16 +} + +func (r *rowBufferPageValueReader) ReadValues(values []Value) (n int, err error) { + for n < len(values) && r.rowIndex < len(r.page.rows) { + for n < len(values) && r.valueIndex < len(r.page.rows[r.rowIndex]) { + if v := r.page.rows[r.rowIndex][r.valueIndex]; v.columnIndex == r.columnIndex { + values[n] = v + n++ + } + r.valueIndex++ + } + r.rowIndex++ + r.valueIndex = 0 + } + if r.rowIndex == len(r.page.rows) { + err = io.EOF + } + return n, err +} + +type rowBufferRows struct { + rows []Row + index int + schema *Schema +} + +func (r *rowBufferRows) Close() error { + r.index = -1 + return nil +} + +func (r *rowBufferRows) Schema() *Schema { + return r.schema +} + +func (r *rowBufferRows) SeekToRow(rowIndex int64) error { + if rowIndex < 0 { + return ErrSeekOutOfRange + } + + if r.index < 0 { + return io.ErrClosedPipe + } + + maxRowIndex := int64(len(r.rows)) + if rowIndex > maxRowIndex { + rowIndex = maxRowIndex + } + + r.index = int(rowIndex) + return nil +} + +func (r *rowBufferRows) ReadRows(rows []Row) (n int, err error) { + if r.index < 0 { + return 0, io.EOF + } + + if n = len(r.rows) - r.index; n > len(rows) { + n = len(rows) + } + + for i, row := range r.rows[r.index : r.index+n] { + rows[i] = append(rows[i][:0], row...) + } + + if r.index += n; r.index == len(r.rows) { + err = io.EOF + } + + return n, err +} + +func (r *rowBufferRows) WriteRowsTo(w RowWriter) (int64, error) { + n, err := w.WriteRows(r.rows[r.index:]) + r.index += n + return int64(n), err +} + +var ( + _ RowGroup = (*RowBuffer[any])(nil) + _ RowWriter = (*RowBuffer[any])(nil) + _ sort.Interface = (*RowBuffer[any])(nil) + + _ RowWriterTo = (*rowBufferRows)(nil) +) diff --git a/vendor/github.com/parquet-go/parquet-go/row_builder.go b/vendor/github.com/parquet-go/parquet-go/row_builder.go new file mode 100644 index 000000000..ebb1f18cd --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/row_builder.go @@ -0,0 +1,204 @@ +package parquet + +// RowBuilder is a type which helps build parquet rows incrementally by adding +// values to columns. +type RowBuilder struct { + columns [][]Value + models []Value + levels []columnLevel + groups []*columnGroup +} + +type columnLevel struct { + repetitionDepth byte + repetitionLevel byte + definitionLevel byte +} + +type columnGroup struct { + baseColumn []Value + members []uint16 + startIndex uint16 + endIndex uint16 + repetitionLevel byte + definitionLevel byte +} + +// NewRowBuilder constructs a RowBuilder which builds rows for the parquet +// schema passed as argument. +func NewRowBuilder(schema Node) *RowBuilder { + if schema.Leaf() { + panic("schema of row builder must be a group") + } + n := numLeafColumnsOf(schema) + b := &RowBuilder{ + columns: make([][]Value, n), + models: make([]Value, n), + levels: make([]columnLevel, n), + } + buffers := make([]Value, len(b.columns)) + for i := range b.columns { + b.columns[i] = buffers[i : i : i+1] + } + topGroup := &columnGroup{baseColumn: []Value{{}}} + endIndex := b.configure(schema, 0, columnLevel{}, topGroup) + topGroup.endIndex = endIndex + b.groups = append(b.groups, topGroup) + return b +} + +func (b *RowBuilder) configure(node Node, columnIndex uint16, level columnLevel, group *columnGroup) (endIndex uint16) { + switch { + case node.Optional(): + level.definitionLevel++ + endIndex = b.configure(Required(node), columnIndex, level, group) + + for i := columnIndex; i < endIndex; i++ { + b.models[i].kind = 0 // null if not set + b.models[i].ptr = nil + b.models[i].u64 = 0 + } + + case node.Repeated(): + level.definitionLevel++ + + group = &columnGroup{ + startIndex: columnIndex, + repetitionLevel: level.repetitionDepth, + definitionLevel: level.definitionLevel, + } + + level.repetitionDepth++ + endIndex = b.configure(Required(node), columnIndex, level, group) + + for i := columnIndex; i < endIndex; i++ { + b.models[i].kind = 0 // null if not set + b.models[i].ptr = nil + b.models[i].u64 = 0 + } + + group.endIndex = endIndex + b.groups = append(b.groups, group) + + case node.Leaf(): + typ := node.Type() + kind := typ.Kind() + model := makeValueKind(kind) + model.repetitionLevel = level.repetitionLevel + model.definitionLevel = level.definitionLevel + // FIXED_LEN_BYTE_ARRAY is the only type which needs to be given a + // non-nil zero-value if the field is required. + if kind == FixedLenByteArray { + if length := typ.Length(); length != 0 { + zero := make([]byte, length) + model.ptr = &zero[0] + model.u64 = uint64(length) + } + } + group.members = append(group.members, columnIndex) + b.models[columnIndex] = model + b.levels[columnIndex] = level + endIndex = columnIndex + 1 + + default: + endIndex = columnIndex + + for _, field := range node.Fields() { + endIndex = b.configure(field, endIndex, level, group) + } + } + return endIndex +} + +// Add adds columnValue to the column at columnIndex. +func (b *RowBuilder) Add(columnIndex int, columnValue Value) { + level := &b.levels[columnIndex] + columnValue.repetitionLevel = level.repetitionLevel + columnValue.definitionLevel = level.definitionLevel + columnValue.columnIndex = ^uint16(columnIndex) + level.repetitionLevel = level.repetitionDepth + b.columns[columnIndex] = append(b.columns[columnIndex], columnValue) +} + +// Next must be called to indicate the start of a new repeated record for the +// column at the given index. +// +// If the column index is part of a repeated group, the builder automatically +// starts a new record for all adjacent columns, the application does not need +// to call this method for each column of the repeated group. +// +// Next must be called after adding a sequence of records. +func (b *RowBuilder) Next(columnIndex int) { + for _, group := range b.groups { + if group.startIndex <= uint16(columnIndex) && uint16(columnIndex) < group.endIndex { + for i := group.startIndex; i < group.endIndex; i++ { + if level := &b.levels[i]; level.repetitionLevel != 0 { + level.repetitionLevel = group.repetitionLevel + } + } + break + } + } +} + +// Reset clears the internal state of b, making it possible to reuse while +// retaining the internal buffers. +func (b *RowBuilder) Reset() { + for i, column := range b.columns { + clearValues(column) + b.columns[i] = column[:0] + } + for i := range b.levels { + b.levels[i].repetitionLevel = 0 + } +} + +// Row materializes the current state of b into a parquet row. +func (b *RowBuilder) Row() Row { + numValues := 0 + for _, column := range b.columns { + numValues += len(column) + } + return b.AppendRow(make(Row, 0, numValues)) +} + +// AppendRow appends the current state of b to row and returns it. +func (b *RowBuilder) AppendRow(row Row) Row { + for _, group := range b.groups { + maxColumn := group.baseColumn + + for _, columnIndex := range group.members { + if column := b.columns[columnIndex]; len(column) > len(maxColumn) { + maxColumn = column + } + } + + if len(maxColumn) != 0 { + columns := b.columns[group.startIndex:group.endIndex] + + for i, column := range columns { + if len(column) < len(maxColumn) { + n := len(column) + column = append(column, maxColumn[n:]...) + + columnIndex := group.startIndex + uint16(i) + model := b.models[columnIndex] + + for n < len(column) { + v := &column[n] + v.kind = model.kind + v.ptr = model.ptr + v.u64 = model.u64 + v.definitionLevel = group.definitionLevel + v.columnIndex = ^columnIndex + n++ + } + + columns[i] = column + } + } + } + } + + return appendRow(row, b.columns) +} diff --git a/vendor/github.com/parquet-go/parquet-go/row_group.go b/vendor/github.com/parquet-go/parquet-go/row_group.go new file mode 100644 index 000000000..66cc65dca --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/row_group.go @@ -0,0 +1,493 @@ +package parquet + +import ( + "errors" + "fmt" + "io" + + "github.com/parquet-go/parquet-go/internal/debug" +) + +// RowGroup is an interface representing a parquet row group. From the Parquet +// docs, a RowGroup is "a logical horizontal partitioning of the data into rows. +// There is no physical structure that is guaranteed for a row group. A row +// group consists of a column chunk for each column in the dataset." +// +// https://github.com/apache/parquet-format#glossary +type RowGroup interface { + // Returns the number of rows in the group. + NumRows() int64 + + // Returns the list of column chunks in this row group. The chunks are + // ordered in the order of leaf columns from the row group's schema. + // + // If the underlying implementation is not read-only, the returned + // parquet.ColumnChunk may implement other interfaces: for example, + // parquet.ColumnBuffer if the chunk is backed by an in-memory buffer, + // or typed writer interfaces like parquet.Int32Writer depending on the + // underlying type of values that can be written to the chunk. + // + // As an optimization, the row group may return the same slice across + // multiple calls to this method. Applications should treat the returned + // slice as read-only. + ColumnChunks() []ColumnChunk + + // Returns the schema of rows in the group. + Schema() *Schema + + // Returns the list of sorting columns describing how rows are sorted in the + // group. + // + // The method will return an empty slice if the rows are not sorted. + SortingColumns() []SortingColumn + + // Returns a reader exposing the rows of the row group. + // + // As an optimization, the returned parquet.Rows object may implement + // parquet.RowWriterTo, and test the RowWriter it receives for an + // implementation of the parquet.RowGroupWriter interface. + // + // This optimization mechanism is leveraged by the parquet.CopyRows function + // to skip the generic row-by-row copy algorithm and delegate the copy logic + // to the parquet.Rows object. + Rows() Rows +} + +// Rows is an interface implemented by row readers returned by calling the Rows +// method of RowGroup instances. +// +// Applications should call Close when they are done using a Rows instance in +// order to release the underlying resources held by the row sequence. +// +// After calling Close, all attempts to read more rows will return io.EOF. +type Rows interface { + RowReadSeekCloser + Schema() *Schema +} + +// RowGroupReader is an interface implemented by types that expose sequences of +// row groups to the application. +type RowGroupReader interface { + ReadRowGroup() (RowGroup, error) +} + +// RowGroupWriter is an interface implemented by types that allow the program +// to write row groups. +type RowGroupWriter interface { + WriteRowGroup(RowGroup) (int64, error) +} + +// SortingColumn represents a column by which a row group is sorted. +type SortingColumn interface { + // Returns the path of the column in the row group schema, omitting the name + // of the root node. + Path() []string + + // Returns true if the column will sort values in descending order. + Descending() bool + + // Returns true if the column will put null values at the beginning. + NullsFirst() bool +} + +// Ascending constructs a SortingColumn value which dictates to sort the column +// at the path given as argument in ascending order. +func Ascending(path ...string) SortingColumn { return ascending(path) } + +// Descending constructs a SortingColumn value which dictates to sort the column +// at the path given as argument in descending order. +func Descending(path ...string) SortingColumn { return descending(path) } + +// NullsFirst wraps the SortingColumn passed as argument so that it instructs +// the row group to place null values first in the column. +func NullsFirst(sortingColumn SortingColumn) SortingColumn { return nullsFirst{sortingColumn} } + +type ascending []string + +func (asc ascending) String() string { return fmt.Sprintf("ascending(%s)", columnPath(asc)) } +func (asc ascending) Path() []string { return asc } +func (asc ascending) Descending() bool { return false } +func (asc ascending) NullsFirst() bool { return false } + +type descending []string + +func (desc descending) String() string { return fmt.Sprintf("descending(%s)", columnPath(desc)) } +func (desc descending) Path() []string { return desc } +func (desc descending) Descending() bool { return true } +func (desc descending) NullsFirst() bool { return false } + +type nullsFirst struct{ SortingColumn } + +func (nf nullsFirst) String() string { return fmt.Sprintf("nulls_first+%s", nf.SortingColumn) } +func (nf nullsFirst) NullsFirst() bool { return true } + +func searchSortingColumn(sortingColumns []SortingColumn, path columnPath) int { + // There are usually a few sorting columns in a row group, so the linear + // scan is the fastest option and works whether the sorting column list + // is sorted or not. Please revisit this decision if this code path ends + // up being more costly than necessary. + for i, sorting := range sortingColumns { + if path.equal(sorting.Path()) { + return i + } + } + return len(sortingColumns) +} + +func sortingColumnsHavePrefix(sortingColumns, prefix []SortingColumn) bool { + if len(sortingColumns) < len(prefix) { + return false + } + for i, sortingColumn := range prefix { + if !sortingColumnsAreEqual(sortingColumns[i], sortingColumn) { + return false + } + } + return true +} + +func sortingColumnsAreEqual(s1, s2 SortingColumn) bool { + path1 := columnPath(s1.Path()) + path2 := columnPath(s2.Path()) + return path1.equal(path2) && s1.Descending() == s2.Descending() && s1.NullsFirst() == s2.NullsFirst() +} + +type rowGroup struct { + schema *Schema + numRows int64 + columns []ColumnChunk + sorting []SortingColumn +} + +func (r *rowGroup) NumRows() int64 { return r.numRows } +func (r *rowGroup) ColumnChunks() []ColumnChunk { return r.columns } +func (r *rowGroup) SortingColumns() []SortingColumn { return r.sorting } +func (r *rowGroup) Schema() *Schema { return r.schema } +func (r *rowGroup) Rows() Rows { return NewRowGroupRowReader(r) } + +func AsyncRowGroup(base RowGroup) RowGroup { + columnChunks := base.ColumnChunks() + asyncRowGroup := &rowGroup{ + schema: base.Schema(), + numRows: base.NumRows(), + sorting: base.SortingColumns(), + columns: make([]ColumnChunk, len(columnChunks)), + } + asyncColumnChunks := make([]asyncColumnChunk, len(columnChunks)) + for i, columnChunk := range columnChunks { + asyncColumnChunks[i].ColumnChunk = columnChunk + asyncRowGroup.columns[i] = &asyncColumnChunks[i] + } + return asyncRowGroup +} + +type rowGroupRows struct { + schema *Schema + bufsize int + buffers []Value + columns []columnChunkRows + closed bool + rowIndex int64 +} + +type columnChunkRows struct { + offset int32 + length int32 + reader columnChunkValueReader +} + +func (r *rowGroupRows) buffer(i int) []Value { + j := (i + 0) * r.bufsize + k := (i + 1) * r.bufsize + return r.buffers[j:k:k] +} + +// / NewRowGroupRowReader constructs a new row reader for the given row group. +func NewRowGroupRowReader(rowGroup RowGroup) Rows { + return newRowGroupRows(rowGroup.Schema(), rowGroup.ColumnChunks(), defaultValueBufferSize) +} + +func newRowGroupRows(schema *Schema, columns []ColumnChunk, bufferSize int) *rowGroupRows { + r := &rowGroupRows{ + schema: schema, + bufsize: bufferSize, + buffers: make([]Value, len(columns)*bufferSize), + columns: make([]columnChunkRows, len(columns)), + rowIndex: -1, + } + for i, column := range columns { + switch column.Type().Kind() { + case ByteArray, FixedLenByteArray: + // (@mdisibio) - If the column can contain pointers, then we must not repool + // the underlying values buffer because the rows returned to the caller + // reference slices within it. Detaching the entire values buffer is more + // efficient than cloning individual values. + r.columns[i].reader.detach = true + } + r.columns[i].reader.pages = column.Pages() + } + // This finalizer is used to ensure that the goroutines started by calling + // init on the underlying page readers will be shutdown in the event that + // Close isn't called and the rowGroupRows object is garbage collected. + debug.SetFinalizer(r, func(r *rowGroupRows) { r.Close() }) + return r +} + +func (r *rowGroupRows) clear() { + for i, c := range r.columns { + r.columns[i] = columnChunkRows{reader: c.reader} + } + clear(r.buffers) +} + +func (r *rowGroupRows) Reset() { + for i := range r.columns { + r.columns[i].reader.Reset() + } + r.clear() +} + +func (r *rowGroupRows) Close() error { + var errs []error + for i := range r.columns { + c := &r.columns[i] + c.offset = 0 + c.length = 0 + if err := c.reader.Close(); err != nil { + errs = append(errs, err) + } + } + r.clear() + r.closed = true + return errors.Join(errs...) +} + +func (r *rowGroupRows) SeekToRow(rowIndex int64) error { + if r.closed { + return io.ErrClosedPipe + } + if rowIndex != r.rowIndex { + for i := range r.columns { + if err := r.columns[i].reader.SeekToRow(rowIndex); err != nil { + return err + } + } + r.clear() + r.rowIndex = rowIndex + } + return nil +} + +func (r *rowGroupRows) ReadRows(rows []Row) (int, error) { + if r.closed { + return 0, io.EOF + } + + // When this is the first call to ReadRows, we issue a seek to the first row + // because this starts prefetching pages asynchronously on columns. + // + // This condition does not apply if SeekToRow was called before ReadRows, + // only when ReadRows is the very first method called on the row reader. + if r.rowIndex < 0 { + if err := r.SeekToRow(0); err != nil { + return 0, err + } + } + + eofCount := 0 + rowCount := 0 + +readColumnValues: + for columnIndex := range r.columns { + c := &r.columns[columnIndex] + b := r.buffer(columnIndex) + eof := false + + for rowIndex := range rows { + numValuesInRow := 1 + + if columnIndex == 0 { + rows[rowIndex] = rows[rowIndex][:0] + } + + for { + if c.offset == c.length { + n, err := c.reader.ReadValues(b) + c.offset = 0 + c.length = int32(n) + + if n == 0 { + if err == io.EOF { + eof = true + eofCount++ + break + } + return 0, err + } + } + + values := b[c.offset:c.length:c.length] + for numValuesInRow < len(values) && values[numValuesInRow].repetitionLevel != 0 { + numValuesInRow++ + } + if numValuesInRow == 0 { + break + } + + rows[rowIndex] = append(rows[rowIndex], values[:numValuesInRow]...) + rowCount = max(rowCount, rowIndex+1) + c.offset += int32(numValuesInRow) + + if numValuesInRow != len(values) { + break + } + if eof { + continue readColumnValues + } + numValuesInRow = 0 + } + } + } + + var err error + if eofCount > 0 { + err = io.EOF + } + r.rowIndex += int64(rowCount) + return rowCount, err +} + +func (r *rowGroupRows) Schema() *Schema { + return r.schema +} + +type seekRowGroup struct { + base RowGroup + seek int64 + columns []ColumnChunk +} + +func (g *seekRowGroup) NumRows() int64 { + return g.base.NumRows() - g.seek +} + +func (g *seekRowGroup) ColumnChunks() []ColumnChunk { + return g.columns +} + +func (g *seekRowGroup) Schema() *Schema { + return g.base.Schema() +} + +func (g *seekRowGroup) SortingColumns() []SortingColumn { + return g.base.SortingColumns() +} + +func (g *seekRowGroup) Rows() Rows { + rows := g.base.Rows() + rows.SeekToRow(g.seek) + return rows +} + +type seekColumnChunk struct { + base ColumnChunk + seek int64 +} + +func (c *seekColumnChunk) Type() Type { + return c.base.Type() +} + +func (c *seekColumnChunk) Column() int { + return c.base.Column() +} + +func (c *seekColumnChunk) Pages() Pages { + pages := c.base.Pages() + pages.SeekToRow(c.seek) + return pages +} + +func (c *seekColumnChunk) ColumnIndex() (ColumnIndex, error) { + return c.base.ColumnIndex() +} + +func (c *seekColumnChunk) OffsetIndex() (OffsetIndex, error) { + return c.base.OffsetIndex() +} + +func (c *seekColumnChunk) BloomFilter() BloomFilter { + return c.base.BloomFilter() +} + +func (c *seekColumnChunk) NumValues() int64 { + return c.base.NumValues() +} + +type emptyRowGroup struct { + schema *Schema + columns []ColumnChunk +} + +func newEmptyRowGroup(schema *Schema) *emptyRowGroup { + columns := schema.Columns() + rowGroup := &emptyRowGroup{ + schema: schema, + columns: make([]ColumnChunk, len(columns)), + } + emptyColumnChunks := make([]emptyColumnChunk, len(columns)) + for i, column := range schema.Columns() { + leaf, _ := schema.Lookup(column...) + emptyColumnChunks[i].typ = leaf.Node.Type() + emptyColumnChunks[i].column = int16(leaf.ColumnIndex) + rowGroup.columns[i] = &emptyColumnChunks[i] + } + return rowGroup +} + +func (g *emptyRowGroup) NumRows() int64 { return 0 } +func (g *emptyRowGroup) ColumnChunks() []ColumnChunk { return g.columns } +func (g *emptyRowGroup) Schema() *Schema { return g.schema } +func (g *emptyRowGroup) SortingColumns() []SortingColumn { return nil } +func (g *emptyRowGroup) Rows() Rows { return emptyRows{g.schema} } + +type emptyColumnChunk struct { + typ Type + column int16 +} + +func (c *emptyColumnChunk) Type() Type { return c.typ } +func (c *emptyColumnChunk) Column() int { return int(c.column) } +func (c *emptyColumnChunk) Pages() Pages { return emptyPages{} } +func (c *emptyColumnChunk) ColumnIndex() (ColumnIndex, error) { return emptyColumnIndex{}, nil } +func (c *emptyColumnChunk) OffsetIndex() (OffsetIndex, error) { return emptyOffsetIndex{}, nil } +func (c *emptyColumnChunk) BloomFilter() BloomFilter { return emptyBloomFilter{} } +func (c *emptyColumnChunk) NumValues() int64 { return 0 } + +type emptyBloomFilter struct{} + +func (emptyBloomFilter) ReadAt([]byte, int64) (int, error) { return 0, io.EOF } +func (emptyBloomFilter) Size() int64 { return 0 } +func (emptyBloomFilter) Check(Value) (bool, error) { return false, nil } + +type emptyRows struct{ schema *Schema } + +func (r emptyRows) Close() error { return nil } +func (r emptyRows) Schema() *Schema { return r.schema } +func (r emptyRows) ReadRows([]Row) (int, error) { return 0, io.EOF } +func (r emptyRows) SeekToRow(int64) error { return nil } +func (r emptyRows) WriteRowsTo(RowWriter) (int64, error) { return 0, nil } + +type emptyPages struct{} + +func (emptyPages) ReadPage() (Page, error) { return nil, io.EOF } +func (emptyPages) SeekToRow(int64) error { return nil } +func (emptyPages) Close() error { return nil } + +var ( + _ RowReaderWithSchema = (*rowGroupRows)(nil) + //_ RowWriterTo = (*rowGroupRows)(nil) + + _ RowReaderWithSchema = emptyRows{} + _ RowWriterTo = emptyRows{} +) diff --git a/vendor/github.com/parquet-go/parquet-go/row_variant.go b/vendor/github.com/parquet-go/parquet-go/row_variant.go new file mode 100644 index 000000000..3fea8b354 --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/row_variant.go @@ -0,0 +1,979 @@ +package parquet + +import ( + "encoding/binary" + "fmt" + "maps" + "reflect" + "unsafe" + + "github.com/google/uuid" + "github.com/parquet-go/parquet-go/variant" +) + +// variantMarshalOrNull marshals a reflect.Value to variant binary. If the +// value is nil/invalid/null, it returns the variant encoding of null. +// If the value is a raw variant struct (with metadata and value []byte fields), +// the pre-encoded bytes are extracted directly. +func variantMarshalOrNull(value reflect.Value) (metadata, val []byte) { + if m, v, ok := extractRawVariantStruct(value); ok { + return m, v + } + var goVal any + if value.IsValid() && !isNullValue(value) { + v := value + for v.Kind() == reflect.Ptr || v.Kind() == reflect.Interface { + if v.IsNil() { + break + } + v = v.Elem() + } + if v.IsValid() && v.Kind() != reflect.Ptr && v.Kind() != reflect.Interface { + goVal = v.Interface() + } + } + metadata, val, err := variant.Marshal(goVal) + if err != nil { + panic(fmt.Sprintf("variant marshal: %v", err)) + } + return metadata, val +} + +// extractRawVariantStruct checks if the value is a struct with "metadata" and +// "value" []byte fields (matching the unshredded variant group layout). If so, +// it extracts the raw bytes directly, supporting passthrough of pre-encoded +// variant data. +func extractRawVariantStruct(value reflect.Value) (metadata, val []byte, ok bool) { + if !value.IsValid() || isNullValue(value) { + return nil, nil, false + } + v := value + for v.Kind() == reflect.Ptr || v.Kind() == reflect.Interface { + if v.IsNil() { + return nil, nil, false + } + v = v.Elem() + } + if v.Kind() != reflect.Struct { + return nil, nil, false + } + t := v.Type() + metaField, hasMetadata := t.FieldByName("Metadata") + valField, hasValue := t.FieldByName("Value") + if !hasMetadata || !hasValue { + return nil, nil, false + } + byteSliceType := reflect.TypeOf([]byte(nil)) + if metaField.Type != byteSliceType || valField.Type != byteSliceType { + return nil, nil, false + } + m := v.FieldByName("Metadata").Bytes() + va := v.FieldByName("Value").Bytes() + if m == nil && va == nil { + return nil, nil, false + } + return m, va, true +} + +// isUnshreddedVariant returns true if the variant node has only metadata and value +// (no typed_value field), indicating an unshredded variant. +func isUnshreddedVariant(node Node) bool { + return fieldByName(node, "typed_value") == nil +} + +// deconstructFuncOfVariant handles deconstruction of Go values into variant +// columns. Dispatches to unshredded or shredded variant handling. +// +//go:noinline +func deconstructFuncOfVariant(columnIndex uint16, node Node) (uint16, deconstructFunc) { + if isUnshreddedVariant(node) { + return deconstructFuncOfUnshreddedVariant(columnIndex, node) + } + return deconstructFuncOfShreddedVariant(columnIndex, node) +} + +// deconstructFuncOfUnshreddedVariant handles the simple 2-column variant: +// metadata (required ByteArray) and value (required ByteArray). +// +//go:noinline +func deconstructFuncOfUnshreddedVariant(columnIndex uint16, node Node) (uint16, deconstructFunc) { + metadataColumnIndex := columnIndex + valueColumnIndex := columnIndex + 1 + nextColumnIndex := columnIndex + 2 + + return nextColumnIndex, func(columns [][]Value, levels columnLevels, value reflect.Value) { + metaIdx := ^metadataColumnIndex + valIdx := ^valueColumnIndex + + metadata, val := variantMarshalOrNull(value) + + metaValue := makeValueByteArray(ByteArray, unsafe.SliceData(metadata), len(metadata)) + metaValue.repetitionLevel = levels.repetitionLevel + metaValue.definitionLevel = levels.definitionLevel + metaValue.columnIndex = metaIdx + + valValue := makeValueByteArray(ByteArray, unsafe.SliceData(val), len(val)) + valValue.repetitionLevel = levels.repetitionLevel + valValue.definitionLevel = levels.definitionLevel + valValue.columnIndex = valIdx + + columns[metadataColumnIndex] = append(columns[metadataColumnIndex], metaValue) + columns[valueColumnIndex] = append(columns[valueColumnIndex], valValue) + } +} + +// deconstructFuncOfShreddedVariant handles the 3-field shredded variant: +// metadata (required), value (optional), typed_value (optional). +// +// When the Go value matches the typed_value schema, the value is written to the +// typed_value columns and the value column is set to null. Otherwise, the variant- +// encoded bytes are written to the value column and typed_value columns are null. +// +//go:noinline +func deconstructFuncOfShreddedVariant(columnIndex uint16, node Node) (uint16, deconstructFunc) { + // Fields are sorted alphabetically: metadata, typed_value, value + var metadataColumnIndex, valueColumnIndex, typedValueStartColumn uint16 + var typedValueLeafCount uint16 + var typedValueNode Node + + col := columnIndex + for _, f := range node.Fields() { + n := numLeafColumnsOf(f) + switch f.Name() { + case "metadata": + metadataColumnIndex = col + col += n + case "typed_value": + typedValueStartColumn = col + typedValueNode = f + typedValueLeafCount = n + col += n + case "value": + valueColumnIndex = col + col += n + } + } + nextColumnIndex := col + + // Determine the shredded type structure + typedInner := Required(typedValueNode) + matcher := buildShreddedMatcher(typedInner) + + return nextColumnIndex, func(columns [][]Value, levels columnLevels, value reflect.Value) { + metaIdx := ^metadataColumnIndex + valIdx := ^valueColumnIndex + + metadata, val := variantMarshalOrNull(value) + + // Always write metadata + metaValue := makeValueByteArray(ByteArray, unsafe.SliceData(metadata), len(metadata)) + metaValue.repetitionLevel = levels.repetitionLevel + metaValue.definitionLevel = levels.definitionLevel + metaValue.columnIndex = metaIdx + columns[metadataColumnIndex] = append(columns[metadataColumnIndex], metaValue) + + // Try to shred the value + goVal := extractGoValue(value) + if goVal != nil && matcher.canShred(goVal) { + // Write null to value column (optional, def level not incremented) + columns[valueColumnIndex] = append(columns[valueColumnIndex], Value{ + repetitionLevel: levels.repetitionLevel, + definitionLevel: levels.definitionLevel, + columnIndex: valIdx, + }) + // Write typed value (increment def level for optional typed_value wrapper) + typedLevels := levels + typedLevels.definitionLevel++ + matcher.shred(columns, typedLevels, typedValueStartColumn, goVal) + } else { + // Write variant bytes to value column (increment def level for optional wrapper) + valueLevels := levels + valueLevels.definitionLevel++ + valValue := makeValueByteArray(ByteArray, unsafe.SliceData(val), len(val)) + valValue.repetitionLevel = valueLevels.repetitionLevel + valValue.definitionLevel = valueLevels.definitionLevel + valValue.columnIndex = valIdx + columns[valueColumnIndex] = append(columns[valueColumnIndex], valValue) + + // Write null to all typed_value columns + writeNullLeaves(columns, typedValueStartColumn, typedValueStartColumn+typedValueLeafCount, levels) + } + } +} + +// extractGoValue extracts the underlying Go value from a reflect.Value, +// dereferencing pointers and interfaces. +func extractGoValue(value reflect.Value) any { + if !value.IsValid() || isNullValue(value) { + return nil + } + v := value + for v.Kind() == reflect.Ptr || v.Kind() == reflect.Interface { + if v.IsNil() { + return nil + } + v = v.Elem() + } + return v.Interface() +} + +// writeNullLeaves writes null values to all leaf columns in the range [start, end). +func writeNullLeaves(columns [][]Value, start, end uint16, levels columnLevels) { + for col := start; col < end; col++ { + columns[col] = append(columns[col], Value{ + repetitionLevel: levels.repetitionLevel, + definitionLevel: levels.definitionLevel, + columnIndex: ^col, + }) + } +} + +// shreddedMatcher determines if a Go value can be shredded into a typed_value +// column and performs the shredding. +// +// A matcher is one of three kinds: +// - primitive: typ is set (leaf column) +// - group: fields is set (object with per-field matchers) +// - unsupported: neither (e.g., LIST — values go to the fallback column) +// +// leafCount is always set to the number of leaf columns occupied. +type shreddedMatcher struct { + typ Type // non-nil for primitive typed_value (leaf node) + fields []shreddedFieldMatcher // non-nil for group typed_value (object node) + leafCount uint16 +} + +func (m *shreddedMatcher) isPrimitive() bool { return m.typ != nil } +func (m *shreddedMatcher) isGroup() bool { return len(m.fields) > 0 } +func (m *shreddedMatcher) kind() Kind { return m.typ.Kind() } +func (m *shreddedMatcher) isString() bool { + lt := m.typ.LogicalType() + return lt != nil && lt.UTF8 != nil +} +func (m *shreddedMatcher) isUUID() bool { + lt := m.typ.LogicalType() + return lt != nil && lt.UUID != nil +} + +type shreddedFieldMatcher struct { + name string + matcher shreddedMatcher +} + +func buildShreddedMatcher(node Node) shreddedMatcher { + if node.Leaf() { + return shreddedMatcher{ + typ: node.Type(), + leafCount: 1, + } + } + + // Check if this is a group with (value, typed_value) pairs for each field + fields := node.Fields() + matchers := make([]shreddedFieldMatcher, 0, len(fields)) + for _, f := range fields { + subFields := f.Fields() + if len(subFields) == 2 { + // This is a (value, typed_value) pair for a named field + valueNode := fieldByName(f, "value") + typedNode := fieldByName(f, "typed_value") + if valueNode != nil && typedNode != nil { + matchers = append(matchers, shreddedFieldMatcher{ + name: f.Name(), + matcher: buildShreddedMatcher(Required(typedNode)), + }) + } + } + } + + if len(matchers) > 0 { + return shreddedMatcher{ + fields: matchers, + leafCount: countMatcherLeaves(matchers), + } + } + + // Unsupported typed_value structure (e.g., LIST). Return a matcher + // that never shreds so values always go to the value column. + // Still compute leaf count so column offsets are correct. + return shreddedMatcher{ + leafCount: numLeafColumnsOf(node), + } +} + +func (m *shreddedMatcher) canShred(v any) bool { + if v == nil { + return false + } + if m.isPrimitive() { + return canShredPrimitive(v, m) + } + if m.isGroup() { + return canShredObject(v, m.fields) + } + return false +} + +func canShredPrimitive(v any, m *shreddedMatcher) bool { + switch m.kind() { + case ByteArray: + if m.isString() { + switch v.(type) { + case string: + return true + } + } else { + switch v.(type) { + case []byte: + return true + } + } + case Int32: + switch v.(type) { + case int8, int16, int32, uint8, uint16: + return true + } + case Int64: + switch v.(type) { + case int8, int16, int32, int64, int, uint8, uint16, uint32: + return true + } + case Float: + switch v.(type) { + case float32: + return true + } + case Double: + switch v.(type) { + case float32, float64: + return true + } + case Boolean: + switch v.(type) { + case bool: + return true + } + case FixedLenByteArray: + if m.isUUID() { + switch v.(type) { + case uuid.UUID, [16]byte: + return true + } + } else { + switch v.(type) { + case [16]byte: + return true + } + } + } + return false +} + +func canShredObject(v any, fields []shreddedFieldMatcher) bool { + m, ok := v.(map[string]any) + if !ok { + return false + } + // An object can be shredded if ALL its fields exist in the shredded schema + for key := range m { + found := false + for _, f := range fields { + if f.name == key { + found = true + break + } + } + if !found { + return false + } + } + return true +} + +func (m *shreddedMatcher) shred(columns [][]Value, levels columnLevels, startColumn uint16, v any) { + if m.isPrimitive() { + col := startColumn + pv := goValueToParquetValue(v, m.kind()) + pv.repetitionLevel = levels.repetitionLevel + pv.definitionLevel = levels.definitionLevel + pv.columnIndex = ^col + columns[col] = append(columns[col], pv) + return + } + if m.isGroup() { + m.shredObject(columns, levels, startColumn, v) + } +} + +func (m *shreddedMatcher) shredObject(columns [][]Value, levels columnLevels, startColumn uint16, v any) { + obj, ok := v.(map[string]any) + if !ok { + // Should not happen if canShred returned true + return + } + + col := startColumn + for _, f := range m.fields { + // Within each field's sub-group, fields are sorted alphabetically: + // typed_value comes before value. + typedValueCol := col + typedCount := f.matcher.leafCount + valueCol := col + typedCount + nextFieldCol := valueCol + 1 + + fieldVal, exists := obj[f.name] + if exists && fieldVal != nil && f.matcher.canShred(fieldVal) { + // Write null to field's value column + columns[valueCol] = append(columns[valueCol], Value{ + repetitionLevel: levels.repetitionLevel, + definitionLevel: levels.definitionLevel, + columnIndex: ^valueCol, + }) + // Write typed value (increment def for optional typed_value) + typedLevels := levels + typedLevels.definitionLevel++ + f.matcher.shred(columns, typedLevels, typedValueCol, fieldVal) + } else if exists && fieldVal != nil { + // Value doesn't match typed schema: write variant to field's value. + // Encode metadata+value together so nested objects can be decoded later. + valueLevels := levels + valueLevels.definitionLevel++ + metadata, val, err := variant.Marshal(fieldVal) + if err != nil { + panic(fmt.Sprintf("variant marshal field %q: %v", f.name, err)) + } + combined := encodeFieldVariant(metadata, val) + valValue := makeValueByteArray(ByteArray, unsafe.SliceData(combined), len(combined)) + valValue.repetitionLevel = valueLevels.repetitionLevel + valValue.definitionLevel = valueLevels.definitionLevel + valValue.columnIndex = ^valueCol + columns[valueCol] = append(columns[valueCol], valValue) + // Write null to typed_value columns + writeNullLeaves(columns, typedValueCol, typedValueCol+typedCount, levels) + } else { + // Field not present or null: write null to both + writeNullLeaves(columns, typedValueCol, nextFieldCol, levels) + } + + col = nextFieldCol + } +} + +// encodeFieldVariant encodes metadata and value bytes together for per-field +// fallback storage. Format: 4-byte LE metadata length + metadata + value. +// This ensures nested objects retain their metadata dictionary for decoding. +func encodeFieldVariant(metadata, value []byte) []byte { + mLen := len(metadata) + buf := make([]byte, 4+mLen+len(value)) + binary.LittleEndian.PutUint32(buf, uint32(mLen)) + copy(buf[4:], metadata) + copy(buf[4+mLen:], value) + return buf +} + +// decodeFieldVariant splits per-field variant bytes back into metadata and value. +func decodeFieldVariant(data []byte) (metadata, value []byte, ok bool) { + if len(data) < 4 { + return nil, data, false + } + mLen := int(binary.LittleEndian.Uint32(data)) + if 4+mLen > len(data) { + return nil, data, false + } + return data[4 : 4+mLen], data[4+mLen:], true +} + +// countMatcherColumns returns the total number of leaf columns for a matcher, +// including the value column for each field in a group. +func countMatcherColumns(m *shreddedMatcher) uint16 { + if m.isGroup() { + return countMatcherLeaves(m.fields) + } + return m.leafCount +} + +// countMatcherLeaves sums the leaf columns for a list of field matchers. +// Each field contributes its typed_value leaf count plus one value column. +func countMatcherLeaves(fields []shreddedFieldMatcher) uint16 { + var count uint16 + for _, f := range fields { + count += f.matcher.leafCount + 1 // typed_value leaves + value column + } + return count +} + +func goValueToParquetValue(v any, kind Kind) Value { + switch kind { + case Boolean: + return BooleanValue(v.(bool)) + case Int32: + switch val := v.(type) { + case int8: + return Int32Value(int32(val)) + case int16: + return Int32Value(int32(val)) + case int32: + return Int32Value(val) + case uint8: + return Int32Value(int32(val)) + case uint16: + return Int32Value(int32(val)) + default: + return Int32Value(0) + } + case Int64: + switch val := v.(type) { + case int8: + return Int64Value(int64(val)) + case int16: + return Int64Value(int64(val)) + case int32: + return Int64Value(int64(val)) + case int64: + return Int64Value(val) + case int: + return Int64Value(int64(val)) + case uint8: + return Int64Value(int64(val)) + case uint16: + return Int64Value(int64(val)) + case uint32: + return Int64Value(int64(val)) + default: + return Int64Value(0) + } + case Float: + return FloatValue(v.(float32)) + case Double: + switch val := v.(type) { + case float32: + return DoubleValue(float64(val)) + case float64: + return DoubleValue(val) + default: + return DoubleValue(0) + } + case ByteArray: + switch val := v.(type) { + case string: + return ByteArrayValue([]byte(val)) + case []byte: + return ByteArrayValue(val) + default: + return ByteArrayValue(nil) + } + case FixedLenByteArray: + switch val := v.(type) { + case uuid.UUID: + b := [16]byte(val) + return FixedLenByteArrayValue(b[:]) + case [16]byte: + return FixedLenByteArrayValue(val[:]) + default: + return FixedLenByteArrayValue(nil) + } + default: + return Value{} + } +} + +// reconstructFuncOfVariant handles reconstruction of variant columns back into +// Go values. Dispatches to unshredded or shredded variant handling. +// +//go:noinline +func reconstructFuncOfVariant(columnIndex uint16, node Node) (uint16, reconstructFunc) { + if isUnshreddedVariant(node) { + return reconstructFuncOfUnshreddedVariant(columnIndex, node) + } + return reconstructFuncOfShreddedVariant(columnIndex, node) +} + +// reconstructFuncOfUnshreddedVariant handles the simple 2-column variant. +// +//go:noinline +func reconstructFuncOfUnshreddedVariant(columnIndex uint16, node Node) (uint16, reconstructFunc) { + nextColumnIndex := columnIndex + 2 + + return nextColumnIndex, func(value reflect.Value, levels columnLevels, columns [][]Value) error { + if len(columns) < 2 { + return fmt.Errorf("variant reconstruction: expected 2 columns, got %d", len(columns)) + } + + metaColumn := columns[0] + valColumn := columns[1] + + if len(metaColumn) == 0 || len(valColumn) == 0 { + return fmt.Errorf("variant reconstruction: no values in columns for column %d", columnIndex) + } + + metaVal := metaColumn[0] + valVal := valColumn[0] + + if metaVal.IsNull() || valVal.IsNull() { + value.Set(reflect.Zero(value.Type())) + return nil + } + + metaBytes := metaVal.ByteArray() + valBytes := valVal.ByteArray() + + // If the target is a raw variant struct (Metadata/Value []byte fields), + // copy the raw bytes directly without decoding. + if setRawVariantStruct(value, metaBytes, valBytes) { + return nil + } + + // Handle empty value bytes gracefully. This can happen when reading + // data that was written with a shredded schema using an unshredded + // schema — the typed_value columns are dropped by the conversion and + // the value column becomes a zero-length byte array. + if len(valBytes) == 0 { + value.Set(reflect.Zero(value.Type())) + return nil + } + + goVal, err := variant.Unmarshal(metaBytes, valBytes) + if err != nil { + return fmt.Errorf("variant unmarshal: %w", err) + } + + return setVariantGoValue(value, goVal) + } +} + +// reconstructFuncOfShreddedVariant handles the 3-field shredded variant. +// +//go:noinline +func reconstructFuncOfShreddedVariant(columnIndex uint16, node Node) (uint16, reconstructFunc) { + // Fields are sorted alphabetically: metadata, typed_value, value + var metadataOffset, valueOffset, typedValueOffset uint16 + var typedValueLeafCount uint16 + var typedValueNode Node + + col := uint16(0) + for _, f := range node.Fields() { + n := numLeafColumnsOf(f) + switch f.Name() { + case "metadata": + metadataOffset = col + col += n + case "typed_value": + typedValueOffset = col + typedValueNode = f + typedValueLeafCount = n + col += n + case "value": + valueOffset = col + col += n + } + } + totalCols := int(col) + nextColumnIndex := columnIndex + col + + typedInner := Required(typedValueNode) + extractor := buildShreddedExtractor(typedInner) + + return nextColumnIndex, func(value reflect.Value, levels columnLevels, columns [][]Value) error { + if len(columns) < totalCols { + return fmt.Errorf("shredded variant reconstruction: expected %d columns, got %d", totalCols, len(columns)) + } + + metaCol := columns[metadataOffset] + valCol := columns[valueOffset] + typedColumns := columns[typedValueOffset : typedValueOffset+typedValueLeafCount] + + if len(metaCol) == 0 { + return fmt.Errorf("variant reconstruction: no values in metadata column %d", columnIndex) + } + + metaVal := metaCol[0] + hasValue := len(valCol) > 0 && !valCol[0].IsNull() + hasTypedValue := hasNonNullInColumns(typedColumns) + + if metaVal.IsNull() && !hasValue && !hasTypedValue { + // All null: missing/null + value.Set(reflect.Zero(value.Type())) + return nil + } + + if hasValue && !hasTypedValue { + // Unshredded value + valBytes := valCol[0].ByteArray() + if len(valBytes) == 0 { + value.Set(reflect.Zero(value.Type())) + return nil + } + metaBytes := metaVal.ByteArray() + if setRawVariantStruct(value, metaBytes, valBytes) { + return nil + } + goVal, err := variant.Unmarshal(metaBytes, valBytes) + if err != nil { + return fmt.Errorf("variant unmarshal: %w", err) + } + return setVariantGoValue(value, goVal) + } + + if !hasValue && hasTypedValue { + // Shredded value: extract from typed columns + goVal := extractor.extract(typedColumns) + return setVariantGoValue(value, goVal) + } + + if hasValue && hasTypedValue { + // Partially shredded: typed_value has some fields, value has the rest + typedVal := extractor.extract(typedColumns) + valBytes := valCol[0].ByteArray() + metaBytes := metaVal.ByteArray() + unshreddedVal, err := variant.Unmarshal(metaBytes, valBytes) + if err != nil { + return fmt.Errorf("variant unmarshal: %w", err) + } + merged := mergeVariantObjects(typedVal, unshreddedVal) + return setVariantGoValue(value, merged) + } + + value.Set(reflect.Zero(value.Type())) + return nil + } +} + +func hasNonNullInColumns(columns [][]Value) bool { + for _, col := range columns { + if len(col) > 0 && !col[0].IsNull() { + return true + } + } + return false +} + +// setRawVariantStruct checks if the target value is a struct with Metadata and +// Value []byte fields. If so, it copies the raw bytes directly and returns true. +func setRawVariantStruct(value reflect.Value, metadata, val []byte) bool { + v := value + for v.Kind() == reflect.Ptr { + if v.IsNil() { + v.Set(reflect.New(v.Type().Elem())) + } + v = v.Elem() + } + if v.Kind() != reflect.Struct { + return false + } + t := v.Type() + byteSliceType := reflect.TypeOf([]byte(nil)) + metaField, hasMetadata := t.FieldByName("Metadata") + valField, hasValue := t.FieldByName("Value") + if !hasMetadata || !hasValue || metaField.Type != byteSliceType || valField.Type != byteSliceType { + return false + } + metaCopy := make([]byte, len(metadata)) + copy(metaCopy, metadata) + valCopy := make([]byte, len(val)) + copy(valCopy, val) + v.FieldByName("Metadata").SetBytes(metaCopy) + v.FieldByName("Value").SetBytes(valCopy) + return true +} + +func setVariantGoValue(value reflect.Value, goVal any) error { + if goVal == nil { + value.Set(reflect.Zero(value.Type())) + return nil + } + goValue := reflect.ValueOf(goVal) + if value.Kind() == reflect.Interface { + value.Set(goValue) + } else if goValue.Type().AssignableTo(value.Type()) { + value.Set(goValue) + } else if goValue.Type().ConvertibleTo(value.Type()) { + value.Set(goValue.Convert(value.Type())) + } else { + return fmt.Errorf("variant: cannot assign %T to %s", goVal, value.Type()) + } + return nil +} + +// shreddedExtractor reconstructs Go values from typed_value columns. +// Same three-kind structure as shreddedMatcher: primitive (typ set), +// group (fields set), or unsupported (neither). +type shreddedExtractor struct { + typ Type // non-nil for primitive (leaf column) + fields []shreddedFieldExtractor // non-nil for group (object) + leafCount int +} + +func (e *shreddedExtractor) isPrimitive() bool { return e.typ != nil } +func (e *shreddedExtractor) isGroup() bool { return len(e.fields) > 0 } +func (e *shreddedExtractor) kind() Kind { return e.typ.Kind() } +func (e *shreddedExtractor) isString() bool { + lt := e.typ.LogicalType() + return lt != nil && lt.UTF8 != nil +} +func (e *shreddedExtractor) isUUID() bool { + lt := e.typ.LogicalType() + return lt != nil && lt.UUID != nil +} + +type shreddedFieldExtractor struct { + name string + extractor shreddedExtractor +} + +func buildShreddedExtractor(node Node) shreddedExtractor { + if node.Leaf() { + return shreddedExtractor{ + typ: node.Type(), + leafCount: 1, + } + } + + fields := node.Fields() + extractors := make([]shreddedFieldExtractor, 0, len(fields)) + for _, f := range fields { + subFields := f.Fields() + if len(subFields) == 2 { + typedNode := fieldByName(f, "typed_value") + if typedNode != nil { + extractors = append(extractors, shreddedFieldExtractor{ + name: f.Name(), + extractor: buildShreddedExtractor(Required(typedNode)), + }) + } + } + } + + if len(extractors) > 0 { + leaves := 0 + for _, f := range extractors { + leaves += f.extractor.leafCount + 1 // typed_value leaves + value column + } + return shreddedExtractor{ + fields: extractors, + leafCount: leaves, + } + } + + // Unsupported typed_value structure (e.g., LIST). Return an extractor + // that always returns nil so values are read from the value column. + return shreddedExtractor{ + leafCount: int(numLeafColumnsOf(node)), + } +} + +func (e *shreddedExtractor) extract(columns [][]Value) any { + if e.isPrimitive() { + if len(columns) == 0 || len(columns[0]) == 0 || columns[0][0].IsNull() { + return nil + } + return parquetValueToGo(columns[0][0], e) + } + if e.isGroup() { + return e.extractObject(columns) + } + return nil +} + +func (e *shreddedExtractor) extractObject(columns [][]Value) any { + result := make(map[string]any) + col := 0 + for _, f := range e.fields { + // Within each field's sub-group, fields are sorted alphabetically: + // typed_value comes before value. + typedStart := col + typedCount := f.extractor.leafCount + valueCol := col + typedCount + nextFieldCol := valueCol + 1 + + hasValue := valueCol < len(columns) && len(columns[valueCol]) > 0 && !columns[valueCol][0].IsNull() + hasTyped := hasNonNullInRange(columns, typedStart, typedStart+typedCount) + + if hasTyped { + val := f.extractor.extract(columns[typedStart : typedStart+typedCount]) + if val != nil { + result[f.name] = val + } + } else if hasValue { + // Field stored as unshredded variant with metadata+value encoded together. + data := columns[valueCol][0].ByteArray() + metaBytes, valBytes, ok := decodeFieldVariant(data) + if ok { + goVal, err := variant.Unmarshal(metaBytes, valBytes) + if err == nil && goVal != nil { + result[f.name] = goVal + } + } + } + + col = nextFieldCol + } + + if len(result) == 0 { + return nil + } + return result +} + +func hasNonNullInRange(columns [][]Value, start, end int) bool { + for i := start; i < end && i < len(columns); i++ { + if len(columns[i]) > 0 && !columns[i][0].IsNull() { + return true + } + } + return false +} + +func parquetValueToGo(v Value, e *shreddedExtractor) any { + switch e.kind() { + case Boolean: + return v.Boolean() + case Int32: + return v.Int32() + case Int64: + return v.Int64() + case Float: + return v.Float() + case Double: + return v.Double() + case ByteArray: + b := v.ByteArray() + if e.isString() { + return string(b) + } + dst := make([]byte, len(b)) + copy(dst, b) + return dst + case FixedLenByteArray: + b := v.ByteArray() + if e.isUUID() && len(b) == 16 { + return uuid.UUID(b) + } + if len(b) == 16 { + var u [16]byte + copy(u[:], b) + return u + } + dst := make([]byte, len(b)) + copy(dst, b) + return dst + default: + return nil + } +} + +// mergeVariantObjects merges two values, preferring typed (shredded) values +// over unshredded ones. Used for partially shredded objects. +func mergeVariantObjects(typed, unshredded any) any { + typedMap, tOk := typed.(map[string]any) + unshreddedMap, uOk := unshredded.(map[string]any) + if tOk && uOk { + result := make(map[string]any, len(typedMap)+len(unshreddedMap)) + maps.Copy(result, unshreddedMap) + maps.Copy(result, typedMap) + return result + } + if tOk { + return typed + } + return unshredded +} diff --git a/vendor/github.com/parquet-go/parquet-go/scan.go b/vendor/github.com/parquet-go/parquet-go/scan.go new file mode 100644 index 000000000..abc287e70 --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/scan.go @@ -0,0 +1,33 @@ +package parquet + +import "io" + +// ScanRowReader constructs a RowReader which exposes rows from reader until +// the predicate returns false for one of the rows, or EOF is reached. +func ScanRowReader(reader RowReader, predicate func(Row, int64) bool) RowReader { + return &scanRowReader{reader: reader, predicate: predicate} +} + +type scanRowReader struct { + reader RowReader + predicate func(Row, int64) bool + rowIndex int64 +} + +func (s *scanRowReader) ReadRows(rows []Row) (int, error) { + if s.rowIndex < 0 { + return 0, io.EOF + } + + n, err := s.reader.ReadRows(rows) + + for i, row := range rows[:n] { + if !s.predicate(row, s.rowIndex) { + s.rowIndex = -1 + return i, io.EOF + } + s.rowIndex++ + } + + return n, err +} diff --git a/vendor/github.com/parquet-go/parquet-go/schema.go b/vendor/github.com/parquet-go/parquet-go/schema.go new file mode 100644 index 000000000..c4d8a8add --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/schema.go @@ -0,0 +1,1396 @@ +package parquet + +import ( + "encoding/json" + "fmt" + "hash/maphash" + "maps" + "math" + "reflect" + "slices" + "strconv" + "strings" + "sync" + "sync/atomic" + "time" + + "github.com/google/uuid" + "github.com/parquet-go/parquet-go/compress" + "github.com/parquet-go/parquet-go/deprecated" + "github.com/parquet-go/parquet-go/encoding" + "github.com/parquet-go/parquet-go/format" + "github.com/parquet-go/parquet-go/internal/memory" +) + +// Schema represents a parquet schema created from a Go value. +// +// Schema implements the Node interface to represent the root node of a parquet +// schema. +// +// Schema values are safe to use concurrently from multiple goroutines but must +// be passed by referenced after being created because their internal state +// contains synchronization primitives that are not safe to copy. +type Schema struct { + name string + root Node + funcs onceValue[schemaFuncs] + state onceValue[schemaState] + cache onceValue[schemaCache] +} + +type schemaFuncs struct { + deconstruct deconstructFunc + reconstruct reconstructFunc +} + +type schemaState struct { + mapping columnMapping + columns [][]string +} + +type schemaCache struct { + hashSeed maphash.Seed + writeValue cacheMap[uint64, writeValueFunc] +} + +type cacheMap[K comparable, V any] struct { + value atomic.Value // map[K]V +} + +func (c *cacheMap[K, V]) load(k K, f func() V) V { + oldMap, _ := c.value.Load().(map[K]V) + value, ok := oldMap[k] + if ok { + return value + } + value = f() + newMap := make(map[K]V, len(oldMap)+1) + maps.Copy(newMap, oldMap) + newMap[k] = value + c.value.Store(newMap) + return value +} + +type onceValue[T any] struct { + once sync.Once + value *T +} + +func (v *onceValue[T]) load(f func() *T) *T { + v.once.Do(func() { v.value = f() }) + return v.value +} + +// SchemaOf constructs a parquet schema from a Go value. +// +// The function can construct parquet schemas from struct or pointer-to-struct +// values only. A panic is raised if a Go value of a different type is passed +// to this function. +// +// When creating a parquet Schema from a Go value, the struct fields may contain +// a "parquet" tag to describe properties of the parquet node. The "parquet" tag +// follows the conventional format of Go struct tags: a comma-separated list of +// values describe the options, with the first one defining the name of the +// parquet column. +// +// The following options are also supported in the "parquet" struct tag: +// +// optional | make the parquet column optional (any type) +// snappy | sets the parquet column compression codec to snappy (any type) +// gzip | sets the parquet column compression codec to gzip (any type) +// brotli | sets the parquet column compression codec to brotli (any type) +// lz4 | sets the parquet column compression codec to lz4 (any type) +// zstd | sets the parquet column compression codec to zstd (any type) +// uncompressed | explicitly sets no compression (any type) +// plain | enables the plain encoding (no-op default, any type) +// dict | enables dictionary encoding on the parquet column (any leaf type) +// delta | enables delta encoding: DeltaBinaryPacked for int32, int64, int, uint, uint32, uint64, time.Time; DeltaByteArray for string, []byte, [N]byte +// list | for slice types, use the parquet LIST logical type +// enum | for string types, use the parquet ENUM logical type +// bytes | for string types, use no parquet logical type +// string | for []byte types, use the parquet STRING logical type +// json | for string, []byte, and map types, use the parquet JSON logical type +// uuid | for string and [16]byte types, use the parquet UUID logical type +// decimal | for int32, int64, []byte, and [N]byte types, use the parquet DECIMAL logical type +// date | for int32, time.Time, and *time.Time types, use the DATE logical type +// time | for int32, int64, time.Duration, and *time.Duration types, use the TIME logical type +// timestamp | for int64, time.Time, and *time.Time types, use the TIMESTAMP logical type (default millisecond precision) +// split | for float32 and float64 types, use the BYTE_STREAM_SPLIT encoding +// geometry | for []byte types, use the GEOMETRY logical type; use geometry(crs) to set the CRS +// geography | for []byte types, use the GEOGRAPHY logical type; use geography(crs:algorithm) to set the CRS and edge algorithm +// int(n) | for integer types, use the parquet INT logical type with the given bit width (8, 16, 32, or 64) +// uint(n) | for integer types, use the parquet UINT logical type with the given bit width (8, 16, 32, or 64) +// id(n) | where n is int denoting a column field id. Example id(2) for a column with field id of 2 +// +// When "optional" is used on a bare slice (without the "list" tag), it applies to the +// repeated elements, not the slice itself. When combined with the "list" tag, "optional" +// applies to the list as a whole; use the parquet-element tag to make list elements +// optional (e.g. parquet-element:",optional"). +// +// # The date logical type is an int32 value of the number of days since the unix epoch +// +// The time and timestamp precision can be changed by defining which precision to use +// as an argument. Supported precisions are: nanosecond, millisecond and microsecond. +// Note that for the time tag, int32 only supports millisecond precision, while int64 +// supports microsecond and nanosecond precision. Example: +// +// type Message struct { +// TimestampMicros int64 `parquet:"timestamp_micros,timestamp(microsecond)" +// } +// +// Both the time and timestamp tags accept an optional second parameter +// to set the `isAdjustedToUTC` annotation of the parquet logical type. +// Valid values are "utc" or "local". If not specified, the default value +// for this annotation will be "utc", which will set the `isAdjustedToUTC` annotation +// value to true. Example: +// +// type Message struct { +// TimestampMicrosAdjusted int64 `parquet:"timestamp_micros_adjusted,timestamp(microsecond:utc)" +// TimestampMicrosNotAdjusted int64 `parquet:"timestamp_micros_not_adjusted,timestamp(microsecond:local)" +// } +// +// The decimal tag must be followed by two integer parameters, the first integer +// representing the scale and the second the precision; for example: +// +// type Item struct { +// Cost int64 `parquet:"cost,decimal(0:3)"` +// } +// +// Invalid combination of struct tags and Go types, or repeating options will +// cause the function to panic. +// +// As a special case, if the field tag is "-", the field is omitted from the schema +// and the data will not be written into the parquet file(s). +// Note that a field with name "-" can still be generated using the tag "-,". +// +// The configuration of Parquet maps are done via two tags: +// - The `parquet-key` tag allows to configure the key of a map. +// - The `parquet-value` tag allows users to configure a map's values, for example to declare their native Parquet types. +// +// When configuring a Parquet map, the `parquet` tag will configure the map itself. +// +// For example, the following will set the int64 key of the map to be a timestamp: +// +// type Actions struct { +// Action map[int64]string `parquet:"," parquet-key:",timestamp"` +// } +// +// To configure the element of a list, use the `parquet-element` tag. For example, the following will +// set the id of the element field to 2: +// +// type Item struct { +// Attributes []string `parquet:",id(1),list" parquet-element:",id(2)"` +// } +// +// Note that the name of the element cannot be changed. +// +// The schema name is the Go type name of the value. +func SchemaOf(model any, opts ...SchemaOption) *Schema { + cfg := SchemaConfig{} + for _, opt := range opts { + opt.ConfigureSchema(&cfg) + } + return schemaOf(dereference(reflect.TypeOf(model)), cfg.StructTags...) +} + +var cachedSchemas sync.Map // map[reflect.Type]*Schema + +func schemaOf(model reflect.Type, tagReplacements ...StructTagOption) *Schema { + cacheable := len(tagReplacements) == 0 + + if cacheable { + cached, _ := cachedSchemas.Load(model) + schema, _ := cached.(*Schema) + if schema != nil { + return schema + } + } + + if model.Kind() != reflect.Struct { + panic("cannot construct parquet schema from value of type " + model.String()) + } + + schema := NewSchema(model.Name(), nodeOf(nil, model, noTags, tagReplacements)) + + if cacheable { + if actual, loaded := cachedSchemas.LoadOrStore(model, schema); loaded { + schema = actual.(*Schema) + } + } + return schema +} + +// NewSchema constructs a new Schema object with the given name and root node. +// +// The function panics if Node contains more leaf columns than supported by the +// package (see parquet.MaxColumnIndex). +func NewSchema(name string, root Node) *Schema { + return &Schema{name: name, root: root} +} + +func dereference(t reflect.Type) reflect.Type { + for t.Kind() == reflect.Ptr { + t = t.Elem() + } + return t +} + +func makeDeconstructFunc(node Node) (deconstruct deconstructFunc) { + if schema, _ := node.(*Schema); schema != nil { + return schema.lazyLoadFuncs().deconstruct + } + if !node.Leaf() { + _, deconstruct = deconstructFuncOf(0, node) + } + return deconstruct +} + +func makeReconstructFunc(node Node) (reconstruct reconstructFunc) { + if schema, _ := node.(*Schema); schema != nil { + return schema.lazyLoadFuncs().reconstruct + } + if !node.Leaf() { + _, reconstruct = reconstructFuncOf(0, node) + } + return reconstruct +} + +func (s *Schema) lazyLoadFuncs() *schemaFuncs { + return s.funcs.load(func() *schemaFuncs { + return &schemaFuncs{ + deconstruct: makeDeconstructFunc(s.root), + reconstruct: makeReconstructFunc(s.root), + } + }) +} + +func (s *Schema) lazyLoadState() *schemaState { + return s.state.load(func() *schemaState { + mapping, columns := columnMappingOf(s.root) + return &schemaState{ + mapping: mapping, + columns: columns, + } + }) +} + +func (s *Schema) lazyLoadCache() *schemaCache { + return s.cache.load(func() *schemaCache { + return &schemaCache{ + hashSeed: maphash.MakeSeed(), + } + }) +} + +// ConfigureRowGroup satisfies the RowGroupOption interface, allowing Schema +// instances to be passed to row group constructors to pre-declare the schema of +// the output parquet file. +func (s *Schema) ConfigureRowGroup(config *RowGroupConfig) { config.Schema = s } + +// ConfigureReader satisfies the ReaderOption interface, allowing Schema +// instances to be passed to NewReader to pre-declare the schema of rows +// read from the reader. +func (s *Schema) ConfigureReader(config *ReaderConfig) { config.Schema = s } + +// ConfigureWriter satisfies the WriterOption interface, allowing Schema +// instances to be passed to NewWriter to pre-declare the schema of the +// output parquet file. +func (s *Schema) ConfigureWriter(config *WriterConfig) { config.Schema = s } + +// ID returns field id of the root node. +func (s *Schema) ID() int { return s.root.ID() } + +// String returns a parquet schema representation of s. +func (s *Schema) String() string { return sprint(s.name, s.root) } + +// Name returns the name of s. +func (s *Schema) Name() string { return s.name } + +// Type returns the parquet type of s. +func (s *Schema) Type() Type { return s.root.Type() } + +// Optional returns false since the root node of a parquet schema is always required. +func (s *Schema) Optional() bool { return s.root.Optional() } + +// Repeated returns false since the root node of a parquet schema is always required. +func (s *Schema) Repeated() bool { return s.root.Repeated() } + +// Required returns true since the root node of a parquet schema is always required. +func (s *Schema) Required() bool { return s.root.Required() } + +// Leaf returns true if the root node of the parquet schema is a leaf column. +func (s *Schema) Leaf() bool { return s.root.Leaf() } + +// Fields returns the list of fields on the root node of the parquet schema. +func (s *Schema) Fields() []Field { return s.root.Fields() } + +// Encoding returns the encoding set on the root node of the parquet schema. +func (s *Schema) Encoding() encoding.Encoding { return s.root.Encoding() } + +// Compression returns the compression codec set on the root node of the parquet +// schema. +func (s *Schema) Compression() compress.Codec { return s.root.Compression() } + +// GoType returns the Go type that best represents the schema. +func (s *Schema) GoType() reflect.Type { return s.root.GoType() } + +// Deconstruct deconstructs a Go value and appends it to a row. +// +// The method panics is the structure of the go value does not match the +// parquet schema. +func (s *Schema) Deconstruct(row Row, value any) Row { + state := s.lazyLoadState() + funcs := s.lazyLoadFuncs() + columns := make([][]Value, len(state.columns)) + values := make([]Value, len(state.columns)) + + for i := range columns { + columns[i] = values[i : i : i+1] + } + + v := reflect.ValueOf(value) + for v.Kind() == reflect.Ptr || v.Kind() == reflect.Interface { + if v.IsNil() { + v = reflect.Value{} + break + } + v = v.Elem() + } + funcs.deconstruct(columns, columnLevels{}, v) + return appendRow(row, columns) +} + +// Reconstruct reconstructs a Go value from a row. +// +// The go value passed as first argument must be a non-nil pointer for the +// row to be decoded into. +// +// The method panics if the structure of the go value and parquet row do not +// match. +func (s *Schema) Reconstruct(value any, row Row) error { + v := reflect.ValueOf(value) + if !v.IsValid() { + panic("cannot reconstruct row into go value of type ") + } + if v.Kind() != reflect.Ptr { + panic("cannot reconstruct row into go value of non-pointer type " + v.Type().String()) + } + if v.IsNil() { + panic("cannot reconstruct row into nil pointer of type " + v.Type().String()) + } + for v.Kind() == reflect.Ptr { + if v.IsNil() { + v.Set(reflect.New(v.Type().Elem())) + } + v = v.Elem() + } + + b := valuesSliceBufferPool.Get( + func() *valuesSliceBuffer { + return &valuesSliceBuffer{ + values: make([][]Value, 0, 64), + } + }, + func(v *valuesSliceBuffer) { v.values = v.values[:0] }, + ) + + state := s.lazyLoadState() + funcs := s.lazyLoadFuncs() + columns := b.reserve(len(state.columns)) + row.Range(func(columnIndex int, columnValues []Value) bool { + if columnIndex < len(columns) { + columns[columnIndex] = columnValues + } + return true + }) + // we avoid the defer penalty by releasing b manually + err := funcs.reconstruct(v, columnLevels{}, columns) + b.release() + return err +} + +type valuesSliceBuffer struct { + values [][]Value +} + +func (v *valuesSliceBuffer) reserve(n int) [][]Value { + if n <= cap(v.values) { + return v.values[:n] + } + // we can try to keep growing by the power of two, but we care more about the + // memory footprint so this should suffice. + // + // The nature of reads tends to be from similar number of columns.The less work + // we do here the better performance we can get. + v.values = make([][]Value, n) + return v.values +} + +func (v *valuesSliceBuffer) release() { + valuesSliceBufferPool.Put(v) +} + +var valuesSliceBufferPool memory.Pool[valuesSliceBuffer] + +// Lookup returns the leaf column at the given path. +// +// The path is the sequence of column names identifying a leaf column (not +// including the root). +// +// If the path was not found in the mapping, or if it did not represent a +// leaf column of the parquet schema, the boolean will be false. +func (s *Schema) Lookup(path ...string) (LeafColumn, bool) { + leaf := s.lazyLoadState().mapping.lookup(path) + return LeafColumn{ + Node: leaf.node, + Path: leaf.path, + ColumnIndex: int(leaf.columnIndex), + MaxRepetitionLevel: int(leaf.maxRepetitionLevel), + MaxDefinitionLevel: int(leaf.maxDefinitionLevel), + }, leaf.node != nil +} + +// Columns returns the list of column paths available in the schema. +// +// The method always returns the same slice value across calls to ColumnPaths, +// applications should treat it as immutable. +func (s *Schema) Columns() [][]string { return s.lazyLoadState().columns } + +// Comparator constructs a comparator function which orders rows according to +// the list of sorting columns passed as arguments. +func (s *Schema) Comparator(sortingColumns ...SortingColumn) func(Row, Row) int { + return compareRowsFuncOf(s, sortingColumns) +} + +func (s *Schema) forEachNode(do func(name string, node Node)) { + forEachNodeOf(s.Name(), s, do) +} + +type structNode struct { + gotype reflect.Type + fields []structField +} + +func structNodeOf(path []string, t reflect.Type, tagReplacements []StructTagOption) *structNode { + // Collect struct fields first so we can order them before generating the + // column indexes. + fields := structFieldsOf(path, t, tagReplacements) + + s := &structNode{ + gotype: t, + fields: make([]structField, len(fields)), + } + + for i := range fields { + field := structField{name: fields[i].Name, index: fields[i].Index} + tags := fromStructTag(fields[i].Tag) + field.Node = makeNodeOf(append(path, fields[i].Name), fields[i].Type, fields[i].Name, tags, tagReplacements) + + s.fields[i] = field + } + + return s +} + +// structFieldsOf returns the list of fields for the given path and type. Struct tags are replaced +// and fields potentially renamed using the provided options. +func structFieldsOf(path []string, t reflect.Type, tagReplacements []StructTagOption) []reflect.StructField { + return appendStructFields(path, t, nil, nil, 0, tagReplacements) +} + +func appendStructFields(path []string, t reflect.Type, fields []reflect.StructField, index []int, offset uintptr, tagReplacements []StructTagOption) []reflect.StructField { + for i, n := 0, t.NumField(); i < n; i++ { + f := t.Field(i) + + // Tag replacements if present. + // Embedded anonymous fields do not extend the + // column path and tags are not used. + if !f.Anonymous { + fpath := append(path, f.Name) + for _, opt := range tagReplacements { + if slices.Equal(fpath, opt.ColumnPath) { + f.Tag = opt.StructTag + } + } + } + + ftags := fromStructTag(f.Tag) + + parquetNameSet := false + if tag := ftags.parquet; tag != "" { + name, _ := split(tag) + if tag != "-," && name == "-" { + continue + } + if name != "" { + f.Name = name + parquetNameSet = true + } + } + + // If no explicit parquet name was set, check for protobuf tag name. + // This allows protobuf-generated structs to use their proto field names + // (typically snake_case) as parquet column names. + if !parquetNameSet { + if protoName := protoFieldNameFromTag(f.Tag); protoName != "" { + f.Name = protoName + } + } + + fieldIndex := index[:len(index):len(index)] + fieldIndex = append(fieldIndex, i) + + f.Offset += offset + + if f.Anonymous { + fields = appendStructFields(path, f.Type, fields, fieldIndex, f.Offset, tagReplacements) + } else if f.IsExported() { + f.Index = fieldIndex + fields = append(fields, f) + } + } + return fields +} + +func (s *structNode) Optional() bool { return false } + +func (s *structNode) Repeated() bool { return false } + +func (s *structNode) Required() bool { return true } + +func (s *structNode) Leaf() bool { return false } + +func (s *structNode) Encoding() encoding.Encoding { return nil } + +func (s *structNode) Compression() compress.Codec { return nil } + +func (s *structNode) GoType() reflect.Type { return s.gotype } + +func (s *structNode) ID() int { return 0 } + +func (s *structNode) String() string { return sprint("", s) } + +func (s *structNode) Type() Type { return groupType{} } + +func (s *structNode) Fields() []Field { + fields := make([]Field, len(s.fields)) + for i := range s.fields { + fields[i] = &s.fields[i] + } + return fields +} + +// fieldByIndex is like reflect.Value.FieldByIndex but returns the zero-value of +// reflect.Value if one of the fields was a nil pointer instead of panicking. +func fieldByIndex(v reflect.Value, index []int) reflect.Value { + for _, i := range index { + if v = v.Field(i); v.Kind() == reflect.Ptr || v.Kind() == reflect.Interface { + if v.IsNil() { + v.Set(reflect.New(v.Type().Elem())) + v = v.Elem() + break + } else { + v = v.Elem() + } + } + } + return v +} + +type structField struct { + Node + name string + index []int +} + +func (f *structField) Name() string { return f.name } + +func (f *structField) Value(base reflect.Value) reflect.Value { + switch base.Kind() { + case reflect.Map: + return base.MapIndex(reflect.ValueOf(&f.name).Elem()) + case reflect.Ptr: + if base.IsNil() { + base.Set(reflect.New(base.Type().Elem())) + } + return fieldByIndex(base.Elem(), f.index) + default: + if len(f.index) == 1 { + return base.Field(f.index[0]) + } else { + return fieldByIndex(base, f.index) + } + } +} + +func nodeString(t reflect.Type, name string, tag ...string) string { + return fmt.Sprintf("%s %s %v", name, t.String(), tag) +} + +func throwInvalidTag(t reflect.Type, name string, tag string) { + panic(tag + " is an invalid parquet tag: " + nodeString(t, name, tag)) +} + +func throwUnknownTag(t reflect.Type, name string, tag string) { + panic(tag + " is an unrecognized parquet tag: " + nodeString(t, name, tag)) +} + +func throwInvalidNode(t reflect.Type, msg, name string, parquetTags parquetTags) { + tags := make([]string, 0, 3) // A node can have at most 3 tags. + if parquetTags.parquet != "" { + tags = append(tags, parquetTags.parquet) + } + if parquetTags.parquetKey != "" { + tags = append(tags, parquetTags.parquetKey) + } + if parquetTags.parquetValue != "" { + tags = append(tags, parquetTags.parquetValue) + } + if parquetTags.parquetElement != "" { + tags = append(tags, parquetTags.parquetElement) + } + panic(msg + ": " + nodeString(t, name, tags...)) +} + +// FixedLenByteArray decimals are sized based on precision +// this function calculates the necessary byte array size. +func decimalFixedLenByteArraySize(precision int) int { + return int(math.Ceil((math.Log10(2) + float64(precision)) / math.Log10(256))) +} + +func forEachStructTagOption(sf reflect.StructField, do func(t reflect.Type, option, args string)) { + if tag := fromStructTag(sf.Tag).parquet; tag != "" { + _, tag = split(tag) // skip the field name + for tag != "" { + option := "" + args := "" + option, tag = split(tag) + option, args = splitOptionArgs(option) + ft := sf.Type + if ft.Kind() == reflect.Ptr { + ft = ft.Elem() + } + do(ft, option, args) + } + } +} + +func nodeOf(path []string, t reflect.Type, tags parquetTags, tagReplacements []StructTagOption) Node { + switch t { + case reflect.TypeFor[deprecated.Int96](): + return Leaf(Int96Type) + case reflect.TypeFor[uuid.UUID](): + return UUID() + case reflect.TypeFor[time.Time](): + return Timestamp(Nanosecond) + } + + var n Node + switch t.Kind() { + case reflect.Bool: + n = Leaf(BooleanType) + + case reflect.Int, reflect.Int64: + n = Int(64) + + case reflect.Int8, reflect.Int16, reflect.Int32: + n = Int(t.Bits()) + + case reflect.Uint, reflect.Uintptr, reflect.Uint64: + n = Uint(64) + + case reflect.Uint8, reflect.Uint16, reflect.Uint32: + n = Uint(t.Bits()) + + case reflect.Float32: + n = Leaf(FloatType) + + case reflect.Float64: + n = Leaf(DoubleType) + + case reflect.String: + n = String() + + case reflect.Ptr: + n = Optional(nodeOf(path, t.Elem(), noTags, tagReplacements)) + + case reflect.Slice: + if elem := t.Elem(); elem.Kind() == reflect.Uint8 { // []byte? + n = Leaf(ByteArrayType) + } else { + n = Repeated(nodeOf(path, elem, noTags, tagReplacements)) + } + + case reflect.Array: + if t.Elem().Kind() == reflect.Uint8 { + n = Leaf(FixedLenByteArrayType(t.Len())) + } + + case reflect.Map: + mapTag := tags.parquet + if strings.Contains(mapTag, "json") { + n = JSON() + } else { + n = Map( + makeNodeOf(append(path, "key_value", "key"), t.Key(), t.Name(), tags.getMapKeyNodeTags(), tagReplacements), + makeNodeOf(append(path, "key_value", "value"), t.Elem(), t.Name(), tags.getMapValueNodeTags(), tagReplacements), + ) + } + + forEachTagOption([]string{mapTag}, func(option, args string) { + switch option { + case "", "json": + return + case "optional": + n = Optional(n) + case "id": + id, err := parseIDArgs(args) + if err != nil { + throwInvalidTag(t, "map", option) + } + n = FieldID(n, id) + default: + throwUnknownTag(t, "map", option) + } + }) + + case reflect.Struct: + return structNodeOf(path, t, tagReplacements) + + case reflect.Interface: + return (Group)(nil) + } + + if n == nil { + panic("cannot create parquet node from go value of type " + t.String()) + } + + return &goNode{Node: n, gotype: t} +} + +func split(s string) (head, tail string) { + if i := strings.IndexByte(s, ','); i < 0 { + head = s + } else { + head, tail = s[:i], s[i+1:] + } + return +} + +func splitOptionArgs(s string) (option, args string) { + if i := strings.IndexByte(s, '('); i >= 0 { + option = s[:i] + args = s[i:] + } else { + option = s + args = "()" + } + return +} + +func parseDecimalArgs(args string) (scale, precision int, err error) { + if !strings.HasPrefix(args, "(") || !strings.HasSuffix(args, ")") { + return 0, 0, fmt.Errorf("malformed decimal args: %s", args) + } + args = strings.TrimPrefix(args, "(") + args = strings.TrimSuffix(args, ")") + parts := strings.Split(args, ":") + if len(parts) != 2 { + return 0, 0, fmt.Errorf("malformed decimal args: (%s)", args) + } + s, err := strconv.ParseInt(parts[0], 10, 32) + if err != nil { + return 0, 0, err + } + p, err := strconv.ParseInt(parts[1], 10, 32) + if err != nil { + return 0, 0, err + } + return int(s), int(p), nil +} + +func parseIDArgs(args string) (int, error) { + if !strings.HasPrefix(args, "(") || !strings.HasSuffix(args, ")") { + return 0, fmt.Errorf("malformed id args: %s", args) + } + args = strings.TrimPrefix(args, "(") + args = strings.TrimSuffix(args, ")") + return strconv.Atoi(args) +} + +func parseTimestampArgs(args string) (unit TimeUnit, isUTCNormalized bool, err error) { + if !strings.HasPrefix(args, "(") || !strings.HasSuffix(args, ")") { + return nil, false, fmt.Errorf("malformed timestamp args: %s", args) + } + + args = strings.TrimPrefix(args, "(") + args = strings.TrimSuffix(args, ")") + + if len(args) == 0 { + return Millisecond, true, nil + } + + parts := strings.Split(args, ":") + if len(parts) > 2 { + return nil, false, fmt.Errorf("malformed timestamp args: (%s)", args) + } + + unit, err = parseTimeUnit(parts[0]) + if err != nil { + return nil, false, err + } + + adjusted := true + if len(parts) > 1 { + adjusted, err = parseUTCNormalization(parts[1]) + if err != nil { + return nil, false, err + } + } + + return unit, adjusted, nil +} + +func parseTimeUnit(arg string) (TimeUnit, error) { + switch arg { + case "millisecond": + return Millisecond, nil + case "microsecond": + return Microsecond, nil + case "nanosecond": + return Nanosecond, nil + default: + } + + return nil, fmt.Errorf("unknown time unit: %s", arg) +} + +func parseUTCNormalization(arg string) (isUTCNormalized bool, err error) { + switch arg { + case "utc": + return true, nil + case "local": + return false, nil + default: + return false, fmt.Errorf("unknown utc normalization: %s", arg) + } +} + +func parseGeometryArgs(args string) (crs string, err error) { + if !strings.HasPrefix(args, "(") || !strings.HasSuffix(args, ")") { + return "", fmt.Errorf("malformed geometry args: %s", args) + } + args = strings.TrimPrefix(args, "(") + args = strings.TrimSuffix(args, ")") + return args, nil +} + +func parseGeographyArgs(args string) (crs string, alg format.EdgeInterpolationAlgorithm, err error) { + if !strings.HasPrefix(args, "(") || !strings.HasSuffix(args, ")") { + return "", 0, fmt.Errorf("malformed geography args: %s", args) + } + args = strings.TrimPrefix(args, "(") + args = strings.TrimSuffix(args, ")") + + // geography has up to two arguments: the CRS and and the edge interpolation + // algorithm. + parts := strings.Split(args, ":") + + switch len(parts) { + case 1: + crs = parts[0] + return crs, alg, nil + case 2: + crs = parts[0] + err = alg.FromString(parts[1]) + if err != nil { + return "", 0, err + } + return crs, alg, nil + case 3: + // CRS very likely contains a colon, so we join all parts except the last one. + crs = strings.Join(parts[:2], ":") + default: + return "", 0, fmt.Errorf("malformed geography args: (%s)", args) + } + + return crs, alg, nil +} + +type goNode struct { + Node + gotype reflect.Type +} + +func (n *goNode) GoType() reflect.Type { return n.gotype } + +var ( + _ RowGroupOption = (*Schema)(nil) + _ ReaderOption = (*Schema)(nil) + _ WriterOption = (*Schema)(nil) +) + +func makeNodeOf(path []string, t reflect.Type, name string, tags parquetTags, tagReplacements []StructTagOption) Node { + var ( + node Node + optional bool + list bool + encoded encoding.Encoding + compressed compress.Codec + fieldID int + ) + + setNode := func(n Node) { + if node != nil { + throwInvalidNode(t, "struct field has multiple logical parquet types declared", name, tags) + } + node = n + } + + setOptional := func() { + if optional { + throwInvalidNode(t, "struct field has multiple declaration of the optional tag", name, tags) + } + optional = true + } + + setList := func() { + if list { + throwInvalidNode(t, "struct field has multiple declaration of the list tag", name, tags) + } + list = true + } + + setEncoding := func(e encoding.Encoding) { + if encoded != nil { + throwInvalidNode(t, "struct field has encoding declared multiple time", name, tags) + } + encoded = e + } + + setCompression := func(c compress.Codec) { + if compressed != nil { + throwInvalidNode(t, "struct field has compression codecs declared multiple times", name, tags) + } + compressed = c + } + + if t.Kind() == reflect.Map { + node = nodeOf(path, t, tags, tagReplacements) + } else { + forEachTagOption([]string{tags.parquet}, func(option, args string) { + switch option { + case "": + return + case "optional": + setOptional() + + case "snappy": + setCompression(&Snappy) + + case "gzip": + setCompression(&Gzip) + + case "brotli": + setCompression(&Brotli) + + case "lz4": + setCompression(&Lz4Raw) + + case "zstd": + setCompression(&Zstd) + + case "uncompressed": + setCompression(&Uncompressed) + + case "plain": + setEncoding(&Plain) + + case "dict": + setEncoding(&RLEDictionary) + + case "json": + setNode(JSON()) + + case "variant": + setNode(Variant()) + + case "delta": + switch t.Kind() { + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, + reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: + setEncoding(&DeltaBinaryPacked) + case reflect.String: + setEncoding(&DeltaByteArray) + case reflect.Slice: + if t.Elem().Kind() == reflect.Uint8 { // []byte? + setEncoding(&DeltaByteArray) + } else { + throwInvalidTag(t, name, option) + } + case reflect.Array: + if t.Elem().Kind() == reflect.Uint8 { // [N]byte? + setEncoding(&DeltaByteArray) + } else { + throwInvalidTag(t, name, option) + } + default: + switch t { + case reflect.TypeFor[time.Time](): + setEncoding(&DeltaBinaryPacked) + default: + throwInvalidTag(t, name, option) + } + } + + case "split": + kind := t.Kind() + baseType := t + if kind == reflect.Ptr { + kind = t.Elem().Kind() + baseType = t.Elem() + } + switch kind { + case reflect.Float32, reflect.Float64, reflect.Int32, reflect.Int64: + setEncoding(&ByteStreamSplit) + case reflect.Array: + if baseType.Elem().Kind() == reflect.Uint8 { + setEncoding(&ByteStreamSplit) + } else { + throwInvalidTag(t, name, option) + } + default: + throwInvalidTag(t, name, option) + } + + case "list": + switch t.Kind() { + case reflect.Slice: + if t == reflect.TypeFor[json.RawMessage]() { + throwInvalidTag(t, name, option) + } + element := makeNodeOf(append(path, "list", "element"), t.Elem(), t.Name(), tags.getListElementNodeTags(), tagReplacements) + setNode(element) + setList() + default: + throwInvalidTag(t, name, option) + } + + case "enum": + switch t.Kind() { + case reflect.String: + setNode(Enum()) + default: + throwInvalidTag(t, name, option) + } + + case "uuid": + switch t.Kind() { + case reflect.Array: + if t.Elem().Kind() != reflect.Uint8 || t.Len() != 16 { + throwInvalidTag(t, name, option) + } + setNode(UUID()) + case reflect.String: + setNode(UUID()) + default: + throwInvalidTag(t, name, option) + } + + case "decimal": + scale, precision, err := parseDecimalArgs(args) + if err != nil { + throwInvalidTag(t, name, option+args) + } + decimalBaseType := func(et reflect.Type) Type { + switch et.Kind() { + case reflect.Int32: + return Int32Type + case reflect.Int64: + return Int64Type + case reflect.Array, reflect.Slice: + return FixedLenByteArrayType(decimalFixedLenByteArraySize(precision)) + } + return nil + } + if t.Kind() == reflect.Ptr { + baseType := decimalBaseType(t.Elem()) + if baseType == nil { + throwInvalidTag(t, name, option) + } + setNode(Optional(Decimal(scale, precision, baseType))) + } else { + baseType := decimalBaseType(t) + if baseType == nil { + throwInvalidTag(t, name, option) + } + setNode(Decimal(scale, precision, baseType)) + } + + case "string": + switch { + case t.Kind() == reflect.String: + case t.Kind() == reflect.Slice && t.Elem().Kind() == reflect.Uint8: + default: + throwInvalidTag(t, name, option) + } + setNode(String()) + + case "bytes": + switch { + case t.Kind() == reflect.String: + case t.Kind() == reflect.Slice && t.Elem().Kind() == reflect.Uint8: + default: + throwInvalidTag(t, name, option) + } + setNode(Leaf(ByteArrayType)) + + case "date": + switch t.Kind() { + case reflect.Int32: + setNode(Date()) + case reflect.Ptr: + elem := t.Elem() + switch { + case elem == reflect.TypeFor[time.Time](): + setNode(Optional(Date())) + case elem.Kind() == reflect.Int32: + setNode(Optional(Date())) + default: + throwInvalidTag(t, name, option) + } + default: + switch t { + case reflect.TypeFor[time.Time](): + setNode(Date()) + default: + throwInvalidTag(t, name, option) + } + } + + case "time": + // Handle time.Duration specially - it can use any time unit + // (millisecond, microsecond, or nanosecond) and TimeAdjusted() + // will automatically select the correct physical type + if t == reflect.TypeFor[time.Duration]() { + timeUnit, adjusted, err := parseTimestampArgs(args) + if err != nil { + throwInvalidTag(t, name, option+args) + } + // If no args provided, default to nanosecond for time.Duration + if args == "()" { + timeUnit = Nanosecond + adjusted = true + } + setNode(TimeAdjusted(timeUnit, adjusted)) + } else { + switch t.Kind() { + case reflect.Int32: + timeUnit, adjusted, err := parseTimestampArgs(args) + if err != nil || timeUnit.Duration() < time.Millisecond { + throwInvalidTag(t, name, option+args) + } + setNode(TimeAdjusted(timeUnit, adjusted)) + case reflect.Int64: + timeUnit, adjusted, err := parseTimestampArgs(args) + if err != nil || timeUnit.Duration() == time.Millisecond { + throwInvalidTag(t, name, option+args) + } + setNode(TimeAdjusted(timeUnit, adjusted)) + case reflect.Ptr: + // Support *time.Duration with time tag + if t.Elem() == reflect.TypeFor[time.Duration]() { + timeUnit, adjusted, err := parseTimestampArgs(args) + if err != nil { + throwInvalidTag(t, name, option+args) + } + if args == "()" { + timeUnit = Nanosecond + adjusted = true + } + setNode(Optional(TimeAdjusted(timeUnit, adjusted))) + } else { + throwInvalidTag(t, name, option) + } + default: + throwInvalidTag(t, name, option) + } + } + + case "timestamp": + switch t.Kind() { + case reflect.Int64: + timeUnit, adjusted, err := parseTimestampArgs(args) + if err != nil { + throwInvalidTag(t, name, option+args) + } + setNode(TimestampAdjusted(timeUnit, adjusted)) + case reflect.Ptr: + elem := t.Elem() + switch { + case elem == reflect.TypeFor[time.Time](): + timeUnit, adjusted, err := parseTimestampArgs(args) + if err != nil { + throwInvalidTag(t, name, option+args) + } + setNode(Optional(TimestampAdjusted(timeUnit, adjusted))) + case elem.Kind() == reflect.Int64: + timeUnit, adjusted, err := parseTimestampArgs(args) + if err != nil { + throwInvalidTag(t, name, option+args) + } + setNode(Optional(TimestampAdjusted(timeUnit, adjusted))) + default: + throwInvalidTag(t, name, option) + } + default: + switch t { + case reflect.TypeFor[time.Time](): + timeUnit, adjusted, err := parseTimestampArgs(args) + if err != nil { + throwInvalidTag(t, name, option+args) + } + setNode(TimestampAdjusted(timeUnit, adjusted)) + default: + throwInvalidTag(t, name, option) + } + } + + case "interval": + switch { + case t == reflect.TypeOf(Interval{}): + setNode(IntervalNode()) + case t.Kind() == reflect.Array && t.Elem().Kind() == reflect.Uint8 && t.Len() == 12: + setNode(IntervalNode()) + default: + throwInvalidTag(t, name, option) + } + + case "int": + bitWidth, err := parseIntBitWidthArgs(args) + if err != nil { + throwInvalidTag(t, name, option+args) + } + kind := t.Kind() + if kind == reflect.Ptr { + kind = t.Elem().Kind() + } + switch kind { + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, + reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + if t.Kind() == reflect.Ptr { + setNode(Optional(Int(bitWidth))) + } else { + setNode(Int(bitWidth)) + } + default: + throwInvalidTag(t, name, option) + } + + case "uint": + bitWidth, err := parseIntBitWidthArgs(args) + if err != nil { + throwInvalidTag(t, name, option+args) + } + kind := t.Kind() + if kind == reflect.Ptr { + kind = t.Elem().Kind() + } + switch kind { + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, + reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + if t.Kind() == reflect.Ptr { + setNode(Optional(Uint(bitWidth))) + } else { + setNode(Uint(bitWidth)) + } + default: + throwInvalidTag(t, name, option) + } + + case "id": + id, err := parseIDArgs(args) + if err != nil { + throwInvalidNode(t, "struct field has field id that is not a valid int", name, tags) + } + fieldID = id + + case "geometry": + crs, err := parseGeometryArgs(args) + if err != nil { + throwInvalidTag(t, name, option+args) + } + setNode(Geometry(crs)) + case "geography": + crs, alg, err := parseGeographyArgs(args) + if err != nil { + throwInvalidTag(t, name, option+args) + } + setNode(Geography(crs, alg)) + } + }) + } + + // Special case: an "optional" struct tag on a slice applies to the + // individual items, not the overall list. The least messy way to + // deal with this is at this level, instead of passing down optional + // information into the nodeOf function, and then passing back whether an + // optional tag was applied. + if node == nil && t.Kind() == reflect.Slice { + isUint8 := t.Elem().Kind() == reflect.Uint8 + // Note for strings "optional" applies only to the entire BYTE_ARRAY and + // not each individual byte. + if optional && !isUint8 { + node = Repeated(Optional(nodeOf(path, t.Elem(), tags, tagReplacements))) + // Don't also apply "optional" to the whole list. + optional = false + } + } + + if node == nil { + node = nodeOf(path, t, tags, tagReplacements) + } + + if compressed != nil { + node = Compressed(node, compressed) + } + + if encoded != nil { + node = Encoded(node, encoded) + } + + if list { + node = List(node) + } + + if node.Repeated() && !list { + repeated := node.GoType().Elem() + if repeated.Kind() == reflect.Slice { + // Special case: allow [][]uint8 as seen in a logical map of strings + if repeated.Elem().Kind() != reflect.Uint8 { + panic("unhandled nested slice on parquet schema without list tag") + } + } + } + + if optional { + node = Optional(node) + } + if fieldID != 0 { + node = FieldID(node, fieldID) + } + return node +} + +func forEachTagOption(tags []string, do func(option, args string)) { + for _, tag := range tags { + _, tag = split(tag) // skip the field name + for tag != "" { + option := "" + option, tag = split(tag) + var args string + option, args = splitOptionArgs(option) + do(option, args) + } + } +} diff --git a/vendor/github.com/parquet-go/parquet-go/schema_int.go b/vendor/github.com/parquet-go/parquet-go/schema_int.go new file mode 100644 index 000000000..6adb5f09f --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/schema_int.go @@ -0,0 +1,25 @@ +package parquet + +import ( + "fmt" + "strconv" + "strings" +) + +func parseIntBitWidthArgs(args string) (int, error) { + if !strings.HasPrefix(args, "(") || !strings.HasSuffix(args, ")") { + return 0, fmt.Errorf("malformed int bit width args: %s", args) + } + args = strings.TrimPrefix(args, "(") + args = strings.TrimSuffix(args, ")") + bitWidth, err := strconv.Atoi(args) + if err != nil { + return 0, err + } + switch bitWidth { + case 8, 16, 32, 64: + return bitWidth, nil + default: + return 0, fmt.Errorf("invalid integer bit width: %d (must be 8, 16, 32, or 64)", bitWidth) + } +} diff --git a/vendor/github.com/parquet-go/parquet-go/search.go b/vendor/github.com/parquet-go/parquet-go/search.go new file mode 100644 index 000000000..49c5eb22a --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/search.go @@ -0,0 +1,95 @@ +package parquet + +// Search is like Find, but uses the default ordering of the given type. Search +// and Find are scoped to a given ColumnChunk and find the pages within a +// ColumnChunk which might contain the result. See Find for more details. +func Search(index ColumnIndex, value Value, typ Type) int { + return Find(index, value, CompareNullsLast(typ.Compare)) +} + +// Find uses the ColumnIndex passed as argument to find the page in a column +// chunk (determined by the given ColumnIndex) that the given value is expected +// to be found in. +// +// The function returns the index of the first page that might contain the +// value. If the function determines that the value does not exist in the +// index, NumPages is returned. +// +// If you want to search the entire parquet file, you must iterate over the +// RowGroups and search each one individually, if there are multiple in the +// file. If you call writer.Flush before closing the file, then you will have +// multiple RowGroups to iterate over, otherwise Flush is called once on Close. +// +// The comparison function passed as last argument is used to determine the +// relative order of values. This should generally be the Compare method of +// the column type, but can sometimes be customized to modify how null values +// are interpreted, for example: +// +// pageIndex := parquet.Find(columnIndex, value, +// parquet.CompareNullsFirst(typ.Compare), +// ) +func Find(index ColumnIndex, value Value, cmp func(Value, Value) int) int { + switch { + case index.IsAscending(): + return binarySearch(index, value, cmp) + default: + return linearSearch(index, value, cmp) + } +} + +func binarySearch(index ColumnIndex, value Value, cmp func(Value, Value) int) int { + numPages := index.NumPages() + topIndex := numPages + currentIndex := 0 + + // while there's at least one more page to check + for currentIndex < topIndex { + // nextIndex is set to halfway between currentIndex and topIndex + nextIndex := ((topIndex - currentIndex) / 2) + currentIndex + + // Compare against both min and max to handle overlapping page bounds. + // When page bounds overlap due to truncation, we need to search left + // to find the first page that might contain the value. + switch { + case cmp(value, index.MinValue(nextIndex)) < 0: + // value < min: can't be in this page or any after it + topIndex = nextIndex + case cmp(value, index.MaxValue(nextIndex)) > 0: + // value > max: can't be in this page or any before it (including nextIndex) + currentIndex = nextIndex + 1 + default: + // min <= value <= max: value might be in this page or an earlier one + // with overlapping bounds, so search left to find the first occurrence + topIndex = nextIndex + } + } + + // After the loop, currentIndex == topIndex points to the candidate page. + // Verify the value is actually within the page bounds. + if currentIndex < numPages { + minValue := index.MinValue(currentIndex) + maxValue := index.MaxValue(currentIndex) + + // If value is not in pages[currentIndex], then it's not in this columnChunk + if cmp(value, minValue) < 0 || cmp(value, maxValue) > 0 { + return numPages + } + } + + return currentIndex +} + +func linearSearch(index ColumnIndex, value Value, cmp func(Value, Value) int) int { + n := index.NumPages() + + for i := range n { + min := index.MinValue(i) + max := index.MaxValue(i) + + if cmp(min, value) <= 0 && cmp(value, max) <= 0 { + return i + } + } + + return n +} diff --git a/vendor/github.com/parquet-go/parquet-go/sorting.go b/vendor/github.com/parquet-go/parquet-go/sorting.go new file mode 100644 index 000000000..cdb5974cc --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/sorting.go @@ -0,0 +1,274 @@ +package parquet + +import ( + "io" + "slices" + "sort" +) + +// SortingWriter is a type similar to GenericWriter but it ensures that rows +// are sorted according to the sorting columns configured on the writer. +// +// The writer accumulates rows in an in-memory buffer which is sorted when it +// reaches the target number of rows, then written to a temporary row group. +// When the writer is flushed or closed, the temporary row groups are merged +// into a row group in the output file, ensuring that rows remain sorted in the +// final row group. +// +// Because row groups get encoded and compressed, they hold a lot less memory +// than if all rows were retained in memory. Sorting then merging rows chunks +// also tends to be a lot more efficient than sorting all rows in memory as it +// results in better CPU cache utilization since sorting multi-megabyte arrays +// causes a lot of cache misses since the data set cannot be held in CPU caches. +type SortingWriter[T any] struct { + rowbuf *RowBuffer[T] + writer *GenericWriter[T] + output *GenericWriter[T] + buffer io.ReadWriteSeeker + maxRows int64 + numRows int64 + sorting SortingConfig + dedupe dedupe +} + +// NewSortingWriter constructs a new sorting writer which writes a parquet file +// where rows of each row group are ordered according to the sorting columns +// configured on the writer. +// +// The sortRowCount argument defines the target number of rows that will be +// sorted in memory before being written to temporary row groups. The greater +// this value the more memory is needed to buffer rows in memory. Choosing a +// value that is too small limits the maximum number of rows that can exist in +// the output file since the writer cannot create more than 32K temporary row +// groups to hold the sorted row chunks. +func NewSortingWriter[T any](output io.Writer, sortRowCount int64, options ...WriterOption) *SortingWriter[T] { + config, err := NewWriterConfig(options...) + if err != nil { + panic(err) + } + return &SortingWriter[T]{ + rowbuf: NewRowBuffer[T](&RowGroupConfig{ + Schema: config.Schema, + Sorting: config.Sorting, + }), + writer: NewGenericWriter[T](io.Discard, &WriterConfig{ + CreatedBy: config.CreatedBy, + ColumnPageBuffers: config.ColumnPageBuffers, + ColumnIndexSizeLimit: config.ColumnIndexSizeLimit, + PageBufferSize: config.PageBufferSize, + WriteBufferSize: config.WriteBufferSize, + DataPageVersion: config.DataPageVersion, + Schema: config.Schema, + Compression: config.Compression, + Sorting: config.Sorting, + Encodings: config.Encodings, + }), + output: NewGenericWriter[T](output, config), + maxRows: sortRowCount, + sorting: config.Sorting, + } +} + +func (w *SortingWriter[T]) Close() error { + if err := w.Flush(); err != nil { + return err + } + + defer w.resetSortingBuffer() + + if w.numRows == 0 { + return w.output.Close() + } + + if err := w.writer.Close(); err != nil { + return err + } + + size, err := w.buffer.Seek(0, io.SeekCurrent) + if err != nil { + return err + } + + f, err := OpenFile(newReaderAt(w.buffer), size, + &FileConfig{ + SkipPageIndex: true, + SkipBloomFilters: true, + ReadBufferSize: defaultReadBufferSize, + }, + ) + if err != nil { + return err + } + + m, err := MergeRowGroups(f.RowGroups(), + &RowGroupConfig{ + Schema: w.Schema(), + Sorting: w.sorting, + }, + ) + if err != nil { + return err + } + + rows := m.Rows() + defer rows.Close() + + reader := RowReader(rows) + if w.sorting.DropDuplicatedRows { + reader = DedupeRowReader(rows, w.rowbuf.compare) + } + + if _, err := CopyRows(w.output, reader); err != nil { + return err + } + + if err := w.output.Flush(); err != nil { + return err + } + + return w.output.Close() +} + +// Flush sorts any buffered rows and writes them to temporary storage. +// This can be called multiple times to manage memory usage. +// The actual merge and write to output happens on Close. +func (w *SortingWriter[T]) Flush() error { + return w.sortAndWriteBufferedRows() +} + +func (w *SortingWriter[T]) Reset(output io.Writer) { + w.output.Reset(output) + w.rowbuf.Reset() + w.resetSortingBuffer() +} + +func (w *SortingWriter[T]) resetSortingBuffer() { + w.writer.Reset(io.Discard) + w.numRows = 0 + + if w.buffer != nil { + w.sorting.SortingBuffers.PutBuffer(w.buffer) + w.buffer = nil + } +} + +func (w *SortingWriter[T]) Write(rows []T) (int, error) { + return w.writeRows(len(rows), func(i, j int) (int, error) { return w.rowbuf.Write(rows[i:j]) }) +} + +func (w *SortingWriter[T]) WriteRows(rows []Row) (int, error) { + return w.writeRows(len(rows), func(i, j int) (int, error) { return w.rowbuf.WriteRows(rows[i:j]) }) +} + +func (w *SortingWriter[T]) writeRows(numRows int, writeRows func(i, j int) (int, error)) (int, error) { + wn := 0 + + for wn < numRows { + if w.rowbuf.NumRows() >= w.maxRows { + if err := w.sortAndWriteBufferedRows(); err != nil { + return wn, err + } + } + + n := int(w.maxRows - w.rowbuf.NumRows()) + n += wn + if n > numRows { + n = numRows + } + + n, err := writeRows(wn, n) + wn += n + + if err != nil { + return wn, err + } + } + + return wn, nil +} + +func (w *SortingWriter[T]) SetKeyValueMetadata(key, value string) { + w.output.SetKeyValueMetadata(key, value) +} + +func (w *SortingWriter[T]) Schema() *Schema { + return w.output.Schema() +} + +func (w *SortingWriter[T]) sortAndWriteBufferedRows() error { + if w.rowbuf.Len() == 0 { + return nil + } + + defer w.rowbuf.Reset() + sort.Sort(w.rowbuf) + + if w.sorting.DropDuplicatedRows { + w.rowbuf.rows = w.rowbuf.rows[:w.dedupe.deduplicate(w.rowbuf.rows, w.rowbuf.compare)] + defer w.dedupe.reset() + } + + rows := w.rowbuf.Rows() + defer rows.Close() + + if w.buffer == nil { + w.buffer = w.sorting.SortingBuffers.GetBuffer() + w.writer.Reset(w.buffer) + } + + n, err := CopyRows(w.writer, rows) + if err != nil { + return err + } + + if err := w.writer.Flush(); err != nil { + return err + } + + w.numRows += n + return nil +} + +// File returns a FileView of the written parquet file. +// Only available after Close is called. +func (w *SortingWriter[T]) File() FileView { + return w.output.File() +} + +// EqualSortingColumns compares two slices of sorting columns for equality. +// +// Two sorting column slices are considered equal if they have the same length +// and each corresponding pair of sorting columns is equal. Two sorting columns +// are equal if they have: +// - The same column path (including nested field paths) +// - The same sort direction (ascending or descending) +// - The same nulls handling (nulls first or nulls last) +// +// The comparison is order-sensitive, meaning that [A, B] is not equal to [B, A]. +// Both nil and empty slices are considered equal. +// +// This function is useful for: +// - Validating that merged row groups maintain expected sorting +// - Comparing sorting configurations between different row groups +// - Testing sorting column propagation in merge operations +// +// Example: +// +// cols1 := []SortingColumn{Ascending("name"), Descending("age")} +// cols2 := []SortingColumn{Ascending("name"), Descending("age")} +// equal := EqualSortingColumns(cols1, cols2) // returns true +// +// cols3 := []SortingColumn{Descending("age"), Ascending("name")} +// equal = EqualSortingColumns(cols1, cols3) // returns false (different order) +// +// cols4 := []SortingColumn{Ascending("name"), Ascending("age")} +// equal = EqualSortingColumns(cols1, cols4) // returns false (different direction) +func EqualSortingColumns(a, b []SortingColumn) bool { + return len(a) == len(b) && slices.EqualFunc(a, b, equalSortingColumn) +} + +// equalSortingColumn compares two individual sorting columns for equality. +// Two sorting columns are equal if they have the same path, direction, and nulls handling. +func equalSortingColumn(a, b SortingColumn) bool { + return slices.Equal(a.Path(), b.Path()) && a.Descending() == b.Descending() && a.NullsFirst() == b.NullsFirst() +} diff --git a/vendor/github.com/parquet-go/parquet-go/sparse/array.go b/vendor/github.com/parquet-go/parquet-go/sparse/array.go new file mode 100644 index 000000000..fecfb4dc4 --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/sparse/array.go @@ -0,0 +1,320 @@ +package sparse + +import ( + "time" + "unsafe" +) + +type Array struct{ array } + +func UnsafeArray(base unsafe.Pointer, length int, offset uintptr) Array { + return Array{unsafeArray(base, length, offset)} +} + +func (a Array) Len() int { return int(a.len) } +func (a Array) Index(i int) unsafe.Pointer { return a.index(i) } +func (a Array) Slice(i, j int) Array { return Array{a.slice(i, j)} } +func (a Array) Offset(off uintptr) Array { return Array{a.offset(off)} } +func (a Array) BoolArray() BoolArray { return BoolArray{a.array} } +func (a Array) Int8Array() Int8Array { return Int8Array{a.array} } +func (a Array) Int16Array() Int16Array { return Int16Array{a.array} } +func (a Array) Int32Array() Int32Array { return Int32Array{a.array} } +func (a Array) Int64Array() Int64Array { return Int64Array{a.array} } +func (a Array) Float32Array() Float32Array { return Float32Array{a.array} } +func (a Array) Float64Array() Float64Array { return Float64Array{a.array} } +func (a Array) Uint8Array() Uint8Array { return Uint8Array{a.array} } +func (a Array) Uint16Array() Uint16Array { return Uint16Array{a.array} } +func (a Array) Uint32Array() Uint32Array { return Uint32Array{a.array} } +func (a Array) Uint64Array() Uint64Array { return Uint64Array{a.array} } +func (a Array) Uint128Array() Uint128Array { return Uint128Array{a.array} } +func (a Array) StringArray() StringArray { return StringArray{a.array} } +func (a Array) TimeArray() TimeArray { return TimeArray{a.array} } + +type array struct { + ptr unsafe.Pointer + len uintptr + off uintptr +} + +func makeArray[T any](base []T) array { + var z T + return array{ + ptr: unsafe.Pointer(unsafe.SliceData(base)), + len: uintptr(len(base)), + off: unsafe.Sizeof(z), + } +} + +func unsafeArray(base unsafe.Pointer, length int, offset uintptr) array { + return array{ptr: base, len: uintptr(length), off: offset} +} + +func (a array) index(i int) unsafe.Pointer { + if uintptr(i) >= a.len { + panic("index out of bounds") + } + return unsafe.Add(a.ptr, a.off*uintptr(i)) +} + +func (a array) slice(i, j int) array { + if uintptr(i) > a.len || uintptr(j) > a.len || i > j { + panic("slice index out of bounds") + } + return array{ + ptr: unsafe.Add(a.ptr, a.off*uintptr(i)), + len: uintptr(j - i), + off: a.off, + } +} + +func (a array) offset(off uintptr) array { + if a.ptr == nil { + panic("offset of nil array") + } + return array{ + ptr: unsafe.Add(a.ptr, off), + len: a.len, + off: a.off, + } +} + +type BoolArray struct{ array } + +func MakeBoolArray(values []bool) BoolArray { + return BoolArray{makeArray(values)} +} + +func UnsafeBoolArray(base unsafe.Pointer, length int, offset uintptr) BoolArray { + return BoolArray{unsafeArray(base, length, offset)} +} + +func (a BoolArray) Len() int { return int(a.len) } +func (a BoolArray) Index(i int) bool { return *(*byte)(a.index(i)) != 0 } +func (a BoolArray) Slice(i, j int) BoolArray { return BoolArray{a.slice(i, j)} } +func (a BoolArray) Uint8Array() Uint8Array { return Uint8Array{a.array} } +func (a BoolArray) UnsafeArray() Array { return Array{a.array} } + +type Int8Array struct{ array } + +func MakeInt8Array(values []int8) Int8Array { + return Int8Array{makeArray(values)} +} + +func UnsafeInt8Array(base unsafe.Pointer, length int, offset uintptr) Int8Array { + return Int8Array{unsafeArray(base, length, offset)} +} + +func (a Int8Array) Len() int { return int(a.len) } +func (a Int8Array) Index(i int) int8 { return *(*int8)(a.index(i)) } +func (a Int8Array) Slice(i, j int) Int8Array { return Int8Array{a.slice(i, j)} } +func (a Int8Array) Uint8Array() Uint8Array { return Uint8Array{a.array} } +func (a Int8Array) UnsafeArray() Array { return Array{a.array} } + +type Int16Array struct{ array } + +func MakeInt16Array(values []int16) Int16Array { + return Int16Array{makeArray(values)} +} + +func UnsafeInt16Array(base unsafe.Pointer, length int, offset uintptr) Int16Array { + return Int16Array{unsafeArray(base, length, offset)} +} + +func (a Int16Array) Len() int { return int(a.len) } +func (a Int16Array) Index(i int) int16 { return *(*int16)(a.index(i)) } +func (a Int16Array) Slice(i, j int) Int16Array { return Int16Array{a.slice(i, j)} } +func (a Int16Array) Int8Array() Int8Array { return Int8Array{a.array} } +func (a Int16Array) Uint8Array() Uint8Array { return Uint8Array{a.array} } +func (a Int16Array) Uint16Array() Uint16Array { return Uint16Array{a.array} } +func (a Int16Array) UnsafeArray() Array { return Array{a.array} } + +type Int32Array struct{ array } + +func MakeInt32Array(values []int32) Int32Array { + return Int32Array{makeArray(values)} +} + +func UnsafeInt32Array(base unsafe.Pointer, length int, offset uintptr) Int32Array { + return Int32Array{unsafeArray(base, length, offset)} +} + +func (a Int32Array) Len() int { return int(a.len) } +func (a Int32Array) Index(i int) int32 { return *(*int32)(a.index(i)) } +func (a Int32Array) Slice(i, j int) Int32Array { return Int32Array{a.slice(i, j)} } +func (a Int32Array) Int8Array() Int8Array { return Int8Array{a.array} } +func (a Int32Array) Int16Array() Int16Array { return Int16Array{a.array} } +func (a Int32Array) Uint8Array() Uint8Array { return Uint8Array{a.array} } +func (a Int32Array) Uint16Array() Uint16Array { return Uint16Array{a.array} } +func (a Int32Array) Uint32Array() Uint32Array { return Uint32Array{a.array} } +func (a Int32Array) UnsafeArray() Array { return Array{a.array} } + +type Int64Array struct{ array } + +func MakeInt64Array(values []int64) Int64Array { + return Int64Array{makeArray(values)} +} + +func UnsafeInt64Array(base unsafe.Pointer, length int, offset uintptr) Int64Array { + return Int64Array{unsafeArray(base, length, offset)} +} + +func (a Int64Array) Len() int { return int(a.len) } +func (a Int64Array) Index(i int) int64 { return *(*int64)(a.index(i)) } +func (a Int64Array) Slice(i, j int) Int64Array { return Int64Array{a.slice(i, j)} } +func (a Int64Array) Int8Array() Int8Array { return Int8Array{a.array} } +func (a Int64Array) Int16Array() Int16Array { return Int16Array{a.array} } +func (a Int64Array) Int32Array() Int32Array { return Int32Array{a.array} } +func (a Int64Array) Uint8Array() Uint8Array { return Uint8Array{a.array} } +func (a Int64Array) Uint16Array() Uint16Array { return Uint16Array{a.array} } +func (a Int64Array) Uint32Array() Uint32Array { return Uint32Array{a.array} } +func (a Int64Array) Uint64Array() Uint64Array { return Uint64Array{a.array} } +func (a Int64Array) UnsafeArray() Array { return Array{a.array} } + +type Float32Array struct{ array } + +func MakeFloat32Array(values []float32) Float32Array { + return Float32Array{makeArray(values)} +} + +func UnsafeFloat32Array(base unsafe.Pointer, length int, offset uintptr) Float32Array { + return Float32Array{unsafeArray(base, length, offset)} +} + +func (a Float32Array) Len() int { return int(a.len) } +func (a Float32Array) Index(i int) float32 { return *(*float32)(a.index(i)) } +func (a Float32Array) Slice(i, j int) Float32Array { return Float32Array{a.slice(i, j)} } +func (a Float32Array) Array() Array { return Array{a.array} } +func (a Float32Array) Uint32Array() Uint32Array { return Uint32Array{a.array} } +func (a Float32Array) UnsafeArray() Array { return Array{a.array} } + +type Float64Array struct{ array } + +func MakeFloat64Array(values []float64) Float64Array { + return Float64Array{makeArray(values)} +} + +func UnsafeFloat64Array(base unsafe.Pointer, length int, offset uintptr) Float64Array { + return Float64Array{unsafeArray(base, length, offset)} +} + +func (a Float64Array) Len() int { return int(a.len) } +func (a Float64Array) Index(i int) float64 { return *(*float64)(a.index(i)) } +func (a Float64Array) Slice(i, j int) Float64Array { return Float64Array{a.slice(i, j)} } +func (a Float64Array) Uint64Array() Uint64Array { return Uint64Array{a.array} } +func (a Float64Array) UnsafeArray() Array { return Array{a.array} } + +type Uint8Array struct{ array } + +func MakeUint8Array(values []uint8) Uint8Array { + return Uint8Array{makeArray(values)} +} + +func UnsafeUint8Array(base unsafe.Pointer, length int, offset uintptr) Uint8Array { + return Uint8Array{unsafeArray(base, length, offset)} +} + +func (a Uint8Array) Len() int { return int(a.len) } +func (a Uint8Array) Index(i int) uint8 { return *(*uint8)(a.index(i)) } +func (a Uint8Array) Slice(i, j int) Uint8Array { return Uint8Array{a.slice(i, j)} } +func (a Uint8Array) UnsafeArray() Array { return Array{a.array} } + +type Uint16Array struct{ array } + +func MakeUint16Array(values []uint16) Uint16Array { + return Uint16Array{makeArray(values)} +} + +func UnsafeUint16Array(base unsafe.Pointer, length int, offset uintptr) Uint16Array { + return Uint16Array{unsafeArray(base, length, offset)} +} + +func (a Uint16Array) Len() int { return int(a.len) } +func (a Uint16Array) Index(i int) uint16 { return *(*uint16)(a.index(i)) } +func (a Uint16Array) Slice(i, j int) Uint16Array { return Uint16Array{a.slice(i, j)} } +func (a Uint16Array) Uint8Array() Uint8Array { return Uint8Array{a.array} } +func (a Uint16Array) UnsafeArray() Array { return Array{a.array} } + +type Uint32Array struct{ array } + +func MakeUint32Array(values []uint32) Uint32Array { + return Uint32Array{makeArray(values)} +} + +func UnsafeUint32Array(base unsafe.Pointer, length int, offset uintptr) Uint32Array { + return Uint32Array{unsafeArray(base, length, offset)} +} + +func (a Uint32Array) Len() int { return int(a.len) } +func (a Uint32Array) Index(i int) uint32 { return *(*uint32)(a.index(i)) } +func (a Uint32Array) Slice(i, j int) Uint32Array { return Uint32Array{a.slice(i, j)} } +func (a Uint32Array) Uint8Array() Uint8Array { return Uint8Array{a.array} } +func (a Uint32Array) Uint16Array() Uint16Array { return Uint16Array{a.array} } +func (a Uint32Array) UnsafeArray() Array { return Array{a.array} } + +type Uint64Array struct{ array } + +func MakeUint64Array(values []uint64) Uint64Array { + return Uint64Array{makeArray(values)} +} + +func UnsafeUint64Array(base unsafe.Pointer, length int, offset uintptr) Uint64Array { + return Uint64Array{unsafeArray(base, length, offset)} +} + +func (a Uint64Array) Len() int { return int(a.len) } +func (a Uint64Array) Index(i int) uint64 { return *(*uint64)(a.index(i)) } +func (a Uint64Array) Slice(i, j int) Uint64Array { return Uint64Array{a.slice(i, j)} } +func (a Uint64Array) Uint8Array() Uint8Array { return Uint8Array{a.array} } +func (a Uint64Array) Uint16Array() Uint16Array { return Uint16Array{a.array} } +func (a Uint64Array) Uint32Array() Uint32Array { return Uint32Array{a.array} } +func (a Uint64Array) UnsafeArray() Array { return Array{a.array} } + +type Uint128Array struct{ array } + +func MakeUint128Array(values [][16]byte) Uint128Array { + return Uint128Array{makeArray(values)} +} + +func UnsafeUint128Array(base unsafe.Pointer, length int, offset uintptr) Uint128Array { + return Uint128Array{unsafeArray(base, length, offset)} +} + +func (a Uint128Array) Len() int { return int(a.len) } +func (a Uint128Array) Index(i int) [16]byte { return *(*[16]byte)(a.index(i)) } +func (a Uint128Array) Slice(i, j int) Uint128Array { return Uint128Array{a.slice(i, j)} } +func (a Uint128Array) Uint8Array() Uint8Array { return Uint8Array{a.array} } +func (a Uint128Array) Uint16Array() Uint16Array { return Uint16Array{a.array} } +func (a Uint128Array) Uint32Array() Uint32Array { return Uint32Array{a.array} } +func (a Uint128Array) Uint64Array() Uint64Array { return Uint64Array{a.array} } +func (a Uint128Array) UnsafeArray() Array { return Array{a.array} } + +type StringArray struct{ array } + +func MakeStringArray(values []string) StringArray { + const sizeOfString = unsafe.Sizeof("") + return StringArray{makeArray(values)} +} + +func UnsafeStringArray(base unsafe.Pointer, length int, offset uintptr) StringArray { + return StringArray{unsafeArray(base, length, offset)} +} + +func (a StringArray) Len() int { return int(a.len) } +func (a StringArray) Index(i int) string { return *(*string)(a.index(i)) } +func (a StringArray) Slice(i, j int) StringArray { return StringArray{a.slice(i, j)} } +func (a StringArray) UnsafeArray() Array { return Array{a.array} } + +type TimeArray struct{ array } + +func MakeTimeArray(values []time.Time) TimeArray { + return TimeArray{makeArray(values)} +} + +func UnsafeTimeArray(base unsafe.Pointer, length int, offset uintptr) TimeArray { + return TimeArray{unsafeArray(base, length, offset)} +} + +func (a TimeArray) Len() int { return int(a.len) } +func (a TimeArray) Index(i int) time.Time { return *(*time.Time)(a.index(i)) } +func (a TimeArray) Slice(i, j int) TimeArray { return TimeArray{a.slice(i, j)} } +func (a TimeArray) UnsafeArray() Array { return Array{a.array} } diff --git a/vendor/github.com/parquet-go/parquet-go/sparse/gather.go b/vendor/github.com/parquet-go/parquet-go/sparse/gather.go new file mode 100644 index 000000000..5ead58aa2 --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/sparse/gather.go @@ -0,0 +1,37 @@ +package sparse + +import "github.com/parquet-go/bitpack/unsafecast" + +func GatherInt32(dst []int32, src Int32Array) int { + return GatherUint32(unsafecast.Slice[uint32](dst), src.Uint32Array()) +} + +func GatherInt64(dst []int64, src Int64Array) int { + return GatherUint64(unsafecast.Slice[uint64](dst), src.Uint64Array()) +} + +func GatherFloat32(dst []float32, src Float32Array) int { + return GatherUint32(unsafecast.Slice[uint32](dst), src.Uint32Array()) +} + +func GatherFloat64(dst []float64, src Float64Array) int { + return GatherUint64(unsafecast.Slice[uint64](dst), src.Uint64Array()) +} + +func GatherBits(dst []byte, src Uint8Array) int { return gatherBits(dst, src) } + +func GatherUint32(dst []uint32, src Uint32Array) int { return gather32(dst, src) } + +func GatherUint64(dst []uint64, src Uint64Array) int { return gather64(dst, src) } + +func GatherUint128(dst [][16]byte, src Uint128Array) int { return gather128(dst, src) } + +func GatherString(dst []string, src StringArray) int { + n := min(len(dst), src.Len()) + + for i := range dst[:n] { + dst[i] = src.Index(i) + } + + return n +} diff --git a/vendor/github.com/parquet-go/parquet-go/sparse/gather_amd64.go b/vendor/github.com/parquet-go/parquet-go/sparse/gather_amd64.go new file mode 100644 index 000000000..427abbccb --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/sparse/gather_amd64.go @@ -0,0 +1,85 @@ +//go:build !purego + +package sparse + +import ( + "golang.org/x/sys/cpu" +) + +func gatherBits(dst []byte, src Uint8Array) int { + n := min(len(dst)*8, src.Len()) + i := 0 + + if n >= 8 { + i = (n / 8) * 8 + // Make sure `offset` is at least 4 bytes, otherwise VPGATHERDD may read + // data beyond the end of the program memory and trigger a fault. + // + // If the boolean values do not have enough padding we must fallback to + // the scalar algorithm to be able to load single bytes from memory. + if src.off >= 4 && cpu.X86.HasAVX2 { + gatherBitsAVX2(dst, src.Slice(0, i)) + } else { + gatherBitsDefault(dst, src.Slice(0, i)) + } + } + + for i < n { + x := i / 8 + y := i % 8 + b := src.Index(i) + dst[x] = ((b & 1) << y) | (dst[x] & ^(1 << y)) + i++ + } + + return n +} + +func gather32(dst []uint32, src Uint32Array) int { + n := min(len(dst), src.Len()) + i := 0 + + if n >= 16 && cpu.X86.HasAVX2 { + i = (n / 8) * 8 + gather32AVX2(dst[:i:i], src) + } + + for i < n { + dst[i] = src.Index(i) + i++ + } + + return n +} + +func gather64(dst []uint64, src Uint64Array) int { + n := min(len(dst), src.Len()) + i := 0 + + if n >= 8 && cpu.X86.HasAVX2 { + i = (n / 4) * 4 + gather64AVX2(dst[:i:i], src) + } + + for i < n { + dst[i] = src.Index(i) + i++ + } + + return n +} + +//go:noescape +func gatherBitsAVX2(dst []byte, src Uint8Array) + +//go:noescape +func gatherBitsDefault(dst []byte, src Uint8Array) + +//go:noescape +func gather32AVX2(dst []uint32, src Uint32Array) + +//go:noescape +func gather64AVX2(dst []uint64, src Uint64Array) + +//go:noescape +func gather128(dst [][16]byte, src Uint128Array) int diff --git a/vendor/github.com/parquet-go/parquet-go/sparse/gather_amd64.s b/vendor/github.com/parquet-go/parquet-go/sparse/gather_amd64.s new file mode 100644 index 000000000..e87dee82a --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/sparse/gather_amd64.s @@ -0,0 +1,193 @@ +//go:build !purego + +#include "textflag.h" + +// func gatherBitsAVX2(dst []byte, src Uint8Array) +TEXT ·gatherBitsAVX2(SB), NOSPLIT, $0-48 + MOVQ dst_base+0(FP), AX + MOVQ src_array_ptr+24(FP), BX + MOVQ src_array_len+32(FP), CX + MOVQ src_array_off+40(FP), DX + XORQ SI, SI + SHRQ $3, CX + + VPBROADCASTD src_array_off+40(FP), Y0 + VPMULLD range0n7<>(SB), Y0, Y0 + VPCMPEQD Y1, Y1, Y1 + VPCMPEQD Y2, Y2, Y2 +loop: + VPGATHERDD Y1, (BX)(Y0*1), Y3 + VMOVDQU Y2, Y1 + VPSLLD $31, Y3, Y3 + VMOVMSKPS Y3, DI + + MOVB DI, (AX)(SI*1) + + LEAQ (BX)(DX*8), BX + INCQ SI + CMPQ SI, CX + JNE loop + VZEROUPPER + RET + +// func gatherBitsDefault(dst []byte, src Uint8Array) +TEXT ·gatherBitsDefault(SB), NOSPLIT, $0-48 + MOVQ dst_base+0(FP), AX + MOVQ src_array_ptr+24(FP), BX + MOVQ src_array_len+32(FP), CX + MOVQ src_array_off+40(FP), DX + XORQ SI, SI + SHRQ $3, CX +loop: + LEAQ (BX)(DX*2), DI + MOVBQZX (BX), R8 + MOVBQZX (BX)(DX*1), R9 + MOVBQZX (DI), R10 + MOVBQZX (DI)(DX*1), R11 + LEAQ (BX)(DX*4), BX + LEAQ (DI)(DX*4), DI + MOVBQZX (BX), R12 + MOVBQZX (BX)(DX*1), R13 + MOVBQZX (DI), R14 + MOVBQZX (DI)(DX*1), R15 + LEAQ (BX)(DX*4), BX + + ANDQ $1, R8 + ANDQ $1, R9 + ANDQ $1, R10 + ANDQ $1, R11 + ANDQ $1, R12 + ANDQ $1, R13 + ANDQ $1, R14 + ANDQ $1, R15 + + SHLQ $1, R9 + SHLQ $2, R10 + SHLQ $3, R11 + SHLQ $4, R12 + SHLQ $5, R13 + SHLQ $6, R14 + SHLQ $7, R15 + + ORQ R9, R8 + ORQ R11, R10 + ORQ R13, R12 + ORQ R15, R14 + ORQ R10, R8 + ORQ R12, R8 + ORQ R14, R8 + + MOVB R8, (AX)(SI*1) + + INCQ SI + CMPQ SI, CX + JNE loop + RET + +// func gather32AVX2(dst []uint32, src Uint32Array) +TEXT ·gather32AVX2(SB), NOSPLIT, $0-48 + MOVQ dst_base+0(FP), AX + MOVQ dst_len+8(FP), CX + MOVQ src_array_ptr+24(FP), BX + MOVQ src_array_off+40(FP), DX + XORQ SI, SI + + VPBROADCASTD src_array_off+40(FP), Y0 + VPMULLD range0n7<>(SB), Y0, Y0 + VPCMPEQD Y1, Y1, Y1 + VPCMPEQD Y2, Y2, Y2 +loop: + VPGATHERDD Y1, (BX)(Y0*1), Y3 + VMOVDQU Y3, (AX)(SI*4) + VMOVDQU Y2, Y1 + + LEAQ (BX)(DX*8), BX + ADDQ $8, SI + CMPQ SI, CX + JNE loop + VZEROUPPER + RET + +// func gather64AVX2(dst []uint64, src Uint64Array) +TEXT ·gather64AVX2(SB), NOSPLIT, $0-48 + MOVQ dst_base+0(FP), AX + MOVQ dst_len+8(FP), CX + MOVQ src_array_ptr+24(FP), BX + MOVQ src_array_off+40(FP), DX + XORQ SI, SI + + VPBROADCASTQ src_array_off+40(FP), Y0 + VPMULLD range0n3<>(SB), Y0, Y0 + VPCMPEQQ Y1, Y1, Y1 + VPCMPEQQ Y2, Y2, Y2 +loop: + VPGATHERQQ Y1, (BX)(Y0*1), Y3 + VMOVDQU Y3, (AX)(SI*8) + VMOVDQU Y2, Y1 + + LEAQ (BX)(DX*4), BX + ADDQ $4, SI + CMPQ SI, CX + JNE loop + VZEROUPPER + RET + +// func gather128(dst [][16]byte, src Uint128Array) int +TEXT ·gather128(SB), NOSPLIT, $0-56 + MOVQ dst_base+0(FP), AX + MOVQ dst_len+8(FP), CX + MOVQ src_array_ptr+24(FP), BX + MOVQ src_array_len+32(FP), DI + MOVQ src_array_off+40(FP), DX + XORQ SI, SI + + CMPQ DI, CX + CMOVQLT DI, CX + + CMPQ CX, $0 + JE done + + CMPQ CX, $1 + JE tail + + XORQ SI, SI + MOVQ CX, DI + SHRQ $1, DI + SHLQ $1, DI +loop: + MOVOU (BX), X0 + MOVOU (BX)(DX*1), X1 + + MOVOU X0, (AX) + MOVOU X1, 16(AX) + + LEAQ (BX)(DX*2), BX + ADDQ $32, AX + ADDQ $2, SI + CMPQ SI, DI + JNE loop + + CMPQ SI, CX + JE done +tail: + MOVOU (BX), X0 + MOVOU X0, (AX) +done: + MOVQ CX, ret+48(FP) + RET + +GLOBL range0n3<>(SB), RODATA|NOPTR, $32 +DATA range0n3<>+0(SB)/8, $0 +DATA range0n3<>+8(SB)/8, $1 +DATA range0n3<>+16(SB)/8, $2 +DATA range0n3<>+24(SB)/8, $3 + +GLOBL range0n7<>(SB), RODATA|NOPTR, $32 +DATA range0n7<>+0(SB)/4, $0 +DATA range0n7<>+4(SB)/4, $1 +DATA range0n7<>+8(SB)/4, $2 +DATA range0n7<>+12(SB)/4, $3 +DATA range0n7<>+16(SB)/4, $4 +DATA range0n7<>+20(SB)/4, $5 +DATA range0n7<>+24(SB)/4, $6 +DATA range0n7<>+28(SB)/4, $7 diff --git a/vendor/github.com/parquet-go/parquet-go/sparse/gather_purego.go b/vendor/github.com/parquet-go/parquet-go/sparse/gather_purego.go new file mode 100644 index 000000000..2f25c4486 --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/sparse/gather_purego.go @@ -0,0 +1,72 @@ +//go:build purego || !amd64 + +package sparse + +func gatherBits(dst []byte, src Uint8Array) int { + n := min(len(dst)*8, src.Len()) + i := 0 + + if k := (n / 8) * 8; k > 0 { + for j := 0; i < k; j++ { + b0 := src.Index(i + 0) + b1 := src.Index(i + 1) + b2 := src.Index(i + 2) + b3 := src.Index(i + 3) + b4 := src.Index(i + 4) + b5 := src.Index(i + 5) + b6 := src.Index(i + 6) + b7 := src.Index(i + 7) + + dst[j] = (b0 & 1) | + ((b1 & 1) << 1) | + ((b2 & 1) << 2) | + ((b3 & 1) << 3) | + ((b4 & 1) << 4) | + ((b5 & 1) << 5) | + ((b6 & 1) << 6) | + ((b7 & 1) << 7) + + i += 8 + } + } + + for i < n { + x := i / 8 + y := i % 8 + b := src.Index(i) + dst[x] = ((b & 1) << y) | (dst[x] & ^(1 << y)) + i++ + } + + return n +} + +func gather32(dst []uint32, src Uint32Array) int { + n := min(len(dst), src.Len()) + + for i := range dst[:n] { + dst[i] = src.Index(i) + } + + return n +} + +func gather64(dst []uint64, src Uint64Array) int { + n := min(len(dst), src.Len()) + + for i := range dst[:n] { + dst[i] = src.Index(i) + } + + return n +} + +func gather128(dst [][16]byte, src Uint128Array) int { + n := min(len(dst), src.Len()) + + for i := range dst[:n] { + dst[i] = src.Index(i) + } + + return n +} diff --git a/vendor/github.com/parquet-go/parquet-go/sparse/sparse.go b/vendor/github.com/parquet-go/parquet-go/sparse/sparse.go new file mode 100644 index 000000000..b2680bf55 --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/sparse/sparse.go @@ -0,0 +1,20 @@ +// Package sparse contains abstractions to help work on arrays of values in +// sparse memory locations. +// +// Conversion between array types is supported when converting integers to a +// lower size (e.g. int32 to int16, or uint64 to uint8), or converting from +// signed integers to unsigned. Float types can also be converted to unsigned +// integers of the same size, in which case the conversion is similar to using +// the standard library's math.Float32bits and math.Float64bits functions. +// +// All array types can be converted to a generic Array type that can be used to erase +// type information and bypass type conversion rules. This conversion is similar +// to using Go's unsafe package to bypass Go's type system and should usually be +// avoided and a sign that the application is attempting to break type safety +// boundaries. +// +// The package provides Gather* functions which retrieve values from sparse +// arrays into contiguous memory buffers. On platforms that support it, these +// operations are implemented using SIMD gather instructions (e.g. VPGATHER on +// Intel CPUs). +package sparse diff --git a/vendor/github.com/parquet-go/parquet-go/tags.go b/vendor/github.com/parquet-go/parquet-go/tags.go new file mode 100644 index 000000000..12a137143 --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/tags.go @@ -0,0 +1,73 @@ +package parquet + +import ( + "reflect" + "strings" +) + +var noTags = parquetTags{} + +// parquetTags represents the superset of all the parquet struct tags that can be used +// to configure a field. +type parquetTags struct { + parquet string + parquetKey string + parquetValue string + parquetElement string +} + +// fromStructTag parses the parquet struct tags from a reflect.StructTag and returns +// a parquetTags struct. +func fromStructTag(tag reflect.StructTag) parquetTags { + parquetTags := parquetTags{} + if val := tag.Get("parquet"); val != "" { + parquetTags.parquet = val + } + if val := tag.Get("parquet-key"); val != "" { + parquetTags.parquetKey = val + } + if val := tag.Get("parquet-value"); val != "" { + parquetTags.parquetValue = val + } + if val := tag.Get("parquet-element"); val != "" { + parquetTags.parquetElement = val + } + return parquetTags +} + +// getMapKeyNodeTags returns the parquet tags for configuring the keys of a map. +func (p parquetTags) getMapKeyNodeTags() parquetTags { + return parquetTags{ + parquet: p.parquetKey, + } +} + +// getMapValueNodeTags returns the parquet tags for configuring the values of a map. +func (p parquetTags) getMapValueNodeTags() parquetTags { + return parquetTags{ + parquet: p.parquetValue, + } +} + +// getListElementNodeTags returns the parquet tags for configuring the elements of a list. +func (p parquetTags) getListElementNodeTags() parquetTags { + return parquetTags{ + parquet: p.parquetElement, + } +} + +// protoFieldNameFromTag extracts the field name from a protobuf struct tag. +// The protobuf tag format is: protobuf:"type,number,opt,name=field_name,json=jsonName,proto3" +// Returns empty string if no name is found. +func protoFieldNameFromTag(tag reflect.StructTag) string { + protoTag := tag.Get("protobuf") + if protoTag == "" { + return "" + } + for part := range strings.SplitSeq(protoTag, ",") { + if name, value, ok := strings.Cut(part, "="); ok && name == "name" { + return value + } + } + return "" +} diff --git a/vendor/github.com/parquet-go/parquet-go/transform.go b/vendor/github.com/parquet-go/parquet-go/transform.go new file mode 100644 index 000000000..318e27c90 --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/transform.go @@ -0,0 +1,140 @@ +package parquet + +// TransformRowReader constructs a RowReader which applies the given transform +// to each row rad from reader. +// +// The transformation function appends the transformed src row to dst, returning +// dst and any error that occurred during the transformation. If dst is returned +// unchanged, the row is skipped. +func TransformRowReader(reader RowReader, transform func(dst, src Row) (Row, error)) RowReader { + return &transformRowReader{reader: reader, transform: transform} +} + +type transformRowReader struct { + reader RowReader + transform func(Row, Row) (Row, error) + rows []Row + offset int + length int +} + +func (t *transformRowReader) ReadRows(rows []Row) (n int, err error) { + if len(t.rows) == 0 { + t.rows = makeRows(len(rows)) + } + + for { + for n < len(rows) && t.offset < t.length { + dst := rows[n][:0] + src := t.rows[t.offset] + rows[n], err = t.transform(dst, src) + if err != nil { + return n, err + } + clearValues(src) + t.rows[t.offset] = src[:0] + t.offset++ + n++ + } + + if n == len(rows) { + return n, nil + } + + r, err := t.reader.ReadRows(t.rows) + if r == 0 && err != nil { + return n, err + } + t.offset = 0 + t.length = r + } +} + +type transformRowBuffer struct { + buffer []Row + offset int32 + length int32 +} + +func (b *transformRowBuffer) init(n int) { + b.buffer = makeRows(n) + b.offset = 0 + b.length = 0 +} + +func (b *transformRowBuffer) discard() { + row := b.buffer[b.offset] + clearValues(row) + b.buffer[b.offset] = row[:0] + + if b.offset++; b.offset == b.length { + b.reset(0) + } +} + +func (b *transformRowBuffer) reset(n int) { + b.offset = 0 + b.length = int32(n) +} + +func (b *transformRowBuffer) rows() []Row { + return b.buffer[b.offset:b.length] +} + +func (b *transformRowBuffer) cap() int { + return len(b.buffer) +} + +func (b *transformRowBuffer) len() int { + return int(b.length - b.offset) +} + +// TransformRowWriter constructs a RowWriter which applies the given transform +// to each row writter to writer. +// +// The transformation function appends the transformed src row to dst, returning +// dst and any error that occurred during the transformation. If dst is returned +// unchanged, the row is skipped. +func TransformRowWriter(writer RowWriter, transform func(dst, src Row) (Row, error)) RowWriter { + return &transformRowWriter{writer: writer, transform: transform} +} + +type transformRowWriter struct { + writer RowWriter + transform func(Row, Row) (Row, error) + rows []Row +} + +func (t *transformRowWriter) WriteRows(rows []Row) (n int, err error) { + if len(t.rows) == 0 { + t.rows = makeRows(len(rows)) + } + + for n < len(rows) { + numRows := min(len(rows)-n, len(t.rows)) + if err := t.writeRows(rows[n : n+numRows]); err != nil { + return n, err + } + n += numRows + } + + return n, nil +} + +func (t *transformRowWriter) writeRows(rows []Row) (err error) { + numRows := 0 + defer func() { clearRows(t.rows[:numRows]) }() + + for _, row := range rows { + t.rows[numRows], err = t.transform(t.rows[numRows][:0], row) + if err != nil { + return err + } + if len(t.rows[numRows]) != 0 { + numRows++ + } + } + + _, err = t.writer.WriteRows(t.rows[:numRows]) + return err +} diff --git a/vendor/github.com/parquet-go/parquet-go/type.go b/vendor/github.com/parquet-go/parquet-go/type.go new file mode 100644 index 000000000..c8e27ec64 --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/type.go @@ -0,0 +1,285 @@ +package parquet + +import ( + "reflect" + + "github.com/parquet-go/parquet-go/deprecated" + "github.com/parquet-go/parquet-go/encoding" + "github.com/parquet-go/parquet-go/format" +) + +// Kind is an enumeration type representing the physical types supported by the +// parquet type system. +type Kind int8 + +const ( + Boolean Kind = Kind(format.Boolean) + Int32 Kind = Kind(format.Int32) + Int64 Kind = Kind(format.Int64) + Int96 Kind = Kind(format.Int96) + Float Kind = Kind(format.Float) + Double Kind = Kind(format.Double) + ByteArray Kind = Kind(format.ByteArray) + FixedLenByteArray Kind = Kind(format.FixedLenByteArray) +) + +// String returns a human-readable representation of the physical type. +func (k Kind) String() string { return format.Type(k).String() } + +// Value constructs a value from k and v. +// +// The method panics if the data is not a valid representation of the value +// kind; for example, if the kind is Int32 but the data is not 4 bytes long. +func (k Kind) Value(v []byte) Value { + x, err := parseValue(k, v) + if err != nil { + panic(err) + } + return x +} + +// The Type interface represents logical types of the parquet type system. +// +// Types are immutable and therefore safe to access from multiple goroutines. +type Type interface { + // Returns a human-readable representation of the parquet type. + String() string + + // Returns the Kind value representing the underlying physical type. + // + // The method panics if it is called on a group type. + Kind() Kind + + // For integer and floating point physical types, the method returns the + // size of values in bits. + // + // For fixed-length byte arrays, the method returns the size of elements + // in bytes. + // + // For other types, the value is zero. + Length() int + + // Returns an estimation of the number of bytes required to hold the given + // number of values of this type in memory. + // + // The method returns zero for group types. + EstimateSize(numValues int) int + + // Returns an estimation of the number of values of this type that can be + // held in the given byte size. + // + // The method returns zero for group types. + EstimateNumValues(size int) int + + // Compares two values and returns a negative integer if a < b, positive if + // a > b, or zero if a == b. + // + // The values' Kind must match the type, otherwise the result is undefined. + // + // The method panics if it is called on a group type. + Compare(a, b Value) int + + // ColumnOrder returns the type's column order. For group types, this method + // returns nil. + // + // The order describes the comparison logic implemented by the Less method. + // + // As an optimization, the method may return the same pointer across + // multiple calls. Applications must treat the returned value as immutable, + // mutating the value will result in undefined behavior. + ColumnOrder() *format.ColumnOrder + + // Returns the physical type as a *format.Type value. For group types, this + // method returns nil. + // + // As an optimization, the method may return the same pointer across + // multiple calls. Applications must treat the returned value as immutable, + // mutating the value will result in undefined behavior. + PhysicalType() *format.Type + + // Returns the logical type as a *format.LogicalType value. When the logical + // type is unknown, the method returns nil. + // + // As an optimization, the method may return the same pointer across + // multiple calls. Applications must treat the returned value as immutable, + // mutating the value will result in undefined behavior. + LogicalType() *format.LogicalType + + // Returns the logical type's equivalent converted type. When there are + // no equivalent converted type, the method returns nil. + // + // As an optimization, the method may return the same pointer across + // multiple calls. Applications must treat the returned value as immutable, + // mutating the value will result in undefined behavior. + ConvertedType() *deprecated.ConvertedType + + // Creates a column indexer for values of this type. + // + // The size limit is a hint to the column indexer that it is allowed to + // truncate the page boundaries to the given size. Only BYTE_ARRAY and + // FIXED_LEN_BYTE_ARRAY types currently take this value into account. + // + // A value of zero or less means no limits. + // + // The method panics if it is called on a group type. + NewColumnIndexer(sizeLimit int) ColumnIndexer + + // Creates a row group buffer column for values of this type. + // + // Column buffers are created using the index of the column they are + // accumulating values in memory for (relative to the parent schema), + // and the size of their memory buffer. + // + // The application may give an estimate of the number of values it expects + // to write to the buffer as second argument. This estimate helps set the + // initialize buffer capacity but is not a hard limit, the underlying memory + // buffer will grown as needed to allow more values to be written. Programs + // may use the Size method of the column buffer (or the parent row group, + // when relevant) to determine how many bytes are being used, and perform a + // flush of the buffers to a storage layer. + // + // The method panics if it is called on a group type. + NewColumnBuffer(columnIndex, numValues int) ColumnBuffer + + // Creates a dictionary holding values of this type. + // + // The dictionary retains the data buffer, it does not make a copy of it. + // If the application needs to share ownership of the memory buffer, it must + // ensure that it will not be modified while the page is in use, or it must + // make a copy of it prior to creating the dictionary. + // + // The method panics if the data type does not correspond to the parquet + // type it is called on. + NewDictionary(columnIndex, numValues int, data encoding.Values) Dictionary + + // Creates a page belonging to a column at the given index, backed by the + // data buffer. + // + // The page retains the data buffer, it does not make a copy of it. If the + // application needs to share ownership of the memory buffer, it must ensure + // that it will not be modified while the page is in use, or it must make a + // copy of it prior to creating the page. + // + // The method panics if the data type does not correspond to the parquet + // type it is called on. + NewPage(columnIndex, numValues int, data encoding.Values) Page + + // Creates an encoding.Values instance backed by the given buffers. + // + // The offsets is only used by BYTE_ARRAY types, where it represents the + // positions of each variable length value in the values buffer. + // + // The following expression creates an empty instance for any type: + // + // values := typ.NewValues(nil, nil) + // + // The method panics if it is called on group types. + NewValues(values []byte, offsets []uint32) encoding.Values + + // Assuming the src buffer contains PLAIN encoded values of the type it is + // called on, applies the given encoding and produces the output to the dst + // buffer passed as first argument by dispatching the call to one of the + // encoding methods. + Encode(dst []byte, src encoding.Values, enc encoding.Encoding) ([]byte, error) + + // Assuming the src buffer contains values encoding in the given encoding, + // decodes the input and produces the encoded values into the dst output + // buffer passed as first argument by dispatching the call to one of the + // encoding methods. + Decode(dst encoding.Values, src []byte, enc encoding.Encoding) (encoding.Values, error) + + // Returns an estimation of the output size after decoding the values passed + // as first argument with the given encoding. + // + // For most types, this is similar to calling EstimateSize with the known + // number of encoded values. For variable size types, using this method may + // provide a more precise result since it can inspect the input buffer. + EstimateDecodeSize(numValues int, src []byte, enc encoding.Encoding) int + + // Assigns a Parquet value to a Go value. Returns an error if assignment is + // not possible. The source Value must be an expected logical type for the + // receiver. This can be accomplished using ConvertValue. + AssignValue(dst reflect.Value, src Value) error + + // Convert a Parquet Value of the given Type into a Parquet Value that is + // compatible with the receiver. The returned Value is suitable to be passed + // to AssignValue. + ConvertValue(val Value, typ Type) (Value, error) +} + +// EqualTypes returns true if type1 and type2 are equal. +// +// Types are considered equal if they have the same Kind, Length, and LogicalType. +// The comparison uses reflect.DeepEqual for LogicalType comparison. +// +// Note: This function is designed for leaf types. For complex group types like +// MAP and LIST, use EqualNodes instead, as those types require structural comparison +// of their nested fields. +func EqualTypes(type1, type2 Type) bool { + return equalKind(type1, type2) && equalLength(type1, type2) && equalLogicalTypes(type1, type2) +} + +func equalKind(type1, type2 Type) bool { + return type1.Kind() == type2.Kind() +} + +func equalLength(type1, type2 Type) bool { + return type1.Length() == type2.Length() +} + +func equalLogicalTypes(type1, type2 Type) bool { + return reflect.DeepEqual(type1.LogicalType(), type2.LogicalType()) +} + +var ( + BooleanType Type = booleanType{} + Int32Type Type = int32Type{} + Int64Type Type = int64Type{} + Int96Type Type = int96Type{} + FloatType Type = floatType{} + DoubleType Type = doubleType{} + ByteArrayType Type = byteArrayType{} + NullType Type = &nullType{} +) + +// In the current parquet version supported by this library, only type-defined +// orders are supported. +var typeDefinedColumnOrder = format.ColumnOrder{ + TypeOrder: new(format.TypeDefinedOrder), +} + +var physicalTypes = [...]format.Type{ + 0: format.Boolean, + 1: format.Int32, + 2: format.Int64, + 3: format.Int96, + 4: format.Float, + 5: format.Double, + 6: format.ByteArray, + 7: format.FixedLenByteArray, +} + +var convertedTypes = [...]deprecated.ConvertedType{ + 0: deprecated.UTF8, + 1: deprecated.Map, + 2: deprecated.MapKeyValue, + 3: deprecated.List, + 4: deprecated.Enum, + 5: deprecated.Decimal, + 6: deprecated.Date, + 7: deprecated.TimeMillis, + 8: deprecated.TimeMicros, + 9: deprecated.TimestampMillis, + 10: deprecated.TimestampMicros, + 11: deprecated.Uint8, + 12: deprecated.Uint16, + 13: deprecated.Uint32, + 14: deprecated.Uint64, + 15: deprecated.Int8, + 16: deprecated.Int16, + 17: deprecated.Int32, + 18: deprecated.Int64, + 19: deprecated.Json, + 20: deprecated.Bson, + 21: deprecated.Interval, +} diff --git a/vendor/github.com/parquet-go/parquet-go/type_boolean.go b/vendor/github.com/parquet-go/parquet-go/type_boolean.go new file mode 100644 index 000000000..bb173ff26 --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/type_boolean.go @@ -0,0 +1,90 @@ +package parquet + +import ( + "reflect" + + "github.com/parquet-go/parquet-go/deprecated" + "github.com/parquet-go/parquet-go/encoding" + "github.com/parquet-go/parquet-go/format" +) + +type booleanType struct{} + +func (t booleanType) String() string { return "BOOLEAN" } +func (t booleanType) Kind() Kind { return Boolean } +func (t booleanType) Length() int { return 1 } +func (t booleanType) EstimateSize(n int) int { return (n + 7) / 8 } +func (t booleanType) EstimateNumValues(n int) int { return 8 * n } +func (t booleanType) Compare(a, b Value) int { return compareBool(a.boolean(), b.boolean()) } +func (t booleanType) ColumnOrder() *format.ColumnOrder { return &typeDefinedColumnOrder } +func (t booleanType) LogicalType() *format.LogicalType { return nil } +func (t booleanType) ConvertedType() *deprecated.ConvertedType { return nil } +func (t booleanType) PhysicalType() *format.Type { return &physicalTypes[Boolean] } + +func (t booleanType) NewColumnIndexer(sizeLimit int) ColumnIndexer { + return newBooleanColumnIndexer() +} + +func (t booleanType) NewColumnBuffer(columnIndex, numValues int) ColumnBuffer { + return newBooleanColumnBuffer(t, makeColumnIndex(columnIndex), makeNumValues(numValues)) +} + +func (t booleanType) NewDictionary(columnIndex, numValues int, data encoding.Values) Dictionary { + return newBooleanDictionary(t, makeColumnIndex(columnIndex), makeNumValues(numValues), data) +} + +func (t booleanType) NewPage(columnIndex, numValues int, data encoding.Values) Page { + return newBooleanPage(t, makeColumnIndex(columnIndex), makeNumValues(numValues), data) +} + +func (t booleanType) NewValues(values []byte, _ []uint32) encoding.Values { + return encoding.BooleanValues(values) +} + +func (t booleanType) Encode(dst []byte, src encoding.Values, enc encoding.Encoding) ([]byte, error) { + return encoding.EncodeBoolean(dst, src, enc) +} + +func (t booleanType) Decode(dst encoding.Values, src []byte, enc encoding.Encoding) (encoding.Values, error) { + return encoding.DecodeBoolean(dst, src, enc) +} + +func (t booleanType) EstimateDecodeSize(numValues int, src []byte, enc encoding.Encoding) int { + return t.EstimateSize(numValues) +} + +func (t booleanType) AssignValue(dst reflect.Value, src Value) error { + v := src.boolean() + switch dst.Kind() { + case reflect.Bool: + dst.SetBool(v) + default: + dst.Set(reflect.ValueOf(v)) + } + return nil +} + +func (t booleanType) ConvertValue(val Value, typ Type) (Value, error) { + switch typ.(type) { + case *stringType: + return convertStringToBoolean(val) + } + switch typ.Kind() { + case Boolean: + return val, nil + case Int32: + return convertInt32ToBoolean(val) + case Int64: + return convertInt64ToBoolean(val) + case Int96: + return convertInt96ToBoolean(val) + case Float: + return convertFloatToBoolean(val) + case Double: + return convertDoubleToBoolean(val) + case ByteArray, FixedLenByteArray: + return convertByteArrayToBoolean(val) + default: + return makeValueKind(Boolean), nil + } +} diff --git a/vendor/github.com/parquet-go/parquet-go/type_bson.go b/vendor/github.com/parquet-go/parquet-go/type_bson.go new file mode 100644 index 000000000..687e5d1f7 --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/type_bson.go @@ -0,0 +1,87 @@ +package parquet + +import ( + "reflect" + + "github.com/parquet-go/parquet-go/deprecated" + "github.com/parquet-go/parquet-go/encoding" + "github.com/parquet-go/parquet-go/format" +) + +// BSON constructs a leaf node of BSON logical type. +// +// https://github.com/apache/parquet-format/blob/master/LogicalTypes.md#bson +func BSON() Node { return Leaf(&bsonType{}) } + +var bsonLogicalType = format.LogicalType{ + Bson: new(format.BsonType), +} + +type bsonType format.BsonType + +func (t *bsonType) String() string { return (*format.BsonType)(t).String() } + +func (t *bsonType) Kind() Kind { return byteArrayType{}.Kind() } + +func (t *bsonType) Length() int { return byteArrayType{}.Length() } + +func (t *bsonType) EstimateSize(n int) int { return byteArrayType{}.EstimateSize(n) } + +func (t *bsonType) EstimateNumValues(n int) int { return byteArrayType{}.EstimateNumValues(n) } + +func (t *bsonType) Compare(a, b Value) int { return byteArrayType{}.Compare(a, b) } + +func (t *bsonType) ColumnOrder() *format.ColumnOrder { return byteArrayType{}.ColumnOrder() } + +func (t *bsonType) PhysicalType() *format.Type { return byteArrayType{}.PhysicalType() } + +func (t *bsonType) LogicalType() *format.LogicalType { return &bsonLogicalType } + +func (t *bsonType) ConvertedType() *deprecated.ConvertedType { + return &convertedTypes[deprecated.Bson] +} + +func (t *bsonType) NewColumnIndexer(sizeLimit int) ColumnIndexer { + return byteArrayType{}.NewColumnIndexer(sizeLimit) +} + +func (t *bsonType) NewDictionary(columnIndex, numValues int, data encoding.Values) Dictionary { + return byteArrayType{}.NewDictionary(columnIndex, numValues, data) +} + +func (t *bsonType) NewColumnBuffer(columnIndex, numValues int) ColumnBuffer { + return byteArrayType{}.NewColumnBuffer(columnIndex, numValues) +} + +func (t *bsonType) NewPage(columnIndex, numValues int, data encoding.Values) Page { + return byteArrayType{}.NewPage(columnIndex, numValues, data) +} + +func (t *bsonType) NewValues(values []byte, offsets []uint32) encoding.Values { + return byteArrayType{}.NewValues(values, offsets) +} + +func (t *bsonType) Encode(dst []byte, src encoding.Values, enc encoding.Encoding) ([]byte, error) { + return byteArrayType{}.Encode(dst, src, enc) +} + +func (t *bsonType) Decode(dst encoding.Values, src []byte, enc encoding.Encoding) (encoding.Values, error) { + return byteArrayType{}.Decode(dst, src, enc) +} + +func (t *bsonType) EstimateDecodeSize(numValues int, src []byte, enc encoding.Encoding) int { + return byteArrayType{}.EstimateDecodeSize(numValues, src, enc) +} + +func (t *bsonType) AssignValue(dst reflect.Value, src Value) error { + return byteArrayType{}.AssignValue(dst, src) +} + +func (t *bsonType) ConvertValue(val Value, typ Type) (Value, error) { + switch typ.(type) { + case byteArrayType, *bsonType: + return val, nil + default: + return val, invalidConversion(val, "BSON", typ.String()) + } +} diff --git a/vendor/github.com/parquet-go/parquet-go/type_byte_array.go b/vendor/github.com/parquet-go/parquet-go/type_byte_array.go new file mode 100644 index 000000000..fc685d3e1 --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/type_byte_array.go @@ -0,0 +1,102 @@ +package parquet + +import ( + "bytes" + "reflect" + + "github.com/parquet-go/parquet-go/deprecated" + "github.com/parquet-go/parquet-go/encoding" + "github.com/parquet-go/parquet-go/format" +) + +type byteArrayType struct{} + +func (t byteArrayType) String() string { return "BYTE_ARRAY" } +func (t byteArrayType) Kind() Kind { return ByteArray } +func (t byteArrayType) Length() int { return 0 } +func (t byteArrayType) EstimateSize(n int) int { return estimatedSizeOfByteArrayValues * n } +func (t byteArrayType) EstimateNumValues(n int) int { return n / estimatedSizeOfByteArrayValues } +func (t byteArrayType) Compare(a, b Value) int { return bytes.Compare(a.byteArray(), b.byteArray()) } +func (t byteArrayType) ColumnOrder() *format.ColumnOrder { return &typeDefinedColumnOrder } +func (t byteArrayType) LogicalType() *format.LogicalType { return nil } +func (t byteArrayType) ConvertedType() *deprecated.ConvertedType { return nil } +func (t byteArrayType) PhysicalType() *format.Type { return &physicalTypes[ByteArray] } + +func (t byteArrayType) NewColumnIndexer(sizeLimit int) ColumnIndexer { + return newByteArrayColumnIndexer(sizeLimit) +} + +func (t byteArrayType) NewColumnBuffer(columnIndex, numValues int) ColumnBuffer { + return newByteArrayColumnBuffer(t, makeColumnIndex(columnIndex), makeNumValues(numValues)) +} + +func (t byteArrayType) NewDictionary(columnIndex, numValues int, data encoding.Values) Dictionary { + return newByteArrayDictionary(t, makeColumnIndex(columnIndex), makeNumValues(numValues), data) +} + +func (t byteArrayType) NewPage(columnIndex, numValues int, data encoding.Values) Page { + return newByteArrayPage(t, makeColumnIndex(columnIndex), makeNumValues(numValues), data) +} + +func (t byteArrayType) NewValues(values []byte, offsets []uint32) encoding.Values { + return encoding.ByteArrayValues(values, offsets) +} + +func (t byteArrayType) Encode(dst []byte, src encoding.Values, enc encoding.Encoding) ([]byte, error) { + return encoding.EncodeByteArray(dst, src, enc) +} + +func (t byteArrayType) Decode(dst encoding.Values, src []byte, enc encoding.Encoding) (encoding.Values, error) { + return encoding.DecodeByteArray(dst, src, enc) +} + +func (t byteArrayType) EstimateDecodeSize(numValues int, src []byte, enc encoding.Encoding) int { + return enc.EstimateDecodeByteArraySize(src) +} + +func (t byteArrayType) AssignValue(dst reflect.Value, src Value) error { + v := src.byteArray() + switch dst.Kind() { + case reflect.String: + dst.SetString(string(v)) + case reflect.Slice: + dst.SetBytes(copyBytes(v)) + case reflect.Ptr: + // Handle pointer types like *string + if src.IsNull() { + dst.Set(reflect.Zero(dst.Type())) + } else { + // Allocate a new value of the element type + elem := reflect.New(dst.Type().Elem()) + if err := t.AssignValue(elem.Elem(), src); err != nil { + return err + } + dst.Set(elem) + } + default: + val := reflect.ValueOf(string(v)) + dst.Set(val) + } + return nil +} + +func (t byteArrayType) ConvertValue(val Value, typ Type) (Value, error) { + switch typ.Kind() { + case Boolean: + return convertBooleanToByteArray(val) + case Int32: + return convertInt32ToByteArray(val) + case Int64: + return convertInt64ToByteArray(val) + case Int96: + return convertInt96ToByteArray(val) + case Float: + return convertFloatToByteArray(val) + case Double: + return convertDoubleToByteArray(val) + case ByteArray, FixedLenByteArray: + return val, nil + default: + return makeValueKind(ByteArray), nil + } +} diff --git a/vendor/github.com/parquet-go/parquet-go/type_date.go b/vendor/github.com/parquet-go/parquet-go/type_date.go new file mode 100644 index 000000000..e408be207 --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/type_date.go @@ -0,0 +1,117 @@ +package parquet + +import ( + "reflect" + "time" + + "github.com/parquet-go/parquet-go/deprecated" + "github.com/parquet-go/parquet-go/encoding" + "github.com/parquet-go/parquet-go/format" +) + +// Date constructs a leaf node of DATE logical type. +// +// https://github.com/apache/parquet-format/blob/master/LogicalTypes.md#date +func Date() Node { return Leaf(&dateType{}) } + +var dateLogicalType = format.LogicalType{ + Date: new(format.DateType), +} + +type dateType format.DateType + +func (t *dateType) String() string { return (*format.DateType)(t).String() } + +func (t *dateType) Kind() Kind { return int32Type{}.Kind() } + +func (t *dateType) Length() int { return int32Type{}.Length() } + +func (t *dateType) EstimateSize(n int) int { return int32Type{}.EstimateSize(n) } + +func (t *dateType) EstimateNumValues(n int) int { return int32Type{}.EstimateNumValues(n) } + +func (t *dateType) Compare(a, b Value) int { return int32Type{}.Compare(a, b) } + +func (t *dateType) ColumnOrder() *format.ColumnOrder { return int32Type{}.ColumnOrder() } + +func (t *dateType) PhysicalType() *format.Type { return int32Type{}.PhysicalType() } + +func (t *dateType) LogicalType() *format.LogicalType { return &dateLogicalType } + +func (t *dateType) ConvertedType() *deprecated.ConvertedType { + return &convertedTypes[deprecated.Date] +} + +func (t *dateType) NewColumnIndexer(sizeLimit int) ColumnIndexer { + return int32Type{}.NewColumnIndexer(sizeLimit) +} + +func (t *dateType) NewDictionary(columnIndex, numValues int, data encoding.Values) Dictionary { + return int32Type{}.NewDictionary(columnIndex, numValues, data) +} + +func (t *dateType) NewColumnBuffer(columnIndex, numValues int) ColumnBuffer { + return int32Type{}.NewColumnBuffer(columnIndex, numValues) +} + +func (t *dateType) NewPage(columnIndex, numValues int, data encoding.Values) Page { + return int32Type{}.NewPage(columnIndex, numValues, data) +} + +func (t *dateType) NewValues(values []byte, offsets []uint32) encoding.Values { + return int32Type{}.NewValues(values, offsets) +} + +func (t *dateType) Encode(dst []byte, src encoding.Values, enc encoding.Encoding) ([]byte, error) { + return int32Type{}.Encode(dst, src, enc) +} + +func (t *dateType) Decode(dst encoding.Values, src []byte, enc encoding.Encoding) (encoding.Values, error) { + return int32Type{}.Decode(dst, src, enc) +} + +func (t *dateType) EstimateDecodeSize(numValues int, src []byte, enc encoding.Encoding) int { + return int32Type{}.EstimateDecodeSize(numValues, src, enc) +} + +func (t *dateType) AssignValue(dst reflect.Value, src Value) error { + switch dst.Type() { + case reflect.TypeOf(time.Time{}): + // Check if the value is NULL - if so, assign zero time.Time + if src.IsNull() { + dst.Set(reflect.ValueOf(time.Time{})) + return nil + } + + // DATE is stored as days since Unix epoch (January 1, 1970) + days := src.int32() + val := time.Unix(int64(days)*86400, 0).UTC() + dst.Set(reflect.ValueOf(val)) + return nil + case reflect.TypeOf((*time.Time)(nil)): + // Handle *time.Time (pointer to time.Time) + if src.IsNull() { + // For NULL values, set the pointer to nil + dst.Set(reflect.Zero(dst.Type())) + return nil + } + + // DATE is stored as days since Unix epoch (January 1, 1970) + days := src.int32() + val := time.Unix(int64(days)*86400, 0).UTC() + ptr := &val + dst.Set(reflect.ValueOf(ptr)) + return nil + } + return int32Type{}.AssignValue(dst, src) +} + +func (t *dateType) ConvertValue(val Value, typ Type) (Value, error) { + switch src := typ.(type) { + case *stringType: + return convertStringToDate(val, time.UTC) + case *timestampType: + return convertTimestampToDate(val, src.Unit, src.tz()) + } + return int32Type{}.ConvertValue(val, typ) +} diff --git a/vendor/github.com/parquet-go/parquet-go/type_decimal.go b/vendor/github.com/parquet-go/parquet-go/type_decimal.go new file mode 100644 index 000000000..a3b71ac7b --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/type_decimal.go @@ -0,0 +1,104 @@ +package parquet + +import ( + "log" + "math" + "math/big" + "reflect" + "strconv" + + "github.com/parquet-go/parquet-go/deprecated" + "github.com/parquet-go/parquet-go/format" +) + +// Decimal constructs a leaf node of decimal logical type with the given +// scale, precision, and underlying type. +// +// https://github.com/apache/parquet-format/blob/master/LogicalTypes.md#decimal +func Decimal(scale, precision int, typ Type) Node { + switch typ.Kind() { + case Int32: + if precision < 1 || precision > 9 { + panic("DECIMAL annotated with Int32 must have precision >= 1 and <= 9, got " + strconv.Itoa(precision)) + } + case Int64: + if precision < 1 || precision > 18 { + panic("DECIMAL annotated with Int64 must have precision >= 1 and <= 18, got " + strconv.Itoa(precision)) + } + if precision < 10 { + log.Printf("WARNING: DECIMAL annotated with Int64 should have a precision >= 10, got %d", precision) + } + case ByteArray, FixedLenByteArray: + default: + panic("DECIMAL node must annotate Int32, Int64, ByteArray or FixedLenByteArray but got " + typ.String()) + } + return Leaf(&decimalType{ + decimal: format.DecimalType{ + Scale: int32(scale), + Precision: int32(precision), + }, + Type: typ, + }) +} + +type decimalType struct { + decimal format.DecimalType + Type +} + +func (t *decimalType) String() string { return t.decimal.String() } + +func (t *decimalType) LogicalType() *format.LogicalType { + return &format.LogicalType{Decimal: &t.decimal} +} + +func (t *decimalType) ConvertedType() *deprecated.ConvertedType { + return &convertedTypes[deprecated.Decimal] +} + +func (t *decimalType) AssignValue(dst reflect.Value, src Value) error { + switch t.Type { + case Int32Type: + switch dst.Kind() { + case reflect.Int32: + dst.SetInt(int64(src.int32())) + default: + dst.Set(reflect.ValueOf(float32(src.int32()) / float32(math.Pow10(int(t.decimal.Scale))))) + } + case Int64Type: + switch dst.Kind() { + case reflect.Int64: + dst.SetInt(src.int64()) + default: + dst.Set(reflect.ValueOf(float64(src.int64()) / math.Pow10(int(t.decimal.Scale)))) + } + default: + // ByteArray and FixedLenByteArray + if t.Type.Kind() != ByteArray && t.Type.Kind() != FixedLenByteArray { + return nil + } + data := src.ByteArray() + val := new(big.Int) + if len(data) > 0 && data[0]&0x80 != 0 { + // Negative number: convert from two's complement + tmp := make([]byte, len(data)) + for i, b := range data { + tmp[i] = ^b + } + val.SetBytes(tmp) + val.Add(val, big.NewInt(1)) + val.Neg(val) + } else { + val.SetBytes(data) + } + // Use enough precision to represent the decimal value accurately + // precision * log2(10) ≈ precision * 3.32, round up generously + prec := max(uint(t.decimal.Precision)*4+64, 192) + f := new(big.Float).SetPrec(prec).SetInt(val) + scaleFactor := new(big.Float).SetPrec(prec) + scaleFactor.SetInt(new(big.Int).Exp(big.NewInt(10), big.NewInt(int64(t.decimal.Scale)), nil)) + f.Quo(f, scaleFactor) + dst.Set(reflect.ValueOf(f)) + } + return nil +} diff --git a/vendor/github.com/parquet-go/parquet-go/type_double.go b/vendor/github.com/parquet-go/parquet-go/type_double.go new file mode 100644 index 000000000..a94ed8654 --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/type_double.go @@ -0,0 +1,90 @@ +package parquet + +import ( + "reflect" + + "github.com/parquet-go/parquet-go/deprecated" + "github.com/parquet-go/parquet-go/encoding" + "github.com/parquet-go/parquet-go/format" +) + +type doubleType struct{} + +func (t doubleType) String() string { return "DOUBLE" } +func (t doubleType) Kind() Kind { return Double } +func (t doubleType) Length() int { return 64 } +func (t doubleType) EstimateSize(n int) int { return 8 * n } +func (t doubleType) EstimateNumValues(n int) int { return n / 8 } +func (t doubleType) Compare(a, b Value) int { return compareFloat64(a.double(), b.double()) } +func (t doubleType) ColumnOrder() *format.ColumnOrder { return &typeDefinedColumnOrder } +func (t doubleType) LogicalType() *format.LogicalType { return nil } +func (t doubleType) ConvertedType() *deprecated.ConvertedType { return nil } +func (t doubleType) PhysicalType() *format.Type { return &physicalTypes[Double] } + +func (t doubleType) NewColumnIndexer(sizeLimit int) ColumnIndexer { + return newDoubleColumnIndexer() +} + +func (t doubleType) NewColumnBuffer(columnIndex, numValues int) ColumnBuffer { + return newDoubleColumnBuffer(t, makeColumnIndex(columnIndex), makeNumValues(numValues)) +} + +func (t doubleType) NewDictionary(columnIndex, numValues int, data encoding.Values) Dictionary { + return newDoubleDictionary(t, makeColumnIndex(columnIndex), makeNumValues(numValues), data) +} + +func (t doubleType) NewPage(columnIndex, numValues int, data encoding.Values) Page { + return newDoublePage(t, makeColumnIndex(columnIndex), makeNumValues(numValues), data) +} + +func (t doubleType) NewValues(values []byte, _ []uint32) encoding.Values { + return encoding.DoubleValuesFromBytes(values) +} + +func (t doubleType) Encode(dst []byte, src encoding.Values, enc encoding.Encoding) ([]byte, error) { + return encoding.EncodeDouble(dst, src, enc) +} + +func (t doubleType) Decode(dst encoding.Values, src []byte, enc encoding.Encoding) (encoding.Values, error) { + return encoding.DecodeDouble(dst, src, enc) +} + +func (t doubleType) EstimateDecodeSize(numValues int, src []byte, enc encoding.Encoding) int { + return t.EstimateSize(numValues) +} + +func (t doubleType) AssignValue(dst reflect.Value, src Value) error { + v := src.double() + switch dst.Kind() { + case reflect.Float32, reflect.Float64: + dst.SetFloat(v) + default: + dst.Set(reflect.ValueOf(v)) + } + return nil +} + +func (t doubleType) ConvertValue(val Value, typ Type) (Value, error) { + switch typ.(type) { + case *stringType: + return convertStringToDouble(val) + } + switch typ.Kind() { + case Boolean: + return convertBooleanToDouble(val) + case Int32: + return convertInt32ToDouble(val) + case Int64: + return convertInt64ToDouble(val) + case Int96: + return convertInt96ToDouble(val) + case Float: + return convertFloatToDouble(val) + case Double: + return val, nil + case ByteArray, FixedLenByteArray: + return convertByteArrayToDouble(val) + default: + return makeValueKind(Double), nil + } +} diff --git a/vendor/github.com/parquet-go/parquet-go/type_enum.go b/vendor/github.com/parquet-go/parquet-go/type_enum.go new file mode 100644 index 000000000..d47c7bbb2 --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/type_enum.go @@ -0,0 +1,87 @@ +package parquet + +import ( + "reflect" + + "github.com/parquet-go/parquet-go/deprecated" + "github.com/parquet-go/parquet-go/encoding" + "github.com/parquet-go/parquet-go/format" +) + +// Enum constructs a leaf node with a logical type representing enumerations. +// +// https://github.com/apache/parquet-format/blob/master/LogicalTypes.md#enum +func Enum() Node { return Leaf(&enumType{}) } + +var enumLogicalType = format.LogicalType{ + Enum: new(format.EnumType), +} + +type enumType format.EnumType + +func (t *enumType) String() string { return (*format.EnumType)(t).String() } + +func (t *enumType) Kind() Kind { return new(stringType).Kind() } + +func (t *enumType) Length() int { return new(stringType).Length() } + +func (t *enumType) EstimateSize(n int) int { return new(stringType).EstimateSize(n) } + +func (t *enumType) EstimateNumValues(n int) int { return new(stringType).EstimateNumValues(n) } + +func (t *enumType) Compare(a, b Value) int { return new(stringType).Compare(a, b) } + +func (t *enumType) ColumnOrder() *format.ColumnOrder { return new(stringType).ColumnOrder() } + +func (t *enumType) PhysicalType() *format.Type { return new(stringType).PhysicalType() } + +func (t *enumType) LogicalType() *format.LogicalType { return &enumLogicalType } + +func (t *enumType) ConvertedType() *deprecated.ConvertedType { + return &convertedTypes[deprecated.Enum] +} + +func (t *enumType) NewColumnIndexer(sizeLimit int) ColumnIndexer { + return new(stringType).NewColumnIndexer(sizeLimit) +} + +func (t *enumType) NewDictionary(columnIndex, numValues int, data encoding.Values) Dictionary { + return new(stringType).NewDictionary(columnIndex, numValues, data) +} + +func (t *enumType) NewColumnBuffer(columnIndex, numValues int) ColumnBuffer { + return new(stringType).NewColumnBuffer(columnIndex, numValues) +} + +func (t *enumType) NewPage(columnIndex, numValues int, data encoding.Values) Page { + return new(stringType).NewPage(columnIndex, numValues, data) +} + +func (t *enumType) NewValues(values []byte, offsets []uint32) encoding.Values { + return new(stringType).NewValues(values, offsets) +} + +func (t *enumType) Encode(dst []byte, src encoding.Values, enc encoding.Encoding) ([]byte, error) { + return new(stringType).Encode(dst, src, enc) +} + +func (t *enumType) Decode(dst encoding.Values, src []byte, enc encoding.Encoding) (encoding.Values, error) { + return new(stringType).Decode(dst, src, enc) +} + +func (t *enumType) EstimateDecodeSize(numValues int, src []byte, enc encoding.Encoding) int { + return new(stringType).EstimateDecodeSize(numValues, src, enc) +} + +func (t *enumType) AssignValue(dst reflect.Value, src Value) error { + return new(stringType).AssignValue(dst, src) +} + +func (t *enumType) ConvertValue(val Value, typ Type) (Value, error) { + switch typ.(type) { + case byteArrayType, *stringType, *enumType: + return val, nil + default: + return val, invalidConversion(val, "ENUM", typ.String()) + } +} diff --git a/vendor/github.com/parquet-go/parquet-go/type_fixed_len_byte_array.go b/vendor/github.com/parquet-go/parquet-go/type_fixed_len_byte_array.go new file mode 100644 index 000000000..43b58c1dd --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/type_fixed_len_byte_array.go @@ -0,0 +1,234 @@ +package parquet + +import ( + "bytes" + "fmt" + "reflect" + "unsafe" + + "github.com/google/uuid" + "github.com/parquet-go/parquet-go/deprecated" + "github.com/parquet-go/parquet-go/encoding" + "github.com/parquet-go/parquet-go/format" +) + +type fixedLenByteArrayType struct { + length int + isUUID bool +} + +func (t fixedLenByteArrayType) String() string { + return fmt.Sprintf("FIXED_LEN_BYTE_ARRAY(%d)", t.length) +} + +func (t fixedLenByteArrayType) Kind() Kind { return FixedLenByteArray } + +func (t fixedLenByteArrayType) Length() int { return t.length } + +func (t fixedLenByteArrayType) EstimateSize(n int) int { return t.length * n } + +func (t fixedLenByteArrayType) EstimateNumValues(n int) int { return n / t.length } + +func (t fixedLenByteArrayType) Compare(a, b Value) int { + return bytes.Compare(a.byteArray(), b.byteArray()) +} + +func (t fixedLenByteArrayType) ColumnOrder() *format.ColumnOrder { return &typeDefinedColumnOrder } + +func (t fixedLenByteArrayType) LogicalType() *format.LogicalType { return nil } + +func (t fixedLenByteArrayType) ConvertedType() *deprecated.ConvertedType { return nil } + +func (t fixedLenByteArrayType) PhysicalType() *format.Type { return &physicalTypes[FixedLenByteArray] } + +func (t fixedLenByteArrayType) NewColumnIndexer(sizeLimit int) ColumnIndexer { + return newFixedLenByteArrayColumnIndexer(t.length, sizeLimit) +} + +func (t fixedLenByteArrayType) NewColumnBuffer(columnIndex, numValues int) ColumnBuffer { + return newFixedLenByteArrayColumnBuffer(t, makeColumnIndex(columnIndex), makeNumValues(numValues)) +} + +func (t fixedLenByteArrayType) NewDictionary(columnIndex, numValues int, data encoding.Values) Dictionary { + return newFixedLenByteArrayDictionary(t, makeColumnIndex(columnIndex), makeNumValues(numValues), data) +} + +func (t fixedLenByteArrayType) NewPage(columnIndex, numValues int, data encoding.Values) Page { + return newFixedLenByteArrayPage(t, makeColumnIndex(columnIndex), makeNumValues(numValues), data) +} + +func (t fixedLenByteArrayType) NewValues(values []byte, _ []uint32) encoding.Values { + return encoding.FixedLenByteArrayValues(values, t.length) +} + +func (t fixedLenByteArrayType) Encode(dst []byte, src encoding.Values, enc encoding.Encoding) ([]byte, error) { + return encoding.EncodeFixedLenByteArray(dst, src, enc) +} + +func (t fixedLenByteArrayType) Decode(dst encoding.Values, src []byte, enc encoding.Encoding) (encoding.Values, error) { + return encoding.DecodeFixedLenByteArray(dst, src, enc) +} + +func (t fixedLenByteArrayType) EstimateDecodeSize(numValues int, src []byte, enc encoding.Encoding) int { + return t.EstimateSize(numValues) +} + +func (t fixedLenByteArrayType) AssignValue(dst reflect.Value, src Value) error { + v := src.byteArray() + switch dst.Kind() { + case reflect.Array: + if dst.Type().Elem().Kind() == reflect.Uint8 && dst.Len() == len(v) { + // This code could be implemented as a call to reflect.Copy but + // it would require creating a reflect.Value from v which causes + // the heap allocation to pack the []byte value. To avoid this + // overhead we instead convert the reflect.Value holding the + // destination array into a byte slice which allows us to use + // a more efficient call to copy. + d := unsafe.Slice((*byte)(reflectValueData(dst)), len(v)) + copy(d, v) + return nil + } + case reflect.Slice: + dst.SetBytes(copyBytes(v)) + return nil + case reflect.String: + if t.isUUID { + dst.SetString(uuid.UUID(v).String()) + return nil + } + } + + val := reflect.ValueOf(copyBytes(v)) + dst.Set(val) + return nil +} + +func reflectValueData(v reflect.Value) unsafe.Pointer { + return (*[2]unsafe.Pointer)(unsafe.Pointer(&v))[1] +} + +func reflectValuePointer(v reflect.Value) unsafe.Pointer { + if v.Kind() == reflect.Map { + // Map values are inlined in the reflect.Value data area, + // because they are a reference type and their paointer is + // packed in the interface. However, we need to get an + // address to the pointer itself, so we extract it and + // return the address of this pointer. It causes a heap + // allocation, which is unfortunate, an we would probably + // want to optimize away eventually. + p := v.UnsafePointer() + return unsafe.Pointer(&p) + } + return reflectValueData(v) +} + +func (t fixedLenByteArrayType) ConvertValue(val Value, typ Type) (Value, error) { + switch typ.(type) { + case *stringType: + return convertStringToFixedLenByteArray(val, t.length) + } + switch typ.Kind() { + case Boolean: + return convertBooleanToFixedLenByteArray(val, t.length) + case Int32: + return convertInt32ToFixedLenByteArray(val, t.length) + case Int64: + return convertInt64ToFixedLenByteArray(val, t.length) + case Int96: + return convertInt96ToFixedLenByteArray(val, t.length) + case Float: + return convertFloatToFixedLenByteArray(val, t.length) + case Double: + return convertDoubleToFixedLenByteArray(val, t.length) + case ByteArray, FixedLenByteArray: + return convertByteArrayToFixedLenByteArray(val, t.length) + default: + return makeValueBytes(FixedLenByteArray, make([]byte, t.length)), nil + } +} + +// BE128 stands for "big-endian 128 bits". This type is used as a special case +// for fixed-length byte arrays of 16 bytes, which are commonly used to +// represent columns of random unique identifiers such as UUIDs. +// +// Comparisons of BE128 values use the natural byte order, the zeroth byte is +// the most significant byte. +// +// The special case is intended to provide optimizations based on the knowledge +// that the values are 16 bytes long. Stronger type checking can also be applied +// by the compiler when using [16]byte values rather than []byte, reducing the +// risk of errors on these common code paths. +type be128Type struct { + isUUID bool +} + +func (t be128Type) String() string { return "FIXED_LEN_BYTE_ARRAY(16)" } + +func (t be128Type) Kind() Kind { return FixedLenByteArray } + +func (t be128Type) Length() int { return 16 } + +func (t be128Type) EstimateSize(n int) int { return 16 * n } + +func (t be128Type) EstimateNumValues(n int) int { return n / 16 } + +func (t be128Type) Compare(a, b Value) int { return compareBE128(a.be128(), b.be128()) } + +func (t be128Type) ColumnOrder() *format.ColumnOrder { return &typeDefinedColumnOrder } + +func (t be128Type) LogicalType() *format.LogicalType { return nil } + +func (t be128Type) ConvertedType() *deprecated.ConvertedType { return nil } + +func (t be128Type) PhysicalType() *format.Type { return &physicalTypes[FixedLenByteArray] } + +func (t be128Type) NewColumnIndexer(sizeLimit int) ColumnIndexer { + return newBE128ColumnIndexer() +} + +func (t be128Type) NewColumnBuffer(columnIndex, numValues int) ColumnBuffer { + return newBE128ColumnBuffer(t, makeColumnIndex(columnIndex), makeNumValues(numValues)) +} + +func (t be128Type) NewDictionary(columnIndex, numValues int, data encoding.Values) Dictionary { + return newBE128Dictionary(t, makeColumnIndex(columnIndex), makeNumValues(numValues), data) +} + +func (t be128Type) NewPage(columnIndex, numValues int, data encoding.Values) Page { + return newBE128Page(t, makeColumnIndex(columnIndex), makeNumValues(numValues), data) +} + +func (t be128Type) NewValues(values []byte, _ []uint32) encoding.Values { + return encoding.FixedLenByteArrayValues(values, 16) +} + +func (t be128Type) Encode(dst []byte, src encoding.Values, enc encoding.Encoding) ([]byte, error) { + return encoding.EncodeFixedLenByteArray(dst, src, enc) +} + +func (t be128Type) Decode(dst encoding.Values, src []byte, enc encoding.Encoding) (encoding.Values, error) { + return encoding.DecodeFixedLenByteArray(dst, src, enc) +} + +func (t be128Type) EstimateDecodeSize(numValues int, src []byte, enc encoding.Encoding) int { + return t.EstimateSize(numValues) +} + +func (t be128Type) AssignValue(dst reflect.Value, src Value) error { + return fixedLenByteArrayType{length: 16, isUUID: t.isUUID}.AssignValue(dst, src) +} + +func (t be128Type) ConvertValue(val Value, typ Type) (Value, error) { + return fixedLenByteArrayType{length: 16, isUUID: t.isUUID}.ConvertValue(val, typ) +} + +// FixedLenByteArrayType constructs a type for fixed-length values of the given +// size (in bytes). +func FixedLenByteArrayType(length int) Type { + switch length { + case 16: + return be128Type{} + default: + return fixedLenByteArrayType{length: length} + } +} diff --git a/vendor/github.com/parquet-go/parquet-go/type_float.go b/vendor/github.com/parquet-go/parquet-go/type_float.go new file mode 100644 index 000000000..104205621 --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/type_float.go @@ -0,0 +1,90 @@ +package parquet + +import ( + "reflect" + + "github.com/parquet-go/parquet-go/deprecated" + "github.com/parquet-go/parquet-go/encoding" + "github.com/parquet-go/parquet-go/format" +) + +type floatType struct{} + +func (t floatType) String() string { return "FLOAT" } +func (t floatType) Kind() Kind { return Float } +func (t floatType) Length() int { return 32 } +func (t floatType) EstimateSize(n int) int { return 4 * n } +func (t floatType) EstimateNumValues(n int) int { return n / 4 } +func (t floatType) Compare(a, b Value) int { return compareFloat32(a.float(), b.float()) } +func (t floatType) ColumnOrder() *format.ColumnOrder { return &typeDefinedColumnOrder } +func (t floatType) LogicalType() *format.LogicalType { return nil } +func (t floatType) ConvertedType() *deprecated.ConvertedType { return nil } +func (t floatType) PhysicalType() *format.Type { return &physicalTypes[Float] } + +func (t floatType) NewColumnIndexer(sizeLimit int) ColumnIndexer { + return newFloatColumnIndexer() +} + +func (t floatType) NewColumnBuffer(columnIndex, numValues int) ColumnBuffer { + return newFloatColumnBuffer(t, makeColumnIndex(columnIndex), makeNumValues(numValues)) +} + +func (t floatType) NewDictionary(columnIndex, numValues int, data encoding.Values) Dictionary { + return newFloatDictionary(t, makeColumnIndex(columnIndex), makeNumValues(numValues), data) +} + +func (t floatType) NewPage(columnIndex, numValues int, data encoding.Values) Page { + return newFloatPage(t, makeColumnIndex(columnIndex), makeNumValues(numValues), data) +} + +func (t floatType) NewValues(values []byte, _ []uint32) encoding.Values { + return encoding.FloatValuesFromBytes(values) +} + +func (t floatType) Encode(dst []byte, src encoding.Values, enc encoding.Encoding) ([]byte, error) { + return encoding.EncodeFloat(dst, src, enc) +} + +func (t floatType) Decode(dst encoding.Values, src []byte, enc encoding.Encoding) (encoding.Values, error) { + return encoding.DecodeFloat(dst, src, enc) +} + +func (t floatType) EstimateDecodeSize(numValues int, src []byte, enc encoding.Encoding) int { + return t.EstimateSize(numValues) +} + +func (t floatType) AssignValue(dst reflect.Value, src Value) error { + v := src.float() + switch dst.Kind() { + case reflect.Float32, reflect.Float64: + dst.SetFloat(float64(v)) + default: + dst.Set(reflect.ValueOf(v)) + } + return nil +} + +func (t floatType) ConvertValue(val Value, typ Type) (Value, error) { + switch typ.(type) { + case *stringType: + return convertStringToFloat(val) + } + switch typ.Kind() { + case Boolean: + return convertBooleanToFloat(val) + case Int32: + return convertInt32ToFloat(val) + case Int64: + return convertInt64ToFloat(val) + case Int96: + return convertInt96ToFloat(val) + case Float: + return val, nil + case Double: + return convertDoubleToFloat(val) + case ByteArray, FixedLenByteArray: + return convertByteArrayToFloat(val) + default: + return makeValueKind(Float), nil + } +} diff --git a/vendor/github.com/parquet-go/parquet-go/type_geography.go b/vendor/github.com/parquet-go/parquet-go/type_geography.go new file mode 100644 index 000000000..f39b0418e --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/type_geography.go @@ -0,0 +1,137 @@ +package parquet + +import ( + "errors" + "reflect" + + "github.com/parquet-go/parquet-go/deprecated" + "github.com/parquet-go/parquet-go/encoding" + "github.com/parquet-go/parquet-go/format" + "github.com/twpayne/go-geom" + "github.com/twpayne/go-geom/encoding/wkb" +) + +func Geography(crs string, algorithm format.EdgeInterpolationAlgorithm) Node { + return Leaf(&geographyType{CRS: crs, Algorithm: algorithm}) +} + +type geographyType format.GeographyType + +func (t *geographyType) String() string { return (*format.GeographyType)(t).String() } + +func (t *geographyType) Kind() Kind { return byteArrayType{}.Kind() } + +func (t *geographyType) Length() int { return byteArrayType{}.Length() } + +func (t *geographyType) EstimateSize(n int) int { return byteArrayType{}.EstimateSize(n) } + +func (t *geographyType) EstimateNumValues(n int) int { return byteArrayType{}.EstimateNumValues(n) } + +func (t *geographyType) Compare(a, b Value) int { return byteArrayType{}.Compare(a, b) } + +func (t *geographyType) ColumnOrder() *format.ColumnOrder { return byteArrayType{}.ColumnOrder() } + +func (t *geographyType) PhysicalType() *format.Type { return byteArrayType{}.PhysicalType() } + +func (t *geographyType) LogicalType() *format.LogicalType { + f := &format.LogicalType{Geography: &format.GeographyType{ + CRS: t.CRS, + Algorithm: t.Algorithm, + }} + if t.CRS == "" { + f.Geography.CRS = format.GeometryDefaultCRS + } + if t.Algorithm != 0 { + f.Geography.Algorithm = t.Algorithm + } + + return f +} + +func (t *geographyType) ConvertedType() *deprecated.ConvertedType { + return nil +} + +func (t *geographyType) NewColumnIndexer(sizeLimit int) ColumnIndexer { + return byteArrayType{}.NewColumnIndexer(sizeLimit) +} + +func (t *geographyType) NewDictionary(columnIndex, numValues int, data encoding.Values) Dictionary { + return byteArrayType{}.NewDictionary(columnIndex, numValues, data) +} + +func (t *geographyType) NewColumnBuffer(columnIndex, numValues int) ColumnBuffer { + return byteArrayType{}.NewColumnBuffer(columnIndex, numValues) +} + +func (t *geographyType) NewPage(columnIndex, numValues int, data encoding.Values) Page { + return byteArrayType{}.NewPage(columnIndex, numValues, data) +} + +func (t *geographyType) NewValues(values []byte, offsets []uint32) encoding.Values { + return byteArrayType{}.NewValues(values, offsets) +} + +func (t *geographyType) Encode(dst []byte, src encoding.Values, enc encoding.Encoding) ([]byte, error) { + return byteArrayType{}.Encode(dst, src, enc) +} + +func (t *geographyType) Decode(dst encoding.Values, src []byte, enc encoding.Encoding) (encoding.Values, error) { + return byteArrayType{}.Decode(dst, src, enc) +} + +func (t *geographyType) EstimateDecodeSize(numValues int, src []byte, enc encoding.Encoding) int { + return byteArrayType{}.EstimateDecodeSize(numValues, src, enc) +} + +func (t *geographyType) AssignValue(dst reflect.Value, src Value) error { + switch dst.Type() { + case reflect.TypeOf(geom.T(nil)): + if src.IsNull() { + dst.Set(reflect.Zero(dst.Type())) + return nil + } + + data := src.Bytes() + g, err := wkb.Unmarshal(data) + if err != nil { + return err + } + dst.Set(reflect.ValueOf(g)) + return nil + case reflect.TypeOf((*geom.T)(nil)).Elem(): + if src.IsNull() { + dst.Set(reflect.Zero(dst.Type())) + return nil + } + + data := src.Bytes() + g, err := wkb.Unmarshal(data) + if err != nil { + return err + } + dst.Set(reflect.ValueOf(g)) + return nil + default: + return byteArrayType{}.AssignValue(dst, src) + } +} + +func (t *geographyType) ConvertValue(val Value, typ Type) (Value, error) { + switch src := typ.(type) { + case *geographyType: + if src.LogicalType().Geography.CRS != t.CRS { + return Value{}, errors.New("cannot convert between geography types with different CRS") + } + if src.LogicalType().Geography.Algorithm != t.LogicalType().Geography.Algorithm { + return Value{}, errors.New("cannot convert between geography types with different Algorithm") + } + return val, nil + case *geometryType: + if src.LogicalType().Geometry.CRS != t.CRS { + return Value{}, errors.New("cannot convert between geography and geometry types with different CRS") + } + return val, nil + } + return byteArrayType{}.ConvertValue(val, typ) +} diff --git a/vendor/github.com/parquet-go/parquet-go/type_geometry.go b/vendor/github.com/parquet-go/parquet-go/type_geometry.go new file mode 100644 index 000000000..54f53ef4f --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/type_geometry.go @@ -0,0 +1,128 @@ +package parquet + +import ( + "errors" + "reflect" + + "github.com/parquet-go/parquet-go/deprecated" + "github.com/parquet-go/parquet-go/encoding" + "github.com/parquet-go/parquet-go/format" + "github.com/twpayne/go-geom" + "github.com/twpayne/go-geom/encoding/wkb" +) + +func Geometry(crs string) Node { return Leaf(&geometryType{CRS: crs}) } + +type geometryType format.GeometryType + +var geometryDefaultCRSLogicType = format.GeometryType{ + CRS: format.GeometryDefaultCRS, +} + +func (t *geometryType) String() string { return (*format.GeometryType)(t).String() } + +func (t *geometryType) Kind() Kind { return byteArrayType{}.Kind() } + +func (t *geometryType) Length() int { return byteArrayType{}.Length() } + +func (t *geometryType) EstimateSize(n int) int { return byteArrayType{}.EstimateSize(n) } + +func (t *geometryType) EstimateNumValues(n int) int { return byteArrayType{}.EstimateNumValues(n) } + +func (t *geometryType) Compare(a, b Value) int { return byteArrayType{}.Compare(a, b) } + +func (t *geometryType) ColumnOrder() *format.ColumnOrder { return byteArrayType{}.ColumnOrder() } + +func (t *geometryType) PhysicalType() *format.Type { return byteArrayType{}.PhysicalType() } + +func (t *geometryType) LogicalType() *format.LogicalType { + if t.CRS == "" { + return &format.LogicalType{Geometry: &geometryDefaultCRSLogicType} + } + return &format.LogicalType{Geometry: (*format.GeometryType)(t)} +} + +func (t *geometryType) ConvertedType() *deprecated.ConvertedType { + return nil +} + +func (t *geometryType) NewColumnIndexer(sizeLimit int) ColumnIndexer { + return byteArrayType{}.NewColumnIndexer(sizeLimit) +} + +func (t *geometryType) NewDictionary(columnIndex, numValues int, data encoding.Values) Dictionary { + return byteArrayType{}.NewDictionary(columnIndex, numValues, data) +} + +func (t *geometryType) NewColumnBuffer(columnIndex, numValues int) ColumnBuffer { + return byteArrayType{}.NewColumnBuffer(columnIndex, numValues) +} + +func (t *geometryType) NewPage(columnIndex, numValues int, data encoding.Values) Page { + return byteArrayType{}.NewPage(columnIndex, numValues, data) +} + +func (t *geometryType) NewValues(values []byte, offsets []uint32) encoding.Values { + return byteArrayType{}.NewValues(values, offsets) +} + +func (t *geometryType) Encode(dst []byte, src encoding.Values, enc encoding.Encoding) ([]byte, error) { + return byteArrayType{}.Encode(dst, src, enc) +} + +func (t *geometryType) Decode(dst encoding.Values, src []byte, enc encoding.Encoding) (encoding.Values, error) { + return byteArrayType{}.Decode(dst, src, enc) +} + +func (t *geometryType) EstimateDecodeSize(numValues int, src []byte, enc encoding.Encoding) int { + return byteArrayType{}.EstimateDecodeSize(numValues, src, enc) +} + +func (t *geometryType) AssignValue(dst reflect.Value, src Value) error { + switch dst.Type() { + case reflect.TypeOf(geom.T(nil)): + if src.IsNull() { + dst.Set(reflect.Zero(dst.Type())) + return nil + } + + data := src.Bytes() + g, err := wkb.Unmarshal(data) + if err != nil { + return err + } + dst.Set(reflect.ValueOf(g)) + return nil + case reflect.TypeOf((*geom.T)(nil)).Elem(): + if src.IsNull() { + dst.Set(reflect.Zero(dst.Type())) + return nil + } + + data := src.Bytes() + g, err := wkb.Unmarshal(data) + if err != nil { + return err + } + dst.Set(reflect.ValueOf(g)) + return nil + default: + return byteArrayType{}.AssignValue(dst, src) + } +} + +func (t *geometryType) ConvertValue(val Value, typ Type) (Value, error) { + switch src := typ.(type) { + case *geometryType: + if src.LogicalType().Geometry.CRS != t.CRS { + return Value{}, errors.New("cannot convert between geometry types with different CRS") + } + return val, nil + case *geographyType: + if src.LogicalType().Geography.CRS != t.CRS { + return Value{}, errors.New("cannot convert between geography and geometry types with different CRS") + } + return val, nil + } + return byteArrayType{}.ConvertValue(val, typ) +} diff --git a/vendor/github.com/parquet-go/parquet-go/type_group.go b/vendor/github.com/parquet-go/parquet-go/type_group.go new file mode 100644 index 000000000..df9610a9a --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/type_group.go @@ -0,0 +1,75 @@ +package parquet + +import ( + "reflect" + + "github.com/parquet-go/parquet-go/deprecated" + "github.com/parquet-go/parquet-go/encoding" + "github.com/parquet-go/parquet-go/format" +) + +type groupType struct{} + +func (groupType) String() string { return "group" } + +func (groupType) Kind() Kind { + panic("cannot call Kind on parquet group") +} + +func (groupType) Compare(Value, Value) int { + panic("cannot compare values on parquet group") +} + +func (groupType) NewColumnIndexer(int) ColumnIndexer { + panic("cannot create column indexer from parquet group") +} + +func (groupType) NewDictionary(int, int, encoding.Values) Dictionary { + panic("cannot create dictionary from parquet group") +} + +func (t groupType) NewColumnBuffer(int, int) ColumnBuffer { + panic("cannot create column buffer from parquet group") +} + +func (t groupType) NewPage(int, int, encoding.Values) Page { + panic("cannot create page from parquet group") +} + +func (t groupType) NewValues(_ []byte, _ []uint32) encoding.Values { + panic("cannot create values from parquet group") +} + +func (groupType) Encode(_ []byte, _ encoding.Values, _ encoding.Encoding) ([]byte, error) { + panic("cannot encode parquet group") +} + +func (groupType) Decode(_ encoding.Values, _ []byte, _ encoding.Encoding) (encoding.Values, error) { + panic("cannot decode parquet group") +} + +func (groupType) EstimateDecodeSize(_ int, _ []byte, _ encoding.Encoding) int { + panic("cannot estimate decode size of parquet group") +} + +func (groupType) AssignValue(reflect.Value, Value) error { + panic("cannot assign value to a parquet group") +} + +func (t groupType) ConvertValue(Value, Type) (Value, error) { + panic("cannot convert value to a parquet group") +} + +func (groupType) Length() int { return 0 } + +func (groupType) EstimateSize(int) int { return 0 } + +func (groupType) EstimateNumValues(int) int { return 0 } + +func (groupType) ColumnOrder() *format.ColumnOrder { return nil } + +func (groupType) PhysicalType() *format.Type { return nil } + +func (groupType) LogicalType() *format.LogicalType { return nil } + +func (groupType) ConvertedType() *deprecated.ConvertedType { return nil } diff --git a/vendor/github.com/parquet-go/parquet-go/type_int32.go b/vendor/github.com/parquet-go/parquet-go/type_int32.go new file mode 100644 index 000000000..bf9200b36 --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/type_int32.go @@ -0,0 +1,132 @@ +package parquet + +import ( + "reflect" + + "github.com/parquet-go/parquet-go/deprecated" + "github.com/parquet-go/parquet-go/encoding" + "github.com/parquet-go/parquet-go/format" +) + +type int32Type struct{} + +var int32LogicalType = format.LogicalType{Integer: &format.IntType{ + BitWidth: 32, + IsSigned: true, +}} + +func (t int32Type) String() string { return "INT32" } +func (t int32Type) Kind() Kind { return Int32 } +func (t int32Type) Length() int { return 32 } +func (t int32Type) EstimateSize(n int) int { return 4 * n } +func (t int32Type) EstimateNumValues(n int) int { return n / 4 } +func (t int32Type) Compare(a, b Value) int { return compareInt32(a.int32(), b.int32()) } +func (t int32Type) ColumnOrder() *format.ColumnOrder { return &typeDefinedColumnOrder } +func (t int32Type) LogicalType() *format.LogicalType { return &int32LogicalType } +func (t int32Type) ConvertedType() *deprecated.ConvertedType { return nil } +func (t int32Type) PhysicalType() *format.Type { return &physicalTypes[Int32] } + +func (t int32Type) NewColumnIndexer(sizeLimit int) ColumnIndexer { + return newInt32ColumnIndexer() +} + +func (t int32Type) NewColumnBuffer(columnIndex, numValues int) ColumnBuffer { + return newInt32ColumnBuffer(t, makeColumnIndex(columnIndex), makeNumValues(numValues)) +} + +func (t int32Type) NewDictionary(columnIndex, numValues int, data encoding.Values) Dictionary { + return newInt32Dictionary(t, makeColumnIndex(columnIndex), makeNumValues(numValues), data) +} + +func (t int32Type) NewPage(columnIndex, numValues int, data encoding.Values) Page { + return newInt32Page(t, makeColumnIndex(columnIndex), makeNumValues(numValues), data) +} + +func (t int32Type) NewValues(values []byte, _ []uint32) encoding.Values { + return encoding.Int32ValuesFromBytes(values) +} + +func (t int32Type) Encode(dst []byte, src encoding.Values, enc encoding.Encoding) ([]byte, error) { + return encoding.EncodeInt32(dst, src, enc) +} + +func (t int32Type) Decode(dst encoding.Values, src []byte, enc encoding.Encoding) (encoding.Values, error) { + return encoding.DecodeInt32(dst, src, enc) +} + +func (t int32Type) EstimateDecodeSize(numValues int, src []byte, enc encoding.Encoding) int { + return t.EstimateSize(numValues) +} + +func (t int32Type) AssignValue(dst reflect.Value, src Value) error { + v := src.int32() + switch dst.Kind() { + case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int, reflect.Int64: + dst.SetInt(int64(v)) + case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint, reflect.Uint64, reflect.Uintptr: + dst.SetUint(uint64(v)) + default: + dst.Set(reflect.ValueOf(v)) + } + return nil +} + +func (t int32Type) ConvertValue(val Value, typ Type) (Value, error) { + switch typ.(type) { + case *stringType: + return convertStringToInt32(val) + } + switch typ.Kind() { + case Boolean: + return convertBooleanToInt32(val) + case Int32: + return val, nil + case Int64: + return convertInt64ToInt32(val) + case Int96: + return convertInt96ToInt32(val) + case Float: + return convertFloatToInt32(val) + case Double: + return convertDoubleToInt32(val) + case ByteArray, FixedLenByteArray: + return convertByteArrayToInt32(val) + default: + return makeValueKind(Int32), nil + } +} + +type uint32Type struct{ int32Type } + +func (t uint32Type) Compare(a, b Value) int { + return compareUint32(a.uint32(), b.uint32()) +} + +func (t uint32Type) NewColumnIndexer(sizeLimit int) ColumnIndexer { + return newUint32ColumnIndexer() +} + +func (t uint32Type) NewColumnBuffer(columnIndex, numValues int) ColumnBuffer { + return newUint32ColumnBuffer(t, makeColumnIndex(columnIndex), makeNumValues(numValues)) +} + +func (t uint32Type) NewDictionary(columnIndex, numValues int, data encoding.Values) Dictionary { + return newUint32Dictionary(t, makeColumnIndex(columnIndex), makeNumValues(numValues), data) +} + +func (t uint32Type) NewPage(columnIndex, numValues int, data encoding.Values) Page { + return newUint32Page(t, makeColumnIndex(columnIndex), makeNumValues(numValues), data) +} + +func (t uint32Type) AssignValue(dst reflect.Value, src Value) error { + v := src.uint32() + switch dst.Kind() { + case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int, reflect.Int64: + dst.SetInt(int64(v)) + case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint, reflect.Uint64, reflect.Uintptr: + dst.SetUint(uint64(v)) + default: + dst.Set(reflect.ValueOf(v)) + } + return nil +} diff --git a/vendor/github.com/parquet-go/parquet-go/type_int64.go b/vendor/github.com/parquet-go/parquet-go/type_int64.go new file mode 100644 index 000000000..db1287b69 --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/type_int64.go @@ -0,0 +1,119 @@ +package parquet + +import ( + "reflect" + + "github.com/parquet-go/parquet-go/deprecated" + "github.com/parquet-go/parquet-go/encoding" + "github.com/parquet-go/parquet-go/format" +) + +type int64Type struct{} + +var int64LogicalType = format.LogicalType{Integer: &format.IntType{ + BitWidth: 64, + IsSigned: true, +}} + +func (t int64Type) String() string { return "INT64" } +func (t int64Type) Kind() Kind { return Int64 } +func (t int64Type) Length() int { return 64 } +func (t int64Type) EstimateSize(n int) int { return 8 * n } +func (t int64Type) EstimateNumValues(n int) int { return n / 8 } +func (t int64Type) Compare(a, b Value) int { return compareInt64(a.int64(), b.int64()) } +func (t int64Type) ColumnOrder() *format.ColumnOrder { return &typeDefinedColumnOrder } +func (t int64Type) LogicalType() *format.LogicalType { return &int64LogicalType } +func (t int64Type) ConvertedType() *deprecated.ConvertedType { return nil } +func (t int64Type) PhysicalType() *format.Type { return &physicalTypes[Int64] } + +func (t int64Type) NewColumnIndexer(sizeLimit int) ColumnIndexer { + return newInt64ColumnIndexer() +} + +func (t int64Type) NewColumnBuffer(columnIndex, numValues int) ColumnBuffer { + return newInt64ColumnBuffer(t, makeColumnIndex(columnIndex), makeNumValues(numValues)) +} + +func (t int64Type) NewDictionary(columnIndex, numValues int, data encoding.Values) Dictionary { + return newInt64Dictionary(t, makeColumnIndex(columnIndex), makeNumValues(numValues), data) +} + +func (t int64Type) NewPage(columnIndex, numValues int, data encoding.Values) Page { + return newInt64Page(t, makeColumnIndex(columnIndex), makeNumValues(numValues), data) +} + +func (t int64Type) NewValues(values []byte, _ []uint32) encoding.Values { + return encoding.Int64ValuesFromBytes(values) +} + +func (t int64Type) Encode(dst []byte, src encoding.Values, enc encoding.Encoding) ([]byte, error) { + return encoding.EncodeInt64(dst, src, enc) +} + +func (t int64Type) Decode(dst encoding.Values, src []byte, enc encoding.Encoding) (encoding.Values, error) { + return encoding.DecodeInt64(dst, src, enc) +} + +func (t int64Type) EstimateDecodeSize(numValues int, src []byte, enc encoding.Encoding) int { + return t.EstimateSize(numValues) +} + +func (t int64Type) AssignValue(dst reflect.Value, src Value) error { + v := src.int64() + switch dst.Kind() { + case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Int: + dst.SetInt(v) + case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uint, reflect.Uintptr: + dst.SetUint(uint64(v)) + default: + dst.Set(reflect.ValueOf(v)) + } + return nil +} + +func (t int64Type) ConvertValue(val Value, typ Type) (Value, error) { + switch typ.(type) { + case *stringType: + return convertStringToInt64(val) + } + switch typ.Kind() { + case Boolean: + return convertBooleanToInt64(val) + case Int32: + return convertInt32ToInt64(val) + case Int64: + return val, nil + case Int96: + return convertInt96ToInt64(val) + case Float: + return convertFloatToInt64(val) + case Double: + return convertDoubleToInt64(val) + case ByteArray, FixedLenByteArray: + return convertByteArrayToInt64(val) + default: + return makeValueKind(Int64), nil + } +} + +type uint64Type struct{ int64Type } + +func (t uint64Type) Compare(a, b Value) int { + return compareUint64(a.uint64(), b.uint64()) +} + +func (t uint64Type) NewColumnIndexer(sizeLimit int) ColumnIndexer { + return newUint64ColumnIndexer() +} + +func (t uint64Type) NewColumnBuffer(columnIndex, numValues int) ColumnBuffer { + return newUint64ColumnBuffer(t, makeColumnIndex(columnIndex), makeNumValues(numValues)) +} + +func (t uint64Type) NewDictionary(columnIndex, numValues int, data encoding.Values) Dictionary { + return newUint64Dictionary(t, makeColumnIndex(columnIndex), makeNumValues(numValues), data) +} + +func (t uint64Type) NewPage(columnIndex, numValues int, data encoding.Values) Page { + return newUint64Page(t, makeColumnIndex(columnIndex), makeNumValues(numValues), data) +} diff --git a/vendor/github.com/parquet-go/parquet-go/type_int96.go b/vendor/github.com/parquet-go/parquet-go/type_int96.go new file mode 100644 index 000000000..9abb6dfd1 --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/type_int96.go @@ -0,0 +1,86 @@ +package parquet + +import ( + "reflect" + + "github.com/parquet-go/parquet-go/deprecated" + "github.com/parquet-go/parquet-go/encoding" + "github.com/parquet-go/parquet-go/format" +) + +type int96Type struct{} + +func (t int96Type) String() string { return "INT96" } + +func (t int96Type) Kind() Kind { return Int96 } +func (t int96Type) Length() int { return 96 } +func (t int96Type) EstimateSize(n int) int { return 12 * n } +func (t int96Type) EstimateNumValues(n int) int { return n / 12 } +func (t int96Type) Compare(a, b Value) int { return compareInt96(a.int96(), b.int96()) } +func (t int96Type) ColumnOrder() *format.ColumnOrder { return &typeDefinedColumnOrder } +func (t int96Type) LogicalType() *format.LogicalType { return nil } +func (t int96Type) ConvertedType() *deprecated.ConvertedType { return nil } +func (t int96Type) PhysicalType() *format.Type { return &physicalTypes[Int96] } + +func (t int96Type) NewColumnIndexer(sizeLimit int) ColumnIndexer { + return newInt96ColumnIndexer() +} + +func (t int96Type) NewColumnBuffer(columnIndex, numValues int) ColumnBuffer { + return newInt96ColumnBuffer(t, makeColumnIndex(columnIndex), makeNumValues(numValues)) +} + +func (t int96Type) NewDictionary(columnIndex, numValues int, data encoding.Values) Dictionary { + return newInt96Dictionary(t, makeColumnIndex(columnIndex), makeNumValues(numValues), data) +} + +func (t int96Type) NewPage(columnIndex, numValues int, data encoding.Values) Page { + return newInt96Page(t, makeColumnIndex(columnIndex), makeNumValues(numValues), data) +} + +func (t int96Type) NewValues(values []byte, _ []uint32) encoding.Values { + return encoding.Int96ValuesFromBytes(values) +} + +func (t int96Type) Encode(dst []byte, src encoding.Values, enc encoding.Encoding) ([]byte, error) { + return encoding.EncodeInt96(dst, src, enc) +} + +func (t int96Type) Decode(dst encoding.Values, src []byte, enc encoding.Encoding) (encoding.Values, error) { + return encoding.DecodeInt96(dst, src, enc) +} + +func (t int96Type) EstimateDecodeSize(numValues int, src []byte, enc encoding.Encoding) int { + return t.EstimateSize(numValues) +} + +func (t int96Type) AssignValue(dst reflect.Value, src Value) error { + v := src.Int96() + dst.Set(reflect.ValueOf(v)) + return nil +} + +func (t int96Type) ConvertValue(val Value, typ Type) (Value, error) { + switch typ.(type) { + case *stringType: + return convertStringToInt96(val) + } + switch typ.Kind() { + case Boolean: + return convertBooleanToInt96(val) + case Int32: + return convertInt32ToInt96(val) + case Int64: + return convertInt64ToInt96(val) + case Int96: + return val, nil + case Float: + return convertFloatToInt96(val) + case Double: + return convertDoubleToInt96(val) + case ByteArray, FixedLenByteArray: + return convertByteArrayToInt96(val) + default: + return makeValueKind(Int96), nil + } +} diff --git a/vendor/github.com/parquet-go/parquet-go/type_int_logical.go b/vendor/github.com/parquet-go/parquet-go/type_int_logical.go new file mode 100644 index 000000000..8a2f7ed76 --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/type_int_logical.go @@ -0,0 +1,202 @@ +package parquet + +import ( + "fmt" + "math/bits" + "reflect" + + "github.com/parquet-go/parquet-go/deprecated" + "github.com/parquet-go/parquet-go/encoding" + "github.com/parquet-go/parquet-go/format" +) + +// Int constructs a leaf node of signed integer logical type of the given bit +// width. +// +// The bit width must be one of 8, 16, 32, 64, or the function will panic. +func Int(bitWidth int) Node { + return Leaf(integerType(bitWidth, &signedIntTypes)) +} + +// Uint constructs a leaf node of unsigned integer logical type of the given +// bit width. +// +// The bit width must be one of 8, 16, 32, 64, or the function will panic. +func Uint(bitWidth int) Node { + return Leaf(integerType(bitWidth, &unsignedIntTypes)) +} + +func integerType(bitWidth int, types *[4]intType) *intType { + switch bitWidth { + case 8: + return &types[0] + case 16: + return &types[1] + case 32: + return &types[2] + case 64: + return &types[3] + default: + panic(fmt.Sprintf("cannot create a %d bits parquet integer node", bitWidth)) + } +} + +var signedIntTypes = [...]intType{ + {BitWidth: 8, IsSigned: true}, + {BitWidth: 16, IsSigned: true}, + {BitWidth: 32, IsSigned: true}, + {BitWidth: 64, IsSigned: true}, +} + +var unsignedIntTypes = [...]intType{ + {BitWidth: 8, IsSigned: false}, + {BitWidth: 16, IsSigned: false}, + {BitWidth: 32, IsSigned: false}, + {BitWidth: 64, IsSigned: false}, +} + +var signedLogicalIntTypes = [...]format.LogicalType{ + {Integer: (*format.IntType)(&signedIntTypes[0])}, + {Integer: (*format.IntType)(&signedIntTypes[1])}, + {Integer: (*format.IntType)(&signedIntTypes[2])}, + {Integer: (*format.IntType)(&signedIntTypes[3])}, +} + +var unsignedLogicalIntTypes = [...]format.LogicalType{ + {Integer: (*format.IntType)(&unsignedIntTypes[0])}, + {Integer: (*format.IntType)(&unsignedIntTypes[1])}, + {Integer: (*format.IntType)(&unsignedIntTypes[2])}, + {Integer: (*format.IntType)(&unsignedIntTypes[3])}, +} + +type intType format.IntType + +func (t *intType) baseType() Type { + if t.IsSigned { + if t.BitWidth == 64 { + return int64Type{} + } else { + return int32Type{} + } + } else { + if t.BitWidth == 64 { + return uint64Type{} + } else { + return uint32Type{} + } + } +} + +func (t *intType) String() string { return (*format.IntType)(t).String() } + +func (t *intType) Kind() Kind { return t.baseType().Kind() } + +func (t *intType) Length() int { return int(t.BitWidth) } + +func (t *intType) EstimateSize(n int) int { return (int(t.BitWidth) / 8) * n } + +func (t *intType) EstimateNumValues(n int) int { return n / (int(t.BitWidth) / 8) } + +func (t *intType) Compare(a, b Value) int { + // This code is similar to t.baseType().Compare(a,b) but comparison methods + // tend to be invoked a lot (e.g. when sorting) so avoiding the interface + // indirection in this case yields much better throughput in some cases. + if t.BitWidth == 64 { + i1 := a.int64() + i2 := b.int64() + if t.IsSigned { + return compareInt64(i1, i2) + } else { + return compareUint64(uint64(i1), uint64(i2)) + } + } else { + i1 := a.int32() + i2 := b.int32() + if t.IsSigned { + return compareInt32(i1, i2) + } else { + return compareUint32(uint32(i1), uint32(i2)) + } + } +} + +func (t *intType) ColumnOrder() *format.ColumnOrder { return t.baseType().ColumnOrder() } + +func (t *intType) PhysicalType() *format.Type { return t.baseType().PhysicalType() } + +func (t *intType) LogicalType() *format.LogicalType { + switch t { + case &signedIntTypes[0]: + return &signedLogicalIntTypes[0] + case &signedIntTypes[1]: + return &signedLogicalIntTypes[1] + case &signedIntTypes[2]: + return &signedLogicalIntTypes[2] + case &signedIntTypes[3]: + return &signedLogicalIntTypes[3] + case &unsignedIntTypes[0]: + return &unsignedLogicalIntTypes[0] + case &unsignedIntTypes[1]: + return &unsignedLogicalIntTypes[1] + case &unsignedIntTypes[2]: + return &unsignedLogicalIntTypes[2] + case &unsignedIntTypes[3]: + return &unsignedLogicalIntTypes[3] + default: + return &format.LogicalType{Integer: (*format.IntType)(t)} + } +} + +func (t *intType) ConvertedType() *deprecated.ConvertedType { + convertedType := bits.Len8(uint8(t.BitWidth)/8) - 1 // 8=>0, 16=>1, 32=>2, 64=>4 + if t.IsSigned { + convertedType += int(deprecated.Int8) + } else { + convertedType += int(deprecated.Uint8) + } + return &convertedTypes[convertedType] +} + +func (t *intType) NewColumnIndexer(sizeLimit int) ColumnIndexer { + return t.baseType().NewColumnIndexer(sizeLimit) +} + +func (t *intType) NewColumnBuffer(columnIndex, numValues int) ColumnBuffer { + return t.baseType().NewColumnBuffer(columnIndex, numValues) +} + +func (t *intType) NewDictionary(columnIndex, numValues int, data encoding.Values) Dictionary { + return t.baseType().NewDictionary(columnIndex, numValues, data) +} + +func (t *intType) NewPage(columnIndex, numValues int, data encoding.Values) Page { + return t.baseType().NewPage(columnIndex, numValues, data) +} + +func (t *intType) NewValues(values []byte, offsets []uint32) encoding.Values { + return t.baseType().NewValues(values, offsets) +} + +func (t *intType) Encode(dst []byte, src encoding.Values, enc encoding.Encoding) ([]byte, error) { + return t.baseType().Encode(dst, src, enc) +} + +func (t *intType) Decode(dst encoding.Values, src []byte, enc encoding.Encoding) (encoding.Values, error) { + return t.baseType().Decode(dst, src, enc) +} + +func (t *intType) EstimateDecodeSize(numValues int, src []byte, enc encoding.Encoding) int { + return t.baseType().EstimateDecodeSize(numValues, src, enc) +} + +func (t *intType) AssignValue(dst reflect.Value, src Value) error { + return t.baseType().AssignValue(dst, src) +} + +func (t *intType) ConvertValue(val Value, typ Type) (Value, error) { + if t.BitWidth == 64 { + return int64Type{}.ConvertValue(val, typ) + } else { + return int32Type{}.ConvertValue(val, typ) + } +} diff --git a/vendor/github.com/parquet-go/parquet-go/type_interval.go b/vendor/github.com/parquet-go/parquet-go/type_interval.go new file mode 100644 index 000000000..3988ff925 --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/type_interval.go @@ -0,0 +1,115 @@ +package parquet + +import ( + "encoding/binary" + "reflect" + + "github.com/parquet-go/parquet-go/deprecated" + "github.com/parquet-go/parquet-go/encoding" + "github.com/parquet-go/parquet-go/format" +) + +// Interval represents a Parquet INTERVAL value. +// +// The physical representation is a FIXED_LEN_BYTE_ARRAY(12) containing three +// little-endian unsigned 32-bit integers: months, days, and milliseconds. +// +// https://github.com/apache/parquet-format/blob/master/LogicalTypes.md#interval +type Interval struct { + Months uint32 + Days uint32 + Milliseconds uint32 +} + +// IntervalNode constructs a leaf node of INTERVAL logical type. +// +// https://github.com/apache/parquet-format/blob/master/LogicalTypes.md#interval +func IntervalNode() Node { return Leaf(&intervalType{}) } + +// intervalType implements the INTERVAL logical type. INTERVAL has no formal +// LogicalType entry in the Parquet Thrift spec (field 9 is reserved but +// undefined); it is expressed solely via ConvertedType = 21 (deprecated.Interval). +type intervalType struct{} + +func (t *intervalType) String() string { return "INTERVAL" } + +func (t *intervalType) Kind() Kind { return fixedLenByteArrayType{length: 12}.Kind() } + +func (t *intervalType) Length() int { return fixedLenByteArrayType{length: 12}.Length() } + +func (t *intervalType) EstimateSize(n int) int { + return fixedLenByteArrayType{length: 12}.EstimateSize(n) +} + +func (t *intervalType) EstimateNumValues(n int) int { + return fixedLenByteArrayType{length: 12}.EstimateNumValues(n) +} + +func (t *intervalType) Compare(a, b Value) int { + return fixedLenByteArrayType{length: 12}.Compare(a, b) +} + +func (t *intervalType) ColumnOrder() *format.ColumnOrder { + return fixedLenByteArrayType{length: 12}.ColumnOrder() +} + +func (t *intervalType) PhysicalType() *format.Type { + return fixedLenByteArrayType{length: 12}.PhysicalType() +} + +func (t *intervalType) LogicalType() *format.LogicalType { return nil } + +func (t *intervalType) ConvertedType() *deprecated.ConvertedType { + return &convertedTypes[deprecated.Interval] +} + +func (t *intervalType) NewColumnIndexer(sizeLimit int) ColumnIndexer { + return fixedLenByteArrayType{length: 12}.NewColumnIndexer(sizeLimit) +} + +func (t *intervalType) NewDictionary(columnIndex, numValues int, data encoding.Values) Dictionary { + return fixedLenByteArrayType{length: 12}.NewDictionary(columnIndex, numValues, data) +} + +func (t *intervalType) NewColumnBuffer(columnIndex, numValues int) ColumnBuffer { + return fixedLenByteArrayType{length: 12}.NewColumnBuffer(columnIndex, numValues) +} + +func (t *intervalType) NewPage(columnIndex, numValues int, data encoding.Values) Page { + return fixedLenByteArrayType{length: 12}.NewPage(columnIndex, numValues, data) +} + +func (t *intervalType) NewValues(values []byte, offsets []uint32) encoding.Values { + return fixedLenByteArrayType{length: 12}.NewValues(values, offsets) +} + +func (t *intervalType) Encode(dst []byte, src encoding.Values, enc encoding.Encoding) ([]byte, error) { + return fixedLenByteArrayType{length: 12}.Encode(dst, src, enc) +} + +func (t *intervalType) Decode(dst encoding.Values, src []byte, enc encoding.Encoding) (encoding.Values, error) { + return fixedLenByteArrayType{length: 12}.Decode(dst, src, enc) +} + +func (t *intervalType) EstimateDecodeSize(numValues int, src []byte, enc encoding.Encoding) int { + return fixedLenByteArrayType{length: 12}.EstimateDecodeSize(numValues, src, enc) +} + +func (t *intervalType) AssignValue(dst reflect.Value, src Value) error { + if dst.Type() == reflect.TypeOf(Interval{}) { + v := src.byteArray() + if len(v) == 12 { + dst.Set(reflect.ValueOf(Interval{ + Months: binary.LittleEndian.Uint32(v[0:4]), + Days: binary.LittleEndian.Uint32(v[4:8]), + Milliseconds: binary.LittleEndian.Uint32(v[8:12]), + })) + return nil + } + } + return fixedLenByteArrayType{length: 12}.AssignValue(dst, src) +} + +func (t *intervalType) ConvertValue(val Value, typ Type) (Value, error) { + return fixedLenByteArrayType{length: 12}.ConvertValue(val, typ) +} diff --git a/vendor/github.com/parquet-go/parquet-go/type_json.go b/vendor/github.com/parquet-go/parquet-go/type_json.go new file mode 100644 index 000000000..abf452f7f --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/type_json.go @@ -0,0 +1,106 @@ +package parquet + +import ( + "encoding/json" + "reflect" + + "github.com/parquet-go/parquet-go/deprecated" + "github.com/parquet-go/parquet-go/encoding" + "github.com/parquet-go/parquet-go/format" +) + +// JSON constructs a leaf node of JSON logical type. +// +// https://github.com/apache/parquet-format/blob/master/LogicalTypes.md#json +func JSON() Node { return Leaf(&jsonType{}) } + +var jsonLogicalType = format.LogicalType{ + Json: new(format.JsonType), +} + +type jsonType format.JsonType + +func (t *jsonType) String() string { return (*format.JsonType)(t).String() } + +func (t *jsonType) Kind() Kind { return byteArrayType{}.Kind() } + +func (t *jsonType) Length() int { return byteArrayType{}.Length() } + +func (t *jsonType) EstimateSize(n int) int { return byteArrayType{}.EstimateSize(n) } + +func (t *jsonType) EstimateNumValues(n int) int { return byteArrayType{}.EstimateNumValues(n) } + +func (t *jsonType) Compare(a, b Value) int { return byteArrayType{}.Compare(a, b) } + +func (t *jsonType) ColumnOrder() *format.ColumnOrder { return byteArrayType{}.ColumnOrder() } + +func (t *jsonType) PhysicalType() *format.Type { return byteArrayType{}.PhysicalType() } + +func (t *jsonType) LogicalType() *format.LogicalType { return &jsonLogicalType } + +func (t *jsonType) ConvertedType() *deprecated.ConvertedType { + return &convertedTypes[deprecated.Json] +} + +func (t *jsonType) NewColumnIndexer(sizeLimit int) ColumnIndexer { + return byteArrayType{}.NewColumnIndexer(sizeLimit) +} + +func (t *jsonType) NewDictionary(columnIndex, numValues int, data encoding.Values) Dictionary { + return byteArrayType{}.NewDictionary(columnIndex, numValues, data) +} + +func (t *jsonType) NewColumnBuffer(columnIndex, numValues int) ColumnBuffer { + return byteArrayType{}.NewColumnBuffer(columnIndex, numValues) +} + +func (t *jsonType) NewPage(columnIndex, numValues int, data encoding.Values) Page { + return byteArrayType{}.NewPage(columnIndex, numValues, data) +} + +func (t *jsonType) NewValues(values []byte, offsets []uint32) encoding.Values { + return byteArrayType{}.NewValues(values, offsets) +} + +func (t *jsonType) Encode(dst []byte, src encoding.Values, enc encoding.Encoding) ([]byte, error) { + return byteArrayType{}.Encode(dst, src, enc) +} + +func (t *jsonType) Decode(dst encoding.Values, src []byte, enc encoding.Encoding) (encoding.Values, error) { + return byteArrayType{}.Decode(dst, src, enc) +} + +func (t *jsonType) EstimateDecodeSize(numValues int, src []byte, enc encoding.Encoding) int { + return byteArrayType{}.EstimateDecodeSize(numValues, src, enc) +} + +func (t *jsonType) AssignValue(dst reflect.Value, src Value) error { + // Assign value using ByteArrayType for BC... + switch dst.Kind() { + case reflect.String: + return byteArrayType{}.AssignValue(dst, src) + case reflect.Slice: + if dst.Type().Elem().Kind() == reflect.Uint8 { + return byteArrayType{}.AssignValue(dst, src) + } + } + + // Otherwise handle with json.Unmarshal + b := src.byteArray() + val := reflect.New(dst.Type()).Elem() + err := json.Unmarshal(b, val.Addr().Interface()) + if err != nil { + return err + } + dst.Set(val) + return nil +} + +func (t *jsonType) ConvertValue(val Value, typ Type) (Value, error) { + switch typ.(type) { + case byteArrayType, *stringType, *jsonType: + return val, nil + default: + return val, invalidConversion(val, "JSON", typ.String()) + } +} diff --git a/vendor/github.com/parquet-go/parquet-go/type_list.go b/vendor/github.com/parquet-go/parquet-go/type_list.go new file mode 100644 index 000000000..3f17fde49 --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/type_list.go @@ -0,0 +1,86 @@ +package parquet + +import ( + "reflect" + + "github.com/parquet-go/parquet-go/deprecated" + "github.com/parquet-go/parquet-go/encoding" + "github.com/parquet-go/parquet-go/format" +) + +// List constructs a node of LIST logical type. +// +// https://github.com/apache/parquet-format/blob/master/LogicalTypes.md#lists +func List(of Node) Node { + return listNode{Group{"list": Repeated(Group{"element": of})}} +} + +type listNode struct{ Group } + +func (listNode) Type() Type { return &listType{} } + +type listType format.ListType + +var listLogicalType = format.LogicalType{List: new(format.ListType)} + +func (t *listType) String() string { return (*format.ListType)(t).String() } + +func (t *listType) Kind() Kind { panic("cannot call Kind on parquet LIST type") } + +func (t *listType) Length() int { return 0 } + +func (t *listType) EstimateSize(int) int { return 0 } + +func (t *listType) EstimateNumValues(int) int { return 0 } + +func (t *listType) Compare(Value, Value) int { panic("cannot compare values on parquet LIST type") } + +func (t *listType) ColumnOrder() *format.ColumnOrder { return nil } + +func (t *listType) PhysicalType() *format.Type { return nil } + +func (t *listType) LogicalType() *format.LogicalType { return &listLogicalType } + +func (t *listType) ConvertedType() *deprecated.ConvertedType { + return &convertedTypes[deprecated.List] +} + +func (t *listType) NewColumnIndexer(int) ColumnIndexer { + panic("create create column indexer from parquet LIST type") +} + +func (t *listType) NewDictionary(int, int, encoding.Values) Dictionary { + panic("cannot create dictionary from parquet LIST type") +} + +func (t *listType) NewColumnBuffer(int, int) ColumnBuffer { + panic("cannot create column buffer from parquet LIST type") +} + +func (t *listType) NewPage(int, int, encoding.Values) Page { + panic("cannot create page from parquet LIST type") +} + +func (t *listType) NewValues(values []byte, _ []uint32) encoding.Values { + panic("cannot create values from parquet LIST type") +} + +func (t *listType) Encode(_ []byte, _ encoding.Values, _ encoding.Encoding) ([]byte, error) { + panic("cannot encode parquet LIST type") +} + +func (t *listType) Decode(_ encoding.Values, _ []byte, _ encoding.Encoding) (encoding.Values, error) { + panic("cannot decode parquet LIST type") +} + +func (t *listType) EstimateDecodeSize(_ int, _ []byte, _ encoding.Encoding) int { + panic("cannot estimate decode size of parquet LIST type") +} + +func (t *listType) AssignValue(reflect.Value, Value) error { + panic("cannot assign value to a parquet LIST type") +} + +func (t *listType) ConvertValue(Value, Type) (Value, error) { + panic("cannot convert value to a parquet LIST type") +} diff --git a/vendor/github.com/parquet-go/parquet-go/type_map.go b/vendor/github.com/parquet-go/parquet-go/type_map.go new file mode 100644 index 000000000..66835cda1 --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/type_map.go @@ -0,0 +1,91 @@ +package parquet + +import ( + "reflect" + + "github.com/parquet-go/parquet-go/deprecated" + "github.com/parquet-go/parquet-go/encoding" + "github.com/parquet-go/parquet-go/format" +) + +// Map constructs a node of MAP logical type. +// +// https://github.com/apache/parquet-format/blob/master/LogicalTypes.md#maps +func Map(key, value Node) Node { + return mapNode{Group{ + "key_value": Repeated(Group{ + "key": Required(key), + "value": value, + }), + }} +} + +type mapNode struct{ Group } + +func (mapNode) Type() Type { return &mapType{} } + +type mapType format.MapType + +var mapLogicalType = format.LogicalType{Map: new(format.MapType)} + +func (t *mapType) String() string { return (*format.MapType)(t).String() } + +func (t *mapType) Kind() Kind { panic("cannot call Kind on parquet MAP type") } + +func (t *mapType) Length() int { return 0 } + +func (t *mapType) EstimateSize(int) int { return 0 } + +func (t *mapType) EstimateNumValues(int) int { return 0 } + +func (t *mapType) Compare(Value, Value) int { panic("cannot compare values on parquet MAP type") } + +func (t *mapType) ColumnOrder() *format.ColumnOrder { return nil } + +func (t *mapType) PhysicalType() *format.Type { return nil } + +func (t *mapType) LogicalType() *format.LogicalType { return &mapLogicalType } + +func (t *mapType) ConvertedType() *deprecated.ConvertedType { + return &convertedTypes[deprecated.Map] +} + +func (t *mapType) NewColumnIndexer(int) ColumnIndexer { + panic("create create column indexer from parquet MAP type") +} + +func (t *mapType) NewDictionary(int, int, encoding.Values) Dictionary { + panic("cannot create dictionary from parquet MAP type") +} + +func (t *mapType) NewColumnBuffer(int, int) ColumnBuffer { + panic("cannot create column buffer from parquet MAP type") +} + +func (t *mapType) NewPage(int, int, encoding.Values) Page { + panic("cannot create page from parquet MAP type") +} + +func (t *mapType) NewValues(values []byte, _ []uint32) encoding.Values { + panic("cannot create values from parquet MAP type") +} + +func (t *mapType) Encode(_ []byte, _ encoding.Values, _ encoding.Encoding) ([]byte, error) { + panic("cannot encode parquet MAP type") +} + +func (t *mapType) Decode(_ encoding.Values, _ []byte, _ encoding.Encoding) (encoding.Values, error) { + panic("cannot decode parquet MAP type") +} + +func (t *mapType) EstimateDecodeSize(_ int, _ []byte, _ encoding.Encoding) int { + panic("cannot estimate decode size of parquet MAP type") +} + +func (t *mapType) AssignValue(reflect.Value, Value) error { + panic("cannot assign value to a parquet MAP type") +} + +func (t *mapType) ConvertValue(Value, Type) (Value, error) { + panic("cannot convert value to a parquet MAP type") +} diff --git a/vendor/github.com/parquet-go/parquet-go/type_null.go b/vendor/github.com/parquet-go/parquet-go/type_null.go new file mode 100644 index 000000000..3a3318226 --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/type_null.go @@ -0,0 +1,77 @@ +package parquet + +import ( + "reflect" + + "github.com/parquet-go/parquet-go/deprecated" + "github.com/parquet-go/parquet-go/encoding" + "github.com/parquet-go/parquet-go/format" +) + +type nullType format.NullType + +var nullLogicalType = format.LogicalType{Unknown: new(format.NullType)} + +func (t *nullType) String() string { return (*format.NullType)(t).String() } + +func (t *nullType) Kind() Kind { return -1 } + +func (t *nullType) Length() int { return 0 } + +func (t *nullType) EstimateSize(int) int { return 0 } + +func (t *nullType) EstimateNumValues(int) int { return 0 } + +func (t *nullType) Compare(Value, Value) int { panic("cannot compare values on parquet NULL type") } + +func (t *nullType) ColumnOrder() *format.ColumnOrder { return &typeDefinedColumnOrder } + +// PhysicalType returns INT32 for NULL-type columns to match the convention used +// by PyArrow and other Parquet implementations. PyArrow generates parquet files +// where columns filled entirely with NULL values use INT32 as the physical type +// with UNKNOWN as the logical type. +func (t *nullType) PhysicalType() *format.Type { return &physicalTypes[Int32] } + +func (t *nullType) LogicalType() *format.LogicalType { return &nullLogicalType } + +func (t *nullType) ConvertedType() *deprecated.ConvertedType { return nil } + +func (t *nullType) NewColumnIndexer(int) ColumnIndexer { + return newNullColumnIndexer() +} + +func (t *nullType) NewDictionary(columnIndex, numValues int, data encoding.Values) Dictionary { + return newNullDictionary(t, makeColumnIndex(columnIndex), makeNumValues(numValues), data) +} + +func (t *nullType) NewColumnBuffer(columnIndex, numValues int) ColumnBuffer { + return newNullColumnBuffer(t, uint16(columnIndex), int32(numValues)) +} + +func (t *nullType) NewPage(columnIndex, numValues int, _ encoding.Values) Page { + return newNullPage(t, makeColumnIndex(columnIndex), makeNumValues(numValues)) +} + +func (t *nullType) NewValues(_ []byte, _ []uint32) encoding.Values { + return encoding.Values{} +} + +func (t *nullType) Encode(dst []byte, _ encoding.Values, _ encoding.Encoding) ([]byte, error) { + return dst[:0], nil +} + +func (t *nullType) Decode(dst encoding.Values, _ []byte, _ encoding.Encoding) (encoding.Values, error) { + return dst, nil +} + +func (t *nullType) EstimateDecodeSize(_ int, _ []byte, _ encoding.Encoding) int { + return 0 +} + +func (t *nullType) AssignValue(reflect.Value, Value) error { + return nil +} + +func (t *nullType) ConvertValue(val Value, _ Type) (Value, error) { + return val, nil +} diff --git a/vendor/github.com/parquet-go/parquet-go/type_string.go b/vendor/github.com/parquet-go/parquet-go/type_string.go new file mode 100644 index 000000000..cdd9afb78 --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/type_string.go @@ -0,0 +1,121 @@ +package parquet + +import ( + "bytes" + "reflect" + + "github.com/parquet-go/parquet-go/deprecated" + "github.com/parquet-go/parquet-go/encoding" + "github.com/parquet-go/parquet-go/format" +) + +// String constructs a leaf node of UTF8 logical type. +// +// https://github.com/apache/parquet-format/blob/master/LogicalTypes.md#string +func String() Node { return Leaf(&stringType{}) } + +var stringLogicalType = format.LogicalType{ + UTF8: new(format.StringType), +} + +type stringType format.StringType + +func (t *stringType) String() string { return (*format.StringType)(t).String() } + +func (t *stringType) Kind() Kind { return ByteArray } + +func (t *stringType) Length() int { return 0 } + +func (t *stringType) EstimateSize(n int) int { return byteArrayType{}.EstimateSize(n) } + +func (t *stringType) EstimateNumValues(n int) int { return byteArrayType{}.EstimateNumValues(n) } + +func (t *stringType) Compare(a, b Value) int { + return bytes.Compare(a.byteArray(), b.byteArray()) +} + +func (t *stringType) ColumnOrder() *format.ColumnOrder { + return &typeDefinedColumnOrder +} + +func (t *stringType) PhysicalType() *format.Type { + return &physicalTypes[ByteArray] +} + +func (t *stringType) LogicalType() *format.LogicalType { + return &stringLogicalType +} + +func (t *stringType) ConvertedType() *deprecated.ConvertedType { + return &convertedTypes[deprecated.UTF8] +} + +func (t *stringType) NewColumnIndexer(sizeLimit int) ColumnIndexer { + return newByteArrayColumnIndexer(sizeLimit) +} + +func (t *stringType) NewDictionary(columnIndex, numValues int, data encoding.Values) Dictionary { + return newByteArrayDictionary(t, makeColumnIndex(columnIndex), makeNumValues(numValues), data) +} + +func (t *stringType) NewColumnBuffer(columnIndex, numValues int) ColumnBuffer { + return newByteArrayColumnBuffer(t, makeColumnIndex(columnIndex), makeNumValues(numValues)) +} + +func (t *stringType) NewPage(columnIndex, numValues int, data encoding.Values) Page { + return newByteArrayPage(t, makeColumnIndex(columnIndex), makeNumValues(numValues), data) +} + +func (t *stringType) NewValues(values []byte, offsets []uint32) encoding.Values { + return encoding.ByteArrayValues(values, offsets) +} + +func (t *stringType) Encode(dst []byte, src encoding.Values, enc encoding.Encoding) ([]byte, error) { + return encoding.EncodeByteArray(dst, src, enc) +} + +func (t *stringType) Decode(dst encoding.Values, src []byte, enc encoding.Encoding) (encoding.Values, error) { + return encoding.DecodeByteArray(dst, src, enc) +} + +func (t *stringType) EstimateDecodeSize(numValues int, src []byte, enc encoding.Encoding) int { + return byteArrayType{}.EstimateDecodeSize(numValues, src, enc) +} + +func (t *stringType) AssignValue(dst reflect.Value, src Value) error { + return byteArrayType{}.AssignValue(dst, src) +} + +func (t *stringType) ConvertValue(val Value, typ Type) (Value, error) { + switch t2 := typ.(type) { + case *dateType: + return convertDateToString(val) + case *timeType: + tz := t2.tz() + if t2.Unit.Micros != nil { + return convertTimeMicrosToString(val, tz) + } else { + return convertTimeMillisToString(val, tz) + } + } + switch typ.Kind() { + case Boolean: + return convertBooleanToString(val) + case Int32: + return convertInt32ToString(val) + case Int64: + return convertInt64ToString(val) + case Int96: + return convertInt96ToString(val) + case Float: + return convertFloatToString(val) + case Double: + return convertDoubleToString(val) + case ByteArray: + return val, nil + case FixedLenByteArray: + return convertFixedLenByteArrayToString(val) + default: + return makeValueKind(ByteArray), nil + } +} diff --git a/vendor/github.com/parquet-go/parquet-go/type_time.go b/vendor/github.com/parquet-go/parquet-go/type_time.go new file mode 100644 index 000000000..327ba8bf0 --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/type_time.go @@ -0,0 +1,279 @@ +package parquet + +import ( + "reflect" + "time" + + "github.com/parquet-go/parquet-go/deprecated" + "github.com/parquet-go/parquet-go/encoding" + "github.com/parquet-go/parquet-go/format" +) + +// TimeUnit represents units of time in the parquet type system. +type TimeUnit interface { + // Returns the precision of the time unit as a time.Duration value. + Duration() time.Duration + // Converts the TimeUnit value to its representation in the parquet thrift + // format. + TimeUnit() format.TimeUnit +} + +var ( + Millisecond TimeUnit = &millisecond{} + Microsecond TimeUnit = µsecond{} + Nanosecond TimeUnit = &nanosecond{} +) + +type millisecond format.MilliSeconds + +func (u *millisecond) Duration() time.Duration { return time.Millisecond } +func (u *millisecond) TimeUnit() format.TimeUnit { + return format.TimeUnit{Millis: (*format.MilliSeconds)(u)} +} + +type microsecond format.MicroSeconds + +func (u *microsecond) Duration() time.Duration { return time.Microsecond } +func (u *microsecond) TimeUnit() format.TimeUnit { + return format.TimeUnit{Micros: (*format.MicroSeconds)(u)} +} + +type nanosecond format.NanoSeconds + +func (u *nanosecond) Duration() time.Duration { return time.Nanosecond } +func (u *nanosecond) TimeUnit() format.TimeUnit { + return format.TimeUnit{Nanos: (*format.NanoSeconds)(u)} +} + +// Time constructs a leaf node of TIME logical type. +// IsAdjustedToUTC is true by default. +// +// https://github.com/apache/parquet-format/blob/master/LogicalTypes.md#time +func Time(unit TimeUnit) Node { + return TimeAdjusted(unit, true) +} + +// TimeAdjusted constructs a leaf node of TIME logical type +// with the IsAdjustedToUTC property explicitly set. +// +// https://github.com/apache/parquet-format/blob/master/LogicalTypes.md#time +func TimeAdjusted(unit TimeUnit, isAdjustedToUTC bool) Node { + // Use pre-allocated instances for common cases + timeUnit := unit.TimeUnit() + if isAdjustedToUTC { + switch { + case timeUnit.Millis != nil: + return Leaf(&timeMilliAdjustedToUTC) + case timeUnit.Micros != nil: + return Leaf(&timeMicroAdjustedToUTC) + case timeUnit.Nanos != nil: + return Leaf(&timeNanoAdjustedToUTC) + } + } else { + switch { + case timeUnit.Millis != nil: + return Leaf(&timeMilliNotAdjustedToUTC) + case timeUnit.Micros != nil: + return Leaf(&timeMicroNotAdjustedToUTC) + case timeUnit.Nanos != nil: + return Leaf(&timeNanoNotAdjustedToUTC) + } + } + // Fallback for unknown unit types + return Leaf(&timeType{IsAdjustedToUTC: isAdjustedToUTC, Unit: timeUnit}) +} + +var timeMilliAdjustedToUTC = timeType{ + IsAdjustedToUTC: true, + Unit: format.TimeUnit{Millis: new(format.MilliSeconds)}, +} + +var timeMicroAdjustedToUTC = timeType{ + IsAdjustedToUTC: true, + Unit: format.TimeUnit{Micros: new(format.MicroSeconds)}, +} + +var timeNanoAdjustedToUTC = timeType{ + IsAdjustedToUTC: true, + Unit: format.TimeUnit{Nanos: new(format.NanoSeconds)}, +} + +var timeMilliNotAdjustedToUTC = timeType{ + IsAdjustedToUTC: false, + Unit: format.TimeUnit{Millis: new(format.MilliSeconds)}, +} + +var timeMicroNotAdjustedToUTC = timeType{ + IsAdjustedToUTC: false, + Unit: format.TimeUnit{Micros: new(format.MicroSeconds)}, +} + +var timeNanoNotAdjustedToUTC = timeType{ + IsAdjustedToUTC: false, + Unit: format.TimeUnit{Nanos: new(format.NanoSeconds)}, +} + +var timeMilliAdjustedToUTCLogicalType = format.LogicalType{ + Time: (*format.TimeType)(&timeMilliAdjustedToUTC), +} + +var timeMicroAdjustedToUTCLogicalType = format.LogicalType{ + Time: (*format.TimeType)(&timeMicroAdjustedToUTC), +} + +var timeNanoAdjustedToUTCLogicalType = format.LogicalType{ + Time: (*format.TimeType)(&timeNanoAdjustedToUTC), +} + +var timeMilliNotAdjustedToUTCLogicalType = format.LogicalType{ + Time: (*format.TimeType)(&timeMilliNotAdjustedToUTC), +} + +var timeMicroNotAdjustedToUTCLogicalType = format.LogicalType{ + Time: (*format.TimeType)(&timeMicroNotAdjustedToUTC), +} + +var timeNanoNotAdjustedToUTCLogicalType = format.LogicalType{ + Time: (*format.TimeType)(&timeNanoNotAdjustedToUTC), +} + +type timeType format.TimeType + +func (t *timeType) tz() *time.Location { + if t.IsAdjustedToUTC { + return time.UTC + } else { + return time.Local + } +} + +func (t *timeType) baseType() Type { + if t.useInt32() { + return int32Type{} + } else { + return int64Type{} + } +} + +func (t *timeType) useInt32() bool { return t.Unit.Millis != nil } + +func (t *timeType) useInt64() bool { return t.Unit.Micros != nil } + +func (t *timeType) String() string { return (*format.TimeType)(t).String() } + +func (t *timeType) Kind() Kind { return t.baseType().Kind() } + +func (t *timeType) Length() int { return t.baseType().Length() } + +func (t *timeType) EstimateSize(n int) int { return t.baseType().EstimateSize(n) } + +func (t *timeType) EstimateNumValues(n int) int { return t.baseType().EstimateNumValues(n) } + +func (t *timeType) Compare(a, b Value) int { return t.baseType().Compare(a, b) } + +func (t *timeType) ColumnOrder() *format.ColumnOrder { return t.baseType().ColumnOrder() } + +func (t *timeType) PhysicalType() *format.Type { return t.baseType().PhysicalType() } + +func (t *timeType) LogicalType() *format.LogicalType { + switch t { + case &timeMilliAdjustedToUTC: + return &timeMilliAdjustedToUTCLogicalType + case &timeMicroAdjustedToUTC: + return &timeMicroAdjustedToUTCLogicalType + case &timeNanoAdjustedToUTC: + return &timeNanoAdjustedToUTCLogicalType + case &timeMilliNotAdjustedToUTC: + return &timeMilliNotAdjustedToUTCLogicalType + case &timeMicroNotAdjustedToUTC: + return &timeMicroNotAdjustedToUTCLogicalType + case &timeNanoNotAdjustedToUTC: + return &timeNanoNotAdjustedToUTCLogicalType + default: + return &format.LogicalType{Time: (*format.TimeType)(t)} + } +} + +func (t *timeType) ConvertedType() *deprecated.ConvertedType { + switch { + case t.useInt32(): + return &convertedTypes[deprecated.TimeMillis] + case t.useInt64(): + return &convertedTypes[deprecated.TimeMicros] + default: + return nil + } +} + +func (t *timeType) NewColumnIndexer(sizeLimit int) ColumnIndexer { + return t.baseType().NewColumnIndexer(sizeLimit) +} + +func (t *timeType) NewColumnBuffer(columnIndex, numValues int) ColumnBuffer { + return t.baseType().NewColumnBuffer(columnIndex, numValues) +} + +func (t *timeType) NewDictionary(columnIndex, numValues int, data encoding.Values) Dictionary { + return t.baseType().NewDictionary(columnIndex, numValues, data) +} + +func (t *timeType) NewPage(columnIndex, numValues int, data encoding.Values) Page { + return t.baseType().NewPage(columnIndex, numValues, data) +} + +func (t *timeType) NewValues(values []byte, offset []uint32) encoding.Values { + return t.baseType().NewValues(values, offset) +} + +func (t *timeType) Encode(dst []byte, src encoding.Values, enc encoding.Encoding) ([]byte, error) { + return t.baseType().Encode(dst, src, enc) +} + +func (t *timeType) Decode(dst encoding.Values, src []byte, enc encoding.Encoding) (encoding.Values, error) { + return t.baseType().Decode(dst, src, enc) +} + +func (t *timeType) EstimateDecodeSize(numValues int, src []byte, enc encoding.Encoding) int { + return t.baseType().EstimateDecodeSize(numValues, src, enc) +} + +func (t *timeType) AssignValue(dst reflect.Value, src Value) error { + // Handle time.Duration specially to convert from the stored time unit to nanoseconds + if dst.Type() == reflect.TypeFor[time.Duration]() { + v := src.int64() + var nanos int64 + switch { + case t.Unit.Millis != nil: + nanos = v * int64(time.Millisecond) + case t.Unit.Micros != nil: + nanos = v * int64(time.Microsecond) + case t.Unit.Nanos != nil: + nanos = v + default: + nanos = v + } + dst.SetInt(nanos) + return nil + } + return t.baseType().AssignValue(dst, src) +} + +func (t *timeType) ConvertValue(val Value, typ Type) (Value, error) { + switch src := typ.(type) { + case *stringType: + tz := t.tz() + if t.Unit.Micros != nil { + return convertStringToTimeMicros(val, tz) + } else { + return convertStringToTimeMillis(val, tz) + } + case *timestampType: + tz := t.tz() + if t.Unit.Micros != nil { + return convertTimestampToTimeMicros(val, src.Unit, src.tz(), tz) + } else { + return convertTimestampToTimeMillis(val, src.Unit, src.tz(), tz) + } + } + return t.baseType().ConvertValue(val, typ) +} diff --git a/vendor/github.com/parquet-go/parquet-go/type_timestamp.go b/vendor/github.com/parquet-go/parquet-go/type_timestamp.go new file mode 100644 index 000000000..08d58633a --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/type_timestamp.go @@ -0,0 +1,257 @@ +package parquet + +import ( + "reflect" + "time" + + "github.com/parquet-go/parquet-go/deprecated" + "github.com/parquet-go/parquet-go/encoding" + "github.com/parquet-go/parquet-go/format" +) + +// Timestamp constructs of leaf node of TIMESTAMP logical type. +// IsAdjustedToUTC is true by default. +// +// https://github.com/apache/parquet-format/blob/master/LogicalTypes.md#timestamp +func Timestamp(unit TimeUnit) Node { + return TimestampAdjusted(unit, true) +} + +// TimestampAdjusted constructs a leaf node of TIMESTAMP logical type +// with the IsAdjustedToUTC property explicitly set. +// +// https://github.com/apache/parquet-format/blob/master/LogicalTypes.md#time +func TimestampAdjusted(unit TimeUnit, isAdjustedToUTC bool) Node { + // Use pre-allocated instances for common cases + timeUnit := unit.TimeUnit() + if isAdjustedToUTC { + switch { + case timeUnit.Millis != nil: + return Leaf(×tampMilliAdjustedToUTC) + case timeUnit.Micros != nil: + return Leaf(×tampMicroAdjustedToUTC) + case timeUnit.Nanos != nil: + return Leaf(×tampNanoAdjustedToUTC) + } + } else { + switch { + case timeUnit.Millis != nil: + return Leaf(×tampMilliNotAdjustedToUTC) + case timeUnit.Micros != nil: + return Leaf(×tampMicroNotAdjustedToUTC) + case timeUnit.Nanos != nil: + return Leaf(×tampNanoNotAdjustedToUTC) + } + } + // Fallback for unknown unit types + return Leaf(×tampType{IsAdjustedToUTC: isAdjustedToUTC, Unit: timeUnit}) +} + +var timestampMilliAdjustedToUTC = timestampType{ + IsAdjustedToUTC: true, + Unit: format.TimeUnit{Millis: new(format.MilliSeconds)}, +} + +var timestampMicroAdjustedToUTC = timestampType{ + IsAdjustedToUTC: true, + Unit: format.TimeUnit{Micros: new(format.MicroSeconds)}, +} + +var timestampNanoAdjustedToUTC = timestampType{ + IsAdjustedToUTC: true, + Unit: format.TimeUnit{Nanos: new(format.NanoSeconds)}, +} + +var timestampMilliNotAdjustedToUTC = timestampType{ + IsAdjustedToUTC: false, + Unit: format.TimeUnit{Millis: new(format.MilliSeconds)}, +} + +var timestampMicroNotAdjustedToUTC = timestampType{ + IsAdjustedToUTC: false, + Unit: format.TimeUnit{Micros: new(format.MicroSeconds)}, +} + +var timestampNanoNotAdjustedToUTC = timestampType{ + IsAdjustedToUTC: false, + Unit: format.TimeUnit{Nanos: new(format.NanoSeconds)}, +} + +var timestampMilliAdjustedToUTCLogicalType = format.LogicalType{ + Timestamp: (*format.TimestampType)(×tampMilliAdjustedToUTC), +} + +var timestampMicroAdjustedToUTCLogicalType = format.LogicalType{ + Timestamp: (*format.TimestampType)(×tampMicroAdjustedToUTC), +} + +var timestampNanoAdjustedToUTCLogicalType = format.LogicalType{ + Timestamp: (*format.TimestampType)(×tampNanoAdjustedToUTC), +} + +var timestampMilliNotAdjustedToUTCLogicalType = format.LogicalType{ + Timestamp: (*format.TimestampType)(×tampMilliNotAdjustedToUTC), +} + +var timestampMicroNotAdjustedToUTCLogicalType = format.LogicalType{ + Timestamp: (*format.TimestampType)(×tampMicroNotAdjustedToUTC), +} + +var timestampNanoNotAdjustedToUTCLogicalType = format.LogicalType{ + Timestamp: (*format.TimestampType)(×tampNanoNotAdjustedToUTC), +} + +type timestampType format.TimestampType + +func (t *timestampType) tz() *time.Location { + if t.IsAdjustedToUTC { + return time.UTC + } else { + return time.Local + } +} + +func (t *timestampType) String() string { return (*format.TimestampType)(t).String() } + +func (t *timestampType) Kind() Kind { return int64Type{}.Kind() } + +func (t *timestampType) Length() int { return int64Type{}.Length() } + +func (t *timestampType) EstimateSize(n int) int { return int64Type{}.EstimateSize(n) } + +func (t *timestampType) EstimateNumValues(n int) int { return int64Type{}.EstimateNumValues(n) } + +func (t *timestampType) Compare(a, b Value) int { return int64Type{}.Compare(a, b) } + +func (t *timestampType) ColumnOrder() *format.ColumnOrder { return int64Type{}.ColumnOrder() } + +func (t *timestampType) PhysicalType() *format.Type { return int64Type{}.PhysicalType() } + +func (t *timestampType) LogicalType() *format.LogicalType { + switch t { + case ×tampMilliAdjustedToUTC: + return ×tampMilliAdjustedToUTCLogicalType + case ×tampMicroAdjustedToUTC: + return ×tampMicroAdjustedToUTCLogicalType + case ×tampNanoAdjustedToUTC: + return ×tampNanoAdjustedToUTCLogicalType + case ×tampMilliNotAdjustedToUTC: + return ×tampMilliNotAdjustedToUTCLogicalType + case ×tampMicroNotAdjustedToUTC: + return ×tampMicroNotAdjustedToUTCLogicalType + case ×tampNanoNotAdjustedToUTC: + return ×tampNanoNotAdjustedToUTCLogicalType + default: + return &format.LogicalType{Timestamp: (*format.TimestampType)(t)} + } +} + +func (t *timestampType) ConvertedType() *deprecated.ConvertedType { + switch { + case t.Unit.Millis != nil: + return &convertedTypes[deprecated.TimestampMillis] + case t.Unit.Micros != nil: + return &convertedTypes[deprecated.TimestampMicros] + default: + return nil + } +} + +func (t *timestampType) NewColumnIndexer(sizeLimit int) ColumnIndexer { + return int64Type{}.NewColumnIndexer(sizeLimit) +} + +func (t *timestampType) NewDictionary(columnIndex, numValues int, data encoding.Values) Dictionary { + return int64Type{}.NewDictionary(columnIndex, numValues, data) +} + +func (t *timestampType) NewColumnBuffer(columnIndex, numValues int) ColumnBuffer { + return int64Type{}.NewColumnBuffer(columnIndex, numValues) +} + +func (t *timestampType) NewPage(columnIndex, numValues int, data encoding.Values) Page { + return int64Type{}.NewPage(columnIndex, numValues, data) +} + +func (t *timestampType) NewValues(values []byte, offsets []uint32) encoding.Values { + return int64Type{}.NewValues(values, offsets) +} + +func (t *timestampType) Encode(dst []byte, src encoding.Values, enc encoding.Encoding) ([]byte, error) { + return int64Type{}.Encode(dst, src, enc) +} + +func (t *timestampType) Decode(dst encoding.Values, src []byte, enc encoding.Encoding) (encoding.Values, error) { + return int64Type{}.Decode(dst, src, enc) +} + +func (t *timestampType) EstimateDecodeSize(numValues int, src []byte, enc encoding.Encoding) int { + return int64Type{}.EstimateDecodeSize(numValues, src, enc) +} + +func (t *timestampType) AssignValue(dst reflect.Value, src Value) error { + switch dst.Type() { + case reflect.TypeOf(time.Time{}): + // Check if the value is NULL - if so, assign zero time.Time + if src.IsNull() { + dst.Set(reflect.ValueOf(time.Time{})) + return nil + } + + unit := Nanosecond.TimeUnit() + lt := t.LogicalType() + if lt != nil && lt.Timestamp != nil { + unit = lt.Timestamp.Unit + } + + nanos := src.int64() + switch { + case unit.Millis != nil: + nanos = nanos * 1e6 + case unit.Micros != nil: + nanos = nanos * 1e3 + } + + val := time.Unix(0, nanos).UTC() + dst.Set(reflect.ValueOf(val)) + return nil + case reflect.TypeOf((*time.Time)(nil)): + // Handle *time.Time (pointer to time.Time) + if src.IsNull() { + // For NULL values, set the pointer to nil + dst.Set(reflect.Zero(dst.Type())) + return nil + } + + unit := Nanosecond.TimeUnit() + lt := t.LogicalType() + if lt != nil && lt.Timestamp != nil { + unit = lt.Timestamp.Unit + } + + nanos := src.int64() + switch { + case unit.Millis != nil: + nanos = nanos * 1e6 + case unit.Micros != nil: + nanos = nanos * 1e3 + } + + val := time.Unix(0, nanos).UTC() + ptr := &val + dst.Set(reflect.ValueOf(ptr)) + return nil + default: + return int64Type{}.AssignValue(dst, src) + } +} + +func (t *timestampType) ConvertValue(val Value, typ Type) (Value, error) { + switch src := typ.(type) { + case *timestampType: + return convertTimestampToTimestamp(val, src.Unit, t.Unit) + case *dateType: + return convertDateToTimestamp(val, t.Unit, t.tz()) + } + return int64Type{}.ConvertValue(val, typ) +} diff --git a/vendor/github.com/parquet-go/parquet-go/type_uuid.go b/vendor/github.com/parquet-go/parquet-go/type_uuid.go new file mode 100644 index 000000000..1cf2a4f94 --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/type_uuid.go @@ -0,0 +1,80 @@ +package parquet + +import ( + "reflect" + + "github.com/parquet-go/parquet-go/deprecated" + "github.com/parquet-go/parquet-go/encoding" + "github.com/parquet-go/parquet-go/format" +) + +// UUID constructs a leaf node of UUID logical type. +// +// https://github.com/apache/parquet-format/blob/master/LogicalTypes.md#uuid +func UUID() Node { return Leaf(&uuidType{}) } + +var uuidLogicaType = format.LogicalType{ + UUID: new(format.UUIDType), +} + +type uuidType format.UUIDType + +func (t *uuidType) String() string { return (*format.UUIDType)(t).String() } + +func (t *uuidType) Kind() Kind { return be128Type{}.Kind() } + +func (t *uuidType) Length() int { return be128Type{}.Length() } + +func (t *uuidType) EstimateSize(n int) int { return be128Type{}.EstimateSize(n) } + +func (t *uuidType) EstimateNumValues(n int) int { return be128Type{}.EstimateNumValues(n) } + +func (t *uuidType) Compare(a, b Value) int { return be128Type{}.Compare(a, b) } + +func (t *uuidType) ColumnOrder() *format.ColumnOrder { return &typeDefinedColumnOrder } + +func (t *uuidType) PhysicalType() *format.Type { return &physicalTypes[FixedLenByteArray] } + +func (t *uuidType) LogicalType() *format.LogicalType { return &uuidLogicaType } + +func (t *uuidType) ConvertedType() *deprecated.ConvertedType { return nil } + +func (t *uuidType) NewColumnIndexer(sizeLimit int) ColumnIndexer { + return be128Type{isUUID: true}.NewColumnIndexer(sizeLimit) +} + +func (t *uuidType) NewDictionary(columnIndex, numValues int, data encoding.Values) Dictionary { + return be128Type{isUUID: true}.NewDictionary(columnIndex, numValues, data) +} + +func (t *uuidType) NewColumnBuffer(columnIndex, numValues int) ColumnBuffer { + return be128Type{isUUID: true}.NewColumnBuffer(columnIndex, numValues) +} + +func (t *uuidType) NewPage(columnIndex, numValues int, data encoding.Values) Page { + return be128Type{isUUID: true}.NewPage(columnIndex, numValues, data) +} + +func (t *uuidType) NewValues(values []byte, offsets []uint32) encoding.Values { + return be128Type{isUUID: true}.NewValues(values, offsets) +} + +func (t *uuidType) Encode(dst []byte, src encoding.Values, enc encoding.Encoding) ([]byte, error) { + return be128Type{isUUID: true}.Encode(dst, src, enc) +} + +func (t *uuidType) Decode(dst encoding.Values, src []byte, enc encoding.Encoding) (encoding.Values, error) { + return be128Type{isUUID: true}.Decode(dst, src, enc) +} + +func (t *uuidType) EstimateDecodeSize(numValues int, src []byte, enc encoding.Encoding) int { + return be128Type{isUUID: true}.EstimateDecodeSize(numValues, src, enc) +} + +func (t *uuidType) AssignValue(dst reflect.Value, src Value) error { + return be128Type{isUUID: true}.AssignValue(dst, src) +} + +func (t *uuidType) ConvertValue(val Value, typ Type) (Value, error) { + return be128Type{isUUID: true}.ConvertValue(val, typ) +} diff --git a/vendor/github.com/parquet-go/parquet-go/type_variant.go b/vendor/github.com/parquet-go/parquet-go/type_variant.go new file mode 100644 index 000000000..8d692e0e0 --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/type_variant.go @@ -0,0 +1,227 @@ +package parquet + +import ( + "errors" + "fmt" + "reflect" + "strings" + + "github.com/parquet-go/parquet-go/deprecated" + "github.com/parquet-go/parquet-go/encoding" + "github.com/parquet-go/parquet-go/format" +) + +// Variant constructs a node of unshredded [VARIANT logical type]. It is a group with +// two required fields, "metadata" and "value", both byte arrays. +// +// *Experimental*: Support for the VARIANT type is still being developed and subject to +// change. +// +// Initial support does not attempt to process the variant data. So reading and writing +// data of this type behaves as if it were just a group with two byte array fields, as +// if the logical type annotation were absent. This may change in the future. +// +// [VARIANT logical type]: https://github.com/apache/parquet-format/blob/master/LogicalTypes.md#variant +func Variant() Node { + return variantNode{Group{"metadata": Required(Leaf(ByteArrayType)), "value": Required(Leaf(ByteArrayType))}} +} + +// ShreddedVariant constructs a node of shredded [VARIANT logical type]. It is a group +// with a required byte array "metadata" field, an optional byte array "value" field +// (for any unshredded values), and an optional "typed_value" field whose type is that +// of the shredded value. If the given node is a group or list (or contains a list), +// the resulting "typed_value" field will have some additional structure to allow +// each group or element in a list to have a mix of shredded and unshredded data. +// +// The given node may only contain types that map to valid [variant value types]. +// Therefore, it may not contain ENUM, FLOAT16, INTERVAL, JSON, BSON, VARIANT, +// GEOMETRY, GEOGRAPHY, MAP, or UNKNOWN logical types. It may only use signed INT +// logical types. It may only use DECIMAL logical types whose precision is less than +// or equal to 38. It may not contain any repeated fields unless they are the middle +// level of a 3-level LIST logical type. Any "required" settings on fields will be +// ignored: shredded fields must always be optional to represent values that may not +// conform to the shredded type. It also may not contain empty groups: any groups +// must have at least one field. +// +// More information on shredded variants can be found in the [Parquet documentation]. +// +// *Experimental*: Support for the VARIANT type is still being developed and subject to +// change. +// +// Initial support does not attempt to process the variant data. So reading and writing +// data of this type behaves as if it were just a group with the three described fields, +// as if the logical type annotation were absent. This may change in the future. +// +// [VARIANT logical type]: https://github.com/apache/parquet-format/blob/master/LogicalTypes.md#variant +// [variant value types]: https://github.com/apache/parquet-format/blob/master/VariantShredding.md#shredded-value-types +// [Parquet documentation]: https://github.com/apache/parquet-format/blob/master/VariantShredding.md +func ShreddedVariant(shreddedType Node) (Node, error) { + typedNode, err := variantTypedValueNode(shreddedType) + if err != nil { + return nil, err + } + return variantNode{Group{ + "metadata": Required(Leaf(ByteArrayType)), + "value": Optional(Leaf(ByteArrayType)), + "typed_value": Optional(typedNode), + }}, nil +} + +func variantTypedValueNode(node Node, fieldPath ...string) (typed Node, err error) { + defer func() { + if err != nil && len(fieldPath) > 0 { + err = fmt.Errorf("field %s: %w", strings.Join(fieldPath, "."), err) + } + }() + if node.Repeated() { + return nil, errors.New("repeated types are not allowed unless they are part of a 3-level LIST logical type") + } + if lt := node.Type().LogicalType(); lt != nil { + switch lt := getLogicalType(lt).(type) { + case *format.ListType: + // We must first extract the inner list.element field of the 3-level LIST type. + children := node.Fields() + if len(children) != 1 { + return nil, fmt.Errorf("invalid LIST logical type: expecting a single 'list' field but instead found %d", len(children)) + } + grandchildren := children[0].Fields() + if len(grandchildren) != 1 { + return nil, fmt.Errorf("invalid LIST logical type: expecting a single 'element' field but instead found %d", len(grandchildren)) + } + elementNode, err := variantTypedValueNode(grandchildren[0], fieldPath...) + if err != nil { + return nil, err + } + list := List(Required(Group{ + "value": Optional(Leaf(ByteArrayType)), + "typed_value": Optional(elementNode), + })) + if node.ID() != 0 { + return FieldID(list, node.ID()), nil + } + return list, nil + case *format.IntType: + if !lt.IsSigned { + return nil, errors.New("signed INT logical types are not allowed") + } + case *format.DecimalType: + if lt.Precision > 38 { + return nil, fmt.Errorf("DECIMAL logical types with precision >38 are not allowed (got %d)", lt.Precision) + } + case *format.DateType: + case *format.TimeType: + case *format.TimestampType: + case *format.StringType: + case *format.UUIDType: + default: + // No other logical types are allowed. + return nil, fmt.Errorf("%s logical types are not allowed", lt) + } + } + if node.Leaf() { + return node, nil + } + children := node.Fields() + if len(children) == 0 { + return nil, errors.New("empty groups are not allowed") + } + group := make(Group, len(children)) + for _, child := range children { + childNode, err := variantTypedValueNode(child, append(fieldPath, child.Name())...) + if err != nil { + return nil, err + } + group[child.Name()] = Required(Group{ + "value": Optional(Leaf(ByteArrayType)), + "typed_value": Optional(childNode), + }) + } + if node.ID() != 0 { + return FieldID(group, node.ID()), nil + } + return group, nil +} + +func getLogicalType(lt *format.LogicalType) any { + // We use reflection so we can always catch a logical type annotation. If a + // new one is added, we don't have to remember to update the switch above (unless + // a new one is added that is also supported as a variant value). + refVal := reflect.Indirect(reflect.ValueOf(lt)) + for i := range refVal.NumField() { + field := refVal.Field(i) + if field.CanInterface() && !field.IsZero() { + return field.Interface() + } + } + return nil +} + +type variantNode struct{ Group } + +func (variantNode) Type() Type { return &variantType{} } + +type variantType format.VariantType + +var variantLogicalType = format.LogicalType{Variant: new(format.VariantType)} + +func (t *variantType) String() string { return (*format.VariantType)(t).String() } + +func (t *variantType) Kind() Kind { panic("cannot call Kind on parquet VARIANT type") } + +func (t *variantType) Length() int { return 0 } + +func (t *variantType) EstimateSize(int) int { return 0 } + +func (t *variantType) EstimateNumValues(int) int { return 0 } + +func (t *variantType) Compare(Value, Value) int { + panic("cannot compare values on parquet VARIANT type") +} + +func (t *variantType) ColumnOrder() *format.ColumnOrder { return nil } + +func (t *variantType) PhysicalType() *format.Type { return nil } + +func (t *variantType) LogicalType() *format.LogicalType { return &variantLogicalType } + +func (t *variantType) ConvertedType() *deprecated.ConvertedType { return nil } + +func (t *variantType) NewColumnIndexer(int) ColumnIndexer { + panic("create create column indexer from parquet VARIANT type") +} + +func (t *variantType) NewDictionary(int, int, encoding.Values) Dictionary { + panic("cannot create dictionary from parquet VARIANT type") +} + +func (t *variantType) NewColumnBuffer(int, int) ColumnBuffer { + panic("cannot create column buffer from parquet VARIANT type") +} + +func (t *variantType) NewPage(int, int, encoding.Values) Page { + panic("cannot create page from parquet VARIANT type") +} + +func (t *variantType) NewValues(values []byte, _ []uint32) encoding.Values { + panic("cannot create values from parquet VARIANT type") +} + +func (t *variantType) Encode(_ []byte, _ encoding.Values, _ encoding.Encoding) ([]byte, error) { + panic("cannot encode parquet VARIANT type") +} + +func (t *variantType) Decode(_ encoding.Values, _ []byte, _ encoding.Encoding) (encoding.Values, error) { + panic("cannot decode parquet VARIANT type") +} + +func (t *variantType) EstimateDecodeSize(_ int, _ []byte, _ encoding.Encoding) int { + panic("cannot estimate decode size of parquet VARIANT type") +} + +func (t *variantType) AssignValue(reflect.Value, Value) error { + panic("cannot assign value to a parquet VARIANT type") +} + +func (t *variantType) ConvertValue(Value, Type) (Value, error) { + panic("cannot convert value to a parquet VARIANT type") +} diff --git a/vendor/github.com/parquet-go/parquet-go/value.go b/vendor/github.com/parquet-go/parquet-go/value.go new file mode 100644 index 000000000..b105dc45a --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/value.go @@ -0,0 +1,1095 @@ +package parquet + +import ( + "bytes" + "encoding/binary" + "fmt" + "io" + "math" + "reflect" + "strconv" + "time" + "unsafe" + + "github.com/google/uuid" + "github.com/parquet-go/parquet-go/deprecated" + "github.com/parquet-go/parquet-go/format" +) + +const ( + // 170 x sizeof(Value) = 4KB + defaultValueBufferSize = 170 + + offsetOfPtr = unsafe.Offsetof(Value{}.ptr) + offsetOfU64 = unsafe.Offsetof(Value{}.u64) + offsetOfU32 = offsetOfU64 + firstByteOffsetOf32BitsValue + offsetOfBool = offsetOfU64 + firstByteOffsetOfBooleanValue +) + +// The Value type is similar to the reflect.Value abstraction of Go values, but +// for parquet values. Value instances wrap underlying Go values mapped to one +// of the parquet physical types. +// +// Value instances are small, immutable objects, and usually passed by value +// between function calls. +// +// The zero-value of Value represents the null parquet value. +type Value struct { + // data + ptr *byte + u64 uint64 + // type + kind int8 // XOR(Kind) so the zero-value is + // levels + definitionLevel byte + repetitionLevel byte + columnIndex uint16 // XOR so the zero-value is -1 +} + +// ValueReader is an interface implemented by types that support reading +// batches of values. +type ValueReader interface { + // Read values into the buffer passed as argument and return the number of + // values read. When all values have been read, the error will be io.EOF. + ReadValues([]Value) (int, error) +} + +// ValueReaderAt is an interface implemented by types that support reading +// values at offsets specified by the application. +type ValueReaderAt interface { + ReadValuesAt([]Value, int64) (int, error) +} + +// ValueReaderFrom is an interface implemented by value writers to read values +// from a reader. +type ValueReaderFrom interface { + ReadValuesFrom(ValueReader) (int64, error) +} + +// ValueWriter is an interface implemented by types that support reading +// batches of values. +type ValueWriter interface { + // Write values from the buffer passed as argument and returns the number + // of values written. + WriteValues([]Value) (int, error) +} + +// ValueWriterTo is an interface implemented by value readers to write values to +// a writer. +type ValueWriterTo interface { + WriteValuesTo(ValueWriter) (int64, error) +} + +// ValueReaderFunc is a function type implementing the ValueReader interface. +type ValueReaderFunc func([]Value) (int, error) + +func (f ValueReaderFunc) ReadValues(values []Value) (int, error) { return f(values) } + +// ValueWriterFunc is a function type implementing the ValueWriter interface. +type ValueWriterFunc func([]Value) (int, error) + +func (f ValueWriterFunc) WriteValues(values []Value) (int, error) { return f(values) } + +// CopyValues copies values from src to dst, returning the number of values +// that were written. +// +// As an optimization, the reader and writer may choose to implement +// ValueReaderFrom and ValueWriterTo to provide their own copy logic. +// +// The function returns any error it encounters reading or writing pages, except +// for io.EOF from the reader which indicates that there were no more values to +// read. +func CopyValues(dst ValueWriter, src ValueReader) (int64, error) { + return copyValues(dst, src, nil) +} + +func copyValues(dst ValueWriter, src ValueReader, buf []Value) (written int64, err error) { + if wt, ok := src.(ValueWriterTo); ok { + return wt.WriteValuesTo(dst) + } + + if rf, ok := dst.(ValueReaderFrom); ok { + return rf.ReadValuesFrom(src) + } + + if len(buf) == 0 { + buf = make([]Value, defaultValueBufferSize) + } + + defer clearValues(buf) + + for { + n, err := src.ReadValues(buf) + + if n > 0 { + wn, werr := dst.WriteValues(buf[:n]) + written += int64(wn) + if werr != nil { + return written, werr + } + } + + if err != nil { + if err == io.EOF { + err = nil + } + return written, err + } + + if n == 0 { + return written, io.ErrNoProgress + } + } +} + +// ValueOf constructs a parquet value from a Go value v. +// +// The physical type of the value is assumed from the Go type of v using the +// following conversion table: +// +// Go type | Parquet physical type +// ------- | --------------------- +// nil | NULL +// bool | BOOLEAN +// int8 | INT32 +// int16 | INT32 +// int32 | INT32 +// int64 | INT64 +// int | INT64 +// uint8 | INT32 +// uint16 | INT32 +// uint32 | INT32 +// uint64 | INT64 +// uintptr | INT64 +// float32 | FLOAT +// float64 | DOUBLE +// string | BYTE_ARRAY +// []byte | BYTE_ARRAY +// [*]byte | FIXED_LEN_BYTE_ARRAY +// +// When converting a []byte or [*]byte value, the underlying byte array is not +// copied; instead, the returned parquet value holds a reference to it. +// +// The repetition and definition levels of the returned value are both zero. +// +// The function panics if the Go value cannot be represented in parquet. +func ValueOf(v any) Value { + k := Kind(-1) + t := reflect.TypeOf(v) + + switch value := v.(type) { + case nil: + return Value{} + case uuid.UUID: + return makeValueBytes(FixedLenByteArray, value[:]) + case deprecated.Int96: + return makeValueInt96(value) + case time.Time: + k = Int64 + } + + switch t.Kind() { + case reflect.Bool: + k = Boolean + case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Uint8, reflect.Uint16, reflect.Uint32: + k = Int32 + case reflect.Int64, reflect.Int, reflect.Uint64, reflect.Uint, reflect.Uintptr: + k = Int64 + case reflect.Float32: + k = Float + case reflect.Float64: + k = Double + case reflect.String: + k = ByteArray + case reflect.Slice: + if t.Elem().Kind() == reflect.Uint8 { + k = ByteArray + } + case reflect.Array: + if t.Elem().Kind() == reflect.Uint8 { + k = FixedLenByteArray + } + } + + if k < 0 { + panic("cannot create parquet value from go value of type " + t.String()) + } + + return makeValue(k, nil, reflect.ValueOf(v)) +} + +// NulLValue constructs a null value, which is the zero-value of the Value type. +func NullValue() Value { return Value{} } + +// ZeroValue constructs a zero value of the given kind. +func ZeroValue(kind Kind) Value { return makeValueKind(kind) } + +// BooleanValue constructs a BOOLEAN parquet value from the bool passed as +// argument. +func BooleanValue(value bool) Value { return makeValueBoolean(value) } + +// Int32Value constructs a INT32 parquet value from the int32 passed as +// argument. +func Int32Value(value int32) Value { return makeValueInt32(value) } + +// Int64Value constructs a INT64 parquet value from the int64 passed as +// argument. +func Int64Value(value int64) Value { return makeValueInt64(value) } + +// Int96Value constructs a INT96 parquet value from the deprecated.Int96 passed +// as argument. +func Int96Value(value deprecated.Int96) Value { return makeValueInt96(value) } + +// FloatValue constructs a FLOAT parquet value from the float32 passed as +// argument. +func FloatValue(value float32) Value { return makeValueFloat(value) } + +// DoubleValue constructs a DOUBLE parquet value from the float64 passed as +// argument. +func DoubleValue(value float64) Value { return makeValueDouble(value) } + +// ByteArrayValue constructs a BYTE_ARRAY parquet value from the byte slice +// passed as argument. +func ByteArrayValue(value []byte) Value { return makeValueBytes(ByteArray, value) } + +// FixedLenByteArrayValue constructs a BYTE_ARRAY parquet value from the byte +// slice passed as argument. +func FixedLenByteArrayValue(value []byte) Value { return makeValueBytes(FixedLenByteArray, value) } + +func makeValue(k Kind, lt *format.LogicalType, v reflect.Value) Value { + if v.Kind() == reflect.Interface { + if v.IsNil() { + return Value{} + } + if v = v.Elem(); v.Kind() == reflect.Pointer && v.IsNil() { + return Value{} + } + } + + switch v.Type() { + case reflect.TypeOf(time.Time{}): + unit := Nanosecond.TimeUnit() + if lt != nil && lt.Timestamp != nil { + unit = lt.Timestamp.Unit + } + + t := v.Interface().(time.Time) + var val int64 + switch { + case unit.Millis != nil: + val = t.UnixMilli() + case unit.Micros != nil: + val = t.UnixMicro() + default: + val = t.UnixNano() + } + return makeValueInt64(val) + } + + switch k { + case Boolean: + return makeValueBoolean(v.Bool()) + + case Int32: + switch v.Kind() { + case reflect.Int8, reflect.Int16, reflect.Int32: + return makeValueInt32(int32(v.Int())) + case reflect.Uint8, reflect.Uint16, reflect.Uint32: + return makeValueInt32(int32(v.Uint())) + } + + case Int64: + switch v.Kind() { + case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Int: + return makeValueInt64(v.Int()) + case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uint, reflect.Uintptr: + return makeValueUint64(v.Uint()) + } + + case Int96: + switch v.Type() { + case reflect.TypeOf(deprecated.Int96{}): + return makeValueInt96(v.Interface().(deprecated.Int96)) + } + + case Float: + switch v.Kind() { + case reflect.Float32: + return makeValueFloat(float32(v.Float())) + } + + case Double: + switch v.Kind() { + case reflect.Float32, reflect.Float64: + return makeValueDouble(v.Float()) + } + + case ByteArray: + switch v.Kind() { + case reflect.String: + return makeValueString(k, v.String()) + case reflect.Slice: + if v.Type().Elem().Kind() == reflect.Uint8 { + return makeValueBytes(k, v.Bytes()) + } + } + + case FixedLenByteArray: + switch v.Kind() { + case reflect.String: + if lt.UUID != nil { // uuid + uuidStr := v.String() + encoded, err := uuid.MustParse(uuidStr).MarshalBinary() + if err != nil { + panic(fmt.Errorf("error marshalling uuid: %w", err)) + } + return makeValueByteArray(k, unsafe.SliceData(encoded), len(encoded)) + } + return makeValueString(k, v.String()) + case reflect.Array: + if v.Type().Elem().Kind() == reflect.Uint8 { + return makeValueFixedLenByteArray(v) + } + case reflect.Slice: + if v.Type().Elem().Kind() == reflect.Uint8 { + return makeValueBytes(k, v.Bytes()) + } + case reflect.Struct: + if v.Type() == reflect.TypeOf(Interval{}) { + iv := v.Interface().(Interval) + buf := make([]byte, 12) + binary.LittleEndian.PutUint32(buf[0:4], iv.Months) + binary.LittleEndian.PutUint32(buf[4:8], iv.Days) + binary.LittleEndian.PutUint32(buf[8:12], iv.Milliseconds) + return makeValueByteArray(k, unsafe.SliceData(buf), 12) + } + } + } + + panic("cannot create parquet value of type " + k.String() + " from go value of type " + v.Type().String()) +} + +func makeValueKind(kind Kind) Value { + return Value{kind: ^int8(kind)} +} + +func makeValueBoolean(value bool) Value { + v := Value{kind: ^int8(Boolean)} + if value { + v.u64 = 1 + } + return v +} + +func makeValueInt32(value int32) Value { + return Value{ + kind: ^int8(Int32), + u64: uint64(value), + } +} + +func makeValueInt64(value int64) Value { + return Value{ + kind: ^int8(Int64), + u64: uint64(value), + } +} + +func makeValueInt96(value deprecated.Int96) Value { + // TODO: this is highly inefficient because we need a heap allocation to + // store the value; we don't expect INT96 to be used frequently since it + // is a deprecated feature of parquet, and it helps keep the Value type + // compact for all the other more common cases. + bits := [12]byte{} + binary.LittleEndian.PutUint32(bits[0:4], value[0]) + binary.LittleEndian.PutUint32(bits[4:8], value[1]) + binary.LittleEndian.PutUint32(bits[8:12], value[2]) + return Value{ + kind: ^int8(Int96), + ptr: &bits[0], + u64: 12, // set the length so we can use the ByteArray method + } +} + +func makeValueUint32(value uint32) Value { + return Value{ + kind: ^int8(Int32), + u64: uint64(value), + } +} + +func makeValueUint64(value uint64) Value { + return Value{ + kind: ^int8(Int64), + u64: value, + } +} + +func makeValueFloat(value float32) Value { + return Value{ + kind: ^int8(Float), + u64: uint64(math.Float32bits(value)), + } +} + +func makeValueDouble(value float64) Value { + return Value{ + kind: ^int8(Double), + u64: math.Float64bits(value), + } +} + +func makeValueBytes(kind Kind, value []byte) Value { + return makeValueByteArray(kind, unsafe.SliceData(value), len(value)) +} + +func makeValueString(kind Kind, value string) Value { + return makeValueByteArray(kind, unsafe.StringData(value), len(value)) +} + +func makeValueFixedLenByteArray(v reflect.Value) Value { + t := v.Type() + // When the array is addressable, we take advantage of this + // condition to avoid the heap allocation otherwise needed + // to pack the reference into an interface{} value. + if v.CanAddr() { + v = v.Addr() + } else { + u := reflect.New(t) + u.Elem().Set(v) + v = u + } + return makeValueByteArray(FixedLenByteArray, (*byte)(v.UnsafePointer()), t.Len()) +} + +func makeValueByteArray(kind Kind, data *byte, size int) Value { + return Value{ + kind: ^int8(kind), + ptr: data, + u64: uint64(size), + } +} + +// These methods are internal versions of methods exported by the Value type, +// they are usually inlined by the compiler and intended to be used inside the +// parquet-go package because they tend to generate better code than their +// exported counter part, which requires making a copy of the receiver. +func (v *Value) isNull() bool { return v.kind == 0 } +func (v *Value) byte() byte { return byte(v.u64) } +func (v *Value) boolean() bool { return v.u64 != 0 } +func (v *Value) int32() int32 { return int32(v.u64) } +func (v *Value) int64() int64 { return int64(v.u64) } +func (v *Value) int96() deprecated.Int96 { return makeInt96(v.byteArray()) } +func (v *Value) float() float32 { return math.Float32frombits(uint32(v.u64)) } +func (v *Value) double() float64 { return math.Float64frombits(uint64(v.u64)) } +func (v *Value) uint32() uint32 { return uint32(v.u64) } +func (v *Value) uint64() uint64 { return v.u64 } +func (v *Value) byteArray() []byte { return unsafe.Slice(v.ptr, v.u64) } +func (v *Value) string() string { return unsafe.String(v.ptr, v.u64) } +func (v *Value) be128() *[16]byte { return (*[16]byte)(unsafe.Pointer(v.ptr)) } +func (v *Value) column() int { + col := ^v.columnIndex + if col == math.MaxUint16 { + return -1 + } + return int(col) +} + +func (v Value) convertToBoolean(x bool) Value { + v.kind = ^int8(Boolean) + v.ptr = nil + v.u64 = 0 + if x { + v.u64 = 1 + } + return v +} + +func (v Value) convertToInt32(x int32) Value { + v.kind = ^int8(Int32) + v.ptr = nil + v.u64 = uint64(x) + return v +} + +func (v Value) convertToInt64(x int64) Value { + v.kind = ^int8(Int64) + v.ptr = nil + v.u64 = uint64(x) + return v +} + +func (v Value) convertToInt96(x deprecated.Int96) Value { + i96 := makeValueInt96(x) + v.kind = i96.kind + v.ptr = i96.ptr + v.u64 = i96.u64 + return v +} + +func (v Value) convertToFloat(x float32) Value { + v.kind = ^int8(Float) + v.ptr = nil + v.u64 = uint64(math.Float32bits(x)) + return v +} + +func (v Value) convertToDouble(x float64) Value { + v.kind = ^int8(Double) + v.ptr = nil + v.u64 = math.Float64bits(x) + return v +} + +func (v Value) convertToByteArray(x []byte) Value { + v.kind = ^int8(ByteArray) + v.ptr = unsafe.SliceData(x) + v.u64 = uint64(len(x)) + return v +} + +func (v Value) convertToFixedLenByteArray(x []byte) Value { + v.kind = ^int8(FixedLenByteArray) + v.ptr = unsafe.SliceData(x) + v.u64 = uint64(len(x)) + return v +} + +// Kind returns the kind of v, which represents its parquet physical type. +func (v Value) Kind() Kind { return ^Kind(v.kind) } + +// IsNull returns true if v is the null value. +func (v Value) IsNull() bool { return v.isNull() } + +// Byte returns v as a byte, which may truncate the underlying byte. +func (v Value) Byte() byte { return v.byte() } + +// Boolean returns v as a bool, assuming the underlying type is BOOLEAN. +func (v Value) Boolean() bool { return v.boolean() } + +// Int32 returns v as a int32, assuming the underlying type is INT32. +func (v Value) Int32() int32 { return v.int32() } + +// Int64 returns v as a int64, assuming the underlying type is INT64. +func (v Value) Int64() int64 { return v.int64() } + +// Int96 returns v as a int96, assuming the underlying type is INT96. +func (v Value) Int96() deprecated.Int96 { + var val deprecated.Int96 + if !v.isNull() { + val = v.int96() + } + return val +} + +// Float returns v as a float32, assuming the underlying type is FLOAT. +func (v Value) Float() float32 { return v.float() } + +// Double returns v as a float64, assuming the underlying type is DOUBLE. +func (v Value) Double() float64 { return v.double() } + +// Uint32 returns v as a uint32, assuming the underlying type is INT32. +func (v Value) Uint32() uint32 { return v.uint32() } + +// Uint64 returns v as a uint64, assuming the underlying type is INT64. +func (v Value) Uint64() uint64 { return v.uint64() } + +// ByteArray returns v as a []byte, assuming the underlying type is either +// BYTE_ARRAY or FIXED_LEN_BYTE_ARRAY. +// +// The application must treat the returned byte slice as a read-only value, +// mutating the content will result in undefined behaviors. +func (v Value) ByteArray() []byte { return v.byteArray() } + +// RepetitionLevel returns the repetition level of v. +func (v Value) RepetitionLevel() int { return int(v.repetitionLevel) } + +// DefinitionLevel returns the definition level of v. +func (v Value) DefinitionLevel() int { return int(v.definitionLevel) } + +// Column returns the column index within the row that v was created from. +// +// Returns -1 if the value does not carry a column index. +func (v Value) Column() int { return v.column() } + +// Bytes returns the binary representation of v. +// +// If v is the null value, an nil byte slice is returned. +func (v Value) Bytes() []byte { + switch v.Kind() { + case Boolean: + buf := [8]byte{} + binary.LittleEndian.PutUint32(buf[:4], v.uint32()) + return buf[0:1] + case Int32, Float: + buf := [8]byte{} + binary.LittleEndian.PutUint32(buf[:4], v.uint32()) + return buf[:4] + case Int64, Double: + buf := [8]byte{} + binary.LittleEndian.PutUint64(buf[:8], v.uint64()) + return buf[:8] + case ByteArray, FixedLenByteArray, Int96: + return v.byteArray() + default: + return nil + } +} + +// AppendBytes appends the binary representation of v to b. +// +// If v is the null value, b is returned unchanged. +func (v Value) AppendBytes(b []byte) []byte { + buf := [8]byte{} + switch v.Kind() { + case Boolean: + binary.LittleEndian.PutUint32(buf[:4], v.uint32()) + return append(b, buf[0]) + case Int32, Float: + binary.LittleEndian.PutUint32(buf[:4], v.uint32()) + return append(b, buf[:4]...) + case Int64, Double: + binary.LittleEndian.PutUint64(buf[:8], v.uint64()) + return append(b, buf[:8]...) + case ByteArray, FixedLenByteArray, Int96: + return append(b, v.byteArray()...) + default: + return b + } +} + +// Format outputs a human-readable representation of v to w, using r as the +// formatting verb to describe how the value should be printed. +// +// The following formatting options are supported: +// +// %c prints the column index +// %+c prints the column index, prefixed with "C:" +// %d prints the definition level +// %+d prints the definition level, prefixed with "D:" +// %r prints the repetition level +// %+r prints the repetition level, prefixed with "R:" +// %q prints the quoted representation of v +// %+q prints the quoted representation of v, prefixed with "V:" +// %s prints the string representation of v +// %+s prints the string representation of v, prefixed with "V:" +// %v same as %s +// %+v prints a verbose representation of v +// %#v prints a Go value representation of v +// +// Format satisfies the fmt.Formatter interface. +func (v Value) Format(w fmt.State, r rune) { + switch r { + case 'c': + if w.Flag('+') { + io.WriteString(w, "C:") + } + fmt.Fprint(w, v.column()) + + case 'd': + if w.Flag('+') { + io.WriteString(w, "D:") + } + fmt.Fprint(w, v.definitionLevel) + + case 'r': + if w.Flag('+') { + io.WriteString(w, "R:") + } + fmt.Fprint(w, v.repetitionLevel) + + case 'q': + if w.Flag('+') { + io.WriteString(w, "V:") + } + switch v.Kind() { + case ByteArray, FixedLenByteArray: + fmt.Fprintf(w, "%q", v.byteArray()) + default: + fmt.Fprintf(w, `"%s"`, v) + } + + case 's': + if w.Flag('+') { + io.WriteString(w, "V:") + } + switch v.Kind() { + case Boolean: + fmt.Fprint(w, v.boolean()) + case Int32: + fmt.Fprint(w, v.int32()) + case Int64: + fmt.Fprint(w, v.int64()) + case Int96: + fmt.Fprint(w, v.int96()) + case Float: + fmt.Fprint(w, v.float()) + case Double: + fmt.Fprint(w, v.double()) + case ByteArray, FixedLenByteArray: + w.Write(v.byteArray()) + default: + io.WriteString(w, "") + } + + case 'v': + switch { + case w.Flag('+'): + fmt.Fprintf(w, "%+[1]c %+[1]d %+[1]r %+[1]s", v) + case w.Flag('#'): + v.formatGoString(w) + default: + v.Format(w, 's') + } + } +} + +func (v Value) formatGoString(w fmt.State) { + io.WriteString(w, "parquet.") + switch v.Kind() { + case Boolean: + fmt.Fprintf(w, "BooleanValue(%t)", v.boolean()) + case Int32: + fmt.Fprintf(w, "Int32Value(%d)", v.int32()) + case Int64: + fmt.Fprintf(w, "Int64Value(%d)", v.int64()) + case Int96: + fmt.Fprintf(w, "Int96Value(%#v)", v.int96()) + case Float: + fmt.Fprintf(w, "FloatValue(%g)", v.float()) + case Double: + fmt.Fprintf(w, "DoubleValue(%g)", v.double()) + case ByteArray: + fmt.Fprintf(w, "ByteArrayValue(%q)", v.byteArray()) + case FixedLenByteArray: + fmt.Fprintf(w, "FixedLenByteArrayValue(%#v)", v.byteArray()) + default: + io.WriteString(w, "Value{}") + return + } + fmt.Fprintf(w, ".Level(%d,%d,%d)", + v.RepetitionLevel(), + v.DefinitionLevel(), + v.Column(), + ) +} + +// String returns a string representation of v. +func (v Value) String() string { + switch v.Kind() { + case Boolean: + return strconv.FormatBool(v.boolean()) + case Int32: + return strconv.FormatInt(int64(v.int32()), 10) + case Int64: + return strconv.FormatInt(v.int64(), 10) + case Int96: + return v.Int96().String() + case Float: + return strconv.FormatFloat(float64(v.float()), 'g', -1, 32) + case Double: + return strconv.FormatFloat(v.double(), 'g', -1, 32) + case ByteArray, FixedLenByteArray: + return string(v.byteArray()) + default: + return "" + } +} + +// GoString returns a Go value string representation of v. +func (v Value) GoString() string { return fmt.Sprintf("%#v", v) } + +// Level returns v with the repetition level, definition level, and column index +// set to the values passed as arguments. +// +// The method panics if either argument is negative. +func (v Value) Level(repetitionLevel, definitionLevel, columnIndex int) Value { + v.repetitionLevel = makeRepetitionLevel(repetitionLevel) + v.definitionLevel = makeDefinitionLevel(definitionLevel) + v.columnIndex = ^makeColumnIndex(columnIndex) + return v +} + +// Clone returns a copy of v which does not share any pointers with it. +func (v Value) Clone() Value { + switch k := v.Kind(); k { + case ByteArray, FixedLenByteArray: + v.ptr = unsafe.SliceData(copyBytes(v.byteArray())) + } + return v +} + +func makeInt96(bits []byte) (i96 deprecated.Int96) { + return deprecated.Int96{ + 2: binary.LittleEndian.Uint32(bits[8:12]), + 1: binary.LittleEndian.Uint32(bits[4:8]), + 0: binary.LittleEndian.Uint32(bits[0:4]), + } +} + +func parseValue(kind Kind, data []byte) (val Value, err error) { + switch kind { + case Boolean: + if len(data) == 1 { + val = makeValueBoolean(data[0] != 0) + } + case Int32: + if len(data) == 4 { + val = makeValueInt32(int32(binary.LittleEndian.Uint32(data))) + } + case Int64: + if len(data) == 8 { + val = makeValueInt64(int64(binary.LittleEndian.Uint64(data))) + } + case Int96: + if len(data) == 12 { + val = makeValueInt96(makeInt96(data)) + } + case Float: + if len(data) == 4 { + val = makeValueFloat(float32(math.Float32frombits(binary.LittleEndian.Uint32(data)))) + } + case Double: + if len(data) == 8 { + val = makeValueDouble(float64(math.Float64frombits(binary.LittleEndian.Uint64(data)))) + } + case ByteArray, FixedLenByteArray: + val = makeValueBytes(kind, data) + } + if val.isNull() { + err = fmt.Errorf("cannot decode %s value from input of length %d", kind, len(data)) + } + return val, err +} + +func copyBytes(b []byte) []byte { + c := make([]byte, len(b)) + copy(c, b) + return c +} + +// Equal returns true if v1 and v2 are equal. +// +// Values are considered equal if they are of the same physical type and hold +// the same Go values. For BYTE_ARRAY and FIXED_LEN_BYTE_ARRAY, the content of +// the underlying byte arrays are tested for equality. +// +// Note that the repetition levels, definition levels, and column indexes are +// not compared by this function, use DeepEqual instead. +func Equal(v1, v2 Value) bool { + if v1.kind != v2.kind { + return false + } + switch ^Kind(v1.kind) { + case Boolean: + return v1.boolean() == v2.boolean() + case Int32: + return v1.int32() == v2.int32() + case Int64: + return v1.int64() == v2.int64() + case Int96: + return v1.int96() == v2.int96() + case Float: + return v1.float() == v2.float() + case Double: + return v1.double() == v2.double() + case ByteArray, FixedLenByteArray: + return bytes.Equal(v1.byteArray(), v2.byteArray()) + case -1: // null + return true + default: + return false + } +} + +// DeepEqual returns true if v1 and v2 are equal, including their repetition +// levels, definition levels, and column indexes. +// +// See Equal for details about how value equality is determined. +func DeepEqual(v1, v2 Value) bool { + return Equal(v1, v2) && + v1.repetitionLevel == v2.repetitionLevel && + v1.definitionLevel == v2.definitionLevel && + v1.columnIndex == v2.columnIndex +} + +var ( + _ fmt.Formatter = Value{} + _ fmt.Stringer = Value{} +) + +func clearValues(values []Value) { + for i := range values { + values[i] = Value{} + } +} + +// BooleanReader is an interface implemented by ValueReader instances which +// expose the content of a column of boolean values. +type BooleanReader interface { + // Read boolean values into the buffer passed as argument. + // + // The method returns io.EOF when all values have been read. + ReadBooleans(values []bool) (int, error) +} + +// BooleanWriter is an interface implemented by ValueWriter instances which +// support writing columns of boolean values. +type BooleanWriter interface { + // Write boolean values. + // + // The method returns the number of values written, and any error that + // occurred while writing the values. + WriteBooleans(values []bool) (int, error) +} + +// Int32Reader is an interface implemented by ValueReader instances which expose +// the content of a column of int32 values. +type Int32Reader interface { + // Read 32 bits integer values into the buffer passed as argument. + // + // The method returns io.EOF when all values have been read. + ReadInt32s(values []int32) (int, error) +} + +// Int32Writer is an interface implemented by ValueWriter instances which +// support writing columns of 32 bits signed integer values. +type Int32Writer interface { + // Write 32 bits signed integer values. + // + // The method returns the number of values written, and any error that + // occurred while writing the values. + WriteInt32s(values []int32) (int, error) +} + +// Int64Reader is an interface implemented by ValueReader instances which expose +// the content of a column of int64 values. +type Int64Reader interface { + // Read 64 bits integer values into the buffer passed as argument. + // + // The method returns io.EOF when all values have been read. + ReadInt64s(values []int64) (int, error) +} + +// Int64Writer is an interface implemented by ValueWriter instances which +// support writing columns of 64 bits signed integer values. +type Int64Writer interface { + // Write 64 bits signed integer values. + // + // The method returns the number of values written, and any error that + // occurred while writing the values. + WriteInt64s(values []int64) (int, error) +} + +// Int96Reader is an interface implemented by ValueReader instances which expose +// the content of a column of int96 values. +type Int96Reader interface { + // Read 96 bits integer values into the buffer passed as argument. + // + // The method returns io.EOF when all values have been read. + ReadInt96s(values []deprecated.Int96) (int, error) +} + +// Int96Writer is an interface implemented by ValueWriter instances which +// support writing columns of 96 bits signed integer values. +type Int96Writer interface { + // Write 96 bits signed integer values. + // + // The method returns the number of values written, and any error that + // occurred while writing the values. + WriteInt96s(values []deprecated.Int96) (int, error) +} + +// FloatReader is an interface implemented by ValueReader instances which expose +// the content of a column of single-precision floating point values. +type FloatReader interface { + // Read single-precision floating point values into the buffer passed as + // argument. + // + // The method returns io.EOF when all values have been read. + ReadFloats(values []float32) (int, error) +} + +// FloatWriter is an interface implemented by ValueWriter instances which +// support writing columns of single-precision floating point values. +type FloatWriter interface { + // Write single-precision floating point values. + // + // The method returns the number of values written, and any error that + // occurred while writing the values. + WriteFloats(values []float32) (int, error) +} + +// DoubleReader is an interface implemented by ValueReader instances which +// expose the content of a column of double-precision float point values. +type DoubleReader interface { + // Read double-precision floating point values into the buffer passed as + // argument. + // + // The method returns io.EOF when all values have been read. + ReadDoubles(values []float64) (int, error) +} + +// DoubleWriter is an interface implemented by ValueWriter instances which +// support writing columns of double-precision floating point values. +type DoubleWriter interface { + // Write double-precision floating point values. + // + // The method returns the number of values written, and any error that + // occurred while writing the values. + WriteDoubles(values []float64) (int, error) +} + +// ByteArrayReader is an interface implemented by ValueReader instances which +// expose the content of a column of variable length byte array values. +type ByteArrayReader interface { + // Read values into the byte buffer passed as argument, returning the number + // of values written to the buffer (not the number of bytes). Values are + // written using the PLAIN encoding, each byte array prefixed with its + // length encoded as a 4 bytes little endian unsigned integer. + // + // The method returns io.EOF when all values have been read. + // + // If the buffer was not empty, but too small to hold at least one value, + // io.ErrShortBuffer is returned. + ReadByteArrays(values []byte) (int, error) +} + +// ByteArrayWriter is an interface implemented by ValueWriter instances which +// support writing columns of variable length byte array values. +type ByteArrayWriter interface { + // Write variable length byte array values. + // + // The values passed as input must be laid out using the PLAIN encoding, + // with each byte array prefixed with the four bytes little endian unsigned + // integer length. + // + // The method returns the number of values written to the underlying column + // (not the number of bytes), or any error that occurred while attempting to + // write the values. + WriteByteArrays(values []byte) (int, error) +} + +// FixedLenByteArrayReader is an interface implemented by ValueReader instances +// which expose the content of a column of fixed length byte array values. +type FixedLenByteArrayReader interface { + // Read values into the byte buffer passed as argument, returning the number + // of values written to the buffer (not the number of bytes). + // + // The method returns io.EOF when all values have been read. + // + // If the buffer was not empty, but too small to hold at least one value, + // io.ErrShortBuffer is returned. + ReadFixedLenByteArrays(values []byte) (int, error) +} + +// FixedLenByteArrayWriter is an interface implemented by ValueWriter instances +// which support writing columns of fixed length byte array values. +type FixedLenByteArrayWriter interface { + // Writes the fixed length byte array values. + // + // The size of the values is assumed to be the same as the expected size of + // items in the column. The method errors if the length of the input values + // is not a multiple of the expected item size. + WriteFixedLenByteArrays(values []byte) (int, error) +} diff --git a/vendor/github.com/parquet-go/parquet-go/value_amd64.go b/vendor/github.com/parquet-go/parquet-go/value_amd64.go new file mode 100644 index 000000000..fbd6432fd --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/value_amd64.go @@ -0,0 +1,18 @@ +//go:build !purego + +package parquet + +import "golang.org/x/sys/cpu" + +//go:noescape +func memsetValuesAVX2(values []Value, model Value, _ uint64) + +func memsetValues(values []Value, model Value) { + if cpu.X86.HasAVX2 { + memsetValuesAVX2(values, model, 0) + } else { + for i := range values { + values[i] = model + } + } +} diff --git a/vendor/github.com/parquet-go/parquet-go/value_amd64.s b/vendor/github.com/parquet-go/parquet-go/value_amd64.s new file mode 100644 index 000000000..255117412 --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/value_amd64.s @@ -0,0 +1,59 @@ +//go:build !purego + +#include "textflag.h" + +#define sizeOfValue 24 + +// This function is an optimized implementation of the memsetValues function +// which assigns the parquet.Value passed as second argument to all elements of +// the first slice argument. +// +// The optimizations relies on the fact that we can pack 4 parquet.Value values +// into 3 YMM registers (24 x 4 = 32 x 3 = 96). +// +// func memsetValuesAVX2(values []Value, model Value, _ uint64) +TEXT ·memsetValuesAVX2(SB), NOSPLIT, $0-56 // 48 + padding to load model in YMM + MOVQ values_base+0(FP), AX + MOVQ values_len+8(FP), BX + + MOVQ model_ptr+24(FP), R10 + MOVQ model_u64+32(FP), R11 + MOVQ model+40(FP), R12 // go vet complains about this line but it's OK + + XORQ SI, SI // byte index + MOVQ BX, DI // byte count + IMULQ $sizeOfValue, DI + + CMPQ BX, $4 + JB test + + MOVQ BX, R8 + SHRQ $2, R8 + SHLQ $2, R8 + IMULQ $sizeOfValue, R8 + + VMOVDQU model+24(FP), Y0 + VMOVDQU Y0, Y1 + VMOVDQU Y0, Y2 + + VPERMQ $0b00100100, Y0, Y0 + VPERMQ $0b01001001, Y1, Y1 + VPERMQ $0b10010010, Y2, Y2 +loop4: + VMOVDQU Y0, 0(AX)(SI*1) + VMOVDQU Y1, 32(AX)(SI*1) + VMOVDQU Y2, 64(AX)(SI*1) + ADDQ $4*sizeOfValue, SI + CMPQ SI, R8 + JNE loop4 + VZEROUPPER + JMP test +loop: + MOVQ R10, 0(AX)(SI*1) + MOVQ R11, 8(AX)(SI*1) + MOVQ R12, 16(AX)(SI*1) + ADDQ $sizeOfValue, SI +test: + CMPQ SI, DI + JNE loop + RET diff --git a/vendor/github.com/parquet-go/parquet-go/value_be.go b/vendor/github.com/parquet-go/parquet-go/value_be.go new file mode 100644 index 000000000..5e7c038df --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/value_be.go @@ -0,0 +1,19 @@ +//go:build s390x + +package parquet + +// On a big endian system, a boolean/byte value, which is in little endian byte +// format, is byte aligned to the 7th byte in a u64 (8 bytes) variable. +// Hence the data will be available at 7th byte when interpreted as a little +// endian byte format. So, in order to access a boolean/byte value out of u64 +// variable, we need to add an offset of "7". +// +// In the same way, an int32/uint32/float value, which is in little endian byte +// format, is byte aligned to the 4th byte in a u64 (8 bytes) variable. +// Hence the data will be available at 4th byte when interpreted as a little +// endian byte format. So, in order to access an int32/uint32/float value out of +// u64 variable, we need to add an offset of "4". +const ( + firstByteOffsetOfBooleanValue = 7 + firstByteOffsetOf32BitsValue = 4 +) diff --git a/vendor/github.com/parquet-go/parquet-go/value_le.go b/vendor/github.com/parquet-go/parquet-go/value_le.go new file mode 100644 index 000000000..f11170ef7 --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/value_le.go @@ -0,0 +1,8 @@ +//go:build !s390x + +package parquet + +const ( + firstByteOffsetOfBooleanValue = 0 + firstByteOffsetOf32BitsValue = 0 +) diff --git a/vendor/github.com/parquet-go/parquet-go/values_purego.go b/vendor/github.com/parquet-go/parquet-go/values_purego.go new file mode 100644 index 000000000..815113464 --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/values_purego.go @@ -0,0 +1,9 @@ +//go:build purego || !amd64 + +package parquet + +func memsetValues(values []Value, model Value) { + for i := range values { + values[i] = model + } +} diff --git a/vendor/github.com/parquet-go/parquet-go/variant/decoding.go b/vendor/github.com/parquet-go/parquet-go/variant/decoding.go new file mode 100644 index 000000000..d426edbe2 --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/variant/decoding.go @@ -0,0 +1,293 @@ +package variant + +import ( + "encoding/binary" + "errors" + "fmt" + "math" +) + +// Decode decodes a variant value from its binary representation using the +// given metadata dictionary. +func Decode(m Metadata, data []byte) (Value, error) { + if len(data) == 0 { + return Null(), errors.New("variant value: empty data") + } + v, _, err := decodeValue(m, data) + return v, err +} + +// decodeValue decodes a value and returns the number of bytes consumed. +func decodeValue(m Metadata, data []byte) (Value, int, error) { + if len(data) == 0 { + return Null(), 0, errors.New("variant value: unexpected end of data") + } + + header := data[0] + basic := BasicType(header & 0x03) + valueHeader := header >> 2 + + switch basic { + case BasicPrimitive: + return decodePrimitive(PrimitiveType(valueHeader), data[1:]) + case BasicShortString: + length := int(valueHeader) + if len(data) < 1+length { + return Null(), 0, fmt.Errorf("variant value: short string length %d exceeds data", length) + } + v := Value{basic: BasicShortString, str: string(data[1 : 1+length])} + return v, 1 + length, nil + case BasicObject: + return decodeObject(m, header, data[1:]) + case BasicArray: + return decodeArray(m, header, data[1:]) + default: + return Null(), 0, fmt.Errorf("variant value: unknown basic type %d", basic) + } +} + +func decodePrimitive(pt PrimitiveType, data []byte) (Value, int, error) { + switch pt { + case PrimitiveNull: + return Null(), 1, nil + case PrimitiveTrue: + return Bool(true), 1, nil + case PrimitiveFalse: + return Bool(false), 1, nil + case PrimitiveInt8: + if len(data) < 1 { + return Null(), 0, errors.New("variant value: not enough data for int8") + } + return Int8(int8(data[0])), 2, nil + case PrimitiveInt16: + if len(data) < 2 { + return Null(), 0, errors.New("variant value: not enough data for int16") + } + return Int16(int16(binary.LittleEndian.Uint16(data[:2]))), 3, nil + case PrimitiveInt32: + if len(data) < 4 { + return Null(), 0, errors.New("variant value: not enough data for int32") + } + return Int32(int32(binary.LittleEndian.Uint32(data[:4]))), 5, nil + case PrimitiveInt64: + if len(data) < 8 { + return Null(), 0, errors.New("variant value: not enough data for int64") + } + return Int64(int64(binary.LittleEndian.Uint64(data[:8]))), 9, nil + case PrimitiveFloat: + if len(data) < 4 { + return Null(), 0, errors.New("variant value: not enough data for float") + } + return Float(math.Float32frombits(binary.LittleEndian.Uint32(data[:4]))), 5, nil + case PrimitiveDouble: + if len(data) < 8 { + return Null(), 0, errors.New("variant value: not enough data for double") + } + return Double(math.Float64frombits(binary.LittleEndian.Uint64(data[:8]))), 9, nil + case PrimitiveString: + if len(data) < 4 { + return Null(), 0, errors.New("variant value: not enough data for string length") + } + length := int(binary.LittleEndian.Uint32(data[:4])) + if len(data) < 4+length { + return Null(), 0, fmt.Errorf("variant value: string length %d exceeds data", length) + } + return String(string(data[4 : 4+length])), 5 + length, nil + case PrimitiveBinary: + if len(data) < 4 { + return Null(), 0, errors.New("variant value: not enough data for binary length") + } + length := int(binary.LittleEndian.Uint32(data[:4])) + if len(data) < 4+length { + return Null(), 0, fmt.Errorf("variant value: binary length %d exceeds data", length) + } + b := make([]byte, length) + copy(b, data[4:4+length]) + return Binary(b), 5 + length, nil + case PrimitiveDate: + if len(data) < 4 { + return Null(), 0, errors.New("variant value: not enough data for date") + } + return Date(int32(binary.LittleEndian.Uint32(data[:4]))), 5, nil + case PrimitiveTimestamp: + if len(data) < 8 { + return Null(), 0, errors.New("variant value: not enough data for timestamp") + } + return Timestamp(int64(binary.LittleEndian.Uint64(data[:8]))), 9, nil + case PrimitiveTimestampNTZ: + if len(data) < 8 { + return Null(), 0, errors.New("variant value: not enough data for timestamp_ntz") + } + return TimestampNTZ(int64(binary.LittleEndian.Uint64(data[:8]))), 9, nil + case PrimitiveTime: + if len(data) < 8 { + return Null(), 0, errors.New("variant value: not enough data for time") + } + return Time(int64(binary.LittleEndian.Uint64(data[:8]))), 9, nil + case PrimitiveUUID: + if len(data) < 16 { + return Null(), 0, errors.New("variant value: not enough data for uuid") + } + var u [16]byte + copy(u[:], data[:16]) + return UUID(u), 17, nil + case PrimitiveDecimal4: + if len(data) < 5 { + return Null(), 0, errors.New("variant value: not enough data for decimal4") + } + scale := data[0] + val := int32(binary.LittleEndian.Uint32(data[1:5])) + return Decimal4(val, scale), 6, nil + case PrimitiveDecimal8: + if len(data) < 9 { + return Null(), 0, errors.New("variant value: not enough data for decimal8") + } + scale := data[0] + val := int64(binary.LittleEndian.Uint64(data[1:9])) + return Decimal8(val, scale), 10, nil + case PrimitiveDecimal16: + if len(data) < 17 { + return Null(), 0, errors.New("variant value: not enough data for decimal16") + } + scale := data[0] + var val [16]byte + copy(val[:], data[1:17]) + return Decimal16(val, scale), 18, nil + default: + return Null(), 0, fmt.Errorf("variant value: unknown primitive type %d", pt) + } +} + +func decodeObject(m Metadata, header byte, data []byte) (Value, int, error) { + fieldIDSizeCode := (header >> 2) & 0x03 + offsetSzCode := (header >> 4) & 0x03 + isLarge := (header >> 6) & 0x01 + + fieldIDSize := offsetSize(fieldIDSizeCode) + offsetSz := offsetSize(offsetSzCode) + + pos := 0 + + // Read num_elements + var numElements int + if isLarge == 1 { + if len(data) < 4 { + return Null(), 0, errors.New("variant value: not enough data for object num_elements") + } + numElements = int(binary.LittleEndian.Uint32(data[:4])) + pos += 4 + } else { + if len(data) < 1 { + return Null(), 0, errors.New("variant value: not enough data for object num_elements") + } + numElements = int(data[0]) + pos += 1 + } + + // Read field IDs + fieldIDs := make([]int, numElements) + for i := range numElements { + v, n, err := readUint(data[pos:], fieldIDSize) + if err != nil { + return Null(), 0, fmt.Errorf("variant value: reading object field id %d: %w", i, err) + } + fieldIDs[i] = v + pos += n + } + + // Read offsets (numElements+1) + offsets := make([]int, numElements+1) + for i := range numElements + 1 { + v, n, err := readUint(data[pos:], offsetSz) + if err != nil { + return Null(), 0, fmt.Errorf("variant value: reading object offset %d: %w", i, err) + } + offsets[i] = v + pos += n + } + + // Value data starts at pos + valueDataStart := pos + + fields := make([]Field, numElements) + for i := range numElements { + name, err := m.Lookup(fieldIDs[i]) + if err != nil { + return Null(), 0, fmt.Errorf("variant value: object field %d: %w", i, err) + } + + valueStart := valueDataStart + offsets[i] + valueEnd := valueDataStart + offsets[i+1] + if valueStart > len(data) || valueEnd > len(data) || valueStart > valueEnd { + return Null(), 0, fmt.Errorf("variant value: object field %d: invalid value offset", i) + } + + v, _, err := decodeValue(m, data[valueStart:valueEnd]) + if err != nil { + return Null(), 0, fmt.Errorf("variant value: object field %q: %w", name, err) + } + + fields[i] = Field{Name: name, Value: v} + } + + totalConsumed := 1 + valueDataStart + offsets[numElements] // +1 for header byte + return MakeObject(fields), totalConsumed, nil +} + +func decodeArray(m Metadata, header byte, data []byte) (Value, int, error) { + offsetSzCode := (header >> 2) & 0x03 + isLarge := (header >> 4) & 0x01 + + offsetSz := offsetSize(offsetSzCode) + + pos := 0 + + // Read num_elements + var numElements int + if isLarge == 1 { + if len(data) < 4 { + return Null(), 0, errors.New("variant value: not enough data for array num_elements") + } + numElements = int(binary.LittleEndian.Uint32(data[:4])) + pos += 4 + } else { + if len(data) < 1 { + return Null(), 0, errors.New("variant value: not enough data for array num_elements") + } + numElements = int(data[0]) + pos += 1 + } + + // Read offsets (numElements+1) + offsets := make([]int, numElements+1) + for i := range numElements + 1 { + v, n, err := readUint(data[pos:], offsetSz) + if err != nil { + return Null(), 0, fmt.Errorf("variant value: reading array offset %d: %w", i, err) + } + offsets[i] = v + pos += n + } + + // Value data starts at pos + valueDataStart := pos + + elements := make([]Value, numElements) + for i := range numElements { + elemStart := valueDataStart + offsets[i] + elemEnd := valueDataStart + offsets[i+1] + if elemStart > len(data) || elemEnd > len(data) || elemStart > elemEnd { + return Null(), 0, fmt.Errorf("variant value: array element %d: invalid offset", i) + } + + v, _, err := decodeValue(m, data[elemStart:elemEnd]) + if err != nil { + return Null(), 0, fmt.Errorf("variant value: array element %d: %w", i, err) + } + + elements[i] = v + } + + totalConsumed := 1 + valueDataStart + offsets[numElements] // +1 for header byte + return MakeArray(elements), totalConsumed, nil +} diff --git a/vendor/github.com/parquet-go/parquet-go/variant/encoding.go b/vendor/github.com/parquet-go/parquet-go/variant/encoding.go new file mode 100644 index 000000000..5767d13bc --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/variant/encoding.go @@ -0,0 +1,290 @@ +package variant + +import ( + "encoding/binary" + "math" + "sort" +) + +// Encode encodes a variant Value into its binary representation. +// Field names for objects are registered with the provided MetadataBuilder. +// Returns the encoded value bytes. +func Encode(b *MetadataBuilder, v Value) []byte { + switch v.basic { + case BasicObject: + return encodeObject(b, v.object) + case BasicArray: + return encodeArray(b, v.array) + default: + return encodePrimitive(v) + } +} + +func encodePrimitive(v Value) []byte { + switch v.primitive { + case PrimitiveNull: + return []byte{makeHeader(BasicPrimitive, byte(PrimitiveNull))} + case PrimitiveTrue: + return []byte{makeHeader(BasicPrimitive, byte(PrimitiveTrue))} + case PrimitiveFalse: + return []byte{makeHeader(BasicPrimitive, byte(PrimitiveFalse))} + case PrimitiveInt8: + return []byte{makeHeader(BasicPrimitive, byte(PrimitiveInt8)), byte(v.i64)} + case PrimitiveInt16: + buf := make([]byte, 3) + buf[0] = makeHeader(BasicPrimitive, byte(PrimitiveInt16)) + binary.LittleEndian.PutUint16(buf[1:], uint16(v.i64)) + return buf + case PrimitiveInt32: + buf := make([]byte, 5) + buf[0] = makeHeader(BasicPrimitive, byte(PrimitiveInt32)) + binary.LittleEndian.PutUint32(buf[1:], uint32(v.i64)) + return buf + case PrimitiveInt64: + buf := make([]byte, 9) + buf[0] = makeHeader(BasicPrimitive, byte(PrimitiveInt64)) + binary.LittleEndian.PutUint64(buf[1:], uint64(v.i64)) + return buf + case PrimitiveFloat: + buf := make([]byte, 5) + buf[0] = makeHeader(BasicPrimitive, byte(PrimitiveFloat)) + binary.LittleEndian.PutUint32(buf[1:], math.Float32bits(float32(v.f64))) + return buf + case PrimitiveDouble: + buf := make([]byte, 9) + buf[0] = makeHeader(BasicPrimitive, byte(PrimitiveDouble)) + binary.LittleEndian.PutUint64(buf[1:], math.Float64bits(v.f64)) + return buf + case PrimitiveString: + s := v.str + if len(s) <= 63 { + // Use short string encoding + buf := make([]byte, 1+len(s)) + buf[0] = makeHeader(BasicShortString, byte(len(s))) + copy(buf[1:], s) + return buf + } + // Long string: primitive string type + 4-byte length + data + buf := make([]byte, 5+len(s)) + buf[0] = makeHeader(BasicPrimitive, byte(PrimitiveString)) + binary.LittleEndian.PutUint32(buf[1:], uint32(len(s))) + copy(buf[5:], s) + return buf + case PrimitiveBinary: + buf := make([]byte, 5+len(v.bytes)) + buf[0] = makeHeader(BasicPrimitive, byte(PrimitiveBinary)) + binary.LittleEndian.PutUint32(buf[1:], uint32(len(v.bytes))) + copy(buf[5:], v.bytes) + return buf + case PrimitiveDate: + buf := make([]byte, 5) + buf[0] = makeHeader(BasicPrimitive, byte(PrimitiveDate)) + binary.LittleEndian.PutUint32(buf[1:], uint32(v.i64)) + return buf + case PrimitiveTimestamp: + buf := make([]byte, 9) + buf[0] = makeHeader(BasicPrimitive, byte(PrimitiveTimestamp)) + binary.LittleEndian.PutUint64(buf[1:], uint64(v.i64)) + return buf + case PrimitiveTimestampNTZ: + buf := make([]byte, 9) + buf[0] = makeHeader(BasicPrimitive, byte(PrimitiveTimestampNTZ)) + binary.LittleEndian.PutUint64(buf[1:], uint64(v.i64)) + return buf + case PrimitiveTime: + buf := make([]byte, 9) + buf[0] = makeHeader(BasicPrimitive, byte(PrimitiveTime)) + binary.LittleEndian.PutUint64(buf[1:], uint64(v.i64)) + return buf + case PrimitiveUUID: + buf := make([]byte, 17) + buf[0] = makeHeader(BasicPrimitive, byte(PrimitiveUUID)) + copy(buf[1:], v.uuid[:]) + return buf + case PrimitiveDecimal4: + buf := make([]byte, 6) + buf[0] = makeHeader(BasicPrimitive, byte(PrimitiveDecimal4)) + buf[1] = v.scale + binary.LittleEndian.PutUint32(buf[2:], uint32(v.i64)) + return buf + case PrimitiveDecimal8: + buf := make([]byte, 10) + buf[0] = makeHeader(BasicPrimitive, byte(PrimitiveDecimal8)) + buf[1] = v.scale + binary.LittleEndian.PutUint64(buf[2:], uint64(v.i64)) + return buf + case PrimitiveDecimal16: + buf := make([]byte, 18) + buf[0] = makeHeader(BasicPrimitive, byte(PrimitiveDecimal16)) + buf[1] = v.scale + copy(buf[2:], v.decimal16[:]) + return buf + default: + return []byte{makeHeader(BasicPrimitive, byte(PrimitiveNull))} + } +} + +func encodeObject(b *MetadataBuilder, obj Object) []byte { + n := len(obj.Fields) + + // Register all field names and get their IDs + type fieldEntry struct { + id int + name string + valueBytes []byte + } + entries := make([]fieldEntry, n) + for i, f := range obj.Fields { + entries[i] = fieldEntry{ + id: b.Add(f.Name), + name: f.Name, + valueBytes: Encode(b, f.Value), + } + } + + // Sort by field name (as required by spec) + sort.Slice(entries, func(i, j int) bool { + return entries[i].name < entries[j].name + }) + + // Compute sizes + maxID := 0 + for _, e := range entries { + if e.id > maxID { + maxID = e.id + } + } + + // Encode all values and compute offsets + totalValueSize := 0 + valueOffsets := make([]int, n+1) + for i, e := range entries { + valueOffsets[i] = totalValueSize + totalValueSize += len(e.valueBytes) + } + valueOffsets[n] = totalValueSize + + fieldIDSizeCode := offsetSizeCode(maxID) + fieldIDSize := offsetSize(fieldIDSizeCode) + offsetSzCode := offsetSizeCode(totalValueSize) + offsetSz := offsetSize(offsetSzCode) + + // Header: basic_type=2 (object), value_header encodes num_fields, field_id_size, offset_size + // Object header byte: bits 0-1 = BasicObject(2), bits 2-3 = field_id_size_minus_one, + // bits 4-5 = offset_size_minus_one, bits 6-7 = unused + // Then: num_elements (1 byte for small objects, but spec says offset_size bytes) + // + // Actually the spec says: + // value_metadata byte: bits 0-1 = basic_type (2=object), bits 2-3 = field_id_size-1, + // bits 4-5 = offset_size-1, bits 6-7 = is_large (0=1-byte num_elements, 1=4-byte) + isLarge := byte(0) + numElemSize := 1 + if n > 255 { + isLarge = 1 + numElemSize = 4 + } + + header := byte(BasicObject) | (fieldIDSizeCode << 2) | (offsetSzCode << 4) | (isLarge << 6) + + // Total size: header(1) + num_elements + field_ids(n*fieldIDSize) + offsets((n+1)*offsetSz) + values + totalSize := 1 + numElemSize + n*fieldIDSize + (n+1)*offsetSz + totalValueSize + buf := make([]byte, totalSize) + buf[0] = header + + pos := 1 + if isLarge == 1 { + binary.LittleEndian.PutUint32(buf[pos:], uint32(n)) + pos += 4 + } else { + buf[pos] = byte(n) + pos += 1 + } + + // Write field IDs + for _, e := range entries { + writeUint(buf[pos:], e.id, fieldIDSize) + pos += fieldIDSize + } + + // Write offsets + for _, off := range valueOffsets { + writeUint(buf[pos:], off, offsetSz) + pos += offsetSz + } + + // Write values + for _, e := range entries { + copy(buf[pos:], e.valueBytes) + pos += len(e.valueBytes) + } + + return buf +} + +func encodeArray(b *MetadataBuilder, arr Array) []byte { + n := len(arr.Elements) + + // Encode all elements + encodedElems := make([][]byte, n) + totalSize := 0 + for i, e := range arr.Elements { + encodedElems[i] = Encode(b, e) + totalSize += len(encodedElems[i]) + } + + // Compute offsets + offsets := make([]int, n+1) + off := 0 + for i, enc := range encodedElems { + offsets[i] = off + off += len(enc) + } + offsets[n] = off + + offsetSzCode := offsetSizeCode(totalSize) + offsetSz := offsetSize(offsetSzCode) + + isLarge := byte(0) + numElemSize := 1 + if n > 255 { + isLarge = 1 + numElemSize = 4 + } + + header := byte(BasicArray) | (offsetSzCode << 2) | (isLarge << 4) + + // Total: header(1) + num_elements + offsets((n+1)*offsetSz) + values + bufSize := 1 + numElemSize + (n+1)*offsetSz + totalSize + buf := make([]byte, bufSize) + buf[0] = header + + pos := 1 + if isLarge == 1 { + binary.LittleEndian.PutUint32(buf[pos:], uint32(n)) + pos += 4 + } else { + buf[pos] = byte(n) + pos += 1 + } + + // Write offsets + for _, o := range offsets { + writeUint(buf[pos:], o, offsetSz) + pos += offsetSz + } + + // Write values + for _, enc := range encodedElems { + copy(buf[pos:], enc) + pos += len(enc) + } + + return buf +} + +// makeHeader constructs a value_metadata byte. +// For primitives: bits 0-1 = basic_type(0), bits 2-7 = primitive_type +// For short strings: bits 0-1 = basic_type(1), bits 2-7 = string length +func makeHeader(basic BasicType, valueHeader byte) byte { + return byte(basic) | (valueHeader << 2) +} diff --git a/vendor/github.com/parquet-go/parquet-go/variant/marshal.go b/vendor/github.com/parquet-go/parquet-go/variant/marshal.go new file mode 100644 index 000000000..377c020ca --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/variant/marshal.go @@ -0,0 +1,187 @@ +package variant + +import ( + "fmt" + "math" + "reflect" + "strings" + + "github.com/google/uuid" +) + +// Marshal converts a Go value to variant binary encoding, returning the +// metadata and value byte slices. +func Marshal(v any) (metadata, value []byte, err error) { + varVal, err := goToVariant(v) + if err != nil { + return nil, nil, err + } + var b MetadataBuilder + valueBytes := Encode(&b, varVal) + _, metadataBytes := b.Build() + return metadataBytes, valueBytes, nil +} + +// Unmarshal decodes variant binary data into a Go value. +func Unmarshal(metadata, value []byte) (any, error) { + m, err := DecodeMetadata(metadata) + if err != nil { + return nil, fmt.Errorf("variant unmarshal: %w", err) + } + v, err := Decode(m, value) + if err != nil { + return nil, fmt.Errorf("variant unmarshal: %w", err) + } + return v.GoValue(), nil +} + +// goToVariant converts a Go value to a variant Value. +func goToVariant(v any) (Value, error) { + if v == nil { + return Null(), nil + } + return goToVariantReflect(reflect.ValueOf(v)) +} + +func goToVariantReflect(rv reflect.Value) (Value, error) { + if !rv.IsValid() { + return Null(), nil + } + + // Dereference pointers/interfaces + for rv.Kind() == reflect.Ptr || rv.Kind() == reflect.Interface { + if rv.IsNil() { + return Null(), nil + } + rv = rv.Elem() + } + + // Check concrete types first + iface := rv.Interface() + switch val := iface.(type) { + case uuid.UUID: + return UUID(val), nil + } + + switch rv.Kind() { + case reflect.Bool: + return Bool(rv.Bool()), nil + case reflect.Int8: + return Int8(int8(rv.Int())), nil + case reflect.Int16: + return Int16(int16(rv.Int())), nil + case reflect.Int32: + return Int32(int32(rv.Int())), nil + case reflect.Int64, reflect.Int: + return Int64(rv.Int()), nil + case reflect.Uint8: + return Int16(int16(rv.Uint())), nil + case reflect.Uint16: + return Int32(int32(rv.Uint())), nil + case reflect.Uint32: + return Int64(int64(rv.Uint())), nil + case reflect.Uint64, reflect.Uint: + u := rv.Uint() + if u > math.MaxInt64 { + return Null(), fmt.Errorf("variant marshal: uint64 value %d overflows int64", u) + } + return Int64(int64(u)), nil + case reflect.Float32: + return Float(float32(rv.Float())), nil + case reflect.Float64: + return Double(rv.Float()), nil + case reflect.String: + return String(rv.String()), nil + case reflect.Slice: + if rv.Type().Elem().Kind() == reflect.Uint8 { + // []byte + return Binary(rv.Bytes()), nil + } + return goSliceToArray(rv) + case reflect.Array: + if rv.Type() == reflect.TypeFor[uuid.UUID]() { + var u uuid.UUID + reflect.ValueOf(&u).Elem().Set(rv) + return UUID(u), nil + } + return goSliceToArray(rv) + case reflect.Map: + return goMapToObject(rv) + case reflect.Struct: + return goStructToObject(rv) + default: + return Null(), fmt.Errorf("variant marshal: unsupported type %s", rv.Type()) + } +} + +func goSliceToArray(rv reflect.Value) (Value, error) { + n := rv.Len() + elements := make([]Value, n) + for i := range n { + v, err := goToVariantReflect(rv.Index(i)) + if err != nil { + return Null(), err + } + elements[i] = v + } + return MakeArray(elements), nil +} + +func goMapToObject(rv reflect.Value) (Value, error) { + if rv.IsNil() { + return Null(), nil + } + fields := make([]Field, 0, rv.Len()) + iter := rv.MapRange() + for iter.Next() { + key := iter.Key() + if key.Kind() != reflect.String { + return Null(), fmt.Errorf("variant marshal: map key must be string, got %s", key.Type()) + } + val, err := goToVariantReflect(iter.Value()) + if err != nil { + return Null(), err + } + fields = append(fields, Field{Name: key.String(), Value: val}) + } + return MakeObject(fields), nil +} + +func goStructToObject(rv reflect.Value) (Value, error) { + rt := rv.Type() + fields := make([]Field, 0, rt.NumField()) + for i := range rt.NumField() { + sf := rt.Field(i) + if !sf.IsExported() { + continue + } + name := fieldName(sf) + if name == "-" { + continue + } + val, err := goToVariantReflect(rv.Field(i)) + if err != nil { + return Null(), err + } + fields = append(fields, Field{Name: name, Value: val}) + } + return MakeObject(fields), nil +} + +// fieldName returns the name to use for a struct field, checking variant and +// json struct tags. +func fieldName(sf reflect.StructField) string { + if tag, ok := sf.Tag.Lookup("variant"); ok { + name, _, _ := strings.Cut(tag, ",") + if name != "" { + return name + } + } + if tag, ok := sf.Tag.Lookup("json"); ok { + name, _, _ := strings.Cut(tag, ",") + if name != "" { + return name + } + } + return sf.Name +} diff --git a/vendor/github.com/parquet-go/parquet-go/variant/metadata.go b/vendor/github.com/parquet-go/parquet-go/variant/metadata.go new file mode 100644 index 000000000..030a23adf --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/variant/metadata.go @@ -0,0 +1,198 @@ +package variant + +import ( + "encoding/binary" + "errors" + "fmt" + "sort" +) + +// Metadata holds the decoded metadata dictionary for a variant value. +type Metadata struct { + Strings []string + Sorted bool +} + +// DecodeMetadata decodes a variant metadata binary blob. +// +// Format: header(1) | dictionary_size(uint) | offsets(dictionary_size+1) | string_data +// +// Header bits: +// +// 0-3: version (must be 1) +// 4: sorted_strings +// 5-6: offset_size_minus_one +// 7: reserved (must be 0) +func DecodeMetadata(data []byte) (Metadata, error) { + if len(data) == 0 { + return Metadata{}, errors.New("variant metadata: empty data") + } + + header := data[0] + version := header & 0x0F + if version != 1 { + return Metadata{}, fmt.Errorf("variant metadata: unsupported version %d", version) + } + if header&0x80 != 0 { + return Metadata{}, errors.New("variant metadata: reserved bit is set") + } + + sorted := (header>>4)&1 == 1 + offsetSz := offsetSize((header >> 5) & 0x03) + + pos := 1 + dictSize, n, err := readUint(data[pos:], offsetSz) + if err != nil { + return Metadata{}, fmt.Errorf("variant metadata: reading dictionary_size: %w", err) + } + pos += n + + // Read (dictSize+1) offsets + offsets := make([]int, dictSize+1) + for i := range offsets { + v, n, err := readUint(data[pos:], offsetSz) + if err != nil { + return Metadata{}, fmt.Errorf("variant metadata: reading offset %d: %w", i, err) + } + offsets[i] = v + pos += n + } + + stringData := data[pos:] + + strings := make([]string, dictSize) + for i := range dictSize { + start := offsets[i] + end := offsets[i+1] + if start > end || end > len(stringData) { + return Metadata{}, fmt.Errorf("variant metadata: invalid string offset [%d, %d) in data of length %d", start, end, len(stringData)) + } + strings[i] = string(stringData[start:end]) + } + + return Metadata{Strings: strings, Sorted: sorted}, nil +} + +// Lookup returns the string at the given dictionary index. +func (m Metadata) Lookup(id int) (string, error) { + if id < 0 || id >= len(m.Strings) { + return "", fmt.Errorf("variant metadata: index %d out of range [0, %d)", id, len(m.Strings)) + } + return m.Strings[id], nil +} + +// MetadataBuilder builds a variant metadata dictionary. +type MetadataBuilder struct { + strings []string + index map[string]int +} + +// Add adds a string to the dictionary and returns its index. +// If the string already exists, the existing index is returned. +func (b *MetadataBuilder) Add(s string) int { + if b.index == nil { + b.index = make(map[string]int) + } + if idx, ok := b.index[s]; ok { + return idx + } + idx := len(b.strings) + b.strings = append(b.strings, s) + b.index[s] = idx + return idx +} + +// Build returns the decoded Metadata and the encoded binary representation. +// The dictionary indices in the output match those returned by Add, so +// encoded values referencing those indices remain valid. +func (b *MetadataBuilder) Build() (Metadata, []byte) { + n := len(b.strings) + + // Check if the strings happen to be sorted + sorted := sort.StringsAreSorted(b.strings) + + // Compute string data and offsets + offsets := make([]int, n+1) + totalLen := 0 + for i, s := range b.strings { + offsets[i] = totalLen + totalLen += len(s) + } + offsets[n] = totalLen + + // Determine offset size + maxOffset := max(totalLen, n) + osc := offsetSizeCode(maxOffset) + offsetSz := offsetSize(osc) + + // Build the binary blob + // header(1) + dict_size(offsetSz) + offsets((n+1)*offsetSz) + string_data(totalLen) + size := 1 + offsetSz + (n+1)*offsetSz + totalLen + buf := make([]byte, size) + + // Header: version=1, sorted_strings flag, offset_size_minus_one + sortedBit := byte(0) + if sorted { + sortedBit = 1 + } + buf[0] = 1 | (sortedBit << 4) | (osc << 5) + + pos := 1 + writeUint(buf[pos:], n, offsetSz) + pos += offsetSz + + for _, off := range offsets { + writeUint(buf[pos:], off, offsetSz) + pos += offsetSz + } + + for _, s := range b.strings { + copy(buf[pos:], s) + pos += len(s) + } + + return Metadata{Strings: b.strings, Sorted: sorted}, buf +} + +// Reset clears the builder for reuse. +func (b *MetadataBuilder) Reset() { + b.strings = b.strings[:0] + for k := range b.index { + delete(b.index, k) + } +} + +// readUint reads an unsigned integer of the given byte width from data. +func readUint(data []byte, size int) (int, int, error) { + if len(data) < size { + return 0, 0, errors.New("not enough data") + } + switch size { + case 1: + return int(data[0]), 1, nil + case 2: + return int(binary.LittleEndian.Uint16(data[:2])), 2, nil + case 3: + return int(data[0]) | int(data[1])<<8 | int(data[2])<<16, 3, nil + case 4: + return int(binary.LittleEndian.Uint32(data[:4])), 4, nil + default: + return 0, 0, fmt.Errorf("invalid offset size %d", size) + } +} + +// writeUint writes an unsigned integer of the given byte width to buf. +func writeUint(buf []byte, v int, size int) { + switch size { + case 1: + buf[0] = byte(v) + case 2: + binary.LittleEndian.PutUint16(buf[:2], uint16(v)) + case 3: + buf[0] = byte(v) + buf[1] = byte(v >> 8) + buf[2] = byte(v >> 16) + case 4: + binary.LittleEndian.PutUint32(buf[:4], uint32(v)) + } +} diff --git a/vendor/github.com/parquet-go/parquet-go/variant/types.go b/vendor/github.com/parquet-go/parquet-go/variant/types.go new file mode 100644 index 000000000..7e1f76a68 --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/variant/types.go @@ -0,0 +1,87 @@ +package variant + +// BasicType represents the basic type encoded in bits 0-1 of a value header byte. +type BasicType byte + +const ( + BasicPrimitive BasicType = 0 + BasicShortString BasicType = 1 + BasicObject BasicType = 2 + BasicArray BasicType = 3 +) + +// PrimitiveType represents the primitive type encoded in bits 2-7 of a +// value header byte when BasicType is BasicPrimitive. +type PrimitiveType byte + +const ( + PrimitiveNull PrimitiveType = 0 + PrimitiveTrue PrimitiveType = 1 + PrimitiveFalse PrimitiveType = 2 + PrimitiveInt8 PrimitiveType = 3 + PrimitiveInt16 PrimitiveType = 4 + PrimitiveInt32 PrimitiveType = 5 + PrimitiveInt64 PrimitiveType = 6 + PrimitiveDouble PrimitiveType = 7 + PrimitiveDecimal4 PrimitiveType = 8 + PrimitiveDecimal8 PrimitiveType = 9 + PrimitiveDecimal16 PrimitiveType = 10 + PrimitiveDate PrimitiveType = 11 + PrimitiveTimestamp PrimitiveType = 12 + PrimitiveTimestampNTZ PrimitiveType = 13 + PrimitiveFloat PrimitiveType = 14 + PrimitiveBinary PrimitiveType = 15 + PrimitiveString PrimitiveType = 16 + PrimitiveTime PrimitiveType = 17 + PrimitiveUUID PrimitiveType = 20 +) + +// primitiveSize returns the byte size of a primitive value's data portion, +// or -1 for variable-length types. +func primitiveSize(p PrimitiveType) int { + switch p { + case PrimitiveNull, PrimitiveTrue, PrimitiveFalse: + return 0 + case PrimitiveInt8: + return 1 + case PrimitiveInt16: + return 2 + case PrimitiveInt32, PrimitiveFloat, PrimitiveDate: + return 4 + case PrimitiveInt64, PrimitiveDouble, PrimitiveTimestamp, PrimitiveTimestampNTZ, PrimitiveTime: + return 8 + case PrimitiveDecimal4: + return 5 // 1 scale + 4 value + case PrimitiveDecimal8: + return 9 // 1 scale + 8 value + case PrimitiveDecimal16: + return 17 // 1 scale + 16 value + case PrimitiveBinary, PrimitiveString: + return -1 // variable length + case PrimitiveUUID: + return 16 + default: + return -1 + } +} + +// offsetSize returns the byte count for encoding integers with the given +// offset size code (0-3 → 1,2,3,4 bytes). +func offsetSize(code byte) int { + return int(code) + 1 +} + +// offsetSizeCode returns the smallest offset size code that can represent +// the given maximum value. +func offsetSizeCode(maxVal int) byte { + switch { + case maxVal <= 0xFF: + return 0 + case maxVal <= 0xFFFF: + return 1 + case maxVal <= 0xFFFFFF: + return 2 + default: + return 3 + } +} diff --git a/vendor/github.com/parquet-go/parquet-go/variant/value.go b/vendor/github.com/parquet-go/parquet-go/variant/value.go new file mode 100644 index 000000000..266118748 --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/variant/value.go @@ -0,0 +1,357 @@ +package variant + +import ( + "math" + + "github.com/google/uuid" +) + +// Value represents a variant value. The zero value is Null. +type Value struct { + primitive PrimitiveType + basic BasicType + scale byte // for decimal types + i64 int64 + f64 float64 + str string + bytes []byte + uuid uuid.UUID + decimal16 [16]byte + object Object + array Array +} + +// Object represents a variant object (ordered set of named fields). +type Object struct { + Fields []Field +} + +// Field is a single named field within an Object. +type Field struct { + Name string + Value Value +} + +// Array represents a variant array. +type Array struct { + Elements []Value +} + +// Null returns a null variant value. +func Null() Value { + return Value{basic: BasicPrimitive, primitive: PrimitiveNull} +} + +// Bool returns a boolean variant value. +func Bool(v bool) Value { + p := PrimitiveFalse + if v { + p = PrimitiveTrue + } + return Value{basic: BasicPrimitive, primitive: p} +} + +// Int8 returns an int8 variant value. +func Int8(v int8) Value { + return Value{basic: BasicPrimitive, primitive: PrimitiveInt8, i64: int64(v)} +} + +// Int16 returns an int16 variant value. +func Int16(v int16) Value { + return Value{basic: BasicPrimitive, primitive: PrimitiveInt16, i64: int64(v)} +} + +// Int32 returns an int32 variant value. +func Int32(v int32) Value { + return Value{basic: BasicPrimitive, primitive: PrimitiveInt32, i64: int64(v)} +} + +// Int64 returns an int64 variant value. +func Int64(v int64) Value { + return Value{basic: BasicPrimitive, primitive: PrimitiveInt64, i64: v} +} + +// Float returns a float32 variant value. +func Float(v float32) Value { + return Value{basic: BasicPrimitive, primitive: PrimitiveFloat, f64: float64(v)} +} + +// Double returns a float64 variant value. +func Double(v float64) Value { + return Value{basic: BasicPrimitive, primitive: PrimitiveDouble, f64: v} +} + +// String returns a string variant value. +func String(v string) Value { + return Value{basic: BasicPrimitive, primitive: PrimitiveString, str: v} +} + +// Binary returns a binary variant value. +func Binary(v []byte) Value { + return Value{basic: BasicPrimitive, primitive: PrimitiveBinary, bytes: v} +} + +// Date returns a date variant value (days since epoch). +func Date(v int32) Value { + return Value{basic: BasicPrimitive, primitive: PrimitiveDate, i64: int64(v)} +} + +// Timestamp returns a timestamp variant value (microseconds since epoch, UTC). +func Timestamp(v int64) Value { + return Value{basic: BasicPrimitive, primitive: PrimitiveTimestamp, i64: v} +} + +// TimestampNTZ returns a timestamp without timezone variant value (microseconds since epoch). +func TimestampNTZ(v int64) Value { + return Value{basic: BasicPrimitive, primitive: PrimitiveTimestampNTZ, i64: v} +} + +// Time returns a time variant value (microseconds since midnight). +func Time(v int64) Value { + return Value{basic: BasicPrimitive, primitive: PrimitiveTime, i64: v} +} + +// UUID returns a UUID variant value. +func UUID(v uuid.UUID) Value { + return Value{basic: BasicPrimitive, primitive: PrimitiveUUID, uuid: v} +} + +// Decimal4 returns a 4-byte decimal variant value with the given unscaled value and scale. +func Decimal4(v int32, scale byte) Value { + return Value{basic: BasicPrimitive, primitive: PrimitiveDecimal4, i64: int64(v), scale: scale} +} + +// Decimal8 returns an 8-byte decimal variant value with the given unscaled value and scale. +func Decimal8(v int64, scale byte) Value { + return Value{basic: BasicPrimitive, primitive: PrimitiveDecimal8, i64: v, scale: scale} +} + +// Decimal16 returns a 16-byte decimal variant value with the given unscaled value and scale. +func Decimal16(v [16]byte, scale byte) Value { + return Value{basic: BasicPrimitive, primitive: PrimitiveDecimal16, decimal16: v, scale: scale} +} + +// MakeObject creates an object variant value from a slice of fields. +func MakeObject(fields []Field) Value { + return Value{basic: BasicObject, object: Object{Fields: fields}} +} + +// MakeArray creates an array variant value from a slice of values. +func MakeArray(elements []Value) Value { + return Value{basic: BasicArray, array: Array{Elements: elements}} +} + +// Type returns the PrimitiveType of the value. For non-primitive values +// (objects, arrays, short strings), it returns -1. +func (v Value) Type() PrimitiveType { + return v.primitive +} + +// Basic returns the BasicType of the value. +func (v Value) Basic() BasicType { + return v.basic +} + +// IsNull returns true if the value is a null variant. +func (v Value) IsNull() bool { + return v.basic == BasicPrimitive && v.primitive == PrimitiveNull +} + +// BoolValue returns the boolean value. Panics if not a boolean. +func (v Value) BoolValue() bool { + return v.primitive == PrimitiveTrue +} + +// Int returns the integer value as int64. Works for Int8, Int16, Int32, Int64, +// Date, Timestamp, TimestampNTZ, and Time. +func (v Value) Int() int64 { + return v.i64 +} + +// FloatValue returns the floating-point value. Works for Float and Double. +func (v Value) FloatValue() float64 { + return v.f64 +} + +// isString returns true if the value is any string type (short or primitive). +func (v Value) isString() bool { + return v.basic == BasicShortString || + (v.basic == BasicPrimitive && v.primitive == PrimitiveString) +} + +// Str returns the string value. +func (v Value) Str() string { + return v.str +} + +// Bytes returns the binary value. +func (v Value) Bytes() []byte { + return v.bytes +} + +// UUIDValue returns the UUID value. +func (v Value) UUIDValue() uuid.UUID { + return v.uuid +} + +// Scale returns the decimal scale. Only valid for Decimal4, Decimal8, Decimal16. +func (v Value) Scale() byte { + return v.scale +} + +// Decimal16Value returns the 16-byte decimal value. +func (v Value) Decimal16Value() [16]byte { + return v.decimal16 +} + +// ObjectValue returns the Object. Panics if not an object. +func (v Value) ObjectValue() Object { + return v.object +} + +// ArrayValue returns the Array. Panics if not an array. +func (v Value) ArrayValue() Array { + return v.array +} + +// GoValue converts the variant value to a natural Go type. +// +// Mapping: +// +// Null → nil +// True/False → bool +// Int8 → int8, Int16 → int16, Int32 → int32, Int64 → int64 +// Float → float32, Double → float64 +// String → string, Binary → []byte +// Date → int32 (days since epoch) +// Timestamp/TimestampNTZ → int64 (microseconds since epoch) +// Time → int64 (microseconds since midnight) +// UUID → uuid.UUID +// Decimal4 → int32 (unscaled), Decimal8 → int64 (unscaled), Decimal16 → [16]byte +// Object → map[string]any +// Array → []any +func (v Value) GoValue() any { + switch v.basic { + case BasicObject: + m := make(map[string]any, len(v.object.Fields)) + for _, f := range v.object.Fields { + m[f.Name] = f.Value.GoValue() + } + return m + case BasicArray: + a := make([]any, len(v.array.Elements)) + for i, e := range v.array.Elements { + a[i] = e.GoValue() + } + return a + case BasicShortString: + return v.str + } + // BasicPrimitive + switch v.primitive { + case PrimitiveNull: + return nil + case PrimitiveTrue: + return true + case PrimitiveFalse: + return false + case PrimitiveInt8: + return int8(v.i64) + case PrimitiveInt16: + return int16(v.i64) + case PrimitiveInt32: + return int32(v.i64) + case PrimitiveInt64: + return v.i64 + case PrimitiveFloat: + return float32(v.f64) + case PrimitiveDouble: + return v.f64 + case PrimitiveString: + return v.str + case PrimitiveBinary: + return v.bytes + case PrimitiveDate: + return int32(v.i64) + case PrimitiveTimestamp, PrimitiveTimestampNTZ, PrimitiveTime: + return v.i64 + case PrimitiveUUID: + return v.uuid + case PrimitiveDecimal4: + return int32(v.i64) + case PrimitiveDecimal8: + return v.i64 + case PrimitiveDecimal16: + return v.decimal16 + default: + return nil + } +} + +// Equal reports whether two variant values are deeply equal. +// Short strings and primitive strings with the same content are considered equal. +func (v Value) Equal(other Value) bool { + // Normalize: treat short strings and primitive strings as equivalent + if v.isString() && other.isString() { + return v.str == other.str + } + if v.basic != other.basic { + return false + } + switch v.basic { + case BasicObject: + if len(v.object.Fields) != len(other.object.Fields) { + return false + } + for i, f := range v.object.Fields { + of := other.object.Fields[i] + if f.Name != of.Name || !f.Value.Equal(of.Value) { + return false + } + } + return true + case BasicArray: + if len(v.array.Elements) != len(other.array.Elements) { + return false + } + for i, e := range v.array.Elements { + if !e.Equal(other.array.Elements[i]) { + return false + } + } + return true + case BasicShortString: + return v.str == other.str + } + if v.primitive != other.primitive { + return false + } + switch v.primitive { + case PrimitiveNull, PrimitiveTrue, PrimitiveFalse: + return true + case PrimitiveFloat: + return math.Float32bits(float32(v.f64)) == math.Float32bits(float32(other.f64)) + case PrimitiveDouble: + return math.Float64bits(v.f64) == math.Float64bits(other.f64) + case PrimitiveString: + return v.str == other.str + case PrimitiveBinary: + if len(v.bytes) != len(other.bytes) { + return false + } + for i := range v.bytes { + if v.bytes[i] != other.bytes[i] { + return false + } + } + return true + case PrimitiveUUID: + return v.uuid == other.uuid + case PrimitiveDecimal4, PrimitiveDecimal8: + return v.i64 == other.i64 && v.scale == other.scale + case PrimitiveDecimal16: + return v.decimal16 == other.decimal16 && v.scale == other.scale + default: + return v.i64 == other.i64 + } +} diff --git a/vendor/github.com/parquet-go/parquet-go/writer.go b/vendor/github.com/parquet-go/parquet-go/writer.go new file mode 100644 index 000000000..645346eb5 --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/writer.go @@ -0,0 +1,2727 @@ +package parquet + +import ( + "bufio" + "bytes" + "cmp" + "encoding/binary" + "fmt" + "hash/crc32" + "io" + "math" + "math/bits" + "os" + "reflect" + "slices" + "strings" + + "github.com/parquet-go/parquet-go/compress" + "github.com/parquet-go/parquet-go/encoding" + "github.com/parquet-go/parquet-go/encoding/plain" + "github.com/parquet-go/parquet-go/encoding/thrift" + "github.com/parquet-go/parquet-go/format" + "github.com/parquet-go/parquet-go/internal/memory" + "github.com/parquet-go/parquet-go/sparse" +) + +const ( + // The uncompressed page size is stored as int32 and must not be larger than the + // maximum int32 value (see format.PageHeader). + maxUncompressedPageSize = math.MaxInt32 +) + +// GenericWriter is similar to a Writer but uses a type parameter to define the +// Go type representing the schema of rows being written. +// +// Using this type over Writer has multiple advantages: +// +// - By leveraging type information, the Go compiler can provide greater +// guarantees that the code is correct. For example, the parquet.Writer.Write +// method accepts an argument of type interface{}, which delays type checking +// until runtime. The parquet.GenericWriter[T].Write method ensures at +// compile time that the values it receives will be of type T, reducing the +// risk of introducing errors. +// +// - Since type information is known at compile time, the implementation of +// parquet.GenericWriter[T] can make safe assumptions, removing the need for +// runtime validation of how the parameters are passed to its methods. +// Optimizations relying on type information are more effective, some of the +// writer's state can be precomputed at initialization, which was not possible +// with parquet.Writer. +// +// - The parquet.GenericWriter[T].Write method uses a data-oriented design, +// accepting an slice of T instead of a single value, creating more +// opportunities to amortize the runtime cost of abstractions. +// This optimization is not available for parquet.Writer because its Write +// method's argument would be of type []interface{}, which would require +// conversions back and forth from concrete types to empty interfaces (since +// a []T cannot be interpreted as []interface{} in Go), would make the API +// more difficult to use and waste compute resources in the type conversions, +// defeating the purpose of the optimization in the first place. +// +// Note that this type is only available when compiling with Go 1.18 or later. +type GenericWriter[T any] struct { + // At this time GenericWriter is expressed in terms of Writer to reuse the + // underlying logic. In the future, and if we accepted to break backward + // compatibility on the Write method, we could modify Writer to be an alias + // to GenericWriter with: + // + // type Writer = GenericWriter[any] + // + base Writer + // This function writes rows of type T to the writer, it gets generated by + // the NewGenericWriter function based on the type T and the underlying + // schema of the parquet file. + write writeFunc[T] + // This field is used to leverage the optimized writeRowsFunc algorithms. + columns []ColumnBuffer +} + +// NewGenericWriter is like NewWriter but returns a GenericWriter[T] suited to +// write rows of Go type T. +// +// The type parameter T should be a map, struct, or any. Any other types will +// cause a panic at runtime. Type checking is a lot more effective when the +// generic parameter is a struct type, using map and interface types is somewhat +// similar to using a Writer. +// +// If the option list may explicitly declare a schema, it must be compatible +// with the schema generated from T. +// +// Sorting columns may be set on the writer to configure the generated row +// groups metadata. However, rows are always written in the order they were +// seen, no reordering is performed, the writer expects the application to +// ensure proper correlation between the order of rows and the list of sorting +// columns. See SortingWriter[T] for a writer which handles reordering rows +// based on the configured sorting columns. +func NewGenericWriter[T any](output io.Writer, options ...WriterOption) *GenericWriter[T] { + config, err := NewWriterConfig(options...) + if err != nil { + panic(err) + } + + t := typeOf[T]() + + var genWriteErr error + if t != nil { + if columnName, ok := validateColumns(dereference(t)); !ok { + genWriteErr = fmt.Errorf("cannot write %v: it has columns with the same parquet column name %q", t, columnName) + } + } + + schemaFromConf := config.Schema + schemaFromType := (*Schema)(nil) + + if t != nil && dereference(t).Kind() == reflect.Struct { + schemaFromType = schemaOf(dereference(t), config.SchemaConfig.StructTags...) + } + + config.Schema = cmp.Or(schemaFromConf, schemaFromType) + if config.Schema == nil { + panic("generic writer must be instantiated with schema or concrete type.") + } + if len(config.Schema.Columns()) == 0 { + genWriteErr = fmt.Errorf("cannot write %v: it has no columns (maybe it has no exported fields)", t) + } + + var writeFn writeFunc[T] + switch { + case genWriteErr != nil: + writeFn = func(*GenericWriter[T], []T) (int, error) { return 0, genWriteErr } + case schemaFromType == config.Schema || (schemaFromType != nil && EqualNodes(config.Schema, schemaFromType)): + // The schema matches the type T, we can use the optimized + // writeRowsFunc algorithms mapping Go values directly to + // parquet columns, using the sparse package. + writeRows := writeRowsFuncOf(t, config.Schema, nil, config.SchemaConfig.StructTags) + writeFn = makeWriteFunc[T](t, writeRows) + default: + // The schema does not match the type T, we have to + // deconstruct each value of type T into a Row first. + // This is less efficient but still type-safe. + _, writeValue := writeValueFuncOf(0, config.Schema) + writeFn = makeWriteFunc[T](t, func(columns []ColumnBuffer, levels columnLevels, rows sparse.Array) { + if rows.Len() == 0 { + writeValue(columns, levels, reflect.Value{}) + return + } + for i := range rows.Len() { + v := reflect.ValueOf((*T)(rows.Index(i))).Elem() + writeValue(columns, levels, v) + } + }) + } + + return &GenericWriter[T]{ + base: Writer{ + output: output, + config: config, + schema: config.Schema, + writer: newWriter(output, config), + }, + write: writeFn, + } +} + +func validateColumns(t reflect.Type) (string, bool) { + if t.Kind() != reflect.Struct { + return "", true + } + + columns := make(map[string]struct{}, t.NumField()) + + for i := range t.NumField() { + f := t.Field(i) + if !f.IsExported() { + continue + } + fieldTag, columnName := f.Tag.Get("parquet"), f.Name + if fieldTag != "" { + if commaIdx := strings.IndexByte(fieldTag, ','); commaIdx >= 0 { + fieldTag = fieldTag[:commaIdx] + } + if fieldTag == "-" { + continue + } + if fieldTag != "" { + columnName = fieldTag + } + } + if _, exists := columns[columnName]; exists { + return columnName, false + } else { + columns[columnName] = struct{}{} + } + } + + return "", true +} + +type writeFunc[T any] func(*GenericWriter[T], []T) (int, error) + +func makeWriteFunc[T any](t reflect.Type, writeRows writeRowsFunc) writeFunc[T] { + return func(w *GenericWriter[T], rows []T) (n int, err error) { + if w.columns == nil { + w.columns = make([]ColumnBuffer, len(w.base.writer.currentRowGroup.columns)) + for i, c := range w.base.writer.currentRowGroup.columns { + // These fields are usually lazily initialized when writing rows, + // we need them to exist now tho. + c.columnBuffer = c.newColumnBuffer() + w.columns[i] = c.columnBuffer + } + } + writeRows(w.columns, columnLevels{}, makeArrayFromSlice(rows)) + return len(rows), nil + } +} + +// Close must be called after all values were produced to the writer in order to +// flush all buffers and write the parquet footer. The writer can only be reused +// if Reset is called first. Failure to do so will result in defined behavior. +func (w *GenericWriter[T]) Close() error { + if err := w.base.Close(); err != nil { + return err + } + return nil +} + +func (w *GenericWriter[T]) Flush() error { + return w.base.Flush() +} + +// Reset clears the state of the writer without flushing any of the buffers, +// and setting the output to the io.Writer passed as argument, allowing the +// writer to be reused to produce another parquet file. +func (w *GenericWriter[T]) Reset(output io.Writer) { + w.base.Reset(output) +} + +func (w *GenericWriter[T]) Write(rows []T) (written int, err error) { + var n int + currentRowGroup := w.base.writer.currentRowGroup + for len(rows) > 0 { + n, err = currentRowGroup.writeRows(len(rows), func(i, j int) (int, error) { + n, err := w.write(w, rows[i:j:j]) + if err != nil { + return n, err + } + + for _, c := range currentRowGroup.columns { + if c.columnBuffer != nil && c.columnBuffer.Size() >= int64(c.bufferSize) { + if err := c.Flush(); err != nil { + return n, err + } + } + } + + return n, nil + }) + rows = rows[n:] + written += n + if err != ErrTooManyRowGroups { + break + } + if err = w.base.writer.flush(); err != nil { + break + } + } + return +} + +func (w *GenericWriter[T]) WriteRows(rows []Row) (int, error) { + return w.base.WriteRows(rows) +} + +func (w *GenericWriter[T]) WriteRowGroup(rowGroup RowGroup) (int64, error) { + return w.base.WriteRowGroup(rowGroup) +} + +// SetKeyValueMetadata sets a key/value pair in the Parquet file metadata. +// +// Keys are assumed to be unique, if the same key is repeated multiple times the +// last value is retained. While the parquet format does not require unique keys, +// this design decision was made to optimize for the most common use case where +// applications leverage this extension mechanism to associate single values to +// keys. This may create incompatibilities with other parquet libraries, or may +// cause some key/value pairs to be lost when open parquet files written with +// repeated keys. We can revisit this decision if it ever becomes a blocker. +func (w *GenericWriter[T]) SetKeyValueMetadata(key, value string) { + w.base.SetKeyValueMetadata(key, value) +} + +func (w *GenericWriter[T]) ReadRowsFrom(rows RowReader) (int64, error) { + return w.base.ReadRowsFrom(rows) +} + +func (w *GenericWriter[T]) Schema() *Schema { + return w.base.Schema() +} + +// Size returns an estimate of the current file size in bytes. +// See Writer.Size for details. +func (w *GenericWriter[T]) Size() int64 { + return w.base.Size() +} + +func (w *GenericWriter[T]) ColumnWriters() []*ColumnWriter { + return w.base.ColumnWriters() +} + +// File returns a FileView of the written parquet file. +// Only available after Close is called. +func (w *GenericWriter[T]) File() FileView { + return w.base.File() +} + +// ConcurrentRowGroupWriter is a row group writer that can be used to write row groups +// in parallel. Multiple row groups can be created concurrently and written to independently, +// but they must be committed serially to maintain the order of row groups in the file. +// +// See BeginRowGroup for more information on how this can be used. +// +// While multiple row groups can be created concurrently, a single row group must be written +// sequentially. +type ConcurrentRowGroupWriter struct { + writer *writer + config *WriterConfig + values [][]Value + numRows int64 + maxRows int64 + columns []*ColumnWriter + columnChunk []format.ColumnChunk + columnIndex []format.ColumnIndex + offsetIndex []format.OffsetIndex +} + +// BeginRowGroup returns a new ConcurrentRowGroupWriter that can be written to in parallel with +// other row groups. However these need to be committed back to the writer serially using the +// Commit method on the row group. +// +// Example usage could look something like: +// +// writer := parquet.NewGenericWriter[any](...) +// rgs := make([]*parquet.ConcurrentRowGroupWriter, 5) +// var wg sync.WaitGroup +// for i := range rgs { +// rg := writer.BeginRowGroup() +// rgs[i] = rg +// wg.Add(1) +// go func() { +// defer wg.Done() +// writeChunkRows(i, rg) +// }() +// } +// wg.Wait() +// for _, rg := range rgs { +// if _, err := rg.Commit(); err != nil { +// return err +// } +// } +// return writer.Close() +func (w *GenericWriter[T]) BeginRowGroup() *ConcurrentRowGroupWriter { + return newConcurrentRowGroupWriter(w.base.writer, w.base.config) +} + +var ( + _ RowWriterWithSchema = (*GenericWriter[any])(nil) + _ RowReaderFrom = (*GenericWriter[any])(nil) + _ RowGroupWriter = (*GenericWriter[any])(nil) + + _ RowWriterWithSchema = (*GenericWriter[struct{}])(nil) + _ RowReaderFrom = (*GenericWriter[struct{}])(nil) + _ RowGroupWriter = (*GenericWriter[struct{}])(nil) + + _ RowWriterWithSchema = (*GenericWriter[map[struct{}]struct{}])(nil) + _ RowReaderFrom = (*GenericWriter[map[struct{}]struct{}])(nil) + _ RowGroupWriter = (*GenericWriter[map[struct{}]struct{}])(nil) +) + +// Deprecated: A Writer uses a parquet schema and sequence of Go values to +// produce a parquet file to an io.Writer. +// +// Use NewGenericWriter instead. To maintain dynamic behavior (schema unknown at compile time), +// use "any" as the type parameter: +// +// This gives you the same behavior as the old Writer +// w := parquet.NewGenericWriter[any](output, schema) +// +// This example showcases a typical use of parquet writers: +// +// writer := parquet.NewWriter(output) +// +// for _, row := range rows { +// if err := writer.Write(row); err != nil { +// ... +// } +// } +// +// if err := writer.Close(); err != nil { +// ... +// } +// +// The Writer type optimizes for minimal memory usage, each page is written as +// soon as it has been filled so only a single page per column needs to be held +// in memory and as a result, there are no opportunities to sort rows within an +// entire row group. Programs that need to produce parquet files with sorted +// row groups should use the Buffer type to buffer and sort the rows prior to +// writing them to a Writer. +// +// For programs building with Go 1.18 or later, the GenericWriter[T] type +// supersedes this one. +type Writer struct { + output io.Writer + config *WriterConfig + schema *Schema + writer *writer + rowbuf []Row +} + +// NewWriter constructs a parquet writer writing a file to the given io.Writer. +// +// The function panics if the writer configuration is invalid. Programs that +// cannot guarantee the validity of the options passed to NewWriter should +// construct the writer configuration independently prior to calling this +// function: +// +// config, err := parquet.NewWriterConfig(options...) +// if err != nil { +// // handle the configuration error +// ... +// } else { +// // this call to create a writer is guaranteed not to panic +// writer := parquet.NewWriter(output, config) +// ... +// } +func NewWriter(output io.Writer, options ...WriterOption) *Writer { + config, err := NewWriterConfig(options...) + if err != nil { + panic(err) + } + w := &Writer{ + output: output, + config: config, + } + if config.Schema != nil { + w.configure(config.Schema) + } + return w +} + +func (w *Writer) configure(schema *Schema) { + if schema != nil { + w.config.Schema = schema + w.schema = schema + w.writer = newWriter(w.output, w.config) + } +} + +// Close must be called after all values were produced to the writer in order to +// flush all buffers and write the parquet footer. +func (w *Writer) Close() error { + for _, c := range w.ColumnWriters() { + if err := c.Close(); err != nil { + return err + } + } + if w.writer != nil { + return w.writer.close() + } + return nil +} + +// Flush flushes all buffers into a row group to the underlying io.Writer. +// +// Flush is called automatically on Close, it is only useful to call explicitly +// if the application needs to limit the size of row groups or wants to produce +// multiple row groups per file. +// +// If the writer attempts to create more than MaxRowGroups row groups the method +// returns ErrTooManyRowGroups. +func (w *Writer) Flush() error { + if w.writer != nil { + return w.writer.flush() + } + return nil +} + +// Reset clears the state of the writer without flushing any of the buffers, +// and setting the output to the io.Writer passed as argument, allowing the +// writer to be reused to produce another parquet file. +// +// Reset may be called at any time, including after a writer was closed. +func (w *Writer) Reset(output io.Writer) { + if w.output = output; w.writer != nil { + w.writer.reset(w.output) + } +} + +// Write is called to write another row to the parquet file. +// +// The method uses the parquet schema configured on w to traverse the Go value +// and decompose it into a set of columns and values. If no schema were passed +// to NewWriter, it is deducted from the Go type of the row, which then have to +// be a struct or pointer to struct. +func (w *Writer) Write(row any) error { + if w.schema == nil { + w.configure(SchemaOf(row)) + } + if cap(w.rowbuf) == 0 { + w.rowbuf = make([]Row, 1) + } else { + w.rowbuf = w.rowbuf[:1] + } + defer clearRows(w.rowbuf) + w.rowbuf[0] = w.schema.Deconstruct(w.rowbuf[0][:0], row) + _, err := w.WriteRows(w.rowbuf) + return err +} + +// WriteRows is called to write rows to the parquet file. +// +// The Writer must have been given a schema when NewWriter was called, otherwise +// the structure of the parquet file cannot be determined from the row only. +// +// The row is expected to contain values for each column of the writer's schema, +// in the order produced by the parquet.(*Schema).Deconstruct method. +func (w *Writer) WriteRows(rows []Row) (int, error) { + return w.writer.WriteRows(rows) +} + +// WriteRowGroup writes a row group to the parquet file. +// +// Buffered rows will be flushed prior to writing rows from the group, unless +// the row group was empty in which case nothing is written to the file. +// +// The content of the row group is flushed to the writer; after the method +// returns successfully, the row group will be empty and in ready to be reused. +func (w *Writer) WriteRowGroup(rowGroup RowGroup) (int64, error) { + rowGroupSchema := rowGroup.Schema() + switch { + case rowGroupSchema == nil: + return 0, ErrRowGroupSchemaMissing + case w.schema == nil: + w.configure(rowGroupSchema) + case !EqualNodes(w.schema, rowGroupSchema): + return 0, ErrRowGroupSchemaMismatch + } + if err := w.writer.flush(); err != nil { + return 0, err + } + w.writer.currentRowGroup.configureBloomFilters(rowGroup.ColumnChunks()) + rows := rowGroup.Rows() + defer rows.Close() + n, err := CopyRows(w.writer, rows) + if err != nil { + return n, err + } + return w.writer.writeRowGroup(w.writer.currentRowGroup, rowGroup.Schema(), rowGroup.SortingColumns()) +} + +// ReadRowsFrom reads rows from the reader passed as arguments and writes them +// to w. +// +// This is similar to calling WriteRow repeatedly, but will be more efficient +// if optimizations are supported by the reader. +func (w *Writer) ReadRowsFrom(rows RowReader) (written int64, err error) { + if w.schema == nil { + if r, ok := rows.(RowReaderWithSchema); ok { + w.configure(r.Schema()) + } + } + if cap(w.rowbuf) < defaultRowBufferSize { + w.rowbuf = make([]Row, defaultRowBufferSize) + } else { + w.rowbuf = w.rowbuf[:cap(w.rowbuf)] + } + return copyRows(w.writer, rows, w.rowbuf) +} + +// Schema returns the schema of rows written by w. +// +// The returned value will be nil if no schema has yet been configured on w. +func (w *Writer) Schema() *Schema { return w.schema } + +// Size returns an estimate of the current file size in bytes, including all +// completed row groups and the current in-progress row group. +// +// The estimate includes bytes already written to the underlying writer plus +// the estimated size of buffered data in the current row group. The buffered +// data has not been compressed yet, so this is an upper-bound estimate. +// +// The size of the footer metadata is not included because it is variable and +// typically small relative to the data. +// +// This method can be used to target a specific file size by calling Flush +// when Size reaches a threshold. +func (w *Writer) Size() int64 { + if w.writer == nil { + return 0 + } + return w.writer.writer.offset + w.writer.currentRowGroup.Size() + w.writer.deferredBloomFilterSize +} + +// SetKeyValueMetadata sets a key/value pair in the Parquet file metadata. +// +// Keys are assumed to be unique, if the same key is repeated multiple times the +// last value is retained. While the parquet format does not require unique keys, +// this design decision was made to optimize for the most common use case where +// applications leverage this extension mechanism to associate single values to +// keys. This may create incompatibilities with other parquet libraries, or may +// cause some key/value pairs to be lost when open parquet files written with +// repeated keys. We can revisit this decision if it ever becomes a blocker. +func (w *Writer) SetKeyValueMetadata(key, value string) { + for i, kv := range w.writer.metadata { + if kv.Key == key { + kv.Value = value + w.writer.metadata[i] = kv + return + } + } + w.writer.metadata = append(w.writer.metadata, format.KeyValue{ + Key: key, + Value: value, + }) +} + +// ColumnWriters returns writers for each column. This allows applications to +// write values directly to each column instead of having to first assemble +// values into rows to use WriteRows. +func (w *Writer) ColumnWriters() []*ColumnWriter { return w.writer.currentRowGroup.columns } + +// BeginRowGroup returns a new ConcurrentRowGroupWriter that can be written to in parallel with +// other row groups. However these need to be committed back to the writer serially using the +// Commit method on the row group. +func (w *Writer) BeginRowGroup() *ConcurrentRowGroupWriter { + return newConcurrentRowGroupWriter(w.writer, w.config) +} + +type writerFileView struct { + writer *writer + schema *Schema +} + +// File returns a FileView of the written parquet file. +// Only available after Close is called. +func (w *Writer) File() FileView { + if w.writer == nil || w.schema == nil { + return nil + } + return &writerFileView{ + w.writer, + w.schema, + } +} + +func (w *writerFileView) Metadata() *format.FileMetaData { + return &w.writer.fileMetaData +} + +func (w *writerFileView) Schema() *Schema { + return w.schema +} + +func (w *writerFileView) NumRows() int64 { + return w.writer.fileMetaData.NumRows +} + +func (w *writerFileView) Lookup(key string) (string, bool) { + return lookupKeyValueMetadata(w.writer.fileMetaData.KeyValueMetadata, key) +} + +func (w *writerFileView) Size() int64 { + return w.writer.writer.offset +} + +func (w *writerFileView) ColumnIndexes() []format.ColumnIndex { + return w.writer.currentRowGroup.columnIndex +} + +func (w *writerFileView) OffsetIndexes() []format.OffsetIndex { + return w.writer.currentRowGroup.offsetIndex +} + +func (w *writerFileView) Root() *Column { + root, _ := openColumns(nil, &w.writer.fileMetaData, w.writer.currentRowGroup.columnIndex, w.writer.currentRowGroup.offsetIndex) + return root +} + +func (w *writerFileView) RowGroups() []RowGroup { + columns := makeLeafColumns(w.Root()) + file := &File{metadata: w.writer.fileMetaData, schema: w.schema} + fileRowGroups, err := makeFileRowGroups(file, columns) + if err != nil { + return nil + } + return makeRowGroups(fileRowGroups) +} + +func newConcurrentRowGroupWriter(w *writer, config *WriterConfig) *ConcurrentRowGroupWriter { + rg := &ConcurrentRowGroupWriter{ + writer: w, + config: config, + maxRows: config.MaxRowsPerRowGroup, + } + + dataPageType := format.DataPage + if config.DataPageVersion == 2 { + dataPageType = format.DataPageV2 + } + + defaultCompression := config.Compression + if defaultCompression == nil { + defaultCompression = &Uncompressed + } + + forEachLeafColumnOf(config.Schema, func(leaf leafColumn) { + encoding := encodingOf(leaf.node, config.Encodings) + dictionary := Dictionary(nil) + columnType := leaf.node.Type() + columnIndex := int(leaf.columnIndex) + compression := leaf.node.Compression() + + if compression == nil { + compression = defaultCompression + } + + if isDictionaryEncoding(encoding) { + dictBuffer := columnType.NewValues(make([]byte, 0, defaultDictBufferSize), nil) + dictionary = columnType.NewDictionary(columnIndex, 0, dictBuffer) + columnType = dictionary.Type() + } + + c := &ColumnWriter{ + pool: config.ColumnPageBuffers, + columnPath: leaf.path, + columnType: columnType, + originalType: columnType, + columnIndex: columnType.NewColumnIndexer(config.ColumnIndexSizeLimit(leaf.path)), + columnFilter: searchBloomFilterColumn(config.BloomFilters, leaf.path), + compression: compression, + bloomFilterCompression: config.BloomFilterCompression, + dictionary: dictionary, + maxRepetitionLevel: leaf.maxRepetitionLevel, + maxDefinitionLevel: leaf.maxDefinitionLevel, + bufferIndex: int32(leaf.columnIndex), + bufferSize: int32(float64(config.PageBufferSize) * 0.98), + writePageStats: config.DataPageStatistics && !slices.ContainsFunc(config.SkipPageStatistics, func(skip []string) bool { + return columnPath(skip).equal(leaf.path) + }), + writePageBounds: !slices.ContainsFunc(config.SkipPageBounds, func(skip []string) bool { + return columnPath(skip).equal(leaf.path) + }), + writeDeprecatedStatistics: config.DeprecatedDataPageStatistics, + encodings: make([]format.Encoding, 0, 3), + isCompressed: isCompressed(compression), + dictionaryMaxBytes: config.DictionaryMaxBytes, + } + + if lt := leaf.node.Type().LogicalType(); lt != nil && (lt.Geometry != nil || lt.Geography != nil) { + c.geospatialAccumulator = newGeospatialBBoxAccumulator() + } + + if dictionary != nil { + c.header.dict.Type = format.DictionaryPage + c.header.dict.DictionaryPageHeader.Valid = true + } + + c.header.page.Type = dataPageType + switch dataPageType { + case format.DataPage: + c.header.page.DataPageHeader.Valid = true + case format.DataPageV2: + c.header.page.DataPageHeaderV2.Valid = true + } + + c.header.encoder.Reset(c.header.protocol.NewWriter(&c.header.buffer)) + + if c.maxRepetitionLevel > 0 { + c.repetitionLevelHistogram = make([]int64, int(c.maxRepetitionLevel)+1) + } + if c.maxDefinitionLevel > 0 { + c.definitionLevelHistogram = make([]int64, int(c.maxDefinitionLevel)+1) + c.encodings = addEncoding(c.encodings, format.RLE) + } + + if isDictionaryEncoding(encoding) { + c.encodings = addEncoding(c.encodings, format.Plain) + } + + c.encoding = encoding + c.originalEncoding = encoding + c.encodings = addEncoding(c.encodings, c.encoding.Encoding()) + sortPageEncodings(c.encodings) + + rg.columns = append(rg.columns, c) + }) + + // Pre-allocate the backing array so that in most cases where the rows + // contain a single value we will hit collocated memory areas when writing + // rows to the writer. This won't benefit repeated columns much but in that + // case we would just waste a bit of memory which we can afford. + values := make([]Value, len(rg.columns)) + rg.values = make([][]Value, len(rg.columns)) + for i := range values { + rg.values[i] = values[i : i : i+1] + } + + rg.columnChunk = make([]format.ColumnChunk, len(rg.columns)) + rg.columnIndex = make([]format.ColumnIndex, len(rg.columns)) + rg.offsetIndex = make([]format.OffsetIndex, len(rg.columns)) + + for i, c := range rg.columns { + rg.columnChunk[i] = format.ColumnChunk{ + MetaData: format.ColumnMetaData{ + Type: format.Type(c.columnType.Kind()), + Encoding: c.encodings, + PathInSchema: thrift.Slice[string](c.columnPath), + Codec: c.compression.CompressionCodec(), + KeyValueMetadata: nil, // TODO + }, + } + } + + for i, c := range rg.columns { + c.columnChunk = &rg.columnChunk[i] + c.offsetIndex = &rg.offsetIndex[i] + } + + return rg +} + +func (rg *ConcurrentRowGroupWriter) reset() { + rg.numRows = 0 + for _, c := range rg.columns { + c.reset() + } +} + +func (rg *ConcurrentRowGroupWriter) configureBloomFilters(columnChunks []ColumnChunk) { + for i, c := range rg.columns { + if c.columnFilter != nil { + c.resizeBloomFilter(columnChunks[i].NumValues()) + } + } +} + +// Schema returns the schema for this row group. +func (rg *ConcurrentRowGroupWriter) Schema() *Schema { + return rg.config.Schema +} + +// ColumnWriters returns the column writers for this row group, allowing +// direct access to write values to individual columns. +func (rg *ConcurrentRowGroupWriter) ColumnWriters() []*ColumnWriter { + return rg.columns +} + +// Flush flushes any buffered data in the row group's column writers. +// This could be called before Commit to ensure all data pages are flushed. +func (rg *ConcurrentRowGroupWriter) Flush() error { + for _, c := range rg.columns { + if err := c.Flush(); err != nil { + return err + } + } + return nil +} + +// Size returns an estimate of the current row group size in bytes. +// +// The estimate sums encoded pages, buffered values not yet encoded, dictionary +// pages, and bloom filters for each column. Because buffered values have not +// been compressed, this is an upper-bound estimate. +func (rg *ConcurrentRowGroupWriter) Size() int64 { + size := int64(0) + for _, c := range rg.columns { + size += c.columnChunk.MetaData.TotalCompressedSize + if c.columnBuffer != nil { + size += c.columnBuffer.Size() + } + if c.dictionary != nil { + size += c.dictionary.Size() + } + size += int64(len(c.filter)) + } + return size +} + +// Commit commits the row group to the parent writer, returning the number +// of rows written and an error if any. This method must be called serially +// (not concurrently) to maintain row group order in the file. +// +// If the parent writer has any pending rows buffered, they will be flushed +// before this row group is written. +// +// After Commit returns successfully, the row group will be empty and can +// be reused. +func (rg *ConcurrentRowGroupWriter) Commit() (int64, error) { + if err := rg.writer.flush(); err != nil { + return 0, err + } + return rg.writer.writeRowGroup(rg, nil, nil) +} + +// WriteRows writes rows to the row group. +func (rg *ConcurrentRowGroupWriter) WriteRows(rows []Row) (int, error) { + return rg.writeRows(len(rows), func(start, end int) (int, error) { + defer func() { + for i, values := range rg.values { + clearValues(values) + rg.values[i] = values[:0] + } + }() + + // TODO: if an error occurs in this method the writer may be left in an + // partially functional state. Applications are not expected to continue + // using the writer after getting an error, but maybe we could ensure that + // we are preventing further use as well? + for _, row := range rows[start:end] { + for columnIndex, columnValues := range row.Range { + rg.values[columnIndex] = append(rg.values[columnIndex], columnValues...) + } + } + + for i, values := range rg.values { + if len(values) > 0 { + if _, err := rg.columns[i].WriteRowValues(values); err != nil { + return 0, err + } + } + } + + return end - start, nil + }) +} + +func (rg *ConcurrentRowGroupWriter) writeRows(numRows int, write func(i, j int) (int, error)) (int, error) { + written := 0 + + for written < numRows { + remain := rg.maxRows - rg.numRows + length := numRows - written + + if remain <= 0 { + return written, ErrTooManyRowGroups + } + + if remain < int64(length) { + length = int(remain) + } + + // Since the writer cannot flush pages across row boundaries, calls to + // WriteRows with very large slices can result in greatly exceeding the + // target page size. To set a limit to the impact of these large writes + // we chunk the input in slices of 64 rows. + const maxRowsPerWrite = 64 + if length > maxRowsPerWrite { + length = maxRowsPerWrite + } + + n, err := write(written, written+length) + written += n + rg.numRows += int64(n) + if err != nil { + return written, err + } + } + + return written, nil +} + +type writer struct { + buffer *bufio.Writer + writer offsetTrackingWriter + currentRowGroup *ConcurrentRowGroupWriter + + createdBy string + metadata []format.KeyValue + + columnOrders []format.ColumnOrder + schemaElements []format.SchemaElement + rowGroups []format.RowGroup + columnIndexes [][]format.ColumnIndex + offsetIndexes [][]format.OffsetIndex + sortingColumns []format.SortingColumn + + deferredBloomFilters []deferredBloomFilter + deferredBloomFilterSize int64 + + fileMetaData format.FileMetaData + footer [8]byte + + encryption *fileEncryptionState // nil when encryption is disabled +} + +type deferredBloomFilter struct { + rowGroup int + column int + buf io.ReadWriteSeeker + reset func() +} + +func newWriter(output io.Writer, config *WriterConfig) *writer { + w := new(writer) + if config.WriteBufferSize <= 0 { + w.writer.Reset(output) + } else { + w.buffer = bufio.NewWriterSize(output, config.WriteBufferSize) + w.writer.Reset(w.buffer) + } + w.createdBy = config.CreatedBy + w.metadata = make([]format.KeyValue, 0, len(config.KeyValueMetadata)) + for k, v := range config.KeyValueMetadata { + w.metadata = append(w.metadata, format.KeyValue{Key: k, Value: v}) + } + sortKeyValueMetadata(w.metadata) + w.sortingColumns = make([]format.SortingColumn, len(config.Sorting.SortingColumns)) + + config.Schema.forEachNode(func(name string, node Node) { + nodeType := node.Type() + + elem := format.SchemaElement{ + Name: name, + FieldID: int32(node.ID()), + } + + if node != config.Schema { // the root has no repetition type + elem.RepetitionType = fieldRepetitionTypeNullOf(node) + } + + // For backward compatibility with older readers, the parquet specification + // recommends to set the scale and precision on schema elements when the + // column is of logical type decimal. + logicalType := nodeType.LogicalType() + if logicalType != nil { + elem.LogicalType.Set(*logicalType) + if logicalType.Decimal != nil { + elem.Scale.Set(logicalType.Decimal.Scale) + elem.Precision.Set(logicalType.Decimal.Precision) + } + } + + if n := int32(nodeType.Length()); n > 0 { + elem.TypeLength.Set(n) + } + + if !node.Leaf() { + n := int32(len(node.Fields())) + elem.NumChildren.Set(n) + } + + if physType := nodeType.PhysicalType(); physType != nil { + elem.Type.Set(*physType) + } + + if convType := nodeType.ConvertedType(); convType != nil { + elem.ConvertedType.Set(*convType) + } + + w.schemaElements = append(w.schemaElements, elem) + }) + + w.currentRowGroup = newConcurrentRowGroupWriter(w, config) + + if len(config.Sorting.SortingColumns) > 0 { + forEachLeafColumnOf(config.Schema, func(leaf leafColumn) { + if sortingIndex := searchSortingColumn(config.Sorting.SortingColumns, leaf.path); sortingIndex < len(w.sortingColumns) { + w.sortingColumns[sortingIndex] = format.SortingColumn{ + ColumnIdx: int32(leaf.columnIndex), + Descending: config.Sorting.SortingColumns[sortingIndex].Descending(), + NullsFirst: config.Sorting.SortingColumns[sortingIndex].NullsFirst(), + } + } + }) + } + + w.columnOrders = make([]format.ColumnOrder, len(w.currentRowGroup.columns)) + for i, c := range w.currentRowGroup.columns { + w.columnOrders[i] = *c.columnType.ColumnOrder() + } + + if config.Encryption != nil { + enc, err := newFileEncryptionState(config.Encryption) + if err != nil { + // newWriter has no error return; panic here mirrors any other init failure in this function. + panic("parquet: " + err.Error()) + } + w.encryption = enc + // Configure each column with its encryption key and state. + // Row group ordinal starts at 0; it is updated after each row group is committed. + for i, c := range w.currentRowGroup.columns { + path := columnPathString(c.columnPath) + c.encKey = enc.columnKeyFor(path) + c.columnOrdinal = int16(i) + c.rowGroupOrdinal = 0 + c.fileUnique = enc.fileUnique + c.aadPrefix = enc.cfg.AadPrefix + } + if config.Encryption.EncryptedFooter { + copy(w.footer[4:], "PARE") + } else { + copy(w.footer[4:], "PAR1") + } + } else { + copy(w.footer[4:], "PAR1") + } + return w +} + +func (w *writer) reset(writer io.Writer) { + for _, bf := range w.deferredBloomFilters { + bf.reset() + } + w.deferredBloomFilters = w.deferredBloomFilters[:0] + w.deferredBloomFilterSize = 0 + if w.buffer == nil { + w.writer.Reset(writer) + } else { + w.buffer.Reset(writer) + w.writer.Reset(w.buffer) + } + w.currentRowGroup.reset() + for i := range w.rowGroups { + w.rowGroups[i].Reset() + } + for i := range w.columnIndexes { + for j := range w.columnIndexes[i] { + w.columnIndexes[i][j].Reset() + } + w.columnIndexes[i] = w.columnIndexes[i][:0] + } + for i := range w.offsetIndexes { + for j := range w.offsetIndexes[i] { + w.offsetIndexes[i][j].Reset() + } + w.offsetIndexes[i] = w.offsetIndexes[i][:0] + } + w.rowGroups = w.rowGroups[:0] + w.columnIndexes = w.columnIndexes[:0] + w.offsetIndexes = w.offsetIndexes[:0] + w.fileMetaData = format.FileMetaData{} +} + +func (w *writer) close() error { + if err := w.writeFileHeader(); err != nil { + return err + } + if err := w.flush(); err != nil { + return err + } + if err := w.writeDeferredBloomFilters(); err != nil { + return err + } + if err := w.writeFileFooter(); err != nil { + return err + } + if w.buffer != nil { + return w.buffer.Flush() + } + return nil +} + +func (w *writer) flush() error { + _, err := w.writeRowGroup(w.currentRowGroup, nil, nil) + return err +} + +func (w *writer) writeFileHeader() error { + if w.writer.writer == nil { + return io.ErrClosedPipe + } + if w.writer.offset == 0 { + magic := "PAR1" + if w.encryption != nil && w.encryption.cfg.EncryptedFooter { + magic = "PARE" + } + _, err := w.writer.WriteString(magic) + return err + } + return nil +} + +func (w *writer) writeDeferredBloomFilters() error { + defer func() { + // Reset here to ensure a re-used closed writer doesn't double return buffers. + for _, bf := range w.deferredBloomFilters { + bf.reset() + } + w.deferredBloomFilters = w.deferredBloomFilters[:0] + w.deferredBloomFilterSize = 0 + }() + for _, bf := range w.deferredBloomFilters { + c := &w.rowGroups[bf.rowGroup].Columns[bf.column] + bloomFilterOffset := w.writer.offset + c.MetaData.BloomFilterOffset = bloomFilterOffset + if _, err := w.writer.ReadFrom(bf.buf); err != nil { + return err + } + bloomFilterLength := w.writer.offset - bloomFilterOffset + c.MetaData.BloomFilterLength = int32(bloomFilterLength) + } + return nil +} + +func (w *writer) writeFileFooter() error { + // The page index is composed of two sections: column and offset indexes. + // They are written after the row groups, right before the footer (which + // is written by the parent Writer.Close call). + // + // This section both writes the page index and generates the values of + // ColumnIndexOffset, ColumnIndexLength, OffsetIndexOffset, and + // OffsetIndexLength in the corresponding columns of the file metadata. + // + // Note: the page index is always written, even if we created data pages v1 + // because the parquet format is backward compatible in this case. Older + // readers will simply ignore this section since they do not know how to + // decode its content, nor have loaded any metadata to reference it. + protocol := new(thrift.CompactProtocol) + encoder := thrift.NewEncoder(protocol.NewWriter(&w.writer)) + + // pageIndexKey returns the AES key used to encrypt this column's page-index + // modules (column index + offset index), or nil if the column is not + // encrypted. When encryption is active, the Parquet spec requires the + // per-column index to be encrypted with the column's key (or the footer + // key when EncryptionWithFooterKey is selected) so that statistics like + // min/max do not leak in the otherwise plaintext page index. + pageIndexKey := func(c *format.ColumnChunk) []byte { + if w.encryption == nil { + return nil + } + switch { + case c.CryptoMetadata.EncryptionWithFooterKey != nil: + return w.encryption.cfg.FooterKey + case c.CryptoMetadata.EncryptionWithColumnKey != nil: + return w.encryption.columnKeyFor(columnPathString(c.CryptoMetadata.EncryptionWithColumnKey.PathInSchema)) + default: + return nil + } + } + + for i, columnIndexes := range w.columnIndexes { + rowGroup := &w.rowGroups[i] + for j := range columnIndexes { + column := &rowGroup.Columns[j] + column.ColumnIndexOffset = w.writer.offset + if key := pageIndexKey(column); key != nil { + var idxBuf bytes.Buffer + idxEnc := thrift.NewEncoder(protocol.NewWriter(&idxBuf)) + if err := idxEnc.Encode(&columnIndexes[j]); err != nil { + return err + } + aad := makeAAD(w.encryption.cfg.AadPrefix, w.encryption.fileUnique, columnIndexModule, int16(i), int16(j)) + envelope, err := encryptModule(key, aad, idxBuf.Bytes()) + if err != nil { + return fmt.Errorf("encrypting column index: rowGroup=%d col=%d: %w", i, j, err) + } + if _, err := w.writer.Write(envelope); err != nil { + return err + } + } else if err := encoder.Encode(&columnIndexes[j]); err != nil { + return err + } + column.ColumnIndexLength = int32(w.writer.offset - column.ColumnIndexOffset) + } + } + + for i, offsetIndexes := range w.offsetIndexes { + rowGroup := &w.rowGroups[i] + for j := range offsetIndexes { + column := &rowGroup.Columns[j] + column.OffsetIndexOffset = w.writer.offset + if key := pageIndexKey(column); key != nil { + var idxBuf bytes.Buffer + idxEnc := thrift.NewEncoder(protocol.NewWriter(&idxBuf)) + if err := idxEnc.Encode(&offsetIndexes[j]); err != nil { + return err + } + aad := makeAAD(w.encryption.cfg.AadPrefix, w.encryption.fileUnique, offsetIndexModule, int16(i), int16(j)) + envelope, err := encryptModule(key, aad, idxBuf.Bytes()) + if err != nil { + return fmt.Errorf("encrypting offset index: rowGroup=%d col=%d: %w", i, j, err) + } + if _, err := w.writer.Write(envelope); err != nil { + return err + } + } else if err := encoder.Encode(&offsetIndexes[j]); err != nil { + return err + } + column.OffsetIndexLength = int32(w.writer.offset - column.OffsetIndexOffset) + } + } + + numRows := int64(0) + for rowGroupIndex := range w.rowGroups { + numRows += w.rowGroups[rowGroupIndex].NumRows + } + + // We implemented the parquet specification version 2+, which is represented + // by the version number 2 in the file metadata. + // + // For reference, see: + // https://github.com/apache/arrow/blob/70b9ef5/go/parquet/metadata/file.go#L122-L127 + const parquetFileFormatVersion = 2 + + w.fileMetaData = format.FileMetaData{ + Version: parquetFileFormatVersion, + Schema: w.schemaElements, + NumRows: numRows, + RowGroups: w.rowGroups, + KeyValueMetadata: w.metadata, + CreatedBy: w.createdBy, + ColumnOrders: w.columnOrders, + } + + if w.encryption != nil { + enc := w.encryption + algo := format.EncryptionAlgorithm{ + AesGcmV1: &format.AesGcmV1{ + AadFileUnique: enc.fileUnique, + AadPrefix: enc.cfg.AadPrefix, + }, + } + + if enc.cfg.EncryptedFooter { + // Encrypted footer mode: write FileCryptoMetaData + encrypted footer + size + "PARE". + // Thrift-encode the FileMetaData into a buffer, then encrypt it. + var footerBuf bytes.Buffer + footerEncoder := thrift.NewEncoder(protocol.NewWriter(&footerBuf)) + if err := footerEncoder.Encode(&w.fileMetaData); err != nil { + return err + } + footerAAD := makeAAD(enc.cfg.AadPrefix, enc.fileUnique, footerModule) + encFooter, err := encryptModule(enc.cfg.FooterKey, footerAAD, footerBuf.Bytes()) + if err != nil { + return fmt.Errorf("encrypting footer: %w", err) + } + + // Capture offset before writing FileCryptoMetaData so we can compute total size. + startOffset := w.writer.offset + + // Write FileCryptoMetaData (plaintext, thrift). + cryptoMeta := format.FileCryptoMetaData{EncryptionAlgorithm: algo} + if err := encoder.Encode(&cryptoMeta); err != nil { + return err + } + // Write encrypted footer envelope. + if _, err := w.writer.Write(encFooter); err != nil { + return err + } + // Footer size = FileCryptoMetaData bytes + encrypted footer bytes. + footerLen := w.writer.offset - startOffset + binary.LittleEndian.PutUint32(w.footer[:4], uint32(footerLen)) + _, err = w.writer.Write(w.footer[:]) + return err + } + + // Plaintext footer mode: write FileMetaData with EncryptionAlgorithm set, + // then append a 28-byte signature (nonce || GCM tag). + w.fileMetaData.EncryptionAlgorithm = algo + + var footerBuf bytes.Buffer + footerEncoder := thrift.NewEncoder(protocol.NewWriter(&footerBuf)) + if err := footerEncoder.Encode(&w.fileMetaData); err != nil { + return err + } + footerBytes := footerBuf.Bytes() + + // Write the plaintext footer to the file. + if _, err := w.writer.Write(footerBytes); err != nil { + return err + } + // Compute and append the 28-byte signature. + footerAAD := makeAAD(enc.cfg.AadPrefix, enc.fileUnique, footerModule) + sig, err := signFooter(enc.cfg.FooterKey, footerAAD, footerBytes) + if err != nil { + return fmt.Errorf("signing footer: %w", err) + } + if _, err := w.writer.Write(sig); err != nil { + return err + } + // Footer length = len(footerBytes) + len(sig). + binary.LittleEndian.PutUint32(w.footer[:4], uint32(len(footerBytes)+len(sig))) + _, err = w.writer.Write(w.footer[:]) + return err + } + + length := w.writer.offset + if err := encoder.Encode(&w.fileMetaData); err != nil { + return err + } + + length = w.writer.offset - length + binary.LittleEndian.PutUint32(w.footer[:4], uint32(length)) + + _, err := w.writer.Write(w.footer[:]) + return err +} + +func (w *writer) writeRowGroup(rg *ConcurrentRowGroupWriter, rowGroupSchema *Schema, rowGroupSortingColumns []SortingColumn) (int64, error) { + if len(rg.columns) == 0 { + return 0, nil + } + numRows := rg.columns[0].totalRowCount() + if numRows == 0 { + return 0, nil + } + + rowGroupIndex := len(w.rowGroups) + columnIndexIndex := len(w.columnIndexes) + offsetIndexIndex := len(w.offsetIndexes) + + if len(w.rowGroups) == MaxRowGroups { + return 0, ErrTooManyRowGroups + } + + defer func() { + rg.reset() + // After reset, update the row group ordinal for the next row group. + if w.encryption != nil { + nextOrdinal := int16(len(w.rowGroups)) + for _, c := range rg.columns { + c.rowGroupOrdinal = nextOrdinal + } + } + }() + + // Ensure all columns use the correct row group ordinal before flushing final pages. + if w.encryption != nil { + for _, c := range rg.columns { + c.rowGroupOrdinal = int16(rowGroupIndex) + } + } + + for _, c := range rg.columns { + if err := c.Flush(); err != nil { + return 0, err + } + if err := c.flushFilterPages(); err != nil { + return 0, err + } + } + + if err := w.writeFileHeader(); err != nil { + return 0, err + } + fileOffset := w.writer.offset + + for i, c := range rg.columns { + rg.columnIndex[i] = c.columnIndex.ColumnIndex() + rg.columnIndex[i].RepetitionLevelHistogram = append(rg.columnIndex[i].RepetitionLevelHistogram[:0], c.pageRepetitionLevelHistograms...) + rg.columnIndex[i].DefinitionLevelHistogram = append(rg.columnIndex[i].DefinitionLevelHistogram[:0], c.pageDefinitionLevelHistograms...) + + c.columnChunk.MetaData.SizeStatistics = format.SizeStatistics{ + UnencodedByteArrayDataBytes: c.totalUnencodedByteArrayBytes, + RepetitionLevelHistogram: slices.Clone(c.repetitionLevelHistogram), + DefinitionLevelHistogram: slices.Clone(c.definitionLevelHistogram), + } + + if c.dictionary != nil { + c.columnChunk.MetaData.DictionaryPageOffset = w.writer.offset + if err := c.writeDictionaryPage(&w.writer, c.dictionary); err != nil { + return 0, fmt.Errorf("writing dictionary page of row group colum %d: %w", i, err) + } + } + + // Skip columns with nil pageBuffer (e.g., empty struct groups with no leaf columns) + if c.pageBuffer == nil { + continue + } + + dataPageOffset := w.writer.offset + c.columnChunk.MetaData.DataPageOffset = dataPageOffset + for j := range c.offsetIndex.PageLocations { + c.offsetIndex.PageLocations[j].Offset += dataPageOffset + } + + if offset, err := c.pageBuffer.Seek(0, io.SeekStart); err != nil { + return 0, err + } else if offset != 0 { + return 0, fmt.Errorf("resetting parquet page buffer to the start expected offset zero but got %d", offset) + } + if _, err := io.Copy(&w.writer, c.pageBuffer); err != nil { + return 0, fmt.Errorf("writing buffered pages of row group column %d: %w", i, err) + } + } + + for i, c := range rg.columns { + if len(c.filter) == 0 { + continue + } + + if rg.config.DeferredBloomFiltersBuffers != nil { + buf := rg.config.DeferredBloomFiltersBuffers.GetBuffer() + reset := func() { rg.config.DeferredBloomFiltersBuffers.PutBuffer(buf) } + + if err := c.writeBloomFilter(buf); err != nil { + reset() + return 0, err + } + size, err := buf.Seek(0, io.SeekCurrent) + if err != nil { + reset() + return 0, err + } + if _, err := buf.Seek(0, io.SeekStart); err != nil { + reset() + return 0, err + } + w.deferredBloomFilterSize += size + w.deferredBloomFilters = append(w.deferredBloomFilters, deferredBloomFilter{ + rowGroup: rowGroupIndex, + column: i, + buf: buf, + reset: reset, + }) + continue + } + + bloomFilterOffset := w.writer.offset + c.columnChunk.MetaData.BloomFilterOffset = bloomFilterOffset + if err := c.writeBloomFilter(&w.writer); err != nil { + return 0, err + } + bloomFilterLength := w.writer.offset - bloomFilterOffset + c.columnChunk.MetaData.BloomFilterLength = int32(bloomFilterLength) + } + + // Accumulate row-group byte sizes before any MetaData zeroing below. + totalByteSize := int64(0) + totalCompressedSize := int64(0) + + for i := range rg.columnChunk { + c := &rg.columnChunk[i].MetaData + sortPageEncodingStats(c.EncodingStats) + totalByteSize += int64(c.TotalUncompressedSize) + totalCompressedSize += int64(c.TotalCompressedSize) + } + + // Set ColumnCryptoMetaData on each column chunk when encryption is active. + if w.encryption != nil { + for i, c := range rg.columns { + path := columnPathString(c.columnPath) + _, hasColumnKey := w.encryption.cfg.ColumnKeys[path] + if !hasColumnKey { + c.columnChunk.CryptoMetadata = format.ColumnCryptoMetaData{ + EncryptionWithFooterKey: &format.EncryptionWithFooterKey{}, + } + } else { + c.columnChunk.CryptoMetadata = format.ColumnCryptoMetaData{ + EncryptionWithColumnKey: &format.EncryptionWithColumnKey{ + PathInSchema: c.columnPath, + }, + } + } + + // In plaintext-footer mode, encrypt the column metadata and store it + // inline in EncryptedColumnMetadata, removing the plaintext MetaData so + // that sensitive statistics/offsets are not exposed in the footer. + if !w.encryption.cfg.EncryptedFooter { + enc := w.encryption + var metaBuf bytes.Buffer + metaProto := new(thrift.CompactProtocol) + metaEnc := thrift.NewEncoder(metaProto.NewWriter(&metaBuf)) + if err := metaEnc.Encode(&c.columnChunk.MetaData); err != nil { + return 0, fmt.Errorf("encoding column metadata for encryption: %w", err) + } + key := enc.columnKeyFor(path) + aad := makeAAD(enc.cfg.AadPrefix, enc.fileUnique, columnMetaDataModule, int16(rowGroupIndex), int16(i)) + encMeta, err := encryptModule(key, aad, metaBuf.Bytes()) + if err != nil { + return 0, fmt.Errorf("encrypting column metadata: %w", err) + } + c.columnChunk.EncryptedColumnMetadata = encMeta + c.columnChunk.MetaData = format.ColumnMetaData{} + } + } + } + + var reuseRowGroup *format.RowGroup = nil + if cap(w.rowGroups) > rowGroupIndex { + w.rowGroups = w.rowGroups[:rowGroupIndex+1] // Extend the slice by one element + reuseRowGroup = &w.rowGroups[rowGroupIndex] // Pointer to the last element + } + + sortingColumns := w.sortingColumns + if len(sortingColumns) == 0 && len(rowGroupSortingColumns) > 0 { + scLen := len(rowGroupSortingColumns) + if reuseRowGroup == nil { + sortingColumns = make([]format.SortingColumn, 0, scLen) + } else { + sortingColumns = reuseRowGroup.SortingColumns + } + forEachLeafColumnOf(rowGroupSchema, func(leaf leafColumn) { + if sortingIndex := searchSortingColumn(rowGroupSortingColumns, leaf.path); sortingIndex < len(sortingColumns) { + sortingColumns[sortingIndex] = format.SortingColumn{ + ColumnIdx: int32(leaf.columnIndex), + Descending: rowGroupSortingColumns[sortingIndex].Descending(), + NullsFirst: rowGroupSortingColumns[sortingIndex].NullsFirst(), + } + } + }) + } + + var reuseColumnIndex *[]format.ColumnIndex = nil + if cap(w.columnIndexes) > columnIndexIndex { + // Extend the slice by one element + w.columnIndexes = w.columnIndexes[:columnIndexIndex+1] + reuseColumnIndex = &w.columnIndexes[columnIndexIndex] + } + var columnIndex []format.ColumnIndex + if reuseColumnIndex != nil { + // Copy the slice + columnIndex = append(*reuseColumnIndex, rg.columnIndex...) + w.columnIndexes[columnIndexIndex] = columnIndex + } else { + columnIndex = slices.Clone(rg.columnIndex) + w.columnIndexes = append(w.columnIndexes, columnIndex) + } + + var reuseOffsetIndex *[]format.OffsetIndex = nil + if cap(w.offsetIndexes) > offsetIndexIndex { + // Extend the slice by one element + w.offsetIndexes = w.offsetIndexes[:offsetIndexIndex+1] + reuseOffsetIndex = &w.offsetIndexes[offsetIndexIndex] + } + var offsetIndex []format.OffsetIndex + if reuseOffsetIndex != nil { + offsetIndex = append(*reuseOffsetIndex, rg.offsetIndex...) + w.offsetIndexes[offsetIndexIndex] = offsetIndex + } else { + offsetIndex = slices.Clone(rg.offsetIndex) + w.offsetIndexes = append(w.offsetIndexes, offsetIndex) + } + + // If the existing row group slice is full, we need to create a new element + var columns []format.ColumnChunk = nil + if reuseRowGroup == nil { + columns = slices.Clone(rg.columnChunk) + } else { + columns = append(reuseRowGroup.Columns, rg.columnChunk...) + } + for i := range columns { + c := &columns[i] + c.MetaData.EncodingStats = append(c.MetaData.EncodingStats[:0], rg.columnChunk[i].MetaData.EncodingStats...) + } + + for i := range offsetIndex { + c := &offsetIndex[i] + c.PageLocations = slices.Clone(rg.offsetIndex[i].PageLocations) + } + + if reuseRowGroup == nil { + w.rowGroups = append(w.rowGroups, format.RowGroup{ + Columns: columns, + TotalByteSize: totalByteSize, + NumRows: numRows, + SortingColumns: sortingColumns, + FileOffset: fileOffset, + TotalCompressedSize: totalCompressedSize, + Ordinal: int16(len(w.rowGroups)), + }) + } else { + reuseRowGroup.Columns = columns + reuseRowGroup.TotalByteSize = totalByteSize + reuseRowGroup.NumRows = numRows + reuseRowGroup.SortingColumns = sortingColumns + reuseRowGroup.FileOffset = fileOffset + reuseRowGroup.TotalCompressedSize = totalCompressedSize + reuseRowGroup.Ordinal = int16(len(w.rowGroups) - 1) + } + + return numRows, nil +} + +func (w *writer) WriteRows(rows []Row) (written int, err error) { + var n int + for len(rows) > 0 { + n, err = w.currentRowGroup.WriteRows(rows) + rows = rows[n:] + written += n + if err != ErrTooManyRowGroups { + break + } + if err = w.flush(); err != nil { + break + } + } + return +} + +// The WriteValues method is intended to work in pair with WritePage to allow +// programs to target writing values to specific columns of of the writer. +func (w *writer) WriteValues(values []Value) (numValues int, err error) { + return w.currentRowGroup.columns[values[0].Column()].writeValues(values) +} + +var ( + dataPageBuffers memory.Pool[writerBuffers] + dictionaryPageBuffers memory.Pool[writerBuffers] +) + +// One writerBuffers is used by each writer instance, the memory buffers here +// are shared by all columns of the writer because serialization is not done +// concurrently, which helps keep memory utilization low, both in the total +// footprint and GC cost. +// +// The type also exposes helper methods to facilitate the generation of parquet +// pages. A scratch space is used when serialization requires combining multiple +// buffers or compressing the page data, with double-buffering technique being +// employed by swapping the scratch and page buffers to minimize memory copies. +type writerBuffers struct { + repetitions []byte // buffer used to encode repetition levels + definitions []byte // buffer used to encode definition levels + page []byte // page buffer holding the page data + scratch []byte // scratch space used for compression +} + +func newWriterBuffers() *writerBuffers { return new(writerBuffers) } + +func (wb *writerBuffers) crc32() (checksum uint32) { + checksum = crc32.Update(checksum, crc32.IEEETable, wb.repetitions) + checksum = crc32.Update(checksum, crc32.IEEETable, wb.definitions) + checksum = crc32.Update(checksum, crc32.IEEETable, wb.page) + return checksum +} + +func (wb *writerBuffers) size() int { + return len(wb.repetitions) + len(wb.definitions) + len(wb.page) +} + +func (wb *writerBuffers) reset() { + wb.repetitions = wb.repetitions[:0] + wb.definitions = wb.definitions[:0] + wb.page = wb.page[:0] +} + +func encodeLevels(dst, src []byte, maxLevel byte) ([]byte, error) { + bitWidth := bits.Len8(maxLevel) + return levelEncodingsRLE[bitWidth-1].EncodeLevels(dst, src) +} + +func (wb *writerBuffers) encodeRepetitionLevels(page Page, maxRepetitionLevel byte) (err error) { + wb.repetitions, err = encodeLevels(wb.repetitions, page.RepetitionLevels(), maxRepetitionLevel) + return +} + +func (wb *writerBuffers) encodeDefinitionLevels(page Page, maxDefinitionLevel byte) (err error) { + wb.definitions, err = encodeLevels(wb.definitions, page.DefinitionLevels(), maxDefinitionLevel) + return +} + +func (wb *writerBuffers) prependLevelsToDataPageV1(maxRepetitionLevel, maxDefinitionLevel byte) { + hasRepetitionLevels := maxRepetitionLevel > 0 + hasDefinitionLevels := maxDefinitionLevel > 0 + + if hasRepetitionLevels || hasDefinitionLevels { + wb.scratch = wb.scratch[:0] + // In data pages v1, the repetition and definition levels are prefixed + // with the 4 bytes length of the sections. While the parquet-format + // documentation indicates that the length prefix is part of the hybrid + // RLE/Bit-Pack encoding, this is the only condition where it is used + // so we treat it as a special case rather than implementing it in the + // encoding. + // + // Reference https://github.com/apache/parquet-format/blob/master/Encodings.md#run-length-encoding--bit-packing-hybrid-rle--3 + if hasRepetitionLevels { + wb.scratch = plain.AppendInt32(wb.scratch, int32(len(wb.repetitions))) + wb.scratch = append(wb.scratch, wb.repetitions...) + wb.repetitions = wb.repetitions[:0] + } + if hasDefinitionLevels { + wb.scratch = plain.AppendInt32(wb.scratch, int32(len(wb.definitions))) + wb.scratch = append(wb.scratch, wb.definitions...) + wb.definitions = wb.definitions[:0] + } + wb.scratch = append(wb.scratch, wb.page...) + wb.swapPageAndScratchBuffers() + } +} + +func (wb *writerBuffers) encode(page Page, enc encoding.Encoding) (err error) { + pageType := page.Type() + pageData := page.Data() + wb.page, err = pageType.Encode(wb.page[:0], pageData, enc) + return err +} + +func (wb *writerBuffers) compress(codec compress.Codec) (err error) { + wb.scratch, err = codec.Encode(wb.scratch[:0], wb.page) + wb.swapPageAndScratchBuffers() + return err +} + +func (wb *writerBuffers) swapPageAndScratchBuffers() { + wb.page, wb.scratch = wb.scratch, wb.page[:0] +} + +// ColumnWriter writes values for a single column to underlying medium. +type ColumnWriter struct { + pool BufferPool + pageBuffer io.ReadWriteSeeker + numPages int + + columnPath columnPath + columnType Type + originalType Type // Original type before any encoding changes + columnIndex ColumnIndexer + columnBuffer ColumnBuffer + plainColumnBuffer ColumnBuffer // Retained plain buffer for fallback after lazy creation + originalColumnBuffer ColumnBuffer // Original buffer to restore after row group flush + columnFilter BloomFilterColumn + encoding encoding.Encoding + originalEncoding encoding.Encoding // Original encoding before any changes + compression compress.Codec + bloomFilterCompression compress.Codec + dictionary Dictionary + + maxRepetitionLevel byte + maxDefinitionLevel byte + + header struct { + buffer bytes.Buffer + protocol thrift.CompactProtocol + encoder thrift.Encoder + dict format.PageHeader + page format.PageHeader + } + + filter []byte + numRows int64 + bufferIndex int32 + bufferSize int32 + writePageStats bool + writePageBounds bool + writeDeprecatedStatistics bool + isCompressed bool + encodings []format.Encoding + + columnChunk *format.ColumnChunk + offsetIndex *format.OffsetIndex + hasSwitchedToPlain bool // Tracks if dictionary encoding was switched to PLAIN + dictionaryMaxBytes int64 // Per-column dictionary size limit + + // Encryption state; nil encKey means column is not encrypted. + encKey []byte + rowGroupOrdinal int16 + columnOrdinal int16 + fileUnique []byte + aadPrefix []byte + + totalUnencodedByteArrayBytes int64 + repetitionLevelHistogram []int64 + definitionLevelHistogram []int64 + pageRepetitionLevelHistograms []int64 + pageDefinitionLevelHistograms []int64 + + geospatialAccumulator *geospatialBBoxAccumulator +} + +func (c *ColumnWriter) reset() { + if c.hasSwitchedToPlain { + c.columnType = c.originalType + c.encoding = c.originalEncoding + c.hasSwitchedToPlain = false + } + if c.originalColumnBuffer != nil { + c.columnBuffer = c.originalColumnBuffer + } + if c.columnBuffer != nil { + c.columnBuffer.Reset() + } + if c.columnIndex != nil { + c.columnIndex.Reset() + } + if c.dictionary != nil { + c.dictionary.Reset() + } + if c.pageBuffer != nil { + c.pool.PutBuffer(c.pageBuffer) + c.pageBuffer = nil + } + c.numPages = 0 + // Bloom filters may change in size between row groups, but we retain the + // buffer to avoid reallocating large memory blocks. + c.filter = c.filter[:0] + c.numRows = 0 + // Reset the fields of column chunks that change between row groups, + // but keep the ones that remain unchanged. + c.columnChunk.MetaData.NumValues = 0 + c.columnChunk.MetaData.TotalUncompressedSize = 0 + c.columnChunk.MetaData.TotalCompressedSize = 0 + c.columnChunk.MetaData.DataPageOffset = 0 + c.columnChunk.MetaData.DictionaryPageOffset = 0 + c.columnChunk.MetaData.Statistics = format.Statistics{} + c.columnChunk.MetaData.GeospatialStatistics = format.GeospatialStatistics{} + if c.geospatialAccumulator != nil { + c.geospatialAccumulator.reset() + } + c.columnChunk.MetaData.EncodingStats = c.columnChunk.MetaData.EncodingStats[:0] + c.columnChunk.MetaData.BloomFilterOffset = 0 + c.offsetIndex.PageLocations = c.offsetIndex.PageLocations[:0] + + c.totalUnencodedByteArrayBytes = 0 + clear(c.repetitionLevelHistogram) + clear(c.definitionLevelHistogram) + c.pageRepetitionLevelHistograms = c.pageRepetitionLevelHistograms[:0] + c.pageDefinitionLevelHistograms = c.pageDefinitionLevelHistograms[:0] +} + +func (c *ColumnWriter) totalRowCount() int64 { + n := c.numRows + if c.columnBuffer != nil { + n += int64(c.columnBuffer.Len()) + } + return n +} + +// Flush writes any buffered data to the underlying [io.Writer]. +func (c *ColumnWriter) Flush() (err error) { + if c.columnBuffer == nil { + return nil + } + if c.columnBuffer.Len() > 0 { + // Check dictionary size limit BEFORE writing the page + // to decide if we should switch to PLAIN for future pages + var fallbackToPlain bool + if c.dictionary != nil && !c.hasSwitchedToPlain { + currentDictSize := c.dictionary.Size() + // Check user-configured limit + if c.dictionaryMaxBytes > 0 && currentDictSize > c.dictionaryMaxBytes { + fallbackToPlain = true + } + // For byte array dictionaries, enforce hard limit of 1GB. + // The offsets are stored as uint32 (max 4GB), but a dictionary larger + // than 1GB doesn't make practical sense and wastes memory. + if !fallbackToPlain && c.columnType.Kind() == ByteArray && currentDictSize > 1024*1024*1024 { + fallbackToPlain = true + } + } + + // Write the current buffered page (still with current encoding) + defer c.columnBuffer.Reset() + _, err = c.writeDataPage(c.columnBuffer.Page()) + if err != nil { + return err + } + + // After writing the page, convert to PLAIN for future pages if needed + // This avoids wasteful buffer allocation if this was the last page + if fallbackToPlain { + if err := c.fallbackDictionaryToPlain(); err != nil { + return fmt.Errorf("converting dictionary to plain: %w", err) + } + } + } + return err +} + +func (c *ColumnWriter) flushFilterPages() (err error) { + if c.columnFilter == nil { + return nil + } + + // If there is a dictionary, it contains all the values that we need to + // write to the filter. + if dict := c.dictionary; dict != nil { + // Need to always attempt to resize the filter, as the writer might + // be reused after resetting which would have reset the length of + // the filter to 0. + c.resizeBloomFilter(int64(dict.Len())) + return c.writePageToFilter(dict.Page()) + } + + // When the filter was already allocated, pages have been written to it as + // they were seen by the column writer. + if len(c.filter) > 0 { + return nil + } + + // Skip columns with nil pageBuffer (e.g., empty struct groups with no leaf columns) + if c.pageBuffer == nil { + return nil + } + + // When the filter was not allocated, the writer did not know how many + // values were going to be seen and therefore could not properly size the + // filter ahead of time. In this case, we read back all the pages that we + // have encoded and copy their values back to the filter. + // + // A prior implementation of the column writer used to create in-memory + // copies of the pages to avoid this decoding step; however, this unbounded + // allocation caused memory exhaustion in production applications. CPU being + // a somewhat more stretchable resource, we prefer spending time on this + // decoding step than having to trigger incident response when production + // systems are getting OOM-Killed. + c.resizeBloomFilter(c.columnChunk.MetaData.NumValues) + + column := &Column{ + // Set all the fields required by the decodeDataPage* methods. + typ: c.columnType, + encoding: c.encoding, + compression: c.compression, + maxRepetitionLevel: c.maxRepetitionLevel, + maxDefinitionLevel: c.maxDefinitionLevel, + index: uint16(c.bufferIndex), + } + + var pageReader io.Reader = c.pageBuffer + if offset, err := c.pageBuffer.Seek(0, io.SeekStart); err != nil { + return err + } else if offset != 0 { + return fmt.Errorf("resetting parquet page buffer to the start expected offset zero but got %d", offset) + } + + if _, ok := pageReader.(*os.File); ok { + rbuf, pool := getBufioReader(pageReader, 1024) + defer func() { + putBufioReader(rbuf, pool) + }() + pageReader = rbuf + } + + pbuf := (*buffer[byte])(nil) + defer func() { + if pbuf != nil { + pbuf.unref() + } + }() + + // For encrypted columns the page buffer contains AES-GCM envelopes, not + // plain thrift. Decrypt each page before decoding it. + if c.encKey != nil { + for pageOrd := range int16(c.numPages) { + hdrAAD := makeAAD(c.aadPrefix, c.fileUnique, dataPageHeaderModule, c.rowGroupOrdinal, c.columnOrdinal, pageOrd) + hdrPlain, err := readDecryptedEnvelopeFrom(pageReader, c.encKey, hdrAAD) + if err != nil { + return err + } + header := new(format.PageHeader) + pr := c.header.protocol.NewReaderFromBytes(hdrPlain) + if err := thrift.NewDecoder(pr).Decode(header); err != nil { + return fmt.Errorf("bloom filter: decoding encrypted page header: %w", err) + } + bodyAAD := makeAAD(c.aadPrefix, c.fileUnique, dataPageBodyModule, c.rowGroupOrdinal, c.columnOrdinal, pageOrd) + bodyPlain, err := readDecryptedEnvelopeFrom(pageReader, c.encKey, bodyAAD) + if err != nil { + return err + } + if pbuf != nil { + pbuf.unref() + } + pbuf = buffers.get(len(bodyPlain)) + copy(pbuf.data.Slice(), bodyPlain) + pbuf.ref() + + var page Page + switch header.Type { + case format.DataPage: + page, err = column.decodeDataPageV1(DataPageHeaderV1{&header.DataPageHeader.V}, pbuf, nil, header.UncompressedPageSize) + case format.DataPageV2: + page, err = column.decodeDataPageV2(DataPageHeaderV2{&header.DataPageHeaderV2.V}, pbuf, nil, header.UncompressedPageSize) + } + if page != nil { + err = c.writePageToFilter(page) + Release(page) + } + if err != nil { + return err + } + } + return nil + } + + decoder := thrift.NewDecoder(c.header.protocol.NewReader(pageReader)) + + for range c.numPages { + header := new(format.PageHeader) + if err := decoder.Decode(header); err != nil { + return err + } + + if pbuf != nil { + pbuf.unref() + } + pbuf = buffers.get(int(header.CompressedPageSize)) + if _, err := io.ReadFull(pageReader, pbuf.data.Slice()); err != nil { + return err + } + + var page Page + + switch header.Type { + case format.DataPage: + page, err = column.decodeDataPageV1(DataPageHeaderV1{&header.DataPageHeader.V}, pbuf, nil, header.UncompressedPageSize) + case format.DataPageV2: + page, err = column.decodeDataPageV2(DataPageHeaderV2{&header.DataPageHeaderV2.V}, pbuf, nil, header.UncompressedPageSize) + } + if page != nil { + err = c.writePageToFilter(page) + Release(page) + } + if err != nil { + return err + } + } + + return nil +} + +func (c *ColumnWriter) resizeBloomFilter(numValues int64) { + filterSize := c.columnFilter.Size(numValues) + if cap(c.filter) < filterSize { + c.filter = make([]byte, filterSize) + } else { + c.filter = c.filter[:filterSize] + for i := range c.filter { + c.filter[i] = 0 + } + } +} + +func (c *ColumnWriter) newColumnBuffer() ColumnBuffer { + columnIndex := int(c.bufferIndex) + switch { + case c.maxRepetitionLevel > 0: + column := c.columnType.NewColumnBuffer(columnIndex, 0) + return newRepeatedColumnBuffer(column, c.maxRepetitionLevel, c.maxDefinitionLevel, nullsGoLast) + case c.maxDefinitionLevel > 0: + column := c.columnType.NewColumnBuffer(columnIndex, 0) + return newOptionalColumnBuffer(column, c.maxDefinitionLevel, nullsGoLast) + default: + numValues := c.columnType.EstimateNumValues(int(c.bufferSize)) + return c.columnType.NewColumnBuffer(columnIndex, numValues) + } +} + +// WriteRowValues writes entire rows to the column. On success, this returns the +// number of rows written (not the number of values). +// +// Unlike ValueWriter, where arbitrary values may be written regardless of row +// boundaries, this method requires whole rows. This is because the written +// values may be automatically flushed to a data page, based on the writer's +// configured page buffer size, and a single row is not permitted to span two +// pages. +func (c *ColumnWriter) WriteRowValues(rows []Value) (int, error) { + var startingRows int64 + if c.columnBuffer == nil { + // Lazily create the row group column so we don't need to allocate it if + // rows are not written individually to the column. + c.columnBuffer = c.newColumnBuffer() + c.originalColumnBuffer = c.columnBuffer + } else { + startingRows = int64(c.columnBuffer.Len()) + } + if _, err := c.columnBuffer.WriteValues(rows); err != nil { + return 0, err + } + numRows := int(int64(c.columnBuffer.Len()) - startingRows) + if c.columnBuffer.Size() >= int64(c.bufferSize) { + return numRows, c.Flush() + } + return numRows, nil +} + +// Close closes the column writer and resets all dependent resources. +// It can be reused after Close is called. +func (c *ColumnWriter) Close() (err error) { + if c.columnBuffer == nil { + return nil + } + if err := c.Flush(); err != nil { + return err + } + c.columnBuffer.Reset() + return nil +} + +func (c *ColumnWriter) writeValues(values []Value) (numValues int, err error) { + if c.columnBuffer == nil { + c.columnBuffer = c.newColumnBuffer() + // Save the original dictionary-encoding buffer to restore after row group flush + if c.originalColumnBuffer == nil { + c.originalColumnBuffer = c.columnBuffer + } + } + return c.columnBuffer.WriteValues(values) +} + +func (c *ColumnWriter) writeBloomFilter(w io.Writer) error { + h := bloomFilterHeader(c.columnFilter, c.bloomFilterCompression) + + filterBytes := c.filter + if c.bloomFilterCompression != nil { + switch c.bloomFilterCompression.CompressionCodec() { + case format.Uncompressed: + // no-op + case format.Gzip: + compressed, err := c.bloomFilterCompression.Encode(nil, filterBytes) + if err != nil { + return fmt.Errorf("compressing bloom filter: %w", err) + } + filterBytes = compressed + default: + return fmt.Errorf("unsupported bloom filter compression codec: %s", c.bloomFilterCompression) + } + } + + h.NumBytes = int32(len(filterBytes)) + + if c.encKey != nil { + // Encode the bloom filter header to a temp buffer, then encrypt both modules. + var hdrBuf bytes.Buffer + e := thrift.NewEncoder(c.header.protocol.NewWriter(&hdrBuf)) + if err := e.Encode(&h); err != nil { + return err + } + hdrAAD := makeAAD(c.aadPrefix, c.fileUnique, bloomFilterHdrModule, c.rowGroupOrdinal, c.columnOrdinal) + encHdr, err := encryptModule(c.encKey, hdrAAD, hdrBuf.Bytes()) + if err != nil { + return fmt.Errorf("encrypting bloom filter header: %w", err) + } + bitsAAD := makeAAD(c.aadPrefix, c.fileUnique, bloomFilterBitsModule, c.rowGroupOrdinal, c.columnOrdinal) + encBits, err := encryptModule(c.encKey, bitsAAD, filterBytes) + if err != nil { + return fmt.Errorf("encrypting bloom filter bitset: %w", err) + } + if _, err := w.Write(encHdr); err != nil { + return err + } + _, err = w.Write(encBits) + return err + } + + e := thrift.NewEncoder(c.header.protocol.NewWriter(w)) + if err := e.Encode(&h); err != nil { + return err + } + _, err := w.Write(filterBytes) + return err +} + +func (c *ColumnWriter) writeDataPage(page Page) (int64, error) { + numValues := page.NumValues() + if numValues == 0 { + return 0, nil + } + + buf := dataPageBuffers.Get(newWriterBuffers, (*writerBuffers).reset) + defer dataPageBuffers.Put(buf) + + if c.maxRepetitionLevel > 0 { + buf.encodeRepetitionLevels(page, c.maxRepetitionLevel) + } + if c.maxDefinitionLevel > 0 { + buf.encodeDefinitionLevels(page, c.maxDefinitionLevel) + } + + if err := buf.encode(page, c.encoding); err != nil { + return 0, fmt.Errorf("encoding parquet data page: %w", err) + } + + if c.header.page.Type == format.DataPage { + buf.prependLevelsToDataPageV1(c.maxRepetitionLevel, c.maxDefinitionLevel) + } + + uncompressedPageSize := buf.size() + if uncompressedPageSize > maxUncompressedPageSize { + return 0, fmt.Errorf("page size limit exceeded: %d>%d", uncompressedPageSize, maxUncompressedPageSize) + } + if c.isCompressed { + if err := buf.compress(c.compression); err != nil { + return 0, fmt.Errorf("compressing parquet data page: %w", err) + } + } + + if page.Dictionary() == nil && len(c.filter) > 0 { + // When the writer knows the number of values in advance (e.g. when + // writing a full row group), the filter encoding is set and the page + // can be directly applied to the filter, which minimizes memory usage + // since there is no need to buffer the values in order to determine + // the size of the filter. + if err := c.writePageToFilter(page); err != nil { + return 0, err + } + } + + statistics := format.Statistics{} + if c.writePageStats { + statistics = c.makePageStatistics(page) + } + + c.header.page.UncompressedPageSize = int32(uncompressedPageSize) + c.header.page.CompressedPageSize = int32(buf.size()) + c.header.page.CRC = int32(buf.crc32()) + + numRows := page.NumRows() + numNulls := page.NumNulls() + switch { + case c.header.page.DataPageHeader.Valid: + c.header.page.DataPageHeader.V = format.DataPageHeader{ + NumValues: int32(numValues), + Encoding: c.encoding.Encoding(), + DefinitionLevelEncoding: format.RLE, + RepetitionLevelEncoding: format.RLE, + Statistics: statistics, + } + case c.header.page.DataPageHeaderV2.Valid: + c.header.page.DataPageHeaderV2.V = format.DataPageHeaderV2{ + NumValues: int32(numValues), + NumNulls: int32(numNulls), + NumRows: int32(numRows), + Encoding: c.encoding.Encoding(), + DefinitionLevelsByteLength: int32(len(buf.definitions)), + RepetitionLevelsByteLength: int32(len(buf.repetitions)), + IsCompressed: thrift.New(c.isCompressed), + Statistics: statistics, + } + } + + c.header.buffer.Reset() + c.header.buffer.Grow(1024) + + if err := c.header.encoder.Encode(&c.header.page); err != nil { + return 0, err + } + + plainHeaderLen := int32(c.header.buffer.Len()) + + if c.encKey != nil { + pageOrd := int16(c.numPages) + hdrAAD := makeAAD(c.aadPrefix, c.fileUnique, dataPageHeaderModule, c.rowGroupOrdinal, c.columnOrdinal, pageOrd) + encHdr, err := encryptModule(c.encKey, hdrAAD, c.header.buffer.Bytes()) + if err != nil { + return 0, fmt.Errorf("encrypting data page header: %w", err) + } + body := make([]byte, 0, len(buf.repetitions)+len(buf.definitions)+len(buf.page)) + body = append(body, buf.repetitions...) + body = append(body, buf.definitions...) + body = append(body, buf.page...) + bodyAAD := makeAAD(c.aadPrefix, c.fileUnique, dataPageBodyModule, c.rowGroupOrdinal, c.columnOrdinal, pageOrd) + encBody, err := encryptModule(c.encKey, bodyAAD, body) + if err != nil { + return 0, fmt.Errorf("encrypting data page body: %w", err) + } + encSize := int64(len(encHdr) + len(encBody)) + err = c.writePageTo(encSize, func(output io.Writer) (int64, error) { + n1, _ := output.Write(encHdr) + n2, werr := output.Write(encBody) + return int64(n1 + n2), werr + }) + if err != nil { + return 0, err + } + c.recordPageStats(plainHeaderLen, &c.header.page, page) + c.patchEncryptedCompressedSize(encSize, plainHeaderLen, c.header.page.CompressedPageSize) + return numValues, nil + } + + size := int64(plainHeaderLen) + + int64(len(buf.repetitions)) + + int64(len(buf.definitions)) + + int64(len(buf.page)) + + err := c.writePageTo(size, func(output io.Writer) (written int64, err error) { + for _, data := range [...][]byte{ + c.header.buffer.Bytes(), + buf.repetitions, + buf.definitions, + buf.page, + } { + wn, err := output.Write(data) + written += int64(wn) + if err != nil { + return written, err + } + } + return written, nil + }) + if err != nil { + return 0, err + } + + c.recordPageStats(plainHeaderLen, &c.header.page, page) + return numValues, nil +} + +func (c *ColumnWriter) writeDictionaryPage(output io.Writer, dict Dictionary) (err error) { + buf := dictionaryPageBuffers.Get(newWriterBuffers, (*writerBuffers).reset) + defer dictionaryPageBuffers.Put(buf) + + if err := buf.encode(dict.Page(), &Plain); err != nil { + return fmt.Errorf("writing parquet dictionary page: %w", err) + } + + uncompressedPageSize := buf.size() + if uncompressedPageSize > maxUncompressedPageSize { + return fmt.Errorf("page size limit exceeded: %d>%d", uncompressedPageSize, maxUncompressedPageSize) + } + if isCompressed(c.compression) { + if err := buf.compress(c.compression); err != nil { + return fmt.Errorf("compressing parquet dictionary page: %w", err) + } + } + + c.header.dict.UncompressedPageSize = int32(uncompressedPageSize) + c.header.dict.CompressedPageSize = int32(buf.size()) + c.header.dict.CRC = int32(buf.crc32()) + + c.header.dict.DictionaryPageHeader.V = format.DictionaryPageHeader{ + NumValues: int32(dict.Len()), + Encoding: format.Plain, + IsSorted: false, + } + + c.header.buffer.Reset() + c.header.buffer.Grow(1024) + + if err := c.header.encoder.Encode(&c.header.dict); err != nil { + return err + } + + if c.encKey != nil { + hdrAAD := makeAAD(c.aadPrefix, c.fileUnique, dictPageHeaderModule, c.rowGroupOrdinal, c.columnOrdinal, 0) + encHdr, err := encryptModule(c.encKey, hdrAAD, c.header.buffer.Bytes()) + if err != nil { + return fmt.Errorf("encrypting dictionary page header: %w", err) + } + bodyAAD := makeAAD(c.aadPrefix, c.fileUnique, dictPageBodyModule, c.rowGroupOrdinal, c.columnOrdinal, 0) + encBody, err := encryptModule(c.encKey, bodyAAD, buf.page) + if err != nil { + return fmt.Errorf("encrypting dictionary page body: %w", err) + } + if _, err := output.Write(encHdr); err != nil { + return err + } + if _, err := output.Write(encBody); err != nil { + return err + } + plainHeaderLen := int32(c.header.buffer.Len()) + c.recordPageStats(plainHeaderLen, &c.header.dict, nil) + c.patchEncryptedCompressedSize(int64(len(encHdr)+len(encBody)), plainHeaderLen, c.header.dict.CompressedPageSize) + return nil + } + + if _, err := output.Write(c.header.buffer.Bytes()); err != nil { + return err + } + if _, err := output.Write(buf.page); err != nil { + return err + } + c.recordPageStats(int32(c.header.buffer.Len()), &c.header.dict, nil) + return nil +} + +// patchEncryptedCompressedSize adjusts the last PageLocation and TotalCompressedSize after +// recordPageStats to reflect the actual encrypted size on disk instead of the plaintext size. +func (c *ColumnWriter) patchEncryptedCompressedSize(encSize int64, plainHeaderLen, plainBodyLen int32) { + plainTotal := int64(plainHeaderLen) + int64(plainBodyLen) + delta := encSize - plainTotal + c.columnChunk.MetaData.TotalCompressedSize += delta + if n := len(c.offsetIndex.PageLocations); n > 0 { + c.offsetIndex.PageLocations[n-1].CompressedPageSize = int32(encSize) + } +} + +func (c *ColumnWriter) writePageToFilter(page Page) (err error) { + pageType := page.Type() + pageData := page.Data() + c.filter, err = pageType.Encode(c.filter, pageData, c.columnFilter.Encoding()) + return err +} + +func (c *ColumnWriter) writePageTo(size int64, writeTo func(io.Writer) (int64, error)) (err error) { + if c.pageBuffer == nil { + c.pageBuffer = c.pool.GetBuffer() + defer func() { + if err != nil { + c.pool.PutBuffer(c.pageBuffer) + c.pageBuffer = nil + } + }() + if _, err = c.pageBuffer.Seek(0, io.SeekStart); err != nil { + return err + } + } + written, err := writeTo(c.pageBuffer) + if err != nil { + return err + } + if written != size { + return fmt.Errorf("writing parquet column page expected %dB but got %dB: %w", size, written, io.ErrShortWrite) + } + c.numPages++ + return nil +} + +// fallbackDictionaryToPlain switches future pages from dictionary to PLAIN encoding. +// This is called when a column's dictionary size limit is exceeded. +func (c *ColumnWriter) fallbackDictionaryToPlain() error { + // Switch to PLAIN encoding for future writes + // Get the underlying type without the indexed wrapper + if indexedType, ok := c.columnType.(*indexedType); ok { + c.columnType = indexedType.Type + } + if c.plainColumnBuffer == nil { + base := c.columnType.NewColumnBuffer(int(c.bufferIndex), int(c.bufferSize)) + switch { + case c.maxRepetitionLevel > 0: + c.plainColumnBuffer = newRepeatedColumnBuffer(base, c.maxRepetitionLevel, c.maxDefinitionLevel, nullsGoLast) + case c.maxDefinitionLevel > 0: + c.plainColumnBuffer = newOptionalColumnBuffer(base, c.maxDefinitionLevel, nullsGoLast) + default: + c.plainColumnBuffer = base + } + } + c.columnBuffer = c.plainColumnBuffer + c.encoding = &plain.Encoding{} + c.encodings = addEncoding(c.encodings, format.Plain) + // DON'T clear the dictionary reference! + // We need to keep it so: + // 1. The dictionary page can be written (required for existing dict-encoded pages) + // 2. Existing pages that were written with dictionary indexes can be read + // + // The hasSwitchedToPlain flag prevents new values from being added to the dictionary + // + // Note: We are NOT re-encoding existing pages. This means: + // - Pages written before the limit was hit will remain dictionary-encoded + // - Pages written after will be PLAIN-encoded + // - The dictionary page will still be written at row group flush + // - Mixed encodings in the same column chunk are valid per Parquet spec + c.hasSwitchedToPlain = true + return nil +} + +func (c *ColumnWriter) makePageStatistics(page Page) format.Statistics { + numNulls := page.NumNulls() + minValue, maxValue, _ := page.Bounds() + minValueBytes := minValue.Bytes() + maxValueBytes := maxValue.Bytes() + return format.Statistics{ + Min: minValueBytes, // deprecated + Max: maxValueBytes, // deprecated + NullCount: numNulls, + MinValue: minValueBytes, + MaxValue: maxValueBytes, + } +} + +func (c *ColumnWriter) recordPageStats(headerSize int32, header *format.PageHeader, page Page) { + uncompressedSize := headerSize + header.UncompressedPageSize + compressedSize := headerSize + header.CompressedPageSize + + if page != nil { + numNulls := page.NumNulls() + numValues := page.NumValues() + + var minValue, maxValue Value + var pageHasBounds bool + if c.writePageBounds { + minValue, maxValue, pageHasBounds = page.Bounds() + } + + c.columnIndex.IndexPage(numValues, numNulls, minValue, maxValue) + c.columnChunk.MetaData.NumValues += numValues + c.columnChunk.MetaData.Statistics.NullCount += numNulls + + if pageHasBounds { + var existingMaxValue, existingMinValue Value + + if c.columnChunk.MetaData.Statistics.MaxValue != nil && c.columnChunk.MetaData.Statistics.MinValue != nil { + existingMaxValue = c.columnType.Kind().Value(c.columnChunk.MetaData.Statistics.MaxValue) + existingMinValue = c.columnType.Kind().Value(c.columnChunk.MetaData.Statistics.MinValue) + } + + if existingMaxValue.isNull() || c.columnType.Compare(maxValue, existingMaxValue) > 0 { + buf := c.columnChunk.MetaData.Statistics.MaxValue[:0] + // if maxValue is empty string, c.columnChunk.MetaData.Statistics.MaxValue should be []bytes{}, but nil + if buf == nil && maxValue.Kind() == ByteArray && len(maxValue.ByteArray()) == 0 { + buf = make([]byte, 0) + } + c.columnChunk.MetaData.Statistics.MaxValue = maxValue.AppendBytes(buf) + if c.writeDeprecatedStatistics { + c.columnChunk.MetaData.Statistics.Max = c.columnChunk.MetaData.Statistics.MaxValue + } + } + + if existingMinValue.isNull() || c.columnType.Compare(minValue, existingMinValue) < 0 { + buf := c.columnChunk.MetaData.Statistics.MinValue[:0] + // same as above + if buf == nil && minValue.Kind() == ByteArray && len(minValue.ByteArray()) == 0 { + buf = make([]byte, 0) + } + c.columnChunk.MetaData.Statistics.MinValue = minValue.AppendBytes(buf) + if c.writeDeprecatedStatistics { + c.columnChunk.MetaData.Statistics.Min = c.columnChunk.MetaData.Statistics.MinValue + } + } + } + + if c.geospatialAccumulator != nil { + c.geospatialAccumulator.accumulatePage(page) + c.columnChunk.MetaData.GeospatialStatistics = c.geospatialAccumulator.toGeospatialStatistics() + } + + c.offsetIndex.PageLocations = append(c.offsetIndex.PageLocations, format.PageLocation{ + Offset: c.columnChunk.MetaData.TotalCompressedSize, + CompressedPageSize: compressedSize, + FirstRowIndex: c.numRows, + }) + + c.numRows += page.NumRows() + c.totalUnencodedByteArrayBytes += computeUnencodedByteArraySize(page) + + repetitionLevels := page.RepetitionLevels() + definitionLevels := page.DefinitionLevels() + + if c.maxRepetitionLevel > 0 { + c.pageRepetitionLevelHistograms = accumulateAndAppendPageLevelHistogram( + c.repetitionLevelHistogram, + c.pageRepetitionLevelHistograms, + repetitionLevels, + c.maxRepetitionLevel, + ) + } + + if c.maxDefinitionLevel > 0 { + c.pageDefinitionLevelHistograms = accumulateAndAppendPageLevelHistogram( + c.definitionLevelHistogram, + c.pageDefinitionLevelHistograms, + definitionLevels, + c.maxDefinitionLevel, + ) + } + } + + pageType := header.Type + encoding := format.Encoding(-1) + switch pageType { + case format.DataPageV2: + encoding = header.DataPageHeaderV2.V.Encoding + case format.DataPage: + encoding = header.DataPageHeader.V.Encoding + case format.DictionaryPage: + encoding = header.DictionaryPageHeader.V.Encoding + } + + c.columnChunk.MetaData.TotalUncompressedSize += int64(uncompressedSize) + c.columnChunk.MetaData.TotalCompressedSize += int64(compressedSize) + c.columnChunk.MetaData.EncodingStats = addPageEncodingStats(c.columnChunk.MetaData.EncodingStats, format.PageEncodingStats{ + PageType: pageType, + Encoding: encoding, + Count: 1, + }) +} + +func addEncoding(encodings []format.Encoding, add format.Encoding) []format.Encoding { + if slices.Contains(encodings, add) { + return encodings + } + return append(encodings, add) +} + +func addPageEncodingStats(stats []format.PageEncodingStats, pages ...format.PageEncodingStats) []format.PageEncodingStats { +addPages: + for _, add := range pages { + for i, st := range stats { + if st.PageType == add.PageType && st.Encoding == add.Encoding { + stats[i].Count += add.Count + continue addPages + } + } + stats = append(stats, add) + } + return stats +} + +func sortPageEncodings(encodings []format.Encoding) { + slices.Sort(encodings) +} + +func sortPageEncodingStats(stats []format.PageEncodingStats) { + slices.SortFunc(stats, func(s1, s2 format.PageEncodingStats) int { + if k := cmp.Compare(s1.PageType, s2.PageType); k != 0 { + return k + } + return cmp.Compare(s1.Encoding, s2.Encoding) + }) +} + +type offsetTrackingWriter struct { + writer io.Writer + offset int64 +} + +func (w *offsetTrackingWriter) Reset(writer io.Writer) { + w.writer = writer + w.offset = 0 +} + +func (w *offsetTrackingWriter) Write(b []byte) (int, error) { + n, err := w.writer.Write(b) + w.offset += int64(n) + return n, err +} + +func (w *offsetTrackingWriter) WriteString(s string) (int, error) { + n, err := io.WriteString(w.writer, s) + w.offset += int64(n) + return n, err +} + +func (w *offsetTrackingWriter) ReadFrom(r io.Reader) (int64, error) { + // io.Copy will make use of io.ReaderFrom if w.writer implements it. + n, err := io.Copy(w.writer, r) + w.offset += n + return n, err +} + +var ( + _ RowWriterWithSchema = (*Writer)(nil) + _ RowReaderFrom = (*Writer)(nil) + _ RowGroupWriter = (*Writer)(nil) + + _ RowWriter = (*writer)(nil) + _ ValueWriter = (*writer)(nil) + + _ io.ReaderFrom = (*offsetTrackingWriter)(nil) + _ io.StringWriter = (*offsetTrackingWriter)(nil) +) diff --git a/vendor/github.com/parquet-go/parquet-go/writer_statistics.go b/vendor/github.com/parquet-go/parquet-go/writer_statistics.go new file mode 100644 index 000000000..72bcd3f44 --- /dev/null +++ b/vendor/github.com/parquet-go/parquet-go/writer_statistics.go @@ -0,0 +1,61 @@ +package parquet + +import ( + "slices" + + "github.com/parquet-go/parquet-go/encoding" +) + +// computeUnencodedByteArraySize calculates the unencoded size of byte array data +// in a page, excluding 4-byte length prefixes. +func computeUnencodedByteArraySize(page Page) int64 { + values := page.Data() + if values.Kind() != encoding.ByteArray { + return 0 + } + return values.Size() +} + +// accumulateLevelHistogram adds level counts from levels to histogram. +func accumulateLevelHistogram(histogram []int64, levels []byte) { + for _, level := range levels { + histogram[level]++ + } +} + +// appendPageLevelHistogram creates a per-page histogram and appends it to histograms. +// Returns the updated histogram slice with (maxLevel + 1) new elements appended. +func appendPageLevelHistogram(histograms []int64, levels []byte, maxLevel byte) []int64 { + histSize := int(maxLevel) + 1 + startIndex := len(histograms) + histograms = slices.Grow(histograms, histSize)[:startIndex+histSize] + clear(histograms[startIndex : startIndex+histSize]) + + for _, level := range levels { + histograms[startIndex+int(level)]++ + } + + return histograms +} + +// accumulateAndAppendPageLevelHistogram combines accumulateLevelHistogram and +// appendPageLevelHistogram into a single pass through the levels array. +// It updates both the column-level histogram and creates a per-page histogram. +func accumulateAndAppendPageLevelHistogram( + columnHistogram []int64, + pageHistograms []int64, + levels []byte, + maxLevel byte, +) []int64 { + histSize := int(maxLevel) + 1 + startIndex := len(pageHistograms) + pageHistograms = slices.Grow(pageHistograms, histSize)[:startIndex+histSize] + clear(pageHistograms[startIndex : startIndex+histSize]) + + for _, level := range levels { + columnHistogram[level]++ + pageHistograms[startIndex+int(level)]++ + } + + return pageHistograms +} diff --git a/vendor/github.com/pierrec/lz4/v4/.gitignore b/vendor/github.com/pierrec/lz4/v4/.gitignore new file mode 100644 index 000000000..5d7e88de0 --- /dev/null +++ b/vendor/github.com/pierrec/lz4/v4/.gitignore @@ -0,0 +1,36 @@ +# Created by https://www.gitignore.io/api/macos + +### macOS ### +*.DS_Store +.AppleDouble +.LSOverride + +# Icon must end with two \r +Icon + + +# Thumbnails +._* + +# Files that might appear in the root of a volume +.DocumentRevisions-V100 +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns +.com.apple.timemachine.donotpresent + +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk + +# End of https://www.gitignore.io/api/macos + +cmd/*/*exe +.idea + +fuzz/*.zip diff --git a/vendor/github.com/pierrec/lz4/v4/LICENSE b/vendor/github.com/pierrec/lz4/v4/LICENSE new file mode 100644 index 000000000..bd899d835 --- /dev/null +++ b/vendor/github.com/pierrec/lz4/v4/LICENSE @@ -0,0 +1,28 @@ +Copyright (c) 2015, Pierre Curto +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +* Neither the name of xxHash nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + diff --git a/vendor/github.com/pierrec/lz4/v4/README.md b/vendor/github.com/pierrec/lz4/v4/README.md new file mode 100644 index 000000000..dee77545b --- /dev/null +++ b/vendor/github.com/pierrec/lz4/v4/README.md @@ -0,0 +1,92 @@ +# lz4 : LZ4 compression in pure Go + +[![Go Reference](https://pkg.go.dev/badge/github.com/pierrec/lz4/v4.svg)](https://pkg.go.dev/github.com/pierrec/lz4/v4) +[![CI](https://github.com/pierrec/lz4/workflows/ci/badge.svg)](https://github.com/pierrec/lz4/actions) +[![Go Report Card](https://goreportcard.com/badge/github.com/pierrec/lz4)](https://goreportcard.com/report/github.com/pierrec/lz4) +[![GitHub tag (latest SemVer)](https://img.shields.io/github/tag/pierrec/lz4.svg?style=social)](https://github.com/pierrec/lz4/tags) + +## Overview + +This package provides a streaming interface to [LZ4 data streams](http://fastcompression.blogspot.fr/2013/04/lz4-streaming-format-final.html) as well as low level compress and uncompress functions for LZ4 data blocks. +The implementation is based on the reference C [one](https://github.com/lz4/lz4). + +## Install + +Assuming you have the go toolchain installed: + +``` +go get github.com/pierrec/lz4/v4 +``` + +There is a command line interface tool to compress and decompress LZ4 files. + +``` +go install github.com/pierrec/lz4/v4/cmd/lz4c@latest +``` + +Usage + +``` +Usage of lz4c: + -version + print the program version + +Subcommands: +Compress the given files or from stdin to stdout. +compress [arguments] [ ...] + -bc + enable block checksum + -l int + compression level (0=fastest) + -sc + disable stream checksum + -size string + block max size [64K,256K,1M,4M] (default "4M") + +Uncompress the given files or from stdin to stdout. +uncompress [arguments] [ ...] + +``` + + +## Example + +``` +// Compress and uncompress an input string. +s := "hello world" +r := strings.NewReader(s) + +// The pipe will uncompress the data from the writer. +pr, pw := io.Pipe() +zw := lz4.NewWriter(pw) +zr := lz4.NewReader(pr) + +go func() { + // Compress the input string. + _, _ = io.Copy(zw, r) + _ = zw.Close() // Make sure the writer is closed + _ = pw.Close() // Terminate the pipe +}() + +_, _ = io.Copy(os.Stdout, zr) + +// Output: +// hello world +``` + +## Contributing + +Contributions are very welcome for bug fixing, performance improvements...! + +- Open an issue with a proper description +- Send a pull request with appropriate test case(s) + +## Contributors + +Thanks to all [contributors](https://github.com/pierrec/lz4/graphs/contributors) so far! + +Special thanks to [@Zariel](https://github.com/Zariel) for his asm implementation of the decoder. + +Special thanks to [@greatroar](https://github.com/greatroar) for his work on the asm implementations of the decoder for amd64 and arm64. + +Special thanks to [@klauspost](https://github.com/klauspost) for his work on optimizing the code. diff --git a/vendor/github.com/pierrec/lz4/v4/compressing_reader.go b/vendor/github.com/pierrec/lz4/v4/compressing_reader.go new file mode 100644 index 000000000..8df0dc76d --- /dev/null +++ b/vendor/github.com/pierrec/lz4/v4/compressing_reader.go @@ -0,0 +1,222 @@ +package lz4 + +import ( + "errors" + "io" + + "github.com/pierrec/lz4/v4/internal/lz4block" + "github.com/pierrec/lz4/v4/internal/lz4errors" + "github.com/pierrec/lz4/v4/internal/lz4stream" +) + +type crState int + +const ( + crStateInitial crState = iota + crStateReading + crStateFlushing + crStateDone +) + +type CompressingReader struct { + state crState + src io.ReadCloser // source reader + level lz4block.CompressionLevel // how hard to try + frame *lz4stream.Frame // frame being built + in []byte + out ovWriter + handler func(int) +} + +// NewCompressingReader creates a reader which reads compressed data from +// raw stream. This makes it a logical opposite of a normal lz4.Reader. +// We require an io.ReadCloser as an underlying source for compatibility +// with Go's http.Request. +func NewCompressingReader(src io.ReadCloser) *CompressingReader { + zrd := &CompressingReader { + frame: lz4stream.NewFrame(), + } + + _ = zrd.Apply(DefaultBlockSizeOption, DefaultChecksumOption, defaultOnBlockDone) + zrd.Reset(src) + + return zrd +} + +// Source exposes the underlying source stream for introspection and control. +func (zrd *CompressingReader) Source() io.ReadCloser { + return zrd.src +} + +// Close simply invokes the underlying stream Close method. This method is +// provided for the benefit of Go http client/server, which relies on Close +// for goroutine termination. +func (zrd *CompressingReader) Close() error { + return zrd.src.Close() +} + +// Apply applies useful options to the lz4 encoder. +func (zrd *CompressingReader) Apply(options ...Option) (err error) { + if zrd.state != crStateInitial { + return lz4errors.ErrOptionClosedOrError + } + + zrd.Reset(zrd.src) + + for _, o := range options { + if err = o(zrd); err != nil { + return + } + } + return +} + +func (*CompressingReader) private() {} + +func (zrd *CompressingReader) init() error { + zrd.frame.InitW(&zrd.out, 1, false) + size := zrd.frame.Descriptor.Flags.BlockSizeIndex() + zrd.in = size.Get() + return zrd.frame.Descriptor.Write(zrd.frame, &zrd.out) +} + +// Read allows reading of lz4 compressed data +func (zrd *CompressingReader) Read(p []byte) (n int, err error) { + defer func() { + if err != nil { + zrd.state = crStateDone + } + }() + + if !zrd.out.reset(p) { + return len(p), nil + } + + switch zrd.state { + case crStateInitial: + err = zrd.init() + if err != nil { + return + } + zrd.state = crStateReading + case crStateDone: + return 0, errors.New("This reader is done") + case crStateFlushing: + if zrd.out.dataPos > 0 { + n = zrd.out.dataPos + zrd.out.data = nil + zrd.out.dataPos = 0 + return + } else { + zrd.state = crStateDone + return 0, io.EOF + } + } + + for zrd.state == crStateReading { + block := zrd.frame.Blocks.Block + + var rCount int + rCount, err = io.ReadFull(zrd.src, zrd.in) + switch err { + case nil: + err = block.Compress( + zrd.frame, zrd.in[ : rCount], zrd.level, + ).Write(zrd.frame, &zrd.out) + zrd.handler(len(block.Data)) + if err != nil { + return + } + + if zrd.out.dataPos == len(zrd.out.data) { + n = zrd.out.dataPos + zrd.out.dataPos = 0 + zrd.out.data = nil + return + } + case io.EOF, io.ErrUnexpectedEOF: // read may be partial + if rCount > 0 { + err = block.Compress( + zrd.frame, zrd.in[ : rCount], zrd.level, + ).Write(zrd.frame, &zrd.out) + zrd.handler(len(block.Data)) + if err != nil { + return + } + } + + err = zrd.frame.CloseW(&zrd.out, 1) + if err != nil { + return + } + zrd.state = crStateFlushing + + n = zrd.out.dataPos + zrd.out.dataPos = 0 + zrd.out.data = nil + return + default: + return + } + } + + err = lz4errors.ErrInternalUnhandledState + return +} + +// Reset makes the stream usable again; mostly handy to reuse lz4 encoder +// instances. +func (zrd *CompressingReader) Reset(src io.ReadCloser) { + zrd.frame.Reset(1) + zrd.state = crStateInitial + zrd.src = src + zrd.out.clear() +} + +type ovWriter struct { + data []byte + ov []byte + dataPos int + ovPos int +} + +func (wr *ovWriter) Write(p []byte) (n int, err error) { + count := copy(wr.data[wr.dataPos : ], p) + wr.dataPos += count + + if count < len(p) { + wr.ov = append(wr.ov, p[count : ]...) + } + + return len(p), nil +} + +func (wr *ovWriter) reset(out []byte) bool { + ovRem := len(wr.ov) - wr.ovPos + + if ovRem >= len(out) { + wr.ovPos += copy(out, wr.ov[wr.ovPos : ]) + return false + } + + if ovRem > 0 { + copy(out, wr.ov[wr.ovPos : ]) + wr.ov = wr.ov[ : 0] + wr.ovPos = 0 + wr.dataPos = ovRem + } else if wr.ovPos > 0 { + wr.ov = wr.ov[ : 0] + wr.ovPos = 0 + wr.dataPos = 0 + } + + wr.data = out + return true +} + +func (wr *ovWriter) clear() { + wr.data = nil + wr.dataPos = 0 + wr.ov = wr.ov[ : 0] + wr.ovPos = 0 +} diff --git a/vendor/github.com/pierrec/lz4/v4/internal/lz4block/block.go b/vendor/github.com/pierrec/lz4/v4/internal/lz4block/block.go new file mode 100644 index 000000000..fec8adb03 --- /dev/null +++ b/vendor/github.com/pierrec/lz4/v4/internal/lz4block/block.go @@ -0,0 +1,481 @@ +package lz4block + +import ( + "encoding/binary" + "math/bits" + "sync" + + "github.com/pierrec/lz4/v4/internal/lz4errors" +) + +const ( + // The following constants are used to setup the compression algorithm. + minMatch = 4 // the minimum size of the match sequence size (4 bytes) + winSizeLog = 16 // LZ4 64Kb window size limit + winSize = 1 << winSizeLog + winMask = winSize - 1 // 64Kb window of previous data for dependent blocks + + // hashLog determines the size of the hash table used to quickly find a previous match position. + // Its value influences the compression speed and memory usage, the lower the faster, + // but at the expense of the compression ratio. + // 16 seems to be the best compromise for fast compression. + hashLog = 16 + htSize = 1 << hashLog + + mfLimit = 10 + minMatch // The last match cannot start within the last 14 bytes. +) + +func recoverBlock(e *error) { + if r := recover(); r != nil && *e == nil { + *e = lz4errors.ErrInvalidSourceShortBuffer + } +} + +// blockHash hashes the lower 6 bytes into a value < htSize. +func blockHash(x uint64) uint32 { + const prime6bytes = 227718039650203 + return uint32(((x << (64 - 48)) * prime6bytes) >> (64 - hashLog)) +} + +func CompressBlockBound(n int) int { + return n + n/255 + 16 +} + +func UncompressBlock(src, dst, dict []byte) (int, error) { + if len(src) == 0 { + return 0, nil + } + if di := decodeBlock(dst, src, dict); di >= 0 { + return di, nil + } + return 0, lz4errors.ErrInvalidSourceShortBuffer +} + +type Compressor struct { + // Offsets are at most 64kiB, so we can store only the lower 16 bits of + // match positions: effectively, an offset from some 64kiB block boundary. + // + // When we retrieve such an offset, we interpret it as relative to the last + // block boundary si &^ 0xffff, or the one before, (si &^ 0xffff) - 0x10000, + // depending on which of these is inside the current window. If a table + // entry was generated more than 64kiB back in the input, we find out by + // inspecting the input stream. + table [htSize]uint16 + + // Bitmap indicating which positions in the table are in use. + // This allows us to quickly reset the table for reuse, + // without having to zero everything. + inUse [htSize / 32]uint32 +} + +// Get returns the position of a presumptive match for the hash h. +// The match may be a false positive due to a hash collision or an old entry. +// If si < winSize, the return value may be negative. +func (c *Compressor) get(h uint32, si int) int { + h &= htSize - 1 + i := 0 + if c.inUse[h/32]&(1<<(h%32)) != 0 { + i = int(c.table[h]) + } + i += si &^ winMask + if i >= si { + // Try previous 64kiB block (negative when in first block). + i -= winSize + } + return i +} + +func (c *Compressor) put(h uint32, si int) { + h &= htSize - 1 + c.table[h] = uint16(si) + c.inUse[h/32] |= 1 << (h % 32) +} + +func (c *Compressor) reset() { c.inUse = [htSize / 32]uint32{} } + +var compressorPool = sync.Pool{New: func() interface{} { return new(Compressor) }} + +func CompressBlock(src, dst []byte) (int, error) { + c := compressorPool.Get().(*Compressor) + n, err := c.CompressBlock(src, dst) + compressorPool.Put(c) + return n, err +} + +func (c *Compressor) CompressBlock(src, dst []byte) (int, error) { + // Zero out reused table to avoid non-deterministic output (issue #65). + c.reset() + + // Return 0, nil only if the destination buffer size is < CompressBlockBound. + isNotCompressible := len(dst) < CompressBlockBound(len(src)) + + // adaptSkipLog sets how quickly the compressor begins skipping blocks when data is incompressible. + // This significantly speeds up incompressible data and usually has very small impact on compression. + // bytes to skip = 1 + (bytes since last match >> adaptSkipLog) + const adaptSkipLog = 7 + + // si: Current position of the search. + // anchor: Position of the current literals. + var si, di, anchor int + sn := len(src) - mfLimit + if sn <= 0 { + goto lastLiterals + } + + // Fast scan strategy: the hash table only stores the last 4 bytes sequences. + for si < sn { + // Hash the next 6 bytes (sequence)... + match := binary.LittleEndian.Uint64(src[si:]) + h := blockHash(match) + h2 := blockHash(match >> 8) + + // We check a match at s, s+1 and s+2 and pick the first one we get. + // Checking 3 only requires us to load the source one. + ref := c.get(h, si) + ref2 := c.get(h2, si+1) + c.put(h, si) + c.put(h2, si+1) + + offset := si - ref + + if offset <= 0 || offset >= winSize || uint32(match) != binary.LittleEndian.Uint32(src[ref:]) { + // No match. Start calculating another hash. + // The processor can usually do this out-of-order. + h = blockHash(match >> 16) + ref3 := c.get(h, si+2) + + // Check the second match at si+1 + si += 1 + offset = si - ref2 + + if offset <= 0 || offset >= winSize || uint32(match>>8) != binary.LittleEndian.Uint32(src[ref2:]) { + // No match. Check the third match at si+2 + si += 1 + offset = si - ref3 + c.put(h, si) + + if offset <= 0 || offset >= winSize || uint32(match>>16) != binary.LittleEndian.Uint32(src[ref3:]) { + // Skip one extra byte (at si+3) before we check 3 matches again. + si += 2 + (si-anchor)>>adaptSkipLog + continue + } + } + } + + // Match found. + lLen := si - anchor // Literal length. + // We already matched 4 bytes. + mLen := 4 + + // Extend backwards if we can, reducing literals. + tOff := si - offset - 1 + for lLen > 0 && tOff >= 0 && src[si-1] == src[tOff] { + si-- + tOff-- + lLen-- + mLen++ + } + + // Add the match length, so we continue search at the end. + // Use mLen to store the offset base. + si, mLen = si+mLen, si+minMatch + + // Find the longest match by looking by batches of 8 bytes. + for si+8 <= sn { + x := binary.LittleEndian.Uint64(src[si:]) ^ binary.LittleEndian.Uint64(src[si-offset:]) + if x == 0 { + si += 8 + } else { + // Stop is first non-zero byte. + si += bits.TrailingZeros64(x) >> 3 + break + } + } + + mLen = si - mLen + if di >= len(dst) { + return 0, lz4errors.ErrInvalidSourceShortBuffer + } + if mLen < 0xF { + dst[di] = byte(mLen) + } else { + dst[di] = 0xF + } + + // Encode literals length. + if lLen < 0xF { + dst[di] |= byte(lLen << 4) + } else { + dst[di] |= 0xF0 + di++ + l := lLen - 0xF + for ; l >= 0xFF && di < len(dst); l -= 0xFF { + dst[di] = 0xFF + di++ + } + if di >= len(dst) { + return 0, lz4errors.ErrInvalidSourceShortBuffer + } + dst[di] = byte(l) + } + di++ + + // Literals. + if di+lLen > len(dst) { + return 0, lz4errors.ErrInvalidSourceShortBuffer + } + copy(dst[di:di+lLen], src[anchor:anchor+lLen]) + di += lLen + 2 + anchor = si + + // Encode offset. + if di > len(dst) { + return 0, lz4errors.ErrInvalidSourceShortBuffer + } + dst[di-2], dst[di-1] = byte(offset), byte(offset>>8) + + // Encode match length part 2. + if mLen >= 0xF { + for mLen -= 0xF; mLen >= 0xFF && di < len(dst); mLen -= 0xFF { + dst[di] = 0xFF + di++ + } + if di >= len(dst) { + return 0, lz4errors.ErrInvalidSourceShortBuffer + } + dst[di] = byte(mLen) + di++ + } + // Check if we can load next values. + if si >= sn { + break + } + // Hash match end-2 + h = blockHash(binary.LittleEndian.Uint64(src[si-2:])) + c.put(h, si-2) + } + +lastLiterals: + if isNotCompressible && anchor == 0 { + // Incompressible. + return 0, nil + } + + // Last literals. + if di >= len(dst) { + return 0, lz4errors.ErrInvalidSourceShortBuffer + } + lLen := len(src) - anchor + if lLen < 0xF { + dst[di] = byte(lLen << 4) + } else { + dst[di] = 0xF0 + di++ + for lLen -= 0xF; lLen >= 0xFF && di < len(dst); lLen -= 0xFF { + dst[di] = 0xFF + di++ + } + if di >= len(dst) { + return 0, lz4errors.ErrInvalidSourceShortBuffer + } + dst[di] = byte(lLen) + } + di++ + + // Write the last literals. + if isNotCompressible && di >= anchor { + // Incompressible. + return 0, nil + } + if di+len(src)-anchor > len(dst) { + return 0, lz4errors.ErrInvalidSourceShortBuffer + } + di += copy(dst[di:di+len(src)-anchor], src[anchor:]) + return di, nil +} + +// blockHash hashes 4 bytes into a value < winSize. +func blockHashHC(x uint32) uint32 { + const hasher uint32 = 2654435761 // Knuth multiplicative hash. + return x * hasher >> (32 - winSizeLog) +} + +type CompressorHC struct { + // hashTable: stores the last position found for a given hash + // chainTable: stores previous positions for a given hash + hashTable, chainTable [htSize]int + needsReset bool +} + +var compressorHCPool = sync.Pool{New: func() interface{} { return new(CompressorHC) }} + +func CompressBlockHC(src, dst []byte, depth CompressionLevel) (int, error) { + c := compressorHCPool.Get().(*CompressorHC) + n, err := c.CompressBlock(src, dst, depth) + compressorHCPool.Put(c) + return n, err +} + +func (c *CompressorHC) CompressBlock(src, dst []byte, depth CompressionLevel) (_ int, err error) { + if c.needsReset { + // Zero out reused table to avoid non-deterministic output (issue #65). + c.hashTable = [htSize]int{} + c.chainTable = [htSize]int{} + } + c.needsReset = true // Only false on first call. + + defer recoverBlock(&err) + + // Return 0, nil only if the destination buffer size is < CompressBlockBound. + isNotCompressible := len(dst) < CompressBlockBound(len(src)) + + // adaptSkipLog sets how quickly the compressor begins skipping blocks when data is incompressible. + // This significantly speeds up incompressible data and usually has very small impact on compression. + // bytes to skip = 1 + (bytes since last match >> adaptSkipLog) + const adaptSkipLog = 7 + + var si, di, anchor int + sn := len(src) - mfLimit + if sn <= 0 { + goto lastLiterals + } + + if depth == 0 { + depth = winSize + } + + for si < sn { + // Hash the next 4 bytes (sequence). + match := binary.LittleEndian.Uint32(src[si:]) + h := blockHashHC(match) + + // Follow the chain until out of window and give the longest match. + mLen := 0 + offset := 0 + for next, try := c.hashTable[h], depth; try > 0 && next > 0 && si-next < winSize; next, try = c.chainTable[next&winMask], try-1 { + // The first (mLen==0) or next byte (mLen>=minMatch) at current match length + // must match to improve on the match length. + if src[next+mLen] != src[si+mLen] { + continue + } + ml := 0 + // Compare the current position with a previous with the same hash. + for ml < sn-si { + x := binary.LittleEndian.Uint64(src[next+ml:]) ^ binary.LittleEndian.Uint64(src[si+ml:]) + if x == 0 { + ml += 8 + } else { + // Stop is first non-zero byte. + ml += bits.TrailingZeros64(x) >> 3 + break + } + } + if ml < minMatch || ml <= mLen { + // Match too small (>adaptSkipLog + continue + } + + // Match found. + // Update hash/chain tables with overlapping bytes: + // si already hashed, add everything from si+1 up to the match length. + winStart := si + 1 + if ws := si + mLen - winSize; ws > winStart { + winStart = ws + } + for si, ml := winStart, si+mLen; si < ml; { + match >>= 8 + match |= uint32(src[si+3]) << 24 + h := blockHashHC(match) + c.chainTable[si&winMask] = c.hashTable[h] + c.hashTable[h] = si + si++ + } + + lLen := si - anchor + si += mLen + mLen -= minMatch // Match length does not include minMatch. + + if mLen < 0xF { + dst[di] = byte(mLen) + } else { + dst[di] = 0xF + } + + // Encode literals length. + if lLen < 0xF { + dst[di] |= byte(lLen << 4) + } else { + dst[di] |= 0xF0 + di++ + l := lLen - 0xF + for ; l >= 0xFF; l -= 0xFF { + dst[di] = 0xFF + di++ + } + dst[di] = byte(l) + } + di++ + + // Literals. + copy(dst[di:di+lLen], src[anchor:anchor+lLen]) + di += lLen + anchor = si + + // Encode offset. + di += 2 + dst[di-2], dst[di-1] = byte(offset), byte(offset>>8) + + // Encode match length part 2. + if mLen >= 0xF { + for mLen -= 0xF; mLen >= 0xFF; mLen -= 0xFF { + dst[di] = 0xFF + di++ + } + dst[di] = byte(mLen) + di++ + } + } + + if isNotCompressible && anchor == 0 { + // Incompressible. + return 0, nil + } + + // Last literals. +lastLiterals: + lLen := len(src) - anchor + if lLen < 0xF { + dst[di] = byte(lLen << 4) + } else { + dst[di] = 0xF0 + di++ + lLen -= 0xF + for ; lLen >= 0xFF; lLen -= 0xFF { + dst[di] = 0xFF + di++ + } + dst[di] = byte(lLen) + } + di++ + + // Write the last literals. + if isNotCompressible && di >= anchor { + // Incompressible. + return 0, nil + } + di += copy(dst[di:di+len(src)-anchor], src[anchor:]) + return di, nil +} diff --git a/vendor/github.com/pierrec/lz4/v4/internal/lz4block/blocks.go b/vendor/github.com/pierrec/lz4/v4/internal/lz4block/blocks.go new file mode 100644 index 000000000..138083d94 --- /dev/null +++ b/vendor/github.com/pierrec/lz4/v4/internal/lz4block/blocks.go @@ -0,0 +1,87 @@ +// Package lz4block provides LZ4 BlockSize types and pools of buffers. +package lz4block + +import "sync" + +const ( + Block64Kb uint32 = 1 << (16 + iota*2) + Block256Kb + Block1Mb + Block4Mb + Block8Mb = 2 * Block4Mb +) + +var ( + BlockPool64K = sync.Pool{New: func() interface{} { return make([]byte, Block64Kb) }} + BlockPool256K = sync.Pool{New: func() interface{} { return make([]byte, Block256Kb) }} + BlockPool1M = sync.Pool{New: func() interface{} { return make([]byte, Block1Mb) }} + BlockPool4M = sync.Pool{New: func() interface{} { return make([]byte, Block4Mb) }} + BlockPool8M = sync.Pool{New: func() interface{} { return make([]byte, Block8Mb) }} +) + +func Index(b uint32) BlockSizeIndex { + switch b { + case Block64Kb: + return 4 + case Block256Kb: + return 5 + case Block1Mb: + return 6 + case Block4Mb: + return 7 + case Block8Mb: // only valid in legacy mode + return 3 + } + return 0 +} + +func IsValid(b uint32) bool { + return Index(b) > 0 +} + +type BlockSizeIndex uint8 + +func (b BlockSizeIndex) IsValid() bool { + switch b { + case 4, 5, 6, 7: + return true + } + return false +} + +func (b BlockSizeIndex) Get() []byte { + var buf interface{} + switch b { + case 4: + buf = BlockPool64K.Get() + case 5: + buf = BlockPool256K.Get() + case 6: + buf = BlockPool1M.Get() + case 7: + buf = BlockPool4M.Get() + case 3: + buf = BlockPool8M.Get() + } + return buf.([]byte) +} + +func Put(buf []byte) { + // Safeguard: do not allow invalid buffers. + switch c := cap(buf); uint32(c) { + case Block64Kb: + BlockPool64K.Put(buf[:c]) + case Block256Kb: + BlockPool256K.Put(buf[:c]) + case Block1Mb: + BlockPool1M.Put(buf[:c]) + case Block4Mb: + BlockPool4M.Put(buf[:c]) + case Block8Mb: + BlockPool8M.Put(buf[:c]) + } +} + +type CompressionLevel uint32 + +const Fast CompressionLevel = 0 diff --git a/vendor/github.com/pierrec/lz4/v4/internal/lz4block/decode_amd64.s b/vendor/github.com/pierrec/lz4/v4/internal/lz4block/decode_amd64.s new file mode 100644 index 000000000..1d00133fa --- /dev/null +++ b/vendor/github.com/pierrec/lz4/v4/internal/lz4block/decode_amd64.s @@ -0,0 +1,448 @@ +// +build !appengine +// +build gc +// +build !noasm + +#include "go_asm.h" +#include "textflag.h" + +// AX scratch +// BX scratch +// CX literal and match lengths +// DX token, match offset +// +// DI &dst +// SI &src +// R8 &dst + len(dst) +// R9 &src + len(src) +// R11 &dst +// R12 short output end +// R13 short input end +// R14 &dict +// R15 len(dict) + +// func decodeBlock(dst, src, dict []byte) int +TEXT ·decodeBlock(SB), NOSPLIT, $48-80 + MOVQ dst_base+0(FP), DI + MOVQ DI, R11 + MOVQ dst_len+8(FP), R8 + ADDQ DI, R8 + + MOVQ src_base+24(FP), SI + MOVQ src_len+32(FP), R9 + CMPQ R9, $0 + JE err_corrupt + ADDQ SI, R9 + + MOVQ dict_base+48(FP), R14 + MOVQ dict_len+56(FP), R15 + + // shortcut ends + // short output end + MOVQ R8, R12 + SUBQ $32, R12 + // short input end + MOVQ R9, R13 + SUBQ $16, R13 + + XORL CX, CX + +loop: + // token := uint32(src[si]) + MOVBLZX (SI), DX + INCQ SI + + // lit_len = token >> 4 + // if lit_len > 0 + // CX = lit_len + MOVL DX, CX + SHRL $4, CX + + // if lit_len != 0xF + CMPL CX, $0xF + JEQ lit_len_loop + CMPQ DI, R12 + JAE copy_literal + CMPQ SI, R13 + JAE copy_literal + + // copy shortcut + + // A two-stage shortcut for the most common case: + // 1) If the literal length is 0..14, and there is enough space, + // enter the shortcut and copy 16 bytes on behalf of the literals + // (in the fast mode, only 8 bytes can be safely copied this way). + // 2) Further if the match length is 4..18, copy 18 bytes in a similar + // manner; but we ensure that there's enough space in the output for + // those 18 bytes earlier, upon entering the shortcut (in other words, + // there is a combined check for both stages). + + // copy literal + MOVOU (SI), X0 + MOVOU X0, (DI) + ADDQ CX, DI + ADDQ CX, SI + + MOVL DX, CX + ANDL $0xF, CX + + // The second stage: prepare for match copying, decode full info. + // If it doesn't work out, the info won't be wasted. + // offset := uint16(data[:2]) + MOVWLZX (SI), DX + TESTL DX, DX + JE err_corrupt + ADDQ $2, SI + JC err_short_buf + + MOVQ DI, AX + SUBQ DX, AX + JC err_corrupt + CMPQ AX, DI + JA err_short_buf + + // if we can't do the second stage then jump straight to read the + // match length, we already have the offset. + CMPL CX, $0xF + JEQ match_len_loop_pre + CMPL DX, $8 + JLT match_len_loop_pre + CMPQ AX, R11 + JB match_len_loop_pre + + // memcpy(op + 0, match + 0, 8); + MOVQ (AX), BX + MOVQ BX, (DI) + // memcpy(op + 8, match + 8, 8); + MOVQ 8(AX), BX + MOVQ BX, 8(DI) + // memcpy(op +16, match +16, 2); + MOVW 16(AX), BX + MOVW BX, 16(DI) + + LEAQ const_minMatch(DI)(CX*1), DI + + // shortcut complete, load next token + JMP loopcheck + + // Read the rest of the literal length: + // do { BX = src[si++]; lit_len += BX } while (BX == 0xFF). +lit_len_loop: + CMPQ SI, R9 + JAE err_short_buf + + MOVBLZX (SI), BX + INCQ SI + ADDQ BX, CX + + CMPB BX, $0xFF + JE lit_len_loop + +copy_literal: + // bounds check src and dst + MOVQ SI, AX + ADDQ CX, AX + JC err_short_buf + CMPQ AX, R9 + JA err_short_buf + + MOVQ DI, BX + ADDQ CX, BX + JC err_short_buf + CMPQ BX, R8 + JA err_short_buf + + // Copy literals of <=48 bytes through the XMM registers. + CMPQ CX, $48 + JGT memmove_lit + + // if len(dst[di:]) < 48 + MOVQ R8, AX + SUBQ DI, AX + CMPQ AX, $48 + JLT memmove_lit + + // if len(src[si:]) < 48 + MOVQ R9, BX + SUBQ SI, BX + CMPQ BX, $48 + JLT memmove_lit + + MOVOU (SI), X0 + MOVOU 16(SI), X1 + MOVOU 32(SI), X2 + MOVOU X0, (DI) + MOVOU X1, 16(DI) + MOVOU X2, 32(DI) + + ADDQ CX, SI + ADDQ CX, DI + + JMP finish_lit_copy + +memmove_lit: + // memmove(to, from, len) + MOVQ DI, 0(SP) + MOVQ SI, 8(SP) + MOVQ CX, 16(SP) + + // Spill registers. Increment SI, DI now so we don't need to save CX. + ADDQ CX, DI + ADDQ CX, SI + MOVQ DI, 24(SP) + MOVQ SI, 32(SP) + MOVL DX, 40(SP) + + CALL runtime·memmove(SB) + + // restore registers + MOVQ 24(SP), DI + MOVQ 32(SP), SI + MOVL 40(SP), DX + + // recalc initial values + MOVQ dst_base+0(FP), R8 + MOVQ R8, R11 + ADDQ dst_len+8(FP), R8 + MOVQ src_base+24(FP), R9 + ADDQ src_len+32(FP), R9 + MOVQ dict_base+48(FP), R14 + MOVQ dict_len+56(FP), R15 + MOVQ R8, R12 + SUBQ $32, R12 + MOVQ R9, R13 + SUBQ $16, R13 + +finish_lit_copy: + // CX := mLen + // free up DX to use for offset + MOVL DX, CX + ANDL $0xF, CX + + CMPQ SI, R9 + JAE end + + // offset + // si += 2 + // DX := int(src[si-2]) | int(src[si-1])<<8 + ADDQ $2, SI + JC err_short_buf + CMPQ SI, R9 + JA err_short_buf + MOVWQZX -2(SI), DX + + // 0 offset is invalid + TESTL DX, DX + JEQ err_corrupt + +match_len_loop_pre: + // if mlen != 0xF + CMPB CX, $0xF + JNE copy_match + + // do { BX = src[si++]; mlen += BX } while (BX == 0xFF). +match_len_loop: + CMPQ SI, R9 + JAE err_short_buf + + MOVBLZX (SI), BX + INCQ SI + ADDQ BX, CX + + CMPB BX, $0xFF + JE match_len_loop + +copy_match: + ADDQ $const_minMatch, CX + + // check we have match_len bytes left in dst + // di+match_len < len(dst) + MOVQ DI, AX + ADDQ CX, AX + JC err_short_buf + CMPQ AX, R8 + JA err_short_buf + + // DX = offset + // CX = match_len + // BX = &dst + (di - offset) + MOVQ DI, BX + SUBQ DX, BX + + // check BX is within dst + // if BX < &dst + JC copy_match_from_dict + CMPQ BX, R11 + JBE copy_match_from_dict + + // if offset + match_len < di + LEAQ (BX)(CX*1), AX + CMPQ DI, AX + JA copy_interior_match + + // AX := len(dst[:di]) + // MOVQ DI, AX + // SUBQ R11, AX + + // copy 16 bytes at a time + // if di-offset < 16 copy 16-(di-offset) bytes to di + // then do the remaining + +copy_match_loop: + // for match_len >= 0 + // dst[di] = dst[i] + // di++ + // i++ + MOVB (BX), AX + MOVB AX, (DI) + INCQ DI + INCQ BX + DECQ CX + JNZ copy_match_loop + + JMP loopcheck + +copy_interior_match: + CMPQ CX, $16 + JGT memmove_match + + // if len(dst[di:]) < 16 + MOVQ R8, AX + SUBQ DI, AX + CMPQ AX, $16 + JLT memmove_match + + MOVOU (BX), X0 + MOVOU X0, (DI) + + ADDQ CX, DI + XORL CX, CX + JMP loopcheck + +copy_match_from_dict: + // CX = match_len + // BX = &dst + (di - offset) + + // AX = offset - di = dict_bytes_available => count of bytes potentially covered by the dictionary + MOVQ R11, AX + SUBQ BX, AX + + // BX = len(dict) - dict_bytes_available + MOVQ R15, BX + SUBQ AX, BX + JS err_short_dict + + ADDQ R14, BX + + // if match_len > dict_bytes_available, match fits entirely within external dictionary : just copy + CMPQ CX, AX + JLT memmove_match + + // The match stretches over the dictionary and our block + // 1) copy what comes from the dictionary + // AX = dict_bytes_available = copy_size + // BX = &dict_end - copy_size + // CX = match_len + + // memmove(to, from, len) + MOVQ DI, 0(SP) + MOVQ BX, 8(SP) + MOVQ AX, 16(SP) + // store extra stuff we want to recover + // spill + MOVQ DI, 24(SP) + MOVQ SI, 32(SP) + MOVQ CX, 40(SP) + CALL runtime·memmove(SB) + + // restore registers + MOVQ 16(SP), AX // copy_size + MOVQ 24(SP), DI + MOVQ 32(SP), SI + MOVQ 40(SP), CX // match_len + + // recalc initial values + MOVQ dst_base+0(FP), R8 + MOVQ R8, R11 // TODO: make these sensible numbers + ADDQ dst_len+8(FP), R8 + MOVQ src_base+24(FP), R9 + ADDQ src_len+32(FP), R9 + MOVQ dict_base+48(FP), R14 + MOVQ dict_len+56(FP), R15 + MOVQ R8, R12 + SUBQ $32, R12 + MOVQ R9, R13 + SUBQ $16, R13 + + // di+=copy_size + ADDQ AX, DI + + // 2) copy the rest from the current block + // CX = match_len - copy_size = rest_size + SUBQ AX, CX + MOVQ R11, BX + + // check if we have a copy overlap + // AX = &dst + rest_size + MOVQ CX, AX + ADDQ BX, AX + // if &dst + rest_size > di, copy byte by byte + CMPQ AX, DI + + JA copy_match_loop + +memmove_match: + // memmove(to, from, len) + MOVQ DI, 0(SP) + MOVQ BX, 8(SP) + MOVQ CX, 16(SP) + + // Spill registers. Increment DI now so we don't need to save CX. + ADDQ CX, DI + MOVQ DI, 24(SP) + MOVQ SI, 32(SP) + + CALL runtime·memmove(SB) + + // restore registers + MOVQ 24(SP), DI + MOVQ 32(SP), SI + + // recalc initial values + MOVQ dst_base+0(FP), R8 + MOVQ R8, R11 // TODO: make these sensible numbers + ADDQ dst_len+8(FP), R8 + MOVQ src_base+24(FP), R9 + ADDQ src_len+32(FP), R9 + MOVQ R8, R12 + SUBQ $32, R12 + MOVQ R9, R13 + SUBQ $16, R13 + MOVQ dict_base+48(FP), R14 + MOVQ dict_len+56(FP), R15 + XORL CX, CX + +loopcheck: + // for si < len(src) + CMPQ SI, R9 + JB loop + +end: + // Remaining length must be zero. + TESTQ CX, CX + JNE err_corrupt + + SUBQ R11, DI + MOVQ DI, ret+72(FP) + RET + +err_corrupt: + MOVQ $-1, ret+72(FP) + RET + +err_short_buf: + MOVQ $-2, ret+72(FP) + RET + +err_short_dict: + MOVQ $-3, ret+72(FP) + RET diff --git a/vendor/github.com/pierrec/lz4/v4/internal/lz4block/decode_arm.s b/vendor/github.com/pierrec/lz4/v4/internal/lz4block/decode_arm.s new file mode 100644 index 000000000..20b21fcf1 --- /dev/null +++ b/vendor/github.com/pierrec/lz4/v4/internal/lz4block/decode_arm.s @@ -0,0 +1,231 @@ +// +build gc +// +build !noasm + +#include "go_asm.h" +#include "textflag.h" + +// Register allocation. +#define dst R0 +#define dstorig R1 +#define src R2 +#define dstend R3 +#define srcend R4 +#define match R5 // Match address. +#define dictend R6 +#define token R7 +#define len R8 // Literal and match lengths. +#define offset R7 // Match offset; overlaps with token. +#define tmp1 R9 +#define tmp2 R11 +#define tmp3 R12 + +// func decodeBlock(dst, src, dict []byte) int +TEXT ·decodeBlock(SB), NOFRAME+NOSPLIT, $-4-40 + MOVW dst_base +0(FP), dst + MOVW dst_len +4(FP), dstend + MOVW src_base +12(FP), src + MOVW src_len +16(FP), srcend + + CMP $0, srcend + BEQ shortSrc + + ADD dst, dstend + ADD src, srcend + + MOVW dst, dstorig + +loop: + // Read token. Extract literal length. + MOVBU.P 1(src), token + MOVW token >> 4, len + CMP $15, len + BNE readLitlenDone + +readLitlenLoop: + CMP src, srcend + BEQ shortSrc + MOVBU.P 1(src), tmp1 + ADD.S tmp1, len + BVS shortDst + CMP $255, tmp1 + BEQ readLitlenLoop + +readLitlenDone: + CMP $0, len + BEQ copyLiteralDone + + // Bounds check dst+len and src+len. + ADD.S dst, len, tmp1 + ADD.CC.S src, len, tmp2 + BCS shortSrc + CMP dstend, tmp1 + //BHI shortDst // Uncomment for distinct error codes. + CMP.LS srcend, tmp2 + BHI shortSrc + + // Copy literal. + CMP $4, len + BLO copyLiteralFinish + + // Copy 0-3 bytes until src is aligned. + TST $1, src + MOVBU.NE.P 1(src), tmp1 + MOVB.NE.P tmp1, 1(dst) + SUB.NE $1, len + + TST $2, src + MOVHU.NE.P 2(src), tmp2 + MOVB.NE.P tmp2, 1(dst) + MOVW.NE tmp2 >> 8, tmp1 + MOVB.NE.P tmp1, 1(dst) + SUB.NE $2, len + + B copyLiteralLoopCond + +copyLiteralLoop: + // Aligned load, unaligned write. + MOVW.P 4(src), tmp1 + MOVW tmp1 >> 8, tmp2 + MOVB tmp2, 1(dst) + MOVW tmp1 >> 16, tmp3 + MOVB tmp3, 2(dst) + MOVW tmp1 >> 24, tmp2 + MOVB tmp2, 3(dst) + MOVB.P tmp1, 4(dst) +copyLiteralLoopCond: + // Loop until len-4 < 0. + SUB.S $4, len + BPL copyLiteralLoop + +copyLiteralFinish: + // Copy remaining 0-3 bytes. + // At this point, len may be < 0, but len&3 is still accurate. + TST $1, len + MOVB.NE.P 1(src), tmp3 + MOVB.NE.P tmp3, 1(dst) + TST $2, len + MOVB.NE.P 2(src), tmp1 + MOVB.NE.P tmp1, 2(dst) + MOVB.NE -1(src), tmp2 + MOVB.NE tmp2, -1(dst) + +copyLiteralDone: + // Initial part of match length. + // This frees up the token register for reuse as offset. + AND $15, token, len + + CMP src, srcend + BEQ end + + // Read offset. + ADD.S $2, src + BCS shortSrc + CMP srcend, src + BHI shortSrc + MOVBU -2(src), offset + MOVBU -1(src), tmp1 + ORR.S tmp1 << 8, offset + BEQ corrupt + + // Read rest of match length. + CMP $15, len + BNE readMatchlenDone + +readMatchlenLoop: + CMP src, srcend + BEQ shortSrc + MOVBU.P 1(src), tmp1 + ADD.S tmp1, len + BVS shortDst + CMP $255, tmp1 + BEQ readMatchlenLoop + +readMatchlenDone: + // Bounds check dst+len+minMatch. + ADD.S dst, len, tmp1 + ADD.CC.S $const_minMatch, tmp1 + BCS shortDst + CMP dstend, tmp1 + BHI shortDst + + RSB dst, offset, match + CMP dstorig, match + BGE copyMatch4 + + // match < dstorig means the match starts in the dictionary, + // at len(dict) - offset + (dst - dstorig). + MOVW dict_base+24(FP), match + MOVW dict_len +28(FP), dictend + + ADD $const_minMatch, len + + RSB dst, dstorig, tmp1 + RSB dictend, offset, tmp2 + ADD.S tmp2, tmp1 + BMI shortDict + ADD match, dictend + ADD tmp1, match + +copyDict: + MOVBU.P 1(match), tmp1 + MOVB.P tmp1, 1(dst) + SUB.S $1, len + CMP.NE match, dictend + BNE copyDict + + // If the match extends beyond the dictionary, the rest is at dstorig. + CMP $0, len + BEQ copyMatchDone + MOVW dstorig, match + B copyMatch + + // Copy a regular match. + // Since len+minMatch is at least four, we can do a 4× unrolled + // byte copy loop. Using MOVW instead of four byte loads is faster, + // but to remain portable we'd have to align match first, which is + // too expensive. By alternating loads and stores, we also handle + // the case offset < 4. +copyMatch4: + SUB.S $4, len + MOVBU.P 4(match), tmp1 + MOVB.P tmp1, 4(dst) + MOVBU -3(match), tmp2 + MOVB tmp2, -3(dst) + MOVBU -2(match), tmp3 + MOVB tmp3, -2(dst) + MOVBU -1(match), tmp1 + MOVB tmp1, -1(dst) + BPL copyMatch4 + + // Restore len, which is now negative. + ADD.S $4, len + BEQ copyMatchDone + +copyMatch: + // Finish with a byte-at-a-time copy. + SUB.S $1, len + MOVBU.P 1(match), tmp2 + MOVB.P tmp2, 1(dst) + BNE copyMatch + +copyMatchDone: + CMP src, srcend + BNE loop + +end: + CMP $0, len + BNE corrupt + SUB dstorig, dst, tmp1 + MOVW tmp1, ret+36(FP) + RET + + // The error cases have distinct labels so we can put different + // return codes here when debugging, or if the error returns need to + // be changed. +shortDict: +shortDst: +shortSrc: +corrupt: + MOVW $-1, tmp1 + MOVW tmp1, ret+36(FP) + RET diff --git a/vendor/github.com/pierrec/lz4/v4/internal/lz4block/decode_arm64.s b/vendor/github.com/pierrec/lz4/v4/internal/lz4block/decode_arm64.s new file mode 100644 index 000000000..d2fe11b8e --- /dev/null +++ b/vendor/github.com/pierrec/lz4/v4/internal/lz4block/decode_arm64.s @@ -0,0 +1,241 @@ +// +build gc +// +build !noasm + +// This implementation assumes that strict alignment checking is turned off. +// The Go compiler makes the same assumption. + +#include "go_asm.h" +#include "textflag.h" + +// Register allocation. +#define dst R0 +#define dstorig R1 +#define src R2 +#define dstend R3 +#define dstend16 R4 // dstend - 16 +#define srcend R5 +#define srcend16 R6 // srcend - 16 +#define match R7 // Match address. +#define dict R8 +#define dictlen R9 +#define dictend R10 +#define token R11 +#define len R12 // Literal and match lengths. +#define lenRem R13 +#define offset R14 // Match offset. +#define tmp1 R15 +#define tmp2 R16 +#define tmp3 R17 +#define tmp4 R19 + +// func decodeBlock(dst, src, dict []byte) int +TEXT ·decodeBlock(SB), NOFRAME+NOSPLIT, $0-80 + LDP dst_base+0(FP), (dst, dstend) + ADD dst, dstend + MOVD dst, dstorig + + LDP src_base+24(FP), (src, srcend) + CBZ srcend, shortSrc + ADD src, srcend + + // dstend16 = max(dstend-16, 0) and similarly for srcend16. + SUBS $16, dstend, dstend16 + CSEL LO, ZR, dstend16, dstend16 + SUBS $16, srcend, srcend16 + CSEL LO, ZR, srcend16, srcend16 + + LDP dict_base+48(FP), (dict, dictlen) + ADD dict, dictlen, dictend + +loop: + // Read token. Extract literal length. + MOVBU.P 1(src), token + LSR $4, token, len + CMP $15, len + BNE readLitlenDone + +readLitlenLoop: + CMP src, srcend + BEQ shortSrc + MOVBU.P 1(src), tmp1 + ADDS tmp1, len + BVS shortDst + CMP $255, tmp1 + BEQ readLitlenLoop + +readLitlenDone: + CBZ len, copyLiteralDone + + // Bounds check dst+len and src+len. + ADDS dst, len, tmp1 + BCS shortSrc + ADDS src, len, tmp2 + BCS shortSrc + CMP dstend, tmp1 + BHI shortDst + CMP srcend, tmp2 + BHI shortSrc + + // Copy literal. + SUBS $16, len + BLO copyLiteralShort + +copyLiteralLoop: + LDP.P 16(src), (tmp1, tmp2) + STP.P (tmp1, tmp2), 16(dst) + SUBS $16, len + BPL copyLiteralLoop + + // Copy (final part of) literal of length 0-15. + // If we have >=16 bytes left in src and dst, just copy 16 bytes. +copyLiteralShort: + CMP dstend16, dst + CCMP LO, src, srcend16, $0b0010 // 0010 = preserve carry (LO). + BHS copyLiteralShortEnd + + AND $15, len + + LDP (src), (tmp1, tmp2) + ADD len, src + STP (tmp1, tmp2), (dst) + ADD len, dst + + B copyLiteralDone + + // Safe but slow copy near the end of src, dst. +copyLiteralShortEnd: + TBZ $3, len, 3(PC) + MOVD.P 8(src), tmp1 + MOVD.P tmp1, 8(dst) + TBZ $2, len, 3(PC) + MOVW.P 4(src), tmp2 + MOVW.P tmp2, 4(dst) + TBZ $1, len, 3(PC) + MOVH.P 2(src), tmp3 + MOVH.P tmp3, 2(dst) + TBZ $0, len, 3(PC) + MOVBU.P 1(src), tmp4 + MOVB.P tmp4, 1(dst) + +copyLiteralDone: + // Initial part of match length. + AND $15, token, len + + CMP src, srcend + BEQ end + + // Read offset. + ADDS $2, src + BCS shortSrc + CMP srcend, src + BHI shortSrc + MOVHU -2(src), offset + CBZ offset, corrupt + + // Read rest of match length. + CMP $15, len + BNE readMatchlenDone + +readMatchlenLoop: + CMP src, srcend + BEQ shortSrc + MOVBU.P 1(src), tmp1 + ADDS tmp1, len + BVS shortDst + CMP $255, tmp1 + BEQ readMatchlenLoop + +readMatchlenDone: + ADD $const_minMatch, len + + // Bounds check dst+len. + ADDS dst, len, tmp2 + BCS shortDst + CMP dstend, tmp2 + BHI shortDst + + SUB offset, dst, match + CMP dstorig, match + BHS copyMatchTry8 + + // match < dstorig means the match starts in the dictionary, + // at len(dict) - offset + (dst - dstorig). + SUB dstorig, dst, tmp1 + SUB offset, dictlen, tmp2 + ADDS tmp2, tmp1 + BMI shortDict + ADD dict, tmp1, match + +copyDict: + MOVBU.P 1(match), tmp3 + MOVB.P tmp3, 1(dst) + SUBS $1, len + CCMP NE, dictend, match, $0b0100 // 0100 sets the Z (EQ) flag. + BNE copyDict + + CBZ len, copyMatchDone + + // If the match extends beyond the dictionary, the rest is at dstorig. + // Recompute the offset for the next check. + MOVD dstorig, match + SUB dstorig, dst, offset + +copyMatchTry8: + // Copy doublewords if both len and offset are at least eight. + // A 16-at-a-time loop doesn't provide a further speedup. + CMP $8, len + CCMP HS, offset, $8, $0 + BLO copyMatchTry4 + + AND $7, len, lenRem + SUB $8, len +copyMatchLoop8: + MOVD.P 8(match), tmp1 + MOVD.P tmp1, 8(dst) + SUBS $8, len + BPL copyMatchLoop8 + + MOVD (match)(len), tmp2 // match+len == match+lenRem-8. + ADD lenRem, dst + MOVD $0, len + MOVD tmp2, -8(dst) + B copyMatchDone + +copyMatchTry4: + // Copy words if both len and offset are at least four. + CMP $4, len + CCMP HS, offset, $4, $0 + BLO copyMatchLoop1 + + MOVWU.P 4(match), tmp2 + MOVWU.P tmp2, 4(dst) + SUBS $4, len + BEQ copyMatchDone + +copyMatchLoop1: + // Byte-at-a-time copy for small offsets <= 3. + MOVBU.P 1(match), tmp2 + MOVB.P tmp2, 1(dst) + SUBS $1, len + BNE copyMatchLoop1 + +copyMatchDone: + CMP src, srcend + BNE loop + +end: + CBNZ len, corrupt + SUB dstorig, dst, tmp1 + MOVD tmp1, ret+72(FP) + RET + + // The error cases have distinct labels so we can put different + // return codes here when debugging, or if the error returns need to + // be changed. +shortDict: +shortDst: +shortSrc: +corrupt: + MOVD $-1, tmp1 + MOVD tmp1, ret+72(FP) + RET diff --git a/vendor/github.com/pierrec/lz4/v4/internal/lz4block/decode_asm.go b/vendor/github.com/pierrec/lz4/v4/internal/lz4block/decode_asm.go new file mode 100644 index 000000000..8d9023d10 --- /dev/null +++ b/vendor/github.com/pierrec/lz4/v4/internal/lz4block/decode_asm.go @@ -0,0 +1,10 @@ +//go:build (amd64 || arm || arm64) && !appengine && gc && !noasm +// +build amd64 arm arm64 +// +build !appengine +// +build gc +// +build !noasm + +package lz4block + +//go:noescape +func decodeBlock(dst, src, dict []byte) int diff --git a/vendor/github.com/pierrec/lz4/v4/internal/lz4block/decode_other.go b/vendor/github.com/pierrec/lz4/v4/internal/lz4block/decode_other.go new file mode 100644 index 000000000..9f568fbb1 --- /dev/null +++ b/vendor/github.com/pierrec/lz4/v4/internal/lz4block/decode_other.go @@ -0,0 +1,139 @@ +//go:build (!amd64 && !arm && !arm64) || appengine || !gc || noasm +// +build !amd64,!arm,!arm64 appengine !gc noasm + +package lz4block + +import ( + "encoding/binary" +) + +func decodeBlock(dst, src, dict []byte) (ret int) { + // Restrict capacities so we don't read or write out of bounds. + dst = dst[:len(dst):len(dst)] + src = src[:len(src):len(src)] + + const hasError = -2 + + if len(src) == 0 { + return hasError + } + + defer func() { + if recover() != nil { + ret = hasError + } + }() + + var si, di uint + for si < uint(len(src)) { + // Literals and match lengths (token). + b := uint(src[si]) + si++ + + // Literals. + if lLen := b >> 4; lLen > 0 { + switch { + case lLen < 0xF && si+16 < uint(len(src)): + // Shortcut 1 + // if we have enough room in src and dst, and the literals length + // is small enough (0..14) then copy all 16 bytes, even if not all + // are part of the literals. + copy(dst[di:], src[si:si+16]) + si += lLen + di += lLen + if mLen := b & 0xF; mLen < 0xF { + // Shortcut 2 + // if the match length (4..18) fits within the literals, then copy + // all 18 bytes, even if not all are part of the literals. + mLen += 4 + if offset := u16(src[si:]); mLen <= offset && offset < di { + i := di - offset + // The remaining buffer may not hold 18 bytes. + // See https://github.com/pierrec/lz4/issues/51. + if end := i + 18; end <= uint(len(dst)) { + copy(dst[di:], dst[i:end]) + si += 2 + di += mLen + continue + } + } + } + case lLen == 0xF: + for { + x := uint(src[si]) + if lLen += x; int(lLen) < 0 { + return hasError + } + si++ + if x != 0xFF { + break + } + } + fallthrough + default: + copy(dst[di:di+lLen], src[si:si+lLen]) + si += lLen + di += lLen + } + } + + mLen := b & 0xF + if si == uint(len(src)) && mLen == 0 { + break + } else if si >= uint(len(src)) { + return hasError + } + + offset := u16(src[si:]) + if offset == 0 { + return hasError + } + si += 2 + + // Match. + mLen += minMatch + if mLen == minMatch+0xF { + for { + x := uint(src[si]) + if mLen += x; int(mLen) < 0 { + return hasError + } + si++ + if x != 0xFF { + break + } + } + } + + // Copy the match. + if di < offset { + // The match is beyond our block, meaning the first part + // is in the dictionary. + fromDict := dict[uint(len(dict))+di-offset:] + n := uint(copy(dst[di:di+mLen], fromDict)) + di += n + if mLen -= n; mLen == 0 { + continue + } + // We copied n = offset-di bytes from the dictionary, + // then set di = di+n = offset, so the following code + // copies from dst[di-offset:] = dst[0:]. + } + + expanded := dst[di-offset:] + if mLen > offset { + // Efficiently copy the match dst[di-offset:di] into the dst slice. + bytesToCopy := offset * (mLen / offset) + for n := offset; n <= bytesToCopy+offset; n *= 2 { + copy(expanded[n:], expanded[:n]) + } + di += bytesToCopy + mLen -= bytesToCopy + } + di += uint(copy(dst[di:di+mLen], expanded[:mLen])) + } + + return int(di) +} + +func u16(p []byte) uint { return uint(binary.LittleEndian.Uint16(p)) } diff --git a/vendor/github.com/pierrec/lz4/v4/internal/lz4errors/errors.go b/vendor/github.com/pierrec/lz4/v4/internal/lz4errors/errors.go new file mode 100644 index 000000000..710ea4281 --- /dev/null +++ b/vendor/github.com/pierrec/lz4/v4/internal/lz4errors/errors.go @@ -0,0 +1,19 @@ +package lz4errors + +type Error string + +func (e Error) Error() string { return string(e) } + +const ( + ErrInvalidSourceShortBuffer Error = "lz4: invalid source or destination buffer too short" + ErrInvalidFrame Error = "lz4: bad magic number" + ErrInternalUnhandledState Error = "lz4: unhandled state" + ErrInvalidHeaderChecksum Error = "lz4: invalid header checksum" + ErrInvalidBlockChecksum Error = "lz4: invalid block checksum" + ErrInvalidFrameChecksum Error = "lz4: invalid frame checksum" + ErrOptionInvalidCompressionLevel Error = "lz4: invalid compression level" + ErrOptionClosedOrError Error = "lz4: cannot apply options on closed or in error object" + ErrOptionInvalidBlockSize Error = "lz4: invalid block size" + ErrOptionNotApplicable Error = "lz4: option not applicable" + ErrWriterNotClosed Error = "lz4: writer not closed" +) diff --git a/vendor/github.com/pierrec/lz4/v4/internal/lz4stream/block.go b/vendor/github.com/pierrec/lz4/v4/internal/lz4stream/block.go new file mode 100644 index 000000000..04aaca848 --- /dev/null +++ b/vendor/github.com/pierrec/lz4/v4/internal/lz4stream/block.go @@ -0,0 +1,348 @@ +package lz4stream + +import ( + "encoding/binary" + "fmt" + "io" + "sync" + + "github.com/pierrec/lz4/v4/internal/lz4block" + "github.com/pierrec/lz4/v4/internal/lz4errors" + "github.com/pierrec/lz4/v4/internal/xxh32" +) + +type Blocks struct { + Block *FrameDataBlock + Blocks chan chan *FrameDataBlock + mu sync.Mutex + err error +} + +func (b *Blocks) initW(f *Frame, dst io.Writer, num int) { + if num == 1 { + b.Blocks = nil + b.Block = NewFrameDataBlock(f) + return + } + b.Block = nil + if cap(b.Blocks) != num { + b.Blocks = make(chan chan *FrameDataBlock, num) + } + // goroutine managing concurrent block compression goroutines. + go func() { + // Process next block compression item. + for c := range b.Blocks { + // Read the next compressed block result. + // Waiting here ensures that the blocks are output in the order they were sent. + // The incoming channel is always closed as it indicates to the caller that + // the block has been processed. + block := <-c + if block == nil { + // Notify the block compression routine that we are done with its result. + // This is used when a sentinel block is sent to terminate the compression. + close(c) + return + } + // Do not attempt to write the block upon any previous failure. + if b.err == nil { + // Write the block. + if err := block.Write(f, dst); err != nil { + // Keep the first error. + b.err = err + // All pending compression goroutines need to shut down, so we need to keep going. + } + } + close(c) + } + }() +} + +func (b *Blocks) close(f *Frame, num int) error { + if num == 1 { + if b.Block != nil { + b.Block.Close(f) + } + err := b.err + b.err = nil + return err + } + if b.Blocks == nil { + err := b.err + b.err = nil + return err + } + c := make(chan *FrameDataBlock) + b.Blocks <- c + c <- nil + <-c + err := b.err + b.err = nil + return err +} + +// ErrorR returns any error set while uncompressing a stream. +func (b *Blocks) ErrorR() error { + b.mu.Lock() + defer b.mu.Unlock() + return b.err +} + +// initR returns a channel that streams the uncompressed blocks if in concurrent +// mode and no error. When the channel is closed, check for any error with b.ErrorR. +// +// If not in concurrent mode, the uncompressed block is b.Block and the returned error +// needs to be checked. +func (b *Blocks) initR(f *Frame, num int, src io.Reader) (chan []byte, error) { + size := f.Descriptor.Flags.BlockSizeIndex() + if num == 1 { + b.Blocks = nil + b.Block = NewFrameDataBlock(f) + return nil, nil + } + b.Block = nil + blocks := make(chan chan []byte, num) + // data receives the uncompressed blocks. + data := make(chan []byte) + // Read blocks from the source sequentially + // and uncompress them concurrently. + + // In legacy mode, accrue the uncompress sizes in cum. + var cum uint32 + go func() { + var cumx uint32 + var err error + for b.ErrorR() == nil { + block := NewFrameDataBlock(f) + cumx, err = block.Read(f, src, 0) + if err != nil { + block.Close(f) + break + } + // Recheck for an error as reading may be slow and uncompressing is expensive. + if b.ErrorR() != nil { + block.Close(f) + break + } + c := make(chan []byte) + blocks <- c + go func() { + defer block.Close(f) + data, err := block.Uncompress(f, size.Get(), nil, false) + if err != nil { + b.closeR(err) + // Close the block channel to indicate an error. + close(c) + } else { + c <- data + } + }() + } + // End the collection loop and the data channel. + c := make(chan []byte) + blocks <- c + c <- nil // signal the collection loop that we are done + <-c // wait for the collect loop to complete + if f.isLegacy() && cum == cumx { + err = io.EOF + } + b.closeR(err) + close(data) + }() + // Collect the uncompressed blocks and make them available + // on the returned channel. + go func(leg bool) { + defer close(blocks) + skipBlocks := false + for c := range blocks { + buf, ok := <-c + if !ok { + // A closed channel indicates an error. + // All remaining channels should be discarded. + skipBlocks = true + continue + } + if buf == nil { + // Signal to end the loop. + close(c) + return + } + if skipBlocks { + // A previous error has occurred, skipping remaining channels. + continue + } + // Perform checksum now as the blocks are received in order. + if f.Descriptor.Flags.ContentChecksum() { + _, _ = f.checksum.Write(buf) + } + if leg { + cum += uint32(len(buf)) + } + data <- buf + close(c) + } + }(f.isLegacy()) + return data, nil +} + +// closeR safely sets the error on b if not already set. +func (b *Blocks) closeR(err error) { + b.mu.Lock() + if b.err == nil { + b.err = err + } + b.mu.Unlock() +} + +func NewFrameDataBlock(f *Frame) *FrameDataBlock { + buf := f.Descriptor.Flags.BlockSizeIndex().Get() + return &FrameDataBlock{Data: buf, data: buf} +} + +type FrameDataBlock struct { + Size DataBlockSize + Data []byte // compressed or uncompressed data (.data or .src) + Checksum uint32 + data []byte // buffer for compressed data + src []byte // uncompressed data + err error // used in concurrent mode +} + +func (b *FrameDataBlock) Close(f *Frame) { + b.Size = 0 + b.Checksum = 0 + b.err = nil + if b.data != nil { + // Block was not already closed. + lz4block.Put(b.data) + b.Data = nil + b.data = nil + b.src = nil + } +} + +// Block compression errors are ignored since the buffer is sized appropriately. +func (b *FrameDataBlock) Compress(f *Frame, src []byte, level lz4block.CompressionLevel) *FrameDataBlock { + data := b.data + if f.isLegacy() { + data = data[:cap(data)] + } else { + data = data[:len(src)] // trigger the incompressible flag in CompressBlock + } + var n int + switch level { + case lz4block.Fast: + n, _ = lz4block.CompressBlock(src, data) + default: + n, _ = lz4block.CompressBlockHC(src, data, level) + } + if n == 0 { + b.Size.UncompressedSet(true) + b.Data = src + } else { + b.Size.UncompressedSet(false) + b.Data = data[:n] + } + b.Size.sizeSet(len(b.Data)) + b.src = src // keep track of the source for content checksum + + if f.Descriptor.Flags.BlockChecksum() { + b.Checksum = xxh32.ChecksumZero(b.Data) + } + return b +} + +func (b *FrameDataBlock) Write(f *Frame, dst io.Writer) error { + // Write is called in the same order as blocks are compressed, + // so content checksum must be done here. + if f.Descriptor.Flags.ContentChecksum() { + _, _ = f.checksum.Write(b.src) + } + buf := f.buf[:] + binary.LittleEndian.PutUint32(buf, uint32(b.Size)) + if _, err := dst.Write(buf[:4]); err != nil { + return err + } + + if _, err := dst.Write(b.Data); err != nil { + return err + } + + if b.Checksum == 0 { + return nil + } + binary.LittleEndian.PutUint32(buf, b.Checksum) + _, err := dst.Write(buf[:4]) + return err +} + +// Read updates b with the next block data, size and checksum if available. +func (b *FrameDataBlock) Read(f *Frame, src io.Reader, cum uint32) (uint32, error) { + x, err := f.readUint32(src) + if err != nil { + return 0, err + } + if f.isLegacy() { + switch x { + case frameMagicLegacy: + // Concatenated legacy frame. + return b.Read(f, src, cum) + case cum: + // Only works in non concurrent mode, for concurrent mode + // it is handled separately. + // Linux kernel format appends the total uncompressed size at the end. + return 0, io.EOF + } + } else if x == 0 { + // Marker for end of stream. + return 0, io.EOF + } + b.Size = DataBlockSize(x) + + size := b.Size.size() + if size > cap(b.data) { + return x, lz4errors.ErrOptionInvalidBlockSize + } + b.data = b.data[:size] + if _, err := io.ReadFull(src, b.data); err != nil { + return x, err + } + if f.Descriptor.Flags.BlockChecksum() { + sum, err := f.readUint32(src) + if err != nil { + return 0, err + } + b.Checksum = sum + } + return x, nil +} + +func (b *FrameDataBlock) Uncompress(f *Frame, dst, dict []byte, sum bool) ([]byte, error) { + if b.Size.Uncompressed() { + n := copy(dst, b.data) + dst = dst[:n] + } else { + n, err := lz4block.UncompressBlock(b.data, dst, dict) + if err != nil { + return nil, err + } + dst = dst[:n] + } + if f.Descriptor.Flags.BlockChecksum() { + if c := xxh32.ChecksumZero(b.data); c != b.Checksum { + err := fmt.Errorf("%w: got %x; expected %x", lz4errors.ErrInvalidBlockChecksum, c, b.Checksum) + return nil, err + } + } + if sum && f.Descriptor.Flags.ContentChecksum() { + _, _ = f.checksum.Write(dst) + } + return dst, nil +} + +func (f *Frame) readUint32(r io.Reader) (x uint32, err error) { + if _, err = io.ReadFull(r, f.buf[:4]); err != nil { + return + } + x = binary.LittleEndian.Uint32(f.buf[:4]) + return +} diff --git a/vendor/github.com/pierrec/lz4/v4/internal/lz4stream/frame.go b/vendor/github.com/pierrec/lz4/v4/internal/lz4stream/frame.go new file mode 100644 index 000000000..18192a943 --- /dev/null +++ b/vendor/github.com/pierrec/lz4/v4/internal/lz4stream/frame.go @@ -0,0 +1,204 @@ +// Package lz4stream provides the types that support reading and writing LZ4 data streams. +package lz4stream + +import ( + "encoding/binary" + "fmt" + "io" + "io/ioutil" + + "github.com/pierrec/lz4/v4/internal/lz4block" + "github.com/pierrec/lz4/v4/internal/lz4errors" + "github.com/pierrec/lz4/v4/internal/xxh32" +) + +//go:generate go run gen.go + +const ( + frameMagic uint32 = 0x184D2204 + frameSkipMagic uint32 = 0x184D2A50 + frameMagicLegacy uint32 = 0x184C2102 +) + +func NewFrame() *Frame { + return &Frame{} +} + +type Frame struct { + buf [15]byte // frame descriptor needs at most 4(magic)+4+8+1=11 bytes + Magic uint32 + Descriptor FrameDescriptor + Blocks Blocks + Checksum uint32 + checksum xxh32.XXHZero +} + +// Reset allows reusing the Frame. +// The Descriptor configuration is not modified. +func (f *Frame) Reset(num int) { + f.Magic = 0 + f.Descriptor.Checksum = 0 + f.Descriptor.ContentSize = 0 + _ = f.Blocks.close(f, num) + f.Checksum = 0 +} + +func (f *Frame) InitW(dst io.Writer, num int, legacy bool) { + if legacy { + f.Magic = frameMagicLegacy + idx := lz4block.Index(lz4block.Block8Mb) + f.Descriptor.Flags.BlockSizeIndexSet(idx) + } else { + f.Magic = frameMagic + f.Descriptor.initW() + } + f.Blocks.initW(f, dst, num) + f.checksum.Reset() +} + +func (f *Frame) CloseW(dst io.Writer, num int) error { + if err := f.Blocks.close(f, num); err != nil { + return err + } + if f.isLegacy() { + return nil + } + buf := f.buf[:0] + // End mark (data block size of uint32(0)). + buf = append(buf, 0, 0, 0, 0) + if f.Descriptor.Flags.ContentChecksum() { + buf = f.checksum.Sum(buf) + } + _, err := dst.Write(buf) + return err +} + +func (f *Frame) isLegacy() bool { + return f.Magic == frameMagicLegacy +} + +func (f *Frame) ParseHeaders(src io.Reader) error { + if f.Magic > 0 { + // Header already read. + return nil + } + +newFrame: + var err error + if f.Magic, err = f.readUint32(src); err != nil { + return err + } + switch m := f.Magic; { + case m == frameMagic || m == frameMagicLegacy: + // All 16 values of frameSkipMagic are valid. + case m>>8 == frameSkipMagic>>8: + skip, err := f.readUint32(src) + if err != nil { + return err + } + if _, err := io.CopyN(ioutil.Discard, src, int64(skip)); err != nil { + return err + } + goto newFrame + default: + return lz4errors.ErrInvalidFrame + } + if err := f.Descriptor.initR(f, src); err != nil { + return err + } + f.checksum.Reset() + return nil +} + +func (f *Frame) InitR(src io.Reader, num int) (chan []byte, error) { + return f.Blocks.initR(f, num, src) +} + +func (f *Frame) CloseR(src io.Reader) (err error) { + if f.isLegacy() { + return nil + } + if !f.Descriptor.Flags.ContentChecksum() { + return nil + } + if f.Checksum, err = f.readUint32(src); err != nil { + return err + } + if c := f.checksum.Sum32(); c != f.Checksum { + return fmt.Errorf("%w: got %x; expected %x", lz4errors.ErrInvalidFrameChecksum, c, f.Checksum) + } + return nil +} + +type FrameDescriptor struct { + Flags DescriptorFlags + ContentSize uint64 + Checksum uint8 +} + +func (fd *FrameDescriptor) initW() { + fd.Flags.VersionSet(1) + fd.Flags.BlockIndependenceSet(true) +} + +func (fd *FrameDescriptor) Write(f *Frame, dst io.Writer) error { + if fd.Checksum > 0 { + // Header already written. + return nil + } + + buf := f.buf[:4] + // Write the magic number here even though it belongs to the Frame. + binary.LittleEndian.PutUint32(buf, f.Magic) + if !f.isLegacy() { + buf = buf[:4+2] + binary.LittleEndian.PutUint16(buf[4:], uint16(fd.Flags)) + + if fd.Flags.Size() { + buf = buf[:4+2+8] + binary.LittleEndian.PutUint64(buf[4+2:], fd.ContentSize) + } + fd.Checksum = descriptorChecksum(buf[4:]) + buf = append(buf, fd.Checksum) + } + + _, err := dst.Write(buf) + return err +} + +func (fd *FrameDescriptor) initR(f *Frame, src io.Reader) error { + if f.isLegacy() { + idx := lz4block.Index(lz4block.Block8Mb) + f.Descriptor.Flags.BlockSizeIndexSet(idx) + return nil + } + // Read the flags and the checksum, hoping that there is not content size. + buf := f.buf[:3] + if _, err := io.ReadFull(src, buf); err != nil { + return err + } + descr := binary.LittleEndian.Uint16(buf) + fd.Flags = DescriptorFlags(descr) + if fd.Flags.Size() { + // Append the 8 missing bytes. + buf = buf[:3+8] + if _, err := io.ReadFull(src, buf[3:]); err != nil { + return err + } + fd.ContentSize = binary.LittleEndian.Uint64(buf[2:]) + } + fd.Checksum = buf[len(buf)-1] // the checksum is the last byte + buf = buf[:len(buf)-1] // all descriptor fields except checksum + if c := descriptorChecksum(buf); fd.Checksum != c { + return fmt.Errorf("%w: got %x; expected %x", lz4errors.ErrInvalidHeaderChecksum, c, fd.Checksum) + } + // Validate the elements that can be. + if idx := fd.Flags.BlockSizeIndex(); !idx.IsValid() { + return lz4errors.ErrOptionInvalidBlockSize + } + return nil +} + +func descriptorChecksum(buf []byte) byte { + return byte(xxh32.ChecksumZero(buf) >> 8) +} diff --git a/vendor/github.com/pierrec/lz4/v4/internal/lz4stream/frame_gen.go b/vendor/github.com/pierrec/lz4/v4/internal/lz4stream/frame_gen.go new file mode 100644 index 000000000..d33a6be95 --- /dev/null +++ b/vendor/github.com/pierrec/lz4/v4/internal/lz4stream/frame_gen.go @@ -0,0 +1,103 @@ +// Code generated by `gen.exe`. DO NOT EDIT. + +package lz4stream + +import "github.com/pierrec/lz4/v4/internal/lz4block" + +// DescriptorFlags is defined as follow: +// field bits +// ----- ---- +// _ 2 +// ContentChecksum 1 +// Size 1 +// BlockChecksum 1 +// BlockIndependence 1 +// Version 2 +// _ 4 +// BlockSizeIndex 3 +// _ 1 +type DescriptorFlags uint16 + +// Getters. +func (x DescriptorFlags) ContentChecksum() bool { return x>>2&1 != 0 } +func (x DescriptorFlags) Size() bool { return x>>3&1 != 0 } +func (x DescriptorFlags) BlockChecksum() bool { return x>>4&1 != 0 } +func (x DescriptorFlags) BlockIndependence() bool { return x>>5&1 != 0 } +func (x DescriptorFlags) Version() uint16 { return uint16(x >> 6 & 0x3) } +func (x DescriptorFlags) BlockSizeIndex() lz4block.BlockSizeIndex { + return lz4block.BlockSizeIndex(x >> 12 & 0x7) +} + +// Setters. +func (x *DescriptorFlags) ContentChecksumSet(v bool) *DescriptorFlags { + const b = 1 << 2 + if v { + *x = *x&^b | b + } else { + *x &^= b + } + return x +} +func (x *DescriptorFlags) SizeSet(v bool) *DescriptorFlags { + const b = 1 << 3 + if v { + *x = *x&^b | b + } else { + *x &^= b + } + return x +} +func (x *DescriptorFlags) BlockChecksumSet(v bool) *DescriptorFlags { + const b = 1 << 4 + if v { + *x = *x&^b | b + } else { + *x &^= b + } + return x +} +func (x *DescriptorFlags) BlockIndependenceSet(v bool) *DescriptorFlags { + const b = 1 << 5 + if v { + *x = *x&^b | b + } else { + *x &^= b + } + return x +} +func (x *DescriptorFlags) VersionSet(v uint16) *DescriptorFlags { + *x = *x&^(0x3<<6) | (DescriptorFlags(v) & 0x3 << 6) + return x +} +func (x *DescriptorFlags) BlockSizeIndexSet(v lz4block.BlockSizeIndex) *DescriptorFlags { + *x = *x&^(0x7<<12) | (DescriptorFlags(v) & 0x7 << 12) + return x +} + +// Code generated by `gen.exe`. DO NOT EDIT. + +// DataBlockSize is defined as follow: +// field bits +// ----- ---- +// size 31 +// Uncompressed 1 +type DataBlockSize uint32 + +// Getters. +func (x DataBlockSize) size() int { return int(x & 0x7FFFFFFF) } +func (x DataBlockSize) Uncompressed() bool { return x>>31&1 != 0 } + +// Setters. +func (x *DataBlockSize) sizeSet(v int) *DataBlockSize { + *x = *x&^0x7FFFFFFF | DataBlockSize(v)&0x7FFFFFFF + return x +} +func (x *DataBlockSize) UncompressedSet(v bool) *DataBlockSize { + const b = 1 << 31 + if v { + *x = *x&^b | b + } else { + *x &^= b + } + return x +} diff --git a/vendor/github.com/pierrec/lz4/v4/internal/xxh32/xxh32zero.go b/vendor/github.com/pierrec/lz4/v4/internal/xxh32/xxh32zero.go new file mode 100644 index 000000000..651d10c10 --- /dev/null +++ b/vendor/github.com/pierrec/lz4/v4/internal/xxh32/xxh32zero.go @@ -0,0 +1,212 @@ +// Package xxh32 implements the very fast XXH hashing algorithm (32 bits version). +// (ported from the reference implementation https://github.com/Cyan4973/xxHash/) +package xxh32 + +import ( + "encoding/binary" +) + +const ( + prime1 uint32 = 2654435761 + prime2 uint32 = 2246822519 + prime3 uint32 = 3266489917 + prime4 uint32 = 668265263 + prime5 uint32 = 374761393 + + primeMask = 0xFFFFFFFF + prime1plus2 = uint32((uint64(prime1) + uint64(prime2)) & primeMask) // 606290984 + prime1minus = uint32((-int64(prime1)) & primeMask) // 1640531535 +) + +// XXHZero represents an xxhash32 object with seed 0. +type XXHZero struct { + v [4]uint32 + totalLen uint64 + buf [16]byte + bufused int +} + +// Sum appends the current hash to b and returns the resulting slice. +// It does not change the underlying hash state. +func (xxh XXHZero) Sum(b []byte) []byte { + h32 := xxh.Sum32() + return append(b, byte(h32), byte(h32>>8), byte(h32>>16), byte(h32>>24)) +} + +// Reset resets the Hash to its initial state. +func (xxh *XXHZero) Reset() { + xxh.v[0] = prime1plus2 + xxh.v[1] = prime2 + xxh.v[2] = 0 + xxh.v[3] = prime1minus + xxh.totalLen = 0 + xxh.bufused = 0 +} + +// Size returns the number of bytes returned by Sum(). +func (xxh *XXHZero) Size() int { + return 4 +} + +// BlockSizeIndex gives the minimum number of bytes accepted by Write(). +func (xxh *XXHZero) BlockSize() int { + return 1 +} + +// Write adds input bytes to the Hash. +// It never returns an error. +func (xxh *XXHZero) Write(input []byte) (int, error) { + if xxh.totalLen == 0 { + xxh.Reset() + } + n := len(input) + m := xxh.bufused + + xxh.totalLen += uint64(n) + + r := len(xxh.buf) - m + if n < r { + copy(xxh.buf[m:], input) + xxh.bufused += len(input) + return n, nil + } + + var buf *[16]byte + if m != 0 { + // some data left from previous update + buf = &xxh.buf + c := copy(buf[m:], input) + n -= c + input = input[c:] + } + update(&xxh.v, buf, input) + xxh.bufused = copy(xxh.buf[:], input[n-n%16:]) + + return n, nil +} + +// Portable version of update. This updates v by processing all of buf +// (if not nil) and all full 16-byte blocks of input. +func updateGo(v *[4]uint32, buf *[16]byte, input []byte) { + // Causes compiler to work directly from registers instead of stack: + v1, v2, v3, v4 := v[0], v[1], v[2], v[3] + + if buf != nil { + v1 = rol13(v1+binary.LittleEndian.Uint32(buf[:])*prime2) * prime1 + v2 = rol13(v2+binary.LittleEndian.Uint32(buf[4:])*prime2) * prime1 + v3 = rol13(v3+binary.LittleEndian.Uint32(buf[8:])*prime2) * prime1 + v4 = rol13(v4+binary.LittleEndian.Uint32(buf[12:])*prime2) * prime1 + } + + for ; len(input) >= 16; input = input[16:] { + sub := input[:16] //BCE hint for compiler + v1 = rol13(v1+binary.LittleEndian.Uint32(sub[:])*prime2) * prime1 + v2 = rol13(v2+binary.LittleEndian.Uint32(sub[4:])*prime2) * prime1 + v3 = rol13(v3+binary.LittleEndian.Uint32(sub[8:])*prime2) * prime1 + v4 = rol13(v4+binary.LittleEndian.Uint32(sub[12:])*prime2) * prime1 + } + v[0], v[1], v[2], v[3] = v1, v2, v3, v4 +} + +// Sum32 returns the 32 bits Hash value. +func (xxh *XXHZero) Sum32() uint32 { + h32 := uint32(xxh.totalLen) + if h32 >= 16 { + h32 += rol1(xxh.v[0]) + rol7(xxh.v[1]) + rol12(xxh.v[2]) + rol18(xxh.v[3]) + } else { + h32 += prime5 + } + + p := 0 + n := xxh.bufused + buf := xxh.buf + for n := n - 4; p <= n; p += 4 { + h32 += binary.LittleEndian.Uint32(buf[p:p+4]) * prime3 + h32 = rol17(h32) * prime4 + } + for ; p < n; p++ { + h32 += uint32(buf[p]) * prime5 + h32 = rol11(h32) * prime1 + } + + h32 ^= h32 >> 15 + h32 *= prime2 + h32 ^= h32 >> 13 + h32 *= prime3 + h32 ^= h32 >> 16 + + return h32 +} + +// Portable version of ChecksumZero. +func checksumZeroGo(input []byte) uint32 { + n := len(input) + h32 := uint32(n) + + if n < 16 { + h32 += prime5 + } else { + v1 := prime1plus2 + v2 := prime2 + v3 := uint32(0) + v4 := prime1minus + p := 0 + for n := n - 16; p <= n; p += 16 { + sub := input[p:][:16] //BCE hint for compiler + v1 = rol13(v1+binary.LittleEndian.Uint32(sub[:])*prime2) * prime1 + v2 = rol13(v2+binary.LittleEndian.Uint32(sub[4:])*prime2) * prime1 + v3 = rol13(v3+binary.LittleEndian.Uint32(sub[8:])*prime2) * prime1 + v4 = rol13(v4+binary.LittleEndian.Uint32(sub[12:])*prime2) * prime1 + } + input = input[p:] + n -= p + h32 += rol1(v1) + rol7(v2) + rol12(v3) + rol18(v4) + } + + p := 0 + for n := n - 4; p <= n; p += 4 { + h32 += binary.LittleEndian.Uint32(input[p:p+4]) * prime3 + h32 = rol17(h32) * prime4 + } + for p < n { + h32 += uint32(input[p]) * prime5 + h32 = rol11(h32) * prime1 + p++ + } + + h32 ^= h32 >> 15 + h32 *= prime2 + h32 ^= h32 >> 13 + h32 *= prime3 + h32 ^= h32 >> 16 + + return h32 +} + +func rol1(u uint32) uint32 { + return u<<1 | u>>31 +} + +func rol7(u uint32) uint32 { + return u<<7 | u>>25 +} + +func rol11(u uint32) uint32 { + return u<<11 | u>>21 +} + +func rol12(u uint32) uint32 { + return u<<12 | u>>20 +} + +func rol13(u uint32) uint32 { + return u<<13 | u>>19 +} + +func rol17(u uint32) uint32 { + return u<<17 | u>>15 +} + +func rol18(u uint32) uint32 { + return u<<18 | u>>14 +} diff --git a/vendor/github.com/pierrec/lz4/v4/internal/xxh32/xxh32zero_arm.go b/vendor/github.com/pierrec/lz4/v4/internal/xxh32/xxh32zero_arm.go new file mode 100644 index 000000000..0978b2665 --- /dev/null +++ b/vendor/github.com/pierrec/lz4/v4/internal/xxh32/xxh32zero_arm.go @@ -0,0 +1,11 @@ +// +build !noasm + +package xxh32 + +// ChecksumZero returns the 32-bit hash of input. +// +//go:noescape +func ChecksumZero(input []byte) uint32 + +//go:noescape +func update(v *[4]uint32, buf *[16]byte, input []byte) diff --git a/vendor/github.com/pierrec/lz4/v4/internal/xxh32/xxh32zero_arm.s b/vendor/github.com/pierrec/lz4/v4/internal/xxh32/xxh32zero_arm.s new file mode 100644 index 000000000..c18ffd574 --- /dev/null +++ b/vendor/github.com/pierrec/lz4/v4/internal/xxh32/xxh32zero_arm.s @@ -0,0 +1,251 @@ +// +build !noasm + +#include "go_asm.h" +#include "textflag.h" + +// Register allocation. +#define p R0 +#define n R1 +#define h R2 +#define v1 R2 // Alias for h. +#define v2 R3 +#define v3 R4 +#define v4 R5 +#define x1 R6 +#define x2 R7 +#define x3 R8 +#define x4 R9 + +// We need the primes in registers. The 16-byte loop only uses prime{1,2}. +#define prime1r R11 +#define prime2r R12 +#define prime3r R3 // The rest can alias v{2-4}. +#define prime4r R4 +#define prime5r R5 + +// Update round macros. These read from and increment p. + +#define round16aligned \ + MOVM.IA.W (p), [x1, x2, x3, x4] \ + \ + MULA x1, prime2r, v1, v1 \ + MULA x2, prime2r, v2, v2 \ + MULA x3, prime2r, v3, v3 \ + MULA x4, prime2r, v4, v4 \ + \ + MOVW v1 @> 19, v1 \ + MOVW v2 @> 19, v2 \ + MOVW v3 @> 19, v3 \ + MOVW v4 @> 19, v4 \ + \ + MUL prime1r, v1 \ + MUL prime1r, v2 \ + MUL prime1r, v3 \ + MUL prime1r, v4 \ + +#define round16unaligned \ + MOVBU.P 16(p), x1 \ + MOVBU -15(p), x2 \ + ORR x2 << 8, x1 \ + MOVBU -14(p), x3 \ + MOVBU -13(p), x4 \ + ORR x4 << 8, x3 \ + ORR x3 << 16, x1 \ + \ + MULA x1, prime2r, v1, v1 \ + MOVW v1 @> 19, v1 \ + MUL prime1r, v1 \ + \ + MOVBU -12(p), x1 \ + MOVBU -11(p), x2 \ + ORR x2 << 8, x1 \ + MOVBU -10(p), x3 \ + MOVBU -9(p), x4 \ + ORR x4 << 8, x3 \ + ORR x3 << 16, x1 \ + \ + MULA x1, prime2r, v2, v2 \ + MOVW v2 @> 19, v2 \ + MUL prime1r, v2 \ + \ + MOVBU -8(p), x1 \ + MOVBU -7(p), x2 \ + ORR x2 << 8, x1 \ + MOVBU -6(p), x3 \ + MOVBU -5(p), x4 \ + ORR x4 << 8, x3 \ + ORR x3 << 16, x1 \ + \ + MULA x1, prime2r, v3, v3 \ + MOVW v3 @> 19, v3 \ + MUL prime1r, v3 \ + \ + MOVBU -4(p), x1 \ + MOVBU -3(p), x2 \ + ORR x2 << 8, x1 \ + MOVBU -2(p), x3 \ + MOVBU -1(p), x4 \ + ORR x4 << 8, x3 \ + ORR x3 << 16, x1 \ + \ + MULA x1, prime2r, v4, v4 \ + MOVW v4 @> 19, v4 \ + MUL prime1r, v4 \ + + +// func ChecksumZero([]byte) uint32 +TEXT ·ChecksumZero(SB), NOFRAME|NOSPLIT, $-4-16 + MOVW input_base+0(FP), p + MOVW input_len+4(FP), n + + MOVW $const_prime1, prime1r + MOVW $const_prime2, prime2r + + // Set up h for n < 16. It's tempting to say {ADD prime5, n, h} + // here, but that's a pseudo-op that generates a load through R11. + MOVW $const_prime5, prime5r + ADD prime5r, n, h + CMP $0, n + BEQ end + + // We let n go negative so we can do comparisons with SUB.S + // instead of separate CMP. + SUB.S $16, n + BMI loop16done + + ADD prime1r, prime2r, v1 + MOVW prime2r, v2 + MOVW $0, v3 + RSB $0, prime1r, v4 + + TST $3, p + BNE loop16unaligned + +loop16aligned: + SUB.S $16, n + round16aligned + BPL loop16aligned + B loop16finish + +loop16unaligned: + SUB.S $16, n + round16unaligned + BPL loop16unaligned + +loop16finish: + MOVW v1 @> 31, h + ADD v2 @> 25, h + ADD v3 @> 20, h + ADD v4 @> 14, h + + // h += len(input) with v2 as temporary. + MOVW input_len+4(FP), v2 + ADD v2, h + +loop16done: + ADD $16, n // Restore number of bytes left. + + SUB.S $4, n + MOVW $const_prime3, prime3r + BMI loop4done + MOVW $const_prime4, prime4r + + TST $3, p + BNE loop4unaligned + +loop4aligned: + SUB.S $4, n + + MOVW.P 4(p), x1 + MULA prime3r, x1, h, h + MOVW h @> 15, h + MUL prime4r, h + + BPL loop4aligned + B loop4done + +loop4unaligned: + SUB.S $4, n + + MOVBU.P 4(p), x1 + MOVBU -3(p), x2 + ORR x2 << 8, x1 + MOVBU -2(p), x3 + ORR x3 << 16, x1 + MOVBU -1(p), x4 + ORR x4 << 24, x1 + + MULA prime3r, x1, h, h + MOVW h @> 15, h + MUL prime4r, h + + BPL loop4unaligned + +loop4done: + ADD.S $4, n // Restore number of bytes left. + BEQ end + + MOVW $const_prime5, prime5r + +loop1: + SUB.S $1, n + + MOVBU.P 1(p), x1 + MULA prime5r, x1, h, h + MOVW h @> 21, h + MUL prime1r, h + + BNE loop1 + +end: + MOVW $const_prime3, prime3r + EOR h >> 15, h + MUL prime2r, h + EOR h >> 13, h + MUL prime3r, h + EOR h >> 16, h + + MOVW h, ret+12(FP) + RET + + +// func update(v *[4]uint64, buf *[16]byte, p []byte) +TEXT ·update(SB), NOFRAME|NOSPLIT, $-4-20 + MOVW v+0(FP), p + MOVM.IA (p), [v1, v2, v3, v4] + + MOVW $const_prime1, prime1r + MOVW $const_prime2, prime2r + + // Process buf, if not nil. + MOVW buf+4(FP), p + CMP $0, p + BEQ noBuffered + + round16aligned + +noBuffered: + MOVW input_base +8(FP), p + MOVW input_len +12(FP), n + + SUB.S $16, n + BMI end + + TST $3, p + BNE loop16unaligned + +loop16aligned: + SUB.S $16, n + round16aligned + BPL loop16aligned + B end + +loop16unaligned: + SUB.S $16, n + round16unaligned + BPL loop16unaligned + +end: + MOVW v+0(FP), p + MOVM.IA [v1, v2, v3, v4], (p) + RET diff --git a/vendor/github.com/pierrec/lz4/v4/internal/xxh32/xxh32zero_other.go b/vendor/github.com/pierrec/lz4/v4/internal/xxh32/xxh32zero_other.go new file mode 100644 index 000000000..c96b59b8c --- /dev/null +++ b/vendor/github.com/pierrec/lz4/v4/internal/xxh32/xxh32zero_other.go @@ -0,0 +1,10 @@ +// +build !arm noasm + +package xxh32 + +// ChecksumZero returns the 32-bit hash of input. +func ChecksumZero(input []byte) uint32 { return checksumZeroGo(input) } + +func update(v *[4]uint32, buf *[16]byte, input []byte) { + updateGo(v, buf, input) +} diff --git a/vendor/github.com/pierrec/lz4/v4/lz4.go b/vendor/github.com/pierrec/lz4/v4/lz4.go new file mode 100644 index 000000000..a62022e08 --- /dev/null +++ b/vendor/github.com/pierrec/lz4/v4/lz4.go @@ -0,0 +1,157 @@ +// Package lz4 implements reading and writing lz4 compressed data. +// +// The package supports both the LZ4 stream format, +// as specified in http://fastcompression.blogspot.fr/2013/04/lz4-streaming-format-final.html, +// and the LZ4 block format, defined at +// http://fastcompression.blogspot.fr/2011/05/lz4-explained.html. +// +// See https://github.com/lz4/lz4 for the reference C implementation. +package lz4 + +import ( + "github.com/pierrec/lz4/v4/internal/lz4block" + "github.com/pierrec/lz4/v4/internal/lz4errors" +) + +func _() { + // Safety checks for duplicated elements. + var x [1]struct{} + _ = x[lz4block.CompressionLevel(Fast)-lz4block.Fast] + _ = x[Block64Kb-BlockSize(lz4block.Block64Kb)] + _ = x[Block256Kb-BlockSize(lz4block.Block256Kb)] + _ = x[Block1Mb-BlockSize(lz4block.Block1Mb)] + _ = x[Block4Mb-BlockSize(lz4block.Block4Mb)] +} + +// CompressBlockBound returns the maximum size of a given buffer of size n, when not compressible. +func CompressBlockBound(n int) int { + return lz4block.CompressBlockBound(n) +} + +// UncompressBlock uncompresses the source buffer into the destination one, +// and returns the uncompressed size. +// +// The destination buffer must be sized appropriately. +// +// An error is returned if the source data is invalid or the destination buffer is too small. +func UncompressBlock(src, dst []byte) (int, error) { + return lz4block.UncompressBlock(src, dst, nil) +} + +// UncompressBlockWithDict uncompresses the source buffer into the destination one using a +// dictionary, and returns the uncompressed size. +// +// The destination buffer must be sized appropriately. +// +// An error is returned if the source data is invalid or the destination buffer is too small. +func UncompressBlockWithDict(src, dst, dict []byte) (int, error) { + return lz4block.UncompressBlock(src, dst, dict) +} + +// A Compressor compresses data into the LZ4 block format. +// It uses a fast compression algorithm. +// +// A Compressor is not safe for concurrent use by multiple goroutines. +// +// Use a Writer to compress into the LZ4 stream format. +type Compressor struct{ c lz4block.Compressor } + +// CompressBlock compresses the source buffer src into the destination dst. +// +// If compression is successful, the first return value is the size of the +// compressed data, which is always >0. +// +// If dst has length at least CompressBlockBound(len(src)), compression always +// succeeds. Otherwise, the first return value is zero. The error return is +// non-nil if the compressed data does not fit in dst, but it might fit in a +// larger buffer that is still smaller than CompressBlockBound(len(src)). The +// return value (0, nil) means the data is likely incompressible and a buffer +// of length CompressBlockBound(len(src)) should be passed in. +func (c *Compressor) CompressBlock(src, dst []byte) (int, error) { + return c.c.CompressBlock(src, dst) +} + +// CompressBlock compresses the source buffer into the destination one. +// This is the fast version of LZ4 compression and also the default one. +// +// The argument hashTable is scratch space for a hash table used by the +// compressor. If provided, it should have length at least 1<<16. If it is +// shorter (or nil), CompressBlock allocates its own hash table. +// +// The size of the compressed data is returned. +// +// If the destination buffer size is lower than CompressBlockBound and +// the compressed size is 0 and no error, then the data is incompressible. +// +// An error is returned if the destination buffer is too small. + +// CompressBlock is equivalent to Compressor.CompressBlock. +// The final argument is ignored and should be set to nil. +// +// This function is deprecated. Use a Compressor instead. +func CompressBlock(src, dst []byte, _ []int) (int, error) { + return lz4block.CompressBlock(src, dst) +} + +// A CompressorHC compresses data into the LZ4 block format. +// Its compression ratio is potentially better than that of a Compressor, +// but it is also slower and requires more memory. +// +// A Compressor is not safe for concurrent use by multiple goroutines. +// +// Use a Writer to compress into the LZ4 stream format. +type CompressorHC struct { + // Level is the maximum search depth for compression. + // Values <= 0 mean no maximum. + Level CompressionLevel + c lz4block.CompressorHC +} + +// CompressBlock compresses the source buffer src into the destination dst. +// +// If compression is successful, the first return value is the size of the +// compressed data, which is always >0. +// +// If dst has length at least CompressBlockBound(len(src)), compression always +// succeeds. Otherwise, the first return value is zero. The error return is +// non-nil if the compressed data does not fit in dst, but it might fit in a +// larger buffer that is still smaller than CompressBlockBound(len(src)). The +// return value (0, nil) means the data is likely incompressible and a buffer +// of length CompressBlockBound(len(src)) should be passed in. +func (c *CompressorHC) CompressBlock(src, dst []byte) (int, error) { + return c.c.CompressBlock(src, dst, lz4block.CompressionLevel(c.Level)) +} + +// CompressBlockHC is equivalent to CompressorHC.CompressBlock. +// The final two arguments are ignored and should be set to nil. +// +// This function is deprecated. Use a CompressorHC instead. +func CompressBlockHC(src, dst []byte, depth CompressionLevel, _, _ []int) (int, error) { + return lz4block.CompressBlockHC(src, dst, lz4block.CompressionLevel(depth)) +} + +const ( + // ErrInvalidSourceShortBuffer is returned by UncompressBlock or CompressBLock when a compressed + // block is corrupted or the destination buffer is not large enough for the uncompressed data. + ErrInvalidSourceShortBuffer = lz4errors.ErrInvalidSourceShortBuffer + // ErrInvalidFrame is returned when reading an invalid LZ4 archive. + ErrInvalidFrame = lz4errors.ErrInvalidFrame + // ErrInternalUnhandledState is an internal error. + ErrInternalUnhandledState = lz4errors.ErrInternalUnhandledState + // ErrInvalidHeaderChecksum is returned when reading a frame. + ErrInvalidHeaderChecksum = lz4errors.ErrInvalidHeaderChecksum + // ErrInvalidBlockChecksum is returned when reading a frame. + ErrInvalidBlockChecksum = lz4errors.ErrInvalidBlockChecksum + // ErrInvalidFrameChecksum is returned when reading a frame. + ErrInvalidFrameChecksum = lz4errors.ErrInvalidFrameChecksum + // ErrOptionInvalidCompressionLevel is returned when the supplied compression level is invalid. + ErrOptionInvalidCompressionLevel = lz4errors.ErrOptionInvalidCompressionLevel + // ErrOptionClosedOrError is returned when an option is applied to a closed or in error object. + ErrOptionClosedOrError = lz4errors.ErrOptionClosedOrError + // ErrOptionInvalidBlockSize is returned when + ErrOptionInvalidBlockSize = lz4errors.ErrOptionInvalidBlockSize + // ErrOptionNotApplicable is returned when trying to apply an option to an object not supporting it. + ErrOptionNotApplicable = lz4errors.ErrOptionNotApplicable + // ErrWriterNotClosed is returned when attempting to reset an unclosed writer. + ErrWriterNotClosed = lz4errors.ErrWriterNotClosed +) diff --git a/vendor/github.com/pierrec/lz4/v4/options.go b/vendor/github.com/pierrec/lz4/v4/options.go new file mode 100644 index 000000000..57a44e767 --- /dev/null +++ b/vendor/github.com/pierrec/lz4/v4/options.go @@ -0,0 +1,242 @@ +package lz4 + +import ( + "fmt" + "reflect" + "runtime" + + "github.com/pierrec/lz4/v4/internal/lz4block" + "github.com/pierrec/lz4/v4/internal/lz4errors" +) + +//go:generate go run golang.org/x/tools/cmd/stringer -type=BlockSize,CompressionLevel -output options_gen.go + +type ( + applier interface { + Apply(...Option) error + private() + } + // Option defines the parameters to setup an LZ4 Writer or Reader. + Option func(applier) error +) + +// String returns a string representation of the option with its parameter(s). +func (o Option) String() string { + return o(nil).Error() +} + +// Default options. +var ( + DefaultBlockSizeOption = BlockSizeOption(Block4Mb) + DefaultChecksumOption = ChecksumOption(true) + DefaultConcurrency = ConcurrencyOption(1) + defaultOnBlockDone = OnBlockDoneOption(nil) +) + +const ( + Block64Kb BlockSize = 1 << (16 + iota*2) + Block256Kb + Block1Mb + Block4Mb +) + +// BlockSizeIndex defines the size of the blocks to be compressed. +type BlockSize uint32 + +// BlockSizeOption defines the maximum size of compressed blocks (default=Block4Mb). +func BlockSizeOption(size BlockSize) Option { + return func(a applier) error { + switch w := a.(type) { + case nil: + s := fmt.Sprintf("BlockSizeOption(%s)", size) + return lz4errors.Error(s) + case *Writer: + size := uint32(size) + if !lz4block.IsValid(size) { + return fmt.Errorf("%w: %d", lz4errors.ErrOptionInvalidBlockSize, size) + } + w.frame.Descriptor.Flags.BlockSizeIndexSet(lz4block.Index(size)) + return nil + case *CompressingReader: + size := uint32(size) + if !lz4block.IsValid(size) { + return fmt.Errorf("%w: %d", lz4errors.ErrOptionInvalidBlockSize, size) + } + w.frame.Descriptor.Flags.BlockSizeIndexSet(lz4block.Index(size)) + return nil + } + return lz4errors.ErrOptionNotApplicable + } +} + +// BlockChecksumOption enables or disables block checksum (default=false). +func BlockChecksumOption(flag bool) Option { + return func(a applier) error { + switch w := a.(type) { + case nil: + s := fmt.Sprintf("BlockChecksumOption(%v)", flag) + return lz4errors.Error(s) + case *Writer: + w.frame.Descriptor.Flags.BlockChecksumSet(flag) + return nil + case *CompressingReader: + w.frame.Descriptor.Flags.BlockChecksumSet(flag) + return nil + } + return lz4errors.ErrOptionNotApplicable + } +} + +// ChecksumOption enables/disables all blocks or content checksum (default=true). +func ChecksumOption(flag bool) Option { + return func(a applier) error { + switch w := a.(type) { + case nil: + s := fmt.Sprintf("ChecksumOption(%v)", flag) + return lz4errors.Error(s) + case *Writer: + w.frame.Descriptor.Flags.ContentChecksumSet(flag) + return nil + case *CompressingReader: + w.frame.Descriptor.Flags.ContentChecksumSet(flag) + return nil + } + return lz4errors.ErrOptionNotApplicable + } +} + +// SizeOption sets the size of the original uncompressed data (default=0). It is useful to know the size of the +// whole uncompressed data stream. +func SizeOption(size uint64) Option { + return func(a applier) error { + switch w := a.(type) { + case nil: + s := fmt.Sprintf("SizeOption(%d)", size) + return lz4errors.Error(s) + case *Writer: + w.frame.Descriptor.Flags.SizeSet(size > 0) + w.frame.Descriptor.ContentSize = size + return nil + case *CompressingReader: + w.frame.Descriptor.Flags.SizeSet(size > 0) + w.frame.Descriptor.ContentSize = size + return nil + } + return lz4errors.ErrOptionNotApplicable + } +} + +// ConcurrencyOption sets the number of go routines used for compression. +// If n <= 0, then the output of runtime.GOMAXPROCS(0) is used. +func ConcurrencyOption(n int) Option { + if n <= 0 { + n = runtime.GOMAXPROCS(0) + } + return func(a applier) error { + switch rw := a.(type) { + case nil: + s := fmt.Sprintf("ConcurrencyOption(%d)", n) + return lz4errors.Error(s) + case *Writer: + rw.num = n + return nil + case *Reader: + rw.num = n + return nil + } + return lz4errors.ErrOptionNotApplicable + } +} + +// CompressionLevel defines the level of compression to use. The higher the better, but slower, compression. +type CompressionLevel uint32 + +const ( + Fast CompressionLevel = 0 + Level1 CompressionLevel = 1 << (8 + iota) + Level2 + Level3 + Level4 + Level5 + Level6 + Level7 + Level8 + Level9 +) + +// CompressionLevelOption defines the compression level (default=Fast). +func CompressionLevelOption(level CompressionLevel) Option { + return func(a applier) error { + switch w := a.(type) { + case nil: + s := fmt.Sprintf("CompressionLevelOption(%s)", level) + return lz4errors.Error(s) + case *Writer: + switch level { + case Fast, Level1, Level2, Level3, Level4, Level5, Level6, Level7, Level8, Level9: + default: + return fmt.Errorf("%w: %d", lz4errors.ErrOptionInvalidCompressionLevel, level) + } + w.level = lz4block.CompressionLevel(level) + return nil + case *CompressingReader: + switch level { + case Fast, Level1, Level2, Level3, Level4, Level5, Level6, Level7, Level8, Level9: + default: + return fmt.Errorf("%w: %d", lz4errors.ErrOptionInvalidCompressionLevel, level) + } + w.level = lz4block.CompressionLevel(level) + return nil + } + return lz4errors.ErrOptionNotApplicable + } +} + +func onBlockDone(int) {} + +// OnBlockDoneOption is triggered when a block has been processed. For a Writer, it is when is has been compressed, +// for a Reader, it is when it has been uncompressed. +func OnBlockDoneOption(handler func(size int)) Option { + if handler == nil { + handler = onBlockDone + } + return func(a applier) error { + switch rw := a.(type) { + case nil: + s := fmt.Sprintf("OnBlockDoneOption(%s)", reflect.TypeOf(handler).String()) + return lz4errors.Error(s) + case *Writer: + rw.handler = handler + return nil + case *Reader: + rw.handler = handler + return nil + case *CompressingReader: + rw.handler = handler + return nil + } + return lz4errors.ErrOptionNotApplicable + } +} + +// LegacyOption provides support for writing LZ4 frames in the legacy format. +// +// See https://github.com/lz4/lz4/blob/dev/doc/lz4_Frame_format.md#legacy-frame. +// +// NB. compressed Linux kernel images use a tweaked LZ4 legacy format where +// the compressed stream is followed by the original (uncompressed) size of +// the kernel (https://events.static.linuxfound.org/sites/events/files/lcjpcojp13_klee.pdf). +// This is also supported as a special case. +func LegacyOption(legacy bool) Option { + return func(a applier) error { + switch rw := a.(type) { + case nil: + s := fmt.Sprintf("LegacyOption(%v)", legacy) + return lz4errors.Error(s) + case *Writer: + rw.legacy = legacy + return nil + } + return lz4errors.ErrOptionNotApplicable + } +} diff --git a/vendor/github.com/pierrec/lz4/v4/options_gen.go b/vendor/github.com/pierrec/lz4/v4/options_gen.go new file mode 100644 index 000000000..2de814909 --- /dev/null +++ b/vendor/github.com/pierrec/lz4/v4/options_gen.go @@ -0,0 +1,92 @@ +// Code generated by "stringer -type=BlockSize,CompressionLevel -output options_gen.go"; DO NOT EDIT. + +package lz4 + +import "strconv" + +func _() { + // An "invalid array index" compiler error signifies that the constant values have changed. + // Re-run the stringer command to generate them again. + var x [1]struct{} + _ = x[Block64Kb-65536] + _ = x[Block256Kb-262144] + _ = x[Block1Mb-1048576] + _ = x[Block4Mb-4194304] +} + +const ( + _BlockSize_name_0 = "Block64Kb" + _BlockSize_name_1 = "Block256Kb" + _BlockSize_name_2 = "Block1Mb" + _BlockSize_name_3 = "Block4Mb" +) + +func (i BlockSize) String() string { + switch { + case i == 65536: + return _BlockSize_name_0 + case i == 262144: + return _BlockSize_name_1 + case i == 1048576: + return _BlockSize_name_2 + case i == 4194304: + return _BlockSize_name_3 + default: + return "BlockSize(" + strconv.FormatInt(int64(i), 10) + ")" + } +} +func _() { + // An "invalid array index" compiler error signifies that the constant values have changed. + // Re-run the stringer command to generate them again. + var x [1]struct{} + _ = x[Fast-0] + _ = x[Level1-512] + _ = x[Level2-1024] + _ = x[Level3-2048] + _ = x[Level4-4096] + _ = x[Level5-8192] + _ = x[Level6-16384] + _ = x[Level7-32768] + _ = x[Level8-65536] + _ = x[Level9-131072] +} + +const ( + _CompressionLevel_name_0 = "Fast" + _CompressionLevel_name_1 = "Level1" + _CompressionLevel_name_2 = "Level2" + _CompressionLevel_name_3 = "Level3" + _CompressionLevel_name_4 = "Level4" + _CompressionLevel_name_5 = "Level5" + _CompressionLevel_name_6 = "Level6" + _CompressionLevel_name_7 = "Level7" + _CompressionLevel_name_8 = "Level8" + _CompressionLevel_name_9 = "Level9" +) + +func (i CompressionLevel) String() string { + switch { + case i == 0: + return _CompressionLevel_name_0 + case i == 512: + return _CompressionLevel_name_1 + case i == 1024: + return _CompressionLevel_name_2 + case i == 2048: + return _CompressionLevel_name_3 + case i == 4096: + return _CompressionLevel_name_4 + case i == 8192: + return _CompressionLevel_name_5 + case i == 16384: + return _CompressionLevel_name_6 + case i == 32768: + return _CompressionLevel_name_7 + case i == 65536: + return _CompressionLevel_name_8 + case i == 131072: + return _CompressionLevel_name_9 + default: + return "CompressionLevel(" + strconv.FormatInt(int64(i), 10) + ")" + } +} diff --git a/vendor/github.com/pierrec/lz4/v4/reader.go b/vendor/github.com/pierrec/lz4/v4/reader.go new file mode 100644 index 000000000..275daad7c --- /dev/null +++ b/vendor/github.com/pierrec/lz4/v4/reader.go @@ -0,0 +1,275 @@ +package lz4 + +import ( + "bytes" + "io" + + "github.com/pierrec/lz4/v4/internal/lz4block" + "github.com/pierrec/lz4/v4/internal/lz4errors" + "github.com/pierrec/lz4/v4/internal/lz4stream" +) + +var readerStates = []aState{ + noState: newState, + errorState: newState, + newState: readState, + readState: closedState, + closedState: newState, +} + +// NewReader returns a new LZ4 frame decoder. +func NewReader(r io.Reader) *Reader { + return newReader(r, false) +} + +func newReader(r io.Reader, legacy bool) *Reader { + zr := &Reader{frame: lz4stream.NewFrame()} + zr.state.init(readerStates) + _ = zr.Apply(DefaultConcurrency, defaultOnBlockDone) + zr.Reset(r) + return zr +} + +// Reader allows reading an LZ4 stream. +type Reader struct { + state _State + src io.Reader // source reader + num int // concurrency level + frame *lz4stream.Frame // frame being read + data []byte // block buffer allocated in non concurrent mode + reads chan []byte // pending data + idx int // size of pending data + handler func(int) + cum uint32 + dict []byte +} + +func (*Reader) private() {} + +func (r *Reader) Apply(options ...Option) (err error) { + defer r.state.check(&err) + switch r.state.state { + case newState: + case errorState: + return r.state.err + default: + return lz4errors.ErrOptionClosedOrError + } + for _, o := range options { + if err = o(r); err != nil { + return + } + } + return +} + +// Size returns the size of the underlying uncompressed data, if set in the stream. +func (r *Reader) Size() int { + switch r.state.state { + case readState, closedState: + if r.frame.Descriptor.Flags.Size() { + return int(r.frame.Descriptor.ContentSize) + } + } + return 0 +} + +func (r *Reader) isNotConcurrent() bool { + return r.num == 1 +} + +func (r *Reader) init() error { + err := r.frame.ParseHeaders(r.src) + if err != nil { + return err + } + if !r.frame.Descriptor.Flags.BlockIndependence() { + // We can't decompress dependent blocks concurrently. + // Instead of throwing an error to the user, silently drop concurrency + r.num = 1 + } + data, err := r.frame.InitR(r.src, r.num) + if err != nil { + return err + } + r.reads = data + r.idx = 0 + size := r.frame.Descriptor.Flags.BlockSizeIndex() + r.data = size.Get() + r.cum = 0 + return nil +} + +func (r *Reader) Read(buf []byte) (n int, err error) { + defer r.state.check(&err) + switch r.state.state { + case readState: + case closedState, errorState: + return 0, r.state.err + case newState: + // First initialization. + if err = r.init(); r.state.next(err) { + return + } + default: + return 0, r.state.fail() + } + for len(buf) > 0 { + var bn int + if r.idx == 0 { + if r.isNotConcurrent() { + bn, err = r.read(buf) + } else { + lz4block.Put(r.data) + r.data = <-r.reads + if len(r.data) == 0 { + // No uncompressed data: something went wrong or we are done. + err = r.frame.Blocks.ErrorR() + } + } + switch err { + case nil: + case io.EOF: + if er := r.frame.CloseR(r.src); er != nil { + err = er + } + lz4block.Put(r.data) + r.data = nil + return + default: + return + } + } + if bn == 0 { + // Fill buf with buffered data. + bn = copy(buf, r.data[r.idx:]) + r.idx += bn + if r.idx == len(r.data) { + // All data read, get ready for the next Read. + r.idx = 0 + } + } + buf = buf[bn:] + n += bn + r.handler(bn) + } + return +} + +// read uncompresses the next block as follow: +// - if buf has enough room, the block is uncompressed into it directly +// and the lenght of used space is returned +// - else, the uncompress data is stored in r.data and 0 is returned +func (r *Reader) read(buf []byte) (int, error) { + block := r.frame.Blocks.Block + _, err := block.Read(r.frame, r.src, r.cum) + if err != nil { + return 0, err + } + var direct bool + dst := r.data[:cap(r.data)] + if len(buf) >= len(dst) { + // Uncompress directly into buf. + direct = true + dst = buf + } + dst, err = block.Uncompress(r.frame, dst, r.dict, true) + if err != nil { + return 0, err + } + if !r.frame.Descriptor.Flags.BlockIndependence() { + if len(r.dict)+len(dst) > 128*1024 { + preserveSize := 64*1024 - len(dst) + if preserveSize < 0 { + preserveSize = 0 + } + r.dict = r.dict[len(r.dict)-preserveSize:] + } + r.dict = append(r.dict, dst...) + } + r.cum += uint32(len(dst)) + if direct { + return len(dst), nil + } + r.data = dst + return 0, nil +} + +// Reset clears the state of the Reader r such that it is equivalent to its +// initial state from NewReader, but instead reading from reader. +// No access to reader is performed. +func (r *Reader) Reset(reader io.Reader) { + if r.data != nil { + lz4block.Put(r.data) + r.data = nil + } + r.frame.Reset(r.num) + r.state.reset() + r.src = reader + r.reads = nil +} + +// WriteTo efficiently uncompresses the data from the Reader underlying source to w. +func (r *Reader) WriteTo(w io.Writer) (n int64, err error) { + switch r.state.state { + case closedState, errorState: + return 0, r.state.err + case newState: + if err = r.init(); r.state.next(err) { + return + } + default: + return 0, r.state.fail() + } + defer r.state.nextd(&err) + + var data []byte + if r.isNotConcurrent() { + size := r.frame.Descriptor.Flags.BlockSizeIndex() + data = size.Get() + defer lz4block.Put(data) + } + for { + var bn int + var dst []byte + if r.isNotConcurrent() { + bn, err = r.read(data) + dst = data[:bn] + } else { + lz4block.Put(dst) + dst = <-r.reads + bn = len(dst) + if bn == 0 { + // No uncompressed data: something went wrong or we are done. + err = r.frame.Blocks.ErrorR() + } + } + switch err { + case nil: + case io.EOF: + err = r.frame.CloseR(r.src) + return + default: + return + } + r.handler(bn) + bn, err = w.Write(dst) + n += int64(bn) + if err != nil { + return + } + } +} + +// ValidFrameHeader returns a bool indicating if the given bytes slice matches a LZ4 header. +func ValidFrameHeader(in []byte) (bool, error) { + f := lz4stream.NewFrame() + err := f.ParseHeaders(bytes.NewReader(in)) + if err == nil { + return true, nil + } + if err == lz4errors.ErrInvalidFrame { + return false, nil + } + return false, err +} diff --git a/vendor/github.com/pierrec/lz4/v4/state.go b/vendor/github.com/pierrec/lz4/v4/state.go new file mode 100644 index 000000000..d94f04d05 --- /dev/null +++ b/vendor/github.com/pierrec/lz4/v4/state.go @@ -0,0 +1,75 @@ +package lz4 + +import ( + "errors" + "fmt" + "io" + + "github.com/pierrec/lz4/v4/internal/lz4errors" +) + +//go:generate go run golang.org/x/tools/cmd/stringer -type=aState -output state_gen.go + +const ( + noState aState = iota // uninitialized reader + errorState // unrecoverable error encountered + newState // instantiated object + readState // reading data + writeState // writing data + closedState // all done +) + +type ( + aState uint8 + _State struct { + states []aState + state aState + err error + } +) + +func (s *_State) init(states []aState) { + s.states = states + s.state = states[0] +} + +func (s *_State) reset() { + s.state = s.states[0] + s.err = nil +} + +// next sets the state to the next one unless it is passed a non nil error. +// It returns whether or not it is in error. +func (s *_State) next(err error) bool { + if err != nil { + s.err = fmt.Errorf("%s: %w", s.state, err) + s.state = errorState + return true + } + s.state = s.states[s.state] + return false +} + +// nextd is like next but for defers. +func (s *_State) nextd(errp *error) bool { + return errp != nil && s.next(*errp) +} + +// check sets s in error if not already in error and if the error is not nil or io.EOF, +func (s *_State) check(errp *error) { + if s.state == errorState || errp == nil { + return + } + if err := *errp; err != nil { + s.err = fmt.Errorf("%w[%s]", err, s.state) + if !errors.Is(err, io.EOF) { + s.state = errorState + } + } +} + +func (s *_State) fail() error { + s.state = errorState + s.err = fmt.Errorf("%w[%s]", lz4errors.ErrInternalUnhandledState, s.state) + return s.err +} diff --git a/vendor/github.com/pierrec/lz4/v4/state_gen.go b/vendor/github.com/pierrec/lz4/v4/state_gen.go new file mode 100644 index 000000000..75fb82892 --- /dev/null +++ b/vendor/github.com/pierrec/lz4/v4/state_gen.go @@ -0,0 +1,28 @@ +// Code generated by "stringer -type=aState -output state_gen.go"; DO NOT EDIT. + +package lz4 + +import "strconv" + +func _() { + // An "invalid array index" compiler error signifies that the constant values have changed. + // Re-run the stringer command to generate them again. + var x [1]struct{} + _ = x[noState-0] + _ = x[errorState-1] + _ = x[newState-2] + _ = x[readState-3] + _ = x[writeState-4] + _ = x[closedState-5] +} + +const _aState_name = "noStateerrorStatenewStatereadStatewriteStateclosedState" + +var _aState_index = [...]uint8{0, 7, 17, 25, 34, 44, 55} + +func (i aState) String() string { + if i >= aState(len(_aState_index)-1) { + return "aState(" + strconv.FormatInt(int64(i), 10) + ")" + } + return _aState_name[_aState_index[i]:_aState_index[i+1]] +} diff --git a/vendor/github.com/pierrec/lz4/v4/writer.go b/vendor/github.com/pierrec/lz4/v4/writer.go new file mode 100644 index 000000000..4358adee1 --- /dev/null +++ b/vendor/github.com/pierrec/lz4/v4/writer.go @@ -0,0 +1,242 @@ +package lz4 + +import ( + "io" + + "github.com/pierrec/lz4/v4/internal/lz4block" + "github.com/pierrec/lz4/v4/internal/lz4errors" + "github.com/pierrec/lz4/v4/internal/lz4stream" +) + +var writerStates = []aState{ + noState: newState, + newState: writeState, + writeState: closedState, + closedState: newState, + errorState: newState, +} + +// NewWriter returns a new LZ4 frame encoder. +func NewWriter(w io.Writer) *Writer { + zw := &Writer{frame: lz4stream.NewFrame()} + zw.state.init(writerStates) + _ = zw.Apply(DefaultBlockSizeOption, DefaultChecksumOption, DefaultConcurrency, defaultOnBlockDone) + zw.Reset(w) + return zw +} + +// Writer allows writing an LZ4 stream. +type Writer struct { + state _State + src io.Writer // destination writer + level lz4block.CompressionLevel // how hard to try + num int // concurrency level + frame *lz4stream.Frame // frame being built + data []byte // pending data + idx int // size of pending data + handler func(int) + legacy bool +} + +func (*Writer) private() {} + +func (w *Writer) Apply(options ...Option) (err error) { + defer w.state.check(&err) + switch w.state.state { + case newState: + case errorState: + return w.state.err + default: + return lz4errors.ErrOptionClosedOrError + } + w.Reset(w.src) + for _, o := range options { + if err = o(w); err != nil { + return + } + } + return +} + +func (w *Writer) isNotConcurrent() bool { + return w.num == 1 +} + +// init sets up the Writer when in newState. It does not change the Writer state. +func (w *Writer) init() error { + w.frame.InitW(w.src, w.num, w.legacy) + size := w.frame.Descriptor.Flags.BlockSizeIndex() + w.data = size.Get() + w.idx = 0 + return w.frame.Descriptor.Write(w.frame, w.src) +} + +func (w *Writer) Write(buf []byte) (n int, err error) { + defer w.state.check(&err) + switch w.state.state { + case writeState: + case closedState, errorState: + return 0, w.state.err + case newState: + if err = w.init(); w.state.next(err) { + return + } + default: + return 0, w.state.fail() + } + + zn := len(w.data) + for len(buf) > 0 { + if w.isNotConcurrent() && w.idx == 0 && len(buf) >= zn { + // Avoid a copy as there is enough data for a block. + if err = w.write(buf[:zn], false); err != nil { + return + } + n += zn + buf = buf[zn:] + continue + } + // Accumulate the data to be compressed. + m := copy(w.data[w.idx:], buf) + n += m + w.idx += m + buf = buf[m:] + + if w.idx < len(w.data) { + // Buffer not filled. + return + } + + // Buffer full. + if err = w.write(w.data, true); err != nil { + return + } + if !w.isNotConcurrent() { + size := w.frame.Descriptor.Flags.BlockSizeIndex() + w.data = size.Get() + } + w.idx = 0 + } + return +} + +func (w *Writer) write(data []byte, safe bool) error { + if w.isNotConcurrent() { + block := w.frame.Blocks.Block + err := block.Compress(w.frame, data, w.level).Write(w.frame, w.src) + w.handler(len(block.Data)) + return err + } + c := make(chan *lz4stream.FrameDataBlock) + w.frame.Blocks.Blocks <- c + go func(c chan *lz4stream.FrameDataBlock, data []byte, safe bool) { + b := lz4stream.NewFrameDataBlock(w.frame) + c <- b.Compress(w.frame, data, w.level) + <-c + w.handler(len(b.Data)) + b.Close(w.frame) + if safe { + // safe to put it back as the last usage of it was FrameDataBlock.Write() called before c is closed + lz4block.Put(data) + } + }(c, data, safe) + + return nil +} + +// Flush any buffered data to the underlying writer immediately. +func (w *Writer) Flush() (err error) { + switch w.state.state { + case writeState: + case errorState: + return w.state.err + case newState: + if err = w.init(); w.state.next(err) { + return + } + default: + return nil + } + + if w.idx > 0 { + // Flush pending data, disable w.data freeing as it is done later on. + if err = w.write(w.data[:w.idx], false); err != nil { + return err + } + w.idx = 0 + } + return nil +} + +// Close closes the Writer, flushing any unwritten data to the underlying writer +// without closing it. +func (w *Writer) Close() error { + if err := w.Flush(); err != nil { + return err + } + err := w.frame.CloseW(w.src, w.num) + // It is now safe to free the buffer. + if w.data != nil { + lz4block.Put(w.data) + w.data = nil + } + return err +} + +// Reset clears the state of the Writer w such that it is equivalent to its +// initial state from NewWriter, but instead writing to writer. +// Reset keeps the previous options unless overwritten by the supplied ones. +// No access to writer is performed. +// +// w.Close must be called before Reset or pending data may be dropped. +func (w *Writer) Reset(writer io.Writer) { + w.frame.Reset(w.num) + w.state.reset() + w.src = writer +} + +// ReadFrom efficiently reads from r and compressed into the Writer destination. +func (w *Writer) ReadFrom(r io.Reader) (n int64, err error) { + switch w.state.state { + case closedState, errorState: + return 0, w.state.err + case newState: + if err = w.init(); w.state.next(err) { + return + } + default: + return 0, w.state.fail() + } + defer w.state.check(&err) + + size := w.frame.Descriptor.Flags.BlockSizeIndex() + var done bool + var rn int + data := size.Get() + if w.isNotConcurrent() { + // Keep the same buffer for the whole process. + defer lz4block.Put(data) + } + for !done { + rn, err = io.ReadFull(r, data) + switch err { + case nil: + case io.EOF, io.ErrUnexpectedEOF: // read may be partial + done = true + default: + return + } + n += int64(rn) + err = w.write(data[:rn], true) + if err != nil { + return + } + w.handler(rn) + if !done && !w.isNotConcurrent() { + // The buffer will be returned automatically by go routines (safe=true) + // so get a new one fo the next round. + data = size.Get() + } + } + return +} diff --git a/vendor/github.com/stretchr/testify/require/doc.go b/vendor/github.com/stretchr/testify/require/doc.go new file mode 100644 index 000000000..c8e3f94a8 --- /dev/null +++ b/vendor/github.com/stretchr/testify/require/doc.go @@ -0,0 +1,31 @@ +// Package require implements the same assertions as the `assert` package but +// stops test execution when a test fails. +// +// # Example Usage +// +// The following is a complete example using require in a standard test function: +// +// import ( +// "testing" +// "github.com/stretchr/testify/require" +// ) +// +// func TestSomething(t *testing.T) { +// +// var a string = "Hello" +// var b string = "Hello" +// +// require.Equal(t, a, b, "The two words should be the same.") +// +// } +// +// # Assertions +// +// The `require` package have same global functions as in the `assert` package, +// but instead of returning a boolean result they call `t.FailNow()`. +// A consequence of this is that it must be called from the goroutine running +// the test function, not from other goroutines created during the test. +// +// Every assertion function also takes an optional string message as the final argument, +// allowing custom error messages to be appended to the message the assertion method outputs. +package require diff --git a/vendor/github.com/stretchr/testify/require/forward_requirements.go b/vendor/github.com/stretchr/testify/require/forward_requirements.go new file mode 100644 index 000000000..1dcb2338c --- /dev/null +++ b/vendor/github.com/stretchr/testify/require/forward_requirements.go @@ -0,0 +1,16 @@ +package require + +// Assertions provides assertion methods around the +// TestingT interface. +type Assertions struct { + t TestingT +} + +// New makes a new Assertions object for the specified TestingT. +func New(t TestingT) *Assertions { + return &Assertions{ + t: t, + } +} + +//go:generate sh -c "cd ../_codegen && go build && cd - && ../_codegen/_codegen -output-package=require -template=require_forward.go.tmpl -include-format-funcs" diff --git a/vendor/github.com/stretchr/testify/require/require.go b/vendor/github.com/stretchr/testify/require/require.go new file mode 100644 index 000000000..2d02f9bce --- /dev/null +++ b/vendor/github.com/stretchr/testify/require/require.go @@ -0,0 +1,2180 @@ +// Code generated with github.com/stretchr/testify/_codegen; DO NOT EDIT. + +package require + +import ( + assert "github.com/stretchr/testify/assert" + http "net/http" + url "net/url" + time "time" +) + +// Condition uses a Comparison to assert a complex condition. +func Condition(t TestingT, comp assert.Comparison, msgAndArgs ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.Condition(t, comp, msgAndArgs...) { + return + } + t.FailNow() +} + +// Conditionf uses a Comparison to assert a complex condition. +func Conditionf(t TestingT, comp assert.Comparison, msg string, args ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.Conditionf(t, comp, msg, args...) { + return + } + t.FailNow() +} + +// Contains asserts that the specified string, list(array, slice...) or map contains the +// specified substring or element. +// +// require.Contains(t, "Hello World", "World") +// require.Contains(t, ["Hello", "World"], "World") +// require.Contains(t, {"Hello": "World"}, "Hello") +func Contains(t TestingT, s interface{}, contains interface{}, msgAndArgs ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.Contains(t, s, contains, msgAndArgs...) { + return + } + t.FailNow() +} + +// Containsf asserts that the specified string, list(array, slice...) or map contains the +// specified substring or element. +// +// require.Containsf(t, "Hello World", "World", "error message %s", "formatted") +// require.Containsf(t, ["Hello", "World"], "World", "error message %s", "formatted") +// require.Containsf(t, {"Hello": "World"}, "Hello", "error message %s", "formatted") +func Containsf(t TestingT, s interface{}, contains interface{}, msg string, args ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.Containsf(t, s, contains, msg, args...) { + return + } + t.FailNow() +} + +// DirExists checks whether a directory exists in the given path. It also fails +// if the path is a file rather a directory or there is an error checking whether it exists. +func DirExists(t TestingT, path string, msgAndArgs ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.DirExists(t, path, msgAndArgs...) { + return + } + t.FailNow() +} + +// DirExistsf checks whether a directory exists in the given path. It also fails +// if the path is a file rather a directory or there is an error checking whether it exists. +func DirExistsf(t TestingT, path string, msg string, args ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.DirExistsf(t, path, msg, args...) { + return + } + t.FailNow() +} + +// ElementsMatch asserts that the specified listA(array, slice...) is equal to specified +// listB(array, slice...) ignoring the order of the elements. If there are duplicate elements, +// the number of appearances of each of them in both lists should match. +// +// require.ElementsMatch(t, [1, 3, 2, 3], [1, 3, 3, 2]) +func ElementsMatch(t TestingT, listA interface{}, listB interface{}, msgAndArgs ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.ElementsMatch(t, listA, listB, msgAndArgs...) { + return + } + t.FailNow() +} + +// ElementsMatchf asserts that the specified listA(array, slice...) is equal to specified +// listB(array, slice...) ignoring the order of the elements. If there are duplicate elements, +// the number of appearances of each of them in both lists should match. +// +// require.ElementsMatchf(t, [1, 3, 2, 3], [1, 3, 3, 2], "error message %s", "formatted") +func ElementsMatchf(t TestingT, listA interface{}, listB interface{}, msg string, args ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.ElementsMatchf(t, listA, listB, msg, args...) { + return + } + t.FailNow() +} + +// Empty asserts that the given value is "empty". +// +// [Zero values] are "empty". +// +// Arrays are "empty" if every element is the zero value of the type (stricter than "empty"). +// +// Slices, maps and channels with zero length are "empty". +// +// Pointer values are "empty" if the pointer is nil or if the pointed value is "empty". +// +// require.Empty(t, obj) +// +// [Zero values]: https://go.dev/ref/spec#The_zero_value +func Empty(t TestingT, object interface{}, msgAndArgs ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.Empty(t, object, msgAndArgs...) { + return + } + t.FailNow() +} + +// Emptyf asserts that the given value is "empty". +// +// [Zero values] are "empty". +// +// Arrays are "empty" if every element is the zero value of the type (stricter than "empty"). +// +// Slices, maps and channels with zero length are "empty". +// +// Pointer values are "empty" if the pointer is nil or if the pointed value is "empty". +// +// require.Emptyf(t, obj, "error message %s", "formatted") +// +// [Zero values]: https://go.dev/ref/spec#The_zero_value +func Emptyf(t TestingT, object interface{}, msg string, args ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.Emptyf(t, object, msg, args...) { + return + } + t.FailNow() +} + +// Equal asserts that two objects are equal. +// +// require.Equal(t, 123, 123) +// +// Pointer variable equality is determined based on the equality of the +// referenced values (as opposed to the memory addresses). Function equality +// cannot be determined and will always fail. +func Equal(t TestingT, expected interface{}, actual interface{}, msgAndArgs ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.Equal(t, expected, actual, msgAndArgs...) { + return + } + t.FailNow() +} + +// EqualError asserts that a function returned an error (i.e. not `nil`) +// and that it is equal to the provided error. +// +// actualObj, err := SomeFunction() +// require.EqualError(t, err, expectedErrorString) +func EqualError(t TestingT, theError error, errString string, msgAndArgs ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.EqualError(t, theError, errString, msgAndArgs...) { + return + } + t.FailNow() +} + +// EqualErrorf asserts that a function returned an error (i.e. not `nil`) +// and that it is equal to the provided error. +// +// actualObj, err := SomeFunction() +// require.EqualErrorf(t, err, expectedErrorString, "error message %s", "formatted") +func EqualErrorf(t TestingT, theError error, errString string, msg string, args ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.EqualErrorf(t, theError, errString, msg, args...) { + return + } + t.FailNow() +} + +// EqualExportedValues asserts that the types of two objects are equal and their public +// fields are also equal. This is useful for comparing structs that have private fields +// that could potentially differ. +// +// type S struct { +// Exported int +// notExported int +// } +// require.EqualExportedValues(t, S{1, 2}, S{1, 3}) => true +// require.EqualExportedValues(t, S{1, 2}, S{2, 3}) => false +func EqualExportedValues(t TestingT, expected interface{}, actual interface{}, msgAndArgs ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.EqualExportedValues(t, expected, actual, msgAndArgs...) { + return + } + t.FailNow() +} + +// EqualExportedValuesf asserts that the types of two objects are equal and their public +// fields are also equal. This is useful for comparing structs that have private fields +// that could potentially differ. +// +// type S struct { +// Exported int +// notExported int +// } +// require.EqualExportedValuesf(t, S{1, 2}, S{1, 3}, "error message %s", "formatted") => true +// require.EqualExportedValuesf(t, S{1, 2}, S{2, 3}, "error message %s", "formatted") => false +func EqualExportedValuesf(t TestingT, expected interface{}, actual interface{}, msg string, args ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.EqualExportedValuesf(t, expected, actual, msg, args...) { + return + } + t.FailNow() +} + +// EqualValues asserts that two objects are equal or convertible to the larger +// type and equal. +// +// require.EqualValues(t, uint32(123), int32(123)) +func EqualValues(t TestingT, expected interface{}, actual interface{}, msgAndArgs ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.EqualValues(t, expected, actual, msgAndArgs...) { + return + } + t.FailNow() +} + +// EqualValuesf asserts that two objects are equal or convertible to the larger +// type and equal. +// +// require.EqualValuesf(t, uint32(123), int32(123), "error message %s", "formatted") +func EqualValuesf(t TestingT, expected interface{}, actual interface{}, msg string, args ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.EqualValuesf(t, expected, actual, msg, args...) { + return + } + t.FailNow() +} + +// Equalf asserts that two objects are equal. +// +// require.Equalf(t, 123, 123, "error message %s", "formatted") +// +// Pointer variable equality is determined based on the equality of the +// referenced values (as opposed to the memory addresses). Function equality +// cannot be determined and will always fail. +func Equalf(t TestingT, expected interface{}, actual interface{}, msg string, args ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.Equalf(t, expected, actual, msg, args...) { + return + } + t.FailNow() +} + +// Error asserts that a function returned an error (i.e. not `nil`). +// +// actualObj, err := SomeFunction() +// require.Error(t, err) +func Error(t TestingT, err error, msgAndArgs ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.Error(t, err, msgAndArgs...) { + return + } + t.FailNow() +} + +// ErrorAs asserts that at least one of the errors in err's chain matches target, and if so, sets target to that error value. +// This is a wrapper for errors.As. +func ErrorAs(t TestingT, err error, target interface{}, msgAndArgs ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.ErrorAs(t, err, target, msgAndArgs...) { + return + } + t.FailNow() +} + +// ErrorAsf asserts that at least one of the errors in err's chain matches target, and if so, sets target to that error value. +// This is a wrapper for errors.As. +func ErrorAsf(t TestingT, err error, target interface{}, msg string, args ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.ErrorAsf(t, err, target, msg, args...) { + return + } + t.FailNow() +} + +// ErrorContains asserts that a function returned an error (i.e. not `nil`) +// and that the error contains the specified substring. +// +// actualObj, err := SomeFunction() +// require.ErrorContains(t, err, expectedErrorSubString) +func ErrorContains(t TestingT, theError error, contains string, msgAndArgs ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.ErrorContains(t, theError, contains, msgAndArgs...) { + return + } + t.FailNow() +} + +// ErrorContainsf asserts that a function returned an error (i.e. not `nil`) +// and that the error contains the specified substring. +// +// actualObj, err := SomeFunction() +// require.ErrorContainsf(t, err, expectedErrorSubString, "error message %s", "formatted") +func ErrorContainsf(t TestingT, theError error, contains string, msg string, args ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.ErrorContainsf(t, theError, contains, msg, args...) { + return + } + t.FailNow() +} + +// ErrorIs asserts that at least one of the errors in err's chain matches target. +// This is a wrapper for errors.Is. +func ErrorIs(t TestingT, err error, target error, msgAndArgs ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.ErrorIs(t, err, target, msgAndArgs...) { + return + } + t.FailNow() +} + +// ErrorIsf asserts that at least one of the errors in err's chain matches target. +// This is a wrapper for errors.Is. +func ErrorIsf(t TestingT, err error, target error, msg string, args ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.ErrorIsf(t, err, target, msg, args...) { + return + } + t.FailNow() +} + +// Errorf asserts that a function returned an error (i.e. not `nil`). +// +// actualObj, err := SomeFunction() +// require.Errorf(t, err, "error message %s", "formatted") +func Errorf(t TestingT, err error, msg string, args ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.Errorf(t, err, msg, args...) { + return + } + t.FailNow() +} + +// Eventually asserts that given condition will be met in waitFor time, +// periodically checking target function each tick. +// +// require.Eventually(t, func() bool { return true; }, time.Second, 10*time.Millisecond) +func Eventually(t TestingT, condition func() bool, waitFor time.Duration, tick time.Duration, msgAndArgs ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.Eventually(t, condition, waitFor, tick, msgAndArgs...) { + return + } + t.FailNow() +} + +// EventuallyWithT asserts that given condition will be met in waitFor time, +// periodically checking target function each tick. In contrast to Eventually, +// it supplies a CollectT to the condition function, so that the condition +// function can use the CollectT to call other assertions. +// The condition is considered "met" if no errors are raised in a tick. +// The supplied CollectT collects all errors from one tick (if there are any). +// If the condition is not met before waitFor, the collected errors of +// the last tick are copied to t. +// +// externalValue := false +// go func() { +// time.Sleep(8*time.Second) +// externalValue = true +// }() +// require.EventuallyWithT(t, func(c *require.CollectT) { +// // add assertions as needed; any assertion failure will fail the current tick +// require.True(c, externalValue, "expected 'externalValue' to be true") +// }, 10*time.Second, 1*time.Second, "external state has not changed to 'true'; still false") +func EventuallyWithT(t TestingT, condition func(collect *assert.CollectT), waitFor time.Duration, tick time.Duration, msgAndArgs ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.EventuallyWithT(t, condition, waitFor, tick, msgAndArgs...) { + return + } + t.FailNow() +} + +// EventuallyWithTf asserts that given condition will be met in waitFor time, +// periodically checking target function each tick. In contrast to Eventually, +// it supplies a CollectT to the condition function, so that the condition +// function can use the CollectT to call other assertions. +// The condition is considered "met" if no errors are raised in a tick. +// The supplied CollectT collects all errors from one tick (if there are any). +// If the condition is not met before waitFor, the collected errors of +// the last tick are copied to t. +// +// externalValue := false +// go func() { +// time.Sleep(8*time.Second) +// externalValue = true +// }() +// require.EventuallyWithTf(t, func(c *require.CollectT, "error message %s", "formatted") { +// // add assertions as needed; any assertion failure will fail the current tick +// require.True(c, externalValue, "expected 'externalValue' to be true") +// }, 10*time.Second, 1*time.Second, "external state has not changed to 'true'; still false") +func EventuallyWithTf(t TestingT, condition func(collect *assert.CollectT), waitFor time.Duration, tick time.Duration, msg string, args ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.EventuallyWithTf(t, condition, waitFor, tick, msg, args...) { + return + } + t.FailNow() +} + +// Eventuallyf asserts that given condition will be met in waitFor time, +// periodically checking target function each tick. +// +// require.Eventuallyf(t, func() bool { return true; }, time.Second, 10*time.Millisecond, "error message %s", "formatted") +func Eventuallyf(t TestingT, condition func() bool, waitFor time.Duration, tick time.Duration, msg string, args ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.Eventuallyf(t, condition, waitFor, tick, msg, args...) { + return + } + t.FailNow() +} + +// Exactly asserts that two objects are equal in value and type. +// +// require.Exactly(t, int32(123), int64(123)) +func Exactly(t TestingT, expected interface{}, actual interface{}, msgAndArgs ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.Exactly(t, expected, actual, msgAndArgs...) { + return + } + t.FailNow() +} + +// Exactlyf asserts that two objects are equal in value and type. +// +// require.Exactlyf(t, int32(123), int64(123), "error message %s", "formatted") +func Exactlyf(t TestingT, expected interface{}, actual interface{}, msg string, args ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.Exactlyf(t, expected, actual, msg, args...) { + return + } + t.FailNow() +} + +// Fail reports a failure through +func Fail(t TestingT, failureMessage string, msgAndArgs ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.Fail(t, failureMessage, msgAndArgs...) { + return + } + t.FailNow() +} + +// FailNow fails test +func FailNow(t TestingT, failureMessage string, msgAndArgs ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.FailNow(t, failureMessage, msgAndArgs...) { + return + } + t.FailNow() +} + +// FailNowf fails test +func FailNowf(t TestingT, failureMessage string, msg string, args ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.FailNowf(t, failureMessage, msg, args...) { + return + } + t.FailNow() +} + +// Failf reports a failure through +func Failf(t TestingT, failureMessage string, msg string, args ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.Failf(t, failureMessage, msg, args...) { + return + } + t.FailNow() +} + +// False asserts that the specified value is false. +// +// require.False(t, myBool) +func False(t TestingT, value bool, msgAndArgs ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.False(t, value, msgAndArgs...) { + return + } + t.FailNow() +} + +// Falsef asserts that the specified value is false. +// +// require.Falsef(t, myBool, "error message %s", "formatted") +func Falsef(t TestingT, value bool, msg string, args ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.Falsef(t, value, msg, args...) { + return + } + t.FailNow() +} + +// FileExists checks whether a file exists in the given path. It also fails if +// the path points to a directory or there is an error when trying to check the file. +func FileExists(t TestingT, path string, msgAndArgs ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.FileExists(t, path, msgAndArgs...) { + return + } + t.FailNow() +} + +// FileExistsf checks whether a file exists in the given path. It also fails if +// the path points to a directory or there is an error when trying to check the file. +func FileExistsf(t TestingT, path string, msg string, args ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.FileExistsf(t, path, msg, args...) { + return + } + t.FailNow() +} + +// Greater asserts that the first element is greater than the second +// +// require.Greater(t, 2, 1) +// require.Greater(t, float64(2), float64(1)) +// require.Greater(t, "b", "a") +func Greater(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.Greater(t, e1, e2, msgAndArgs...) { + return + } + t.FailNow() +} + +// GreaterOrEqual asserts that the first element is greater than or equal to the second +// +// require.GreaterOrEqual(t, 2, 1) +// require.GreaterOrEqual(t, 2, 2) +// require.GreaterOrEqual(t, "b", "a") +// require.GreaterOrEqual(t, "b", "b") +func GreaterOrEqual(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.GreaterOrEqual(t, e1, e2, msgAndArgs...) { + return + } + t.FailNow() +} + +// GreaterOrEqualf asserts that the first element is greater than or equal to the second +// +// require.GreaterOrEqualf(t, 2, 1, "error message %s", "formatted") +// require.GreaterOrEqualf(t, 2, 2, "error message %s", "formatted") +// require.GreaterOrEqualf(t, "b", "a", "error message %s", "formatted") +// require.GreaterOrEqualf(t, "b", "b", "error message %s", "formatted") +func GreaterOrEqualf(t TestingT, e1 interface{}, e2 interface{}, msg string, args ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.GreaterOrEqualf(t, e1, e2, msg, args...) { + return + } + t.FailNow() +} + +// Greaterf asserts that the first element is greater than the second +// +// require.Greaterf(t, 2, 1, "error message %s", "formatted") +// require.Greaterf(t, float64(2), float64(1), "error message %s", "formatted") +// require.Greaterf(t, "b", "a", "error message %s", "formatted") +func Greaterf(t TestingT, e1 interface{}, e2 interface{}, msg string, args ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.Greaterf(t, e1, e2, msg, args...) { + return + } + t.FailNow() +} + +// HTTPBodyContains asserts that a specified handler returns a +// body that contains a string. +// +// require.HTTPBodyContains(t, myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky") +// +// Returns whether the assertion was successful (true) or not (false). +func HTTPBodyContains(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, str interface{}, msgAndArgs ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.HTTPBodyContains(t, handler, method, url, values, str, msgAndArgs...) { + return + } + t.FailNow() +} + +// HTTPBodyContainsf asserts that a specified handler returns a +// body that contains a string. +// +// require.HTTPBodyContainsf(t, myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky", "error message %s", "formatted") +// +// Returns whether the assertion was successful (true) or not (false). +func HTTPBodyContainsf(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, str interface{}, msg string, args ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.HTTPBodyContainsf(t, handler, method, url, values, str, msg, args...) { + return + } + t.FailNow() +} + +// HTTPBodyNotContains asserts that a specified handler returns a +// body that does not contain a string. +// +// require.HTTPBodyNotContains(t, myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky") +// +// Returns whether the assertion was successful (true) or not (false). +func HTTPBodyNotContains(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, str interface{}, msgAndArgs ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.HTTPBodyNotContains(t, handler, method, url, values, str, msgAndArgs...) { + return + } + t.FailNow() +} + +// HTTPBodyNotContainsf asserts that a specified handler returns a +// body that does not contain a string. +// +// require.HTTPBodyNotContainsf(t, myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky", "error message %s", "formatted") +// +// Returns whether the assertion was successful (true) or not (false). +func HTTPBodyNotContainsf(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, str interface{}, msg string, args ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.HTTPBodyNotContainsf(t, handler, method, url, values, str, msg, args...) { + return + } + t.FailNow() +} + +// HTTPError asserts that a specified handler returns an error status code. +// +// require.HTTPError(t, myHandler, "POST", "/a/b/c", url.Values{"a": []string{"b", "c"}} +// +// Returns whether the assertion was successful (true) or not (false). +func HTTPError(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, msgAndArgs ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.HTTPError(t, handler, method, url, values, msgAndArgs...) { + return + } + t.FailNow() +} + +// HTTPErrorf asserts that a specified handler returns an error status code. +// +// require.HTTPErrorf(t, myHandler, "POST", "/a/b/c", url.Values{"a": []string{"b", "c"}} +// +// Returns whether the assertion was successful (true) or not (false). +func HTTPErrorf(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, msg string, args ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.HTTPErrorf(t, handler, method, url, values, msg, args...) { + return + } + t.FailNow() +} + +// HTTPRedirect asserts that a specified handler returns a redirect status code. +// +// require.HTTPRedirect(t, myHandler, "GET", "/a/b/c", url.Values{"a": []string{"b", "c"}} +// +// Returns whether the assertion was successful (true) or not (false). +func HTTPRedirect(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, msgAndArgs ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.HTTPRedirect(t, handler, method, url, values, msgAndArgs...) { + return + } + t.FailNow() +} + +// HTTPRedirectf asserts that a specified handler returns a redirect status code. +// +// require.HTTPRedirectf(t, myHandler, "GET", "/a/b/c", url.Values{"a": []string{"b", "c"}} +// +// Returns whether the assertion was successful (true) or not (false). +func HTTPRedirectf(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, msg string, args ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.HTTPRedirectf(t, handler, method, url, values, msg, args...) { + return + } + t.FailNow() +} + +// HTTPStatusCode asserts that a specified handler returns a specified status code. +// +// require.HTTPStatusCode(t, myHandler, "GET", "/notImplemented", nil, 501) +// +// Returns whether the assertion was successful (true) or not (false). +func HTTPStatusCode(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, statuscode int, msgAndArgs ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.HTTPStatusCode(t, handler, method, url, values, statuscode, msgAndArgs...) { + return + } + t.FailNow() +} + +// HTTPStatusCodef asserts that a specified handler returns a specified status code. +// +// require.HTTPStatusCodef(t, myHandler, "GET", "/notImplemented", nil, 501, "error message %s", "formatted") +// +// Returns whether the assertion was successful (true) or not (false). +func HTTPStatusCodef(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, statuscode int, msg string, args ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.HTTPStatusCodef(t, handler, method, url, values, statuscode, msg, args...) { + return + } + t.FailNow() +} + +// HTTPSuccess asserts that a specified handler returns a success status code. +// +// require.HTTPSuccess(t, myHandler, "POST", "http://www.google.com", nil) +// +// Returns whether the assertion was successful (true) or not (false). +func HTTPSuccess(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, msgAndArgs ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.HTTPSuccess(t, handler, method, url, values, msgAndArgs...) { + return + } + t.FailNow() +} + +// HTTPSuccessf asserts that a specified handler returns a success status code. +// +// require.HTTPSuccessf(t, myHandler, "POST", "http://www.google.com", nil, "error message %s", "formatted") +// +// Returns whether the assertion was successful (true) or not (false). +func HTTPSuccessf(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, msg string, args ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.HTTPSuccessf(t, handler, method, url, values, msg, args...) { + return + } + t.FailNow() +} + +// Implements asserts that an object is implemented by the specified interface. +// +// require.Implements(t, (*MyInterface)(nil), new(MyObject)) +func Implements(t TestingT, interfaceObject interface{}, object interface{}, msgAndArgs ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.Implements(t, interfaceObject, object, msgAndArgs...) { + return + } + t.FailNow() +} + +// Implementsf asserts that an object is implemented by the specified interface. +// +// require.Implementsf(t, (*MyInterface)(nil), new(MyObject), "error message %s", "formatted") +func Implementsf(t TestingT, interfaceObject interface{}, object interface{}, msg string, args ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.Implementsf(t, interfaceObject, object, msg, args...) { + return + } + t.FailNow() +} + +// InDelta asserts that the two numerals are within delta of each other. +// +// require.InDelta(t, math.Pi, 22/7.0, 0.01) +func InDelta(t TestingT, expected interface{}, actual interface{}, delta float64, msgAndArgs ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.InDelta(t, expected, actual, delta, msgAndArgs...) { + return + } + t.FailNow() +} + +// InDeltaMapValues is the same as InDelta, but it compares all values between two maps. Both maps must have exactly the same keys. +func InDeltaMapValues(t TestingT, expected interface{}, actual interface{}, delta float64, msgAndArgs ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.InDeltaMapValues(t, expected, actual, delta, msgAndArgs...) { + return + } + t.FailNow() +} + +// InDeltaMapValuesf is the same as InDelta, but it compares all values between two maps. Both maps must have exactly the same keys. +func InDeltaMapValuesf(t TestingT, expected interface{}, actual interface{}, delta float64, msg string, args ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.InDeltaMapValuesf(t, expected, actual, delta, msg, args...) { + return + } + t.FailNow() +} + +// InDeltaSlice is the same as InDelta, except it compares two slices. +func InDeltaSlice(t TestingT, expected interface{}, actual interface{}, delta float64, msgAndArgs ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.InDeltaSlice(t, expected, actual, delta, msgAndArgs...) { + return + } + t.FailNow() +} + +// InDeltaSlicef is the same as InDelta, except it compares two slices. +func InDeltaSlicef(t TestingT, expected interface{}, actual interface{}, delta float64, msg string, args ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.InDeltaSlicef(t, expected, actual, delta, msg, args...) { + return + } + t.FailNow() +} + +// InDeltaf asserts that the two numerals are within delta of each other. +// +// require.InDeltaf(t, math.Pi, 22/7.0, 0.01, "error message %s", "formatted") +func InDeltaf(t TestingT, expected interface{}, actual interface{}, delta float64, msg string, args ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.InDeltaf(t, expected, actual, delta, msg, args...) { + return + } + t.FailNow() +} + +// InEpsilon asserts that expected and actual have a relative error less than epsilon +func InEpsilon(t TestingT, expected interface{}, actual interface{}, epsilon float64, msgAndArgs ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.InEpsilon(t, expected, actual, epsilon, msgAndArgs...) { + return + } + t.FailNow() +} + +// InEpsilonSlice is the same as InEpsilon, except it compares each value from two slices. +func InEpsilonSlice(t TestingT, expected interface{}, actual interface{}, epsilon float64, msgAndArgs ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.InEpsilonSlice(t, expected, actual, epsilon, msgAndArgs...) { + return + } + t.FailNow() +} + +// InEpsilonSlicef is the same as InEpsilon, except it compares each value from two slices. +func InEpsilonSlicef(t TestingT, expected interface{}, actual interface{}, epsilon float64, msg string, args ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.InEpsilonSlicef(t, expected, actual, epsilon, msg, args...) { + return + } + t.FailNow() +} + +// InEpsilonf asserts that expected and actual have a relative error less than epsilon +func InEpsilonf(t TestingT, expected interface{}, actual interface{}, epsilon float64, msg string, args ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.InEpsilonf(t, expected, actual, epsilon, msg, args...) { + return + } + t.FailNow() +} + +// IsDecreasing asserts that the collection is decreasing +// +// require.IsDecreasing(t, []int{2, 1, 0}) +// require.IsDecreasing(t, []float{2, 1}) +// require.IsDecreasing(t, []string{"b", "a"}) +func IsDecreasing(t TestingT, object interface{}, msgAndArgs ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.IsDecreasing(t, object, msgAndArgs...) { + return + } + t.FailNow() +} + +// IsDecreasingf asserts that the collection is decreasing +// +// require.IsDecreasingf(t, []int{2, 1, 0}, "error message %s", "formatted") +// require.IsDecreasingf(t, []float{2, 1}, "error message %s", "formatted") +// require.IsDecreasingf(t, []string{"b", "a"}, "error message %s", "formatted") +func IsDecreasingf(t TestingT, object interface{}, msg string, args ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.IsDecreasingf(t, object, msg, args...) { + return + } + t.FailNow() +} + +// IsIncreasing asserts that the collection is increasing +// +// require.IsIncreasing(t, []int{1, 2, 3}) +// require.IsIncreasing(t, []float{1, 2}) +// require.IsIncreasing(t, []string{"a", "b"}) +func IsIncreasing(t TestingT, object interface{}, msgAndArgs ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.IsIncreasing(t, object, msgAndArgs...) { + return + } + t.FailNow() +} + +// IsIncreasingf asserts that the collection is increasing +// +// require.IsIncreasingf(t, []int{1, 2, 3}, "error message %s", "formatted") +// require.IsIncreasingf(t, []float{1, 2}, "error message %s", "formatted") +// require.IsIncreasingf(t, []string{"a", "b"}, "error message %s", "formatted") +func IsIncreasingf(t TestingT, object interface{}, msg string, args ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.IsIncreasingf(t, object, msg, args...) { + return + } + t.FailNow() +} + +// IsNonDecreasing asserts that the collection is not decreasing +// +// require.IsNonDecreasing(t, []int{1, 1, 2}) +// require.IsNonDecreasing(t, []float{1, 2}) +// require.IsNonDecreasing(t, []string{"a", "b"}) +func IsNonDecreasing(t TestingT, object interface{}, msgAndArgs ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.IsNonDecreasing(t, object, msgAndArgs...) { + return + } + t.FailNow() +} + +// IsNonDecreasingf asserts that the collection is not decreasing +// +// require.IsNonDecreasingf(t, []int{1, 1, 2}, "error message %s", "formatted") +// require.IsNonDecreasingf(t, []float{1, 2}, "error message %s", "formatted") +// require.IsNonDecreasingf(t, []string{"a", "b"}, "error message %s", "formatted") +func IsNonDecreasingf(t TestingT, object interface{}, msg string, args ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.IsNonDecreasingf(t, object, msg, args...) { + return + } + t.FailNow() +} + +// IsNonIncreasing asserts that the collection is not increasing +// +// require.IsNonIncreasing(t, []int{2, 1, 1}) +// require.IsNonIncreasing(t, []float{2, 1}) +// require.IsNonIncreasing(t, []string{"b", "a"}) +func IsNonIncreasing(t TestingT, object interface{}, msgAndArgs ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.IsNonIncreasing(t, object, msgAndArgs...) { + return + } + t.FailNow() +} + +// IsNonIncreasingf asserts that the collection is not increasing +// +// require.IsNonIncreasingf(t, []int{2, 1, 1}, "error message %s", "formatted") +// require.IsNonIncreasingf(t, []float{2, 1}, "error message %s", "formatted") +// require.IsNonIncreasingf(t, []string{"b", "a"}, "error message %s", "formatted") +func IsNonIncreasingf(t TestingT, object interface{}, msg string, args ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.IsNonIncreasingf(t, object, msg, args...) { + return + } + t.FailNow() +} + +// IsNotType asserts that the specified objects are not of the same type. +// +// require.IsNotType(t, &NotMyStruct{}, &MyStruct{}) +func IsNotType(t TestingT, theType interface{}, object interface{}, msgAndArgs ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.IsNotType(t, theType, object, msgAndArgs...) { + return + } + t.FailNow() +} + +// IsNotTypef asserts that the specified objects are not of the same type. +// +// require.IsNotTypef(t, &NotMyStruct{}, &MyStruct{}, "error message %s", "formatted") +func IsNotTypef(t TestingT, theType interface{}, object interface{}, msg string, args ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.IsNotTypef(t, theType, object, msg, args...) { + return + } + t.FailNow() +} + +// IsType asserts that the specified objects are of the same type. +// +// require.IsType(t, &MyStruct{}, &MyStruct{}) +func IsType(t TestingT, expectedType interface{}, object interface{}, msgAndArgs ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.IsType(t, expectedType, object, msgAndArgs...) { + return + } + t.FailNow() +} + +// IsTypef asserts that the specified objects are of the same type. +// +// require.IsTypef(t, &MyStruct{}, &MyStruct{}, "error message %s", "formatted") +func IsTypef(t TestingT, expectedType interface{}, object interface{}, msg string, args ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.IsTypef(t, expectedType, object, msg, args...) { + return + } + t.FailNow() +} + +// JSONEq asserts that two JSON strings are equivalent. +// +// require.JSONEq(t, `{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`) +func JSONEq(t TestingT, expected string, actual string, msgAndArgs ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.JSONEq(t, expected, actual, msgAndArgs...) { + return + } + t.FailNow() +} + +// JSONEqf asserts that two JSON strings are equivalent. +// +// require.JSONEqf(t, `{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`, "error message %s", "formatted") +func JSONEqf(t TestingT, expected string, actual string, msg string, args ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.JSONEqf(t, expected, actual, msg, args...) { + return + } + t.FailNow() +} + +// Len asserts that the specified object has specific length. +// Len also fails if the object has a type that len() not accept. +// +// require.Len(t, mySlice, 3) +func Len(t TestingT, object interface{}, length int, msgAndArgs ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.Len(t, object, length, msgAndArgs...) { + return + } + t.FailNow() +} + +// Lenf asserts that the specified object has specific length. +// Lenf also fails if the object has a type that len() not accept. +// +// require.Lenf(t, mySlice, 3, "error message %s", "formatted") +func Lenf(t TestingT, object interface{}, length int, msg string, args ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.Lenf(t, object, length, msg, args...) { + return + } + t.FailNow() +} + +// Less asserts that the first element is less than the second +// +// require.Less(t, 1, 2) +// require.Less(t, float64(1), float64(2)) +// require.Less(t, "a", "b") +func Less(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.Less(t, e1, e2, msgAndArgs...) { + return + } + t.FailNow() +} + +// LessOrEqual asserts that the first element is less than or equal to the second +// +// require.LessOrEqual(t, 1, 2) +// require.LessOrEqual(t, 2, 2) +// require.LessOrEqual(t, "a", "b") +// require.LessOrEqual(t, "b", "b") +func LessOrEqual(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.LessOrEqual(t, e1, e2, msgAndArgs...) { + return + } + t.FailNow() +} + +// LessOrEqualf asserts that the first element is less than or equal to the second +// +// require.LessOrEqualf(t, 1, 2, "error message %s", "formatted") +// require.LessOrEqualf(t, 2, 2, "error message %s", "formatted") +// require.LessOrEqualf(t, "a", "b", "error message %s", "formatted") +// require.LessOrEqualf(t, "b", "b", "error message %s", "formatted") +func LessOrEqualf(t TestingT, e1 interface{}, e2 interface{}, msg string, args ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.LessOrEqualf(t, e1, e2, msg, args...) { + return + } + t.FailNow() +} + +// Lessf asserts that the first element is less than the second +// +// require.Lessf(t, 1, 2, "error message %s", "formatted") +// require.Lessf(t, float64(1), float64(2), "error message %s", "formatted") +// require.Lessf(t, "a", "b", "error message %s", "formatted") +func Lessf(t TestingT, e1 interface{}, e2 interface{}, msg string, args ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.Lessf(t, e1, e2, msg, args...) { + return + } + t.FailNow() +} + +// Negative asserts that the specified element is negative +// +// require.Negative(t, -1) +// require.Negative(t, -1.23) +func Negative(t TestingT, e interface{}, msgAndArgs ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.Negative(t, e, msgAndArgs...) { + return + } + t.FailNow() +} + +// Negativef asserts that the specified element is negative +// +// require.Negativef(t, -1, "error message %s", "formatted") +// require.Negativef(t, -1.23, "error message %s", "formatted") +func Negativef(t TestingT, e interface{}, msg string, args ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.Negativef(t, e, msg, args...) { + return + } + t.FailNow() +} + +// Never asserts that the given condition doesn't satisfy in waitFor time, +// periodically checking the target function each tick. +// +// require.Never(t, func() bool { return false; }, time.Second, 10*time.Millisecond) +func Never(t TestingT, condition func() bool, waitFor time.Duration, tick time.Duration, msgAndArgs ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.Never(t, condition, waitFor, tick, msgAndArgs...) { + return + } + t.FailNow() +} + +// Neverf asserts that the given condition doesn't satisfy in waitFor time, +// periodically checking the target function each tick. +// +// require.Neverf(t, func() bool { return false; }, time.Second, 10*time.Millisecond, "error message %s", "formatted") +func Neverf(t TestingT, condition func() bool, waitFor time.Duration, tick time.Duration, msg string, args ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.Neverf(t, condition, waitFor, tick, msg, args...) { + return + } + t.FailNow() +} + +// Nil asserts that the specified object is nil. +// +// require.Nil(t, err) +func Nil(t TestingT, object interface{}, msgAndArgs ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.Nil(t, object, msgAndArgs...) { + return + } + t.FailNow() +} + +// Nilf asserts that the specified object is nil. +// +// require.Nilf(t, err, "error message %s", "formatted") +func Nilf(t TestingT, object interface{}, msg string, args ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.Nilf(t, object, msg, args...) { + return + } + t.FailNow() +} + +// NoDirExists checks whether a directory does not exist in the given path. +// It fails if the path points to an existing _directory_ only. +func NoDirExists(t TestingT, path string, msgAndArgs ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.NoDirExists(t, path, msgAndArgs...) { + return + } + t.FailNow() +} + +// NoDirExistsf checks whether a directory does not exist in the given path. +// It fails if the path points to an existing _directory_ only. +func NoDirExistsf(t TestingT, path string, msg string, args ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.NoDirExistsf(t, path, msg, args...) { + return + } + t.FailNow() +} + +// NoError asserts that a function returned no error (i.e. `nil`). +// +// actualObj, err := SomeFunction() +// if require.NoError(t, err) { +// require.Equal(t, expectedObj, actualObj) +// } +func NoError(t TestingT, err error, msgAndArgs ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.NoError(t, err, msgAndArgs...) { + return + } + t.FailNow() +} + +// NoErrorf asserts that a function returned no error (i.e. `nil`). +// +// actualObj, err := SomeFunction() +// if require.NoErrorf(t, err, "error message %s", "formatted") { +// require.Equal(t, expectedObj, actualObj) +// } +func NoErrorf(t TestingT, err error, msg string, args ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.NoErrorf(t, err, msg, args...) { + return + } + t.FailNow() +} + +// NoFileExists checks whether a file does not exist in a given path. It fails +// if the path points to an existing _file_ only. +func NoFileExists(t TestingT, path string, msgAndArgs ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.NoFileExists(t, path, msgAndArgs...) { + return + } + t.FailNow() +} + +// NoFileExistsf checks whether a file does not exist in a given path. It fails +// if the path points to an existing _file_ only. +func NoFileExistsf(t TestingT, path string, msg string, args ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.NoFileExistsf(t, path, msg, args...) { + return + } + t.FailNow() +} + +// NotContains asserts that the specified string, list(array, slice...) or map does NOT contain the +// specified substring or element. +// +// require.NotContains(t, "Hello World", "Earth") +// require.NotContains(t, ["Hello", "World"], "Earth") +// require.NotContains(t, {"Hello": "World"}, "Earth") +func NotContains(t TestingT, s interface{}, contains interface{}, msgAndArgs ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.NotContains(t, s, contains, msgAndArgs...) { + return + } + t.FailNow() +} + +// NotContainsf asserts that the specified string, list(array, slice...) or map does NOT contain the +// specified substring or element. +// +// require.NotContainsf(t, "Hello World", "Earth", "error message %s", "formatted") +// require.NotContainsf(t, ["Hello", "World"], "Earth", "error message %s", "formatted") +// require.NotContainsf(t, {"Hello": "World"}, "Earth", "error message %s", "formatted") +func NotContainsf(t TestingT, s interface{}, contains interface{}, msg string, args ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.NotContainsf(t, s, contains, msg, args...) { + return + } + t.FailNow() +} + +// NotElementsMatch asserts that the specified listA(array, slice...) is NOT equal to specified +// listB(array, slice...) ignoring the order of the elements. If there are duplicate elements, +// the number of appearances of each of them in both lists should not match. +// This is an inverse of ElementsMatch. +// +// require.NotElementsMatch(t, [1, 1, 2, 3], [1, 1, 2, 3]) -> false +// +// require.NotElementsMatch(t, [1, 1, 2, 3], [1, 2, 3]) -> true +// +// require.NotElementsMatch(t, [1, 2, 3], [1, 2, 4]) -> true +func NotElementsMatch(t TestingT, listA interface{}, listB interface{}, msgAndArgs ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.NotElementsMatch(t, listA, listB, msgAndArgs...) { + return + } + t.FailNow() +} + +// NotElementsMatchf asserts that the specified listA(array, slice...) is NOT equal to specified +// listB(array, slice...) ignoring the order of the elements. If there are duplicate elements, +// the number of appearances of each of them in both lists should not match. +// This is an inverse of ElementsMatch. +// +// require.NotElementsMatchf(t, [1, 1, 2, 3], [1, 1, 2, 3], "error message %s", "formatted") -> false +// +// require.NotElementsMatchf(t, [1, 1, 2, 3], [1, 2, 3], "error message %s", "formatted") -> true +// +// require.NotElementsMatchf(t, [1, 2, 3], [1, 2, 4], "error message %s", "formatted") -> true +func NotElementsMatchf(t TestingT, listA interface{}, listB interface{}, msg string, args ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.NotElementsMatchf(t, listA, listB, msg, args...) { + return + } + t.FailNow() +} + +// NotEmpty asserts that the specified object is NOT [Empty]. +// +// if require.NotEmpty(t, obj) { +// require.Equal(t, "two", obj[1]) +// } +func NotEmpty(t TestingT, object interface{}, msgAndArgs ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.NotEmpty(t, object, msgAndArgs...) { + return + } + t.FailNow() +} + +// NotEmptyf asserts that the specified object is NOT [Empty]. +// +// if require.NotEmptyf(t, obj, "error message %s", "formatted") { +// require.Equal(t, "two", obj[1]) +// } +func NotEmptyf(t TestingT, object interface{}, msg string, args ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.NotEmptyf(t, object, msg, args...) { + return + } + t.FailNow() +} + +// NotEqual asserts that the specified values are NOT equal. +// +// require.NotEqual(t, obj1, obj2) +// +// Pointer variable equality is determined based on the equality of the +// referenced values (as opposed to the memory addresses). +func NotEqual(t TestingT, expected interface{}, actual interface{}, msgAndArgs ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.NotEqual(t, expected, actual, msgAndArgs...) { + return + } + t.FailNow() +} + +// NotEqualValues asserts that two objects are not equal even when converted to the same type +// +// require.NotEqualValues(t, obj1, obj2) +func NotEqualValues(t TestingT, expected interface{}, actual interface{}, msgAndArgs ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.NotEqualValues(t, expected, actual, msgAndArgs...) { + return + } + t.FailNow() +} + +// NotEqualValuesf asserts that two objects are not equal even when converted to the same type +// +// require.NotEqualValuesf(t, obj1, obj2, "error message %s", "formatted") +func NotEqualValuesf(t TestingT, expected interface{}, actual interface{}, msg string, args ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.NotEqualValuesf(t, expected, actual, msg, args...) { + return + } + t.FailNow() +} + +// NotEqualf asserts that the specified values are NOT equal. +// +// require.NotEqualf(t, obj1, obj2, "error message %s", "formatted") +// +// Pointer variable equality is determined based on the equality of the +// referenced values (as opposed to the memory addresses). +func NotEqualf(t TestingT, expected interface{}, actual interface{}, msg string, args ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.NotEqualf(t, expected, actual, msg, args...) { + return + } + t.FailNow() +} + +// NotErrorAs asserts that none of the errors in err's chain matches target, +// but if so, sets target to that error value. +func NotErrorAs(t TestingT, err error, target interface{}, msgAndArgs ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.NotErrorAs(t, err, target, msgAndArgs...) { + return + } + t.FailNow() +} + +// NotErrorAsf asserts that none of the errors in err's chain matches target, +// but if so, sets target to that error value. +func NotErrorAsf(t TestingT, err error, target interface{}, msg string, args ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.NotErrorAsf(t, err, target, msg, args...) { + return + } + t.FailNow() +} + +// NotErrorIs asserts that none of the errors in err's chain matches target. +// This is a wrapper for errors.Is. +func NotErrorIs(t TestingT, err error, target error, msgAndArgs ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.NotErrorIs(t, err, target, msgAndArgs...) { + return + } + t.FailNow() +} + +// NotErrorIsf asserts that none of the errors in err's chain matches target. +// This is a wrapper for errors.Is. +func NotErrorIsf(t TestingT, err error, target error, msg string, args ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.NotErrorIsf(t, err, target, msg, args...) { + return + } + t.FailNow() +} + +// NotImplements asserts that an object does not implement the specified interface. +// +// require.NotImplements(t, (*MyInterface)(nil), new(MyObject)) +func NotImplements(t TestingT, interfaceObject interface{}, object interface{}, msgAndArgs ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.NotImplements(t, interfaceObject, object, msgAndArgs...) { + return + } + t.FailNow() +} + +// NotImplementsf asserts that an object does not implement the specified interface. +// +// require.NotImplementsf(t, (*MyInterface)(nil), new(MyObject), "error message %s", "formatted") +func NotImplementsf(t TestingT, interfaceObject interface{}, object interface{}, msg string, args ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.NotImplementsf(t, interfaceObject, object, msg, args...) { + return + } + t.FailNow() +} + +// NotNil asserts that the specified object is not nil. +// +// require.NotNil(t, err) +func NotNil(t TestingT, object interface{}, msgAndArgs ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.NotNil(t, object, msgAndArgs...) { + return + } + t.FailNow() +} + +// NotNilf asserts that the specified object is not nil. +// +// require.NotNilf(t, err, "error message %s", "formatted") +func NotNilf(t TestingT, object interface{}, msg string, args ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.NotNilf(t, object, msg, args...) { + return + } + t.FailNow() +} + +// NotPanics asserts that the code inside the specified PanicTestFunc does NOT panic. +// +// require.NotPanics(t, func(){ RemainCalm() }) +func NotPanics(t TestingT, f assert.PanicTestFunc, msgAndArgs ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.NotPanics(t, f, msgAndArgs...) { + return + } + t.FailNow() +} + +// NotPanicsf asserts that the code inside the specified PanicTestFunc does NOT panic. +// +// require.NotPanicsf(t, func(){ RemainCalm() }, "error message %s", "formatted") +func NotPanicsf(t TestingT, f assert.PanicTestFunc, msg string, args ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.NotPanicsf(t, f, msg, args...) { + return + } + t.FailNow() +} + +// NotRegexp asserts that a specified regexp does not match a string. +// +// require.NotRegexp(t, regexp.MustCompile("starts"), "it's starting") +// require.NotRegexp(t, "^start", "it's not starting") +func NotRegexp(t TestingT, rx interface{}, str interface{}, msgAndArgs ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.NotRegexp(t, rx, str, msgAndArgs...) { + return + } + t.FailNow() +} + +// NotRegexpf asserts that a specified regexp does not match a string. +// +// require.NotRegexpf(t, regexp.MustCompile("starts"), "it's starting", "error message %s", "formatted") +// require.NotRegexpf(t, "^start", "it's not starting", "error message %s", "formatted") +func NotRegexpf(t TestingT, rx interface{}, str interface{}, msg string, args ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.NotRegexpf(t, rx, str, msg, args...) { + return + } + t.FailNow() +} + +// NotSame asserts that two pointers do not reference the same object. +// +// require.NotSame(t, ptr1, ptr2) +// +// Both arguments must be pointer variables. Pointer variable sameness is +// determined based on the equality of both type and value. +func NotSame(t TestingT, expected interface{}, actual interface{}, msgAndArgs ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.NotSame(t, expected, actual, msgAndArgs...) { + return + } + t.FailNow() +} + +// NotSamef asserts that two pointers do not reference the same object. +// +// require.NotSamef(t, ptr1, ptr2, "error message %s", "formatted") +// +// Both arguments must be pointer variables. Pointer variable sameness is +// determined based on the equality of both type and value. +func NotSamef(t TestingT, expected interface{}, actual interface{}, msg string, args ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.NotSamef(t, expected, actual, msg, args...) { + return + } + t.FailNow() +} + +// NotSubset asserts that the list (array, slice, or map) does NOT contain all +// elements given in the subset (array, slice, or map). +// Map elements are key-value pairs unless compared with an array or slice where +// only the map key is evaluated. +// +// require.NotSubset(t, [1, 3, 4], [1, 2]) +// require.NotSubset(t, {"x": 1, "y": 2}, {"z": 3}) +// require.NotSubset(t, [1, 3, 4], {1: "one", 2: "two"}) +// require.NotSubset(t, {"x": 1, "y": 2}, ["z"]) +func NotSubset(t TestingT, list interface{}, subset interface{}, msgAndArgs ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.NotSubset(t, list, subset, msgAndArgs...) { + return + } + t.FailNow() +} + +// NotSubsetf asserts that the list (array, slice, or map) does NOT contain all +// elements given in the subset (array, slice, or map). +// Map elements are key-value pairs unless compared with an array or slice where +// only the map key is evaluated. +// +// require.NotSubsetf(t, [1, 3, 4], [1, 2], "error message %s", "formatted") +// require.NotSubsetf(t, {"x": 1, "y": 2}, {"z": 3}, "error message %s", "formatted") +// require.NotSubsetf(t, [1, 3, 4], {1: "one", 2: "two"}, "error message %s", "formatted") +// require.NotSubsetf(t, {"x": 1, "y": 2}, ["z"], "error message %s", "formatted") +func NotSubsetf(t TestingT, list interface{}, subset interface{}, msg string, args ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.NotSubsetf(t, list, subset, msg, args...) { + return + } + t.FailNow() +} + +// NotZero asserts that i is not the zero value for its type. +func NotZero(t TestingT, i interface{}, msgAndArgs ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.NotZero(t, i, msgAndArgs...) { + return + } + t.FailNow() +} + +// NotZerof asserts that i is not the zero value for its type. +func NotZerof(t TestingT, i interface{}, msg string, args ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.NotZerof(t, i, msg, args...) { + return + } + t.FailNow() +} + +// Panics asserts that the code inside the specified PanicTestFunc panics. +// +// require.Panics(t, func(){ GoCrazy() }) +func Panics(t TestingT, f assert.PanicTestFunc, msgAndArgs ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.Panics(t, f, msgAndArgs...) { + return + } + t.FailNow() +} + +// PanicsWithError asserts that the code inside the specified PanicTestFunc +// panics, and that the recovered panic value is an error that satisfies the +// EqualError comparison. +// +// require.PanicsWithError(t, "crazy error", func(){ GoCrazy() }) +func PanicsWithError(t TestingT, errString string, f assert.PanicTestFunc, msgAndArgs ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.PanicsWithError(t, errString, f, msgAndArgs...) { + return + } + t.FailNow() +} + +// PanicsWithErrorf asserts that the code inside the specified PanicTestFunc +// panics, and that the recovered panic value is an error that satisfies the +// EqualError comparison. +// +// require.PanicsWithErrorf(t, "crazy error", func(){ GoCrazy() }, "error message %s", "formatted") +func PanicsWithErrorf(t TestingT, errString string, f assert.PanicTestFunc, msg string, args ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.PanicsWithErrorf(t, errString, f, msg, args...) { + return + } + t.FailNow() +} + +// PanicsWithValue asserts that the code inside the specified PanicTestFunc panics, and that +// the recovered panic value equals the expected panic value. +// +// require.PanicsWithValue(t, "crazy error", func(){ GoCrazy() }) +func PanicsWithValue(t TestingT, expected interface{}, f assert.PanicTestFunc, msgAndArgs ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.PanicsWithValue(t, expected, f, msgAndArgs...) { + return + } + t.FailNow() +} + +// PanicsWithValuef asserts that the code inside the specified PanicTestFunc panics, and that +// the recovered panic value equals the expected panic value. +// +// require.PanicsWithValuef(t, "crazy error", func(){ GoCrazy() }, "error message %s", "formatted") +func PanicsWithValuef(t TestingT, expected interface{}, f assert.PanicTestFunc, msg string, args ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.PanicsWithValuef(t, expected, f, msg, args...) { + return + } + t.FailNow() +} + +// Panicsf asserts that the code inside the specified PanicTestFunc panics. +// +// require.Panicsf(t, func(){ GoCrazy() }, "error message %s", "formatted") +func Panicsf(t TestingT, f assert.PanicTestFunc, msg string, args ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.Panicsf(t, f, msg, args...) { + return + } + t.FailNow() +} + +// Positive asserts that the specified element is positive +// +// require.Positive(t, 1) +// require.Positive(t, 1.23) +func Positive(t TestingT, e interface{}, msgAndArgs ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.Positive(t, e, msgAndArgs...) { + return + } + t.FailNow() +} + +// Positivef asserts that the specified element is positive +// +// require.Positivef(t, 1, "error message %s", "formatted") +// require.Positivef(t, 1.23, "error message %s", "formatted") +func Positivef(t TestingT, e interface{}, msg string, args ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.Positivef(t, e, msg, args...) { + return + } + t.FailNow() +} + +// Regexp asserts that a specified regexp matches a string. +// +// require.Regexp(t, regexp.MustCompile("start"), "it's starting") +// require.Regexp(t, "start...$", "it's not starting") +func Regexp(t TestingT, rx interface{}, str interface{}, msgAndArgs ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.Regexp(t, rx, str, msgAndArgs...) { + return + } + t.FailNow() +} + +// Regexpf asserts that a specified regexp matches a string. +// +// require.Regexpf(t, regexp.MustCompile("start"), "it's starting", "error message %s", "formatted") +// require.Regexpf(t, "start...$", "it's not starting", "error message %s", "formatted") +func Regexpf(t TestingT, rx interface{}, str interface{}, msg string, args ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.Regexpf(t, rx, str, msg, args...) { + return + } + t.FailNow() +} + +// Same asserts that two pointers reference the same object. +// +// require.Same(t, ptr1, ptr2) +// +// Both arguments must be pointer variables. Pointer variable sameness is +// determined based on the equality of both type and value. +func Same(t TestingT, expected interface{}, actual interface{}, msgAndArgs ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.Same(t, expected, actual, msgAndArgs...) { + return + } + t.FailNow() +} + +// Samef asserts that two pointers reference the same object. +// +// require.Samef(t, ptr1, ptr2, "error message %s", "formatted") +// +// Both arguments must be pointer variables. Pointer variable sameness is +// determined based on the equality of both type and value. +func Samef(t TestingT, expected interface{}, actual interface{}, msg string, args ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.Samef(t, expected, actual, msg, args...) { + return + } + t.FailNow() +} + +// Subset asserts that the list (array, slice, or map) contains all elements +// given in the subset (array, slice, or map). +// Map elements are key-value pairs unless compared with an array or slice where +// only the map key is evaluated. +// +// require.Subset(t, [1, 2, 3], [1, 2]) +// require.Subset(t, {"x": 1, "y": 2}, {"x": 1}) +// require.Subset(t, [1, 2, 3], {1: "one", 2: "two"}) +// require.Subset(t, {"x": 1, "y": 2}, ["x"]) +func Subset(t TestingT, list interface{}, subset interface{}, msgAndArgs ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.Subset(t, list, subset, msgAndArgs...) { + return + } + t.FailNow() +} + +// Subsetf asserts that the list (array, slice, or map) contains all elements +// given in the subset (array, slice, or map). +// Map elements are key-value pairs unless compared with an array or slice where +// only the map key is evaluated. +// +// require.Subsetf(t, [1, 2, 3], [1, 2], "error message %s", "formatted") +// require.Subsetf(t, {"x": 1, "y": 2}, {"x": 1}, "error message %s", "formatted") +// require.Subsetf(t, [1, 2, 3], {1: "one", 2: "two"}, "error message %s", "formatted") +// require.Subsetf(t, {"x": 1, "y": 2}, ["x"], "error message %s", "formatted") +func Subsetf(t TestingT, list interface{}, subset interface{}, msg string, args ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.Subsetf(t, list, subset, msg, args...) { + return + } + t.FailNow() +} + +// True asserts that the specified value is true. +// +// require.True(t, myBool) +func True(t TestingT, value bool, msgAndArgs ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.True(t, value, msgAndArgs...) { + return + } + t.FailNow() +} + +// Truef asserts that the specified value is true. +// +// require.Truef(t, myBool, "error message %s", "formatted") +func Truef(t TestingT, value bool, msg string, args ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.Truef(t, value, msg, args...) { + return + } + t.FailNow() +} + +// WithinDuration asserts that the two times are within duration delta of each other. +// +// require.WithinDuration(t, time.Now(), time.Now(), 10*time.Second) +func WithinDuration(t TestingT, expected time.Time, actual time.Time, delta time.Duration, msgAndArgs ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.WithinDuration(t, expected, actual, delta, msgAndArgs...) { + return + } + t.FailNow() +} + +// WithinDurationf asserts that the two times are within duration delta of each other. +// +// require.WithinDurationf(t, time.Now(), time.Now(), 10*time.Second, "error message %s", "formatted") +func WithinDurationf(t TestingT, expected time.Time, actual time.Time, delta time.Duration, msg string, args ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.WithinDurationf(t, expected, actual, delta, msg, args...) { + return + } + t.FailNow() +} + +// WithinRange asserts that a time is within a time range (inclusive). +// +// require.WithinRange(t, time.Now(), time.Now().Add(-time.Second), time.Now().Add(time.Second)) +func WithinRange(t TestingT, actual time.Time, start time.Time, end time.Time, msgAndArgs ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.WithinRange(t, actual, start, end, msgAndArgs...) { + return + } + t.FailNow() +} + +// WithinRangef asserts that a time is within a time range (inclusive). +// +// require.WithinRangef(t, time.Now(), time.Now().Add(-time.Second), time.Now().Add(time.Second), "error message %s", "formatted") +func WithinRangef(t TestingT, actual time.Time, start time.Time, end time.Time, msg string, args ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.WithinRangef(t, actual, start, end, msg, args...) { + return + } + t.FailNow() +} + +// YAMLEq asserts that two YAML strings are equivalent. +func YAMLEq(t TestingT, expected string, actual string, msgAndArgs ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.YAMLEq(t, expected, actual, msgAndArgs...) { + return + } + t.FailNow() +} + +// YAMLEqf asserts that two YAML strings are equivalent. +func YAMLEqf(t TestingT, expected string, actual string, msg string, args ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.YAMLEqf(t, expected, actual, msg, args...) { + return + } + t.FailNow() +} + +// Zero asserts that i is the zero value for its type. +func Zero(t TestingT, i interface{}, msgAndArgs ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.Zero(t, i, msgAndArgs...) { + return + } + t.FailNow() +} + +// Zerof asserts that i is the zero value for its type. +func Zerof(t TestingT, i interface{}, msg string, args ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.Zerof(t, i, msg, args...) { + return + } + t.FailNow() +} diff --git a/vendor/github.com/stretchr/testify/require/require.go.tmpl b/vendor/github.com/stretchr/testify/require/require.go.tmpl new file mode 100644 index 000000000..8b3283685 --- /dev/null +++ b/vendor/github.com/stretchr/testify/require/require.go.tmpl @@ -0,0 +1,6 @@ +{{ replace .Comment "assert." "require."}} +func {{.DocInfo.Name}}(t TestingT, {{.Params}}) { + if h, ok := t.(tHelper); ok { h.Helper() } + if assert.{{.DocInfo.Name}}(t, {{.ForwardedParams}}) { return } + t.FailNow() +} diff --git a/vendor/github.com/stretchr/testify/require/require_forward.go b/vendor/github.com/stretchr/testify/require/require_forward.go new file mode 100644 index 000000000..e6f7e9446 --- /dev/null +++ b/vendor/github.com/stretchr/testify/require/require_forward.go @@ -0,0 +1,1724 @@ +// Code generated with github.com/stretchr/testify/_codegen; DO NOT EDIT. + +package require + +import ( + assert "github.com/stretchr/testify/assert" + http "net/http" + url "net/url" + time "time" +) + +// Condition uses a Comparison to assert a complex condition. +func (a *Assertions) Condition(comp assert.Comparison, msgAndArgs ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + Condition(a.t, comp, msgAndArgs...) +} + +// Conditionf uses a Comparison to assert a complex condition. +func (a *Assertions) Conditionf(comp assert.Comparison, msg string, args ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + Conditionf(a.t, comp, msg, args...) +} + +// Contains asserts that the specified string, list(array, slice...) or map contains the +// specified substring or element. +// +// a.Contains("Hello World", "World") +// a.Contains(["Hello", "World"], "World") +// a.Contains({"Hello": "World"}, "Hello") +func (a *Assertions) Contains(s interface{}, contains interface{}, msgAndArgs ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + Contains(a.t, s, contains, msgAndArgs...) +} + +// Containsf asserts that the specified string, list(array, slice...) or map contains the +// specified substring or element. +// +// a.Containsf("Hello World", "World", "error message %s", "formatted") +// a.Containsf(["Hello", "World"], "World", "error message %s", "formatted") +// a.Containsf({"Hello": "World"}, "Hello", "error message %s", "formatted") +func (a *Assertions) Containsf(s interface{}, contains interface{}, msg string, args ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + Containsf(a.t, s, contains, msg, args...) +} + +// DirExists checks whether a directory exists in the given path. It also fails +// if the path is a file rather a directory or there is an error checking whether it exists. +func (a *Assertions) DirExists(path string, msgAndArgs ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + DirExists(a.t, path, msgAndArgs...) +} + +// DirExistsf checks whether a directory exists in the given path. It also fails +// if the path is a file rather a directory or there is an error checking whether it exists. +func (a *Assertions) DirExistsf(path string, msg string, args ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + DirExistsf(a.t, path, msg, args...) +} + +// ElementsMatch asserts that the specified listA(array, slice...) is equal to specified +// listB(array, slice...) ignoring the order of the elements. If there are duplicate elements, +// the number of appearances of each of them in both lists should match. +// +// a.ElementsMatch([1, 3, 2, 3], [1, 3, 3, 2]) +func (a *Assertions) ElementsMatch(listA interface{}, listB interface{}, msgAndArgs ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + ElementsMatch(a.t, listA, listB, msgAndArgs...) +} + +// ElementsMatchf asserts that the specified listA(array, slice...) is equal to specified +// listB(array, slice...) ignoring the order of the elements. If there are duplicate elements, +// the number of appearances of each of them in both lists should match. +// +// a.ElementsMatchf([1, 3, 2, 3], [1, 3, 3, 2], "error message %s", "formatted") +func (a *Assertions) ElementsMatchf(listA interface{}, listB interface{}, msg string, args ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + ElementsMatchf(a.t, listA, listB, msg, args...) +} + +// Empty asserts that the given value is "empty". +// +// [Zero values] are "empty". +// +// Arrays are "empty" if every element is the zero value of the type (stricter than "empty"). +// +// Slices, maps and channels with zero length are "empty". +// +// Pointer values are "empty" if the pointer is nil or if the pointed value is "empty". +// +// a.Empty(obj) +// +// [Zero values]: https://go.dev/ref/spec#The_zero_value +func (a *Assertions) Empty(object interface{}, msgAndArgs ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + Empty(a.t, object, msgAndArgs...) +} + +// Emptyf asserts that the given value is "empty". +// +// [Zero values] are "empty". +// +// Arrays are "empty" if every element is the zero value of the type (stricter than "empty"). +// +// Slices, maps and channels with zero length are "empty". +// +// Pointer values are "empty" if the pointer is nil or if the pointed value is "empty". +// +// a.Emptyf(obj, "error message %s", "formatted") +// +// [Zero values]: https://go.dev/ref/spec#The_zero_value +func (a *Assertions) Emptyf(object interface{}, msg string, args ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + Emptyf(a.t, object, msg, args...) +} + +// Equal asserts that two objects are equal. +// +// a.Equal(123, 123) +// +// Pointer variable equality is determined based on the equality of the +// referenced values (as opposed to the memory addresses). Function equality +// cannot be determined and will always fail. +func (a *Assertions) Equal(expected interface{}, actual interface{}, msgAndArgs ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + Equal(a.t, expected, actual, msgAndArgs...) +} + +// EqualError asserts that a function returned an error (i.e. not `nil`) +// and that it is equal to the provided error. +// +// actualObj, err := SomeFunction() +// a.EqualError(err, expectedErrorString) +func (a *Assertions) EqualError(theError error, errString string, msgAndArgs ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + EqualError(a.t, theError, errString, msgAndArgs...) +} + +// EqualErrorf asserts that a function returned an error (i.e. not `nil`) +// and that it is equal to the provided error. +// +// actualObj, err := SomeFunction() +// a.EqualErrorf(err, expectedErrorString, "error message %s", "formatted") +func (a *Assertions) EqualErrorf(theError error, errString string, msg string, args ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + EqualErrorf(a.t, theError, errString, msg, args...) +} + +// EqualExportedValues asserts that the types of two objects are equal and their public +// fields are also equal. This is useful for comparing structs that have private fields +// that could potentially differ. +// +// type S struct { +// Exported int +// notExported int +// } +// a.EqualExportedValues(S{1, 2}, S{1, 3}) => true +// a.EqualExportedValues(S{1, 2}, S{2, 3}) => false +func (a *Assertions) EqualExportedValues(expected interface{}, actual interface{}, msgAndArgs ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + EqualExportedValues(a.t, expected, actual, msgAndArgs...) +} + +// EqualExportedValuesf asserts that the types of two objects are equal and their public +// fields are also equal. This is useful for comparing structs that have private fields +// that could potentially differ. +// +// type S struct { +// Exported int +// notExported int +// } +// a.EqualExportedValuesf(S{1, 2}, S{1, 3}, "error message %s", "formatted") => true +// a.EqualExportedValuesf(S{1, 2}, S{2, 3}, "error message %s", "formatted") => false +func (a *Assertions) EqualExportedValuesf(expected interface{}, actual interface{}, msg string, args ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + EqualExportedValuesf(a.t, expected, actual, msg, args...) +} + +// EqualValues asserts that two objects are equal or convertible to the larger +// type and equal. +// +// a.EqualValues(uint32(123), int32(123)) +func (a *Assertions) EqualValues(expected interface{}, actual interface{}, msgAndArgs ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + EqualValues(a.t, expected, actual, msgAndArgs...) +} + +// EqualValuesf asserts that two objects are equal or convertible to the larger +// type and equal. +// +// a.EqualValuesf(uint32(123), int32(123), "error message %s", "formatted") +func (a *Assertions) EqualValuesf(expected interface{}, actual interface{}, msg string, args ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + EqualValuesf(a.t, expected, actual, msg, args...) +} + +// Equalf asserts that two objects are equal. +// +// a.Equalf(123, 123, "error message %s", "formatted") +// +// Pointer variable equality is determined based on the equality of the +// referenced values (as opposed to the memory addresses). Function equality +// cannot be determined and will always fail. +func (a *Assertions) Equalf(expected interface{}, actual interface{}, msg string, args ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + Equalf(a.t, expected, actual, msg, args...) +} + +// Error asserts that a function returned an error (i.e. not `nil`). +// +// actualObj, err := SomeFunction() +// a.Error(err) +func (a *Assertions) Error(err error, msgAndArgs ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + Error(a.t, err, msgAndArgs...) +} + +// ErrorAs asserts that at least one of the errors in err's chain matches target, and if so, sets target to that error value. +// This is a wrapper for errors.As. +func (a *Assertions) ErrorAs(err error, target interface{}, msgAndArgs ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + ErrorAs(a.t, err, target, msgAndArgs...) +} + +// ErrorAsf asserts that at least one of the errors in err's chain matches target, and if so, sets target to that error value. +// This is a wrapper for errors.As. +func (a *Assertions) ErrorAsf(err error, target interface{}, msg string, args ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + ErrorAsf(a.t, err, target, msg, args...) +} + +// ErrorContains asserts that a function returned an error (i.e. not `nil`) +// and that the error contains the specified substring. +// +// actualObj, err := SomeFunction() +// a.ErrorContains(err, expectedErrorSubString) +func (a *Assertions) ErrorContains(theError error, contains string, msgAndArgs ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + ErrorContains(a.t, theError, contains, msgAndArgs...) +} + +// ErrorContainsf asserts that a function returned an error (i.e. not `nil`) +// and that the error contains the specified substring. +// +// actualObj, err := SomeFunction() +// a.ErrorContainsf(err, expectedErrorSubString, "error message %s", "formatted") +func (a *Assertions) ErrorContainsf(theError error, contains string, msg string, args ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + ErrorContainsf(a.t, theError, contains, msg, args...) +} + +// ErrorIs asserts that at least one of the errors in err's chain matches target. +// This is a wrapper for errors.Is. +func (a *Assertions) ErrorIs(err error, target error, msgAndArgs ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + ErrorIs(a.t, err, target, msgAndArgs...) +} + +// ErrorIsf asserts that at least one of the errors in err's chain matches target. +// This is a wrapper for errors.Is. +func (a *Assertions) ErrorIsf(err error, target error, msg string, args ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + ErrorIsf(a.t, err, target, msg, args...) +} + +// Errorf asserts that a function returned an error (i.e. not `nil`). +// +// actualObj, err := SomeFunction() +// a.Errorf(err, "error message %s", "formatted") +func (a *Assertions) Errorf(err error, msg string, args ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + Errorf(a.t, err, msg, args...) +} + +// Eventually asserts that given condition will be met in waitFor time, +// periodically checking target function each tick. +// +// a.Eventually(func() bool { return true; }, time.Second, 10*time.Millisecond) +func (a *Assertions) Eventually(condition func() bool, waitFor time.Duration, tick time.Duration, msgAndArgs ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + Eventually(a.t, condition, waitFor, tick, msgAndArgs...) +} + +// EventuallyWithT asserts that given condition will be met in waitFor time, +// periodically checking target function each tick. In contrast to Eventually, +// it supplies a CollectT to the condition function, so that the condition +// function can use the CollectT to call other assertions. +// The condition is considered "met" if no errors are raised in a tick. +// The supplied CollectT collects all errors from one tick (if there are any). +// If the condition is not met before waitFor, the collected errors of +// the last tick are copied to t. +// +// externalValue := false +// go func() { +// time.Sleep(8*time.Second) +// externalValue = true +// }() +// a.EventuallyWithT(func(c *assert.CollectT) { +// // add assertions as needed; any assertion failure will fail the current tick +// assert.True(c, externalValue, "expected 'externalValue' to be true") +// }, 10*time.Second, 1*time.Second, "external state has not changed to 'true'; still false") +func (a *Assertions) EventuallyWithT(condition func(collect *assert.CollectT), waitFor time.Duration, tick time.Duration, msgAndArgs ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + EventuallyWithT(a.t, condition, waitFor, tick, msgAndArgs...) +} + +// EventuallyWithTf asserts that given condition will be met in waitFor time, +// periodically checking target function each tick. In contrast to Eventually, +// it supplies a CollectT to the condition function, so that the condition +// function can use the CollectT to call other assertions. +// The condition is considered "met" if no errors are raised in a tick. +// The supplied CollectT collects all errors from one tick (if there are any). +// If the condition is not met before waitFor, the collected errors of +// the last tick are copied to t. +// +// externalValue := false +// go func() { +// time.Sleep(8*time.Second) +// externalValue = true +// }() +// a.EventuallyWithTf(func(c *assert.CollectT, "error message %s", "formatted") { +// // add assertions as needed; any assertion failure will fail the current tick +// assert.True(c, externalValue, "expected 'externalValue' to be true") +// }, 10*time.Second, 1*time.Second, "external state has not changed to 'true'; still false") +func (a *Assertions) EventuallyWithTf(condition func(collect *assert.CollectT), waitFor time.Duration, tick time.Duration, msg string, args ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + EventuallyWithTf(a.t, condition, waitFor, tick, msg, args...) +} + +// Eventuallyf asserts that given condition will be met in waitFor time, +// periodically checking target function each tick. +// +// a.Eventuallyf(func() bool { return true; }, time.Second, 10*time.Millisecond, "error message %s", "formatted") +func (a *Assertions) Eventuallyf(condition func() bool, waitFor time.Duration, tick time.Duration, msg string, args ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + Eventuallyf(a.t, condition, waitFor, tick, msg, args...) +} + +// Exactly asserts that two objects are equal in value and type. +// +// a.Exactly(int32(123), int64(123)) +func (a *Assertions) Exactly(expected interface{}, actual interface{}, msgAndArgs ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + Exactly(a.t, expected, actual, msgAndArgs...) +} + +// Exactlyf asserts that two objects are equal in value and type. +// +// a.Exactlyf(int32(123), int64(123), "error message %s", "formatted") +func (a *Assertions) Exactlyf(expected interface{}, actual interface{}, msg string, args ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + Exactlyf(a.t, expected, actual, msg, args...) +} + +// Fail reports a failure through +func (a *Assertions) Fail(failureMessage string, msgAndArgs ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + Fail(a.t, failureMessage, msgAndArgs...) +} + +// FailNow fails test +func (a *Assertions) FailNow(failureMessage string, msgAndArgs ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + FailNow(a.t, failureMessage, msgAndArgs...) +} + +// FailNowf fails test +func (a *Assertions) FailNowf(failureMessage string, msg string, args ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + FailNowf(a.t, failureMessage, msg, args...) +} + +// Failf reports a failure through +func (a *Assertions) Failf(failureMessage string, msg string, args ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + Failf(a.t, failureMessage, msg, args...) +} + +// False asserts that the specified value is false. +// +// a.False(myBool) +func (a *Assertions) False(value bool, msgAndArgs ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + False(a.t, value, msgAndArgs...) +} + +// Falsef asserts that the specified value is false. +// +// a.Falsef(myBool, "error message %s", "formatted") +func (a *Assertions) Falsef(value bool, msg string, args ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + Falsef(a.t, value, msg, args...) +} + +// FileExists checks whether a file exists in the given path. It also fails if +// the path points to a directory or there is an error when trying to check the file. +func (a *Assertions) FileExists(path string, msgAndArgs ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + FileExists(a.t, path, msgAndArgs...) +} + +// FileExistsf checks whether a file exists in the given path. It also fails if +// the path points to a directory or there is an error when trying to check the file. +func (a *Assertions) FileExistsf(path string, msg string, args ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + FileExistsf(a.t, path, msg, args...) +} + +// Greater asserts that the first element is greater than the second +// +// a.Greater(2, 1) +// a.Greater(float64(2), float64(1)) +// a.Greater("b", "a") +func (a *Assertions) Greater(e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + Greater(a.t, e1, e2, msgAndArgs...) +} + +// GreaterOrEqual asserts that the first element is greater than or equal to the second +// +// a.GreaterOrEqual(2, 1) +// a.GreaterOrEqual(2, 2) +// a.GreaterOrEqual("b", "a") +// a.GreaterOrEqual("b", "b") +func (a *Assertions) GreaterOrEqual(e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + GreaterOrEqual(a.t, e1, e2, msgAndArgs...) +} + +// GreaterOrEqualf asserts that the first element is greater than or equal to the second +// +// a.GreaterOrEqualf(2, 1, "error message %s", "formatted") +// a.GreaterOrEqualf(2, 2, "error message %s", "formatted") +// a.GreaterOrEqualf("b", "a", "error message %s", "formatted") +// a.GreaterOrEqualf("b", "b", "error message %s", "formatted") +func (a *Assertions) GreaterOrEqualf(e1 interface{}, e2 interface{}, msg string, args ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + GreaterOrEqualf(a.t, e1, e2, msg, args...) +} + +// Greaterf asserts that the first element is greater than the second +// +// a.Greaterf(2, 1, "error message %s", "formatted") +// a.Greaterf(float64(2), float64(1), "error message %s", "formatted") +// a.Greaterf("b", "a", "error message %s", "formatted") +func (a *Assertions) Greaterf(e1 interface{}, e2 interface{}, msg string, args ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + Greaterf(a.t, e1, e2, msg, args...) +} + +// HTTPBodyContains asserts that a specified handler returns a +// body that contains a string. +// +// a.HTTPBodyContains(myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky") +// +// Returns whether the assertion was successful (true) or not (false). +func (a *Assertions) HTTPBodyContains(handler http.HandlerFunc, method string, url string, values url.Values, str interface{}, msgAndArgs ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + HTTPBodyContains(a.t, handler, method, url, values, str, msgAndArgs...) +} + +// HTTPBodyContainsf asserts that a specified handler returns a +// body that contains a string. +// +// a.HTTPBodyContainsf(myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky", "error message %s", "formatted") +// +// Returns whether the assertion was successful (true) or not (false). +func (a *Assertions) HTTPBodyContainsf(handler http.HandlerFunc, method string, url string, values url.Values, str interface{}, msg string, args ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + HTTPBodyContainsf(a.t, handler, method, url, values, str, msg, args...) +} + +// HTTPBodyNotContains asserts that a specified handler returns a +// body that does not contain a string. +// +// a.HTTPBodyNotContains(myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky") +// +// Returns whether the assertion was successful (true) or not (false). +func (a *Assertions) HTTPBodyNotContains(handler http.HandlerFunc, method string, url string, values url.Values, str interface{}, msgAndArgs ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + HTTPBodyNotContains(a.t, handler, method, url, values, str, msgAndArgs...) +} + +// HTTPBodyNotContainsf asserts that a specified handler returns a +// body that does not contain a string. +// +// a.HTTPBodyNotContainsf(myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky", "error message %s", "formatted") +// +// Returns whether the assertion was successful (true) or not (false). +func (a *Assertions) HTTPBodyNotContainsf(handler http.HandlerFunc, method string, url string, values url.Values, str interface{}, msg string, args ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + HTTPBodyNotContainsf(a.t, handler, method, url, values, str, msg, args...) +} + +// HTTPError asserts that a specified handler returns an error status code. +// +// a.HTTPError(myHandler, "POST", "/a/b/c", url.Values{"a": []string{"b", "c"}} +// +// Returns whether the assertion was successful (true) or not (false). +func (a *Assertions) HTTPError(handler http.HandlerFunc, method string, url string, values url.Values, msgAndArgs ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + HTTPError(a.t, handler, method, url, values, msgAndArgs...) +} + +// HTTPErrorf asserts that a specified handler returns an error status code. +// +// a.HTTPErrorf(myHandler, "POST", "/a/b/c", url.Values{"a": []string{"b", "c"}} +// +// Returns whether the assertion was successful (true) or not (false). +func (a *Assertions) HTTPErrorf(handler http.HandlerFunc, method string, url string, values url.Values, msg string, args ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + HTTPErrorf(a.t, handler, method, url, values, msg, args...) +} + +// HTTPRedirect asserts that a specified handler returns a redirect status code. +// +// a.HTTPRedirect(myHandler, "GET", "/a/b/c", url.Values{"a": []string{"b", "c"}} +// +// Returns whether the assertion was successful (true) or not (false). +func (a *Assertions) HTTPRedirect(handler http.HandlerFunc, method string, url string, values url.Values, msgAndArgs ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + HTTPRedirect(a.t, handler, method, url, values, msgAndArgs...) +} + +// HTTPRedirectf asserts that a specified handler returns a redirect status code. +// +// a.HTTPRedirectf(myHandler, "GET", "/a/b/c", url.Values{"a": []string{"b", "c"}} +// +// Returns whether the assertion was successful (true) or not (false). +func (a *Assertions) HTTPRedirectf(handler http.HandlerFunc, method string, url string, values url.Values, msg string, args ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + HTTPRedirectf(a.t, handler, method, url, values, msg, args...) +} + +// HTTPStatusCode asserts that a specified handler returns a specified status code. +// +// a.HTTPStatusCode(myHandler, "GET", "/notImplemented", nil, 501) +// +// Returns whether the assertion was successful (true) or not (false). +func (a *Assertions) HTTPStatusCode(handler http.HandlerFunc, method string, url string, values url.Values, statuscode int, msgAndArgs ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + HTTPStatusCode(a.t, handler, method, url, values, statuscode, msgAndArgs...) +} + +// HTTPStatusCodef asserts that a specified handler returns a specified status code. +// +// a.HTTPStatusCodef(myHandler, "GET", "/notImplemented", nil, 501, "error message %s", "formatted") +// +// Returns whether the assertion was successful (true) or not (false). +func (a *Assertions) HTTPStatusCodef(handler http.HandlerFunc, method string, url string, values url.Values, statuscode int, msg string, args ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + HTTPStatusCodef(a.t, handler, method, url, values, statuscode, msg, args...) +} + +// HTTPSuccess asserts that a specified handler returns a success status code. +// +// a.HTTPSuccess(myHandler, "POST", "http://www.google.com", nil) +// +// Returns whether the assertion was successful (true) or not (false). +func (a *Assertions) HTTPSuccess(handler http.HandlerFunc, method string, url string, values url.Values, msgAndArgs ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + HTTPSuccess(a.t, handler, method, url, values, msgAndArgs...) +} + +// HTTPSuccessf asserts that a specified handler returns a success status code. +// +// a.HTTPSuccessf(myHandler, "POST", "http://www.google.com", nil, "error message %s", "formatted") +// +// Returns whether the assertion was successful (true) or not (false). +func (a *Assertions) HTTPSuccessf(handler http.HandlerFunc, method string, url string, values url.Values, msg string, args ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + HTTPSuccessf(a.t, handler, method, url, values, msg, args...) +} + +// Implements asserts that an object is implemented by the specified interface. +// +// a.Implements((*MyInterface)(nil), new(MyObject)) +func (a *Assertions) Implements(interfaceObject interface{}, object interface{}, msgAndArgs ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + Implements(a.t, interfaceObject, object, msgAndArgs...) +} + +// Implementsf asserts that an object is implemented by the specified interface. +// +// a.Implementsf((*MyInterface)(nil), new(MyObject), "error message %s", "formatted") +func (a *Assertions) Implementsf(interfaceObject interface{}, object interface{}, msg string, args ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + Implementsf(a.t, interfaceObject, object, msg, args...) +} + +// InDelta asserts that the two numerals are within delta of each other. +// +// a.InDelta(math.Pi, 22/7.0, 0.01) +func (a *Assertions) InDelta(expected interface{}, actual interface{}, delta float64, msgAndArgs ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + InDelta(a.t, expected, actual, delta, msgAndArgs...) +} + +// InDeltaMapValues is the same as InDelta, but it compares all values between two maps. Both maps must have exactly the same keys. +func (a *Assertions) InDeltaMapValues(expected interface{}, actual interface{}, delta float64, msgAndArgs ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + InDeltaMapValues(a.t, expected, actual, delta, msgAndArgs...) +} + +// InDeltaMapValuesf is the same as InDelta, but it compares all values between two maps. Both maps must have exactly the same keys. +func (a *Assertions) InDeltaMapValuesf(expected interface{}, actual interface{}, delta float64, msg string, args ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + InDeltaMapValuesf(a.t, expected, actual, delta, msg, args...) +} + +// InDeltaSlice is the same as InDelta, except it compares two slices. +func (a *Assertions) InDeltaSlice(expected interface{}, actual interface{}, delta float64, msgAndArgs ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + InDeltaSlice(a.t, expected, actual, delta, msgAndArgs...) +} + +// InDeltaSlicef is the same as InDelta, except it compares two slices. +func (a *Assertions) InDeltaSlicef(expected interface{}, actual interface{}, delta float64, msg string, args ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + InDeltaSlicef(a.t, expected, actual, delta, msg, args...) +} + +// InDeltaf asserts that the two numerals are within delta of each other. +// +// a.InDeltaf(math.Pi, 22/7.0, 0.01, "error message %s", "formatted") +func (a *Assertions) InDeltaf(expected interface{}, actual interface{}, delta float64, msg string, args ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + InDeltaf(a.t, expected, actual, delta, msg, args...) +} + +// InEpsilon asserts that expected and actual have a relative error less than epsilon +func (a *Assertions) InEpsilon(expected interface{}, actual interface{}, epsilon float64, msgAndArgs ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + InEpsilon(a.t, expected, actual, epsilon, msgAndArgs...) +} + +// InEpsilonSlice is the same as InEpsilon, except it compares each value from two slices. +func (a *Assertions) InEpsilonSlice(expected interface{}, actual interface{}, epsilon float64, msgAndArgs ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + InEpsilonSlice(a.t, expected, actual, epsilon, msgAndArgs...) +} + +// InEpsilonSlicef is the same as InEpsilon, except it compares each value from two slices. +func (a *Assertions) InEpsilonSlicef(expected interface{}, actual interface{}, epsilon float64, msg string, args ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + InEpsilonSlicef(a.t, expected, actual, epsilon, msg, args...) +} + +// InEpsilonf asserts that expected and actual have a relative error less than epsilon +func (a *Assertions) InEpsilonf(expected interface{}, actual interface{}, epsilon float64, msg string, args ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + InEpsilonf(a.t, expected, actual, epsilon, msg, args...) +} + +// IsDecreasing asserts that the collection is decreasing +// +// a.IsDecreasing([]int{2, 1, 0}) +// a.IsDecreasing([]float{2, 1}) +// a.IsDecreasing([]string{"b", "a"}) +func (a *Assertions) IsDecreasing(object interface{}, msgAndArgs ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + IsDecreasing(a.t, object, msgAndArgs...) +} + +// IsDecreasingf asserts that the collection is decreasing +// +// a.IsDecreasingf([]int{2, 1, 0}, "error message %s", "formatted") +// a.IsDecreasingf([]float{2, 1}, "error message %s", "formatted") +// a.IsDecreasingf([]string{"b", "a"}, "error message %s", "formatted") +func (a *Assertions) IsDecreasingf(object interface{}, msg string, args ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + IsDecreasingf(a.t, object, msg, args...) +} + +// IsIncreasing asserts that the collection is increasing +// +// a.IsIncreasing([]int{1, 2, 3}) +// a.IsIncreasing([]float{1, 2}) +// a.IsIncreasing([]string{"a", "b"}) +func (a *Assertions) IsIncreasing(object interface{}, msgAndArgs ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + IsIncreasing(a.t, object, msgAndArgs...) +} + +// IsIncreasingf asserts that the collection is increasing +// +// a.IsIncreasingf([]int{1, 2, 3}, "error message %s", "formatted") +// a.IsIncreasingf([]float{1, 2}, "error message %s", "formatted") +// a.IsIncreasingf([]string{"a", "b"}, "error message %s", "formatted") +func (a *Assertions) IsIncreasingf(object interface{}, msg string, args ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + IsIncreasingf(a.t, object, msg, args...) +} + +// IsNonDecreasing asserts that the collection is not decreasing +// +// a.IsNonDecreasing([]int{1, 1, 2}) +// a.IsNonDecreasing([]float{1, 2}) +// a.IsNonDecreasing([]string{"a", "b"}) +func (a *Assertions) IsNonDecreasing(object interface{}, msgAndArgs ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + IsNonDecreasing(a.t, object, msgAndArgs...) +} + +// IsNonDecreasingf asserts that the collection is not decreasing +// +// a.IsNonDecreasingf([]int{1, 1, 2}, "error message %s", "formatted") +// a.IsNonDecreasingf([]float{1, 2}, "error message %s", "formatted") +// a.IsNonDecreasingf([]string{"a", "b"}, "error message %s", "formatted") +func (a *Assertions) IsNonDecreasingf(object interface{}, msg string, args ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + IsNonDecreasingf(a.t, object, msg, args...) +} + +// IsNonIncreasing asserts that the collection is not increasing +// +// a.IsNonIncreasing([]int{2, 1, 1}) +// a.IsNonIncreasing([]float{2, 1}) +// a.IsNonIncreasing([]string{"b", "a"}) +func (a *Assertions) IsNonIncreasing(object interface{}, msgAndArgs ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + IsNonIncreasing(a.t, object, msgAndArgs...) +} + +// IsNonIncreasingf asserts that the collection is not increasing +// +// a.IsNonIncreasingf([]int{2, 1, 1}, "error message %s", "formatted") +// a.IsNonIncreasingf([]float{2, 1}, "error message %s", "formatted") +// a.IsNonIncreasingf([]string{"b", "a"}, "error message %s", "formatted") +func (a *Assertions) IsNonIncreasingf(object interface{}, msg string, args ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + IsNonIncreasingf(a.t, object, msg, args...) +} + +// IsNotType asserts that the specified objects are not of the same type. +// +// a.IsNotType(&NotMyStruct{}, &MyStruct{}) +func (a *Assertions) IsNotType(theType interface{}, object interface{}, msgAndArgs ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + IsNotType(a.t, theType, object, msgAndArgs...) +} + +// IsNotTypef asserts that the specified objects are not of the same type. +// +// a.IsNotTypef(&NotMyStruct{}, &MyStruct{}, "error message %s", "formatted") +func (a *Assertions) IsNotTypef(theType interface{}, object interface{}, msg string, args ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + IsNotTypef(a.t, theType, object, msg, args...) +} + +// IsType asserts that the specified objects are of the same type. +// +// a.IsType(&MyStruct{}, &MyStruct{}) +func (a *Assertions) IsType(expectedType interface{}, object interface{}, msgAndArgs ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + IsType(a.t, expectedType, object, msgAndArgs...) +} + +// IsTypef asserts that the specified objects are of the same type. +// +// a.IsTypef(&MyStruct{}, &MyStruct{}, "error message %s", "formatted") +func (a *Assertions) IsTypef(expectedType interface{}, object interface{}, msg string, args ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + IsTypef(a.t, expectedType, object, msg, args...) +} + +// JSONEq asserts that two JSON strings are equivalent. +// +// a.JSONEq(`{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`) +func (a *Assertions) JSONEq(expected string, actual string, msgAndArgs ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + JSONEq(a.t, expected, actual, msgAndArgs...) +} + +// JSONEqf asserts that two JSON strings are equivalent. +// +// a.JSONEqf(`{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`, "error message %s", "formatted") +func (a *Assertions) JSONEqf(expected string, actual string, msg string, args ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + JSONEqf(a.t, expected, actual, msg, args...) +} + +// Len asserts that the specified object has specific length. +// Len also fails if the object has a type that len() not accept. +// +// a.Len(mySlice, 3) +func (a *Assertions) Len(object interface{}, length int, msgAndArgs ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + Len(a.t, object, length, msgAndArgs...) +} + +// Lenf asserts that the specified object has specific length. +// Lenf also fails if the object has a type that len() not accept. +// +// a.Lenf(mySlice, 3, "error message %s", "formatted") +func (a *Assertions) Lenf(object interface{}, length int, msg string, args ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + Lenf(a.t, object, length, msg, args...) +} + +// Less asserts that the first element is less than the second +// +// a.Less(1, 2) +// a.Less(float64(1), float64(2)) +// a.Less("a", "b") +func (a *Assertions) Less(e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + Less(a.t, e1, e2, msgAndArgs...) +} + +// LessOrEqual asserts that the first element is less than or equal to the second +// +// a.LessOrEqual(1, 2) +// a.LessOrEqual(2, 2) +// a.LessOrEqual("a", "b") +// a.LessOrEqual("b", "b") +func (a *Assertions) LessOrEqual(e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + LessOrEqual(a.t, e1, e2, msgAndArgs...) +} + +// LessOrEqualf asserts that the first element is less than or equal to the second +// +// a.LessOrEqualf(1, 2, "error message %s", "formatted") +// a.LessOrEqualf(2, 2, "error message %s", "formatted") +// a.LessOrEqualf("a", "b", "error message %s", "formatted") +// a.LessOrEqualf("b", "b", "error message %s", "formatted") +func (a *Assertions) LessOrEqualf(e1 interface{}, e2 interface{}, msg string, args ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + LessOrEqualf(a.t, e1, e2, msg, args...) +} + +// Lessf asserts that the first element is less than the second +// +// a.Lessf(1, 2, "error message %s", "formatted") +// a.Lessf(float64(1), float64(2), "error message %s", "formatted") +// a.Lessf("a", "b", "error message %s", "formatted") +func (a *Assertions) Lessf(e1 interface{}, e2 interface{}, msg string, args ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + Lessf(a.t, e1, e2, msg, args...) +} + +// Negative asserts that the specified element is negative +// +// a.Negative(-1) +// a.Negative(-1.23) +func (a *Assertions) Negative(e interface{}, msgAndArgs ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + Negative(a.t, e, msgAndArgs...) +} + +// Negativef asserts that the specified element is negative +// +// a.Negativef(-1, "error message %s", "formatted") +// a.Negativef(-1.23, "error message %s", "formatted") +func (a *Assertions) Negativef(e interface{}, msg string, args ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + Negativef(a.t, e, msg, args...) +} + +// Never asserts that the given condition doesn't satisfy in waitFor time, +// periodically checking the target function each tick. +// +// a.Never(func() bool { return false; }, time.Second, 10*time.Millisecond) +func (a *Assertions) Never(condition func() bool, waitFor time.Duration, tick time.Duration, msgAndArgs ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + Never(a.t, condition, waitFor, tick, msgAndArgs...) +} + +// Neverf asserts that the given condition doesn't satisfy in waitFor time, +// periodically checking the target function each tick. +// +// a.Neverf(func() bool { return false; }, time.Second, 10*time.Millisecond, "error message %s", "formatted") +func (a *Assertions) Neverf(condition func() bool, waitFor time.Duration, tick time.Duration, msg string, args ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + Neverf(a.t, condition, waitFor, tick, msg, args...) +} + +// Nil asserts that the specified object is nil. +// +// a.Nil(err) +func (a *Assertions) Nil(object interface{}, msgAndArgs ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + Nil(a.t, object, msgAndArgs...) +} + +// Nilf asserts that the specified object is nil. +// +// a.Nilf(err, "error message %s", "formatted") +func (a *Assertions) Nilf(object interface{}, msg string, args ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + Nilf(a.t, object, msg, args...) +} + +// NoDirExists checks whether a directory does not exist in the given path. +// It fails if the path points to an existing _directory_ only. +func (a *Assertions) NoDirExists(path string, msgAndArgs ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + NoDirExists(a.t, path, msgAndArgs...) +} + +// NoDirExistsf checks whether a directory does not exist in the given path. +// It fails if the path points to an existing _directory_ only. +func (a *Assertions) NoDirExistsf(path string, msg string, args ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + NoDirExistsf(a.t, path, msg, args...) +} + +// NoError asserts that a function returned no error (i.e. `nil`). +// +// actualObj, err := SomeFunction() +// if a.NoError(err) { +// assert.Equal(t, expectedObj, actualObj) +// } +func (a *Assertions) NoError(err error, msgAndArgs ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + NoError(a.t, err, msgAndArgs...) +} + +// NoErrorf asserts that a function returned no error (i.e. `nil`). +// +// actualObj, err := SomeFunction() +// if a.NoErrorf(err, "error message %s", "formatted") { +// assert.Equal(t, expectedObj, actualObj) +// } +func (a *Assertions) NoErrorf(err error, msg string, args ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + NoErrorf(a.t, err, msg, args...) +} + +// NoFileExists checks whether a file does not exist in a given path. It fails +// if the path points to an existing _file_ only. +func (a *Assertions) NoFileExists(path string, msgAndArgs ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + NoFileExists(a.t, path, msgAndArgs...) +} + +// NoFileExistsf checks whether a file does not exist in a given path. It fails +// if the path points to an existing _file_ only. +func (a *Assertions) NoFileExistsf(path string, msg string, args ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + NoFileExistsf(a.t, path, msg, args...) +} + +// NotContains asserts that the specified string, list(array, slice...) or map does NOT contain the +// specified substring or element. +// +// a.NotContains("Hello World", "Earth") +// a.NotContains(["Hello", "World"], "Earth") +// a.NotContains({"Hello": "World"}, "Earth") +func (a *Assertions) NotContains(s interface{}, contains interface{}, msgAndArgs ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + NotContains(a.t, s, contains, msgAndArgs...) +} + +// NotContainsf asserts that the specified string, list(array, slice...) or map does NOT contain the +// specified substring or element. +// +// a.NotContainsf("Hello World", "Earth", "error message %s", "formatted") +// a.NotContainsf(["Hello", "World"], "Earth", "error message %s", "formatted") +// a.NotContainsf({"Hello": "World"}, "Earth", "error message %s", "formatted") +func (a *Assertions) NotContainsf(s interface{}, contains interface{}, msg string, args ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + NotContainsf(a.t, s, contains, msg, args...) +} + +// NotElementsMatch asserts that the specified listA(array, slice...) is NOT equal to specified +// listB(array, slice...) ignoring the order of the elements. If there are duplicate elements, +// the number of appearances of each of them in both lists should not match. +// This is an inverse of ElementsMatch. +// +// a.NotElementsMatch([1, 1, 2, 3], [1, 1, 2, 3]) -> false +// +// a.NotElementsMatch([1, 1, 2, 3], [1, 2, 3]) -> true +// +// a.NotElementsMatch([1, 2, 3], [1, 2, 4]) -> true +func (a *Assertions) NotElementsMatch(listA interface{}, listB interface{}, msgAndArgs ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + NotElementsMatch(a.t, listA, listB, msgAndArgs...) +} + +// NotElementsMatchf asserts that the specified listA(array, slice...) is NOT equal to specified +// listB(array, slice...) ignoring the order of the elements. If there are duplicate elements, +// the number of appearances of each of them in both lists should not match. +// This is an inverse of ElementsMatch. +// +// a.NotElementsMatchf([1, 1, 2, 3], [1, 1, 2, 3], "error message %s", "formatted") -> false +// +// a.NotElementsMatchf([1, 1, 2, 3], [1, 2, 3], "error message %s", "formatted") -> true +// +// a.NotElementsMatchf([1, 2, 3], [1, 2, 4], "error message %s", "formatted") -> true +func (a *Assertions) NotElementsMatchf(listA interface{}, listB interface{}, msg string, args ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + NotElementsMatchf(a.t, listA, listB, msg, args...) +} + +// NotEmpty asserts that the specified object is NOT [Empty]. +// +// if a.NotEmpty(obj) { +// assert.Equal(t, "two", obj[1]) +// } +func (a *Assertions) NotEmpty(object interface{}, msgAndArgs ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + NotEmpty(a.t, object, msgAndArgs...) +} + +// NotEmptyf asserts that the specified object is NOT [Empty]. +// +// if a.NotEmptyf(obj, "error message %s", "formatted") { +// assert.Equal(t, "two", obj[1]) +// } +func (a *Assertions) NotEmptyf(object interface{}, msg string, args ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + NotEmptyf(a.t, object, msg, args...) +} + +// NotEqual asserts that the specified values are NOT equal. +// +// a.NotEqual(obj1, obj2) +// +// Pointer variable equality is determined based on the equality of the +// referenced values (as opposed to the memory addresses). +func (a *Assertions) NotEqual(expected interface{}, actual interface{}, msgAndArgs ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + NotEqual(a.t, expected, actual, msgAndArgs...) +} + +// NotEqualValues asserts that two objects are not equal even when converted to the same type +// +// a.NotEqualValues(obj1, obj2) +func (a *Assertions) NotEqualValues(expected interface{}, actual interface{}, msgAndArgs ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + NotEqualValues(a.t, expected, actual, msgAndArgs...) +} + +// NotEqualValuesf asserts that two objects are not equal even when converted to the same type +// +// a.NotEqualValuesf(obj1, obj2, "error message %s", "formatted") +func (a *Assertions) NotEqualValuesf(expected interface{}, actual interface{}, msg string, args ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + NotEqualValuesf(a.t, expected, actual, msg, args...) +} + +// NotEqualf asserts that the specified values are NOT equal. +// +// a.NotEqualf(obj1, obj2, "error message %s", "formatted") +// +// Pointer variable equality is determined based on the equality of the +// referenced values (as opposed to the memory addresses). +func (a *Assertions) NotEqualf(expected interface{}, actual interface{}, msg string, args ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + NotEqualf(a.t, expected, actual, msg, args...) +} + +// NotErrorAs asserts that none of the errors in err's chain matches target, +// but if so, sets target to that error value. +func (a *Assertions) NotErrorAs(err error, target interface{}, msgAndArgs ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + NotErrorAs(a.t, err, target, msgAndArgs...) +} + +// NotErrorAsf asserts that none of the errors in err's chain matches target, +// but if so, sets target to that error value. +func (a *Assertions) NotErrorAsf(err error, target interface{}, msg string, args ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + NotErrorAsf(a.t, err, target, msg, args...) +} + +// NotErrorIs asserts that none of the errors in err's chain matches target. +// This is a wrapper for errors.Is. +func (a *Assertions) NotErrorIs(err error, target error, msgAndArgs ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + NotErrorIs(a.t, err, target, msgAndArgs...) +} + +// NotErrorIsf asserts that none of the errors in err's chain matches target. +// This is a wrapper for errors.Is. +func (a *Assertions) NotErrorIsf(err error, target error, msg string, args ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + NotErrorIsf(a.t, err, target, msg, args...) +} + +// NotImplements asserts that an object does not implement the specified interface. +// +// a.NotImplements((*MyInterface)(nil), new(MyObject)) +func (a *Assertions) NotImplements(interfaceObject interface{}, object interface{}, msgAndArgs ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + NotImplements(a.t, interfaceObject, object, msgAndArgs...) +} + +// NotImplementsf asserts that an object does not implement the specified interface. +// +// a.NotImplementsf((*MyInterface)(nil), new(MyObject), "error message %s", "formatted") +func (a *Assertions) NotImplementsf(interfaceObject interface{}, object interface{}, msg string, args ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + NotImplementsf(a.t, interfaceObject, object, msg, args...) +} + +// NotNil asserts that the specified object is not nil. +// +// a.NotNil(err) +func (a *Assertions) NotNil(object interface{}, msgAndArgs ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + NotNil(a.t, object, msgAndArgs...) +} + +// NotNilf asserts that the specified object is not nil. +// +// a.NotNilf(err, "error message %s", "formatted") +func (a *Assertions) NotNilf(object interface{}, msg string, args ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + NotNilf(a.t, object, msg, args...) +} + +// NotPanics asserts that the code inside the specified PanicTestFunc does NOT panic. +// +// a.NotPanics(func(){ RemainCalm() }) +func (a *Assertions) NotPanics(f assert.PanicTestFunc, msgAndArgs ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + NotPanics(a.t, f, msgAndArgs...) +} + +// NotPanicsf asserts that the code inside the specified PanicTestFunc does NOT panic. +// +// a.NotPanicsf(func(){ RemainCalm() }, "error message %s", "formatted") +func (a *Assertions) NotPanicsf(f assert.PanicTestFunc, msg string, args ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + NotPanicsf(a.t, f, msg, args...) +} + +// NotRegexp asserts that a specified regexp does not match a string. +// +// a.NotRegexp(regexp.MustCompile("starts"), "it's starting") +// a.NotRegexp("^start", "it's not starting") +func (a *Assertions) NotRegexp(rx interface{}, str interface{}, msgAndArgs ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + NotRegexp(a.t, rx, str, msgAndArgs...) +} + +// NotRegexpf asserts that a specified regexp does not match a string. +// +// a.NotRegexpf(regexp.MustCompile("starts"), "it's starting", "error message %s", "formatted") +// a.NotRegexpf("^start", "it's not starting", "error message %s", "formatted") +func (a *Assertions) NotRegexpf(rx interface{}, str interface{}, msg string, args ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + NotRegexpf(a.t, rx, str, msg, args...) +} + +// NotSame asserts that two pointers do not reference the same object. +// +// a.NotSame(ptr1, ptr2) +// +// Both arguments must be pointer variables. Pointer variable sameness is +// determined based on the equality of both type and value. +func (a *Assertions) NotSame(expected interface{}, actual interface{}, msgAndArgs ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + NotSame(a.t, expected, actual, msgAndArgs...) +} + +// NotSamef asserts that two pointers do not reference the same object. +// +// a.NotSamef(ptr1, ptr2, "error message %s", "formatted") +// +// Both arguments must be pointer variables. Pointer variable sameness is +// determined based on the equality of both type and value. +func (a *Assertions) NotSamef(expected interface{}, actual interface{}, msg string, args ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + NotSamef(a.t, expected, actual, msg, args...) +} + +// NotSubset asserts that the list (array, slice, or map) does NOT contain all +// elements given in the subset (array, slice, or map). +// Map elements are key-value pairs unless compared with an array or slice where +// only the map key is evaluated. +// +// a.NotSubset([1, 3, 4], [1, 2]) +// a.NotSubset({"x": 1, "y": 2}, {"z": 3}) +// a.NotSubset([1, 3, 4], {1: "one", 2: "two"}) +// a.NotSubset({"x": 1, "y": 2}, ["z"]) +func (a *Assertions) NotSubset(list interface{}, subset interface{}, msgAndArgs ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + NotSubset(a.t, list, subset, msgAndArgs...) +} + +// NotSubsetf asserts that the list (array, slice, or map) does NOT contain all +// elements given in the subset (array, slice, or map). +// Map elements are key-value pairs unless compared with an array or slice where +// only the map key is evaluated. +// +// a.NotSubsetf([1, 3, 4], [1, 2], "error message %s", "formatted") +// a.NotSubsetf({"x": 1, "y": 2}, {"z": 3}, "error message %s", "formatted") +// a.NotSubsetf([1, 3, 4], {1: "one", 2: "two"}, "error message %s", "formatted") +// a.NotSubsetf({"x": 1, "y": 2}, ["z"], "error message %s", "formatted") +func (a *Assertions) NotSubsetf(list interface{}, subset interface{}, msg string, args ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + NotSubsetf(a.t, list, subset, msg, args...) +} + +// NotZero asserts that i is not the zero value for its type. +func (a *Assertions) NotZero(i interface{}, msgAndArgs ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + NotZero(a.t, i, msgAndArgs...) +} + +// NotZerof asserts that i is not the zero value for its type. +func (a *Assertions) NotZerof(i interface{}, msg string, args ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + NotZerof(a.t, i, msg, args...) +} + +// Panics asserts that the code inside the specified PanicTestFunc panics. +// +// a.Panics(func(){ GoCrazy() }) +func (a *Assertions) Panics(f assert.PanicTestFunc, msgAndArgs ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + Panics(a.t, f, msgAndArgs...) +} + +// PanicsWithError asserts that the code inside the specified PanicTestFunc +// panics, and that the recovered panic value is an error that satisfies the +// EqualError comparison. +// +// a.PanicsWithError("crazy error", func(){ GoCrazy() }) +func (a *Assertions) PanicsWithError(errString string, f assert.PanicTestFunc, msgAndArgs ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + PanicsWithError(a.t, errString, f, msgAndArgs...) +} + +// PanicsWithErrorf asserts that the code inside the specified PanicTestFunc +// panics, and that the recovered panic value is an error that satisfies the +// EqualError comparison. +// +// a.PanicsWithErrorf("crazy error", func(){ GoCrazy() }, "error message %s", "formatted") +func (a *Assertions) PanicsWithErrorf(errString string, f assert.PanicTestFunc, msg string, args ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + PanicsWithErrorf(a.t, errString, f, msg, args...) +} + +// PanicsWithValue asserts that the code inside the specified PanicTestFunc panics, and that +// the recovered panic value equals the expected panic value. +// +// a.PanicsWithValue("crazy error", func(){ GoCrazy() }) +func (a *Assertions) PanicsWithValue(expected interface{}, f assert.PanicTestFunc, msgAndArgs ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + PanicsWithValue(a.t, expected, f, msgAndArgs...) +} + +// PanicsWithValuef asserts that the code inside the specified PanicTestFunc panics, and that +// the recovered panic value equals the expected panic value. +// +// a.PanicsWithValuef("crazy error", func(){ GoCrazy() }, "error message %s", "formatted") +func (a *Assertions) PanicsWithValuef(expected interface{}, f assert.PanicTestFunc, msg string, args ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + PanicsWithValuef(a.t, expected, f, msg, args...) +} + +// Panicsf asserts that the code inside the specified PanicTestFunc panics. +// +// a.Panicsf(func(){ GoCrazy() }, "error message %s", "formatted") +func (a *Assertions) Panicsf(f assert.PanicTestFunc, msg string, args ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + Panicsf(a.t, f, msg, args...) +} + +// Positive asserts that the specified element is positive +// +// a.Positive(1) +// a.Positive(1.23) +func (a *Assertions) Positive(e interface{}, msgAndArgs ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + Positive(a.t, e, msgAndArgs...) +} + +// Positivef asserts that the specified element is positive +// +// a.Positivef(1, "error message %s", "formatted") +// a.Positivef(1.23, "error message %s", "formatted") +func (a *Assertions) Positivef(e interface{}, msg string, args ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + Positivef(a.t, e, msg, args...) +} + +// Regexp asserts that a specified regexp matches a string. +// +// a.Regexp(regexp.MustCompile("start"), "it's starting") +// a.Regexp("start...$", "it's not starting") +func (a *Assertions) Regexp(rx interface{}, str interface{}, msgAndArgs ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + Regexp(a.t, rx, str, msgAndArgs...) +} + +// Regexpf asserts that a specified regexp matches a string. +// +// a.Regexpf(regexp.MustCompile("start"), "it's starting", "error message %s", "formatted") +// a.Regexpf("start...$", "it's not starting", "error message %s", "formatted") +func (a *Assertions) Regexpf(rx interface{}, str interface{}, msg string, args ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + Regexpf(a.t, rx, str, msg, args...) +} + +// Same asserts that two pointers reference the same object. +// +// a.Same(ptr1, ptr2) +// +// Both arguments must be pointer variables. Pointer variable sameness is +// determined based on the equality of both type and value. +func (a *Assertions) Same(expected interface{}, actual interface{}, msgAndArgs ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + Same(a.t, expected, actual, msgAndArgs...) +} + +// Samef asserts that two pointers reference the same object. +// +// a.Samef(ptr1, ptr2, "error message %s", "formatted") +// +// Both arguments must be pointer variables. Pointer variable sameness is +// determined based on the equality of both type and value. +func (a *Assertions) Samef(expected interface{}, actual interface{}, msg string, args ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + Samef(a.t, expected, actual, msg, args...) +} + +// Subset asserts that the list (array, slice, or map) contains all elements +// given in the subset (array, slice, or map). +// Map elements are key-value pairs unless compared with an array or slice where +// only the map key is evaluated. +// +// a.Subset([1, 2, 3], [1, 2]) +// a.Subset({"x": 1, "y": 2}, {"x": 1}) +// a.Subset([1, 2, 3], {1: "one", 2: "two"}) +// a.Subset({"x": 1, "y": 2}, ["x"]) +func (a *Assertions) Subset(list interface{}, subset interface{}, msgAndArgs ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + Subset(a.t, list, subset, msgAndArgs...) +} + +// Subsetf asserts that the list (array, slice, or map) contains all elements +// given in the subset (array, slice, or map). +// Map elements are key-value pairs unless compared with an array or slice where +// only the map key is evaluated. +// +// a.Subsetf([1, 2, 3], [1, 2], "error message %s", "formatted") +// a.Subsetf({"x": 1, "y": 2}, {"x": 1}, "error message %s", "formatted") +// a.Subsetf([1, 2, 3], {1: "one", 2: "two"}, "error message %s", "formatted") +// a.Subsetf({"x": 1, "y": 2}, ["x"], "error message %s", "formatted") +func (a *Assertions) Subsetf(list interface{}, subset interface{}, msg string, args ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + Subsetf(a.t, list, subset, msg, args...) +} + +// True asserts that the specified value is true. +// +// a.True(myBool) +func (a *Assertions) True(value bool, msgAndArgs ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + True(a.t, value, msgAndArgs...) +} + +// Truef asserts that the specified value is true. +// +// a.Truef(myBool, "error message %s", "formatted") +func (a *Assertions) Truef(value bool, msg string, args ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + Truef(a.t, value, msg, args...) +} + +// WithinDuration asserts that the two times are within duration delta of each other. +// +// a.WithinDuration(time.Now(), time.Now(), 10*time.Second) +func (a *Assertions) WithinDuration(expected time.Time, actual time.Time, delta time.Duration, msgAndArgs ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + WithinDuration(a.t, expected, actual, delta, msgAndArgs...) +} + +// WithinDurationf asserts that the two times are within duration delta of each other. +// +// a.WithinDurationf(time.Now(), time.Now(), 10*time.Second, "error message %s", "formatted") +func (a *Assertions) WithinDurationf(expected time.Time, actual time.Time, delta time.Duration, msg string, args ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + WithinDurationf(a.t, expected, actual, delta, msg, args...) +} + +// WithinRange asserts that a time is within a time range (inclusive). +// +// a.WithinRange(time.Now(), time.Now().Add(-time.Second), time.Now().Add(time.Second)) +func (a *Assertions) WithinRange(actual time.Time, start time.Time, end time.Time, msgAndArgs ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + WithinRange(a.t, actual, start, end, msgAndArgs...) +} + +// WithinRangef asserts that a time is within a time range (inclusive). +// +// a.WithinRangef(time.Now(), time.Now().Add(-time.Second), time.Now().Add(time.Second), "error message %s", "formatted") +func (a *Assertions) WithinRangef(actual time.Time, start time.Time, end time.Time, msg string, args ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + WithinRangef(a.t, actual, start, end, msg, args...) +} + +// YAMLEq asserts that two YAML strings are equivalent. +func (a *Assertions) YAMLEq(expected string, actual string, msgAndArgs ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + YAMLEq(a.t, expected, actual, msgAndArgs...) +} + +// YAMLEqf asserts that two YAML strings are equivalent. +func (a *Assertions) YAMLEqf(expected string, actual string, msg string, args ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + YAMLEqf(a.t, expected, actual, msg, args...) +} + +// Zero asserts that i is the zero value for its type. +func (a *Assertions) Zero(i interface{}, msgAndArgs ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + Zero(a.t, i, msgAndArgs...) +} + +// Zerof asserts that i is the zero value for its type. +func (a *Assertions) Zerof(i interface{}, msg string, args ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + Zerof(a.t, i, msg, args...) +} diff --git a/vendor/github.com/stretchr/testify/require/require_forward.go.tmpl b/vendor/github.com/stretchr/testify/require/require_forward.go.tmpl new file mode 100644 index 000000000..54124df1d --- /dev/null +++ b/vendor/github.com/stretchr/testify/require/require_forward.go.tmpl @@ -0,0 +1,5 @@ +{{.CommentWithoutT "a"}} +func (a *Assertions) {{.DocInfo.Name}}({{.Params}}) { + if h, ok := a.t.(tHelper); ok { h.Helper() } + {{.DocInfo.Name}}(a.t, {{.ForwardedParams}}) +} diff --git a/vendor/github.com/stretchr/testify/require/requirements.go b/vendor/github.com/stretchr/testify/require/requirements.go new file mode 100644 index 000000000..6b7ce929e --- /dev/null +++ b/vendor/github.com/stretchr/testify/require/requirements.go @@ -0,0 +1,29 @@ +package require + +// TestingT is an interface wrapper around *testing.T +type TestingT interface { + Errorf(format string, args ...interface{}) + FailNow() +} + +type tHelper = interface { + Helper() +} + +// ComparisonAssertionFunc is a common function prototype when comparing two values. Can be useful +// for table driven tests. +type ComparisonAssertionFunc func(TestingT, interface{}, interface{}, ...interface{}) + +// ValueAssertionFunc is a common function prototype when validating a single value. Can be useful +// for table driven tests. +type ValueAssertionFunc func(TestingT, interface{}, ...interface{}) + +// BoolAssertionFunc is a common function prototype when validating a bool value. Can be useful +// for table driven tests. +type BoolAssertionFunc func(TestingT, bool, ...interface{}) + +// ErrorAssertionFunc is a common function prototype when validating an error value. Can be useful +// for table driven tests. +type ErrorAssertionFunc func(TestingT, error, ...interface{}) + +//go:generate sh -c "cd ../_codegen && go build && cd - && ../_codegen/_codegen -output-package=require -template=require.go.tmpl -include-format-funcs" diff --git a/vendor/github.com/twpayne/go-geom/.gitignore b/vendor/github.com/twpayne/go-geom/.gitignore new file mode 100644 index 000000000..73fba366f --- /dev/null +++ b/vendor/github.com/twpayne/go-geom/.gitignore @@ -0,0 +1,2 @@ +/bin +/coverage.out diff --git a/vendor/github.com/twpayne/go-geom/.golangci.yml b/vendor/github.com/twpayne/go-geom/.golangci.yml new file mode 100644 index 000000000..ad896dcf4 --- /dev/null +++ b/vendor/github.com/twpayne/go-geom/.golangci.yml @@ -0,0 +1,174 @@ +version: "2" +run: + go: "1.22" +linters: + enable: + - asasalint + - asciicheck + - bidichk + - bodyclose + - canonicalheader + - containedctx + - contextcheck + - copyloopvar + - decorder + - dogsled + - dupl + - dupword + - durationcheck + - err113 + - errchkjson + - errorlint + - exptostd + - fatcontext + - forbidigo + - forcetypeassert + - ginkgolinter + - gocheckcompilerdirectives + - gochecknoinits + - gochecksumtype + - gocritic + - godot + - goheader + - gomoddirectives + - gomodguard + - goprintffuncname + - gosmopolitan + - grouper + - iface + - importas + - inamedparam + - interfacebloat + - intrange + - ireturn + - loggercheck + - makezero + - mirror + - misspell + - musttag + - nakedret + - nilerr + - nilnesserr + - nilnil + - noctx + - nolintlint + - nosprintfhostport + - perfsprint + - prealloc + - predeclared + - promlinter + - protogetter + - reassign + - recvcheck + - revive + - rowserrcheck + - sloglint + - spancheck + - sqlclosecheck + - staticcheck + - tagalign + - tagliatelle + - testableexamples + - testifylint + - thelper + - tparallel + - unconvert + - unparam + - usestdlibvars + - usetesting + - wastedassign + - whitespace + - zerologlint + disable: + - cyclop + - depguard + - errname + - exhaustive + - exhaustruct + - funlen + - gochecknoglobals + - gocognit + - goconst + - gocyclo + - godox + - gosec + - lll + - maintidx + - mnd + - nestif + - nlreturn + - nonamedreturns + - paralleltest + - testpackage + - varnamelen + - wrapcheck + - wsl + settings: + ireturn: + allow: + - error + - stdlib + - ^github\.com/twpayne/go-geom\.T$ + - ^github\.com/twpayne/go-kml/v3\.Element$ + exclusions: + generated: lax + presets: + - comments + - common-false-positives + - legacy + - std-error-handling + rules: + - linters: + - dupl + - scopelint + path: _test\.go + - linters: + - godot + path: ^bigxy/ + - linters: + - gocritic + path: _example_test\.go$ + - linters: + - godot + path: ^sorting/ + - linters: + - godot + path: ^transform/ + - linters: + - gocritic + - godot + path: ^xy/ + - linters: + - godot + path: ^xyz/ + - linters: + - err113 + text: do not define dynamic errors, use wrapped static errors instead + - linters: + - forbidigo + path: ^internal/cmd/ + paths: + - third_party$ + - builtin$ + - examples$ +formatters: + enable: + - gci + - gofmt + - gofumpt + - goimports + settings: + gci: + sections: + - standard + - default + - prefix(github.com/twpayne/go-geom) + goimports: + local-prefixes: + - github.com/twpayne/go-geom + exclusions: + generated: lax + paths: + - third_party$ + - builtin$ + - examples$ diff --git a/vendor/github.com/twpayne/go-geom/INTERNALS.md b/vendor/github.com/twpayne/go-geom/INTERNALS.md new file mode 100644 index 000000000..8e1530105 --- /dev/null +++ b/vendor/github.com/twpayne/go-geom/INTERNALS.md @@ -0,0 +1,119 @@ +# `go-geom` Internals + + +## Introduction + +`go-geom` attempts to implement efficient, standards-compatible OGC-style +geometries for Go. This document describes some of the key ideas required to +understand its implementation. + +`go-geom` is an evolution of the techniques developed for the [OpenLayers 3 +geometry library](http://openlayers.org/en/master/apidoc/ol.geom.html), +designed to efficiently handle large geometries in a resource-constrained, +garbage-collected environment, but adapted to the Go programming language and +its type system. + + +## Type flexibility + +There are three priniciple 2D geometry types: `Point`s, `LineString`s, and +`Polygon`s. + +OGC extends these three into collections of the principle types: `MultiPoint`, +`MultiLineString`, and `MultiPolygon`. This gives 3 geometry types * 2 +multi-or-not-multi = 6 combinations. + +On top of this, there are multiple combinations of dimensions, e.g. 2D (XY), 3D +(XYZ), 2D varying over time/distance (XYM), and 3D varying over time/distance +(XYZM). + +3 geometry types * 2 multi-or-not-multi * 4 different dimensionalities = 24 +distinct types. + +Go has neither generics, nor macros, nor a rich type system. `go-geom` attempts +to manage this combinatorial explosion while maintaining an idiomatic Go API, +implementation efficiency. and high runtime performance. + + +## Structural similarity + +`go-geom` exploits structural similarity between different geometry types to +share code. Consider: + +0. A `Point` consists of a single coordinate. This single coordinate is a + `geom.Coord`. + +1. A `LineString`, `LinearRing`, and `MultiPoint` consist of a collection of + coordinates. They all have different semantics (a `LineString` is ordered, +a `LinearRing` is ordered and closed, a `MultiPoint` is neither ordered nor +closed) yet all share a similar underlying structure. + +2. A `Polygon` and a `MultiLineString` are a collection of collections of + coordinates. Again, the semantics vary: a `Polygon` is a weakly ordered +collection of `LinearRing`s (the first `LinearRing` is the outer boundary, +subsequent `LinearRing`s are inner boundaries (holes)). A `MultiLineString` is +an unordered collection of `LineString`s. + +3. A `MultiPolygon` is an unordered collection of `Polygon`s. + +`go-geom` makes these structural similarities explicit: + +0. A `Point` is a `geom.Coord`, also known as `geom0`. + +1. `LineString`s, `LinearRing`s, and and `MultiPoint`s are `[]geom.Coord`, also + known as `geom1`. + +2. `Polygon`s and `MultiLineString`s are `[][]geom.Coord`, also known as + `geom2`. + +3. `MultiPolygon`s are `[][][]geom.Coord`, also known as `geom3`. + +Under the hood, `go-geom` uses Go's structural composition to share common +code. For example, `LineString`s, `LinearRing`s, and `MultiPoint`s all embed a +single anonymous `geom1`. + +The hierarchy of embedding is: + + geom0 + +- geom1 + +- geom2 + +- geom3 + +Note that `geom2` and `geom3` independently embed `geom1`. Despite their +numerical ordering, `geom2` and `geom3` are separate branches of the geometry +tree. + +We can exploit these structural similarities to share code. For example, +calculating the bounds of a geometry only involves finding the minimum and +maximum values in each dimension, which can be found by iterating over all +coordinates in the geometry. The semantic meaning of these coordinates - +whether they're points on a line, or points on a polygon inner or outer +boundary, or something else - does not matter. Therefore, as long as we can +treat any geometry as a collection of coordinates, we can use the same code to +calculate bounds across all geometry types. + +Similarly, we can exploit higher-level similarities. For example, the "length" +of a `MultiLineString` is the sum of the lengths of its component +`LineString`s, and the "length" (perimeter) of a `Polygon` is the sum of the +lengths (perimeters) of its component `LinearRing`s. + + +## Efficient + +At the time of writing (2016), CPUs are fast, cache hits are quite fast, cache +misses are slow, memory is very slow, and garbage collection takes an eternity. + +Typical geometry libraries use multiple levels of nested arrays, e.g. a +`[][][]float64` for a polygon. This requires multiple levels of indirection to +access a single coordinate value, and as different sub-arrays might be stored +in different parts of memory, is more likely to lead to cache miss. + +In contrast, `go-geom` packs all the coordinates for a geometry, whatever its +structure, into a single `[]float64`. The underlying array is stored in a +single blob of memory. Most operations do a linear scan over the array, which +is particularly cache friendly. There are also fewer objects for the garbage +collector to manage. + +Parts of the underlying array can be shared between multitple objects. For +example, retrieving the outer ring of a `Polygon` returns a `LinearRing` that +references the coordinates of the `Polygon`. No coordinate data are copied. diff --git a/vendor/github.com/twpayne/go-geom/LICENSE b/vendor/github.com/twpayne/go-geom/LICENSE new file mode 100644 index 000000000..3bc8155e4 --- /dev/null +++ b/vendor/github.com/twpayne/go-geom/LICENSE @@ -0,0 +1,23 @@ +Copyright (c) 2013, Tom Payne +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + + Redistributions in binary form must reproduce the above copyright notice, this + list of conditions and the following disclaimer in the documentation and/or + other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \ No newline at end of file diff --git a/vendor/github.com/twpayne/go-geom/Makefile b/vendor/github.com/twpayne/go-geom/Makefile new file mode 100644 index 000000000..17994aa2d --- /dev/null +++ b/vendor/github.com/twpayne/go-geom/Makefile @@ -0,0 +1,56 @@ +GO?=go +GOFUMPT_VERSION=$(shell awk '/GOFUMPT_VERSION:/ { print $$2 }' .github/workflows/main.yml) +GOLANGCI_LINT_VERSION=$(shell awk '/GOLANGCI_LINT_VERSION:/ { print $$2 }' .github/workflows/main.yml) + +.PHONY: all +all: test lint + +.PHONY: test +test: + ${GO} test ./... + +.PHONY: coverage.out +coverage.out: + ${GO} test -covermode=count --coverprofile=$@ ./... + +.PHONY: lint +lint: ensure-golangci-lint + ./bin/golangci-lint run + +.PHONY: format +format: ensure-gofumpt + find . -name \*.go | xargs ./bin/gofumpt -extra -w + +.PHONY: generate +generate: ensure-goderive ensure-goyacc + PATH=$$PATH:$(shell pwd)/bin ${GO} generate ./... + +.PHONY: install-tools +install-tools: ensure-goderive ensure-gofumpt ensure-golangci-lint ensure-goyacc + +.PHONY: ensure-goderive +ensure-goderive: + if [ ! -x bin/goderive ] ; then \ + mkdir -p bin ; \ + GOBIN=$(shell pwd)/bin ${GO} install "github.com/awalterschulze/goderive@latest" ; \ + fi + +.PHONY: ensure-gofumpt +ensure-gofumpt: + if [ ! -x bin/gofumpt ] || ( ./bin/gofumpt --version | grep -Fqv "v${GOFUMPT_VERSION}" ) ; then \ + mkdir -p bin ; \ + GOBIN=$(shell pwd)/bin ${GO} install "mvdan.cc/gofumpt@v${GOFUMPT_VERSION}" ; \ + fi + +.PHONY: ensure-golangci-lint +ensure-golangci-lint: + if [ ! -x bin/golangci-lint ] || ( ./bin/golangci-lint --version | grep -Fqv "version ${GOLANGCI_LINT_VERSION}" ) ; then \ + curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- v${GOLANGCI_LINT_VERSION} ; \ + fi + +.PHONY: ensure-goyacc +ensure-goyacc: + if [ ! -x bin/goyacc ] ; then \ + mkdir -p bin ; \ + GOBIN=$(shell pwd)/bin ${GO} install "golang.org/x/tools/cmd/goyacc@latest" ; \ + fi diff --git a/vendor/github.com/twpayne/go-geom/README.md b/vendor/github.com/twpayne/go-geom/README.md new file mode 100644 index 000000000..7ca719fdd --- /dev/null +++ b/vendor/github.com/twpayne/go-geom/README.md @@ -0,0 +1,75 @@ +# go-geom + +[![PkgGoDev](https://pkg.go.dev/badge/github.com/twpayne/go-geom)](https://pkg.go.dev/github.com/twpayne/go-geom) +[![Go Report Card](https://goreportcard.com/badge/github.com/twpayne/go-geom)](https://goreportcard.com/report/github.com/twpayne/go-geom) + +Package `geom` implements efficient geometry types for geospatial applications. + +## Key features + +* OpenGeo Consortium-style geometries. +* Support for 2D and 3D geometries, measures (time and/or distance), and + unlimited extra dimensions. +* Encoding and decoding of common geometry formats (GeoJSON, KML, WKB, and + others) including [`sql.Scanner`](https://pkg.go.dev/database/sql#Scanner) and + [`driver.Value`](https://pkg.go.dev/database/sql/driver#Value) interface + implementations for easy database integration. +* [2D](https://pkg.go.dev/github.com/twpayne/go-geom/xy) and + [3D](https://pkg.go.dev/github.com/twpayne/go-geom/xyz) topology functions. +* Efficient, cache-friendly [internal representation](INTERNALS.md). +* Optional protection against malicious or malformed inputs. + +## Examples + +* [PostGIS, EWKB, and GeoJSON](https://github.com/twpayne/go-geom/tree/master/examples/postgis). + +## Detailed features + +### Geometry types + +* [Point](https://pkg.go.dev/github.com/twpayne/go-geom#Point) +* [LineString](https://pkg.go.dev/github.com/twpayne/go-geom#LineString) +* [Polygon](https://pkg.go.dev/github.com/twpayne/go-geom#Polygon) +* [MultiPoint](https://pkg.go.dev/github.com/twpayne/go-geom#MultiPoint) +* [MultiLineString](https://pkg.go.dev/github.com/twpayne/go-geom#MultiLineString) +* [MultiPolygon](https://pkg.go.dev/github.com/twpayne/go-geom#MultiPolygon) +* [GeometryCollection](https://pkg.go.dev/github.com/twpayne/go-geom#GeometryCollection) + +### Encoding and decoding + +* [GeoJSON](https://pkg.go.dev/github.com/twpayne/go-geom/encoding/geojson) +* [IGC](https://pkg.go.dev/github.com/twpayne/go-geom/encoding/igc) +* [KML](https://pkg.go.dev/github.com/twpayne/go-geom/encoding/kml) (encoding only) +* [WKB](https://pkg.go.dev/github.com/twpayne/go-geom/encoding/wkb) +* [EWKB](https://pkg.go.dev/github.com/twpayne/go-geom/encoding/ewkb) +* [WKT](https://pkg.go.dev/github.com/twpayne/go-geom/encoding/wkt) (encoding only) +* [WKB Hex](https://pkg.go.dev/github.com/twpayne/go-geom/encoding/wkbhex) +* [EWKB Hex](https://pkg.go.dev/github.com/twpayne/go-geom/encoding/ewkbhex) + +### Geometry functions + +* [XY](https://pkg.go.dev/github.com/twpayne/go-geom/xy) 2D geometry functions +* [XYZ](https://pkg.go.dev/github.com/twpayne/go-geom/xyz) 3D geometry functions + +## Protection against malicious or malformed inputs + +The WKB and EWKB formats encode geometry sizes, and memory is allocated for +those geometries. If the input is malicious or malformed, the memory allocation +can be very large, leading to a memory starvation denial-of-service attack +against the server. For example, a client might send a `MultiPoint` with header +indicating that it contains 2^32-1 points. This will result in the server +reading that geometry to allocate 2 × `sizeof(float64)` × (2^32-1) = 64GB of +memory to store those points. By default, malicious or malformed input +protection is disabled, but can be enabled by setting positive values for +`wkbcommon.MaxGeometryElements`. + +## Related libraries + +* [github.com/twpayne/go-gpx](https://github.com/twpayne/go-gpx) GPX encoding and decoding +* [github.com/twpayne/go-kml](https://github.com/twpayne/go-kml) KML encoding +* [github.com/twpayne/go-polyline](https://github.com/twpayne/go-polyline) Google Maps Polyline encoding and decoding +* [github.com/twpayne/go-vali](https://github.com/twpayne/go-vali) IGC validation + +## License + +BSD-2-Clause \ No newline at end of file diff --git a/vendor/github.com/twpayne/go-geom/bounds.go b/vendor/github.com/twpayne/go-geom/bounds.go new file mode 100644 index 000000000..db4e2b02a --- /dev/null +++ b/vendor/github.com/twpayne/go-geom/bounds.go @@ -0,0 +1,178 @@ +package geom + +import "math" + +// A Bounds represents a multi-dimensional bounding box. +type Bounds struct { + layout Layout + min Coord + max Coord +} + +// NewBounds creates a new Bounds. +func NewBounds(layout Layout) *Bounds { + stride := layout.Stride() + minValue, maxValue := make(Coord, stride), make(Coord, stride) + for i := range stride { + minValue[i], maxValue[i] = math.Inf(1), math.Inf(-1) + } + return &Bounds{ + layout: layout, + min: minValue, + max: maxValue, + } +} + +// Clone returns a deep copy of b. +func (b *Bounds) Clone() *Bounds { + return deriveCloneBounds(b) +} + +// Extend extends b to include geometry g. +func (b *Bounds) Extend(g T) *Bounds { + b.extendLayout(g.Layout()) + if b.layout == XYZM && g.Layout() == XYM { + return b.extendXYZMFlatCoordsWithXYM(g.FlatCoords(), 0, len(g.FlatCoords())) + } + return b.extendFlatCoords(g.FlatCoords(), 0, len(g.FlatCoords()), g.Stride()) +} + +// IsEmpty returns true if b is empty. +func (b *Bounds) IsEmpty() bool { + if b.layout == NoLayout { + return true + } + for i, stride := 0, b.layout.Stride(); i < stride; i++ { + if b.max[i] < b.min[i] { + return true + } + } + return false +} + +// Layout returns b's layout. +func (b *Bounds) Layout() Layout { + return b.layout +} + +// Max returns the maximum value in dimension dim. +func (b *Bounds) Max(dim int) float64 { + return b.max[dim] +} + +// Min returns the minimum value in dimension dim. +func (b *Bounds) Min(dim int) float64 { + return b.min[dim] +} + +// Overlaps returns true if b overlaps b2 in layout. +func (b *Bounds) Overlaps(layout Layout, b2 *Bounds) bool { + for i, stride := 0, layout.Stride(); i < stride; i++ { + if b.min[i] > b2.max[i] || b.max[i] < b2.min[i] { + return false + } + } + return true +} + +// Polygon returns b as a two-dimensional Polygon. +func (b *Bounds) Polygon() *Polygon { + if b.IsEmpty() { + return NewPolygonFlat(XY, nil, nil) + } + x1, y1 := b.min[0], b.min[1] + x2, y2 := b.max[0], b.max[1] + flatCoords := []float64{ + x1, y1, + x1, y2, + x2, y2, + x2, y1, + x1, y1, + } + return NewPolygonFlat(XY, flatCoords, []int{len(flatCoords)}) +} + +// Set sets the minimum and maximum values. args must be an even number of +// values: the first half are the minimum values for each dimension and the +// second half are the maximum values for each dimension. If necessary, the +// layout of b will be extended to cover all the supplied dimensions implied by +// args. +func (b *Bounds) Set(args ...float64) *Bounds { + if len(args)&1 != 0 { + panic("geom: even number of arguments required") + } + stride := len(args) / 2 + b.extendStride(stride) + for i := range stride { + b.min[i], b.max[i] = args[i], args[i+stride] + } + return b +} + +// SetCoords sets the minimum and maximum values of the Bounds. +func (b *Bounds) SetCoords(minCoord, maxCoord Coord) *Bounds { + b.min = Coord(make([]float64, b.layout.Stride())) + b.max = Coord(make([]float64, b.layout.Stride())) + for i := range b.layout.Stride() { + b.min[i] = math.Min(minCoord[i], maxCoord[i]) + b.max[i] = math.Max(minCoord[i], maxCoord[i]) + } + return b +} + +// OverlapsPoint determines if the bounding box overlaps the point (point is +// within or on the border of the bounds). +func (b *Bounds) OverlapsPoint(layout Layout, point Coord) bool { + for i, stride := 0, layout.Stride(); i < stride; i++ { + if b.min[i] > point[i] || b.max[i] < point[i] { + return false + } + } + return true +} + +func (b *Bounds) extendFlatCoords(flatCoords []float64, offset, end, stride int) *Bounds { + b.extendStride(stride) + for i := offset; i < end; i += stride { + for j := range stride { + b.min[j] = math.Min(b.min[j], flatCoords[i+j]) + b.max[j] = math.Max(b.max[j], flatCoords[i+j]) + } + } + return b +} + +func (b *Bounds) extendLayout(layout Layout) { + switch { + case b.layout == XYZ && layout == XYM: + b.min = append(b.min, math.Inf(1)) + b.max = append(b.max, math.Inf(-1)) + b.layout = XYZM + case b.layout == XYM && (layout == XYZ || layout == XYZM): + b.min = append(b.min[:2], math.Inf(1), b.min[2]) + b.max = append(b.max[:2], math.Inf(-1), b.max[2]) + b.layout = XYZM + case b.layout < layout: + b.extendStride(layout.Stride()) + b.layout = layout + } +} + +func (b *Bounds) extendStride(stride int) { + for s := b.layout.Stride(); s < stride; s++ { + b.min = append(b.min, math.Inf(1)) + b.max = append(b.max, math.Inf(-1)) + } +} + +func (b *Bounds) extendXYZMFlatCoordsWithXYM(flatCoords []float64, offset, end int) *Bounds { + for i := offset; i < end; i += 3 { + b.min[0] = math.Min(b.min[0], flatCoords[i+0]) + b.max[0] = math.Max(b.max[0], flatCoords[i+0]) + b.min[1] = math.Min(b.min[1], flatCoords[i+1]) + b.max[1] = math.Max(b.max[1], flatCoords[i+1]) + b.min[3] = math.Min(b.min[3], flatCoords[i+2]) + b.max[3] = math.Max(b.max[3], flatCoords[i+2]) + } + return b +} diff --git a/vendor/github.com/twpayne/go-geom/derived.gen.go b/vendor/github.com/twpayne/go-geom/derived.gen.go new file mode 100644 index 000000000..44fda3190 --- /dev/null +++ b/vendor/github.com/twpayne/go-geom/derived.gen.go @@ -0,0 +1,314 @@ +// Code generated by goderive DO NOT EDIT. + +package geom + +// deriveCloneBounds returns a clone of the src parameter. +func deriveCloneBounds(src *Bounds) *Bounds { + if src == nil { + return nil + } + dst := new(Bounds) + deriveDeepCopy(dst, src) + return dst +} + +// deriveCloneCoord returns a clone of the src parameter. +func deriveCloneCoord(src Coord) Coord { + if src == nil { + return nil + } + dst := make(Coord, len(src)) + deriveDeepCopy_(dst, src) + return dst +} + +// deriveCloneLinearRing returns a clone of the src parameter. +func deriveCloneLinearRing(src *LinearRing) *LinearRing { + if src == nil { + return nil + } + dst := new(LinearRing) + deriveDeepCopy_1(dst, src) + return dst +} + +// deriveCloneLineString returns a clone of the src parameter. +func deriveCloneLineString(src *LineString) *LineString { + if src == nil { + return nil + } + dst := new(LineString) + deriveDeepCopy_2(dst, src) + return dst +} + +// deriveCloneMultiLineString returns a clone of the src parameter. +func deriveCloneMultiLineString(src *MultiLineString) *MultiLineString { + if src == nil { + return nil + } + dst := new(MultiLineString) + deriveDeepCopy_3(dst, src) + return dst +} + +// deriveCloneMultiPoint returns a clone of the src parameter. +func deriveCloneMultiPoint(src *MultiPoint) *MultiPoint { + if src == nil { + return nil + } + dst := new(MultiPoint) + deriveDeepCopy_4(dst, src) + return dst +} + +// deriveCloneMultiPolygon returns a clone of the src parameter. +func deriveCloneMultiPolygon(src *MultiPolygon) *MultiPolygon { + if src == nil { + return nil + } + dst := new(MultiPolygon) + deriveDeepCopy_5(dst, src) + return dst +} + +// deriveClonePoint returns a clone of the src parameter. +func deriveClonePoint(src *Point) *Point { + if src == nil { + return nil + } + dst := new(Point) + deriveDeepCopy_6(dst, src) + return dst +} + +// deriveClonePolygon returns a clone of the src parameter. +func deriveClonePolygon(src *Polygon) *Polygon { + if src == nil { + return nil + } + dst := new(Polygon) + deriveDeepCopy_7(dst, src) + return dst +} + +// deriveDeepCopy recursively copies the contents of src into dst. +func deriveDeepCopy(dst, src *Bounds) { + dst.layout = src.layout + if src.min == nil { + dst.min = nil + } else { + if dst.min != nil { + if len(src.min) > len(dst.min) { + if cap(dst.min) >= len(src.min) { + dst.min = (dst.min)[:len(src.min)] + } else { + dst.min = make([]float64, len(src.min)) + } + } else if len(src.min) < len(dst.min) { + dst.min = (dst.min)[:len(src.min)] + } + } else { + dst.min = make([]float64, len(src.min)) + } + copy(dst.min, src.min) + } + if src.max == nil { + dst.max = nil + } else { + if dst.max != nil { + if len(src.max) > len(dst.max) { + if cap(dst.max) >= len(src.max) { + dst.max = (dst.max)[:len(src.max)] + } else { + dst.max = make([]float64, len(src.max)) + } + } else if len(src.max) < len(dst.max) { + dst.max = (dst.max)[:len(src.max)] + } + } else { + dst.max = make([]float64, len(src.max)) + } + copy(dst.max, src.max) + } +} + +// deriveDeepCopy_ recursively copies the contents of src into dst. +func deriveDeepCopy_(dst, src Coord) { + copy(dst, src) +} + +// deriveDeepCopy_1 recursively copies the contents of src into dst. +func deriveDeepCopy_1(dst, src *LinearRing) { + func() { + field := new(geom1) + deriveDeepCopy_8(field, &src.geom1) + dst.geom1 = *field + }() +} + +// deriveDeepCopy_2 recursively copies the contents of src into dst. +func deriveDeepCopy_2(dst, src *LineString) { + func() { + field := new(geom1) + deriveDeepCopy_8(field, &src.geom1) + dst.geom1 = *field + }() +} + +// deriveDeepCopy_3 recursively copies the contents of src into dst. +func deriveDeepCopy_3(dst, src *MultiLineString) { + func() { + field := new(geom2) + deriveDeepCopy_9(field, &src.geom2) + dst.geom2 = *field + }() +} + +// deriveDeepCopy_4 recursively copies the contents of src into dst. +func deriveDeepCopy_4(dst, src *MultiPoint) { + func() { + field := new(geom2) + deriveDeepCopy_9(field, &src.geom2) + dst.geom2 = *field + }() +} + +// deriveDeepCopy_5 recursively copies the contents of src into dst. +func deriveDeepCopy_5(dst, src *MultiPolygon) { + func() { + field := new(geom3) + deriveDeepCopy_10(field, &src.geom3) + dst.geom3 = *field + }() +} + +// deriveDeepCopy_6 recursively copies the contents of src into dst. +func deriveDeepCopy_6(dst, src *Point) { + func() { + field := new(geom0) + deriveDeepCopy_11(field, &src.geom0) + dst.geom0 = *field + }() +} + +// deriveDeepCopy_7 recursively copies the contents of src into dst. +func deriveDeepCopy_7(dst, src *Polygon) { + func() { + field := new(geom2) + deriveDeepCopy_9(field, &src.geom2) + dst.geom2 = *field + }() +} + +// deriveDeepCopy_8 recursively copies the contents of src into dst. +func deriveDeepCopy_8(dst, src *geom1) { + func() { + field := new(geom0) + deriveDeepCopy_11(field, &src.geom0) + dst.geom0 = *field + }() +} + +// deriveDeepCopy_9 recursively copies the contents of src into dst. +func deriveDeepCopy_9(dst, src *geom2) { + func() { + field := new(geom1) + deriveDeepCopy_8(field, &src.geom1) + dst.geom1 = *field + }() + if src.ends == nil { + dst.ends = nil + } else { + if dst.ends != nil { + if len(src.ends) > len(dst.ends) { + if cap(dst.ends) >= len(src.ends) { + dst.ends = (dst.ends)[:len(src.ends)] + } else { + dst.ends = make([]int, len(src.ends)) + } + } else if len(src.ends) < len(dst.ends) { + dst.ends = (dst.ends)[:len(src.ends)] + } + } else { + dst.ends = make([]int, len(src.ends)) + } + copy(dst.ends, src.ends) + } +} + +// deriveDeepCopy_10 recursively copies the contents of src into dst. +func deriveDeepCopy_10(dst, src *geom3) { + func() { + field := new(geom1) + deriveDeepCopy_8(field, &src.geom1) + dst.geom1 = *field + }() + if src.endss == nil { + dst.endss = nil + } else { + if dst.endss != nil { + if len(src.endss) > len(dst.endss) { + if cap(dst.endss) >= len(src.endss) { + dst.endss = (dst.endss)[:len(src.endss)] + } else { + dst.endss = make([][]int, len(src.endss)) + } + } else if len(src.endss) < len(dst.endss) { + dst.endss = (dst.endss)[:len(src.endss)] + } + } else { + dst.endss = make([][]int, len(src.endss)) + } + deriveDeepCopy_12(dst.endss, src.endss) + } +} + +// deriveDeepCopy_11 recursively copies the contents of src into dst. +func deriveDeepCopy_11(dst, src *geom0) { + dst.layout = src.layout + dst.stride = src.stride + if src.flatCoords == nil { + dst.flatCoords = nil + } else { + if dst.flatCoords != nil { + if len(src.flatCoords) > len(dst.flatCoords) { + if cap(dst.flatCoords) >= len(src.flatCoords) { + dst.flatCoords = (dst.flatCoords)[:len(src.flatCoords)] + } else { + dst.flatCoords = make([]float64, len(src.flatCoords)) + } + } else if len(src.flatCoords) < len(dst.flatCoords) { + dst.flatCoords = (dst.flatCoords)[:len(src.flatCoords)] + } + } else { + dst.flatCoords = make([]float64, len(src.flatCoords)) + } + copy(dst.flatCoords, src.flatCoords) + } + dst.srid = src.srid +} + +// deriveDeepCopy_12 recursively copies the contents of src into dst. +func deriveDeepCopy_12(dst, src [][]int) { + for src_i, src_value := range src { + if src_value == nil { + dst[src_i] = nil + } else { + if dst[src_i] != nil { + if len(src_value) > len(dst[src_i]) { + if cap(dst[src_i]) >= len(src_value) { + dst[src_i] = (dst[src_i])[:len(src_value)] + } else { + dst[src_i] = make([]int, len(src_value)) + } + } else if len(src_value) < len(dst[src_i]) { + dst[src_i] = (dst[src_i])[:len(src_value)] + } + } else { + dst[src_i] = make([]int, len(src_value)) + } + copy(dst[src_i], src_value) + } + } +} diff --git a/vendor/github.com/twpayne/go-geom/encoding/wkb/.gitignore b/vendor/github.com/twpayne/go-geom/encoding/wkb/.gitignore new file mode 100644 index 000000000..4b8c16c35 --- /dev/null +++ b/vendor/github.com/twpayne/go-geom/encoding/wkb/.gitignore @@ -0,0 +1 @@ +wkb-fuzz.zip diff --git a/vendor/github.com/twpayne/go-geom/encoding/wkb/Makefile b/vendor/github.com/twpayne/go-geom/encoding/wkb/Makefile new file mode 100644 index 000000000..f0d8126fc --- /dev/null +++ b/vendor/github.com/twpayne/go-geom/encoding/wkb/Makefile @@ -0,0 +1,3 @@ +fuzz: + go-fuzz-build github.com/twpayne/go-geom/encoding/wkb + go-fuzz -bin=wkb-fuzz.zip -workdir=workdir diff --git a/vendor/github.com/twpayne/go-geom/encoding/wkb/fuzz.go b/vendor/github.com/twpayne/go-geom/encoding/wkb/fuzz.go new file mode 100644 index 000000000..8a5e7f4e8 --- /dev/null +++ b/vendor/github.com/twpayne/go-geom/encoding/wkb/fuzz.go @@ -0,0 +1,11 @@ +//go:build gofuzz +// +build gofuzz + +package wkb + +func Fuzz(data []byte) int { + if _, err := Unmarshal(data); err != nil { + return 0 + } + return 1 +} diff --git a/vendor/github.com/twpayne/go-geom/encoding/wkb/sql.go b/vendor/github.com/twpayne/go-geom/encoding/wkb/sql.go new file mode 100644 index 000000000..75cbad996 --- /dev/null +++ b/vendor/github.com/twpayne/go-geom/encoding/wkb/sql.go @@ -0,0 +1,270 @@ +package wkb + +import ( + "database/sql/driver" + "fmt" + "strings" + + "github.com/twpayne/go-geom" + "github.com/twpayne/go-geom/encoding/wkbcommon" +) + +// ErrExpectedByteSlice is returned when a []byte is expected. +type ErrExpectedByteSlice struct { + Value interface{} +} + +func (e ErrExpectedByteSlice) Error() string { + return fmt.Sprintf("wkb: want []byte, got %T", e.Value) +} + +// A Geom is a WKB-ecoded Geometry that implements the sql.Scanner and +// driver.Value interfaces. +// It can be used when the geometry shape is not defined. +type Geom struct { + geom.T + opts []wkbcommon.WKBOption +} + +// A Point is a WKB-encoded Point that implements the sql.Scanner and +// driver.Valuer interfaces. +type Point struct { + *geom.Point + opts []wkbcommon.WKBOption +} + +// A LineString is a WKB-encoded LineString that implements the sql.Scanner and +// driver.Valuer interfaces. +type LineString struct { + *geom.LineString + opts []wkbcommon.WKBOption +} + +// A Polygon is a WKB-encoded Polygon that implements the sql.Scanner and +// driver.Valuer interfaces. +type Polygon struct { + *geom.Polygon + opts []wkbcommon.WKBOption +} + +// A MultiPoint is a WKB-encoded MultiPoint that implements the sql.Scanner and +// driver.Valuer interfaces. +type MultiPoint struct { + *geom.MultiPoint + opts []wkbcommon.WKBOption +} + +// A MultiLineString is a WKB-encoded MultiLineString that implements the +// sql.Scanner and driver.Valuer interfaces. +type MultiLineString struct { + *geom.MultiLineString + opts []wkbcommon.WKBOption +} + +// A MultiPolygon is a WKB-encoded MultiPolygon that implements the sql.Scanner +// and driver.Valuer interfaces. +type MultiPolygon struct { + *geom.MultiPolygon + opts []wkbcommon.WKBOption +} + +// A GeometryCollection is a WKB-encoded GeometryCollection that implements the +// sql.Scanner and driver.Valuer interfaces. +type GeometryCollection struct { + *geom.GeometryCollection + opts []wkbcommon.WKBOption +} + +// Scan scans from a []byte. +func (g *Geom) Scan(src interface{}) error { + b, ok := src.([]byte) + if !ok { + return ErrExpectedByteSlice{Value: src} + } + // NOTE(tb) other Scanners do not check the len of b, is it really useful ? + if len(b) == 0 { + return nil + } + var err error + g.T, err = Unmarshal(b, g.opts...) + return err +} + +// Value returns the WKB encoding of g. +func (g *Geom) Value() (driver.Value, error) { + return value(g.T) +} + +// Geom returns the underlying geom.T. +func (g *Geom) Geom() geom.T { + return g.T +} + +// Scan scans from a []byte. +func (p *Point) Scan(src interface{}) error { + b, ok := src.([]byte) + if !ok { + return ErrExpectedByteSlice{Value: src} + } + got, err := Unmarshal(b, p.opts...) + if err != nil { + return err + } + p1, ok := got.(*geom.Point) + if !ok { + return wkbcommon.ErrUnexpectedType{Got: got, Want: p} + } + p.Point = p1 + return nil +} + +// Value returns the WKB encoding of p. +func (p *Point) Value() (driver.Value, error) { + return value(p.Point) +} + +// Scan scans from a []byte. +func (ls *LineString) Scan(src interface{}) error { + b, ok := src.([]byte) + if !ok { + return ErrExpectedByteSlice{Value: src} + } + got, err := Unmarshal(b, ls.opts...) + if err != nil { + return err + } + ls1, ok := got.(*geom.LineString) + if !ok { + return wkbcommon.ErrUnexpectedType{Got: got, Want: ls} + } + ls.LineString = ls1 + return nil +} + +// Value returns the WKB encoding of ls. +func (ls *LineString) Value() (driver.Value, error) { + return value(ls.LineString) +} + +// Scan scans from a []byte. +func (p *Polygon) Scan(src interface{}) error { + b, ok := src.([]byte) + if !ok { + return ErrExpectedByteSlice{Value: src} + } + got, err := Unmarshal(b, p.opts...) + if err != nil { + return err + } + p1, ok := got.(*geom.Polygon) + if !ok { + return wkbcommon.ErrUnexpectedType{Got: got, Want: p} + } + p.Polygon = p1 + return nil +} + +// Value returns the WKB encoding of p. +func (p *Polygon) Value() (driver.Value, error) { + return value(p.Polygon) +} + +// Scan scans from a []byte. +func (mp *MultiPoint) Scan(src interface{}) error { + b, ok := src.([]byte) + if !ok { + return ErrExpectedByteSlice{Value: src} + } + got, err := Unmarshal(b, mp.opts...) + if err != nil { + return err + } + mp1, ok := got.(*geom.MultiPoint) + if !ok { + return wkbcommon.ErrUnexpectedType{Got: got, Want: mp} + } + mp.MultiPoint = mp1 + return nil +} + +// Value returns the WKB encoding of mp. +func (mp *MultiPoint) Value() (driver.Value, error) { + return value(mp.MultiPoint) +} + +// Scan scans from a []byte. +func (mls *MultiLineString) Scan(src interface{}) error { + b, ok := src.([]byte) + if !ok { + return ErrExpectedByteSlice{Value: src} + } + got, err := Unmarshal(b, mls.opts...) + if err != nil { + return err + } + mls1, ok := got.(*geom.MultiLineString) + if !ok { + return wkbcommon.ErrUnexpectedType{Got: got, Want: mls} + } + mls.MultiLineString = mls1 + return nil +} + +// Value returns the WKB encoding of mls. +func (mls *MultiLineString) Value() (driver.Value, error) { + return value(mls.MultiLineString) +} + +// Scan scans from a []byte. +func (mp *MultiPolygon) Scan(src interface{}) error { + b, ok := src.([]byte) + if !ok { + return ErrExpectedByteSlice{Value: src} + } + got, err := Unmarshal(b, mp.opts...) + if err != nil { + return err + } + mp1, ok := got.(*geom.MultiPolygon) + if !ok { + return wkbcommon.ErrUnexpectedType{Got: got, Want: mp} + } + mp.MultiPolygon = mp1 + return nil +} + +// Value returns the WKB encoding of mp. +func (mp *MultiPolygon) Value() (driver.Value, error) { + return value(mp.MultiPolygon) +} + +// Scan scans from a []byte. +func (gc *GeometryCollection) Scan(src interface{}) error { + b, ok := src.([]byte) + if !ok { + return ErrExpectedByteSlice{Value: src} + } + got, err := Unmarshal(b, gc.opts...) + if err != nil { + return err + } + gc1, ok := got.(*geom.GeometryCollection) + if !ok { + return wkbcommon.ErrUnexpectedType{Got: got, Want: gc} + } + gc.GeometryCollection = gc1 + return nil +} + +// Value returns the WKB encoding of gc. +func (gc *GeometryCollection) Value() (driver.Value, error) { + return value(gc.GeometryCollection) +} + +func value(g geom.T) (driver.Value, error) { + sb := &strings.Builder{} + if err := Write(sb, NDR, g); err != nil { + return nil, err + } + return []byte(sb.String()), nil +} diff --git a/vendor/github.com/twpayne/go-geom/encoding/wkb/wkb.go b/vendor/github.com/twpayne/go-geom/encoding/wkb/wkb.go new file mode 100644 index 000000000..b3bf84073 --- /dev/null +++ b/vendor/github.com/twpayne/go-geom/encoding/wkb/wkb.go @@ -0,0 +1,335 @@ +// Package wkb implements Well Known Binary encoding and decoding. +// +// If you are encoding geometries in WKB to send to PostgreSQL/PostGIS, then +// you must specify binary_parameters=yes in the data source name that you pass +// to sql.Open. +package wkb + +import ( + "bytes" + "encoding/binary" + "errors" + "fmt" + "io" + + "github.com/twpayne/go-geom" + "github.com/twpayne/go-geom/encoding/wkbcommon" +) + +var ( + // XDR is big endian. + XDR = wkbcommon.XDR + // NDR is little endian. + NDR = wkbcommon.NDR +) + +const ( + wkbXYID = 0 + wkbXYZID = 1000 + wkbXYMID = 2000 + wkbXYZMID = 3000 +) + +// Read reads an arbitrary geometry from r. +func Read(r io.Reader, opts ...wkbcommon.WKBOption) (geom.T, error) { + params := wkbcommon.InitWKBParams( + wkbcommon.WKBParams{ + EmptyPointHandling: wkbcommon.EmptyPointHandlingError, + }, + opts..., + ) + + wkbByteOrder, err := wkbcommon.ReadByte(r) + if err != nil { + return nil, err + } + var byteOrder binary.ByteOrder + switch wkbByteOrder { + case wkbcommon.XDRID: + byteOrder = XDR + case wkbcommon.NDRID: + byteOrder = NDR + default: + return nil, wkbcommon.ErrUnknownByteOrder(wkbByteOrder) + } + + wkbGeometryType, err := wkbcommon.ReadUInt32(r, byteOrder) + if err != nil { + return nil, err + } + t := wkbcommon.Type(wkbGeometryType) + + var layout geom.Layout + switch 1000 * (t / 1000) { + case wkbXYID: + layout = geom.XY + case wkbXYZID: + layout = geom.XYZ + case wkbXYMID: + layout = geom.XYM + case wkbXYZMID: + layout = geom.XYZM + default: + return nil, wkbcommon.ErrUnknownType(t) + } + + switch t % 1000 { + case wkbcommon.PointID: + flatCoords, err := wkbcommon.ReadFlatCoords0(r, byteOrder, layout.Stride()) + if err != nil { + return nil, err + } + if params.EmptyPointHandling == wkbcommon.EmptyPointHandlingNaN { + return geom.NewPointFlatMaybeEmpty(layout, flatCoords), nil + } + return geom.NewPointFlat(layout, flatCoords), nil + case wkbcommon.LineStringID: + flatCoords, err := wkbcommon.ReadFlatCoords1(r, byteOrder, layout.Stride()) + if err != nil { + return nil, err + } + return geom.NewLineStringFlat(layout, flatCoords), nil + case wkbcommon.PolygonID: + flatCoords, ends, err := wkbcommon.ReadFlatCoords2(r, byteOrder, layout.Stride()) + if err != nil { + return nil, err + } + return geom.NewPolygonFlat(layout, flatCoords, ends), nil + case wkbcommon.MultiPointID: + n, err := wkbcommon.ReadUInt32(r, byteOrder) + if err != nil { + return nil, err + } + if limit := wkbcommon.MaxGeometryElements[1]; limit >= 0 && int(n) > limit { + return nil, wkbcommon.ErrGeometryTooLarge{Level: 1, N: int(n), Limit: limit} + } + mp := geom.NewMultiPoint(layout) + for range n { + g, err := Read(r, opts...) + if err != nil { + return nil, err + } + p, ok := g.(*geom.Point) + if !ok { + return nil, wkbcommon.ErrUnexpectedType{Got: g, Want: &geom.Point{}} + } + if err = mp.Push(p); err != nil { + return nil, err + } + } + return mp, nil + case wkbcommon.MultiLineStringID: + n, err := wkbcommon.ReadUInt32(r, byteOrder) + if err != nil { + return nil, err + } + if limit := wkbcommon.MaxGeometryElements[2]; limit >= 0 && int(n) > limit { + return nil, wkbcommon.ErrGeometryTooLarge{Level: 2, N: int(n), Limit: limit} + } + mls := geom.NewMultiLineString(layout) + for range n { + g, err := Read(r, opts...) + if err != nil { + return nil, err + } + p, ok := g.(*geom.LineString) + if !ok { + return nil, wkbcommon.ErrUnexpectedType{Got: g, Want: &geom.LineString{}} + } + if err = mls.Push(p); err != nil { + return nil, err + } + } + return mls, nil + case wkbcommon.MultiPolygonID: + n, err := wkbcommon.ReadUInt32(r, byteOrder) + if err != nil { + return nil, err + } + if limit := wkbcommon.MaxGeometryElements[3]; limit >= 0 && int(n) > limit { + return nil, wkbcommon.ErrGeometryTooLarge{Level: 3, N: int(n), Limit: limit} + } + mp := geom.NewMultiPolygon(layout) + for range n { + g, err := Read(r, opts...) + if err != nil { + return nil, err + } + p, ok := g.(*geom.Polygon) + if !ok { + return nil, wkbcommon.ErrUnexpectedType{Got: g, Want: &geom.Polygon{}} + } + if err = mp.Push(p); err != nil { + return nil, err + } + } + return mp, nil + case wkbcommon.GeometryCollectionID: + n, err := wkbcommon.ReadUInt32(r, byteOrder) + if err != nil { + return nil, err + } + gc := geom.NewGeometryCollection() + for range n { + g, err := Read(r, opts...) + if err != nil { + return nil, err + } + if err := gc.Push(g); err != nil { + return nil, err + } + } + // If EMPTY, mark the collection with a fixed layout to differentiate + // GEOMETRYCOLLECTION EMPTY between 2D/Z/M/ZM. + if gc.Empty() && gc.NumGeoms() == 0 { + if err := gc.SetLayout(layout); err != nil { + return nil, err + } + } + return gc, nil + default: + return nil, wkbcommon.ErrUnsupportedType(wkbGeometryType) + } +} + +// Unmarshal unmrshals an arbitrary geometry from a []byte. +func Unmarshal(data []byte, opts ...wkbcommon.WKBOption) (geom.T, error) { + return Read(bytes.NewBuffer(data), opts...) +} + +// Write writes an arbitrary geometry to w. +func Write(w io.Writer, byteOrder binary.ByteOrder, g geom.T, opts ...wkbcommon.WKBOption) error { + params := wkbcommon.InitWKBParams( + wkbcommon.WKBParams{ + EmptyPointHandling: wkbcommon.EmptyPointHandlingError, + }, + opts..., + ) + + var wkbByteOrder byte + switch byteOrder { + case XDR: + wkbByteOrder = wkbcommon.XDRID + case NDR: + wkbByteOrder = wkbcommon.NDRID + default: + return wkbcommon.ErrUnsupportedByteOrder{} + } + if err := wkbcommon.WriteByte(w, wkbByteOrder); err != nil { + return err + } + + var wkbGeometryType uint32 + switch g.(type) { + case *geom.Point: + wkbGeometryType = wkbcommon.PointID + case *geom.LineString: + wkbGeometryType = wkbcommon.LineStringID + case *geom.Polygon: + wkbGeometryType = wkbcommon.PolygonID + case *geom.MultiPoint: + wkbGeometryType = wkbcommon.MultiPointID + case *geom.MultiLineString: + wkbGeometryType = wkbcommon.MultiLineStringID + case *geom.MultiPolygon: + wkbGeometryType = wkbcommon.MultiPolygonID + case *geom.GeometryCollection: + wkbGeometryType = wkbcommon.GeometryCollectionID + default: + return geom.ErrUnsupportedType{Value: g} + } + switch g.Layout() { + case geom.NoLayout: + // Special case for empty GeometryCollections + if _, ok := g.(*geom.GeometryCollection); !ok || !g.Empty() { + return geom.ErrUnsupportedLayout(g.Layout()) + } + case geom.XY: + wkbGeometryType += wkbXYID + case geom.XYZ: + wkbGeometryType += wkbXYZID + case geom.XYM: + wkbGeometryType += wkbXYMID + case geom.XYZM: + wkbGeometryType += wkbXYZMID + default: + return geom.ErrUnsupportedLayout(g.Layout()) + } + if err := wkbcommon.WriteUInt32(w, byteOrder, wkbGeometryType); err != nil { + return err + } + + switch g := g.(type) { + case *geom.Point: + if g.Empty() { + switch params.EmptyPointHandling { + case wkbcommon.EmptyPointHandlingNaN: + return wkbcommon.WriteEmptyPointAsNaN(w, byteOrder, g.Stride()) + case wkbcommon.EmptyPointHandlingError: + return errors.New("cannot encode empty Point in WKB") + default: + return fmt.Errorf("cannot encode empty Point in WKB (unknown option: %d)", wkbcommon.EmptyPointHandlingNaN) + } + } + return wkbcommon.WriteFlatCoords0(w, byteOrder, g.FlatCoords()) + case *geom.LineString: + return wkbcommon.WriteFlatCoords1(w, byteOrder, g.FlatCoords(), g.Stride()) + case *geom.Polygon: + return wkbcommon.WriteFlatCoords2(w, byteOrder, g.FlatCoords(), g.Ends(), g.Stride()) + case *geom.MultiPoint: + n := g.NumPoints() + if err := wkbcommon.WriteUInt32(w, byteOrder, uint32(n)); err != nil { + return err + } + for i := range n { + if err := Write(w, byteOrder, g.Point(i), opts...); err != nil { + return err + } + } + return nil + case *geom.MultiLineString: + n := g.NumLineStrings() + if err := wkbcommon.WriteUInt32(w, byteOrder, uint32(n)); err != nil { + return err + } + for i := range n { + if err := Write(w, byteOrder, g.LineString(i), opts...); err != nil { + return err + } + } + return nil + case *geom.MultiPolygon: + n := g.NumPolygons() + if err := wkbcommon.WriteUInt32(w, byteOrder, uint32(n)); err != nil { + return err + } + for i := range n { + if err := Write(w, byteOrder, g.Polygon(i), opts...); err != nil { + return err + } + } + return nil + case *geom.GeometryCollection: + n := g.NumGeoms() + if err := wkbcommon.WriteUInt32(w, byteOrder, uint32(n)); err != nil { + return err + } + for i := range n { + if err := Write(w, byteOrder, g.Geom(i), opts...); err != nil { + return err + } + } + return nil + default: + return geom.ErrUnsupportedType{Value: g} + } +} + +// Marshal marshals an arbitrary geometry to a []byte. +func Marshal(g geom.T, byteOrder binary.ByteOrder, opts ...wkbcommon.WKBOption) ([]byte, error) { + w := bytes.NewBuffer(nil) + if err := Write(w, byteOrder, g, opts...); err != nil { + return nil, err + } + return w.Bytes(), nil +} diff --git a/vendor/github.com/twpayne/go-geom/encoding/wkbcommon/binary.go b/vendor/github.com/twpayne/go-geom/encoding/wkbcommon/binary.go new file mode 100644 index 000000000..ceec0ed2b --- /dev/null +++ b/vendor/github.com/twpayne/go-geom/encoding/wkbcommon/binary.go @@ -0,0 +1,86 @@ +// Package wkbcommon contains code common to WKB and EWKB encoding. +package wkbcommon + +import ( + "encoding/binary" + "io" + "math" + + "github.com/twpayne/go-geom" +) + +func readFloat(buf []byte, byteOrder binary.ByteOrder) float64 { + u := byteOrder.Uint64(buf) + return math.Float64frombits(u) +} + +// ReadUInt32 reads a uint32 from r. +func ReadUInt32(r io.Reader, byteOrder binary.ByteOrder) (uint32, error) { + var buf [4]byte + if _, err := io.ReadFull(r, buf[:]); err != nil { + return 0, err + } + return byteOrder.Uint32(buf[:]), nil +} + +// ReadFloatArray reads a []float64 from r. +func ReadFloatArray(r io.Reader, byteOrder binary.ByteOrder, array []float64) error { + buf := make([]byte, 8*len(array)) + if _, err := io.ReadFull(r, buf); err != nil { + return err + } + // Convert to an array of floats + for i := range array { + array[i] = readFloat(buf[8*i:], byteOrder) + } + return nil +} + +// ReadByte reads a byte from r. +func ReadByte(r io.Reader) (byte, error) { + var buf [1]byte + if _, err := r.Read(buf[:]); err != nil { + return 0, err + } + return buf[0], nil +} + +func writeFloat(buf []byte, byteOrder binary.ByteOrder, value float64) { + u := math.Float64bits(value) + byteOrder.PutUint64(buf, u) +} + +// WriteFloatArray writes a []float64 to w. +func WriteFloatArray(w io.Writer, byteOrder binary.ByteOrder, array []float64) error { + buf := make([]byte, 8*len(array)) + for i, f := range array { + writeFloat(buf[8*i:], byteOrder, f) + } + _, err := w.Write(buf) + return err +} + +// WriteUInt32 writes a uint32 to w. +func WriteUInt32(w io.Writer, byteOrder binary.ByteOrder, value uint32) error { + var buf [4]byte + byteOrder.PutUint32(buf[:], value) + _, err := w.Write(buf[:]) + return err +} + +// WriteByte wrties a byte to w. +func WriteByte(w io.Writer, value byte) error { + var buf [1]byte + buf[0] = value + _, err := w.Write(buf[:]) + return err +} + +// WriteEmptyPointAsNaN outputs EmptyPoint as NaN values. +func WriteEmptyPointAsNaN(w io.Writer, byteOrder binary.ByteOrder, numCoords int) error { + coords := make([]float64, numCoords) + for i := range numCoords { + coords[i] = geom.PointEmptyCoord() + } + return WriteFlatCoords0(w, byteOrder, coords) +} diff --git a/vendor/github.com/twpayne/go-geom/encoding/wkbcommon/options.go b/vendor/github.com/twpayne/go-geom/encoding/wkbcommon/options.go new file mode 100644 index 000000000..b6e5cb893 --- /dev/null +++ b/vendor/github.com/twpayne/go-geom/encoding/wkbcommon/options.go @@ -0,0 +1,36 @@ +package wkbcommon + +// EmptyPointHandling is the mechanism to handle an empty point. +type EmptyPointHandling uint8 + +const ( + // EmptyPointHandlingError will error if an empty point is found. + EmptyPointHandlingError EmptyPointHandling = iota + // EmptyPointHandlingNaN will decipher empty points with NaN as coordinates. + // This is in line with Requirement 152 of the GeoPackage spec (http://www.geopackage.org/spec/). + EmptyPointHandlingNaN +) + +// WKBParams are parameters for encoding and decoding WKB items. +type WKBParams struct { + EmptyPointHandling EmptyPointHandling +} + +// WKBOption is an option to set on WKBParams. +type WKBOption func(WKBParams) WKBParams + +// WKBOptionEmptyPointHandling sets the params to the specified EmptyPointHandling. +func WKBOptionEmptyPointHandling(h EmptyPointHandling) WKBOption { + return func(p WKBParams) WKBParams { + p.EmptyPointHandling = h + return p + } +} + +// InitWKBParams initializes WKBParams from an initial parameter and some options. +func InitWKBParams(params WKBParams, opts ...WKBOption) WKBParams { + for _, opt := range opts { + params = opt(params) + } + return params +} diff --git a/vendor/github.com/twpayne/go-geom/encoding/wkbcommon/wkbcommon.go b/vendor/github.com/twpayne/go-geom/encoding/wkbcommon/wkbcommon.go new file mode 100644 index 000000000..1431aa35b --- /dev/null +++ b/vendor/github.com/twpayne/go-geom/encoding/wkbcommon/wkbcommon.go @@ -0,0 +1,179 @@ +// Package wkbcommon contains code common to WKB and EWKB encoding. +package wkbcommon + +import ( + "encoding/binary" + "fmt" + "io" +) + +// Byte order IDs. +const ( + XDRID = 0 + NDRID = 1 +) + +// Byte orders. +var ( + XDR = binary.BigEndian + NDR = binary.LittleEndian +) + +// An ErrUnknownByteOrder is returned when an unknown byte order is encountered. +type ErrUnknownByteOrder byte + +func (e ErrUnknownByteOrder) Error() string { + return fmt.Sprintf("wkb: unknown byte order: %b", byte(e)) +} + +// An ErrUnsupportedByteOrder is returned when an unsupported byte order is encountered. +type ErrUnsupportedByteOrder struct{} + +func (e ErrUnsupportedByteOrder) Error() string { + return "wkb: unsupported byte order" +} + +// A Type is a WKB code. +type Type uint32 + +// An ErrUnknownType is returned when an unknown type is encountered. +type ErrUnknownType Type + +func (e ErrUnknownType) Error() string { + return fmt.Sprintf("wkb: unknown type: %d", uint(e)) +} + +// An ErrUnsupportedType is returned when an unsupported type is encountered. +type ErrUnsupportedType Type + +func (e ErrUnsupportedType) Error() string { + return fmt.Sprintf("wkb: unsupported type: %d", uint(e)) +} + +// An ErrUnexpectedType is returned when an unexpected type is encountered. +type ErrUnexpectedType struct { + Got interface{} + Want interface{} +} + +func (e ErrUnexpectedType) Error() string { + return fmt.Sprintf("wkb: got %T, want %T", e.Got, e.Want) +} + +// MaxGeometryElements is the maximum number of elements that will be decoded +// at different levels. Its primary purpose is to prevent corrupt inputs from +// causing excessive memory allocations (which could be used as a denial of +// service attack). +// +// This is a variable, so you can override it in your application code by +// importing the `github.com/twpayne/go-geom/encoding/wkbcommon` module and +// setting the value of `wkbcommon.MaxGeometryElements`. +// +// FIXME This should be Codec-specific, not global. +// FIXME Consider overall per-geometry limit rather than per-level limit. +var MaxGeometryElements = [4]int{ + 0, // Unused + -1, // LineString, LinearRing, and MultiPoint + -1, // MultiLineString and Polygon + -1, // MultiPolygon +} + +// An ErrGeometryTooLarge is returned when the geometry is too large. +type ErrGeometryTooLarge struct { + Level int + N int + Limit int +} + +func (e ErrGeometryTooLarge) Error() string { + return fmt.Sprintf("wkb: number of elements at level %d (%d) exceeds %d", e.Level, e.N, e.Limit) +} + +// Geometry type IDs. +const ( + PointID = 1 + LineStringID = 2 + PolygonID = 3 + MultiPointID = 4 + MultiLineStringID = 5 + MultiPolygonID = 6 + GeometryCollectionID = 7 + PolyhedralSurfaceID = 15 + TINID = 16 + TriangleID = 17 +) + +// ReadFlatCoords0 reads flat coordinates 0. +func ReadFlatCoords0(r io.Reader, byteOrder binary.ByteOrder, stride int) ([]float64, error) { + coord := make([]float64, stride) + if err := ReadFloatArray(r, byteOrder, coord); err != nil { + return nil, err + } + return coord, nil +} + +// ReadFlatCoords1 reads flat coordinates 1. +func ReadFlatCoords1(r io.Reader, byteOrder binary.ByteOrder, stride int) ([]float64, error) { + n, err := ReadUInt32(r, byteOrder) + if err != nil { + return nil, err + } + if limit := MaxGeometryElements[1]; limit >= 0 && int(n) > limit { + return nil, ErrGeometryTooLarge{Level: 1, N: int(n), Limit: limit} + } + flatCoords := make([]float64, int(n)*stride) + if err := ReadFloatArray(r, byteOrder, flatCoords); err != nil { + return nil, err + } + return flatCoords, nil +} + +// ReadFlatCoords2 reads flat coordinates 2. +func ReadFlatCoords2(r io.Reader, byteOrder binary.ByteOrder, stride int) ([]float64, []int, error) { + n, err := ReadUInt32(r, byteOrder) + if err != nil { + return nil, nil, err + } + if limit := MaxGeometryElements[2]; limit >= 0 && int(n) > limit { + return nil, nil, ErrGeometryTooLarge{Level: 2, N: int(n), Limit: limit} + } + var flatCoordss []float64 + ends := make([]int, n) + for i := range n { + flatCoords, err := ReadFlatCoords1(r, byteOrder, stride) + if err != nil { + return nil, nil, err + } + flatCoordss = append(flatCoordss, flatCoords...) + ends[i] = len(flatCoordss) + } + return flatCoordss, ends, nil +} + +// WriteFlatCoords0 writes flat coordinates 0. +func WriteFlatCoords0(w io.Writer, byteOrder binary.ByteOrder, coord []float64) error { + return WriteFloatArray(w, byteOrder, coord) +} + +// WriteFlatCoords1 writes flat coordinates 1. +func WriteFlatCoords1(w io.Writer, byteOrder binary.ByteOrder, coords []float64, stride int) error { + if err := WriteUInt32(w, byteOrder, uint32(len(coords)/stride)); err != nil { + return err + } + return WriteFloatArray(w, byteOrder, coords) +} + +// WriteFlatCoords2 writes flat coordinates 2. +func WriteFlatCoords2(w io.Writer, byteOrder binary.ByteOrder, flatCoords []float64, ends []int, stride int) error { + if err := WriteUInt32(w, byteOrder, uint32(len(ends))); err != nil { + return err + } + offset := 0 + for _, end := range ends { + if err := WriteFlatCoords1(w, byteOrder, flatCoords[offset:end], stride); err != nil { + return err + } + offset = end + } + return nil +} diff --git a/vendor/github.com/twpayne/go-geom/flat.go b/vendor/github.com/twpayne/go-geom/flat.go new file mode 100644 index 000000000..8b85b1db7 --- /dev/null +++ b/vendor/github.com/twpayne/go-geom/flat.go @@ -0,0 +1,423 @@ +package geom + +import "math" + +type geom0 struct { + layout Layout + stride int + flatCoords []float64 + srid int +} + +type geom1 struct { + geom0 +} + +type geom2 struct { + geom1 + ends []int +} + +type geom3 struct { + geom1 + endss [][]int +} + +// Bounds returns the bounds of g. +func (g *geom0) Bounds() *Bounds { + return NewBounds(g.layout).extendFlatCoords(g.flatCoords, 0, len(g.flatCoords), g.stride) +} + +// Coords returns all the coordinates in g, i.e. a single coordinate. +func (g *geom0) Coords() Coord { + return inflate0(g.flatCoords, 0, len(g.flatCoords), g.stride) +} + +// Empty returns true if g contains no coordinates. +func (g *geom0) Empty() bool { + return len(g.flatCoords) == 0 +} + +// Ends returns the end indexes of sub-structures of g, i.e. an empty slice. +func (g *geom0) Ends() []int { + return nil +} + +// Endss returns the end indexes of sub-sub-structures of g, i.e. an empty +// slice. +func (g *geom0) Endss() [][]int { + return nil +} + +// FlatCoords returns the flat coordinates of g. +func (g *geom0) FlatCoords() []float64 { + return g.flatCoords +} + +// Layout returns g's layout. +func (g *geom0) Layout() Layout { + return g.layout +} + +// NumCoords returns the number of coordinates in g, i.e. 1. +func (g *geom0) NumCoords() int { + return 1 +} + +// Reserve reserves space in g for n coordinates. +func (g *geom0) Reserve(n int) { + if cap(g.flatCoords) < n*g.stride { + fcs := make([]float64, len(g.flatCoords), n*g.stride) + copy(fcs, g.flatCoords) + g.flatCoords = fcs + } +} + +// SRID returns g's SRID. +func (g *geom0) SRID() int { + return g.srid +} + +func (g *geom0) setCoords(coords0 []float64) error { + var err error + g.flatCoords, err = deflate0(nil, coords0, g.stride) + return err +} + +// Stride returns g's stride. +func (g *geom0) Stride() int { + return g.stride +} + +func (g *geom0) verify() error { + if g.stride != g.layout.Stride() { + return errStrideLayoutMismatch + } + if g.stride == 0 { + if len(g.flatCoords) != 0 { + return errNonEmptyFlatCoords + } + return nil + } + if len(g.flatCoords) != g.stride { + return errLengthStrideMismatch + } + return nil +} + +// Coord returns the ith coord of g. +func (g *geom1) Coord(i int) Coord { + return g.flatCoords[i*g.stride : (i+1)*g.stride] +} + +// Coords unpacks and returns all of g's coordinates. +func (g *geom1) Coords() []Coord { + return inflate1(g.flatCoords, 0, len(g.flatCoords), g.stride) +} + +// NumCoords returns the number of coordinates in g. +func (g *geom1) NumCoords() int { + return len(g.flatCoords) / g.stride +} + +// Reverse reverses the order of g's coordinates. +func (g *geom1) Reverse() { + reverse1(g.flatCoords, 0, len(g.flatCoords), g.stride) +} + +func (g *geom1) setCoords(coords1 []Coord) error { + var err error + g.flatCoords, err = deflate1(nil, coords1, g.stride) + return err +} + +func (g *geom1) verify() error { + if g.stride != g.layout.Stride() { + return errStrideLayoutMismatch + } + if g.stride == 0 { + if len(g.flatCoords) != 0 { + return errNonEmptyFlatCoords + } + } else { + if len(g.flatCoords)%g.stride != 0 { + return errLengthStrideMismatch + } + } + return nil +} + +// Coords returns all of g's coordinates. +func (g *geom2) Coords() [][]Coord { + return inflate2(g.flatCoords, 0, g.ends, g.stride) +} + +// Ends returns the end indexes of all sub-structures in g. +func (g *geom2) Ends() []int { + return g.ends +} + +// Reverse reverses the order of coordinates for each sub-structure in g. +func (g *geom2) Reverse() { + reverse2(g.flatCoords, 0, g.ends, g.stride) +} + +func (g *geom2) setCoords(coords2 [][]Coord) error { + var err error + g.flatCoords, g.ends, err = deflate2(nil, nil, coords2, g.stride) + return err +} + +func (g *geom2) verify() error { + if g.stride != g.layout.Stride() { + return errStrideLayoutMismatch + } + if g.stride == 0 { + if len(g.flatCoords) != 0 { + return errNonEmptyFlatCoords + } + if len(g.ends) != 0 { + return errNonEmptyEnds + } + return nil + } + if len(g.flatCoords)%g.stride != 0 { + return errLengthStrideMismatch + } + offset := 0 + for _, end := range g.ends { + if end%g.stride != 0 { + return errMisalignedEnd + } + if end < offset { + return errOutOfOrderEnd + } + offset = end + } + if offset != len(g.flatCoords) { + return errIncorrectEnd + } + return nil +} + +// Coords returns all the coordinates in g. +func (g *geom3) Coords() [][][]Coord { + return inflate3(g.flatCoords, 0, g.endss, g.stride) +} + +// Endss returns a list of all the sub-sub-structures in g. +func (g *geom3) Endss() [][]int { + return g.endss +} + +// Reverse reverses the order of coordinates for each sub-sub-structure in g. +func (g *geom3) Reverse() { + reverse3(g.flatCoords, 0, g.endss, g.stride) +} + +func (g *geom3) setCoords(coords3 [][][]Coord) error { + var err error + g.flatCoords, g.endss, err = deflate3(nil, nil, coords3, g.stride) + return err +} + +func (g *geom3) verify() error { + if g.stride != g.layout.Stride() { + return errStrideLayoutMismatch + } + if g.stride == 0 { + if len(g.flatCoords) != 0 { + return errNonEmptyFlatCoords + } + if len(g.endss) != 0 { + return errNonEmptyEndss + } + return nil + } + if len(g.flatCoords)%g.stride != 0 { + return errLengthStrideMismatch + } + offset := 0 + for _, ends := range g.endss { + for _, end := range ends { + if end%g.stride != 0 { + return errMisalignedEnd + } + if end < offset { + return errOutOfOrderEnd + } + offset = end + } + } + if offset != len(g.flatCoords) { + return errIncorrectEnd + } + return nil +} + +func doubleArea1(flatCoords []float64, offset, end, stride int) float64 { + var doubleArea float64 + for i := offset + stride; i < end; i += stride { + doubleArea += (flatCoords[i+1] - flatCoords[i+1-stride]) * (flatCoords[i] + flatCoords[i-stride]) + } + return doubleArea +} + +func doubleArea2(flatCoords []float64, offset int, ends []int, stride int) float64 { + var doubleArea float64 + for _, end := range ends { + doubleArea += doubleArea1(flatCoords, offset, end, stride) + offset = end + } + return doubleArea +} + +func doubleArea3(flatCoords []float64, offset int, endss [][]int, stride int) float64 { + var doubleArea float64 + for _, ends := range endss { + doubleArea += doubleArea2(flatCoords, offset, ends, stride) + offset = ends[len(ends)-1] + } + return doubleArea +} + +func deflate0(flatCoords []float64, c Coord, stride int) ([]float64, error) { + if len(c) != stride { + return nil, ErrStrideMismatch{Got: len(c), Want: stride} + } + flatCoords = append(flatCoords, c...) + return flatCoords, nil +} + +func deflate1(flatCoords []float64, coords1 []Coord, stride int) ([]float64, error) { + for _, c := range coords1 { + var err error + flatCoords, err = deflate0(flatCoords, c, stride) + if err != nil { + return nil, err + } + } + return flatCoords, nil +} + +func deflate2( + flatCoords []float64, ends []int, coords2 [][]Coord, stride int, +) ([]float64, []int, error) { + for _, coords1 := range coords2 { + var err error + flatCoords, err = deflate1(flatCoords, coords1, stride) + if err != nil { + return nil, nil, err + } + ends = append(ends, len(flatCoords)) + } + return flatCoords, ends, nil +} + +func deflate3( + flatCoords []float64, endss [][]int, coords3 [][][]Coord, stride int, +) ([]float64, [][]int, error) { + for _, coords2 := range coords3 { + var err error + var ends []int + flatCoords, ends, err = deflate2(flatCoords, ends, coords2, stride) + if err != nil { + return nil, nil, err + } + endss = append(endss, ends) + } + return flatCoords, endss, nil +} + +func inflate0(flatCoords []float64, offset, end, stride int) Coord { + if offset+stride != end { + panic("geom: stride mismatch") + } + c := make([]float64, stride) + copy(c, flatCoords[offset:end]) + return c +} + +func inflate1(flatCoords []float64, offset, end, stride int) []Coord { + coords1 := make([]Coord, (end-offset)/stride) + for i := range coords1 { + coords1[i] = inflate0(flatCoords, offset, offset+stride, stride) + offset += stride + } + return coords1 +} + +func inflate2(flatCoords []float64, offset int, ends []int, stride int) [][]Coord { + coords2 := make([][]Coord, len(ends)) + for i := range coords2 { + end := ends[i] + coords2[i] = inflate1(flatCoords, offset, end, stride) + offset = end + } + return coords2 +} + +func inflate3(flatCoords []float64, offset int, endss [][]int, stride int) [][][]Coord { + coords3 := make([][][]Coord, len(endss)) + for i := range coords3 { + ends := endss[i] + coords3[i] = inflate2(flatCoords, offset, ends, stride) + if len(ends) > 0 { + offset = ends[len(ends)-1] + } + } + return coords3 +} + +func length1(flatCoords []float64, offset, end, stride int) float64 { + var length float64 + for i := offset + stride; i < end; i += stride { + dx := flatCoords[i] - flatCoords[i-stride] + dy := flatCoords[i+1] - flatCoords[i+1-stride] + length += math.Sqrt(dx*dx + dy*dy) + } + return length +} + +func length2(flatCoords []float64, offset int, ends []int, stride int) float64 { + var length float64 + for _, end := range ends { + length += length1(flatCoords, offset, end, stride) + offset = end + } + return length +} + +func length3(flatCoords []float64, offset int, endss [][]int, stride int) float64 { + var length float64 + for _, ends := range endss { + length += length2(flatCoords, offset, ends, stride) + offset = ends[len(ends)-1] + } + return length +} + +func reverse1(flatCoords []float64, offset, end, stride int) { + for i, j := offset+stride, end; i <= j; i, j = i+stride, j-stride { + for k := range stride { + flatCoords[i-stride+k], flatCoords[j-stride+k] = flatCoords[j-stride+k], flatCoords[i-stride+k] + } + } +} + +func reverse2(flatCoords []float64, offset int, ends []int, stride int) { + for _, end := range ends { + reverse1(flatCoords, offset, end, stride) + offset = end + } +} + +func reverse3(flatCoords []float64, offset int, endss [][]int, stride int) { + for _, ends := range endss { + if len(ends) == 0 { + continue + } + reverse2(flatCoords, offset, ends, stride) + offset = ends[len(ends)-1] + } +} diff --git a/vendor/github.com/twpayne/go-geom/geom.go b/vendor/github.com/twpayne/go-geom/geom.go new file mode 100644 index 000000000..bacdebf80 --- /dev/null +++ b/vendor/github.com/twpayne/go-geom/geom.go @@ -0,0 +1,251 @@ +// Package geom implements efficient geometry types for geospatial +// applications. +package geom + +//go:generate goderive . + +import ( + "errors" + "fmt" + "math" +) + +// A Layout describes the meaning of an N-dimensional coordinate. Layout(N) for +// N > 4 is a valid layout, in which case the first dimensions are interpreted +// to be X, Y, Z, and M and extra dimensions have no special meaning. M values +// are considered part of a linear referencing system (e.g. classical time or +// distance along a path). 1-dimensional layouts are not supported. +type Layout int + +const ( + // NoLayout is an unknown layout. + NoLayout Layout = iota + // XY is a 2D layout (X and Y). + XY + // XYZ is 3D layout (X, Y, and Z). + XYZ + // XYM is a 2D layout with an M value. + XYM + // XYZM is a 3D layout with an M value. + XYZM +) + +// An ErrLayoutMismatch is returned when geometries with different layouts +// cannot be combined. +type ErrLayoutMismatch struct { + Got Layout + Want Layout +} + +func (e ErrLayoutMismatch) Error() string { + return fmt.Sprintf("geom: layout mismatch, got %s, want %s", e.Got, e.Want) +} + +// An ErrStrideMismatch is returned when the stride does not match the expected +// stride. +type ErrStrideMismatch struct { + Got int + Want int +} + +func (e ErrStrideMismatch) Error() string { + return fmt.Sprintf("geom: stride mismatch, got %d, want %d", e.Got, e.Want) +} + +// An ErrUnsupportedLayout is returned when the requested layout is not +// supported. +type ErrUnsupportedLayout Layout + +func (e ErrUnsupportedLayout) Error() string { + return fmt.Sprintf("geom: unsupported layout %s", Layout(e)) +} + +// An ErrUnsupportedType is returned when the requested type is not supported. +type ErrUnsupportedType struct { + Value interface{} +} + +func (e ErrUnsupportedType) Error() string { + return fmt.Sprintf("geom: unsupported type %T", e.Value) +} + +// A Coord represents an N-dimensional coordinate. +type Coord []float64 + +// Clone returns a deep copy of c. +func (c Coord) Clone() Coord { + return deriveCloneCoord(c) +} + +// X returns the x coordinate of c. X is assumed to be the first ordinate. +func (c Coord) X() float64 { + return c[0] +} + +// Y returns the y coordinate of c. Y is assumed to be the second ordinate. +func (c Coord) Y() float64 { + return c[1] +} + +// Set copies the ordinate data from the other coord to this coord. +func (c Coord) Set(other Coord) { + copy(c, other) +} + +// Equal compares that all ordinates are the same in this and the other coords. +// It is assumed that this coord and other coord both have the same (provided) +// layout. +func (c Coord) Equal(layout Layout, other Coord) bool { + numOrds := len(c) + + if layout.Stride() < numOrds { + numOrds = layout.Stride() + } + + if (len(c) < layout.Stride() || len(other) < layout.Stride()) && len(c) != len(other) { + return false + } + + for i := range numOrds { + if math.IsNaN(c[i]) || math.IsNaN(other[i]) { + if !math.IsNaN(c[i]) || !math.IsNaN(other[i]) { + return false + } + } else if c[i] != other[i] { + return false + } + } + + return true +} + +// T is a generic interface implemented by all geometry types. +type T interface { + Layout() Layout + Stride() int + Bounds() *Bounds + FlatCoords() []float64 + Ends() []int + Endss() [][]int + SRID() int + Empty() bool +} + +// MIndex returns the index of the M dimension, or -1 if the l does not have an +// M dimension. +func (l Layout) MIndex() int { + switch l { + case NoLayout, XY, XYZ: + return -1 + case XYM: + return 2 + case XYZM: + return 3 + default: + return 3 + } +} + +// Stride returns l's number of dimensions. +func (l Layout) Stride() int { + switch l { + case NoLayout: + return 0 + case XY: + return 2 + case XYZ: + return 3 + case XYM: + return 3 + case XYZM: + return 4 + default: + return int(l) + } +} + +// String returns a human-readable string representing l. +func (l Layout) String() string { + switch l { + case NoLayout: + return "NoLayout" + case XY: + return "XY" + case XYZ: + return "XYZ" + case XYM: + return "XYM" + case XYZM: + return "XYZM" + default: + return fmt.Sprintf("Layout(%d)", int(l)) + } +} + +// ZIndex returns the index of l's Z dimension, or -1 if l does not have a Z +// dimension. +func (l Layout) ZIndex() int { + switch l { + case NoLayout, XY, XYM: + return -1 + default: + return 2 + } +} + +// SetSRID sets the SRID of an arbitrary geometry. +func SetSRID(g T, srid int) (T, error) { + switch g := g.(type) { + case *Point: + return g.SetSRID(srid), nil + case *LineString: + return g.SetSRID(srid), nil + case *LinearRing: + return g.SetSRID(srid), nil + case *Polygon: + return g.SetSRID(srid), nil + case *MultiPoint: + return g.SetSRID(srid), nil + case *MultiLineString: + return g.SetSRID(srid), nil + case *MultiPolygon: + return g.SetSRID(srid), nil + case *GeometryCollection: + return g.SetSRID(srid), nil + default: + return g, &ErrUnsupportedType{ + Value: g, + } + } +} + +// TransformInPlace replaces all coordinates in g using f. +func TransformInPlace(g T, f func(Coord)) T { + var ( + flatCoords = g.FlatCoords() + stride = g.Stride() + ) + for i, n := 0, len(flatCoords); i < n; i += stride { + f(flatCoords[i : i+stride]) + } + return g +} + +// Must panics if err is not nil, otherwise it returns g. +func Must(g T, err error) T { + if err != nil { + panic(err) + } + return g +} + +var ( + errIncorrectEnd = errors.New("geom: incorrect end") + errLengthStrideMismatch = errors.New("geom: length/stride mismatch") + errMisalignedEnd = errors.New("geom: misaligned end") + errNonEmptyEnds = errors.New("geom: non-empty ends") + errNonEmptyEndss = errors.New("geom: non-empty endss") + errNonEmptyFlatCoords = errors.New("geom: non-empty flatCoords") + errOutOfOrderEnd = errors.New("geom: out-of-order end") + errStrideLayoutMismatch = errors.New("geom: stride/layout mismatch") +) diff --git a/vendor/github.com/twpayne/go-geom/geometrycollection.go b/vendor/github.com/twpayne/go-geom/geometrycollection.go new file mode 100644 index 000000000..3b7244b40 --- /dev/null +++ b/vendor/github.com/twpayne/go-geom/geometrycollection.go @@ -0,0 +1,169 @@ +package geom + +// A GeometryCollection is a collection of arbitrary geometries with the same +// SRID. +type GeometryCollection struct { + layout Layout + geoms []T + srid int +} + +// NewGeometryCollection returns a new empty GeometryCollection. +func NewGeometryCollection() *GeometryCollection { + return &GeometryCollection{} +} + +// Geom returns the ith geometry in g. +func (g *GeometryCollection) Geom(i int) T { + return g.geoms[i] +} + +// Geoms returns the geometries in g. +func (g *GeometryCollection) Geoms() []T { + return g.geoms +} + +// Layout returns the smallest layout that covers all of the layouts in g's +// geometries. +func (g *GeometryCollection) Layout() Layout { + if g.layout != NoLayout { + return g.layout + } + maxLayout := NoLayout + for _, g := range g.geoms { + switch l := g.Layout(); l { + case XYZ: + if maxLayout == XYM { + maxLayout = XYZM + } else if l > maxLayout { + maxLayout = l + } + case XYM: + if maxLayout == XYZ { + maxLayout = XYZM + } else if l > maxLayout { + maxLayout = l + } + default: + if l > maxLayout { + maxLayout = l + } + } + } + return maxLayout +} + +// NumGeoms returns the number of geometries in g. +func (g *GeometryCollection) NumGeoms() int { + return len(g.geoms) +} + +// Stride returns the stride of g's layout. +func (g *GeometryCollection) Stride() int { + return g.Layout().Stride() +} + +// Bounds returns the bounds of all the geometries in g. +func (g *GeometryCollection) Bounds() *Bounds { + // FIXME this needs work for mixing layouts, e.g. XYZ and XYM + b := NewBounds(g.Layout()) + for _, g := range g.geoms { + b = b.Extend(g) + } + return b +} + +// Empty returns true if the collection is empty. +// This can return true if the GeometryCollection contains multiple Geometry objects +// which are all empty. +func (g *GeometryCollection) Empty() bool { + for _, g := range g.geoms { + if !g.Empty() { + return false + } + } + return true +} + +// FlatCoords panics. +func (g *GeometryCollection) FlatCoords() []float64 { + panic("FlatCoords() called on a GeometryCollection") +} + +// Ends panics. +func (g *GeometryCollection) Ends() []int { + panic("Ends() called on a GeometryCollection") +} + +// Endss panics. +func (g *GeometryCollection) Endss() [][]int { + panic("Endss() called on a GeometryCollection") +} + +// SRID returns g's SRID. +func (g *GeometryCollection) SRID() int { + return g.srid +} + +// MustPush pushes gs to g. It panics on any error. +func (g *GeometryCollection) MustPush(gs ...T) *GeometryCollection { + if err := g.Push(gs...); err != nil { + panic(err) + } + return g +} + +// CheckLayout checks all geometries in the collection match the given +// layout. +func (g *GeometryCollection) CheckLayout(layout Layout) error { + if layout != NoLayout { + for _, geom := range g.geoms { + if geomLayout := geom.Layout(); geomLayout != layout { + return ErrLayoutMismatch{ + Got: layout, + Want: geomLayout, + } + } + } + } + return nil +} + +// MustSetLayout sets g's layout. It panics on any error. +func (g *GeometryCollection) MustSetLayout(layout Layout) *GeometryCollection { + if err := g.SetLayout(layout); err != nil { + panic(err) + } + return g +} + +// Push appends geometries. +func (g *GeometryCollection) Push(gs ...T) error { + if g.layout != NoLayout { + for _, geom := range gs { + if geomLayout := geom.Layout(); geomLayout != g.layout { + return ErrLayoutMismatch{ + Got: geomLayout, + Want: g.layout, + } + } + } + } + g.geoms = append(g.geoms, gs...) + return nil +} + +// SetLayout sets g's layout. +func (g *GeometryCollection) SetLayout(layout Layout) error { + if err := g.CheckLayout(layout); err != nil { + return err + } + g.layout = layout + return nil +} + +// SetSRID sets g's SRID and the SRID of all its elements. +func (g *GeometryCollection) SetSRID(srid int) *GeometryCollection { + g.srid = srid + return g +} diff --git a/vendor/github.com/twpayne/go-geom/linearring.go b/vendor/github.com/twpayne/go-geom/linearring.go new file mode 100644 index 000000000..d46dae574 --- /dev/null +++ b/vendor/github.com/twpayne/go-geom/linearring.go @@ -0,0 +1,60 @@ +package geom + +// A LinearRing is a linear ring. +type LinearRing struct { + geom1 +} + +// NewLinearRing returns a new LinearRing with no coordinates. +func NewLinearRing(layout Layout) *LinearRing { + return NewLinearRingFlat(layout, nil) +} + +// NewLinearRingFlat returns a new LinearRing with the given flat coordinates. +func NewLinearRingFlat(layout Layout, flatCoords []float64) *LinearRing { + g := new(LinearRing) + g.layout = layout + g.stride = layout.Stride() + g.flatCoords = flatCoords + return g +} + +// Area returns the area. +func (g *LinearRing) Area() float64 { + return doubleArea1(g.flatCoords, 0, len(g.flatCoords), g.stride) / 2 +} + +// Clone returns a deep copy. +func (g *LinearRing) Clone() *LinearRing { + return deriveCloneLinearRing(g) +} + +// Length returns the length of the perimeter. +func (g *LinearRing) Length() float64 { + return length1(g.flatCoords, 0, len(g.flatCoords), g.stride) +} + +// MustSetCoords sets the coordinates and panics if there is any error. +func (g *LinearRing) MustSetCoords(coords []Coord) *LinearRing { + Must(g.SetCoords(coords)) + return g +} + +// SetCoords sets the coordinates. +func (g *LinearRing) SetCoords(coords []Coord) (*LinearRing, error) { + if err := g.setCoords(coords); err != nil { + return nil, err + } + return g, nil +} + +// SetSRID sets the SRID of g. +func (g *LinearRing) SetSRID(srid int) *LinearRing { + g.srid = srid + return g +} + +// Swap swaps the values of g and g2. +func (g *LinearRing) Swap(g2 *LinearRing) { + *g, *g2 = *g2, *g +} diff --git a/vendor/github.com/twpayne/go-geom/linestring.go b/vendor/github.com/twpayne/go-geom/linestring.go new file mode 100644 index 000000000..2c7ea9050 --- /dev/null +++ b/vendor/github.com/twpayne/go-geom/linestring.go @@ -0,0 +1,99 @@ +package geom + +// A LineString represents a single, unbroken line, linearly interpreted +// between zero or more control points. +type LineString struct { + geom1 +} + +// NewLineString returns a new LineString with layout l and no control points. +func NewLineString(l Layout) *LineString { + return NewLineStringFlat(l, nil) +} + +// NewLineStringFlat returns a new LineString with layout l and control points +// flatCoords. +func NewLineStringFlat(layout Layout, flatCoords []float64) *LineString { + g := new(LineString) + g.layout = layout + g.stride = layout.Stride() + g.flatCoords = flatCoords + return g +} + +// Area returns the area of g, i.e. zero. +func (g *LineString) Area() float64 { + return 0 +} + +// Clone returns a copy of g that does not alias g. +func (g *LineString) Clone() *LineString { + return deriveCloneLineString(g) +} + +// Interpolate returns the index and delta of val in dimension dim. +func (g *LineString) Interpolate(val float64, dim int) (int, float64) { + n := len(g.flatCoords) + if n == 0 { + panic("geom: empty linestring") + } + if val <= g.flatCoords[dim] { + return 0, 0 + } + if g.flatCoords[n-g.stride+dim] <= val { + return (n - 1) / g.stride, 0 + } + low := 0 + high := n / g.stride + for low < high { + mid := (low + high) / 2 + if val < g.flatCoords[mid*g.stride+dim] { + high = mid + } else { + low = mid + 1 + } + } + low-- + val0 := g.flatCoords[low*g.stride+dim] + if val == val0 { + return low, 0 + } + val1 := g.flatCoords[(low+1)*g.stride+dim] + return low, (val - val0) / (val1 - val0) +} + +// Length returns the length of g. +func (g *LineString) Length() float64 { + return length1(g.flatCoords, 0, len(g.flatCoords), g.stride) +} + +// MustSetCoords is like SetCoords but it panics on any error. +func (g *LineString) MustSetCoords(coords []Coord) *LineString { + Must(g.SetCoords(coords)) + return g +} + +// SetCoords sets the coordinates of g. +func (g *LineString) SetCoords(coords []Coord) (*LineString, error) { + if err := g.setCoords(coords); err != nil { + return nil, err + } + return g, nil +} + +// SetSRID sets the SRID of g. +func (g *LineString) SetSRID(srid int) *LineString { + g.srid = srid + return g +} + +// SubLineString returns a LineString from starts at index start and stops at +// index stop of g. The returned LineString aliases g. +func (g *LineString) SubLineString(start, stop int) *LineString { + return NewLineStringFlat(g.layout, g.flatCoords[start*g.stride:stop*g.stride]) +} + +// Swap swaps the values of g and g2. +func (g *LineString) Swap(g2 *LineString) { + *g, *g2 = *g2, *g +} diff --git a/vendor/github.com/twpayne/go-geom/multilinestring.go b/vendor/github.com/twpayne/go-geom/multilinestring.go new file mode 100644 index 000000000..645f74dde --- /dev/null +++ b/vendor/github.com/twpayne/go-geom/multilinestring.go @@ -0,0 +1,88 @@ +package geom + +// A MultiLineString is a collection of LineStrings. +type MultiLineString struct { + geom2 +} + +// NewMultiLineString returns a new MultiLineString with no LineStrings. +func NewMultiLineString(layout Layout) *MultiLineString { + return NewMultiLineStringFlat(layout, nil, nil) +} + +// NewMultiLineStringFlat returns a new MultiLineString with the given flat coordinates. +func NewMultiLineStringFlat(layout Layout, flatCoords []float64, ends []int) *MultiLineString { + g := new(MultiLineString) + g.layout = layout + g.stride = layout.Stride() + g.flatCoords = flatCoords + g.ends = ends + return g +} + +// Area returns the area of g, i.e. 0. +func (g *MultiLineString) Area() float64 { + return 0 +} + +// Clone returns a deep copy. +func (g *MultiLineString) Clone() *MultiLineString { + return deriveCloneMultiLineString(g) +} + +// Length returns the sum of the length of the LineStrings. +func (g *MultiLineString) Length() float64 { + return length2(g.flatCoords, 0, g.ends, g.stride) +} + +// LineString returns the ith LineString. +func (g *MultiLineString) LineString(i int) *LineString { + offset := 0 + if i > 0 { + offset = g.ends[i-1] + } + if offset == g.ends[i] { + return NewLineString(g.layout) + } + return NewLineStringFlat(g.layout, g.flatCoords[offset:g.ends[i]]) +} + +// MustSetCoords sets the coordinates and panics on any error. +func (g *MultiLineString) MustSetCoords(coords [][]Coord) *MultiLineString { + Must(g.SetCoords(coords)) + return g +} + +// NumLineStrings returns the number of LineStrings. +func (g *MultiLineString) NumLineStrings() int { + return len(g.ends) +} + +// Push appends a LineString. +func (g *MultiLineString) Push(ls *LineString) error { + if ls.layout != g.layout { + return ErrLayoutMismatch{Got: ls.layout, Want: g.layout} + } + g.flatCoords = append(g.flatCoords, ls.flatCoords...) + g.ends = append(g.ends, len(g.flatCoords)) + return nil +} + +// SetCoords sets the coordinates. +func (g *MultiLineString) SetCoords(coords [][]Coord) (*MultiLineString, error) { + if err := g.setCoords(coords); err != nil { + return nil, err + } + return g, nil +} + +// SetSRID sets the SRID of g. +func (g *MultiLineString) SetSRID(srid int) *MultiLineString { + g.srid = srid + return g +} + +// Swap swaps the values of g and g2. +func (g *MultiLineString) Swap(g2 *MultiLineString) { + *g, *g2 = *g2, *g +} diff --git a/vendor/github.com/twpayne/go-geom/multipoint.go b/vendor/github.com/twpayne/go-geom/multipoint.go new file mode 100644 index 000000000..2d37f9f6f --- /dev/null +++ b/vendor/github.com/twpayne/go-geom/multipoint.go @@ -0,0 +1,160 @@ +package geom + +// A MultiPoint is a collection of Points. +type MultiPoint struct { + // To represent an MultiPoint that allows EMPTY elements, e.g. + // MULTIPOINT ( EMPTY, POINT(1.0 1.0), EMPTY), we have to allow + // record ends. If there is an empty point, ends[i] == ends[i-1]. + geom2 +} + +// NewMultiPoint returns a new, empty, MultiPoint. +func NewMultiPoint(layout Layout) *MultiPoint { + return NewMultiPointFlat(layout, nil) +} + +// NewMultiPointFlatOption represents an option that can be passed into +// NewMultiPointFlat. +type NewMultiPointFlatOption func(*MultiPoint) + +// NewMultiPointFlatOptionWithEnds allows passing ends to NewMultiPointFlat, +// which allows the representation of empty points. +func NewMultiPointFlatOptionWithEnds(ends []int) NewMultiPointFlatOption { + return func(mp *MultiPoint) { + mp.ends = ends + } +} + +// NewMultiPointFlat returns a new MultiPoint with the given flat coordinates. +// Assumes no points are empty by default. Use `NewMultiPointFlatOptionWithEnds` +// to specify empty points. +func NewMultiPointFlat( + layout Layout, flatCoords []float64, opts ...NewMultiPointFlatOption, +) *MultiPoint { + g := new(MultiPoint) + g.layout = layout + g.stride = layout.Stride() + g.flatCoords = flatCoords + for _, opt := range opts { + opt(g) + } + // If no ends are provided, assume all points are non empty. + if g.ends == nil && len(g.flatCoords) > 0 { + numCoords := 0 + if g.stride > 0 { + numCoords = len(flatCoords) / g.stride + } + g.ends = make([]int, numCoords) + for i := range numCoords { + g.ends[i] = (i + 1) * g.stride + } + } + return g +} + +// Area returns the area of g, i.e. zero. +func (g *MultiPoint) Area() float64 { + return 0 +} + +// Clone returns a deep copy. +func (g *MultiPoint) Clone() *MultiPoint { + return deriveCloneMultiPoint(g) +} + +// Length returns zero. +func (g *MultiPoint) Length() float64 { + return 0 +} + +// MustSetCoords sets the coordinates and panics on any error. +func (g *MultiPoint) MustSetCoords(coords []Coord) *MultiPoint { + Must(g.SetCoords(coords)) + return g +} + +// Coord returns the ith coord of g. +func (g *MultiPoint) Coord(i int) Coord { + before := 0 + if i > 0 { + before = g.ends[i-1] + } + if g.ends[i] == before { + return nil + } + return g.flatCoords[before:g.ends[i]] +} + +// SetCoords sets the coordinates. +func (g *MultiPoint) SetCoords(coords []Coord) (*MultiPoint, error) { + g.flatCoords = nil + g.ends = nil + for _, c := range coords { + if c != nil { + var err error + g.flatCoords, err = deflate0(g.flatCoords, c, g.stride) + if err != nil { + return nil, err + } + } + g.ends = append(g.ends, len(g.flatCoords)) + } + return g, nil +} + +// Coords unpacks and returns all of g's coordinates. +func (g *MultiPoint) Coords() []Coord { + coords1 := make([]Coord, len(g.ends)) + offset := 0 + prevEnd := 0 + for i, end := range g.ends { + if end != prevEnd { + coords1[i] = inflate0(g.flatCoords, offset, offset+g.stride, g.stride) + offset += g.stride + } + prevEnd = end + } + return coords1 +} + +// NumCoords returns the number of coordinates in g. +func (g *MultiPoint) NumCoords() int { + return len(g.ends) +} + +// SetSRID sets the SRID of g. +func (g *MultiPoint) SetSRID(srid int) *MultiPoint { + g.srid = srid + return g +} + +// NumPoints returns the number of Points. +func (g *MultiPoint) NumPoints() int { + return len(g.ends) +} + +// Point returns the ith Point. +func (g *MultiPoint) Point(i int) *Point { + coord := g.Coord(i) + if coord == nil { + return NewPointEmpty(g.layout) + } + return NewPointFlat(g.layout, coord) +} + +// Push appends a point. +func (g *MultiPoint) Push(p *Point) error { + if p.layout != g.layout { + return ErrLayoutMismatch{Got: p.layout, Want: g.layout} + } + if !p.Empty() { + g.flatCoords = append(g.flatCoords, p.flatCoords...) + } + g.ends = append(g.ends, len(g.flatCoords)) + return nil +} + +// Swap swaps the values of g and g2. +func (g *MultiPoint) Swap(g2 *MultiPoint) { + *g, *g2 = *g2, *g +} diff --git a/vendor/github.com/twpayne/go-geom/multipolygon.go b/vendor/github.com/twpayne/go-geom/multipolygon.go new file mode 100644 index 000000000..b9c8ca608 --- /dev/null +++ b/vendor/github.com/twpayne/go-geom/multipolygon.go @@ -0,0 +1,115 @@ +package geom + +// A MultiPolygon is a collection of Polygons. +type MultiPolygon struct { + geom3 +} + +// NewMultiPolygon returns a new MultiPolygon with no Polygons. +func NewMultiPolygon(layout Layout) *MultiPolygon { + return NewMultiPolygonFlat(layout, nil, nil) +} + +// NewMultiPolygonFlat returns a new MultiPolygon with the given flat coordinates. +func NewMultiPolygonFlat(layout Layout, flatCoords []float64, endss [][]int) *MultiPolygon { + g := new(MultiPolygon) + g.layout = layout + g.stride = layout.Stride() + g.flatCoords = flatCoords + g.endss = endss + return g +} + +// Area returns the sum of the area of the individual Polygons. +func (g *MultiPolygon) Area() float64 { + return doubleArea3(g.flatCoords, 0, g.endss, g.stride) / 2 +} + +// Clone returns a deep copy. +func (g *MultiPolygon) Clone() *MultiPolygon { + return deriveCloneMultiPolygon(g) +} + +// Length returns the sum of the perimeters of the Polygons. +func (g *MultiPolygon) Length() float64 { + return length3(g.flatCoords, 0, g.endss, g.stride) +} + +// MustSetCoords sets the coordinates and panics on any error. +func (g *MultiPolygon) MustSetCoords(coords [][][]Coord) *MultiPolygon { + Must(g.SetCoords(coords)) + return g +} + +// NumPolygons returns the number of Polygons. +func (g *MultiPolygon) NumPolygons() int { + return len(g.endss) +} + +// Polygon returns the ith Polygon. +func (g *MultiPolygon) Polygon(i int) *Polygon { + if len(g.endss[i]) == 0 { + return NewPolygon(g.layout) + } + // Find the offset from the previous non-empty polygon element. + offset := 0 + lastNonEmptyIdx := i - 1 + for lastNonEmptyIdx >= 0 { + ends := g.endss[lastNonEmptyIdx] + if len(ends) > 0 { + offset = ends[len(ends)-1] + break + } + lastNonEmptyIdx-- + } + ends := make([]int, len(g.endss[i])) + if offset == 0 { + copy(ends, g.endss[i]) + } else { + for j, end := range g.endss[i] { + ends[j] = end - offset + } + } + return NewPolygonFlat(g.layout, g.flatCoords[offset:g.endss[i][len(g.endss[i])-1]], ends) +} + +// Push appends a Polygon. +func (g *MultiPolygon) Push(p *Polygon) error { + if p.layout != g.layout { + return ErrLayoutMismatch{Got: p.layout, Want: g.layout} + } + offset := len(g.flatCoords) + var ends []int + if len(p.ends) > 0 { + ends = make([]int, len(p.ends)) + if offset == 0 { + copy(ends, p.ends) + } else { + for i, end := range p.ends { + ends[i] = end + offset + } + } + } + g.flatCoords = append(g.flatCoords, p.flatCoords...) + g.endss = append(g.endss, ends) + return nil +} + +// SetCoords sets the coordinates. +func (g *MultiPolygon) SetCoords(coords [][][]Coord) (*MultiPolygon, error) { + if err := g.setCoords(coords); err != nil { + return nil, err + } + return g, nil +} + +// SetSRID sets the SRID of g. +func (g *MultiPolygon) SetSRID(srid int) *MultiPolygon { + g.srid = srid + return g +} + +// Swap swaps the values of g and g2. +func (g *MultiPolygon) Swap(g2 *MultiPolygon) { + *g, *g2 = *g2, *g +} diff --git a/vendor/github.com/twpayne/go-geom/point.go b/vendor/github.com/twpayne/go-geom/point.go new file mode 100644 index 000000000..69dc6d6c4 --- /dev/null +++ b/vendor/github.com/twpayne/go-geom/point.go @@ -0,0 +1,120 @@ +package geom + +import "math" + +// PointEmptyCoordHex is the hex representation of a NaN that represents +// an empty coord in a shape. +const PointEmptyCoordHex = 0x7FF8000000000000 + +// PointEmptyCoord is the NaN float64 representation of the empty coordinate. +func PointEmptyCoord() float64 { + return math.Float64frombits(PointEmptyCoordHex) +} + +// A Point represents a single point. +type Point struct { + geom0 +} + +// NewPoint allocates a new Point with layout l and all values zero. +func NewPoint(l Layout) *Point { + return NewPointFlat(l, make([]float64, l.Stride())) +} + +// NewPointEmpty allocates a new Point with no coordinates. +func NewPointEmpty(l Layout) *Point { + return NewPointFlat(l, nil) +} + +// NewPointFlat allocates a new Point with layout l and flat coordinates flatCoords. +func NewPointFlat(l Layout, flatCoords []float64) *Point { + g := new(Point) + g.layout = l + g.stride = l.Stride() + g.flatCoords = flatCoords + return g +} + +// NewPointFlatMaybeEmpty returns a new point, checking whether the point may be empty +// by checking wther all the points are NaN. +func NewPointFlatMaybeEmpty(layout Layout, flatCoords []float64) *Point { + isEmpty := true + for _, coord := range flatCoords { + if math.Float64bits(coord) != PointEmptyCoordHex { + isEmpty = false + break + } + } + if isEmpty { + return NewPointEmpty(layout) + } + return NewPointFlat(layout, flatCoords) +} + +// Area returns g's area, i.e. zero. +func (g *Point) Area() float64 { + return 0 +} + +// Clone returns a copy of g that does not alias g. +func (g *Point) Clone() *Point { + return deriveClonePoint(g) +} + +// Length returns the length of g, i.e. zero. +func (g *Point) Length() float64 { + return 0 +} + +// MustSetCoords is like SetCoords but panics on any error. +func (g *Point) MustSetCoords(coords Coord) *Point { + Must(g.SetCoords(coords)) + return g +} + +// SetCoords sets the coordinates of g. +func (g *Point) SetCoords(coords Coord) (*Point, error) { + if err := g.setCoords(coords); err != nil { + return nil, err + } + return g, nil +} + +// SetSRID sets the SRID of g. +func (g *Point) SetSRID(srid int) *Point { + g.srid = srid + return g +} + +// Swap swaps the values of g and g2. +func (g *Point) Swap(g2 *Point) { + *g, *g2 = *g2, *g +} + +// X returns g's X-coordinate. +func (g *Point) X() float64 { + return g.flatCoords[0] +} + +// Y returns g's Y-coordinate. +func (g *Point) Y() float64 { + return g.flatCoords[1] +} + +// Z returns g's Z-coordinate, or zero if g has no Z-coordinate. +func (g *Point) Z() float64 { + zIndex := g.layout.ZIndex() + if zIndex == -1 { + return 0 + } + return g.flatCoords[zIndex] +} + +// M returns g's M-coordinate, or zero if g has no M-coordinate. +func (g *Point) M() float64 { + mIndex := g.layout.MIndex() + if mIndex == -1 { + return 0 + } + return g.flatCoords[mIndex] +} diff --git a/vendor/github.com/twpayne/go-geom/polygon.go b/vendor/github.com/twpayne/go-geom/polygon.go new file mode 100644 index 000000000..e6d0b4791 --- /dev/null +++ b/vendor/github.com/twpayne/go-geom/polygon.go @@ -0,0 +1,87 @@ +package geom + +// A Polygon represents a polygon as a collection of LinearRings. The first +// LinearRing is the outer boundary. Subsequent LinearRings are inner +// boundaries (holes). +type Polygon struct { + geom2 +} + +// NewPolygon returns a new, empty, Polygon. +func NewPolygon(layout Layout) *Polygon { + return NewPolygonFlat(layout, nil, nil) +} + +// NewPolygonFlat returns a new Polygon with the given flat coordinates. +func NewPolygonFlat(layout Layout, flatCoords []float64, ends []int) *Polygon { + g := new(Polygon) + g.layout = layout + g.stride = layout.Stride() + g.flatCoords = flatCoords + g.ends = ends + return g +} + +// Area returns the area. +func (g *Polygon) Area() float64 { + return doubleArea2(g.flatCoords, 0, g.ends, g.stride) / 2 +} + +// Clone returns a deep copy. +func (g *Polygon) Clone() *Polygon { + return deriveClonePolygon(g) +} + +// Length returns the perimter. +func (g *Polygon) Length() float64 { + return length2(g.flatCoords, 0, g.ends, g.stride) +} + +// LinearRing returns the ith LinearRing. +func (g *Polygon) LinearRing(i int) *LinearRing { + offset := 0 + if i > 0 { + offset = g.ends[i-1] + } + return NewLinearRingFlat(g.layout, g.flatCoords[offset:g.ends[i]]) +} + +// MustSetCoords sets the coordinates and panics on any error. +func (g *Polygon) MustSetCoords(coords [][]Coord) *Polygon { + Must(g.SetCoords(coords)) + return g +} + +// NumLinearRings returns the number of LinearRings. +func (g *Polygon) NumLinearRings() int { + return len(g.ends) +} + +// Push appends a LinearRing. +func (g *Polygon) Push(lr *LinearRing) error { + if lr.layout != g.layout { + return ErrLayoutMismatch{Got: lr.layout, Want: g.layout} + } + g.flatCoords = append(g.flatCoords, lr.flatCoords...) + g.ends = append(g.ends, len(g.flatCoords)) + return nil +} + +// SetCoords sets the coordinates. +func (g *Polygon) SetCoords(coords [][]Coord) (*Polygon, error) { + if err := g.setCoords(coords); err != nil { + return nil, err + } + return g, nil +} + +// SetSRID sets the SRID of g. +func (g *Polygon) SetSRID(srid int) *Polygon { + g.srid = srid + return g +} + +// Swap swaps the values of g and g2. +func (g *Polygon) Swap(g2 *Polygon) { + *g, *g2 = *g2, *g +} diff --git a/vendor/golang.org/x/sys/cpu/asm_aix_ppc64.s b/vendor/golang.org/x/sys/cpu/asm_aix_ppc64.s new file mode 100644 index 000000000..269e173ca --- /dev/null +++ b/vendor/golang.org/x/sys/cpu/asm_aix_ppc64.s @@ -0,0 +1,17 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build gc + +#include "textflag.h" + +// +// System calls for ppc64, AIX are implemented in runtime/syscall_aix.go +// + +TEXT ·syscall6(SB),NOSPLIT,$0-88 + JMP syscall·syscall6(SB) + +TEXT ·rawSyscall6(SB),NOSPLIT,$0-88 + JMP syscall·rawSyscall6(SB) diff --git a/vendor/golang.org/x/sys/cpu/asm_darwin_arm64_gc.s b/vendor/golang.org/x/sys/cpu/asm_darwin_arm64_gc.s new file mode 100644 index 000000000..e07fa75eb --- /dev/null +++ b/vendor/golang.org/x/sys/cpu/asm_darwin_arm64_gc.s @@ -0,0 +1,12 @@ +// Copyright 2024 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build darwin && arm64 && gc + +#include "textflag.h" + +TEXT libc_sysctlbyname_trampoline<>(SB),NOSPLIT,$0-0 + JMP libc_sysctlbyname(SB) +GLOBL ·libc_sysctlbyname_trampoline_addr(SB), RODATA, $8 +DATA ·libc_sysctlbyname_trampoline_addr(SB)/8, $libc_sysctlbyname_trampoline<>(SB) diff --git a/vendor/golang.org/x/sys/cpu/asm_darwin_x86_gc.s b/vendor/golang.org/x/sys/cpu/asm_darwin_x86_gc.s new file mode 100644 index 000000000..ec2acfe54 --- /dev/null +++ b/vendor/golang.org/x/sys/cpu/asm_darwin_x86_gc.s @@ -0,0 +1,17 @@ +// Copyright 2024 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build darwin && amd64 && gc + +#include "textflag.h" + +TEXT libc_sysctl_trampoline<>(SB),NOSPLIT,$0-0 + JMP libc_sysctl(SB) +GLOBL ·libc_sysctl_trampoline_addr(SB), RODATA, $8 +DATA ·libc_sysctl_trampoline_addr(SB)/8, $libc_sysctl_trampoline<>(SB) + +TEXT libc_sysctlbyname_trampoline<>(SB),NOSPLIT,$0-0 + JMP libc_sysctlbyname(SB) +GLOBL ·libc_sysctlbyname_trampoline_addr(SB), RODATA, $8 +DATA ·libc_sysctlbyname_trampoline_addr(SB)/8, $libc_sysctlbyname_trampoline<>(SB) diff --git a/vendor/golang.org/x/sys/cpu/byteorder.go b/vendor/golang.org/x/sys/cpu/byteorder.go new file mode 100644 index 000000000..271055be0 --- /dev/null +++ b/vendor/golang.org/x/sys/cpu/byteorder.go @@ -0,0 +1,66 @@ +// Copyright 2019 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package cpu + +import ( + "runtime" +) + +// byteOrder is a subset of encoding/binary.ByteOrder. +type byteOrder interface { + Uint32([]byte) uint32 + Uint64([]byte) uint64 +} + +type littleEndian struct{} +type bigEndian struct{} + +func (littleEndian) Uint32(b []byte) uint32 { + _ = b[3] // bounds check hint to compiler; see golang.org/issue/14808 + return uint32(b[0]) | uint32(b[1])<<8 | uint32(b[2])<<16 | uint32(b[3])<<24 +} + +func (littleEndian) Uint64(b []byte) uint64 { + _ = b[7] // bounds check hint to compiler; see golang.org/issue/14808 + return uint64(b[0]) | uint64(b[1])<<8 | uint64(b[2])<<16 | uint64(b[3])<<24 | + uint64(b[4])<<32 | uint64(b[5])<<40 | uint64(b[6])<<48 | uint64(b[7])<<56 +} + +func (bigEndian) Uint32(b []byte) uint32 { + _ = b[3] // bounds check hint to compiler; see golang.org/issue/14808 + return uint32(b[3]) | uint32(b[2])<<8 | uint32(b[1])<<16 | uint32(b[0])<<24 +} + +func (bigEndian) Uint64(b []byte) uint64 { + _ = b[7] // bounds check hint to compiler; see golang.org/issue/14808 + return uint64(b[7]) | uint64(b[6])<<8 | uint64(b[5])<<16 | uint64(b[4])<<24 | + uint64(b[3])<<32 | uint64(b[2])<<40 | uint64(b[1])<<48 | uint64(b[0])<<56 +} + +// hostByteOrder returns littleEndian on little-endian machines and +// bigEndian on big-endian machines. +func hostByteOrder() byteOrder { + switch runtime.GOARCH { + case "386", "amd64", "amd64p32", + "alpha", + "arm", "arm64", + "loong64", + "mipsle", "mips64le", "mips64p32le", + "nios2", + "ppc64le", + "riscv", "riscv64", + "sh": + return littleEndian{} + case "armbe", "arm64be", + "m68k", + "mips", "mips64", "mips64p32", + "ppc", "ppc64", + "s390", "s390x", + "shbe", + "sparc", "sparc64": + return bigEndian{} + } + panic("unknown architecture") +} diff --git a/vendor/golang.org/x/sys/cpu/cpu.go b/vendor/golang.org/x/sys/cpu/cpu.go new file mode 100644 index 000000000..f1ce515d5 --- /dev/null +++ b/vendor/golang.org/x/sys/cpu/cpu.go @@ -0,0 +1,343 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package cpu implements processor feature detection for +// various CPU architectures. +package cpu + +import ( + "os" + "strings" +) + +// Initialized reports whether the CPU features were initialized. +// +// For some GOOS/GOARCH combinations initialization of the CPU features depends +// on reading an operating specific file, e.g. /proc/self/auxv on linux/arm +// Initialized will report false if reading the file fails. +var Initialized bool + +// CacheLinePad is used to pad structs to avoid false sharing. +type CacheLinePad struct{ _ [cacheLineSize]byte } + +// X86 contains the supported CPU features of the +// current X86/AMD64 platform. If the current platform +// is not X86/AMD64 then all feature flags are false. +// +// X86 is padded to avoid false sharing. Further the HasAVX +// and HasAVX2 are only set if the OS supports XMM and YMM +// registers in addition to the CPUID feature bit being set. +var X86 struct { + _ CacheLinePad + HasAES bool // AES hardware implementation (AES NI) + HasADX bool // Multi-precision add-carry instruction extensions + HasAVX bool // Advanced vector extension + HasAVX2 bool // Advanced vector extension 2 + HasAVX512 bool // Advanced vector extension 512 + HasAVX512F bool // Advanced vector extension 512 Foundation Instructions + HasAVX512CD bool // Advanced vector extension 512 Conflict Detection Instructions + HasAVX512ER bool // Advanced vector extension 512 Exponential and Reciprocal Instructions + HasAVX512PF bool // Advanced vector extension 512 Prefetch Instructions + HasAVX512VL bool // Advanced vector extension 512 Vector Length Extensions + HasAVX512BW bool // Advanced vector extension 512 Byte and Word Instructions + HasAVX512DQ bool // Advanced vector extension 512 Doubleword and Quadword Instructions + HasAVX512IFMA bool // Advanced vector extension 512 Integer Fused Multiply Add + HasAVX512VBMI bool // Advanced vector extension 512 Vector Byte Manipulation Instructions + HasAVX5124VNNIW bool // Advanced vector extension 512 Vector Neural Network Instructions Word variable precision + HasAVX5124FMAPS bool // Advanced vector extension 512 Fused Multiply Accumulation Packed Single precision + HasAVX512VPOPCNTDQ bool // Advanced vector extension 512 Double and quad word population count instructions + HasAVX512VPCLMULQDQ bool // Advanced vector extension 512 Vector carry-less multiply operations + HasAVX512VNNI bool // Advanced vector extension 512 Vector Neural Network Instructions + HasAVX512GFNI bool // Advanced vector extension 512 Galois field New Instructions + HasAVX512VAES bool // Advanced vector extension 512 Vector AES instructions + HasAVX512VBMI2 bool // Advanced vector extension 512 Vector Byte Manipulation Instructions 2 + HasAVX512BITALG bool // Advanced vector extension 512 Bit Algorithms + HasAVX512BF16 bool // Advanced vector extension 512 BFloat16 Instructions + HasAMXTile bool // Advanced Matrix Extension Tile instructions + HasAMXInt8 bool // Advanced Matrix Extension Int8 instructions + HasAMXBF16 bool // Advanced Matrix Extension BFloat16 instructions + HasBMI1 bool // Bit manipulation instruction set 1 + HasBMI2 bool // Bit manipulation instruction set 2 + HasCX16 bool // Compare and exchange 16 Bytes + HasERMS bool // Enhanced REP for MOVSB and STOSB + HasFMA bool // Fused-multiply-add instructions + HasOSXSAVE bool // OS supports XSAVE/XRESTOR for saving/restoring XMM registers. + HasPCLMULQDQ bool // PCLMULQDQ instruction - most often used for AES-GCM + HasPOPCNT bool // Hamming weight instruction POPCNT. + HasRDRAND bool // RDRAND instruction (on-chip random number generator) + HasRDSEED bool // RDSEED instruction (on-chip random number generator) + HasSSE2 bool // Streaming SIMD extension 2 (always available on amd64) + HasSSE3 bool // Streaming SIMD extension 3 + HasSSSE3 bool // Supplemental streaming SIMD extension 3 + HasSSE41 bool // Streaming SIMD extension 4 and 4.1 + HasSSE42 bool // Streaming SIMD extension 4 and 4.2 + HasAVXIFMA bool // Advanced vector extension Integer Fused Multiply Add + HasAVXVNNI bool // Advanced vector extension Vector Neural Network Instructions + HasAVXVNNIInt8 bool // Advanced vector extension Vector Neural Network Int8 instructions + _ CacheLinePad +} + +// ARM64 contains the supported CPU features of the +// current ARMv8(aarch64) platform. If the current platform +// is not arm64 then all feature flags are false. +var ARM64 struct { + _ CacheLinePad + HasFP bool // Floating-point instruction set (always available) + HasASIMD bool // Advanced SIMD (always available) + HasEVTSTRM bool // Event stream support + HasAES bool // AES hardware implementation + HasPMULL bool // Polynomial multiplication instruction set + HasSHA1 bool // SHA1 hardware implementation + HasSHA2 bool // SHA2 hardware implementation + HasCRC32 bool // CRC32 hardware implementation + HasATOMICS bool // Atomic memory operation instruction set + HasFPHP bool // Half precision floating-point instruction set + HasASIMDHP bool // Advanced SIMD half precision instruction set + HasCPUID bool // CPUID identification scheme registers + HasASIMDRDM bool // Rounding double multiply add/subtract instruction set + HasJSCVT bool // Javascript conversion from floating-point to integer + HasFCMA bool // Floating-point multiplication and addition of complex numbers + HasLRCPC bool // Release Consistent processor consistent support + HasDCPOP bool // Persistent memory support + HasSHA3 bool // SHA3 hardware implementation + HasSM3 bool // SM3 hardware implementation + HasSM4 bool // SM4 hardware implementation + HasASIMDDP bool // Advanced SIMD double precision instruction set + HasSHA512 bool // SHA512 hardware implementation + HasSVE bool // Scalable Vector Extensions + HasSVE2 bool // Scalable Vector Extensions 2 + HasASIMDFHM bool // Advanced SIMD multiplication FP16 to FP32 + HasDIT bool // Data Independent Timing support + HasI8MM bool // Advanced SIMD Int8 matrix multiplication instructions + _ CacheLinePad +} + +// ARM contains the supported CPU features of the current ARM (32-bit) platform. +// All feature flags are false if: +// 1. the current platform is not arm, or +// 2. the current operating system is not Linux. +var ARM struct { + _ CacheLinePad + HasSWP bool // SWP instruction support + HasHALF bool // Half-word load and store support + HasTHUMB bool // ARM Thumb instruction set + Has26BIT bool // Address space limited to 26-bits + HasFASTMUL bool // 32-bit operand, 64-bit result multiplication support + HasFPA bool // Floating point arithmetic support + HasVFP bool // Vector floating point support + HasEDSP bool // DSP Extensions support + HasJAVA bool // Java instruction set + HasIWMMXT bool // Intel Wireless MMX technology support + HasCRUNCH bool // MaverickCrunch context switching and handling + HasTHUMBEE bool // Thumb EE instruction set + HasNEON bool // NEON instruction set + HasVFPv3 bool // Vector floating point version 3 support + HasVFPv3D16 bool // Vector floating point version 3 D8-D15 + HasTLS bool // Thread local storage support + HasVFPv4 bool // Vector floating point version 4 support + HasIDIVA bool // Integer divide instruction support in ARM mode + HasIDIVT bool // Integer divide instruction support in Thumb mode + HasVFPD32 bool // Vector floating point version 3 D15-D31 + HasLPAE bool // Large Physical Address Extensions + HasEVTSTRM bool // Event stream support + HasAES bool // AES hardware implementation + HasPMULL bool // Polynomial multiplication instruction set + HasSHA1 bool // SHA1 hardware implementation + HasSHA2 bool // SHA2 hardware implementation + HasCRC32 bool // CRC32 hardware implementation + _ CacheLinePad +} + +// The booleans in Loong64 contain the correspondingly named cpu feature bit. +// The struct is padded to avoid false sharing. +var Loong64 struct { + _ CacheLinePad + HasLSX bool // support 128-bit vector extension + HasLASX bool // support 256-bit vector extension + HasCRC32 bool // support CRC instruction + HasLAMCAS bool // support AMCAS[_DB].{B/H/W/D} + HasLAM_BH bool // support AM{SWAP/ADD}[_DB].{B/H} instruction + HasLLACQ_SCREL bool // support LLACQ.{W/D}, SCREL.{W/D} instruction + HasSCQ bool // support SC.Q instruction + HasDBAR_HINTS bool // supports finer-grained DBAR hints + + _ CacheLinePad +} + +// MIPS64X contains the supported CPU features of the current mips64/mips64le +// platforms. If the current platform is not mips64/mips64le or the current +// operating system is not Linux then all feature flags are false. +var MIPS64X struct { + _ CacheLinePad + HasMSA bool // MIPS SIMD architecture + _ CacheLinePad +} + +// PPC64 contains the supported CPU features of the current ppc64/ppc64le platforms. +// If the current platform is not ppc64/ppc64le then all feature flags are false. +// +// For ppc64/ppc64le, it is safe to check only for ISA level starting on ISA v3.00, +// since there are no optional categories. There are some exceptions that also +// require kernel support to work (DARN, SCV), so there are feature bits for +// those as well. The struct is padded to avoid false sharing. +var PPC64 struct { + _ CacheLinePad + HasDARN bool // Hardware random number generator (requires kernel enablement) + HasSCV bool // Syscall vectored (requires kernel enablement) + IsPOWER8 bool // ISA v2.07 (POWER8) + IsPOWER9 bool // ISA v3.00 (POWER9), implies IsPOWER8 + _ CacheLinePad +} + +// S390X contains the supported CPU features of the current IBM Z +// (s390x) platform. If the current platform is not IBM Z then all +// feature flags are false. +// +// S390X is padded to avoid false sharing. Further HasVX is only set +// if the OS supports vector registers in addition to the STFLE +// feature bit being set. +var S390X struct { + _ CacheLinePad + HasZARCH bool // z/Architecture mode is active [mandatory] + HasSTFLE bool // store facility list extended + HasLDISP bool // long (20-bit) displacements + HasEIMM bool // 32-bit immediates + HasDFP bool // decimal floating point + HasETF3EH bool // ETF-3 enhanced + HasMSA bool // message security assist (CPACF) + HasAES bool // KM-AES{128,192,256} functions + HasAESCBC bool // KMC-AES{128,192,256} functions + HasAESCTR bool // KMCTR-AES{128,192,256} functions + HasAESGCM bool // KMA-GCM-AES{128,192,256} functions + HasGHASH bool // KIMD-GHASH function + HasSHA1 bool // K{I,L}MD-SHA-1 functions + HasSHA256 bool // K{I,L}MD-SHA-256 functions + HasSHA512 bool // K{I,L}MD-SHA-512 functions + HasSHA3 bool // K{I,L}MD-SHA3-{224,256,384,512} and K{I,L}MD-SHAKE-{128,256} functions + HasVX bool // vector facility + HasVXE bool // vector-enhancements facility 1 + _ CacheLinePad +} + +// RISCV64 contains the supported CPU features and performance characteristics for riscv64 +// platforms. The booleans in RISCV64, with the exception of HasFastMisaligned, indicate +// the presence of RISC-V extensions. +// +// It is safe to assume that all the RV64G extensions are supported and so they are omitted from +// this structure. As riscv64 Go programs require at least RV64G, the code that populates +// this structure cannot run successfully if some of the RV64G extensions are missing. +// The struct is padded to avoid false sharing. +var RISCV64 struct { + _ CacheLinePad + HasFastMisaligned bool // Fast misaligned accesses + HasC bool // Compressed instruction-set extension + HasV bool // Vector extension compatible with RVV 1.0 + HasZba bool // Address generation instructions extension + HasZbb bool // Basic bit-manipulation extension + HasZbs bool // Single-bit instructions extension + HasZbc bool // Carryless multiplication extension + HasZvbb bool // Vector Basic Bit-manipulation + HasZvbc bool // Vector Carryless Multiplication + HasZvkb bool // Vector Cryptography Bit-manipulation + HasZvkt bool // Vector Data-Independent Execution Latency + HasZvkg bool // Vector GCM/GMAC + HasZvkn bool // NIST Algorithm Suite (AES/SHA256/SHA512) + HasZvknc bool // NIST Algorithm Suite with carryless multiply + HasZvkng bool // NIST Algorithm Suite with GCM + HasZvks bool // ShangMi Algorithm Suite + HasZvksc bool // ShangMi Algorithm Suite with carryless multiplication + HasZvksg bool // ShangMi Algorithm Suite with GCM + _ CacheLinePad +} + +func init() { + archInit() + initOptions() + processOptions() +} + +// options contains the cpu debug options that can be used in GODEBUG. +// Options are arch dependent and are added by the arch specific initOptions functions. +// Features that are mandatory for the specific GOARCH should have the Required field set +// (e.g. SSE2 on amd64). +var options []option + +// Option names should be lower case. e.g. avx instead of AVX. +type option struct { + Name string + Feature *bool + Specified bool // whether feature value was specified in GODEBUG + Enable bool // whether feature should be enabled + Required bool // whether feature is mandatory and can not be disabled +} + +func processOptions() { + env := os.Getenv("GODEBUG") +field: + for env != "" { + field := "" + i := strings.IndexByte(env, ',') + if i < 0 { + field, env = env, "" + } else { + field, env = env[:i], env[i+1:] + } + if len(field) < 4 || field[:4] != "cpu." { + continue + } + i = strings.IndexByte(field, '=') + if i < 0 { + print("GODEBUG sys/cpu: no value specified for \"", field, "\"\n") + continue + } + key, value := field[4:i], field[i+1:] // e.g. "SSE2", "on" + + var enable bool + switch value { + case "on": + enable = true + case "off": + enable = false + default: + print("GODEBUG sys/cpu: value \"", value, "\" not supported for cpu option \"", key, "\"\n") + continue field + } + + if key == "all" { + for i := range options { + options[i].Specified = true + options[i].Enable = enable || options[i].Required + } + continue field + } + + for i := range options { + if options[i].Name == key { + options[i].Specified = true + options[i].Enable = enable + continue field + } + } + + print("GODEBUG sys/cpu: unknown cpu feature \"", key, "\"\n") + } + + for _, o := range options { + if !o.Specified { + continue + } + + if o.Enable && !*o.Feature { + print("GODEBUG sys/cpu: can not enable \"", o.Name, "\", missing CPU support\n") + continue + } + + if !o.Enable && o.Required { + print("GODEBUG sys/cpu: can not disable \"", o.Name, "\", required CPU feature\n") + continue + } + + *o.Feature = o.Enable + } +} diff --git a/vendor/golang.org/x/sys/cpu/cpu_aix.go b/vendor/golang.org/x/sys/cpu/cpu_aix.go new file mode 100644 index 000000000..9bf0c32eb --- /dev/null +++ b/vendor/golang.org/x/sys/cpu/cpu_aix.go @@ -0,0 +1,33 @@ +// Copyright 2019 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build aix + +package cpu + +const ( + // getsystemcfg constants + _SC_IMPL = 2 + _IMPL_POWER8 = 0x10000 + _IMPL_POWER9 = 0x20000 +) + +func archInit() { + impl := getsystemcfg(_SC_IMPL) + if impl&_IMPL_POWER8 != 0 { + PPC64.IsPOWER8 = true + } + if impl&_IMPL_POWER9 != 0 { + PPC64.IsPOWER8 = true + PPC64.IsPOWER9 = true + } + + Initialized = true +} + +func getsystemcfg(label int) (n uint64) { + r0, _ := callgetsystemcfg(label) + n = uint64(r0) + return +} diff --git a/vendor/golang.org/x/sys/cpu/cpu_arm.go b/vendor/golang.org/x/sys/cpu/cpu_arm.go new file mode 100644 index 000000000..301b752e9 --- /dev/null +++ b/vendor/golang.org/x/sys/cpu/cpu_arm.go @@ -0,0 +1,73 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package cpu + +const cacheLineSize = 32 + +// HWCAP/HWCAP2 bits. +// These are specific to Linux. +const ( + hwcap_SWP = 1 << 0 + hwcap_HALF = 1 << 1 + hwcap_THUMB = 1 << 2 + hwcap_26BIT = 1 << 3 + hwcap_FAST_MULT = 1 << 4 + hwcap_FPA = 1 << 5 + hwcap_VFP = 1 << 6 + hwcap_EDSP = 1 << 7 + hwcap_JAVA = 1 << 8 + hwcap_IWMMXT = 1 << 9 + hwcap_CRUNCH = 1 << 10 + hwcap_THUMBEE = 1 << 11 + hwcap_NEON = 1 << 12 + hwcap_VFPv3 = 1 << 13 + hwcap_VFPv3D16 = 1 << 14 + hwcap_TLS = 1 << 15 + hwcap_VFPv4 = 1 << 16 + hwcap_IDIVA = 1 << 17 + hwcap_IDIVT = 1 << 18 + hwcap_VFPD32 = 1 << 19 + hwcap_LPAE = 1 << 20 + hwcap_EVTSTRM = 1 << 21 + + hwcap2_AES = 1 << 0 + hwcap2_PMULL = 1 << 1 + hwcap2_SHA1 = 1 << 2 + hwcap2_SHA2 = 1 << 3 + hwcap2_CRC32 = 1 << 4 +) + +func initOptions() { + options = []option{ + {Name: "pmull", Feature: &ARM.HasPMULL}, + {Name: "sha1", Feature: &ARM.HasSHA1}, + {Name: "sha2", Feature: &ARM.HasSHA2}, + {Name: "swp", Feature: &ARM.HasSWP}, + {Name: "thumb", Feature: &ARM.HasTHUMB}, + {Name: "thumbee", Feature: &ARM.HasTHUMBEE}, + {Name: "tls", Feature: &ARM.HasTLS}, + {Name: "vfp", Feature: &ARM.HasVFP}, + {Name: "vfpd32", Feature: &ARM.HasVFPD32}, + {Name: "vfpv3", Feature: &ARM.HasVFPv3}, + {Name: "vfpv3d16", Feature: &ARM.HasVFPv3D16}, + {Name: "vfpv4", Feature: &ARM.HasVFPv4}, + {Name: "half", Feature: &ARM.HasHALF}, + {Name: "26bit", Feature: &ARM.Has26BIT}, + {Name: "fastmul", Feature: &ARM.HasFASTMUL}, + {Name: "fpa", Feature: &ARM.HasFPA}, + {Name: "edsp", Feature: &ARM.HasEDSP}, + {Name: "java", Feature: &ARM.HasJAVA}, + {Name: "iwmmxt", Feature: &ARM.HasIWMMXT}, + {Name: "crunch", Feature: &ARM.HasCRUNCH}, + {Name: "neon", Feature: &ARM.HasNEON}, + {Name: "idivt", Feature: &ARM.HasIDIVT}, + {Name: "idiva", Feature: &ARM.HasIDIVA}, + {Name: "lpae", Feature: &ARM.HasLPAE}, + {Name: "evtstrm", Feature: &ARM.HasEVTSTRM}, + {Name: "aes", Feature: &ARM.HasAES}, + {Name: "crc32", Feature: &ARM.HasCRC32}, + } + +} diff --git a/vendor/golang.org/x/sys/cpu/cpu_arm64.go b/vendor/golang.org/x/sys/cpu/cpu_arm64.go new file mode 100644 index 000000000..5fc09e293 --- /dev/null +++ b/vendor/golang.org/x/sys/cpu/cpu_arm64.go @@ -0,0 +1,191 @@ +// Copyright 2019 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package cpu + +import "runtime" + +// cacheLineSize is used to prevent false sharing of cache lines. +// We choose 128 because Apple Silicon, a.k.a. M1, has 128-byte cache line size. +// It doesn't cost much and is much more future-proof. +const cacheLineSize = 128 + +func initOptions() { + options = []option{ + {Name: "fp", Feature: &ARM64.HasFP}, + {Name: "asimd", Feature: &ARM64.HasASIMD}, + {Name: "evstrm", Feature: &ARM64.HasEVTSTRM}, + {Name: "aes", Feature: &ARM64.HasAES}, + {Name: "fphp", Feature: &ARM64.HasFPHP}, + {Name: "jscvt", Feature: &ARM64.HasJSCVT}, + {Name: "lrcpc", Feature: &ARM64.HasLRCPC}, + {Name: "pmull", Feature: &ARM64.HasPMULL}, + {Name: "sha1", Feature: &ARM64.HasSHA1}, + {Name: "sha2", Feature: &ARM64.HasSHA2}, + {Name: "sha3", Feature: &ARM64.HasSHA3}, + {Name: "sha512", Feature: &ARM64.HasSHA512}, + {Name: "sm3", Feature: &ARM64.HasSM3}, + {Name: "sm4", Feature: &ARM64.HasSM4}, + {Name: "sve", Feature: &ARM64.HasSVE}, + {Name: "sve2", Feature: &ARM64.HasSVE2}, + {Name: "crc32", Feature: &ARM64.HasCRC32}, + {Name: "atomics", Feature: &ARM64.HasATOMICS}, + {Name: "asimdhp", Feature: &ARM64.HasASIMDHP}, + {Name: "cpuid", Feature: &ARM64.HasCPUID}, + {Name: "asimrdm", Feature: &ARM64.HasASIMDRDM}, + {Name: "fcma", Feature: &ARM64.HasFCMA}, + {Name: "dcpop", Feature: &ARM64.HasDCPOP}, + {Name: "asimddp", Feature: &ARM64.HasASIMDDP}, + {Name: "asimdfhm", Feature: &ARM64.HasASIMDFHM}, + {Name: "dit", Feature: &ARM64.HasDIT}, + {Name: "i8mm", Feature: &ARM64.HasI8MM}, + } +} + +func archInit() { + if runtime.GOOS == "freebsd" { + readARM64Registers() + } else { + // Most platforms don't seem to allow directly reading these registers. + doinit() + } +} + +// setMinimalFeatures fakes the minimal ARM64 features expected by +// TestARM64minimalFeatures. +func setMinimalFeatures() { + ARM64.HasASIMD = true + ARM64.HasFP = true +} + +func readARM64Registers() { + Initialized = true + + parseARM64SystemRegisters(getisar0(), getisar1(), getpfr0()) +} + +func parseARM64SystemRegisters(isar0, isar1, pfr0 uint64) { + // ID_AA64ISAR0_EL1 + switch extractBits(isar0, 4, 7) { + case 1: + ARM64.HasAES = true + case 2: + ARM64.HasAES = true + ARM64.HasPMULL = true + } + + switch extractBits(isar0, 8, 11) { + case 1: + ARM64.HasSHA1 = true + } + + switch extractBits(isar0, 12, 15) { + case 1: + ARM64.HasSHA2 = true + case 2: + ARM64.HasSHA2 = true + ARM64.HasSHA512 = true + } + + switch extractBits(isar0, 16, 19) { + case 1: + ARM64.HasCRC32 = true + } + + switch extractBits(isar0, 20, 23) { + case 2: + ARM64.HasATOMICS = true + } + + switch extractBits(isar0, 28, 31) { + case 1: + ARM64.HasASIMDRDM = true + } + + switch extractBits(isar0, 32, 35) { + case 1: + ARM64.HasSHA3 = true + } + + switch extractBits(isar0, 36, 39) { + case 1: + ARM64.HasSM3 = true + } + + switch extractBits(isar0, 40, 43) { + case 1: + ARM64.HasSM4 = true + } + + switch extractBits(isar0, 44, 47) { + case 1: + ARM64.HasASIMDDP = true + } + + // ID_AA64ISAR1_EL1 + switch extractBits(isar1, 0, 3) { + case 1: + ARM64.HasDCPOP = true + } + + switch extractBits(isar1, 12, 15) { + case 1: + ARM64.HasJSCVT = true + } + + switch extractBits(isar1, 16, 19) { + case 1: + ARM64.HasFCMA = true + } + + switch extractBits(isar1, 20, 23) { + case 1: + ARM64.HasLRCPC = true + } + + switch extractBits(isar1, 52, 55) { + case 1: + ARM64.HasI8MM = true + } + + // ID_AA64PFR0_EL1 + switch extractBits(pfr0, 16, 19) { + case 0: + ARM64.HasFP = true + case 1: + ARM64.HasFP = true + ARM64.HasFPHP = true + } + + switch extractBits(pfr0, 20, 23) { + case 0: + ARM64.HasASIMD = true + case 1: + ARM64.HasASIMD = true + ARM64.HasASIMDHP = true + } + + switch extractBits(pfr0, 32, 35) { + case 1: + ARM64.HasSVE = true + + parseARM64SVERegister(getzfr0()) + } + + switch extractBits(pfr0, 48, 51) { + case 1: + ARM64.HasDIT = true + } +} + +func parseARM64SVERegister(zfr0 uint64) { + switch extractBits(zfr0, 0, 3) { + case 1: + ARM64.HasSVE2 = true + } +} + +func extractBits(data uint64, start, end uint) uint { + return (uint)(data>>start) & ((1 << (end - start + 1)) - 1) +} diff --git a/vendor/golang.org/x/sys/cpu/cpu_arm64.s b/vendor/golang.org/x/sys/cpu/cpu_arm64.s new file mode 100644 index 000000000..3b0450a06 --- /dev/null +++ b/vendor/golang.org/x/sys/cpu/cpu_arm64.s @@ -0,0 +1,35 @@ +// Copyright 2019 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build gc + +#include "textflag.h" + +// func getisar0() uint64 +TEXT ·getisar0(SB),NOSPLIT,$0-8 + // get Instruction Set Attributes 0 into x0 + MRS ID_AA64ISAR0_EL1, R0 + MOVD R0, ret+0(FP) + RET + +// func getisar1() uint64 +TEXT ·getisar1(SB),NOSPLIT,$0-8 + // get Instruction Set Attributes 1 into x0 + MRS ID_AA64ISAR1_EL1, R0 + MOVD R0, ret+0(FP) + RET + +// func getpfr0() uint64 +TEXT ·getpfr0(SB),NOSPLIT,$0-8 + // get Processor Feature Register 0 into x0 + MRS ID_AA64PFR0_EL1, R0 + MOVD R0, ret+0(FP) + RET + +// func getzfr0() uint64 +TEXT ·getzfr0(SB),NOSPLIT,$0-8 + // get SVE Feature Register 0 into x0 + MRS ID_AA64ZFR0_EL1, R0 + MOVD R0, ret+0(FP) + RET diff --git a/vendor/golang.org/x/sys/cpu/cpu_darwin_arm64.go b/vendor/golang.org/x/sys/cpu/cpu_darwin_arm64.go new file mode 100644 index 000000000..0b470744a --- /dev/null +++ b/vendor/golang.org/x/sys/cpu/cpu_darwin_arm64.go @@ -0,0 +1,67 @@ +// Copyright 2026 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build darwin && arm64 && gc + +package cpu + +func doinit() { + setMinimalFeatures() + + // The feature flags are explained in [Instruction Set Detection]. + // There are some differences between MacOS versions: + // + // MacOS 11 and 12 do not have "hw.optional" sysctl values for some of the features. + // + // MacOS 13 changed some of the naming conventions to align with ARM Architecture Reference Manual. + // For example "hw.optional.armv8_2_sha512" became "hw.optional.arm.FEAT_SHA512". + // It currently checks both to stay compatible with MacOS 11 and 12. + // The old names also work with MacOS 13, however it's not clear whether + // they will continue working with future OS releases. + // + // Once MacOS 12 is no longer supported the old names can be removed. + // + // [Instruction Set Detection]: https://developer.apple.com/documentation/kernel/1387446-sysctlbyname/determining_instruction_set_characteristics + + // Encryption, hashing and checksum capabilities + + // For the following flags there are no MacOS 11 sysctl flags. + ARM64.HasAES = true || darwinSysctlEnabled([]byte("hw.optional.arm.FEAT_AES\x00")) + ARM64.HasPMULL = true || darwinSysctlEnabled([]byte("hw.optional.arm.FEAT_PMULL\x00")) + ARM64.HasSHA1 = true || darwinSysctlEnabled([]byte("hw.optional.arm.FEAT_SHA1\x00")) + ARM64.HasSHA2 = true || darwinSysctlEnabled([]byte("hw.optional.arm.FEAT_SHA256\x00")) + + ARM64.HasSHA3 = darwinSysctlEnabled([]byte("hw.optional.armv8_2_sha3\x00")) || darwinSysctlEnabled([]byte("hw.optional.arm.FEAT_SHA3\x00")) + ARM64.HasSHA512 = darwinSysctlEnabled([]byte("hw.optional.armv8_2_sha512\x00")) || darwinSysctlEnabled([]byte("hw.optional.arm.FEAT_SHA512\x00")) + + ARM64.HasCRC32 = darwinSysctlEnabled([]byte("hw.optional.armv8_crc32\x00")) + + // Atomic and memory ordering + ARM64.HasATOMICS = darwinSysctlEnabled([]byte("hw.optional.armv8_1_atomics\x00")) || darwinSysctlEnabled([]byte("hw.optional.arm.FEAT_LSE\x00")) + ARM64.HasLRCPC = darwinSysctlEnabled([]byte("hw.optional.arm.FEAT_LRCPC\x00")) + + // SIMD and floating point capabilities + ARM64.HasFPHP = darwinSysctlEnabled([]byte("hw.optional.neon_fp16\x00")) || darwinSysctlEnabled([]byte("hw.optional.arm.FEAT_FP16\x00")) + ARM64.HasASIMDHP = darwinSysctlEnabled([]byte("hw.optional.neon_hpfp\x00")) || darwinSysctlEnabled([]byte("hw.optional.AdvSIMD_HPFPCvt\x00")) + ARM64.HasASIMDRDM = darwinSysctlEnabled([]byte("hw.optional.arm.FEAT_RDM\x00")) + ARM64.HasASIMDDP = darwinSysctlEnabled([]byte("hw.optional.arm.FEAT_DotProd\x00")) + ARM64.HasASIMDFHM = darwinSysctlEnabled([]byte("hw.optional.armv8_2_fhm\x00")) || darwinSysctlEnabled([]byte("hw.optional.arm.FEAT_FHM\x00")) + ARM64.HasI8MM = darwinSysctlEnabled([]byte("hw.optional.arm.FEAT_I8MM\x00")) + + ARM64.HasJSCVT = darwinSysctlEnabled([]byte("hw.optional.arm.FEAT_JSCVT\x00")) + ARM64.HasFCMA = darwinSysctlEnabled([]byte("hw.optional.armv8_3_compnum\x00")) || darwinSysctlEnabled([]byte("hw.optional.arm.FEAT_FCMA\x00")) + + // Miscellaneous + ARM64.HasDCPOP = darwinSysctlEnabled([]byte("hw.optional.arm.FEAT_DPB\x00")) + ARM64.HasEVTSTRM = darwinSysctlEnabled([]byte("hw.optional.arm.FEAT_ECV\x00")) + ARM64.HasDIT = darwinSysctlEnabled([]byte("hw.optional.arm.FEAT_DIT\x00")) + + // Not supported, but added for completeness + ARM64.HasCPUID = false + + ARM64.HasSM3 = false // darwinSysctlEnabled([]byte("hw.optional.arm.FEAT_SM3\x00")) + ARM64.HasSM4 = false // darwinSysctlEnabled([]byte("hw.optional.arm.FEAT_SM4\x00")) + ARM64.HasSVE = false // darwinSysctlEnabled([]byte("hw.optional.arm.FEAT_SVE\x00")) + ARM64.HasSVE2 = false // darwinSysctlEnabled([]byte("hw.optional.arm.FEAT_SVE2\x00")) +} diff --git a/vendor/golang.org/x/sys/cpu/cpu_darwin_arm64_other.go b/vendor/golang.org/x/sys/cpu/cpu_darwin_arm64_other.go new file mode 100644 index 000000000..37ecc6644 --- /dev/null +++ b/vendor/golang.org/x/sys/cpu/cpu_darwin_arm64_other.go @@ -0,0 +1,31 @@ +// Copyright 2026 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build darwin && arm64 && !gc + +package cpu + +import "runtime" + +func doinit() { + setMinimalFeatures() + + ARM64.HasASIMD = true + ARM64.HasFP = true + + // Go already assumes these to be available because they were on the M1 + // and these are supported on all Apple arm64 chips. + ARM64.HasAES = true + ARM64.HasPMULL = true + ARM64.HasSHA1 = true + ARM64.HasSHA2 = true + + if runtime.GOOS != "ios" { + // Apple A7 processors do not support these, however + // M-series SoCs are at least armv8.4-a + ARM64.HasCRC32 = true // armv8.1 + ARM64.HasATOMICS = true // armv8.2 + ARM64.HasJSCVT = true // armv8.3, if HasFP + } +} diff --git a/vendor/golang.org/x/sys/cpu/cpu_darwin_x86.go b/vendor/golang.org/x/sys/cpu/cpu_darwin_x86.go new file mode 100644 index 000000000..b838cb9e9 --- /dev/null +++ b/vendor/golang.org/x/sys/cpu/cpu_darwin_x86.go @@ -0,0 +1,61 @@ +// Copyright 2024 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build darwin && amd64 && gc + +package cpu + +// darwinSupportsAVX512 checks Darwin kernel for AVX512 support via sysctl +// call (see issue 43089). It also restricts AVX512 support for Darwin to +// kernel version 21.3.0 (MacOS 12.2.0) or later (see issue 49233). +// +// Background: +// Darwin implements a special mechanism to economize on thread state when +// AVX512 specific registers are not in use. This scheme minimizes state when +// preempting threads that haven't yet used any AVX512 instructions, but adds +// special requirements to check for AVX512 hardware support at runtime (e.g. +// via sysctl call or commpage inspection). See issue 43089 and link below for +// full background: +// https://github.com/apple-oss-distributions/xnu/blob/xnu-11215.1.10/osfmk/i386/fpu.c#L214-L240 +// +// Additionally, all versions of the Darwin kernel from 19.6.0 through 21.2.0 +// (corresponding to MacOS 10.15.6 - 12.1) have a bug that can cause corruption +// of the AVX512 mask registers (K0-K7) upon signal return. For this reason +// AVX512 is considered unsafe to use on Darwin for kernel versions prior to +// 21.3.0, where a fix has been confirmed. See issue 49233 for full background. +func darwinSupportsAVX512() bool { + return darwinSysctlEnabled([]byte("hw.optional.avx512f\x00")) && darwinKernelVersionCheck(21, 3, 0) +} + +// Ensure Darwin kernel version is at least major.minor.patch, avoiding dependencies +func darwinKernelVersionCheck(major, minor, patch int) bool { + var release [256]byte + err := darwinOSRelease(&release) + if err != nil { + return false + } + + var mmp [3]int + c := 0 +Loop: + for _, b := range release[:] { + switch { + case b >= '0' && b <= '9': + mmp[c] = 10*mmp[c] + int(b-'0') + case b == '.': + c++ + if c > 2 { + return false + } + case b == 0: + break Loop + default: + return false + } + } + if c != 2 { + return false + } + return mmp[0] > major || mmp[0] == major && (mmp[1] > minor || mmp[1] == minor && mmp[2] >= patch) +} diff --git a/vendor/golang.org/x/sys/cpu/cpu_gc_arm64.go b/vendor/golang.org/x/sys/cpu/cpu_gc_arm64.go new file mode 100644 index 000000000..6ac6e1efb --- /dev/null +++ b/vendor/golang.org/x/sys/cpu/cpu_gc_arm64.go @@ -0,0 +1,12 @@ +// Copyright 2019 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build gc + +package cpu + +func getisar0() uint64 +func getisar1() uint64 +func getpfr0() uint64 +func getzfr0() uint64 diff --git a/vendor/golang.org/x/sys/cpu/cpu_gc_s390x.go b/vendor/golang.org/x/sys/cpu/cpu_gc_s390x.go new file mode 100644 index 000000000..c8ae6ddc1 --- /dev/null +++ b/vendor/golang.org/x/sys/cpu/cpu_gc_s390x.go @@ -0,0 +1,21 @@ +// Copyright 2019 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build gc + +package cpu + +// haveAsmFunctions reports whether the other functions in this file can +// be safely called. +func haveAsmFunctions() bool { return true } + +// The following feature detection functions are defined in cpu_s390x.s. +// They are likely to be expensive to call so the results should be cached. +func stfle() facilityList +func kmQuery() queryResult +func kmcQuery() queryResult +func kmctrQuery() queryResult +func kmaQuery() queryResult +func kimdQuery() queryResult +func klmdQuery() queryResult diff --git a/vendor/golang.org/x/sys/cpu/cpu_gc_x86.go b/vendor/golang.org/x/sys/cpu/cpu_gc_x86.go new file mode 100644 index 000000000..32a44514e --- /dev/null +++ b/vendor/golang.org/x/sys/cpu/cpu_gc_x86.go @@ -0,0 +1,15 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build (386 || amd64 || amd64p32) && gc + +package cpu + +// cpuid is implemented in cpu_gc_x86.s for gc compiler +// and in cpu_gccgo.c for gccgo. +func cpuid(eaxArg, ecxArg uint32) (eax, ebx, ecx, edx uint32) + +// xgetbv with ecx = 0 is implemented in cpu_gc_x86.s for gc compiler +// and in cpu_gccgo.c for gccgo. +func xgetbv() (eax, edx uint32) diff --git a/vendor/golang.org/x/sys/cpu/cpu_gc_x86.s b/vendor/golang.org/x/sys/cpu/cpu_gc_x86.s new file mode 100644 index 000000000..ce208ce6d --- /dev/null +++ b/vendor/golang.org/x/sys/cpu/cpu_gc_x86.s @@ -0,0 +1,26 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build (386 || amd64 || amd64p32) && gc + +#include "textflag.h" + +// func cpuid(eaxArg, ecxArg uint32) (eax, ebx, ecx, edx uint32) +TEXT ·cpuid(SB), NOSPLIT, $0-24 + MOVL eaxArg+0(FP), AX + MOVL ecxArg+4(FP), CX + CPUID + MOVL AX, eax+8(FP) + MOVL BX, ebx+12(FP) + MOVL CX, ecx+16(FP) + MOVL DX, edx+20(FP) + RET + +// func xgetbv() (eax, edx uint32) +TEXT ·xgetbv(SB), NOSPLIT, $0-8 + MOVL $0, CX + XGETBV + MOVL AX, eax+0(FP) + MOVL DX, edx+4(FP) + RET diff --git a/vendor/golang.org/x/sys/cpu/cpu_gccgo_arm64.go b/vendor/golang.org/x/sys/cpu/cpu_gccgo_arm64.go new file mode 100644 index 000000000..05913081e --- /dev/null +++ b/vendor/golang.org/x/sys/cpu/cpu_gccgo_arm64.go @@ -0,0 +1,12 @@ +// Copyright 2019 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build gccgo + +package cpu + +func getisar0() uint64 { return 0 } +func getisar1() uint64 { return 0 } +func getpfr0() uint64 { return 0 } +func getzfr0() uint64 { return 0 } diff --git a/vendor/golang.org/x/sys/cpu/cpu_gccgo_s390x.go b/vendor/golang.org/x/sys/cpu/cpu_gccgo_s390x.go new file mode 100644 index 000000000..9526d2ce3 --- /dev/null +++ b/vendor/golang.org/x/sys/cpu/cpu_gccgo_s390x.go @@ -0,0 +1,22 @@ +// Copyright 2019 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build gccgo + +package cpu + +// haveAsmFunctions reports whether the other functions in this file can +// be safely called. +func haveAsmFunctions() bool { return false } + +// TODO(mundaym): the following feature detection functions are currently +// stubs. See https://golang.org/cl/162887 for how to fix this. +// They are likely to be expensive to call so the results should be cached. +func stfle() facilityList { panic("not implemented for gccgo") } +func kmQuery() queryResult { panic("not implemented for gccgo") } +func kmcQuery() queryResult { panic("not implemented for gccgo") } +func kmctrQuery() queryResult { panic("not implemented for gccgo") } +func kmaQuery() queryResult { panic("not implemented for gccgo") } +func kimdQuery() queryResult { panic("not implemented for gccgo") } +func klmdQuery() queryResult { panic("not implemented for gccgo") } diff --git a/vendor/golang.org/x/sys/cpu/cpu_gccgo_x86.c b/vendor/golang.org/x/sys/cpu/cpu_gccgo_x86.c new file mode 100644 index 000000000..3f73a05dc --- /dev/null +++ b/vendor/golang.org/x/sys/cpu/cpu_gccgo_x86.c @@ -0,0 +1,37 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build (386 || amd64 || amd64p32) && gccgo + +#include +#include +#include + +// Need to wrap __get_cpuid_count because it's declared as static. +int +gccgoGetCpuidCount(uint32_t leaf, uint32_t subleaf, + uint32_t *eax, uint32_t *ebx, + uint32_t *ecx, uint32_t *edx) +{ + return __get_cpuid_count(leaf, subleaf, eax, ebx, ecx, edx); +} + +#pragma GCC diagnostic ignored "-Wunknown-pragmas" +#pragma GCC push_options +#pragma GCC target("xsave") +#pragma clang attribute push (__attribute__((target("xsave"))), apply_to=function) + +// xgetbv reads the contents of an XCR (Extended Control Register) +// specified in the ECX register into registers EDX:EAX. +// Currently, the only supported value for XCR is 0. +void +gccgoXgetbv(uint32_t *eax, uint32_t *edx) +{ + uint64_t v = _xgetbv(0); + *eax = v & 0xffffffff; + *edx = v >> 32; +} + +#pragma clang attribute pop +#pragma GCC pop_options diff --git a/vendor/golang.org/x/sys/cpu/cpu_gccgo_x86.go b/vendor/golang.org/x/sys/cpu/cpu_gccgo_x86.go new file mode 100644 index 000000000..170d21ddf --- /dev/null +++ b/vendor/golang.org/x/sys/cpu/cpu_gccgo_x86.go @@ -0,0 +1,25 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build (386 || amd64 || amd64p32) && gccgo + +package cpu + +//extern gccgoGetCpuidCount +func gccgoGetCpuidCount(eaxArg, ecxArg uint32, eax, ebx, ecx, edx *uint32) + +func cpuid(eaxArg, ecxArg uint32) (eax, ebx, ecx, edx uint32) { + var a, b, c, d uint32 + gccgoGetCpuidCount(eaxArg, ecxArg, &a, &b, &c, &d) + return a, b, c, d +} + +//extern gccgoXgetbv +func gccgoXgetbv(eax, edx *uint32) + +func xgetbv() (eax, edx uint32) { + var a, d uint32 + gccgoXgetbv(&a, &d) + return a, d +} diff --git a/vendor/golang.org/x/sys/cpu/cpu_linux.go b/vendor/golang.org/x/sys/cpu/cpu_linux.go new file mode 100644 index 000000000..743eb5435 --- /dev/null +++ b/vendor/golang.org/x/sys/cpu/cpu_linux.go @@ -0,0 +1,15 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build !386 && !amd64 && !amd64p32 && !arm64 + +package cpu + +func archInit() { + if err := readHWCAP(); err != nil { + return + } + doinit() + Initialized = true +} diff --git a/vendor/golang.org/x/sys/cpu/cpu_linux_arm.go b/vendor/golang.org/x/sys/cpu/cpu_linux_arm.go new file mode 100644 index 000000000..2057006dc --- /dev/null +++ b/vendor/golang.org/x/sys/cpu/cpu_linux_arm.go @@ -0,0 +1,39 @@ +// Copyright 2019 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package cpu + +func doinit() { + ARM.HasSWP = isSet(hwCap, hwcap_SWP) + ARM.HasHALF = isSet(hwCap, hwcap_HALF) + ARM.HasTHUMB = isSet(hwCap, hwcap_THUMB) + ARM.Has26BIT = isSet(hwCap, hwcap_26BIT) + ARM.HasFASTMUL = isSet(hwCap, hwcap_FAST_MULT) + ARM.HasFPA = isSet(hwCap, hwcap_FPA) + ARM.HasVFP = isSet(hwCap, hwcap_VFP) + ARM.HasEDSP = isSet(hwCap, hwcap_EDSP) + ARM.HasJAVA = isSet(hwCap, hwcap_JAVA) + ARM.HasIWMMXT = isSet(hwCap, hwcap_IWMMXT) + ARM.HasCRUNCH = isSet(hwCap, hwcap_CRUNCH) + ARM.HasTHUMBEE = isSet(hwCap, hwcap_THUMBEE) + ARM.HasNEON = isSet(hwCap, hwcap_NEON) + ARM.HasVFPv3 = isSet(hwCap, hwcap_VFPv3) + ARM.HasVFPv3D16 = isSet(hwCap, hwcap_VFPv3D16) + ARM.HasTLS = isSet(hwCap, hwcap_TLS) + ARM.HasVFPv4 = isSet(hwCap, hwcap_VFPv4) + ARM.HasIDIVA = isSet(hwCap, hwcap_IDIVA) + ARM.HasIDIVT = isSet(hwCap, hwcap_IDIVT) + ARM.HasVFPD32 = isSet(hwCap, hwcap_VFPD32) + ARM.HasLPAE = isSet(hwCap, hwcap_LPAE) + ARM.HasEVTSTRM = isSet(hwCap, hwcap_EVTSTRM) + ARM.HasAES = isSet(hwCap2, hwcap2_AES) + ARM.HasPMULL = isSet(hwCap2, hwcap2_PMULL) + ARM.HasSHA1 = isSet(hwCap2, hwcap2_SHA1) + ARM.HasSHA2 = isSet(hwCap2, hwcap2_SHA2) + ARM.HasCRC32 = isSet(hwCap2, hwcap2_CRC32) +} + +func isSet(hwc uint, value uint) bool { + return hwc&value != 0 +} diff --git a/vendor/golang.org/x/sys/cpu/cpu_linux_arm64.go b/vendor/golang.org/x/sys/cpu/cpu_linux_arm64.go new file mode 100644 index 000000000..f1caf0f78 --- /dev/null +++ b/vendor/golang.org/x/sys/cpu/cpu_linux_arm64.go @@ -0,0 +1,120 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package cpu + +import ( + "strings" + "syscall" +) + +// HWCAP/HWCAP2 bits. These are exposed by Linux. +const ( + hwcap_FP = 1 << 0 + hwcap_ASIMD = 1 << 1 + hwcap_EVTSTRM = 1 << 2 + hwcap_AES = 1 << 3 + hwcap_PMULL = 1 << 4 + hwcap_SHA1 = 1 << 5 + hwcap_SHA2 = 1 << 6 + hwcap_CRC32 = 1 << 7 + hwcap_ATOMICS = 1 << 8 + hwcap_FPHP = 1 << 9 + hwcap_ASIMDHP = 1 << 10 + hwcap_CPUID = 1 << 11 + hwcap_ASIMDRDM = 1 << 12 + hwcap_JSCVT = 1 << 13 + hwcap_FCMA = 1 << 14 + hwcap_LRCPC = 1 << 15 + hwcap_DCPOP = 1 << 16 + hwcap_SHA3 = 1 << 17 + hwcap_SM3 = 1 << 18 + hwcap_SM4 = 1 << 19 + hwcap_ASIMDDP = 1 << 20 + hwcap_SHA512 = 1 << 21 + hwcap_SVE = 1 << 22 + hwcap_ASIMDFHM = 1 << 23 + hwcap_DIT = 1 << 24 + + hwcap2_SVE2 = 1 << 1 + hwcap2_I8MM = 1 << 13 +) + +// linuxKernelCanEmulateCPUID reports whether we're running +// on Linux 4.11+. Ideally we'd like to ask the question about +// whether the current kernel contains +// https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=77c97b4ee21290f5f083173d957843b615abbff2 +// but the version number will have to do. +func linuxKernelCanEmulateCPUID() bool { + var un syscall.Utsname + syscall.Uname(&un) + var sb strings.Builder + for _, b := range un.Release[:] { + if b == 0 { + break + } + sb.WriteByte(byte(b)) + } + major, minor, _, ok := parseRelease(sb.String()) + return ok && (major > 4 || major == 4 && minor >= 11) +} + +func doinit() { + if err := readHWCAP(); err != nil { + // We failed to read /proc/self/auxv. This can happen if the binary has + // been given extra capabilities(7) with /bin/setcap. + // + // When this happens, we have two options. If the Linux kernel is new + // enough (4.11+), we can read the arm64 registers directly which'll + // trap into the kernel and then return back to userspace. + // + // But on older kernels, such as Linux 4.4.180 as used on many Synology + // devices, calling readARM64Registers (specifically getisar0) will + // cause a SIGILL and we'll die. So for older kernels, parse /proc/cpuinfo + // instead. + // + // See golang/go#57336. + if linuxKernelCanEmulateCPUID() { + readARM64Registers() + } else { + readLinuxProcCPUInfo() + } + return + } + + // HWCAP feature bits + ARM64.HasFP = isSet(hwCap, hwcap_FP) + ARM64.HasASIMD = isSet(hwCap, hwcap_ASIMD) + ARM64.HasEVTSTRM = isSet(hwCap, hwcap_EVTSTRM) + ARM64.HasAES = isSet(hwCap, hwcap_AES) + ARM64.HasPMULL = isSet(hwCap, hwcap_PMULL) + ARM64.HasSHA1 = isSet(hwCap, hwcap_SHA1) + ARM64.HasSHA2 = isSet(hwCap, hwcap_SHA2) + ARM64.HasCRC32 = isSet(hwCap, hwcap_CRC32) + ARM64.HasATOMICS = isSet(hwCap, hwcap_ATOMICS) + ARM64.HasFPHP = isSet(hwCap, hwcap_FPHP) + ARM64.HasASIMDHP = isSet(hwCap, hwcap_ASIMDHP) + ARM64.HasCPUID = isSet(hwCap, hwcap_CPUID) + ARM64.HasASIMDRDM = isSet(hwCap, hwcap_ASIMDRDM) + ARM64.HasJSCVT = isSet(hwCap, hwcap_JSCVT) + ARM64.HasFCMA = isSet(hwCap, hwcap_FCMA) + ARM64.HasLRCPC = isSet(hwCap, hwcap_LRCPC) + ARM64.HasDCPOP = isSet(hwCap, hwcap_DCPOP) + ARM64.HasSHA3 = isSet(hwCap, hwcap_SHA3) + ARM64.HasSM3 = isSet(hwCap, hwcap_SM3) + ARM64.HasSM4 = isSet(hwCap, hwcap_SM4) + ARM64.HasASIMDDP = isSet(hwCap, hwcap_ASIMDDP) + ARM64.HasSHA512 = isSet(hwCap, hwcap_SHA512) + ARM64.HasSVE = isSet(hwCap, hwcap_SVE) + ARM64.HasASIMDFHM = isSet(hwCap, hwcap_ASIMDFHM) + ARM64.HasDIT = isSet(hwCap, hwcap_DIT) + + // HWCAP2 feature bits + ARM64.HasSVE2 = isSet(hwCap2, hwcap2_SVE2) + ARM64.HasI8MM = isSet(hwCap2, hwcap2_I8MM) +} + +func isSet(hwc uint, value uint) bool { + return hwc&value != 0 +} diff --git a/vendor/golang.org/x/sys/cpu/cpu_linux_loong64.go b/vendor/golang.org/x/sys/cpu/cpu_linux_loong64.go new file mode 100644 index 000000000..4f3411432 --- /dev/null +++ b/vendor/golang.org/x/sys/cpu/cpu_linux_loong64.go @@ -0,0 +1,22 @@ +// Copyright 2025 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package cpu + +// HWCAP bits. These are exposed by the Linux kernel. +const ( + hwcap_LOONGARCH_LSX = 1 << 4 + hwcap_LOONGARCH_LASX = 1 << 5 +) + +func doinit() { + // TODO: Features that require kernel support like LSX and LASX can + // be detected here once needed in std library or by the compiler. + Loong64.HasLSX = hwcIsSet(hwCap, hwcap_LOONGARCH_LSX) + Loong64.HasLASX = hwcIsSet(hwCap, hwcap_LOONGARCH_LASX) +} + +func hwcIsSet(hwc uint, val uint) bool { + return hwc&val != 0 +} diff --git a/vendor/golang.org/x/sys/cpu/cpu_linux_mips64x.go b/vendor/golang.org/x/sys/cpu/cpu_linux_mips64x.go new file mode 100644 index 000000000..4686c1d54 --- /dev/null +++ b/vendor/golang.org/x/sys/cpu/cpu_linux_mips64x.go @@ -0,0 +1,22 @@ +// Copyright 2020 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build linux && (mips64 || mips64le) + +package cpu + +// HWCAP bits. These are exposed by the Linux kernel 5.4. +const ( + // CPU features + hwcap_MIPS_MSA = 1 << 1 +) + +func doinit() { + // HWCAP feature bits + MIPS64X.HasMSA = isSet(hwCap, hwcap_MIPS_MSA) +} + +func isSet(hwc uint, value uint) bool { + return hwc&value != 0 +} diff --git a/vendor/golang.org/x/sys/cpu/cpu_linux_noinit.go b/vendor/golang.org/x/sys/cpu/cpu_linux_noinit.go new file mode 100644 index 000000000..a428dec9c --- /dev/null +++ b/vendor/golang.org/x/sys/cpu/cpu_linux_noinit.go @@ -0,0 +1,9 @@ +// Copyright 2019 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build linux && !arm && !arm64 && !loong64 && !mips64 && !mips64le && !ppc64 && !ppc64le && !s390x && !riscv64 + +package cpu + +func doinit() {} diff --git a/vendor/golang.org/x/sys/cpu/cpu_linux_ppc64x.go b/vendor/golang.org/x/sys/cpu/cpu_linux_ppc64x.go new file mode 100644 index 000000000..197188e67 --- /dev/null +++ b/vendor/golang.org/x/sys/cpu/cpu_linux_ppc64x.go @@ -0,0 +1,30 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build linux && (ppc64 || ppc64le) + +package cpu + +// HWCAP/HWCAP2 bits. These are exposed by the kernel. +const ( + // ISA Level + _PPC_FEATURE2_ARCH_2_07 = 0x80000000 + _PPC_FEATURE2_ARCH_3_00 = 0x00800000 + + // CPU features + _PPC_FEATURE2_DARN = 0x00200000 + _PPC_FEATURE2_SCV = 0x00100000 +) + +func doinit() { + // HWCAP2 feature bits + PPC64.IsPOWER8 = isSet(hwCap2, _PPC_FEATURE2_ARCH_2_07) + PPC64.IsPOWER9 = isSet(hwCap2, _PPC_FEATURE2_ARCH_3_00) + PPC64.HasDARN = isSet(hwCap2, _PPC_FEATURE2_DARN) + PPC64.HasSCV = isSet(hwCap2, _PPC_FEATURE2_SCV) +} + +func isSet(hwc uint, value uint) bool { + return hwc&value != 0 +} diff --git a/vendor/golang.org/x/sys/cpu/cpu_linux_riscv64.go b/vendor/golang.org/x/sys/cpu/cpu_linux_riscv64.go new file mode 100644 index 000000000..f4fb52ee9 --- /dev/null +++ b/vendor/golang.org/x/sys/cpu/cpu_linux_riscv64.go @@ -0,0 +1,162 @@ +// Copyright 2024 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package cpu + +import ( + "syscall" + "unsafe" +) + +// RISC-V extension discovery code for Linux. The approach here is to first try the riscv_hwprobe +// syscall falling back to HWCAP to check for the C extension if riscv_hwprobe is not available. +// +// A note on detection of the Vector extension using HWCAP. +// +// Support for the Vector extension version 1.0 was added to the Linux kernel in release 6.5. +// Support for the riscv_hwprobe syscall was added in 6.4. It follows that if the riscv_hwprobe +// syscall is not available then neither is the Vector extension (which needs kernel support). +// The riscv_hwprobe syscall should then be all we need to detect the Vector extension. +// However, some RISC-V board manufacturers ship boards with an older kernel on top of which +// they have back-ported various versions of the Vector extension patches but not the riscv_hwprobe +// patches. These kernels advertise support for the Vector extension using HWCAP. Falling +// back to HWCAP to detect the Vector extension, if riscv_hwprobe is not available, or simply not +// bothering with riscv_hwprobe at all and just using HWCAP may then seem like an attractive option. +// +// Unfortunately, simply checking the 'V' bit in AT_HWCAP will not work as this bit is used by +// RISC-V board and cloud instance providers to mean different things. The Lichee Pi 4A board +// and the Scaleway RV1 cloud instances use the 'V' bit to advertise their support for the unratified +// 0.7.1 version of the Vector Specification. The Banana Pi BPI-F3 and the CanMV-K230 board use +// it to advertise support for 1.0 of the Vector extension. Versions 0.7.1 and 1.0 of the Vector +// extension are binary incompatible. HWCAP can then not be used in isolation to populate the +// HasV field as this field indicates that the underlying CPU is compatible with RVV 1.0. +// +// There is a way at runtime to distinguish between versions 0.7.1 and 1.0 of the Vector +// specification by issuing a RVV 1.0 vsetvli instruction and checking the vill bit of the vtype +// register. This check would allow us to safely detect version 1.0 of the Vector extension +// with HWCAP, if riscv_hwprobe were not available. However, the check cannot +// be added until the assembler supports the Vector instructions. +// +// Note the riscv_hwprobe syscall does not suffer from these ambiguities by design as all of the +// extensions it advertises support for are explicitly versioned. It's also worth noting that +// the riscv_hwprobe syscall is the only way to detect multi-letter RISC-V extensions, e.g., Zba. +// These cannot be detected using HWCAP and so riscv_hwprobe must be used to detect the majority +// of RISC-V extensions. +// +// Please see https://docs.kernel.org/arch/riscv/hwprobe.html for more information. + +// golang.org/x/sys/cpu is not allowed to depend on golang.org/x/sys/unix so we must +// reproduce the constants, types and functions needed to make the riscv_hwprobe syscall +// here. + +const ( + // Copied from golang.org/x/sys/unix/ztypes_linux_riscv64.go. + riscv_HWPROBE_KEY_IMA_EXT_0 = 0x4 + riscv_HWPROBE_IMA_C = 0x2 + riscv_HWPROBE_IMA_V = 0x4 + riscv_HWPROBE_EXT_ZBA = 0x8 + riscv_HWPROBE_EXT_ZBB = 0x10 + riscv_HWPROBE_EXT_ZBS = 0x20 + riscv_HWPROBE_EXT_ZBC = 0x80 + riscv_HWPROBE_EXT_ZVBB = 0x20000 + riscv_HWPROBE_EXT_ZVBC = 0x40000 + riscv_HWPROBE_EXT_ZVKB = 0x80000 + riscv_HWPROBE_EXT_ZVKG = 0x100000 + riscv_HWPROBE_EXT_ZVKNED = 0x200000 + riscv_HWPROBE_EXT_ZVKNHB = 0x800000 + riscv_HWPROBE_EXT_ZVKSED = 0x1000000 + riscv_HWPROBE_EXT_ZVKSH = 0x2000000 + riscv_HWPROBE_EXT_ZVKT = 0x4000000 + riscv_HWPROBE_KEY_CPUPERF_0 = 0x5 + riscv_HWPROBE_MISALIGNED_FAST = 0x3 + riscv_HWPROBE_MISALIGNED_MASK = 0x7 +) + +const ( + // sys_RISCV_HWPROBE is copied from golang.org/x/sys/unix/zsysnum_linux_riscv64.go. + sys_RISCV_HWPROBE = 258 +) + +// riscvHWProbePairs is copied from golang.org/x/sys/unix/ztypes_linux_riscv64.go. +type riscvHWProbePairs struct { + key int64 + value uint64 +} + +const ( + // CPU features + hwcap_RISCV_ISA_C = 1 << ('C' - 'A') +) + +func doinit() { + // A slice of key/value pair structures is passed to the RISCVHWProbe syscall. The key + // field should be initialised with one of the key constants defined above, e.g., + // RISCV_HWPROBE_KEY_IMA_EXT_0. The syscall will set the value field to the appropriate value. + // If the kernel does not recognise a key it will set the key field to -1 and the value field to 0. + + pairs := []riscvHWProbePairs{ + {riscv_HWPROBE_KEY_IMA_EXT_0, 0}, + {riscv_HWPROBE_KEY_CPUPERF_0, 0}, + } + + // This call only indicates that extensions are supported if they are implemented on all cores. + if riscvHWProbe(pairs, 0) { + if pairs[0].key != -1 { + v := uint(pairs[0].value) + RISCV64.HasC = isSet(v, riscv_HWPROBE_IMA_C) + RISCV64.HasV = isSet(v, riscv_HWPROBE_IMA_V) + RISCV64.HasZba = isSet(v, riscv_HWPROBE_EXT_ZBA) + RISCV64.HasZbb = isSet(v, riscv_HWPROBE_EXT_ZBB) + RISCV64.HasZbs = isSet(v, riscv_HWPROBE_EXT_ZBS) + RISCV64.HasZbc = isSet(v, riscv_HWPROBE_EXT_ZBC) + RISCV64.HasZvbb = isSet(v, riscv_HWPROBE_EXT_ZVBB) + RISCV64.HasZvbc = isSet(v, riscv_HWPROBE_EXT_ZVBC) + RISCV64.HasZvkb = isSet(v, riscv_HWPROBE_EXT_ZVKB) + RISCV64.HasZvkg = isSet(v, riscv_HWPROBE_EXT_ZVKG) + RISCV64.HasZvkt = isSet(v, riscv_HWPROBE_EXT_ZVKT) + // Cryptography shorthand extensions + RISCV64.HasZvkn = isSet(v, riscv_HWPROBE_EXT_ZVKNED) && + isSet(v, riscv_HWPROBE_EXT_ZVKNHB) && RISCV64.HasZvkb && RISCV64.HasZvkt + RISCV64.HasZvknc = RISCV64.HasZvkn && RISCV64.HasZvbc + RISCV64.HasZvkng = RISCV64.HasZvkn && RISCV64.HasZvkg + RISCV64.HasZvks = isSet(v, riscv_HWPROBE_EXT_ZVKSED) && + isSet(v, riscv_HWPROBE_EXT_ZVKSH) && RISCV64.HasZvkb && RISCV64.HasZvkt + RISCV64.HasZvksc = RISCV64.HasZvks && RISCV64.HasZvbc + RISCV64.HasZvksg = RISCV64.HasZvks && RISCV64.HasZvkg + } + if pairs[1].key != -1 { + v := pairs[1].value & riscv_HWPROBE_MISALIGNED_MASK + RISCV64.HasFastMisaligned = v == riscv_HWPROBE_MISALIGNED_FAST + } + } + + // Let's double check with HWCAP if the C extension does not appear to be supported. + // This may happen if we're running on a kernel older than 6.4. + + if !RISCV64.HasC { + RISCV64.HasC = isSet(hwCap, hwcap_RISCV_ISA_C) + } +} + +func isSet(hwc uint, value uint) bool { + return hwc&value != 0 +} + +// riscvHWProbe is a simplified version of the generated wrapper function found in +// golang.org/x/sys/unix/zsyscall_linux_riscv64.go. We simplify it by removing the +// cpuCount and cpus parameters which we do not need. We always want to pass 0 for +// these parameters here so the kernel only reports the extensions that are present +// on all cores. +func riscvHWProbe(pairs []riscvHWProbePairs, flags uint) bool { + var _zero uintptr + var p0 unsafe.Pointer + if len(pairs) > 0 { + p0 = unsafe.Pointer(&pairs[0]) + } else { + p0 = unsafe.Pointer(&_zero) + } + + _, _, e1 := syscall.Syscall6(sys_RISCV_HWPROBE, uintptr(p0), uintptr(len(pairs)), uintptr(0), uintptr(0), uintptr(flags), 0) + return e1 == 0 +} diff --git a/vendor/golang.org/x/sys/cpu/cpu_linux_s390x.go b/vendor/golang.org/x/sys/cpu/cpu_linux_s390x.go new file mode 100644 index 000000000..1517ac61d --- /dev/null +++ b/vendor/golang.org/x/sys/cpu/cpu_linux_s390x.go @@ -0,0 +1,40 @@ +// Copyright 2019 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package cpu + +const ( + // bit mask values from /usr/include/bits/hwcap.h + hwcap_ZARCH = 2 + hwcap_STFLE = 4 + hwcap_MSA = 8 + hwcap_LDISP = 16 + hwcap_EIMM = 32 + hwcap_DFP = 64 + hwcap_ETF3EH = 256 + hwcap_VX = 2048 + hwcap_VXE = 8192 +) + +func initS390Xbase() { + // test HWCAP bit vector + has := func(featureMask uint) bool { + return hwCap&featureMask == featureMask + } + + // mandatory + S390X.HasZARCH = has(hwcap_ZARCH) + + // optional + S390X.HasSTFLE = has(hwcap_STFLE) + S390X.HasLDISP = has(hwcap_LDISP) + S390X.HasEIMM = has(hwcap_EIMM) + S390X.HasETF3EH = has(hwcap_ETF3EH) + S390X.HasDFP = has(hwcap_DFP) + S390X.HasMSA = has(hwcap_MSA) + S390X.HasVX = has(hwcap_VX) + if S390X.HasVX { + S390X.HasVXE = has(hwcap_VXE) + } +} diff --git a/vendor/golang.org/x/sys/cpu/cpu_loong64.go b/vendor/golang.org/x/sys/cpu/cpu_loong64.go new file mode 100644 index 000000000..8c234b441 --- /dev/null +++ b/vendor/golang.org/x/sys/cpu/cpu_loong64.go @@ -0,0 +1,62 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build loong64 + +package cpu + +const cacheLineSize = 64 + +// Bit fields for CPUCFG registers, Related reference documents: +// https://loongson.github.io/LoongArch-Documentation/LoongArch-Vol1-EN.html#_cpucfg +const ( + // CPUCFG1 bits + cpucfg1_CRC32 = 1 << 25 + + // CPUCFG2 bits + cpucfg2_LAM_BH = 1 << 27 + cpucfg2_LAMCAS = 1 << 28 + cpucfg2_LLACQ_SCREL = 1 << 29 + cpucfg2_SCQ = 1 << 30 + + // CPUCFG3 bits + cpucfg3_DBAR_HINTS = 1 << 17 +) + +func initOptions() { + options = []option{ + {Name: "lsx", Feature: &Loong64.HasLSX}, + {Name: "lasx", Feature: &Loong64.HasLASX}, + {Name: "crc32", Feature: &Loong64.HasCRC32}, + {Name: "lam_bh", Feature: &Loong64.HasLAM_BH}, + {Name: "lamcas", Feature: &Loong64.HasLAMCAS}, + {Name: "llacq_screl", Feature: &Loong64.HasLLACQ_SCREL}, + {Name: "scq", Feature: &Loong64.HasSCQ}, + {Name: "dbar_hints", Feature: &Loong64.HasDBAR_HINTS}, + } + + // The CPUCFG data on Loong64 only reflects the hardware capabilities, + // not the kernel support status, so features such as LSX and LASX that + // require kernel support cannot be obtained from the CPUCFG data. + // + // These features only require hardware capability support and do not + // require kernel specific support, so they can be obtained directly + // through CPUCFG + cfg1 := get_cpucfg(1) + cfg2 := get_cpucfg(2) + cfg3 := get_cpucfg(3) + + Loong64.HasCRC32 = cfgIsSet(cfg1, cpucfg1_CRC32) + Loong64.HasLAMCAS = cfgIsSet(cfg2, cpucfg2_LAMCAS) + Loong64.HasLAM_BH = cfgIsSet(cfg2, cpucfg2_LAM_BH) + Loong64.HasLLACQ_SCREL = cfgIsSet(cfg2, cpucfg2_LLACQ_SCREL) + Loong64.HasSCQ = cfgIsSet(cfg2, cpucfg2_SCQ) + Loong64.HasDBAR_HINTS = cfgIsSet(cfg3, cpucfg3_DBAR_HINTS) +} + +func get_cpucfg(reg uint32) uint32 + +func cfgIsSet(cfg uint32, val uint32) bool { + return cfg&val != 0 +} diff --git a/vendor/golang.org/x/sys/cpu/cpu_loong64.s b/vendor/golang.org/x/sys/cpu/cpu_loong64.s new file mode 100644 index 000000000..71cbaf1ce --- /dev/null +++ b/vendor/golang.org/x/sys/cpu/cpu_loong64.s @@ -0,0 +1,13 @@ +// Copyright 2025 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#include "textflag.h" + +// func get_cpucfg(reg uint32) uint32 +TEXT ·get_cpucfg(SB), NOSPLIT|NOFRAME, $0 + MOVW reg+0(FP), R5 + // CPUCFG R5, R4 = 0x00006ca4 + WORD $0x00006ca4 + MOVW R4, ret+8(FP) + RET diff --git a/vendor/golang.org/x/sys/cpu/cpu_mips64x.go b/vendor/golang.org/x/sys/cpu/cpu_mips64x.go new file mode 100644 index 000000000..fedb00cc4 --- /dev/null +++ b/vendor/golang.org/x/sys/cpu/cpu_mips64x.go @@ -0,0 +1,15 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build mips64 || mips64le + +package cpu + +const cacheLineSize = 32 + +func initOptions() { + options = []option{ + {Name: "msa", Feature: &MIPS64X.HasMSA}, + } +} diff --git a/vendor/golang.org/x/sys/cpu/cpu_mipsx.go b/vendor/golang.org/x/sys/cpu/cpu_mipsx.go new file mode 100644 index 000000000..ffb4ec7eb --- /dev/null +++ b/vendor/golang.org/x/sys/cpu/cpu_mipsx.go @@ -0,0 +1,11 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build mips || mipsle + +package cpu + +const cacheLineSize = 32 + +func initOptions() {} diff --git a/vendor/golang.org/x/sys/cpu/cpu_netbsd_arm64.go b/vendor/golang.org/x/sys/cpu/cpu_netbsd_arm64.go new file mode 100644 index 000000000..ebfb3fc8e --- /dev/null +++ b/vendor/golang.org/x/sys/cpu/cpu_netbsd_arm64.go @@ -0,0 +1,173 @@ +// Copyright 2020 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package cpu + +import ( + "syscall" + "unsafe" +) + +// Minimal copy of functionality from x/sys/unix so the cpu package can call +// sysctl without depending on x/sys/unix. + +const ( + _CTL_QUERY = -2 + + _SYSCTL_VERS_1 = 0x1000000 +) + +var _zero uintptr + +func sysctl(mib []int32, old *byte, oldlen *uintptr, new *byte, newlen uintptr) (err error) { + var _p0 unsafe.Pointer + if len(mib) > 0 { + _p0 = unsafe.Pointer(&mib[0]) + } else { + _p0 = unsafe.Pointer(&_zero) + } + _, _, errno := syscall.Syscall6( + syscall.SYS___SYSCTL, + uintptr(_p0), + uintptr(len(mib)), + uintptr(unsafe.Pointer(old)), + uintptr(unsafe.Pointer(oldlen)), + uintptr(unsafe.Pointer(new)), + uintptr(newlen)) + if errno != 0 { + return errno + } + return nil +} + +type sysctlNode struct { + Flags uint32 + Num int32 + Name [32]int8 + Ver uint32 + __rsvd uint32 + Un [16]byte + _sysctl_size [8]byte + _sysctl_func [8]byte + _sysctl_parent [8]byte + _sysctl_desc [8]byte +} + +func sysctlNodes(mib []int32) ([]sysctlNode, error) { + var olen uintptr + + // Get a list of all sysctl nodes below the given MIB by performing + // a sysctl for the given MIB with CTL_QUERY appended. + mib = append(mib, _CTL_QUERY) + qnode := sysctlNode{Flags: _SYSCTL_VERS_1} + qp := (*byte)(unsafe.Pointer(&qnode)) + sz := unsafe.Sizeof(qnode) + if err := sysctl(mib, nil, &olen, qp, sz); err != nil { + return nil, err + } + + // Now that we know the size, get the actual nodes. + nodes := make([]sysctlNode, olen/sz) + np := (*byte)(unsafe.Pointer(&nodes[0])) + if err := sysctl(mib, np, &olen, qp, sz); err != nil { + return nil, err + } + + return nodes, nil +} + +func nametomib(name string) ([]int32, error) { + // Split name into components. + var parts []string + last := 0 + for i := 0; i < len(name); i++ { + if name[i] == '.' { + parts = append(parts, name[last:i]) + last = i + 1 + } + } + parts = append(parts, name[last:]) + + mib := []int32{} + // Discover the nodes and construct the MIB OID. + for partno, part := range parts { + nodes, err := sysctlNodes(mib) + if err != nil { + return nil, err + } + for _, node := range nodes { + n := make([]byte, 0) + for i := range node.Name { + if node.Name[i] != 0 { + n = append(n, byte(node.Name[i])) + } + } + if string(n) == part { + mib = append(mib, int32(node.Num)) + break + } + } + if len(mib) != partno+1 { + return nil, err + } + } + + return mib, nil +} + +// aarch64SysctlCPUID is struct aarch64_sysctl_cpu_id from NetBSD's +type aarch64SysctlCPUID struct { + midr uint64 /* Main ID Register */ + revidr uint64 /* Revision ID Register */ + mpidr uint64 /* Multiprocessor Affinity Register */ + aa64dfr0 uint64 /* A64 Debug Feature Register 0 */ + aa64dfr1 uint64 /* A64 Debug Feature Register 1 */ + aa64isar0 uint64 /* A64 Instruction Set Attribute Register 0 */ + aa64isar1 uint64 /* A64 Instruction Set Attribute Register 1 */ + aa64mmfr0 uint64 /* A64 Memory Model Feature Register 0 */ + aa64mmfr1 uint64 /* A64 Memory Model Feature Register 1 */ + aa64mmfr2 uint64 /* A64 Memory Model Feature Register 2 */ + aa64pfr0 uint64 /* A64 Processor Feature Register 0 */ + aa64pfr1 uint64 /* A64 Processor Feature Register 1 */ + aa64zfr0 uint64 /* A64 SVE Feature ID Register 0 */ + mvfr0 uint32 /* Media and VFP Feature Register 0 */ + mvfr1 uint32 /* Media and VFP Feature Register 1 */ + mvfr2 uint32 /* Media and VFP Feature Register 2 */ + pad uint32 + clidr uint64 /* Cache Level ID Register */ + ctr uint64 /* Cache Type Register */ +} + +func sysctlCPUID(name string) (*aarch64SysctlCPUID, error) { + mib, err := nametomib(name) + if err != nil { + return nil, err + } + + out := aarch64SysctlCPUID{} + n := unsafe.Sizeof(out) + _, _, errno := syscall.Syscall6( + syscall.SYS___SYSCTL, + uintptr(unsafe.Pointer(&mib[0])), + uintptr(len(mib)), + uintptr(unsafe.Pointer(&out)), + uintptr(unsafe.Pointer(&n)), + uintptr(0), + uintptr(0)) + if errno != 0 { + return nil, errno + } + return &out, nil +} + +func doinit() { + cpuid, err := sysctlCPUID("machdep.cpu0.cpu_id") + if err != nil { + setMinimalFeatures() + return + } + parseARM64SystemRegisters(cpuid.aa64isar0, cpuid.aa64isar1, cpuid.aa64pfr0) + + Initialized = true +} diff --git a/vendor/golang.org/x/sys/cpu/cpu_openbsd_arm64.go b/vendor/golang.org/x/sys/cpu/cpu_openbsd_arm64.go new file mode 100644 index 000000000..85b64d5cc --- /dev/null +++ b/vendor/golang.org/x/sys/cpu/cpu_openbsd_arm64.go @@ -0,0 +1,65 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package cpu + +import ( + "syscall" + "unsafe" +) + +// Minimal copy of functionality from x/sys/unix so the cpu package can call +// sysctl without depending on x/sys/unix. + +const ( + // From OpenBSD's sys/sysctl.h. + _CTL_MACHDEP = 7 + + // From OpenBSD's machine/cpu.h. + _CPU_ID_AA64ISAR0 = 2 + _CPU_ID_AA64ISAR1 = 3 +) + +// Implemented in the runtime package (runtime/sys_openbsd3.go) +func syscall_syscall6(fn, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2 uintptr, err syscall.Errno) + +//go:linkname syscall_syscall6 syscall.syscall6 + +func sysctl(mib []uint32, old *byte, oldlen *uintptr, new *byte, newlen uintptr) (err error) { + _, _, errno := syscall_syscall6(libc_sysctl_trampoline_addr, uintptr(unsafe.Pointer(&mib[0])), uintptr(len(mib)), uintptr(unsafe.Pointer(old)), uintptr(unsafe.Pointer(oldlen)), uintptr(unsafe.Pointer(new)), uintptr(newlen)) + if errno != 0 { + return errno + } + return nil +} + +var libc_sysctl_trampoline_addr uintptr + +//go:cgo_import_dynamic libc_sysctl sysctl "libc.so" + +func sysctlUint64(mib []uint32) (uint64, bool) { + var out uint64 + nout := unsafe.Sizeof(out) + if err := sysctl(mib, (*byte)(unsafe.Pointer(&out)), &nout, nil, 0); err != nil { + return 0, false + } + return out, true +} + +func doinit() { + setMinimalFeatures() + + // Get ID_AA64ISAR0 and ID_AA64ISAR1 from sysctl. + isar0, ok := sysctlUint64([]uint32{_CTL_MACHDEP, _CPU_ID_AA64ISAR0}) + if !ok { + return + } + isar1, ok := sysctlUint64([]uint32{_CTL_MACHDEP, _CPU_ID_AA64ISAR1}) + if !ok { + return + } + parseARM64SystemRegisters(isar0, isar1, 0) + + Initialized = true +} diff --git a/vendor/golang.org/x/sys/cpu/cpu_openbsd_arm64.s b/vendor/golang.org/x/sys/cpu/cpu_openbsd_arm64.s new file mode 100644 index 000000000..054ba05d6 --- /dev/null +++ b/vendor/golang.org/x/sys/cpu/cpu_openbsd_arm64.s @@ -0,0 +1,11 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#include "textflag.h" + +TEXT libc_sysctl_trampoline<>(SB),NOSPLIT,$0-0 + JMP libc_sysctl(SB) + +GLOBL ·libc_sysctl_trampoline_addr(SB), RODATA, $8 +DATA ·libc_sysctl_trampoline_addr(SB)/8, $libc_sysctl_trampoline<>(SB) diff --git a/vendor/golang.org/x/sys/cpu/cpu_other_arm.go b/vendor/golang.org/x/sys/cpu/cpu_other_arm.go new file mode 100644 index 000000000..e9ecf2a45 --- /dev/null +++ b/vendor/golang.org/x/sys/cpu/cpu_other_arm.go @@ -0,0 +1,9 @@ +// Copyright 2020 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build !linux && arm + +package cpu + +func archInit() {} diff --git a/vendor/golang.org/x/sys/cpu/cpu_other_arm64.go b/vendor/golang.org/x/sys/cpu/cpu_other_arm64.go new file mode 100644 index 000000000..6c7c5bfd5 --- /dev/null +++ b/vendor/golang.org/x/sys/cpu/cpu_other_arm64.go @@ -0,0 +1,11 @@ +// Copyright 2019 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build !darwin && !linux && !netbsd && !openbsd && !windows && arm64 + +package cpu + +func doinit() { + setMinimalFeatures() +} diff --git a/vendor/golang.org/x/sys/cpu/cpu_other_mips64x.go b/vendor/golang.org/x/sys/cpu/cpu_other_mips64x.go new file mode 100644 index 000000000..5f8f2419a --- /dev/null +++ b/vendor/golang.org/x/sys/cpu/cpu_other_mips64x.go @@ -0,0 +1,11 @@ +// Copyright 2020 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build !linux && (mips64 || mips64le) + +package cpu + +func archInit() { + Initialized = true +} diff --git a/vendor/golang.org/x/sys/cpu/cpu_other_ppc64x.go b/vendor/golang.org/x/sys/cpu/cpu_other_ppc64x.go new file mode 100644 index 000000000..89608fba2 --- /dev/null +++ b/vendor/golang.org/x/sys/cpu/cpu_other_ppc64x.go @@ -0,0 +1,12 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build !aix && !linux && (ppc64 || ppc64le) + +package cpu + +func archInit() { + PPC64.IsPOWER8 = true + Initialized = true +} diff --git a/vendor/golang.org/x/sys/cpu/cpu_other_riscv64.go b/vendor/golang.org/x/sys/cpu/cpu_other_riscv64.go new file mode 100644 index 000000000..5ab87808f --- /dev/null +++ b/vendor/golang.org/x/sys/cpu/cpu_other_riscv64.go @@ -0,0 +1,11 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build !linux && riscv64 + +package cpu + +func archInit() { + Initialized = true +} diff --git a/vendor/golang.org/x/sys/cpu/cpu_other_x86.go b/vendor/golang.org/x/sys/cpu/cpu_other_x86.go new file mode 100644 index 000000000..a0fd7e2f7 --- /dev/null +++ b/vendor/golang.org/x/sys/cpu/cpu_other_x86.go @@ -0,0 +1,11 @@ +// Copyright 2024 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build 386 || amd64p32 || (amd64 && (!darwin || !gc)) + +package cpu + +func darwinSupportsAVX512() bool { + panic("only implemented for gc && amd64 && darwin") +} diff --git a/vendor/golang.org/x/sys/cpu/cpu_ppc64x.go b/vendor/golang.org/x/sys/cpu/cpu_ppc64x.go new file mode 100644 index 000000000..c14f12b14 --- /dev/null +++ b/vendor/golang.org/x/sys/cpu/cpu_ppc64x.go @@ -0,0 +1,16 @@ +// Copyright 2020 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build ppc64 || ppc64le + +package cpu + +const cacheLineSize = 128 + +func initOptions() { + options = []option{ + {Name: "darn", Feature: &PPC64.HasDARN}, + {Name: "scv", Feature: &PPC64.HasSCV}, + } +} diff --git a/vendor/golang.org/x/sys/cpu/cpu_riscv64.go b/vendor/golang.org/x/sys/cpu/cpu_riscv64.go new file mode 100644 index 000000000..d4e9885f3 --- /dev/null +++ b/vendor/golang.org/x/sys/cpu/cpu_riscv64.go @@ -0,0 +1,33 @@ +// Copyright 2019 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build riscv64 + +package cpu + +const cacheLineSize = 64 + +func initOptions() { + options = []option{ + {Name: "fastmisaligned", Feature: &RISCV64.HasFastMisaligned}, + {Name: "c", Feature: &RISCV64.HasC}, + {Name: "v", Feature: &RISCV64.HasV}, + {Name: "zba", Feature: &RISCV64.HasZba}, + {Name: "zbb", Feature: &RISCV64.HasZbb}, + {Name: "zbs", Feature: &RISCV64.HasZbs}, + {Name: "zbc", Feature: &RISCV64.HasZbc}, + // RISC-V Cryptography Extensions + {Name: "zvbb", Feature: &RISCV64.HasZvbb}, + {Name: "zvbc", Feature: &RISCV64.HasZvbc}, + {Name: "zvkb", Feature: &RISCV64.HasZvkb}, + {Name: "zvkg", Feature: &RISCV64.HasZvkg}, + {Name: "zvkt", Feature: &RISCV64.HasZvkt}, + {Name: "zvkn", Feature: &RISCV64.HasZvkn}, + {Name: "zvknc", Feature: &RISCV64.HasZvknc}, + {Name: "zvkng", Feature: &RISCV64.HasZvkng}, + {Name: "zvks", Feature: &RISCV64.HasZvks}, + {Name: "zvksc", Feature: &RISCV64.HasZvksc}, + {Name: "zvksg", Feature: &RISCV64.HasZvksg}, + } +} diff --git a/vendor/golang.org/x/sys/cpu/cpu_s390x.go b/vendor/golang.org/x/sys/cpu/cpu_s390x.go new file mode 100644 index 000000000..5881b8833 --- /dev/null +++ b/vendor/golang.org/x/sys/cpu/cpu_s390x.go @@ -0,0 +1,172 @@ +// Copyright 2020 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package cpu + +const cacheLineSize = 256 + +func initOptions() { + options = []option{ + {Name: "zarch", Feature: &S390X.HasZARCH, Required: true}, + {Name: "stfle", Feature: &S390X.HasSTFLE, Required: true}, + {Name: "ldisp", Feature: &S390X.HasLDISP, Required: true}, + {Name: "eimm", Feature: &S390X.HasEIMM, Required: true}, + {Name: "dfp", Feature: &S390X.HasDFP}, + {Name: "etf3eh", Feature: &S390X.HasETF3EH}, + {Name: "msa", Feature: &S390X.HasMSA}, + {Name: "aes", Feature: &S390X.HasAES}, + {Name: "aescbc", Feature: &S390X.HasAESCBC}, + {Name: "aesctr", Feature: &S390X.HasAESCTR}, + {Name: "aesgcm", Feature: &S390X.HasAESGCM}, + {Name: "ghash", Feature: &S390X.HasGHASH}, + {Name: "sha1", Feature: &S390X.HasSHA1}, + {Name: "sha256", Feature: &S390X.HasSHA256}, + {Name: "sha3", Feature: &S390X.HasSHA3}, + {Name: "sha512", Feature: &S390X.HasSHA512}, + {Name: "vx", Feature: &S390X.HasVX}, + {Name: "vxe", Feature: &S390X.HasVXE}, + } +} + +// bitIsSet reports whether the bit at index is set. The bit index +// is in big endian order, so bit index 0 is the leftmost bit. +func bitIsSet(bits []uint64, index uint) bool { + return bits[index/64]&((1<<63)>>(index%64)) != 0 +} + +// facility is a bit index for the named facility. +type facility uint8 + +const ( + // mandatory facilities + zarch facility = 1 // z architecture mode is active + stflef facility = 7 // store-facility-list-extended + ldisp facility = 18 // long-displacement + eimm facility = 21 // extended-immediate + + // miscellaneous facilities + dfp facility = 42 // decimal-floating-point + etf3eh facility = 30 // extended-translation 3 enhancement + + // cryptography facilities + msa facility = 17 // message-security-assist + msa3 facility = 76 // message-security-assist extension 3 + msa4 facility = 77 // message-security-assist extension 4 + msa5 facility = 57 // message-security-assist extension 5 + msa8 facility = 146 // message-security-assist extension 8 + msa9 facility = 155 // message-security-assist extension 9 + + // vector facilities + vx facility = 129 // vector facility + vxe facility = 135 // vector-enhancements 1 + vxe2 facility = 148 // vector-enhancements 2 +) + +// facilityList contains the result of an STFLE call. +// Bits are numbered in big endian order so the +// leftmost bit (the MSB) is at index 0. +type facilityList struct { + bits [4]uint64 +} + +// Has reports whether the given facilities are present. +func (s *facilityList) Has(fs ...facility) bool { + if len(fs) == 0 { + panic("no facility bits provided") + } + for _, f := range fs { + if !bitIsSet(s.bits[:], uint(f)) { + return false + } + } + return true +} + +// function is the code for the named cryptographic function. +type function uint8 + +const ( + // KM{,A,C,CTR} function codes + aes128 function = 18 // AES-128 + aes192 function = 19 // AES-192 + aes256 function = 20 // AES-256 + + // K{I,L}MD function codes + sha1 function = 1 // SHA-1 + sha256 function = 2 // SHA-256 + sha512 function = 3 // SHA-512 + sha3_224 function = 32 // SHA3-224 + sha3_256 function = 33 // SHA3-256 + sha3_384 function = 34 // SHA3-384 + sha3_512 function = 35 // SHA3-512 + shake128 function = 36 // SHAKE-128 + shake256 function = 37 // SHAKE-256 + + // KLMD function codes + ghash function = 65 // GHASH +) + +// queryResult contains the result of a Query function +// call. Bits are numbered in big endian order so the +// leftmost bit (the MSB) is at index 0. +type queryResult struct { + bits [2]uint64 +} + +// Has reports whether the given functions are present. +func (q *queryResult) Has(fns ...function) bool { + if len(fns) == 0 { + panic("no function codes provided") + } + for _, f := range fns { + if !bitIsSet(q.bits[:], uint(f)) { + return false + } + } + return true +} + +func doinit() { + initS390Xbase() + + // We need implementations of stfle, km and so on + // to detect cryptographic features. + if !haveAsmFunctions() { + return + } + + // optional cryptographic functions + if S390X.HasMSA { + aes := []function{aes128, aes192, aes256} + + // cipher message + km, kmc := kmQuery(), kmcQuery() + S390X.HasAES = km.Has(aes...) + S390X.HasAESCBC = kmc.Has(aes...) + if S390X.HasSTFLE { + facilities := stfle() + if facilities.Has(msa4) { + kmctr := kmctrQuery() + S390X.HasAESCTR = kmctr.Has(aes...) + } + if facilities.Has(msa8) { + kma := kmaQuery() + S390X.HasAESGCM = kma.Has(aes...) + } + } + + // compute message digest + kimd := kimdQuery() // intermediate (no padding) + klmd := klmdQuery() // last (padding) + S390X.HasSHA1 = kimd.Has(sha1) && klmd.Has(sha1) + S390X.HasSHA256 = kimd.Has(sha256) && klmd.Has(sha256) + S390X.HasSHA512 = kimd.Has(sha512) && klmd.Has(sha512) + S390X.HasGHASH = kimd.Has(ghash) // KLMD-GHASH does not exist + sha3 := []function{ + sha3_224, sha3_256, sha3_384, sha3_512, + shake128, shake256, + } + S390X.HasSHA3 = kimd.Has(sha3...) && klmd.Has(sha3...) + } +} diff --git a/vendor/golang.org/x/sys/cpu/cpu_s390x.s b/vendor/golang.org/x/sys/cpu/cpu_s390x.s new file mode 100644 index 000000000..1fb4b7013 --- /dev/null +++ b/vendor/golang.org/x/sys/cpu/cpu_s390x.s @@ -0,0 +1,57 @@ +// Copyright 2019 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build gc + +#include "textflag.h" + +// func stfle() facilityList +TEXT ·stfle(SB), NOSPLIT|NOFRAME, $0-32 + MOVD $ret+0(FP), R1 + MOVD $3, R0 // last doubleword index to store + XC $32, (R1), (R1) // clear 4 doublewords (32 bytes) + WORD $0xb2b01000 // store facility list extended (STFLE) + RET + +// func kmQuery() queryResult +TEXT ·kmQuery(SB), NOSPLIT|NOFRAME, $0-16 + MOVD $0, R0 // set function code to 0 (KM-Query) + MOVD $ret+0(FP), R1 // address of 16-byte return value + WORD $0xB92E0024 // cipher message (KM) + RET + +// func kmcQuery() queryResult +TEXT ·kmcQuery(SB), NOSPLIT|NOFRAME, $0-16 + MOVD $0, R0 // set function code to 0 (KMC-Query) + MOVD $ret+0(FP), R1 // address of 16-byte return value + WORD $0xB92F0024 // cipher message with chaining (KMC) + RET + +// func kmctrQuery() queryResult +TEXT ·kmctrQuery(SB), NOSPLIT|NOFRAME, $0-16 + MOVD $0, R0 // set function code to 0 (KMCTR-Query) + MOVD $ret+0(FP), R1 // address of 16-byte return value + WORD $0xB92D4024 // cipher message with counter (KMCTR) + RET + +// func kmaQuery() queryResult +TEXT ·kmaQuery(SB), NOSPLIT|NOFRAME, $0-16 + MOVD $0, R0 // set function code to 0 (KMA-Query) + MOVD $ret+0(FP), R1 // address of 16-byte return value + WORD $0xb9296024 // cipher message with authentication (KMA) + RET + +// func kimdQuery() queryResult +TEXT ·kimdQuery(SB), NOSPLIT|NOFRAME, $0-16 + MOVD $0, R0 // set function code to 0 (KIMD-Query) + MOVD $ret+0(FP), R1 // address of 16-byte return value + WORD $0xB93E0024 // compute intermediate message digest (KIMD) + RET + +// func klmdQuery() queryResult +TEXT ·klmdQuery(SB), NOSPLIT|NOFRAME, $0-16 + MOVD $0, R0 // set function code to 0 (KLMD-Query) + MOVD $ret+0(FP), R1 // address of 16-byte return value + WORD $0xB93F0024 // compute last message digest (KLMD) + RET diff --git a/vendor/golang.org/x/sys/cpu/cpu_wasm.go b/vendor/golang.org/x/sys/cpu/cpu_wasm.go new file mode 100644 index 000000000..384787ea3 --- /dev/null +++ b/vendor/golang.org/x/sys/cpu/cpu_wasm.go @@ -0,0 +1,17 @@ +// Copyright 2019 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build wasm + +package cpu + +// We're compiling the cpu package for an unknown (software-abstracted) CPU. +// Make CacheLinePad an empty struct and hope that the usual struct alignment +// rules are good enough. + +const cacheLineSize = 0 + +func initOptions() {} + +func archInit() {} diff --git a/vendor/golang.org/x/sys/cpu/cpu_windows.go b/vendor/golang.org/x/sys/cpu/cpu_windows.go new file mode 100644 index 000000000..99ec8fdfc --- /dev/null +++ b/vendor/golang.org/x/sys/cpu/cpu_windows.go @@ -0,0 +1,26 @@ +// Copyright 2026 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package cpu + +//go:generate go run golang.org/x/sys/windows/mkwinsyscall -systemdll=false -output zcpu_windows.go cpu_windows.go + +//sys isProcessorFeaturePresent(ProcessorFeature uint32) (ret bool) = kernel32.IsProcessorFeaturePresent + +// The processor features to be tested for IsProcessorFeaturePresent, see +// https://learn.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-isprocessorfeaturepresent +const ( + _PF_ARM_V8_CRYPTO_INSTRUCTIONS_AVAILABLE = 30 + _PF_ARM_V8_CRC32_INSTRUCTIONS_AVAILABLE = 31 + _PF_ARM_V81_ATOMIC_INSTRUCTIONS_AVAILABLE = 34 + _PF_ARM_V82_DP_INSTRUCTIONS_AVAILABLE = 43 + + _PF_ARM_V83_JSCVT_INSTRUCTIONS_AVAILABLE = 44 + _PF_ARM_V83_LRCPC_INSTRUCTIONS_AVAILABLE = 45 + _PF_ARM_SVE_INSTRUCTIONS_AVAILABLE = 46 + _PF_ARM_SVE2_INSTRUCTIONS_AVAILABLE = 47 + + _PF_ARM_SHA3_INSTRUCTIONS_AVAILABLE = 64 + _PF_ARM_SHA512_INSTRUCTIONS_AVAILABLE = 65 +) diff --git a/vendor/golang.org/x/sys/cpu/cpu_windows_arm64.go b/vendor/golang.org/x/sys/cpu/cpu_windows_arm64.go new file mode 100644 index 000000000..034732e55 --- /dev/null +++ b/vendor/golang.org/x/sys/cpu/cpu_windows_arm64.go @@ -0,0 +1,38 @@ +// Copyright 2026 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package cpu + +func doinit() { + // set HasASIMD and HasFP to true as per + // https://learn.microsoft.com/en-us/cpp/build/arm64-windows-abi-conventions?view=msvc-170#base-requirements + // + // The ARM64 version of Windows always presupposes that it's running on an ARMv8 or later architecture. + // Both floating-point and NEON support are presumed to be present in hardware. + // + ARM64.HasASIMD = true + ARM64.HasFP = true + + if isProcessorFeaturePresent(_PF_ARM_V8_CRYPTO_INSTRUCTIONS_AVAILABLE) { + ARM64.HasAES = true + ARM64.HasPMULL = true + ARM64.HasSHA1 = true + ARM64.HasSHA2 = true + } + ARM64.HasSHA3 = isProcessorFeaturePresent(_PF_ARM_SHA3_INSTRUCTIONS_AVAILABLE) + ARM64.HasCRC32 = isProcessorFeaturePresent(_PF_ARM_V8_CRC32_INSTRUCTIONS_AVAILABLE) + ARM64.HasSHA512 = isProcessorFeaturePresent(_PF_ARM_SHA512_INSTRUCTIONS_AVAILABLE) + ARM64.HasATOMICS = isProcessorFeaturePresent(_PF_ARM_V81_ATOMIC_INSTRUCTIONS_AVAILABLE) + if isProcessorFeaturePresent(_PF_ARM_V82_DP_INSTRUCTIONS_AVAILABLE) { + ARM64.HasASIMDDP = true + ARM64.HasASIMDRDM = true + } + if isProcessorFeaturePresent(_PF_ARM_V83_LRCPC_INSTRUCTIONS_AVAILABLE) { + ARM64.HasLRCPC = true + ARM64.HasSM3 = true + } + ARM64.HasSVE = isProcessorFeaturePresent(_PF_ARM_SVE_INSTRUCTIONS_AVAILABLE) + ARM64.HasSVE2 = isProcessorFeaturePresent(_PF_ARM_SVE2_INSTRUCTIONS_AVAILABLE) + ARM64.HasJSCVT = isProcessorFeaturePresent(_PF_ARM_V83_JSCVT_INSTRUCTIONS_AVAILABLE) +} diff --git a/vendor/golang.org/x/sys/cpu/cpu_x86.go b/vendor/golang.org/x/sys/cpu/cpu_x86.go new file mode 100644 index 000000000..f5723d4f7 --- /dev/null +++ b/vendor/golang.org/x/sys/cpu/cpu_x86.go @@ -0,0 +1,236 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build 386 || amd64 || amd64p32 + +package cpu + +import "runtime" + +const cacheLineSize = 64 + +func initOptions() { + options = []option{ + {Name: "adx", Feature: &X86.HasADX}, + {Name: "aes", Feature: &X86.HasAES}, + {Name: "avx", Feature: &X86.HasAVX}, + {Name: "avx2", Feature: &X86.HasAVX2}, + {Name: "avx512", Feature: &X86.HasAVX512}, + {Name: "avx512f", Feature: &X86.HasAVX512F}, + {Name: "avx512cd", Feature: &X86.HasAVX512CD}, + {Name: "avx512er", Feature: &X86.HasAVX512ER}, + {Name: "avx512pf", Feature: &X86.HasAVX512PF}, + {Name: "avx512vl", Feature: &X86.HasAVX512VL}, + {Name: "avx512bw", Feature: &X86.HasAVX512BW}, + {Name: "avx512dq", Feature: &X86.HasAVX512DQ}, + {Name: "avx512ifma", Feature: &X86.HasAVX512IFMA}, + {Name: "avx512vbmi", Feature: &X86.HasAVX512VBMI}, + {Name: "avx512vnniw", Feature: &X86.HasAVX5124VNNIW}, + {Name: "avx5124fmaps", Feature: &X86.HasAVX5124FMAPS}, + {Name: "avx512vpopcntdq", Feature: &X86.HasAVX512VPOPCNTDQ}, + {Name: "avx512vpclmulqdq", Feature: &X86.HasAVX512VPCLMULQDQ}, + {Name: "avx512vnni", Feature: &X86.HasAVX512VNNI}, + {Name: "avx512gfni", Feature: &X86.HasAVX512GFNI}, + {Name: "avx512vaes", Feature: &X86.HasAVX512VAES}, + {Name: "avx512vbmi2", Feature: &X86.HasAVX512VBMI2}, + {Name: "avx512bitalg", Feature: &X86.HasAVX512BITALG}, + {Name: "avx512bf16", Feature: &X86.HasAVX512BF16}, + {Name: "amxtile", Feature: &X86.HasAMXTile}, + {Name: "amxint8", Feature: &X86.HasAMXInt8}, + {Name: "amxbf16", Feature: &X86.HasAMXBF16}, + {Name: "bmi1", Feature: &X86.HasBMI1}, + {Name: "bmi2", Feature: &X86.HasBMI2}, + {Name: "cx16", Feature: &X86.HasCX16}, + {Name: "erms", Feature: &X86.HasERMS}, + {Name: "fma", Feature: &X86.HasFMA}, + {Name: "osxsave", Feature: &X86.HasOSXSAVE}, + {Name: "pclmulqdq", Feature: &X86.HasPCLMULQDQ}, + {Name: "popcnt", Feature: &X86.HasPOPCNT}, + {Name: "rdrand", Feature: &X86.HasRDRAND}, + {Name: "rdseed", Feature: &X86.HasRDSEED}, + {Name: "sse3", Feature: &X86.HasSSE3}, + {Name: "sse41", Feature: &X86.HasSSE41}, + {Name: "sse42", Feature: &X86.HasSSE42}, + {Name: "ssse3", Feature: &X86.HasSSSE3}, + {Name: "avxifma", Feature: &X86.HasAVXIFMA}, + {Name: "avxvnni", Feature: &X86.HasAVXVNNI}, + {Name: "avxvnniint8", Feature: &X86.HasAVXVNNIInt8}, + + // These capabilities should always be enabled on amd64: + {Name: "sse2", Feature: &X86.HasSSE2, Required: runtime.GOARCH == "amd64"}, + } +} + +func archInit() { + + // From internal/cpu + const ( + // eax bits + cpuid_AVXVNNI = 1 << 4 + + // ecx bits + cpuid_SSE3 = 1 << 0 + cpuid_PCLMULQDQ = 1 << 1 + cpuid_AVX512VBMI = 1 << 1 + cpuid_AVX512VBMI2 = 1 << 6 + cpuid_SSSE3 = 1 << 9 + cpuid_AVX512GFNI = 1 << 8 + cpuid_AVX512VAES = 1 << 9 + cpuid_AVX512VNNI = 1 << 11 + cpuid_AVX512BITALG = 1 << 12 + cpuid_FMA = 1 << 12 + cpuid_AVX512VPOPCNTDQ = 1 << 14 + cpuid_SSE41 = 1 << 19 + cpuid_SSE42 = 1 << 20 + cpuid_POPCNT = 1 << 23 + cpuid_AES = 1 << 25 + cpuid_OSXSAVE = 1 << 27 + cpuid_AVX = 1 << 28 + + // "Extended Feature Flag" bits returned in EBX for CPUID EAX=0x7 ECX=0x0 + cpuid_BMI1 = 1 << 3 + cpuid_AVX2 = 1 << 5 + cpuid_BMI2 = 1 << 8 + cpuid_ERMS = 1 << 9 + cpuid_AVX512F = 1 << 16 + cpuid_AVX512DQ = 1 << 17 + cpuid_ADX = 1 << 19 + cpuid_AVX512CD = 1 << 28 + cpuid_SHA = 1 << 29 + cpuid_AVX512BW = 1 << 30 + cpuid_AVX512VL = 1 << 31 + + // "Extended Feature Flag" bits returned in ECX for CPUID EAX=0x7 ECX=0x0 + cpuid_AVX512_VBMI = 1 << 1 + cpuid_AVX512_VBMI2 = 1 << 6 + cpuid_GFNI = 1 << 8 + cpuid_AVX512VPCLMULQDQ = 1 << 10 + cpuid_AVX512_BITALG = 1 << 12 + + // edx bits + cpuid_FSRM = 1 << 4 + // edx bits for CPUID 0x80000001 + cpuid_RDTSCP = 1 << 27 + ) + // Additional constants not in internal/cpu + const ( + // eax=1: edx + cpuid_SSE2 = 1 << 26 + // eax=1: ecx + cpuid_CX16 = 1 << 13 + cpuid_RDRAND = 1 << 30 + // eax=7,ecx=0: ebx + cpuid_RDSEED = 1 << 18 + cpuid_AVX512IFMA = 1 << 21 + cpuid_AVX512PF = 1 << 26 + cpuid_AVX512ER = 1 << 27 + // eax=7,ecx=0: edx + cpuid_AVX5124VNNIW = 1 << 2 + cpuid_AVX5124FMAPS = 1 << 3 + cpuid_AMXBF16 = 1 << 22 + cpuid_AMXTile = 1 << 24 + cpuid_AMXInt8 = 1 << 25 + // eax=7,ecx=1: eax + cpuid_AVX512BF16 = 1 << 5 + cpuid_AVXIFMA = 1 << 23 + // eax=7,ecx=1: edx + cpuid_AVXVNNIInt8 = 1 << 4 + ) + + Initialized = true + + maxID, _, _, _ := cpuid(0, 0) + + if maxID < 1 { + return + } + + _, _, ecx1, edx1 := cpuid(1, 0) + X86.HasSSE2 = isSet(edx1, cpuid_SSE2) + + X86.HasSSE3 = isSet(ecx1, cpuid_SSE3) + X86.HasPCLMULQDQ = isSet(ecx1, cpuid_PCLMULQDQ) + X86.HasSSSE3 = isSet(ecx1, cpuid_SSSE3) + X86.HasFMA = isSet(ecx1, cpuid_FMA) + X86.HasCX16 = isSet(ecx1, cpuid_CX16) + X86.HasSSE41 = isSet(ecx1, cpuid_SSE41) + X86.HasSSE42 = isSet(ecx1, cpuid_SSE42) + X86.HasPOPCNT = isSet(ecx1, cpuid_POPCNT) + X86.HasAES = isSet(ecx1, cpuid_AES) + X86.HasOSXSAVE = isSet(ecx1, cpuid_OSXSAVE) + X86.HasRDRAND = isSet(ecx1, cpuid_RDRAND) + + var osSupportsAVX, osSupportsAVX512 bool + // For XGETBV, OSXSAVE bit is required and sufficient. + if X86.HasOSXSAVE { + eax, _ := xgetbv() + // Check if XMM and YMM registers have OS support. + osSupportsAVX = isSet(eax, 1<<1) && isSet(eax, 1<<2) + + if runtime.GOOS == "darwin" { + // Darwin requires special AVX512 checks, see cpu_darwin_x86.go + osSupportsAVX512 = osSupportsAVX && darwinSupportsAVX512() + } else { + // Check if OPMASK and ZMM registers have OS support. + osSupportsAVX512 = osSupportsAVX && isSet(eax, 1<<5) && isSet(eax, 1<<6) && isSet(eax, 1<<7) + } + } + + X86.HasAVX = isSet(ecx1, cpuid_AVX) && osSupportsAVX + + if maxID < 7 { + return + } + + eax7, ebx7, ecx7, edx7 := cpuid(7, 0) + X86.HasBMI1 = isSet(ebx7, cpuid_BMI1) + X86.HasAVX2 = isSet(ebx7, cpuid_AVX2) && osSupportsAVX + X86.HasBMI2 = isSet(ebx7, cpuid_BMI2) + X86.HasERMS = isSet(ebx7, cpuid_ERMS) + X86.HasRDSEED = isSet(ebx7, cpuid_RDSEED) + X86.HasADX = isSet(ebx7, cpuid_ADX) + + X86.HasAVX512 = isSet(ebx7, cpuid_AVX512F) && osSupportsAVX512 // Because avx-512 foundation is the core required extension + if X86.HasAVX512 { + X86.HasAVX512F = true + X86.HasAVX512CD = isSet(ebx7, cpuid_AVX512CD) + X86.HasAVX512ER = isSet(ebx7, cpuid_AVX512ER) + X86.HasAVX512PF = isSet(ebx7, cpuid_AVX512PF) + X86.HasAVX512VL = isSet(ebx7, cpuid_AVX512VL) + X86.HasAVX512BW = isSet(ebx7, cpuid_AVX512BW) + X86.HasAVX512DQ = isSet(ebx7, cpuid_AVX512DQ) + X86.HasAVX512IFMA = isSet(ebx7, cpuid_AVX512IFMA) + X86.HasAVX512VBMI = isSet(ecx7, cpuid_AVX512_VBMI) + X86.HasAVX5124VNNIW = isSet(edx7, cpuid_AVX5124VNNIW) + X86.HasAVX5124FMAPS = isSet(edx7, cpuid_AVX5124FMAPS) + X86.HasAVX512VPOPCNTDQ = isSet(ecx7, cpuid_AVX512VPOPCNTDQ) + X86.HasAVX512VPCLMULQDQ = isSet(ecx7, cpuid_AVX512VPCLMULQDQ) + X86.HasAVX512VNNI = isSet(ecx7, cpuid_AVX512VNNI) + X86.HasAVX512GFNI = isSet(ecx7, cpuid_AVX512GFNI) + X86.HasAVX512VAES = isSet(ecx7, cpuid_AVX512VAES) + X86.HasAVX512VBMI2 = isSet(ecx7, cpuid_AVX512VBMI2) + X86.HasAVX512BITALG = isSet(ecx7, cpuid_AVX512BITALG) + } + + X86.HasAMXTile = isSet(edx7, cpuid_AMXTile) + X86.HasAMXInt8 = isSet(edx7, cpuid_AMXInt8) + X86.HasAMXBF16 = isSet(edx7, cpuid_AMXBF16) + + // These features depend on the second level of extended features. + if eax7 >= 1 { + eax71, _, _, edx71 := cpuid(7, 1) + if X86.HasAVX512 { + X86.HasAVX512BF16 = isSet(eax71, cpuid_AVX512BF16) + } + if X86.HasAVX { + X86.HasAVXIFMA = isSet(eax71, cpuid_AVXIFMA) + X86.HasAVXVNNI = isSet(eax71, cpuid_AVXVNNI) + X86.HasAVXVNNIInt8 = isSet(edx71, cpuid_AVXVNNIInt8) + } + } +} + +func isSet(hwc uint32, value uint32) bool { + return hwc&value != 0 +} diff --git a/vendor/golang.org/x/sys/cpu/cpu_zos.go b/vendor/golang.org/x/sys/cpu/cpu_zos.go new file mode 100644 index 000000000..5f54683a2 --- /dev/null +++ b/vendor/golang.org/x/sys/cpu/cpu_zos.go @@ -0,0 +1,10 @@ +// Copyright 2020 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package cpu + +func archInit() { + doinit() + Initialized = true +} diff --git a/vendor/golang.org/x/sys/cpu/cpu_zos_s390x.go b/vendor/golang.org/x/sys/cpu/cpu_zos_s390x.go new file mode 100644 index 000000000..ccb1b708a --- /dev/null +++ b/vendor/golang.org/x/sys/cpu/cpu_zos_s390x.go @@ -0,0 +1,25 @@ +// Copyright 2020 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package cpu + +func initS390Xbase() { + // get the facilities list + facilities := stfle() + + // mandatory + S390X.HasZARCH = facilities.Has(zarch) + S390X.HasSTFLE = facilities.Has(stflef) + S390X.HasLDISP = facilities.Has(ldisp) + S390X.HasEIMM = facilities.Has(eimm) + + // optional + S390X.HasETF3EH = facilities.Has(etf3eh) + S390X.HasDFP = facilities.Has(dfp) + S390X.HasMSA = facilities.Has(msa) + S390X.HasVX = facilities.Has(vx) + if S390X.HasVX { + S390X.HasVXE = facilities.Has(vxe) + } +} diff --git a/vendor/golang.org/x/sys/cpu/endian_big.go b/vendor/golang.org/x/sys/cpu/endian_big.go new file mode 100644 index 000000000..7fe04b0a1 --- /dev/null +++ b/vendor/golang.org/x/sys/cpu/endian_big.go @@ -0,0 +1,10 @@ +// Copyright 2023 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build armbe || arm64be || m68k || mips || mips64 || mips64p32 || ppc || ppc64 || s390 || s390x || shbe || sparc || sparc64 + +package cpu + +// IsBigEndian records whether the GOARCH's byte order is big endian. +const IsBigEndian = true diff --git a/vendor/golang.org/x/sys/cpu/endian_little.go b/vendor/golang.org/x/sys/cpu/endian_little.go new file mode 100644 index 000000000..48eccc4c7 --- /dev/null +++ b/vendor/golang.org/x/sys/cpu/endian_little.go @@ -0,0 +1,10 @@ +// Copyright 2023 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build 386 || amd64 || amd64p32 || alpha || arm || arm64 || loong64 || mipsle || mips64le || mips64p32le || nios2 || ppc64le || riscv || riscv64 || sh || wasm + +package cpu + +// IsBigEndian records whether the GOARCH's byte order is big endian. +const IsBigEndian = false diff --git a/vendor/golang.org/x/sys/cpu/hwcap_linux.go b/vendor/golang.org/x/sys/cpu/hwcap_linux.go new file mode 100644 index 000000000..34e49f955 --- /dev/null +++ b/vendor/golang.org/x/sys/cpu/hwcap_linux.go @@ -0,0 +1,71 @@ +// Copyright 2019 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package cpu + +import ( + "os" +) + +const ( + _AT_HWCAP = 16 + _AT_HWCAP2 = 26 + + procAuxv = "/proc/self/auxv" + + uintSize = int(32 << (^uint(0) >> 63)) +) + +// For those platforms don't have a 'cpuid' equivalent we use HWCAP/HWCAP2 +// These are initialized in cpu_$GOARCH.go +// and should not be changed after they are initialized. +var hwCap uint +var hwCap2 uint + +func readHWCAP() error { + // For Go 1.21+, get auxv from the Go runtime. + if a := getAuxv(); len(a) > 0 { + for len(a) >= 2 { + tag, val := a[0], uint(a[1]) + a = a[2:] + switch tag { + case _AT_HWCAP: + hwCap = val + case _AT_HWCAP2: + hwCap2 = val + } + } + return nil + } + + buf, err := os.ReadFile(procAuxv) + if err != nil { + // e.g. on android /proc/self/auxv is not accessible, so silently + // ignore the error and leave Initialized = false. On some + // architectures (e.g. arm64) doinit() implements a fallback + // readout and will set Initialized = true again. + return err + } + bo := hostByteOrder() + for len(buf) >= 2*(uintSize/8) { + var tag, val uint + switch uintSize { + case 32: + tag = uint(bo.Uint32(buf[0:])) + val = uint(bo.Uint32(buf[4:])) + buf = buf[8:] + case 64: + tag = uint(bo.Uint64(buf[0:])) + val = uint(bo.Uint64(buf[8:])) + buf = buf[16:] + } + switch tag { + case _AT_HWCAP: + hwCap = val + case _AT_HWCAP2: + hwCap2 = val + } + } + return nil +} diff --git a/vendor/golang.org/x/sys/cpu/parse.go b/vendor/golang.org/x/sys/cpu/parse.go new file mode 100644 index 000000000..56a7e1a17 --- /dev/null +++ b/vendor/golang.org/x/sys/cpu/parse.go @@ -0,0 +1,43 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package cpu + +import "strconv" + +// parseRelease parses a dot-separated version number. It follows the semver +// syntax, but allows the minor and patch versions to be elided. +// +// This is a copy of the Go runtime's parseRelease from +// https://golang.org/cl/209597. +func parseRelease(rel string) (major, minor, patch int, ok bool) { + // Strip anything after a dash or plus. + for i := range len(rel) { + if rel[i] == '-' || rel[i] == '+' { + rel = rel[:i] + break + } + } + + next := func() (int, bool) { + for i := range len(rel) { + if rel[i] == '.' { + ver, err := strconv.Atoi(rel[:i]) + rel = rel[i+1:] + return ver, err == nil + } + } + ver, err := strconv.Atoi(rel) + rel = "" + return ver, err == nil + } + if major, ok = next(); !ok || rel == "" { + return + } + if minor, ok = next(); !ok || rel == "" { + return + } + patch, ok = next() + return +} diff --git a/vendor/golang.org/x/sys/cpu/proc_cpuinfo_linux.go b/vendor/golang.org/x/sys/cpu/proc_cpuinfo_linux.go new file mode 100644 index 000000000..4cd64c704 --- /dev/null +++ b/vendor/golang.org/x/sys/cpu/proc_cpuinfo_linux.go @@ -0,0 +1,53 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build linux && arm64 + +package cpu + +import ( + "errors" + "io" + "os" + "strings" +) + +func readLinuxProcCPUInfo() error { + f, err := os.Open("/proc/cpuinfo") + if err != nil { + return err + } + defer f.Close() + + var buf [1 << 10]byte // enough for first CPU + n, err := io.ReadFull(f, buf[:]) + if err != nil && err != io.ErrUnexpectedEOF { + return err + } + in := string(buf[:n]) + const features = "\nFeatures : " + i := strings.Index(in, features) + if i == -1 { + return errors.New("no CPU features found") + } + in = in[i+len(features):] + if i := strings.Index(in, "\n"); i != -1 { + in = in[:i] + } + m := map[string]*bool{} + + initOptions() // need it early here; it's harmless to call twice + for _, o := range options { + m[o.Name] = o.Feature + } + // The EVTSTRM field has alias "evstrm" in Go, but Linux calls it "evtstrm". + m["evtstrm"] = &ARM64.HasEVTSTRM + + for _, f := range strings.Fields(in) { + if p, ok := m[f]; ok { + *p = true + } + } + return nil +} diff --git a/vendor/golang.org/x/sys/cpu/runtime_auxv.go b/vendor/golang.org/x/sys/cpu/runtime_auxv.go new file mode 100644 index 000000000..5f92ac9a2 --- /dev/null +++ b/vendor/golang.org/x/sys/cpu/runtime_auxv.go @@ -0,0 +1,16 @@ +// Copyright 2023 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package cpu + +// getAuxvFn is non-nil on Go 1.21+ (via runtime_auxv_go121.go init) +// on platforms that use auxv. +var getAuxvFn func() []uintptr + +func getAuxv() []uintptr { + if getAuxvFn == nil { + return nil + } + return getAuxvFn() +} diff --git a/vendor/golang.org/x/sys/cpu/runtime_auxv_go121.go b/vendor/golang.org/x/sys/cpu/runtime_auxv_go121.go new file mode 100644 index 000000000..4c9788ea8 --- /dev/null +++ b/vendor/golang.org/x/sys/cpu/runtime_auxv_go121.go @@ -0,0 +1,18 @@ +// Copyright 2023 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build go1.21 + +package cpu + +import ( + _ "unsafe" // for linkname +) + +//go:linkname runtime_getAuxv runtime.getAuxv +func runtime_getAuxv() []uintptr + +func init() { + getAuxvFn = runtime_getAuxv +} diff --git a/vendor/golang.org/x/sys/cpu/syscall_aix_gccgo.go b/vendor/golang.org/x/sys/cpu/syscall_aix_gccgo.go new file mode 100644 index 000000000..1b9ccb091 --- /dev/null +++ b/vendor/golang.org/x/sys/cpu/syscall_aix_gccgo.go @@ -0,0 +1,26 @@ +// Copyright 2020 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Recreate a getsystemcfg syscall handler instead of +// using the one provided by x/sys/unix to avoid having +// the dependency between them. (See golang.org/issue/32102) +// Moreover, this file will be used during the building of +// gccgo's libgo and thus must not used a CGo method. + +//go:build aix && gccgo + +package cpu + +import ( + "syscall" +) + +//extern getsystemcfg +func gccgoGetsystemcfg(label uint32) (r uint64) + +func callgetsystemcfg(label int) (r1 uintptr, e1 syscall.Errno) { + r1 = uintptr(gccgoGetsystemcfg(uint32(label))) + e1 = syscall.GetErrno() + return +} diff --git a/vendor/golang.org/x/sys/cpu/syscall_aix_ppc64_gc.go b/vendor/golang.org/x/sys/cpu/syscall_aix_ppc64_gc.go new file mode 100644 index 000000000..e8b6cdbe9 --- /dev/null +++ b/vendor/golang.org/x/sys/cpu/syscall_aix_ppc64_gc.go @@ -0,0 +1,35 @@ +// Copyright 2019 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Minimal copy of x/sys/unix so the cpu package can make a +// system call on AIX without depending on x/sys/unix. +// (See golang.org/issue/32102) + +//go:build aix && ppc64 && gc + +package cpu + +import ( + "syscall" + "unsafe" +) + +//go:cgo_import_dynamic libc_getsystemcfg getsystemcfg "libc.a/shr_64.o" + +//go:linkname libc_getsystemcfg libc_getsystemcfg + +type syscallFunc uintptr + +var libc_getsystemcfg syscallFunc + +type errno = syscall.Errno + +// Implemented in runtime/syscall_aix.go. +func rawSyscall6(trap, nargs, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2 uintptr, err errno) +func syscall6(trap, nargs, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2 uintptr, err errno) + +func callgetsystemcfg(label int) (r1 uintptr, e1 errno) { + r1, _, e1 = syscall6(uintptr(unsafe.Pointer(&libc_getsystemcfg)), 1, uintptr(label), 0, 0, 0, 0, 0) + return +} diff --git a/vendor/golang.org/x/sys/cpu/syscall_darwin_arm64_gc.go b/vendor/golang.org/x/sys/cpu/syscall_darwin_arm64_gc.go new file mode 100644 index 000000000..7b4e67ff9 --- /dev/null +++ b/vendor/golang.org/x/sys/cpu/syscall_darwin_arm64_gc.go @@ -0,0 +1,54 @@ +// Copyright 2024 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Minimal copy from internal/cpu and runtime to make sysctl calls. + +//go:build darwin && arm64 && gc + +package cpu + +import ( + "syscall" + "unsafe" +) + +type Errno = syscall.Errno + +// adapted from internal/cpu/cpu_arm64_darwin.go +func darwinSysctlEnabled(name []byte) bool { + out := int32(0) + nout := unsafe.Sizeof(out) + if ret := sysctlbyname(&name[0], (*byte)(unsafe.Pointer(&out)), &nout, nil, 0); ret != nil { + return false + } + return out > 0 +} + +//go:cgo_import_dynamic libc_sysctl sysctl "/usr/lib/libSystem.B.dylib" + +var libc_sysctlbyname_trampoline_addr uintptr + +// adapted from runtime/sys_darwin.go in the pattern of sysctl() above, as defined in x/sys/unix +func sysctlbyname(name *byte, old *byte, oldlen *uintptr, new *byte, newlen uintptr) error { + if _, _, err := syscall_syscall6( + libc_sysctlbyname_trampoline_addr, + uintptr(unsafe.Pointer(name)), + uintptr(unsafe.Pointer(old)), + uintptr(unsafe.Pointer(oldlen)), + uintptr(unsafe.Pointer(new)), + uintptr(newlen), + 0, + ); err != 0 { + return err + } + + return nil +} + +//go:cgo_import_dynamic libc_sysctlbyname sysctlbyname "/usr/lib/libSystem.B.dylib" + +// Implemented in the runtime package (runtime/sys_darwin.go) +func syscall_syscall6(fn, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2 uintptr, err Errno) + +//go:linkname syscall_syscall6 syscall.syscall6 diff --git a/vendor/golang.org/x/sys/cpu/syscall_darwin_x86_gc.go b/vendor/golang.org/x/sys/cpu/syscall_darwin_x86_gc.go new file mode 100644 index 000000000..4d0888b0c --- /dev/null +++ b/vendor/golang.org/x/sys/cpu/syscall_darwin_x86_gc.go @@ -0,0 +1,98 @@ +// Copyright 2024 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Minimal copy of x/sys/unix so the cpu package can make a +// system call on Darwin without depending on x/sys/unix. + +//go:build darwin && amd64 && gc + +package cpu + +import ( + "syscall" + "unsafe" +) + +type _C_int int32 + +// adapted from unix.Uname() at x/sys/unix/syscall_darwin.go L419 +func darwinOSRelease(release *[256]byte) error { + // from x/sys/unix/zerrors_openbsd_amd64.go + const ( + CTL_KERN = 0x1 + KERN_OSRELEASE = 0x2 + ) + + mib := []_C_int{CTL_KERN, KERN_OSRELEASE} + n := unsafe.Sizeof(*release) + + return sysctl(mib, &release[0], &n, nil, 0) +} + +type Errno = syscall.Errno + +var _zero uintptr // Single-word zero for use when we need a valid pointer to 0 bytes. + +// from x/sys/unix/zsyscall_darwin_amd64.go L791-807 +func sysctl(mib []_C_int, old *byte, oldlen *uintptr, new *byte, newlen uintptr) error { + var _p0 unsafe.Pointer + if len(mib) > 0 { + _p0 = unsafe.Pointer(&mib[0]) + } else { + _p0 = unsafe.Pointer(&_zero) + } + if _, _, err := syscall_syscall6( + libc_sysctl_trampoline_addr, + uintptr(_p0), + uintptr(len(mib)), + uintptr(unsafe.Pointer(old)), + uintptr(unsafe.Pointer(oldlen)), + uintptr(unsafe.Pointer(new)), + uintptr(newlen), + ); err != 0 { + return err + } + + return nil +} + +var libc_sysctl_trampoline_addr uintptr + +// adapted from internal/cpu/cpu_arm64_darwin.go +func darwinSysctlEnabled(name []byte) bool { + out := int32(0) + nout := unsafe.Sizeof(out) + if ret := sysctlbyname(&name[0], (*byte)(unsafe.Pointer(&out)), &nout, nil, 0); ret != nil { + return false + } + return out > 0 +} + +//go:cgo_import_dynamic libc_sysctl sysctl "/usr/lib/libSystem.B.dylib" + +var libc_sysctlbyname_trampoline_addr uintptr + +// adapted from runtime/sys_darwin.go in the pattern of sysctl() above, as defined in x/sys/unix +func sysctlbyname(name *byte, old *byte, oldlen *uintptr, new *byte, newlen uintptr) error { + if _, _, err := syscall_syscall6( + libc_sysctlbyname_trampoline_addr, + uintptr(unsafe.Pointer(name)), + uintptr(unsafe.Pointer(old)), + uintptr(unsafe.Pointer(oldlen)), + uintptr(unsafe.Pointer(new)), + uintptr(newlen), + 0, + ); err != 0 { + return err + } + + return nil +} + +//go:cgo_import_dynamic libc_sysctlbyname sysctlbyname "/usr/lib/libSystem.B.dylib" + +// Implemented in the runtime package (runtime/sys_darwin.go) +func syscall_syscall6(fn, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2 uintptr, err Errno) + +//go:linkname syscall_syscall6 syscall.syscall6 diff --git a/vendor/golang.org/x/sys/cpu/zcpu_windows.go b/vendor/golang.org/x/sys/cpu/zcpu_windows.go new file mode 100644 index 000000000..6411a7a71 --- /dev/null +++ b/vendor/golang.org/x/sys/cpu/zcpu_windows.go @@ -0,0 +1,48 @@ +// Code generated by 'go generate'; DO NOT EDIT. + +package cpu + +import ( + "syscall" + "unsafe" +) + +var _ unsafe.Pointer + +// Do the interface allocations only once for common +// Errno values. +const ( + errnoERROR_IO_PENDING = 997 +) + +var ( + errERROR_IO_PENDING error = syscall.Errno(errnoERROR_IO_PENDING) + errERROR_EINVAL error = syscall.EINVAL +) + +// errnoErr returns common boxed Errno values, to prevent +// allocations at runtime. +func errnoErr(e syscall.Errno) error { + switch e { + case 0: + return errERROR_EINVAL + case errnoERROR_IO_PENDING: + return errERROR_IO_PENDING + } + // TODO: add more here, after collecting data on the common + // error values see on Windows. (perhaps when running + // all.bat?) + return e +} + +var ( + modkernel32 = syscall.NewLazyDLL("kernel32.dll") + + procIsProcessorFeaturePresent = modkernel32.NewProc("IsProcessorFeaturePresent") +) + +func isProcessorFeaturePresent(ProcessorFeature uint32) (ret bool) { + r0, _, _ := syscall.SyscallN(procIsProcessorFeaturePresent.Addr(), uintptr(ProcessorFeature)) + ret = r0 != 0 + return +} diff --git a/vendor/google.golang.org/grpc/balancer/balancer.go b/vendor/google.golang.org/grpc/balancer/balancer.go index 326888ae3..7e3dbaad2 100644 --- a/vendor/google.golang.org/grpc/balancer/balancer.go +++ b/vendor/google.golang.org/grpc/balancer/balancer.go @@ -60,7 +60,7 @@ func Register(b Builder) { if !envconfig.CaseSensitiveBalancerRegistries { name = strings.ToLower(name) if name != b.Name() { - logger.Warningf("Balancer registered with name %q. grpc-go will be switching to case sensitive balancer registries soon. After 2 releases, we will enable the env var by default.", b.Name()) + logger.Warningf("Balancer registered with name %q. grpc-go has switched to case sensitive balancer registries. GRPC_GO_EXPERIMENTAL_CASE_SENSITIVE_BALANCER_REGISTRIES env variable will be removed in release v1.82.0", b.Name()) } } m[name] = b @@ -85,7 +85,7 @@ func Get(name string) Builder { if !envconfig.CaseSensitiveBalancerRegistries { lowerName := strings.ToLower(name) if lowerName != name { - logger.Warningf("Balancer retrieved for name %q. grpc-go will be switching to case sensitive balancer registries soon. After 2 releases, we will enable the env var by default.", name) + logger.Warningf("Balancer retrieved for name %q. grpc-go has switched to case sensitive balancer registries. GRPC_GO_EXPERIMENTAL_CASE_SENSITIVE_BALANCER_REGISTRIES env variable will be removed in release v1.82.0", name) } name = lowerName } diff --git a/vendor/google.golang.org/grpc/balancer/pickfirst/pickfirst.go b/vendor/google.golang.org/grpc/balancer/pickfirst/pickfirst.go index 518a69d57..d48bc304c 100644 --- a/vendor/google.golang.org/grpc/balancer/pickfirst/pickfirst.go +++ b/vendor/google.golang.org/grpc/balancer/pickfirst/pickfirst.go @@ -35,9 +35,9 @@ import ( "google.golang.org/grpc/balancer" "google.golang.org/grpc/balancer/pickfirst/internal" "google.golang.org/grpc/connectivity" + "google.golang.org/grpc/experimental/balancer/weight" expstats "google.golang.org/grpc/experimental/stats" "google.golang.org/grpc/grpclog" - "google.golang.org/grpc/internal/balancer/weight" "google.golang.org/grpc/internal/envconfig" internalgrpclog "google.golang.org/grpc/internal/grpclog" "google.golang.org/grpc/internal/pretty" diff --git a/vendor/google.golang.org/grpc/dialoptions.go b/vendor/google.golang.org/grpc/dialoptions.go index 4ec5f9cd0..3af08e1ab 100644 --- a/vendor/google.golang.org/grpc/dialoptions.go +++ b/vendor/google.golang.org/grpc/dialoptions.go @@ -173,10 +173,8 @@ func newJoinDialOption(opts ...DialOption) DialOption { // If this option is set to true every connection will release the buffer after // flushing the data on the wire. // -// # Experimental -// -// Notice: This API is EXPERIMENTAL and may be changed or removed in a -// later release. +// Deprecated: shared write buffer is enabled by default. WithSharedWriteBuffer +// will be removed in a future release. func WithSharedWriteBuffer(val bool) DialOption { return newFuncDialOption(func(o *dialOptions) { o.copts.SharedWriteBuffer = val @@ -229,6 +227,14 @@ func WithInitialConnWindowSize(s int32) DialOption { // WithStaticStreamWindowSize returns a DialOption which sets the initial // stream window size to the value provided and disables dynamic flow control. +// +// Note that this also disables dynamic flow control for the connection, +// falling back to a default static connection-level window of 64KB. To +// use a larger connection-level window, you must also use the +// [WithStaticConnWindowSize] DialOption. +// +// Most users should not configure static flow control windows unless +// operating in a memory-constrained environment. func WithStaticStreamWindowSize(s int32) DialOption { return newFuncDialOption(func(o *dialOptions) { o.copts.InitialWindowSize = s @@ -239,6 +245,14 @@ func WithStaticStreamWindowSize(s int32) DialOption { // WithStaticConnWindowSize returns a DialOption which sets the initial // connection window size to the value provided and disables dynamic flow // control. +// +// Note that this also disables dynamic flow control for individual streams, +// falling back to a default static connection-level window of 64KB. To +// explicitly configure the stream-level window size, you must also use the +// [WithStaticStreamWindowSize] DialOption. +// +// Most users should not configure static flow control windows unless +// operating in a memory-constrained environment. func WithStaticConnWindowSize(s int32) DialOption { return newFuncDialOption(func(o *dialOptions) { o.copts.InitialConnWindowSize = s diff --git a/vendor/google.golang.org/grpc/encoding/encoding.go b/vendor/google.golang.org/grpc/encoding/encoding.go index 296f38c3a..bfa8b268f 100644 --- a/vendor/google.golang.org/grpc/encoding/encoding.go +++ b/vendor/google.golang.org/grpc/encoding/encoding.go @@ -66,6 +66,9 @@ type Compressor interface { // Decompress reads data from r, decompresses it, and provides the // uncompressed data via the returned io.Reader. If an error occurs while // initializing the decompressor, that error is returned instead. + // + // The returned io.Reader may optionally implement io.ReadCloser, and if it + // does, gRPC will call Close() exactly once. Decompress(r io.Reader) (io.Reader, error) // Name is the name of the compression codec and is used to set the content // coding header. The result must be static; the result cannot change diff --git a/vendor/google.golang.org/grpc/internal/balancer/weight/weight.go b/vendor/google.golang.org/grpc/experimental/balancer/weight/weight.go similarity index 71% rename from vendor/google.golang.org/grpc/internal/balancer/weight/weight.go rename to vendor/google.golang.org/grpc/experimental/balancer/weight/weight.go index 11beb07d1..beab9e07c 100644 --- a/vendor/google.golang.org/grpc/internal/balancer/weight/weight.go +++ b/vendor/google.golang.org/grpc/experimental/balancer/weight/weight.go @@ -16,23 +16,23 @@ * */ -// Package weight contains utilities to manage endpoint weights. Weights are -// used by LB policies such as ringhash to distribute load across multiple -// endpoints. +// Package weight contains utilities to manage endpoint weights. +// Weights may be used by LB policies to distribute load across +// multiple endpoints. +// +// # Experimental +// +// Notice: All APIs in this package are EXPERIMENTAL and may be changed +// or removed in a later release. package weight -import ( - "fmt" - - "google.golang.org/grpc/resolver" -) +import "google.golang.org/grpc/resolver" // attributeKey is the type used as the key to store EndpointInfo in the // Attributes field of resolver.Endpoint. type attributeKey struct{} -// EndpointInfo will be stored in the Attributes field of Endpoints in order to -// use the ringhash balancer. +// EndpointInfo will be stored in the Attributes field of Endpoints. type EndpointInfo struct { Weight uint32 } @@ -43,22 +43,16 @@ func (a EndpointInfo) Equal(o any) bool { return ok && oa.Weight == a.Weight } -// Set returns a copy of endpoint in which the Attributes field is updated with -// EndpointInfo. +// Set returns a copy of endpoint in which the Attributes field is +// updated with EndpointInfo. func Set(endpoint resolver.Endpoint, epInfo EndpointInfo) resolver.Endpoint { endpoint.Attributes = endpoint.Attributes.WithValue(attributeKey{}, epInfo) return endpoint } -// String returns a human-readable representation of EndpointInfo. -// This method is intended for logging, testing, and debugging purposes only. -// Do not rely on the output format, as it is not guaranteed to remain stable. -func (a EndpointInfo) String() string { - return fmt.Sprintf("Weight: %d", a.Weight) -} - -// FromEndpoint returns the EndpointInfo stored in the Attributes field of an -// endpoint. It returns an empty EndpointInfo if attribute is not found. +// FromEndpoint returns the EndpointInfo stored in the Attributes +// field of an endpoint. It returns an empty EndpointInfo if attribute +// is not found. func FromEndpoint(endpoint resolver.Endpoint) EndpointInfo { v := endpoint.Attributes.Value(attributeKey{}) ei, _ := v.(EndpointInfo) diff --git a/vendor/google.golang.org/grpc/internal/envconfig/envconfig.go b/vendor/google.golang.org/grpc/internal/envconfig/envconfig.go index 8ca87a57a..ba05b65d5 100644 --- a/vendor/google.golang.org/grpc/internal/envconfig/envconfig.go +++ b/vendor/google.golang.org/grpc/internal/envconfig/envconfig.go @@ -59,6 +59,15 @@ var ( // unconditionally. XDSEndpointHashKeyBackwardCompat = boolFromEnv("GRPC_XDS_ENDPOINT_HASH_KEY_BACKWARD_COMPAT", false) + // LabelServerGoroutines controls setting [runtime/pprof.Labels] on the + // goroutines spawned by [grpc.Server] type. + // For now, this is limited to the goroutines spawned to handle incoming + // requests on the server. + // Set "GRPC_GO_SERVER_GOROUTINE_LABELS" to "grpc.method=true" to + // enable this grpc.method label, or "all" to enable all valid labels. + // This variable is a bit-field. + LabelServerGoroutines = goroutineLabelsFromEnv("GRPC_GO_SERVER_GOROUTINE_LABELS", 0) + // RingHashSetRequestHashKey is set if the ring hash balancer can get the // request hash header by setting the "requestHashHeader" field, according // to gRFC A76. It can be disabled by setting the environment variable @@ -78,12 +87,12 @@ var ( EnableDefaultPortForProxyTarget = boolFromEnv("GRPC_EXPERIMENTAL_ENABLE_DEFAULT_PORT_FOR_PROXY_TARGET", true) // CaseSensitiveBalancerRegistries is set if the balancer registry should be - // case-sensitive. This is disabled by default, but can be enabled by setting + // case-sensitive. This is enabled by default, but can be disabled by setting // the env variable "GRPC_GO_EXPERIMENTAL_CASE_SENSITIVE_BALANCER_REGISTRIES" - // to "true". + // to "false". // - // TODO: After 2 releases, we will enable the env var by default. - CaseSensitiveBalancerRegistries = boolFromEnv("GRPC_GO_EXPERIMENTAL_CASE_SENSITIVE_BALANCER_REGISTRIES", false) + // This env varible will be removed in release v1.82.0. + CaseSensitiveBalancerRegistries = boolFromEnv("GRPC_GO_EXPERIMENTAL_CASE_SENSITIVE_BALANCER_REGISTRIES", true) // XDSAuthorityRewrite indicates whether xDS authority rewriting is enabled. // This feature is defined in gRFC A81 and is enabled by setting the @@ -104,22 +113,6 @@ var ( // to "false". XDSRecoverPanicInResourceParsing = boolFromEnv("GRPC_GO_EXPERIMENTAL_XDS_RESOURCE_PANIC_RECOVERY", true) - // DisableStrictPathChecking indicates whether strict path checking is - // disabled. This feature can be disabled by setting the environment - // variable GRPC_GO_EXPERIMENTAL_DISABLE_STRICT_PATH_CHECKING to "true". - // - // When strict path checking is enabled, gRPC will reject requests with - // paths that do not conform to the gRPC over HTTP/2 specification found at - // https://github.com/grpc/grpc/blob/master/doc/PROTOCOL-HTTP2.md. - // - // When disabled, gRPC will allow paths that do not contain a leading slash. - // Enabling strict path checking is recommended for security reasons, as it - // prevents potential path traversal vulnerabilities. - // - // A future release will remove this environment variable, enabling strict - // path checking behavior unconditionally. - DisableStrictPathChecking = boolFromEnv("GRPC_GO_EXPERIMENTAL_DISABLE_STRICT_PATH_CHECKING", false) - // EnablePriorityLBChildPolicyCache controls whether the priority balancer // should cache child balancers that are removed from the LB policy config, // for a period of 15 minutes. This is disabled by default, but can be @@ -127,6 +120,18 @@ var ( // GRPC_EXPERIMENTAL_ENABLE_PRIORITY_LB_CHILD_POLICY_CACHE to true. EnablePriorityLBChildPolicyCache = boolFromEnv("GRPC_EXPERIMENTAL_ENABLE_PRIORITY_LB_CHILD_POLICY_CACHE", false) + // Enable8KBDefaultHeaderListSize indicates that default maximum header list + // size is restricted to 8KB. This is disabled by default, but can be enabled + // by setting the environment variable + // "GRPC_GO_EXPERIMENTAL_ENABLE_8KB_DEFAULT_HEADER_LIST_SIZE" to "true". + // When disabled, the default maximum header list size of 16MB is used. + // + // When enabled, RPCs with a total size of headers exceeding 8KB will fail + // unless explicitly configured otherwise by the user. + // + // TODO: In release v1.82.0, env var will be enabled by default. + Enable8KBDefaultHeaderListSize = boolFromEnv("GRPC_GO_EXPERIMENTAL_ENABLE_8KB_DEFAULT_HEADER_LIST_SIZE", false) + // EnableHTTPFramerReadBufferPooling enables the use of the // readyreader.Reader interface to perform non-memory-pinning reads, // provided the underlying net.Conn supports it. This reduces memory usage @@ -160,3 +165,52 @@ func uint64FromEnv(envVar string, def, min, max uint64) uint64 { } return v } + +// GoroutineLabels is a bitfield indicating which goroutine labels are enabled. +type GoroutineLabels uint16 + +func goroutineLabelsFromEnv(envVar string, def GoroutineLabels) GoroutineLabels { + val := def + v := os.Getenv(envVar) + if strings.EqualFold(v, "all") { + return AllGoroutineLabels + } else if strings.EqualFold(v, "none") { + return 0 + } + for s := range strings.SplitSeq(v, ",") { + s = strings.TrimSpace(s) + if len(s) == 0 { + continue + } + pre, post, ok := strings.Cut(s, "=") + if !ok { + // no equals sign + continue + } + post = strings.TrimSpace(post) + pre = strings.TrimSpace(pre) + bitDesignator := GoroutineLabels(0) + switch { + case strings.EqualFold(pre, "grpc.method"): + bitDesignator = GoroutineLabelServerMethod + default: + continue + } + if strings.EqualFold(post, "true") { + val |= bitDesignator + } else if strings.EqualFold(post, "false") { + val &^= bitDesignator + } + } + return val +} + +const ( + // GoroutineLabelServerMethod sets the grpc.method label on new + // server-side gRPC streams. + GoroutineLabelServerMethod GoroutineLabels = 1 << iota +) + +// AllGoroutineLabels is an or'd together bitfield of all valid GoroutineLabels +// constant values (above). +const AllGoroutineLabels = GoroutineLabelServerMethod diff --git a/vendor/google.golang.org/grpc/internal/envconfig/xds.go b/vendor/google.golang.org/grpc/internal/envconfig/xds.go index 333d8a0b0..a2312f8ea 100644 --- a/vendor/google.golang.org/grpc/internal/envconfig/xds.go +++ b/vendor/google.golang.org/grpc/internal/envconfig/xds.go @@ -89,4 +89,14 @@ var ( // filtered and prefix-propagated to the LRS server. For more details, see: // https://github.com/grpc/proposal/blob/master/A85-lrs-custom-metrics-changes.md XDSORCAToLRSPropEnabled = boolFromEnv("GRPC_EXPERIMENTAL_XDS_ORCA_LRS_PROPAGATION", false) + + // XDSClientExtProcEnabled indicates whether ExtProc filter is enabled on + // the client side. For more details, see: + // https://github.com/grpc/proposal/blob/master/A93-xds-ext-proc.md + XDSClientExtProcEnabled = boolFromEnv("GRPC_EXPERIMENTAL_XDS_EXT_PROC_ON_CLIENT", false) + + // GCPAuthenticationFilterEnabled enables the xDS GCP Authentication + // filter. For more details, see: + // https://github.com/grpc/proposal/blob/master/A83-xds-gcp-authn-filter.md + GCPAuthenticationFilterEnabled = boolFromEnv("GRPC_EXPERIMENTAL_XDS_GCP_AUTHENTICATION_FILTER", false) ) diff --git a/vendor/google.golang.org/grpc/internal/grpcutil/encode_duration.go b/vendor/google.golang.org/grpc/internal/grpcutil/encode_duration.go index b25b0baec..1cc43fc6b 100644 --- a/vendor/google.golang.org/grpc/internal/grpcutil/encode_duration.go +++ b/vendor/google.golang.org/grpc/internal/grpcutil/encode_duration.go @@ -39,7 +39,6 @@ func div(d, r time.Duration) int64 { // // https://github.com/grpc/grpc/blob/master/doc/PROTOCOL-HTTP2.md#requests func EncodeDuration(t time.Duration) string { - // TODO: This is simplistic and not bandwidth efficient. Improve it. if t <= 0 { return "0n" } diff --git a/vendor/google.golang.org/grpc/internal/resolver/config_selector.go b/vendor/google.golang.org/grpc/internal/resolver/config_selector.go index 3db62ccad..6320e9b57 100644 --- a/vendor/google.golang.org/grpc/internal/resolver/config_selector.go +++ b/vendor/google.golang.org/grpc/internal/resolver/config_selector.go @@ -106,14 +106,24 @@ type ClientStream interface { // ClientInterceptor is an interceptor for gRPC client streams. type ClientInterceptor interface { - // NewStream produces a ClientStream for an RPC which may optionally use - // the provided function to produce a stream for delegation. Note: - // RPCInfo.Context should not be used (will be nil). + // NewStream creates a ClientStream for an RPC. // - // done is invoked when the RPC is finished using its connection, or could - // not be assigned a connection. RPC operations may still occur on - // ClientStream after done is called, since the interceptor is invoked by - // application-layer operations. done must never be nil when called. + // Implementations must delegate stream creation to the provided newStream + // function. To intercept or override stream behavior, implementations + // may wrap the ClientStream returned by the delegate. + // + // Note: RPCInfo.Context is currently unused and will be nil. + // + // The done function is invoked when the RPC has finished using its + // underlying connection or if a connection could not be assigned. Because + // interceptors operate at the application layer, RPC operations may + // continue on the ClientStream even after done has been called. The + // caller must ensure done is non-nil. + // + // To ensure RPC completion notifications propagate through the entire + // interceptor chain, implementations must ensure that the done function + // passed to the delegate newStream invokes the done function passed to + // NewStream. NewStream(ctx context.Context, ri RPCInfo, done func(), newStream func(ctx context.Context, done func()) (ClientStream, error)) (ClientStream, error) // Close closes the interceptor. Once called, no new calls to NewStream are // accepted. Ongoing calls to NewStream are allowed to complete. diff --git a/vendor/google.golang.org/grpc/internal/stats/labels.go b/vendor/google.golang.org/grpc/internal/stats/labels.go index fd33af51a..5ea898cb5 100644 --- a/vendor/google.golang.org/grpc/internal/stats/labels.go +++ b/vendor/google.golang.org/grpc/internal/stats/labels.go @@ -19,24 +19,56 @@ // Package stats provides internal stats related functionality. package stats -import "context" +import ( + "context" + "maps" +) -// Labels are the labels for metrics. -type Labels struct { - // TelemetryLabels are the telemetry labels to record. - TelemetryLabels map[string]string +// LabelCallback is a function that is executed when telemetry +// label keys are updated. +type LabelCallback func(map[string]string) +type telemetryLabelCallbackKey struct{} + +// UpdateLabels executes registered telemetry callbacks with the update labels. Labels +// are copied before being processed by any callbacks to ensure mutations are not +// shared among derived contexts. +// +// It is the responsibility of the registrant to handle conflicts or label resets. +func UpdateLabels(ctx context.Context, update map[string]string) { + executeTelemetryLabelCallbacks(ctx, update) } -type labelsKey struct{} +// RegisterTelemetryLabelCallback registers a callback function that is executed whenever +// telemetry labels are updated. +func RegisterTelemetryLabelCallback(ctx context.Context, callback LabelCallback) context.Context { + if callback == nil { + return ctx + } + + callbacks, ok := ctx.Value(telemetryLabelCallbackKey{}).([]LabelCallback) + if !ok { + return context.WithValue(ctx, telemetryLabelCallbackKey{}, []LabelCallback{callback}) + } + return context.WithValue(ctx, telemetryLabelCallbackKey{}, append(append([]LabelCallback(nil), callbacks...), callback)) -// GetLabels returns the Labels stored in the context, or nil if there is one. -func GetLabels(ctx context.Context) *Labels { - labels, _ := ctx.Value(labelsKey{}).(*Labels) - return labels } -// SetLabels sets the Labels in the context. -func SetLabels(ctx context.Context, labels *Labels) context.Context { - // could also append - return context.WithValue(ctx, labelsKey{}, labels) +// executeTelemetryLabelCallback runs the registered callbacks in the order they were +// registered on the context with the provided labels. If no callbacks are registered +// it does nothing. +// +// To ensure callbacks do not mutate the state of the provided label map it is copied +// before execution. +func executeTelemetryLabelCallbacks(ctx context.Context, labels map[string]string) { + callbacks, ok := ctx.Value(telemetryLabelCallbackKey{}).([]LabelCallback) + if !ok { + return + } + + labelsCopy := map[string]string{} + maps.Copy(labelsCopy, labels) + for _, callback := range callbacks { + callback(labelsCopy) + } + } diff --git a/vendor/google.golang.org/grpc/internal/transport/client_stream.go b/vendor/google.golang.org/grpc/internal/transport/client_stream.go index cd8152ef1..ad382b0fd 100644 --- a/vendor/google.golang.org/grpc/internal/transport/client_stream.go +++ b/vendor/google.golang.org/grpc/internal/transport/client_stream.go @@ -19,6 +19,7 @@ package transport import ( + "fmt" "sync/atomic" "golang.org/x/net/http2" @@ -28,6 +29,12 @@ import ( "google.golang.org/grpc/status" ) +// nonGRPCDataMaxLen is the maximum length of nonGRPCDataBuf. +// +// NOTE: If changed this value, you MUST update the corresponding test in: +// - /test/end2end_test.go:TestHTTPServerSendsNonGRPCHeaderSurfaceFurtherData +const nonGRPCDataMaxLen = 1024 + // ClientStream implements streaming functionality for a gRPC client. type ClientStream struct { Stream // Embed for common stream functionality. @@ -46,7 +53,11 @@ type ClientStream struct { // headerValid indicates whether a valid header was received. Only // meaningful after headerChan is closed (always call waitOnHeader() before // reading its value). - headerValid bool + headerValid bool + + nonGRPCStatus *status.Status // the initial status from the non-gRPC response header, finalized with collected data before closing. + nonGRPCDataBuf []byte // stores the data of a non-gRPC response. + noHeaders bool // set if the client never received headers (set only after the stream is done). headerChanClosed uint32 // set when headerChan is closed. Used to avoid closing headerChan multiple times. bytesReceived atomic.Bool // indicates whether any bytes have been received on this stream @@ -54,6 +65,29 @@ type ClientStream struct { statsHandler stats.Handler // nil for internal streams (e.g., health check, ORCA) where telemetry is not supported. } +func (s *ClientStream) startNonGRPCDataCollection(st *status.Status) { + s.nonGRPCStatus = st + s.nonGRPCDataBuf = make([]byte, 0, nonGRPCDataMaxLen) +} + +// finalizeNonGRPCStatus builds the terminal status by appending the collected +// response body to the original non-gRPC status message. +func (s *ClientStream) finalizeNonGRPCStatus() *status.Status { + msg := fmt.Sprintf("%s\ndata: %q", s.nonGRPCStatus.Message(), s.nonGRPCDataBuf) + return status.New(s.nonGRPCStatus.Code(), msg) +} + +// handleNonGRPCData collects non-gRPC body from the given data frame. +// It returns non-nil value when the stream should be closed with it. +func (s *ClientStream) handleNonGRPCData(f *parsedDataFrame) *status.Status { + n := min(f.data.Len(), nonGRPCDataMaxLen-len(s.nonGRPCDataBuf)) + s.nonGRPCDataBuf = append(s.nonGRPCDataBuf, f.data.ReadOnlyData()[0:n]...) + if len(s.nonGRPCDataBuf) >= nonGRPCDataMaxLen || f.StreamEnded() { + return s.finalizeNonGRPCStatus() + } + return nil +} + // Read reads an n byte message from the input stream. func (s *ClientStream) Read(n int) (mem.BufferSlice, error) { b, err := s.Stream.read(n) diff --git a/vendor/google.golang.org/grpc/internal/transport/controlbuf.go b/vendor/google.golang.org/grpc/internal/transport/controlbuf.go index 7efa52478..c5a76b70a 100644 --- a/vendor/google.golang.org/grpc/internal/transport/controlbuf.go +++ b/vendor/google.golang.org/grpc/internal/transport/controlbuf.go @@ -956,39 +956,16 @@ func (l *loopyWriter) processData() (bool, error) { // from data is copied to h to make as big as the maximum possible HTTP2 frame // size. - if len(dataItem.h) == 0 && reader.Remaining() == 0 { // Empty data frame - // Client sends out empty data frame with endStream = true - if err := l.framer.writeData(dataItem.streamID, dataItem.endStream, nil); err != nil { - return false, err - } - str.itl.dequeue() // remove the empty data item from stream - reader.Close() - if str.itl.isEmpty() { - str.state = empty - } else if trailer, ok := str.itl.peek().(*headerFrame); ok { // the next item is trailers. - if err := l.writeHeader(trailer.streamID, trailer.endStream, trailer.hf, trailer.onWrite); err != nil { - return false, err - } - if err := l.cleanupStreamHandler(trailer.cleanup); err != nil { - return false, err - } - } else { - l.activeStreams.enqueue(str) - } - return false, nil - } - + isEmpty := len(dataItem.h) == 0 && reader.Remaining() == 0 // Figure out the maximum size we can send maxSize := http2MaxFrameLen - if strQuota := int(l.oiws) - str.bytesOutStanding; strQuota <= 0 { // stream-level flow control. + strQuota := int(l.oiws) - str.bytesOutStanding + if strQuota <= 0 && !isEmpty { // stream-level flow control. str.state = waitingOnStreamQuota return false, nil - } else if maxSize > strQuota { - maxSize = strQuota - } - if maxSize > int(l.sendQuota) { // connection-level flow control. - maxSize = int(l.sendQuota) } + maxSize = min(maxSize, max(strQuota, 0)) + maxSize = min(maxSize, int(l.sendQuota)) // connection-level flow control. // Compute how much of the header and data we can send within quota and max frame length hSize := min(maxSize, len(dataItem.h)) dSize := min(maxSize-hSize, reader.Remaining()) @@ -1039,19 +1016,23 @@ func (l *loopyWriter) processData() (bool, error) { reader.Close() str.itl.dequeue() } + return false, l.updateStreamAfterWrite(str) +} + +func (l *loopyWriter) updateStreamAfterWrite(str *outStream) error { if str.itl.isEmpty() { str.state = empty - } else if trailer, ok := str.itl.peek().(*headerFrame); ok { // The next item is trailers. + } else if trailer, ok := str.itl.peek().(*headerFrame); ok { // the next item is trailers. if err := l.writeHeader(trailer.streamID, trailer.endStream, trailer.hf, trailer.onWrite); err != nil { - return false, err + return err } if err := l.cleanupStreamHandler(trailer.cleanup); err != nil { - return false, err + return err } } else if int(l.oiws)-str.bytesOutStanding <= 0 { // Ran out of stream quota. str.state = waitingOnStreamQuota } else { // Otherwise add it back to the list of active streams. l.activeStreams.enqueue(str) } - return false, nil + return nil } diff --git a/vendor/google.golang.org/grpc/internal/transport/flowcontrol.go b/vendor/google.golang.org/grpc/internal/transport/flowcontrol.go index 7cfbc9637..98cef9ec2 100644 --- a/vendor/google.golang.org/grpc/internal/transport/flowcontrol.go +++ b/vendor/google.golang.org/grpc/internal/transport/flowcontrol.go @@ -115,7 +115,6 @@ func (f *trInFlow) getSize() uint32 { return atomic.LoadUint32(&f.effectiveWindowSize) } -// TODO(mmukhi): Simplify this code. // inFlow deals with inbound flow control type inFlow struct { mu sync.Mutex @@ -174,14 +173,14 @@ func (f *inFlow) maybeAdjust(n uint32) uint32 { // onData is invoked when some data frame is received. It updates pendingData. func (f *inFlow) onData(n uint32) error { f.mu.Lock() + defer f.mu.Unlock() + f.pendingData += n if f.pendingData+f.pendingUpdate > f.limit+f.delta { limit := f.limit rcvd := f.pendingData + f.pendingUpdate - f.mu.Unlock() return fmt.Errorf("received %d-bytes data exceeding the limit %d bytes", rcvd, limit) } - f.mu.Unlock() return nil } @@ -189,8 +188,9 @@ func (f *inFlow) onData(n uint32) error { // to be sent to the peer. func (f *inFlow) onRead(n uint32) uint32 { f.mu.Lock() + defer f.mu.Unlock() + if f.pendingData == 0 { - f.mu.Unlock() return 0 } f.pendingData -= n @@ -205,9 +205,7 @@ func (f *inFlow) onRead(n uint32) uint32 { if f.pendingUpdate >= f.limit/4 { wu := f.pendingUpdate f.pendingUpdate = 0 - f.mu.Unlock() return wu } - f.mu.Unlock() return 0 } diff --git a/vendor/google.golang.org/grpc/internal/transport/handler_server.go b/vendor/google.golang.org/grpc/internal/transport/handler_server.go index 7ab3422b8..a8356c9ad 100644 --- a/vendor/google.golang.org/grpc/internal/transport/handler_server.go +++ b/vendor/google.golang.org/grpc/internal/transport/handler_server.go @@ -479,8 +479,8 @@ func (ht *serverHandlerTransport) runStream() { func (ht *serverHandlerTransport) incrMsgRecv() {} -func (ht *serverHandlerTransport) Drain(string) { - panic("Drain() is not implemented") +func (ht *serverHandlerTransport) Drain(s string) { + ht.Close(errors.New(s)) } // mapRecvMsgError returns the non-nil err into the appropriate diff --git a/vendor/google.golang.org/grpc/internal/transport/http2_client.go b/vendor/google.golang.org/grpc/internal/transport/http2_client.go index d6bc6a6cc..133f5d706 100644 --- a/vendor/google.golang.org/grpc/internal/transport/http2_client.go +++ b/vendor/google.golang.org/grpc/internal/transport/http2_client.go @@ -39,6 +39,7 @@ import ( "google.golang.org/grpc/internal" "google.golang.org/grpc/internal/channelz" icredentials "google.golang.org/grpc/internal/credentials" + "google.golang.org/grpc/internal/envconfig" "google.golang.org/grpc/internal/grpclog" "google.golang.org/grpc/internal/grpcsync" "google.golang.org/grpc/internal/grpcutil" @@ -318,7 +319,13 @@ func NewHTTP2Client(connectCtx, ctx context.Context, addr resolver.Address, opts } writeBufSize := opts.WriteBufferSize readBufSize := opts.ReadBufferSize + // The default header list size is moving from 16MB to 8KB. The 8KB limit + // is only used if Enable8KBDefaultHeaderListSize is true; otherwise, the + // old 16MB default is used. User-specified options always take precedence. maxHeaderListSize := defaultClientMaxHeaderListSize + if envconfig.Enable8KBDefaultHeaderListSize { + maxHeaderListSize = upcomingDefaultHeaderListSize + } if opts.MaxHeaderListSize != nil { maxHeaderListSize = *opts.MaxHeaderListSize } @@ -879,8 +886,8 @@ func (t *http2Client) NewStream(ctx context.Context, callHdr *CallHdr, handler s return false } } - if sz > int64(upcomingDefaultHeaderListSize) { - t.logger.Warningf("Header list size to send (%d bytes) is larger than the upcoming default limit (%d bytes). In a future release, this will be restricted to %d bytes.", sz, upcomingDefaultHeaderListSize, upcomingDefaultHeaderListSize) + if !envconfig.Enable8KBDefaultHeaderListSize && sz > int64(upcomingDefaultHeaderListSize) { + t.logger.Warningf("Header list size to send (%d bytes) is larger than the upcoming default limit (%d bytes). In release v1.82.0, GRPC_GO_EXPERIMENTAL_ENABLE_8KB_DEFAULT_HEADER_LIST_SIZE will be enabled by default, enforcing this limit.", sz, upcomingDefaultHeaderListSize) } return true } @@ -1224,6 +1231,23 @@ func (t *http2Client) handleData(f *parsedDataFrame) { t.closeStream(s, io.EOF, true, http2.ErrCodeFlowControl, status.New(codes.Internal, err.Error()), nil, false) return } + + if s.nonGRPCStatus != nil { + // The frame should be handled as a non-gRPC response body + st := s.handleNonGRPCData(f) + if st != nil { + t.closeStream(s, st.Err(), true, http2.ErrCodeProtocol, st, nil, true) + return + } + if w := s.fc.onRead(size); w > 0 { + t.controlBuf.put(&outgoingWindowUpdate{ + streamID: s.id, + increment: w, + }) + } + return + } + dataLen := f.data.Len() if f.Header().Flags.Has(http2.FlagDataPadded) { if w := s.fc.onRead(size - uint32(dataLen)); w > 0 { @@ -1468,6 +1492,17 @@ func (t *http2Client) operateHeaders(frame *http2.MetaHeadersFrame) { return } + // If we are collecting non-gRPC response data and receive a trailing + // HEADERS frame with END_STREAM, finalize the buffered data and close + // the stream. + if s.nonGRPCStatus != nil { + if endStream { + st := s.finalizeNonGRPCStatus() + t.closeStream(s, st.Err(), true, http2.ErrCodeProtocol, st, nil, true) + } + return + } + var ( // If a gRPC Response-Headers has already been received, then it means // that the peer is speaking gRPC and we are in gRPC mode. @@ -1568,7 +1603,12 @@ func (t *http2Client) operateHeaders(frame *http2.MetaHeadersFrame) { } se := status.New(grpcErrorCode, strings.Join(errs, "; ")) - t.closeStream(s, se.Err(), true, http2.ErrCodeProtocol, se, nil, endStream) + if endStream { + t.closeStream(s, se.Err(), true, http2.ErrCodeProtocol, se, nil, true) + return + } + + s.startNonGRPCDataCollection(se) return } diff --git a/vendor/google.golang.org/grpc/internal/transport/http2_server.go b/vendor/google.golang.org/grpc/internal/transport/http2_server.go index 3a8c36e4f..1acd44be4 100644 --- a/vendor/google.golang.org/grpc/internal/transport/http2_server.go +++ b/vendor/google.golang.org/grpc/internal/transport/http2_server.go @@ -38,11 +38,13 @@ import ( "google.golang.org/protobuf/proto" "google.golang.org/grpc/internal" + "google.golang.org/grpc/internal/envconfig" "google.golang.org/grpc/internal/grpclog" "google.golang.org/grpc/internal/grpcutil" "google.golang.org/grpc/internal/pretty" istatus "google.golang.org/grpc/internal/status" "google.golang.org/grpc/internal/syscall" + transportinternal "google.golang.org/grpc/internal/transport/internal" "google.golang.org/grpc/mem" "google.golang.org/grpc/codes" @@ -165,7 +167,13 @@ func NewServerTransport(conn net.Conn, config *ServerConfig) (_ ServerTransport, } writeBufSize := config.WriteBufferSize readBufSize := config.ReadBufferSize + // The default header list size is moving from 16MB to 8KB. The 8KB limit + // is only used if Enable8KBDefaultHeaderListSize is true; otherwise, the + // old 16MB default is used. User-specified options always take precedence. maxHeaderListSize := defaultServerMaxHeaderListSize + if envconfig.Enable8KBDefaultHeaderListSize { + maxHeaderListSize = upcomingDefaultHeaderListSize + } if config.MaxHeaderListSize != nil { maxHeaderListSize = *config.MaxHeaderListSize } @@ -948,8 +956,8 @@ func (t *http2Server) checkForHeaderListSize(hf []hpack.HeaderField) bool { return false } } - if sz > int64(upcomingDefaultHeaderListSize) { - t.logger.Warningf("Header list size to send (%d bytes) is larger than the upcoming default limit (%d bytes). In a future release, this will be restricted to %d bytes.", sz, upcomingDefaultHeaderListSize, upcomingDefaultHeaderListSize) + if !envconfig.Enable8KBDefaultHeaderListSize && sz > int64(upcomingDefaultHeaderListSize) { + t.logger.Warningf("Header list size to send (%d bytes) is larger than the upcoming default limit (%d bytes). In release v1.82.0, GRPC_GO_EXPERIMENTAL_ENABLE_8KB_DEFAULT_HEADER_LIST_SIZE will be enabled by default, enforcing this limit.", sz, upcomingDefaultHeaderListSize) } return true } @@ -1441,14 +1449,14 @@ func (t *http2Server) socketMetrics() *channelz.EphemeralSocketMetrics { func (t *http2Server) incrMsgSent() { if channelz.IsOn() { t.channelz.SocketMetrics.MessagesSent.Add(1) - t.channelz.SocketMetrics.LastMessageSentTimestamp.Add(1) + t.channelz.SocketMetrics.LastMessageSentTimestamp.Store(transportinternal.TimeNowFunc()) } } func (t *http2Server) incrMsgRecv() { if channelz.IsOn() { t.channelz.SocketMetrics.MessagesReceived.Add(1) - t.channelz.SocketMetrics.LastMessageReceivedTimestamp.Add(1) + t.channelz.SocketMetrics.LastMessageReceivedTimestamp.Store(transportinternal.TimeNowFunc()) } } diff --git a/vendor/google.golang.org/grpc/internal/grpcutil/regex.go b/vendor/google.golang.org/grpc/internal/transport/internal/internal.go similarity index 59% rename from vendor/google.golang.org/grpc/internal/grpcutil/regex.go rename to vendor/google.golang.org/grpc/internal/transport/internal/internal.go index 7a092b2b8..a7c7c7d5a 100644 --- a/vendor/google.golang.org/grpc/internal/grpcutil/regex.go +++ b/vendor/google.golang.org/grpc/internal/transport/internal/internal.go @@ -1,6 +1,6 @@ /* * - * Copyright 2021 gRPC authors. + * Copyright 2026 gRPC authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,16 +16,10 @@ * */ -package grpcutil +// Package internal contains functionality internal to the transport package. +package internal -import "regexp" - -// FullMatchWithRegex returns whether the full text matches the regex provided. -func FullMatchWithRegex(re *regexp.Regexp, text string) bool { - if len(text) == 0 { - return re.MatchString(text) - } - re.Longest() - rem := re.FindString(text) - return len(rem) == len(text) -} +// TimeNowFunc is a variable that can be set to override the default behavior of +// getting the current time in nanoseconds. It is used in transport code to set +// channelz timestamps, and is exposed here for testing purposes. +var TimeNowFunc func() int64 diff --git a/vendor/google.golang.org/grpc/internal/transport/transport.go b/vendor/google.golang.org/grpc/internal/transport/transport.go index 1e224576e..6dfae3984 100644 --- a/vendor/google.golang.org/grpc/internal/transport/transport.go +++ b/vendor/google.golang.org/grpc/internal/transport/transport.go @@ -35,6 +35,7 @@ import ( "google.golang.org/grpc/codes" "google.golang.org/grpc/credentials" "google.golang.org/grpc/internal/channelz" + "google.golang.org/grpc/internal/transport/internal" "google.golang.org/grpc/keepalive" "google.golang.org/grpc/mem" "google.golang.org/grpc/metadata" @@ -46,6 +47,10 @@ import ( const logLevel = 2 +func init() { + internal.TimeNowFunc = func() int64 { return time.Now().UnixNano() } +} + // recvMsg represents the received msg from the transport. All transport // protocol specific info has been removed. type recvMsg struct { diff --git a/vendor/google.golang.org/grpc/reflection/grpc_reflection_v1/reflection_grpc.pb.go b/vendor/google.golang.org/grpc/reflection/grpc_reflection_v1/reflection_grpc.pb.go index 81ced2fbf..21142c64e 100644 --- a/vendor/google.golang.org/grpc/reflection/grpc_reflection_v1/reflection_grpc.pb.go +++ b/vendor/google.golang.org/grpc/reflection/grpc_reflection_v1/reflection_grpc.pb.go @@ -21,7 +21,7 @@ // Code generated by protoc-gen-go-grpc. DO NOT EDIT. // versions: -// - protoc-gen-go-grpc v1.6.1 +// - protoc-gen-go-grpc v1.6.2 // - protoc v5.27.1 // source: grpc/reflection/v1/reflection.proto diff --git a/vendor/google.golang.org/grpc/reflection/grpc_reflection_v1alpha/reflection_grpc.pb.go b/vendor/google.golang.org/grpc/reflection/grpc_reflection_v1alpha/reflection_grpc.pb.go index 18061e8ba..2a3f45d1b 100644 --- a/vendor/google.golang.org/grpc/reflection/grpc_reflection_v1alpha/reflection_grpc.pb.go +++ b/vendor/google.golang.org/grpc/reflection/grpc_reflection_v1alpha/reflection_grpc.pb.go @@ -18,7 +18,7 @@ // Code generated by protoc-gen-go-grpc. DO NOT EDIT. // versions: -// - protoc-gen-go-grpc v1.6.1 +// - protoc-gen-go-grpc v1.6.2 // - protoc v5.27.1 // grpc/reflection/v1alpha/reflection.proto is a deprecated file. diff --git a/vendor/google.golang.org/grpc/rpc_util.go b/vendor/google.golang.org/grpc/rpc_util.go index ee7f7dead..52f4ea513 100644 --- a/vendor/google.golang.org/grpc/rpc_util.go +++ b/vendor/google.golang.org/grpc/rpc_util.go @@ -128,6 +128,16 @@ func NewGZIPDecompressor() Decompressor { } func (d *gzipDecompressor) Do(r io.Reader) ([]byte, error) { + return d.doWithMaxSize(r, math.MaxInt64) +} + +// doWithMaxSize behaves like Do but caps the size of the decompressed +// payload at maxMessageSize+1 bytes. The Decompressor interface does not +// allow extra parameters, so callers inside the package type-assert to +// *gzipDecompressor to invoke this method directly. The +1 byte makes it +// possible for the caller to detect that the limit was exceeded and +// return ResourceExhausted instead of materializing an unbounded payload. +func (d *gzipDecompressor) doWithMaxSize(r io.Reader, maxMessageSize int64) ([]byte, error) { var z *gzip.Reader switch maybeZ := d.pool.Get().(type) { case nil: @@ -148,7 +158,11 @@ func (d *gzipDecompressor) Do(r io.Reader) ([]byte, error) { z.Close() d.pool.Put(z) }() - return io.ReadAll(z) + var src io.Reader = z + if maxMessageSize < math.MaxInt64 { + src = io.LimitReader(z, maxMessageSize+1) + } + return io.ReadAll(src) } func (d *gzipDecompressor) Type() string { @@ -830,15 +844,15 @@ func compress(in mem.BufferSlice, cp Compressor, compressor encoding.Compressor, if compressor != nil { z, err := compressor.Compress(w) if err != nil { - return nil, 0, wrapErr(err) + return nil, compressionNone, wrapErr(err) } for _, b := range in { if _, err := z.Write(b.ReadOnlyData()); err != nil { - return nil, 0, wrapErr(err) + return nil, compressionNone, wrapErr(err) } } if err := z.Close(); err != nil { - return nil, 0, wrapErr(err) + return nil, compressionNone, wrapErr(err) } } else { // This is obviously really inefficient since it fully materializes the data, but @@ -848,7 +862,7 @@ func compress(in mem.BufferSlice, cp Compressor, compressor encoding.Compressor, buf := in.MaterializeToBuffer(pool) defer buf.Free() if err := cp.Do(w, buf.ReadOnlyData()); err != nil { - return nil, 0, wrapErr(err) + return nil, compressionNone, wrapErr(err) } } return out, compressionMade, nil @@ -971,7 +985,20 @@ func recvAndDecompress(p *parser, s recvCompressor, dc Decompressor, maxReceiveM func decompress(compressor encoding.Compressor, d mem.BufferSlice, dc Decompressor, maxReceiveMessageSize int, pool mem.BufferPool) (mem.BufferSlice, error) { if dc != nil { r := d.Reader() - uncompressed, err := dc.Do(r) + // For the built-in gzip decompressor, bound the decompressed output + // at maxReceiveMessageSize+1 so that a small but highly compressed + // payload (a "zip bomb") cannot expand to gigabytes in memory before + // the post-decompression size check below has a chance to fire. The + // Decompressor interface does not accept an extra size parameter, + // so we type-assert to invoke a size-aware helper. Third-party + // Decompressor implementations keep the original Do behavior. + var uncompressed []byte + var err error + if gd, ok := dc.(*gzipDecompressor); ok { + uncompressed, err = gd.doWithMaxSize(r, int64(maxReceiveMessageSize)) + } else { + uncompressed, err = dc.Do(r) + } if err != nil { r.Close() // ensure buffers are reused return nil, status.Errorf(codes.Internal, "grpc: failed to decompress the received message: %v", err) @@ -989,6 +1016,9 @@ func decompress(compressor encoding.Compressor, d mem.BufferSlice, dc Decompress r.Close() // ensure buffers are reused return nil, status.Errorf(codes.Internal, "grpc: failed to decompress the message: %v", err) } + if closer, ok := dcReader.(io.Closer); ok { + defer closer.Close() + } // Read at most one byte more than the limit from the decompressor. // Unless the limit is MaxInt64, in which case, that's impossible, so diff --git a/vendor/google.golang.org/grpc/server.go b/vendor/google.golang.org/grpc/server.go index 5229adf71..cf0a20671 100644 --- a/vendor/google.golang.org/grpc/server.go +++ b/vendor/google.golang.org/grpc/server.go @@ -28,6 +28,7 @@ import ( "net/http" "reflect" "runtime" + "runtime/pprof" "strings" "sync" "sync/atomic" @@ -150,8 +151,6 @@ type Server struct { serverWorkerChannel chan func() serverWorkerChannelClose func() - - strictPathCheckingLogEmitted atomic.Bool } type serverOptions struct { @@ -250,10 +249,8 @@ func newJoinServerOption(opts ...ServerOption) ServerOption { // If this option is set to true every connection will release the buffer after // flushing the data on the wire. // -// # Experimental -// -// Notice: This API is EXPERIMENTAL and may be changed or removed in a -// later release. +// Deprecated: shared write buffer is enabled by default. SharedWriteBuffer +// will be removed in a future release. func SharedWriteBuffer(val bool) ServerOption { return newFuncServerOption(func(o *serverOptions) { o.sharedWriteBuffer = val @@ -302,6 +299,14 @@ func InitialConnWindowSize(s int32) ServerOption { // window size to the value provided and disables dynamic flow control. // The lower bound for window size is 64K and any value smaller than that // will be ignored. +// +// Note that this also disables dynamic flow control for the connection, +// falling back to a default static connection-level window of 64KB. To +// use a larger connection-level window, you must also use the +// [StaticConnWindowSize] ServerOption. +// +// Most users should not configure static flow control windows unless +// operating in a memory-constrained environment. func StaticStreamWindowSize(s int32) ServerOption { return newFuncServerOption(func(o *serverOptions) { o.initialWindowSize = s @@ -313,6 +318,14 @@ func StaticStreamWindowSize(s int32) ServerOption { // window size to the value provided and disables dynamic flow control. // The lower bound for window size is 64K and any value smaller than that // will be ignored. +// +// Note that this also disables dynamic flow control for individual streams, +// falling back to a default static connection-level window of 64KB. To +// explicitly configure the stream-level window size, you must also use the +// [StaticStreamWindowSize] ServerOption. +// +// Most users should not configure static flow control windows unless +// operating in a memory-constrained environment. func StaticConnWindowSize(s int32) ServerOption { return newFuncServerOption(func(o *serverOptions) { o.initialConnWindowSize = s @@ -1787,6 +1800,12 @@ func (s *Server) handleMalformedMethodName(stream *transport.ServerStream, ti *t func (s *Server) handleStream(t transport.ServerTransport, stream *transport.ServerStream) { ctx := stream.Context() ctx = contextWithServer(ctx, s) + if envconfig.LabelServerGoroutines&envconfig.GoroutineLabelServerMethod != 0 { + // This method always runs in its own goroutine, so we can set a + // goroutine label without needing to restore a previous context. + ctx = pprof.WithLabels(ctx, pprof.Labels("grpc.method", stream.Method())) + pprof.SetGoroutineLabels(ctx) + } var ti *traceInfo if EnableTracing { tr := newTrace("grpc.Recv."+methodFamily(stream.Method()), stream.Method()) @@ -1803,28 +1822,11 @@ func (s *Server) handleStream(t transport.ServerTransport, stream *transport.Ser } } - sm := stream.Method() - if sm == "" { + sm, found := strings.CutPrefix(stream.Method(), "/") + if !found { s.handleMalformedMethodName(stream, ti) return } - if sm[0] != '/' { - // TODO(easwars): Add a link to the CVE in the below log messages once - // published. - if envconfig.DisableStrictPathChecking { - if old := s.strictPathCheckingLogEmitted.Swap(true); !old { - channelz.Warningf(logger, s.channelz, "grpc: Server.handleStream received malformed method name %q. Allowing it because the environment variable GRPC_GO_EXPERIMENTAL_DISABLE_STRICT_PATH_CHECKING is set to true, but this option will be removed in a future release.", sm) - } - } else { - if old := s.strictPathCheckingLogEmitted.Swap(true); !old { - channelz.Warningf(logger, s.channelz, "grpc: Server.handleStream rejected malformed method name %q. To temporarily allow such requests, set the environment variable GRPC_GO_EXPERIMENTAL_DISABLE_STRICT_PATH_CHECKING to true. Note that this is not recommended as it may allow requests to bypass security policies.", sm) - } - s.handleMalformedMethodName(stream, ti) - return - } - } else { - sm = sm[1:] - } pos := strings.LastIndex(sm, "/") if pos == -1 { s.handleMalformedMethodName(stream, ti) diff --git a/vendor/google.golang.org/grpc/version.go b/vendor/google.golang.org/grpc/version.go index 3ccfe515f..cf114ef4b 100644 --- a/vendor/google.golang.org/grpc/version.go +++ b/vendor/google.golang.org/grpc/version.go @@ -19,4 +19,4 @@ package grpc // Version is the current grpc version. -const Version = "1.81.1" +const Version = "1.82.0" diff --git a/vendor/google.golang.org/protobuf/types/known/structpb/struct.pb.go b/vendor/google.golang.org/protobuf/types/known/structpb/struct.pb.go new file mode 100644 index 000000000..30411b728 --- /dev/null +++ b/vendor/google.golang.org/protobuf/types/known/structpb/struct.pb.go @@ -0,0 +1,767 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// https://developers.google.com/protocol-buffers/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Code generated by protoc-gen-go. DO NOT EDIT. +// source: google/protobuf/struct.proto + +// Package structpb contains generated types for google/protobuf/struct.proto. +// +// The messages (i.e., Value, Struct, and ListValue) defined in struct.proto are +// used to represent arbitrary JSON. The Value message represents a JSON value, +// the Struct message represents a JSON object, and the ListValue message +// represents a JSON array. See https://json.org for more information. +// +// The Value, Struct, and ListValue types have generated MarshalJSON and +// UnmarshalJSON methods such that they serialize JSON equivalent to what the +// messages themselves represent. Use of these types with the +// "google.golang.org/protobuf/encoding/protojson" package +// ensures that they will be serialized as their JSON equivalent. +// +// # Conversion to and from a Go interface +// +// The standard Go "encoding/json" package has functionality to serialize +// arbitrary types to a large degree. The Value.AsInterface, Struct.AsMap, and +// ListValue.AsSlice methods can convert the protobuf message representation into +// a form represented by any, map[string]any, and []any. +// This form can be used with other packages that operate on such data structures +// and also directly with the standard json package. +// +// In order to convert the any, map[string]any, and []any +// forms back as Value, Struct, and ListValue messages, use the NewStruct, +// NewList, and NewValue constructor functions. +// +// # Example usage +// +// Consider the following example JSON object: +// +// { +// "firstName": "John", +// "lastName": "Smith", +// "isAlive": true, +// "age": 27, +// "address": { +// "streetAddress": "21 2nd Street", +// "city": "New York", +// "state": "NY", +// "postalCode": "10021-3100" +// }, +// "phoneNumbers": [ +// { +// "type": "home", +// "number": "212 555-1234" +// }, +// { +// "type": "office", +// "number": "646 555-4567" +// } +// ], +// "children": [], +// "spouse": null +// } +// +// To construct a Value message representing the above JSON object: +// +// m, err := structpb.NewValue(map[string]any{ +// "firstName": "John", +// "lastName": "Smith", +// "isAlive": true, +// "age": 27, +// "address": map[string]any{ +// "streetAddress": "21 2nd Street", +// "city": "New York", +// "state": "NY", +// "postalCode": "10021-3100", +// }, +// "phoneNumbers": []any{ +// map[string]any{ +// "type": "home", +// "number": "212 555-1234", +// }, +// map[string]any{ +// "type": "office", +// "number": "646 555-4567", +// }, +// }, +// "children": []any{}, +// "spouse": nil, +// }) +// if err != nil { +// ... // handle error +// } +// ... // make use of m as a *structpb.Value +package structpb + +import ( + base64 "encoding/base64" + json "encoding/json" + protojson "google.golang.org/protobuf/encoding/protojson" + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + math "math" + reflect "reflect" + sync "sync" + utf8 "unicode/utf8" + unsafe "unsafe" +) + +// `NullValue` is a singleton enumeration to represent the null value for the +// `Value` type union. +// +// The JSON representation for `NullValue` is JSON `null`. +type NullValue int32 + +const ( + // Null value. + NullValue_NULL_VALUE NullValue = 0 +) + +// Enum value maps for NullValue. +var ( + NullValue_name = map[int32]string{ + 0: "NULL_VALUE", + } + NullValue_value = map[string]int32{ + "NULL_VALUE": 0, + } +) + +func (x NullValue) Enum() *NullValue { + p := new(NullValue) + *p = x + return p +} + +func (x NullValue) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (NullValue) Descriptor() protoreflect.EnumDescriptor { + return file_google_protobuf_struct_proto_enumTypes[0].Descriptor() +} + +func (NullValue) Type() protoreflect.EnumType { + return &file_google_protobuf_struct_proto_enumTypes[0] +} + +func (x NullValue) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use NullValue.Descriptor instead. +func (NullValue) EnumDescriptor() ([]byte, []int) { + return file_google_protobuf_struct_proto_rawDescGZIP(), []int{0} +} + +// `Struct` represents a structured data value, consisting of fields +// which map to dynamically typed values. In some languages, `Struct` +// might be supported by a native representation. For example, in +// scripting languages like JS a struct is represented as an +// object. The details of that representation are described together +// with the proto support for the language. +// +// The JSON representation for `Struct` is JSON object. +type Struct struct { + state protoimpl.MessageState `protogen:"open.v1"` + // Unordered map of dynamically typed values. + Fields map[string]*Value `protobuf:"bytes,1,rep,name=fields,proto3" json:"fields,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +// NewStruct constructs a Struct from a general-purpose Go map. +// The map keys must be valid UTF-8. +// The map values are converted using NewValue. +func NewStruct(v map[string]any) (*Struct, error) { + x := &Struct{Fields: make(map[string]*Value, len(v))} + for k, v := range v { + if !utf8.ValidString(k) { + return nil, protoimpl.X.NewError("invalid UTF-8 in string: %q", k) + } + var err error + x.Fields[k], err = NewValue(v) + if err != nil { + return nil, err + } + } + return x, nil +} + +// AsMap converts x to a general-purpose Go map. +// The map values are converted by calling Value.AsInterface. +func (x *Struct) AsMap() map[string]any { + f := x.GetFields() + vs := make(map[string]any, len(f)) + for k, v := range f { + vs[k] = v.AsInterface() + } + return vs +} + +func (x *Struct) MarshalJSON() ([]byte, error) { + return protojson.Marshal(x) +} + +func (x *Struct) UnmarshalJSON(b []byte) error { + return protojson.Unmarshal(b, x) +} + +func (x *Struct) Reset() { + *x = Struct{} + mi := &file_google_protobuf_struct_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *Struct) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Struct) ProtoMessage() {} + +func (x *Struct) ProtoReflect() protoreflect.Message { + mi := &file_google_protobuf_struct_proto_msgTypes[0] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Struct.ProtoReflect.Descriptor instead. +func (*Struct) Descriptor() ([]byte, []int) { + return file_google_protobuf_struct_proto_rawDescGZIP(), []int{0} +} + +func (x *Struct) GetFields() map[string]*Value { + if x != nil { + return x.Fields + } + return nil +} + +// `Value` represents a dynamically typed value which can be either +// null, a number, a string, a boolean, a recursive struct value, or a +// list of values. A producer of value is expected to set one of these +// variants. Absence of any variant indicates an error. +// +// The JSON representation for `Value` is JSON value. +type Value struct { + state protoimpl.MessageState `protogen:"open.v1"` + // The kind of value. + // + // Types that are valid to be assigned to Kind: + // + // *Value_NullValue + // *Value_NumberValue + // *Value_StringValue + // *Value_BoolValue + // *Value_StructValue + // *Value_ListValue + Kind isValue_Kind `protobuf_oneof:"kind"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +// NewValue constructs a Value from a general-purpose Go interface. +// +// ╔═══════════════════════════════════════╤════════════════════════════════════════════╗ +// ║ Go type │ Conversion ║ +// ╠═══════════════════════════════════════╪════════════════════════════════════════════╣ +// ║ nil │ stored as NullValue ║ +// ║ bool │ stored as BoolValue ║ +// ║ int, int8, int16, int32, int64 │ stored as NumberValue ║ +// ║ uint, uint8, uint16, uint32, uint64 │ stored as NumberValue ║ +// ║ float32, float64 │ stored as NumberValue ║ +// ║ json.Number │ stored as NumberValue ║ +// ║ string │ stored as StringValue; must be valid UTF-8 ║ +// ║ []byte │ stored as StringValue; base64-encoded ║ +// ║ map[string]any │ stored as StructValue ║ +// ║ []any │ stored as ListValue ║ +// ╚═══════════════════════════════════════╧════════════════════════════════════════════╝ +// +// When converting an int64 or uint64 to a NumberValue, numeric precision loss +// is possible since they are stored as a float64. +func NewValue(v any) (*Value, error) { + switch v := v.(type) { + case nil: + return NewNullValue(), nil + case bool: + return NewBoolValue(v), nil + case int: + return NewNumberValue(float64(v)), nil + case int8: + return NewNumberValue(float64(v)), nil + case int16: + return NewNumberValue(float64(v)), nil + case int32: + return NewNumberValue(float64(v)), nil + case int64: + return NewNumberValue(float64(v)), nil + case uint: + return NewNumberValue(float64(v)), nil + case uint8: + return NewNumberValue(float64(v)), nil + case uint16: + return NewNumberValue(float64(v)), nil + case uint32: + return NewNumberValue(float64(v)), nil + case uint64: + return NewNumberValue(float64(v)), nil + case float32: + return NewNumberValue(float64(v)), nil + case float64: + return NewNumberValue(float64(v)), nil + case json.Number: + n, err := v.Float64() + if err != nil { + return nil, protoimpl.X.NewError("invalid number format %q, expected a float64: %v", v, err) + } + return NewNumberValue(n), nil + case string: + if !utf8.ValidString(v) { + return nil, protoimpl.X.NewError("invalid UTF-8 in string: %q", v) + } + return NewStringValue(v), nil + case []byte: + s := base64.StdEncoding.EncodeToString(v) + return NewStringValue(s), nil + case map[string]any: + v2, err := NewStruct(v) + if err != nil { + return nil, err + } + return NewStructValue(v2), nil + case []any: + v2, err := NewList(v) + if err != nil { + return nil, err + } + return NewListValue(v2), nil + default: + return nil, protoimpl.X.NewError("invalid type: %T", v) + } +} + +// NewNullValue constructs a new null Value. +func NewNullValue() *Value { + return &Value{Kind: &Value_NullValue{NullValue: NullValue_NULL_VALUE}} +} + +// NewBoolValue constructs a new boolean Value. +func NewBoolValue(v bool) *Value { + return &Value{Kind: &Value_BoolValue{BoolValue: v}} +} + +// NewNumberValue constructs a new number Value. +func NewNumberValue(v float64) *Value { + return &Value{Kind: &Value_NumberValue{NumberValue: v}} +} + +// NewStringValue constructs a new string Value. +func NewStringValue(v string) *Value { + return &Value{Kind: &Value_StringValue{StringValue: v}} +} + +// NewStructValue constructs a new struct Value. +func NewStructValue(v *Struct) *Value { + return &Value{Kind: &Value_StructValue{StructValue: v}} +} + +// NewListValue constructs a new list Value. +func NewListValue(v *ListValue) *Value { + return &Value{Kind: &Value_ListValue{ListValue: v}} +} + +// AsInterface converts x to a general-purpose Go interface. +// +// Calling Value.MarshalJSON and "encoding/json".Marshal on this output produce +// semantically equivalent JSON (assuming no errors occur). +// +// Floating-point values (i.e., "NaN", "Infinity", and "-Infinity") are +// converted as strings to remain compatible with MarshalJSON. +func (x *Value) AsInterface() any { + switch v := x.GetKind().(type) { + case *Value_NumberValue: + if v != nil { + switch { + case math.IsNaN(v.NumberValue): + return "NaN" + case math.IsInf(v.NumberValue, +1): + return "Infinity" + case math.IsInf(v.NumberValue, -1): + return "-Infinity" + default: + return v.NumberValue + } + } + case *Value_StringValue: + if v != nil { + return v.StringValue + } + case *Value_BoolValue: + if v != nil { + return v.BoolValue + } + case *Value_StructValue: + if v != nil { + return v.StructValue.AsMap() + } + case *Value_ListValue: + if v != nil { + return v.ListValue.AsSlice() + } + } + return nil +} + +func (x *Value) MarshalJSON() ([]byte, error) { + return protojson.Marshal(x) +} + +func (x *Value) UnmarshalJSON(b []byte) error { + return protojson.Unmarshal(b, x) +} + +func (x *Value) Reset() { + *x = Value{} + mi := &file_google_protobuf_struct_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *Value) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Value) ProtoMessage() {} + +func (x *Value) ProtoReflect() protoreflect.Message { + mi := &file_google_protobuf_struct_proto_msgTypes[1] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Value.ProtoReflect.Descriptor instead. +func (*Value) Descriptor() ([]byte, []int) { + return file_google_protobuf_struct_proto_rawDescGZIP(), []int{1} +} + +func (x *Value) GetKind() isValue_Kind { + if x != nil { + return x.Kind + } + return nil +} + +func (x *Value) GetNullValue() NullValue { + if x != nil { + if x, ok := x.Kind.(*Value_NullValue); ok { + return x.NullValue + } + } + return NullValue_NULL_VALUE +} + +func (x *Value) GetNumberValue() float64 { + if x != nil { + if x, ok := x.Kind.(*Value_NumberValue); ok { + return x.NumberValue + } + } + return 0 +} + +func (x *Value) GetStringValue() string { + if x != nil { + if x, ok := x.Kind.(*Value_StringValue); ok { + return x.StringValue + } + } + return "" +} + +func (x *Value) GetBoolValue() bool { + if x != nil { + if x, ok := x.Kind.(*Value_BoolValue); ok { + return x.BoolValue + } + } + return false +} + +func (x *Value) GetStructValue() *Struct { + if x != nil { + if x, ok := x.Kind.(*Value_StructValue); ok { + return x.StructValue + } + } + return nil +} + +func (x *Value) GetListValue() *ListValue { + if x != nil { + if x, ok := x.Kind.(*Value_ListValue); ok { + return x.ListValue + } + } + return nil +} + +type isValue_Kind interface { + isValue_Kind() +} + +type Value_NullValue struct { + // Represents a null value. + NullValue NullValue `protobuf:"varint,1,opt,name=null_value,json=nullValue,proto3,enum=google.protobuf.NullValue,oneof"` +} + +type Value_NumberValue struct { + // Represents a double value. + NumberValue float64 `protobuf:"fixed64,2,opt,name=number_value,json=numberValue,proto3,oneof"` +} + +type Value_StringValue struct { + // Represents a string value. + StringValue string `protobuf:"bytes,3,opt,name=string_value,json=stringValue,proto3,oneof"` +} + +type Value_BoolValue struct { + // Represents a boolean value. + BoolValue bool `protobuf:"varint,4,opt,name=bool_value,json=boolValue,proto3,oneof"` +} + +type Value_StructValue struct { + // Represents a structured value. + StructValue *Struct `protobuf:"bytes,5,opt,name=struct_value,json=structValue,proto3,oneof"` +} + +type Value_ListValue struct { + // Represents a repeated `Value`. + ListValue *ListValue `protobuf:"bytes,6,opt,name=list_value,json=listValue,proto3,oneof"` +} + +func (*Value_NullValue) isValue_Kind() {} + +func (*Value_NumberValue) isValue_Kind() {} + +func (*Value_StringValue) isValue_Kind() {} + +func (*Value_BoolValue) isValue_Kind() {} + +func (*Value_StructValue) isValue_Kind() {} + +func (*Value_ListValue) isValue_Kind() {} + +// `ListValue` is a wrapper around a repeated field of values. +// +// The JSON representation for `ListValue` is JSON array. +type ListValue struct { + state protoimpl.MessageState `protogen:"open.v1"` + // Repeated field of dynamically typed values. + Values []*Value `protobuf:"bytes,1,rep,name=values,proto3" json:"values,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +// NewList constructs a ListValue from a general-purpose Go slice. +// The slice elements are converted using NewValue. +func NewList(v []any) (*ListValue, error) { + x := &ListValue{Values: make([]*Value, len(v))} + for i, v := range v { + var err error + x.Values[i], err = NewValue(v) + if err != nil { + return nil, err + } + } + return x, nil +} + +// AsSlice converts x to a general-purpose Go slice. +// The slice elements are converted by calling Value.AsInterface. +func (x *ListValue) AsSlice() []any { + vals := x.GetValues() + vs := make([]any, len(vals)) + for i, v := range vals { + vs[i] = v.AsInterface() + } + return vs +} + +func (x *ListValue) MarshalJSON() ([]byte, error) { + return protojson.Marshal(x) +} + +func (x *ListValue) UnmarshalJSON(b []byte) error { + return protojson.Unmarshal(b, x) +} + +func (x *ListValue) Reset() { + *x = ListValue{} + mi := &file_google_protobuf_struct_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *ListValue) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ListValue) ProtoMessage() {} + +func (x *ListValue) ProtoReflect() protoreflect.Message { + mi := &file_google_protobuf_struct_proto_msgTypes[2] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ListValue.ProtoReflect.Descriptor instead. +func (*ListValue) Descriptor() ([]byte, []int) { + return file_google_protobuf_struct_proto_rawDescGZIP(), []int{2} +} + +func (x *ListValue) GetValues() []*Value { + if x != nil { + return x.Values + } + return nil +} + +var File_google_protobuf_struct_proto protoreflect.FileDescriptor + +const file_google_protobuf_struct_proto_rawDesc = "" + + "\n" + + "\x1cgoogle/protobuf/struct.proto\x12\x0fgoogle.protobuf\"\x98\x01\n" + + "\x06Struct\x12;\n" + + "\x06fields\x18\x01 \x03(\v2#.google.protobuf.Struct.FieldsEntryR\x06fields\x1aQ\n" + + "\vFieldsEntry\x12\x10\n" + + "\x03key\x18\x01 \x01(\tR\x03key\x12,\n" + + "\x05value\x18\x02 \x01(\v2\x16.google.protobuf.ValueR\x05value:\x028\x01\"\xb2\x02\n" + + "\x05Value\x12;\n" + + "\n" + + "null_value\x18\x01 \x01(\x0e2\x1a.google.protobuf.NullValueH\x00R\tnullValue\x12#\n" + + "\fnumber_value\x18\x02 \x01(\x01H\x00R\vnumberValue\x12#\n" + + "\fstring_value\x18\x03 \x01(\tH\x00R\vstringValue\x12\x1f\n" + + "\n" + + "bool_value\x18\x04 \x01(\bH\x00R\tboolValue\x12<\n" + + "\fstruct_value\x18\x05 \x01(\v2\x17.google.protobuf.StructH\x00R\vstructValue\x12;\n" + + "\n" + + "list_value\x18\x06 \x01(\v2\x1a.google.protobuf.ListValueH\x00R\tlistValueB\x06\n" + + "\x04kind\";\n" + + "\tListValue\x12.\n" + + "\x06values\x18\x01 \x03(\v2\x16.google.protobuf.ValueR\x06values*\x1b\n" + + "\tNullValue\x12\x0e\n" + + "\n" + + "NULL_VALUE\x10\x00B\x7f\n" + + "\x13com.google.protobufB\vStructProtoP\x01Z/google.golang.org/protobuf/types/known/structpb\xf8\x01\x01\xa2\x02\x03GPB\xaa\x02\x1eGoogle.Protobuf.WellKnownTypesb\x06proto3" + +var ( + file_google_protobuf_struct_proto_rawDescOnce sync.Once + file_google_protobuf_struct_proto_rawDescData []byte +) + +func file_google_protobuf_struct_proto_rawDescGZIP() []byte { + file_google_protobuf_struct_proto_rawDescOnce.Do(func() { + file_google_protobuf_struct_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_google_protobuf_struct_proto_rawDesc), len(file_google_protobuf_struct_proto_rawDesc))) + }) + return file_google_protobuf_struct_proto_rawDescData +} + +var file_google_protobuf_struct_proto_enumTypes = make([]protoimpl.EnumInfo, 1) +var file_google_protobuf_struct_proto_msgTypes = make([]protoimpl.MessageInfo, 4) +var file_google_protobuf_struct_proto_goTypes = []any{ + (NullValue)(0), // 0: google.protobuf.NullValue + (*Struct)(nil), // 1: google.protobuf.Struct + (*Value)(nil), // 2: google.protobuf.Value + (*ListValue)(nil), // 3: google.protobuf.ListValue + nil, // 4: google.protobuf.Struct.FieldsEntry +} +var file_google_protobuf_struct_proto_depIdxs = []int32{ + 4, // 0: google.protobuf.Struct.fields:type_name -> google.protobuf.Struct.FieldsEntry + 0, // 1: google.protobuf.Value.null_value:type_name -> google.protobuf.NullValue + 1, // 2: google.protobuf.Value.struct_value:type_name -> google.protobuf.Struct + 3, // 3: google.protobuf.Value.list_value:type_name -> google.protobuf.ListValue + 2, // 4: google.protobuf.ListValue.values:type_name -> google.protobuf.Value + 2, // 5: google.protobuf.Struct.FieldsEntry.value:type_name -> google.protobuf.Value + 6, // [6:6] is the sub-list for method output_type + 6, // [6:6] is the sub-list for method input_type + 6, // [6:6] is the sub-list for extension type_name + 6, // [6:6] is the sub-list for extension extendee + 0, // [0:6] is the sub-list for field type_name +} + +func init() { file_google_protobuf_struct_proto_init() } +func file_google_protobuf_struct_proto_init() { + if File_google_protobuf_struct_proto != nil { + return + } + file_google_protobuf_struct_proto_msgTypes[1].OneofWrappers = []any{ + (*Value_NullValue)(nil), + (*Value_NumberValue)(nil), + (*Value_StringValue)(nil), + (*Value_BoolValue)(nil), + (*Value_StructValue)(nil), + (*Value_ListValue)(nil), + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: unsafe.Slice(unsafe.StringData(file_google_protobuf_struct_proto_rawDesc), len(file_google_protobuf_struct_proto_rawDesc)), + NumEnums: 1, + NumMessages: 4, + NumExtensions: 0, + NumServices: 0, + }, + GoTypes: file_google_protobuf_struct_proto_goTypes, + DependencyIndexes: file_google_protobuf_struct_proto_depIdxs, + EnumInfos: file_google_protobuf_struct_proto_enumTypes, + MessageInfos: file_google_protobuf_struct_proto_msgTypes, + }.Build() + File_google_protobuf_struct_proto = out.File + file_google_protobuf_struct_proto_goTypes = nil + file_google_protobuf_struct_proto_depIdxs = nil +} diff --git a/vendor/google.golang.org/protobuf/types/known/wrapperspb/wrappers.pb.go b/vendor/google.golang.org/protobuf/types/known/wrapperspb/wrappers.pb.go new file mode 100644 index 000000000..b7c2d0607 --- /dev/null +++ b/vendor/google.golang.org/protobuf/types/known/wrapperspb/wrappers.pb.go @@ -0,0 +1,648 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// https://developers.google.com/protocol-buffers/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Wrappers for primitive (non-message) types. These types were needed +// for legacy reasons and are not recommended for use in new APIs. +// +// Historically these wrappers were useful to have presence on proto3 primitive +// fields, but proto3 syntax has been updated to support the `optional` keyword. +// Using that keyword is now the strongly preferred way to add presence to +// proto3 primitive fields. +// +// A secondary usecase was to embed primitives in the `google.protobuf.Any` +// type: it is now recommended that you embed your value in your own wrapper +// message which can be specifically documented. +// +// These wrappers have no meaningful use within repeated fields as they lack +// the ability to detect presence on individual elements. +// These wrappers have no meaningful use within a map or a oneof since +// individual entries of a map or fields of a oneof can already detect presence. + +// Code generated by protoc-gen-go. DO NOT EDIT. +// source: google/protobuf/wrappers.proto + +package wrapperspb + +import ( + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" + unsafe "unsafe" +) + +// Wrapper message for `double`. +// +// The JSON representation for `DoubleValue` is JSON number. +// +// Not recommended for use in new APIs, but still useful for legacy APIs and +// has no plan to be removed. +type DoubleValue struct { + state protoimpl.MessageState `protogen:"open.v1"` + // The double value. + Value float64 `protobuf:"fixed64,1,opt,name=value,proto3" json:"value,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +// Double stores v in a new DoubleValue and returns a pointer to it. +func Double(v float64) *DoubleValue { + return &DoubleValue{Value: v} +} + +func (x *DoubleValue) Reset() { + *x = DoubleValue{} + mi := &file_google_protobuf_wrappers_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *DoubleValue) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*DoubleValue) ProtoMessage() {} + +func (x *DoubleValue) ProtoReflect() protoreflect.Message { + mi := &file_google_protobuf_wrappers_proto_msgTypes[0] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use DoubleValue.ProtoReflect.Descriptor instead. +func (*DoubleValue) Descriptor() ([]byte, []int) { + return file_google_protobuf_wrappers_proto_rawDescGZIP(), []int{0} +} + +func (x *DoubleValue) GetValue() float64 { + if x != nil { + return x.Value + } + return 0 +} + +// Wrapper message for `float`. +// +// The JSON representation for `FloatValue` is JSON number. +// +// Not recommended for use in new APIs, but still useful for legacy APIs and +// has no plan to be removed. +type FloatValue struct { + state protoimpl.MessageState `protogen:"open.v1"` + // The float value. + Value float32 `protobuf:"fixed32,1,opt,name=value,proto3" json:"value,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +// Float stores v in a new FloatValue and returns a pointer to it. +func Float(v float32) *FloatValue { + return &FloatValue{Value: v} +} + +func (x *FloatValue) Reset() { + *x = FloatValue{} + mi := &file_google_protobuf_wrappers_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *FloatValue) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*FloatValue) ProtoMessage() {} + +func (x *FloatValue) ProtoReflect() protoreflect.Message { + mi := &file_google_protobuf_wrappers_proto_msgTypes[1] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use FloatValue.ProtoReflect.Descriptor instead. +func (*FloatValue) Descriptor() ([]byte, []int) { + return file_google_protobuf_wrappers_proto_rawDescGZIP(), []int{1} +} + +func (x *FloatValue) GetValue() float32 { + if x != nil { + return x.Value + } + return 0 +} + +// Wrapper message for `int64`. +// +// The JSON representation for `Int64Value` is JSON string. +// +// Not recommended for use in new APIs, but still useful for legacy APIs and +// has no plan to be removed. +type Int64Value struct { + state protoimpl.MessageState `protogen:"open.v1"` + // The int64 value. + Value int64 `protobuf:"varint,1,opt,name=value,proto3" json:"value,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +// Int64 stores v in a new Int64Value and returns a pointer to it. +func Int64(v int64) *Int64Value { + return &Int64Value{Value: v} +} + +func (x *Int64Value) Reset() { + *x = Int64Value{} + mi := &file_google_protobuf_wrappers_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *Int64Value) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Int64Value) ProtoMessage() {} + +func (x *Int64Value) ProtoReflect() protoreflect.Message { + mi := &file_google_protobuf_wrappers_proto_msgTypes[2] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Int64Value.ProtoReflect.Descriptor instead. +func (*Int64Value) Descriptor() ([]byte, []int) { + return file_google_protobuf_wrappers_proto_rawDescGZIP(), []int{2} +} + +func (x *Int64Value) GetValue() int64 { + if x != nil { + return x.Value + } + return 0 +} + +// Wrapper message for `uint64`. +// +// The JSON representation for `UInt64Value` is JSON string. +// +// Not recommended for use in new APIs, but still useful for legacy APIs and +// has no plan to be removed. +type UInt64Value struct { + state protoimpl.MessageState `protogen:"open.v1"` + // The uint64 value. + Value uint64 `protobuf:"varint,1,opt,name=value,proto3" json:"value,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +// UInt64 stores v in a new UInt64Value and returns a pointer to it. +func UInt64(v uint64) *UInt64Value { + return &UInt64Value{Value: v} +} + +func (x *UInt64Value) Reset() { + *x = UInt64Value{} + mi := &file_google_protobuf_wrappers_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *UInt64Value) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*UInt64Value) ProtoMessage() {} + +func (x *UInt64Value) ProtoReflect() protoreflect.Message { + mi := &file_google_protobuf_wrappers_proto_msgTypes[3] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use UInt64Value.ProtoReflect.Descriptor instead. +func (*UInt64Value) Descriptor() ([]byte, []int) { + return file_google_protobuf_wrappers_proto_rawDescGZIP(), []int{3} +} + +func (x *UInt64Value) GetValue() uint64 { + if x != nil { + return x.Value + } + return 0 +} + +// Wrapper message for `int32`. +// +// The JSON representation for `Int32Value` is JSON number. +// +// Not recommended for use in new APIs, but still useful for legacy APIs and +// has no plan to be removed. +type Int32Value struct { + state protoimpl.MessageState `protogen:"open.v1"` + // The int32 value. + Value int32 `protobuf:"varint,1,opt,name=value,proto3" json:"value,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +// Int32 stores v in a new Int32Value and returns a pointer to it. +func Int32(v int32) *Int32Value { + return &Int32Value{Value: v} +} + +func (x *Int32Value) Reset() { + *x = Int32Value{} + mi := &file_google_protobuf_wrappers_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *Int32Value) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Int32Value) ProtoMessage() {} + +func (x *Int32Value) ProtoReflect() protoreflect.Message { + mi := &file_google_protobuf_wrappers_proto_msgTypes[4] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Int32Value.ProtoReflect.Descriptor instead. +func (*Int32Value) Descriptor() ([]byte, []int) { + return file_google_protobuf_wrappers_proto_rawDescGZIP(), []int{4} +} + +func (x *Int32Value) GetValue() int32 { + if x != nil { + return x.Value + } + return 0 +} + +// Wrapper message for `uint32`. +// +// The JSON representation for `UInt32Value` is JSON number. +// +// Not recommended for use in new APIs, but still useful for legacy APIs and +// has no plan to be removed. +type UInt32Value struct { + state protoimpl.MessageState `protogen:"open.v1"` + // The uint32 value. + Value uint32 `protobuf:"varint,1,opt,name=value,proto3" json:"value,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +// UInt32 stores v in a new UInt32Value and returns a pointer to it. +func UInt32(v uint32) *UInt32Value { + return &UInt32Value{Value: v} +} + +func (x *UInt32Value) Reset() { + *x = UInt32Value{} + mi := &file_google_protobuf_wrappers_proto_msgTypes[5] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *UInt32Value) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*UInt32Value) ProtoMessage() {} + +func (x *UInt32Value) ProtoReflect() protoreflect.Message { + mi := &file_google_protobuf_wrappers_proto_msgTypes[5] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use UInt32Value.ProtoReflect.Descriptor instead. +func (*UInt32Value) Descriptor() ([]byte, []int) { + return file_google_protobuf_wrappers_proto_rawDescGZIP(), []int{5} +} + +func (x *UInt32Value) GetValue() uint32 { + if x != nil { + return x.Value + } + return 0 +} + +// Wrapper message for `bool`. +// +// The JSON representation for `BoolValue` is JSON `true` and `false`. +// +// Not recommended for use in new APIs, but still useful for legacy APIs and +// has no plan to be removed. +type BoolValue struct { + state protoimpl.MessageState `protogen:"open.v1"` + // The bool value. + Value bool `protobuf:"varint,1,opt,name=value,proto3" json:"value,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +// Bool stores v in a new BoolValue and returns a pointer to it. +func Bool(v bool) *BoolValue { + return &BoolValue{Value: v} +} + +func (x *BoolValue) Reset() { + *x = BoolValue{} + mi := &file_google_protobuf_wrappers_proto_msgTypes[6] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *BoolValue) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*BoolValue) ProtoMessage() {} + +func (x *BoolValue) ProtoReflect() protoreflect.Message { + mi := &file_google_protobuf_wrappers_proto_msgTypes[6] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use BoolValue.ProtoReflect.Descriptor instead. +func (*BoolValue) Descriptor() ([]byte, []int) { + return file_google_protobuf_wrappers_proto_rawDescGZIP(), []int{6} +} + +func (x *BoolValue) GetValue() bool { + if x != nil { + return x.Value + } + return false +} + +// Wrapper message for `string`. +// +// The JSON representation for `StringValue` is JSON string. +// +// Not recommended for use in new APIs, but still useful for legacy APIs and +// has no plan to be removed. +type StringValue struct { + state protoimpl.MessageState `protogen:"open.v1"` + // The string value. + Value string `protobuf:"bytes,1,opt,name=value,proto3" json:"value,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +// String stores v in a new StringValue and returns a pointer to it. +func String(v string) *StringValue { + return &StringValue{Value: v} +} + +func (x *StringValue) Reset() { + *x = StringValue{} + mi := &file_google_protobuf_wrappers_proto_msgTypes[7] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *StringValue) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*StringValue) ProtoMessage() {} + +func (x *StringValue) ProtoReflect() protoreflect.Message { + mi := &file_google_protobuf_wrappers_proto_msgTypes[7] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use StringValue.ProtoReflect.Descriptor instead. +func (*StringValue) Descriptor() ([]byte, []int) { + return file_google_protobuf_wrappers_proto_rawDescGZIP(), []int{7} +} + +func (x *StringValue) GetValue() string { + if x != nil { + return x.Value + } + return "" +} + +// Wrapper message for `bytes`. +// +// The JSON representation for `BytesValue` is JSON string. +// +// Not recommended for use in new APIs, but still useful for legacy APIs and +// has no plan to be removed. +type BytesValue struct { + state protoimpl.MessageState `protogen:"open.v1"` + // The bytes value. + Value []byte `protobuf:"bytes,1,opt,name=value,proto3" json:"value,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +// Bytes stores v in a new BytesValue and returns a pointer to it. +func Bytes(v []byte) *BytesValue { + return &BytesValue{Value: v} +} + +func (x *BytesValue) Reset() { + *x = BytesValue{} + mi := &file_google_protobuf_wrappers_proto_msgTypes[8] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *BytesValue) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*BytesValue) ProtoMessage() {} + +func (x *BytesValue) ProtoReflect() protoreflect.Message { + mi := &file_google_protobuf_wrappers_proto_msgTypes[8] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use BytesValue.ProtoReflect.Descriptor instead. +func (*BytesValue) Descriptor() ([]byte, []int) { + return file_google_protobuf_wrappers_proto_rawDescGZIP(), []int{8} +} + +func (x *BytesValue) GetValue() []byte { + if x != nil { + return x.Value + } + return nil +} + +var File_google_protobuf_wrappers_proto protoreflect.FileDescriptor + +const file_google_protobuf_wrappers_proto_rawDesc = "" + + "\n" + + "\x1egoogle/protobuf/wrappers.proto\x12\x0fgoogle.protobuf\"#\n" + + "\vDoubleValue\x12\x14\n" + + "\x05value\x18\x01 \x01(\x01R\x05value\"\"\n" + + "\n" + + "FloatValue\x12\x14\n" + + "\x05value\x18\x01 \x01(\x02R\x05value\"\"\n" + + "\n" + + "Int64Value\x12\x14\n" + + "\x05value\x18\x01 \x01(\x03R\x05value\"#\n" + + "\vUInt64Value\x12\x14\n" + + "\x05value\x18\x01 \x01(\x04R\x05value\"\"\n" + + "\n" + + "Int32Value\x12\x14\n" + + "\x05value\x18\x01 \x01(\x05R\x05value\"#\n" + + "\vUInt32Value\x12\x14\n" + + "\x05value\x18\x01 \x01(\rR\x05value\"!\n" + + "\tBoolValue\x12\x14\n" + + "\x05value\x18\x01 \x01(\bR\x05value\"#\n" + + "\vStringValue\x12\x14\n" + + "\x05value\x18\x01 \x01(\tR\x05value\"\"\n" + + "\n" + + "BytesValue\x12\x14\n" + + "\x05value\x18\x01 \x01(\fR\x05valueB\x83\x01\n" + + "\x13com.google.protobufB\rWrappersProtoP\x01Z1google.golang.org/protobuf/types/known/wrapperspb\xf8\x01\x01\xa2\x02\x03GPB\xaa\x02\x1eGoogle.Protobuf.WellKnownTypesb\x06proto3" + +var ( + file_google_protobuf_wrappers_proto_rawDescOnce sync.Once + file_google_protobuf_wrappers_proto_rawDescData []byte +) + +func file_google_protobuf_wrappers_proto_rawDescGZIP() []byte { + file_google_protobuf_wrappers_proto_rawDescOnce.Do(func() { + file_google_protobuf_wrappers_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_google_protobuf_wrappers_proto_rawDesc), len(file_google_protobuf_wrappers_proto_rawDesc))) + }) + return file_google_protobuf_wrappers_proto_rawDescData +} + +var file_google_protobuf_wrappers_proto_msgTypes = make([]protoimpl.MessageInfo, 9) +var file_google_protobuf_wrappers_proto_goTypes = []any{ + (*DoubleValue)(nil), // 0: google.protobuf.DoubleValue + (*FloatValue)(nil), // 1: google.protobuf.FloatValue + (*Int64Value)(nil), // 2: google.protobuf.Int64Value + (*UInt64Value)(nil), // 3: google.protobuf.UInt64Value + (*Int32Value)(nil), // 4: google.protobuf.Int32Value + (*UInt32Value)(nil), // 5: google.protobuf.UInt32Value + (*BoolValue)(nil), // 6: google.protobuf.BoolValue + (*StringValue)(nil), // 7: google.protobuf.StringValue + (*BytesValue)(nil), // 8: google.protobuf.BytesValue +} +var file_google_protobuf_wrappers_proto_depIdxs = []int32{ + 0, // [0:0] is the sub-list for method output_type + 0, // [0:0] is the sub-list for method input_type + 0, // [0:0] is the sub-list for extension type_name + 0, // [0:0] is the sub-list for extension extendee + 0, // [0:0] is the sub-list for field type_name +} + +func init() { file_google_protobuf_wrappers_proto_init() } +func file_google_protobuf_wrappers_proto_init() { + if File_google_protobuf_wrappers_proto != nil { + return + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: unsafe.Slice(unsafe.StringData(file_google_protobuf_wrappers_proto_rawDesc), len(file_google_protobuf_wrappers_proto_rawDesc)), + NumEnums: 0, + NumMessages: 9, + NumExtensions: 0, + NumServices: 0, + }, + GoTypes: file_google_protobuf_wrappers_proto_goTypes, + DependencyIndexes: file_google_protobuf_wrappers_proto_depIdxs, + MessageInfos: file_google_protobuf_wrappers_proto_msgTypes, + }.Build() + File_google_protobuf_wrappers_proto = out.File + file_google_protobuf_wrappers_proto_goTypes = nil + file_google_protobuf_wrappers_proto_depIdxs = nil +} diff --git a/vendor/modules.txt b/vendor/modules.txt index 4c2d99614..3adc1efa7 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -1,6 +1,10 @@ # github.com/Masterminds/semver/v3 v3.4.0 ## explicit; go 1.21 github.com/Masterminds/semver/v3 +# github.com/andybalholm/brotli v1.1.1 +## explicit; go 1.13 +github.com/andybalholm/brotli +github.com/andybalholm/brotli/matchfinder # github.com/beorn7/perks v1.0.1 ## explicit; go 1.11 github.com/beorn7/perks/quantile @@ -164,6 +168,21 @@ github.com/jpillora/sizestr # github.com/json-iterator/go v1.1.12 ## explicit; go 1.12 github.com/json-iterator/go +# github.com/klauspost/compress v1.18.6 +## explicit; go 1.24 +github.com/klauspost/compress +github.com/klauspost/compress/flate +github.com/klauspost/compress/fse +github.com/klauspost/compress/gzip +github.com/klauspost/compress/huff0 +github.com/klauspost/compress/internal/cpuinfo +github.com/klauspost/compress/internal/le +github.com/klauspost/compress/internal/race +github.com/klauspost/compress/internal/snapref +github.com/klauspost/compress/s2 +github.com/klauspost/compress/snappy +github.com/klauspost/compress/zstd +github.com/klauspost/compress/zstd/internal/xxhash # github.com/lucasb-eyer/go-colorful v1.3.0 ## explicit; go 1.12 github.com/lucasb-eyer/go-colorful @@ -192,13 +211,14 @@ github.com/mxk/go-flowrate/flowrate # github.com/navidys/tvxwidgets v0.14.0 ## explicit; go 1.25.6 github.com/navidys/tvxwidgets -# github.com/netobserv/flowlogs-pipeline v1.11.5-community +# github.com/netobserv/flowlogs-pipeline v1.11.5-community => ../flowlogs-pipeline ## explicit; go 1.25.7 github.com/netobserv/flowlogs-pipeline/pkg/api github.com/netobserv/flowlogs-pipeline/pkg/config github.com/netobserv/flowlogs-pipeline/pkg/pipeline/utils github.com/netobserv/flowlogs-pipeline/pkg/pipeline/write/grpc github.com/netobserv/flowlogs-pipeline/pkg/pipeline/write/grpc/genericmap +github.com/netobserv/flowlogs-pipeline/pkg/schema github.com/netobserv/flowlogs-pipeline/pkg/utils # github.com/netobserv/netobserv-ebpf-agent v1.11.5-community ## explicit; go 1.25.7 @@ -244,6 +264,50 @@ github.com/onsi/gomega/types # github.com/ovn-org/ovn-kubernetes/go-controller v0.0.0-20250227173154-57a2590a1d16 ## explicit; go 1.22.0 github.com/ovn-org/ovn-kubernetes/go-controller/observability-lib/model +# github.com/parquet-go/bitpack v1.0.0 +## explicit; go 1.24.0 +github.com/parquet-go/bitpack +github.com/parquet-go/bitpack/unsafecast +# github.com/parquet-go/jsonlite v1.0.0 +## explicit; go 1.23 +github.com/parquet-go/jsonlite +# github.com/parquet-go/parquet-go v0.30.1 +## explicit; go 1.24.9 +github.com/parquet-go/parquet-go +github.com/parquet-go/parquet-go/bloom +github.com/parquet-go/parquet-go/bloom/xxhash +github.com/parquet-go/parquet-go/compress +github.com/parquet-go/parquet-go/compress/brotli +github.com/parquet-go/parquet-go/compress/gzip +github.com/parquet-go/parquet-go/compress/lz4 +github.com/parquet-go/parquet-go/compress/snappy +github.com/parquet-go/parquet-go/compress/uncompressed +github.com/parquet-go/parquet-go/compress/zstd +github.com/parquet-go/parquet-go/deprecated +github.com/parquet-go/parquet-go/encoding +github.com/parquet-go/parquet-go/encoding/bitpacked +github.com/parquet-go/parquet-go/encoding/bytestreamsplit +github.com/parquet-go/parquet-go/encoding/delta +github.com/parquet-go/parquet-go/encoding/plain +github.com/parquet-go/parquet-go/encoding/rle +github.com/parquet-go/parquet-go/encoding/thrift +github.com/parquet-go/parquet-go/format +github.com/parquet-go/parquet-go/hashprobe +github.com/parquet-go/parquet-go/hashprobe/aeshash +github.com/parquet-go/parquet-go/hashprobe/wyhash +github.com/parquet-go/parquet-go/internal/bytealg +github.com/parquet-go/parquet-go/internal/debug +github.com/parquet-go/parquet-go/internal/memory +github.com/parquet-go/parquet-go/internal/unsafecast +github.com/parquet-go/parquet-go/sparse +github.com/parquet-go/parquet-go/variant +# github.com/pierrec/lz4/v4 v4.1.22 +## explicit; go 1.14 +github.com/pierrec/lz4/v4 +github.com/pierrec/lz4/v4/internal/lz4block +github.com/pierrec/lz4/v4/internal/lz4errors +github.com/pierrec/lz4/v4/internal/lz4stream +github.com/pierrec/lz4/v4/internal/xxh32 # github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 ## explicit github.com/pmezard/go-difflib/difflib @@ -295,6 +359,12 @@ github.com/spf13/pflag ## explicit; go 1.17 github.com/stretchr/testify/assert github.com/stretchr/testify/assert/yaml +github.com/stretchr/testify/require +# github.com/twpayne/go-geom v1.6.1 +## explicit; go 1.22 +github.com/twpayne/go-geom +github.com/twpayne/go-geom/encoding/wkb +github.com/twpayne/go-geom/encoding/wkbcommon # github.com/vladimirvivien/gexe v0.5.0 ## explicit; go 1.23 github.com/vladimirvivien/gexe @@ -366,6 +436,7 @@ golang.org/x/oauth2/internal golang.org/x/sync/errgroup # golang.org/x/sys v0.46.0 ## explicit; go 1.25.0 +golang.org/x/sys/cpu golang.org/x/sys/plan9 golang.org/x/sys/unix golang.org/x/sys/windows @@ -422,7 +493,7 @@ golang.org/x/tools/internal/versions # google.golang.org/genproto/googleapis/rpc v0.0.0-20260526163538-3dc84a4a5aaa ## explicit; go 1.25.0 google.golang.org/genproto/googleapis/rpc/status -# google.golang.org/grpc v1.81.1 +# google.golang.org/grpc v1.82.0 ## explicit; go 1.25.0 google.golang.org/grpc google.golang.org/grpc/attributes @@ -443,13 +514,13 @@ google.golang.org/grpc/credentials/insecure google.golang.org/grpc/encoding google.golang.org/grpc/encoding/internal google.golang.org/grpc/encoding/proto +google.golang.org/grpc/experimental/balancer/weight google.golang.org/grpc/experimental/stats google.golang.org/grpc/grpclog google.golang.org/grpc/grpclog/internal google.golang.org/grpc/internal google.golang.org/grpc/internal/backoff google.golang.org/grpc/internal/balancer/gracefulswitch -google.golang.org/grpc/internal/balancer/weight google.golang.org/grpc/internal/balancerload google.golang.org/grpc/internal/binarylog google.golang.org/grpc/internal/buffer @@ -475,6 +546,7 @@ google.golang.org/grpc/internal/stats google.golang.org/grpc/internal/status google.golang.org/grpc/internal/syscall google.golang.org/grpc/internal/transport +google.golang.org/grpc/internal/transport/internal google.golang.org/grpc/internal/transport/networktype google.golang.org/grpc/internal/transport/readyreader google.golang.org/grpc/keepalive @@ -530,7 +602,9 @@ google.golang.org/protobuf/types/descriptorpb google.golang.org/protobuf/types/gofeaturespb google.golang.org/protobuf/types/known/anypb google.golang.org/protobuf/types/known/durationpb +google.golang.org/protobuf/types/known/structpb google.golang.org/protobuf/types/known/timestamppb +google.golang.org/protobuf/types/known/wrapperspb # gopkg.in/evanphx/json-patch.v4 v4.13.0 ## explicit gopkg.in/evanphx/json-patch.v4 @@ -890,3 +964,4 @@ sigs.k8s.io/structured-merge-diff/v6/value # sigs.k8s.io/yaml v1.6.0 ## explicit; go 1.22 sigs.k8s.io/yaml +# github.com/netobserv/flowlogs-pipeline => ../flowlogs-pipeline From 4d85581cef9c4a167f6c8b4348c0468c8d040308 Mon Sep 17 00:00:00 2001 From: Julien Pinsonneau Date: Mon, 20 Jul 2026 14:30:41 +0200 Subject: [PATCH 2/2] feat: local multi-format sinks and S3 Parquet export for flows Default local capture to json,sqlite (parquet opt-in via --format) and add optional Hive-partitioned Parquet export to S3-compatible storage via the embedded FLP encode.s3 stage, aligned with operator/console. Co-authored-by: Cursor --- README.md | 89 +++++++++ cmd/display.go | 33 +++- cmd/flow_capture.go | 192 +++++++++++++++---- cmd/flow_display.go | 4 + cmd/flow_parquet.go | 272 +++++++++++++++++++++++++++ cmd/flow_parquet_test.go | 330 ++++++++++++++++++++++++++++++++ cmd/metric_capture.go | 12 +- cmd/metric_display.go | 3 + cmd/packet_capture.go | 43 +++-- cmd/root.go | 34 +++- commands/netobserv | 9 +- e2e/script_test.go | 5 + e2e/yaml_test.go | 279 +++++++++++++++++++++++++-- scripts/functions.sh | 395 ++++++++++++++++++++++++++++++++++++++- scripts/generate-doc.sh | 16 +- scripts/help.sh | 54 ++++++ 16 files changed, 1687 insertions(+), 83 deletions(-) create mode 100644 cmd/flow_parquet.go create mode 100644 cmd/flow_parquet_test.go diff --git a/README.md b/README.md index 974602789..7a72a077d 100644 --- a/README.md +++ b/README.md @@ -122,6 +122,95 @@ sqlite> SELECT DnsLatencyMs, DnsFlagsResponseCode, DnsId, DstAddr, DstPort, Inte or `dbeaver`: ![dbeaver](./img/dbeaver.png) +### Local output formats + +By default, flow capture writes **JSON + SQLite** under `output/flow/` (`--format=json,sqlite`). Parquet is **not** written unless you include `parquet` in `--format`. + +`--format` is a **non-exclusive** list (comma-separated and/or repeatable): + +| Flag | Writes | +|------|--------| +| _(omitted)_ / `--format=json,sqlite` | JSON + SQLite (default) | +| `--format=parquet` | Parquet only | +| `--format=json,parquet` | JSON + Parquet | +| `--format=json,sqlite,parquet` | all three | +| `--format=json --format=parquet` | same as `json,parquet` | + +Invalid tokens fail with a clear error. Hive-partitioned Parquet (schema v1, metadata `netobserv.parquet.version = 1`) lands at: + +```text +./output/flow//cluster_id=cli/year=YYYY/month=MM/day=DD/hour=HH/part-cli-….parquet +``` + +**Important:** the collector runs **inside a cluster pod** (`kubectl run` / `oc run`), not as your host binary. Host `./output/flow/` stays empty until files are copied from the pod (same `--copy` prompt as other formats; use `--copy=true` / `--copy=false` to skip the prompt). You must run a **collector image that includes parquet support** (rebuild/push your own tag — `quay.io/netobserv/network-observability-cli:main` will not have unreleased changes): + +```bash +IMAGE_ORG= VERSION=dev make image-build +# push/manifest as needed, then: +NETOBSERV_COLLECTOR_IMAGE=quay.io//network-observability-cli:dev \ + kubectl netobserv flows --format=parquet +``` + +During capture, `Capture size` tracks inbound flow bytes in memory for the TUI. Parquet parts are flushed every ~2s and again on exit. + +```bash +# Default: JSON + SQLite only (no parquet) +kubectl netobserv flows + +# Parquet only: +kubectl netobserv flows --format=parquet + +# JSON + Parquet, plus optional S3 export: +kubectl netobserv flows --format=json,parquet \ + --s3-endpoint=https://s3.example.com \ + --s3-bucket= \ + --s3-account= +``` + +Example with DuckDB: + +```bash +duckdb -c "SELECT SrcK8S_Namespace, sum(Bytes) FROM read_parquet('output/flow/**/*.parquet') GROUP BY 1 ORDER BY 2 DESC LIMIT 20;" +``` + +### S3 Parquet export (optional) + +In addition to local sinks under `output/flow/`, you can opt in to export the same enriched flows as **Hive-partitioned Parquet** to any S3-compatible store. This uses the embedded agent FLP `encode.s3` stage (`format: parquet`), matching the NetObserv operator / console layout: + +```text +s3:////cluster_id=/year=YYYY/month=MM/day=DD/hour=HH/part-….parquet +``` + +**Credentials:** do not pass secret keys as CLI flags (shell history / `ps`). Prefer environment variables, or a credentials file / Kubernetes Secret. + +```bash +export NETOBSERV_S3_ACCESS_KEY= +export NETOBSERV_S3_SECRET_KEY= + +kubectl netobserv flows \ + --s3-endpoint=https://s3.example.com \ + --s3-bucket= \ + --s3-account= \ + --s3-prefix=netobserv + +# Or AWS-style env names: +# export AWS_ACCESS_KEY_ID= AWS_SECRET_ACCESS_KEY= + +# Or a credentials file (YAML/JSON with accessKeyId/secretAccessKey, or AWS shared credentials): +# kubectl netobserv flows --s3-endpoint=... --s3-bucket=... --s3-credentials-file=./s3-creds.yaml + +# Or a Secret already in the CLI namespace (keys: accessKeyId, secretAccessKey): +# kubectl netobserv flows --s3-endpoint=... --s3-bucket=... --s3-secret=netobserv-s3-creds +``` + +Local JSON/SQLite capture stays enabled by default (`--format=json,sqlite`); S3 is an additional sink independent of local `--format`. Include `parquet` in `--format` for local Parquet. + +S3 is configured on the **agent** DaemonSet via `FLP_CONFIG` (direct-flp `encode.s3`), not as collector `--options`. Missing credentials fail the CLI before deploy. Default flush interval is `15s` (capped by `--max-time`); agents also flush pending Parquet parts on SIGTERM. Use an agent image with FLP S3 Parquet support, e.g.: + +```bash +export NETOBSERV_AGENT_IMAGE=quay.io/jpinsonn/netobserv-ebpf-agent:s3-flowbuffer +export NETOBSERV_COLLECTOR_IMAGE=quay.io/jpinsonn/network-observability-cli:s3-flowbuffer +``` ### Packet Capture diff --git a/cmd/display.go b/cmd/display.go index 90e743d3a..6b5204349 100644 --- a/cmd/display.go +++ b/cmd/display.go @@ -2,10 +2,14 @@ package cmd import ( "fmt" + "io" + "os" + "path/filepath" "time" "github.com/jpillora/sizestr" "github.com/rivo/tview" + "github.com/sirupsen/logrus" ) const ( @@ -157,6 +161,32 @@ func updateStatusTexts() { sizeText.SetText(getSizeText()) } +// redirectLogsAwayFromTTY sends logrus output to a file while the TUI owns the +// terminal. kubectl/oc exec -it merges stderr into the same TTY as tview, so +// periodic Info logs (e.g. parquet flush) otherwise corrupt the screen. +// When --loglevel=debug|trace, logs stay on stderr for live troubleshooting. +func redirectLogsAwayFromTTY() (restore func()) { + if log.IsLevelEnabled(logrus.DebugLevel) { + return func() {} + } + prev := log.Out + dir := outputRoot + if err := os.MkdirAll(dir, 0700); err != nil { + log.SetOutput(io.Discard) + return func() { log.SetOutput(prev) } + } + f, err := os.OpenFile(filepath.Join(dir, "collector-ui.log"), os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0600) + if err != nil { + log.SetOutput(io.Discard) + return func() { log.SetOutput(prev) } + } + log.SetOutput(f) + return func() { + log.SetOutput(prev) + _ = f.Close() + } +} + func hearbeat() { for { if captureEnded { @@ -170,7 +200,8 @@ func hearbeat() { updateTableAndSuggestions() } - // refresh + // Partial redraw: updateStatusTexts / table content only — do not rebuild + // the widget tree (updateScreen) on the size tick. if app != nil { app.Draw() } diff --git a/cmd/flow_capture.go b/cmd/flow_capture.go index 0b190378c..546e142c5 100644 --- a/cmd/flow_capture.go +++ b/cmd/flow_capture.go @@ -1,7 +1,10 @@ package cmd import ( + "database/sql" "encoding/json" + "fmt" + "os" "strings" "time" @@ -24,12 +27,62 @@ var flowCmd = &cobra.Command{ func runFlowCapture(_ *cobra.Command, _ []string) { capture = Flow showCount = defaultFlowShowCount - if isBackground { - go backgroundHearbeat() // show table periodically in background + + done := make(chan struct{}) + go func() { + defer close(done) startFlowCollector() - } else { - go startFlowCollector() - createFlowDisplay() + }() + + // Always enforce maxtime even when no flows arrive (maxTime was previously + // only checked inside the receive loop). + go enforceTimeLimit() + + if isBackground { + go backgroundHearbeat() + <-done + return + } + + uiStarted := time.Now() + createFlowDisplay() + + // Previous bug: always calling requestCollectorStop() after createFlowDisplay() + // aborted the collector when the TUI failed to start (no TTY / kubectl quirks), + // leaving an empty output/flow// directory and Capture size 0. + // Also treat an instant successful Run() return the same way (TTY half-broken). + uiTooShort := errAdvancedDisplay == nil && time.Since(uiStarted) < 500*time.Millisecond + if errAdvancedDisplay != nil || uiTooShort { + reason := errAdvancedDisplay + if reason == nil { + reason = fmt.Errorf("UI exited immediately") + } + log.Warnf("UI unavailable (%v); continuing capture until --maxtime, --maxbytes, or signal", reason) + <-done + return + } + + // TUI exited (Ctrl-C / quit): stop collector and wait so parquet Close/Flush runs + // before the process exits and before shell copyOutput. + requestCollectorStop() + <-done +} + +// enforceTimeLimit stops the collector when --maxtime is reached, even if no +// flows have been received yet (needed when the receive-loop check never runs). +func enforceTimeLimit() { + timer := time.NewTimer(maxTime) + defer timer.Stop() + select { + case <-timer.C: + log.Infof("Capture reached %s, exiting now...", maxTime) + if exit := onLimitReached(); exit || isBackground { + requestCollectorStop() + } + if app != nil { + app.Stop() + } + case <-collectorStopCh: } } @@ -43,17 +96,51 @@ func startFlowCollector() { ":", "") // get rid of offensive colons } - // Create a text file to receive json chunks; the file will be fixed and renamed as json later, when pulled in shell. - f, err := createOutputFile("flow", filename+".txt") + formats, err := parseOutputFormats(options) if err != nil { - log.Fatalf("Creating output file failed: %v", err) + log.Fatalf("Invalid --format: %v", err) + } + log.Infof("Local output format(s): %s", formats) + + var ( + f *os.File + db *sql.DB + pq *parquetFlowWriter + ) + + if formats.Parquet { + pq, err = newParquetFlowWriter(filename, defaultParquetBatchSize) + if err != nil { + log.Fatalf("Creating parquet writer failed: %v", err) + } + defer func() { + if closeErr := pq.Close(); closeErr != nil { + log.Errorf("Closing parquet writer: %v", closeErr) + } else if n := pq.BytesReceived(); n > 0 { + log.Infof("Parquet capture complete: %s inbound, %s on disk under %s/flow/%s/", + sizestr.ToString(n), sizestr.ToString(pq.BytesWritten()), outputRoot, filename) + } else { + log.Warnf("Parquet capture ended with 0 flows under %s/flow/%s/", outputRoot, filename) + } + }() + log.Infof("Writing Parquet schema v1 under %s/flow/%s/ (Hive layout; flushed every %s and on exit)", + outputRoot, filename, parquetFlushInterval) } - defer f.Close() - log.Debugf("Created flow logs txt file: %s", f.Name()) - // Initialize sqlite DB - db := initFlowDB(filename) - log.Debug("Initialized database") + if formats.JSON { + // Create a text file to receive json chunks; the file will be fixed and renamed as json later, when pulled in shell. + f, err = createOutputFile("flow", filename+".txt") + if err != nil { + log.Fatalf("Creating output file failed: %v", err) + } + defer f.Close() + log.Debugf("Created flow logs txt file: %s", f.Name()) + } + + if formats.SQLite { + db = initFlowDB(filename) + log.Debug("Initialized database") + } flowPackets := make(chan *genericmap.Flow, 100) collector, err := grpc.StartCollector(port, flowPackets) @@ -65,51 +152,87 @@ func startFlowCollector() { collectorStarted = true go func() { - <-utils.ExitChannel() + select { + case <-collectorStopCh: + case <-utils.ExitChannel(): + } log.Debug("Ending collector") close(flowPackets) collector.Close() - db.Close() + if db != nil { + db.Close() + } log.Debug("Done") }() + // Flush buffered parquet periodically so short captures still produce files + // even if the process is later killed before a full batch. + if pq != nil { + go func() { + ticker := time.NewTicker(parquetFlushInterval) + defer ticker.Stop() + for { + select { + case <-collectorStopCh: + // Final Close is handled by defer on startFlowCollector. + return + case <-ticker.C: + if flushErr := pq.Flush(); flushErr != nil { + log.Errorf("Periodic parquet flush: %v", flushErr) + } + } + } + }() + } + log.Debug("Ready ! Waiting for flows...") for fp := range flowPackets { if !captureStarted { log.Debugf("Received first %d flows", len(flowPackets)) } - if stopReceived { - log.Debug("Stop received") - return - } + inbound := int64(len(fp.GenericMap.Value)) + // parse and display flow async go parseGenericMapAndAppendFlow(fp.GenericMap.Value) - // Write flows to sqlite DB - err = queryFlowDB(fp.GenericMap.Value, db) - if err != nil { - log.Error("Error while writing to DB:", err.Error()) - } - if !captureStarted { - log.Debug("Wrote flows to DB") + if pq != nil { + if err = pq.AddJSON(fp.GenericMap.Value); err != nil { + log.Error("Error while writing parquet:", err.Error()) + } + if !captureStarted { + log.Debug("Wrote flows to parquet buffer") + } } - // append new line between each record to read file easilly - bytes, err := f.Write(append(fp.GenericMap.Value, []byte(",\n")...)) - if err != nil { - log.Error(err) - return + if db != nil { + if err = queryFlowDB(fp.GenericMap.Value, db); err != nil { + log.Error("Error while writing to DB:", err.Error()) + } + if !captureStarted { + log.Debug("Wrote flows to DB") + } } - if !captureStarted { - log.Debug("Wrote flows to json") + + if f != nil { + if _, writeErr := f.Write(append(fp.GenericMap.Value, []byte(",\n")...)); writeErr != nil { + log.Error(writeErr) + return + } + if !captureStarted { + log.Debug("Wrote flows to json") + } } + // Count inbound JSON bytes once (not per sink) so Capture size / --maxbytes + // stay correct for any format combination. + totalBytes += inbound + // terminate capture if max bytes reached - totalBytes += int64(bytes) if totalBytes > maxBytes { if exit := onLimitReached(); exit { log.Infof("Capture reached %s, exiting now...", sizestr.ToString(maxBytes)) + requestCollectorStop() return } } @@ -120,6 +243,7 @@ func startFlowCollector() { if duration > maxTime { if exit := onLimitReached(); exit { log.Infof("Capture reached %s, exiting now...", maxTime) + requestCollectorStop() return } } diff --git a/cmd/flow_display.go b/cmd/flow_display.go index 87047cb67..a5098b48c 100644 --- a/cmd/flow_display.go +++ b/cmd/flow_display.go @@ -55,6 +55,9 @@ var ( ) func createFlowDisplay() { + restoreLogs := redirectLogsAwayFromTTY() + defer restoreLogs() + focus = "inputField" app = tview.NewApplication(). SetInputCapture(func(event *tcell.EventKey) *tcell.EventKey { @@ -62,6 +65,7 @@ func createFlowDisplay() { switch event.Key() { case tcell.KeyCtrlC: log.Info("Ctrl-C pressed, exiting program.") + requestCollectorStop() if app != nil { app.Stop() } diff --git a/cmd/flow_parquet.go b/cmd/flow_parquet.go new file mode 100644 index 000000000..ebc7c15bb --- /dev/null +++ b/cmd/flow_parquet.go @@ -0,0 +1,272 @@ +package cmd + +import ( + "encoding/json" + "fmt" + "os" + "path/filepath" + "strings" + "sync" + "sync/atomic" + "time" + + "github.com/netobserv/flowlogs-pipeline/pkg/config" + "github.com/netobserv/flowlogs-pipeline/pkg/schema" +) + +const ( + outputFormatJSON = "json" + outputFormatSQLite = "sqlite" + outputFormatParquet = "parquet" + + defaultParquetBatchSize = 5000 + parquetFlushInterval = 2 * time.Second + parquetStreamID = "cli" +) + +// outputFormats is a non-exclusive set of local flow sinks. +// Default (when --format is omitted) is json + sqlite. +type outputFormats struct { + JSON bool + SQLite bool + Parquet bool +} + +func (f outputFormats) String() string { + parts := make([]string, 0, 3) + if f.JSON { + parts = append(parts, outputFormatJSON) + } + if f.SQLite { + parts = append(parts, outputFormatSQLite) + } + if f.Parquet { + parts = append(parts, outputFormatParquet) + } + if len(parts) == 0 { + return "" + } + return strings.Join(parts, ",") +} + +func (f outputFormats) empty() bool { + return !f.JSON && !f.SQLite && !f.Parquet +} + +func defaultOutputFormats() outputFormats { + return outputFormats{JSON: true, SQLite: true} +} + +// outputRoot is the directory under which flow/pcap captures are written. +// Overridable in tests. +var outputRoot = "./output" + +// parseOutputFormats returns the local flows output format set from --options +// (pipe-separated). Supports comma-separated and repeated format= values, e.g.: +// +// format=json,sqlite +// format=parquet +// format=json|format=parquet +// +// Default when format is omitted: json,sqlite (never parquet). +// Unknown tokens return an error. +func parseOutputFormats(opts string) (outputFormats, error) { + var ( + found bool + result outputFormats + ) + for _, part := range strings.Split(opts, "|") { + part = strings.TrimSpace(part) + if !strings.HasPrefix(part, "format=") { + continue + } + found = true + raw := strings.TrimPrefix(part, "format=") + if strings.TrimSpace(raw) == "" { + return outputFormats{}, fmt.Errorf("missing value for --format (json|sqlite|parquet)") + } + for _, tok := range strings.Split(raw, ",") { + tok = strings.ToLower(strings.TrimSpace(tok)) + if tok == "" { + continue + } + switch tok { + case outputFormatJSON: + result.JSON = true + case outputFormatSQLite: + result.SQLite = true + case outputFormatParquet: + result.Parquet = true + default: + return outputFormats{}, fmt.Errorf("invalid --format token %q (use json, sqlite, and/or parquet)", tok) + } + } + } + if !found || result.empty() { + return defaultOutputFormats(), nil + } + return result, nil +} + +// parseOutputFormat is kept for callers that only need a single primary label. +// Prefer parseOutputFormats for sink selection. +func parseOutputFormat(opts string) string { + f, err := parseOutputFormats(opts) + if err != nil { + log.Warnf("%v; using json,sqlite", err) + return defaultOutputFormats().String() + } + return f.String() +} + +// parquetFlowWriter batches flows and writes Hive-partitioned Parquet parts under output/flow//. +type parquetFlowWriter struct { + mu sync.Mutex + session string + baseDir string + batchSize int + pending []config.GenericMap + seq int64 + bytes int64 + inbound int64 + closed atomic.Bool +} + +func newParquetFlowWriter(session string, batchSize int) (*parquetFlowWriter, error) { + if batchSize <= 0 { + batchSize = defaultParquetBatchSize + } + base := filepath.Join(outputRoot, "flow", session) + if err := os.MkdirAll(base, 0700); err != nil { + return nil, fmt.Errorf("creating parquet output dir: %w", err) + } + return &parquetFlowWriter{ + session: session, + baseDir: base, + batchSize: batchSize, + pending: make([]config.GenericMap, 0, batchSize), + }, nil +} + +func (w *parquetFlowWriter) AddJSON(raw []byte) error { + var gm config.GenericMap + if err := json.Unmarshal(raw, &gm); err != nil { + return err + } + return w.add(gm, int64(len(raw))) +} + +func (w *parquetFlowWriter) Add(gm config.GenericMap) error { + return w.add(gm, 0) +} + +func (w *parquetFlowWriter) add(gm config.GenericMap, inboundBytes int64) error { + if w.closed.Load() { + return fmt.Errorf("parquet writer already closed") + } + w.mu.Lock() + if inboundBytes > 0 { + w.inbound += inboundBytes + } + w.pending = append(w.pending, gm) + shouldFlush := len(w.pending) >= w.batchSize + w.mu.Unlock() + + // Flush outside the lock so encode/disk I/O does not stall the receive loop + // (and the TUI heartbeat) while a part is being written. + if shouldFlush { + return w.Flush() + } + return nil +} + +// Flush writes any buffered flows to a new parquet part. Safe to call periodically and on stop. +// Encode and file I/O run without holding the writer mutex so Add can continue buffering. +func (w *parquetFlowWriter) Flush() error { + batch, seq, ok := w.takePending() + if !ok { + return nil + } + + data, err := schema.EncodeParquetBytes(batch) + if err != nil { + w.restoreBatch(batch) + return fmt.Errorf("encoding parquet: %w", err) + } + + now := time.Now().UTC() + rel := filepath.Join( + fmt.Sprintf("cluster_id=%s", parquetStreamID), + fmt.Sprintf("year=%04d", now.Year()), + fmt.Sprintf("month=%02d", now.Month()), + fmt.Sprintf("day=%02d", now.Day()), + fmt.Sprintf("hour=%02d", now.Hour()), + fmt.Sprintf("part-%s-%08d.parquet", parquetStreamID, seq), + ) + full := filepath.Join(w.baseDir, rel) + if err := os.MkdirAll(filepath.Dir(full), 0700); err != nil { + w.restoreBatch(batch) + return err + } + if err := os.WriteFile(full, data, 0600); err != nil { + w.restoreBatch(batch) + return fmt.Errorf("writing parquet %s: %w", full, err) + } + + w.mu.Lock() + w.bytes += int64(len(data)) + w.mu.Unlock() + // Debug only: Info would spam stderr every ~2s and corrupt the interactive TUI. + log.Debugf("Wrote parquet part %s (%d bytes, %d flows)", full, len(data), len(batch)) + return nil +} + +// takePending drains the buffer and reserves a part sequence number under the lock. +func (w *parquetFlowWriter) takePending() (batch []config.GenericMap, seq int64, ok bool) { + w.mu.Lock() + defer w.mu.Unlock() + if len(w.pending) == 0 { + return nil, 0, false + } + batch = w.pending + seq = w.seq + w.seq++ + // Clear pending before encode/write so a failed flush can be retried with new flows + // without duplicating successfully-encoded rows on the next successful write. + w.pending = make([]config.GenericMap, 0, w.batchSize) + return batch, seq, true +} + +func (w *parquetFlowWriter) restoreBatch(batch []config.GenericMap) { + w.mu.Lock() + defer w.mu.Unlock() + w.pending = append(batch, w.pending...) +} + +// Close flushes remaining data and marks the writer closed. Idempotent. +func (w *parquetFlowWriter) Close() error { + if !w.closed.CompareAndSwap(false, true) { + // Already closed — still try a final flush in case of races. + return w.Flush() + } + return w.Flush() +} + +func (w *parquetFlowWriter) BytesWritten() int64 { + w.mu.Lock() + defer w.mu.Unlock() + return w.bytes +} + +// BytesReceived returns inbound JSON bytes counted before parquet parts are flushed. +func (w *parquetFlowWriter) BytesReceived() int64 { + w.mu.Lock() + defer w.mu.Unlock() + return w.inbound +} + +func (w *parquetFlowWriter) Pending() int { + w.mu.Lock() + defer w.mu.Unlock() + return len(w.pending) +} diff --git a/cmd/flow_parquet_test.go b/cmd/flow_parquet_test.go new file mode 100644 index 000000000..645180e23 --- /dev/null +++ b/cmd/flow_parquet_test.go @@ -0,0 +1,330 @@ +package cmd + +import ( + "bytes" + "os" + "path/filepath" + "testing" + "time" + + "github.com/netobserv/flowlogs-pipeline/pkg/config" + "github.com/netobserv/flowlogs-pipeline/pkg/schema" + parquet "github.com/parquet-go/parquet-go" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestParseOutputFormats(t *testing.T) { + def := defaultOutputFormats() + assert.True(t, def.JSON && def.SQLite && !def.Parquet) + + f, err := parseOutputFormats("") + require.NoError(t, err) + assert.Equal(t, def, f) + + f, err = parseOutputFormats("background=true") + require.NoError(t, err) + assert.Equal(t, def, f) + + f, err = parseOutputFormats("format=json,sqlite") + require.NoError(t, err) + assert.Equal(t, def, f) + + f, err = parseOutputFormats("format=json") + require.NoError(t, err) + assert.True(t, f.JSON && !f.SQLite && !f.Parquet) + + f, err = parseOutputFormats("format=parquet") + require.NoError(t, err) + assert.True(t, !f.JSON && !f.SQLite && f.Parquet) + + f, err = parseOutputFormats("format=json,parquet") + require.NoError(t, err) + assert.True(t, f.JSON && !f.SQLite && f.Parquet) + + f, err = parseOutputFormats("format=json,sqlite,parquet") + require.NoError(t, err) + assert.True(t, f.JSON && f.SQLite && f.Parquet) + + f, err = parseOutputFormats("background=true|format=parquet|copy=true") + require.NoError(t, err) + assert.True(t, f.Parquet && !f.JSON) + + // Repeatable format= flags merge + f, err = parseOutputFormats("format=json|format=parquet") + require.NoError(t, err) + assert.True(t, f.JSON && f.Parquet && !f.SQLite) + + _, err = parseOutputFormats("format=unknown") + require.Error(t, err) + assert.Contains(t, err.Error(), "invalid --format token") + + _, err = parseOutputFormats("format=json,bogus") + require.Error(t, err) + + assert.Equal(t, "json,sqlite", parseOutputFormat("")) + assert.Equal(t, "parquet", parseOutputFormat("format=parquet")) +} + +func TestParquetFlowWriter(t *testing.T) { + tmp := t.TempDir() + prev := outputRoot + outputRoot = tmp + defer func() { outputRoot = prev }() + + session := "test-parquet-session" + w, err := newParquetFlowWriter(session, 2) + require.NoError(t, err) + + require.NoError(t, w.Add(config.GenericMap{ + "TimeFlowStartMs": int64(1700000000000), + "TimeFlowEndMs": int64(1700000001000), + "SrcAddr": "10.0.0.1", + "DstAddr": "10.0.0.2", + "Bytes": int64(100), + "Packets": int64(1), + "SrcK8S_Namespace": "ns-a", + })) + assert.Equal(t, int64(0), w.BytesWritten(), "batch not full yet") + + require.NoError(t, w.Add(config.GenericMap{ + "TimeFlowStartMs": int64(1700000002000), + "TimeFlowEndMs": int64(1700000003000), + "SrcAddr": "10.0.0.3", + "DstAddr": "10.0.0.4", + "Bytes": int64(200), + "Packets": int64(2), + "SrcK8S_Namespace": "ns-b", + })) + assert.Greater(t, w.BytesWritten(), int64(0), "batch flushed") + + require.NoError(t, w.Flush()) + + var parts []string + err = filepath.Walk(filepath.Join(tmp, "flow", session), func(path string, info os.FileInfo, err error) error { + if err != nil { + return err + } + if !info.IsDir() && filepath.Ext(path) == ".parquet" { + parts = append(parts, path) + } + return nil + }) + require.NoError(t, err) + require.Len(t, parts, 1) + assert.Contains(t, parts[0], "cluster_id=cli") + assert.Contains(t, parts[0], "part-cli-") + + data, err := os.ReadFile(parts[0]) + require.NoError(t, err) + + file, err := parquet.OpenFile(bytes.NewReader(data), int64(len(data))) + require.NoError(t, err) + found := false + for _, kv := range file.Metadata().KeyValueMetadata { + if kv.Key == schema.ParquetVersionKey && kv.Value == schema.ParquetVersion { + found = true + break + } + } + assert.True(t, found, "expected netobserv.parquet.version metadata") + + rows, err := parquet.Read[schema.FlowRecordV1](bytes.NewReader(data), int64(len(data))) + require.NoError(t, err) + require.Len(t, rows, 2) + assert.Equal(t, "10.0.0.1", rows[0].SrcAddr) + assert.Equal(t, "ns-a", rows[0].SrcK8S_Namespace) +} + +func TestParquetFlowWriterAddJSON(t *testing.T) { + tmp := t.TempDir() + prev := outputRoot + outputRoot = tmp + defer func() { outputRoot = prev }() + + session := "test-parquet-json" + w, err := newParquetFlowWriter(session, 10) + require.NoError(t, err) + raw := []byte(`{"SrcAddr":"1.2.3.4","Bytes":42,"TimeFlowEndMs":1700000001000}`) + require.NoError(t, w.AddJSON(raw)) + require.NoError(t, w.Flush()) + assert.Greater(t, w.BytesWritten(), int64(0)) + assert.Equal(t, int64(len(raw)), w.BytesReceived()) +} + +// TestParquetFlowWriterFlushOnShutdown covers the interactive CLI case: fewer +// flows than batchSize, then Close (as on collector stop). Must produce a +// .parquet file with non-zero size under the session directory. +func TestParquetFlowWriterFlushOnShutdown(t *testing.T) { + tmp := t.TempDir() + prev := outputRoot + outputRoot = tmp + defer func() { outputRoot = prev }() + + const n = 3 + session := "test-parquet-shutdown" + w, err := newParquetFlowWriter(session, defaultParquetBatchSize) + require.NoError(t, err) + + for i := 0; i < n; i++ { + require.NoError(t, w.Add(config.GenericMap{ + "TimeFlowStartMs": int64(1700000000000 + i), + "TimeFlowEndMs": int64(1700000001000 + i), + "SrcAddr": "10.0.0.1", + "DstAddr": "10.0.0.2", + "Bytes": int64(100 + i), + "Packets": int64(1), + })) + } + assert.Equal(t, int64(0), w.BytesWritten(), "nothing on disk before flush") + assert.Equal(t, n, w.Pending()) + + require.NoError(t, w.Close()) + assert.Greater(t, w.BytesWritten(), int64(0)) + assert.Equal(t, 0, w.Pending(), "Close must drain pending buffer") + + var parts []string + err = filepath.Walk(filepath.Join(tmp, "flow", session), func(path string, info os.FileInfo, err error) error { + if err != nil { + return err + } + if !info.IsDir() && filepath.Ext(path) == ".parquet" { + parts = append(parts, path) + } + return nil + }) + require.NoError(t, err) + require.Len(t, parts, 1, "expected one parquet part after shutdown flush") + + data, err := os.ReadFile(parts[0]) + require.NoError(t, err) + rows, err := parquet.Read[schema.FlowRecordV1](bytes.NewReader(data), int64(len(data))) + require.NoError(t, err) + assert.Len(t, rows, n) +} + +// TestParquetConcurrentAddDuringFlush ensures encode/write does not hold the mutex +// long enough to block Add (receive-loop / TUI stall every periodic flush). +func TestParquetConcurrentAddDuringFlush(t *testing.T) { + tmp := t.TempDir() + prev := outputRoot + outputRoot = tmp + defer func() { outputRoot = prev }() + + session := "test-parquet-concurrent" + w, err := newParquetFlowWriter(session, 50) + require.NoError(t, err) + + for i := 0; i < 10; i++ { + require.NoError(t, w.Add(config.GenericMap{ + "SrcAddr": "10.0.0.1", "DstAddr": "10.0.0.2", + "Bytes": int64(i), "TimeFlowEndMs": int64(1700000001000 + i), + })) + } + + done := make(chan error, 1) + go func() { + done <- w.Flush() + }() + + // Adds while flush is in flight must succeed promptly (not wait on encode I/O). + deadline := time.After(2 * time.Second) + for i := 0; i < 20; i++ { + select { + case <-deadline: + t.Fatal("Add blocked too long during Flush") + default: + } + require.NoError(t, w.Add(config.GenericMap{ + "SrcAddr": "10.0.0.3", "DstAddr": "10.0.0.4", + "Bytes": int64(100 + i), "TimeFlowEndMs": int64(1700000002000 + i), + })) + } + require.NoError(t, <-done) + require.NoError(t, w.Close()) + assert.Greater(t, w.BytesWritten(), int64(0)) +} + +// TestParquetFlowWriterCloseMustFlush fails if stop skips Flush/Close while +// flows are still buffered (the bug that left empty output/flow//). +func TestParquetFlowWriterCloseMustFlush(t *testing.T) { + tmp := t.TempDir() + prev := outputRoot + outputRoot = tmp + defer func() { outputRoot = prev }() + + session := "test-parquet-skip-flush" + w, err := newParquetFlowWriter(session, defaultParquetBatchSize) + require.NoError(t, err) + + require.NoError(t, w.AddJSON([]byte( + `{"SrcAddr":"10.0.0.1","DstAddr":"10.0.0.2","Bytes":100,"TimeFlowEndMs":1700000001000}`, + ))) + assert.Equal(t, 1, w.Pending()) + assert.Equal(t, int64(0), w.BytesWritten()) + + // Simulate collector stop without Close → empty capture dir (regression). + partsBeforeClose := listParquetParts(t, filepath.Join(tmp, "flow", session)) + assert.Empty(t, partsBeforeClose, "buffered flows must not be on disk yet") + + require.NoError(t, w.Close()) + + parts := listParquetParts(t, filepath.Join(tmp, "flow", session)) + require.Len(t, parts, 1, "Close/Flush on stop must write parquet parts") + assert.Greater(t, w.BytesWritten(), int64(0)) + assert.Greater(t, w.BytesReceived(), int64(0)) +} + +func TestParquetPeriodicFlushWritesEarly(t *testing.T) { + tmp := t.TempDir() + prev := outputRoot + outputRoot = tmp + defer func() { outputRoot = prev }() + + session := "test-parquet-periodic" + w, err := newParquetFlowWriter(session, defaultParquetBatchSize) + require.NoError(t, err) + + require.NoError(t, w.Add(config.GenericMap{ + "SrcAddr": "10.0.0.1", "DstAddr": "10.0.0.2", + "Bytes": int64(1), "TimeFlowEndMs": int64(1700000001000), + })) + + // Mimic the 2s periodic flusher used by startFlowCollector. + done := make(chan struct{}) + go func() { + ticker := time.NewTicker(50 * time.Millisecond) + defer ticker.Stop() + for { + select { + case <-done: + return + case <-ticker.C: + _ = w.Flush() + } + } + }() + defer close(done) + + require.Eventually(t, func() bool { + return w.BytesWritten() > 0 + }, 2*time.Second, 20*time.Millisecond, "periodic Flush must write parts before batch is full") + + require.NoError(t, w.Close()) +} + +func listParquetParts(t *testing.T, root string) []string { + t.Helper() + var parts []string + err := filepath.Walk(root, func(path string, info os.FileInfo, err error) error { + if err != nil { + return err + } + if !info.IsDir() && filepath.Ext(path) == ".parquet" { + parts = append(parts, path) + } + return nil + }) + require.NoError(t, err) + return parts +} diff --git a/cmd/metric_capture.go b/cmd/metric_capture.go index f9a7de242..c1ee1b68a 100644 --- a/cmd/metric_capture.go +++ b/cmd/metric_capture.go @@ -35,10 +35,16 @@ func runMetricCapture(c *cobra.Command, _ []string) { updateGraphs(false) // initial update of graphs to have something to display if isBackground { startMetricCollector(c.Context()) - } else { - go startMetricCollector(c.Context()) - createMetricDisplay() + return } + done := make(chan struct{}) + go func() { + defer close(done) + startMetricCollector(c.Context()) + }() + createMetricDisplay() + requestCollectorStop() + <-done } func startMetricCollector(ctx context.Context) { diff --git a/cmd/metric_display.go b/cmd/metric_display.go index 44c77ea56..0ad013eb3 100644 --- a/cmd/metric_display.go +++ b/cmd/metric_display.go @@ -65,6 +65,9 @@ var ( ) func createMetricDisplay() { + restoreLogs := redirectLogsAwayFromTTY() + defer restoreLogs() + updateShowMetricCount() app = tview.NewApplication(). diff --git a/cmd/packet_capture.go b/cmd/packet_capture.go index 3b1cca1c9..bb6194d68 100644 --- a/cmd/packet_capture.go +++ b/cmd/packet_capture.go @@ -34,13 +34,34 @@ var ( func runPacketCapture(_ *cobra.Command, _ []string) { capture = Packet - if isBackground { - go backgroundHearbeat() // show table periodically in background + + done := make(chan struct{}) + go func() { + defer close(done) startPacketCollector() - } else { - go startPacketCollector() - createFlowDisplay() + }() + go enforceTimeLimit() + + if isBackground { + go backgroundHearbeat() + <-done + return } + + uiStarted := time.Now() + createFlowDisplay() + uiTooShort := errAdvancedDisplay == nil && time.Since(uiStarted) < 500*time.Millisecond + if errAdvancedDisplay != nil || uiTooShort { + reason := errAdvancedDisplay + if reason == nil { + reason = fmt.Errorf("UI exited immediately") + } + log.Warnf("UI unavailable (%v); continuing capture until --maxtime, --maxbytes, or signal", reason) + <-done + return + } + requestCollectorStop() + <-done } //nolint:cyclop @@ -79,7 +100,10 @@ func startPacketCollector() { collectorStarted = true go func() { - <-utils.ExitChannel() + select { + case <-collectorStopCh: + case <-utils.ExitChannel(): + } log.Debug("Ending collector") close(flowPackets) collector.Close() @@ -92,11 +116,6 @@ func startPacketCollector() { log.Debugf("Received first %d packets", len(flowPackets)) } - if stopReceived { - log.Debug("Stop received") - return - } - genericMap := config.GenericMap{} err := json.Unmarshal(fp.GenericMap.Value, &genericMap) if err != nil { @@ -127,6 +146,7 @@ func startPacketCollector() { if totalBytes > maxBytes { if exit := onLimitReached(); exit { log.Infof("Capture reached %s, exiting now...", sizestr.ToString(maxBytes)) + requestCollectorStop() return } } @@ -137,6 +157,7 @@ func startPacketCollector() { if int(duration) > int(maxTime) { if exit := onLimitReached(); exit { log.Infof("Capture reached %s, exiting now...", maxTime) + requestCollectorStop() return } } diff --git a/cmd/root.go b/cmd/root.go index e13812566..b5727169e 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -6,6 +6,7 @@ import ( "os" "os/exec" "os/signal" + "path/filepath" "strings" "sync" "syscall" @@ -56,8 +57,26 @@ var ( stopReceived = false useMocks = false isBackground = false + + collectorStopMu sync.Mutex + collectorStopped bool + collectorStopCh = make(chan struct{}) ) +// requestCollectorStop unblocks collectors waiting on flow/packet channels and +// sets stopReceived. Safe to call multiple times. Does not call os.Exit — callers +// must wait for in-flight work (e.g. parquet Flush) to finish before the process ends. +func requestCollectorStop() { + collectorStopMu.Lock() + defer collectorStopMu.Unlock() + if collectorStopped { + return + } + collectorStopped = true + stopReceived = true + close(collectorStopCh) +} + // Execute executes the root command. func Execute() error { return rootCmd.Execute() @@ -76,13 +95,16 @@ func init() { rootCmd.PersistentFlags().BoolVarP(&useMocks, "mock", "", false, "Use mock") c := make(chan os.Signal, 1) - signal.Notify(c, syscall.SIGTERM) + signal.Notify(c, syscall.SIGTERM, syscall.SIGINT) go func() { <-c - log.Info("Received SIGTERM; cleaning up...") - stopReceived = true - - os.Exit(0) + log.Info("Received shutdown signal; flushing and stopping collector...") + requestCollectorStop() + if app != nil { + app.Stop() + } + // Do not os.Exit here — that skips deferred Flush (critical for parquet). + // Collectors exit after collectorStopCh closes their input channels. }() // flow @@ -193,7 +215,7 @@ func onLimitReached() bool { // Create output file, preventing path traversal func createOutputFile(kind, filename string) (*os.File, error) { - base := "./output/" + kind + "/" + base := filepath.Join(outputRoot, kind) + string(os.PathSeparator) if err := os.MkdirAll(base, 0700); err != nil { return nil, err } diff --git a/commands/netobserv b/commands/netobserv index 8ba8f007d..e16a4d3a7 100755 --- a/commands/netobserv +++ b/commands/netobserv @@ -16,7 +16,7 @@ if [ -z "${runBackground+x}" ]; then runBackground="false"; fi # output as yaml only if [ -z "${outputYAML+x}" ]; then outputYAML="false"; fi # formated date for file names -if [ -z "${dateName+x}" ]; then dateName="$(date +"%Y_%m_%d_%I_%M")"; fi +if [ -z "${dateName+x}" ]; then dateName="$(date +"%Y_%m_%d_%H_%M")"; fi # options such as filters, background etc options="" @@ -157,8 +157,9 @@ case "$1" in esac function onExit() { + local code=$? cleanup - exit 0 + exit "$code" } trap onExit EXIT @@ -170,6 +171,10 @@ if [[ "$command" == "flows" || "$command" == "packets" || "$command" == "metrics optionStr="" for opt in "${options[@]}"; do opt="${opt#--}" # remove leading -- + # S3 connection/cred flags are applied to agent FLP_CONFIG only; keep them off the collector --options + case "$opt" in + s3-*) continue ;; + esac if [ -n "$optionStr" ]; then optionStr="$optionStr|$opt" else diff --git a/e2e/script_test.go b/e2e/script_test.go index cc7d2098c..3c4b1dedd 100644 --- a/e2e/script_test.go +++ b/e2e/script_test.go @@ -73,7 +73,12 @@ func TestSubcommandHelp(t *testing.T) { assert.Contains(t, plain, "Features:") assert.Contains(t, plain, "Filters:") assert.Contains(t, plain, "Options:") + assert.Contains(t, plain, "--format:") + assert.Contains(t, plain, "json,sqlite") + assert.Contains(t, plain, "parquet") assert.Contains(t, plain, "Examples:") + assert.Contains(t, plain, "netobserv flows --format=parquet") + assert.Contains(t, plain, "netobserv flows --format=json,parquet") }) t.Run("flows --help at any position", func(t *testing.T) { diff --git a/e2e/yaml_test.go b/e2e/yaml_test.go index ad604b5cf..56a89bd99 100644 --- a/e2e/yaml_test.go +++ b/e2e/yaml_test.go @@ -48,33 +48,34 @@ func TestFlowFiltersYAML(t *testing.T) { }, ).Assess("check generated yaml output", func(ctx context.Context, t *testing.T, cfg *envconf.Config) context.Context { - var yamls []string - dirPath := "output" assert.True(t, dirExists(dirPath), "directory %s not found", dirPath) + + var yamlStr string err := filepath.Walk(dirPath, func(path string, info os.FileInfo, err error) error { if err != nil { - fmt.Println(err) + return err } - - if !info.IsDir() { - if strings.Contains(path, "flows_capture") && filepath.Ext(path) == ".yml" { - yamls = append(yamls, path) - } + if info.IsDir() || filepath.Ext(path) != ".yml" || !strings.Contains(path, "flows_capture") { + return nil + } + yamlBytes, readErr := os.ReadFile(path) + if readErr != nil { + return readErr + } + content := string(yamlBytes) + // Prefer the filter-focused capture (not S3 export) + if strings.Contains(content, `"protocol": "TCP"`) && strings.Contains(content, `"port": 8080`) && + !strings.Contains(content, `"name": "s3-export"`) { + yamlStr = content } - return nil }) assert.Nil(t, err) - - // check yaml file - assert.Equal(t, 1, len(yamls)) - yamlBytes, err := os.ReadFile(yamls[0]) - assert.Nil(t, err) + assert.NotEmpty(t, yamlStr, "expected a flows_capture yaml with TCP/8080 filters") // check yamls parts - yamlStr := string(yamlBytes[:]) - yamls = strings.Split(yamlStr, "---") + yamls := strings.Split(yamlStr, "---") assert.Equal(t, 8, len(yamls)) // check yaml contents @@ -354,6 +355,252 @@ func TestMetricYAML(t *testing.T) { testCluster.TestEnv().Test(t, f1) } +func TestFlowS3ExportYAML(t *testing.T) { + f1 := features.New("flow s3 yaml").Setup( + func(ctx context.Context, t *testing.T, cfg *envconf.Config) context.Context { + t.Setenv("NETOBSERV_S3_ACCESS_KEY", "test-access-key") + t.Setenv("NETOBSERV_S3_SECRET_KEY", "test-secret-key") + + output, err := RunCommand(ylog, "commands/oc-netobserv", "flows", + "--s3-endpoint=http://minio.example:9000", + "--s3-bucket=netobserv-flows", + "--s3-account=e2e-cluster", + "--s3-prefix=netobserv", + "--s3-batch-size=100", + "--s3-write-timeout=10s", + "--max-time=1m", + "--yaml") + assert.Nil(t, err) + + err = os.WriteFile(path.Join("output", StartupDate+"-flowS3YAMLOutput"), []byte(output), 0666) + assert.Nil(t, err) + + assert.NotEmpty(t, output) + assert.Contains(t, output, "creating flow-capture agents") + assert.Contains(t, output, "S3 Parquet export enabled") + assert.Contains(t, output, "Check the generated YAML file in output folder") + // S3 is agent FLP_CONFIG only — not collector --options + assert.NotContains(t, output, `--options "s3-`) + + return ctx + }, + ).Assess("check S3 encode stage in agent FLP_CONFIG", + func(ctx context.Context, t *testing.T, cfg *envconf.Config) context.Context { + dirPath := "output" + assert.True(t, dirExists(dirPath), "directory %s not found", dirPath) + + var foundS3YAML bool + err := filepath.Walk(dirPath, func(path string, info os.FileInfo, err error) error { + if err != nil { + return err + } + if info.IsDir() || filepath.Ext(path) != ".yml" || !strings.Contains(path, "flows_capture") { + return nil + } + yamlBytes, readErr := os.ReadFile(path) + if readErr != nil { + return readErr + } + yamlStr := string(yamlBytes) + if !strings.Contains(yamlStr, `"name": "s3-export"`) { + return nil + } + foundS3YAML = true + assert.Contains(t, yamlStr, `"type": "s3"`) + assert.Contains(t, yamlStr, `"format": "parquet"`) + assert.Contains(t, yamlStr, `"endpoint": "http://minio.example:9000"`) + assert.Contains(t, yamlStr, `"bucket": "netobserv-flows"`) + assert.Contains(t, yamlStr, `"account": "e2e-cluster"`) + assert.Contains(t, yamlStr, `"prefix": "netobserv"`) + assert.Contains(t, yamlStr, `"batchSize": 100`) + assert.Contains(t, yamlStr, `"writeTimeout": "10s"`) + assert.Contains(t, yamlStr, `"accessKeyId": "test-access-key"`) + assert.Contains(t, yamlStr, `"secretAccessKey": "test-secret-key"`) + assert.Contains(t, yamlStr, `"follows": "enrich"`) + assert.Contains(t, yamlStr, `name: FLP_CONFIG`) + assert.Contains(t, Normalize(yamlStr), Normalize(`"grpc": { "targetHost": "collector.netobserv-cli.svc.cluster.local", "targetPort": 9999 }`)) + return nil + }) + assert.Nil(t, err) + assert.True(t, foundS3YAML, "expected a flows_capture yaml containing s3-export in FLP_CONFIG") + + return ctx + }, + ).Feature() + testCluster.TestEnv().Test(t, f1) +} + +func TestFlowS3MissingCredentialsYAML(t *testing.T) { + f1 := features.New("flow s3 missing credentials").Setup( + func(ctx context.Context, t *testing.T, cfg *envconf.Config) context.Context { + t.Setenv("NETOBSERV_S3_ACCESS_KEY", "") + t.Setenv("NETOBSERV_S3_SECRET_KEY", "") + t.Setenv("AWS_ACCESS_KEY_ID", "") + t.Setenv("AWS_SECRET_ACCESS_KEY", "") + + output, err := RunCommand(ylog, "commands/oc-netobserv", "flows", + "--s3-endpoint=http://minio.example:9000", + "--s3-bucket=netobserv-flows", + "--yaml") + assert.NotNil(t, err) + assert.Contains(t, output, "S3 export requires credentials") + assert.NotContains(t, output, "S3 Parquet export enabled") + return ctx + }, + ).Feature() + testCluster.TestEnv().Test(t, f1) +} + +func TestFlowParquetFormatYAML(t *testing.T) { + f1 := features.New("flow parquet format yaml").Setup( + func(ctx context.Context, t *testing.T, cfg *envconf.Config) context.Context { + output, err := RunCommand(ylog, "commands/oc-netobserv", "flows", + "--format=parquet", + "--yaml") + assert.Nil(t, err) + + err = os.WriteFile(path.Join("output", StartupDate+"-flowParquetFormatYAMLOutput"), []byte(output), 0666) + assert.Nil(t, err) + + assert.NotEmpty(t, output) + assert.Contains(t, output, "creating flow-capture agents") + assert.Contains(t, output, "Local output format(s): parquet") + assert.Contains(t, output, "Check the generated YAML file in output folder") + // Local format is collector-side; agent FLP still sends gRPC (no parquet encode stage) + assert.NotContains(t, output, "S3 Parquet export enabled") + + return ctx + }, + ).Assess("agent yaml unchanged except still has grpc send", + func(ctx context.Context, t *testing.T, cfg *envconf.Config) context.Context { + dirPath := "output" + assert.True(t, dirExists(dirPath), "directory %s not found", dirPath) + + var foundYAML bool + err := filepath.Walk(dirPath, func(path string, info os.FileInfo, err error) error { + if err != nil { + return err + } + if info.IsDir() || filepath.Ext(path) != ".yml" || !strings.Contains(path, "flows_capture") { + return nil + } + yamlBytes, readErr := os.ReadFile(path) + if readErr != nil { + return readErr + } + yamlStr := string(yamlBytes) + // Prefer the parquet-format run (no s3-export) + if strings.Contains(yamlStr, `"name": "s3-export"`) { + return nil + } + foundYAML = true + assert.NotContains(t, yamlStr, `"type": "s3"`) + assert.Contains(t, Normalize(yamlStr), Normalize(`"grpc": { "targetHost": "collector.netobserv-cli.svc.cluster.local", "targetPort": 9999 }`)) + return nil + }) + assert.Nil(t, err) + assert.True(t, foundYAML, "expected a flows_capture yaml without s3-export") + + return ctx + }, + ).Feature() + testCluster.TestEnv().Test(t, f1) +} + +func TestFlowMultiFormatYAML(t *testing.T) { + f1 := features.New("flow multi format yaml").Setup( + func(ctx context.Context, t *testing.T, cfg *envconf.Config) context.Context { + output, err := RunCommand(ylog, "commands/oc-netobserv", "flows", + "--format=json,parquet", + "--yaml") + assert.Nil(t, err) + assert.Contains(t, output, "Local output format(s): json,parquet") + assert.NotContains(t, output, "Auto-enabling --copy=true for parquet") + assert.NotContains(t, output, "S3 Parquet export enabled") + return ctx + }, + ).Feature() + testCluster.TestEnv().Test(t, f1) +} + +func TestFlowDefaultFormatNoParquetYAML(t *testing.T) { + f1 := features.New("flow default format no parquet").Setup( + func(ctx context.Context, t *testing.T, cfg *envconf.Config) context.Context { + output, err := RunCommand(ylog, "commands/oc-netobserv", "flows", "--yaml") + assert.Nil(t, err) + // Default path must not enable parquet + assert.NotContains(t, output, "Local output format(s):") + assert.NotContains(t, output, "Auto-enabling --copy=true for parquet") + assert.NotContains(t, output, "S3 Parquet export enabled") + return ctx + }, + ).Feature() + testCluster.TestEnv().Test(t, f1) +} + +func TestFlowInvalidFormatYAML(t *testing.T) { + f1 := features.New("flow invalid format yaml").Setup( + func(ctx context.Context, t *testing.T, cfg *envconf.Config) context.Context { + output, err := RunCommand(ylog, "commands/oc-netobserv", "flows", + "--format=json,bogus", + "--yaml") + assert.NotNil(t, err) + assert.Contains(t, output, "invalid --format token") + return ctx + }, + ).Feature() + testCluster.TestEnv().Test(t, f1) +} + +func TestFlowParquetFormatWithS3YAML(t *testing.T) { + f1 := features.New("flow parquet format with s3 yaml").Setup( + func(ctx context.Context, t *testing.T, cfg *envconf.Config) context.Context { + t.Setenv("NETOBSERV_S3_ACCESS_KEY", "test-access-key") + t.Setenv("NETOBSERV_S3_SECRET_KEY", "test-secret-key") + + output, err := RunCommand(ylog, "commands/oc-netobserv", "flows", + "--format=parquet", + "--s3-endpoint=http://minio.example:9000", + "--s3-bucket=netobserv-flows", + "--yaml") + assert.Nil(t, err) + + assert.Contains(t, output, "Local output format(s): parquet") + assert.Contains(t, output, "S3 Parquet export enabled") + + return ctx + }, + ).Assess("s3 encode still present when format=parquet", + func(ctx context.Context, t *testing.T, cfg *envconf.Config) context.Context { + var foundS3YAML bool + err := filepath.Walk("output", func(path string, info os.FileInfo, err error) error { + if err != nil { + return err + } + if info.IsDir() || filepath.Ext(path) != ".yml" || !strings.Contains(path, "flows_capture") { + return nil + } + yamlBytes, readErr := os.ReadFile(path) + if readErr != nil { + return readErr + } + yamlStr := string(yamlBytes) + if !strings.Contains(yamlStr, `"name": "s3-export"`) { + return nil + } + foundS3YAML = true + assert.Contains(t, yamlStr, `"format": "parquet"`) + assert.Contains(t, yamlStr, `"bucket": "netobserv-flows"`) + return nil + }) + assert.Nil(t, err) + assert.True(t, foundS3YAML, "expected s3-export when combining --format=parquet with --s3-*") + return ctx + }, + ).Feature() + testCluster.TestEnv().Test(t, f1) +} + func Normalize(str string) string { str = strings.ReplaceAll(str, "\n", "") str = strings.ReplaceAll(str, " ", "") diff --git a/scripts/functions.sh b/scripts/functions.sh index cd12179fa..1e1f15094 100755 --- a/scripts/functions.sh +++ b/scripts/functions.sh @@ -60,6 +60,26 @@ CONFIG_JSON_TEMP="config.json" CLUSTER_CONFIG="cluster-config-v1.yaml" NETWORK_CONFIG="cluster-network.yaml" +# Optional S3 Parquet export (opt-in; local JSON/SQLite remains default) +s3Endpoint="" +s3Bucket="" +s3Account="cli" +s3Prefix="" +s3Secure="" +s3BatchSize="5000" +# Default flush interval for CLI captures (operator default is 60s; too long for --max-time=1m). +s3WriteTimeout="15s" +s3WriteTimeoutSet="false" +s3CredentialsFile="" +s3Secret="" +s3AccessKeyId="" +s3SecretAccessKey="" +s3ExportEnabled="false" +# Local flows output formats (comma-separated): json, sqlite, parquet. +# Default when --format is omitted: json,sqlite (never parquet). +outputFormat="json,sqlite" +formatFlagSet=false + function loadYAMLs() { namespaceYAML=' namespaceYAMLContent @@ -409,12 +429,27 @@ function copyOutput() { if [[ ! -d ${OUTPUT_PATH} ]]; then mkdir -p ${OUTPUT_PATH} >/dev/null fi - ${K8S_CLI_BIN} cp -n "$namespace" collector:output ./output + # Collector writes under /output (WORKDIR /); kubectl cp path is container-absolute. + ${K8S_CLI_BIN} cp -n "$namespace" collector:/output ./output flowFile=$(find ./output -name "*txt" | sort | tail -1) if [[ -n "$flowFile" ]] ; then buildJSON "$flowFile" rm "$flowFile" fi + # Only report parquet when this run requested it. Do not count leftover + # *.parquet from prior sessions under ./output/flow/ (false "still exported"). + if [[ ",${outputFormat}," == *",parquet,"* ]]; then + parquetFiles=$(find ./output/flow -path '*/cluster_id=*/*.parquet' 2>/dev/null | wc -l | tr -d ' ') + if [[ "${parquetFiles}" -gt 0 ]]; then + echo "Copied ${parquetFiles} Parquet part(s) under ./output/flow/ (--format includes parquet)" + find ./output/flow -path '*/cluster_id=*/*.parquet' 2>/dev/null | head -20 + else + echo "WARNING: --format includes parquet but no .parquet files were copied." + echo " Check collector logs for flush errors, and rebuild the collector image if needed:" + echo " IMAGE_ORG= VERSION=dev make image-build image-push manifest-build manifest-push" + echo " NETOBSERV_COLLECTOR_IMAGE=quay.io//network-observability-cli:dev oc netobserv flows --format=parquet --copy=true" + fi + fi } function buildJSON() { @@ -489,6 +524,23 @@ function cleanup() { fi printf "\nCleaning up..." + if [[ "$s3ExportEnabled" == "true" ]]; then + # Give agents time to receive SIGTERM and flush remaining S3 Parquet parts + # (encode.S3 flushes on exit; allow one writeTimeout + network RTT). + _grace="${s3WriteTimeout%s}" + if [[ -z "$_grace" || ! "$_grace" =~ ^[0-9]+$ ]]; then + _grace=15 + fi + _grace=$(( _grace + 5 )) + echo + echo "Waiting ${_grace}s for agent S3 Parquet flush before deleting DaemonSet... (press Enter to skip)" + if [[ -t 0 ]]; then + # Interactive: Enter skips; timeout continues after full grace period. + read -r -t "$_grace" _ || true + else + sleep "$_grace" + fi + fi deleteServiceMonitor deleteDashboardCM deleteDaemonset @@ -537,14 +589,176 @@ function overridePromStage() { # update FLP Config function updateFLPConfig() { - jsonContent=$(cat "$1") - # already escaped chars must be double-escaped - jsonContent=${jsonContent//\\/\\\\} - # get json as string with escaped quotes - jsonContent=${jsonContent//\"/\\\"} - - # update FLP_CONFIG env - "$YQ_BIN" e --inplace ".spec.template.spec.containers[0].env[] |= select(.name==\"FLP_CONFIG\").value|=\"$jsonContent\"" "$2" + # Use strenv so secrets/special characters are not shell-interpolated into the + # yq expression (the previous quote-escaping path could silently no-op). + FLP_JSON="$(cat "$1")" \ + "$YQ_BIN" e --inplace \ + '(.spec.template.spec.containers[0].env[] | select(.name == "FLP_CONFIG").value) = strenv(FLP_JSON)' \ + "$2" +} + +# Load S3 credentials without putting secrets on the CLI argv. +# Precedence: --s3-credentials-file > --s3-secret > NETOBSERV_S3_* > AWS_* +function resolve_s3_credentials() { + if [[ -n "$s3CredentialsFile" ]]; then + if [[ ! -f "$s3CredentialsFile" ]]; then + echo "S3 credentials file not found: $s3CredentialsFile" >&2 + exit 1 + fi + # JSON/YAML keys: accessKeyId / secretAccessKey (operator Secret layout) + s3AccessKeyId=$("$YQ_BIN" e -r '.accessKeyId // .access_key_id // .aws_access_key_id // ""' "$s3CredentialsFile" 2>/dev/null || true) + s3SecretAccessKey=$("$YQ_BIN" e -r '.secretAccessKey // .secret_access_key // .aws_secret_access_key // ""' "$s3CredentialsFile" 2>/dev/null || true) + # AWS shared-credentials style ([default] aws_access_key_id=...) + if [[ -z "$s3AccessKeyId" || -z "$s3SecretAccessKey" ]]; then + s3AccessKeyId=$(awk -F' *= *' '/^[[:space:]]*aws_access_key_id[[:space:]]*=/{print $2; exit}' "$s3CredentialsFile") + s3SecretAccessKey=$(awk -F' *= *' '/^[[:space:]]*aws_secret_access_key[[:space:]]*=/{print $2; exit}' "$s3CredentialsFile") + fi + elif [[ -n "$s3Secret" ]]; then + echo "loading S3 credentials from Secret/$s3Secret in namespace $namespace" + s3AccessKeyId=$(${K8S_CLI_BIN} get secret "$s3Secret" -n "$namespace" -o jsonpath='{.data.accessKeyId}' 2>/dev/null | base64 -d 2>/dev/null || true) + s3SecretAccessKey=$(${K8S_CLI_BIN} get secret "$s3Secret" -n "$namespace" -o jsonpath='{.data.secretAccessKey}' 2>/dev/null | base64 -d 2>/dev/null || true) + if [[ -z "$s3AccessKeyId" || -z "$s3SecretAccessKey" ]]; then + # AWS-style secret keys + s3AccessKeyId=$(${K8S_CLI_BIN} get secret "$s3Secret" -n "$namespace" -o jsonpath='{.data.AWS_ACCESS_KEY_ID}' 2>/dev/null | base64 -d 2>/dev/null || true) + s3SecretAccessKey=$(${K8S_CLI_BIN} get secret "$s3Secret" -n "$namespace" -o jsonpath='{.data.AWS_SECRET_ACCESS_KEY}' 2>/dev/null | base64 -d 2>/dev/null || true) + fi + elif [[ -n "${NETOBSERV_S3_ACCESS_KEY:-}" && -n "${NETOBSERV_S3_SECRET_KEY:-}" ]]; then + s3AccessKeyId="$NETOBSERV_S3_ACCESS_KEY" + s3SecretAccessKey="$NETOBSERV_S3_SECRET_KEY" + elif [[ -n "${AWS_ACCESS_KEY_ID:-}" && -n "${AWS_SECRET_ACCESS_KEY:-}" ]]; then + s3AccessKeyId="$AWS_ACCESS_KEY_ID" + s3SecretAccessKey="$AWS_SECRET_ACCESS_KEY" + fi + + if [[ -z "$s3AccessKeyId" || -z "$s3SecretAccessKey" ]]; then + echo "S3 export requires credentials via one of:" >&2 + echo " - env NETOBSERV_S3_ACCESS_KEY / NETOBSERV_S3_SECRET_KEY" >&2 + echo " - env AWS_ACCESS_KEY_ID / AWS_SECRET_ACCESS_KEY" >&2 + echo " - --s3-credentials-file= (accessKeyId/secretAccessKey or AWS shared credentials)" >&2 + echo " - --s3-secret= (Kubernetes Secret in namespace $namespace)" >&2 + echo "Do not pass secret keys as CLI flags (visible in shell history and process list)." >&2 + exit 1 + fi +} + +# Derive secure TLS flag from endpoint when --s3-secure was not set +function resolve_s3_secure() { + if [[ -n "$s3Secure" ]]; then + return + fi + if [[ "$s3Endpoint" == https://* ]]; then + s3Secure="true" + else + s3Secure="false" + fi +} + +# Apply optional S3 Parquet encode stage (fork) matching FLP EncodeS3 / operator layout. +# S3 is configured on the *agent* DaemonSet via FLP_CONFIG (direct-flp), not collector --options. +function apply_s3_export() { + resolve_s3_secure + resolve_s3_credentials + + # Cap default flush interval so short --max-time captures still upload before cleanup. + if [[ "$s3WriteTimeoutSet" != "true" && -n "$maxTime" ]]; then + case "$maxTime" in + *s) + _mt_sec="${maxTime%s}" + ;; + *m) + _mt_sec=$(( ${maxTime%m} * 60 )) + ;; + *h) + _mt_sec=$(( ${maxTime%h} * 3600 )) + ;; + *) + _mt_sec="" + ;; + esac + if [[ -n "$_mt_sec" && "$_mt_sec" -gt 0 ]]; then + _flush=$(( _mt_sec / 4 )) + if [[ "$_flush" -lt 5 ]]; then + _flush=5 + elif [[ "$_flush" -gt 15 ]]; then + _flush=15 + fi + s3WriteTimeout="${_flush}s" + fi + fi + + copyFLPConfig "$manifest" + + # Fork from the same stage that feeds local gRPC (enrich or filter) + follows=$("$YQ_BIN" -r '.pipeline[] | select(.name == "send") | .follows // "enrich"' "$json") + if [[ -z "$follows" || "$follows" == "null" ]]; then + follows="enrich" + fi + # yq may emit a trailing newline; keep a single token + follows=$(printf '%s' "$follows" | tr -d '\n') + + existing=$("$YQ_BIN" -r '.pipeline[] | select(.name == "s3-export") | .name' "$json") + if [[ -z "$existing" ]]; then + "$YQ_BIN" e -oj --inplace '.parameters += [{"name":"s3-export","encode":{"type":"s3","s3":{}}}]' "$json" + "$YQ_BIN" e -oj --inplace ".pipeline += [{\"name\":\"s3-export\",\"follows\":\"$follows\"}]" "$json" + fi + + # Use strenv so secrets with quotes/special chars are not shell-interpolated into yq exprs + S3_ENDPOINT="$s3Endpoint" \ + S3_BUCKET="$s3Bucket" \ + S3_ACCOUNT="$s3Account" \ + S3_PREFIX="$s3Prefix" \ + S3_SECURE="$s3Secure" \ + S3_BATCH_SIZE="$s3BatchSize" \ + S3_WRITE_TIMEOUT="$s3WriteTimeout" \ + S3_ACCESS_KEY_ID="$s3AccessKeyId" \ + S3_SECRET_ACCESS_KEY="$s3SecretAccessKey" \ + "$YQ_BIN" e -oj --inplace ' + (.parameters[] | select(.name == "s3-export").encode.s3) |= { + "account": strenv(S3_ACCOUNT), + "endpoint": strenv(S3_ENDPOINT), + "bucket": strenv(S3_BUCKET), + "prefix": strenv(S3_PREFIX), + "format": "parquet", + "batchSize": env(S3_BATCH_SIZE) | tonumber, + "writeTimeout": strenv(S3_WRITE_TIMEOUT), + "secure": env(S3_SECURE) == "true", + "accessKeyId": strenv(S3_ACCESS_KEY_ID), + "secretAccessKey": strenv(S3_SECRET_ACCESS_KEY) + } + ' "$json" + + # Omit empty prefix for cleaner config + if [[ -z "$s3Prefix" ]]; then + "$YQ_BIN" e -oj --inplace 'del(.parameters[] | select(.name == "s3-export").encode.s3.prefix)' "$json" + fi + + updateFLPConfig "$json" "$manifest" + + # Fail hard if FLP_CONFIG was not actually updated (silent yq/env failures) + _flp_check=$("$YQ_BIN" e -r '.spec.template.spec.containers[0].env[] | select(.name=="FLP_CONFIG").value' "$manifest") + if ! printf '%s' "$_flp_check" | grep -Fq '"name": "s3-export"'; then + echo "ERROR: failed to embed s3-export stage into agent FLP_CONFIG" >&2 + exit 1 + fi + if ! printf '%s' "$_flp_check" | grep -Fq "\"endpoint\": \"$s3Endpoint\""; then + echo "ERROR: S3 endpoint missing from agent FLP_CONFIG after update" >&2 + exit 1 + fi + if ! printf '%s' "$_flp_check" | grep -Fq "\"bucket\": \"$s3Bucket\""; then + echo "ERROR: S3 bucket missing from agent FLP_CONFIG after update" >&2 + exit 1 + fi + if ! printf '%s' "$_flp_check" | grep -Fq '"accessKeyId":'; then + echo "ERROR: S3 credentials missing from agent FLP_CONFIG after update" >&2 + exit 1 + fi + + s3ExportEnabled="true" + if [[ "$agentImg" == *"netobserv/netobserv-ebpf-agent:main"* ]]; then + echo "WARNING: agent image is $agentImg — S3 Parquet encode requires an agent built with FLP S3 Parquet support." >&2 + echo " Set NETOBSERV_AGENT_IMAGE to a custom image (e.g. quay.io//netobserv-ebpf-agent:s3-flowbuffer)." >&2 + fi + echo "S3 Parquet export enabled → s3://$s3Bucket/${s3Prefix:+$s3Prefix/}cluster_id=$s3Account/… (Hive layout, flush every $s3WriteTimeout + on agent exit)" } # append a new flow filter rule to array @@ -778,6 +992,9 @@ function edit_manifest() { overridePromStage updateFLPConfig "$json" "$manifest" ;; + "s3_export") + apply_s3_export + ;; esac } @@ -1162,6 +1379,145 @@ function parse_args() { echo "invalid value for --get-subnets" fi ;; + *format) # Local flows output formats (collector): comma-separated and/or repeatable + if [[ "$command" != "flows" ]]; then + echo "--format is only valid for flows" >&2 + exit 1 + fi + if [[ -z "$value" || "$value" == "$key" ]]; then + echo "missing value for --format (json|sqlite|parquet)" >&2 + exit 1 + fi + # Reset default on first --format; merge subsequent --format flags. + if [[ "$formatFlagSet" != "true" ]]; then + outputFormat="" + formatFlagSet=true + fi + IFS=',' read -ra _fmt_tokens <<< "$value" + for _tok in "${_fmt_tokens[@]}"; do + _tok=$(echo "$_tok" | tr '[:upper:]' '[:lower:]' | tr -d '[:space:]') + if [[ -z "$_tok" ]]; then + continue + fi + case "$_tok" in + json|sqlite|parquet) + if [[ ",${outputFormat}," != *",${_tok},"* ]]; then + if [[ -n "$outputFormat" ]]; then + outputFormat="${outputFormat},${_tok}" + else + outputFormat="$_tok" + fi + fi + ;; + *) + echo "invalid --format token '${_tok}' (use json, sqlite, and/or parquet)" >&2 + exit 1 + ;; + esac + done + if [[ -z "$outputFormat" ]]; then + echo "missing value for --format (json|sqlite|parquet)" >&2 + exit 1 + fi + ;; + *s3-endpoint) + if [[ "$command" != "flows" ]]; then + echo "--s3-endpoint is only valid for flows" >&2 + exit 1 + fi + if [[ -z "$value" || "$value" == "$key" ]]; then + echo "missing value for --s3-endpoint" >&2 + exit 1 + fi + s3Endpoint="$value" + ;; + *s3-bucket) + if [[ "$command" != "flows" ]]; then + echo "--s3-bucket is only valid for flows" >&2 + exit 1 + fi + if [[ -z "$value" || "$value" == "$key" ]]; then + echo "missing value for --s3-bucket" >&2 + exit 1 + fi + s3Bucket="$value" + ;; + *s3-account) + if [[ "$command" != "flows" ]]; then + echo "--s3-account is only valid for flows" >&2 + exit 1 + fi + if [[ -z "$value" || "$value" == "$key" ]]; then + echo "missing value for --s3-account" >&2 + exit 1 + fi + s3Account="$value" + ;; + *s3-prefix) + if [[ "$command" != "flows" ]]; then + echo "--s3-prefix is only valid for flows" >&2 + exit 1 + fi + s3Prefix="$value" + ;; + *s3-secure) + if [[ "$command" != "flows" ]]; then + echo "--s3-secure is only valid for flows" >&2 + exit 1 + fi + defaultValue "true" + if [[ "$value" == "true" || "$value" == "false" ]]; then + s3Secure="$value" + else + echo "invalid value for --s3-secure (use true or false)" >&2 + exit 1 + fi + ;; + *s3-batch-size) + if [[ "$command" != "flows" ]]; then + echo "--s3-batch-size is only valid for flows" >&2 + exit 1 + fi + if [[ -z "$value" || "$value" == "$key" ]]; then + echo "missing value for --s3-batch-size" >&2 + exit 1 + fi + s3BatchSize="$value" + ;; + *s3-write-timeout) + if [[ "$command" != "flows" ]]; then + echo "--s3-write-timeout is only valid for flows" >&2 + exit 1 + fi + if [[ -z "$value" || "$value" == "$key" ]]; then + echo "missing value for --s3-write-timeout" >&2 + exit 1 + fi + s3WriteTimeout="$value" + s3WriteTimeoutSet="true" + ;; + *s3-credentials-file) + if [[ "$command" != "flows" ]]; then + echo "--s3-credentials-file is only valid for flows" >&2 + exit 1 + fi + if [[ -z "$value" || "$value" == "$key" ]]; then + echo "missing value for --s3-credentials-file" >&2 + exit 1 + fi + s3CredentialsFile="$value" + ;; + *s3-secret) + if [[ "$command" != "flows" ]]; then + echo "--s3-secret is only valid for flows" >&2 + exit 1 + fi + if [[ -z "$value" || "$value" == "$key" ]]; then + echo "missing value for --s3-secret" >&2 + exit 1 + fi + s3Secret="$value" + ;; *include_list) # Restrict metrics capture if [[ "$command" == "metrics" ]]; then includeList="$value" @@ -1177,6 +1533,14 @@ function parse_args() { esac done + # Summarize local output formats once (after merging comma-separated / repeatable --format) + if [[ "$formatFlagSet" == "true" ]]; then + echo "Local output format(s): ${outputFormat}" + if [[ ",${outputFormat}," == *",parquet,"* ]]; then + echo " Parquet → ./output/flow//cluster_id=cli/year=…/part-cli-….parquet (schema v1)" + fi + fi + # avoid packet capture without filters if [[ "$command" = "packets" ]]; then currentFilters=$("$YQ_BIN" -r ".spec.template.spec.containers[0].env[] | select(.name == \"FLOW_FILTER_RULES\").value" "$manifest") @@ -1191,4 +1555,17 @@ function parse_args() { # always restrict generated metrics edit_manifest "include_list" "$includeList" fi + + # Optional S3 Parquet export (flows only); applied after filters so the fork follows enrich/filter + if [[ -n "$s3Endpoint" || -n "$s3Bucket" || -n "$s3CredentialsFile" || -n "$s3Secret" ]]; then + if [[ "$command" != "flows" ]]; then + echo "S3 export is only supported for flows" >&2 + exit 1 + fi + if [[ -z "$s3Endpoint" || -z "$s3Bucket" ]]; then + echo "--s3-endpoint and --s3-bucket are both required to enable S3 export" >&2 + exit 1 + fi + edit_manifest "s3_export" + fi } diff --git a/scripts/generate-doc.sh b/scripts/generate-doc.sh index 622a3871b..72df5fd75 100755 --- a/scripts/generate-doc.sh +++ b/scripts/generate-doc.sh @@ -67,6 +67,8 @@ $ oc netobserv flows [] [] | Option | Description | Default" features_usage flowsAndPackets_collector_usage +flows_format_usage +s3_export_usage filters_usage flowsAndMetrics_filters_usage echo -e "|===" @@ -75,7 +77,19 @@ echo " .Example running flows capture on TCP protocol and port 49051 with PacketDrop and RTT features enabled: [source,terminal] ---- -$ oc netobserv flows --enable_pkt_drop --enable_rtt --action=Accept --cidr=0.0.0.0/0 --protocol=TCP --port=49051 +\$ oc netobserv flows --enable_pkt_drop --enable_rtt --action=Accept --cidr=0.0.0.0/0 --protocol=TCP --port=49051 +---- + +.Example writing local Parquet (schema v1) only: +[source,terminal] +---- +\$ oc netobserv flows --format=parquet +---- + +.Example writing JSON and Parquet: +[source,terminal] +---- +\$ oc netobserv flows --format=json,parquet ----" # packets table diff --git a/scripts/help.sh b/scripts/help.sh index 9d78629b0..03f8732fe 100644 --- a/scripts/help.sh +++ b/scripts/help.sh @@ -78,6 +78,17 @@ function flows_examples { echo " netobserv flows --query='SrcK8S_Namespace=~\"app-.*\"'" echo " Capture flows from/to a specific pod pattern on a specific node:" echo " netobserv flows --node-selector=kubernetes.io/hostname:my-node --query='SrcK8S_Name=~\".*my-pod.*\" or DstK8S_Name=~\".*my-pod.*\"'" + echo " Also export Hive-partitioned Parquet to S3 (any S3-compatible endpoint; credentials via env, not flags):" + echo " export NETOBSERV_S3_ACCESS_KEY= NETOBSERV_S3_SECRET_KEY=" + echo " netobserv flows --s3-endpoint=https://s3.example.com --s3-bucket= --s3-account=" + echo " Write local Parquet (schema v1) only:" + echo " netobserv flows --format=parquet" + echo " Write JSON + Parquet (no SQLite):" + echo " netobserv flows --format=json,parquet" + echo " Write all local sinks:" + echo " netobserv flows --format=json,sqlite,parquet" + echo " Local Parquet plus S3 export:" + echo " netobserv flows --format=parquet --s3-endpoint=https://s3.example.com --s3-bucket=" } # packets examples @@ -129,6 +140,45 @@ function flowsAndPackets_collector_usage { echo " --max-bytes: maximum capture bytes (default: 50000000 = 50MB)" } +# flows local output formats (collector; non-exclusive list) +function flows_format_usage { + echo " --format: local flows output sink(s) (default: json,sqlite)" + echo " Comma-separated and/or repeatable. Tokens:" + echo " json → JSON under output/flow/" + echo " sqlite → SQLite DB under output/flow/" + echo " parquet → Hive-partitioned Parquet schema v1 under" + echo " output/flow//cluster_id=cli/…" + echo " Examples: --format=json,sqlite (default when omitted)" + echo " --format=parquet" + echo " --format=json,parquet" + echo " --format=json,sqlite,parquet" + echo " --format=json --format=parquet" + echo " When parquet is selected: collector runs in-cluster;" + echo " parts are flushed every few seconds + on exit;" + echo " --copy still prompts by default (same as other" + echo " formats); requires a collector image built with" + echo " parquet support (not quay …:main alone);" + echo " same schema as S3 Parquet export; independent of --s3-*" +} + +# Optional S3 Parquet export (flows only; local JSON/SQLite remains default) +function s3_export_usage { + echo " --s3-endpoint: S3-compatible endpoint URL (default: n/a)" + echo " --s3-bucket: S3 bucket name (default: n/a)" + echo " --s3-account: Hive cluster_id partition value (default: cli)" + echo " --s3-prefix: optional key prefix before cluster_id (default: n/a)" + echo " --s3-secure: use HTTPS (default: true if endpoint is https://) (default: auto)" + echo " --s3-batch-size: flows per Parquet object before flush (default: 5000)" + echo " --s3-write-timeout: flush timeout when batch is not full (default: 15s, capped by --max-time)" + echo " --s3-credentials-file: path to credentials file (not secret values) (default: n/a)" + echo " --s3-secret: Kubernetes Secret name in the CLI namespace (default: n/a)" + echo " Credentials (prefer env; never pass secret keys as flags):" + echo " NETOBSERV_S3_ACCESS_KEY / NETOBSERV_S3_SECRET_KEY" + echo " or AWS_ACCESS_KEY_ID / AWS_SECRET_ACCESS_KEY" + echo " or --s3-credentials-file with accessKeyId/secretAccessKey (or AWS shared credentials)" + echo " or --s3-secret= Secret keys accessKeyId/secretAccessKey" +} + # fmetrics collector options function metrics_collector_usage { echo " --background: run in background (default: false)" @@ -193,8 +243,12 @@ function flows_usage { echo echo "Options:" flowsAndPackets_collector_usage + flows_format_usage script_usage echo + echo "S3 Parquet export (optional; requires agent image with FLP S3 Parquet encode):" + s3_export_usage + echo echo "Examples:" flows_examples }