async-codec-lite-0.0.2/.cargo_vcs_info.json0000644000000001360000000000100141560ustar { "git": { "sha1": "157a0e1225bde8b13d218e476da2a42b00e380dd" }, "path_in_vcs": "" }async-codec-lite-0.0.2/.editorconfig000064400000000000000000000002530072674642500154530ustar 00000000000000root = true [*] charset = utf-8 end_of_line = lf indent_size = 2 indent_style = space insert_final_newline = true trim_trailing_whitespace = true [*.rs] indent_size = 4 async-codec-lite-0.0.2/.gitattributes000064400000000000000000000000230072674642500156640ustar 00000000000000* text=auto eol=lf async-codec-lite-0.0.2/.github/codecov.yml000064400000000000000000000002020072674642500164750ustar 00000000000000coverage: status: project: default: informational: true patch: default: informational: true async-codec-lite-0.0.2/.github/dependabot.yml000064400000000000000000000001540072674642500171660ustar 00000000000000version: 2 updates: - package-ecosystem: "cargo" directory: "/" schedule: interval: "daily" async-codec-lite-0.0.2/.github/workflows/main.yaml000064400000000000000000000132110072674642500202010ustar 00000000000000name: main on: push: branches: - main pull_request: branches: - main jobs: # skip ci if the last commit contains the appropriate tag skip-commit: name: Conditionally skip ci runs-on: ubuntu-latest steps: - if: "contains(github.event.head_commit.message, '[skip-ci]') || contains(github.event.head_commit.message, '[skip ci]') || contains(github.event.head_commit.message, '[ci-skip]') || contains(github.event.head_commit.message, '[ci skip]')" run: exit 78 # verify that Cargo.lock passes audit cargo-audit: name: Run cargo audit needs: [skip-commit] runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - name: Fetch latest release version of cargo-audit run: | mkdir -p .github/caching cargo search cargo-audit | grep '^cargo-audit' | awk '{gsub(/"/,"",$3); print $3}' > .github/caching/cargo-audit.lock - name: Cache cargo-audit/bin id: cache-cargo-audit uses: actions/cache@v1 with: path: ${{ runner.tool_cache }}/cargo-audit/bin key: cargo-audit-bin-${{ hashFiles('.github/caching/cargo-audit.lock') }} - name: Install cargo-audit if: "steps.cache-cargo-audit.outputs.cache-hit != 'true'" uses: actions-rs/cargo@v1 with: command: install args: --root ${{ runner.tool_cache }}/cargo-audit --force cargo-audit - run: echo "${{ runner.tool_cache }}/cargo-audit/bin" >> $GITHUB_PATH - run: cargo audit # verify that project passes clippy lints cargo-clippy: name: Run cargo clippy needs: [skip-commit] runs-on: ubuntu-latest env: RUST_TOOLCHAIN: stable steps: - uses: actions/checkout@v2 - name: Install Rust toolchain uses: actions-rs/toolchain@v1 with: profile: minimal components: clippy toolchain: ${{ env.RUST_TOOLCHAIN }} override: true - name: Run cargo clippy uses: actions-rs/clippy-check@v1 with: token: ${{ secrets.GITHUB_TOKEN }} args: --all-features --all-targets --workspace -- -D warnings # build the documentation cargo-docs: name: Run cargo docs needs: [skip-commit] runs-on: ubuntu-latest env: RUST_TOOLCHAIN: nightly steps: - uses: actions/checkout@v2 - name: Install Rust toolchain uses: actions-rs/toolchain@v1 with: profile: minimal toolchain: ${{ env.RUST_TOOLCHAIN }} override: true - name: Run cargo doc uses: actions-rs/cargo@v1 with: command: doc args: --all-features --no-deps - uses: peaceiris/actions-gh-pages@v3 with: github_token: ${{ secrets.GITHUB_TOKEN }} publish_dir: ./target/doc if: github.event_name == 'push' && github.ref == 'refs/heads/main' # verify that code is formatted cargo-fmt: name: Run cargo fmt needs: [skip-commit] runs-on: ubuntu-latest env: RUST_TOOLCHAIN: nightly steps: - uses: actions/checkout@v2 - name: Install Rust toolchain uses: actions-rs/toolchain@v1 with: profile: minimal components: rustfmt toolchain: ${{ env.RUST_TOOLCHAIN }} override: true - name: Run cargo fmt uses: actions-rs/cargo@v1 with: toolchain: ${{ env.RUST_TOOLCHAIN }} command: fmt args: --all -- --check # verify that tests pass and calculate coverage with tarpaulin cargo-test-coverage: name: Run cargo tarpaulin needs: [skip-commit] runs-on: ubuntu-latest env: RUST_TOOLCHAIN: nightly steps: - uses: actions/checkout@v2 - name: Install Rust toolchain uses: actions-rs/toolchain@v1 with: profile: minimal toolchain: ${{ env.RUST_TOOLCHAIN }} override: true - name: Fetch latest release version of cargo-tarpaulin run: | mkdir -p .github/caching curl -sL https://api.github.com/repos/xd009642/tarpaulin/releases/latest | jq -r '.name' > .github/caching/cargo-tarpaulin.lock - name: Cache cargo-tarpaulin/bin id: cache-cargo-tarpaulin uses: actions/cache@v1 with: path: ${{ runner.tool_cache }}/cargo-tarpaulin/bin key: cargo-tarpaulin-bin-${{ hashFiles('.github/caching/cargo-tarpaulin.lock') }} - name: Install cargo-tarpaulin if: "steps.cache-cargo-tarpaulin.outputs.cache-hit != 'true'" uses: actions-rs/cargo@v1 with: command: install args: --root ${{ runner.tool_cache }}/cargo-tarpaulin --force cargo-tarpaulin - run: echo "${{ runner.tool_cache }}/cargo-tarpaulin/bin" >> $GITHUB_PATH - name: Run cargo tarpaulin uses: actions-rs/cargo@v1 with: command: tarpaulin args: --all-features - name: Upload to codecov.io uses: codecov/codecov-action@v1 with: token: ${{ secrets.CODECOV_TOKEN }} fail_ci_if_error: true # verify that tests pass cargo-test: name: Run cargo test needs: [skip-commit] strategy: matrix: os: [macos-latest, ubuntu-latest, windows-latest] runs-on: ${{ matrix.os }} env: RUST_TOOLCHAIN: stable steps: - uses: actions/checkout@v2 - name: Install Rust toolchain uses: actions-rs/toolchain@v1 with: profile: minimal toolchain: ${{ env.RUST_TOOLCHAIN }} override: yes - name: Run cargo test uses: actions-rs/cargo@v1 with: command: test args: --all-features async-codec-lite-0.0.2/.gitignore000064400000000000000000000275450072674642500150030ustar 00000000000000### Archives # It's better to unpack these files and commit the raw source because # git has its own built in compression methods. *.7z *.jar *.rar *.zip *.gz *.gzip *.tgz *.bzip *.bzip2 *.bz2 *.xz *.lzma *.cab *.xar # Packing-only formats *.iso *.tar # Package management formats *.dmg *.xpi *.gem *.egg *.deb *.rpm *.msi *.msm *.msp *.txz ### Backup *.bak *.gho *.ori *.orig *.tmp ### Diff *.patch *.diff ### Eclipse .metadata bin/ tmp/ *.tmp *.bak *.swp *~.nib local.properties .settings/ .loadpath .recommenders # External tool builders .externalToolBuilders/ # Locally stored "Eclipse launch configurations" *.launch # PyDev specific (Python IDE for Eclipse) *.pydevproject # CDT-specific (C/C++ Development Tooling) .cproject # CDT- autotools .autotools # Java annotation processor (APT) .factorypath # PDT-specific (PHP Development Tools) .buildpath # sbteclipse plugin .target # Tern plugin .tern-project # TeXlipse plugin .texlipse # STS (Spring Tool Suite) .springBeans # Code Recommenders .recommenders/ # Annotation Processing .apt_generated/ .apt_generated_test/ # Scala IDE specific (Scala & Java development for Eclipse) .cache-main .scala_dependencies .worksheet # Uncomment this line if you wish to ignore the project description file. # Typically, this file would be tracked if it contains build/dependency configurations: #.project ### Emacs # -*- mode: gitignore; -*- *~ \#*\# /.emacs.desktop /.emacs.desktop.lock *.elc auto-save-list tramp .\#* # Org-mode .org-id-locations *_archive # flymake-mode *_flymake.* # eshell files /eshell/history /eshell/lastdir # elpa packages /elpa/ # reftex files *.rel # AUCTeX auto folder /auto/ # cask packages .cask/ dist/ # Flycheck flycheck_*.el # server auth directory /server/ # projectiles files .projectile # directory configuration .dir-locals.el # network security /network-security.data ### Linux *~ # temporary files which can be created if a process still has a handle open of a deleted file .fuse_hidden* # KDE directory preferences .directory # Linux trash folder which might appear on any partition or disk .Trash-* # .nfs files are created when an open file is removed but is still being accessed .nfs* ### macOS # General .DS_Store .AppleDouble .LSOverride # Icon must end with two \r Icon # Thumbnails ._* # Files that might appear in the root of a volume .DocumentRevisions-V100 .fseventsd .Spotlight-V100 .TemporaryItems .Trashes .VolumeIcon.icns .com.apple.timemachine.donotpresent # Directories potentially created on remote AFP share .AppleDB .AppleDesktop Network Trash Folder Temporary Items .apdisk ### Rust # Generated by Cargo # will have compiled files and executables debug/ target/ # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries # More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html Cargo.lock # These are backup files generated by rustfmt **/*.rs.bk ### SublimeText # Cache files for Sublime Text *.tmlanguage.cache *.tmPreferences.cache *.stTheme.cache # Workspace files are user-specific *.sublime-workspace # Project files should be checked into the repository, unless a significant # proportion of contributors will probably not be using Sublime Text # *.sublime-project # SFTP configuration file sftp-config.json sftp-config-alt*.json # Package control specific files Package Control.last-run Package Control.ca-list Package Control.ca-bundle Package Control.system-ca-bundle Package Control.cache/ Package Control.ca-certs/ Package Control.merged-ca-bundle Package Control.user-ca-bundle oscrypto-ca-bundle.crt bh_unicode_properties.cache # Sublime-github package stores a github token in this file # https://packagecontrol.io/packages/sublime-github GitHub.sublime-settings ### Tags # Ignore tags created by etags, ctags, gtags (GNU global) and cscope TAGS .TAGS !TAGS/ tags .tags !tags/ gtags.files GTAGS GRTAGS GPATH GSYMS cscope.files cscope.out cscope.in.out cscope.po.out ### TextMate *.tmproj *.tmproject tmtags ### Vim # Swap [._]*.s[a-v][a-z] !*.svg # comment out if you don't need vector files [._]*.sw[a-p] [._]s[a-rt-v][a-z] [._]ss[a-gi-z] [._]sw[a-p] # Session Session.vim Sessionx.vim # Temporary .netrwhist *~ # Auto-generated tag files tags # Persistent undo [._]*.un~ ### VisualStudio ## Ignore Visual Studio temporary files, build results, and ## files generated by popular Visual Studio add-ons. ## ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore # User-specific files *.rsuser *.suo *.user *.userosscache *.sln.docstates # User-specific files (MonoDevelop/Xamarin Studio) *.userprefs # Mono auto generated files mono_crash.* # Build results [Dd]ebug/ [Dd]ebugPublic/ [Rr]elease/ [Rr]eleases/ x64/ x86/ [Ww][Ii][Nn]32/ [Aa][Rr][Mm]/ [Aa][Rr][Mm]64/ bld/ [Bb]in/ [Oo]bj/ [Ll]og/ [Ll]ogs/ # Visual Studio 2015/2017 cache/options directory .vs/ # Uncomment if you have tasks that create the project's static files in wwwroot #wwwroot/ # Visual Studio 2017 auto generated files Generated\ Files/ # MSTest test Results [Tt]est[Rr]esult*/ [Bb]uild[Ll]og.* # NUnit *.VisualState.xml TestResult.xml nunit-*.xml # Build Results of an ATL Project [Dd]ebugPS/ [Rr]eleasePS/ dlldata.c # Benchmark Results BenchmarkDotNet.Artifacts/ # .NET Core project.lock.json project.fragment.lock.json artifacts/ # ASP.NET Scaffolding ScaffoldingReadMe.txt # StyleCop StyleCopReport.xml # Files built by Visual Studio *_i.c *_p.c *_h.h *.ilk *.meta *.obj *.iobj *.pch *.pdb *.ipdb *.pgc *.pgd *.rsp *.sbr *.tlb *.tli *.tlh *.tmp *.tmp_proj *_wpftmp.csproj *.log *.vspscc *.vssscc .builds *.pidb *.svclog *.scc # Chutzpah Test files _Chutzpah* # Visual C++ cache files ipch/ *.aps *.ncb *.opendb *.opensdf *.sdf *.cachefile *.VC.db *.VC.VC.opendb # Visual Studio profiler *.psess *.vsp *.vspx *.sap # Visual Studio Trace Files *.e2e # TFS 2012 Local Workspace $tf/ # Guidance Automation Toolkit *.gpState # ReSharper is a .NET coding add-in _ReSharper*/ *.[Rr]e[Ss]harper *.DotSettings.user # TeamCity is a build add-in _TeamCity* # DotCover is a Code Coverage Tool *.dotCover # AxoCover is a Code Coverage Tool .axoCover/* !.axoCover/settings.json # Coverlet is a free, cross platform Code Coverage Tool coverage*.json coverage*.xml coverage*.info # Visual Studio code coverage results *.coverage *.coveragexml # NCrunch _NCrunch_* .*crunch*.local.xml nCrunchTemp_* # MightyMoose *.mm.* AutoTest.Net/ # Web workbench (sass) .sass-cache/ # Installshield output folder [Ee]xpress/ # DocProject is a documentation generator add-in DocProject/buildhelp/ DocProject/Help/*.HxT DocProject/Help/*.HxC DocProject/Help/*.hhc DocProject/Help/*.hhk DocProject/Help/*.hhp DocProject/Help/Html2 DocProject/Help/html # Click-Once directory publish/ # Publish Web Output *.[Pp]ublish.xml *.azurePubxml # Note: Comment the next line if you want to checkin your web deploy settings, # but database connection strings (with potential passwords) will be unencrypted *.pubxml *.publishproj # Microsoft Azure Web App publish settings. Comment the next line if you want to # checkin your Azure Web App publish settings, but sensitive information contained # in these scripts will be unencrypted PublishScripts/ # NuGet Packages *.nupkg # NuGet Symbol Packages *.snupkg # The packages folder can be ignored because of Package Restore **/[Pp]ackages/* # except build/, which is used as an MSBuild target. !**/[Pp]ackages/build/ # Uncomment if necessary however generally it will be regenerated when needed #!**/[Pp]ackages/repositories.config # NuGet v3's project.json files produces more ignorable files *.nuget.props *.nuget.targets # Microsoft Azure Build Output csx/ *.build.csdef # Microsoft Azure Emulator ecf/ rcf/ # Windows Store app package directories and files AppPackages/ BundleArtifacts/ Package.StoreAssociation.xml _pkginfo.txt *.appx *.appxbundle *.appxupload # Visual Studio cache files # files ending in .cache can be ignored *.[Cc]ache # but keep track of directories ending in .cache !?*.[Cc]ache/ # Others ClientBin/ ~$* *~ *.dbmdl *.dbproj.schemaview *.jfm *.pfx *.publishsettings orleans.codegen.cs # Including strong name files can present a security risk # (https://github.com/github/gitignore/pull/2483#issue-259490424) #*.snk # Since there are multiple workflows, uncomment next line to ignore bower_components # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) #bower_components/ # RIA/Silverlight projects Generated_Code/ # Backup & report files from converting an old project file # to a newer Visual Studio version. Backup files are not needed, # because we have git ;-) _UpgradeReport_Files/ Backup*/ UpgradeLog*.XML UpgradeLog*.htm ServiceFabricBackup/ *.rptproj.bak # SQL Server files *.mdf *.ldf *.ndf # Business Intelligence projects *.rdl.data *.bim.layout *.bim_*.settings *.rptproj.rsuser *- [Bb]ackup.rdl *- [Bb]ackup ([0-9]).rdl *- [Bb]ackup ([0-9][0-9]).rdl # Microsoft Fakes FakesAssemblies/ # GhostDoc plugin setting file *.GhostDoc.xml # Node.js Tools for Visual Studio .ntvs_analysis.dat node_modules/ # Visual Studio 6 build log *.plg # Visual Studio 6 workspace options file *.opt # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) *.vbw # Visual Studio LightSwitch build output **/*.HTMLClient/GeneratedArtifacts **/*.DesktopClient/GeneratedArtifacts **/*.DesktopClient/ModelManifest.xml **/*.Server/GeneratedArtifacts **/*.Server/ModelManifest.xml _Pvt_Extensions # Paket dependency manager .paket/paket.exe paket-files/ # FAKE - F# Make .fake/ # CodeRush personal settings .cr/personal # Python Tools for Visual Studio (PTVS) __pycache__/ *.pyc # Cake - Uncomment if you are using it # tools/** # !tools/packages.config # Tabs Studio *.tss # Telerik's JustMock configuration file *.jmconfig # BizTalk build output *.btp.cs *.btm.cs *.odx.cs *.xsd.cs # OpenCover UI analysis results OpenCover/ # Azure Stream Analytics local run output ASALocalRun/ # MSBuild Binary and Structured Log *.binlog # NVidia Nsight GPU debugger configuration file *.nvuser # MFractors (Xamarin productivity tool) working folder .mfractor/ # Local History for Visual Studio .localhistory/ # BeatPulse healthcheck temp database healthchecksdb # Backup folder for Package Reference Convert tool in Visual Studio 2017 MigrationBackup/ # Ionide (cross platform F# VS Code tools) working folder .ionide/ # Fody - auto-generated XML schema FodyWeavers.xsd ### Xcode # Xcode # # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore ## User settings xcuserdata/ ## compatibility with Xcode 8 and earlier (ignoring not required starting Xcode 9) *.xcscmblueprint *.xccheckout ## compatibility with Xcode 3 and earlier (ignoring not required starting Xcode 4) build/ DerivedData/ *.moved-aside *.pbxuser !default.pbxuser *.mode1v3 !default.mode1v3 *.mode2v3 !default.mode2v3 *.perspectivev3 !default.perspectivev3 ## Gcc Patch /*.gcno ### Windows # Windows thumbnail cache files Thumbs.db Thumbs.db:encryptable ehthumbs.db ehthumbs_vista.db # Dump file *.stackdump # Folder config file [Dd]esktop.ini # Recycle Bin used on file shares $RECYCLE.BIN/ # Windows Installer files *.cab *.msi *.msix *.msm *.msp # Windows shortcuts *.lnk async-codec-lite-0.0.2/Cargo.toml0000644000000031530000000000100121560ustar # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies. # # If you are reading this file be aware that the original Cargo.toml # will likely look very different (and much more reasonable). # See Cargo.toml.orig for the original contents. [package] edition = "2021" name = "async-codec-lite" version = "0.0.2" authors = [""] description = """ Adaptors from AsyncRead/AsyncWrite to Stream/Sink using futures. """ license = "Apache-2.0 WITH LLVM-exception AND MIT" resolver = "2" [package.metadata.docs.rs] all-features = true [dependencies.anyhow] version = "1.0" [dependencies.bytes] version = "1.0" [dependencies.futures-core] version = "0.3" [dependencies.futures-io] version = "0.3" [dependencies.futures-sink] version = "0.3" [dependencies.log] version = "0.4" [dependencies.memchr] version = "2.3" optional = true [dependencies.pin-project-lite] version = "0.2" [dependencies.serde] version = "1.0" features = ["derive"] optional = true [dependencies.serde_cbor] version = "0.11" optional = true [dependencies.serde_json] version = "1.0" optional = true [dependencies.thiserror] version = "1.0" [dev-dependencies.futures-lite] version = "1.11" [dev-dependencies.futures-util] version = "0.3" features = [ "io", "sink", ] [features] cbor = [ "serde", "serde_cbor", ] default = [] json = [ "serde", "serde_json", ] lines = ["memchr"] async-codec-lite-0.0.2/Cargo.toml.orig000064400000000000000000000016620072674642500156720ustar 00000000000000[package] name = "async-codec-lite" edition = "2021" version = "0.0.2" authors = [""] license = "Apache-2.0 WITH LLVM-exception AND MIT" description = """ Adaptors from AsyncRead/AsyncWrite to Stream/Sink using futures. """ [package.metadata.docs.rs] all-features = true [features] default = [] cbor = ["serde", "serde_cbor"] json = ["serde", "serde_json"] lines = ["memchr"] [dependencies] anyhow = "1.0" bytes = "1.0" futures-core = "0.3" futures-io = "0.3" futures-sink = "0.3" log = "0.4" pin-project-lite = "0.2" thiserror = "1.0" [dependencies.memchr] version = "2.3" optional = true [dependencies.serde] version = "1.0" optional = true features = [ "derive" ] [dependencies.serde_cbor] version = "0.11" optional = true [dependencies.serde_json] version = "1.0" optional = true [dev-dependencies] futures-lite = "1.11" [dev-dependencies.futures-util] version = "0.3" features = ["io", "sink"] async-codec-lite-0.0.2/LICENSE-Apache000064400000000000000000000277220072674642500151740ustar 00000000000000 Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright [yyyy] [name of copyright owner] Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. --- LLVM Exceptions to the Apache 2.0 License ---- As an exception, if, as a result of your compiling your source code, portions of this Software are embedded into an Object form of such source code, you may redistribute such embedded portions in such Object form without complying with the conditions of Sections 4(a), 4(b) and 4(d) of the License. In addition, if you combine or link compiled forms of this Software with software that is licensed under the GPLv2 ("Combined Software") and if a court of competent jurisdiction determines that the patent provision (Section 3), the indemnity provision (Section 9) or other Section of the License conflicts with the conditions of the GPLv2, you may retroactively and prospectively choose to deem waived or otherwise exclude such Section(s) of the License, but only in their entirety and only with respect to the Combined Software. async-codec-lite-0.0.2/LICENSE-MIT000064400000000000000000000021610072674642500144320ustar 00000000000000MIT License Copyright (c) 2019 Matt Hunzinger Copyright (c) 2020 Erik Zscheile Copyright (c) 2021 Darin Morrison Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.async-codec-lite-0.0.2/README.md000064400000000000000000000027410072674642500142610ustar 00000000000000

