From ceca244eaf56ed1eef23a0ca82ec77f8eed84d5f Mon Sep 17 00:00:00 2001 From: yangyudong <916291030@qq.com> Date: Sat, 19 Apr 2025 22:56:37 +0800 Subject: [PATCH] init --- .github/workflows/go.yml | 51 ++ .gitignore | 3 + .gitmodules | 3 + LICENSE | 24 + README.md | 80 +++ enc_test.go | 230 +++++++++ encode.go | 105 ++++ encode_frame.go | 427 ++++++++++++++++ encode_meta.go | 419 ++++++++++++++++ encode_subframe.go | 369 ++++++++++++++ example_test.go | 121 +++++ flac.go | 426 ++++++++++++++++ flac_test.go | 202 ++++++++ frame/frame.go | 687 +++++++++++++++++++++++++ frame/frame_test.go | 193 ++++++++ frame/subframe.go | 534 ++++++++++++++++++++ go.mod | 8 + go.sum | 37 ++ internal/bits/reader.go | 84 ++++ internal/bits/reader_test.go | 688 ++++++++++++++++++++++++++ internal/bits/twocomp.go | 27 + internal/bits/twocomp_test.go | 27 + internal/bits/unary.go | 58 +++ internal/bits/unary_test.go | 36 ++ internal/bits/zigzag.go | 41 ++ internal/bits/zigzag_test.go | 49 ++ internal/bufseekio/readseeker.go | 152 ++++++ internal/bufseekio/readseeker_test.go | 266 ++++++++++ internal/hashutil/crc16/crc16.go | 115 +++++ internal/hashutil/crc16/crc16_test.go | 106 ++++ internal/hashutil/crc8/crc8.go | 114 +++++ internal/hashutil/crc8/crc8_test.go | 106 ++++ internal/hashutil/hashutil.go | 18 + internal/ioutilx/byte.go | 24 + internal/ioutilx/zero.go | 17 + internal/utf8/decode.go | 160 ++++++ internal/utf8/encode.go | 72 +++ meta/application.go | 38 ++ meta/cuesheet.go | 242 +++++++++ meta/meta.go | 210 ++++++++ meta/meta_test.go | 260 ++++++++++ meta/padding.go | 39 ++ meta/picture.go | 120 +++++ meta/reader.go | 24 + meta/seektable.go | 67 +++ meta/streaminfo.go | 116 +++++ meta/testdata/README.md | 35 ++ meta/testdata/silence.jpg | Bin 0 -> 70585 bytes meta/vorbiscomment.go | 69 +++ testdata/README.md | 22 + 50 files changed, 7321 insertions(+) create mode 100755 .github/workflows/go.yml create mode 100755 .gitignore create mode 100755 .gitmodules create mode 100755 LICENSE create mode 100755 README.md create mode 100755 enc_test.go create mode 100755 encode.go create mode 100755 encode_frame.go create mode 100755 encode_meta.go create mode 100755 encode_subframe.go create mode 100755 example_test.go create mode 100755 flac.go create mode 100755 flac_test.go create mode 100755 frame/frame.go create mode 100755 frame/frame_test.go create mode 100755 frame/subframe.go create mode 100755 go.mod create mode 100755 go.sum create mode 100755 internal/bits/reader.go create mode 100755 internal/bits/reader_test.go create mode 100755 internal/bits/twocomp.go create mode 100755 internal/bits/twocomp_test.go create mode 100755 internal/bits/unary.go create mode 100755 internal/bits/unary_test.go create mode 100755 internal/bits/zigzag.go create mode 100755 internal/bits/zigzag_test.go create mode 100755 internal/bufseekio/readseeker.go create mode 100755 internal/bufseekio/readseeker_test.go create mode 100755 internal/hashutil/crc16/crc16.go create mode 100755 internal/hashutil/crc16/crc16_test.go create mode 100755 internal/hashutil/crc8/crc8.go create mode 100755 internal/hashutil/crc8/crc8_test.go create mode 100755 internal/hashutil/hashutil.go create mode 100755 internal/ioutilx/byte.go create mode 100755 internal/ioutilx/zero.go create mode 100755 internal/utf8/decode.go create mode 100755 internal/utf8/encode.go create mode 100755 meta/application.go create mode 100755 meta/cuesheet.go create mode 100755 meta/meta.go create mode 100755 meta/meta_test.go create mode 100755 meta/padding.go create mode 100755 meta/picture.go create mode 100755 meta/reader.go create mode 100755 meta/seektable.go create mode 100755 meta/streaminfo.go create mode 100755 meta/testdata/README.md create mode 100755 meta/testdata/silence.jpg create mode 100755 meta/vorbiscomment.go create mode 100755 testdata/README.md diff --git a/.github/workflows/go.yml b/.github/workflows/go.yml new file mode 100755 index 0000000..88d0aea --- /dev/null +++ b/.github/workflows/go.yml @@ -0,0 +1,51 @@ +# This workflow will build a golang project +# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-go + +name: Go + +on: + push: + branches: [ "master" ] + pull_request: + branches: [ "master" ] + +jobs: + + build: + runs-on: ubuntu-latest + strategy: + matrix: + go-version: ['1.14', 'stable'] + name: Build with Go ${{ matrix.go-version }} + steps: + - uses: actions/checkout@v3 + with: + submodules: recursive + + - name: Set up Go + uses: actions/setup-go@v4 + with: + go-version: ${{ matrix.go-version }} + + - name: Install goveralls + if: ${{ matrix.go-version == 'stable' && github.ref == 'refs/heads/master' }} + run: go install github.com/mattn/goveralls@latest + + - name: Build + run: go build -v ./... + + - name: Test + run: go test -v -covermode atomic -coverprofile=covprofile ./... + + - name: Gofmt + # Run gofmt, print the output and exit with status code 1 if it isn't empty. + run: | + OUTPUT=$(gofmt -d ./) + echo "$OUTPUT" + test -z "$OUTPUT" + + - name: Send coverage + if: ${{ matrix.go-version == 'stable' && github.ref == 'refs/heads/master' }} + env: + COVERALLS_TOKEN: ${{ secrets.COVERALLS_REPO_TOKEN }} + run: goveralls -coverprofile=covprofile -service=github diff --git a/.gitignore b/.gitignore new file mode 100755 index 0000000..939f6a5 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +*.flac +*.wav +_resources_ diff --git a/.gitmodules b/.gitmodules new file mode 100755 index 0000000..aa52eb0 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "flac-test-files"] + path = testdata/flac-test-files + url = https://github.com/ietf-wg-cellar/flac-test-files diff --git a/LICENSE b/LICENSE new file mode 100755 index 0000000..68a49da --- /dev/null +++ b/LICENSE @@ -0,0 +1,24 @@ +This is free and unencumbered software released into the public domain. + +Anyone is free to copy, modify, publish, use, compile, sell, or +distribute this software, either in source code form or as a compiled +binary, for any purpose, commercial or non-commercial, and by any +means. + +In jurisdictions that recognize copyright laws, the author or authors +of this software dedicate any and all copyright interest in the +software to the public domain. We make this dedication for the benefit +of the public at large and to the detriment of our heirs and +successors. We intend this dedication to be an overt act of +relinquishment in perpetuity of all present and future rights to this +software under copyright law. + +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 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. + +For more information, please refer to diff --git a/README.md b/README.md new file mode 100755 index 0000000..dc09430 --- /dev/null +++ b/README.md @@ -0,0 +1,80 @@ +# flac + +[![Go build status](https://github.com/mewkiz/flac/actions/workflows/go.yml/badge.svg?branch=master)](https://github.com/mewkiz/flac/actions/workflows/go.yml) +[![Coverage Status](https://coveralls.io/repos/github/mewkiz/flac/badge.svg?branch=master)](https://coveralls.io/github/mewkiz/flac?branch=master) +[![GoDoc](https://pkg.go.dev/badge/github.com/mewkiz/flac)](https://pkg.go.dev/github.com/mewkiz/flac) + +This package provides access to [FLAC][1] (Free Lossless Audio Codec) streams. + +[1]: http://flac.sourceforge.net/format.html + +## Documentation + +Documentation provided by GoDoc. + +- [flac]: provides access to FLAC (Free Lossless Audio Codec) streams. + - [frame][flac/frame]: implements access to FLAC audio frames. + - [meta][flac/meta]: implements access to FLAC metadata blocks. + +[flac]: http://pkg.go.dev/github.com/mewkiz/flac +[flac/frame]: http://pkg.go.dev/github.com/mewkiz/flac/frame +[flac/meta]: http://pkg.go.dev/github.com/mewkiz/flac/meta + +## Changes + +* Version 1.0.12 (2024-08-11) + - Improve performance of flac.NewSeek() by using a buffered reader (see [#72](https://github.com/mewkiz/flac/pull/72)). + - Fix off-by-one error in Seek end of stream check (see [#73](https://github.com/mewkiz/flac/pull/73)). + +* Version 1.0.11 (2024-08-04) + - Move example tools to dedicated [mewkiz/flac-tools](https://github.com/mewkiz/flac-tools) repository to reduce external dependencies (see [#62](https://github.com/mewkiz/flac/pull/62)). + - Fix seek to frame start (see [#71](https://github.com/mewkiz/flac/pull/71)). Thanks to [Mark Kremer](https://github.com/MarkKremer). + - Simplify internal handling of io.Closer element in flac.Stream and flac.Encoder types (see [#70](https://github.com/mewkiz/flac/pull/70)). Thanks to [Mario Salgado](https://github.com/zalgonoise). + +* Version 1.0.10 (2023-11-11) + - Add support for LPC audio sample encoding (see [#66](https://github.com/mewkiz/flac/pull/66)). Thanks to [Mark Kremer](https://github.com/MarkKremer) for bug fixes and [Mattias Wadman](https://github.com/wader) for the invaluable [fq](https://github.com/wader/fq) tool used to investigate FLAC encoding issues. + - Replace Travis CI with GitHub actions for CI build status, test status and code coverage [#64](https://github.com/mewkiz/flac/pull/64)). Thanks to [Mark Kremer](https://github.com/MarkKremer). + +* Version 1.0.9 (2023-10-24) + - Fix integer overflow during unfolding of rice residual (see [#61](https://github.com/mewkiz/flac/pull/61)). Thanks to [Mark Kremer](https://github.com/MarkKremer). + - Fix decoding of escaped partition audio samples (see [#60](https://github.com/mewkiz/flac/issues/60)). Thanks to [Mark Kremer](https://github.com/MarkKremer). + - Handle frame hashing of audio samples with bits-per-sample not evenly divisible by 8 (see [9d50c9e](https://github.com/mewkiz/flac/commit/9d50c9ee99ba322f487ed60442dc16f22b2affb8)). + +* Version 1.0.8 (2023-04-09) + - Fix race condition when reading meta data (see [#56](https://github.com/mewkiz/flac/pull/56)). Thanks to [Zach Orosz](https://github.com/zachorosz). + - Fix encoding of 8-bps WAV audio samples (see [#52](https://github.com/mewkiz/flac/pull/52)). Thanks to [Martijn van Beurden](https://github.com/ktmf01). + - Fix StreamInfo block type error message (see [#49](https://github.com/mewkiz/flac/pull/49)). + +* Version 1.0.7 (2021-01-28) + - Add seek API (see [#44](https://github.com/mewkiz/flac/pull/44) and [#46](https://github.com/mewkiz/flac/pull/46)). Thanks to [Craig Swank](https://github.com/cswank). + +* Version 1.0.6 (2019-12-20) + - Add experimental Encoder API to encode audio samples and metadata blocks (see [#32](https://github.com/mewkiz/flac/pull/32)). + - Use go.mod. + - Skip ID3v2 data prepended to flac files when parsing (see [36cc17e](https://github.com/mewkiz/flac/commit/36cc17efed51a9bae283d6a3a7a10997492945e7)). + - Remove dependency on encodebytes. Thanks to [Mikey Dickerson](https://github.com/mdickers47). + - Add 16kHz test case. Thanks to [Chewxy](https://github.com/chewxy). + - Fix lint issues (see [#25](https://github.com/mewkiz/flac/issues/25)). + +* Version 1.0.5 (2016-05-06) + - Simplify import paths. Drop use of gopkg.in, and rely on vendoring instead (see [azul3d/engine#1](https://github.com/azul3d/engine/issues/1)). + - Add FLAC decoding benchmark (see [d675e0a](https://github.com/mewkiz/flac/blob/d675e0aaccf2e43055f56b9b3feeddfdeed402e2/frame/frame_test.go#L60)). + +* Version 1.0.4 (2016-02-11) + - Add API examples to documentation (see [#11](https://github.com/mewkiz/flac/issues/11)). + - Extend test cases (see [aadf80a](https://github.com/mewkiz/flac/commit/aadf80aa28c463a94b8d5c49757e5a0948613ce2)). + +* Version 1.0.3 (2016-02-02) + - Implement decoding of FLAC files with wasted bits-per-sample (see [#12](https://github.com/mewkiz/flac/issues/12)). + - Stress test the library using [go-fuzz](https://github.com/dvyukov/go-fuzz) (see [#10](https://github.com/mewkiz/flac/pull/10)). Thanks to [Patrick Mézard](https://github.com/pmezard). + +* Version 1.0.2 (2015-06-05) + - Fix decoding of blocking strategy (see [#9](https://github.com/mewkiz/flac/pull/9)). Thanks to [Sergey Didyk](https://github.com/sdidyk). + +* Version 1.0.1 (2015-02-25) + - Fix two subframe decoding bugs (see [#7](https://github.com/mewkiz/flac/pull/7)). Thanks to [Jonathan MacMillan](https://github.com/perotinus). + - Add frame decoding test cases. + +* Version 1.0.0 (2014-09-30) + - Initial release. + - Implement decoding of FLAC files. diff --git a/enc_test.go b/enc_test.go new file mode 100755 index 0000000..de1acb6 --- /dev/null +++ b/enc_test.go @@ -0,0 +1,230 @@ +package flac_test + +import ( + "bytes" + "io" + "io/ioutil" + "testing" + + "github.com/mewkiz/flac" + "github.com/mewkiz/flac/meta" +) + +func TestEncode(t *testing.T) { + paths := []string{ + // metadata test cases. + "meta/testdata/input-SCPAP.flac", + "meta/testdata/input-SCVA.flac", + "meta/testdata/input-SCVPAP.flac", + "meta/testdata/input-VA.flac", + "meta/testdata/input-SCVAUP.flac", // empty metadata block (of type 0x7e) + "meta/testdata/input-SVAUP.flac", // empty metadata block (of type 0x7e) + "meta/testdata/silence.flac", + // flac test cases. + "testdata/19875.flac", // prediction method 3 (FIR) + "testdata/44127.flac", // prediction method 3 (FIR) + "testdata/59996.flac", + "testdata/80574.flac", // prediction method 3 (FIR) + "testdata/172960.flac", + "testdata/189983.flac", + "testdata/191885.flac", + "testdata/212768.flac", + "testdata/220014.flac", // prediction method 2 (Fixed) + "testdata/243749.flac", // prediction method 2 (Fixed) + "testdata/256529.flac", + "testdata/257344.flac", // prediction method 3 (FIR) + "testdata/8297-275156-0011.flac", // prediction method 3 (FIR) + "testdata/love.flac", // wasted bits + // IETF test cases. + "testdata/flac-test-files/subset/01 - blocksize 4096.flac", + "testdata/flac-test-files/subset/02 - blocksize 4608.flac", + "testdata/flac-test-files/subset/03 - blocksize 16.flac", + "testdata/flac-test-files/subset/04 - blocksize 192.flac", + "testdata/flac-test-files/subset/05 - blocksize 254.flac", + "testdata/flac-test-files/subset/06 - blocksize 512.flac", + "testdata/flac-test-files/subset/07 - blocksize 725.flac", + "testdata/flac-test-files/subset/08 - blocksize 1000.flac", + "testdata/flac-test-files/subset/09 - blocksize 1937.flac", + "testdata/flac-test-files/subset/10 - blocksize 2304.flac", + "testdata/flac-test-files/subset/11 - partition order 8.flac", + "testdata/flac-test-files/subset/12 - qlp precision 15 bit.flac", + "testdata/flac-test-files/subset/13 - qlp precision 2 bit.flac", + "testdata/flac-test-files/subset/14 - wasted bits.flac", + "testdata/flac-test-files/subset/15 - only verbatim subframes.flac", + "testdata/flac-test-files/subset/16 - partition order 8 containing escaped partitions.flac", + "testdata/flac-test-files/subset/17 - all fixed orders.flac", + "testdata/flac-test-files/subset/18 - precision search.flac", + "testdata/flac-test-files/subset/19 - samplerate 35467Hz.flac", + "testdata/flac-test-files/subset/20 - samplerate 39kHz.flac", + "testdata/flac-test-files/subset/21 - samplerate 22050Hz.flac", + "testdata/flac-test-files/subset/22 - 12 bit per sample.flac", + "testdata/flac-test-files/subset/23 - 8 bit per sample.flac", + "testdata/flac-test-files/subset/24 - variable blocksize file created with flake revision 264.flac", + "testdata/flac-test-files/subset/25 - variable blocksize file created with flake revision 264, modified to create smaller blocks.flac", + // NOTE: the only diff is that "26 - ...flac" uses `block_size: 0b111 + // (end of header (16 bit))` to encode the block size at the end of the + // header, whereas mewkiz/flac encodes it directly `block_size: 4096 + // (0b1100)`. Notably, the computed md5 hash of the decoded audio samples + // is identical (MD5: 3b2939b39ae7369b80451c77865e60c1). Thus, ignore the + // test case. + //"testdata/flac-test-files/subset/26 - variable blocksize file created with CUETools.Flake 2.1.6.flac", + // NOTE: the only diff is that "27 - ...flac" uses `block_size: 0b111 + // (end of header (16 bit))` to encode the block size at the end of the + // header, whereas mewkiz/flac encodes it directly `block_size: 4608 + // (0b101)`. Notably, the computed md5 hash of the decoded audio samples + // is identical (MD5: 9fb66177d2f735d4b1f501a5af1320a3). Thus, ignore the + // test case. + //"testdata/flac-test-files/subset/27 - old format variable blocksize file created with Flake 0.11.flac", + "testdata/flac-test-files/subset/28 - high resolution audio, default settings.flac", + "testdata/flac-test-files/subset/29 - high resolution audio, blocksize 16384.flac", + "testdata/flac-test-files/subset/30 - high resolution audio, blocksize 13456.flac", + "testdata/flac-test-files/subset/31 - high resolution audio, using only 32nd order predictors.flac", + "testdata/flac-test-files/subset/32 - high resolution audio, partition order 8 containing escaped partitions.flac", + "testdata/flac-test-files/subset/33 - samplerate 192kHz.flac", + // NOTE: the only diff is that "34 - ...flac" uses `0b1100 (end of header + // (8 bit*1000))` to encode the sample rate at the end of the header, + // whereas mewkiz/flac encodes it directly `192000 (0b11)`. Notably, the + // computed md5 hash of the decoded audio samples is identical + // (MD5: 942f56e503437dfd4c269c331774b2e3). Thus, ignore the test case. + //"testdata/flac-test-files/subset/34 - samplerate 192kHz, using only 32nd order predictors.flac", + "testdata/flac-test-files/subset/35 - samplerate 134560Hz.flac", + "testdata/flac-test-files/subset/36 - samplerate 384kHz.flac", + "testdata/flac-test-files/subset/37 - 20 bit per sample.flac", + "testdata/flac-test-files/subset/38 - 3 channels (3.0).flac", + "testdata/flac-test-files/subset/39 - 4 channels (4.0).flac", + "testdata/flac-test-files/subset/40 - 5 channels (5.0).flac", + "testdata/flac-test-files/subset/41 - 6 channels (5.1).flac", + "testdata/flac-test-files/subset/42 - 7 channels (6.1).flac", + "testdata/flac-test-files/subset/43 - 8 channels (7.1).flac", + // NOTE: the only diff is that "44 - ...flac" uses `0b1100 (end of header + // (8 bit*1000))` to encode the sample rate at the end of the header, + // whereas mewkiz/flac encodes it directly `192000 (0b11)`. Notably, the + // computed md5 hash of the decoded audio samples is identical + // (MD5: cdf531d4d4b95233986bc499518a89db). Thus, ignore the test case. + //"testdata/flac-test-files/subset/44 - 8-channel surround, 192kHz, 24 bit, using only 32nd order predictors.flac", + "testdata/flac-test-files/subset/45 - no total number of samples set.flac", + "testdata/flac-test-files/subset/46 - no min-max framesize set.flac", + "testdata/flac-test-files/subset/47 - only STREAMINFO.flac", + "testdata/flac-test-files/subset/48 - Extremely large SEEKTABLE.flac", + "testdata/flac-test-files/subset/49 - Extremely large PADDING.flac", + "testdata/flac-test-files/subset/50 - Extremely large PICTURE.flac", + "testdata/flac-test-files/subset/51 - Extremely large VORBISCOMMENT.flac", + "testdata/flac-test-files/subset/52 - Extremely large APPLICATION.flac", + "testdata/flac-test-files/subset/53 - CUESHEET with very many indexes.flac", + "testdata/flac-test-files/subset/54 - 1000x repeating VORBISCOMMENT.flac", + "testdata/flac-test-files/subset/55 - file 48-53 combined.flac", + "testdata/flac-test-files/subset/56 - JPG PICTURE.flac", + "testdata/flac-test-files/subset/57 - PNG PICTURE.flac", + "testdata/flac-test-files/subset/58 - GIF PICTURE.flac", + "testdata/flac-test-files/subset/59 - AVIF PICTURE.flac", + "testdata/flac-test-files/subset/60 - mono audio.flac", + "testdata/flac-test-files/subset/61 - predictor overflow check, 16-bit.flac", + "testdata/flac-test-files/subset/62 - predictor overflow check, 20-bit.flac", + "testdata/flac-test-files/subset/63 - predictor overflow check, 24-bit.flac", + "testdata/flac-test-files/subset/64 - rice partitions with escape code zero.flac", + } + for _, path := range paths { + t.Run(path, func(t *testing.T) { + // Decode source file. + stream, err := flac.ParseFile(path) + if err != nil { + t.Fatalf("%q: unable to parse FLAC file; %v", path, err) + } + defer stream.Close() + + // Open encoder for FLAC stream. + out := new(bytes.Buffer) + enc, err := flac.NewEncoder(out, stream.Info, stream.Blocks...) + if err != nil { + t.Fatalf("%q: unable to create encoder for FLAC stream; %v", path, err) + } + // Encode audio samples. + for { + frame, err := stream.ParseNext() + if err != nil { + if err == io.EOF { + break + } + t.Fatalf("%q: unable to parse audio frame of FLAC stream; %v", path, err) + } + if err := enc.WriteFrame(frame); err != nil { + t.Fatalf("%q: unable to encode audio frame of FLAC stream; %v", path, err) + } + } + // Close encoder and flush pending writes. + if err := enc.Close(); err != nil { + t.Fatalf("%q: unable to close encoder for FLAC stream; %v", path, err) + } + + // Compare source and destination FLAC streams. + want, err := ioutil.ReadFile(path) + if err != nil { + t.Fatalf("%q: unable to read file; %v", path, err) + } + got := out.Bytes() + if !bytes.Equal(got, want) { + t.Fatalf("%q: content mismatch; expected % X, got % X", path, want, got) + } + }) + } +} + +func TestEncodeComment(t *testing.T) { + // Decode FLAC file. + const path = "meta/testdata/input-VA.flac" + src, err := flac.ParseFile(path) + if err != nil { + t.Fatalf("unable to parse input FLAC file; %v", err) + } + defer src.Close() + + // Add custom vorbis comment. + const want = "FLAC encoding test case" + for _, block := range src.Blocks { + if comment, ok := block.Body.(*meta.VorbisComment); ok { + comment.Vendor = want + } + } + + // Open encoder for FLAC stream. + out := new(bytes.Buffer) + enc, err := flac.NewEncoder(out, src.Info, src.Blocks...) + if err != nil { + t.Fatalf("%q: unable to create encoder for FLAC stream; %v", path, err) + } + // Encode audio samples. + for { + frame, err := src.ParseNext() + if err != nil { + if err == io.EOF { + break + } + t.Fatalf("%q: unable to parse audio frame of FLAC stream; %v", path, err) + } + if err := enc.WriteFrame(frame); err != nil { + t.Fatalf("%q: unable to encode audio frame of FLAC stream; %v", path, err) + } + } + // Close encoder and flush pending writes. + if err := enc.Close(); err != nil { + t.Fatalf("%q: unable to close encoder for FLAC stream; %v", path, err) + } + + // Parse encoded FLAC file. + stream, err := flac.Parse(out) + if err != nil { + t.Fatalf("unable to parse output FLAC file; %v", err) + } + defer stream.Close() + + // Add custom vorbis comment. + for _, block := range stream.Blocks { + if comment, ok := block.Body.(*meta.VorbisComment); ok { + got := comment.Vendor + if got != want { + t.Errorf("Vorbis comment mismatch; expected %q, got %q", want, got) + continue + } + } + } +} diff --git a/encode.go b/encode.go new file mode 100755 index 0000000..892d7a7 --- /dev/null +++ b/encode.go @@ -0,0 +1,105 @@ +package flac + +import ( + "crypto/md5" + "hash" + "io" + + "github.com/icza/bitio" + "github.com/mewkiz/flac/meta" + "github.com/mewkiz/pkg/errutil" +) + +// An Encoder represents a FLAC encoder. +type Encoder struct { + // FLAC stream of encoder. + *Stream + // Underlying io.Writer or io.WriteCloser to the output stream. + w io.Writer + // Minimum and maximum block size (in samples) of frames written by encoder. + blockSizeMin, blockSizeMax uint16 + // Minimum and maximum frame size (in bytes) of frames written by encoder. + frameSizeMin, frameSizeMax uint32 + // MD5 running hash of unencoded audio samples. + md5sum hash.Hash + // Total number of samples (per channel) written by encoder. + nsamples uint64 + // Current frame number if block size is fixed, and the first sample number + // of the current frame otherwise. + curNum uint64 +} + +// NewEncoder returns a new FLAC encoder for the given metadata StreamInfo block +// and optional metadata blocks. +func NewEncoder(w io.Writer, info *meta.StreamInfo, blocks ...*meta.Block) (*Encoder, error) { + // Store FLAC signature. + enc := &Encoder{ + Stream: &Stream{ + Info: info, + Blocks: blocks, + }, + w: w, + md5sum: md5.New(), + } + + bw := bitio.NewWriter(w) + if _, err := bw.Write(flacSignature); err != nil { + return nil, errutil.Err(err) + } + // Encode metadata blocks. + // TODO: consider using bufio.NewWriter. + if err := encodeStreamInfo(bw, info, len(blocks) == 0); err != nil { + return nil, errutil.Err(err) + } + for i, block := range blocks { + if err := encodeBlock(bw, block, i == len(blocks)-1); err != nil { + return nil, errutil.Err(err) + } + } + // Flush pending writes of metadata blocks. + if _, err := bw.Align(); err != nil { + return nil, errutil.Err(err) + } + // Return encoder to be used for encoding audio samples. + return enc, nil +} + +// Close closes the underlying io.Writer of the encoder and flushes any pending +// writes. If the io.Writer implements io.Seeker, the encoder will update the +// StreamInfo metadata block with the MD5 checksum of the unencoded audio +// samples, the number of samples, and the minimum and maximum frame size and +// block size. +func (enc *Encoder) Close() error { + // TODO: check if bit writer should be flushed before seeking on enc.w. + // Update StreamInfo metadata block. + if ws, ok := enc.w.(io.WriteSeeker); ok { + if _, err := ws.Seek(int64(len(flacSignature)), io.SeekStart); err != nil { + return errutil.Err(err) + } + // Update minimum and maximum block size (in samples) of FLAC stream. + enc.Info.BlockSizeMin = enc.blockSizeMin + enc.Info.BlockSizeMax = enc.blockSizeMax + // Update minimum and maximum frame size (in bytes) of FLAC stream. + enc.Info.FrameSizeMin = enc.frameSizeMin + enc.Info.FrameSizeMax = enc.frameSizeMax + // Update total number of samples (per channel) of FLAC stream. + enc.Info.NSamples = enc.nsamples + // Update MD5 checksum of the unencoded audio samples. + sum := enc.md5sum.Sum(nil) + for i := range sum { + enc.Info.MD5sum[i] = sum[i] + } + bw := bitio.NewWriter(ws) + // Write updated StreamInfo metadata block to output stream. + if err := encodeStreamInfo(bw, enc.Info, len(enc.Blocks) == 0); err != nil { + return errutil.Err(err) + } + if _, err := bw.Align(); err != nil { + return errutil.Err(err) + } + } + if closer, ok := enc.w.(io.Closer); ok { + return closer.Close() + } + return nil +} diff --git a/encode_frame.go b/encode_frame.go new file mode 100755 index 0000000..8822af1 --- /dev/null +++ b/encode_frame.go @@ -0,0 +1,427 @@ +package flac + +import ( + "encoding/binary" + "io" + "math" + + "github.com/icza/bitio" + "github.com/mewkiz/flac/frame" + "github.com/mewkiz/flac/internal/hashutil/crc16" + "github.com/mewkiz/flac/internal/hashutil/crc8" + "github.com/mewkiz/flac/internal/utf8" + "github.com/mewkiz/pkg/errutil" +) + +// --- [ Frame ] --------------------------------------------------------------- + +// WriteFrame encodes the given audio frame to the output stream. The Num field +// of the frame header is automatically calculated by the encoder. +func (enc *Encoder) WriteFrame(f *frame.Frame) error { + // Sanity checks. + nchannels := int(enc.Info.NChannels) + if nchannels != len(f.Subframes) { + return errutil.Newf("subframe and channel count mismatch; expected %d, got %d", nchannels, len(f.Subframes)) + } + nsamplesPerChannel := f.Subframes[0].NSamples + for i, subframe := range f.Subframes { + if nsamplesPerChannel != len(subframe.Samples) { + return errutil.Newf("invalid number of samples in channel %d; expected %d, got %d", i, nsamplesPerChannel, len(subframe.Samples)) + } + } + if nchannels != f.Channels.Count() { + return errutil.Newf("channel count mismatch; expected %d, got %d", nchannels, f.Channels.Count()) + } + + // Create a new CRC-16 hash writer which adds the data from all write + // operations to a running hash. + h := crc16.NewIBM() + hw := io.MultiWriter(h, enc.w) + + // Encode frame header. + f.Num = enc.curNum + if f.HasFixedBlockSize { + enc.curNum++ + } else { + enc.curNum += uint64(nsamplesPerChannel) + } + enc.nsamples += uint64(nsamplesPerChannel) + blockSize := uint16(nsamplesPerChannel) + if enc.blockSizeMin == 0 || blockSize < enc.blockSizeMin { + enc.blockSizeMin = blockSize + } + if enc.blockSizeMax == 0 || blockSize > enc.blockSizeMax { + enc.blockSizeMax = blockSize + } + // TODO: track number of bytes written to hw, to update values of + // frameSizeMin and frameSizeMax. + // Add unencoded audio samples to running MD5 hash. + f.Hash(enc.md5sum) + if err := enc.encodeFrameHeader(hw, f.Header); err != nil { + return errutil.Err(err) + } + + // Inter-channel decorrelation of subframe samples. + f.Decorrelate() + defer f.Correlate() // NOTE: revert decorrelation of audio samples after encoding is done (to make encode non-destructive). + + // Encode subframes. + bw := bitio.NewWriter(hw) + for channel, subframe := range f.Subframes { + // The side channel requires an extra bit per sample when using + // inter-channel decorrelation. + bps := uint(f.BitsPerSample) + switch f.Channels { + case frame.ChannelsSideRight: + // channel 0 is the side channel. + if channel == 0 { + bps++ + } + case frame.ChannelsLeftSide, frame.ChannelsMidSide: + // channel 1 is the side channel. + if channel == 1 { + bps++ + } + } + + if err := encodeSubframe(bw, f.Header, subframe, bps); err != nil { + return errutil.Err(err) + } + } + + // Zero-padding to byte alignment. + // Flush pending writes to subframe. + if _, err := bw.Align(); err != nil { + return errutil.Err(err) + } + + // CRC-16 (polynomial = x^16 + x^15 + x^2 + x^0, initialized with 0) of + // everything before the crc, back to and including the frame header sync + // code. + crc := h.Sum16() + if err := binary.Write(enc.w, binary.BigEndian, crc); err != nil { + return errutil.Err(err) + } + + return nil +} + +// --- [ Frame header ] -------------------------------------------------------- + +// encodeFrameHeader encodes the given frame header, writing to w. +func (enc *Encoder) encodeFrameHeader(w io.Writer, hdr frame.Header) error { + // Create a new CRC-8 hash writer which adds the data from all write + // operations to a running hash. + h := crc8.NewATM() + hw := io.MultiWriter(h, w) + bw := bitio.NewWriter(hw) + + // Closing the *bitio.Writer will not close the underlying writer + defer bw.Close() + + // Sync code: 11111111111110 + if err := bw.WriteBits(0x3FFE, 14); err != nil { + return errutil.Err(err) + } + + // Reserved: 0 + if err := bw.WriteBits(0x0, 1); err != nil { + return errutil.Err(err) + } + + // Blocking strategy: + // 0 : fixed-blocksize stream; frame header encodes the frame number + // 1 : variable-blocksize stream; frame header encodes the sample number + if err := bw.WriteBool(!hdr.HasFixedBlockSize); err != nil { + return errutil.Err(err) + } + + // Encode block size. + nblockSizeSuffixBits, err := encodeFrameHeaderBlockSize(bw, hdr.BlockSize) + if err != nil { + return errutil.Err(err) + } + + // Encode sample rate. + sampleRateSuffixBits, nsampleRateSuffixBits, err := encodeFrameHeaderSampleRate(bw, hdr.SampleRate) + if err != nil { + return errutil.Err(err) + } + + // Encode channels assignment. + if err := encodeFrameHeaderChannels(bw, hdr.Channels); err != nil { + return errutil.Err(err) + } + + // Encode bits-per-sample. + if err := encodeFrameHeaderBitsPerSample(bw, hdr.BitsPerSample); err != nil { + return errutil.Err(err) + } + + // Reserved: 0 + if err := bw.WriteBits(0x0, 1); err != nil { + return errutil.Err(err) + } + + // if (variable blocksize) + // <8-56>:"UTF-8" coded sample number (decoded number is 36 bits) + // else + // <8-48>:"UTF-8" coded frame number (decoded number is 31 bits) + if err := utf8.Encode(bw, hdr.Num); err != nil { + return errutil.Err(err) + } + + // Write block size after the frame header (used for uncommon block sizes). + if nblockSizeSuffixBits > 0 { + // 0110 : get 8 bit (blocksize-1) from end of header + // 0111 : get 16 bit (blocksize-1) from end of header + if err := bw.WriteBits(uint64(hdr.BlockSize-1), nblockSizeSuffixBits); err != nil { + return errutil.Err(err) + } + } + + // Write sample rate after the frame header (used for uncommon sample rates). + if nsampleRateSuffixBits > 0 { + if err := bw.WriteBits(sampleRateSuffixBits, nsampleRateSuffixBits); err != nil { + return errutil.Err(err) + } + } + + // Flush pending writes to frame header. + if _, err := bw.Align(); err != nil { + return errutil.Err(err) + } + + // CRC-8 (polynomial = x^8 + x^2 + x^1 + x^0, initialized with 0) of + // everything before the crc, including the sync code. + crc := h.Sum8() + if err := binary.Write(w, binary.BigEndian, crc); err != nil { + return errutil.Err(err) + } + + return nil +} + +// ~~~ [ Block size ] ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +// encodeFrameHeaderBlockSize encodes the block size of the frame header, +// writing to bw. It returns the number of bits used to store block size after +// the frame header. +func encodeFrameHeaderBlockSize(bw *bitio.Writer, blockSize uint16) (nblockSizeSuffixBits byte, err error) { + // Block size in inter-channel samples: + // 0000 : reserved + // 0001 : 192 samples + // 0010-0101 : 576 * (2^(n-2)) samples, i.e. 576/1152/2304/4608 + // 0110 : get 8 bit (blocksize-1) from end of header + // 0111 : get 16 bit (blocksize-1) from end of header + // 1000-1111 : 256 * (2^(n-8)) samples, i.e. 256/512/1024/2048/4096/8192/16384/32768 + var bits uint64 + switch blockSize { + case 192: + // 0001 + bits = 0x1 + case 576, 1152, 2304, 4608: + // 0010-0101 : 576 * (2^(n-2)) samples, i.e. 576/1152/2304/4608 + bits = 0x2 + uint64(math.Log2(float64(blockSize/576))) + case 256, 512, 1024, 2048, 4096, 8192, 16384, 32768: + // 1000-1111 : 256 * (2^(n-8)) samples, i.e. 256/512/1024/2048/4096/8192/16384/32768 + bits = 0x8 + uint64(math.Log2(float64(blockSize/256))) + default: + if blockSize <= 256 { + // 0110 : get 8 bit (blocksize-1) from end of header + bits = 0x6 + nblockSizeSuffixBits = 8 + } else { + // 0111 : get 16 bit (blocksize-1) from end of header + bits = 0x7 + nblockSizeSuffixBits = 16 + } + } + if err := bw.WriteBits(bits, 4); err != nil { + return 0, errutil.Err(err) + } + return nblockSizeSuffixBits, nil +} + +// ~~~ [ Sample rate ] ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +// encodeFrameHeaderSampleRate encodes the sample rate of the frame header, +// writing to bw. It returns the bits and the number of bits used to store +// sample rate after the frame header. +func encodeFrameHeaderSampleRate(bw *bitio.Writer, sampleRate uint32) (sampleRateSuffixBits uint64, nsampleRateSuffixBits byte, err error) { + // Sample rate: + // 0000 : get from STREAMINFO metadata block + // 0001 : 88.2kHz + // 0010 : 176.4kHz + // 0011 : 192kHz + // 0100 : 8kHz + // 0101 : 16kHz + // 0110 : 22.05kHz + // 0111 : 24kHz + // 1000 : 32kHz + // 1001 : 44.1kHz + // 1010 : 48kHz + // 1011 : 96kHz + // 1100 : get 8 bit sample rate (in kHz) from end of header + // 1101 : get 16 bit sample rate (in Hz) from end of header + // 1110 : get 16 bit sample rate (in tens of Hz) from end of header + // 1111 : invalid, to prevent sync-fooling string of 1s + var bits uint64 + switch sampleRate { + case 0: + // 0000 : get from STREAMINFO metadata block + bits = 0 + case 88200: + // 0001 : 88.2kHz + bits = 0x1 + case 176400: + // 0010 : 176.4kHz + bits = 0x2 + case 192000: + // 0011 : 192kHz + bits = 0x3 + case 8000: + // 0100 : 8kHz + bits = 0x4 + case 16000: + // 0101 : 16kHz + bits = 0x5 + case 22050: + // 0110 : 22.05kHz + bits = 0x6 + case 24000: + // 0111 : 24kHz + bits = 0x7 + case 32000: + // 1000 : 32kHz + bits = 0x8 + case 44100: + // 1001 : 44.1kHz + bits = 0x9 + case 48000: + // 1010 : 48kHz + bits = 0xA + case 96000: + // 1011 : 96kHz + bits = 0xB + default: + switch { + case sampleRate <= 255000 && sampleRate%1000 == 0: + // 1100 : get 8 bit sample rate (in kHz) from end of header + bits = 0xC + sampleRateSuffixBits = uint64(sampleRate / 1000) + nsampleRateSuffixBits = 8 + case sampleRate <= 65535: + // 1101 : get 16 bit sample rate (in Hz) from end of header + bits = 0xD + sampleRateSuffixBits = uint64(sampleRate) + nsampleRateSuffixBits = 16 + case sampleRate <= 655350 && sampleRate%10 == 0: + // 1110 : get 16 bit sample rate (in tens of Hz) from end of header + bits = 0xE + sampleRateSuffixBits = uint64(sampleRate / 10) + nsampleRateSuffixBits = 16 + default: + return 0, 0, errutil.Newf("unable to encode sample rate %v", sampleRate) + } + } + if err := bw.WriteBits(bits, 4); err != nil { + return 0, 0, errutil.Err(err) + } + return sampleRateSuffixBits, nsampleRateSuffixBits, nil +} + +// ~~~ [ Channels assignment ] ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +// encodeFrameHeaderChannels encodes the channels assignment of the frame +// header, writing to bw. +func encodeFrameHeaderChannels(bw *bitio.Writer, channels frame.Channels) error { + // Channel assignment. + // 0000-0111 : (number of independent channels)-1. Where defined, the channel order follows SMPTE/ITU-R recommendations. The assignments are as follows: + // 1 channel: mono + // 2 channels: left, right + // 3 channels: left, right, center + // 4 channels: front left, front right, back left, back right + // 5 channels: front left, front right, front center, back/surround left, back/surround right + // 6 channels: front left, front right, front center, LFE, back/surround left, back/surround right + // 7 channels: front left, front right, front center, LFE, back center, side left, side right + // 8 channels: front left, front right, front center, LFE, back left, back right, side left, side right + // 1000 : left/side stereo: channel 0 is the left channel, channel 1 is the side(difference) channel + // 1001 : right/side stereo: channel 0 is the side(difference) channel, channel 1 is the right channel + // 1010 : mid/side stereo: channel 0 is the mid(average) channel, channel 1 is the side(difference) channel + // 1011-1111 : reserved + var bits uint64 + switch channels { + case frame.ChannelsMono, frame.ChannelsLR, frame.ChannelsLRC, frame.ChannelsLRLsRs, frame.ChannelsLRCLsRs, frame.ChannelsLRCLfeLsRs, frame.ChannelsLRCLfeCsSlSr, frame.ChannelsLRCLfeLsRsSlSr: + // 1 channel: mono. + // 2 channels: left, right. + // 3 channels: left, right, center. + // 4 channels: left, right, left surround, right surround. + // 5 channels: left, right, center, left surround, right surround. + // 6 channels: left, right, center, LFE, left surround, right surround. + // 7 channels: left, right, center, LFE, center surround, side left, side right. + // 8 channels: left, right, center, LFE, left surround, right surround, side left, side right. + bits = uint64(channels.Count() - 1) + case frame.ChannelsLeftSide: + // 2 channels: left, side; using inter-channel decorrelation. + // 1000 : left/side stereo: channel 0 is the left channel, channel 1 is the side(difference) channel + bits = 0x8 + case frame.ChannelsSideRight: + // 2 channels: side, right; using inter-channel decorrelation. + // 1001 : right/side stereo: channel 0 is the side(difference) channel, channel 1 is the right channel + bits = 0x9 + case frame.ChannelsMidSide: + // 2 channels: mid, side; using inter-channel decorrelation. + // 1010 : mid/side stereo: channel 0 is the mid(average) channel, channel 1 is the side(difference) channel + bits = 0xA + default: + return errutil.Newf("support for channel assignment %v not yet implemented", channels) + } + if err := bw.WriteBits(bits, 4); err != nil { + return errutil.Err(err) + } + return nil +} + +// ~~~ [ Bits-per-sample ] ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +// encodeFrameHeaderBitsPerSample encodes the bits-per-sample of the frame +// header, writing to bw. +func encodeFrameHeaderBitsPerSample(bw *bitio.Writer, bps uint8) error { + // Sample size in bits: + // 000 : get from STREAMINFO metadata block + // 001 : 8 bits per sample + // 010 : 12 bits per sample + // 011 : reserved + // 100 : 16 bits per sample + // 101 : 20 bits per sample + // 110 : 24 bits per sample + // 111 : reserved + var bits uint64 + switch bps { + case 0: + // 000 : get from STREAMINFO metadata block + bits = 0x0 + case 8: + // 001 : 8 bits per sample + bits = 0x1 + case 12: + // 010 : 12 bits per sample + bits = 0x2 + case 16: + // 100 : 16 bits per sample + bits = 0x4 + case 20: + // 101 : 20 bits per sample + bits = 0x5 + case 24: + // 110 : 24 bits per sample + bits = 0x6 + default: + return errutil.Newf("support for sample size %v not yet implemented", bps) + } + if err := bw.WriteBits(bits, 3); err != nil { + return errutil.Err(err) + } + return nil +} diff --git a/encode_meta.go b/encode_meta.go new file mode 100755 index 0000000..2a7edfe --- /dev/null +++ b/encode_meta.go @@ -0,0 +1,419 @@ +package flac + +import ( + "encoding/binary" + "fmt" + "io" + + "github.com/icza/bitio" + "github.com/mewkiz/flac/internal/ioutilx" + "github.com/mewkiz/flac/meta" + "github.com/mewkiz/pkg/errutil" +) + +// --- [ Metadata block ] ------------------------------------------------------ + +// encodeBlock encodes the metadata block, writing to bw. +func encodeBlock(bw *bitio.Writer, block *meta.Block, last bool) error { + if block.Type == meta.TypePadding { + return encodePadding(bw, block.Length, last) + } + if block.Length == 0 { + return encodeEmptyBlock(bw, block.Type, last) + } + switch body := block.Body.(type) { + case *meta.StreamInfo: + return encodeStreamInfo(bw, body, last) + case *meta.Application: + return encodeApplication(bw, body, last) + case *meta.SeekTable: + return encodeSeekTable(bw, body, last) + case *meta.VorbisComment: + return encodeVorbisComment(bw, body, last) + case *meta.CueSheet: + return encodeCueSheet(bw, body, last) + case *meta.Picture: + return encodePicture(bw, body, last) + default: + panic(fmt.Errorf("support for metadata block body type %T not yet implemented", body)) + } +} + +// --- [ Metadata block header ] ----------------------------------------------- + +// encodeEmptyBlock encodes the metadata block header of an empty metadata +// block with the specified type, writing to bw. +func encodeEmptyBlock(bw *bitio.Writer, typ meta.Type, last bool) error { + // Store metadata block header. + hdr := &meta.Header{ + IsLast: last, + Type: typ, + Length: 0, + } + if err := encodeBlockHeader(bw, hdr); err != nil { + return errutil.Err(err) + } + return nil +} + +// --- [ Metadata block header ] ----------------------------------------------- + +// encodeBlockHeader encodes the metadata block header, writing to bw. +func encodeBlockHeader(bw *bitio.Writer, hdr *meta.Header) error { + // 1 bit: IsLast. + if err := bw.WriteBool(hdr.IsLast); err != nil { + return errutil.Err(err) + } + // 7 bits: Type. + if err := bw.WriteBits(uint64(hdr.Type), 7); err != nil { + return errutil.Err(err) + } + // 24 bits: Length. + if err := bw.WriteBits(uint64(hdr.Length), 24); err != nil { + return errutil.Err(err) + } + return nil +} + +// --- [ StreamInfo ] ---------------------------------------------------------- + +// encodeStreamInfo encodes the StreamInfo metadata block, writing to bw. +func encodeStreamInfo(bw *bitio.Writer, info *meta.StreamInfo, last bool) error { + // Store metadata block header. + const nbits = 16 + 16 + 24 + 24 + 20 + 3 + 5 + 36 + 8*16 + hdr := &meta.Header{ + IsLast: last, + Type: meta.TypeStreamInfo, + Length: nbits / 8, + } + if err := encodeBlockHeader(bw, hdr); err != nil { + return errutil.Err(err) + } + + // Store metadata block body. + // 16 bits: BlockSizeMin. + if err := bw.WriteBits(uint64(info.BlockSizeMin), 16); err != nil { + return errutil.Err(err) + } + // 16 bits: BlockSizeMax. + if err := bw.WriteBits(uint64(info.BlockSizeMax), 16); err != nil { + return errutil.Err(err) + } + // 24 bits: FrameSizeMin. + if err := bw.WriteBits(uint64(info.FrameSizeMin), 24); err != nil { + return errutil.Err(err) + } + // 24 bits: FrameSizeMax. + if err := bw.WriteBits(uint64(info.FrameSizeMax), 24); err != nil { + return errutil.Err(err) + } + // 20 bits: SampleRate. + if err := bw.WriteBits(uint64(info.SampleRate), 20); err != nil { + return errutil.Err(err) + } + // 3 bits: NChannels; stored as (number of channels) - 1. + if err := bw.WriteBits(uint64(info.NChannels-1), 3); err != nil { + return errutil.Err(err) + } + // 5 bits: BitsPerSample; stored as (bits-per-sample) - 1. + if err := bw.WriteBits(uint64(info.BitsPerSample-1), 5); err != nil { + return errutil.Err(err) + } + // 36 bits: NSamples. + if err := bw.WriteBits(info.NSamples, 36); err != nil { + return errutil.Err(err) + } + // 16 bytes: MD5sum. + if _, err := bw.Write(info.MD5sum[:]); err != nil { + return errutil.Err(err) + } + return nil +} + +// --- [ Padding ] ---------------------------------------------------------- + +// encodePadding encodes the Padding metadata block, writing to bw. +func encodePadding(bw *bitio.Writer, length int64, last bool) error { + // Store metadata block header. + hdr := &meta.Header{ + IsLast: last, + Type: meta.TypePadding, + Length: length, + } + if err := encodeBlockHeader(bw, hdr); err != nil { + return errutil.Err(err) + } + // Store metadata block body. + if _, err := io.CopyN(bw, ioutilx.Zero, length); err != nil { + return errutil.Err(err) + } + return nil +} + +// --- [ Application ] --------------------------------------------------------- + +// encodeApplication encodes the Application metadata block, writing to bw. +func encodeApplication(bw *bitio.Writer, app *meta.Application, last bool) error { + // Store metadata block header. + nbits := int64(32 + 8*len(app.Data)) + hdr := &meta.Header{ + IsLast: last, + Type: meta.TypeApplication, + Length: nbits / 8, + } + if err := encodeBlockHeader(bw, hdr); err != nil { + return errutil.Err(err) + } + + // Store metadata block body. + // 32 bits: ID. + if err := bw.WriteBits(uint64(app.ID), 32); err != nil { + return errutil.Err(err) + } + // TODO: check if the Application block may contain only an ID. + if _, err := bw.Write(app.Data); err != nil { + return errutil.Err(err) + } + return nil +} + +// --- [ SeekTable ] ----------------------------------------------------------- + +// encodeSeekTable encodes the SeekTable metadata block, writing to bw. +func encodeSeekTable(bw *bitio.Writer, table *meta.SeekTable, last bool) error { + // Store metadata block header. + nbits := int64((64 + 64 + 16) * len(table.Points)) + hdr := &meta.Header{ + IsLast: last, + Type: meta.TypeSeekTable, + Length: nbits / 8, + } + if err := encodeBlockHeader(bw, hdr); err != nil { + return errutil.Err(err) + } + + // Store metadata block body. + for _, point := range table.Points { + if err := binary.Write(bw, binary.BigEndian, point); err != nil { + return errutil.Err(err) + } + } + return nil +} + +// --- [ VorbisComment ] ------------------------------------------------------- + +// encodeVorbisComment encodes the VorbisComment metadata block, writing to bw. +func encodeVorbisComment(bw *bitio.Writer, comment *meta.VorbisComment, last bool) error { + // Store metadata block header. + nbits := int64(32 + 8*len(comment.Vendor) + 32) + for _, tag := range comment.Tags { + nbits += int64(32 + 8*(len(tag[0])+1+len(tag[1]))) + } + hdr := &meta.Header{ + IsLast: last, + Type: meta.TypeVorbisComment, + Length: nbits / 8, + } + if err := encodeBlockHeader(bw, hdr); err != nil { + return errutil.Err(err) + } + + // Store metadata block body. + // 32 bits: vendor length. + // TODO: verify that little-endian encoding is used; otherwise, switch to + // using bw.WriteBits. + if err := binary.Write(bw, binary.LittleEndian, uint32(len(comment.Vendor))); err != nil { + return errutil.Err(err) + } + // (vendor length) bits: Vendor. + if _, err := bw.Write([]byte(comment.Vendor)); err != nil { + return errutil.Err(err) + } + // Store tags. + // 32 bits: number of tags. + if err := binary.Write(bw, binary.LittleEndian, uint32(len(comment.Tags))); err != nil { + return errutil.Err(err) + } + for _, tag := range comment.Tags { + // Store tag, which has the following format: + // NAME=VALUE + buf := []byte(fmt.Sprintf("%s=%s", tag[0], tag[1])) + // 32 bits: vector length + if err := binary.Write(bw, binary.LittleEndian, uint32(len(buf))); err != nil { + return errutil.Err(err) + } + // (vector length): vector. + if _, err := bw.Write(buf); err != nil { + return errutil.Err(err) + } + } + return nil +} + +// --- [ CueSheet ] ------------------------------------------------------------ + +// encodeCueSheet encodes the CueSheet metadata block, writing to bw. +func encodeCueSheet(bw *bitio.Writer, cs *meta.CueSheet, last bool) error { + // Store metadata block header. + nbits := int64(8*128 + 64 + 1 + 7 + 8*258 + 8) + for _, track := range cs.Tracks { + nbits += 64 + 8 + 8*12 + 1 + 1 + 6 + 8*13 + 8 + for range track.Indicies { + nbits += 64 + 8 + 8*3 + } + } + hdr := &meta.Header{ + IsLast: last, + Type: meta.TypeCueSheet, + Length: nbits / 8, + } + if err := encodeBlockHeader(bw, hdr); err != nil { + return errutil.Err(err) + } + + // Store metadata block body. + // Store cue sheet. + // 128 bytes: MCN. + var mcn [128]byte + copy(mcn[:], cs.MCN) + if _, err := bw.Write(mcn[:]); err != nil { + return errutil.Err(err) + } + // 64 bits: NLeadInSamples. + if err := bw.WriteBits(cs.NLeadInSamples, 64); err != nil { + return errutil.Err(err) + } + // 1 bit: IsCompactDisc. + if err := bw.WriteBool(cs.IsCompactDisc); err != nil { + return errutil.Err(err) + } + // 7 bits and 258 bytes: reserved. + if err := bw.WriteBits(0, 7); err != nil { + return errutil.Err(err) + } + if _, err := io.CopyN(bw, ioutilx.Zero, 258); err != nil { + return errutil.Err(err) + } + // Store cue sheet tracks. + // 8 bits: (number of tracks) + if err := bw.WriteBits(uint64(len(cs.Tracks)), 8); err != nil { + return errutil.Err(err) + } + for _, track := range cs.Tracks { + // 64 bits: Offset. + if err := bw.WriteBits(track.Offset, 64); err != nil { + return errutil.Err(err) + } + // 8 bits: Num. + if err := bw.WriteBits(uint64(track.Num), 8); err != nil { + return errutil.Err(err) + } + // 12 bytes: ISRC. + var isrc [12]byte + copy(isrc[:], track.ISRC) + if _, err := bw.Write(isrc[:]); err != nil { + return errutil.Err(err) + } + // 1 bit: IsAudio. + if err := bw.WriteBool(!track.IsAudio); err != nil { + return errutil.Err(err) + } + // 1 bit: HasPreEmphasis. + // mask = 01000000 + if err := bw.WriteBool(track.HasPreEmphasis); err != nil { + return errutil.Err(err) + } + // 6 bits and 13 bytes: reserved. + // mask = 00111111 + if err := bw.WriteBits(0, 6); err != nil { + return errutil.Err(err) + } + if _, err := io.CopyN(bw, ioutilx.Zero, 13); err != nil { + return errutil.Err(err) + } + // Store indicies. + // 8 bits: (number of indicies) + if err := bw.WriteBits(uint64(len(track.Indicies)), 8); err != nil { + return errutil.Err(err) + } + for _, index := range track.Indicies { + // 64 bits: Offset. + if err := bw.WriteBits(index.Offset, 64); err != nil { + return errutil.Err(err) + } + // 8 bits: Num. + if err := bw.WriteBits(uint64(index.Num), 8); err != nil { + return errutil.Err(err) + } + // 3 bytes: reserved. + if _, err := io.CopyN(bw, ioutilx.Zero, 3); err != nil { + return errutil.Err(err) + } + } + } + return nil +} + +// --- [ Picture ] ------------------------------------------------------------- + +// encodePicture encodes the Picture metadata block, writing to bw. +func encodePicture(bw *bitio.Writer, pic *meta.Picture, last bool) error { + // Store metadata block header. + nbits := int64(32 + 32 + 8*len(pic.MIME) + 32 + 8*len(pic.Desc) + 32 + 32 + 32 + 32 + 32 + 8*len(pic.Data)) + hdr := &meta.Header{ + IsLast: last, + Type: meta.TypePicture, + Length: nbits / 8, + } + if err := encodeBlockHeader(bw, hdr); err != nil { + return errutil.Err(err) + } + + // Store metadata block body. + // 32 bits: Type. + if err := bw.WriteBits(uint64(pic.Type), 32); err != nil { + return errutil.Err(err) + } + // 32 bits: (MIME type length). + if err := bw.WriteBits(uint64(len(pic.MIME)), 32); err != nil { + return errutil.Err(err) + } + // (MIME type length) bytes: MIME. + if _, err := bw.Write([]byte(pic.MIME)); err != nil { + return errutil.Err(err) + } + // 32 bits: (description length). + if err := bw.WriteBits(uint64(len(pic.Desc)), 32); err != nil { + return errutil.Err(err) + } + // (description length) bytes: Desc. + if _, err := bw.Write([]byte(pic.Desc)); err != nil { + return errutil.Err(err) + } + // 32 bits: Width. + if err := bw.WriteBits(uint64(pic.Width), 32); err != nil { + return errutil.Err(err) + } + // 32 bits: Height. + if err := bw.WriteBits(uint64(pic.Height), 32); err != nil { + return errutil.Err(err) + } + // 32 bits: Depth. + if err := bw.WriteBits(uint64(pic.Depth), 32); err != nil { + return errutil.Err(err) + } + // 32 bits: NPalColors. + if err := bw.WriteBits(uint64(pic.NPalColors), 32); err != nil { + return errutil.Err(err) + } + // 32 bits: (data length). + if err := bw.WriteBits(uint64(len(pic.Data)), 32); err != nil { + return errutil.Err(err) + } + // (data length) bytes: Data. + if _, err := bw.Write(pic.Data); err != nil { + return errutil.Err(err) + } + return nil +} diff --git a/encode_subframe.go b/encode_subframe.go new file mode 100755 index 0000000..f74b0d4 --- /dev/null +++ b/encode_subframe.go @@ -0,0 +1,369 @@ +package flac + +import ( + "fmt" + + "github.com/icza/bitio" + "github.com/mewkiz/flac/frame" + iobits "github.com/mewkiz/flac/internal/bits" + "github.com/mewkiz/pkg/errutil" +) + +// --- [ Subframe ] ------------------------------------------------------------ + +// encodeSubframe encodes the given subframe, writing to bw. +func encodeSubframe(bw *bitio.Writer, hdr frame.Header, subframe *frame.Subframe, bps uint) error { + // Encode subframe header. + if err := encodeSubframeHeader(bw, subframe.SubHeader); err != nil { + return errutil.Err(err) + } + + // Adjust bps of subframe for wasted bits-per-sample. + bps -= subframe.Wasted + + // Right shift to account for wasted bits-per-sample. + if subframe.Wasted > 0 { + for i, sample := range subframe.Samples { + subframe.Samples[i] = sample >> subframe.Wasted + } + // NOTE: use defer to restore original samples after encode. + defer func() { + for i, sample := range subframe.Samples { + subframe.Samples[i] = sample << subframe.Wasted + } + }() + } + + // Encode audio samples. + switch subframe.Pred { + case frame.PredConstant: + if err := encodeConstantSamples(bw, hdr, subframe, bps); err != nil { + return errutil.Err(err) + } + case frame.PredVerbatim: + if err := encodeVerbatimSamples(bw, hdr, subframe, bps); err != nil { + return errutil.Err(err) + } + case frame.PredFixed: + if err := encodeFixedSamples(bw, hdr, subframe, bps); err != nil { + return errutil.Err(err) + } + case frame.PredFIR: + if err := encodeFIRSamples(bw, hdr, subframe, bps); err != nil { + return errutil.Err(err) + } + default: + return errutil.Newf("support for prediction method %v not yet implemented", subframe.Pred) + } + return nil +} + +// --- [ Subframe header ] ----------------------------------------------------- + +// encodeSubframeHeader encodes the given subframe header, writing to bw. +func encodeSubframeHeader(bw *bitio.Writer, subHdr frame.SubHeader) error { + // Zero bit padding, to prevent sync-fooling string of 1s. + if err := bw.WriteBits(0x0, 1); err != nil { + return errutil.Err(err) + } + + // Subframe type: + // 000000 : SUBFRAME_CONSTANT + // 000001 : SUBFRAME_VERBATIM + // 00001x : reserved + // 0001xx : reserved + // 001xxx : if(xxx <= 4) SUBFRAME_FIXED, xxx=order ; else reserved + // 01xxxx : reserved + // 1xxxxx : SUBFRAME_LPC, xxxxx=order-1 + var bits uint64 + switch subHdr.Pred { + case frame.PredConstant: + // 000000 : SUBFRAME_CONSTANT + bits = 0x00 + case frame.PredVerbatim: + // 000001 : SUBFRAME_VERBATIM + bits = 0x01 + case frame.PredFixed: + // 001xxx : if(xxx <= 4) SUBFRAME_FIXED, xxx=order ; else reserved + bits = 0x08 | uint64(subHdr.Order) + case frame.PredFIR: + // 1xxxxx : SUBFRAME_LPC, xxxxx=order-1 + bits = 0x20 | uint64(subHdr.Order-1) + } + if err := bw.WriteBits(bits, 6); err != nil { + return errutil.Err(err) + } + + // <1+k> 'Wasted bits-per-sample' flag: + // + // 0 : no wasted bits-per-sample in source subblock, k=0 + // 1 : k wasted bits-per-sample in source subblock, k-1 follows, unary coded; e.g. k=3 => 001 follows, k=7 => 0000001 follows. + hasWastedBits := subHdr.Wasted > 0 + if err := bw.WriteBool(hasWastedBits); err != nil { + return errutil.Err(err) + } + if hasWastedBits { + if err := iobits.WriteUnary(bw, uint64(subHdr.Wasted-1)); err != nil { + return errutil.Err(err) + } + } + return nil +} + +// --- [ Constant samples ] ---------------------------------------------------- + +// encodeConstantSamples stores the given constant sample, writing to bw. +func encodeConstantSamples(bw *bitio.Writer, hdr frame.Header, subframe *frame.Subframe, bps uint) error { + samples := subframe.Samples + sample := samples[0] + for _, s := range samples[1:] { + if sample != s { + return errutil.Newf("constant sample mismatch; expected %v, got %v", sample, s) + } + } + // Unencoded constant value of the subblock, n = frame's bits-per-sample. + if err := bw.WriteBits(uint64(sample), uint8(bps)); err != nil { + return errutil.Err(err) + } + return nil +} + +// --- [ Verbatim samples ] ---------------------------------------------------- + +// encodeVerbatimSamples stores the given samples verbatim (uncompressed), +// writing to bw. +func encodeVerbatimSamples(bw *bitio.Writer, hdr frame.Header, subframe *frame.Subframe, bps uint) error { + // Unencoded subblock; n = frame's bits-per-sample, i = frame's blocksize. + samples := subframe.Samples + if int(hdr.BlockSize) != len(samples) { + return errutil.Newf("block size and sample count mismatch; expected %d, got %d", hdr.BlockSize, len(samples)) + } + for _, sample := range samples { + if err := bw.WriteBits(uint64(sample), uint8(bps)); err != nil { + return errutil.Err(err) + } + } + return nil +} + +// --- [ Fixed samples ] ------------------------------------------------------- + +// encodeFixedSamples stores the given samples using linear prediction coding +// with a fixed set of predefined polynomial coefficients, writing to bw. +func encodeFixedSamples(bw *bitio.Writer, hdr frame.Header, subframe *frame.Subframe, bps uint) error { + // Encode unencoded warm-up samples. + samples := subframe.Samples + for i := 0; i < subframe.Order; i++ { + sample := samples[i] + if err := bw.WriteBits(uint64(sample), uint8(bps)); err != nil { + return errutil.Err(err) + } + } + + // Compute residuals (signal errors of the prediction) between audio + // samples and LPC predicted audio samples. + const shift = 0 + residuals, err := getLPCResiduals(subframe, frame.FixedCoeffs[subframe.Order], shift) + if err != nil { + return errutil.Err(err) + } + + // Encode subframe residuals. + if err := encodeResiduals(bw, subframe, residuals); err != nil { + return errutil.Err(err) + } + return nil +} + +// --- [ FIR samples ] ------------------------------------------------------- + +// encodeFIRSamples stores the given samples using linear prediction coding +// with a custom set of predefined polynomial coefficients, writing to bw. +func encodeFIRSamples(bw *bitio.Writer, hdr frame.Header, subframe *frame.Subframe, bps uint) error { + // Encode unencoded warm-up samples. + samples := subframe.Samples + for i := 0; i < subframe.Order; i++ { + sample := samples[i] + if err := bw.WriteBits(uint64(sample), uint8(bps)); err != nil { + return errutil.Err(err) + } + } + + // 4 bits: (coefficients' precision in bits) - 1. + if err := bw.WriteBits(uint64(subframe.CoeffPrec-1), 4); err != nil { + return errutil.Err(err) + } + + // 5 bits: predictor coefficient shift needed in bits. + if err := bw.WriteBits(uint64(subframe.CoeffShift), 5); err != nil { + return errutil.Err(err) + } + + // Encode coefficients. + for _, coeff := range subframe.Coeffs { + // (prec) bits: Predictor coefficient. + if err := bw.WriteBits(uint64(coeff), uint8(subframe.CoeffPrec)); err != nil { + return errutil.Err(err) + } + } + + // Compute residuals (signal errors of the prediction) between audio + // samples and LPC predicted audio samples. + residuals, err := getLPCResiduals(subframe, subframe.Coeffs, subframe.CoeffShift) + if err != nil { + return errutil.Err(err) + } + + // Encode subframe residuals. + if err := encodeResiduals(bw, subframe, residuals); err != nil { + return errutil.Err(err) + } + return nil +} + +// encodeResiduals encodes the residuals (prediction method error signals) of the +// subframe. +// +// ref: https://www.xiph.org/flac/format.html#residual +func encodeResiduals(bw *bitio.Writer, subframe *frame.Subframe, residuals []int32) error { + // 2 bits: Residual coding method. + if err := bw.WriteBits(uint64(subframe.ResidualCodingMethod), 2); err != nil { + return errutil.Err(err) + } + // The 2 bits are used to specify the residual coding method as follows: + // 00: Rice coding with a 4-bit Rice parameter. + // 01: Rice coding with a 5-bit Rice parameter. + // 10: reserved. + // 11: reserved. + switch subframe.ResidualCodingMethod { + case frame.ResidualCodingMethodRice1: + return encodeRicePart(bw, subframe, 4, residuals) + case frame.ResidualCodingMethodRice2: + return encodeRicePart(bw, subframe, 5, residuals) + default: + return fmt.Errorf("encodeResiduals: reserved residual coding method bit pattern (%02b)", uint8(subframe.ResidualCodingMethod)) + } +} + +// encodeRicePart encodes a Rice partition of residuals from the subframe, using +// a Rice parameter of the specified size in bits. +// +// ref: https://www.xiph.org/flac/format.html#partitioned_rice +// ref: https://www.xiph.org/flac/format.html#partitioned_rice2 +func encodeRicePart(bw *bitio.Writer, subframe *frame.Subframe, paramSize uint, residuals []int32) error { + // 4 bits: Partition order. + riceSubframe := subframe.RiceSubframe + if err := bw.WriteBits(uint64(riceSubframe.PartOrder), 4); err != nil { + return errutil.Err(err) + } + + // Parse Rice partitions; in total 2^partOrder partitions. + // + // ref: https://www.xiph.org/flac/format.html#rice_partition + // ref: https://www.xiph.org/flac/format.html#rice2_partition + partOrder := riceSubframe.PartOrder + nparts := 1 << partOrder + curResidualIndex := 0 + for i := range riceSubframe.Partitions { + partition := &riceSubframe.Partitions[i] + // (4 or 5) bits: Rice parameter. + param := partition.Param + if err := bw.WriteBits(uint64(param), uint8(paramSize)); err != nil { + return errutil.Err(err) + } + + // Determine the number of Rice encoded samples in the partition. + var nsamples int + if partOrder == 0 { + nsamples = subframe.NSamples - subframe.Order + } else if i != 0 { + nsamples = subframe.NSamples / nparts + } else { + nsamples = subframe.NSamples/nparts - subframe.Order + } + + if paramSize == 4 && param == 0xF || paramSize == 5 && param == 0x1F { + // 1111 or 11111: Escape code, meaning the partition is in unencoded + // binary form using n bits per sample; n follows as a 5-bit number. + if err := bw.WriteBits(uint64(partition.EscapedBitsPerSample), 5); err != nil { + return errutil.Err(err) + } + for j := 0; j < nsamples; j++ { + // ref: https://datatracker.ietf.org/doc/draft-ietf-cellar-flac/ + // + // From section 9.2.7.1. Escaped partition: + // + // The residual samples themselves are stored signed two's + // complement. For example, when a partition is escaped and each + // residual sample is stored with 3 bits, the number -1 is + // represented as 0b111. + residual := residuals[curResidualIndex] + curResidualIndex++ + if err := bw.WriteBits(uint64(residual), uint8(partition.EscapedBitsPerSample)); err != nil { + return errutil.Err(err) + } + } + continue + } + + // Encode the Rice residuals of the partition. + for j := 0; j < nsamples; j++ { + residual := residuals[curResidualIndex] + curResidualIndex++ + if err := encodeRiceResidual(bw, param, residual); err != nil { + return errutil.Err(err) + } + } + } + + return nil +} + +// encodeRiceResidual encodes a Rice residual (error signal). +func encodeRiceResidual(bw *bitio.Writer, k uint, residual int32) error { + // ZigZag encode. + folded := iobits.EncodeZigZag(residual) + + // unfold into low- and high. + lowMask := ^uint32(0) >> (32 - k) // lower k bits. + highMask := ^uint32(0) << k // upper bits. + high := (folded & highMask) >> k + low := folded & lowMask + + // Write unary encoded most significant bits. + if err := iobits.WriteUnary(bw, uint64(high)); err != nil { + return errutil.Err(err) + } + + // Write binary encoded least significant bits. + if err := bw.WriteBits(uint64(low), uint8(k)); err != nil { + return errutil.Err(err) + } + return nil +} + +// getLPCResiduals returns the residuals (signal errors of the prediction) +// between the given audio samples and the LPC predicted audio samples, using +// the coefficients of a given polynomial, and a couple (order of polynomial; +// i.e. len(coeffs)) of unencoded warm-up samples. +func getLPCResiduals(subframe *frame.Subframe, coeffs []int32, shift int32) ([]int32, error) { + if len(coeffs) != subframe.Order { + return nil, fmt.Errorf("getLPCResiduals: prediction order (%d) differs from number of coefficients (%d)", subframe.Order, len(coeffs)) + } + if shift < 0 { + return nil, fmt.Errorf("getLPCResiduals: invalid negative shift") + } + if subframe.NSamples != len(subframe.Samples) { + return nil, fmt.Errorf("getLPCResiduals: subframe sample count mismatch; expected %d, got %d", subframe.NSamples, len(subframe.Samples)) + } + var residuals []int32 + for i := subframe.Order; i < subframe.NSamples; i++ { + var sample int64 + for j, c := range coeffs { + sample += int64(c) * int64(subframe.Samples[i-j-1]) + } + residual := subframe.Samples[i] - int32(sample>>uint(shift)) + residuals = append(residuals, residual) + } + return residuals, nil +} diff --git a/example_test.go b/example_test.go new file mode 100755 index 0000000..55142c8 --- /dev/null +++ b/example_test.go @@ -0,0 +1,121 @@ +package flac_test + +import ( + "bytes" + "crypto/md5" + "fmt" + "io" + "log" + + "github.com/mewkiz/flac" +) + +func ExampleParseFile() { + // Parse metadata of love.flac + stream, err := flac.ParseFile("testdata/love.flac") + if err != nil { + log.Fatal(err) + } + defer stream.Close() + + fmt.Printf("unencoded audio md5sum: %032x\n", stream.Info.MD5sum[:]) + for i, block := range stream.Blocks { + fmt.Printf("block %d: %v\n", i, block.Type) + } + // Output: + // unencoded audio md5sum: bdf6f7d31f77cb696a02b2192d192a89 + // block 0: seek table + // block 1: vorbis comment + // block 2: padding +} + +func ExampleOpen() { + // Open love.flac for audio streaming without parsing metadata. + stream, err := flac.Open("testdata/love.flac") + if err != nil { + log.Fatal(err) + } + defer stream.Close() + + // Parse audio samples and verify the MD5 signature of the decoded audio + // samples. + md5sum := md5.New() + for { + // Parse one frame of audio samples at the time, each frame containing one + // subframe per audio channel. + frame, err := stream.ParseNext() + if err != nil { + if err == io.EOF { + break + } + log.Fatal(err) + } + frame.Hash(md5sum) + + // Print first three samples from each channel of the first five frames. + if frame.Num < 5 { + fmt.Printf("frame %d\n", frame.Num) + for i, subframe := range frame.Subframes { + fmt.Printf(" subframe %d\n", i) + for j, sample := range subframe.Samples { + if j >= 3 { + break + } + fmt.Printf(" sample %d: %v\n", j, sample) + } + } + } + } + fmt.Println() + + got, want := md5sum.Sum(nil), stream.Info.MD5sum[:] + fmt.Println("decoded audio md5sum valid:", bytes.Equal(got, want)) + // Output: + // frame 0 + // subframe 0 + // sample 0: 126 + // sample 1: 126 + // sample 2: 126 + // subframe 1 + // sample 0: 126 + // sample 1: 126 + // sample 2: 126 + // frame 1 + // subframe 0 + // sample 0: 126 + // sample 1: 126 + // sample 2: 126 + // subframe 1 + // sample 0: 126 + // sample 1: 126 + // sample 2: 126 + // frame 2 + // subframe 0 + // sample 0: 121 + // sample 1: 130 + // sample 2: 137 + // subframe 1 + // sample 0: 121 + // sample 1: 130 + // sample 2: 137 + // frame 3 + // subframe 0 + // sample 0: -9501 + // sample 1: -6912 + // sample 2: -3916 + // subframe 1 + // sample 0: -9501 + // sample 1: -6912 + // sample 2: -3916 + // frame 4 + // subframe 0 + // sample 0: 513 + // sample 1: 206 + // sample 2: 152 + // subframe 1 + // sample 0: 513 + // sample 1: 206 + // sample 2: 152 + // + // decoded audio md5sum valid: true +} diff --git a/flac.go b/flac.go new file mode 100755 index 0000000..d96f53e --- /dev/null +++ b/flac.go @@ -0,0 +1,426 @@ +// TODO(u): Evaluate storing the samples (and residuals) during frame audio +// decoding in a buffer allocated for the stream. This buffer would be allocated +// using BlockSize and NChannels from the StreamInfo block, and it could be +// reused in between calls to Next and ParseNext. This should reduce GC +// pressure. + +// TODO: Remove note about encoder API. + +// Package flac provides access to FLAC (Free Lossless Audio Codec) streams. +// +// A brief introduction of the FLAC stream format [1] follows. Each FLAC stream +// starts with a 32-bit signature ("fLaC"), followed by one or more metadata +// blocks, and then one or more audio frames. The first metadata block +// (StreamInfo) describes the basic properties of the audio stream and it is the +// only mandatory metadata block. Subsequent metadata blocks may appear in an +// arbitrary order. +// +// Please refer to the documentation of the meta [2] and the frame [3] packages +// for a brief introduction of their respective formats. +// +// [1]: https://www.xiph.org/flac/format.html#stream +// [2]: https://godoc.org/github.com/mewkiz/flac/meta +// [3]: https://godoc.org/github.com/mewkiz/flac/frame +// +// Note: the Encoder API is experimental until the 1.1.x release. As such, it's +// API is expected to change. +package flac + +import ( + "bufio" + "bytes" + "errors" + "fmt" + "io" + "os" + + "github.com/mewkiz/flac/frame" + "github.com/mewkiz/flac/internal/bufseekio" + "github.com/mewkiz/flac/meta" +) + +// A Stream contains the metadata blocks and provides access to the audio frames +// of a FLAC stream. +// +// ref: https://www.xiph.org/flac/format.html#stream +type Stream struct { + // The StreamInfo metadata block describes the basic properties of the FLAC + // audio stream. + Info *meta.StreamInfo + // Zero or more metadata blocks. + Blocks []*meta.Block + + // seekTable contains one or more pre-calculated audio frame seek points of + // the stream; nil if uninitialized. + seekTable *meta.SeekTable + // seekTableSize determines how many seek points the seekTable should have if + // the flac file does not include one in the metadata. + seekTableSize int + // dataStart is the offset of the first frame header since SeekPoint.Offset + // is relative to this position. + dataStart int64 + + // Underlying io.Reader, or io.ReadCloser. + r io.Reader +} + +// New creates a new Stream for accessing the audio samples of r. It reads and +// parses the FLAC signature and the StreamInfo metadata block, but skips all +// other metadata blocks. +// +// Call Stream.Next to parse the frame header of the next audio frame, and call +// Stream.ParseNext to parse the entire next frame including audio samples. +func New(r io.Reader) (stream *Stream, err error) { + // Verify FLAC signature and parse the StreamInfo metadata block. + br := bufio.NewReader(r) + stream = &Stream{r: br} + block, err := stream.parseStreamInfo() + if err != nil { + return nil, err + } + + // Skip the remaining metadata blocks. + for !block.IsLast { + block, err = meta.New(br) + if err != nil && err != meta.ErrReservedType { + return stream, err + } + if err = block.Skip(); err != nil { + return stream, err + } + } + + return stream, nil +} + +// NewSeek returns a Stream that has seeking enabled. The incoming io.ReadSeeker +// will not be buffered, which might result in performance issues. Using an +// in-memory buffer like *bytes.Reader should work well. +func NewSeek(rs io.ReadSeeker) (stream *Stream, err error) { + br := bufseekio.NewReadSeeker(rs) + stream = &Stream{r: br, seekTableSize: defaultSeekTableSize} + + // Verify FLAC signature and parse the StreamInfo metadata block. + block, err := stream.parseStreamInfo() + if err != nil { + return stream, err + } + + for !block.IsLast { + block, err = meta.Parse(stream.r) + if err != nil { + if err != meta.ErrReservedType { + return stream, err + } + if err = block.Skip(); err != nil { + return stream, err + } + } + + if block.Header.Type == meta.TypeSeekTable { + stream.seekTable = block.Body.(*meta.SeekTable) + } + } + + // Record file offset of the first frame header. + stream.dataStart, err = br.Seek(0, io.SeekCurrent) + return stream, err +} + +var ( + // flacSignature marks the beginning of a FLAC stream. + flacSignature = []byte("fLaC") + + // id3Signature marks the beginning of an ID3 stream, used to skip over ID3 + // data. + id3Signature = []byte("ID3") + + // ErrNoSeeker reports that flac.NewSeek was called with an io.Reader not + // implementing io.Seeker, and thus does not allow for seeking. + ErrNoSeeker = errors.New("stream.Seek: reader does not implement io.Seeker") + + // ErrNoSeektable reports that no seektable has been generated. Therefore, + // it is not possible to seek in the stream. + ErrNoSeektable = errors.New("stream.searchFromStart: no seektable exists") +) + +const ( + defaultSeekTableSize = 100 +) + +// parseStreamInfo verifies the signature which marks the beginning of a FLAC +// stream, and parses the StreamInfo metadata block. It returns a boolean value +// which specifies if the StreamInfo block was the last metadata block of the +// FLAC stream. +func (stream *Stream) parseStreamInfo() (block *meta.Block, err error) { + // Verify FLAC signature. + r := stream.r + var buf [4]byte + if _, err = io.ReadFull(r, buf[:]); err != nil { + return block, err + } + + // Skip prepended ID3v2 data. + if bytes.Equal(buf[:3], id3Signature) { + if err := stream.skipID3v2(); err != nil { + return block, err + } + + // Second attempt at verifying signature. + if _, err = io.ReadFull(r, buf[:]); err != nil { + return block, err + } + } + + if !bytes.Equal(buf[:], flacSignature) { + return block, fmt.Errorf("flac.parseStreamInfo: invalid FLAC signature; expected %q, got %q", flacSignature, buf) + } + + // Parse StreamInfo metadata block. + block, err = meta.Parse(r) + if err != nil { + return block, err + } + si, ok := block.Body.(*meta.StreamInfo) + if !ok { + return block, fmt.Errorf("flac.parseStreamInfo: incorrect type of first metadata block; expected *meta.StreamInfo, got %T", block.Body) + } + stream.Info = si + return block, nil +} + +// skipID3v2 skips ID3v2 data prepended to flac files. +func (stream *Stream) skipID3v2() error { + r := bufio.NewReader(stream.r) + + // Discard unnecessary data from the ID3v2 header. + if _, err := r.Discard(2); err != nil { + return err + } + + // Read the size from the ID3v2 header. + var sizeBuf [4]byte + if _, err := r.Read(sizeBuf[:]); err != nil { + return err + } + // The size is encoded as a synchsafe integer. + size := int(sizeBuf[0])<<21 | int(sizeBuf[1])<<14 | int(sizeBuf[2])<<7 | int(sizeBuf[3]) + + _, err := r.Discard(size) + return err +} + +// Parse creates a new Stream for accessing the metadata blocks and audio +// samples of r. It reads and parses the FLAC signature and all metadata blocks. +// +// Call Stream.Next to parse the frame header of the next audio frame, and call +// Stream.ParseNext to parse the entire next frame including audio samples. +func Parse(r io.Reader) (stream *Stream, err error) { + // Verify FLAC signature and parse the StreamInfo metadata block. + br := bufio.NewReader(r) + stream = &Stream{r: br} + block, err := stream.parseStreamInfo() + if err != nil { + return nil, err + } + + // Parse the remaining metadata blocks. + for !block.IsLast { + block, err = meta.Parse(br) + if err != nil { + if err != meta.ErrReservedType { + return stream, err + } + // Skip the body of unknown (reserved) metadata blocks, as stated by + // the specification. + // + // ref: https://www.xiph.org/flac/format.html#format_overview + if err = block.Skip(); err != nil { + return stream, err + } + } + stream.Blocks = append(stream.Blocks, block) + } + + return stream, nil +} + +// Open creates a new Stream for accessing the audio samples of path. It reads +// and parses the FLAC signature and the StreamInfo metadata block, but skips +// all other metadata blocks. +// +// Call Stream.Next to parse the frame header of the next audio frame, and call +// Stream.ParseNext to parse the entire next frame including audio samples. +// +// Note: The Close method of the stream must be called when finished using it. +func Open(path string) (stream *Stream, err error) { + f, err := os.Open(path) + if err != nil { + return nil, err + } + + stream, err = New(f) + if err != nil { + return nil, err + } + + return stream, err +} + +// ParseFile creates a new Stream for accessing the metadata blocks and audio +// samples of path. It reads and parses the FLAC signature and all metadata +// blocks. +// +// Call Stream.Next to parse the frame header of the next audio frame, and call +// Stream.ParseNext to parse the entire next frame including audio samples. +// +// Note: The Close method of the stream must be called when finished using it. +func ParseFile(path string) (stream *Stream, err error) { + f, err := os.Open(path) + if err != nil { + return nil, err + } + stream, err = Parse(f) + if err != nil { + return nil, err + } + + return stream, err +} + +// Close closes the stream gracefully if the underlying io.Reader also implements the io.Closer interface. +func (stream *Stream) Close() error { + if closer, ok := stream.r.(io.Closer); ok { + return closer.Close() + } + + return nil +} + +// Next parses the frame header of the next audio frame. It returns io.EOF to +// signal a graceful end of FLAC stream. +// +// Call Frame.Parse to parse the audio samples of its subframes. +func (stream *Stream) Next() (f *frame.Frame, err error) { + return frame.New(stream.r) +} + +// ParseNext parses the entire next frame including audio samples. It returns +// io.EOF to signal a graceful end of FLAC stream. +func (stream *Stream) ParseNext() (f *frame.Frame, err error) { + return frame.Parse(stream.r) +} + +// Seek seeks to the frame containing the given absolute sample number. The +// return value specifies the first sample number of the frame containing +// sampleNum. +func (stream *Stream) Seek(sampleNum uint64) (uint64, error) { + if stream.seekTable == nil && stream.seekTableSize > 0 { + if err := stream.makeSeekTable(); err != nil { + return 0, err + } + } + + rs := stream.r.(io.ReadSeeker) + + isBiggerThanStream := stream.Info.NSamples != 0 && sampleNum >= stream.Info.NSamples + if isBiggerThanStream || sampleNum < 0 { + return 0, fmt.Errorf("unable to seek to sample number %d", sampleNum) + } + point, err := stream.searchFromStart(sampleNum) + if err != nil { + return 0, err + } + + if _, err := rs.Seek(stream.dataStart+int64(point.Offset), io.SeekStart); err != nil { + return 0, err + } + for { + // Record seek offset to start of frame. + offset, err := rs.Seek(0, io.SeekCurrent) + if err != nil { + return 0, err + } + frame, err := stream.ParseNext() + if err != nil { + return 0, err + } + if frame.SampleNumber()+uint64(frame.BlockSize) > sampleNum { + // Restore seek offset to the start of the frame containing the + // specified sample number. + _, err := rs.Seek(offset, io.SeekStart) + return frame.SampleNumber(), err + } + } +} + +// TODO(_): Utilize binary search in searchFromStart. + +// searchFromStart searches for the given sample number from the start of the +// seek table and returns the last seek point containing the sample number. If +// no seek point contains the sample number, the last seek point preceding the +// sample number is returned. If the sample number is lower than the first seek +// point, the first seek point is returned. +func (stream *Stream) searchFromStart(sampleNum uint64) (meta.SeekPoint, error) { + if len(stream.seekTable.Points) == 0 { + return meta.SeekPoint{}, ErrNoSeektable + } + prev := stream.seekTable.Points[0] + for _, p := range stream.seekTable.Points { + if p.SampleNum+uint64(p.NSamples) >= sampleNum { + return prev, nil + } + prev = p + } + return prev, nil +} + +// makeSeekTable creates a seek table with seek points to each frame of the FLAC +// stream. +func (stream *Stream) makeSeekTable() (err error) { + rs, ok := stream.r.(io.ReadSeeker) + if !ok { + return ErrNoSeeker + } + + pos, err := rs.Seek(0, io.SeekCurrent) + if err != nil { + return err + } + + _, err = rs.Seek(stream.dataStart, io.SeekStart) + if err != nil { + return err + } + + var i int + var sampleNum uint64 + var points []meta.SeekPoint + for { + // Record seek offset to start of frame. + off, err := rs.Seek(0, io.SeekCurrent) + if err != nil { + return err + } + f, err := stream.ParseNext() + if err != nil { + if err == io.EOF { + break + } + return err + } + points = append(points, meta.SeekPoint{ + SampleNum: sampleNum, + Offset: uint64(off - stream.dataStart), + NSamples: f.BlockSize, + }) + + sampleNum += uint64(f.BlockSize) + i++ + } + + stream.seekTable = &meta.SeekTable{Points: points} + + _, err = rs.Seek(pos, io.SeekStart) + return err +} diff --git a/flac_test.go b/flac_test.go new file mode 100755 index 0000000..0783caf --- /dev/null +++ b/flac_test.go @@ -0,0 +1,202 @@ +package flac_test + +import ( + "fmt" + "io" + "os" + "testing" + + "github.com/mewkiz/flac" +) + +func TestSkipID3v2(t *testing.T) { + if _, err := flac.ParseFile("testdata/id3.flac"); err != nil { + t.Fatal(err) + } +} + +func TestSeek(t *testing.T) { + f, err := os.Open("testdata/172960.flac") + if err != nil { + t.Fatal(err) + } + + defer f.Close() + + //Seek Table: + // {SampleNum:0 Offset:8283 NSamples:4096} + // {SampleNum:4096 Offset:17777 NSamples:4096} + // {SampleNum:8192 Offset:27141 NSamples:4096} + // {SampleNum:12288 Offset:36665 NSamples:4096} + // {SampleNum:16384 Offset:46179 NSamples:4096} + // {SampleNum:20480 Offset:55341 NSamples:4096} + // {SampleNum:24576 Offset:64690 NSamples:4096} + // {SampleNum:28672 Offset:74269 NSamples:4096} + // {SampleNum:32768 Offset:81984 NSamples:4096} + // {SampleNum:36864 Offset:86656 NSamples:4096} + // {SampleNum:40960 Offset:89596 NSamples:2723} + + testPos := []struct { + seek uint64 + expected uint64 + err string + }{ + {seek: 0, expected: 0}, + {seek: 9000, expected: 8192}, + {seek: 0, expected: 0}, + {seek: 8000, expected: 4096}, + {seek: 0, expected: 0}, + {seek: 50000, expected: 0, err: "unable to seek to sample number 50000"}, + {seek: 100, expected: 0}, + {seek: 8192, expected: 8192}, + {seek: 8191, expected: 4096}, + //{seek: 40960 + 2723 - 1, expected: 40960}, // last sample // TODO: re-enable when it works. See https://github.com/mewkiz/flac/pull/73 + {seek: 40960 + 2723, expected: 0, err: "unable to seek to sample number 43683"}, // one after last sample + } + + stream, err := flac.NewSeek(f) + if err != nil { + t.Fatal(err) + } + + for i, pos := range testPos { + t.Run(fmt.Sprintf("%02d", i), func(t *testing.T) { + p, err := stream.Seek(pos.seek) + if err != nil { + if err.Error() != pos.err { + t.Fatal(err) + } + } + + if p != pos.expected { + t.Fatalf("pos %d does not equal %d", p, pos.expected) + } + + _, err = stream.ParseNext() + if err != nil && err != io.EOF { + t.Fatal(err) + } + }) + + } +} + +func TestDecode(t *testing.T) { + paths := []string{ + "meta/testdata/input-SCPAP.flac", + "meta/testdata/input-SCVA.flac", + "meta/testdata/input-SCVPAP.flac", + "meta/testdata/input-VA.flac", + "meta/testdata/silence.flac", + "testdata/19875.flac", + "testdata/44127.flac", + "testdata/59996.flac", + "testdata/80574.flac", + "testdata/172960.flac", + "testdata/189983.flac", + "testdata/191885.flac", + "testdata/212768.flac", + "testdata/220014.flac", + "testdata/243749.flac", + "testdata/256529.flac", + "testdata/257344.flac", + "testdata/8297-275156-0011.flac", + "testdata/love.flac", + // IETF test cases. + // + // ref: https://github.com/ietf-wg-cellar/flac-test-files/tree/main/subset + "testdata/flac-test-files/subset/01 - blocksize 4096.flac", + "testdata/flac-test-files/subset/02 - blocksize 4608.flac", + "testdata/flac-test-files/subset/03 - blocksize 16.flac", + "testdata/flac-test-files/subset/04 - blocksize 192.flac", + "testdata/flac-test-files/subset/05 - blocksize 254.flac", + "testdata/flac-test-files/subset/06 - blocksize 512.flac", + "testdata/flac-test-files/subset/07 - blocksize 725.flac", + "testdata/flac-test-files/subset/08 - blocksize 1000.flac", + "testdata/flac-test-files/subset/09 - blocksize 1937.flac", + "testdata/flac-test-files/subset/10 - blocksize 2304.flac", + "testdata/flac-test-files/subset/11 - partition order 8.flac", + "testdata/flac-test-files/subset/12 - qlp precision 15 bit.flac", + "testdata/flac-test-files/subset/13 - qlp precision 2 bit.flac", + "testdata/flac-test-files/subset/14 - wasted bits.flac", + "testdata/flac-test-files/subset/15 - only verbatim subframes.flac", + "testdata/flac-test-files/subset/16 - partition order 8 containing escaped partitions.flac", + "testdata/flac-test-files/subset/17 - all fixed orders.flac", + "testdata/flac-test-files/subset/18 - precision search.flac", + "testdata/flac-test-files/subset/19 - samplerate 35467Hz.flac", + "testdata/flac-test-files/subset/20 - samplerate 39kHz.flac", + "testdata/flac-test-files/subset/21 - samplerate 22050Hz.flac", + "testdata/flac-test-files/subset/22 - 12 bit per sample.flac", + "testdata/flac-test-files/subset/23 - 8 bit per sample.flac", + "testdata/flac-test-files/subset/24 - variable blocksize file created with flake revision 264.flac", + "testdata/flac-test-files/subset/25 - variable blocksize file created with flake revision 264, modified to create smaller blocks.flac", + "testdata/flac-test-files/subset/26 - variable blocksize file created with CUETools.Flake 2.1.6.flac", + "testdata/flac-test-files/subset/27 - old format variable blocksize file created with Flake 0.11.flac", + "testdata/flac-test-files/subset/28 - high resolution audio, default settings.flac", + "testdata/flac-test-files/subset/29 - high resolution audio, blocksize 16384.flac", + "testdata/flac-test-files/subset/30 - high resolution audio, blocksize 13456.flac", + "testdata/flac-test-files/subset/31 - high resolution audio, using only 32nd order predictors.flac", + "testdata/flac-test-files/subset/32 - high resolution audio, partition order 8 containing escaped partitions.flac", + "testdata/flac-test-files/subset/33 - samplerate 192kHz.flac", + "testdata/flac-test-files/subset/34 - samplerate 192kHz, using only 32nd order predictors.flac", + "testdata/flac-test-files/subset/35 - samplerate 134560Hz.flac", + "testdata/flac-test-files/subset/36 - samplerate 384kHz.flac", + "testdata/flac-test-files/subset/37 - 20 bit per sample.flac", + "testdata/flac-test-files/subset/38 - 3 channels (3.0).flac", + "testdata/flac-test-files/subset/39 - 4 channels (4.0).flac", + "testdata/flac-test-files/subset/40 - 5 channels (5.0).flac", + "testdata/flac-test-files/subset/41 - 6 channels (5.1).flac", + "testdata/flac-test-files/subset/42 - 7 channels (6.1).flac", + "testdata/flac-test-files/subset/43 - 8 channels (7.1).flac", + "testdata/flac-test-files/subset/44 - 8-channel surround, 192kHz, 24 bit, using only 32nd order predictors.flac", + "testdata/flac-test-files/subset/45 - no total number of samples set.flac", + "testdata/flac-test-files/subset/46 - no min-max framesize set.flac", + "testdata/flac-test-files/subset/47 - only STREAMINFO.flac", + "testdata/flac-test-files/subset/48 - Extremely large SEEKTABLE.flac", + "testdata/flac-test-files/subset/49 - Extremely large PADDING.flac", + "testdata/flac-test-files/subset/50 - Extremely large PICTURE.flac", + "testdata/flac-test-files/subset/51 - Extremely large VORBISCOMMENT.flac", + "testdata/flac-test-files/subset/52 - Extremely large APPLICATION.flac", + "testdata/flac-test-files/subset/53 - CUESHEET with very many indexes.flac", + "testdata/flac-test-files/subset/54 - 1000x repeating VORBISCOMMENT.flac", + "testdata/flac-test-files/subset/55 - file 48-53 combined.flac", + "testdata/flac-test-files/subset/56 - JPG PICTURE.flac", + "testdata/flac-test-files/subset/57 - PNG PICTURE.flac", + "testdata/flac-test-files/subset/58 - GIF PICTURE.flac", + "testdata/flac-test-files/subset/59 - AVIF PICTURE.flac", + "testdata/flac-test-files/subset/60 - mono audio.flac", + "testdata/flac-test-files/subset/61 - predictor overflow check, 16-bit.flac", + "testdata/flac-test-files/subset/62 - predictor overflow check, 20-bit.flac", + "testdata/flac-test-files/subset/63 - predictor overflow check, 24-bit.flac", + "testdata/flac-test-files/subset/64 - rice partitions with escape code zero.flac", + } + + funcs := map[string]func(io.Reader) (*flac.Stream, error){ + "new": flac.New, + "newSeek": func(r io.Reader) (*flac.Stream, error) { return flac.NewSeek(r.(io.ReadSeeker)) }, + "parse": flac.Parse, + } + + for _, path := range paths { + for k, f := range funcs { + t.Run(fmt.Sprintf("%s/%s", k, path), func(t *testing.T) { + file, err := os.Open(path) + if err != nil { + t.Fatal(err) + } + + stream, err := f(file) + if err != nil { + t.Fatal(err) + } + + _, err = stream.ParseNext() + if err != nil { + t.Fatal(err) + } + + file.Close() + }) + } + } +} diff --git a/frame/frame.go b/frame/frame.go new file mode 100755 index 0000000..877569a --- /dev/null +++ b/frame/frame.go @@ -0,0 +1,687 @@ +// Package frame implements access to FLAC audio frames. +// +// A brief introduction of the FLAC audio format [1] follows. FLAC encoders +// divide the audio stream into blocks through a process called blocking [2]. A +// block contains the unencoded audio samples from all channels during a short +// period of time. Each audio block is divided into subblocks, one per channel. +// +// There is often a correlation between the left and right channel of stereo +// audio. Using inter-channel decorrelation [3] it is possible to store only one +// of the channels and the difference between the channels, or store the average +// of the channels and their difference. An encoder decorrelates audio samples +// as follows: +// +// mid = (left + right)/2 // average of the channels +// side = left - right // difference between the channels +// +// The blocks are encoded using a variety of prediction methods [4][5] and +// stored in frames. Blocks and subblocks contains unencoded audio samples while +// frames and subframes contain encoded audio samples. A FLAC stream contains +// one or more audio frames. +// +// [1]: https://www.xiph.org/flac/format.html#architecture +// [2]: https://www.xiph.org/flac/format.html#blocking +// [3]: https://www.xiph.org/flac/format.html#interchannel +// [4]: https://www.xiph.org/flac/format.html#prediction +// [5]: https://godoc.org/github.com/mewkiz/flac/frame#Pred +package frame + +import ( + "encoding/binary" + "errors" + "fmt" + "hash" + "io" + "log" + + "github.com/mewkiz/flac/internal/bits" + "github.com/mewkiz/flac/internal/hashutil" + "github.com/mewkiz/flac/internal/hashutil/crc16" + "github.com/mewkiz/flac/internal/hashutil/crc8" + "github.com/mewkiz/flac/internal/utf8" +) + +// A Frame contains the header and subframes of an audio frame. It holds the +// encoded samples from a block (a part) of the audio stream. Each subframe +// holding the samples from one of its channel. +// +// ref: https://www.xiph.org/flac/format.html#frame +type Frame struct { + // Audio frame header. + Header + // One subframe per channel, containing encoded audio samples. + Subframes []*Subframe + // CRC-16 hash sum, calculated by read operations on hr. + crc hashutil.Hash16 + // A bit reader, wrapping read operations to hr. + br *bits.Reader + // A CRC-16 hash reader, wrapping read operations to r. + hr io.Reader + // Underlying io.Reader. + r io.Reader +} + +// New creates a new Frame for accessing the audio samples of r. It reads and +// parses an audio frame header. It returns io.EOF to signal a graceful end of +// FLAC stream. +// +// Call Frame.Parse to parse the audio samples of its subframes. +func New(r io.Reader) (frame *Frame, err error) { + // Create a new CRC-16 hash reader which adds the data from all read + // operations to a running hash. + crc := crc16.NewIBM() + hr := io.TeeReader(r, crc) + + // Parse frame header. + frame = &Frame{crc: crc, hr: hr, r: r} + err = frame.parseHeader() + return frame, err +} + +// Parse reads and parses the header, and the audio samples from each subframe +// of a frame. If the samples are inter-channel decorrelated between the +// subframes, it correlates them. It returns io.EOF to signal a graceful end of +// FLAC stream. +// +// ref: https://www.xiph.org/flac/format.html#interchannel +func Parse(r io.Reader) (frame *Frame, err error) { + // Parse frame header. + frame, err = New(r) + if err != nil { + return frame, err + } + + // Parse subframes. + err = frame.Parse() + return frame, err +} + +// Parse reads and parses the audio samples from each subframe of the frame. If +// the samples are inter-channel decorrelated between the subframes, it +// correlates them. +// +// ref: https://www.xiph.org/flac/format.html#interchannel +func (frame *Frame) Parse() error { + // Parse subframes. + frame.Subframes = make([]*Subframe, frame.Channels.Count()) + var err error + for channel := range frame.Subframes { + // The side channel requires an extra bit per sample when using + // inter-channel decorrelation. + bps := uint(frame.BitsPerSample) + switch frame.Channels { + case ChannelsSideRight: + // channel 0 is the side channel. + if channel == 0 { + bps++ + } + case ChannelsLeftSide, ChannelsMidSide: + // channel 1 is the side channel. + if channel == 1 { + bps++ + } + } + + // Parse subframe. + frame.Subframes[channel], err = frame.parseSubframe(frame.br, bps) + if err != nil { + return err + } + } + + // Inter-channel correlation of subframe samples. + frame.Correlate() + + // 2 bytes: CRC-16 checksum. + var want uint16 + if err = binary.Read(frame.r, binary.BigEndian, &want); err != nil { + return unexpected(err) + } + got := frame.crc.Sum16() + if got != want { + return fmt.Errorf("frame.Frame.Parse: CRC-16 checksum mismatch; expected 0x%04X, got 0x%04X", want, got) + } + + return nil +} + +// Hash adds the decoded audio samples of the frame to a running MD5 hash. It +// can be used in conjunction with StreamInfo.MD5sum to verify the integrity of +// the decoded audio samples. +// +// Note: The audio samples of the frame must be decoded before calling Hash. +func (frame *Frame) Hash(md5sum hash.Hash) { + // Write decoded samples to a running MD5 hash. + bps := frame.BitsPerSample + var buf [3]byte + for i := 0; i < int(frame.BlockSize); i++ { + for _, subframe := range frame.Subframes { + sample := subframe.Samples[i] + switch { + case 1 <= bps && bps <= 8: + buf[0] = uint8(sample) + md5sum.Write(buf[:1]) + case 9 <= bps && bps <= 16: + buf[0] = uint8(sample) + buf[1] = uint8(sample >> 8) + md5sum.Write(buf[:2]) + case 17 <= bps && bps <= 24: + buf[0] = uint8(sample) + buf[1] = uint8(sample >> 8) + buf[2] = uint8(sample >> 16) + md5sum.Write(buf[:]) + default: + log.Printf("frame.Frame.Hash: support for %d-bit sample size not yet implemented", bps) + } + } + } +} + +// A Header contains the basic properties of an audio frame, such as its sample +// rate and channel count. To facilitate random access decoding each frame +// header starts with a sync-code. This allows the decoder to synchronize and +// locate the start of a frame header. +// +// ref: https://www.xiph.org/flac/format.html#frame_header +type Header struct { + // Specifies if the block size is fixed or variable. + HasFixedBlockSize bool + // Block size in inter-channel samples, i.e. the number of audio samples in + // each subframe. + BlockSize uint16 + // Sample rate in Hz; a 0 value implies unknown, get sample rate from + // StreamInfo. + SampleRate uint32 + // Specifies the number of channels (subframes) that exist in the frame, + // their order and possible inter-channel decorrelation. + Channels Channels + // Sample size in bits-per-sample; a 0 value implies unknown, get sample size + // from StreamInfo. + BitsPerSample uint8 + // Specifies the frame number if the block size is fixed, and the first + // sample number in the frame otherwise. When using fixed block size, the + // first sample number in the frame can be derived by multiplying the frame + // number with the block size (in samples). + Num uint64 +} + +// Errors returned by Frame.parseHeader. +var ( + ErrInvalidSync = errors.New("frame.Frame.parseHeader: invalid sync-code") +) + +// parseHeader reads and parses the header of an audio frame. +func (frame *Frame) parseHeader() error { + // Create a new CRC-8 hash reader which adds the data from all read + // operations to a running hash. + h := crc8.NewATM() + hr := io.TeeReader(frame.hr, h) + + // Create bit reader. + br := bits.NewReader(hr) + frame.br = br + + // 14 bits: sync-code (11111111111110) + x, err := br.Read(14) + if err != nil { + // This is the only place an audio frame may return io.EOF, which signals + // a graceful end of a FLAC stream. + return err + } + if x != 0x3FFE { + return ErrInvalidSync + } + + // 1 bit: reserved. + x, err = br.Read(1) + if err != nil { + return unexpected(err) + } + if x != 0 { + return errors.New("frame.Frame.parseHeader: non-zero reserved value") + } + + // 1 bit: HasFixedBlockSize. + x, err = br.Read(1) + if err != nil { + return unexpected(err) + } + if x == 0 { + frame.HasFixedBlockSize = true + } + + // 4 bits: BlockSize. The block size parsing is simplified by deferring it to + // the end of the header. + blockSize, err := br.Read(4) + if err != nil { + return unexpected(err) + } + + // 4 bits: SampleRate. The sample rate parsing is simplified by deferring it + // to the end of the header. + sampleRate, err := br.Read(4) + if err != nil { + return unexpected(err) + } + + // Parse channels. + if err := frame.parseChannels(br); err != nil { + return err + } + + // Parse bits per sample. + if err := frame.parseBitsPerSample(br); err != nil { + return err + } + + // 1 bit: reserved. + x, err = br.Read(1) + if err != nil { + return unexpected(err) + } + if x != 0 { + return errors.New("frame.Frame.parseHeader: non-zero reserved value") + } + + // if (fixed block size) + // 1-6 bytes: UTF-8 encoded frame number. + // else + // 1-7 bytes: UTF-8 encoded sample number. + frame.Num, err = utf8.Decode(hr) + if err != nil { + return unexpected(err) + } + + // Parse block size. + if err := frame.parseBlockSize(br, blockSize); err != nil { + return err + } + + // Parse sample rate. + if err := frame.parseSampleRate(br, sampleRate); err != nil { + return err + } + + // 1 byte: CRC-8 checksum. + var want uint8 + if err = binary.Read(frame.hr, binary.BigEndian, &want); err != nil { + return unexpected(err) + } + got := h.Sum8() + if want != got { + return fmt.Errorf("frame.Frame.parseHeader: CRC-8 checksum mismatch; expected 0x%02X, got 0x%02X", want, got) + } + + return nil +} + +// parseBitsPerSample parses the bits per sample of the header. +func (frame *Frame) parseBitsPerSample(br *bits.Reader) error { + // 3 bits: BitsPerSample. + x, err := br.Read(3) + if err != nil { + return unexpected(err) + } + + // The 3 bits are used to specify the sample size as follows: + // 000: unknown sample size; get from StreamInfo. + // 001: 8 bits-per-sample. + // 010: 12 bits-per-sample. + // 011: reserved. + // 100: 16 bits-per-sample. + // 101: 20 bits-per-sample. + // 110: 24 bits-per-sample. + // 111: reserved. + switch x { + case 0x0: + // 000: unknown bits-per-sample; get from StreamInfo. + case 0x1: + // 001: 8 bits-per-sample. + frame.BitsPerSample = 8 + case 0x2: + // 010: 12 bits-per-sample. + frame.BitsPerSample = 12 + case 0x4: + // 100: 16 bits-per-sample. + frame.BitsPerSample = 16 + case 0x5: + // 101: 20 bits-per-sample. + frame.BitsPerSample = 20 + case 0x6: + // 110: 24 bits-per-sample. + frame.BitsPerSample = 24 + default: + // 011: reserved. + // 111: reserved. + return fmt.Errorf("frame.Frame.parseHeader: reserved sample size bit pattern (%03b)", x) + } + return nil +} + +// parseChannels parses the channels of the header. +func (frame *Frame) parseChannels(br *bits.Reader) error { + // 4 bits: Channels. + // + // The 4 bits are used to specify the channels as follows: + // 0000: (1 channel) mono. + // 0001: (2 channels) left, right. + // 0010: (3 channels) left, right, center. + // 0011: (4 channels) left, right, left surround, right surround. + // 0100: (5 channels) left, right, center, left surround, right surround. + // 0101: (6 channels) left, right, center, LFE, left surround, right surround. + // 0110: (7 channels) left, right, center, LFE, center surround, side left, side right. + // 0111: (8 channels) left, right, center, LFE, left surround, right surround, side left, side right. + // 1000: (2 channels) left, side; using inter-channel decorrelation. + // 1001: (2 channels) side, right; using inter-channel decorrelation. + // 1010: (2 channels) mid, side; using inter-channel decorrelation. + // 1011: reserved. + // 1100: reserved. + // 1101: reserved. + // 1111: reserved. + x, err := br.Read(4) + if err != nil { + return unexpected(err) + } + if x >= 0xB { + return fmt.Errorf("frame.Frame.parseHeader: reserved channels bit pattern (%04b)", x) + } + frame.Channels = Channels(x) + return nil +} + +// parseBlockSize parses the block size of the header. +func (frame *Frame) parseBlockSize(br *bits.Reader, blockSize uint64) error { + // The 4 bits of n are used to specify the block size as follows: + // 0000: reserved. + // 0001: 192 samples. + // 0010-0101: 576 * 2^(n-2) samples. + // 0110: get 8 bit (block size)-1 from the end of the header. + // 0111: get 16 bit (block size)-1 from the end of the header. + // 1000-1111: 256 * 2^(n-8) samples. + n := blockSize + switch { + case n == 0x0: + // 0000: reserved. + return errors.New("frame.Frame.parseHeader: reserved block size bit pattern (0000)") + case n == 0x1: + // 0001: 192 samples. + frame.BlockSize = 192 + case n >= 0x2 && n <= 0x5: + // 0010-0101: 576 * 2^(n-2) samples. + frame.BlockSize = 576 * (1 << (n - 2)) + case n == 0x6: + // 0110: get 8 bit (block size)-1 from the end of the header. + x, err := br.Read(8) + if err != nil { + return unexpected(err) + } + frame.BlockSize = uint16(x + 1) + case n == 0x7: + // 0111: get 16 bit (block size)-1 from the end of the header. + x, err := br.Read(16) + if err != nil { + return unexpected(err) + } + frame.BlockSize = uint16(x + 1) + default: + // 1000-1111: 256 * 2^(n-8) samples. + frame.BlockSize = 256 * (1 << (n - 8)) + } + return nil +} + +// parseSampleRate parses the sample rate of the header. +func (frame *Frame) parseSampleRate(br *bits.Reader, sampleRate uint64) error { + // The 4 bits are used to specify the sample rate as follows: + // 0000: unknown sample rate; get from StreamInfo. + // 0001: 88.2 kHz. + // 0010: 176.4 kHz. + // 0011: 192 kHz. + // 0100: 8 kHz. + // 0101: 16 kHz. + // 0110: 22.05 kHz. + // 0111: 24 kHz. + // 1000: 32 kHz. + // 1001: 44.1 kHz. + // 1010: 48 kHz. + // 1011: 96 kHz. + // 1100: get 8 bit sample rate (in kHz) from the end of the header. + // 1101: get 16 bit sample rate (in Hz) from the end of the header. + // 1110: get 16 bit sample rate (in daHz) from the end of the header. + // 1111: invalid. + switch sampleRate { + case 0x0: + // 0000: unknown sample rate; get from StreamInfo. + case 0x1: + // 0001: 88.2 kHz. + frame.SampleRate = 88200 + case 0x2: + // 0010: 176.4 kHz. + frame.SampleRate = 176400 + // TODO(u): Remove log message when the test cases have been extended. + log.Printf("frame.Frame.parseHeader: The flac library test cases do not yet include any audio files with sample rate %d. If possible please consider contributing this audio sample to improve the reliability of the test cases.", frame.SampleRate) + case 0x3: + // 0011: 192 kHz. + frame.SampleRate = 192000 + case 0x4: + // 0100: 8 kHz. + frame.SampleRate = 8000 + case 0x5: + // 0101: 16 kHz. + frame.SampleRate = 16000 + case 0x6: + // 0110: 22.05 kHz. + frame.SampleRate = 22050 + case 0x7: + // 0111: 24 kHz. + frame.SampleRate = 24000 + // TODO(u): Remove log message when the test cases have been extended. + log.Printf("frame.Frame.parseHeader: The flac library test cases do not yet include any audio files with sample rate %d. If possible please consider contributing this audio sample to improve the reliability of the test cases.", frame.SampleRate) + case 0x8: + // 1000: 32 kHz. + frame.SampleRate = 32000 + case 0x9: + // 1001: 44.1 kHz. + frame.SampleRate = 44100 + case 0xA: + // 1010: 48 kHz. + frame.SampleRate = 48000 + case 0xB: + // 1011: 96 kHz. + frame.SampleRate = 96000 + case 0xC: + // 1100: get 8 bit sample rate (in kHz) from the end of the header. + x, err := br.Read(8) + if err != nil { + return unexpected(err) + } + frame.SampleRate = uint32(x * 1000) + case 0xD: + // 1101: get 16 bit sample rate (in Hz) from the end of the header. + x, err := br.Read(16) + if err != nil { + return unexpected(err) + } + frame.SampleRate = uint32(x) + case 0xE: + // 1110: get 16 bit sample rate (in daHz) from the end of the header. + x, err := br.Read(16) + if err != nil { + return unexpected(err) + } + frame.SampleRate = uint32(x * 10) + default: + // 1111: invalid. + return errors.New("frame.Frame.parseHeader: invalid sample rate bit pattern (1111)") + } + return nil +} + +// Channels specifies the number of channels (subframes) that exist in a frame, +// their order and possible inter-channel decorrelation. +type Channels uint8 + +// Channel assignments. The following abbreviations are used: +// +// C: center (directly in front) +// R: right (standard stereo) +// Sr: side right (directly to the right) +// Rs: right surround (back right) +// Cs: center surround (rear center) +// Ls: left surround (back left) +// Sl: side left (directly to the left) +// L: left (standard stereo) +// Lfe: low-frequency effect (placed according to room acoustics) +// +// The first 6 channel constants follow the SMPTE/ITU-R channel order: +// +// L R C Lfe Ls Rs +const ( + ChannelsMono Channels = iota // 1 channel: mono. + ChannelsLR // 2 channels: left, right. + ChannelsLRC // 3 channels: left, right, center. + ChannelsLRLsRs // 4 channels: left, right, left surround, right surround. + ChannelsLRCLsRs // 5 channels: left, right, center, left surround, right surround. + ChannelsLRCLfeLsRs // 6 channels: left, right, center, LFE, left surround, right surround. + ChannelsLRCLfeCsSlSr // 7 channels: left, right, center, LFE, center surround, side left, side right. + ChannelsLRCLfeLsRsSlSr // 8 channels: left, right, center, LFE, left surround, right surround, side left, side right. + ChannelsLeftSide // 2 channels: left, side; using inter-channel decorrelation. + ChannelsSideRight // 2 channels: side, right; using inter-channel decorrelation. + ChannelsMidSide // 2 channels: mid, side; using inter-channel decorrelation. +) + +// nChannels specifies the number of channels used by each channel assignment. +var nChannels = [...]int{ + ChannelsMono: 1, + ChannelsLR: 2, + ChannelsLRC: 3, + ChannelsLRLsRs: 4, + ChannelsLRCLsRs: 5, + ChannelsLRCLfeLsRs: 6, + ChannelsLRCLfeCsSlSr: 7, + ChannelsLRCLfeLsRsSlSr: 8, + ChannelsLeftSide: 2, + ChannelsSideRight: 2, + ChannelsMidSide: 2, +} + +// Count returns the number of channels (subframes) used by the provided channel +// assignment. +func (channels Channels) Count() int { + return nChannels[channels] +} + +// Correlate reverts any inter-channel decorrelation between the samples of the +// subframes. +// +// An encoder decorrelates audio samples as follows: +// +// mid = (left + right)/2 +// side = left - right +func (frame *Frame) Correlate() { + switch frame.Channels { + case ChannelsLeftSide: + // 2 channels: left, side; using inter-channel decorrelation. + left := frame.Subframes[0].Samples + side := frame.Subframes[1].Samples + for i := range side { + // right = left - side + side[i] = left[i] - side[i] + } + case ChannelsSideRight: + // 2 channels: side, right; using inter-channel decorrelation. + side := frame.Subframes[0].Samples + right := frame.Subframes[1].Samples + for i := range side { + // left = right + side + side[i] = right[i] + side[i] + } + case ChannelsMidSide: + // 2 channels: mid, side; using inter-channel decorrelation. + mid := frame.Subframes[0].Samples + side := frame.Subframes[1].Samples + for i := range side { + // left = (2*mid + side)/2 + // right = (2*mid - side)/2 + m := mid[i] + s := side[i] + m *= 2 + // Notice that the integer division in mid = (left + right)/2 discards + // the least significant bit. It can be reconstructed however, since a + // sum A+B and a difference A-B has the same least significant bit. + // + // ref: Data Compression: The Complete Reference (ch. 7, Decorrelation) + m |= s & 1 + mid[i] = (m + s) / 2 + side[i] = (m - s) / 2 + } + } +} + +// Decorrelate performs inter-channel decorrelation between the samples of the +// subframes. +// +// An encoder decorrelates audio samples as follows: +// +// mid = (left + right)/2 +// side = left - right +func (frame *Frame) Decorrelate() { + switch frame.Channels { + case ChannelsLeftSide: + // 2 channels: left, side; using inter-channel decorrelation. + left := frame.Subframes[0].Samples // already left; no change after inter-channel decorrelation. + right := frame.Subframes[1].Samples // set to side after inter-channel decorrelation. + for i := range left { + l := left[i] + r := right[i] + // inter-channel decorrelation: + // side = left - right + side := l - r + right[i] = side + } + case ChannelsSideRight: + // 2 channels: side, right; using inter-channel decorrelation. + left := frame.Subframes[0].Samples // set to side after inter-channel decorrelation. + right := frame.Subframes[1].Samples // already right; no change after inter-channel decorrelation. + for i := range left { + l := left[i] + r := right[i] + // inter-channel decorrelation: + // side = left - right + side := l - r + left[i] = side + } + case ChannelsMidSide: + // 2 channels: mid, side; using inter-channel decorrelation. + left := frame.Subframes[0].Samples // set to mid after inter-channel decorrelation. + right := frame.Subframes[1].Samples // set to side after inter-channel decorrelation. + for i := range left { + // inter-channel decorrelation: + // mid = (left + right)/2 + // side = left - right + l := left[i] + r := right[i] + mid := int32((int64(l) + int64(r)) >> 1) // NOTE: using `(left + right) >> 1`, not the same as `(left + right) / 2`. + side := l - r + left[i] = mid + right[i] = side + } + } +} + +// SampleNumber returns the first sample number contained within the frame. +func (frame *Frame) SampleNumber() uint64 { + if frame.HasFixedBlockSize { + return frame.Num * uint64(frame.BlockSize) + } + return frame.Num +} + +// unexpected returns io.ErrUnexpectedEOF if err is io.EOF, and returns err +// otherwise. +func unexpected(err error) error { + if err == io.EOF { + return io.ErrUnexpectedEOF + } + return err +} diff --git a/frame/frame_test.go b/frame/frame_test.go new file mode 100755 index 0000000..cf66425 --- /dev/null +++ b/frame/frame_test.go @@ -0,0 +1,193 @@ +package frame_test + +import ( + "bytes" + "crypto/md5" + "io" + "testing" + + "github.com/mewkiz/flac" +) + +var golden = []struct { + path string +}{ + {path: "../testdata/love.flac"}, + {path: "../testdata/19875.flac"}, + {path: "../testdata/44127.flac"}, + {path: "../testdata/59996.flac"}, + {path: "../testdata/80574.flac"}, + {path: "../testdata/172960.flac"}, + {path: "../testdata/189983.flac"}, + {path: "../testdata/191885.flac"}, + {path: "../testdata/212768.flac"}, + {path: "../testdata/220014.flac"}, + {path: "../testdata/243749.flac"}, + {path: "../testdata/256529.flac"}, + {path: "../testdata/257344.flac"}, + + // IETF test cases. + {path: "../testdata/flac-test-files/subset/01 - blocksize 4096.flac"}, + {path: "../testdata/flac-test-files/subset/02 - blocksize 4608.flac"}, + {path: "../testdata/flac-test-files/subset/03 - blocksize 16.flac"}, + {path: "../testdata/flac-test-files/subset/04 - blocksize 192.flac"}, + {path: "../testdata/flac-test-files/subset/05 - blocksize 254.flac"}, + {path: "../testdata/flac-test-files/subset/06 - blocksize 512.flac"}, + {path: "../testdata/flac-test-files/subset/07 - blocksize 725.flac"}, + {path: "../testdata/flac-test-files/subset/08 - blocksize 1000.flac"}, + {path: "../testdata/flac-test-files/subset/09 - blocksize 1937.flac"}, + {path: "../testdata/flac-test-files/subset/10 - blocksize 2304.flac"}, + {path: "../testdata/flac-test-files/subset/11 - partition order 8.flac"}, + {path: "../testdata/flac-test-files/subset/12 - qlp precision 15 bit.flac"}, + {path: "../testdata/flac-test-files/subset/13 - qlp precision 2 bit.flac"}, + {path: "../testdata/flac-test-files/subset/14 - wasted bits.flac"}, + {path: "../testdata/flac-test-files/subset/15 - only verbatim subframes.flac"}, + {path: "../testdata/flac-test-files/subset/16 - partition order 8 containing escaped partitions.flac"}, + {path: "../testdata/flac-test-files/subset/17 - all fixed orders.flac"}, + {path: "../testdata/flac-test-files/subset/18 - precision search.flac"}, + {path: "../testdata/flac-test-files/subset/19 - samplerate 35467Hz.flac"}, + {path: "../testdata/flac-test-files/subset/20 - samplerate 39kHz.flac"}, + {path: "../testdata/flac-test-files/subset/21 - samplerate 22050Hz.flac"}, + {path: "../testdata/flac-test-files/subset/22 - 12 bit per sample.flac"}, + {path: "../testdata/flac-test-files/subset/23 - 8 bit per sample.flac"}, + {path: "../testdata/flac-test-files/subset/24 - variable blocksize file created with flake revision 264.flac"}, + {path: "../testdata/flac-test-files/subset/25 - variable blocksize file created with flake revision 264, modified to create smaller blocks.flac"}, + {path: "../testdata/flac-test-files/subset/26 - variable blocksize file created with CUETools.Flake 2.1.6.flac"}, + {path: "../testdata/flac-test-files/subset/27 - old format variable blocksize file created with Flake 0.11.flac"}, + {path: "../testdata/flac-test-files/subset/28 - high resolution audio, default settings.flac"}, + {path: "../testdata/flac-test-files/subset/29 - high resolution audio, blocksize 16384.flac"}, + {path: "../testdata/flac-test-files/subset/30 - high resolution audio, blocksize 13456.flac"}, + {path: "../testdata/flac-test-files/subset/31 - high resolution audio, using only 32nd order predictors.flac"}, + {path: "../testdata/flac-test-files/subset/32 - high resolution audio, partition order 8 containing escaped partitions.flac"}, + {path: "../testdata/flac-test-files/subset/33 - samplerate 192kHz.flac"}, + {path: "../testdata/flac-test-files/subset/34 - samplerate 192kHz, using only 32nd order predictors.flac"}, + {path: "../testdata/flac-test-files/subset/35 - samplerate 134560Hz.flac"}, + {path: "../testdata/flac-test-files/subset/36 - samplerate 384kHz.flac"}, + {path: "../testdata/flac-test-files/subset/37 - 20 bit per sample.flac"}, + {path: "../testdata/flac-test-files/subset/38 - 3 channels (3.0).flac"}, + {path: "../testdata/flac-test-files/subset/39 - 4 channels (4.0).flac"}, + {path: "../testdata/flac-test-files/subset/40 - 5 channels (5.0).flac"}, + {path: "../testdata/flac-test-files/subset/41 - 6 channels (5.1).flac"}, + {path: "../testdata/flac-test-files/subset/42 - 7 channels (6.1).flac"}, + {path: "../testdata/flac-test-files/subset/43 - 8 channels (7.1).flac"}, + {path: "../testdata/flac-test-files/subset/44 - 8-channel surround, 192kHz, 24 bit, using only 32nd order predictors.flac"}, + {path: "../testdata/flac-test-files/subset/45 - no total number of samples set.flac"}, + {path: "../testdata/flac-test-files/subset/46 - no min-max framesize set.flac"}, + {path: "../testdata/flac-test-files/subset/47 - only STREAMINFO.flac"}, + {path: "../testdata/flac-test-files/subset/48 - Extremely large SEEKTABLE.flac"}, + {path: "../testdata/flac-test-files/subset/49 - Extremely large PADDING.flac"}, + {path: "../testdata/flac-test-files/subset/50 - Extremely large PICTURE.flac"}, + {path: "../testdata/flac-test-files/subset/51 - Extremely large VORBISCOMMENT.flac"}, + {path: "../testdata/flac-test-files/subset/52 - Extremely large APPLICATION.flac"}, + {path: "../testdata/flac-test-files/subset/53 - CUESHEET with very many indexes.flac"}, + {path: "../testdata/flac-test-files/subset/54 - 1000x repeating VORBISCOMMENT.flac"}, + {path: "../testdata/flac-test-files/subset/55 - file 48-53 combined.flac"}, + {path: "../testdata/flac-test-files/subset/56 - JPG PICTURE.flac"}, + {path: "../testdata/flac-test-files/subset/57 - PNG PICTURE.flac"}, + {path: "../testdata/flac-test-files/subset/58 - GIF PICTURE.flac"}, + {path: "../testdata/flac-test-files/subset/59 - AVIF PICTURE.flac"}, + {path: "../testdata/flac-test-files/subset/60 - mono audio.flac"}, + {path: "../testdata/flac-test-files/subset/61 - predictor overflow check, 16-bit.flac"}, + {path: "../testdata/flac-test-files/subset/62 - predictor overflow check, 20-bit.flac"}, + // TODO: fix decoding of "subset/63 - ...flac": MD5 checksum mismatch for decoded audio samples; expected e4e4a6b3a672a849a3e2157c11ad23c6, got a0343afaaaa6229266d78ccf3175eb8d + {path: "../testdata/flac-test-files/subset/63 - predictor overflow check, 24-bit.flac"}, + {path: "../testdata/flac-test-files/subset/64 - rice partitions with escape code zero.flac"}, +} + +func TestFrameHash(t *testing.T) { + var zeroHash [md5.Size]byte + for _, g := range golden { + t.Run(g.path, func(t *testing.T) { + stream, err := flac.Open(g.path) + if err != nil { + t.Fatal(err) + } + defer stream.Close() + + // Skip frame hash test if no MD5 hash was set in StreamInfo. + want := stream.Info.MD5sum[:] + if bytes.Equal(want, zeroHash[:]) { + t.Skipf("path=%q, skipping frame hash test as no MD5 hash was set in StreamInfo", g.path) + return + } + + md5sum := md5.New() + for frameNum := 0; ; frameNum++ { + frame, err := stream.ParseNext() + if err != nil { + if err == io.EOF { + break + } + t.Errorf("path=%q, frameNum=%d: error while parsing frame; %v", g.path, frameNum, err) + continue + } + frame.Hash(md5sum) + } + got := md5sum.Sum(nil) + // Verify the decoded audio samples by comparing the MD5 checksum that is + // stored in StreamInfo with the computed one. + if !bytes.Equal(got, want) { + t.Errorf("path=%q: MD5 checksum mismatch for decoded audio samples; expected %32x, got %32x", g.path, want, got) + } + }) + } +} + +func BenchmarkFrameParse(b *testing.B) { + // The file 151185.flac is a 119.5 MB public domain FLAC file used to + // benchmark the flac library. Because of its size, it has not been included + // in the repository, but is available for download at + // + // http://freesound.org/people/jarfil/sounds/151185/ + for i := 0; i < b.N; i++ { + stream, err := flac.Open("../testdata/benchmark/151185.flac") + if err != nil { + b.Fatal(err) + } + for { + _, err := stream.ParseNext() + if err != nil { + if err == io.EOF { + break + } + stream.Close() + b.Fatal(err) + } + } + stream.Close() + } +} + +func BenchmarkFrameHash(b *testing.B) { + // The file 151185.flac is a 119.5 MB public domain FLAC file used to + // benchmark the flac library. Because of its size, it has not been included + // in the repository, but is available for download at + // + // http://freesound.org/people/jarfil/sounds/151185/ + for i := 0; i < b.N; i++ { + stream, err := flac.Open("../testdata/benchmark/151185.flac") + if err != nil { + b.Fatal(err) + } + md5sum := md5.New() + for { + frame, err := stream.ParseNext() + if err != nil { + if err == io.EOF { + break + } + stream.Close() + b.Fatal(err) + } + frame.Hash(md5sum) + } + stream.Close() + want := stream.Info.MD5sum[:] + got := md5sum.Sum(nil) + // Verify the decoded audio samples by comparing the MD5 checksum that is + // stored in StreamInfo with the computed one. + if !bytes.Equal(got, want) { + b.Fatalf("MD5 checksum mismatch for decoded audio samples; expected %32x, got %32x", want, got) + } + } +} diff --git a/frame/subframe.go b/frame/subframe.go new file mode 100755 index 0000000..b906609 --- /dev/null +++ b/frame/subframe.go @@ -0,0 +1,534 @@ +package frame + +import ( + "errors" + "fmt" + + "github.com/mewkiz/flac/internal/bits" +) + +// A Subframe contains the encoded audio samples from one channel of an audio +// block (a part of the audio stream). +// +// ref: https://www.xiph.org/flac/format.html#subframe +type Subframe struct { + // Subframe header. + SubHeader + // Unencoded audio samples. Samples is initially nil, and gets populated by a + // call to Frame.Parse. + // + // Samples is used by decodeFixed and decodeFIR to temporarily store + // residuals. Before returning they call decodeLPC which decodes the audio + // samples. + Samples []int32 + // Number of audio samples in the subframe. + NSamples int +} + +// parseSubframe reads and parses the header, and the audio samples of a +// subframe. +func (frame *Frame) parseSubframe(br *bits.Reader, bps uint) (subframe *Subframe, err error) { + // Parse subframe header. + subframe = new(Subframe) + if err = subframe.parseHeader(br); err != nil { + return subframe, err + } + // Adjust bps of subframe for wasted bits-per-sample. + bps -= subframe.Wasted + + // Decode subframe audio samples. + subframe.NSamples = int(frame.BlockSize) + subframe.Samples = make([]int32, 0, subframe.NSamples) + switch subframe.Pred { + case PredConstant: + err = subframe.decodeConstant(br, bps) + case PredVerbatim: + err = subframe.decodeVerbatim(br, bps) + case PredFixed: + err = subframe.decodeFixed(br, bps) + case PredFIR: + err = subframe.decodeFIR(br, bps) + } + + // Left shift to account for wasted bits-per-sample. + for i, sample := range subframe.Samples { + subframe.Samples[i] = sample << subframe.Wasted + } + return subframe, err +} + +// A SubHeader specifies the prediction method and order of a subframe. +// +// ref: https://www.xiph.org/flac/format.html#subframe_header +type SubHeader struct { + // Specifies the prediction method used to encode the audio sample of the + // subframe. + Pred Pred + // Prediction order used by fixed and FIR linear prediction decoding. + Order int + // Wasted bits-per-sample. + Wasted uint + // Residual coding method used by fixed and FIR linear prediction decoding. + ResidualCodingMethod ResidualCodingMethod + // Coefficients' precision in bits used by FIR linear prediction decoding. + CoeffPrec uint + // Predictor coefficient shift needed in bits used by FIR linear prediction + // decoding. + CoeffShift int32 + // Predictor coefficients used by FIR linear prediction decoding. + Coeffs []int32 + // Rice-coding subframe fields used by residual coding methods rice1 and + // rice2; nil if unused. + RiceSubframe *RiceSubframe +} + +// RiceSubframe holds rice-coding subframe fields used by residual coding +// methods rice1 and rice2. +type RiceSubframe struct { + // Partition order used by fixed and FIR linear prediction decoding + // (for residual coding methods, rice1 and rice2). + PartOrder int // TODO: remove PartOrder and infer from int(math.Log2(float64(len(Partitions))))? + // Rice partitions. + Partitions []RicePartition +} + +// RicePartition is a partition containing a subset of the residuals of a +// subframe. +type RicePartition struct { + // Rice parameter. + Param uint + // Residual sample size in bits-per-sample used by escaped partitions. + EscapedBitsPerSample uint +} + +// parseHeader reads and parses the header of a subframe. +func (subframe *Subframe) parseHeader(br *bits.Reader) error { + // 1 bit: zero-padding. + x, err := br.Read(1) + if err != nil { + return unexpected(err) + } + if x != 0 { + return errors.New("frame.Subframe.parseHeader: non-zero padding") + } + + // 6 bits: Pred. + x, err = br.Read(6) + if err != nil { + return unexpected(err) + } + // The 6 bits are used to specify the prediction method and order as follows: + // 000000: Constant prediction method. + // 000001: Verbatim prediction method. + // 00001x: reserved. + // 0001xx: reserved. + // 001xxx: + // if (xxx <= 4) + // Fixed prediction method; xxx=order + // else + // reserved. + // 01xxxx: reserved. + // 1xxxxx: FIR prediction method; xxxxx=order-1 + switch { + case x < 1: + // 000000: Constant prediction method. + subframe.Pred = PredConstant + case x < 2: + // 000001: Verbatim prediction method. + subframe.Pred = PredVerbatim + case x < 8: + // 00001x: reserved. + // 0001xx: reserved. + return fmt.Errorf("frame.Subframe.parseHeader: reserved prediction method bit pattern (%06b)", x) + case x < 16: + // 001xxx: + // if (xxx <= 4) + // Fixed prediction method; xxx=order + // else + // reserved. + order := int(x & 0x07) + if order > 4 { + return fmt.Errorf("frame.Subframe.parseHeader: reserved prediction method bit pattern (%06b)", x) + } + subframe.Pred = PredFixed + subframe.Order = order + case x < 32: + // 01xxxx: reserved. + return fmt.Errorf("frame.Subframe.parseHeader: reserved prediction method bit pattern (%06b)", x) + default: + // 1xxxxx: FIR prediction method; xxxxx=order-1 + subframe.Pred = PredFIR + subframe.Order = int(x&0x1F) + 1 + } + + // 1 bit: hasWastedBits. + x, err = br.Read(1) + if err != nil { + return unexpected(err) + } + if x != 0 { + // k wasted bits-per-sample in source subblock, k-1 follows, unary coded; + // e.g. k=3 => 001 follows, k=7 => 0000001 follows. + x, err = br.ReadUnary() + if err != nil { + return unexpected(err) + } + subframe.Wasted = uint(x) + 1 + } + + return nil +} + +// Pred specifies the prediction method used to encode the audio samples of a +// subframe. +type Pred uint8 + +// Prediction methods. +const ( + // PredConstant specifies that the subframe contains a constant sound. The + // audio samples are encoded using run-length encoding. Since every audio + // sample has the same constant value, a single unencoded audio sample is + // stored in practice. It is replicated a number of times, as specified by + // BlockSize in the frame header. + PredConstant Pred = iota + // PredVerbatim specifies that the subframe contains unencoded audio samples. + // Random sound is often stored verbatim, since no prediction method can + // compress it sufficiently. + PredVerbatim + // PredFixed specifies that the subframe contains linear prediction coded + // audio samples. The coefficients of the prediction polynomial are selected + // from a fixed set, and can represent 0th through fourth-order polynomials. + // The prediction order (0 through 4) is stored within the subframe along + // with the same number of unencoded warm-up samples, which are used to kick + // start the prediction polynomial. The remainder of the subframe stores + // encoded residuals (signal errors) which specify the difference between the + // predicted and the original audio samples. + PredFixed + // PredFIR specifies that the subframe contains linear prediction coded audio + // samples. The coefficients of the prediction polynomial are stored in the + // subframe, and can represent 0th through 32nd-order polynomials. The + // prediction order (0 through 32) is stored within the subframe along with + // the same number of unencoded warm-up samples, which are used to kick start + // the prediction polynomial. The remainder of the subframe stores encoded + // residuals (signal errors) which specify the difference between the + // predicted and the original audio samples. + PredFIR +) + +// signExtend interprets x as a signed n-bit integer value and sign extends it +// to 32 bits. +func signExtend(x uint64, n uint) int32 { + // x is signed if its most significant bit is set. + if x&(1<<(n-1)) != 0 { + // Sign extend x. + return int32(x | ^uint64(0)<> uint(shift)) + } + return nil +} diff --git a/go.mod b/go.mod new file mode 100755 index 0000000..2986412 --- /dev/null +++ b/go.mod @@ -0,0 +1,8 @@ +module github.com/mewkiz/flac + +go 1.14 + +require ( + github.com/icza/bitio v1.1.0 + github.com/mewkiz/pkg v0.0.0-20230226050401-4010bf0fec14 +) diff --git a/go.sum b/go.sum new file mode 100755 index 0000000..3653f5a --- /dev/null +++ b/go.sum @@ -0,0 +1,37 @@ +github.com/d4l3k/messagediff v1.2.2-0.20190829033028-7e0a312ae40b/go.mod h1:Oozbb1TVXFac9FtSIxHBMnBCq2qeH/2KkEQxENCrlLo= +github.com/icza/bitio v1.1.0 h1:ysX4vtldjdi3Ygai5m1cWy4oLkhWTAi+SyO6HC8L9T0= +github.com/icza/bitio v1.1.0/go.mod h1:0jGnlLAx8MKMr9VGnn/4YrvZiprkvBelsVIbA9Jjr9A= +github.com/icza/mighty v0.0.0-20180919140131-cfd07d671de6 h1:8UsGZ2rr2ksmEru6lToqnXgA8Mz1DP11X4zSJ159C3k= +github.com/icza/mighty v0.0.0-20180919140131-cfd07d671de6/go.mod h1:xQig96I1VNBDIWGCdTt54nHt6EeI639SmHycLYL7FkA= +github.com/jszwec/csvutil v1.5.1/go.mod h1:Rpu7Uu9giO9subDyMCIQfHVDuLrcaC36UA4YcJjGBkg= +github.com/mewkiz/pkg v0.0.0-20230226050401-4010bf0fec14 h1:tnAPMExbRERsyEYkmR1YjhTgDM0iqyiBYf8ojRXxdbA= +github.com/mewkiz/pkg v0.0.0-20230226050401-4010bf0fec14/go.mod h1:QYCFBiH5q6XTHEbWhR0uhR3M9qNPoD2CSQzr0g75kE4= +github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/image v0.5.0/go.mod h1:FVC7BI/5Ym8R25iw5OLsgshdUBbT1h5jZTpA+mvAdZ4= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= diff --git a/internal/bits/reader.go b/internal/bits/reader.go new file mode 100755 index 0000000..85027bc --- /dev/null +++ b/internal/bits/reader.go @@ -0,0 +1,84 @@ +// Package bits provides bit access operations and binary decoding algorithms. +package bits + +import ( + "fmt" + "io" +) + +// A Reader handles bit reading operations. It buffers bits up to the next byte +// boundary. +type Reader struct { + // Underlying reader. + r io.Reader + // Temporary read buffer. + buf [8]uint8 + // Between 0 and 7 buffered bits since previous read operations. + x uint8 + // The number of buffered bits in x. + n uint +} + +// NewReader returns a new Reader that reads bits from r. +func NewReader(r io.Reader) *Reader { + return &Reader{r: r} +} + +// Read reads and returns the next n bits, at most 64. It buffers bits up to the +// next byte boundary. +func (br *Reader) Read(n uint) (x uint64, err error) { + if n == 0 { + return 0, nil + } + if n > 64 { + return 0, fmt.Errorf("bit.Reader.Read: invalid number of bits; n (%d) exceeds 64", n) + } + + // Read buffered bits. + if br.n > 0 { + switch { + case br.n == n: + br.n = 0 + return uint64(br.x), nil + case br.n > n: + br.n -= n + mask := ^uint8(0) << br.n + x = uint64(br.x&mask) >> br.n + br.x &^= mask + return x, nil + } + n -= br.n + x = uint64(br.x) + br.n = 0 + } + + // Fill the temporary buffer. + bytes := n / 8 + bits := n % 8 + if bits > 0 { + bytes++ + } + _, err = io.ReadFull(br.r, br.buf[:bytes]) + if err != nil { + return 0, err + } + + // Read bits from the temporary buffer. + for _, b := range br.buf[:bytes-1] { + x <<= 8 + x |= uint64(b) + } + b := br.buf[bytes-1] + if bits > 0 { + x <<= bits + br.n = 8 - bits + mask := ^uint8(0) << br.n + x |= uint64(b&mask) >> br.n + br.x = b & ^mask + } else { + x <<= 8 + x |= uint64(b) + } + + return x, nil +} diff --git a/internal/bits/reader_test.go b/internal/bits/reader_test.go new file mode 100755 index 0000000..5acaede --- /dev/null +++ b/internal/bits/reader_test.go @@ -0,0 +1,688 @@ +// © 2013 the Bits Authors under the MIT license. See AUTHORS for the list of authors. +// +// Some benchmark functions in this file were adapted from github.com/bamiaux/iobit +// which came with the following copyright notice: +// Copyright 2013 Benoît Amiaux. All rights reserved. + +package bits + +import ( + "bytes" + "io" + "math/rand" + "testing" +) + +func TestRead(t *testing.T) { + tests := []struct { + data []byte + ns []uint + vals []uint64 + }{ + // 11111111 + {[]byte{0xFF}, []uint{1, 1, 1, 1, 1, 1, 1, 1}, []uint64{1, 1, 1, 1, 1, 1, 1, 1}}, + {[]byte{0xFF}, []uint{2, 2, 2, 2}, []uint64{0x3, 0x3, 0x3, 0x3}}, + {[]byte{0xFF}, []uint{3, 3, 2}, []uint64{0x7, 0x7, 0x3}}, + {[]byte{0xFF}, []uint{4, 4}, []uint64{0xF, 0xF}}, + {[]byte{0xFF}, []uint{5, 3}, []uint64{0x1F, 0x7}}, + {[]byte{0xFF}, []uint{6, 2}, []uint64{0x3F, 0x3}}, + {[]byte{0xFF}, []uint{7, 1}, []uint64{0x7F, 0x1}}, + {[]byte{0xFF}, []uint{8}, []uint64{0xFF}}, + + // 10101010 + {[]byte{0xAA}, []uint{1, 1, 1, 1, 1, 1, 1, 1}, []uint64{1, 0, 1, 0, 1, 0, 1, 0}}, + {[]byte{0xAA}, []uint{2, 2, 2, 2}, []uint64{0x2, 0x2, 0x2, 0x2}}, + {[]byte{0xAA}, []uint{3, 3, 2}, []uint64{0x5, 0x2, 0x2}}, + {[]byte{0xAA}, []uint{4, 4}, []uint64{0xA, 0xA}}, + {[]byte{0xAA}, []uint{5, 3}, []uint64{0x15, 0x2}}, + {[]byte{0xAA}, []uint{6, 2}, []uint64{0x2A, 0x2}}, + {[]byte{0xAA}, []uint{7, 1}, []uint64{0x55, 0x0}}, + {[]byte{0xAA}, []uint{8}, []uint64{0xAA}}, + + {[]byte{0xAA}, []uint{0}, []uint64{0}}, + + // orig: 101010101010101010101010101010101010101010101010101010101010101010101010 + // 6 bits: 101010 (0x2A) + // 64 bits: 1010101010101010101010101010101010101010101010101010101010101010 (0xAAAAAAAAAAAAAAAA) + // 2 bit: 10 (0x2) + {[]byte{0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA}, []uint{6, 64, 2}, []uint64{0x2A, 0xAAAAAAAAAAAAAAAA, 0x2}}, + + // 01 1011011 011011 0 + {[]byte{0x6D, 0xB6}, []uint{2, 7, 6, 1}, []uint64{0x1, 0x5B, 0x1B, 0x0}}, + + { + []byte{0x21, 0x0F, 0xC7, 0xBB, 0x81, 0x86, 0x39, 0xAC, 0x48, 0xA4, 0xC6, 0xAF, 0xA2, 0xF1, 0x58, 0x1A, 0x8B, 0x95, 0x25, 0xE2, 0x0F, 0xDA, 0x68, 0x92, 0x7F, 0x2B, 0x2F, 0xF8, 0x36, 0xF7, 0x35, 0x78, 0xDB, 0x0F, 0xA5, 0x4C, 0x29, 0xF7, 0xFD, 0x92, 0x8D, 0x92, 0xCA, 0x43, 0xF1, 0x93, 0xDE, 0xE4, 0x7F, 0x59, 0x15, 0x49, 0xF5, 0x97, 0xA8, 0x11, 0xC8, 0xFA, 0x67, 0xAB, 0x03, 0x1E, 0xBD, 0x9C, 0x6A, 0xA4, 0xE9, 0x82, 0x9F, 0x22, 0x4B, 0xE8, 0xEA, 0xF6, 0x67, 0x26, 0xC9, 0x07, 0x7C, 0xB4, 0x1F, 0x79, 0x01, 0x9D, 0x89, 0x2B, 0xE9, 0x93, 0x03, 0xB2, 0xBE, 0x58, 0x82, 0xF3, 0x24, 0x07, 0x58, 0xA3, 0x8D, 0x7E}, + []uint{2, 40, 28, 62, 8, 59, 51, 54, 63, 49, 11, 42, 64, 7, 41, 2, 4, 58, 24, 55, 63, 13}, + []uint64{0x00, 0x843F1EEE06, 0x18E6B12, 0xA4C6AFA2F1581A8, 0xB9, 0x292F107ED34493F, 0x4ACBFE0DBDCD5, 0x38DB0FA54C29F7, 0x7EC946C96521F8C9, 0x1DEE47F591549, 0x7AC, 0x2F502391F4C, 0xF56063D7B38D549D, 0x18, 0x53E4497D1D, 0x01, 0x07, 0x2CCE4D920EF9683, 0xEF2033, 0x5892BE99303B2B, 0x72C41799203AC51C, 0xD7E}, + }, + { + []byte{0x0D, 0xAF, 0x62, 0xD6, 0x5D, 0xCE, 0x5B, 0xA5, 0x24, 0xF7, 0x35, 0x8E, 0xFB, 0xB5, 0xB8, 0x32, 0x20, 0xCF, 0x58, 0x63, 0x6C, 0xBC, 0x40, 0xCF, 0xAC, 0x9A, 0xEB, 0x3C, 0xC8, 0x47, 0xBC, 0xDC, 0xF1, 0x0F, 0x71, 0x7A, 0xA2, 0x62, 0x77, 0xFF, 0x0A, 0x3A, 0x0E, 0xC7, 0x3E, 0x10, 0xFF, 0x40, 0x8E, 0xCE, 0x87, 0x28, 0xF8, 0x4A, 0xE1, 0xAF, 0x7B, 0xBF, 0x86, 0xE9, 0x94, 0x6F, 0xB0, 0x8F, 0xB6, 0x58, 0x97, 0x5A, 0xB5, 0x52, 0x9D, 0x70, 0x40, 0x74, 0xAF, 0x4A, 0xC8, 0xC0, 0xF5, 0xA4, 0x6F, 0x09, 0x6E, 0xE8, 0x47, 0x7A, 0x77, 0x54, 0x30, 0x03, 0x8F, 0xC0, 0x0E, 0xAC, 0x03, 0xAF, 0x4D, 0xDC, 0xD3, 0x25}, + []uint{62, 44, 50, 50, 7, 53, 11, 9, 57, 37, 35, 54, 30, 45, 5, 8, 59, 9, 13, 48, 62, 46, 6}, + []uint64{0x36BD8B5977396E9, 0x493DCD63BEE, 0x35B83220CF586, 0xDB2F1033EB26, 0x5D, 0xCF3211EF373C4, 0x1EE, 0x5E, 0x151313BFF851D07, 0xC73E10FF4, 0x47674394, 0x1F095C35EF77F0, 0x374CA37D, 0x108FB658975A, 0x16, 0xAA, 0x29D704074AF4AC8, 0x181, 0x1D69, 0x1BC25BBA11DE, 0x2775430038FC00EA, 0x300EBD37734C, 0x25}, + }, + { + []byte{0x08, 0x0F, 0x1D, 0xFD, 0xC9, 0xC4, 0xAD, 0x25, 0x6F, 0x47, 0x56, 0x20, 0x46, 0xFC, 0x40, 0x54, 0xF5, 0x9B, 0x5B, 0xE5, 0x46, 0x5E, 0x75, 0xE6, 0xE0, 0xAA, 0x60, 0xC8, 0xEB, 0x2E, 0xE5, 0xD4, 0xCD, 0x26, 0x50, 0xA8, 0x1C, 0xCE, 0xE3, 0x55, 0x07, 0xA1, 0x1A, 0x37, 0x90, 0x71, 0xC7, 0x51, 0xF7, 0x1F, 0xDF, 0x0D, 0xFE, 0xB3, 0xFB, 0xC8, 0xF0, 0x08, 0x25, 0xE6, 0x4C, 0x27, 0x62, 0xFA, 0xC9, 0xFE, 0x63, 0xE2, 0x42, 0x03, 0x7A, 0x8B, 0xC4, 0x03, 0x69, 0x6E, 0x07, 0x33, 0x42, 0x37, 0x10, 0x4C, 0x5E, 0xC5, 0x64, 0x2C, 0xA3, 0xC1, 0xC2, 0x55, 0x0A, 0x87, 0x16, 0xA9, 0x28, 0xE7, 0xCD, 0xBA, 0xEA, 0xC9}, + []uint{64, 38, 1, 25, 10, 6, 13, 38, 27, 13, 48, 53, 3, 30, 60, 39, 32, 34, 5, 1, 56, 26, 47, 21, 43, 32, 35}, + []uint64{0x80F1DFDC9C4AD25, 0x1BD1D58811, 0x01, 0xFC4054, 0x3D6, 0x1B, 0xB7C, 0x2A32F3AF37, 0x2A9832, 0x759, 0x772EA6693285, 0x81CCEE35507A1, 0x00, 0x346F20E3, 0x8EA3EE3FBE1BFD6, 0x3FBC8F0082, 0x5E64C276, 0xBEB27F98, 0x1F, 0x00, 0x242037A8BC4036, 0x25B81CC, 0x6846E2098BD8, 0x1590B2, 0x478384AA150, 0xE2D5251C, 0x7CDBAEAC9}, + }, + { + []byte{0x49, 0x2C, 0x95, 0x56, 0xD7, 0x40, 0x88, 0xA9, 0x13, 0x2E, 0x4A, 0x5C, 0x13, 0xCC, 0x15, 0x9A, 0xA6, 0xEB, 0x4A, 0x0E, 0x9B, 0x96, 0x3C, 0xAD, 0xD1, 0x6E, 0x9C, 0x2D, 0xBA, 0xFD, 0xCE, 0x26, 0xC7, 0x18, 0xBC, 0xDC, 0x0F, 0xA7, 0xD4, 0xAD, 0x15, 0x5E, 0xEB, 0xCC, 0xB9, 0x46, 0x71, 0xE3, 0xDD, 0xFB, 0x4B, 0x99, 0x7D, 0x5B, 0x3F, 0xE4, 0xA4, 0x8B, 0x59, 0x8E, 0x7D, 0x89, 0xDF, 0xFF, 0x84, 0x0D, 0xAE, 0xCA, 0xA8, 0x9B, 0x8E, 0xF2, 0x31, 0xF6, 0xF2, 0x7E, 0x13, 0xDA, 0xEB, 0xE2, 0xED, 0xCD, 0xED, 0x9F, 0x38, 0xC6, 0x9E, 0x7F, 0x7A, 0xA1, 0x83, 0x4E, 0xDA, 0x01, 0xE3, 0x35, 0x41, 0x20, 0x10, 0xA6}, + []uint{18, 61, 54, 29, 43, 60, 15, 25, 42, 47, 8, 6, 7, 2, 37, 8, 13, 24, 49, 16, 22, 19, 36, 17, 43, 27, 9, 5, 36, 22}, + []uint64{0x124B2, 0xAAB6BA044548997, 0x94B827982B354, 0x1BAD283A, 0x372C795BA2D, 0xD385B75FB9C4D8E, 0x18BC, 0x1B81F4F, 0x2A568AAF75E, 0x32E519C78F77, 0xED, 0x0B, 0x4C, 0x02, 0x1F56CFF929, 0x22, 0x1ACC, 0x73EC4E, 0x1FFF840DAECAA, 0x89B8, 0x3BC8C7, 0x6DE4F, 0xC27B5D7C5, 0x1B737, 0x5B3E718D3CF, 0x77AA183, 0x9D, 0x16, 0x8078CD504, 0x2010A6}, + }, + { + []byte{0x7B, 0x22, 0xD1, 0x15, 0x7D, 0x2A, 0x8F, 0x5E, 0x35, 0x8E, 0xFD, 0x26, 0x3D, 0x98, 0xF0, 0x10, 0x18, 0xD7, 0x1E, 0xDC, 0x1D, 0x54, 0x3A, 0x4D, 0xF3, 0xED, 0xDB, 0x19, 0x46, 0xF8, 0x5B, 0xF3, 0xE5, 0x2C, 0x4B, 0xB6, 0x80, 0x08, 0x4D, 0x27, 0x71, 0x58, 0xAA, 0x81, 0x28, 0x1C, 0x8A, 0xB5, 0x47, 0x6A, 0x84, 0x1B, 0xF2, 0x23, 0xC1, 0xC0, 0x6E, 0x51, 0xF9, 0xB5, 0x19, 0x80, 0xCD, 0xF8, 0x06, 0x6B, 0x31, 0xF6, 0x23, 0x84, 0x1C, 0xB6, 0xBF, 0xEA, 0x59, 0x9B, 0xD8, 0x4F, 0x84, 0x04, 0xDB, 0x4B, 0x71, 0xE4, 0xAE, 0xF2, 0xD6, 0xE9, 0x2A, 0x16, 0x42, 0x9E, 0x0C, 0xFC, 0xA6, 0x84, 0x79, 0xC8, 0x2A, 0x23}, + []uint{52, 58, 42, 30, 54, 55, 26, 15, 18, 43, 47, 34, 58, 3, 15, 17, 48, 8, 50, 49, 37, 18, 23}, + []uint64{0x7B22D1157D2A8, 0x3D78D63BF498F66, 0xF01018D71E, 0x3707550E, 0x24DF3EDDB1946F, 0x42DF9F29625DB4, 0x109A4, 0x7715, 0x22AA0, 0x25039156A8E, 0x6A841BF223C1, 0x301B947E6, 0x351980CDF8066B3, 0x00, 0x7D88, 0x1C20E, 0x5B5FF52CCDEC, 0x27, 0x30809B696E3C9, 0xBBCB5BA4A859, 0x14F067E53, 0x108F3, 0x482A23}, + }, + { + []byte{0x0B, 0xBD, 0x5B, 0x49, 0x1C, 0x14, 0x1C, 0xE6, 0x96, 0x97, 0x97, 0x3C, 0x76, 0x70, 0xF4, 0x3E, 0xBA, 0x37, 0x88, 0xB2, 0x46, 0xBF, 0x22, 0xE9, 0xA2, 0x84, 0x7A, 0x3D, 0xF2, 0x12, 0xC9, 0xB5, 0x28, 0x15, 0x0A, 0x31, 0x4E, 0xFC, 0x13, 0x09, 0x02, 0x41, 0x3F, 0xCC, 0x8E, 0x0B, 0x06, 0xD1, 0xA3, 0x80, 0x6E, 0x48, 0x12, 0x00, 0xA7, 0xD2, 0x77, 0xCD, 0x9D, 0xB5, 0x91, 0x13, 0x0A, 0x45, 0xBB, 0xE3, 0xFA, 0x0F, 0xC7, 0x8F, 0x4F, 0x4C, 0x3C, 0xB3, 0xC1, 0xD7, 0xC8, 0x92, 0xB1, 0x32, 0x0B, 0x07, 0x21, 0x27, 0x60, 0x0A, 0xF5, 0x44, 0xDB, 0x90, 0x8C, 0x62, 0xBB, 0x20, 0xB1, 0x84, 0x3B, 0xDB, 0xAF, 0xDB}, + []uint{5, 4, 44, 10, 7, 54, 2, 22, 24, 49, 32, 52, 63, 57, 25, 5, 61, 22, 9, 25, 51, 12, 14, 1, 23, 57, 16, 40, 14}, + []uint64{0x01, 0x07, 0x7AB69238283, 0x273, 0x25, 0x297973C7670F43, 0x03, 0x2BA378, 0x8B246B, 0x1E45D34508F47, 0xBE425936, 0xA502A14629DF8, 0x130902413FCC8E0B, 0xDA34700DC9024, 0x29F49, 0x1B, 0x1CD9DB591130A45B, 0x2F8FE8, 0x7E, 0x78F4F4, 0x61E59E0EBE449, 0x589, 0x2416, 0x00, 0xE424E, 0x1802BD5136E4231, 0x8AEC, 0x82C610EF6E, 0x2FDB}, + }, + { + []byte{0x48, 0xB3, 0x01, 0xCE, 0x08, 0x20, 0xE4, 0xBD, 0x27, 0x21, 0x0A, 0x77, 0x34, 0x5A, 0x50, 0x74, 0xD5, 0x56, 0xCB, 0xE2, 0xCB, 0x7A, 0x63, 0x5F, 0xE3, 0x04, 0x52, 0x87, 0xA8, 0x16, 0x3D, 0x78, 0xE5, 0xC3, 0x82, 0x84, 0x80, 0xC9, 0x67, 0xD4, 0x34, 0xB4, 0xAF, 0xEF, 0x9F, 0x91, 0x5E, 0x1B, 0x8D, 0xF5, 0x43, 0x24, 0xB4, 0xDA, 0xD0, 0xBA, 0xC0, 0xEE, 0xF1, 0x94, 0xA1, 0xE8, 0xAC, 0xDB, 0x84, 0xB8, 0xDC, 0x99, 0x62, 0x4B, 0x19, 0xD1, 0xF8, 0xC5, 0x48, 0x7E, 0xEB, 0x9F, 0x82, 0xFF, 0xE9, 0xA4, 0x88, 0x86, 0x5C, 0x28, 0x60, 0x0F, 0xA3, 0xA7, 0x0B, 0x97, 0xFE, 0x4C, 0x99, 0x17, 0x08, 0xB5, 0x58, 0x94}, + []uint{34, 25, 22, 55, 18, 20, 35, 30, 1, 12, 30, 64, 32, 33, 51, 3, 32, 32, 15, 10, 25, 26, 26, 20, 62, 63, 24}, + []uint64{0x122CC0738, 0x410725, 0x3A4E42, 0xA77345A5074D5, 0x15B2F, 0x8B2DE, 0x4C6BFC608, 0x2943D40B, 0x00, 0x3D7, 0x23970E0A, 0x1203259F50D2D2BF, 0xBE7E4578, 0xDC6FAA19, 0x12D36B42EB03B, 0x05, 0xE32943D1, 0x59B70971, 0x5C99, 0x189, 0x58CE8F, 0x318A90F, 0x375CFC1, 0x7FF4D, 0x9110CB850C01F47, 0x270B97FE4C991708, 0xB55894}, + }, + { + []byte{0x48, 0x34, 0xA6, 0xF7, 0xD5, 0x71, 0x21, 0x1F, 0x5D, 0x73, 0xC4, 0xCF, 0x93, 0x0B, 0x9C, 0x62, 0xD3, 0xD2, 0x0F, 0x53, 0x68, 0xFC, 0x22, 0x1B, 0x99, 0x91, 0x60, 0x87, 0x45, 0x9C, 0x56, 0x41, 0x66, 0x1C, 0x32, 0x52, 0xB0, 0xAA, 0xA1, 0x65, 0xED, 0x1D, 0x0F, 0x3E, 0x40, 0x5B, 0x80, 0xD1, 0xE8, 0x6B, 0x4C, 0x1A, 0x7E, 0xAD, 0xC2, 0x77, 0x36, 0xA5, 0x02, 0x01, 0x21, 0x98, 0x92, 0x1C, 0x7A, 0xCB, 0x68, 0x3B, 0x03, 0xFC, 0xC9, 0x67, 0xF7, 0x77, 0x65, 0xE7, 0xFA, 0x5E, 0xF9, 0xE5, 0x92, 0x2A, 0x97, 0x7C, 0xAC, 0x82, 0xF5, 0xEE, 0xAD, 0x81, 0xF4, 0xB9, 0xF0, 0xF7, 0xA7, 0x9C, 0x91, 0xC6, 0x51, 0x4D}, + []uint{61, 55, 35, 55, 14, 6, 37, 60, 46, 25, 52, 62, 27, 23, 3, 27, 39, 19, 31, 2, 6, 52, 7, 48, 8}, + []uint64{0x90694DEFAAE2423, 0x75D73C4CF930B9, 0x63169E907, 0x54DA3F0886E664, 0x1608, 0x1D, 0x2CE2B20B3, 0xE1929585550B2F, 0x1A3A1E7C80B7, 0x347A1, 0xAD3069FAB709D, 0x336A502012198921, 0x63D65B4, 0xEC0FF, 0x01, 0x4967F77, 0x3B2F3FD2F7, 0x67964, 0x4552EF95, 0x02, 0x10, 0x5EBDD5B03E973, 0x70, 0xF7A79C91C651, 0x4D}, + }, + { + []byte{0x04, 0x2A, 0xDC, 0xC2, 0x25, 0xBF, 0x31, 0x81, 0x18, 0xE5, 0x6F, 0xAD, 0xE0, 0x60, 0x2C, 0xAC, 0x62, 0xF2, 0xD5, 0x59, 0xB9, 0x26, 0xAE, 0x4D, 0x67, 0x86, 0x7B, 0x23, 0xA2, 0xCB, 0xAC, 0x63, 0x06, 0xB2, 0xE3, 0x2F, 0x73, 0x59, 0x64, 0x79, 0xAC, 0x74, 0x15, 0xF2, 0x51, 0x14, 0xFB, 0x45, 0x06, 0xBB, 0xF0, 0x29, 0x5A, 0xD2, 0x90, 0x6F, 0x24, 0xB9, 0x8F, 0x06, 0x54, 0xAE, 0x56, 0x33, 0x3D, 0x79, 0x92, 0x42, 0x50, 0xCF, 0x16, 0x53, 0xCB, 0xC6, 0x57, 0x45, 0x17, 0xEA, 0x69, 0x40, 0xAC, 0xCB, 0x97, 0x74, 0xA0, 0x8A, 0x79, 0x40, 0xA1, 0x2E, 0x63, 0xCA, 0x61, 0xCD, 0x98, 0x2B, 0xCF, 0x55, 0x3A, 0xCB}, + []uint{4, 14, 31, 4, 42, 46, 62, 30, 58, 51, 6, 1, 39, 61, 52, 24, 48, 57, 6, 50, 44, 1, 54, 15}, + []uint64{0x00, 0x10AB, 0x39844B7E, 0x06, 0xC08C72B7D6, 0x3C0C05958C5E, 0x16AACDC935726B3C, 0xCF64745, 0x25D63183597197B, 0x4D6591E6B1D05, 0x1F, 0x00, 0x25114FB450, 0xD77E052B5A520DE, 0x49731E0CA95CA, 0xC667AF, 0x32484A19E2CA, 0xF2F195D145FA9A, 0x14, 0x2B32E5DD2822, 0x9E50284B98F, 0x00, 0x14C39B30579EAA, 0x3ACB}, + }, + { + []byte{0x8A, 0xCC, 0x20, 0x56, 0x0B, 0x1B, 0x64, 0xA3, 0x37, 0x3A, 0x54, 0xD7, 0x6E, 0x2B, 0x16, 0x8E, 0x92, 0xE5, 0xC1, 0xCA, 0x2B, 0xE8, 0x00, 0x8A, 0x64, 0xBF, 0x5C, 0x3F, 0x3F, 0xF6, 0x3C, 0x11, 0x80, 0x34, 0x84, 0x3E, 0xE4, 0x04, 0x51, 0x7C, 0x54, 0xA0, 0x07, 0x59, 0xD3, 0x2E, 0x19, 0x3E, 0x1E, 0xAE, 0x16, 0x47, 0x2F, 0xF5, 0xF1, 0x19, 0xB6, 0xA1, 0x36, 0x2D, 0xFC, 0x1B, 0x86, 0x13, 0xE0, 0xF4, 0xF9, 0x68, 0x57, 0x9D, 0x1F, 0xDE, 0xE5, 0x12, 0xD1, 0x94, 0x47, 0xD8, 0xE5, 0x2A, 0xAF, 0xCE, 0xE0, 0xB7, 0x98, 0xB2, 0xE1, 0xEE, 0xA5, 0x7A, 0x89, 0xB4, 0x06, 0x8B, 0x59, 0xD6, 0xBF, 0x69, 0x56, 0x4B}, + []uint{13, 1, 5, 35, 47, 53, 53, 18, 10, 42, 12, 6, 6, 62, 43, 42, 38, 55, 35, 21, 27, 57, 25, 13, 32, 33, 6, 10}, + []uint64{0x1159, 0x01, 0x01, 0x1582C6D9, 0x1466E74A9AED, 0x18AC5A3A4B9707, 0x515F40045325F, 0x2B87E, 0x1FF, 0x2C782300690, 0x87D, 0x32, 0x00, 0x228BE2A5003ACE99, 0x3864F87AB85, 0x2472FF5F119, 0x2DA84D8B7F, 0x370C27C1E9F2D, 0x579D1FDE, 0x1CA25A, 0x19447D8, 0x1CA555F9DC16F31, 0xCB87BA, 0x12BD, 0x44DA0345, 0x159D6BF69, 0x15, 0x24B}, + }, + { + []byte{0x47, 0xC2, 0x96, 0xCD, 0x76, 0xCD, 0x5C, 0x93, 0xA4, 0x08, 0xD0, 0x96, 0x39, 0x5C, 0xE1, 0x02, 0x05, 0xBF, 0xD5, 0x7B, 0xF8, 0xD6, 0xCD, 0x5D, 0x30, 0x6E, 0xD2, 0x31, 0x28, 0xEB, 0x5C, 0x3C, 0x4A, 0x95, 0xF1, 0x3F, 0xB6, 0xA8, 0xAD, 0xB8, 0x53, 0xC8, 0xED, 0x3D, 0x9D, 0xB4, 0xC2, 0x2F, 0xE5, 0x79, 0x94, 0x3F, 0x15, 0x38, 0xBE, 0xEB, 0x51, 0x9B, 0xB9, 0x6F, 0x6F, 0xF1, 0x4F, 0xA6, 0x7F, 0xB4, 0xD0, 0x1B, 0xF7, 0x8A, 0xF7, 0xCC, 0xD8, 0x36, 0x17, 0xD4, 0x9A, 0xAE, 0xFF, 0x04, 0x07, 0xAA, 0x86, 0xC6, 0x12, 0x31, 0x77, 0x8A, 0x5B, 0x15, 0x0B, 0xEB, 0x1C, 0xA4, 0xF8, 0xA2, 0x12, 0xD8, 0x81, 0xA2}, + []uint{16, 21, 34, 33, 40, 35, 26, 3, 34, 6, 45, 11, 45, 44, 6, 14, 7, 32, 11, 16, 35, 16, 48, 43, 63, 14, 13, 34, 35, 13, 7}, + []uint64{0x47C2, 0x12D9AE, 0x366AE49D2, 0x8D09639, 0x5CE10205BF, 0x6ABDFC6B6, 0x1ABA60D, 0x06, 0x348C4A3AD, 0x1C, 0x78952BE27F6, 0x6A8, 0x15B70A791DA7, 0xB3B69845FCA, 0x3C, 0x3287, 0x71, 0x538BEEB5, 0xCD, 0xDCB7, 0x5BFC53E99, 0xFED3, 0x406FDE2BDF33, 0x306C2FA9355, 0x6FF0407AA86C6123, 0x5DE, 0x52D, 0x22A17D639, 0x24F8A212D, 0x1103, 0x22}, + }, + { + []byte{0xAD, 0x57, 0x64, 0x74, 0x59, 0x9A, 0x31, 0x7D, 0x46, 0x3A, 0xD3, 0x15, 0xEA, 0xE7, 0x3A, 0xBC, 0xBF, 0xE7, 0x6A, 0x36, 0xF2, 0x99, 0x7A, 0x5F, 0x09, 0xB2, 0x3E, 0xE5, 0xD6, 0x7D, 0x84, 0x7A, 0x62, 0xB3, 0xD8, 0xC3, 0x84, 0x42, 0x40, 0x59, 0xBE, 0x8A, 0x59, 0xC7, 0x35, 0xD7, 0xC9, 0xB2, 0x6F, 0xDD, 0x67, 0x04, 0x3B, 0x20, 0xDC, 0x27, 0x63, 0x82, 0x34, 0x3D, 0xBC, 0xE6, 0x29, 0x45, 0x15, 0x94, 0xC9, 0x0C, 0xFA, 0xCF, 0x0F, 0x35, 0x58, 0x06, 0x12, 0x51, 0x18, 0xE7, 0x42, 0x11, 0x39, 0xB3, 0xB4, 0xDD, 0xFE, 0x3F, 0xD7, 0x54, 0xB0, 0xF3, 0x56, 0x61, 0x7D, 0xC4, 0x09, 0xA2, 0x1E, 0x36, 0xFA, 0xFA}, + []uint{23, 21, 29, 16, 60, 23, 38, 41, 14, 27, 15, 61, 34, 40, 47, 21, 23, 51, 2, 30, 50, 53, 38, 4, 29, 10}, + []uint64{0x56ABB2, 0x74599, 0x1462FA8C, 0x75A6, 0x2BD5CE75797FCED, 0x236F29, 0x25E97C26C8, 0x1F72EB3EC23, 0x34C5, 0x33D8C38, 0x2212, 0x59BE8A59C735D7, 0x326C9BF75, 0x9C10EC8370, 0x4EC704687B79, 0x198A51, 0x22B299, 0x10CFACF0F3558, 0x00, 0x6125118, 0x39D0844E6CED3, 0xEFF1FEBAA5879, 0x2ACC2FB881, 0x03, 0x8878DBE, 0x2FA}, + }, + { + []byte{0xCD, 0x03, 0x06, 0x80, 0x27, 0x9D, 0x7E, 0x06, 0x6D, 0x29, 0x42, 0xAE, 0x44, 0x8D, 0xA3, 0xC9, 0x85, 0x03, 0xF1, 0x4D, 0x48, 0xB6, 0xC7, 0xDF, 0xBF, 0x87, 0x7E, 0x58, 0xB1, 0x92, 0x87, 0x05, 0x06, 0xE1, 0x6E, 0x0D, 0x15, 0x51, 0x99, 0xC5, 0x6C, 0x71, 0xCF, 0xFB, 0x8F, 0xE2, 0xD3, 0xA2, 0x31, 0x90, 0x4F, 0x38, 0x9C, 0x3A, 0xD7, 0x50, 0xD0, 0x75, 0x70, 0x33, 0x8B, 0x62, 0x44, 0x23, 0x9E, 0x6A, 0x8C, 0xE7, 0xB2, 0xCA, 0x84, 0x3C, 0x9F, 0x57, 0x70, 0x51, 0xA5, 0xBE, 0xD4, 0x4A, 0x3A, 0x61, 0xE8, 0x4E, 0x75, 0x25, 0xCD, 0x4E, 0x72, 0x5C, 0x69, 0xB3, 0x5B, 0x74, 0xF2, 0x4B, 0x76, 0x9C, 0x8B, 0xF0}, + []uint{47, 52, 30, 11, 13, 23, 37, 17, 13, 18, 26, 3, 64, 5, 37, 21, 37, 46, 28, 8, 23, 48, 1, 9, 30, 42, 15, 23, 26, 16, 12, 19}, + []uint64{0x6681834013CE, 0xBF033694A1572, 0x91B4793, 0x50, 0x7E2, 0x4D48B6, 0x18FBF7F0EF, 0x1962C, 0xC94, 0xE0A0, 0x370B706, 0x04, 0x55466715B1C73FEE, 0x07, 0x1E2D3A2319, 0x9E71, 0x70EB5D434, 0x7570338B624, 0x4239E6A, 0x8C, 0x73D965, 0x421E4FABB828, 0x01, 0x14B, 0x1F6A251D, 0xC3D09CEA4B, 0x4D4E, 0x392E34, 0x366B6E9, 0xE496, 0xED3, 0x48BF0}, + }, + { + []byte{0x69, 0x4F, 0x3D, 0x68, 0x86, 0x48, 0x1B, 0x9E, 0x94, 0xFF, 0xDE, 0x36, 0xDD, 0xD1, 0xBF, 0x69, 0x48, 0x8D, 0xCF, 0x2C, 0x2A, 0xA0, 0xA2, 0xFF, 0xC9, 0xD9, 0x6A, 0x99, 0x3D, 0x6D, 0xFA, 0x39, 0x79, 0xAC, 0x87, 0xC1, 0xAF, 0xCE, 0x2A, 0xCF, 0x09, 0x84, 0xFD, 0xC1, 0xE6, 0xC4, 0x27, 0x2E, 0x4A, 0x4C, 0x64, 0x0F, 0xBC, 0x81, 0xFA, 0xED, 0xED, 0x23, 0x29, 0x02, 0x6F, 0xF5, 0x81, 0xC5, 0x18, 0x33, 0x08, 0xC8, 0x7F, 0xE0, 0xD0, 0x90, 0xC0, 0x12, 0x77, 0xEE, 0xEB, 0x6A, 0x6C, 0x11, 0x08, 0xA0, 0xBB, 0xF2, 0x94, 0x80, 0xA1, 0x98, 0xBB, 0x44, 0xDC, 0xE4, 0x07, 0x99, 0x1D, 0x54, 0x18, 0x58, 0x14, 0x39}, + []uint{20, 47, 3, 29, 11, 38, 53, 36, 6, 20, 14, 8, 55, 6, 32, 23, 49, 28, 16, 50, 20, 55, 41, 3, 31, 13, 33, 39, 21}, + []uint64{0x694F3, 0x6B443240DCF4, 0x05, 0x7FEF1B6, 0x774, 0x1BF69488DC, 0x1E58554145FF93, 0xB2D5327AD, 0x2F, 0xD1CBC, 0x3590, 0xF8, 0x1AFCE2ACF0984F, 0x37, 0x79B109C, 0x5C9498, 0x1903EF207EBB7, 0xB48CA40, 0x9BFD, 0x181C5183308C8, 0x7FE0D, 0x4860093BF775B, 0xA6C1108A0B, 0x05, 0x7CA52028, 0xCC5, 0x1B44DCE40, 0x3CC8EAA0C2, 0x181439}, + }, + { + []byte{0xAA, 0xE8, 0x77, 0xB9, 0x9B, 0xB1, 0xDF, 0xCD, 0x24, 0x21, 0xCA, 0xFC, 0x15, 0x32, 0xFC, 0x71, 0x41, 0x53, 0x20, 0x3C, 0x9A, 0x26, 0x5E, 0x35, 0x58, 0x6E, 0x97, 0xC3, 0xEE, 0x5A, 0x8F, 0x6C, 0x26, 0x9C, 0xD9, 0xB0, 0x54, 0xCE, 0x57, 0x3D, 0xDC, 0x41, 0x56, 0xE6, 0xBE, 0x7A, 0x20, 0xEF, 0x04, 0xF6, 0x14, 0x4F, 0x21, 0x78, 0x5A, 0x55, 0x66, 0xA1, 0x3D, 0xF8, 0x7D, 0x5C, 0x10, 0x3C, 0x3F, 0x28, 0x4C, 0x05, 0x62, 0x3F, 0xDA, 0xBF, 0x11, 0xA7, 0x02, 0x8E, 0xC8, 0x4F, 0xC2, 0x4A, 0x86, 0xE4, 0xFD, 0xD4, 0xAC, 0x37, 0x10, 0xD7, 0x60, 0x05, 0x97, 0xE0, 0x08, 0xD8, 0xB6, 0xAA, 0x08, 0xC9, 0xD5, 0x1F}, + []uint{9, 23, 6, 21, 31, 58, 51, 34, 50, 22, 35, 63, 13, 37, 12, 29, 63, 64, 16, 46, 9, 28, 47, 33}, + []uint64{0x155, 0x6877B9, 0x26, 0x1D8EFE, 0x3490872B, 0x3C1532FC7141532, 0x1E4D132F1AAC, 0xDD2F87DC, 0x2D47B6134E6CD, 0x20A99C, 0x573DDC415, 0x3735F3D1077827B0, 0x144F, 0x42F0B4AAC, 0xD42, 0xF7E1F57, 0x20787E50980AC47, 0xFB57E234E051D909, 0xF849, 0x143727EEA561, 0x171, 0xD76005, 0x4BF0046C5B55, 0x8C9D51F}, + }, + { + []byte{0x7E, 0x3B, 0x8C, 0x41, 0x5D, 0x41, 0x24, 0xF4, 0x14, 0x8C, 0x75, 0xCB, 0x0D, 0x8E, 0x08, 0xEA, 0xD9, 0xE5, 0x84, 0x3E, 0x54, 0xA0, 0xD9, 0x22, 0xF5, 0xB8, 0x0D, 0xED, 0x3A, 0x7F, 0x93, 0x06, 0xF8, 0xC8, 0x4A, 0x60, 0x15, 0x65, 0x06, 0x43, 0xFF, 0x3A, 0x50, 0xFE, 0xCE, 0xE0, 0x15, 0x1F, 0x03, 0x7F, 0x2B, 0xB1, 0x04, 0x07, 0x9C, 0xCD, 0x9B, 0xEA, 0xCE, 0xC7, 0xAB, 0x96, 0xD5, 0x42, 0x88, 0x93, 0xBB, 0xD0, 0x2B, 0x74, 0x06, 0x68, 0x3E, 0xE2, 0x80, 0xDA, 0x78, 0x10, 0x94, 0x47, 0x3C, 0x7B, 0xA1, 0x8F, 0x27, 0x55, 0xD7, 0x37, 0xA9, 0x5F, 0xDD, 0x1D, 0xE1, 0xF4, 0x03, 0xA8, 0x99, 0xC2, 0xD0, 0x86}, + []uint{34, 59, 46, 49, 28, 60, 53, 47, 38, 45, 23, 33, 8, 1, 8, 12, 11, 50, 16, 25, 30, 33, 2, 3, 33, 35, 18}, + []uint64{0x1F8EE3105, 0x3A8249E82918EB9, 0x186C704756CF, 0x5843E54A0D92, 0x2F5B80D, 0xED3A7F9306F8C84, 0x14C02ACA0C87FE, 0x3A50FECEE015, 0x7C0DFCAEC, 0x8203CE66CDF, 0x2B3B1E, 0x15CB6AA14, 0x44, 0x01, 0x3B, 0xBD0, 0x15B, 0x280CD07DC501B, 0x4F02, 0x2511CF, 0x7BA18F2, 0xEABAE6F5, 0x00, 0x05, 0xFEE8EF0F, 0x500EA2670, 0x2D086}, + }, + { + []byte{0xDD, 0xE8, 0x87, 0xA5, 0xCA, 0xBB, 0xC8, 0x9F, 0x2F, 0xAF, 0x66, 0xE8, 0xB9, 0x48, 0x0D, 0x31, 0xB5, 0x8E, 0xAA, 0x48, 0x6A, 0x74, 0xCB, 0xC5, 0x9E, 0x1C, 0x1D, 0xE5, 0x1D, 0x89, 0x0A, 0x4B, 0x10, 0x12, 0xAA, 0xFB, 0x08, 0x6B, 0x10, 0x62, 0x55, 0x94, 0xA1, 0x38, 0xB0, 0x4D, 0x3F, 0xB1, 0xA8, 0xFC, 0xA2, 0x0E, 0x6C, 0x6B, 0x65, 0x73, 0x2D, 0x61, 0x47, 0x24, 0xB9, 0xBB, 0xA0, 0x4A, 0xF8, 0x05, 0xF4, 0x74, 0x1B, 0x06, 0x99, 0x93, 0x63, 0x4A, 0xF7, 0x9D, 0x43, 0x23, 0x00, 0x30, 0x39, 0x5A, 0x65, 0x8A, 0xA4, 0x13, 0xF1, 0x6A, 0x29, 0xB7, 0x16, 0x24, 0xDD, 0xDB, 0xF7, 0x75, 0x05, 0x94, 0x48, 0x76}, + []uint{32, 10, 14, 19, 40, 24, 28, 60, 31, 59, 43, 11, 27, 19, 22, 33, 46, 51, 41, 10, 29, 31, 34, 9, 25, 28, 24}, + []uint64{0xDDE887A5, 0x32A, 0x3BC8, 0x4F97D, 0x7B3745CA40, 0x698DAC, 0x7552435, 0x3A65E2CF0E0EF28, 0x7624292C, 0x202555F610D620C, 0x25594A138B0, 0x269, 0x7EC6A3F, 0x1441C, 0x3635B2, 0x1732D6147, 0x92E6EE812BE, 0xBE8E8360D33, 0x4D8D2BDE75, 0x32, 0x6006072, 0x5A658AA4, 0x4FC5A8A6, 0x1B8, 0x1624DDD, 0xBF77505, 0x944876}, + }, + { + []byte{0x80, 0x94, 0x8F, 0xFF, 0xAB, 0xB3, 0x58, 0x12, 0x91, 0x7D, 0xEB, 0xBA, 0x4F, 0xF2, 0x01, 0x6B, 0xCD, 0xFF, 0x44, 0xB7, 0xD7, 0xC7, 0x19, 0xAE, 0x12, 0x9A, 0x65, 0x48, 0x18, 0xD7, 0x10, 0x30, 0x2D, 0x72, 0x76, 0xC1, 0x97, 0x4E, 0xC2, 0x91, 0x40, 0x30, 0x92, 0x47, 0x5E, 0xCB, 0xC2, 0x85, 0xBA, 0xF4, 0xEF, 0x47, 0x3D, 0x70, 0x2D, 0x64, 0xD8, 0x63, 0xAD, 0xB2, 0x96, 0xC4, 0xF1, 0x1A, 0x57, 0xB2, 0xAB, 0xF4, 0x6E, 0x1E, 0x4E, 0x4E, 0x93, 0x32, 0xF1, 0x6C, 0x76, 0x1F, 0xA1, 0xAE, 0xD6, 0x5A, 0x27, 0x1D, 0x37, 0xA9, 0x2B, 0x32, 0x4A, 0xF6, 0x29, 0x47, 0xC0, 0x29, 0x69, 0xCE, 0xA9, 0x64, 0x93, 0xBC}, + []uint{37, 49, 14, 17, 29, 33, 20, 9, 52, 30, 60, 24, 18, 22, 41, 3, 11, 30, 23, 14, 38, 14, 20, 27, 6, 15, 25, 63, 42, 4, 10}, + []uint64{0x101291FFF5, 0xECD604A45F7A, 0x3BA4, 0x1FE40, 0x5AF37FD, 0x25BEBE38, 0xCD709, 0x9A, 0x654818D710302, 0x35C9DB06, 0x5D3B0A4500C2491, 0xD7B2F0, 0x285BA, 0x3D3BD1, 0x19EB816B26C, 0x01, 0x475, 0x2D94B627, 0x44695E, 0x32AB, 0x3D1B879393, 0x2933, 0x2F16C, 0x3B0FD0D, 0x1D, 0x565A, 0x4E3A6F, 0x292B324AF62947C0, 0xA5A73AA592, 0x04, 0x3BC}, + }, + { + []byte{0x00, 0xFA, 0x33, 0x12, 0xCB, 0xA1, 0x5D, 0xC3, 0x6C, 0x6A, 0x85, 0xBF, 0xA2, 0x49, 0x3C, 0x04, 0x16, 0xD2, 0xF2, 0x2E, 0xCA, 0x84, 0x20, 0x29, 0x9D, 0x54, 0x0C, 0xE2, 0x4A, 0x7D, 0x26, 0x36, 0x54, 0xC2, 0x7B, 0x72, 0x3D, 0x4A, 0xA6, 0x6E, 0xAD, 0xDE, 0xF7, 0x94, 0x78, 0xF5, 0xB5, 0x7D, 0x24, 0xB2, 0x9C, 0xC4, 0x5E, 0xA5, 0xE2, 0xF6, 0x2D, 0x5C, 0x7D, 0x8D, 0x32, 0x47, 0x4A, 0xC6, 0x51, 0xBA, 0x66, 0x80, 0xA4, 0xF9, 0x22, 0xF0, 0x3E, 0x51, 0x09, 0xAD, 0x1E, 0x26, 0x1E, 0xC5, 0x0C, 0x2A, 0xFA, 0x7A, 0xDC, 0x8A, 0x6F, 0xF2, 0x3C, 0x0A, 0xD4, 0xAB, 0x25, 0x1F, 0xFD, 0x1A, 0xC1, 0x9E, 0x35, 0x8D}, + []uint{11, 43, 6, 27, 24, 36, 9, 63, 39, 53, 11, 56, 17, 59, 57, 52, 42, 11, 45, 15, 54, 17, 21, 13, 18, 1}, + []uint64{0x07, 0x68CC4B2E857, 0x1C, 0x1B63542, 0xDFD124, 0x9E020B697, 0x122, 0x765421014CEAA067, 0x929F498D9, 0xA613DB91EA553, 0x1BA, 0xB77BDE51E3D6D5, 0x1E925, 0x4A73117A978BD8B, 0xAE3EC69923A563, 0x28DD3340527C9, 0x5E07CA2135, 0x51E, 0x4C3D8A1855F, 0x27AD, 0x3229BFC8F02B52, 0x15928, 0x1FFD1A, 0x1833, 0x31AC6, 0x01}, + }, + { + []byte{0x78, 0xF4, 0x61, 0xC2, 0x47, 0x09, 0x4B, 0x98, 0xDD, 0x2A, 0xF5, 0x9F, 0x24, 0x99, 0x71, 0x4A, 0x3E, 0x0E, 0xB0, 0xEF, 0xB0, 0xAA, 0x6C, 0x61, 0xB2, 0xF6, 0x46, 0x0F, 0xB5, 0x94, 0x3E, 0x64, 0x4A, 0x5B, 0x55, 0xF4, 0x16, 0xF0, 0x65, 0xB4, 0x74, 0xD9, 0x3F, 0x07, 0x29, 0x7E, 0x12, 0xBF, 0xF6, 0xC1, 0x39, 0x0B, 0x1E, 0x33, 0xC0, 0x65, 0xEC, 0x68, 0xA2, 0x1E, 0xBE, 0xD1, 0x5A, 0x66, 0xC2, 0xCD, 0xA6, 0xF2, 0x46, 0x39, 0x03, 0x84, 0xE1, 0xF9, 0x72, 0x46, 0xF5, 0x3E, 0xA3, 0x3B, 0x49, 0x47, 0x0D, 0xB2, 0xBA, 0x43, 0x5A, 0x6B, 0x52, 0xF2, 0x01, 0x99, 0x3F, 0xBB, 0x0F, 0xA3, 0x55, 0x95, 0xB3, 0x5E}, + []uint{22, 44, 17, 40, 32, 4, 31, 20, 14, 57, 50, 57, 3, 10, 24, 59, 58, 18, 40, 51, 19, 25, 33, 55, 17}, + []uint64{0x1E3D18, 0x7091C252E63, 0xE957, 0xACF924CB8A, 0x51F07587, 0x07, 0x6C2A9B18, 0x6CBD9, 0x60F, 0x16B287CC894B6AB, 0x3A0B7832DA3A6, 0x193F07297E12BFF, 0x03, 0x182, 0x72163C, 0x33C065EC68A21EB, 0x3B45699B0B369BC, 0x24639, 0x384E1F972, 0x237A9F519DA4A, 0x1C36C, 0x15D21AD, 0x6B52F201, 0x4C9FDD87D1AACA, 0x1B35E}, + }, + { + []byte{0x5D, 0x8E, 0x1E, 0xCA, 0x9E, 0xB2, 0x62, 0x1F, 0xC3, 0x5C, 0xD0, 0x64, 0xC8, 0x63, 0xD9, 0x53, 0x2F, 0x2E, 0x5E, 0x6D, 0x03, 0xC6, 0xB1, 0xFE, 0x3A, 0xAE, 0x99, 0x3C, 0x47, 0x91, 0x34, 0x15, 0x1C, 0x95, 0xE4, 0xD1, 0xD0, 0xC0, 0xE7, 0xFD, 0x66, 0x35, 0x33, 0xC3, 0x03, 0xAA, 0x77, 0xCC, 0x1C, 0x77, 0x92, 0xB9, 0xA2, 0xD7, 0xE4, 0xEA, 0xD1, 0x4A, 0x74, 0xF4, 0x05, 0x4C, 0xCB, 0xBF, 0xEB, 0x98, 0x20, 0x8F, 0x77, 0x4D, 0xAA, 0x13, 0xD6, 0xB1, 0xCF, 0x80, 0x9D, 0x6B, 0xDB, 0x02, 0xA9, 0xA3, 0xD2, 0x0F, 0xCA, 0x4D, 0xDC, 0xC5, 0x58, 0x10, 0x3D, 0x96, 0x82, 0xF1, 0x00, 0x32, 0x6B, 0x25, 0xFC, 0x0E}, + []uint{39, 44, 24, 38, 3, 27, 12, 58, 22, 28, 54, 20, 35, 30, 27, 46, 41, 35, 44, 19, 1, 20, 41, 8, 36, 48}, + []uint64{0x2EC70F654F, 0x59310FE1AE6, 0x832643, 0x7B2A65E5C, 0x05, 0x73681E3, 0x58F, 0x3C755D32788F226, 0x20A8E4, 0xAF268E8, 0x181CFFACC6A678, 0x60754, 0x77CC1C779, 0xAE68B5F, 0x49D5A29, 0x13A7A02A665D, 0x1FEB98208F7, 0x3A6D509EB, 0x58E7C04EB5E, 0x6C0AA, 0x00, 0xD1E90, 0xFCA4DDCC55, 0x81, 0x3D9682F1, 0x326B25FC0E}, + }, + { + []byte{0xB4, 0x6D, 0x53, 0x49, 0xB7, 0x06, 0x34, 0x33, 0x99, 0x26, 0xC8, 0x16, 0x3E, 0x5F, 0x8E, 0x87, 0x26, 0xDE, 0x13, 0xBD, 0xBA, 0xEA, 0x1A, 0x03, 0xCD, 0x2E, 0xAC, 0x7D, 0x79, 0xFA, 0x25, 0x4F, 0xAF, 0x5B, 0xC4, 0xE4, 0x6A, 0x50, 0x33, 0x83, 0xCC, 0xF5, 0x49, 0x3E, 0xCF, 0xAC, 0xAA, 0x94, 0x89, 0x33, 0xB6, 0xC4, 0x77, 0xBF, 0xAF, 0xE6, 0xFA, 0x07, 0x07, 0x5B, 0x8E, 0x6B, 0x8B, 0xFE, 0x1C, 0xE3, 0x20, 0xF3, 0x17, 0x54, 0x3F, 0x38, 0x99, 0xD4, 0xAB, 0x85, 0xE7, 0x7D, 0x47, 0xE3, 0x41, 0x98, 0xE9, 0x5B, 0xF4, 0x38, 0x18, 0xEE, 0x13, 0x64, 0x2E, 0xC3, 0x5F, 0x22, 0x42, 0x41, 0xCB, 0x62, 0x59, 0xBE}, + []uint{56, 11, 10, 52, 2, 11, 13, 21, 25, 24, 13, 2, 52, 27, 43, 10, 16, 41, 13, 46, 60, 57, 4, 62, 43, 13, 40, 28, 5}, + []uint64{0xB46D5349B70634, 0x19C, 0x324, 0xD902C7CBF1D0E, 0x01, 0x1B7, 0x109D, 0x1DBAEA, 0x34079A, 0x5D58FA, 0x1E7E, 0x02, 0x254FAF5BC4E46, 0x52819C1, 0x733D524FB3E, 0x2CA, 0xA948, 0x12676D88EF7, 0x1EBF, 0x26FA07075B8E, 0x6B8BFE1CE320F31, 0xEA87E7133A9570, 0x0B, 0x33BEA3F1A0CC74AD, 0x7D0E063B84D, 0x1217, 0x61AF912120, 0xE5B12CD, 0x1E}, + }, + { + []byte{0x23, 0xFE, 0xE6, 0xE3, 0x2F, 0x43, 0xE0, 0x65, 0xBD, 0xCA, 0xD1, 0xD8, 0x44, 0x1A, 0x37, 0x2C, 0xF8, 0xB3, 0x17, 0x83, 0x0E, 0x12, 0xDB, 0x23, 0x90, 0x83, 0x9C, 0xBF, 0x7D, 0x16, 0x5E, 0x28, 0xCA, 0x6E, 0x86, 0xE0, 0x09, 0x61, 0x71, 0xE5, 0x00, 0x4D, 0xCE, 0xE1, 0x8A, 0xA8, 0xBD, 0xB1, 0xD5, 0xC4, 0xF2, 0xEF, 0x0B, 0xEE, 0x07, 0x6A, 0x12, 0x2F, 0xB1, 0x2B, 0x5D, 0x0B, 0xC4, 0xD3, 0xE0, 0x9A, 0xC6, 0xDC, 0x25, 0x75, 0x87, 0xC8, 0xB2, 0x7B, 0xAE, 0x6A, 0xC8, 0x3C, 0xF9, 0x2A, 0xC6, 0xD7, 0xBD, 0x6D, 0x88, 0x91, 0x82, 0xAB, 0x01, 0xA0, 0x3C, 0x3E, 0xE2, 0x5B, 0x22, 0xB9, 0xF4, 0x40, 0x13, 0x27}, + []uint{46, 17, 6, 2, 47, 18, 58, 46, 1, 3, 45, 56, 64, 21, 53, 35, 1, 38, 57, 31, 7, 49, 8, 60, 31}, + []uint64{0x8FFB9B8CBD0, 0x1F032, 0x37, 0x02, 0x72B47611068D, 0x32CF8, 0x2CC5E0C384B6C8E, 0x10839CBF7D16, 0x00, 0x05, 0x1C5194DD0DC0, 0x12C2E3CA009B9D, 0xC315517B63AB89E5, 0x1BC2FB, 0x103B50917D895A, 0x742F134F8, 0x00, 0x1358DB84AE, 0x161F22C9EEB9AB2, 0x79F2558, 0x6D, 0xF7ADB1123055, 0x60, 0x340787DC4B64573, 0x74401327}, + }, + { + []byte{0xD7, 0x29, 0xE8, 0xD8, 0x9D, 0x5F, 0x04, 0x11, 0x02, 0x6B, 0xDE, 0x9E, 0x4F, 0x4B, 0xF9, 0x97, 0x75, 0x01, 0xB6, 0x49, 0xE5, 0x48, 0xFF, 0x34, 0x2A, 0xE6, 0xCF, 0x5D, 0x85, 0x6D, 0xF8, 0x35, 0x8C, 0xEA, 0x04, 0xB3, 0x10, 0x5B, 0x07, 0x80, 0x3B, 0xD5, 0x69, 0x4E, 0xD2, 0xA5, 0x9F, 0x6A, 0x38, 0x77, 0x58, 0x5C, 0x79, 0xEB, 0x03, 0x30, 0x5C, 0x5D, 0x7D, 0x47, 0xD1, 0xD3, 0xAE, 0xC2, 0xE8, 0xB5, 0x69, 0x35, 0x58, 0xD5, 0xC2, 0x91, 0x80, 0x4E, 0x1B, 0x87, 0x92, 0xB2, 0x60, 0x69, 0x02, 0x6F, 0x47, 0x1B, 0x89, 0x14, 0x7E, 0x4F, 0xE4, 0xF9, 0xF0, 0x4D, 0xBD, 0x76, 0x72, 0x36, 0xF3, 0xD8, 0xB8, 0x4D}, + []uint{43, 23, 21, 42, 26, 28, 54, 30, 49, 8, 33, 11, 25, 35, 39, 38, 21, 37, 62, 7, 60, 57, 36, 15}, + []uint64{0x6B94F46C4EA, 0x7C1044, 0x135EF, 0x13C9E97F32E, 0x3A80DB2, 0x4F2A47F, 0x26855CD9EBB0AD, 0x2FC1AC67, 0xA04B3105B078, 0x03, 0x17AAD29DA, 0x2A5, 0x13ED470, 0x77585C79E, 0x581982E2EB, 0x3A8FA3A75D, 0x10BA2D, 0xB49AAC6AE, 0x523009C370F2564, 0x60, 0x69026F471B89147, 0x1C9FC9F3E09B7AE, 0xCE46DE7B1, 0x384D}, + }, + { + []byte{0x76, 0x2E, 0x87, 0xCE, 0x9D, 0x1C, 0xF7, 0x87, 0xD8, 0x0E, 0xD4, 0x6B, 0x3D, 0x01, 0x84, 0x6C, 0xDE, 0xF7, 0x72, 0xC4, 0xCC, 0xAE, 0xC4, 0xF4, 0x35, 0xBD, 0xDC, 0xA5, 0x69, 0x4F, 0xFF, 0x98, 0x7D, 0x30, 0xBE, 0x40, 0xE7, 0x39, 0xF6, 0xFD, 0x52, 0x19, 0xD9, 0x51, 0xB4, 0x04, 0x22, 0x5C, 0x93, 0x07, 0x7D, 0xB9, 0xF0, 0x5D, 0x53, 0x67, 0x6E, 0xAA, 0xCF, 0x28, 0x16, 0x14, 0x2C, 0x28, 0x5A, 0xB9, 0x20, 0xD3, 0x8C, 0x42, 0xFC, 0x6D, 0xE5, 0x68, 0x47, 0x84, 0xBF, 0x4D, 0x9C, 0x55, 0x62, 0x56, 0x10, 0xAE, 0xFC, 0x1E, 0x91, 0x8B, 0x52, 0x52, 0x49, 0x25, 0x3C, 0x6F, 0xC0, 0x59, 0x59, 0x2D, 0x80, 0x43}, + []uint{42, 51, 40, 8, 22, 55, 9, 12, 60, 53, 35, 48, 31, 50, 34, 7, 7, 53, 55, 41, 35, 32, 2, 18}, + []uint64{0x1D8BA1F3A74, 0x39EF0FB01DA8D, 0x67A0308D9B, 0xDE, 0x3B9626, 0x32BB13D0D6F772, 0x12B, 0x4A7, 0xFFCC3E985F20739, 0x19F6FD5219D951, 0x5A02112E4, 0x983BEDCF82EA, 0x4D9DBAAB, 0xF2816142C285, 0x2AE4834E3, 0x08, 0x2F, 0x18DBCAD08F097E, 0x4D9C55625610AE, 0x1F83D2316A4, 0x5249253C6, 0xFC059592, 0x03, 0x18043}, + }, + { + []byte{0x33, 0xC2, 0xC3, 0xBC, 0x9D, 0x2A, 0x36, 0xB4, 0x92, 0x36, 0x02, 0x93, 0x54, 0xB9, 0x86, 0x32, 0xC1, 0x0C, 0x75, 0xC4, 0x3B, 0xE4, 0x0A, 0x3F, 0xDF, 0x4E, 0xF6, 0x05, 0x4F, 0x30, 0x19, 0xA6, 0xE9, 0xE6, 0x55, 0xAF, 0xE8, 0xC4, 0x3F, 0xFF, 0xE3, 0x40, 0xC1, 0xA2, 0xE1, 0x05, 0xD7, 0x77, 0x82, 0xD5, 0xF9, 0xA6, 0x0F, 0x54, 0xED, 0x96, 0xA0, 0x68, 0xEC, 0xAE, 0x57, 0xE3, 0x6A, 0xD2, 0xCE, 0x18, 0x9A, 0xC2, 0x5E, 0x10, 0x3C, 0x21, 0x5C, 0x38, 0xA9, 0xC4, 0x82, 0x76, 0xF9, 0xA9, 0x9B, 0x33, 0x43, 0x40, 0x61, 0xDD, 0x0D, 0x83, 0x9E, 0xDE, 0x08, 0x4C, 0x13, 0x33, 0x82, 0x6A, 0xF4, 0xAB, 0x61, 0x1C}, + []uint{28, 60, 58, 5, 41, 56, 29, 27, 38, 2, 28, 51, 3, 63, 33, 57, 36, 61, 22, 1, 62, 26, 13}, + []uint64{0x33C2C3B, 0xC9D2A36B4923602, 0x24D52E618CB0431, 0x1A, 0x1C43BE40A3F, 0xDF4EF6054F3019, 0x14DD3CCA, 0x5AFE8C4, 0xFFFF8D030, 0x01, 0xA2E105D, 0x3BBC16AFCD307, 0x05, 0x29DB2D40D1D95CAF, 0x18DAB4B38, 0xC4D612F081E10A, 0xE1C54E241, 0x76F9A99B3343406, 0x77436, 0x00, 0x73DBC1098266704, 0x357A55B, 0x11C}, + }, + { + []byte{0xCB, 0x0A, 0x64, 0xC1, 0xEF, 0xEC, 0x67, 0x09, 0xE1, 0x9A, 0x2A, 0xC2, 0xC5, 0xD9, 0xC7, 0x3D, 0x2A, 0x49, 0x3E, 0xF4, 0x30, 0xC9, 0x47, 0x2E, 0xEC, 0xAA, 0x46, 0xDA, 0x31, 0x62, 0x27, 0x4F, 0xAD, 0x4C, 0x50, 0xCD, 0x83, 0x51, 0x37, 0x09, 0x8B, 0xC7, 0x6D, 0x62, 0xFE, 0xC3, 0xE9, 0x91, 0xE5, 0xE8, 0x4D, 0x11, 0x1B, 0xDE, 0x03, 0x23, 0xAA, 0x1E, 0x65, 0xAD, 0x06, 0x23, 0x28, 0xD2, 0xDF, 0x59, 0x47, 0x52, 0xF2, 0x3F, 0xC7, 0x68, 0x50, 0x20, 0xF3, 0x2A, 0xC0, 0xFB, 0xBB, 0xC0, 0xAD, 0x26, 0xA6, 0x75, 0x19, 0x1B, 0xD8, 0x1F, 0xF9, 0xFD, 0x33, 0x10, 0x2C, 0x15, 0x80, 0xDC, 0xA7, 0xF6, 0xF4, 0x50}, + []uint{21, 51, 60, 7, 33, 20, 14, 36, 57, 10, 51, 3, 17, 50, 55, 3, 14, 7, 35, 45, 12, 43, 50, 51, 28, 27}, + []uint64{0x19614C, 0x4C1EFEC6709E1, 0x9A2AC2C5D9C73D2, 0x52, 0x93EF430C, 0x9472E, 0x3B2A, 0x91B68C588, 0x13A7D6A62866C1A, 0x226, 0x7098BC76D62FE, 0x06, 0x3E99, 0x797A134446F7, 0x40647543CCB5A0, 0x06, 0x8CA, 0x1A, 0x2DF594752, 0x1E47F8ED0A04, 0x1E6, 0x2AC0FBBBC0A, 0x349A99D4646F6, 0x3FF3FA662058, 0x2B01B94, 0x7F6F450}, + }, + { + []byte{0x56, 0x6B, 0xDA, 0x9E, 0xD9, 0x04, 0x9A, 0xE7, 0x5E, 0x85, 0x25, 0x0B, 0x1D, 0xEF, 0xB8, 0x65, 0x6F, 0xFF, 0xE5, 0x6F, 0x02, 0xA8, 0xE2, 0x9B, 0x18, 0xC2, 0x11, 0x8A, 0x7E, 0xB5, 0xDF, 0x72, 0x44, 0x0C, 0xC7, 0x85, 0x43, 0xC5, 0x75, 0x3A, 0x2B, 0x1F, 0xA5, 0x75, 0x66, 0x42, 0x15, 0x03, 0xB3, 0x2F, 0x90, 0xCB, 0x2B, 0x56, 0x8E, 0x2E, 0xB1, 0x98, 0xD0, 0x5D, 0x53, 0x8D, 0x7C, 0xF8, 0x0E, 0x07, 0x98, 0x60, 0x4E, 0x8D, 0x69, 0xF9, 0x2E, 0xD7, 0x9A, 0xE2, 0x4D, 0x37, 0x49, 0xB9, 0x59, 0x5D, 0x2D, 0xC2, 0x7E, 0x50, 0x7B, 0x97, 0x4F, 0x06, 0x2B, 0xBC, 0xCB, 0x10, 0x3E, 0x0E, 0xD3, 0xF8, 0x7E, 0x78}, + []uint{38, 55, 32, 34, 58, 58, 53, 8, 29, 19, 37, 46, 7, 55, 39, 44, 24, 5, 5, 22, 19, 11, 49, 40, 13}, + []uint64{0x159AF6A7B6, 0x20935CEBD0A4A1, 0x63BDF70C, 0x2B7FFF2B7, 0x20551C536318423, 0x53F5AEFB922066, 0x78543C5753A2B, 0x1F, 0x14AEACC8, 0x21503, 0x1665F21965, 0x1AB471758CC6, 0x41, 0x3AA71AF9F01C0F, 0x18604E8D69, 0xF92ED79AE24, 0xD3749B, 0x12, 0x16, 0x15D2DC, 0x13F28, 0x1EE, 0xBA78315DE658, 0x81F0769FC3, 0x1E78}, + }, + { + []byte{0x52, 0xEE, 0x85, 0xDF, 0x00, 0xC1, 0xE3, 0x65, 0x76, 0x46, 0x99, 0x52, 0xDC, 0xA5, 0x26, 0x5C, 0x31, 0x62, 0xEB, 0xC2, 0xCD, 0xC7, 0x27, 0x12, 0xE4, 0xFC, 0xC5, 0x56, 0xB5, 0xDB, 0x87, 0x39, 0x94, 0xFB, 0x3D, 0x31, 0x24, 0xB7, 0x16, 0x4D, 0x6E, 0x51, 0x8E, 0x47, 0x8E, 0xFF, 0xC0, 0x58, 0x09, 0xC3, 0xE7, 0xCC, 0x5C, 0xA8, 0x82, 0x0F, 0xE2, 0x4D, 0xD2, 0x6D, 0xBF, 0x9B, 0x3D, 0x28, 0xD6, 0x65, 0x6D, 0x0A, 0x05, 0x1B, 0xFA, 0x66, 0x9C, 0xF6, 0x15, 0xCE, 0x50, 0xE1, 0x21, 0x90, 0x9B, 0xA0, 0xDE, 0xDB, 0x76, 0x7C, 0xB6, 0x2D, 0x30, 0xEB, 0xCD, 0x74, 0xE5, 0x19, 0x18, 0xC0, 0x7D, 0x43, 0xA3, 0x61}, + []uint{33, 22, 19, 36, 45, 49, 18, 64, 5, 22, 40, 5, 2, 47, 9, 13, 9, 54, 55, 11, 32, 15, 22, 55, 41, 37, 24, 7, 9}, + []uint64{0xA5DD0BBE, 0x60F1, 0x595D9, 0x1A654B729, 0x932E18B175E, 0x2CDC72712E4F, 0x33155, 0xAD76E1CE653ECF4C, 0x09, 0x96E2C, 0x9ADCA31C8F, 0x03, 0x02, 0x7FE02C04E1F3, 0x1CC, 0xB95, 0x20, 0x20FE24DD26DBF9, 0x59E946B32B6850, 0x146, 0xFE99A73D, 0x42B9, 0x328709, 0x6426E837B6DD9, 0x1E5B169875E, 0xD74E51918, 0xC07D43, 0x51, 0x161}, + }, + { + []byte{0xB0, 0x67, 0x0E, 0x66, 0x77, 0x4D, 0x4E, 0xDB, 0xEB, 0xDC, 0x05, 0x3B, 0xAA, 0xBB, 0xE8, 0x71, 0x59, 0xD4, 0x71, 0x53, 0xBE, 0xF4, 0x78, 0x2A, 0xB8, 0x9F, 0x09, 0xBB, 0x18, 0x2D, 0x89, 0xD7, 0xDF, 0x15, 0xB9, 0xD6, 0x6D, 0x90, 0x3F, 0x2B, 0xBF, 0xF7, 0xC7, 0x41, 0x1D, 0xCA, 0xAB, 0x52, 0x41, 0x21, 0x6F, 0xB1, 0xF1, 0x2F, 0xCD, 0xC8, 0x85, 0xC3, 0xD7, 0x80, 0x82, 0xFB, 0x86, 0x14, 0x3E, 0x34, 0x95, 0x3E, 0x82, 0xE2, 0x14, 0x51, 0x93, 0xFE, 0xEE, 0x0A, 0xF2, 0xFE, 0xDE, 0x97, 0xB7, 0xEF, 0x1F, 0x95, 0x5D, 0x8F, 0xE8, 0x59, 0xC9, 0x5E, 0x9B, 0x9D, 0x35, 0x0C, 0x84, 0x54, 0x7D, 0x7D, 0x30, 0x42}, + []uint{1, 13, 4, 42, 5, 62, 28, 12, 37, 22, 51, 1, 36, 54, 18, 39, 13, 57, 64, 24, 56, 44, 24, 63, 30}, + []uint64{0x01, 0xC19, 0x0C, 0xE66774D4ED, 0x17, 0x35EE029DD55DF438, 0xACEA38A, 0x9DF, 0xF4782AB89, 0x3C26EC, 0x305B13AFBE2B7, 0x00, 0x759B640FC, 0x2BBFF7C7411DCA, 0x2AD49, 0x242DF63E2, 0xBF3, 0xE442E1EBC0417D, 0xC30A1F1A4A9F4171, 0xA28C9, 0xFF7705797F6F4B, 0xDBF78FCAAEC, 0x7F42CE, 0x257A6E74D4321151, 0x3D7D3042}, + }, + { + []byte{0xD8, 0x92, 0xDB, 0xF2, 0x33, 0xA4, 0x8B, 0x06, 0xD6, 0xC5, 0xBE, 0x2D, 0x6B, 0x2B, 0x82, 0x33, 0x75, 0xA7, 0xD4, 0x36, 0x2F, 0x46, 0x81, 0xE1, 0xC7, 0xBB, 0xDC, 0xF9, 0x1C, 0x93, 0xF8, 0xEB, 0xFA, 0x14, 0xF3, 0xD2, 0xCB, 0xD7, 0x70, 0x24, 0xAA, 0x92, 0xE5, 0xB3, 0x14, 0x5F, 0x18, 0x14, 0xEC, 0xC3, 0x5B, 0xF0, 0x33, 0xE7, 0xF6, 0xA7, 0x2D, 0x0F, 0xF7, 0x0A, 0x84, 0xD1, 0x87, 0xEF, 0xDD, 0xCD, 0xDC, 0xA8, 0xA1, 0xEF, 0x18, 0x6F, 0xDB, 0x05, 0x79, 0xD6, 0xF9, 0x33, 0x89, 0x9A, 0x57, 0xB7, 0xAC, 0xFE, 0x1C, 0x49, 0xA5, 0x6B, 0x5D, 0x15, 0x19, 0x1F, 0x74, 0x35, 0x87, 0xB3, 0xDD, 0xF6, 0x91, 0x59}, + []uint{59, 22, 40, 1, 33, 56, 30, 30, 20, 23, 23, 64, 4, 27, 7, 23, 11, 33, 13, 43, 20, 8, 21, 55, 56, 64, 14}, + []uint64{0x6C496DF919D2458, 0xDAD8B, 0x7C5AD65704, 0x00, 0x19BAD3EA1, 0xB17A340F0E3DDE, 0x39F23927, 0x3C75FD0A, 0x79E96, 0x2F5DC0, 0x495525, 0xCB6628BE3029D986, 0x0B, 0x3F033E7, 0x7B, 0x29CB43, 0x7EE, 0x2A13461F, 0x17EE, 0x73772A287BC, 0x61BF6, 0xC1, 0xBCEB7, 0x64CE26695EDEB3, 0xF8712695AD7454, 0x647DD0D61ECF77DA, 0x1159}, + }, + { + []byte{0xEE, 0xDC, 0x02, 0xB6, 0x64, 0xD5, 0x98, 0xBA, 0x54, 0x05, 0x83, 0x4E, 0xB1, 0xC6, 0x5E, 0x02, 0x6F, 0x12, 0x3A, 0xC4, 0xD4, 0x99, 0xF8, 0xF4, 0x4E, 0xC5, 0x5F, 0x69, 0xB9, 0xEE, 0xFF, 0xB0, 0x5A, 0x10, 0x69, 0xEF, 0x95, 0x82, 0xA6, 0x51, 0xAC, 0x06, 0x46, 0x6A, 0x69, 0x80, 0x07, 0x5A, 0xDB, 0x0B, 0x58, 0x49, 0xD3, 0x9B, 0xD6, 0x66, 0xFC, 0x14, 0x05, 0x58, 0x46, 0x98, 0x16, 0xC0, 0x3E, 0x50, 0xDB, 0xD7, 0x68, 0xC1, 0xFE, 0x16, 0xA8, 0xE6, 0x80, 0x21, 0xBA, 0xC1, 0x89, 0xB7, 0xD3, 0xCF, 0xDF, 0x3E, 0x5E, 0xB0, 0x76, 0x25, 0xB0, 0xB8, 0x65, 0xA1, 0x57, 0x35, 0x2B, 0x79, 0x35, 0x23, 0x70, 0x7A}, + []uint{19, 14, 28, 59, 53, 55, 20, 4, 1, 40, 36, 2, 42, 20, 60, 61, 59, 46, 62, 23, 50, 4, 42}, + []uint64{0x776E0, 0x56C, 0xC9AB317, 0x25405834EB1C65E, 0x4DE247589A93, 0x1F8F44EC55F69B, 0x9EEFF, 0x0B, 0x00, 0xB420D3DF2, 0xB054CA358, 0x00, 0xC8CD4D3000, 0xEB5B6, 0x16B093A737ACCDF, 0x105015611A605B00, 0x7CA1B7AED183FC2, 0x354734010DD6, 0x3136FA79FBE7CBD, 0x307625, 0x2C2E196855CD4, 0x0A, 0x3793523707A}, + }, + { + []byte{0x26, 0x44, 0x9D, 0xFF, 0x6D, 0x96, 0x22, 0x0A, 0xD1, 0xF2, 0xE7, 0x81, 0xD8, 0xEC, 0x90, 0xFD, 0x74, 0x52, 0x63, 0x4A, 0x3C, 0x34, 0xDB, 0x1A, 0xA8, 0xDF, 0xA5, 0x62, 0x34, 0x57, 0x32, 0x2C, 0x53, 0xEC, 0xDF, 0x03, 0xFC, 0xE0, 0x86, 0x03, 0x24, 0xCD, 0x44, 0x36, 0xDF, 0xE2, 0x2F, 0x55, 0x25, 0xD6, 0x27, 0xF3, 0x2E, 0xDB, 0x38, 0x31, 0x5D, 0xAC, 0x2C, 0x28, 0x05, 0x33, 0xA2, 0xFB, 0x0D, 0x7C, 0x02, 0x1B, 0x8A, 0xD4, 0xDF, 0xFC, 0x02, 0x69, 0xC4, 0x2C, 0x65, 0x6F, 0x8C, 0xA4, 0x2F, 0x19, 0x04, 0xBE, 0xC5, 0x0F, 0x8C, 0xF0, 0xB1, 0x8A, 0x2E, 0x58, 0x96, 0xC3, 0x87, 0x1F, 0x89, 0x4F, 0xA9, 0x44}, + []uint{37, 50, 48, 42, 39, 50, 30, 29, 11, 10, 17, 20, 29, 52, 50, 17, 27, 47, 16, 16, 25, 61, 28, 8, 29, 12}, + []uint64{0x4C893BFED, 0x2CB110568F973, 0xC0EC76487EBA, 0xA4C6947869, 0x5B1AA8DFA5, 0x188D15CC8B14F, 0x2CDF03FC, 0x1C10C064, 0x4CD, 0x110, 0x1B6FF, 0x117AA, 0x125D627F, 0x32EDB38315DAC, 0xB0A014CE8BEC, 0x6BE0, 0x86E2B5, 0x1BFF804D3885, 0x8CAD, 0xF194, 0x10BC641, 0x5F6287C67858C51, 0x72C4B61, 0xC3, 0x11F894FA, 0x944}, + }, + { + []byte{0x93, 0xC5, 0x74, 0xE2, 0x73, 0x08, 0xB2, 0xDD, 0xB8, 0xD7, 0xD5, 0xC3, 0x88, 0x2F, 0x98, 0xA2, 0xB8, 0x42, 0x98, 0xE2, 0xFD, 0xC6, 0x7F, 0x6C, 0x47, 0xE6, 0xC5, 0x9D, 0x30, 0x31, 0x40, 0x3C, 0x63, 0xE7, 0xD1, 0x20, 0x1E, 0x85, 0xF9, 0x40, 0x98, 0xE8, 0x9F, 0x31, 0xDF, 0x9E, 0x14, 0xEE, 0xB5, 0x62, 0x56, 0x7F, 0x88, 0x3F, 0xE1, 0xD1, 0xEB, 0xAC, 0x09, 0xCD, 0x71, 0xF8, 0x23, 0x07, 0xDA, 0x37, 0x32, 0x37, 0xDE, 0x65, 0x21, 0x72, 0x9B, 0x89, 0x9B, 0xEC, 0x35, 0xBC, 0x6B, 0xB8, 0x20, 0x00, 0xF6, 0xA9, 0x06, 0xDC, 0xAC, 0xE8, 0xC3, 0x52, 0x89, 0x20, 0x74, 0xC9, 0xB3, 0xCC, 0x72, 0x45, 0x0E, 0x76}, + []uint{49, 42, 47, 16, 27, 33, 16, 39, 45, 23, 47, 52, 64, 20, 17, 63, 30, 50, 23, 54, 31, 12}, + []uint64{0x1278AE9C4E611, 0x196EDC6BEAE, 0xE20BE628AE1, 0xA63, 0x45FB8CF, 0x1DB11F9B1, 0x674C, 0x628078C7C, 0x1F44807A17E5, 0x131D1, 0x1F31DF9E14EE, 0xB562567F883FE, 0x1D1EBAC09CD71F82, 0x307DA, 0x6E64, 0x37DE6521729B899B, 0x3B0D6F1A, 0x3B82000F6A906, 0x6E5674, 0x186A51240E9936, 0x3CC72450, 0xE76}, + }, + { + []byte{0xCA, 0x4E, 0x83, 0x60, 0x1A, 0xEF, 0xAC, 0x61, 0x4C, 0x22, 0x4A, 0xE3, 0xFC, 0xEF, 0x32, 0x2A, 0xDB, 0x52, 0x73, 0xC3, 0xEB, 0xA6, 0xF0, 0xAE, 0x35, 0x1D, 0x0C, 0xDE, 0x1D, 0x38, 0x41, 0x75, 0x4B, 0xF3, 0x62, 0x98, 0x73, 0x07, 0xB9, 0x58, 0x83, 0x38, 0x29, 0xE3, 0xCD, 0x38, 0xBA, 0xF0, 0x59, 0xDC, 0x74, 0xE4, 0xE4, 0x17, 0x6C, 0x83, 0xBF, 0x52, 0xDB, 0xEA, 0xF5, 0xFA, 0xBF, 0x1D, 0xBA, 0x13, 0xED, 0xDA, 0x93, 0x6E, 0xCB, 0x89, 0x1E, 0x83, 0x72, 0xC5, 0x28, 0x3A, 0x5F, 0x8D, 0xAF, 0xDB, 0xDD, 0x6E, 0xA9, 0xD2, 0x76, 0x4B, 0x7F, 0x4A, 0xD8, 0x0E, 0xBF, 0xE0, 0xC6, 0x5F, 0xA0, 0x77, 0x7A, 0x2B}, + []uint{57, 31, 47, 37, 29, 27, 49, 3, 52, 31, 34, 41, 35, 37, 47, 59, 31, 46, 10, 7, 14, 54, 22}, + []uint64{0x1949D06C035DF58, 0x614C224A, 0x71FE7799156D, 0x15273C3EBA, 0xDE15C6A, 0x1D0CDE1, 0x1A7082EA97E6C, 0x02, 0x987307B958833, 0x414F1E69, 0x3175E0B3B, 0x11D393905DB, 0x1077EA5B7, 0x1ABD7EAFC7, 0x37427DBB526D, 0x6CB891E8372C528, 0x1D2FC6D7, 0x3B7BADD53A4E, 0x325, 0x5F, 0x34AD, 0x203AFF83197E81, 0x377A2B}, + }, + { + []byte{0x2C, 0x77, 0x59, 0x98, 0x44, 0xE0, 0xD5, 0x78, 0x5F, 0xFE, 0xD1, 0x44, 0xAD, 0xCC, 0x78, 0x46, 0x69, 0x3B, 0xD5, 0xC2, 0x03, 0xE1, 0x24, 0x54, 0x8C, 0xAB, 0x06, 0x61, 0x78, 0x78, 0x89, 0x42, 0x8B, 0x1C, 0x9E, 0xA1, 0x13, 0xC8, 0x09, 0xAB, 0x7E, 0xD6, 0xC6, 0xB2, 0xAF, 0x4D, 0x1D, 0xB4, 0x85, 0xB5, 0x5D, 0x39, 0x23, 0x8E, 0x67, 0x8D, 0x48, 0xA1, 0x1B, 0x42, 0xDD, 0xBD, 0xCB, 0x53, 0xA8, 0x20, 0x50, 0xF7, 0x44, 0xE7, 0xBC, 0x7A, 0x85, 0x09, 0x71, 0x04, 0xCF, 0x81, 0xCA, 0xB4, 0x10, 0xF2, 0xBF, 0xC7, 0x7C, 0x05, 0xC3, 0x62, 0x19, 0x79, 0x04, 0x7F, 0xF8, 0x54, 0x85, 0x0E, 0x35, 0xE4, 0x64, 0x4C}, + []uint{35, 33, 39, 14, 2, 40, 63, 64, 53, 56, 47, 42, 51, 12, 9, 43, 58, 55, 32, 52}, + []uint64{0x163BACCC2, 0x4E0D5785, 0x7FF68A256E, 0x18F0, 0x02, 0x3349DEAE10, 0xF84915232AC1985, 0xE1E2250A2C727A84, 0x9E404D5BF6B63, 0x5957A68EDA42DA, 0x574E48E399E3, 0x148A11B42DD, 0x5EE5A9D410287, 0xBA2, 0xE7, 0x5E3D4284B88, 0x99F03956821E57, 0x7C77C05C362197, 0x9047FF85, 0x4850E35E4644C}, + }, + { + []byte{0x2F, 0x1E, 0xB8, 0x50, 0xC1, 0xF6, 0xD4, 0xD4, 0x33, 0xD8, 0x40, 0xC9, 0x5E, 0xB5, 0x29, 0x9E, 0x60, 0x65, 0xB4, 0xD2, 0x68, 0x4D, 0x3F, 0x7E, 0x0D, 0xCB, 0xE6, 0x9E, 0x0D, 0x2A, 0x4C, 0xF5, 0xD4, 0x0D, 0x20, 0x36, 0x17, 0xCB, 0xCD, 0x2A, 0xA4, 0xBE, 0x08, 0x48, 0x7C, 0xFE, 0x77, 0x28, 0xDA, 0xC0, 0xFF, 0xAC, 0x48, 0xF5, 0xCD, 0x34, 0xCD, 0x1D, 0x6E, 0x13, 0x19, 0xDD, 0x0D, 0xF1, 0x78, 0x41, 0x59, 0x87, 0xDB, 0x13, 0x44, 0xC1, 0xE0, 0xB7, 0x9C, 0x18, 0xF9, 0x4E, 0x09, 0x53, 0x99, 0x95, 0x9D, 0x7C, 0x0E, 0xDB, 0xF5, 0x7D, 0x55, 0x20, 0x19, 0x6A, 0x39, 0x30, 0xC8, 0x6E, 0xE7, 0x0E, 0xCA, 0x02}, + []uint{15, 11, 7, 40, 53, 47, 60, 55, 29, 8, 29, 25, 53, 33, 45, 9, 30, 17, 30, 11, 7, 37, 35, 43, 25, 19, 8, 19}, + []uint64{0x178F, 0x2E1, 0x21, 0x83EDA9A867, 0x16103257AD4A67, 0x4C0CB69A4D09, 0xA7EFC1B97CD3C1A, 0x2A4CF5D40D2036, 0x2F979A5, 0x54, 0x12F82121, 0x1E7F3B9, 0x8DAC0FFAC48F5, 0x19A699A3A, 0x1B84C677437C, 0xBC, 0x82B30FB, 0xC4D1, 0xC1E0B79, 0x60C, 0x3E, 0xA704A9CCC, 0x5675F03B6, 0x7EAFAAA4032, 0x1A8E4C3, 0x10DDC, 0xE1, 0x6CA02}, + }, + { + []byte{0x52, 0x18, 0x09, 0x9C, 0xE3, 0xF6, 0x3F, 0xA5, 0xED, 0xF7, 0x0E, 0xB9, 0x49, 0x47, 0x88, 0x47, 0x62, 0x11, 0x91, 0x9A, 0xDD, 0x33, 0x2C, 0xF9, 0x77, 0x0C, 0xFE, 0x5D, 0xBC, 0x11, 0x25, 0x0C, 0x57, 0x41, 0x2E, 0x26, 0x6A, 0x84, 0xF4, 0x6B, 0x74, 0x45, 0x74, 0x06, 0x1F, 0xAF, 0x0E, 0x4D, 0x5A, 0xBF, 0x4E, 0x02, 0x6E, 0xF5, 0x7B, 0xA4, 0xCD, 0x5F, 0xFC, 0x9B, 0xA3, 0x7D, 0x8D, 0x35, 0x11, 0xEE, 0x63, 0x6B, 0xE1, 0xC8, 0x51, 0xDB, 0x79, 0x94, 0x42, 0x4D, 0xEA, 0xAE, 0xBD, 0xC0, 0x24, 0x0C, 0x28, 0x3B, 0xCB, 0x2D, 0x67, 0x10, 0x38, 0x99, 0x8F, 0xFA, 0x9D, 0x76, 0xC5, 0xD9, 0x85, 0x0E, 0xDA, 0x45}, + []uint{33, 3, 51, 28, 16, 63, 3, 16, 33, 58, 33, 12, 64, 41, 13, 11, 52, 27, 8, 15, 1, 20, 30, 61, 34, 39, 35}, + []uint64{0xA4301339, 0x06, 0x1FB1FD2F6FB87, 0x5CA4A3C, 0x423B, 0x846466B74CCB3E5, 0x06, 0xE19F, 0x1976F0449, 0x10C57412E266A84, 0x1E8D6E88A, 0xE80, 0xC3F5E1C9AB57E9C0, 0x9BBD5EE933, 0xAFF, 0x726, 0xE8DF634D447B9, 0x46D7C39, 0x0A, 0x1DB7, 0x01, 0x32884, 0x26F5575E, 0x1C0240C283BCB2D6, 0x1C40E2663, 0x7F53AED8BB, 0x1850EDA45}, + }, + { + []byte{0xD6, 0x66, 0x64, 0xE0, 0x81, 0x98, 0x56, 0xB5, 0xF4, 0xA8, 0x1E, 0x12, 0x6F, 0xB0, 0x17, 0xE7, 0x91, 0xF9, 0x6C, 0x82, 0x9F, 0x22, 0xE2, 0x15, 0x5A, 0xCE, 0x73, 0x71, 0x29, 0x69, 0x8F, 0x94, 0xC7, 0xB3, 0x4F, 0x1B, 0x16, 0x28, 0xE7, 0xD6, 0x7E, 0x5F, 0xDE, 0xB6, 0x93, 0xA8, 0xDB, 0x9B, 0x44, 0x19, 0xDC, 0xE7, 0x57, 0x69, 0x4F, 0xA9, 0x33, 0x4C, 0x16, 0x5A, 0x91, 0xE4, 0x69, 0x33, 0x34, 0x93, 0x92, 0xE0, 0x00, 0xA2, 0xF2, 0xFB, 0xA9, 0xE0, 0xB5, 0xE0, 0xC0, 0x9D, 0x39, 0x52, 0xD6, 0xA4, 0x45, 0xB3, 0x9E, 0x96, 0x51, 0x44, 0xAD, 0xF0, 0x70, 0x51, 0x7D, 0xA7, 0x9F, 0xBB, 0x20, 0x41, 0x0C, 0x94}, + []uint{49, 14, 13, 57, 39, 20, 12, 63, 47, 53, 9, 52, 41, 9, 24, 60, 44, 16, 50, 21, 42, 36, 29}, + []uint64{0x1ACCCC9C10330, 0x2B5A, 0x1F4A, 0x103C24DF602FCF2, 0x1F96C829F2, 0x2E215, 0x5AC, 0x739B894B4C7CA63D, 0x4D3C6C58A39F, 0xB3F2FEF5B49D4, 0xDB, 0x9B4419DCE7576, 0x129F5266982, 0x196, 0xA4791A, 0x4CCD24E4B80028B, 0xCBEEA782D78, 0x3027, 0x13952D6A445B3, 0x13D2CA, 0xA256F83828, 0xBED3CFDD9, 0x410C94}, + }, + { + []byte{0xA1, 0xA6, 0x11, 0xEE, 0xBB, 0x60, 0x43, 0x9A, 0x10, 0xDA, 0x7D, 0xBB, 0x9D, 0xFC, 0x67, 0x7E, 0xE5, 0x57, 0x36, 0x8C, 0xB4, 0x20, 0x6D, 0x5A, 0xD1, 0x61, 0x1F, 0x1B, 0x1F, 0x23, 0x72, 0x26, 0xB1, 0xFF, 0x12, 0x26, 0xBB, 0x6D, 0x2B, 0x7A, 0x7E, 0x15, 0x5A, 0x2F, 0x0E, 0x17, 0xEF, 0x71, 0xCB, 0x1C, 0x18, 0xD0, 0xFE, 0x00, 0x9F, 0xC6, 0x0C, 0xB7, 0x45, 0xED, 0xF7, 0xB9, 0x93, 0xAF, 0xA5, 0x02, 0xB6, 0xFA, 0x8D, 0x7C, 0xF6, 0x2C, 0x36, 0x9A, 0x81, 0xB8, 0xCE, 0x89, 0x63, 0xAD, 0x26, 0x5C, 0x3B, 0x1B, 0xF7, 0xB8, 0xAE, 0xAF, 0x5C, 0x07, 0x47, 0x3C, 0x79, 0x14, 0x0F, 0x18, 0x35, 0xAA, 0x30, 0xCE}, + []uint{30, 56, 32, 18, 22, 48, 9, 2, 41, 20, 9, 18, 51, 20, 28, 52, 47, 37, 24, 24, 20, 8, 19, 25, 9, 29, 24, 37, 2, 6, 33}, + []uint64{0x2869847B, 0xAED810E684369F, 0x6EE77F19, 0x37EE5, 0x15CDA3, 0x2D081B56B458, 0x8F, 0x02, 0x6C7C8DC89A, 0xC7FC4, 0x113, 0x176DA, 0x2B7A7E155A2F0, 0xE17EF, 0x71CB1C1, 0x8D0FE009FC60C, 0x5BA2F6FBDCC9, 0x1AFA502B6F, 0xA8D7CF, 0x62C369, 0xA81B8, 0xCE, 0x44B1D, 0xD265C3, 0x163, 0xFDEE2BA, 0xBD701D, 0x39E3C8A07, 0x02, 0x0C, 0x35AA30CE}, + }, + { + []byte{0x44, 0x4D, 0xAD, 0xF6, 0xC5, 0x49, 0x84, 0x42, 0x7C, 0x1C, 0x52, 0xAB, 0x5E, 0xB4, 0xB1, 0x05, 0x2A, 0x91, 0xE7, 0xAD, 0x2C, 0x17, 0x8F, 0xCE, 0xB5, 0x0A, 0x3A, 0xAA, 0x05, 0x57, 0x36, 0xFD, 0x18, 0xA4, 0x2F, 0x32, 0x14, 0xAE, 0x60, 0x5D, 0xEE, 0x36, 0xBD, 0x7B, 0x1F, 0x6A, 0xB5, 0x10, 0x26, 0x0A, 0x19, 0xD1, 0x25, 0x53, 0xEA, 0x97, 0xF3, 0x9C, 0xE6, 0x71, 0x1E, 0xA6, 0x33, 0x98, 0x6C, 0x3E, 0xB4, 0xBD, 0x1D, 0x5B, 0xDF, 0xAB, 0xEF, 0x5D, 0xDE, 0x17, 0xB0, 0x62, 0x7D, 0x71, 0x8A, 0x36, 0xF5, 0x5B, 0xD2, 0x92, 0x54, 0xB7, 0xD0, 0x1B, 0xAD, 0x75, 0x69, 0xA0, 0x5A, 0x77, 0xFD, 0xF2, 0x81, 0xF5}, + []uint{29, 23, 54, 22, 44, 39, 56, 7, 49, 58, 48, 51, 17, 63, 28, 7, 55, 32, 62, 56}, + []uint64{0x889B5BE, 0x6C5498, 0x1109F0714AAD7A, 0x34B105, 0x2A91E7AD2C1, 0x3C7E75A851, 0xD5502AB9B7E8C5, 0x10, 0x17990A57302EF, 0x1C6D7AF63ED56A2, 0x4C1433A24AA, 0x3EA97F39CE671, 0x3D4C, 0x33986C3EB4BD1D5B, 0xDFABEF5, 0x6E, 0x785EC189F5C628, 0xDBD56F4A, 0x1254B7D01BAD7569, 0xA05A77FDF281F5}, + }, + { + []byte{0x0C, 0x0A, 0x46, 0x32, 0x3A, 0x2A, 0x4C, 0xD8, 0x81, 0x7D, 0x88, 0x36, 0xA9, 0x0D, 0xD2, 0xF7, 0x1C, 0x9C, 0xB9, 0xE8, 0x6B, 0x6D, 0x1D, 0xA5, 0x89, 0x16, 0x95, 0x20, 0x42, 0x46, 0x59, 0xD0, 0x4B, 0xEF, 0x6A, 0x1A, 0xD6, 0x92, 0xFF, 0x34, 0x44, 0x7D, 0x2D, 0x61, 0xC6, 0xE1, 0xAD, 0x75, 0x64, 0xDD, 0x00, 0xDA, 0x0F, 0x3E, 0xAB, 0x4D, 0x1B, 0x79, 0x55, 0x02, 0xC8, 0xF6, 0x8C, 0xF0, 0x4F, 0xAD, 0x64, 0x37, 0x08, 0xDC, 0x05, 0x4C, 0xE0, 0x3F, 0xE6, 0x0E, 0x6A, 0xC9, 0x5B, 0x82, 0x8B, 0x54, 0x2B, 0x28, 0x0F, 0xE5, 0x41, 0x4A, 0x53, 0x53, 0x81, 0x9C, 0x22, 0x75, 0xD4, 0x2A, 0x9F, 0x03, 0x14, 0x2D}, + []uint{7, 25, 64, 47, 3, 22, 11, 18, 56, 12, 64, 63, 8, 35, 40, 12, 31, 18, 64, 51, 58, 22, 64, 5}, + []uint64{0x06, 0xA4632, 0x3A2A4CD8817D8836, 0x5486E97B8E4E, 0x02, 0x39E86B, 0x368, 0x3B4B1, 0x22D2A40848CB3A, 0x97, 0xDED435AD25FE6888, 0x7D2D61C6E1AD7564, 0xDD, 0x6D079F5, 0x5A68DBCAA8, 0x164, 0x3DA33C13, 0x3AD64, 0x3708DC054CE03FE6, 0x73564ADC145A, 0x2856501FCA8294A, 0x1A9C0C, 0xE113AEA154F818A1, 0x0D}, + }, + { + []byte{0xB0, 0x2B, 0x72, 0xA2, 0xB5, 0xBA, 0x1F, 0xA0, 0x87, 0xEF, 0xEE, 0xE5, 0x01, 0x7F, 0x46, 0x66, 0x05, 0x7E, 0xBD, 0x0B, 0x06, 0x43, 0x3D, 0xA2, 0xCC, 0x6B, 0xA1, 0xB5, 0xBF, 0x91, 0xA7, 0x6C, 0x44, 0xC7, 0xB7, 0x7A, 0xBE, 0x20, 0xE9, 0xB1, 0xA7, 0xF9, 0x58, 0x2E, 0x92, 0x5F, 0xA2, 0xD5, 0x10, 0x98, 0xF9, 0x58, 0x1F, 0x91, 0x84, 0xCE, 0xEC, 0x53, 0x42, 0xE5, 0xBA, 0xD8, 0xC9, 0x3C, 0x5B, 0x4F, 0x7A, 0x8D, 0xC4, 0xFE, 0x6E, 0x56, 0x0A, 0x1A, 0x08, 0x2B, 0xD5, 0xFF, 0x8F, 0x28, 0xAB, 0x6A, 0xB3, 0x6C, 0x8A, 0x0F, 0x2B, 0xA5, 0x73, 0xED, 0xC7, 0x2C, 0xB9, 0xB7, 0xFD, 0x9C, 0xCA, 0xE7, 0x4E, 0xBA}, + []uint{51, 62, 30, 50, 54, 45, 5, 36, 27, 34, 30, 27, 60, 20, 44, 43, 45, 5, 5, 12, 9, 25, 18, 50, 13}, + []uint64{0x5815B9515ADD0, 0x3F410FDFDDCA02FE, 0x233302BF, 0x17A160C867B45, 0x2635D0DADFC8D3, 0x16C44C7B77AB, 0x1C, 0x41D3634FF, 0x1582E92, 0x17E8B5442, 0x18F9581F, 0x48C2677, 0x629A172DD6C649E, 0x2DA7B, 0xD46E27F372B, 0x286820AF57, 0x1FC79455B559, 0x16, 0x19, 0x141, 0x1CA, 0x1D2B9F6, 0x38E59, 0x1CDBFECE6573A, 0xEBA}, + }, + { + []byte{0x8E, 0xEB, 0xED, 0x77, 0xFC, 0xB9, 0x57, 0xAA, 0xC6, 0xF4, 0xEA, 0xF7, 0x59, 0xA3, 0x3F, 0xC7, 0x98, 0xF2, 0x6A, 0xA1, 0x0C, 0x08, 0xE5, 0x54, 0x8A, 0x69, 0x24, 0xE8, 0xCD, 0xD2, 0x27, 0x74, 0xC6, 0x96, 0x64, 0x1D, 0x00, 0x76, 0x21, 0xA1, 0x4F, 0xE8, 0xCF, 0x42, 0xFD, 0x51, 0x86, 0xD8, 0xBD, 0xA9, 0x28, 0x00, 0x42, 0xE9, 0x4B, 0xCB, 0xF5, 0xCF, 0x20, 0xFA, 0x96, 0x2F, 0x39, 0x86, 0x1B, 0x22, 0x73, 0x15, 0x65, 0x78, 0xFE, 0xB9, 0x12, 0xCD, 0x70, 0x2C, 0x8D, 0xE6, 0x4B, 0x08, 0x3C, 0xBB, 0x62, 0x96, 0xF9, 0xC3, 0x18, 0x29, 0xB6, 0x01, 0xEF, 0x76, 0xB6, 0x10, 0x8D, 0x00, 0x09, 0xC8, 0xBD, 0xC9}, + []uint{23, 25, 14, 24, 54, 57, 17, 10, 55, 55, 48, 6, 52, 23, 29, 46, 62, 52, 16, 9, 28, 44, 19, 20, 12}, + []uint64{0x4775F6, 0x177FCB9, 0x15EA, 0xB1BD3A, 0x2F759A33FC798F, 0x4D5421811CAA91, 0x9A49, 0xE8, 0x66E913BA634B32, 0x7401D886853FA, 0x33D0BF5461B6, 0x0B, 0xDA9280042E94B, 0x65FAE7, 0x120FA962, 0x3CE6186C89CC, 0x156578FEB912CD70, 0x2C8DE64B083CB, 0xB629, 0xDF, 0x3863053, 0x6C03DEED6C2, 0x8D00, 0x9C8B, 0xDC9}, + }, + { + []byte{0xCF, 0x4B, 0x63, 0xE2, 0x82, 0xA4, 0xA3, 0x18, 0x6B, 0x5B, 0x1E, 0xAA, 0x55, 0x8D, 0xBD, 0x1D, 0xE3, 0xA5, 0xAE, 0x26, 0x41, 0xBA, 0x07, 0xC5, 0x85, 0xA2, 0xB0, 0xCC, 0xA9, 0x90, 0x6B, 0xA4, 0x07, 0xBF, 0xE3, 0x01, 0x1F, 0x0D, 0x73, 0xBD, 0xC4, 0xAB, 0x03, 0xC0, 0xD7, 0xEC, 0x47, 0xCE, 0x21, 0x9C, 0x43, 0x3B, 0x88, 0xD4, 0xA3, 0xDE, 0x49, 0x70, 0x48, 0x47, 0x84, 0x97, 0xD8, 0x03, 0x34, 0x74, 0x26, 0x4B, 0x0E, 0x52, 0x32, 0x18, 0xDF, 0x36, 0xD2, 0x34, 0xA1, 0x9C, 0x04, 0x97, 0x83, 0x10, 0xC2, 0x32, 0xF8, 0x5A, 0xD2, 0xCB, 0xD8, 0x3B, 0x35, 0x58, 0x86, 0x00, 0x89, 0xDE, 0xF5, 0x7C, 0x1A, 0xFA}, + []uint{23, 40, 60, 52, 61, 12, 53, 39, 40, 17, 40, 58, 31, 13, 58, 39, 47, 4, 38, 16, 43, 12, 4}, + []uint64{0x67A5B1, 0xF14152518C, 0x35AD8F552AC6DE8, 0xEF1D2D71320DD, 0x7C585A2B0CCA99, 0x6B, 0x1480F7FC6023E1, 0x573BDC4AB0, 0x3C0D7EC47C, 0x1C433, 0x8867711A94, 0x1EF24B82423C24B, 0x7600CD1D, 0x132, 0x161CA46431BE6DA, 0x234A19C049, 0x3C18861197C2, 0x0D, 0x1A597B0766, 0xAB10, 0x60089DEF57C, 0x1AF, 0x0A}, + }, + { + []byte{0xC5, 0x28, 0x7C, 0xA6, 0x66, 0xF1, 0xAE, 0xF5, 0xC2, 0x0E, 0xCA, 0x71, 0xCB, 0x22, 0xE0, 0xB5, 0xA6, 0x6E, 0xC9, 0x23, 0xBE, 0xD6, 0x6A, 0x16, 0x8E, 0xC1, 0x00, 0x9B, 0xD7, 0x59, 0xA7, 0x8E, 0x68, 0x41, 0x13, 0x55, 0xAD, 0x6A, 0xC9, 0x49, 0xE1, 0x6A, 0x17, 0xBE, 0x8B, 0xA3, 0xFC, 0x81, 0x3B, 0xFB, 0x88, 0xCB, 0x1C, 0x66, 0x3F, 0x66, 0x28, 0x84, 0xB4, 0xBF, 0xFD, 0xAC, 0x65, 0x7F, 0x80, 0x67, 0x98, 0x32, 0xBF, 0x6E, 0xDC, 0xBD, 0xCD, 0x18, 0x7C, 0xBB, 0x54, 0xF9, 0xBA, 0x66, 0x79, 0x6A, 0x39, 0xC1, 0x55, 0x0E, 0x2C, 0x2B, 0x08, 0x10, 0x7F, 0x0A, 0x8A, 0xFA, 0xE3, 0x24, 0x31, 0x99, 0xFE, 0xAA}, + []uint{29, 64, 55, 11, 37, 3, 50, 2, 21, 56, 17, 35, 48, 42, 61, 26, 3, 16, 19, 11, 9, 64, 50, 11, 29, 14, 17}, + []uint64{0x18A50F94, 0xCCDE35DEB841D94E, 0x1CB22E0B5A66EC, 0x491, 0x1BED66A168, 0x07, 0x1820137AEB34F, 0x00, 0xE6841, 0x1355AD6AC949E1, 0xD42F, 0x3E8BA3FC8, 0x13BFB88CB1C6, 0x18FD98A212D, 0x5FFED632BFC033C, 0x30657ED, 0x06, 0xDCBD, 0x668C3, 0x72E, 0x1AA, 0x7CDD333CB51CE0AA, 0x21C58561020FE, 0xA8, 0x15F5C648, 0x18CC, 0x1FEAA}, + }, + { + []byte{0x69, 0x50, 0x9D, 0x23, 0xBD, 0xE7, 0x5B, 0x7F, 0x45, 0x9A, 0xD4, 0xD8, 0x62, 0x06, 0xCD, 0x3E, 0x7F, 0x32, 0xB5, 0x41, 0x25, 0x12, 0xD2, 0x13, 0xC9, 0x91, 0x44, 0x49, 0x86, 0x50, 0x8E, 0xCF, 0x38, 0x1E, 0x85, 0x65, 0xCB, 0x68, 0x6A, 0xFF, 0x1E, 0x03, 0x28, 0x0A, 0xB0, 0x93, 0xB6, 0xD5, 0x50, 0x15, 0x04, 0xB1, 0x6C, 0x71, 0xF1, 0xE7, 0xFE, 0xC9, 0x10, 0x89, 0xD6, 0xCE, 0x3F, 0x4F, 0xFF, 0x2F, 0xE8, 0xE7, 0xD1, 0x88, 0x19, 0x74, 0xEF, 0xC4, 0xD8, 0xAA, 0x49, 0x77, 0x09, 0xE9, 0x73, 0x5C, 0x75, 0xCB, 0x2F, 0x97, 0xB2, 0xF7, 0x8A, 0x71, 0x1C, 0xC5, 0x9D, 0x72, 0x16, 0x66, 0x18, 0x39, 0x3F, 0xBF}, + []uint{47, 4, 10, 61, 39, 3, 60, 41, 7, 5, 46, 14, 54, 21, 57, 14, 48, 57, 4, 56, 60, 34, 33, 25}, + []uint64{0x34A84E91DEF3, 0x0A, 0x36F, 0x1D166B5361881B34, 0x7CFE656A82, 0x02, 0x512D213C9914449, 0x10CA11D9E70, 0x1E, 0x10, 0x2B2E5B4357F8, 0x3C06, 0x14055849DB6AA8, 0x1504B, 0x2D8E3E3CFFD922, 0x44E, 0xB671FA7FF97F, 0x8E7D1881974EFC, 0x04, 0xD8AA497709E973, 0x5C75CB2F97B2F78, 0x29C473167, 0xB90B330C, 0x393FBF}, + }, + { + []byte{0x66, 0x39, 0x3F, 0x83, 0xCF, 0x72, 0x88, 0x33, 0xFB, 0xC8, 0x2F, 0x5D, 0x9E, 0x20, 0xD3, 0xF3, 0x8B, 0x9D, 0xC9, 0xE8, 0xEC, 0xC1, 0x77, 0xD0, 0x76, 0xFF, 0x95, 0x84, 0x0E, 0x97, 0x97, 0x1F, 0x62, 0x6B, 0x24, 0x42, 0x72, 0x39, 0x75, 0x5B, 0x1C, 0x65, 0xA4, 0x91, 0x43, 0xDF, 0xCA, 0x1D, 0x4F, 0x15, 0x64, 0x1E, 0xA5, 0xFC, 0xBF, 0x4B, 0x2D, 0x5D, 0xD9, 0x3F, 0xC8, 0xD9, 0x09, 0x15, 0x81, 0x71, 0xF7, 0x2D, 0x0C, 0xAD, 0xE1, 0x6F, 0x76, 0x92, 0x48, 0x1F, 0xF7, 0x63, 0xC5, 0x4E, 0x9B, 0x0E, 0xB7, 0x1F, 0xE3, 0x9C, 0x71, 0x1D, 0x85, 0xB6, 0x23, 0x48, 0x3D, 0x5B, 0x27, 0x54, 0xD0, 0xE6, 0xFF, 0x66}, + []uint{63, 43, 6, 51, 18, 20, 8, 45, 60, 24, 54, 8, 60, 4, 35, 38, 42, 26, 28, 18, 25, 15, 28, 8, 16, 57}, + []uint64{0x331C9FC1E7B94419, 0x7EF20BD7678, 0x20, 0x69F9C5CEE4F47, 0x1982E, 0xFA0ED, 0xFF, 0x56103A5E5C7, 0xD89AC9109C8E5D5, 0x6C7196, 0x249143DFCA1D4F, 0x15, 0x641EA5FCBF4B2D5, 0x0D, 0x6C9FE46C8, 0x122B02E3EE, 0x168656F0B7B, 0x2D24903, 0xFEEC78A, 0x274D8, 0xEB71FE, 0x1CE3, 0x88EC2DB, 0x11, 0xA41E, 0x15B2754D0E6FF66}, + }, + { + []byte{0x8F, 0x63, 0xBD, 0x4A, 0x79, 0xCC, 0x5B, 0x8B, 0x94, 0xD1, 0x09, 0xEC, 0xEA, 0x03, 0x58, 0xA1, 0xED, 0x5C, 0x72, 0x41, 0xBE, 0x2E, 0x84, 0x52, 0x1F, 0xE0, 0x20, 0x0F, 0x6E, 0x34, 0x3C, 0xEF, 0xC2, 0xD4, 0x1B, 0x4C, 0x52, 0x99, 0x33, 0x4F, 0x53, 0xB0, 0xD7, 0x54, 0x4B, 0x59, 0x2E, 0xEB, 0x04, 0x59, 0xAE, 0xFF, 0x4E, 0x47, 0xE1, 0x06, 0x8F, 0xB2, 0xF2, 0xB2, 0xB4, 0x73, 0x3B, 0xDA, 0x65, 0xBF, 0x04, 0x74, 0xAB, 0xE0, 0x8C, 0x51, 0x04, 0x7A, 0x68, 0x7D, 0xFE, 0xF1, 0xF1, 0xC6, 0x87, 0xD9, 0x20, 0xEE, 0x9C, 0xD7, 0x05, 0xB7, 0x7F, 0xB2, 0x9E, 0xC7, 0xB7, 0x1E, 0x28, 0x85, 0xE0, 0x86, 0xA8, 0x3C}, + []uint{7, 18, 6, 31, 48, 10, 41, 63, 35, 22, 27, 13, 22, 64, 30, 9, 14, 53, 18, 47, 8, 62, 30, 18, 41, 56, 7}, + []uint64{0x47, 0x2C77A, 0x25, 0x1E7316E2, 0xE534427B3A80, 0x358, 0x143DAB8E483, 0x3E2E84521FE0200F, 0x371A1E77E, 0x5A836, 0x4C52993, 0x69E, 0x29D86B, 0xAA25AC9775822CD7, 0x1FE9C8FC, 0x41, 0x28FB, 0x5E56568E677B4, 0x32DF8, 0x11D2AF823144, 0x11, 0x3A687DFEF1F1C687, 0x36483BA7, 0xD705, 0x16EFF653D8F, 0x6E3C510BC10D50, 0x3C}, + }, + { + []byte{0x68, 0xBC, 0xC2, 0x6E, 0x0B, 0xE7, 0x1E, 0x5A, 0x9E, 0xE4, 0x64, 0xB1, 0xF7, 0x37, 0xA7, 0xF8, 0x1B, 0x6E, 0xD3, 0x99, 0x2A, 0xE4, 0x42, 0x43, 0xA7, 0x5E, 0xD0, 0x02, 0x1D, 0x64, 0x72, 0x99, 0x26, 0xF5, 0x76, 0xD1, 0xD5, 0x0D, 0x11, 0x30, 0x71, 0x58, 0x4F, 0xD3, 0xAB, 0x73, 0xBB, 0xD0, 0x1C, 0xE9, 0xCA, 0x37, 0x41, 0xA1, 0xF6, 0x76, 0x13, 0xD6, 0x7A, 0xF2, 0xA0, 0x76, 0x66, 0xC3, 0x3B, 0x3A, 0x9C, 0xE8, 0x28, 0x50, 0x9A, 0x76, 0x2C, 0xFC, 0x48, 0x2A, 0x23, 0xD9, 0xF4, 0x39, 0x9F, 0xC1, 0x85, 0x49, 0xFE, 0xFD, 0x2C, 0x14, 0x73, 0x3D, 0x08, 0xE9, 0x15, 0x9E, 0xC1, 0x86, 0xA5, 0x27, 0xAB, 0x9D}, + []uint{7, 27, 15, 37, 13, 26, 25, 4, 56, 38, 25, 11, 13, 38, 26, 32, 51, 7, 53, 13, 46, 53, 41, 50, 41, 52}, + []uint64{0x34, 0x2F309B8, 0x17CE, 0x796A7B919, 0x58F, 0x2E6F4FF, 0x6DBB4, 0x0E, 0x64AB91090E9D7B, 0x10021D6472, 0x1324DEA, 0x76D, 0x3AA, 0x6889838AC, 0x9FA756, 0xE777A039, 0x69CA3741A1F67, 0x30, 0x13D67AF2A07666, 0x1867, 0x19D4E7414284, 0x1A762CFC482A23, 0x1B3E8733F83, 0x2A4FF7E960A3, 0x133D08E9159, 0xEC186A527AB9D}, + }, + { + []byte{0xDE, 0xFA, 0x7D, 0x18, 0x91, 0x5F, 0xD5, 0x6A, 0x70, 0x0D, 0xEA, 0x2C, 0x70, 0x3E, 0xC2, 0x17, 0x65, 0x69, 0xB9, 0x94, 0x4D, 0xEF, 0x61, 0x47, 0xFB, 0x07, 0x57, 0x14, 0x60, 0xEE, 0x6F, 0x2C, 0x50, 0x2C, 0xDC, 0x56, 0xAC, 0x5A, 0x1E, 0xA6, 0x3D, 0xAE, 0xEB, 0xEB, 0x52, 0xA0, 0x8F, 0xEB, 0x2C, 0x01, 0x55, 0x6B, 0x7D, 0x29, 0x15, 0x06, 0xB9, 0x12, 0x9E, 0x88, 0x15, 0x06, 0xE5, 0x81, 0x77, 0x61, 0x3E, 0x6A, 0x0E, 0xE4, 0xA9, 0xE2, 0x73, 0x78, 0x7A, 0x04, 0x3A, 0x8E, 0xB2, 0x68, 0x3E, 0x97, 0xFE, 0x8B, 0xA7, 0x54, 0x7C, 0x44, 0xCD, 0x50, 0x75, 0x8D, 0x1E, 0x3C, 0xF8, 0x0F, 0x09, 0x7A, 0x30, 0x38}, + []uint{18, 55, 46, 36, 6, 40, 36, 28, 62, 26, 58, 8, 62, 57, 61, 31, 4, 14, 55, 59, 6, 5, 27}, + []uint64{0x37BE9, 0x7A3122BFAAD4E0, 0x6F516381F61, 0xBB2B4DCC, 0x28, 0x9BDEC28FF6, 0xEAE28C1D, 0xCDE58A0, 0x166E2B562D0F531E, 0x35DD7D6, 0x295047F59600AAB, 0x5B, 0x3A522A0D72253D10, 0x541B9605DD84F9, 0x15077254F139BC3D, 0x10EA3AC, 0x09, 0x283E, 0x4BFF45D3AA3E22, 0x33541D63478F3E0, 0x0F, 0x01, 0x17A3038}, + }, + { + []byte{0x1F, 0x14, 0x9C, 0x3B, 0x12, 0xAB, 0xA7, 0x1E, 0x56, 0x9C, 0x0F, 0x6B, 0xD7, 0x06, 0x53, 0x96, 0xD3, 0x93, 0x66, 0x48, 0x51, 0x47, 0x3B, 0x8B, 0x73, 0x6D, 0x56, 0x25, 0xE1, 0x37, 0x33, 0x5E, 0x50, 0x51, 0xD5, 0xA2, 0xDF, 0x36, 0x71, 0x08, 0xA4, 0x45, 0xA2, 0xD8, 0x7A, 0xE4, 0x71, 0x2C, 0x8F, 0x2F, 0x58, 0x1C, 0x92, 0x53, 0x36, 0x72, 0xD1, 0x3E, 0xED, 0xDB, 0xBE, 0x81, 0x6E, 0xA5, 0x3C, 0x88, 0xE6, 0x86, 0xA2, 0xFD, 0x2C, 0xEC, 0xE2, 0x87, 0x3C, 0x4E, 0xA8, 0xB9, 0x30, 0xB6, 0xD2, 0x44, 0xFD, 0x7C, 0xDD, 0x9D, 0x68, 0x7E, 0xE3, 0xC0, 0xE1, 0x55, 0xB5, 0x46, 0xEE, 0x6E, 0x3A, 0xEC, 0xC0, 0xEE}, + []uint{27, 22, 22, 27, 60, 40, 63, 9, 2, 14, 31, 30, 40, 57, 18, 25, 52, 61, 35, 27, 25, 51, 9, 40, 13}, + []uint64{0xF8A4E1, 0x362557, 0x138F2B, 0x2703DAF, 0x5C194E5B4E4D992, 0x1451CEE2DC, 0x6DAAC4BC26E66BCA, 0x14, 0x01, 0x3568, 0x5BE6CE21, 0x5222D16, 0xC3D7238964, 0xF2F581C9253367, 0xB44F, 0x176EDDF, 0x40B7529E44734, 0x6A2FD2CECE2873C, 0x27545C985, 0x5B4913F, 0xBE6ECE, 0x5A1FB8F038556, 0x1AA, 0x377371D766, 0xEE}, + }, + { + []byte{0xC6, 0x50, 0x5B, 0xDC, 0x8F, 0x17, 0x18, 0xFC, 0x74, 0xB0, 0xA2, 0x6B, 0x88, 0x04, 0x43, 0x34, 0x39, 0xEE, 0x82, 0x49, 0x11, 0xCE, 0x55, 0x79, 0xC0, 0xBE, 0xC5, 0x9A, 0x68, 0xA7, 0xC3, 0xF3, 0x10, 0x6D, 0xCC, 0x36, 0x5A, 0xCE, 0x18, 0x02, 0xE0, 0x3F, 0x72, 0xCF, 0x15, 0xBF, 0xE6, 0x8E, 0x53, 0xA8, 0xE8, 0xC7, 0x8A, 0xF2, 0x36, 0x8F, 0x23, 0xB4, 0xF1, 0x2A, 0xF2, 0xBB, 0x12, 0x3E, 0x89, 0xDA, 0x20, 0x08, 0x70, 0x08, 0xC3, 0xD7, 0x29, 0x87, 0x29, 0xA9, 0x3C, 0x63, 0x28, 0x7B, 0x85, 0xE4, 0x58, 0x91, 0x9A, 0x28, 0xCD, 0x52, 0x3D, 0x8F, 0x57, 0xC6, 0xE6, 0xD2, 0xC7, 0x4E, 0x8C, 0x8C, 0x94, 0x0B}, + []uint{30, 9, 43, 58, 57, 60, 39, 34, 10, 51, 14, 9, 33, 26, 38, 8, 24, 11, 40, 60, 41, 44, 40, 14, 7}, + []uint64{0x319416F7, 0x47, 0x45C63F1D2C2, 0x226B8804433439E, 0x1D0492239CAAF38, 0x17D8B34D14F87E6, 0x106DCC365A, 0x338600B80, 0x3F7, 0x1678ADFF34729, 0x351D, 0x31, 0x1C5791B47, 0x24769E2, 0x15795D891F, 0x44, 0xED1004, 0x1C0, 0x230F5CA61C, 0xA6A4F18CA1EE179, 0x2C48CD1466, 0xA91EC7ABE37, 0x36963A7464, 0x1928, 0x0B}, + }, + { + []byte{0x92, 0x34, 0xDC, 0xA7, 0x57, 0x47, 0x83, 0xFB, 0x4C, 0x08, 0x2D, 0x84, 0xED, 0x7A, 0xD1, 0x7A, 0x8F, 0xDF, 0x10, 0xAE, 0xD4, 0x58, 0xEB, 0xF7, 0x69, 0xE8, 0x94, 0x41, 0x97, 0xAC, 0x78, 0x04, 0x9C, 0x5E, 0xB3, 0xB9, 0x0C, 0xE3, 0x48, 0xC5, 0x45, 0x1A, 0xBB, 0x8D, 0x21, 0xA0, 0x4B, 0x77, 0xE1, 0x01, 0x06, 0x6D, 0x89, 0x37, 0xD8, 0xB9, 0xCF, 0xD3, 0x94, 0xD0, 0xFD, 0xF8, 0xE2, 0xC5, 0x33, 0x3E, 0xAF, 0x7F, 0x9B, 0xF1, 0xDE, 0x2D, 0xF7, 0x10, 0x3A, 0xBA, 0xD9, 0x17, 0xF8, 0x79, 0x7A, 0xF4, 0xAC, 0x44, 0x60, 0x5E, 0x2E, 0xA5, 0x87, 0xC9, 0x9C, 0x7D, 0x0A, 0x42, 0xAC, 0xBF, 0x90, 0x0A, 0x83, 0xB9}, + []uint{41, 57, 52, 49, 23, 39, 53, 1, 6, 37, 2, 19, 13, 10, 52, 17, 38, 20, 20, 45, 36, 56, 22, 20, 49, 5, 18}, + []uint64{0x12469B94EAE, 0x11E0FED3020B613, 0xB5EB45EA3F7C4, 0x576A2C75FBB4, 0x7A2510, 0x32F58F0093, 0x117ACEE4338D23, 0x00, 0x0A, 0x1146AEE348, 0x01, 0x5025B, 0x17E1, 0x04, 0x19B624DF62E73, 0x1E9CA, 0x1A1FBF1C58, 0xA667D, 0x5EFF3, 0xFC778B7DC40, 0xEAEB645FE, 0x1E5EBD2B111817, 0x22EA58, 0x7C99C, 0xFA1485597F20, 0x02, 0x283B9}, + }, + { + []byte{0x14, 0x51, 0xA2, 0xCB, 0xC5, 0x75, 0xFD, 0x6D, 0xAA, 0x55, 0x35, 0x8A, 0x0C, 0x63, 0xD7, 0xD0, 0x6A, 0x88, 0x56, 0x78, 0xDE, 0x38, 0xA8, 0xAA, 0xB0, 0x3A, 0x8A, 0xD4, 0xCC, 0x91, 0x9A, 0x6D, 0xA2, 0xC9, 0xA5, 0x3A, 0xAB, 0x4C, 0xC4, 0xFC, 0x3C, 0xB2, 0x44, 0xBB, 0x3C, 0xFB, 0xF8, 0x62, 0x82, 0x90, 0xF8, 0x5E, 0x6A, 0x3C, 0x6A, 0x14, 0x61, 0x7E, 0xA8, 0x0C, 0x3B, 0x7F, 0x00, 0xF8, 0x18, 0x14, 0xBD, 0x13, 0xA3, 0xC6, 0xE6, 0xF1, 0xB0, 0x9F, 0x91, 0xF7, 0x16, 0xC9, 0xDB, 0x9D, 0xC8, 0xB8, 0x96, 0xA0, 0x22, 0x95, 0xF3, 0x39, 0x8C, 0xE8, 0x6D, 0xDC, 0xE2, 0x75, 0xAD, 0xC3, 0xC9, 0x2E, 0x2A, 0xDC}, + []uint{9, 52, 26, 1, 3, 3, 39, 45, 14, 59, 41, 42, 38, 61, 61, 57, 29, 56, 58, 6, 11, 32, 33, 17, 7}, + []uint64{0x28, 0xA345978AEBFAD, 0x2D52A9A, 0x01, 0x04, 0x02, 0x418C7AFA0D, 0xA2159E378E2, 0x28AA, 0x581D456A6648CD3, 0xDA2C9A53AA, 0x2D3313F0F2C, 0x244BB3CFBF, 0x10C50521F0BCD478, 0x1A85185FAA030EDF, 0x1807C0C0A5E89D1, 0x1C6E6F1B, 0x9F91F716C9DB9, 0x3722E25A808A57C, 0x33, 0x4C6, 0x7436EE71, 0x75ADC3C9, 0x5C55, 0x5C}, + }, + { + []byte{0x71, 0x4F, 0x73, 0x51, 0xAA, 0x39, 0xC9, 0x4F, 0xF7, 0x6E, 0x45, 0x35, 0x07, 0x37, 0x78, 0x28, 0x44, 0x01, 0x14, 0xE7, 0xCA, 0x39, 0x71, 0x8B, 0x93, 0x42, 0x99, 0x23, 0x8B, 0x38, 0x34, 0x2F, 0xD9, 0x59, 0xF7, 0xFA, 0x8F, 0x96, 0x99, 0x23, 0x4D, 0x5D, 0x6D, 0xF2, 0xDD, 0xEB, 0x60, 0xF6, 0x7F, 0x01, 0xBA, 0x5D, 0xAD, 0x7E, 0xD2, 0x2C, 0x34, 0x11, 0x1E, 0xD7, 0x77, 0xAE, 0x59, 0x52, 0x8B, 0xAC, 0x48, 0x9F, 0xF3, 0x26, 0xCF, 0x56, 0xE2, 0x16, 0x56, 0x15, 0xB1, 0xDE, 0xDC, 0x0E, 0x00, 0x35, 0x4A, 0x59, 0x72, 0x56, 0x44, 0xD7, 0x22, 0x77, 0x2E, 0x78, 0x26, 0x3E, 0x0E, 0xF2, 0xB7, 0x37, 0x5B, 0x86}, + []uint{49, 54, 59, 50, 64, 64, 51, 12, 33, 26, 54, 4, 29, 33, 32, 61, 44, 41, 18, 22}, + []uint64{0xE29EE6A35473, 0x24A7FBB7229A83, 0x4DDE0A11004539F, 0xA39718B93429, 0x9238B38342FD959F, 0x7FA8F9699234D5D6, 0x6F96EF5B07B3F, 0x80D, 0x1A5DAD7ED, 0x8B0D04, 0x11ED777AE59528, 0x0B, 0x158913FE, 0xC9B3D5B8, 0x8595856C, 0xEF6E07001AA52CB, 0x92B226B913B, 0x12E78263E0E, 0x3CADC, 0x375B86}, + }, + { + []byte{0x50, 0xFB, 0x8B, 0x19, 0xF8, 0xE7, 0x6D, 0xEF, 0xCC, 0x78, 0x91, 0x98, 0x77, 0xAD, 0x00, 0xA7, 0x6F, 0x35, 0x25, 0xA8, 0x8F, 0xC2, 0x2E, 0x07, 0x1C, 0x95, 0x09, 0x10, 0xED, 0xCC, 0x86, 0x3C, 0xF1, 0x04, 0x2C, 0xC6, 0xC6, 0xE7, 0x89, 0xC1, 0x7F, 0x34, 0x16, 0xBE, 0x45, 0x47, 0xD8, 0xE7, 0x84, 0x74, 0x30, 0x91, 0x36, 0xDD, 0x88, 0x00, 0xB9, 0x73, 0x22, 0xA9, 0xF1, 0x72, 0x5F, 0x6B, 0x61, 0x1F, 0x09, 0xFA, 0xB8, 0x8F, 0xE1, 0x24, 0xBB, 0xDF, 0xB9, 0x43, 0xD8, 0x46, 0x3E, 0x4C, 0xD9, 0x77, 0x56, 0xD5, 0x4F, 0x98, 0x7D, 0x61, 0xD6, 0xFA, 0x27, 0xB6, 0xA1, 0x95, 0x8A, 0xB2, 0x40, 0x6E, 0x1F, 0xC2}, + []uint{9, 19, 15, 27, 49, 35, 15, 38, 48, 39, 29, 49, 62, 51, 3, 17, 38, 37, 35, 13, 6, 33, 54, 8, 14, 35, 22}, + []uint64{0xA1, 0x7B8B1, 0x4FC7, 0x1DB7BF3, 0x3C48CC3BD680, 0x29DBCD496, 0x511F, 0x2117038E4A, 0x848876E6431E, 0x3C410B31B1, 0x173C4E0B, 0x1F3416BE4547D, 0x239E11D0C244DB76, 0x100172E64553E, 0x01, 0xE4BE, 0x35B08F84FD, 0xB88FE124B, 0x5EFDCA1EC, 0x463, 0x39, 0x66CBBAB6, 0x2A9F30FAC3ADF4, 0x4F, 0x1B50, 0x6562AC901, 0x2E1FC2}, + }, + { + []byte{0xCB, 0x0B, 0xBC, 0xEA, 0x8E, 0x98, 0xFC, 0x67, 0x6A, 0x13, 0x54, 0x10, 0x90, 0xE9, 0xAF, 0x4D, 0xA5, 0x28, 0xAD, 0x14, 0xD3, 0x62, 0x21, 0x81, 0x4F, 0x15, 0x40, 0x65, 0x32, 0x37, 0x39, 0x8C, 0x71, 0x38, 0xBE, 0xF8, 0x37, 0x6B, 0x96, 0x5A, 0x2C, 0x19, 0x6F, 0xF3, 0xCF, 0xF9, 0x08, 0xC3, 0x4B, 0xAB, 0x9C, 0xB3, 0xA2, 0xE7, 0x62, 0x61, 0xBC, 0x59, 0x83, 0x95, 0x5B, 0x22, 0x0B, 0x1B, 0x7D, 0x7B, 0xDE, 0xF7, 0x96, 0xC0, 0x30, 0x29, 0xEF, 0x4E, 0x46, 0xE8, 0x8D, 0x24, 0xFC, 0x0B, 0x5E, 0xC1, 0x40, 0x6E, 0xA6, 0x81, 0xB1, 0x66, 0x15, 0x78, 0xED, 0xF6, 0x3A, 0x6D, 0x3B, 0xC6, 0x29, 0x3B, 0x29, 0xAD}, + []uint{34, 62, 46, 7, 8, 55, 14, 33, 30, 14, 16, 63, 29, 31, 39, 52, 60, 2, 21, 4, 60, 45, 9, 51, 15}, + []uint64{0x32C2EF3AA, 0xE98FC676A135410, 0x243A6BD3694A, 0x15, 0xA2, 0x4D36221814F154, 0x194, 0x191B9CC63, 0x22717DF0, 0x1BB5, 0xCB2D, 0xB065BFCF3FE4230, 0x1A5D5CE5, 0x4E8B9D89, 0x4378B3072A, 0xB6441636FAF7B, 0xDEF2D806053DE9C, 0x02, 0x6E88D, 0x02, 0x4FC0B5EC1406EA6, 0x10362CC2AF1D, 0x17D, 0x474DA778C5276, 0x29AD}, + }, + { + []byte{0x65, 0x00, 0xA4, 0x4F, 0x09, 0x6B, 0x77, 0xDC, 0xB3, 0x75, 0xDA, 0x0C, 0xEF, 0x14, 0x65, 0x7B, 0x1B, 0xDF, 0x21, 0x1E, 0xBA, 0x98, 0xC0, 0xD0, 0x34, 0xB3, 0x75, 0x8F, 0x90, 0x17, 0xF0, 0x73, 0x99, 0x7D, 0x1A, 0x89, 0xDF, 0x48, 0xEE, 0xF4, 0x7C, 0x23, 0x93, 0x2B, 0xFF, 0xFE, 0x5A, 0x3C, 0x4F, 0x9D, 0x4C, 0x22, 0x2E, 0xD0, 0x31, 0x23, 0xD6, 0x00, 0xA2, 0x63, 0xD8, 0x78, 0xAD, 0x90, 0xB3, 0xF2, 0x8B, 0x38, 0x45, 0xB3, 0x1A, 0xB7, 0x5B, 0xD3, 0x5A, 0x93, 0x43, 0x65, 0x33, 0xB2, 0xB4, 0x74, 0x2C, 0x87, 0x63, 0x15, 0xCF, 0x9F, 0x20, 0x6D, 0xCF, 0xE7, 0x49, 0xFD, 0x32, 0x85, 0x0D, 0x4C, 0x56, 0x23}, + []uint{33, 45, 36, 59, 14, 38, 44, 46, 39, 5, 16, 58, 26, 45, 28, 1, 48, 46, 26, 54, 57, 19, 17}, + []uint64{0xCA01489E, 0x25ADDF72CDD, 0x76833BC51, 0x4AF637BE423D753, 0x606, 0x206966EB1F, 0x202FE0E732F, 0x28D44EFA4777, 0x51F08E4CAF, 0x1F, 0xFF2D, 0x789F3A98445DA0, 0x1891EB0, 0xA263D878AD, 0x90B3F28, 0x01, 0x6708B66356EB, 0x1E9AD49A1B29, 0x276568E, 0x21643B18AE7CF9, 0x6DCFE749FD328, 0x286A6, 0x5623}, + }, + { + []byte{0x31, 0xB1, 0x41, 0xCC, 0x16, 0x3C, 0xA8, 0x45, 0x95, 0x99, 0x28, 0x22, 0xBC, 0xDB, 0x79, 0xB1, 0x73, 0x27, 0xF2, 0x83, 0xFF, 0x6D, 0x48, 0x0D, 0xAF, 0x90, 0x08, 0xF8, 0x9C, 0xD3, 0x54, 0x39, 0x63, 0x63, 0x75, 0xD7, 0xA0, 0x88, 0x47, 0x1A, 0x8A, 0x0F, 0x3D, 0x0E, 0x92, 0x7B, 0x6E, 0x5D, 0x01, 0x4F, 0x0E, 0x59, 0x79, 0xF0, 0x4B, 0xDC, 0x53, 0x58, 0x2A, 0x6E, 0x98, 0xC9, 0x62, 0xAD, 0x24, 0x08, 0xB7, 0x37, 0xAB, 0x65, 0x9A, 0x2A, 0x59, 0x3E, 0xE4, 0x29, 0x9C, 0x10, 0xC3, 0x45, 0xD7, 0x6A, 0x1C, 0x4D, 0xC3, 0x85, 0xFF, 0x94, 0x8E, 0x56, 0x3B, 0x21, 0x7A, 0x3D, 0x6C, 0x7D, 0xE3, 0x97, 0x37, 0x6C}, + []uint{37, 10, 30, 25, 3, 25, 7, 11, 15, 8, 19, 20, 25, 37, 45, 63, 33, 52, 55, 12, 63, 24, 1, 31, 53, 25, 54, 17}, + []uint64{0x636283982, 0x31E, 0x1508B2B3, 0x4A08AF, 0x01, 0x16DE6C5, 0x66, 0x27F, 0x141F, 0xFB, 0x35203, 0x6BE40, 0x47C4E6, 0x1354396363, 0xEBAF41108E3, 0x28A0F3D0E927B6E5, 0x1A029E1CB, 0x2F3E097B8A6B0, 0x2A6E98C962AD24, 0x8B, 0x39BD5B2CD152C9F7, 0x214CE0, 0x01, 0x61A2EBB, 0xA1C4DC385FF94, 0x11CAC76, 0x10BD1EB63EF1CB, 0x1376C}, + }, + { + []byte{0x8B, 0x89, 0x8A, 0x74, 0xAA, 0x17, 0xFF, 0xDA, 0x92, 0xDF, 0xB8, 0xC9, 0xFE, 0xB4, 0xB6, 0x84, 0x9C, 0x55, 0x64, 0x82, 0x41, 0x81, 0xFE, 0x23, 0xF1, 0xAC, 0x76, 0x5D, 0x55, 0xC8, 0x13, 0x6C, 0x5A, 0x0E, 0xAA, 0x71, 0x12, 0xFA, 0xED, 0x0C, 0x56, 0x2F, 0xD9, 0x70, 0x38, 0x7F, 0x8D, 0x06, 0x00, 0x3C, 0xD8, 0xB7, 0xE4, 0x99, 0x69, 0xD4, 0xFC, 0xA0, 0x35, 0x5D, 0x9F, 0xD2, 0x59, 0x32, 0x93, 0x60, 0x4A, 0x57, 0xCD, 0x93, 0x94, 0x74, 0x6F, 0x99, 0x5C, 0x49, 0x58, 0x62, 0xE5, 0xF3, 0x59, 0x9B, 0x33, 0xFE, 0x9D, 0x86, 0x2D, 0xA3, 0x0A, 0xB8, 0xB8, 0x7E, 0xFB, 0x51, 0xD0, 0x16, 0xF6, 0xF3, 0x22, 0x22}, + []uint{31, 27, 6, 12, 61, 3, 47, 47, 8, 8, 15, 29, 29, 32, 27, 22, 42, 51, 14, 52, 6, 25, 6, 22, 51, 62, 60, 5}, + []uint64{0x45C4C53A, 0x2A85FFF, 0x1A, 0x92D, 0x1F7193FD696D0938, 0x05, 0x2B24120C0FF1, 0xFC6B1D97557, 0x20, 0x4D, 0x58B4, 0x3AA9C44, 0x17D76862, 0xB17ECB81, 0x61FE341, 0x2003CD, 0x22DF9265A75, 0x1F9406ABB3FA4, 0x2C99, 0x49B0252BE6C9C, 0x28, 0x1D1BE65, 0x1C, 0x125618, 0x5CBE6B33667FD, 0xEC316D1855C5C3F, 0x7DA8E80B7B79911, 0x02}, + }, + { + []byte{0xC8, 0x02, 0x2F, 0x55, 0xFC, 0x45, 0xDF, 0xA9, 0x4D, 0x04, 0xDC, 0x73, 0xDF, 0x5C, 0x82, 0x14, 0x9E, 0x65, 0xC8, 0xFF, 0x12, 0x5E, 0x15, 0x8F, 0xCA, 0x0A, 0xC9, 0xB9, 0x59, 0x99, 0x64, 0x7A, 0x6A, 0xE6, 0x49, 0x9C, 0x74, 0x72, 0xCD, 0xB3, 0x6F, 0xCF, 0x99, 0x02, 0xB1, 0x2C, 0xB4, 0x78, 0xF4, 0x43, 0x1C, 0xB9, 0xB0, 0xE6, 0xC6, 0x94, 0xF3, 0x01, 0x66, 0x13, 0x5E, 0x2A, 0xF9, 0xD0, 0x33, 0xFC, 0x77, 0xC4, 0xFA, 0x0F, 0x7B, 0xFD, 0x1C, 0xC2, 0x77, 0xED, 0x37, 0xAA, 0x97, 0x3D, 0x1E, 0x86, 0x96, 0x9C, 0x67, 0x12, 0xC2, 0x8B, 0xA7, 0xCB, 0xC1, 0x35, 0xCE, 0x80, 0xF2, 0x4F, 0xCB, 0xB8, 0x9F, 0xE4}, + []uint{28, 39, 42, 11, 32, 11, 2, 11, 58, 55, 37, 23, 17, 57, 41, 8, 22, 21, 23, 3, 1, 49, 45, 27, 46, 48, 9, 34}, + []uint64{0xC8022F5, 0x2FE22EFD4A, 0x1A09B8E7BEB, 0x482, 0x149E65C8, 0x7F8, 0x02, 0x25E, 0x563F282B26E566, 0x32C8F4D5CC9338, 0x1D1CB36CDB, 0x79F320, 0xAC4B, 0x5A3C7A218E5CD8, 0xE6C694F301, 0x66, 0x4D78A, 0x17CE81, 0x4FF1DF, 0x00, 0x01, 0x7D07BDFE8E61, 0x77ED37AA973, 0x68F434B, 0x138CE2585174, 0xF97826B9D01E, 0x93, 0x3CBB89FE4}, + }, + { + []byte{0x69, 0x18, 0xCA, 0x1F, 0x76, 0xD9, 0x00, 0xA8, 0xC4, 0x7C, 0x90, 0xB8, 0xC9, 0x4B, 0xF2, 0xE1, 0xEF, 0xFA, 0x7C, 0x5A, 0x6E, 0xB2, 0xDA, 0x4A, 0x86, 0x2E, 0x93, 0x5A, 0xB0, 0x61, 0x15, 0x72, 0xDD, 0x20, 0xEA, 0xBF, 0xA5, 0x4D, 0x9C, 0x7C, 0x9E, 0xD0, 0xA4, 0x0D, 0x1D, 0x85, 0xF1, 0x03, 0xBA, 0xA0, 0xF7, 0x37, 0x96, 0x0E, 0x05, 0x4C, 0xCD, 0x96, 0x9E, 0x7F, 0xF2, 0xE9, 0xE5, 0xA2, 0x7F, 0xED, 0x32, 0x13, 0x24, 0x1F, 0x60, 0x93, 0x0F, 0x25, 0x4D, 0x96, 0x9B, 0x4C, 0x19, 0xE1, 0xA8, 0x20, 0x41, 0x27, 0xE5, 0x1C, 0x92, 0x30, 0x93, 0x15, 0x0D, 0xD8, 0xCB, 0xAC, 0x25, 0xE1, 0x60, 0xE2, 0x4D, 0x1E}, + []uint{36, 31, 43, 61, 45, 54, 48, 21, 50, 49, 44, 30, 7, 12, 26, 30, 62, 63, 60, 28}, + []uint64{0x6918CA1F7, 0x36C80546, 0x11F242E3252, 0x1F970F7FD3E2D375, 0x12DA4A862E93, 0x16AC18455CB748, 0x3AAFE953671F, 0x4F685, 0x81A3B0BE2077, 0xA83DCDE58381, 0x533365A79FF, 0x32E9E5A2, 0x3F, 0xF69, 0x2426483, 0x3B049879, 0xA9B2D369833C350, 0x204127E51C923093, 0x150DD8CBAC25E16, 0xE24D1E}, + }, + { + []byte{0x06, 0x15, 0x43, 0xBF, 0x63, 0x7B, 0xFD, 0x1D, 0xED, 0xB3, 0x50, 0x4E, 0x2F, 0x27, 0x4D, 0x97, 0xD2, 0x0D, 0x47, 0x39, 0xD1, 0x16, 0x90, 0x26, 0xEC, 0xC6, 0x5D, 0x6C, 0xA5, 0x99, 0x92, 0x02, 0xD7, 0x1D, 0x35, 0x00, 0x4A, 0xE0, 0xFF, 0xE5, 0xEC, 0x16, 0x13, 0x59, 0xB3, 0x05, 0xE9, 0x4D, 0xD5, 0x7D, 0x24, 0x81, 0x14, 0x2C, 0xF4, 0xF7, 0xBD, 0xD0, 0x19, 0xFB, 0x51, 0xE9, 0x9E, 0x54, 0x17, 0x84, 0x9E, 0x39, 0x23, 0x1E, 0xFC, 0x9B, 0xC1, 0xF2, 0x75, 0xA1, 0xC6, 0xFC, 0x3D, 0xEA, 0xB4, 0xA0, 0x64, 0xA0, 0xE0, 0x43, 0xD6, 0x66, 0xF7, 0x77, 0x0B, 0xF0, 0x1E, 0x66, 0x6B, 0x7B, 0xAD, 0xCA, 0xDB, 0xEC}, + []uint{39, 11, 11, 10, 48, 32, 20, 46, 26, 16, 25, 24, 37, 16, 47, 63, 18, 9, 51, 38, 56, 63, 61, 32, 1}, + []uint64{0x30AA1DFB1, 0x5EF, 0x7A3, 0x2F6, 0xD9A8271793A6, 0xCBE906A3, 0x9CE88, 0x2D204DD98CBA, 0x3652CCC, 0x9016, 0x171D350, 0x4AE0F, 0x1FCBD82C26, 0xB366, 0x5E94DD57D24, 0x408A167A7BDEE80C, 0x3F6A3, 0x1A6, 0x3CA82F093C724, 0x18F7E4DE0F, 0x93AD0E37E1EF55, 0x52819283810F599B, 0x1BBB85F80F3335BD, 0xD6E56DF6, 0x00}, + }, + { + []byte{0xA3, 0x34, 0x99, 0x5A, 0xA7, 0x43, 0xDE, 0x9D, 0x0D, 0xB0, 0x07, 0x3A, 0x06, 0x10, 0x48, 0x4F, 0x9D, 0x6E, 0xA1, 0x12, 0xBE, 0x3E, 0xBE, 0x16, 0x5C, 0x0D, 0xC9, 0x30, 0x78, 0x71, 0x44, 0x0F, 0xC1, 0xB5, 0x54, 0x00, 0xEE, 0xEB, 0x28, 0x81, 0x2C, 0xF9, 0x16, 0xEA, 0xA6, 0x31, 0x28, 0xC0, 0x08, 0xDD, 0x33, 0x89, 0x6B, 0x58, 0x2F, 0x6A, 0x73, 0xA6, 0xED, 0xDD, 0xFA, 0x6D, 0xAE, 0x45, 0xA4, 0xE1, 0x2B, 0xD3, 0xDA, 0xF1, 0x1F, 0x4A, 0x89, 0x03, 0xD4, 0x9A, 0xF1, 0x0C, 0x9B, 0x01, 0xF3, 0x67, 0x87, 0x64, 0x93, 0x21, 0x2C, 0xC7, 0x84, 0x3D, 0xBE, 0x07, 0x6B, 0x2B, 0xD2, 0x87, 0x5B, 0xAB, 0x1E, 0x2A}, + []uint{17, 5, 3, 47, 17, 35, 37, 43, 52, 22, 45, 63, 37, 55, 37, 16, 12, 32, 12, 13, 2, 49, 3, 55, 5, 37, 15, 33, 1}, + []uint64{0x14669, 0x06, 0x02, 0x5AA743DE9D0D, 0x1600E, 0x3A0610484, 0x1F3ADD4225, 0x3E3EBE165C0, 0xDC9307871440F, 0x306D55, 0x777594409, 0x33E45BAA98C4A300, 0x46E99C4B5, 0x560BDA9CE9BB77, 0xFD36D722D, 0x2709, 0x5E9, 0xED788FA5, 0x448, 0x3D4, 0x02, 0xD78864D80F9B, 0x01, 0x70EC92642598F0, 0x10, 0x1EDF03B595, 0x74A1, 0x1ADD58F15, 0x00}, + }, + { + []byte{0xC2, 0x1C, 0x29, 0x23, 0xD2, 0x85, 0x5C, 0x44, 0xDD, 0x85, 0xEB, 0xB0, 0x7D, 0x6B, 0x97, 0x2B, 0x4A, 0x92, 0x13, 0xDF, 0x85, 0x7B, 0x3F, 0xF1, 0x81, 0xFD, 0x59, 0xC5, 0x6B, 0x34, 0xE8, 0x98, 0xFB, 0x49, 0x38, 0x07, 0x6B, 0xE7, 0x0C, 0x2B, 0x48, 0x23, 0x87, 0x36, 0x1E, 0x2A, 0x00, 0x39, 0x0E, 0x01, 0x9C, 0x34, 0x67, 0x8D, 0x62, 0x2F, 0xD3, 0x59, 0x80, 0x20, 0x14, 0xB1, 0xDF, 0xE0, 0xA9, 0xF3, 0x05, 0x8D, 0xFB, 0x1F, 0xD2, 0xE5, 0x43, 0xEA, 0xEB, 0xE0, 0xF3, 0x78, 0x15, 0x16, 0x34, 0x1A, 0xD7, 0x05, 0xB3, 0x66, 0xC1, 0xB1, 0x7A, 0x98, 0xFB, 0x9C, 0x88, 0x1F, 0xD4, 0xD4, 0xEF, 0x03, 0x43, 0xFA}, + []uint{54, 24, 30, 28, 48, 31, 42, 39, 14, 8, 55, 63, 17, 57, 64, 10, 62, 60, 10, 41, 31, 12}, + []uint64{0x30870A48F4A157, 0x113761, 0x1EBB07D6, 0xB972B4A, 0x9213DF857B3F, 0x78C0FEAC, 0x38AD669D131, 0x7B4938076B, 0x39C3, 0x0A, 0x690470E6C3C540, 0x390E019C34678D6, 0x45FA, 0xD66008052C77F8, 0x2A7CC1637EC7F4B9, 0x143, 0x3ABAF83CDE05458D, 0x6B5C16CD9B06C5, 0x3A9, 0x11F739103FA, 0x4D4EF034, 0x3FA}, + }, + { + []byte{0x2E, 0xB7, 0x5F, 0x66, 0x0C, 0xEE, 0x9F, 0x22, 0x62, 0xEE, 0x89, 0x47, 0x18, 0x75, 0xC2, 0x9C, 0x4B, 0xC0, 0x5A, 0xB1, 0x11, 0x1F, 0x57, 0xA4, 0x68, 0xF4, 0xA9, 0xF4, 0x0A, 0x52, 0x86, 0xA5, 0x4B, 0x56, 0xBF, 0xC0, 0x2F, 0x16, 0x31, 0x62, 0xA2, 0x78, 0xD1, 0x9D, 0xE9, 0x0F, 0x2D, 0x40, 0xC5, 0xA7, 0x3C, 0x6C, 0x53, 0x8A, 0xBF, 0xD2, 0x16, 0x78, 0xDB, 0x22, 0xD3, 0x19, 0x69, 0x17, 0x9D, 0x81, 0x43, 0x98, 0x3A, 0x82, 0x93, 0x84, 0xFF, 0x4B, 0xA0, 0x26, 0xB0, 0x5C, 0xC3, 0x72, 0xA5, 0x7C, 0xDE, 0xC8, 0xD5, 0x2E, 0x6F, 0x80, 0x75, 0x14, 0xA5, 0xAB, 0x96, 0xEE, 0x60, 0x94, 0x0D, 0x42, 0xB9, 0x62}, + []uint{49, 25, 55, 41, 56, 18, 46, 16, 29, 17, 38, 39, 23, 18, 12, 34, 45, 9, 4, 48, 28, 59, 9, 14, 15, 34, 19}, + []uint64{0x5D6EBECC19DD, 0x7C898B, 0x5D128E30EB8538, 0x12F016AC444, 0x7D5E91A3D2A7D0, 0xA528, 0x1A952D5AFF00, 0xBC58, 0x18B1513C, 0xD19D, 0x3A43CB5031, 0x34E78D8A71, 0x2BFD21, 0x19E36, 0xC8B, 0x131969179, 0x1B0287307505, 0x4E, 0x01, 0x3FD2E809AC17, 0x30DCA95, 0x79BD91AA5CDF00E, 0x145, 0xA5A, 0x5CB7, 0x1CC1281A8, 0x2B962}, + }, + { + []byte{0x11, 0x9E, 0xE6, 0x8D, 0x03, 0x9F, 0x19, 0x2F, 0x7C, 0x53, 0xA3, 0xA6, 0x8F, 0x6C, 0xA2, 0xF4, 0x0E, 0xE2, 0x21, 0x84, 0x09, 0xDB, 0x3C, 0xDF, 0x61, 0xEC, 0xCE, 0x30, 0xF0, 0x0B, 0x4E, 0x47, 0x97, 0x26, 0xE2, 0xE8, 0x13, 0x7A, 0x9D, 0x77, 0x17, 0x28, 0xB2, 0x5F, 0x77, 0x64, 0x25, 0x93, 0x01, 0xEB, 0x8B, 0xD9, 0x8F, 0x54, 0x49, 0x2C, 0x0B, 0x6F, 0x85, 0x9E, 0x06, 0x34, 0x26, 0x0C, 0xF1, 0x44, 0xC9, 0x5D, 0x04, 0x46, 0x92, 0x69, 0xA1, 0x16, 0xA9, 0x0D, 0x32, 0x3A, 0x88, 0x11, 0x33, 0x3B, 0xCD, 0x3F, 0x78, 0x4F, 0x0A, 0x5D, 0x55, 0x69, 0x35, 0x22, 0x2C, 0x90, 0xFD, 0xC2, 0x49, 0xF5, 0x44, 0x7D}, + []uint{50, 55, 18, 59, 59, 49, 28, 6, 23, 35, 63, 16, 40, 13, 12, 43, 56, 7, 34, 44, 3, 18, 11, 52, 6}, + []uint64{0x467B9A340E7C, 0x325EF8A7474D1E, 0x36517, 0x503B888610276CF, 0x1BEC3D99C61E016, 0x1391E5C9B8BA0, 0x4DEA75D, 0x31, 0x394592, 0x7DDD90964, 0x603D717B31EA8925, 0x816D, 0xF0B3C0C684, 0x1833, 0xC51, 0x192BA088D24, 0xD3422D521A6475, 0x08, 0x44CCEF34, 0xFDE13C29755, 0x02, 0x349A9, 0x8B, 0x243F70927D511, 0x3D}, + }, + { + []byte{0x6D, 0x2D, 0x02, 0x1B, 0x65, 0xC7, 0x93, 0x72, 0xA1, 0xC8, 0x00, 0xE7, 0xD6, 0xB5, 0x89, 0x88, 0x9B, 0x48, 0x4E, 0x8A, 0x7F, 0x41, 0x10, 0x12, 0x6B, 0xF5, 0x3C, 0x74, 0x9F, 0x86, 0x6F, 0x9E, 0x74, 0x0C, 0x6C, 0x80, 0x6D, 0x65, 0xBB, 0xAC, 0x9A, 0xAF, 0x74, 0x6E, 0x28, 0x07, 0x2D, 0xCD, 0x12, 0x07, 0xEE, 0x47, 0x97, 0xC0, 0xF8, 0x85, 0xBC, 0x6C, 0x00, 0xE6, 0x03, 0x07, 0x19, 0xB2, 0xE0, 0xC0, 0x76, 0x24, 0xB8, 0xD0, 0x98, 0x5B, 0xD3, 0xBE, 0x06, 0x6C, 0x9C, 0xF2, 0x64, 0xC2, 0xAC, 0x9D, 0xE2, 0x58, 0x76, 0xF0, 0x2F, 0x1D, 0xA8, 0x92, 0xDA, 0xB8, 0x54, 0xEE, 0xC9, 0xB2, 0x6F, 0xE2, 0xC2, 0x3A}, + []uint{49, 35, 28, 50, 45, 11, 8, 11, 41, 57, 52, 4, 57, 7, 55, 27, 27, 30, 12, 10, 64, 32, 58, 10, 20}, + []uint64{0xDA5A0436CB8F, 0x1372A1C80, 0xE7D6B5, 0x226226D213A29, 0x1FA0880935FA, 0x4F1, 0xD2, 0x3F0, 0x19BE79D031B, 0x4036B2DDD64D57, 0xBA37140396E68, 0x09, 0x7EE4797C0F885, 0x5E, 0x1B003980C1C66C, 0x5C180EC, 0x24B8D09, 0x216F4EF8, 0x19B, 0x9C, 0xF264C2AC9DE25876, 0xF02F1DA8, 0x24B6AE153BB26C9, 0x2FE, 0x2C23A}, + }, + { + []byte{0xF6, 0x50, 0x44, 0x6B, 0xE9, 0x59, 0x96, 0x1E, 0x48, 0x15, 0x9A, 0x63, 0x62, 0xDC, 0x04, 0x5E, 0x86, 0xE3, 0xCA, 0x7A, 0x47, 0x38, 0x1C, 0xB8, 0xD0, 0x0F, 0x4D, 0x0B, 0x7C, 0x4D, 0xB8, 0xE7, 0xAB, 0xD2, 0x11, 0x03, 0x13, 0x90, 0xD1, 0x08, 0x50, 0xD0, 0xC9, 0xAE, 0xBD, 0x67, 0xB2, 0x3B, 0xDE, 0xED, 0x7B, 0x79, 0x0F, 0x23, 0xE2, 0x89, 0x33, 0x7E, 0xE9, 0x07, 0xE1, 0x24, 0xDC, 0xAB, 0x49, 0xDE, 0x26, 0xFC, 0xA1, 0xE3, 0x9B, 0x6C, 0x16, 0x5E, 0x60, 0xEA, 0xFF, 0x67, 0x56, 0x9B, 0xF3, 0x88, 0xCC, 0x57, 0x56, 0xCB, 0x76, 0x25, 0xDA, 0xB5, 0x9C, 0xE8, 0x3D, 0xB7, 0x79, 0xED, 0xF5, 0xB4, 0x99, 0xB4}, + []uint{44, 12, 8, 57, 32, 29, 32, 28, 18, 40, 8, 58, 60, 59, 64, 50, 46, 4, 46, 59, 46}, + []uint64{0xF650446BE95, 0x996, 0x1E, 0x902B34C6C5B808, 0xBD0DC794, 0x1E91CE07, 0x2E3403D3, 0x42DF136, 0x38E7A, 0xBD21103139, 0x0D, 0x421434326BAF59, 0xEC8EF7BB5EDE43C, 0x47C51266FDD20FC, 0x249B95693BC4DF94, 0xF1CDB60B2F30, 0x1D5FECEAD37E, 0x07, 0x4662BAB65BB, 0x976AD673A0F6DD, 0x39EDF5B499B4}, + }, + { + []byte{0xB0, 0x15, 0x5C, 0x01, 0xE3, 0x0E, 0x1A, 0xB6, 0x97, 0x04, 0x07, 0x63, 0xBB, 0x29, 0x2D, 0xB7, 0x80, 0xD3, 0x50, 0x42, 0x6B, 0xA3, 0xCA, 0x7C, 0x71, 0x40, 0xF2, 0xB6, 0x51, 0x89, 0x52, 0x84, 0xF1, 0x93, 0xAB, 0xAF, 0xC9, 0x9D, 0x1C, 0x3D, 0x43, 0xEC, 0xB8, 0x6F, 0xA1, 0xCE, 0xAB, 0x94, 0xF2, 0xF0, 0xF3, 0x14, 0xE9, 0x41, 0x02, 0x9B, 0xA6, 0x42, 0x83, 0xB7, 0xF6, 0x58, 0x60, 0x60, 0xDD, 0xD1, 0xC3, 0xCF, 0xCA, 0x4C, 0x74, 0xDC, 0x6D, 0xEF, 0x7F, 0x8C, 0x9F, 0xE9, 0xE4, 0x1F, 0xDF, 0xCD, 0x59, 0xAC, 0x70, 0x43, 0xE0, 0xF9, 0x5E, 0xB6, 0x8D, 0x10, 0x5F, 0xE0, 0x4B, 0x85, 0xD8, 0xCF, 0x59, 0xE3}, + []uint{55, 6, 54, 27, 46, 46, 43, 11, 7, 18, 16, 23, 23, 60, 12, 9, 54, 61, 39, 31, 54, 34, 40, 31}, + []uint64{0x580AAE00F1870D, 0x16, 0x34B8203B1DD949, 0x36DE034, 0x350426BA3CA7, 0x31C503CAD946, 0x12A509E3275, 0x3AF, 0x64, 0x33A38, 0x7A87, 0x6CB86F, 0x50E755, 0xCA7978798A74A08, 0x14D, 0x1A6, 0x10A0EDFD961818, 0x6EE8E1E7E5263A6, 0x71B7BDFE32, 0x3FD3C83F, 0x2FE6ACD63821F0, 0x1F2BD6D1A, 0x20BFC0970B, 0x58CF59E3}, + }, + { + []byte{0x7F, 0x0D, 0xFB, 0x8B, 0x93, 0x2C, 0x51, 0xC3, 0xAE, 0x2F, 0xBE, 0xD2, 0xAD, 0xFE, 0x95, 0x3F, 0xEB, 0x41, 0xF1, 0x59, 0xDD, 0xBA, 0x1F, 0x31, 0xC1, 0x5C, 0x82, 0x78, 0x93, 0x3A, 0x53, 0x99, 0x29, 0x1F, 0x3E, 0x5B, 0xB5, 0x69, 0x25, 0x22, 0x34, 0xF3, 0x7C, 0x3A, 0xA8, 0xB6, 0xE4, 0xE3, 0x5E, 0xD5, 0x9D, 0xFB, 0x69, 0x49, 0xD0, 0x9F, 0x01, 0x4C, 0x03, 0x88, 0xD2, 0x82, 0x6E, 0xC7, 0x85, 0x35, 0x4D, 0xE7, 0x19, 0xA6, 0xF8, 0x1D, 0x3D, 0xDD, 0xC7, 0xE1, 0x89, 0x83, 0x69, 0xD9, 0xA3, 0x30, 0x25, 0x42, 0x95, 0x6E, 0x02, 0xFE, 0xC9, 0xDC, 0x6E, 0xD8, 0x58, 0xBB, 0xB6, 0x61, 0x48, 0xB9, 0x6D, 0xF8}, + []uint{57, 11, 41, 47, 48, 3, 18, 37, 2, 23, 46, 10, 41, 43, 30, 51, 30, 22, 54, 24, 59, 11, 30, 21, 6, 34, 1}, + []uint64{0xFE1BF7172658A3, 0x43A, 0x1C5F7DA55BF, 0x6953FEB41F15, 0x9DDBA1F31C15, 0x06, 0x104F1, 0x4CE94E64A, 0x01, 0xF9F2D, 0x36AD24A4469E, 0x1BE, 0x3AA8B6E4E3, 0x2F6ACEFDB4A, 0x13A13E02, 0x4C0388D2826EC, 0x1E14D537, 0x2719A6, 0x3E074F7771F862, 0x60DA76, 0x346604A852ADC05, 0x7EC, 0x2771BB61, 0xC5DDB, 0x0C, 0xA45CB6FC, 0x00}, + }, + { + []byte{0xA8, 0x79, 0x2E, 0x69, 0x7C, 0xA9, 0x38, 0x1C, 0xBB, 0x72, 0x13, 0x55, 0xA9, 0x2B, 0xC6, 0xCD, 0xB9, 0x05, 0xAA, 0x90, 0xE3, 0x86, 0x2A, 0xB2, 0xD4, 0x18, 0x0C, 0xC0, 0x79, 0x2E, 0xFB, 0x29, 0x9B, 0x65, 0xA7, 0xB5, 0x06, 0x46, 0xD4, 0x1C, 0x7C, 0x4D, 0xDA, 0x29, 0x68, 0x83, 0xA6, 0x66, 0xFE, 0xA8, 0x96, 0x52, 0xAF, 0x88, 0x38, 0x25, 0x15, 0x51, 0xE0, 0x46, 0x29, 0x70, 0x7D, 0x74, 0xC9, 0xA2, 0xDD, 0x9C, 0xEA, 0x0D, 0x42, 0xFA, 0x50, 0x6A, 0x52, 0x77, 0x24, 0xC8, 0x37, 0x40, 0x45, 0x01, 0xEC, 0x9C, 0xDE, 0x91, 0x94, 0x23, 0xBD, 0x0A, 0xFA, 0x68, 0x39, 0xF3, 0x4F, 0xAD, 0x4E, 0xF4, 0xEB, 0x35}, + []uint{16, 23, 38, 47, 6, 47, 2, 15, 21, 1, 13, 37, 17, 45, 6, 40, 31, 56, 59, 25, 27, 12, 14, 60, 64, 35, 10, 33}, + []uint64{0xA879, 0x1734BE, 0x152703976E, 0x21355A92BC6C, 0x36, 0x720B5521C70C, 0x01, 0x2ACB, 0xA0C06, 0x00, 0x180F, 0x4BBECA66D, 0x12D3D, 0x150646D41C7C, 0x13, 0x768A5A20E9, 0x4CDFD512, 0xCA55F10704A2AA, 0x1E04629707D74C9, 0x145BB39, 0x6A0D42F, 0xA50, 0x1A94, 0x9DC9320DD011407, 0xB2737A46508EF42B, 0x74D073E69, 0x3D6, 0x14EF4EB35}, + }, + { + []byte{0x79, 0xD5, 0x86, 0x52, 0x20, 0xE8, 0xC2, 0x30, 0x48, 0xBC, 0x55, 0xC3, 0x16, 0x7C, 0xAA, 0xED, 0x94, 0xB2, 0xEC, 0x72, 0xB6, 0x37, 0x2D, 0xDC, 0xC5, 0x32, 0xD7, 0x37, 0xEC, 0x9F, 0xBF, 0x05, 0x83, 0x87, 0x82, 0x02, 0xE9, 0xDF, 0x9A, 0x27, 0x20, 0x18, 0xC4, 0x63, 0x9B, 0x2B, 0x74, 0xFB, 0xC9, 0xBD, 0xE2, 0xF0, 0xD6, 0xB7, 0x88, 0x28, 0x2B, 0x8F, 0xD1, 0xE5, 0xE0, 0xFC, 0x54, 0x53, 0xC6, 0x3C, 0xD5, 0xC6, 0xCC, 0x18, 0x12, 0xDC, 0xAB, 0x57, 0x56, 0x3A, 0xCC, 0x3A, 0x42, 0x65, 0x30, 0x02, 0xB5, 0x17, 0x69, 0x18, 0x86, 0xB0, 0xC0, 0xE8, 0x65, 0x96, 0xBB, 0x24, 0x42, 0xCA, 0x60, 0x74, 0x3E, 0x4E}, + []uint{49, 12, 22, 14, 60, 8, 49, 19, 64, 29, 5, 37, 17, 56, 40, 3, 4, 19, 29, 45, 6, 51, 14, 10, 53, 48, 37}, + []uint64{0xF3AB0CA441D1, 0x846, 0x245E2, 0x2B86, 0x2CF955DB2965D8E, 0x56, 0x18DCB77314CB5, 0x66FD9, 0x3F7E0B070F0405D3, 0x17E689C8, 0x00, 0x18C4639B2B, 0xE9F7, 0x937BC5E1AD6F10, 0x50571FA3CB, 0x06, 0x00, 0x7E2A2, 0x13C63CD5, 0x18D983025B95, 0x1A, 0x5D58EB30E9099, 0x1300, 0xAD, 0x8BB48C4358607, 0x432CB5D92216, 0xA60743E4E}, + }, + { + []byte{0x1D, 0x25, 0xA6, 0x20, 0x3A, 0x6C, 0x00, 0xE9, 0xD1, 0x02, 0xD0, 0xA1, 0xB6, 0xEF, 0xA5, 0x89, 0x34, 0xCC, 0x79, 0x82, 0x7E, 0x3A, 0x46, 0x57, 0xF5, 0x83, 0x6E, 0xAE, 0xF2, 0x43, 0x25, 0xA5, 0xD5, 0xF0, 0x9B, 0x8E, 0xDD, 0x0F, 0xCC, 0x8E, 0x0C, 0xD7, 0xB1, 0xC3, 0x56, 0x9F, 0xA0, 0x2A, 0x9D, 0x0C, 0x37, 0xD1, 0xBD, 0x13, 0x67, 0x31, 0xD4, 0x84, 0xD3, 0xE9, 0xCE, 0xB2, 0x34, 0xDB, 0xFC, 0x31, 0x75, 0xCD, 0x70, 0x08, 0x4C, 0x71, 0x75, 0xDD, 0x8F, 0x3F, 0xD5, 0xFA, 0xC3, 0x24, 0xE8, 0xC0, 0x10, 0x9B, 0x7C, 0xF9, 0xCA, 0xE9, 0x54, 0x21, 0xD8, 0xE3, 0x8D, 0x31, 0xA3, 0x39, 0xA5, 0xC4, 0xF3, 0xFC}, + []uint{32, 13, 52, 46, 22, 7, 13, 37, 50, 64, 63, 60, 52, 42, 57, 35, 35, 54, 35, 31}, + []uint64{0x1D25A620, 0x74D, 0x801D3A205A143, 0x1B77D2C49A66, 0xF304F, 0x63, 0x148C, 0x15FD60DBAB, 0x2F24325A5D5F0, 0x9B8EDD0FCC8E0CD7, 0x58E1AB4FD0154E86, 0x1BE8DE89B398EA4, 0x269F4E7591A6D, 0x3F862EB9AE0, 0x2131C5D7763CFF, 0x2BF58649D, 0xC0109B7C, 0x3E72BA55087638, 0x71A634673, 0x25C4F3FC}, + }, + { + []byte{0x68, 0x5A, 0x69, 0x38, 0xFD, 0x40, 0x54, 0xB3, 0x83, 0x36, 0x19, 0x7E, 0x49, 0x98, 0x71, 0xA3, 0x2A, 0x61, 0xF7, 0xE7, 0x8A, 0x7E, 0x6B, 0x1A, 0xB8, 0x98, 0x47, 0x80, 0x1A, 0x45, 0x10, 0xDC, 0xD7, 0x19, 0x89, 0xBF, 0xF0, 0x6A, 0x58, 0xFF, 0x62, 0xE4, 0xEC, 0x13, 0x4C, 0x50, 0x88, 0xFA, 0xFC, 0xA6, 0x12, 0xA6, 0xB3, 0xA4, 0xDB, 0x1E, 0x4C, 0xA4, 0x6D, 0x6B, 0xA2, 0xE1, 0xB7, 0x38, 0x47, 0x98, 0x64, 0xF9, 0xBD, 0xC2, 0x52, 0xB9, 0x85, 0x5A, 0xAA, 0x08, 0x67, 0xC9, 0xA6, 0x10, 0x6C, 0x9F, 0xB1, 0x85, 0xAB, 0x93, 0x8E, 0x27, 0x9E, 0x5A, 0xD8, 0x4B, 0xAB, 0x1D, 0x07, 0x24, 0x5A, 0x92, 0xE8, 0xFE}, + []uint{53, 49, 47, 35, 53, 37, 62, 62, 2, 44, 23, 43, 10, 31, 49, 39, 56, 4, 58, 43}, + []uint64{0xD0B4D271FA80A, 0x12CE0CD865F92, 0x330E34654C3E, 0x7E78A7E6B, 0x3571308F00348, 0x1443735C66, 0x9BFF06A58FF62E4, 0x3B04D314223EBF29, 0x02, 0x12A6B3A4DB1, 0x726523, 0x35AE8B86DCE, 0x47, 0x4C327CDE, 0x1C252B9855AAA, 0x433E4D308, 0x364FD8C2D5C9C7, 0x01, 0xF3CB5B097563A0, 0x7245A92E8FE}, + }, + { + []byte{0x81, 0xB5, 0xA2, 0x65, 0x98, 0x2C, 0x6B, 0x6B, 0xC7, 0x8F, 0x17, 0x8A, 0xE9, 0x66, 0xF9, 0x70, 0x5D, 0xCE, 0x2F, 0x35, 0xC3, 0xB7, 0xEC, 0xB8, 0x3F, 0xA1, 0xB8, 0x6F, 0x08, 0x31, 0xAB, 0x93, 0x9E, 0x7C, 0xFB, 0x43, 0x1C, 0xA4, 0xB6, 0xD1, 0x89, 0xD3, 0x4F, 0xD4, 0x2A, 0x89, 0x72, 0x13, 0x50, 0xB9, 0x02, 0xC7, 0x6A, 0x98, 0xE0, 0x3F, 0x75, 0x33, 0x61, 0x36, 0x8F, 0x7B, 0xC7, 0x8F, 0x7F, 0x78, 0x21, 0xC1, 0xD1, 0x73, 0x7D, 0xCE, 0x8C, 0xD6, 0xB3, 0x20, 0x9C, 0xAF, 0x5A, 0x57, 0x8B, 0xE5, 0xC6, 0xA4, 0xCA, 0x76, 0x1E, 0x50, 0x20, 0x43, 0x87, 0xE4, 0xCE, 0x0F, 0x89, 0x21, 0x64, 0x65, 0x9B, 0xD3}, + []uint{1, 15, 30, 2, 9, 48, 40, 45, 20, 27, 29, 1, 13, 35, 47, 59, 24, 38, 50, 39, 37, 8, 54, 46, 14, 58, 11}, + []uint64{0x01, 0x1B5, 0x2899660B, 0x00, 0xD6, 0xD78F1E2F15D2, 0xCDF2E0BB9C, 0xBCD70EDFB2E, 0xFE86, 0x70DE106, 0x6AE4E79, 0x01, 0x1CFB, 0x218E525B6, 0x46274D3F50AA, 0x12E426A172058ED, 0x531C07, 0x3BA99B09B4, 0x1EF78F1EFEF04, 0x1C1D1737DC, 0x1D19AD6641, 0x39, 0x17AD2BC5F2E352, 0x194EC3CA0408, 0x1C3F, 0x99C1F1242C8CB3, 0x3D3}, + }, + { + []byte{0x71, 0xF3, 0xAA, 0x6E, 0x84, 0xDF, 0xE6, 0x77, 0xA6, 0xC2, 0xAE, 0x13, 0x8C, 0x9E, 0xDF, 0xFA, 0x68, 0x97, 0x18, 0x10, 0x2D, 0x29, 0xE9, 0x8A, 0xBF, 0xA3, 0xB6, 0x34, 0x93, 0xA0, 0x81, 0xE6, 0xBB, 0xB2, 0x2B, 0xCC, 0x6C, 0x2E, 0xD2, 0xD0, 0x97, 0xB8, 0x7B, 0x41, 0x3D, 0x11, 0xFB, 0xFC, 0xE0, 0xE3, 0x5B, 0x75, 0xE4, 0x6A, 0x28, 0x88, 0x0A, 0x2D, 0x41, 0x6B, 0x00, 0xE3, 0xB8, 0xA1, 0x98, 0x45, 0x63, 0xD1, 0x89, 0xF1, 0xDD, 0xE5, 0xAB, 0xB8, 0x07, 0xA1, 0x7A, 0xDF, 0xA6, 0xF4, 0x89, 0x6D, 0x41, 0x90, 0xA2, 0x3B, 0x79, 0x07, 0xA6, 0xFE, 0xB5, 0x19, 0x32, 0x0D, 0x7E, 0x8F, 0x37, 0x49, 0xB1, 0x1F}, + []uint{18, 39, 32, 4, 63, 43, 56, 45, 45, 5, 15, 29, 6, 33, 52, 33, 44, 44, 21, 32, 57, 45, 23, 16}, + []uint64{0x1C7CE, 0x54DD09BFCC, 0xEF4D855C, 0x02, 0x38C9EDFFA6897181, 0x1694F4C55F, 0xD1DB1A49D040F3, 0xBBB22BCC6C2, 0x1DA5A12F70F6, 0x10, 0x27A2, 0x7EFF383, 0x23, 0xB6EBC8D4, 0x5110145A82D60, 0x38EE2866, 0x1158F4627C7, 0x7796AEE01E8, 0xBD6FD, 0x37A44B6A, 0x190A23B7907A6F, 0x1D6A32641AFD, 0xF3749, 0xB11F}, + }, + { + []byte{0xAC, 0x14, 0xB0, 0x1B, 0x9B, 0xF6, 0x27, 0xD0, 0xB0, 0xB4, 0x78, 0xD8, 0x72, 0x2F, 0x7B, 0x35, 0x27, 0x93, 0xD7, 0x03, 0xBE, 0x3D, 0x08, 0x38, 0xA4, 0x4F, 0x77, 0xC6, 0x93, 0x8C, 0xDF, 0x54, 0x7B, 0x25, 0x14, 0xDB, 0xF4, 0x3A, 0x1B, 0xB0, 0x20, 0x0C, 0x7A, 0x40, 0x15, 0xE4, 0xCE, 0x72, 0x9E, 0x50, 0x05, 0x85, 0xFE, 0x5A, 0xDA, 0x51, 0xC1, 0x10, 0x35, 0xB6, 0x93, 0x7D, 0xE1, 0x29, 0x8A, 0xA1, 0x64, 0xCB, 0xD7, 0x43, 0x53, 0x6F, 0x1A, 0x31, 0x83, 0x1E, 0x90, 0xFF, 0x5D, 0x99, 0xF8, 0xF1, 0xB4, 0xBD, 0x91, 0xEF, 0xF3, 0xB7, 0x70, 0x71, 0x1D, 0x09, 0x4B, 0x8A, 0x94, 0x47, 0x86, 0xDF, 0xB9, 0x73}, + []uint{63, 40, 4, 14, 10, 41, 30, 19, 11, 22, 20, 18, 17, 26, 63, 12, 39, 8, 12, 7, 23, 64, 2, 57, 48, 5, 26, 18, 31, 29, 21}, + []uint64{0x560A580DCDFB13E8, 0x585A3C6C39, 0x01, 0x1EF6, 0x1A9, 0x793D703BE3, 0x3420E291, 0x1EEF8, 0x693, 0x2337D5, 0x1EC94, 0x14DBF, 0x8743, 0x1D81006, 0x1E900579339CA794, 0x16, 0xBFCB5B4A3, 0x82, 0x206, 0x5B, 0x349BEF, 0x94C550B265EBA1A, 0x02, 0xDBC68C60C7A43F, 0xD7667E3C6D2F, 0x0C, 0x23DFE76, 0x3B838, 0x474252E2, 0x14A23C36, 0x1FB973}, + }, + { + []byte{0xE4, 0x50, 0xCC, 0x46, 0xE5, 0x6C, 0xB4, 0x5E, 0x7D, 0x27, 0x6A, 0x12, 0x75, 0x27, 0x71, 0xB2, 0x5F, 0xCD, 0x62, 0x8E, 0x39, 0x4C, 0x93, 0xAB, 0x74, 0x84, 0x7D, 0x73, 0x72, 0x69, 0x2B, 0x3E, 0x09, 0x98, 0x75, 0xCE, 0xEA, 0xCE, 0x41, 0x90, 0x1B, 0x5E, 0x43, 0x77, 0x9F, 0x03, 0x45, 0xD5, 0x5C, 0xE2, 0xED, 0x27, 0xF9, 0x3C, 0x74, 0x7B, 0x43, 0x46, 0x74, 0x48, 0xBA, 0x17, 0x38, 0xD5, 0x20, 0xE5, 0xA0, 0xF4, 0xF6, 0xF2, 0xDD, 0x35, 0x11, 0xDC, 0x2C, 0x05, 0x24, 0xF9, 0x32, 0x87, 0xF2, 0x46, 0x5C, 0x38, 0x5F, 0x30, 0xD5, 0xE5, 0xCA, 0x7B, 0xB2, 0x84, 0x2B, 0x52, 0x79, 0x1E, 0xA2, 0xA7, 0xF2, 0xE5}, + []uint{36, 51, 25, 30, 33, 48, 1, 40, 47, 24, 52, 35, 50, 19, 46, 35, 49, 42, 43, 25, 3, 46, 20}, + []uint64{0xE450CC46E, 0x2B65A2F3E93B5, 0x127527, 0x1C6C97F3, 0xB1471CA6, 0x49D5BA423EB9, 0x01, 0x72692B3E09, 0x4C3AE7756720, 0xC80DAF, 0x21BBCF81A2EAA, 0x738BB49FE, 0x13C747B434674, 0x245D0, 0x2E71AA41CB41, 0x74F6F2DD3, 0xA23B8580A49F, 0x9943F9232E, 0xE17CC35797, 0x53DD94, 0x01, 0x2B52791EA2A, 0x7F2E5}, + }, + { + []byte{0x62, 0x1F, 0x77, 0x6B, 0x70, 0x29, 0x95, 0x1E, 0xE8, 0xF4, 0xFB, 0x70, 0x7C, 0x70, 0xB6, 0x51, 0xF6, 0xD5, 0xD8, 0x16, 0x6D, 0xB0, 0x7D, 0x7F, 0xE3, 0x3A, 0x1C, 0x01, 0x3C, 0x72, 0xD5, 0x19, 0x42, 0x22, 0xE9, 0xCB, 0x6E, 0xF5, 0x4F, 0x63, 0xAA, 0x74, 0xDA, 0x86, 0x06, 0xB0, 0x0E, 0x2B, 0xF1, 0xD8, 0x12, 0x3D, 0x78, 0xCD, 0x60, 0x4F, 0xE4, 0x24, 0x18, 0xE9, 0xB2, 0x8A, 0xAC, 0xB4, 0x95, 0x53, 0x1D, 0x31, 0xDF, 0x2C, 0x24, 0x59, 0x32, 0x26, 0xFC, 0xDE, 0x18, 0x5A, 0x10, 0x25, 0x66, 0xDF, 0x5D, 0xFB, 0x4F, 0xC6, 0x40, 0xBC, 0x81, 0x51, 0xB1, 0xC5, 0xC7, 0xED, 0x68, 0xEF, 0xEF, 0x03, 0xD1, 0x3B}, + []uint{43, 47, 64, 7, 19, 26, 16, 29, 30, 10, 17, 28, 35, 31, 49, 46, 25, 43, 18, 14, 24, 58, 17, 2, 22, 22, 18, 40}, + []uint64{0x310FBBB5B81, 0x26547BA3D3ED, 0xC1F1C2D947DB5760, 0x2C, 0x6DB07, 0x35FF8CE, 0x8700, 0x9E396A8, 0x328445D3, 0x25B, 0xEF54, 0xF63AA74, 0x6D4303580, 0x38AFC760, 0x91EBC66B027F, 0x84831D36515, 0xB2D255, 0x263A63BE584, 0x22C99, 0x4DF, 0x9BC30B, 0x10812B36FAEFDA7, 0x1C640, 0x02, 0x3C8151, 0x2C7171, 0x3ED68, 0xEFEF03D13B}, + }, + { + []byte{0xCD, 0x82, 0x19, 0x41, 0x84, 0x11, 0x69, 0x22, 0x42, 0x4E, 0xA2, 0x7B, 0xF6, 0x52, 0xC1, 0x02, 0x54, 0x2D, 0xFB, 0x27, 0x0E, 0x8C, 0x34, 0x0A, 0xCE, 0x0E, 0x4F, 0x05, 0x94, 0x9D, 0xF2, 0x5A, 0xCB, 0x48, 0x05, 0xA3, 0x38, 0x41, 0xDE, 0x45, 0x83, 0x4F, 0x0B, 0xBF, 0x11, 0x7A, 0x01, 0x5A, 0x30, 0x93, 0x0F, 0x18, 0x86, 0x95, 0x95, 0x7E, 0x3C, 0x73, 0xB4, 0x45, 0x5F, 0xE0, 0x3B, 0xA1, 0xCA, 0x5B, 0x6E, 0x12, 0xCE, 0xF7, 0xC1, 0x48, 0x89, 0xFA, 0x79, 0x8B, 0xD2, 0x7D, 0xCC, 0x75, 0x5C, 0xF4, 0x8D, 0x72, 0x54, 0xD5, 0xED, 0x47, 0x8E, 0x6E, 0x0E, 0x18, 0x0F, 0x1B, 0xB6, 0xA3, 0x62, 0x32, 0xB2, 0xB3}, + []uint{43, 57, 38, 50, 12, 62, 63, 24, 14, 12, 14, 42, 6, 17, 10, 51, 53, 7, 58, 1, 4, 20, 14, 29, 35, 30, 5, 29}, + []uint64{0x66C10CA0C20, 0x116922424EA27BF, 0x194B040950, 0x2DFB270E8C340, 0xACE, 0x393C165277C96B2, 0x6900B467083BC8B0, 0x69E177, 0x388B, 0xD00, 0x2B46, 0x49878C434A, 0x32, 0x15F8F, 0x73, 0x5A22AFF01DD0E, 0xA5B6E12CEF7C1, 0x24, 0x113F4F317A4FB98, 0x01, 0x0D, 0x573D2, 0xD72, 0xA9ABDA8, 0x78E6E0E18, 0x3C6EDA8, 0x1B, 0x232B2B3}, + }, + { + []byte{0x34, 0xFA, 0x8F, 0x98, 0x59, 0x1E, 0xE3, 0x3E, 0x39, 0xCE, 0x5F, 0x3C, 0xBB, 0xEF, 0xBB, 0xE9, 0xE2, 0xA0, 0x31, 0x73, 0x67, 0x50, 0x0C, 0xF0, 0x94, 0xCC, 0x5F, 0x23, 0xF9, 0x86, 0x9D, 0xF8, 0xBD, 0xF7, 0xB9, 0x91, 0xB7, 0x06, 0xF5, 0xE0, 0x50, 0x6C, 0x2C, 0xF9, 0xE6, 0xE8, 0x2B, 0xDC, 0xBA, 0xBC, 0x02, 0x90, 0xBF, 0xF7, 0x63, 0x91, 0xF3, 0x4D, 0xAF, 0x53, 0x8D, 0xAC, 0xAE, 0xC3, 0x44, 0x24, 0xE6, 0x90, 0xCC, 0xB0, 0x16, 0x12, 0x6E, 0x35, 0x0B, 0x77, 0x2C, 0xF1, 0xF5, 0xE3, 0xA1, 0x82, 0x58, 0x2C, 0x0E, 0x64, 0xCC, 0x98, 0x39, 0x5D, 0x92, 0x91, 0xBE, 0x50, 0xC7, 0x27, 0x0C, 0x12, 0xF4, 0x0E}, + []uint{23, 44, 62, 40, 13, 51, 6, 42, 31, 59, 29, 37, 37, 23, 23, 41, 64, 42, 44, 8, 63, 18}, + []uint64{0x1A7D47, 0xCC2C8F719F1, 0x339CBE7977DF77D3, 0xC54062E6CE, 0x1403, 0x1E12998BE47F3, 0x03, 0x13BF17BEF73, 0x11B706F5, 0x702836167CF3741, 0xBDCBABC, 0x5217FEEC, 0xE47CD36BD, 0x271B59, 0x2EC344, 0x49CD219960, 0x2C24DC6A16EE59E3, 0x3AF1D0C12C1, 0x60732664C1C, 0xAE, 0x64A46F9431C9C304, 0x2F40E}, + }, + { + []byte{0xA8, 0xC6, 0x63, 0x71, 0x15, 0xAF, 0x2F, 0x1D, 0x44, 0x45, 0xC8, 0xCC, 0x64, 0xED, 0x7E, 0xB7, 0xC5, 0x8A, 0xC1, 0x2A, 0xF9, 0xA0, 0x9E, 0x6B, 0xD2, 0x1E, 0xC7, 0x83, 0x6C, 0x44, 0x73, 0x04, 0x5F, 0xD8, 0x31, 0x89, 0xCF, 0xA2, 0x2C, 0xDE, 0xCF, 0x5F, 0xF3, 0xE6, 0xCF, 0x88, 0x7A, 0xE9, 0x3F, 0xD2, 0x23, 0x25, 0x9D, 0x4F, 0xF4, 0xC3, 0x39, 0xB9, 0x60, 0xCA, 0x31, 0xD2, 0xFC, 0xF3, 0xC1, 0x29, 0xC8, 0x5C, 0x05, 0x95, 0xA1, 0xB7, 0x72, 0xC9, 0x8B, 0x85, 0xFD, 0x18, 0xCB, 0x05, 0x9B, 0x11, 0xD8, 0xEA, 0xF6, 0x4E, 0xB2, 0xDA, 0x35, 0xEC, 0xDD, 0x75, 0x3E, 0xCC, 0xCE, 0x5F, 0x3A, 0xFB, 0xC3, 0x01}, + []uint{2, 38, 8, 59, 17, 7, 23, 38, 34, 52, 45, 60, 36, 45, 22, 55, 6, 9, 26, 11, 37, 37, 19, 56, 27, 31}, + []uint64{0x02, 0x28C6637115, 0xAF, 0x178EA222E466327, 0xD7EB, 0x3E, 0x162B04, 0x2AF9A09E6B, 0x3487B1E0D, 0xB111CC117F60C, 0xC4E7D1166F6, 0x7AFF9F367C43D74, 0x9FE91192C, 0x1D4FF4C339B9, 0x18328C, 0x3A5F9E7825390B, 0x20, 0x59, 0x1686DDC, 0x593, 0x2E17F4632, 0x182CD88EC7, 0x2BD93, 0xACB68D7B375D4F, 0x5999CBE, 0x3AFBC301}, + }, + { + []byte{0x2B, 0x17, 0x50, 0xEA, 0x8B, 0xD1, 0x09, 0x00, 0x9C, 0x2E, 0x3C, 0x07, 0x3B, 0x0D, 0xFC, 0x07, 0xAA, 0xD1, 0x55, 0x1E, 0x37, 0x40, 0xE0, 0xBB, 0xC5, 0x38, 0x40, 0x64, 0xF5, 0x3C, 0x0B, 0x0B, 0x37, 0x76, 0x43, 0xDA, 0x7F, 0x01, 0x63, 0x03, 0xA3, 0xF9, 0xD8, 0x06, 0x93, 0x4C, 0xF0, 0x01, 0xE6, 0x86, 0xAA, 0x94, 0x35, 0xD5, 0xB3, 0xA2, 0x1D, 0xB6, 0x2A, 0x40, 0x83, 0xFE, 0x63, 0x2E, 0x40, 0x81, 0x42, 0x11, 0xC4, 0x61, 0x9A, 0xAF, 0x08, 0x0D, 0x36, 0x20, 0x32, 0x67, 0xEA, 0x49, 0xF4, 0x26, 0xA5, 0x8D, 0xF4, 0xFD, 0xAC, 0x15, 0xB2, 0x5B, 0x97, 0x49, 0xC2, 0xDE, 0x1D, 0x79, 0x43, 0xD8, 0x20, 0x63}, + []uint{58, 27, 18, 25, 4, 25, 47, 9, 22, 36, 4, 53, 13, 53, 15, 15, 2, 54, 34, 62, 62, 37, 41, 34, 50}, + []uint64{0xAC5D43AA2F4424, 0x1385C7, 0x2039D, 0x10DFC07, 0x0A, 0x15A2AA3, 0x63740E0BBC53, 0x108, 0x327A9, 0xE05859BBB, 0x02, 0x3DA7F016303A3, 0x1F3B, 0x1A4D33C0079A, 0xD55, 0x1435, 0x03, 0x15B3A21DB62A40, 0x20FF98CB9, 0x814211C4619AAF, 0x2034D880C99FA92, 0xFA1352C6F, 0x14FDAC15B25, 0x2E5D270B7, 0x21D7943D82063}, + }, + { + []byte{0x93, 0x6A, 0xA0, 0xA2, 0xA9, 0x33, 0x42, 0x9C, 0x74, 0x54, 0x5E, 0x06, 0x0E, 0xE2, 0x05, 0x9E, 0x8F, 0xCC, 0x56, 0xBC, 0x59, 0xBD, 0xAF, 0x76, 0x71, 0x9C, 0x06, 0x88, 0x42, 0x5A, 0xBC, 0x12, 0x29, 0x1C, 0x48, 0x11, 0x96, 0xA0, 0x78, 0x15, 0x6D, 0xDE, 0x8F, 0xF4, 0xBC, 0x9D, 0xB1, 0x4B, 0xEB, 0xDC, 0x9F, 0x6B, 0x70, 0xE3, 0x77, 0x32, 0x42, 0xAE, 0x35, 0x82, 0x8A, 0xD9, 0x1E, 0x26, 0x33, 0xA2, 0x51, 0xD9, 0x2B, 0xAE, 0x07, 0xBF, 0x8B, 0x14, 0x8D, 0x53, 0xC5, 0xFF, 0xE4, 0x27, 0x9F, 0x10, 0xC3, 0x2B, 0x4B, 0x90, 0x9F, 0xB2, 0x99, 0x38, 0xA7, 0xB4, 0xD2, 0xA4, 0x4B, 0xF3, 0x5D, 0x52, 0x94, 0xD2}, + []uint{29, 7, 49, 40, 43, 23, 4, 17, 13, 29, 59, 63, 8, 25, 2, 28, 49, 27, 50, 14, 6, 5, 22, 20, 3, 35, 32, 19, 12, 48, 19}, + []uint64{0x126D5414, 0x2A, 0x12668538E8A8B, 0xC0C1DC40B3, 0x68FCC56BC59, 0x5ED7BB, 0x03, 0x119C0, 0xD10, 0x1096AF04, 0x4523890232D40F0, 0x156DDE8FF4BC9DB1, 0x4B, 0x1D7B93E, 0x03, 0x5B871BB, 0x13242AE35828A, 0x6C8F131, 0x2744A3B2575C0, 0x3DFC, 0x16, 0x05, 0x8D53C, 0x5FFE4, 0x01, 0x1E7C430CA, 0xD2E427EC, 0x53271, 0x4F6, 0x9A54897E6BAA, 0x294D2}, + }, + { + []byte{0x41, 0x7C, 0x00, 0xB7, 0x0A, 0x88, 0x91, 0x50, 0x11, 0x1E, 0x2D, 0xDC, 0xA6, 0xF0, 0x3D, 0xDE, 0xC4, 0x37, 0xD7, 0x61, 0x65, 0x8D, 0x2B, 0x3A, 0x94, 0xAB, 0x46, 0xC3, 0x9D, 0xD1, 0x8B, 0x38, 0x7B, 0x5F, 0x17, 0xB5, 0xFF, 0x73, 0xE1, 0xAB, 0x17, 0x34, 0x4B, 0x0F, 0x5B, 0xB7, 0x84, 0x94, 0x06, 0xB1, 0x01, 0x15, 0x57, 0x4C, 0x77, 0xBF, 0xF8, 0x31, 0x6C, 0x2D, 0xDB, 0x80, 0x94, 0x7F, 0x70, 0xF5, 0x9F, 0xB9, 0xAF, 0xEB, 0x79, 0x15, 0xD5, 0xCF, 0x69, 0x55, 0x39, 0x69, 0xF0, 0x2E, 0xEB, 0x7F, 0x98, 0x8F, 0x9D, 0x51, 0x8B, 0x5F, 0xC0, 0xD1, 0x3F, 0xAB, 0xA7, 0x6B, 0x27, 0xED, 0x5C, 0x81, 0x42, 0x87}, + []uint{43, 57, 36, 34, 17, 2, 53, 49, 40, 39, 30, 24, 9, 35, 21, 28, 19, 53, 4, 53, 57, 53, 12, 22, 10}, + []uint64{0x20BE005B854, 0x89150111E2DDCA, 0x6F03DDEC4, 0xDF5D8596, 0x6959, 0x03, 0xA52AD1B0E7746, 0x59C3DAF8BDAF, 0xFB9F0D58B9, 0x512C3D6EDE, 0x49406B1, 0x11557, 0x98, 0x77BFF8316, 0x185BB7, 0x128FEE, 0xF59F, 0x1735FD6F22BAB9, 0x0E, 0x1A554E5A7C0BBA, 0x1BFCC47CEA8C5AF, 0x1C0D13FABA76B2, 0x7ED, 0x172050, 0x287}, + }, + { + []byte{0x62, 0x94, 0x17, 0x81, 0xEB, 0x57, 0xC8, 0x95, 0x9F, 0x6D, 0x5E, 0xB5, 0xB7, 0x75, 0xE0, 0x67, 0xB5, 0x74, 0xBB, 0x0D, 0xCE, 0xA3, 0x1A, 0x2A, 0xC7, 0x51, 0x0E, 0x1B, 0x08, 0x9E, 0xA2, 0xFC, 0x01, 0x20, 0xBF, 0xA3, 0x6B, 0xAC, 0x4B, 0x5B, 0x25, 0x35, 0xE1, 0x2A, 0x6F, 0xD2, 0x27, 0x6C, 0x29, 0x3C, 0x8A, 0x7E, 0x1C, 0x0D, 0xCD, 0x6A, 0x0B, 0x51, 0x7F, 0x12, 0x4D, 0xE8, 0xAC, 0x16, 0x18, 0x83, 0x16, 0xFD, 0xFE, 0x01, 0xA1, 0xC7, 0xF9, 0x6A, 0xBB, 0x48, 0x76, 0x98, 0xF0, 0x09, 0xE3, 0x9A, 0x2F, 0x83, 0xDF, 0x55, 0x4D, 0x41, 0xDC, 0x6F, 0x39, 0xA4, 0xEA, 0x6D, 0x3E, 0x4D, 0xD3, 0xE7, 0x8F, 0xFB}, + []uint{20, 19, 52, 17, 9, 10, 40, 5, 57, 24, 46, 25, 24, 11, 18, 13, 24, 39, 8, 27, 34, 11, 53, 48, 36, 44, 36, 50}, + []uint64{0x62941, 0x3C0F5, 0xABE44ACFB6AF5, 0x15B77, 0xBC, 0x33, 0xDABA5D86E7, 0x0A, 0x634558EA21C361, 0x13D45F, 0x200905FD1B5D, 0xC4B5B2, 0x535E12, 0x537, 0x3A44E, 0x1B0A, 0x4F229F, 0x4381B9AD41, 0x6A, 0x17F124D, 0x3A2B05862, 0x62, 0x1BF7F806871FE5, 0xAAED21DA63C0, 0x278E68BE0, 0xF7D55350771, 0xBCE693A9B, 0x13E4DD3E78FFB}, + }, + { + []byte{0x6B, 0x8C, 0x0C, 0x85, 0x7A, 0x48, 0x0C, 0xBA, 0x1D, 0x28, 0x30, 0xB4, 0x23, 0xA2, 0x16, 0x85, 0x62, 0x13, 0x29, 0x59, 0x81, 0x4E, 0x23, 0xDC, 0xA7, 0xD7, 0x85, 0x14, 0x4B, 0xD8, 0x8E, 0xA7, 0xD7, 0xC2, 0x3A, 0xD2, 0x86, 0x60, 0xA0, 0xBE, 0x6A, 0x22, 0xCD, 0xF1, 0x07, 0x75, 0xD2, 0x48, 0x9A, 0xEA, 0x56, 0x3D, 0x16, 0x70, 0xEF, 0x9F, 0xF5, 0xC2, 0x94, 0x86, 0x4C, 0x21, 0x29, 0x85, 0x9A, 0x84, 0x52, 0xF4, 0x41, 0xAC, 0xC5, 0x17, 0xD9, 0x72, 0xBE, 0xBA, 0x88, 0x38, 0xC2, 0xD2, 0x7D, 0xB3, 0xC1, 0x7A, 0x93, 0x61, 0x35, 0xEF, 0x34, 0xB3, 0x92, 0xA0, 0x7A, 0x34, 0x7B, 0x4D, 0x93, 0xDE, 0x1C, 0x46}, + []uint{61, 42, 37, 33, 39, 19, 40, 14, 62, 26, 60, 57, 36, 52, 19, 64, 51, 33, 55}, + []uint64{0xD718190AF490197, 0x10E94185A11, 0x1A21685621, 0x652B3029, 0x623DCA7D78, 0x28A25, 0xEC4753EBE1, 0x75A, 0x14330505F351166F, 0x220EEBA, 0x49135D4AC7A2CE1, 0x1BE7FD70A521930, 0x84A6166A1, 0x14BD106B3145F, 0x32E57, 0xD75107185A4FB678, 0x17A936135EF34, 0x1672540F4, 0x347B4D93DE1C46}, + }, + { + []byte{0xC8, 0x0A, 0xF4, 0xE9, 0x19, 0x51, 0xA6, 0xEA, 0x2C, 0x73, 0xB2, 0x54, 0xCE, 0x8B, 0x7D, 0x94, 0xDC, 0xB5, 0x38, 0x27, 0x6E, 0xD0, 0x3A, 0x1E, 0xA2, 0x09, 0x63, 0x7B, 0x9E, 0x2B, 0x7F, 0x8C, 0x9E, 0x49, 0x42, 0x8A, 0x15, 0xDF, 0xF7, 0xF9, 0xDF, 0xCA, 0xE3, 0xB5, 0x3D, 0x4F, 0xE0, 0x67, 0x7C, 0xC5, 0x77, 0x59, 0x1C, 0x51, 0x28, 0xD0, 0x0A, 0xE7, 0x4A, 0x14, 0xE5, 0x49, 0xF9, 0xA1, 0x49, 0x08, 0x3B, 0xE7, 0x3D, 0xC2, 0xB9, 0x95, 0x06, 0x8A, 0xA3, 0x7F, 0x63, 0x9E, 0x7F, 0x87, 0x67, 0xFC, 0x63, 0x54, 0x98, 0x00, 0x15, 0x3D, 0x4F, 0x99, 0xCD, 0x0F, 0x4A, 0xCC, 0x0D, 0x03, 0x16, 0x69, 0xB6, 0x67}, + []uint{33, 50, 24, 24, 5, 53, 5, 61, 26, 56, 35, 50, 2, 22, 59, 54, 8, 7, 48, 13, 14, 27, 42, 37, 42, 3}, + []uint64{0x19015E9D2, 0xCA8D3751639D, 0x92A674, 0x5BECA6, 0x1C, 0x16A704EDDA0743, 0x1A, 0x1104B1BDCF15BFC6, 0x13C9285, 0x142BBFEFF3BF95, 0x63B53D4FE, 0x19DF315DD647, 0x00, 0x144A34, 0x15CE9429CA93F3, 0x10A4841DF39EE1, 0x5C, 0x65, 0x41A2A8DFD8E7, 0x13FC, 0xECF, 0x7C63549, 0x200054F53E6, 0xE687A5660, 0x1A062CD36CC, 0x07}, + }, + { + []byte{0x83, 0x89, 0x65, 0xAD, 0xCF, 0xD1, 0xA9, 0x1F, 0xB5, 0x29, 0x97, 0x34, 0xA0, 0x5C, 0x67, 0xEC, 0x10, 0x7C, 0xB5, 0xB9, 0xB1, 0x3F, 0x70, 0xCD, 0x54, 0x37, 0xCF, 0x1A, 0x0A, 0xF8, 0x7C, 0x68, 0xF1, 0x7A, 0x4E, 0x69, 0x53, 0xAA, 0xBA, 0x8E, 0x42, 0xC8, 0x49, 0x28, 0xB4, 0xED, 0x55, 0x08, 0x88, 0x0D, 0xAF, 0xF1, 0x34, 0x2B, 0x4A, 0x14, 0x2F, 0x00, 0x2E, 0xB7, 0x9B, 0xEE, 0x86, 0x62, 0x25, 0x12, 0x39, 0xF6, 0x12, 0xD6, 0x9F, 0x95, 0x52, 0x68, 0x2F, 0x9B, 0x2E, 0xBC, 0x32, 0xC4, 0xC6, 0x98, 0x99, 0xB6, 0xB9, 0x93, 0x61, 0xA2, 0x07, 0x20, 0x70, 0x1D, 0x1C, 0x9F, 0xC3, 0xCF, 0x72, 0xFA, 0x4A, 0x1B}, + []uint{47, 7, 1, 29, 41, 12, 14, 2, 46, 54, 62, 59, 12, 45, 62, 1, 6, 6, 50, 14, 23, 27, 45, 11, 51, 36, 20, 17}, + []uint64{0x41C4B2D6E7E8, 0x6A, 0x00, 0x11FB5299, 0xE6940B8CFD, 0x820, 0x3E5A, 0x03, 0x1CD89FB866AA, 0x6F9E3415F0F8D, 0x78BD2734A9D55D4, 0x390B2124A2D3B55, 0x422, 0x406D7F89A15, 0x294285E005D6F37D, 0x01, 0x28, 0x19, 0x22251239F612D, 0x1A7E, 0x2AA4D0, 0x2F9B2EB, 0x1865898D3133, 0x36B, 0x4C9B0D1039038, 0xE8E4FE1E, 0x7B97D, 0x4A1B}, + }, + { + []byte{0x9D, 0xCF, 0xB2, 0x8C, 0xE0, 0xE6, 0xD3, 0xD4, 0xC0, 0x77, 0x4C, 0x43, 0xDB, 0xEE, 0x32, 0xD6, 0x67, 0x9F, 0xED, 0xAA, 0xB6, 0x0C, 0x1D, 0x65, 0x61, 0x02, 0xA1, 0xB0, 0x08, 0xB9, 0x06, 0xCF, 0xA1, 0xD4, 0xE0, 0x54, 0x75, 0x30, 0x59, 0xDA, 0xFE, 0x16, 0x9F, 0x36, 0x88, 0xD4, 0x70, 0x3D, 0xE4, 0x2E, 0x78, 0x17, 0x5F, 0x18, 0x5A, 0x7F, 0x35, 0x31, 0x63, 0xD0, 0xF9, 0xCF, 0x34, 0xD6, 0xDF, 0x7E, 0xB9, 0x07, 0x24, 0x37, 0x2B, 0x5C, 0xFC, 0xD0, 0x3B, 0x32, 0xD4, 0xA7, 0x96, 0x28, 0xD1, 0x23, 0xDA, 0x6C, 0xEA, 0x4C, 0x29, 0x31, 0xB2, 0x20, 0x3C, 0x23, 0xE2, 0xEB, 0x22, 0x78, 0x79, 0xD1, 0xBC, 0xEB}, + []uint{49, 29, 9, 37, 23, 47, 33, 37, 49, 15, 60, 35, 21, 35, 36, 5, 9, 45, 31, 23, 18, 64, 13, 58, 19}, + []uint64{0x13B9F6519C1CD, 0x14F5301D, 0x1A6, 0x43DBEE32D, 0x333CFF, 0x36AAD8307595, 0x108150D80, 0x8B906CFA1, 0x1A9C0A8EA60B3, 0x5AFE, 0x169F3688D4703DE, 0x2173C0BAF, 0x1185A7, 0x79A98B1E8, 0x7CE79A6B6, 0x1F, 0xFD, 0xE41C90DCAD7, 0x1F9A0766, 0x2D4A79, 0x18A34, 0x48F69B3A930A4C6C, 0x1101, 0x3847C5D644F0F3A, 0x1BCEB}, + }, + { + []byte{0x91, 0x0B, 0x7C, 0xD2, 0x4C, 0xB9, 0x12, 0xA4, 0x56, 0x7E, 0xE5, 0x29, 0xEC, 0x0D, 0xEE, 0xD9, 0xCD, 0x10, 0x96, 0x04, 0xDE, 0xAE, 0x9A, 0x8E, 0xFA, 0x41, 0x7C, 0x47, 0x22, 0x53, 0xD3, 0xBF, 0x1E, 0x78, 0x75, 0xBA, 0x7A, 0x88, 0x06, 0x39, 0xC3, 0x8C, 0x59, 0x79, 0x6B, 0x4F, 0x52, 0x30, 0x08, 0xF6, 0x7E, 0x0A, 0xB9, 0x74, 0xB8, 0x25, 0x9C, 0x20, 0x53, 0x7E, 0x4C, 0xAC, 0xE4, 0x58, 0x38, 0xCC, 0xAA, 0x55, 0xCA, 0xE9, 0x1F, 0x2A, 0xD8, 0x15, 0xB8, 0x53, 0x7D, 0xD3, 0x16, 0x47, 0x96, 0xC2, 0x98, 0x56, 0x35, 0xDB, 0x83, 0x5E, 0xD7, 0xCF, 0xB6, 0x03, 0x6A, 0xBF, 0x7A, 0x6C, 0x7A, 0x54, 0x99, 0x0C}, + []uint{34, 58, 36, 43, 41, 55, 14, 6, 6, 12, 62, 43, 34, 27, 12, 29, 46, 25, 51, 13, 36, 42, 35, 40}, + []uint64{0x2442DF349, 0xCB912A4567EE52, 0x9EC0DEED9, 0x66884B026F5, 0xE9A8EFA417, 0x6239129E9DF8F3, 0x30EB, 0x1D, 0x0F, 0x510, 0x31CE1C62CBCB5A7, 0x548C023D9F8, 0xAB974B82, 0x2CE1029, 0xBF2, 0xCACE458, 0xE332A9572BA, 0x8F956C, 0x56E14DF74C59, 0x3CB, 0x614C2B1AE, 0x3706BDAF9F6, 0x6036ABF7A, 0x6C7A54990C}, + }, + { + []byte{0xAE, 0x2E, 0x61, 0xAB, 0x47, 0x76, 0x8C, 0x9E, 0x28, 0x13, 0x95, 0x06, 0x9A, 0x10, 0x8A, 0x5B, 0xFD, 0xB2, 0x2F, 0xC5, 0x44, 0x29, 0xB3, 0xBF, 0x5B, 0x39, 0xD5, 0x5A, 0xD2, 0xED, 0x48, 0xA6, 0x09, 0x11, 0x37, 0x9A, 0x18, 0x04, 0xA2, 0xC5, 0x07, 0x94, 0xAF, 0x3F, 0x3C, 0x59, 0x3E, 0x8E, 0xE6, 0x9C, 0x25, 0x03, 0xFC, 0xFA, 0xB3, 0x95, 0xB8, 0xAD, 0x53, 0x12, 0x2F, 0xB9, 0x91, 0x9C, 0xD0, 0x93, 0x52, 0xF8, 0x03, 0x68, 0xD2, 0x72, 0xD4, 0x63, 0x2B, 0xAA, 0xFF, 0x92, 0x8E, 0xFB, 0x82, 0xC4, 0x41, 0xEC, 0x40, 0x0C, 0x9D, 0xFB, 0x40, 0x25, 0x0B, 0x2F, 0xB1, 0x34, 0x05, 0xD7, 0xB4, 0x73, 0xAE, 0x05}, + []uint{20, 26, 55, 35, 26, 47, 15, 22, 5, 30, 16, 29, 32, 21, 47, 57, 49, 56, 32, 13, 23, 8, 54, 20, 31, 31}, + []uint64{0xAE2E6, 0x6AD1DD, 0x5193C50272A0D3, 0x2108A5BFD, 0x2C8BF15, 0x853677EB673, 0x555A, 0x34BB52, 0x05, 0xC12226F, 0x3430, 0x128B141, 0xE52BCFCF, 0x2C9F4, 0x3B9A70940FF3, 0x1D59CADC56A9891, 0xFB9919CD0935, 0x2F80368D272D46, 0x32BAAFF9, 0x51D, 0x7B82C4, 0x41, 0x3B1003277ED009, 0x42CBE, 0x62680BAF, 0x3473AE05}, + }, + { + []byte{0xC3, 0x42, 0xF8, 0x38, 0x04, 0x5F, 0x4F, 0x7A, 0x7A, 0x2F, 0x32, 0xEC, 0xE5, 0x0F, 0xDD, 0x4C, 0x16, 0x10, 0x31, 0x0A, 0xF0, 0xAE, 0x6D, 0xE1, 0xEB, 0xE3, 0x4E, 0x11, 0x30, 0x6C, 0xC1, 0xA8, 0xA6, 0xB5, 0x33, 0xD3, 0x58, 0x03, 0xBF, 0x16, 0x09, 0x37, 0x4D, 0xE7, 0xE9, 0x82, 0xCC, 0xBC, 0xFA, 0x12, 0x4D, 0xAC, 0xF6, 0x53, 0x22, 0xBD, 0xF7, 0xFD, 0x22, 0x77, 0xEC, 0x5B, 0xD1, 0xC4, 0xA2, 0x97, 0xC9, 0x04, 0x1C, 0x62, 0x7B, 0xD4, 0x03, 0x82, 0x21, 0x22, 0x2B, 0x20, 0xC6, 0x0D, 0xF8, 0xAA, 0x03, 0xC0, 0x30, 0x21, 0x31, 0xF0, 0xEE, 0x9B, 0x17, 0x60, 0x24, 0x94, 0x71, 0x5F, 0x04, 0xC5, 0x21, 0x97}, + []uint{62, 59, 50, 16, 6, 35, 61, 35, 43, 22, 8, 55, 5, 33, 27, 32, 33, 23, 21, 40, 38, 23, 2, 27, 43, 1}, + []uint64{0x30D0BE0E0117D3DE, 0x4F45E65D9CA1FBA, 0x260B081885785, 0x736F, 0x03, 0x6BE34E113, 0xD983514D6A67A6, 0x5803BF160, 0x49BA6F3F4C1, 0x19979F, 0x42, 0x24DACF65322BDF, 0x0F, 0x1F489DFB1, 0x37A3894, 0x52F92083, 0x1189EF500, 0x704424, 0x8AC83, 0x1837E2A80F, 0x302131F0, 0x774D8B, 0x02, 0x6024947, 0xAF826290CB, 0x01}, + }, + { + []byte{0xDB, 0xB6, 0x8B, 0x84, 0x54, 0x5F, 0x96, 0xF5, 0xA7, 0x0A, 0x0C, 0x86, 0x2B, 0x97, 0x6C, 0x1D, 0x06, 0xF1, 0x2B, 0x6B, 0x3C, 0xD6, 0xE2, 0x4E, 0x00, 0x50, 0xFF, 0x99, 0x4E, 0x19, 0x48, 0xC0, 0x51, 0xAE, 0x3F, 0x8A, 0x89, 0xB5, 0x4E, 0x1D, 0x4B, 0xA7, 0x4E, 0xC6, 0xF9, 0x51, 0x4C, 0xAC, 0xFA, 0x68, 0x40, 0xB1, 0xD6, 0xA5, 0x40, 0xF3, 0xCD, 0xDC, 0xCB, 0x3B, 0x5E, 0xBD, 0x4D, 0x42, 0xA1, 0xDE, 0x60, 0x5F, 0xFF, 0x04, 0xB8, 0xC4, 0xC6, 0xCC, 0x6D, 0xAE, 0xE0, 0xD4, 0x5C, 0x19, 0x12, 0x0C, 0xE1, 0x5D, 0xEA, 0x52, 0x81, 0x48, 0x36, 0x7E, 0xB4, 0x3E, 0x2F, 0x55, 0xE8, 0xF1, 0x5C, 0xCC, 0xE9, 0x69}, + []uint{40, 14, 56, 38, 64, 31, 42, 45, 46, 13, 7, 51, 4, 7, 54, 61, 37, 57, 15, 30, 56, 32}, + []uint64{0xDBB68B8454, 0x17E5, 0xBD69C283218AE5, 0x36C1D06F12, 0xB6B3CD6E24E0050F, 0x7CCA70CA, 0x1180A35C7F1, 0xA26D538752E, 0x274EC6F9514C, 0x159F, 0x26, 0x42058EB52A079, 0x0E, 0x37, 0x1CCB3B5EBD4D42, 0x143BCC0BFFE09718, 0x131B31B6BB, 0x106A2E0C890670A, 0x77A9, 0x12814836, 0x7EB43E2F55E8F1, 0x5CCCE969}, + }, + { + []byte{0xC1, 0xD6, 0x8F, 0x43, 0xE4, 0xDA, 0x73, 0x69, 0xFF, 0xD2, 0xBD, 0x90, 0x9C, 0xA7, 0x7F, 0x80, 0x19, 0x74, 0xED, 0xAA, 0xFC, 0x25, 0x6C, 0xB8, 0x2B, 0xD9, 0xE1, 0x2F, 0xC1, 0xD8, 0xCF, 0x8C, 0x5F, 0xA9, 0xA7, 0x4A, 0x4B, 0xFD, 0x69, 0x5D, 0x81, 0x47, 0x74, 0x01, 0x04, 0xC0, 0xE2, 0x44, 0xC0, 0x6A, 0x9D, 0x55, 0x36, 0x67, 0x97, 0x1D, 0xA5, 0x77, 0xF1, 0x66, 0xA7, 0xFF, 0x9A, 0xE7, 0xE7, 0x2C, 0x11, 0xBA, 0xD1, 0x35, 0xAB, 0xFE, 0x6E, 0x1B, 0x4E, 0x71, 0x8E, 0xE1, 0x76, 0x9A, 0x37, 0xC9, 0xB9, 0x0D, 0xE7, 0x48, 0xE3, 0xF5, 0x1B, 0xD0, 0x37, 0xAD, 0xB5, 0x6A, 0x5D, 0x1D, 0x68, 0xA6, 0x25, 0xA5}, + []uint{43, 24, 64, 30, 32, 57, 56, 61, 50, 3, 45, 46, 3, 60, 22, 42, 5, 60, 20, 24, 10, 43}, + []uint64{0x60EB47A1F26, 0xD39B4F, 0xFE95EC84E53BFC00, 0x32E9DB55, 0xF84AD970, 0xAF6784BF07633E, 0x317EA69D292FF5, 0x14AEC0A3BA008260, 0x1C48980D53AAA, 0x03, 0xCCF2E3B4AEF, 0x38B353FFCD73, 0x07, 0x9CB046EB44D6AFF, 0x26E1B4, 0x39C63B85DA6, 0x11, 0xBE4DC86F3A471FA, 0x8DE81, 0xBD6DAB, 0x14B, 0x51D68A625A5}, + }, + { + []byte{0xD2, 0x0F, 0x71, 0xCA, 0xCA, 0xC7, 0x54, 0x23, 0xD0, 0x56, 0x71, 0xA4, 0x96, 0x84, 0x3D, 0xA6, 0x34, 0x6E, 0x5E, 0x7F, 0x4E, 0x49, 0x22, 0xE0, 0x9E, 0xC8, 0x94, 0x2E, 0xAF, 0x34, 0x2F, 0xF6, 0xD3, 0xA7, 0x90, 0x9E, 0xEC, 0x25, 0x15, 0xC5, 0xFB, 0xE4, 0xBD, 0x08, 0x3D, 0x5A, 0x70, 0x77, 0x17, 0x20, 0x2F, 0x1B, 0x1B, 0xAB, 0xA5, 0xC9, 0x83, 0x6D, 0x44, 0x21, 0xD6, 0x0A, 0x01, 0xBD, 0x5D, 0x9F, 0xFD, 0xE4, 0xDF, 0xE4, 0xFD, 0x83, 0xA2, 0x7C, 0xAF, 0x23, 0x7B, 0xB1, 0x6B, 0xAA, 0x71, 0x50, 0x11, 0xBB, 0x48, 0x66, 0xE8, 0x6C, 0xF4, 0x42, 0x28, 0x36, 0x78, 0xEE, 0x64, 0xA5, 0xEA, 0xFC, 0x2E, 0x5A}, + []uint{32, 19, 41, 28, 7, 20, 33, 64, 3, 54, 63, 16, 12, 12, 63, 9, 64, 3, 29, 29, 18, 6, 28, 3, 2, 39, 41, 40, 22}, + []uint64{0xD20F71CA, 0x6563A, 0x1423D05671A, 0x496843D, 0x53, 0x1A372, 0x1E7F4E492, 0x2E09EC8942EAF342, 0x07, 0x3EDA74F213DD84, 0x515C5FBE4BD083D5, 0xA707, 0x717, 0x202, 0x78D8DD5D2E4C1B6A, 0x42, 0x1D60A01BD5D9FFDE, 0x02, 0xDFE4FD8, 0x744F95E, 0x11BDD, 0x22, 0xD754E2A, 0x00, 0x00, 0x237690CDD0, 0x1B3D108A0D9, 0xE3B99297AB, 0x3C2E5A}, + }, + { + []byte{0x97, 0xD2, 0x7B, 0x0E, 0xF3, 0x1D, 0x05, 0xA6, 0x82, 0x27, 0xD8, 0xE0, 0x4C, 0x2C, 0xFE, 0x8E, 0x5B, 0x92, 0x67, 0xA1, 0xDA, 0x2F, 0xC8, 0x6A, 0x3F, 0x83, 0x20, 0x33, 0xB2, 0xF7, 0xE6, 0x6E, 0xE2, 0x93, 0x98, 0xA2, 0x32, 0x15, 0xD1, 0x21, 0x41, 0xBC, 0x2B, 0xDE, 0xFE, 0x55, 0xFB, 0x6C, 0x34, 0x28, 0xC4, 0x41, 0xA1, 0xFE, 0x33, 0x0C, 0xD3, 0xF5, 0x4A, 0x90, 0xF7, 0x58, 0x04, 0x0C, 0xB6, 0xEE, 0x44, 0xCC, 0x80, 0x3F, 0x00, 0x8F, 0xA8, 0x85, 0xE8, 0x9C, 0x9D, 0xF5, 0x5A, 0xEA, 0xA0, 0x6B, 0x3B, 0x59, 0xD1, 0x89, 0x81, 0x5A, 0xE0, 0x10, 0xD5, 0xD3, 0x7B, 0x1A, 0x4E, 0xAF, 0x3D, 0xE9, 0x3E, 0x59}, + []uint{22, 40, 15, 18, 48, 34, 7, 54, 40, 57, 21, 10, 60, 13, 5, 38, 25, 14, 28, 20, 11, 53, 9, 36, 23, 46, 17, 4, 32}, + []uint64{0x25F49E, 0xC3BCC74169, 0x5044, 0x3EC70, 0x26167F472DC9, 0xCF43B45F, 0x48, 0x1A8FE0C80CECBD, 0xF99BB8A4E6, 0x51190AE890A0DE, 0x2BDEF, 0x395, 0x7EDB0D0A3110687, 0x1F19, 0x10, 0x334FD52A43, 0x1BAC020, 0x196D, 0xDC89990, 0x7E01, 0xFA, 0x110BD1393BEAB5, 0x1AA, 0x81ACED674, 0x31302B, 0x170086AE9BD8, 0x1A4EA, 0x0F, 0x3DE93E59}, + }, + { + []byte{0xAB, 0x5F, 0xA3, 0x18, 0x91, 0x10, 0x4D, 0x85, 0x95, 0xC6, 0x23, 0xBA, 0x0D, 0xBD, 0xC4, 0x2A, 0x12, 0x30, 0xE1, 0xD3, 0xFE, 0xA2, 0x0B, 0xB9, 0xD9, 0x3D, 0x61, 0x91, 0x97, 0x8B, 0x53, 0x2A, 0xC1, 0x80, 0x3C, 0x46, 0x46, 0x44, 0x57, 0x8A, 0x23, 0x66, 0xF2, 0x51, 0xCC, 0xF8, 0xFB, 0xEF, 0x0A, 0x0B, 0x72, 0xF0, 0xA8, 0x36, 0xE7, 0x26, 0x04, 0x83, 0x15, 0x36, 0x04, 0x73, 0x38, 0x48, 0x41, 0x61, 0x0B, 0x09, 0x46, 0xBC, 0x29, 0xA8, 0x02, 0xB6, 0x80, 0xDB, 0xCF, 0x22, 0x54, 0x5B, 0x5A, 0x2E, 0x34, 0x24, 0x06, 0xB9, 0x66, 0x4C, 0xD1, 0xB7, 0x1A, 0xFF, 0x92, 0x06, 0x49, 0xD1, 0x26, 0xE6, 0x23, 0xAD}, + []uint{61, 43, 8, 31, 38, 4, 45, 5, 51, 58, 5, 2, 51, 22, 38, 44, 26, 20, 52, 51, 49, 39, 50, 7}, + []uint64{0x156BF463122209B0, 0x595C623BA0D, 0xBD, 0x62150918, 0x1C3A7FD441, 0x07, 0xE764F586465, 0x1C, 0x2D4CAB0600F11, 0x24644578A2366F2, 0x0A, 0x00, 0x733E3EFBC282D, 0x32F0A8, 0xDB9C98120, 0xC54D811CCE1, 0x841610, 0xB0946, 0xBC29A802B680D, 0x5E7912A2DAD17, 0x342406B9664C, 0x68DB8D7FC9, 0xC93A24DCC47, 0x2D}, + }, + { + []byte{0xAA, 0x55}, + []uint{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, + []uint64{1, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0, 1}, + }, + + { + []byte{0xAA, 0x55}, + []uint{7, 8, 1}, + []uint64{0x55, 0x2A, 0x1}, + }, + + { + []byte{0xAA, 0x55}, + []uint{3, 3, 3, 3, 3, 1}, + []uint64{0x5, 0x2, 0x4, 0x5, 0x2, 0x1}, + }, + + { + []byte{0xAA, 0x55}, + []uint{16}, + []uint64{0xAA55}, + }, + + { + []byte{0xAA, 0x55, 0xAA, 0x55, 0xAA, 0x55, 0xAA, 0x55}, + []uint{32, 32}, + []uint64{0xAA55AA55, 0xAA55AA55}, + }, + + { + []byte{0xAA, 0x55, 0xAA, 0x55, 0xAA, 0x55, 0xAA, 0x55}, + []uint{33, 31}, + []uint64{0x154AB54AB, 0x2A55AA55}, + }, + } + + for i, test := range tests { + r := NewReader(bytes.NewReader(test.data)) + if len(test.ns) != len(test.vals) { + panic("Number of reads does not match number of results") + } + for j, n := range test.ns { + m, err := r.Read(n) + if err != nil { + panic("Unexpected error: " + err.Error()) + } + if m != test.vals[j] { + t.Errorf("i=%d; %v with reads %v: read %d gave %x, expected %x", i, test.data, test.ns, j, m, test.vals[j]) + } + } + } +} + +func TestReadEOF(t *testing.T) { + tests := []struct { + data []byte + n uint + err error + }{ + {[]byte{0xFF}, 8, nil}, + {[]byte{0xFF}, 2, nil}, + {[]byte{0xFF}, 9, io.ErrUnexpectedEOF}, + {[]byte{}, 1, io.EOF}, + {[]byte{0xFF, 0xFF}, 16, nil}, + {[]byte{0xFF, 0xFF}, 17, io.ErrUnexpectedEOF}, + } + + for i, test := range tests { + r := NewReader(bytes.NewReader(test.data)) + if _, err := r.Read(test.n); err != test.err { + t.Errorf("i=%d; Reading %d from %v, expected err=%s, got err=%s", i, test.n, test.data, test.err, err) + } + } +} + +func BenchmarkReadAlign1(b *testing.B) { + benchmarkReads(b, 64, 1) +} + +func BenchmarkReadAlign32(b *testing.B) { + benchmarkReads(b, 64, 32) +} + +func BenchmarkReadAlign64(b *testing.B) { + benchmarkReads(b, 64, 64) +} + +func benchmarkReads(b *testing.B, chunk, align int) { + size := 1 << 12 + buf, bits, _, last := prepareBenchmark(size, chunk, align) + b.SetBytes(int64(len(buf))) + b.ResetTimer() + for i := 0; i < b.N; i++ { + r := NewReader(bytes.NewReader(buf)) + for j := 0; j < last; j++ { + if _, err := r.Read(bits[j]); err != nil { + b.Error(err) + continue + } + } + } +} + +func prepareBenchmark(size, chunk, align int) ([]byte, []uint, []uint64, int) { + buf := make([]byte, size) + bits := make([]uint, size) + values := make([]uint64, size) + idx := 0 + last := 0 + for i := 0; i < size; i++ { + val := getNumBits(idx, size*8, chunk, align) + idx += val + if val != 0 { + last = i + 1 + } + bits[i] = uint(val) + values[i] = uint64(rand.Uint32())<<32 + uint64(rand.Uint32()) + } + return buf, bits, values, last +} + +func getNumBits(read, max, chunk, align int) int { + bits := 1 + if align != chunk { + bits += rand.Intn(chunk / align) + } + bits *= align + if read+bits > max { + bits = max - read + } + if bits > chunk { + panic("too many bits") + } + return bits +} diff --git a/internal/bits/twocomp.go b/internal/bits/twocomp.go new file mode 100755 index 0000000..45d52b4 --- /dev/null +++ b/internal/bits/twocomp.go @@ -0,0 +1,27 @@ +package bits + +// IntN returns the signed two's complement of x with the specified integer bit +// width. +// +// Examples of unsigned (n-bit width) x values on the left and decoded values on +// the right: +// +// 0b011 -> 3 +// 0b010 -> 2 +// 0b001 -> 1 +// 0b000 -> 0 +// 0b111 -> -1 +// 0b110 -> -2 +// 0b101 -> -3 +// 0b100 -> -4 +func IntN(x uint64, n uint) int64 { + signBitMask := uint64(1 << (n - 1)) + if x&signBitMask == 0 { + // positive. + return int64(x) + } + // negative. + v := int64(x ^ signBitMask) // clear sign bit. + v -= int64(signBitMask) + return v +} diff --git a/internal/bits/twocomp_test.go b/internal/bits/twocomp_test.go new file mode 100755 index 0000000..d803889 --- /dev/null +++ b/internal/bits/twocomp_test.go @@ -0,0 +1,27 @@ +package bits + +import "testing" + +func TestIntN(t *testing.T) { + golden := []struct { + x uint64 + n uint + want int64 + }{ + {x: 0b011, n: 3, want: 3}, + {x: 0b010, n: 3, want: 2}, + {x: 0b001, n: 3, want: 1}, + {x: 0b000, n: 3, want: 0}, + {x: 0b111, n: 3, want: -1}, + {x: 0b110, n: 3, want: -2}, + {x: 0b101, n: 3, want: -3}, + {x: 0b100, n: 3, want: -4}, + } + for _, g := range golden { + got := IntN(g.x, g.n) + if g.want != got { + t.Errorf("result mismatch of IntN(x=0b%03b, n=%d); expected %d, got %d", g.x, g.n, g.want, got) + continue + } + } +} diff --git a/internal/bits/unary.go b/internal/bits/unary.go new file mode 100755 index 0000000..b4a81fc --- /dev/null +++ b/internal/bits/unary.go @@ -0,0 +1,58 @@ +package bits + +import ( + "github.com/icza/bitio" +) + +// ReadUnary decodes and returns an unary coded integer, whose value is +// represented by the number of leading zeros before a one. +// +// Examples of unary coded binary on the left and decoded decimal on the right: +// +// 1 => 0 +// 01 => 1 +// 001 => 2 +// 0001 => 3 +// 00001 => 4 +// 000001 => 5 +// 0000001 => 6 +func (br *Reader) ReadUnary() (x uint64, err error) { + for { + bit, err := br.Read(1) + if err != nil { + return 0, err + } + if bit == 1 { + break + } + x++ + } + return x, nil +} + +// WriteUnary encodes x as an unary coded integer, whose value is represented by +// the number of leading zeros before a one. +// +// Examples of unary coded binary on the left and decoded decimal on the right: +// +// 0 => 1 +// 1 => 01 +// 2 => 001 +// 3 => 0001 +// 4 => 00001 +// 5 => 000001 +// 6 => 0000001 +func WriteUnary(bw *bitio.Writer, x uint64) error { + for ; x > 8; x -= 8 { + if err := bw.WriteByte(0x0); err != nil { + return err + } + } + + bits := uint64(1) + n := byte(x + 1) + if err := bw.WriteBits(bits, n); err != nil { + return err + } + return nil +} diff --git a/internal/bits/unary_test.go b/internal/bits/unary_test.go new file mode 100755 index 0000000..4db524a --- /dev/null +++ b/internal/bits/unary_test.go @@ -0,0 +1,36 @@ +package bits_test + +import ( + "bytes" + "testing" + + "github.com/icza/bitio" + "github.com/mewkiz/flac/internal/bits" +) + +func TestUnary(t *testing.T) { + buf := &bytes.Buffer{} + bw := bitio.NewWriter(buf) + + for want := uint64(0); want < 1000; want++ { + // Write unary + if err := bits.WriteUnary(bw, want); err != nil { + t.Fatalf("unable to write unary; %v", err) + } + // Flush buffer + if err := bw.Close(); err != nil { + t.Fatalf("unable to close (flush) the bit buffer; %v", err) + } + + // Read written unary + r := bits.NewReader(buf) + got, err := r.ReadUnary() + if err != nil { + t.Fatalf("unable to read unary; %v", err) + } + + if want != got { + t.Fatalf("mismatch between written and read unary value; expected: %d, got: %d", want, got) + } + } +} diff --git a/internal/bits/zigzag.go b/internal/bits/zigzag.go new file mode 100755 index 0000000..3d6ac40 --- /dev/null +++ b/internal/bits/zigzag.go @@ -0,0 +1,41 @@ +package bits + +// DecodeZigZag decodes a ZigZag encoded integer and returns it. +// +// Examples of ZigZag encoded values on the left and decoded values on the +// right: +// +// 0 => 0 +// 1 => -1 +// 2 => 1 +// 3 => -2 +// 4 => 2 +// 5 => -3 +// 6 => 3 +// +// ref: https://developers.google.com/protocol-buffers/docs/encoding +func DecodeZigZag(x uint32) int32 { + return int32(x>>1) ^ -int32(x&1) +} + +// EncodeZigZag encodes a given integer to ZigZag-encoding. +// +// Examples of integer input on the left and corresponding ZigZag encoded values +// on the right: +// +// 0 => 0 +// -1 => 1 +// 1 => 2 +// -2 => 3 +// 2 => 4 +// -3 => 5 +// 3 => 6 +// +// ref: https://developers.google.com/protocol-buffers/docs/encoding +func EncodeZigZag(x int32) uint32 { + if x < 0 { + x = -x + return uint32(x)<<1 - 1 + } + return uint32(x) << 1 +} diff --git a/internal/bits/zigzag_test.go b/internal/bits/zigzag_test.go new file mode 100755 index 0000000..2a8fdc5 --- /dev/null +++ b/internal/bits/zigzag_test.go @@ -0,0 +1,49 @@ +package bits + +import ( + "testing" +) + +func TestDecodeZigZag(t *testing.T) { + golden := []struct { + x uint32 + want int32 + }{ + {x: 0, want: 0}, + {x: 1, want: -1}, + {x: 2, want: 1}, + {x: 3, want: -2}, + {x: 4, want: 2}, + {x: 5, want: -3}, + {x: 6, want: 3}, + } + for _, g := range golden { + got := DecodeZigZag(g.x) + if g.want != got { + t.Errorf("result mismatch of DecodeZigZag(x=%d); expected %d, got %d", g.x, g.want, got) + continue + } + } +} + +func TestEncodeZigZag(t *testing.T) { + golden := []struct { + x int32 + want uint32 + }{ + {x: 0, want: 0}, + {x: -1, want: 1}, + {x: 1, want: 2}, + {x: -2, want: 3}, + {x: 2, want: 4}, + {x: -3, want: 5}, + {x: 3, want: 6}, + } + for _, g := range golden { + got := EncodeZigZag(g.x) + if g.want != got { + t.Errorf("result mismatch of EncodeZigZag(x=%d); expected %d, got %d", g.x, g.want, got) + continue + } + } +} diff --git a/internal/bufseekio/readseeker.go b/internal/bufseekio/readseeker.go new file mode 100755 index 0000000..9678eda --- /dev/null +++ b/internal/bufseekio/readseeker.go @@ -0,0 +1,152 @@ +package bufseekio + +import ( + "errors" + "io" +) + +const ( + defaultBufSize = 4096 +) + +// ReadSeeker implements buffering for an io.ReadSeeker object. +// ReadSeeker is based on bufio.Reader with Seek functionality added +// and unneeded functionality removed. +type ReadSeeker struct { + buf []byte + pos int64 // absolute start position of buf + rd io.ReadSeeker // read-seeker provided by the client + r, w int // buf read and write positions within buf + err error +} + +const minReadBufferSize = 16 + +// NewReadSeekerSize returns a new ReadSeeker whose buffer has at least the specified +// size. If the argument io.ReadSeeker is already a ReadSeeker with large enough +// size, it returns the underlying ReadSeeker. +func NewReadSeekerSize(rd io.ReadSeeker, size int) *ReadSeeker { + // Is it already a Reader? + b, ok := rd.(*ReadSeeker) + if ok && len(b.buf) >= size { + return b + } + if size < minReadBufferSize { + size = minReadBufferSize + } + r := new(ReadSeeker) + r.reset(make([]byte, size), rd) + return r +} + +// NewReadSeeker returns a new ReadSeeker whose buffer has the default size. +func NewReadSeeker(rd io.ReadSeeker) *ReadSeeker { + return NewReadSeekerSize(rd, defaultBufSize) +} + +var errNegativeRead = errors.New("bufseekio: reader returned negative count from Read") + +func (b *ReadSeeker) reset(buf []byte, r io.ReadSeeker) { + *b = ReadSeeker{ + buf: buf, + rd: r, + } +} + +func (b *ReadSeeker) readErr() error { + err := b.err + b.err = nil + return err +} + +// Read reads data into p. +// It returns the number of bytes read into p. +// The bytes are taken from at most one Read on the underlying Reader, +// hence n may be less than len(p). +// To read exactly len(p) bytes, use io.ReadFull(b, p). +// If the underlying Reader can return a non-zero count with io.EOF, +// then this Read method can do so as well; see the [io.Reader] docs. +func (b *ReadSeeker) Read(p []byte) (n int, err error) { + n = len(p) + if n == 0 { + if b.buffered() > 0 { + return 0, nil + } + return 0, b.readErr() + } + if b.r == b.w { + if b.err != nil { + return 0, b.readErr() + } + if len(p) >= len(b.buf) { + // Large read, empty buffer. + // Read directly into p to avoid copy. + n, b.err = b.rd.Read(p) + if n < 0 { + panic(errNegativeRead) + } + b.pos += int64(n) + return n, b.readErr() + } + // One read. + b.pos += int64(b.r) + b.r = 0 + b.w = 0 + n, b.err = b.rd.Read(b.buf) + if n < 0 { + panic(errNegativeRead) + } + if n == 0 { + return 0, b.readErr() + } + b.w += n + } + + // copy as much as we can + // Note: if the slice panics here, it is probably because + // the underlying reader returned a bad count. See issue 49795. + n = copy(p, b.buf[b.r:b.w]) + b.r += n + return n, nil +} + +// buffered returns the number of bytes that can be read from the current buffer. +func (b *ReadSeeker) buffered() int { return b.w - b.r } + +func (b *ReadSeeker) Seek(offset int64, whence int) (int64, error) { + // The stream.Seek() implementation makes heavy use of seeking with offset 0 + // to obtain the current position; let's optimize for it. + if offset == 0 && whence == io.SeekCurrent { + return b.position(), nil + } + // When seeking from the end, the absolute position isn't known by ReadSeeker + // so the current buffer cannot be used. Seeking cannot be avoided. + if whence == io.SeekEnd { + return b.seek(offset, whence) + } + // Calculate the absolute offset. + abs := offset + if whence == io.SeekCurrent { + abs += b.position() + } + // Check if the offset is within buf. + if abs >= b.pos && abs < b.pos+int64(b.w) { + b.r = int(abs - b.pos) + return abs, nil + } + + return b.seek(abs, io.SeekStart) +} + +func (b *ReadSeeker) seek(offset int64, whence int) (int64, error) { + b.r = 0 + b.w = 0 + var err error + b.pos, err = b.rd.Seek(offset, whence) + return b.pos, err +} + +// position returns the absolute read offset. +func (b *ReadSeeker) position() int64 { + return b.pos + int64(b.r) +} diff --git a/internal/bufseekio/readseeker_test.go b/internal/bufseekio/readseeker_test.go new file mode 100755 index 0000000..06b162d --- /dev/null +++ b/internal/bufseekio/readseeker_test.go @@ -0,0 +1,266 @@ +package bufseekio + +import ( + "bytes" + "errors" + "io" + "reflect" + "testing" +) + +func TestNewReadSeekerSize(t *testing.T) { + buf := bytes.NewReader(make([]byte, 100)) + + // Test custom buffer size. + if rs := NewReadSeekerSize(buf, 20); len(rs.buf) != 20 { + t.Fatalf("want %d got %d", 20, len(rs.buf)) + } + + // Test too small buffer size. + if rs := NewReadSeekerSize(buf, 1); len(rs.buf) != minReadBufferSize { + t.Fatalf("want %d got %d", minReadBufferSize, len(rs.buf)) + } + + // Test reuse existing ReadSeeker. + rs := NewReadSeekerSize(buf, 20) + if rs2 := NewReadSeekerSize(rs, 5); rs != rs2 { + t.Fatal("expected ReadSeeker to be reused but got a different ReadSeeker") + } +} + +func TestNewReadSeeker(t *testing.T) { + buf := bytes.NewReader(make([]byte, 100)) + if rs := NewReadSeeker(buf); len(rs.buf) != defaultBufSize { + t.Fatalf("want %d got %d", defaultBufSize, len(rs.buf)) + } +} + +func TestReadSeeker_Read(t *testing.T) { + data := make([]byte, 100) + for i := range data { + data[i] = byte(i) + } + rs := NewReadSeekerSize(bytes.NewReader(data), 20) + if len(rs.buf) != 20 { + t.Fatal("the buffer size was changed and the validity of this test has become unknown") + } + + // Test small read. + got := make([]byte, 5) + if n, err := rs.Read(got); err != nil || n != 5 || !reflect.DeepEqual(got, []byte{0, 1, 2, 3, 4}) { + t.Fatalf("want n read %d got %d, want buffer %v got %v, err=%v", 5, n, []byte{0, 1, 2, 3, 4}, got, err) + } + if p, err := rs.Seek(0, io.SeekCurrent); err != nil || p != 5 { + t.Fatalf("want %d got %d, err=%v", 5, p, err) + } + + // Test big read with initially filled buffer. + got = make([]byte, 25) + if n, err := rs.Read(got); err != nil || n != 15 || !reflect.DeepEqual(got, []byte{5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}) { + t.Fatalf("want n read %d got %d, want buffer %v got %v, err=%v", 15, n, []byte{5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, got, err) + } + if p, err := rs.Seek(0, io.SeekCurrent); err != nil || p != 20 { + t.Fatalf("want %d got %d, err=%v", 20, p, err) + } + + // Test big read with initially empty buffer. + if n, err := rs.Read(got); err != nil || n != 25 || !reflect.DeepEqual(got, []byte{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}) { + t.Fatalf("want n read %d got %d, want buffer %v got %v, err=%v", 25, n, []byte{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}, got, err) + } + if p, err := rs.Seek(0, io.SeekCurrent); err != nil || p != 45 { + t.Fatalf("want %d got %d, err=%v", 45, p, err) + } + + // Test EOF. + if p, err := rs.Seek(98, io.SeekStart); err != nil || p != 98 { + t.Fatalf("want %d got %d, err=%v", 98, p, err) + } + got = make([]byte, 5) + if n, err := rs.Read(got); err != nil || n != 2 || !reflect.DeepEqual(got, []byte{98, 99, 0, 0, 0}) { + t.Fatalf("want n read %d got %d, want buffer %v got %v, err=%v", 2, n, []byte{98, 99, 0, 0, 0}, got, err) + } + if n, err := rs.Read(got); err != io.EOF || n != 0 { + t.Fatalf("want n read %d got %d, err=%v", 0, n, err) + } + + // Test source that returns bytes and an error at the same time. + rs = NewReadSeekerSize(&readAndError{bytes: []byte{2, 3, 5}}, 20) + if len(rs.buf) != 20 { + t.Fatal("the buffer size was changed and the validity of this test has become unknown") + } + got = make([]byte, 5) + if n, err := rs.Read(got); err != nil || n != 3 || !reflect.DeepEqual(got, []byte{2, 3, 5, 0, 0}) { + t.Fatalf("want n read %d got %d, want buffer %v got %v, err=%v", 3, n, []byte{2, 3, 5, 0, 0}, got, err) + } + if n, err := rs.Read(got); err != expectedErr || n != 0 { + t.Fatalf("want n read %d got %d, want error %v, got %v", 0, n, expectedErr, err) + } + + // Test read nothing with an empty buffer and a queued error. + rs = NewReadSeekerSize(&readAndError{bytes: []byte{2, 3, 5}}, 20) + if len(rs.buf) != 20 { + t.Fatal("the buffer size was changed and the validity of this test has become unknown") + } + got = make([]byte, 3) + if n, err := rs.Read(got); err != nil || n != 3 || !reflect.DeepEqual(got, []byte{2, 3, 5}) { + t.Fatalf("want n read %d got %d, want buffer %v got %v, err=%v", 3, n, []byte{2, 3, 5}, got, err) + } + if n, err := rs.Read(nil); err != expectedErr || n != 0 { + t.Fatalf("want n read %d got %d, want error %v, got %v", 0, n, expectedErr, err) + } + if n, err := rs.Read(nil); err != nil || n != 0 { + t.Fatalf("want n read %d got %d, err=%v", 0, n, err) + } + + // Test read nothing with a non-empty buffer and a queued error. + rs = NewReadSeekerSize(&readAndError{bytes: []byte{2, 3, 5}}, 20) + if len(rs.buf) != 20 { + t.Fatal("the buffer size was changed and the validity of this test has become unknown") + } + got = make([]byte, 1) + if n, err := rs.Read(got); err != nil || n != 1 || !reflect.DeepEqual(got, []byte{2}) { + t.Fatalf("want n read %d got %d, want buffer %v got %v, err=%v", 1, n, []byte{}, got, err) + } + if n, err := rs.Read(nil); err != nil || n != 0 { + t.Fatalf("want n read %d got %d, err=%v", 0, n, err) + } +} + +var expectedErr = errors.New("expected error") + +type readAndError struct { + bytes []byte +} + +func (r *readAndError) Read(p []byte) (n int, err error) { + for i, b := range r.bytes { + p[i] = b + } + return len(r.bytes), expectedErr +} + +func (r *readAndError) Seek(offset int64, whence int) (int64, error) { + panic("not implemented") +} + +func TestReadSeeker_Seek(t *testing.T) { + data := make([]byte, 100) + for i := range data { + data[i] = byte(i) + } + r := &seekRecorder{rs: bytes.NewReader(data)} + rs := NewReadSeekerSize(r, 20) + if len(rs.buf) != 20 { + t.Fatal("the buffer size was changed and the validity of this test has become unknown") + } + + got := make([]byte, 5) + + // Test with io.SeekStart + if p, err := rs.Seek(10, io.SeekStart); err != nil || p != 10 { + t.Fatalf("want %d got %d, err=%v", 10, p, err) + } + r.assertSeeked(t, []seekRecord{{10, io.SeekStart}}) + if n, err := rs.Read(got); err != nil || n != 5 || !reflect.DeepEqual(got, []byte{10, 11, 12, 13, 14}) { + t.Fatalf("want n read %d got %d, want buffer %v got %v, err=%v", 5, n, []byte{10, 11, 12, 13, 14}, got, err) + } + if p, err := rs.Seek(0, io.SeekCurrent); err != nil || p != 15 { + t.Fatalf("want %d got %d, err=%v", 15, p, err) + } + r.assertSeeked(t, nil) + + // Test with io.SeekCurrent and positive offset within buffer. + if p, err := rs.Seek(5, io.SeekCurrent); err != nil || p != 20 { + t.Fatalf("want %d got %d, err=%v", 20, p, err) + } + r.assertSeeked(t, nil) + if n, err := rs.Read(got); err != nil || n != 5 || !reflect.DeepEqual(got, []byte{20, 21, 22, 23, 24}) { + t.Fatalf("want n read %d got %d, want buffer %v got %v, err=%v", 5, n, []byte{20, 21, 22, 23, 24}, got, err) + } + if p, err := rs.Seek(0, io.SeekCurrent); err != nil || p != 25 { + t.Fatalf("want %d got %d, err=%v", 25, p, err) + } + + // Test with io.SeekCurrent and negative offset within buffer. + if p, err := rs.Seek(-10, io.SeekCurrent); err != nil || p != 15 { + t.Fatalf("want %d got %d, err=%v", 15, p, err) + } + r.assertSeeked(t, nil) + if n, err := rs.Read(got); err != nil || n != 5 || !reflect.DeepEqual(got, []byte{15, 16, 17, 18, 19}) { + t.Fatalf("want n read %d got %d, want buffer %v got %v, err=%v", 5, n, []byte{15, 16, 17, 18, 19}, got, err) + } + if p, err := rs.Seek(0, io.SeekCurrent); err != nil || p != 20 { + t.Fatalf("want %d got %d, err=%v", 20, p, err) + } + + // Test with io.SeekCurrent and positive offset outside buffer. + if p, err := rs.Seek(30, io.SeekCurrent); err != nil || p != 50 { + t.Fatalf("want %d got %d, err=%v", 50, p, err) + } + r.assertSeeked(t, []seekRecord{{50, io.SeekStart}}) + if n, err := rs.Read(got); err != nil || n != 5 || !reflect.DeepEqual(got, []byte{50, 51, 52, 53, 54}) { + t.Fatalf("want n read %d got %d, want buffer %v got %v, err=%v", 5, n, []byte{50, 51, 52, 53, 54}, got, err) + } + if p, err := rs.Seek(0, io.SeekCurrent); err != nil || p != 55 { + t.Fatalf("want %d got %d, err=%v", 55, p, err) + } + + // Test seek with io.SeekEnd within buffer. + if p, err := rs.Seek(-45, io.SeekEnd); err != nil || p != 55 { + t.Fatalf("want %d got %d, err=%v", 55, p, err) + } + r.assertSeeked(t, []seekRecord{{-45, io.SeekEnd}}) + if n, err := rs.Read(got); err != nil || n != 5 || !reflect.DeepEqual(got, []byte{55, 56, 57, 58, 59}) { + t.Fatalf("want n read %d got %d, want buffer %v got %v, err=%v", 5, n, []byte{55, 56, 57, 58, 59}, got, err) + } + if p, err := rs.Seek(0, io.SeekCurrent); err != nil || p != 60 { + t.Fatalf("want %d got %d, err=%v", 60, p, err) + } + + // Test seek with error. + if _, err := rs.Seek(-100, io.SeekStart); err == nil || err.Error() != "bytes.Reader.Seek: negative position" { + t.Fatalf("want error 'bytes.Reader.Seek: negative position' got %v", err) + } + r.assertSeeked(t, []seekRecord{{-100, io.SeekStart}}) + + // Test seek after error. + if p, err := rs.Seek(10, io.SeekStart); err != nil || p != 10 { + t.Fatalf("want %d got %d, err=%v", 10, p, err) + } + r.assertSeeked(t, []seekRecord{{10, io.SeekStart}}) + if n, err := rs.Read(got); err != nil || n != 5 || !reflect.DeepEqual(got, []byte{10, 11, 12, 13, 14}) { + t.Fatalf("want n read %d got %d, want buffer %v got %v, err=%v", 5, n, []byte{10, 11, 12, 13, 14}, got, err) + } +} + +type seekRecord struct { + offset int64 + whence int +} + +type seekRecorder struct { + rs io.ReadSeeker + seeks []seekRecord +} + +func (r *seekRecorder) Read(p []byte) (n int, err error) { + return r.rs.Read(p) +} + +func (r *seekRecorder) Seek(offset int64, whence int) (int64, error) { + r.seeks = append(r.seeks, seekRecord{offset: offset, whence: whence}) + return r.rs.Seek(offset, whence) +} + +func (r *seekRecorder) assertSeeked(t *testing.T, expected []seekRecord) { + t.Helper() + + if !reflect.DeepEqual(expected, r.seeks) { + t.Fatalf("seek mismatch; expected %#v, got %#v", expected, r.seeks) + } + r.reset() +} + +func (r *seekRecorder) reset() { + r.seeks = nil +} diff --git a/internal/hashutil/crc16/crc16.go b/internal/hashutil/crc16/crc16.go new file mode 100755 index 0000000..4e963c6 --- /dev/null +++ b/internal/hashutil/crc16/crc16.go @@ -0,0 +1,115 @@ +// Copyright 2013 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 crc16 implements the 16-bit cyclic redundancy check, or CRC-16, +// checksum. See http://en.wikipedia.org/wiki/Cyclic_redundancy_check and +// http://www.ross.net/crc/download/crc_v3.txt for information. +package crc16 + +import "github.com/mewkiz/flac/internal/hashutil" + +// Size of a CRC-16 checksum in bytes. +const Size = 2 + +// Predefined polynomials. +const ( + IBM = 0x8005 // x^16 + x^15 + x^2 + x^0 +) + +// Table is a 256-word table representing the polynomial for efficient +// processing. +type Table [256]uint16 + +// IBMTable is the table for the IBM polynomial. +var IBMTable = makeTable(IBM) + +// MakeTable returns the Table constructed from the specified polynomial. +func MakeTable(poly uint16) (table *Table) { + switch poly { + case IBM: + return IBMTable + } + return makeTable(poly) +} + +// makeTable returns the Table constructed from the specified polynomial. +func makeTable(poly uint16) (table *Table) { + table = new(Table) + for i := range table { + crc := uint16(i << 8) + for j := 0; j < 8; j++ { + if crc&0x8000 != 0 { + crc = crc<<1 ^ poly + } else { + crc <<= 1 + } + } + table[i] = crc + } + return table +} + +// digest represents the partial evaluation of a checksum. +type digest struct { + crc uint16 + table *Table +} + +// New creates a new hashutil.Hash16 computing the CRC-16 checksum using the +// polynomial represented by the Table. +func New(table *Table) hashutil.Hash16 { + return &digest{0, table} +} + +// NewIBM creates a new hashutil.Hash16 computing the CRC-16 checksum using the +// IBM polynomial. +func NewIBM() hashutil.Hash16 { + return New(IBMTable) +} + +func (d *digest) Size() int { + return Size +} + +func (d *digest) BlockSize() int { + return 1 +} + +func (d *digest) Reset() { + d.crc = 0 +} + +// Update returns the result of adding the bytes in p to the crc. +func Update(crc uint16, table *Table, p []byte) uint16 { + for _, v := range p { + crc = crc<<8 ^ table[crc>>8^uint16(v)] + } + return crc +} + +func (d *digest) Write(p []byte) (n int, err error) { + d.crc = Update(d.crc, d.table, p) + return len(p), nil +} + +// Sum16 returns the 16-bit checksum of the hash. +func (d *digest) Sum16() uint16 { + return d.crc +} + +func (d *digest) Sum(in []byte) []byte { + s := d.Sum16() + return append(in, byte(s>>8), byte(s)) +} + +// Checksum returns the CRC-16 checksum of data, using the polynomial +// represented by the Table. +func Checksum(data []byte, table *Table) uint16 { + return Update(0, table, data) +} + +// ChecksumIBM returns the CRC-16 checksum of data using the IBM polynomial. +func ChecksumIBM(data []byte) uint16 { + return Update(0, IBMTable, data) +} diff --git a/internal/hashutil/crc16/crc16_test.go b/internal/hashutil/crc16/crc16_test.go new file mode 100755 index 0000000..1741087 --- /dev/null +++ b/internal/hashutil/crc16/crc16_test.go @@ -0,0 +1,106 @@ +package crc16 + +import ( + "io" + "testing" +) + +type test struct { + want uint16 + in string +} + +var golden = []test{ + {0x0000, ""}, + {0x8145, "a"}, + {0xC749, "ab"}, + {0xCADB, "abc"}, + {0x58E7, "abcd"}, + {0x678D, "abcde"}, + {0x0D05, "abcdef"}, + {0x047C, "abcdefg"}, + {0x7D68, "abcdefgh"}, + {0x6878, "abcdefghi"}, + {0xF80F, "abcdefghij"}, + {0x0F8E, "Discard medicine more than two years old."}, + {0xE149, "He who has a shady past knows that nice guys finish last."}, + {0x02B7, "I wouldn't marry him with a ten foot pole."}, + {0x7F6A, "Free! Free!/A trip/to Mars/for 900/empty jars/Burma Shave"}, + {0x28BD, "The days of the digital watch are numbered. -Tom Stoppard"}, + {0x7C55, "Nepal premier won't resign."}, + {0xC92B, "For every action there is an equal and opposite government program."}, + {0x3E41, "His money is twice tainted: 'taint yours and 'taint mine."}, + {0xDA56, "There is no reason for any individual to have a computer in their home. -Ken Olsen, 1977"}, + {0x7F66, "It's a tiny change to the code and not completely disgusting. - Bob Manchek"}, + {0x2A00, "size: a.out: bad magic"}, + {0x25B2, "The major problem is with sendmail. -Mark Horton"}, + {0xBD71, "Give me a rock, paper and scissors and I will move the world. CCFestoon"}, + {0x8596, "If the enemy is within range, then so are you."}, + {0x74A2, "It's well we cannot hear the screams/That we create in others' dreams."}, + {0x0D73, "You remind me of a TV show, but that's all right: I watch it anyway."}, + {0xEE65, "C is as portable as Stonehedge!!"}, + {0xA94E, "Even if I could be Shakespeare, I think I should still choose to be Faraday. - A. Huxley"}, + {0x0B98, "The fugacity of a constituent in a mixture of gases at a given temperature is proportional to its mole fraction. Lewis-Randall Rule"}, + {0xF560, "How can you write a big system without C++? -Paul Glick"}, + {0x60AE, "The quick brown fox jumps over the lazy dog"}, +} + +func TestCrc16IBM(t *testing.T) { + for _, g := range golden { + h := NewIBM() + if _, err := io.WriteString(h, g.in); err != nil { + t.Error(err) + continue + } + got := h.Sum16() + if got != g.want { + t.Errorf("IBM(%q); expected 0x%04X, got 0x%04X.", g.in, g.want, got) + } + } +} + +func BenchmarkNewIBM(b *testing.B) { + for i := 0; i < b.N; i++ { + NewIBM() + } +} + +func BenchmarkCrc16_1K(b *testing.B) { + benchmarkCrc16(b, 1024) +} + +func BenchmarkCrc16_2K(b *testing.B) { + benchmarkCrc16(b, 2*1024) +} + +func BenchmarkCrc16_4K(b *testing.B) { + benchmarkCrc16(b, 4*1024) +} + +func BenchmarkCrc16_8K(b *testing.B) { + benchmarkCrc16(b, 8*1024) +} + +func BenchmarkCrc16_16K(b *testing.B) { + benchmarkCrc16(b, 16*1024) +} + +func benchmarkCrc16(b *testing.B, count int64) { + b.SetBytes(count) + data := make([]byte, count) + for i := range data { + data[i] = byte(i) + } + h := NewIBM() + in := make([]byte, 0, h.Size()) + + b.ResetTimer() + for i := 0; i < b.N; i++ { + h.Reset() + if _, err := h.Write(data); err != nil { + b.Error(err) + continue + } + h.Sum(in) + } +} diff --git a/internal/hashutil/crc8/crc8.go b/internal/hashutil/crc8/crc8.go new file mode 100755 index 0000000..f701373 --- /dev/null +++ b/internal/hashutil/crc8/crc8.go @@ -0,0 +1,114 @@ +// Copyright 2012 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 crc8 implements the 8-bit cyclic redundancy check, or CRC-8, +// checksum. See http://en.wikipedia.org/wiki/Cyclic_redundancy_check and +// http://www.ross.net/crc/download/crc_v3.txt for information. +package crc8 + +import "github.com/mewkiz/flac/internal/hashutil" + +// Size of a CRC-8 checksum in bytes. +const Size = 1 + +// Predefined polynomials. +const ( + ATM = 0x07 // x^8 + x^2 + x + 1 +) + +// Table is a 256-word table representing the polynomial for efficient +// processing. +type Table [256]uint8 + +// ATMTable is the table for the ATM polynomial. +var ATMTable = makeTable(ATM) + +// MakeTable returns the Table constructed from the specified polynomial. +func MakeTable(poly uint8) (table *Table) { + switch poly { + case ATM: + return ATMTable + } + return makeTable(poly) +} + +// makeTable returns the Table constructed from the specified polynomial. +func makeTable(poly uint8) (table *Table) { + table = new(Table) + for i := range table { + crc := uint8(i) + for j := 0; j < 8; j++ { + if crc&0x80 != 0 { + crc = crc<<1 ^ poly + } else { + crc <<= 1 + } + } + table[i] = crc + } + return table +} + +// digest represents the partial evaluation of a checksum. +type digest struct { + crc uint8 + table *Table +} + +// New creates a new hashutil.Hash8 computing the CRC-8 checksum using the +// polynomial represented by the Table. +func New(table *Table) hashutil.Hash8 { + return &digest{0, table} +} + +// NewATM creates a new hashutil.Hash8 computing the CRC-8 checksum using the +// ATM polynomial. +func NewATM() hashutil.Hash8 { + return New(ATMTable) +} + +func (d *digest) Size() int { + return Size +} + +func (d *digest) BlockSize() int { + return 1 +} + +func (d *digest) Reset() { + d.crc = 0 +} + +// Update returns the result of adding the bytes in p to the crc. +func Update(crc uint8, table *Table, p []byte) uint8 { + for _, v := range p { + crc = table[crc^v] + } + return crc +} + +func (d *digest) Write(p []byte) (n int, err error) { + d.crc = Update(d.crc, d.table, p) + return len(p), nil +} + +// Sum8 returns the 8-bit checksum of the hash. +func (d *digest) Sum8() uint8 { + return d.crc +} + +func (d *digest) Sum(in []byte) []byte { + return append(in, d.crc) +} + +// Checksum returns the CRC-8 checksum of data, using the polynomial represented +// by the Table. +func Checksum(data []byte, table *Table) uint8 { + return Update(0, table, data) +} + +// ChecksumATM returns the CRC-8 checksum of data using the ATM polynomial. +func ChecksumATM(data []byte) uint8 { + return Update(0, ATMTable, data) +} diff --git a/internal/hashutil/crc8/crc8_test.go b/internal/hashutil/crc8/crc8_test.go new file mode 100755 index 0000000..5e639ab --- /dev/null +++ b/internal/hashutil/crc8/crc8_test.go @@ -0,0 +1,106 @@ +package crc8 + +import ( + "io" + "testing" +) + +type test struct { + want uint8 + in string +} + +var golden = []test{ + {0x00, ""}, + {0x20, "a"}, + {0xC9, "ab"}, + {0x5F, "abc"}, + {0xA1, "abcd"}, + {0x52, "abcde"}, + {0x8C, "abcdef"}, + {0x9F, "abcdefg"}, + {0xCB, "abcdefgh"}, + {0x67, "abcdefghi"}, + {0x23, "abcdefghij"}, + {0x56, "Discard medicine more than two years old."}, + {0x6B, "He who has a shady past knows that nice guys finish last."}, + {0x70, "I wouldn't marry him with a ten foot pole."}, + {0x8F, "Free! Free!/A trip/to Mars/for 900/empty jars/Burma Shave"}, + {0x48, "The days of the digital watch are numbered. -Tom Stoppard"}, + {0x5E, "Nepal premier won't resign."}, + {0x3C, "For every action there is an equal and opposite government program."}, + {0xA8, "His money is twice tainted: 'taint yours and 'taint mine."}, + {0x46, "There is no reason for any individual to have a computer in their home. -Ken Olsen, 1977"}, + {0xC7, "It's a tiny change to the code and not completely disgusting. - Bob Manchek"}, + {0x31, "size: a.out: bad magic"}, + {0xB6, "The major problem is with sendmail. -Mark Horton"}, + {0x7D, "Give me a rock, paper and scissors and I will move the world. CCFestoon"}, + {0xDC, "If the enemy is within range, then so are you."}, + {0x13, "It's well we cannot hear the screams/That we create in others' dreams."}, + {0x96, "You remind me of a TV show, but that's all right: I watch it anyway."}, + {0x96, "C is as portable as Stonehedge!!"}, + {0x3C, "Even if I could be Shakespeare, I think I should still choose to be Faraday. - A. Huxley"}, + {0xEE, "The fugacity of a constituent in a mixture of gases at a given temperature is proportional to its mole fraction. Lewis-Randall Rule"}, + {0x33, "How can you write a big system without C++? -Paul Glick"}, + {0xC1, "The quick brown fox jumps over the lazy dog"}, +} + +func TestCrc8ATM(t *testing.T) { + for _, g := range golden { + h := NewATM() + if _, err := io.WriteString(h, g.in); err != nil { + t.Error(err) + continue + } + got := h.Sum8() + if got != g.want { + t.Errorf("ATM(%q); expected 0x%02X, got 0x%02X.", g.in, g.want, got) + } + } +} + +func BenchmarkNewATM(b *testing.B) { + for i := 0; i < b.N; i++ { + NewATM() + } +} + +func BenchmarkCrc8_1K(b *testing.B) { + benchmarkCrc8(b, 1024) +} + +func BenchmarkCrc8_2K(b *testing.B) { + benchmarkCrc8(b, 2*1024) +} + +func BenchmarkCrc8_4K(b *testing.B) { + benchmarkCrc8(b, 4*1024) +} + +func BenchmarkCrc8_8K(b *testing.B) { + benchmarkCrc8(b, 8*1024) +} + +func BenchmarkCrc8_16K(b *testing.B) { + benchmarkCrc8(b, 16*1024) +} + +func benchmarkCrc8(b *testing.B, count int64) { + b.SetBytes(count) + data := make([]byte, count) + for i := range data { + data[i] = byte(i) + } + h := NewATM() + in := make([]byte, 0, h.Size()) + + b.ResetTimer() + for i := 0; i < b.N; i++ { + h.Reset() + if _, err := h.Write(data); err != nil { + b.Error(err) + continue + } + h.Sum(in) + } +} diff --git a/internal/hashutil/hashutil.go b/internal/hashutil/hashutil.go new file mode 100755 index 0000000..5976032 --- /dev/null +++ b/internal/hashutil/hashutil.go @@ -0,0 +1,18 @@ +// Package hashutil provides utility interfaces for hash functions. +package hashutil + +import "hash" + +// Hash8 is the common interface implemented by all 8-bit hash functions. +type Hash8 interface { + hash.Hash + // Sum8 returns the 8-bit checksum of the hash. + Sum8() uint8 +} + +// Hash16 is the common interface implemented by all 16-bit hash functions. +type Hash16 interface { + hash.Hash + // Sum16 returns the 16-bit checksum of the hash. + Sum16() uint16 +} diff --git a/internal/ioutilx/byte.go b/internal/ioutilx/byte.go new file mode 100755 index 0000000..33b9dd0 --- /dev/null +++ b/internal/ioutilx/byte.go @@ -0,0 +1,24 @@ +// Package ioutilx implements extended input/output utility functions. +package ioutilx + +import ( + "io" +) + +// ReadByte reads and returns the next byte from r. +func ReadByte(r io.Reader) (byte, error) { + var buf [1]byte + if _, err := io.ReadFull(r, buf[:]); err != nil { + return 0, err + } + return buf[0], nil +} + +// WriteByte writes the given byte to w. +func WriteByte(w io.Writer, b byte) error { + buf := [1]byte{b} + if _, err := w.Write(buf[:]); err != nil { + return err + } + return nil +} diff --git a/internal/ioutilx/zero.go b/internal/ioutilx/zero.go new file mode 100755 index 0000000..bdd9a14 --- /dev/null +++ b/internal/ioutilx/zero.go @@ -0,0 +1,17 @@ +package ioutilx + +// Zero is an io.Reader which always reads zero bytes. +var Zero zero + +// zero is an io.Reader which always reads zero bytes. +type zero struct { +} + +// Read reads len(b) zero bytes into b. It returns the number of bytes read and +// a nil error value. +func (zero) Read(b []byte) (n int, err error) { + for i := range b { + b[i] = 0 + } + return len(b), nil +} diff --git a/internal/utf8/decode.go b/internal/utf8/decode.go new file mode 100755 index 0000000..ce7fde5 --- /dev/null +++ b/internal/utf8/decode.go @@ -0,0 +1,160 @@ +// Package utf8 implements encoding and decoding of UTF-8 coded numbers. +package utf8 + +import ( + "errors" + "fmt" + "io" + + "github.com/mewkiz/flac/internal/ioutilx" +) + +const ( + tx = 0x80 // 1000 0000 + t2 = 0xC0 // 1100 0000 + t3 = 0xE0 // 1110 0000 + t4 = 0xF0 // 1111 0000 + t5 = 0xF8 // 1111 1000 + t6 = 0xFC // 1111 1100 + t7 = 0xFE // 1111 1110 + t8 = 0xFF // 1111 1111 + + maskx = 0x3F // 0011 1111 + mask2 = 0x1F // 0001 1111 + mask3 = 0x0F // 0000 1111 + mask4 = 0x07 // 0000 0111 + mask5 = 0x03 // 0000 0011 + mask6 = 0x01 // 0000 0001 + + rune1Max = 1<<7 - 1 + rune2Max = 1<<11 - 1 + rune3Max = 1<<16 - 1 + rune4Max = 1<<21 - 1 + rune5Max = 1<<26 - 1 + rune6Max = 1<<31 - 1 + rune7Max = 1<<36 - 1 +) + +// Decode decodes a "UTF-8" coded number and returns it. +// +// ref: http://permalink.gmane.org/gmane.comp.audio.compression.flac.devel/3033 +// +// Algorithm description: +// - read one byte B0 from the stream +// - if B0 = 0xxxxxxx then the read value is B0 -> end +// - if B0 = 10xxxxxx, the encoding is invalid +// - if B0 = 11xxxxxx, set L to the number of leading binary 1s minus 1: +// B0 = 110xxxxx -> L = 1 +// B0 = 1110xxxx -> L = 2 +// B0 = 11110xxx -> L = 3 +// B0 = 111110xx -> L = 4 +// B0 = 1111110x -> L = 5 +// B0 = 11111110 -> L = 6 +// - assign the bits following the encoding (the x bits in the examples) to +// a variable R with a magnitude of at least 36 bits +// - loop from 1 to L +// - left shift R 6 bits +// - read B from the stream +// - if B does not match 10xxxxxx, the encoding is invalid +// - set R = R or +// - the read value is R +func Decode(r io.Reader) (x uint64, err error) { + c0, err := ioutilx.ReadByte(r) + if err != nil { + return 0, err + } + + // 1-byte, 7-bit sequence? + if c0 < tx { + // if c0 == 0xxxxxxx + // total: 7 bits (7) + return uint64(c0), nil + } + + // unexpected continuation byte? + if c0 < t2 { + // if c0 == 10xxxxxx + return 0, errors.New("frame.decodeUTF8Int: unexpected continuation byte") + } + + // get number of continuation bytes and store bits from c0. + var l int + switch { + case c0 < t3: + // if c0 == 110xxxxx + // total: 11 bits (5 + 6) + l = 1 + x = uint64(c0 & mask2) + case c0 < t4: + // if c0 == 1110xxxx + // total: 16 bits (4 + 6 + 6) + l = 2 + x = uint64(c0 & mask3) + case c0 < t5: + // if c0 == 11110xxx + // total: 21 bits (3 + 6 + 6 + 6) + l = 3 + x = uint64(c0 & mask4) + case c0 < t6: + // if c0 == 111110xx + // total: 26 bits (2 + 6 + 6 + 6 + 6) + l = 4 + x = uint64(c0 & mask5) + case c0 < t7: + // if c0 == 1111110x + // total: 31 bits (1 + 6 + 6 + 6 + 6 + 6) + l = 5 + x = uint64(c0 & mask6) + case c0 < t8: + // if c0 == 11111110 + // total: 36 bits (0 + 6 + 6 + 6 + 6 + 6 + 6) + l = 6 + x = 0 + } + + // store bits from continuation bytes. + for i := 0; i < l; i++ { + x <<= 6 + c, err := ioutilx.ReadByte(r) + if err != nil { + if err == io.EOF { + return 0, io.ErrUnexpectedEOF + } + return 0, err + } + if c < tx || t2 <= c { + // if c != 10xxxxxx + return 0, errors.New("frame.decodeUTF8Int: expected continuation byte") + } + x |= uint64(c & maskx) + } + + // check if number representation is larger than necessary. + switch l { + case 1: + if x <= rune1Max { + return 0, fmt.Errorf("frame.decodeUTF8Int: larger number representation than necessary; x (%d) stored in %d bytes, could be stored in %d bytes", x, l+1, l) + } + case 2: + if x <= rune2Max { + return 0, fmt.Errorf("frame.decodeUTF8Int: larger number representation than necessary; x (%d) stored in %d bytes, could be stored in %d bytes", x, l+1, l) + } + case 3: + if x <= rune3Max { + return 0, fmt.Errorf("frame.decodeUTF8Int: larger number representation than necessary; x (%d) stored in %d bytes, could be stored in %d bytes", x, l+1, l) + } + case 4: + if x <= rune4Max { + return 0, fmt.Errorf("frame.decodeUTF8Int: larger number representation than necessary; x (%d) stored in %d bytes, could be stored in %d bytes", x, l+1, l) + } + case 5: + if x <= rune5Max { + return 0, fmt.Errorf("frame.decodeUTF8Int: larger number representation than necessary; x (%d) stored in %d bytes, could be stored in %d bytes", x, l+1, l) + } + case 6: + if x <= rune6Max { + return 0, fmt.Errorf("frame.decodeUTF8Int: larger number representation than necessary; x (%d) stored in %d bytes, could be stored in %d bytes", x, l+1, l) + } + } + return x, nil +} diff --git a/internal/utf8/encode.go b/internal/utf8/encode.go new file mode 100755 index 0000000..43f263b --- /dev/null +++ b/internal/utf8/encode.go @@ -0,0 +1,72 @@ +package utf8 + +import ( + "io" + + "github.com/mewkiz/flac/internal/ioutilx" + "github.com/mewkiz/pkg/errutil" +) + +// Encode encodes x as a "UTF-8" coded number. +func Encode(w io.Writer, x uint64) error { + // 1-byte, 7-bit sequence? + if x <= rune1Max { + if err := ioutilx.WriteByte(w, byte(x)); err != nil { + return errutil.Err(err) + } + return nil + } + + // get number of continuation bytes and store bits of c0. + var ( + // number of continuation bytes., + l int + // bits of c0. + bits uint64 + ) + switch { + case x <= rune2Max: + // if c0 == 110xxxxx + // total: 11 bits (5 + 6) + l = 1 + bits = t2 | (x>>6)&mask2 + case x <= rune3Max: + // if c0 == 1110xxxx + // total: 16 bits (4 + 6 + 6) + l = 2 + bits = t3 | (x>>(6*2))&mask3 + case x <= rune4Max: + // if c0 == 11110xxx + // total: 21 bits (3 + 6 + 6 + 6) + l = 3 + bits = t4 | (x>>(6*3))&mask4 + case x <= rune5Max: + // if c0 == 111110xx + // total: 26 bits (2 + 6 + 6 + 6 + 6) + l = 4 + bits = t5 | (x>>(6*4))&mask5 + case x <= rune6Max: + // if c0 == 1111110x + // total: 31 bits (1 + 6 + 6 + 6 + 6 + 6) + l = 5 + bits = t6 | (x>>(6*5))&mask6 + case x <= rune7Max: + // if c0 == 11111110 + // total: 36 bits (0 + 6 + 6 + 6 + 6 + 6 + 6) + l = 6 + bits = 0 + } + // Store bits of c0. + if err := ioutilx.WriteByte(w, byte(bits)); err != nil { + return errutil.Err(err) + } + + // Store continuation bytes. + for i := l - 1; i >= 0; i-- { + bits := tx | (x>>uint(6*i))&maskx + if err := ioutilx.WriteByte(w, byte(bits)); err != nil { + return errutil.Err(err) + } + } + return nil +} diff --git a/meta/application.go b/meta/application.go new file mode 100755 index 0000000..6f5a2e2 --- /dev/null +++ b/meta/application.go @@ -0,0 +1,38 @@ +package meta + +import ( + "encoding/binary" + "io/ioutil" +) + +// Application contains third party application specific data. +// +// ref: https://www.xiph.org/flac/format.html#metadata_block_application +type Application struct { + // Registered application ID. + // + // ref: https://www.xiph.org/flac/id.html + ID uint32 + // Application data. + Data []byte +} + +// parseApplication reads and parses the body of an Application metadata block. +func (block *Block) parseApplication() error { + // 32 bits: ID. + app := new(Application) + block.Body = app + err := binary.Read(block.lr, binary.BigEndian, &app.ID) + if err != nil { + return unexpected(err) + } + + // Check if the Application block only contains an ID. + if block.Length == 4 { + return nil + } + + // (block length)-4 bytes: Data. + app.Data, err = ioutil.ReadAll(block.lr) + return unexpected(err) +} diff --git a/meta/cuesheet.go b/meta/cuesheet.go new file mode 100755 index 0000000..d066267 --- /dev/null +++ b/meta/cuesheet.go @@ -0,0 +1,242 @@ +package meta + +import ( + "encoding/binary" + "errors" + "fmt" + "io" + "io/ioutil" + "strings" +) + +// A CueSheet describes how tracks are laid out within a FLAC stream. +// +// ref: https://www.xiph.org/flac/format.html#metadata_block_cuesheet +type CueSheet struct { + // Media catalog number. + MCN string + // Number of lead-in samples. This field only has meaning for CD-DA cue + // sheets; for other uses it should be 0. Refer to the spec for additional + // information. + NLeadInSamples uint64 + // Specifies if the cue sheet corresponds to a Compact Disc. + IsCompactDisc bool + // One or more tracks. The last track of a cue sheet is always the lead-out + // track. + Tracks []CueSheetTrack +} + +// parseCueSheet reads and parses the body of a CueSheet metadata block. +func (block *Block) parseCueSheet() error { + // Parse cue sheet. + // 128 bytes: MCN. + szMCN, err := readString(block.lr, 128) + if err != nil { + return unexpected(err) + } + cs := &CueSheet{ + MCN: stringFromSZ(szMCN), + } + block.Body = cs + + // 64 bits: NLeadInSamples. + if err = binary.Read(block.lr, binary.BigEndian, &cs.NLeadInSamples); err != nil { + return unexpected(err) + } + + // 1 bit: IsCompactDisc. + var x uint8 + if err := binary.Read(block.lr, binary.BigEndian, &x); err != nil { + return unexpected(err) + } + // mask = 10000000 + if x&0x80 != 0 { + cs.IsCompactDisc = true + } + + // 7 bits and 258 bytes: reserved. + // mask = 01111111 + if x&0x7F != 0 { + return ErrInvalidPadding + } + lr := io.LimitReader(block.lr, 258) + zr := zeros{r: lr} + if _, err := io.Copy(ioutil.Discard, zr); err != nil { + return err + } + + // Parse cue sheet tracks. + // 8 bits: (number of tracks) + if err := binary.Read(block.lr, binary.BigEndian, &x); err != nil { + return unexpected(err) + } + if x < 1 { + return errors.New("meta.Block.parseCueSheet: at least one track required") + } + if cs.IsCompactDisc && x > 100 { + return fmt.Errorf("meta.Block.parseCueSheet: number of CD-DA tracks (%d) exceeds 100", x) + } + cs.Tracks = make([]CueSheetTrack, x) + // Each track number within a cue sheet must be unique; use uniq to keep + // track. + uniq := make(map[uint8]struct{}) + for i := range cs.Tracks { + if err := block.parseTrack(cs, i, uniq); err != nil { + return err + } + } + + return nil +} + +// parseTrack parses the i:th cue sheet track, and ensures that its track number +// is unique. +func (block *Block) parseTrack(cs *CueSheet, i int, uniq map[uint8]struct{}) error { + track := &cs.Tracks[i] + // 64 bits: Offset. + if err := binary.Read(block.lr, binary.BigEndian, &track.Offset); err != nil { + return unexpected(err) + } + if cs.IsCompactDisc && track.Offset%588 != 0 { + return fmt.Errorf("meta.Block.parseCueSheet: CD-DA track offset (%d) must be evenly divisible by 588", track.Offset) + } + + // 8 bits: Num. + if err := binary.Read(block.lr, binary.BigEndian, &track.Num); err != nil { + return unexpected(err) + } + if _, ok := uniq[track.Num]; ok { + return fmt.Errorf("meta.Block.parseCueSheet: duplicated track number %d", track.Num) + } + uniq[track.Num] = struct{}{} + if track.Num == 0 { + return errors.New("meta.Block.parseCueSheet: invalid track number (0)") + } + isLeadOut := i == len(cs.Tracks)-1 + if cs.IsCompactDisc { + if !isLeadOut { + if track.Num >= 100 { + return fmt.Errorf("meta.Block.parseCueSheet: CD-DA track number (%d) exceeds 99", track.Num) + } + } else { + if track.Num != 170 { + return fmt.Errorf("meta.Block.parseCueSheet: invalid lead-out CD-DA track number; expected 170, got %d", track.Num) + } + } + } else { + if isLeadOut && track.Num != 255 { + return fmt.Errorf("meta.Block.parseCueSheet: invalid lead-out track number; expected 255, got %d", track.Num) + } + } + + // 12 bytes: ISRC. + szISRC, err := readString(block.lr, 12) + if err != nil { + return unexpected(err) + } + track.ISRC = stringFromSZ(szISRC) + + // 1 bit: IsAudio. + var x uint8 + if err = binary.Read(block.lr, binary.BigEndian, &x); err != nil { + return unexpected(err) + } + // mask = 10000000 + if x&0x80 == 0 { + track.IsAudio = true + } + + // 1 bit: HasPreEmphasis. + // mask = 01000000 + if x&0x40 != 0 { + track.HasPreEmphasis = true + } + + // 6 bits and 13 bytes: reserved. + // mask = 00111111 + if x&0x3F != 0 { + return ErrInvalidPadding + } + lr := io.LimitReader(block.lr, 13) + zr := zeros{r: lr} + _, err = io.Copy(ioutil.Discard, zr) + if err != nil { + return err + } + + // Parse indicies. + // 8 bits: (number of indicies) + if err = binary.Read(block.lr, binary.BigEndian, &x); err != nil { + return unexpected(err) + } + if x < 1 { + if !isLeadOut { + return errors.New("meta.Block.parseCueSheet: at least one track index required") + } + // Lead-out track has no track indices to parse; return early. + return nil + } + track.Indicies = make([]CueSheetTrackIndex, x) + for i := range track.Indicies { + index := &track.Indicies[i] + // 64 bits: Offset. + if err = binary.Read(block.lr, binary.BigEndian, &index.Offset); err != nil { + return unexpected(err) + } + + // 8 bits: Num. + if err = binary.Read(block.lr, binary.BigEndian, &index.Num); err != nil { + return unexpected(err) + } + + // 3 bytes: reserved. + lr = io.LimitReader(block.lr, 3) + zr = zeros{r: lr} + _, err = io.Copy(ioutil.Discard, zr) + if err != nil { + return err + } + } + return nil +} + +// stringFromSZ returns a copy of the given string terminated at the first +// occurrence of a NULL character. +func stringFromSZ(szStr string) string { + pos := strings.IndexByte(szStr, '\x00') + if pos == -1 { + return szStr + } + return string(szStr[:pos]) +} + +// CueSheetTrack contains the start offset of a track and other track specific +// metadata. +type CueSheetTrack struct { + // Track offset in samples, relative to the beginning of the FLAC audio + // stream. + Offset uint64 + // Track number; never 0, always unique. + Num uint8 + // International Standard Recording Code; empty string if not present. + // + // ref: http://isrc.ifpi.org/ + ISRC string + // Specifies if the track contains audio or data. + IsAudio bool + // Specifies if the track has been recorded with pre-emphasis + HasPreEmphasis bool + // Every track has one or more track index points, except for the lead-out + // track which has zero. Each index point specifies a position within the + // track. + Indicies []CueSheetTrackIndex +} + +// A CueSheetTrackIndex specifies a position within a track. +type CueSheetTrackIndex struct { + // Index point offset in samples, relative to the track offset. + Offset uint64 + // Index point number; subsequently incrementing by 1 and always unique + // within a track. + Num uint8 +} diff --git a/meta/meta.go b/meta/meta.go new file mode 100755 index 0000000..9f91db2 --- /dev/null +++ b/meta/meta.go @@ -0,0 +1,210 @@ +// Package meta implements access to FLAC metadata blocks. +// +// A brief introduction of the FLAC metadata format [1] follows. FLAC metadata +// is stored in blocks; each block contains a header followed by a body. The +// block header describes the type of the block body, its length in bytes, and +// specifies if the block was the last metadata block in a FLAC stream. The +// contents of the block body depends on the type specified in the block header. +// +// At the time of this writing, the FLAC metadata format defines seven different +// metadata block types, namely: +// - StreamInfo [2] +// - Padding [3] +// - Application [4] +// - SeekTable [5] +// - VorbisComment [6] +// - CueSheet [7] +// - Picture [8] +// +// Please refer to their respective documentation for further information. +// +// [1]: https://www.xiph.org/flac/format.html#format_overview +// [2]: https://godoc.org/github.com/mewkiz/flac/meta#StreamInfo +// [3]: https://www.xiph.org/flac/format.html#metadata_block_padding +// [4]: https://godoc.org/github.com/mewkiz/flac/meta#Application +// [5]: https://godoc.org/github.com/mewkiz/flac/meta#SeekTable +// [6]: https://godoc.org/github.com/mewkiz/flac/meta#VorbisComment +// [7]: https://godoc.org/github.com/mewkiz/flac/meta#CueSheet +// [8]: https://godoc.org/github.com/mewkiz/flac/meta#Picture +package meta + +import ( + "errors" + "io" + "io/ioutil" + + "github.com/mewkiz/flac/internal/bits" +) + +// A Block contains the header and body of a metadata block. +// +// ref: https://www.xiph.org/flac/format.html#metadata_block +type Block struct { + // Metadata block header. + Header + // Metadata block body of type *StreamInfo, *Application, ... etc. Body is + // initially nil, and gets populated by a call to Block.Parse. + Body interface{} + // Underlying io.Reader; limited by the length of the block body. + lr io.Reader +} + +// New creates a new Block for accessing the metadata of r. It reads and parses +// a metadata block header. +// +// Call Block.Parse to parse the metadata block body, and call Block.Skip to +// ignore it. +func New(r io.Reader) (block *Block, err error) { + block = new(Block) + if err = block.parseHeader(r); err != nil { + return block, err + } + block.lr = io.LimitReader(r, block.Length) + return block, nil +} + +// Parse reads and parses the header and body of a metadata block. Use New for +// additional granularity. +func Parse(r io.Reader) (block *Block, err error) { + block, err = New(r) + if err != nil { + return block, err + } + if err = block.Parse(); err != nil { + return block, err + } + return block, nil +} + +// Errors returned by Parse. +var ( + ErrReservedType = errors.New("meta.Block.Parse: reserved block type") + ErrInvalidType = errors.New("meta.Block.Parse: invalid block type") +) + +// Parse reads and parses the metadata block body. +func (block *Block) Parse() error { + switch block.Type { + case TypeStreamInfo: + return block.parseStreamInfo() + case TypePadding: + return block.verifyPadding() + case TypeApplication: + return block.parseApplication() + case TypeSeekTable: + return block.parseSeekTable() + case TypeVorbisComment: + return block.parseVorbisComment() + case TypeCueSheet: + return block.parseCueSheet() + case TypePicture: + return block.parsePicture() + } + if block.Type >= 7 && block.Type <= 126 { + return ErrReservedType + } + return ErrInvalidType +} + +// Skip ignores the contents of the metadata block body. +func (block *Block) Skip() error { + if sr, ok := block.lr.(io.Seeker); ok { + _, err := sr.Seek(0, io.SeekEnd) + return err + } + _, err := io.Copy(ioutil.Discard, block.lr) + return err +} + +// A Header contains information about the type and length of a metadata block. +// +// ref: https://www.xiph.org/flac/format.html#metadata_block_header +type Header struct { + // Metadata block body type. + Type Type + // Length of body data in bytes. + Length int64 + // IsLast specifies if the block is the last metadata block. + IsLast bool +} + +// parseHeader reads and parses the header of a metadata block. +func (block *Block) parseHeader(r io.Reader) error { + // 1 bit: IsLast. + br := bits.NewReader(r) + x, err := br.Read(1) + if err != nil { + // This is the only place a metadata block may return io.EOF, which + // signals a graceful end of a FLAC stream (from a metadata point of + // view). + // + // Note that valid FLAC streams always contain at least one audio frame + // after the last metadata block. Therefore an io.EOF error at this + // location is always invalid. This logic is to be handled by the flac + // package however. + return err + } + if x != 0 { + block.IsLast = true + } + + // 7 bits: Type. + x, err = br.Read(7) + if err != nil { + return unexpected(err) + } + block.Type = Type(x) + + // 24 bits: Length. + x, err = br.Read(24) + if err != nil { + return unexpected(err) + } + block.Length = int64(x) + + return nil +} + +// Type represents the type of a metadata block body. +type Type uint8 + +// Metadata block body types. +const ( + TypeStreamInfo Type = 0 + TypePadding Type = 1 + TypeApplication Type = 2 + TypeSeekTable Type = 3 + TypeVorbisComment Type = 4 + TypeCueSheet Type = 5 + TypePicture Type = 6 +) + +func (t Type) String() string { + switch t { + case TypeStreamInfo: + return "stream info" + case TypePadding: + return "padding" + case TypeApplication: + return "application" + case TypeSeekTable: + return "seek table" + case TypeVorbisComment: + return "vorbis comment" + case TypeCueSheet: + return "cue sheet" + case TypePicture: + return "picture" + default: + return "" + } +} + +// unexpected returns io.ErrUnexpectedEOF if err is io.EOF, and returns err +// otherwise. +func unexpected(err error) error { + if err == io.EOF { + return io.ErrUnexpectedEOF + } + return err +} diff --git a/meta/meta_test.go b/meta/meta_test.go new file mode 100755 index 0000000..268e63a --- /dev/null +++ b/meta/meta_test.go @@ -0,0 +1,260 @@ +package meta_test + +import ( + "bytes" + "io/ioutil" + "reflect" + "testing" + + "github.com/mewkiz/flac" + "github.com/mewkiz/flac/meta" +) + +var golden = []struct { + path string + info *meta.StreamInfo + blocks []*meta.Block +}{ + { + path: "../testdata/59996.flac", + info: &meta.StreamInfo{BlockSizeMin: 0x1000, BlockSizeMax: 0x1000, FrameSizeMin: 0x44c5, FrameSizeMax: 0x4588, SampleRate: 0xac44, NChannels: 0x2, BitsPerSample: 0x18, NSamples: 0x2000, MD5sum: [16]uint8{0x95, 0xba, 0xe5, 0xe2, 0xc7, 0x45, 0xbb, 0x3c, 0xa9, 0x5c, 0xa3, 0xb1, 0x35, 0xc9, 0x43, 0xf4}}, + blocks: []*meta.Block{ + { + Header: meta.Header{Type: 0x4, Length: 202, IsLast: true}, + Body: &meta.VorbisComment{Vendor: "reference libFLAC 1.2.1 20070917", Tags: [][2]string{{"Description", "Waving a bamboo staff"}, {"YEAR", "2008"}, {"ARTIST", "qubodup aka Iwan Gabovitch | qubodup@gmail.com"}, {"COMMENTS", "I release this file into the public domain"}}}, + }, + }, + }, + { + path: "../testdata/172960.flac", + info: &meta.StreamInfo{BlockSizeMin: 0x1000, BlockSizeMax: 0x1000, FrameSizeMin: 0xb7c, FrameSizeMax: 0x256b, SampleRate: 0x17700, NChannels: 0x2, BitsPerSample: 0x10, NSamples: 0xaaa3, MD5sum: [16]uint8{0x76, 0x3d, 0xa8, 0xa5, 0xb7, 0x58, 0xe6, 0x2, 0x61, 0xb4, 0xd4, 0xc2, 0x88, 0x4d, 0x8e, 0xe}}, + blocks: []*meta.Block{ + { + Header: meta.Header{Type: 0x4, Length: 180, IsLast: true}, + Body: &meta.VorbisComment{Vendor: "reference libFLAC 1.2.1 20070917", Tags: [][2]string{{"GENRE", "Sound Clip"}, {"ARTIST", "Iwan 'qubodup' Gabovitch"}, {"Artist Homepage", "http://qubodup.net"}, {"Artist Email", "qubodup@gmail.com"}, {"DATE", "2012"}}}, + }, + }, + }, + { + path: "../testdata/189983.flac", + info: &meta.StreamInfo{BlockSizeMin: 0x1200, BlockSizeMax: 0x1200, FrameSizeMin: 0x94d, FrameSizeMax: 0x264a, SampleRate: 0xac44, NChannels: 0x2, BitsPerSample: 0x10, NSamples: 0x50f4, MD5sum: [16]uint8{0x63, 0x28, 0xed, 0x6d, 0xd3, 0xe, 0x55, 0xfb, 0xa5, 0x73, 0x69, 0x2b, 0xb7, 0x35, 0x73, 0xb7}}, + blocks: []*meta.Block{ + { + Header: meta.Header{Type: 0x4, Length: 40, IsLast: true}, + Body: &meta.VorbisComment{Vendor: "reference libFLAC 1.2.1 20070917", Tags: nil}, + }, + }, + }, + { + path: "testdata/input-SCPAP.flac", + info: &meta.StreamInfo{BlockSizeMin: 0x1200, BlockSizeMax: 0x1200, FrameSizeMin: 0xe, FrameSizeMax: 0x10, SampleRate: 0xac44, NChannels: 0x2, BitsPerSample: 0x10, NSamples: 0x16f8, MD5sum: [16]uint8{0x74, 0xff, 0xd4, 0x73, 0x7e, 0xb5, 0x48, 0x8d, 0x51, 0x2b, 0xe4, 0xaf, 0x58, 0x94, 0x33, 0x62}}, + blocks: []*meta.Block{ + { + Header: meta.Header{Type: 0x3, Length: 180, IsLast: false}, + Body: &meta.SeekTable{Points: []meta.SeekPoint{{SampleNum: 0x0, Offset: 0x0, NSamples: 0x1200}, {SampleNum: 0x1200, Offset: 0xe, NSamples: 0x4f8}, {SampleNum: 0xffffffffffffffff, Offset: 0x0, NSamples: 0x0}, {SampleNum: 0xffffffffffffffff, Offset: 0x0, NSamples: 0x0}, {SampleNum: 0xffffffffffffffff, Offset: 0x0, NSamples: 0x0}, {SampleNum: 0xffffffffffffffff, Offset: 0x0, NSamples: 0x0}, {SampleNum: 0xffffffffffffffff, Offset: 0x0, NSamples: 0x0}, {SampleNum: 0xffffffffffffffff, Offset: 0x0, NSamples: 0x0}, {SampleNum: 0xffffffffffffffff, Offset: 0x0, NSamples: 0x0}, {SampleNum: 0xffffffffffffffff, Offset: 0x0, NSamples: 0x0}}}, + }, + { + Header: meta.Header{Type: 0x5, Length: 540, IsLast: false}, + Body: &meta.CueSheet{MCN: "1234567890123", NLeadInSamples: 0x15888, IsCompactDisc: true, Tracks: []meta.CueSheetTrack{{Offset: 0x0, Num: 0x1, ISRC: "", IsAudio: true, HasPreEmphasis: false, Indicies: []meta.CueSheetTrackIndex{{Offset: 0x0, Num: 0x1}, {Offset: 0x24c, Num: 0x2}}}, {Offset: 0xb7c, Num: 0x2, ISRC: "", IsAudio: true, HasPreEmphasis: false, Indicies: []meta.CueSheetTrackIndex{{Offset: 0x0, Num: 0x1}}}, {Offset: 0x16f8, Num: 0xaa, ISRC: "", IsAudio: true, HasPreEmphasis: false, Indicies: []meta.CueSheetTrackIndex(nil)}}}, + }, + { + Header: meta.Header{Type: 0x1, Length: 4, IsLast: false}, + Body: nil, + }, + { + Header: meta.Header{Type: 0x2, Length: 4, IsLast: false}, + Body: &meta.Application{ID: 0x66616b65, Data: nil}, + }, + { + Header: meta.Header{Type: 0x1, Length: 3201, IsLast: true}, + Body: nil, + }, + }, + }, + { + path: "testdata/input-SCVA.flac", + info: &meta.StreamInfo{BlockSizeMin: 0x1200, BlockSizeMax: 0x1200, FrameSizeMin: 0xe, FrameSizeMax: 0x10, SampleRate: 0xac44, NChannels: 0x2, BitsPerSample: 0x10, NSamples: 0x16f8, MD5sum: [16]uint8{0x74, 0xff, 0xd4, 0x73, 0x7e, 0xb5, 0x48, 0x8d, 0x51, 0x2b, 0xe4, 0xaf, 0x58, 0x94, 0x33, 0x62}}, + blocks: []*meta.Block{ + { + Header: meta.Header{Type: 0x3, Length: 180, IsLast: false}, + Body: &meta.SeekTable{Points: []meta.SeekPoint{{SampleNum: 0x0, Offset: 0x0, NSamples: 0x1200}, {SampleNum: 0x1200, Offset: 0xe, NSamples: 0x4f8}, {SampleNum: 0xffffffffffffffff, Offset: 0x0, NSamples: 0x0}, {SampleNum: 0xffffffffffffffff, Offset: 0x0, NSamples: 0x0}, {SampleNum: 0xffffffffffffffff, Offset: 0x0, NSamples: 0x0}, {SampleNum: 0xffffffffffffffff, Offset: 0x0, NSamples: 0x0}, {SampleNum: 0xffffffffffffffff, Offset: 0x0, NSamples: 0x0}, {SampleNum: 0xffffffffffffffff, Offset: 0x0, NSamples: 0x0}, {SampleNum: 0xffffffffffffffff, Offset: 0x0, NSamples: 0x0}, {SampleNum: 0xffffffffffffffff, Offset: 0x0, NSamples: 0x0}}}, + }, + { + Header: meta.Header{Type: 0x5, Length: 540, IsLast: false}, + Body: &meta.CueSheet{MCN: "1234567890123", NLeadInSamples: 0x15888, IsCompactDisc: true, Tracks: []meta.CueSheetTrack{{Offset: 0x0, Num: 0x1, ISRC: "", IsAudio: true, HasPreEmphasis: false, Indicies: []meta.CueSheetTrackIndex{{Offset: 0x0, Num: 0x1}, {Offset: 0x24c, Num: 0x2}}}, {Offset: 0xb7c, Num: 0x2, ISRC: "", IsAudio: true, HasPreEmphasis: false, Indicies: []meta.CueSheetTrackIndex{{Offset: 0x0, Num: 0x1}}}, {Offset: 0x16f8, Num: 0xaa, ISRC: "", IsAudio: true, HasPreEmphasis: false, Indicies: []meta.CueSheetTrackIndex(nil)}}}, + }, + { + Header: meta.Header{Type: 0x4, Length: 203, IsLast: false}, + Body: &meta.VorbisComment{Vendor: "reference libFLAC 1.1.3 20060805", Tags: [][2]string{{"REPLAYGAIN_TRACK_PEAK", "0.99996948"}, {"REPLAYGAIN_TRACK_GAIN", "-7.89 dB"}, {"REPLAYGAIN_ALBUM_PEAK", "0.99996948"}, {"REPLAYGAIN_ALBUM_GAIN", "-7.89 dB"}, {"artist", "1"}, {"title", "2"}}}, + }, + { + Header: meta.Header{Type: 0x2, Length: 4, IsLast: true}, + Body: &meta.Application{ID: 0x66616b65, Data: nil}, + }, + }, + }, + { + path: "testdata/input-SCVAUP.flac", + info: &meta.StreamInfo{BlockSizeMin: 0x1200, BlockSizeMax: 0x1200, FrameSizeMin: 0xe, FrameSizeMax: 0x10, SampleRate: 0xac44, NChannels: 0x2, BitsPerSample: 0x10, NSamples: 0x16f8, MD5sum: [16]uint8{0x74, 0xff, 0xd4, 0x73, 0x7e, 0xb5, 0x48, 0x8d, 0x51, 0x2b, 0xe4, 0xaf, 0x58, 0x94, 0x33, 0x62}}, + blocks: []*meta.Block{ + { + Header: meta.Header{Type: 0x3, Length: 180, IsLast: false}, + Body: &meta.SeekTable{Points: []meta.SeekPoint{{SampleNum: 0x0, Offset: 0x0, NSamples: 0x1200}, {SampleNum: 0x1200, Offset: 0xe, NSamples: 0x4f8}, {SampleNum: 0xffffffffffffffff, Offset: 0x0, NSamples: 0x0}, {SampleNum: 0xffffffffffffffff, Offset: 0x0, NSamples: 0x0}, {SampleNum: 0xffffffffffffffff, Offset: 0x0, NSamples: 0x0}, {SampleNum: 0xffffffffffffffff, Offset: 0x0, NSamples: 0x0}, {SampleNum: 0xffffffffffffffff, Offset: 0x0, NSamples: 0x0}, {SampleNum: 0xffffffffffffffff, Offset: 0x0, NSamples: 0x0}, {SampleNum: 0xffffffffffffffff, Offset: 0x0, NSamples: 0x0}, {SampleNum: 0xffffffffffffffff, Offset: 0x0, NSamples: 0x0}}}, + }, + { + Header: meta.Header{Type: 0x5, Length: 540, IsLast: false}, + Body: &meta.CueSheet{MCN: "1234567890123", NLeadInSamples: 0x15888, IsCompactDisc: true, Tracks: []meta.CueSheetTrack{{Offset: 0x0, Num: 0x1, ISRC: "", IsAudio: true, HasPreEmphasis: false, Indicies: []meta.CueSheetTrackIndex{{Offset: 0x0, Num: 0x1}, {Offset: 0x24c, Num: 0x2}}}, {Offset: 0xb7c, Num: 0x2, ISRC: "", IsAudio: true, HasPreEmphasis: false, Indicies: []meta.CueSheetTrackIndex{{Offset: 0x0, Num: 0x1}}}, {Offset: 0x16f8, Num: 0xaa, ISRC: "", IsAudio: true, HasPreEmphasis: false, Indicies: []meta.CueSheetTrackIndex(nil)}}}, + }, + { + Header: meta.Header{Type: 0x4, Length: 203, IsLast: false}, + Body: &meta.VorbisComment{Vendor: "reference libFLAC 1.1.3 20060805", Tags: [][2]string{{"REPLAYGAIN_TRACK_PEAK", "0.99996948"}, {"REPLAYGAIN_TRACK_GAIN", "-7.89 dB"}, {"REPLAYGAIN_ALBUM_PEAK", "0.99996948"}, {"REPLAYGAIN_ALBUM_GAIN", "-7.89 dB"}, {"artist", "1"}, {"title", "2"}}}, + }, + { + Header: meta.Header{Type: 0x2, Length: 4, IsLast: false}, + Body: &meta.Application{ID: 0x66616b65, Data: nil}, + }, + { + Header: meta.Header{Type: 0x7e, Length: 0, IsLast: false}, + Body: nil, + }, + { + Header: meta.Header{Type: 0x1, Length: 3201, IsLast: true}, + Body: nil, + }, + }, + }, + { + path: "testdata/input-SCVPAP.flac", + info: &meta.StreamInfo{BlockSizeMin: 0x1200, BlockSizeMax: 0x1200, FrameSizeMin: 0xe, FrameSizeMax: 0x10, SampleRate: 0xac44, NChannels: 0x2, BitsPerSample: 0x10, NSamples: 0x16f8, MD5sum: [16]uint8{0x74, 0xff, 0xd4, 0x73, 0x7e, 0xb5, 0x48, 0x8d, 0x51, 0x2b, 0xe4, 0xaf, 0x58, 0x94, 0x33, 0x62}}, + blocks: []*meta.Block{ + { + Header: meta.Header{Type: 0x3, Length: 180, IsLast: false}, + Body: &meta.SeekTable{Points: []meta.SeekPoint{{SampleNum: 0x0, Offset: 0x0, NSamples: 0x1200}, {SampleNum: 0x1200, Offset: 0xe, NSamples: 0x4f8}, {SampleNum: 0xffffffffffffffff, Offset: 0x0, NSamples: 0x0}, {SampleNum: 0xffffffffffffffff, Offset: 0x0, NSamples: 0x0}, {SampleNum: 0xffffffffffffffff, Offset: 0x0, NSamples: 0x0}, {SampleNum: 0xffffffffffffffff, Offset: 0x0, NSamples: 0x0}, {SampleNum: 0xffffffffffffffff, Offset: 0x0, NSamples: 0x0}, {SampleNum: 0xffffffffffffffff, Offset: 0x0, NSamples: 0x0}, {SampleNum: 0xffffffffffffffff, Offset: 0x0, NSamples: 0x0}, {SampleNum: 0xffffffffffffffff, Offset: 0x0, NSamples: 0x0}}}, + }, + { + Header: meta.Header{Type: 0x5, Length: 540, IsLast: false}, + Body: &meta.CueSheet{MCN: "1234567890123", NLeadInSamples: 0x15888, IsCompactDisc: true, Tracks: []meta.CueSheetTrack{{Offset: 0x0, Num: 0x1, ISRC: "", IsAudio: true, HasPreEmphasis: false, Indicies: []meta.CueSheetTrackIndex{{Offset: 0x0, Num: 0x1}, {Offset: 0x24c, Num: 0x2}}}, {Offset: 0xb7c, Num: 0x2, ISRC: "", IsAudio: true, HasPreEmphasis: false, Indicies: []meta.CueSheetTrackIndex{{Offset: 0x0, Num: 0x1}}}, {Offset: 0x16f8, Num: 0xaa, ISRC: "", IsAudio: true, HasPreEmphasis: false, Indicies: []meta.CueSheetTrackIndex(nil)}}}, + }, + { + Header: meta.Header{Type: 0x4, Length: 203, IsLast: false}, + Body: &meta.VorbisComment{Vendor: "reference libFLAC 1.1.3 20060805", Tags: [][2]string{{"REPLAYGAIN_TRACK_PEAK", "0.99996948"}, {"REPLAYGAIN_TRACK_GAIN", "-7.89 dB"}, {"REPLAYGAIN_ALBUM_PEAK", "0.99996948"}, {"REPLAYGAIN_ALBUM_GAIN", "-7.89 dB"}, {"artist", "1"}, {"title", "2"}}}, + }, + { + Header: meta.Header{Type: 0x1, Length: 4, IsLast: false}, + Body: nil, + }, + { + Header: meta.Header{Type: 0x2, Length: 4, IsLast: false}, + Body: &meta.Application{ID: 0x66616b65, Data: nil}, + }, + { + Header: meta.Header{Type: 0x1, Length: 3201, IsLast: true}, + Body: nil, + }, + }, + }, + { + path: "testdata/input-SVAUP.flac", + info: &meta.StreamInfo{BlockSizeMin: 0x1200, BlockSizeMax: 0x1200, FrameSizeMin: 0xe, FrameSizeMax: 0x10, SampleRate: 0xac44, NChannels: 0x2, BitsPerSample: 0x10, NSamples: 0x16f8, MD5sum: [16]uint8{0x74, 0xff, 0xd4, 0x73, 0x7e, 0xb5, 0x48, 0x8d, 0x51, 0x2b, 0xe4, 0xaf, 0x58, 0x94, 0x33, 0x62}}, + blocks: []*meta.Block{ + { + Header: meta.Header{Type: 0x3, Length: 180, IsLast: false}, + Body: &meta.SeekTable{Points: []meta.SeekPoint{{SampleNum: 0x0, Offset: 0x0, NSamples: 0x1200}, {SampleNum: 0x1200, Offset: 0xe, NSamples: 0x4f8}, {SampleNum: 0xffffffffffffffff, Offset: 0x0, NSamples: 0x0}, {SampleNum: 0xffffffffffffffff, Offset: 0x0, NSamples: 0x0}, {SampleNum: 0xffffffffffffffff, Offset: 0x0, NSamples: 0x0}, {SampleNum: 0xffffffffffffffff, Offset: 0x0, NSamples: 0x0}, {SampleNum: 0xffffffffffffffff, Offset: 0x0, NSamples: 0x0}, {SampleNum: 0xffffffffffffffff, Offset: 0x0, NSamples: 0x0}, {SampleNum: 0xffffffffffffffff, Offset: 0x0, NSamples: 0x0}, {SampleNum: 0xffffffffffffffff, Offset: 0x0, NSamples: 0x0}}}, + }, + { + Header: meta.Header{Type: 0x4, Length: 203, IsLast: false}, + Body: &meta.VorbisComment{Vendor: "reference libFLAC 1.1.3 20060805", Tags: [][2]string{{"REPLAYGAIN_TRACK_PEAK", "0.99996948"}, {"REPLAYGAIN_TRACK_GAIN", "-7.89 dB"}, {"REPLAYGAIN_ALBUM_PEAK", "0.99996948"}, {"REPLAYGAIN_ALBUM_GAIN", "-7.89 dB"}, {"artist", "1"}, {"title", "2"}}}, + }, + { + Header: meta.Header{Type: 0x2, Length: 4, IsLast: false}, + Body: &meta.Application{ID: 0x66616b65, Data: nil}, + }, + { + Header: meta.Header{Type: 0x7e, Length: 0, IsLast: false}, + Body: nil, + }, + { + Header: meta.Header{Type: 0x1, Length: 3201, IsLast: true}, + Body: nil, + }, + }, + }, + { + path: "testdata/input-VA.flac", + info: &meta.StreamInfo{BlockSizeMin: 0x1200, BlockSizeMax: 0x1200, FrameSizeMin: 0xe, FrameSizeMax: 0x10, SampleRate: 0xac44, NChannels: 0x2, BitsPerSample: 0x10, NSamples: 0x16f8, MD5sum: [16]uint8{0x74, 0xff, 0xd4, 0x73, 0x7e, 0xb5, 0x48, 0x8d, 0x51, 0x2b, 0xe4, 0xaf, 0x58, 0x94, 0x33, 0x62}}, + blocks: []*meta.Block{ + { + Header: meta.Header{Type: 0x4, Length: 203, IsLast: false}, + Body: &meta.VorbisComment{Vendor: "reference libFLAC 1.1.3 20060805", Tags: [][2]string{{"REPLAYGAIN_TRACK_PEAK", "0.99996948"}, {"REPLAYGAIN_TRACK_GAIN", "-7.89 dB"}, {"REPLAYGAIN_ALBUM_PEAK", "0.99996948"}, {"REPLAYGAIN_ALBUM_GAIN", "-7.89 dB"}, {"artist", "1"}, {"title", "2"}}}, + }, + { + Header: meta.Header{Type: 0x2, Length: 4, IsLast: true}, + Body: &meta.Application{ID: 0x66616b65, Data: nil}, + }, + }, + }, +} + +func TestParseBlocks(t *testing.T) { + for _, g := range golden { + stream, err := flac.ParseFile(g.path) + if err != nil { + t.Fatal(err) + } + defer stream.Close() + blocks := stream.Blocks + + if len(blocks) != len(g.blocks) { + t.Errorf("path=%q: invalid number of metadata blocks; expected %d, got %d", g.path, len(g.blocks), len(blocks)) + continue + } + + got := stream.Info + want := g.info + if !reflect.DeepEqual(got, want) { + t.Errorf("path=%q: metadata StreamInfo block bodies differ; expected %#v, got %#v", g.path, want, got) + } + + for blockNum, got := range blocks { + want := g.blocks[blockNum] + if !reflect.DeepEqual(got.Header, want.Header) { + t.Errorf("path=%q, blockNum=%d: metadata block headers differ; expected %#v, got %#v", g.path, blockNum, want.Header, got.Header) + } + if !reflect.DeepEqual(got.Body, want.Body) { + t.Errorf("path=%q, blockNum=%d: metadata block bodies differ; expected %#v, got %#v", g.path, blockNum, want.Body, got.Body) + } + } + } +} + +func TestParsePicture(t *testing.T) { + stream, err := flac.ParseFile("testdata/silence.flac") + if err != nil { + t.Fatal(err) + } + defer stream.Close() + + want, err := ioutil.ReadFile("testdata/silence.jpg") + if err != nil { + t.Fatal(err) + } + + for _, block := range stream.Blocks { + if block.Type == meta.TypePicture { + pic := block.Body.(*meta.Picture) + got := pic.Data + if !bytes.Equal(got, want) { + t.Errorf("picture data differ; expected %v, got %v", want, got) + } + break + } + } +} + +// TODO: better error verification than string-based comparisons. +func TestMissingValue(t *testing.T) { + _, err := flac.ParseFile("testdata/missing-value.flac") + if err.Error() != `meta.Block.parseVorbisComment: unable to locate '=' in vector "title 2"` { + t.Fatal(err) + } +} diff --git a/meta/padding.go b/meta/padding.go new file mode 100755 index 0000000..0d66fa3 --- /dev/null +++ b/meta/padding.go @@ -0,0 +1,39 @@ +package meta + +import ( + "errors" + "io" + "io/ioutil" +) + +// verifyPadding verifies the body of a Padding metadata block. It should only +// contain zero-padding. +// +// ref: https://www.xiph.org/flac/format.html#metadata_block_padding +func (block *Block) verifyPadding() error { + zr := zeros{r: block.lr} + _, err := io.Copy(ioutil.Discard, zr) + return err +} + +// Errors returned by zeros.Read. +var ( + ErrInvalidPadding = errors.New("invalid padding") +) + +// zeros implements an io.Reader, with a Read method which returns an error if +// any byte read isn't zero. +type zeros struct { + r io.Reader +} + +// Read returns an error if any byte read isn't zero. +func (zr zeros) Read(p []byte) (n int, err error) { + n, err = zr.r.Read(p) + for i := 0; i < n; i++ { + if p[i] != 0 { + return n, ErrInvalidPadding + } + } + return n, err +} diff --git a/meta/picture.go b/meta/picture.go new file mode 100755 index 0000000..5c52d8a --- /dev/null +++ b/meta/picture.go @@ -0,0 +1,120 @@ +package meta + +import ( + "encoding/binary" + "io" +) + +// Picture contains the image data of an embedded picture. +// +// ref: https://www.xiph.org/flac/format.html#metadata_block_picture +type Picture struct { + // Picture type according to the ID3v2 APIC frame: + // + // 0: Other + // 1: 32x32 pixels 'file icon' (PNG only) + // 2: Other file icon + // 3: Cover (front) + // 4: Cover (back) + // 5: Leaflet page + // 6: Media (e.g. label side of CD) + // 7: Lead artist/lead performer/soloist + // 8: Artist/performer + // 9: Conductor + // 10: Band/Orchestra + // 11: Composer + // 12: Lyricist/text writer + // 13: Recording Location + // 14: During recording + // 15: During performance + // 16: Movie/video screen capture + // 17: A bright coloured fish + // 18: Illustration + // 19: Band/artist logotype + // 20: Publisher/Studio logotype + // + // ref: http://id3.org/id3v2.4.0-frames + Type uint32 + // MIME type string. The MIME type "-->" specifies that the picture data is + // to be interpreted as an URL instead of image data. + MIME string + // Description of the picture. + Desc string + // Image dimensions. + Width, Height uint32 + // Color depth in bits-per-pixel. + Depth uint32 + // Number of colors in palette; 0 for non-indexed images. + NPalColors uint32 + // Image data. + Data []byte +} + +// parsePicture reads and parses the body of a Picture metadata block. +func (block *Block) parsePicture() error { + // 32 bits: Type. + pic := new(Picture) + block.Body = pic + err := binary.Read(block.lr, binary.BigEndian, &pic.Type) + if err != nil { + return unexpected(err) + } + + // 32 bits: (MIME type length). + var x uint32 + if err = binary.Read(block.lr, binary.BigEndian, &x); err != nil { + return unexpected(err) + } + + // (MIME type length) bytes: MIME. + mime, err := readString(block.lr, int(x)) + if err != nil { + return unexpected(err) + } + pic.MIME = mime + + // 32 bits: (description length). + if err = binary.Read(block.lr, binary.BigEndian, &x); err != nil { + return unexpected(err) + } + + // (description length) bytes: Desc. + desc, err := readString(block.lr, int(x)) + if err != nil { + return unexpected(err) + } + pic.Desc = desc + + // 32 bits: Width. + if err = binary.Read(block.lr, binary.BigEndian, &pic.Width); err != nil { + return unexpected(err) + } + + // 32 bits: Height. + if err = binary.Read(block.lr, binary.BigEndian, &pic.Height); err != nil { + return unexpected(err) + } + + // 32 bits: Depth. + if err = binary.Read(block.lr, binary.BigEndian, &pic.Depth); err != nil { + return unexpected(err) + } + + // 32 bits: NPalColors. + if err = binary.Read(block.lr, binary.BigEndian, &pic.NPalColors); err != nil { + return unexpected(err) + } + + // 32 bits: (data length). + if err = binary.Read(block.lr, binary.BigEndian, &x); err != nil { + return unexpected(err) + } + if x == 0 { + return nil + } + + // (data length) bytes: Data. + pic.Data = make([]byte, x) + _, err = io.ReadFull(block.lr, pic.Data) + return unexpected(err) +} diff --git a/meta/reader.go b/meta/reader.go new file mode 100755 index 0000000..58f55b6 --- /dev/null +++ b/meta/reader.go @@ -0,0 +1,24 @@ +package meta + +import "io" + +// readString reads and returns exactly n bytes from the provided io.Reader. +// +// The error is io.EOF only if no bytes were read. If an io.EOF happens after +// reading some but not all the bytes, ReadFull returns io.ErrUnexpectedEOF. On +// return, n == len(buf) if and only if err == nil. +func readString(r io.Reader, n int) (string, error) { + // readBuf is the local buffer used by readBytes. + var backingArray [4096]byte // hopefully allocated on stack. + readBuf := backingArray[:] + if n > len(readBuf) { + // The local buffer is initially 4096 bytes and will grow automatically if + // so required. + readBuf = make([]byte, n) + } + _, err := io.ReadFull(r, readBuf[:n]) + if err != nil { + return "", err + } + return string(readBuf[:n]), nil +} diff --git a/meta/seektable.go b/meta/seektable.go new file mode 100755 index 0000000..c0b96ce --- /dev/null +++ b/meta/seektable.go @@ -0,0 +1,67 @@ +package meta + +import ( + "encoding/binary" + "errors" + "fmt" +) + +// SeekTable contains one or more pre-calculated audio frame seek points. +// +// ref: https://www.xiph.org/flac/format.html#metadata_block_seektable +type SeekTable struct { + // One or more seek points. + Points []SeekPoint +} + +// parseSeekTable reads and parses the body of a SeekTable metadata block. +func (block *Block) parseSeekTable() error { + // The number of seek points is derived from the header length, divided by + // the size of a SeekPoint; which is 18 bytes. + n := block.Length / 18 + if n < 1 { + return errors.New("meta.Block.parseSeekTable: at least one seek point is required") + } + table := &SeekTable{Points: make([]SeekPoint, n)} + block.Body = table + var prev uint64 + for i := range table.Points { + point := &table.Points[i] + err := binary.Read(block.lr, binary.BigEndian, point) + if err != nil { + return unexpected(err) + } + // Seek points within a table must be sorted in ascending order by sample + // number. Each seek point must have a unique sample number, except for + // placeholder points. + sampleNum := point.SampleNum + if i != 0 && sampleNum != PlaceholderPoint { + switch { + case sampleNum < prev: + return fmt.Errorf("meta.Block.parseSeekTable: invalid seek point order; sample number (%d) < prev (%d)", sampleNum, prev) + case sampleNum == prev: + return fmt.Errorf("meta.Block.parseSeekTable: duplicate seek point with sample number (%d)", sampleNum) + } + } + } + return nil +} + +// A SeekPoint specifies the byte offset and initial sample number of a given +// target frame. +// +// ref: https://www.xiph.org/flac/format.html#seekpoint +type SeekPoint struct { + // Sample number of the first sample in the target frame, or + // 0xFFFFFFFFFFFFFFFF for a placeholder point. + SampleNum uint64 + // Offset in bytes from the first byte of the first frame header to the first + // byte of the target frame's header. + Offset uint64 + // Number of samples in the target frame. + NSamples uint16 +} + +// PlaceholderPoint represent the sample number used to specify placeholder seek +// points. +const PlaceholderPoint = 0xFFFFFFFFFFFFFFFF diff --git a/meta/streaminfo.go b/meta/streaminfo.go new file mode 100755 index 0000000..f72fbfc --- /dev/null +++ b/meta/streaminfo.go @@ -0,0 +1,116 @@ +package meta + +import ( + "crypto/md5" + "errors" + "fmt" + "io" + + "github.com/mewkiz/flac/internal/bits" +) + +// StreamInfo contains the basic properties of a FLAC audio stream, such as its +// sample rate and channel count. It is the only mandatory metadata block and +// must be present as the first metadata block of a FLAC stream. +// +// ref: https://www.xiph.org/flac/format.html#metadata_block_streaminfo +type StreamInfo struct { + // Minimum block size (in samples) used in the stream; between 16 and 65535 + // samples. + BlockSizeMin uint16 + // Maximum block size (in samples) used in the stream; between 16 and 65535 + // samples. + BlockSizeMax uint16 + // Minimum frame size in bytes; a 0 value implies unknown. + FrameSizeMin uint32 + // Maximum frame size in bytes; a 0 value implies unknown. + FrameSizeMax uint32 + // Sample rate in Hz; between 1 and 655350 Hz. + SampleRate uint32 + // Number of channels; between 1 and 8 channels. + NChannels uint8 + // Sample size in bits-per-sample; between 4 and 32 bits. + BitsPerSample uint8 + // Total number of inter-channel samples in the stream. One second of 44.1 + // KHz audio will have 44100 samples regardless of the number of channels. A + // 0 value implies unknown. + NSamples uint64 + // MD5 checksum of the unencoded audio data. + MD5sum [md5.Size]uint8 +} + +// parseStreamInfo reads and parses the body of a StreamInfo metadata block. +func (block *Block) parseStreamInfo() error { + // 16 bits: BlockSizeMin. + br := bits.NewReader(block.lr) + x, err := br.Read(16) + if err != nil { + return unexpected(err) + } + if x < 16 { + return fmt.Errorf("meta.Block.parseStreamInfo: invalid minimum block size (%d); expected >= 16", x) + } + si := new(StreamInfo) + block.Body = si + si.BlockSizeMin = uint16(x) + + // 16 bits: BlockSizeMax. + x, err = br.Read(16) + if err != nil { + return unexpected(err) + } + if x < 16 { + return fmt.Errorf("meta.Block.parseStreamInfo: invalid maximum block size (%d); expected >= 16", x) + } + si.BlockSizeMax = uint16(x) + + // 24 bits: FrameSizeMin. + x, err = br.Read(24) + if err != nil { + return unexpected(err) + } + si.FrameSizeMin = uint32(x) + + // 24 bits: FrameSizeMax. + x, err = br.Read(24) + if err != nil { + return unexpected(err) + } + si.FrameSizeMax = uint32(x) + + // 20 bits: SampleRate. + x, err = br.Read(20) + if err != nil { + return unexpected(err) + } + if x == 0 { + return errors.New("meta.Block.parseStreamInfo: invalid sample rate (0)") + } + si.SampleRate = uint32(x) + + // 3 bits: NChannels. + x, err = br.Read(3) + if err != nil { + return unexpected(err) + } + // x contains: (number of channels) - 1 + si.NChannels = uint8(x + 1) + + // 5 bits: BitsPerSample. + x, err = br.Read(5) + if err != nil { + return unexpected(err) + } + // x contains: (bits-per-sample) - 1 + si.BitsPerSample = uint8(x + 1) + + // 36 bits: NSamples. + si.NSamples, err = br.Read(36) + if err != nil { + return unexpected(err) + } + + // 16 bytes: MD5sum. + _, err = io.ReadFull(block.lr, si.MD5sum[:]) + return unexpected(err) +} diff --git a/meta/testdata/README.md b/meta/testdata/README.md new file mode 100755 index 0000000..f2ec6b0 --- /dev/null +++ b/meta/testdata/README.md @@ -0,0 +1,35 @@ +# Testcase Licences + +## BSD License + +The following testcase sounds have been copied from the [reference implementation] library, which is released under a [BSD license]. + +* input-SCPAP.flac +* input-SCVA.flac +* input-SCVAUP.flac +* input-SCVPAP.flac +* input-SVAUP.flac +* input-VA.flac +* `missing-value.flac`, created using the following command. + +```shell +sed 's/title=/title /' input-SCVA.flac > missing-value.flac +``` + +[reference implementation]: https://git.xiph.org/?p=flac.git +[BSD license]: https://git.xiph.org/?p=flac.git;a=blob_plain;f=COPYING.Xiph + +## Public domain + +The following testcase images and sounds have been released into the [public domain]. + +* [silence.jpg](http://www.pdpics.com/photo/2546-silence-word-magnified/) +* `silence.flac`, created using the following commands. + +```shell +ffmpeg -f lavfi -i "aevalsrc=0|0:d=3" silence.wav +flac silence.wav +metaflac --import-picture=silence.jpg silence.flac +``` + +[public domain]: https://creativecommons.org/publicdomain/zero/1.0/ diff --git a/meta/testdata/silence.jpg b/meta/testdata/silence.jpg new file mode 100755 index 0000000000000000000000000000000000000000..6236e6d9d0347457b9863ad2969468e691b2a21e GIT binary patch literal 70585 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