async-codec-lite

Adaptors from AsyncRead/AsyncWrite to Stream/Sink using futures.

# async-codec-lite Adaptors from AsyncRead/AsyncWrite to Stream/Sink using futures. ## Description This crate is similar to existing crates that also provide `FramedWrite` adapters. The difference between this crate and other non-tokio alternatives is that it does not require `T: Unpin` in the `Sink` implementation for `FramedWrite`. This unnecessarily strict requirement made using `FramedWrite` with `tower-lsp` problematic, as discussed in the issue [here](https://github.com/matthunz/futures-codec/issues/46). ## Acknowledgements This crate is based on code and ideas from the following crates: * [matthunz/futures-codec](https://github.com/matthunz/futures-codec) * [tokio-rs/tokio](https://github.com/tokio-rs/tokio) async-codec-lite-0.0.2/clippy.toml000064400000000000000000000000000072674642500151610ustar 00000000000000async-codec-lite-0.0.2/rustfmt.toml000064400000000000000000000033600072674642500154010ustar 00000000000000# binop_separator = "Front" # blank_lines_lower_bound = 0 # blank_lines_upper_bound = 1 # brace_style = "SameLineWhere" # color = "Auto" # combine_control_expr = true comment_width = 100 condense_wildcard_suffixes = true # control_brace_style = "AlwaysSameLine" # disable_all_formatting = false edition = "2018" empty_item_single_line = false # enum_discrim_align_threshold = 0 error_on_line_overflow = true error_on_unformatted = true # fn_args_layout = "Tall" # fn_single_line = false force_explicit_abi = false # force_multiline_blocks = false format_code_in_doc_comments = true format_macro_matchers = true # format_macro_bodies = true # format_strings = false # hard_tabs = false # hide_parse_errors = false # ignore = [] imports_granularity = "Crate" # imports_indent = "Block" imports_layout = "HorizontalVertical" # indent_style = "Block" # inline_attribute_width = 0 # license_template_path = "" # match_arm_blocks = true match_block_trailing_comma = true max_width = 120 # merge_derives = true newline_style = "Unix" normalize_comments = true normalize_doc_attributes = true overflow_delimited_expr = true # remove_nested_parens = true reorder_impl_items = true # reorder_imports = true # reorder_modules = true # report_fixme = "Never" # report_todo = "Never" # required_version = "CARGO_PKG_VERSION" # skip_children = false # space_after_colon = true # space_before_colon = false spaces_around_ranges = true # struct_field_align_threshold = 0 # struct_lit_single_line = true # tab_spaces = 4 # trailing_comma = "Vertical" # trailing_semicolon = true # type_punctuation_density = "Wide" unstable_features = true use_field_init_shorthand = true # use_small_heuristics = "Max" use_try_shorthand = true version = "Two" # where_single_line = false wrap_comments = true async-codec-lite-0.0.2/src/codec/bytes.rs000064400000000000000000000013170072674642500163400ustar 00000000000000use super::{Decoder, Encoder}; use bytes::{Bytes, BytesMut}; use std::convert::Infallible; #[derive(Clone, Debug, Default, PartialEq)] pub struct BytesCodec; impl Encoder for BytesCodec { type Error = Infallible; type Item = Bytes; fn encode(&mut self, src: Self::Item, dst: &mut BytesMut) -> Result<(), Self::Error> { dst.extend_from_slice(&src); Ok(()) } } impl Decoder for BytesCodec { type Error = Infallible; type Item = Bytes; fn decode(&mut self, src: &mut BytesMut) -> Result, Self::Error> { let len = src.len(); Ok(if len > 0 { Some(src.split_to(len).freeze()) } else { None }) } } async-codec-lite-0.0.2/src/codec/cbor.rs000064400000000000000000000047750072674642500161520ustar 00000000000000use super::{Decoder, Encoder}; use bytes::{Buf, BufMut, BytesMut}; use serde::{Deserialize, Serialize}; use std::marker::PhantomData; pub struct CborCodec(PhantomData<(Enc, Dec)>); impl_phantom!(CborCodec); impl Decoder for CborCodec where for<'de> Dec: Deserialize<'de> + 'static, { type Error = serde_cbor::Error; type Item = Dec; fn decode(&mut self, buf: &mut BytesMut) -> Result, Self::Error> { let mut de = serde_cbor::Deserializer::from_slice(buf); let res: Result = serde::de::Deserialize::deserialize(&mut de); let res = match res { Ok(v) => Ok(Some(v)), Err(e) if e.is_eof() => Ok(None), Err(e) => Err(e), }; let offset = de.byte_offset(); buf.advance(offset); res } } impl Encoder for CborCodec where Enc: Serialize + 'static, { type Error = serde_cbor::Error; type Item = Enc; fn encode(&mut self, data: Self::Item, buf: &mut BytesMut) -> Result<(), Self::Error> { let j = serde_cbor::to_vec(&data)?; buf.reserve(j.len()); buf.put_slice(&j); Ok(()) } } #[cfg(test)] mod test { use bytes::BytesMut; use serde::{Deserialize, Serialize}; use super::{CborCodec, Decoder, Encoder}; #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] struct TestStruct { pub name: String, pub data: u16, } #[test] fn cbor_codec_encode_decode() { let mut codec = CborCodec::::new(); let mut buff = BytesMut::new(); let item1 = TestStruct { name: "Test name".to_owned(), data: 16, }; codec.encode(item1.clone(), &mut buff).unwrap(); let item2 = codec.decode(&mut buff).unwrap().unwrap(); assert_eq!(item1, item2); assert_eq!(codec.decode(&mut buff).unwrap(), None); assert_eq!(buff.len(), 0); } #[test] fn cbor_codec_partial_decode() { let mut codec = CborCodec::::new(); let mut buff = BytesMut::new(); let item1 = TestStruct { name: "Test name".to_owned(), data: 34, }; codec.encode(item1, &mut buff).unwrap(); let mut start = buff.clone().split_to(4); assert_eq!(codec.decode(&mut start).unwrap(), None); codec.decode(&mut buff).unwrap().unwrap(); assert_eq!(buff.len(), 0); } } async-codec-lite-0.0.2/src/codec/json.rs000064400000000000000000000050400072674642500161600ustar 00000000000000use super::{Decoder, Encoder}; use bytes::{Buf, BufMut, BytesMut}; use serde::{Deserialize, Serialize}; use serde_json::Error; use std::marker::PhantomData; pub struct JsonCodec(PhantomData<(Enc, Dec)>); impl_phantom!(JsonCodec); impl Decoder for JsonCodec where for<'de> Dec: Deserialize<'de> + 'static, { type Error = Error; type Item = Dec; fn decode(&mut self, buf: &mut BytesMut) -> Result, Self::Error> { let de = serde_json::Deserializer::from_slice(buf); let mut iter = de.into_iter::(); let res = match iter.next() { Some(Ok(v)) => Ok(Some(v)), Some(Err(ref e)) if e.is_eof() => Ok(None), Some(Err(e)) => Err(e), None => Ok(None), }; let offset = iter.byte_offset(); buf.advance(offset); res } } impl Encoder for JsonCodec where Enc: Serialize + 'static, { type Error = Error; type Item = Enc; fn encode(&mut self, data: Self::Item, buf: &mut BytesMut) -> Result<(), Self::Error> { let j = serde_json::to_string(&data)?; buf.reserve(j.len()); buf.put_slice(j.as_bytes()); Ok(()) } } #[cfg(test)] mod test { use bytes::BytesMut; use serde::{Deserialize, Serialize}; use super::{Decoder, Encoder, JsonCodec}; #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] struct TestStruct { pub name: String, pub data: u16, } #[test] fn json_codec_encode_decode() { let mut codec = JsonCodec::::new(); let mut buff = BytesMut::new(); let item1 = TestStruct { name: "Test name".to_owned(), data: 16, }; codec.encode(item1.clone(), &mut buff).unwrap(); let item2 = codec.decode(&mut buff).unwrap().unwrap(); assert_eq!(item1, item2); assert_eq!(codec.decode(&mut buff).unwrap(), None); assert_eq!(buff.len(), 0); } #[test] fn json_codec_partial_decode() { let mut codec = JsonCodec::::new(); let mut buff = BytesMut::new(); let item1 = TestStruct { name: "Test name".to_owned(), data: 34, }; codec.encode(item1, &mut buff).unwrap(); let mut start = buff.clone().split_to(4); assert_eq!(codec.decode(&mut start).unwrap(), None); codec.decode(&mut buff).unwrap().unwrap(); assert_eq!(buff.len(), 0); } } async-codec-lite-0.0.2/src/codec/length.rs000064400000000000000000000052240072674642500164740ustar 00000000000000use super::{Decoder, Encoder}; use bytes::{Buf, Bytes, BytesMut}; use std::{convert::TryFrom, marker::PhantomData}; pub struct LengthCodec(PhantomData); impl_phantom!(LengthCodec); #[derive(Debug, thiserror::Error)] #[error("length overflow")] pub struct OverflowError; impl LengthCodec { const HEADER_LEN: usize = std::mem::size_of::(); } pub trait Length { fn encode(x: usize, dst: &mut BytesMut) -> Result<(), OverflowError>; fn start_decode(src: &[u8]) -> Result; } macro_rules! impl_length { ($($x:ty => $y:expr),+ $(,)?) => { $( impl Length for $x { fn encode(x: usize, dst: &mut BytesMut) -> Result<(), OverflowError> { let this = Self::try_from(x).map_err(|_| OverflowError)?; dst.extend_from_slice(&Self::to_be_bytes(this)); Ok(()) } fn start_decode(src: &[u8]) -> Result { let mut len_bytes = [0u8; $y]; len_bytes.copy_from_slice(&src[..$y]); usize::try_from(Self::from_be_bytes(len_bytes)).map_err(|_| OverflowError) } } )+ } } impl_length!(u8 => 1, u16 => 2, u32 => 4, u64 => 8); impl Encoder for LengthCodec { type Error = OverflowError; type Item = Bytes; fn encode(&mut self, src: Self::Item, dst: &mut BytesMut) -> Result<(), Self::Error> { dst.reserve(Self::HEADER_LEN + src.len()); L::encode(src.len(), dst)?; dst.extend_from_slice(&src); Ok(()) } } impl Decoder for LengthCodec { type Error = OverflowError; type Item = Bytes; fn decode(&mut self, src: &mut BytesMut) -> Result, Self::Error> { Ok(if src.len() < std::mem::size_of::() { None } else { let len = L::start_decode(src)?; if src.len() - Self::HEADER_LEN >= len { // Skip the length header we already read. src.advance(Self::HEADER_LEN); Some(src.split_to(len).freeze()) } else { None } }) } } #[cfg(test)] mod tests { use super::*; mod decode { use super::*; #[test] fn it_returns_bytes_withouth_length_header() { use bytes::BufMut; let mut codec = LengthCodec::::new(); let mut src = BytesMut::with_capacity(5); src.put(&[0, 0, 0, 0, 0, 0, 0, 3u8, 1, 2, 3, 4][..]); let item = codec.decode(&mut src).unwrap(); assert!(item == Some(Bytes::from(&[1u8, 2, 3][..]))); } } } async-codec-lite-0.0.2/src/codec/limit.rs000064400000000000000000000062620072674642500163340ustar 00000000000000use super::{Decoder, Encoder}; use bytes::{Buf, BytesMut}; #[allow(missing_docs)] pub trait SkipAheadHandler: Sized + std::fmt::Debug { fn continue_skipping(self, src: &[u8]) -> anyhow::Result<(usize, Option)>; } impl SkipAheadHandler for () { fn continue_skipping(self, _: &[u8]) -> anyhow::Result<(usize, Option)> { Ok((0, None)) } } #[allow(missing_docs)] pub trait DecoderWithSkipAhead: Decoder { type Handler: SkipAheadHandler; fn prepare_skip_ahead(&mut self, src: &mut BytesMut) -> Self::Handler; } #[derive(Debug)] pub struct LimitCodec { inner: C, max_frame_size: usize, skip_ahead_state: Option<::Handler>, decoder_defunct: bool, } impl LimitCodec where C: DecoderWithSkipAhead, { #[allow(missing_docs)] pub fn new(inner: C, max_frame_size: usize) -> Self { Self { inner, max_frame_size, skip_ahead_state: None, decoder_defunct: false, } } } #[derive(Debug, thiserror::Error)] pub enum LimitError { #[error("frame size limit exceeded (detected at {0} bytes)")] LimitExceeded(usize), #[error("codec couldn't recover from invalid or too big frame")] Defunct, #[error(transparent)] Inner(#[from] E), } impl Encoder for LimitCodec where C: Encoder + DecoderWithSkipAhead, { type Error = LimitError<::Error>; type Item = ::Item; fn encode(&mut self, src: Self::Item, dst: &mut BytesMut) -> Result<(), Self::Error> { let mut tmp_dst = dst.split_off(dst.len()); self.inner.encode(src, &mut tmp_dst)?; if tmp_dst.len() > self.max_frame_size { return Err(LimitError::LimitExceeded(tmp_dst.len())); } dst.unsplit(tmp_dst); Ok(()) } } impl Decoder for LimitCodec where C: DecoderWithSkipAhead, { type Error = LimitError<::Error>; type Item = ::Item; fn decode(&mut self, src: &mut BytesMut) -> Result, Self::Error> { while let Some(sas) = self.skip_ahead_state.take() { match sas.continue_skipping(src) { Ok((amount, next)) => { self.skip_ahead_state = next; debug_assert!(amount <= src.len()); src.advance(amount); debug_assert!(amount != 0 || self.skip_ahead_state.is_none()); if src.is_empty() { return Ok(None); } }, Err(_err) => { self.decoder_defunct = true; }, } } if self.decoder_defunct { src.clear(); return Err(LimitError::Defunct); } match self.inner.decode(src) { Ok(None) if src.len() > self.max_frame_size => { self.skip_ahead_state = Some(self.inner.prepare_skip_ahead(src)); Err(LimitError::LimitExceeded(src.len())) }, Ok(x) => Ok(x), Err(x) => Err(LimitError::Inner(x)), } } } async-codec-lite-0.0.2/src/codec/lines.rs000064400000000000000000000015430072674642500163250ustar 00000000000000use super::{Decoder, Encoder}; use bytes::{BufMut, BytesMut}; use memchr::memchr; use std::convert::Infallible; #[derive(Clone, Debug, Default, PartialEq)] pub struct LinesCodec; impl Encoder for LinesCodec { type Error = Infallible; type Item = String; fn encode(&mut self, item: Self::Item, dst: &mut BytesMut) -> Result<(), Self::Error> { dst.reserve(item.len()); dst.put(item.as_bytes()); Ok(()) } } impl Decoder for LinesCodec { type Error = std::string::FromUtf8Error; type Item = String; fn decode(&mut self, src: &mut BytesMut) -> Result, Self::Error> { match memchr(b'\n', src) { Some(pos) => { let buf = src.split_to(pos + 1); String::from_utf8(buf.to_vec()).map(Some) }, _ => Ok(None), } } } async-codec-lite-0.0.2/src/codec/mod.rs000064400000000000000000000035210072674642500157700ustar 00000000000000use ::bytes::BytesMut; macro_rules! impl_phantom { ($t:ident < $($param:ident),+ >) => { impl<$($param),+> $t<$($param),+> { #[allow(missing_docs)] pub const fn new() -> Self { Self(PhantomData) } } impl<$($param),+> ::std::clone::Clone for $t<$($param),+> { fn clone(&self) -> Self { Self::new() } } impl<$($param),+> ::std::fmt::Debug for $t<$($param),+> { fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result { f.debug_struct(stringify!($t)).finish() } } impl<$($param),+> ::std::default::Default for $t<$($param),+> { fn default() -> Self { Self::new() } } impl<$($param),+> ::std::cmp::PartialEq for $t<$($param),+> { fn eq(&self, _other: &Self) -> bool { true } } } } mod bytes; pub use self::bytes::BytesCodec; mod length; pub use self::length::{LengthCodec, OverflowError}; mod limit; pub use self::limit::{DecoderWithSkipAhead, LimitCodec, LimitError, SkipAheadHandler}; #[cfg(feature = "lines")] mod lines; #[cfg(feature = "lines")] pub use self::lines::LinesCodec; #[cfg(feature = "cbor")] mod cbor; #[cfg(feature = "cbor")] pub use self::cbor::CborCodec; #[cfg(feature = "json")] mod json; #[cfg(feature = "json")] pub use self::json::JsonCodec; pub trait Decoder { type Item; type Error: std::error::Error + 'static; fn decode(&mut self, src: &mut BytesMut) -> Result, Self::Error>; fn decode_eof(&mut self, src: &mut BytesMut) -> Result, Self::Error> { self.decode(src) } } pub trait Encoder { type Item; type Error: std::error::Error + 'static; fn encode(&mut self, item: Self::Item, dst: &mut BytesMut) -> Result<(), Self::Error>; } async-codec-lite-0.0.2/src/error.rs000064400000000000000000000003170072674642500152650ustar 00000000000000#[derive(Debug, thiserror::Error)] pub enum Error { #[error("codec error: {0}")] Codec(#[source] C), #[error("I/O error: {0}")] Io(#[from] std::io::Error), } async-codec-lite-0.0.2/src/framed/inner.rs000064400000000000000000000171020072674642500165050ustar 00000000000000use crate::{ codec::{Decoder, Encoder}, error::Error, }; use bytes::{Buf, BytesMut}; use futures_core::{ready, Stream}; use futures_io::{AsyncRead, AsyncWrite}; use futures_sink::Sink; use pin_project_lite::pin_project; use std::{ borrow::{Borrow, BorrowMut}, io, ops::Deref, pin::Pin, task::{Context, Poll}, }; const INITIAL_CAPACITY: usize = 8 * 1024; const BACKPRESSURE_BOUNDARY: usize = INITIAL_CAPACITY; pin_project! { #[derive(Debug)] pub(super) struct FramedInner { #[pin] pub(super) inner: T, pub(super) state: State, pub(super) codec: U, } } impl Deref for FramedInner { type Target = T; fn deref(&self) -> &T { &self.inner } } impl Stream for FramedInner where T: AsyncRead, U: Decoder, R: BorrowMut, { type Item = Result>; fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { let mut pinned = self.project(); let state: &mut ReadFrame = pinned.state.borrow_mut(); let mut buf = [0u8; INITIAL_CAPACITY]; loop { // Return `None` if we have encountered an error from the underlying decoder // See: https://github.com/tokio-rs/tokio/issues/3976 if state.has_errored { log::trace!("Returning None and setting paused"); state.is_readable = false; state.has_errored = false; return Poll::Ready(None); } if state.is_readable { // pausing or framing if state.eof { // pausing let frame = pinned.codec.decode_eof(&mut state.buffer).map_err(|err| { log::trace!("Got an error, going to errored state"); state.has_errored = true; Error::Codec(err) })?; if frame.is_none() { // prepare pausing -> paused state.is_readable = false; } // implicit pausing -> pausing or pausing -> paused return Poll::Ready(frame.map(Ok)); } // framing log::trace!("Attempting to decode a frame"); if let Some(frame) = pinned.codec.decode(&mut state.buffer).map_err(|err| { log::trace!("Got an error, going to errored state"); state.has_errored = true; Error::Codec(err) })? { log::trace!("Frame decoded from buffer"); // implicit framing -> framing return Poll::Ready(Some(Ok(frame))); } // framing -> reading state.is_readable = false; } // reading or paused let bytes_read = ready!(pinned.inner.as_mut().poll_read(cx, &mut buf).map_err(|err| { log::trace!("Got an error, going to errored state"); state.has_errored = true; err }))?; state.buffer.extend_from_slice(&buf[.. bytes_read]); if bytes_read == 0 { if state.eof { // implicit paused -> paused return Poll::Ready(None); } // prepare reading -> paused state.eof = true; } else { // prepare paused -> framing or noop reading -> framing state.eof = false; } // paused -> framing or reading -> framing or reading -> pausing state.is_readable = true; } } } impl FramedInner where T: AsyncWrite, U: Encoder, W: BorrowMut, { fn poll_flush_until(self: Pin<&mut Self>, cx: &mut Context<'_>, limit: usize) -> Poll> { let mut pinned = self.project(); let state = pinned.state.borrow_mut(); let orig_len = state.buffer.len(); log::trace!("Flushing framed transport"); while state.buffer.len() > limit { log::trace!("Writing; remaining = {}", state.buffer.len()); let num_write = ready!(pinned.inner.as_mut().poll_write(cx, &state.buffer))?; if num_write == 0 { return Poll::Ready(Err(io::Error::new( io::ErrorKind::UnexpectedEof, "FramedWrite: end of input", ))); } state.buffer.advance(num_write); } log::trace!("Framed transport flushed"); if orig_len != state.buffer.len() { pinned.inner.poll_flush(cx) } else { Poll::Ready(Ok(())) } } } impl Sink for FramedInner where T: AsyncWrite, U: Encoder, W: BorrowMut, { type Error = Error; fn poll_ready(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { self.poll_flush_until(cx, BACKPRESSURE_BOUNDARY - 1).map_err(Into::into) } fn start_send(self: Pin<&mut Self>, item: U::Item) -> Result<(), Self::Error> { let pinned = self.project(); pinned .codec .encode(item, &mut pinned.state.borrow_mut().buffer) .map_err(Error::Codec) } fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { self.poll_flush_until(cx, 0).map_err(Into::into) } fn poll_close(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { ready!(self.as_mut().poll_flush(cx))?; self.project().inner.poll_close(cx).map_err(Into::into) } } pub(super) struct ReadFrame { pub(super) buffer: BytesMut, pub(super) eof: bool, pub(super) has_errored: bool, pub(super) is_readable: bool, } impl Default for ReadFrame { fn default() -> Self { Self { buffer: BytesMut::with_capacity(INITIAL_CAPACITY), eof: false, has_errored: false, is_readable: false, } } } impl From for ReadFrame { fn from(mut buffer: BytesMut) -> Self { let size = buffer.capacity(); if size < INITIAL_CAPACITY { buffer.reserve(INITIAL_CAPACITY - size); } Self { buffer, eof: false, has_errored: false, is_readable: size > 0, } } } pub(super) struct WriteFrame { pub(super) buffer: BytesMut, } impl Default for WriteFrame { fn default() -> Self { Self { buffer: BytesMut::with_capacity(INITIAL_CAPACITY), } } } impl From for WriteFrame { fn from(mut buffer: BytesMut) -> Self { let size = buffer.capacity(); if size < INITIAL_CAPACITY { buffer.reserve(INITIAL_CAPACITY - size); } Self { buffer } } } #[derive(Default)] pub(super) struct RWFrames { pub(super) read: ReadFrame, pub(super) write: WriteFrame, } impl Borrow for RWFrames { fn borrow(&self) -> &ReadFrame { &self.read } } impl BorrowMut for RWFrames { fn borrow_mut(&mut self) -> &mut ReadFrame { &mut self.read } } impl Borrow for RWFrames { fn borrow(&self) -> &WriteFrame { &self.write } } impl BorrowMut for RWFrames { fn borrow_mut(&mut self) -> &mut WriteFrame { &mut self.write } } async-codec-lite-0.0.2/src/framed/mod.rs000064400000000000000000000105600072674642500161520ustar 00000000000000mod inner; mod read; mod write; pub use self::{read::FramedRead, write::FramedWrite}; use self::inner::{FramedInner, RWFrames, ReadFrame, WriteFrame}; use crate::{ codec::{Decoder, Encoder}, error::Error, }; pub use bytes::{Bytes, BytesMut}; use futures_core::Stream; use futures_io::{AsyncRead, AsyncWrite}; use futures_sink::Sink; use pin_project_lite::pin_project; use std::{ fmt, pin::Pin, task::{Context, Poll}, }; pin_project! { pub struct Framed { #[pin] inner: FramedInner } } impl Framed { pub fn new(inner: T, codec: U) -> Framed { Framed { inner: FramedInner { inner, codec, state: Default::default(), }, } } pub fn with_capacity(inner: T, codec: U, capacity: usize) -> Framed { Framed { inner: FramedInner { inner, codec, state: RWFrames { read: ReadFrame { buffer: BytesMut::with_capacity(capacity), eof: false, has_errored: false, is_readable: false, }, write: WriteFrame::default(), }, }, } } pub fn from_parts(parts: FramedParts) -> Framed { Framed { inner: FramedInner { inner: parts.io, codec: parts.codec, state: RWFrames { read: parts.read_buf.into(), write: parts.write_buf.into(), }, }, } } pub fn get_ref(&self) -> &T { &self.inner.inner } pub fn get_mut(&mut self) -> &mut T { &mut self.inner.inner } pub fn get_pin_mut(self: Pin<&mut Self>) -> Pin<&mut T> { self.project().inner.project().inner } pub fn codec(&self) -> &U { &self.inner.codec } pub fn codec_mut(&mut self) -> &mut U { &mut self.inner.codec } pub fn read_buffer(&self) -> &BytesMut { &self.inner.state.read.buffer } pub fn read_buffer_mut(&mut self) -> &mut BytesMut { &mut self.inner.state.read.buffer } pub fn into_inner(self) -> T { self.inner.inner } pub fn into_parts(self) -> FramedParts { FramedParts { io: self.inner.inner, codec: self.inner.codec, read_buf: self.inner.state.read.buffer, write_buf: self.inner.state.write.buffer, _priv: (), } } } impl Stream for Framed where T: AsyncRead, U: Decoder, { type Item = Result>; fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { self.project().inner.poll_next(cx) } } impl Sink for Framed where T: AsyncWrite, U: Encoder, { type Error = Error; fn poll_ready(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { self.project().inner.poll_ready(cx) } fn start_send(self: Pin<&mut Self>, item: U::Item) -> Result<(), Self::Error> { self.project().inner.start_send(item) } fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { self.project().inner.poll_flush(cx) } fn poll_close(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { self.project().inner.poll_close(cx) } } impl fmt::Debug for Framed where T: fmt::Debug, U: fmt::Debug, { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("Framed") .field("io", self.get_ref()) .field("codec", self.codec()) .finish() } } #[derive(Debug)] #[allow(clippy::manual_non_exhaustive)] pub struct FramedParts { pub io: T, pub codec: U, pub read_buf: BytesMut, pub write_buf: BytesMut, _priv: (), } impl FramedParts { pub fn new(io: T, codec: U) -> FramedParts where U: Encoder, { FramedParts { io, codec, read_buf: BytesMut::new(), write_buf: BytesMut::new(), _priv: (), } } } async-codec-lite-0.0.2/src/framed/read.rs000064400000000000000000000062660072674642500163160ustar 00000000000000use super::inner::{FramedInner, ReadFrame}; use crate::{codec::Decoder, error::Error}; pub use bytes::{Bytes, BytesMut}; use futures_core::Stream; use futures_io::AsyncRead; use futures_sink::Sink; use pin_project_lite::pin_project; use std::{ fmt, pin::Pin, task::{Context, Poll}, }; pin_project! { pub struct FramedRead { #[pin] inner: FramedInner, } } impl FramedRead where T: AsyncRead, D: Decoder, { pub fn new(inner: T, decoder: D) -> FramedRead { FramedRead { inner: FramedInner { inner, codec: decoder, state: Default::default(), }, } } pub fn with_capacity(inner: T, decoder: D, capacity: usize) -> FramedRead { FramedRead { inner: FramedInner { inner, codec: decoder, state: ReadFrame { buffer: BytesMut::with_capacity(capacity), eof: false, has_errored: false, is_readable: false, }, }, } } } impl FramedRead { pub fn get_ref(&self) -> &T { &self.inner.inner } pub fn get_mut(&mut self) -> &mut T { &mut self.inner.inner } pub fn get_pin_mut(self: Pin<&mut Self>) -> Pin<&mut T> { self.project().inner.project().inner } pub fn into_inner(self) -> T { self.inner.inner } pub fn decoder(&self) -> &D { &self.inner.codec } pub fn decoder_mut(&mut self) -> &mut D { &mut self.inner.codec } pub fn read_buffer(&self) -> &BytesMut { &self.inner.state.buffer } pub fn read_buffer_mut(&mut self) -> &mut BytesMut { &mut self.inner.state.buffer } } impl Stream for FramedRead where T: AsyncRead, D: Decoder, { type Item = Result>; fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { self.project().inner.poll_next(cx) } } impl Sink for FramedRead where T: Sink, { type Error = T::Error; fn poll_ready(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { self.project().inner.project().inner.poll_ready(cx) } fn start_send(self: Pin<&mut Self>, item: I) -> Result<(), Self::Error> { self.project().inner.project().inner.start_send(item) } fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { self.project().inner.project().inner.poll_flush(cx) } fn poll_close(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { self.project().inner.project().inner.poll_close(cx) } } impl fmt::Debug for FramedRead where T: fmt::Debug, D: fmt::Debug, { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("FramedRead") .field("inner", &self.get_ref()) .field("decoder", &self.decoder()) .field("buffer", &self.read_buffer()) .finish() } } async-codec-lite-0.0.2/src/framed/write.rs000064400000000000000000000050000072674642500165160ustar 00000000000000use super::inner::{FramedInner, WriteFrame}; use crate::{codec::Encoder, error::Error}; pub use bytes::{Bytes, BytesMut}; use futures_core::Stream; use futures_io::AsyncWrite; use futures_sink::Sink; use pin_project_lite::pin_project; use std::{ fmt, pin::Pin, task::{Context, Poll}, }; pin_project! { pub struct FramedWrite { #[pin] inner: FramedInner, } } impl FramedWrite where T: AsyncWrite, { pub fn new(inner: T, encoder: E) -> FramedWrite { FramedWrite { inner: FramedInner { inner, codec: encoder, state: WriteFrame::default(), }, } } } impl FramedWrite { pub fn get_ref(&self) -> &T { &self.inner.inner } pub fn get_mut(&mut self) -> &mut T { &mut self.inner.inner } pub fn get_pin_mut(self: Pin<&mut Self>) -> Pin<&mut T> { self.project().inner.project().inner } pub fn into_inner(self) -> T { self.inner.inner } pub fn encoder(&self) -> &E { &self.inner.codec } pub fn encoder_mut(&mut self) -> &mut E { &mut self.inner.codec } } impl Sink for FramedWrite where T: AsyncWrite, E: Encoder, { type Error = Error; fn poll_ready(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { self.project().inner.poll_ready(cx) } fn start_send(self: Pin<&mut Self>, item: E::Item) -> Result<(), Self::Error> { self.project().inner.start_send(item) } fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { self.project().inner.poll_flush(cx) } fn poll_close(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { self.project().inner.poll_close(cx) } } impl Stream for FramedWrite where T: Stream, { type Item = T::Item; fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { self.project().inner.project().inner.poll_next(cx) } } impl fmt::Debug for FramedWrite where T: fmt::Debug, U: fmt::Debug, { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("FramedWrite") .field("inner", &self.get_ref()) .field("encoder", &self.encoder()) .field("buffer", &self.inner.state.buffer) .finish() } } async-codec-lite-0.0.2/src/lib.rs000064400000000000000000000004130072674642500146770ustar 00000000000000#![forbid(unsafe_code)] #![warn(missing_debug_implementations, rust_2018_idioms)] #![warn(clippy::all)] mod codec; mod error; mod framed; pub use self::{ codec::*, framed::{Framed, FramedParts, FramedRead, FramedWrite}, }; pub use bytes::{Bytes, BytesMut}; async-codec-lite-0.0.2/tests/all/codec/bytes.rs000064400000000000000000000007330072674642500174640ustar 00000000000000use async_codec_lite::{BytesCodec, Framed}; use futures_lite::future::block_on; use futures_util::{io::Cursor, stream::TryStreamExt}; #[test] fn decodes() { let mut buf = [0u8; 32]; let expected = buf; let cur = Cursor::new(&mut buf[..]); let mut framed = Framed::new(cur, BytesCodec {}); let read = block_on(framed.try_next()).unwrap().unwrap(); assert_eq!(&read[..], &expected[..]); assert!(block_on(framed.try_next()).unwrap().is_none()); } async-codec-lite-0.0.2/tests/all/codec/length.rs000064400000000000000000000016740072674642500176240ustar 00000000000000use async_codec_lite::{Bytes, Framed, LengthCodec}; use futures_lite::future::block_on; use futures_util::{io::Cursor, sink::SinkExt, stream::StreamExt}; #[test] fn same_msgs_are_received_as_were_sent() { let cur = Cursor::new(vec![0; 256]); let mut framed = Framed::new(cur, LengthCodec::::new()); let send_msgs = async { framed.send(Bytes::from("msg1")).await.unwrap(); framed.send(Bytes::from("msg2")).await.unwrap(); framed.send(Bytes::from("msg3")).await.unwrap(); }; block_on(send_msgs); let mut parts = framed.into_parts(); parts.io.set_position(0); let framed = Framed::new(parts.io, LengthCodec::::new()); let recv_msgs = framed .take(3) .map(|res| res.unwrap()) .map(|buf| String::from_utf8(buf.to_vec()).unwrap()) .collect::>(); let msgs: Vec = block_on(recv_msgs); assert!(msgs == vec!["msg1", "msg2", "msg3"]); } async-codec-lite-0.0.2/tests/all/codec/lines.rs000064400000000000000000000011140072674642500174420ustar 00000000000000use async_codec_lite::{Framed, LinesCodec}; use futures_lite::future::block_on; use futures_util::{io::Cursor, stream::TryStreamExt}; #[test] fn it_works() { let buf = "Hello\nWorld\nError".to_owned(); let cur = Cursor::new(buf); let mut framed = Framed::new(cur, LinesCodec {}); let next = block_on(framed.try_next()).unwrap(); assert_eq!(next, Some(String::from("Hello\n"))); let next = block_on(framed.try_next()).unwrap(); assert_eq!(next, Some(String::from("World\n"))); let next = block_on(framed.try_next()).unwrap(); assert_eq!(next, None); } async-codec-lite-0.0.2/tests/all/codec/mod.rs000064400000000000000000000000740072674642500171130ustar 00000000000000mod bytes; mod length; #[cfg(feature = "lines")] mod lines; async-codec-lite-0.0.2/tests/all/framed/mod.rs000064400000000000000000000000250072674642500172700ustar 00000000000000mod read; mod write; async-codec-lite-0.0.2/tests/all/framed/read.rs000064400000000000000000000042560072674642500174360ustar 00000000000000#[cfg(feature = "lines")] use async_codec_lite::LinesCodec; use async_codec_lite::{BytesMut, Decoder, Framed}; use futures_lite::future::block_on; use futures_util::{io::AsyncRead, stream::StreamExt}; use std::{ io, pin::Pin, task::{Context, Poll}, }; struct MockBurstySender { sent: bool, } impl AsyncRead for MockBurstySender { fn poll_read(mut self: Pin<&mut Self>, _cx: &mut Context<'_>, buf: &mut [u8]) -> Poll> { const MESSAGES: &[u8] = b"one\ntwo\n"; if !self.sent && buf.len() >= MESSAGES.len() { self.sent = true; buf[0 .. MESSAGES.len()].clone_from_slice(MESSAGES); Poll::Ready(Ok(MESSAGES.len())) } else { Poll::Pending } } } #[cfg(feature = "lines")] #[test] fn line_read_multi() { let io = MockBurstySender { sent: false }; let mut framed = Framed::new(io, LinesCodec {}); let one = block_on(framed.next()).unwrap().unwrap(); assert_eq!(one, "one\n"); let two = block_on(framed.next()).unwrap().unwrap(); assert_eq!(two, "two\n"); } struct OneByteAtATime<'a> { input: &'a [u8], } impl AsyncRead for OneByteAtATime<'_> { fn poll_read(mut self: Pin<&mut Self>, _cx: &mut Context<'_>, buf: &mut [u8]) -> Poll> { if self.input.is_empty() { Poll::Ready(Ok(0)) } else { buf[0] = self.input[0]; self.input = &self.input[1 ..]; Poll::Ready(Ok(1)) } } } struct AllTheAs; impl Decoder for AllTheAs { type Error = io::Error; type Item = char; fn decode(&mut self, src: &mut BytesMut) -> Result, Self::Error> { while !src.is_empty() { let buf = src.split_to(1); let c = char::from(buf[0]); if c == 'a' { return Ok(Some(c)); } } Ok(None) } } #[test] fn read_few_messages() { let string: &[u8] = b"aabbbabbbabbbabb"; let input = OneByteAtATime { input: string }; let mut framed = Framed::new(input, AllTheAs); for _ in 0 .. 5 { let item = block_on(framed.next()).unwrap().unwrap(); assert_eq!(item, 'a'); } } async-codec-lite-0.0.2/tests/all/framed/write.rs000064400000000000000000000041570072674642500176550ustar 00000000000000use async_codec_lite::Bytes; use core::iter::Iterator; use futures_util::io::AsyncWrite; use std::{ pin::Pin, task::{Context, Poll}, }; struct ZeroBytes { pub count: usize, pub limit: usize, } impl Iterator for ZeroBytes { type Item = Bytes; fn next(&mut self) -> Option { if self.count >= self.limit { None } else { self.count += 1; Some(Bytes::from_static(b"\0")) } } } struct AsyncWriteNull { pub num_poll_write: usize, pub last_write_size: usize, } impl AsyncWrite for AsyncWriteNull { fn poll_write(mut self: Pin<&mut Self>, _cx: &mut Context<'_>, buf: &[u8]) -> Poll> { self.num_poll_write += 1; self.last_write_size = buf.len(); Poll::Ready(Ok(buf.len())) } fn poll_flush(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll> { Poll::Ready(Ok(())) } fn poll_close(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll> { Poll::Ready(Ok(())) } } #[cfg(feature = "lines")] mod line { use async_codec_lite::{Framed, LinesCodec}; use futures_lite::future::block_on; use futures_util::{io::Cursor, sink::SinkExt}; #[test] fn write() { let curs = Cursor::new(vec![0u8; 16]); let mut framer = Framed::new(curs, LinesCodec {}); block_on(framer.send("Hello\n".to_owned())).unwrap(); block_on(framer.send("World\n".to_owned())).unwrap(); let parts = framer.into_parts(); assert_eq!(&parts.io.get_ref()[0 .. 12], b"Hello\nWorld\n"); assert_eq!(parts.io.position(), 12); } #[cfg(feature = "lines")] #[test] fn write_to_eof() { let mut buf = [0u8; 16]; let curs = Cursor::new(&mut buf[..]); let mut framer = Framed::new(curs, LinesCodec {}); let _err = block_on(framer.send("This will fill up the buffer\n".to_owned())).unwrap_err(); let parts = framer.into_parts(); assert_eq!(parts.io.position(), 16); assert_eq!(&parts.io.get_ref()[0 .. 16], b"This will fill u"); } } async-codec-lite-0.0.2/tests/all/main.rs000064400000000000000000000000270072674642500162010ustar 00000000000000mod codec; mod framed;