[
  {
    "path": ".git-blame-ignore-revs",
    "content": "# Scala Steward: Reformat with scalafmt 3.9.7\n5ccb66cf1c2c22e7c4aeb4c73fec92b874e6d296\n"
  },
  {
    "path": ".github/workflows/ci.yml",
    "content": "# This file was automatically generated by sbt-github-actions using the\n# githubWorkflowGenerate task. You should add and commit this file to\n# your git repository. It goes without saying that you shouldn't edit\n# this file by hand! Instead, if you wish to make changes, you should\n# change your sbt build configuration to revise the workflow description\n# to meet your needs, then regenerate this file.\n\nname: Continuous Integration\n\non:\n  pull_request:\n    branches: ['**', '!update/**', '!pr/**']\n  push:\n    branches: ['**', '!update/**', '!pr/**']\n    tags: [v*]\n\nenv:\n  GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}\n\n\nconcurrency:\n  group: ${{ github.workflow }} @ ${{ github.ref }}\n  cancel-in-progress: true\n\njobs:\n  build:\n    name: Test\n    strategy:\n      matrix:\n        os: [ubuntu-22.04]\n        scala: [2.12, 2.13, 3]\n        java: [temurin@8]\n        project: [rootJS, rootJVM, rootNative]\n    runs-on: ${{ matrix.os }}\n    timeout-minutes: 60\n    steps:\n      - name: Checkout current branch (full)\n        uses: actions/checkout@v6\n        with:\n          fetch-depth: 0\n\n      - name: Setup sbt\n        uses: sbt/setup-sbt@v1\n\n      - name: Setup Java (temurin@8)\n        id: setup-java-temurin-8\n        if: matrix.java == 'temurin@8'\n        uses: actions/setup-java@v5\n        with:\n          distribution: temurin\n          java-version: 8\n          cache: sbt\n\n      - name: sbt update\n        if: matrix.java == 'temurin@8' && steps.setup-java-temurin-8.outputs.cache-hit == 'false'\n        run: sbt +update\n\n      - name: Check that workflows are up to date\n        run: sbt githubWorkflowCheck\n\n      - name: Check headers and formatting\n        if: matrix.java == 'temurin@8' && matrix.os == 'ubuntu-22.04'\n        run: sbt 'project ${{ matrix.project }}' '++ ${{ matrix.scala }}' headerCheckAll scalafmtCheckAll 'project /' scalafmtSbtCheck\n\n      - name: scalaJSLink\n        if: matrix.project == 'rootJS'\n        run: sbt 'project ${{ matrix.project }}' '++ ${{ matrix.scala }}' Test/scalaJSLinkerResult\n\n      - name: nativeLink\n        if: matrix.project == 'rootNative'\n        run: sbt 'project ${{ matrix.project }}' '++ ${{ matrix.scala }}' Test/nativeLink\n\n      - name: Test\n        run: sbt 'project ${{ matrix.project }}' '++ ${{ matrix.scala }}' test\n\n      - name: Check binary compatibility\n        if: matrix.java == 'temurin@8' && matrix.os == 'ubuntu-22.04'\n        run: sbt 'project ${{ matrix.project }}' '++ ${{ matrix.scala }}' mimaReportBinaryIssues\n\n      - name: Generate API documentation\n        if: matrix.java == 'temurin@8' && matrix.os == 'ubuntu-22.04'\n        run: sbt 'project ${{ matrix.project }}' '++ ${{ matrix.scala }}' doc\n\n      - name: Make target directories\n        if: github.event_name != 'pull_request' && (startsWith(github.ref, 'refs/tags/v') || github.ref == 'refs/heads/main')\n        run: mkdir -p native/target test-kit/js/target js/target test-kit/native/target test-kit/jvm/target jvm/target project/target\n\n      - name: Compress target directories\n        if: github.event_name != 'pull_request' && (startsWith(github.ref, 'refs/tags/v') || github.ref == 'refs/heads/main')\n        run: tar cf targets.tar native/target test-kit/js/target js/target test-kit/native/target test-kit/jvm/target jvm/target project/target\n\n      - name: Upload target directories\n        if: github.event_name != 'pull_request' && (startsWith(github.ref, 'refs/tags/v') || github.ref == 'refs/heads/main')\n        uses: actions/upload-artifact@v5\n        with:\n          name: target-${{ matrix.os }}-${{ matrix.java }}-${{ matrix.scala }}-${{ matrix.project }}\n          path: targets.tar\n\n  publish:\n    name: Publish Artifacts\n    needs: [build]\n    if: github.event_name != 'pull_request' && (startsWith(github.ref, 'refs/tags/v') || github.ref == 'refs/heads/main')\n    strategy:\n      matrix:\n        os: [ubuntu-22.04]\n        java: [temurin@8]\n    runs-on: ${{ matrix.os }}\n    steps:\n      - name: Checkout current branch (full)\n        uses: actions/checkout@v6\n        with:\n          fetch-depth: 0\n\n      - name: Setup sbt\n        uses: sbt/setup-sbt@v1\n\n      - name: Setup Java (temurin@8)\n        id: setup-java-temurin-8\n        if: matrix.java == 'temurin@8'\n        uses: actions/setup-java@v5\n        with:\n          distribution: temurin\n          java-version: 8\n          cache: sbt\n\n      - name: sbt update\n        if: matrix.java == 'temurin@8' && steps.setup-java-temurin-8.outputs.cache-hit == 'false'\n        run: sbt +update\n\n      - name: Download target directories (2.12, rootJS)\n        uses: actions/download-artifact@v6\n        with:\n          name: target-${{ matrix.os }}-${{ matrix.java }}-2.12-rootJS\n\n      - name: Inflate target directories (2.12, rootJS)\n        run: |\n          tar xf targets.tar\n          rm targets.tar\n\n      - name: Download target directories (2.12, rootJVM)\n        uses: actions/download-artifact@v6\n        with:\n          name: target-${{ matrix.os }}-${{ matrix.java }}-2.12-rootJVM\n\n      - name: Inflate target directories (2.12, rootJVM)\n        run: |\n          tar xf targets.tar\n          rm targets.tar\n\n      - name: Download target directories (2.12, rootNative)\n        uses: actions/download-artifact@v6\n        with:\n          name: target-${{ matrix.os }}-${{ matrix.java }}-2.12-rootNative\n\n      - name: Inflate target directories (2.12, rootNative)\n        run: |\n          tar xf targets.tar\n          rm targets.tar\n\n      - name: Download target directories (2.13, rootJS)\n        uses: actions/download-artifact@v6\n        with:\n          name: target-${{ matrix.os }}-${{ matrix.java }}-2.13-rootJS\n\n      - name: Inflate target directories (2.13, rootJS)\n        run: |\n          tar xf targets.tar\n          rm targets.tar\n\n      - name: Download target directories (2.13, rootJVM)\n        uses: actions/download-artifact@v6\n        with:\n          name: target-${{ matrix.os }}-${{ matrix.java }}-2.13-rootJVM\n\n      - name: Inflate target directories (2.13, rootJVM)\n        run: |\n          tar xf targets.tar\n          rm targets.tar\n\n      - name: Download target directories (2.13, rootNative)\n        uses: actions/download-artifact@v6\n        with:\n          name: target-${{ matrix.os }}-${{ matrix.java }}-2.13-rootNative\n\n      - name: Inflate target directories (2.13, rootNative)\n        run: |\n          tar xf targets.tar\n          rm targets.tar\n\n      - name: Download target directories (3, rootJS)\n        uses: actions/download-artifact@v6\n        with:\n          name: target-${{ matrix.os }}-${{ matrix.java }}-3-rootJS\n\n      - name: Inflate target directories (3, rootJS)\n        run: |\n          tar xf targets.tar\n          rm targets.tar\n\n      - name: Download target directories (3, rootJVM)\n        uses: actions/download-artifact@v6\n        with:\n          name: target-${{ matrix.os }}-${{ matrix.java }}-3-rootJVM\n\n      - name: Inflate target directories (3, rootJVM)\n        run: |\n          tar xf targets.tar\n          rm targets.tar\n\n      - name: Download target directories (3, rootNative)\n        uses: actions/download-artifact@v6\n        with:\n          name: target-${{ matrix.os }}-${{ matrix.java }}-3-rootNative\n\n      - name: Inflate target directories (3, rootNative)\n        run: |\n          tar xf targets.tar\n          rm targets.tar\n\n      - name: Import signing key\n        if: env.PGP_SECRET != '' && env.PGP_PASSPHRASE == ''\n        env:\n          PGP_SECRET: ${{ secrets.PGP_SECRET }}\n          PGP_PASSPHRASE: ${{ secrets.PGP_PASSPHRASE }}\n        run: echo $PGP_SECRET | base64 -d -i - | gpg --import\n\n      - name: Import signing key and strip passphrase\n        if: env.PGP_SECRET != '' && env.PGP_PASSPHRASE != ''\n        env:\n          PGP_SECRET: ${{ secrets.PGP_SECRET }}\n          PGP_PASSPHRASE: ${{ secrets.PGP_PASSPHRASE }}\n        run: |\n          echo \"$PGP_SECRET\" | base64 -d -i - > /tmp/signing-key.gpg\n          echo \"$PGP_PASSPHRASE\" | gpg --pinentry-mode loopback --passphrase-fd 0 --import /tmp/signing-key.gpg\n          (echo \"$PGP_PASSPHRASE\"; echo; echo) | gpg --command-fd 0 --pinentry-mode loopback --change-passphrase $(gpg --list-secret-keys --with-colons 2> /dev/null | grep '^sec:' | cut --delimiter ':' --fields 5 | tail -n 1)\n\n      - name: Publish\n        env:\n          SONATYPE_USERNAME: ${{ secrets.SONATYPE_USERNAME }}\n          SONATYPE_PASSWORD: ${{ secrets.SONATYPE_PASSWORD }}\n          SONATYPE_CREDENTIAL_HOST: ${{ secrets.SONATYPE_CREDENTIAL_HOST }}\n        run: sbt tlCiRelease\n\n  dependency-submission:\n    name: Submit Dependencies\n    if: github.event.repository.fork == false && github.event_name != 'pull_request'\n    strategy:\n      matrix:\n        os: [ubuntu-22.04]\n        java: [temurin@8]\n    runs-on: ${{ matrix.os }}\n    steps:\n      - name: Checkout current branch (full)\n        uses: actions/checkout@v6\n        with:\n          fetch-depth: 0\n\n      - name: Setup sbt\n        uses: sbt/setup-sbt@v1\n\n      - name: Setup Java (temurin@8)\n        id: setup-java-temurin-8\n        if: matrix.java == 'temurin@8'\n        uses: actions/setup-java@v5\n        with:\n          distribution: temurin\n          java-version: 8\n          cache: sbt\n\n      - name: sbt update\n        if: matrix.java == 'temurin@8' && steps.setup-java-temurin-8.outputs.cache-hit == 'false'\n        run: sbt +update\n\n      - name: Submit Dependencies\n        uses: scalacenter/sbt-dependency-submission@v2\n        with:\n          modules-ignore: rootjs_2.12 rootjs_2.13 rootjs_3 rootjvm_2.12 rootjvm_2.13 rootjvm_3 rootnative_2.12 rootnative_2.13 rootnative_3\n          configs-ignore: test scala-tool scala-doc-tool test-internal\n"
  },
  {
    "path": ".github/workflows/clean.yml",
    "content": "# This file was automatically generated by sbt-github-actions using the\n# githubWorkflowGenerate task. You should add and commit this file to\n# your git repository. It goes without saying that you shouldn't edit\n# this file by hand! Instead, if you wish to make changes, you should\n# change your sbt build configuration to revise the workflow description\n# to meet your needs, then regenerate this file.\n\nname: Clean\n\non: push\n\njobs:\n  delete-artifacts:\n    name: Delete Artifacts\n    runs-on: ubuntu-latest\n    env:\n      GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}\n    steps:\n      - name: Delete artifacts\n        run: |\n          # Customize those three lines with your repository and credentials:\n          REPO=${GITHUB_API_URL}/repos/${{ github.repository }}\n\n          # A shortcut to call GitHub API.\n          ghapi() { curl --silent --location --user _:$GITHUB_TOKEN \"$@\"; }\n\n          # A temporary file which receives HTTP response headers.\n          TMPFILE=/tmp/tmp.$$\n\n          # An associative array, key: artifact name, value: number of artifacts of that name.\n          declare -A ARTCOUNT\n\n          # Process all artifacts on this repository, loop on returned \"pages\".\n          URL=$REPO/actions/artifacts\n          while [[ -n \"$URL\" ]]; do\n\n            # Get current page, get response headers in a temporary file.\n            JSON=$(ghapi --dump-header $TMPFILE \"$URL\")\n\n            # Get URL of next page. Will be empty if we are at the last page.\n            URL=$(grep '^Link:' \"$TMPFILE\" | tr ',' '\\n' | grep 'rel=\"next\"' | head -1 | sed -e 's/.*<//' -e 's/>.*//')\n            rm -f $TMPFILE\n\n            # Number of artifacts on this page:\n            COUNT=$(( $(jq <<<$JSON -r '.artifacts | length') ))\n\n            # Loop on all artifacts on this page.\n            for ((i=0; $i < $COUNT; i++)); do\n\n              # Get name of artifact and count instances of this name.\n              name=$(jq <<<$JSON -r \".artifacts[$i].name?\")\n              ARTCOUNT[$name]=$(( $(( ${ARTCOUNT[$name]} )) + 1))\n\n              id=$(jq <<<$JSON -r \".artifacts[$i].id?\")\n              size=$(( $(jq <<<$JSON -r \".artifacts[$i].size_in_bytes?\") ))\n              printf \"Deleting '%s' #%d, %'d bytes\\n\" $name ${ARTCOUNT[$name]} $size\n              ghapi -X DELETE $REPO/actions/artifacts/$id\n            done\n          done\n"
  },
  {
    "path": ".gitignore",
    "content": "target\n.classpath\n.project\n.settings/\n.worksheet/\n.cache\n.tags\n.idea\n.idea_modules/\n.DS_Store\n*.swp\n.metaserver\n.bloop\n.metals\n.vscode\nproject/metals.sbt\nproject/project\n.bsp\n\n"
  },
  {
    "path": ".sbtopts",
    "content": "-J-Xms2g\n-J-Xmx4g\n-J-XX:MaxMetaspaceSize=512m\n"
  },
  {
    "path": ".scalafmt.conf",
    "content": "version = \"3.10.7\"\n\nmaxColumn: 120\n\nrunner.dialect = scala213source3\n\nproject.layout = StandardConvention\n\nrewrite.scala3.convertToNewSyntax = true\nrewrite.scala3.newSyntax.control = false\nrunner.dialectOverride.allowUnderscoreAsTypePlaceholder = false\nrunner.dialectOverride.allowSignificantIndentation = false\nrunner.dialectOverride.allowStarWildcardImport = false\n\ndocstrings.wrap = \"no\"\n"
  },
  {
    "path": "CODE_OF_CONDUCT.md",
    "content": "# Code of Conduct\n\nWe are committed to providing a friendly, safe and welcoming environment for all, regardless of level of experience, gender, gender identity and expression, sexual orientation, disability, personal appearance, body size, race, ethnicity, age, religion, nationality, or other such characteristics.\n\nEveryone is expected to follow the [Scala Code of Conduct] when discussing the project on the available communication channels. If you are being harassed, please contact us immediately so that we can support you.\n\n## Moderation\n\nAny questions, concerns, or moderation requests please contact a member of the project.\n\n- [Michael Pilquist](mailto:mpilquist@gmail.com)\n\n[Scala Code of Conduct]: https://www.scala-lang.org/conduct/\n"
  },
  {
    "path": "CONTRIBUTING",
    "content": "If you would like to contribute code to this project you can do so through\nGitHub by forking the repository and sending a pull request.\n\nBefore Comcast merges your code into the project you must sign the [Comcast\nContributor License Agreement (CLA)](https://gist.github.com/ComcastOSS/a7b8933dd8e368535378cda25c92d19a).\n\nIf you haven't previously signed a Comcast CLA, you'll automatically be asked\nto when you open a pull request. Alternatively, we can send you a PDF that\nyou can sign and scan back to us. Please create a new GitHub issue to request\na PDF version of the CLA.\n\n"
  },
  {
    "path": "LICENSE",
    "content": "                                 Apache License\n                           Version 2.0, January 2004\n                        http://www.apache.org/licenses/\n\n   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n   1. Definitions.\n\n      \"License\" shall mean the terms and conditions for use, reproduction,\n      and distribution as defined by Sections 1 through 9 of this document.\n\n      \"Licensor\" shall mean the copyright owner or entity authorized by\n      the copyright owner that is granting the License.\n\n      \"Legal Entity\" shall mean the union of the acting entity and all\n      other entities that control, are controlled by, or are under common\n      control with that entity. For the purposes of this definition,\n      \"control\" means (i) the power, direct or indirect, to cause the\n      direction or management of such entity, whether by contract or\n      otherwise, or (ii) ownership of fifty percent (50%) or more of the\n      outstanding shares, or (iii) beneficial ownership of such entity.\n\n      \"You\" (or \"Your\") shall mean an individual or Legal Entity\n      exercising permissions granted by this License.\n\n      \"Source\" form shall mean the preferred form for making modifications,\n      including but not limited to software source code, documentation\n      source, and configuration files.\n\n      \"Object\" form shall mean any form resulting from mechanical\n      transformation or translation of a Source form, including but\n      not limited to compiled object code, generated documentation,\n      and conversions to other media types.\n\n      \"Work\" shall mean the work of authorship, whether in Source or\n      Object form, made available under the License, as indicated by a\n      copyright notice that is included in or attached to the work\n      (an example is provided in the Appendix below).\n\n      \"Derivative Works\" shall mean any work, whether in Source or Object\n      form, that is based on (or derived from) the Work and for which the\n      editorial revisions, annotations, elaborations, or other modifications\n      represent, as a whole, an original work of authorship. For the purposes\n      of this License, Derivative Works shall not include works that remain\n      separable from, or merely link (or bind by name) to the interfaces of,\n      the Work and Derivative Works thereof.\n\n      \"Contribution\" shall mean any work of authorship, including\n      the original version of the Work and any modifications or additions\n      to that Work or Derivative Works thereof, that is intentionally\n      submitted to Licensor for inclusion in the Work by the copyright owner\n      or by an individual or Legal Entity authorized to submit on behalf of\n      the copyright owner. For the purposes of this definition, \"submitted\"\n      means any form of electronic, verbal, or written communication sent\n      to the Licensor or its representatives, including but not limited to\n      communication on electronic mailing lists, source code control systems,\n      and issue tracking systems that are managed by, or on behalf of, the\n      Licensor for the purpose of discussing and improving the Work, but\n      excluding communication that is conspicuously marked or otherwise\n      designated in writing by the copyright owner as \"Not a Contribution.\"\n\n      \"Contributor\" shall mean Licensor and any individual or Legal Entity\n      on behalf of whom a Contribution has been received by Licensor and\n      subsequently incorporated within the Work.\n\n   2. Grant of Copyright License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      copyright license to reproduce, prepare Derivative Works of,\n      publicly display, publicly perform, sublicense, and distribute the\n      Work and such Derivative Works in Source or Object form.\n\n   3. Grant of Patent License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      (except as stated in this section) patent license to make, have made,\n      use, offer to sell, sell, import, and otherwise transfer the Work,\n      where such license applies only to those patent claims licensable\n      by such Contributor that are necessarily infringed by their\n      Contribution(s) alone or by combination of their Contribution(s)\n      with the Work to which such Contribution(s) was submitted. If You\n      institute patent litigation against any entity (including a\n      cross-claim or counterclaim in a lawsuit) alleging that the Work\n      or a Contribution incorporated within the Work constitutes direct\n      or contributory patent infringement, then any patent licenses\n      granted to You under this License for that Work shall terminate\n      as of the date such litigation is filed.\n\n   4. Redistribution. You may reproduce and distribute copies of the\n      Work or Derivative Works thereof in any medium, with or without\n      modifications, and in Source or Object form, provided that You\n      meet the following conditions:\n\n      (a) You must give any other recipients of the Work or\n          Derivative Works a copy of this License; and\n\n      (b) You must cause any modified files to carry prominent notices\n          stating that You changed the files; and\n\n      (c) You must retain, in the Source form of any Derivative Works\n          that You distribute, all copyright, patent, trademark, and\n          attribution notices from the Source form of the Work,\n          excluding those notices that do not pertain to any part of\n          the Derivative Works; and\n\n      (d) If the Work includes a \"NOTICE\" text file as part of its\n          distribution, then any Derivative Works that You distribute must\n          include a readable copy of the attribution notices contained\n          within such NOTICE file, excluding those notices that do not\n          pertain to any part of the Derivative Works, in at least one\n          of the following places: within a NOTICE text file distributed\n          as part of the Derivative Works; within the Source form or\n          documentation, if provided along with the Derivative Works; or,\n          within a display generated by the Derivative Works, if and\n          wherever such third-party notices normally appear. The contents\n          of the NOTICE file are for informational purposes only and\n          do not modify the License. You may add Your own attribution\n          notices within Derivative Works that You distribute, alongside\n          or as an addendum to the NOTICE text from the Work, provided\n          that such additional attribution notices cannot be construed\n          as modifying the License.\n\n      You may add Your own copyright statement to Your modifications and\n      may provide additional or different license terms and conditions\n      for use, reproduction, or distribution of Your modifications, or\n      for any such Derivative Works as a whole, provided Your use,\n      reproduction, and distribution of the Work otherwise complies with\n      the conditions stated in this License.\n\n   5. Submission of Contributions. Unless You explicitly state otherwise,\n      any Contribution intentionally submitted for inclusion in the Work\n      by You to the Licensor shall be under the terms and conditions of\n      this License, without any additional terms or conditions.\n      Notwithstanding the above, nothing herein shall supersede or modify\n      the terms of any separate license agreement you may have executed\n      with Licensor regarding such Contributions.\n\n   6. Trademarks. This License does not grant permission to use the trade\n      names, trademarks, service marks, or product names of the Licensor,\n      except as required for reasonable and customary use in describing the\n      origin of the Work and reproducing the content of the NOTICE file.\n\n   7. Disclaimer of Warranty. Unless required by applicable law or\n      agreed to in writing, Licensor provides the Work (and each\n      Contributor provides its Contributions) on an \"AS IS\" BASIS,\n      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n      implied, including, without limitation, any warranties or conditions\n      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n      PARTICULAR PURPOSE. You are solely responsible for determining the\n      appropriateness of using or redistributing the Work and assume any\n      risks associated with Your exercise of permissions under this License.\n\n   8. Limitation of Liability. In no event and under no legal theory,\n      whether in tort (including negligence), contract, or otherwise,\n      unless required by applicable law (such as deliberate and grossly\n      negligent acts) or agreed to in writing, shall any Contributor be\n      liable to You for damages, including any direct, indirect, special,\n      incidental, or consequential damages of any character arising as a\n      result of this License or out of the use or inability to use the\n      Work (including but not limited to damages for loss of goodwill,\n      work stoppage, computer failure or malfunction, or any and all\n      other commercial damages or losses), even if such Contributor\n      has been advised of the possibility of such damages.\n\n   9. Accepting Warranty or Additional Liability. While redistributing\n      the Work or Derivative Works thereof, You may choose to offer,\n      and charge a fee for, acceptance of support, warranty, indemnity,\n      or other liability obligations and/or rights consistent with this\n      License. However, in accepting such obligations, You may act only\n      on Your own behalf and on Your sole responsibility, not on behalf\n      of any other Contributor, and only if You agree to indemnify,\n      defend, and hold each Contributor harmless for any liability\n      incurred by, or claims asserted against, such Contributor by reason\n      of your accepting any such warranty or additional liability.\n\n   END OF TERMS AND CONDITIONS\n\n   APPENDIX: How to apply the Apache License to your work.\n\n      To apply the Apache License to your work, attach the following\n      boilerplate notice, with the fields enclosed by brackets \"[]\"\n      replaced with your own identifying information. (Don't include\n      the brackets!)  The text should be enclosed in the appropriate\n      comment syntax for the file format. We also recommend that a\n      file or class name and description of purpose be included on the\n      same \"printed page\" as the copyright notice for easier\n      identification within third-party archives.\n\n   Copyright [yyyy] [name of copyright owner]\n\n   Licensed under the Apache License, Version 2.0 (the \"License\");\n   you may not use this file except in compliance with the License.\n   You may obtain a copy of the License at\n\n       http://www.apache.org/licenses/LICENSE-2.0\n\n   Unless required by applicable law or agreed to in writing, software\n   distributed under the License is distributed on an \"AS IS\" BASIS,\n   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n   See the License for the specific language governing permissions and\n   limitations under the License.\n"
  },
  {
    "path": "NOTICE",
    "content": "IP Addresses for Scala and Scala.js\nCopyright 2018 Comcast Cable Communications Management, LLC\nLicensed 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\nhttp://www.apache.org/licenses/LICENSE-2.0\nUnless 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.\nThis product includes software developed at Comcast (http://www.comcast.com/).\n\nThis product includes a dependency on and sources derived from [punycode.js](https://github.com/mathiasbynens/punycode.js), which is available under the following MIT license.\n\n```\nCopyright Mathias Bynens <https://mathiasbynens.be/>\n\nPermission is hereby granted, free of charge, to any person obtaining\na copy of this software and associated documentation files (the\n\"Software\"), to deal in the Software without restriction, including\nwithout limitation the rights to use, copy, modify, merge, publish,\ndistribute, sublicense, and/or sell copies of the Software, and to\npermit persons to whom the Software is furnished to do so, subject to\nthe following conditions:\n\nThe above copyright notice and this permission notice shall be\nincluded in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\nEXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\nMERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\nNONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\nLIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\nOF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\nWITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n```\n\nThis product includes sources derived from [libuv](https://github.com/libuv/libuv/), which is available under the following license.\n\n```\nCopyright (c) 2015-present libuv project contributors.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to\ndeal in the Software without restriction, including without limitation the\nrights to use, copy, modify, merge, publish, distribute, sublicense, and/or\nsell copies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in\nall copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\nFROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\nIN THE SOFTWARE.\n```\n"
  },
  {
    "path": "README.md",
    "content": "[![Published Artifact](https://img.shields.io/maven-central/v/com.comcast/ip4s-core_3.svg)](http://search.maven.org/#search%7Cga%7C1%7Cip4s-core_3)\n[![javadoc](https://javadoc.io/badge2/com.comcast/ip4s-core_3/javadoc.svg)](https://javadoc.io/doc/com.comcast/ip4s-core_3)\n\nip4s: IP Addresses for Scala, Scala.js & Scala Native\n=====================================================\n\nThis project defines immutable, safe data structures for describing IP addresses, multicast joins, socket addresses and similar IP & network related data types.\n\nThere are two defining characteristics of this project that make it different from other similar projects:\n- all data types are immutable and every function/method is referentially transparent (e.g., no accidental DNS lookups by calling `InetAddress.getByName(...)`)\n- published for Scala, Scala.js and Scala Native\n\nSee the [guide](docs/guide.md) and [ScalaDoc](https://javadoc.io/doc/com.comcast/ip4s-core_3) for more details.\n\n## Getting Binaries\n\nThis library is published on Maven Central under group id `com.comcast` and artifact id `ip4s-core_${scalaBinaryVersion}`. Add the following to your SBT build:\n\n```scala\nlibraryDependencies += \"com.comcast\" %% \"ip4s-core\" % \"version\"\n```\n\n## Interop\n\nAs of 1.4, ip4s depends on cats and provides type class instances directly in data type companion objects. For Scalaz support, we recommend [shims](https://github.com/djspiewak/shims).\n\n## Copyright and License\n\nThis project is made available under the [Apache License, Version 2.0](LICENSE). Copyright information can be found in [NOTICE](NOTICE).\n\n## Code of Conduct\n\nSee the [Code of Conduct](CODE_OF_CONDUCT.md).\n\n"
  },
  {
    "path": "build.sbt",
    "content": "import com.typesafe.tools.mima.core._\n\nThisBuild / tlBaseVersion := \"3.7\"\n\nThisBuild / organization := \"com.comcast\"\nThisBuild / organizationName := \"Comcast Cable Communications Management, LLC\"\n\nThisBuild / startYear := Some(2018)\n\nThisBuild / developers ++= List(\n  tlGitHubDev(\"mpilquist\", \"Michael Pilquist\"),\n  tlGitHubDev(\"matthughes\", \"Matt Hughes\"),\n  tlGitHubDev(\"nequissimus\", \"Tim Steinbach\")\n)\n\nThisBuild / githubWorkflowJavaVersions := Seq(JavaSpec.temurin(\"8\"))\n\nThisBuild / crossScalaVersions := List(\"2.12.21\", \"2.13.18\", \"3.3.7\")\n\nThisBuild / tlVersionIntroduced := Map(\"3\" -> \"3.0.3\")\n\nThisBuild / doctestTestFramework := DoctestTestFramework.ScalaCheck\n\nThisBuild / initialCommands := \"import com.comcast.ip4s._\"\n\nThisBuild / mimaBinaryIssueFilters ++= Seq(\n  ProblemFilters.exclude[ReversedMissingMethodProblem](\"com.comcast.ip4s.IpAddress.toDefaultString\"), // #553\n  ProblemFilters.exclude[DirectMissingMethodProblem](\"com.comcast.ip4s.Ipv6Address.toInetAddress\"),\n  ProblemFilters.exclude[ReversedMissingMethodProblem](\"com.comcast.ip4s.Dns.*\"), // sealed trait\n  // Scala 3 (erroneously?) considered Multicast/SourceSpecificMulticast as sum types\n  ProblemFilters.exclude[DirectMissingMethodProblem](\"com.comcast.ip4s.Multicast.ordinal\"),\n  ProblemFilters.exclude[MissingTypesProblem](\"com.comcast.ip4s.Multicast$\"),\n  ProblemFilters.exclude[DirectMissingMethodProblem](\"com.comcast.ip4s.SourceSpecificMulticast.ordinal\"),\n  ProblemFilters.exclude[MissingTypesProblem](\"com.comcast.ip4s.SourceSpecificMulticast$\"),\n  ProblemFilters.exclude[ReversedMissingMethodProblem](\"com.comcast.ip4s.IpAddress.isPrivate\"), // #562\n  ProblemFilters.exclude[ReversedMissingMethodProblem](\"com.comcast.ip4s.IpAddress.isLoopback\"),\n  ProblemFilters.exclude[ReversedMissingMethodProblem](\"com.comcast.ip4s.IpAddress.isLinkLocal\"),\n  // Removed JS-specifc Punycode bindings\n  ProblemFilters.exclude[MissingClassProblem](\"com.comcast.ip4s.Punycode\"),\n  ProblemFilters.exclude[MissingClassProblem](\"com.comcast.ip4s.Punycode$\"),\n  ProblemFilters.exclude[MissingTypesProblem](\"com.comcast.ip4s.IDN$\"),\n  ProblemFilters.exclude[MissingClassProblem](\"com.comcast.ip4s.IDNCompanionPlatform\")\n)\n\nlazy val root = tlCrossRootProject.aggregate(core, testKit)\n\nlazy val testKit = crossProject(JVMPlatform, JSPlatform, NativePlatform)\n  .in(file(\"./test-kit\"))\n  .settings(commonSettings)\n  .settings(\n    name := \"ip4s-test-kit\"\n  )\n  .settings(mimaPreviousArtifacts := Set.empty)\n  .settings(\n    libraryDependencies ++= Seq(\n      \"org.scalacheck\" %%% \"scalacheck\" % \"1.19.0\",\n      \"org.scalameta\" %%% \"munit-scalacheck\" % \"1.2.0\" % Test,\n      \"org.typelevel\" %%% \"munit-cats-effect\" % \"2.2.0\" % Test\n    )\n  )\n  .jvmSettings(\n    libraryDependencies += \"com.google.guava\" % \"guava\" % \"33.5.0-jre\" % \"test\"\n  )\n  .dependsOn(core % \"compile->compile\")\n\nlazy val testKitJVM = testKit.jvm\nlazy val testKitJS = testKit.js\n  .disablePlugins(DoctestPlugin)\n  .enablePlugins(ScalaJSBundlerPlugin)\nlazy val testKitNative = testKit.js\n  .disablePlugins(DoctestPlugin)\n  .settings(commonNativeSettings)\n\nlazy val core = crossProject(JVMPlatform, JSPlatform, NativePlatform)\n  .in(file(\".\"))\n  .settings(commonSettings)\n  .settings(\n    name := \"ip4s-core\",\n    libraryDependencies ++= {\n      if (tlIsScala3.value) Nil\n      else List(\"org.scala-lang\" % \"scala-reflect\" % scalaVersion.value % \"provided\")\n    },\n    scalacOptions := scalacOptions.value.filterNot(_ == \"-source:3.0-migration\"),\n    Compile / doc / scalacOptions ++= (if (scalaVersion.value.startsWith(\"2.\")) Seq(\"-nowarn\")\n                                       else Nil)\n  )\n  .settings(\n    libraryDependencies ++= Seq(\n      \"org.typelevel\" %%% \"literally\" % \"1.2.0\",\n      \"org.typelevel\" %%% \"cats-core\" % \"2.13.0\",\n      \"org.typelevel\" %%% \"cats-effect\" % \"3.7.0\",\n      \"org.typelevel\" %%% \"idna4s-core\" % \"0.1.0\",\n      \"org.scalacheck\" %%% \"scalacheck\" % \"1.19.0\" % Test\n    )\n  )\n\nlazy val coreJVM = core.jvm.settings(\n  doctestIgnoreRegex := Some(\".*Literals.scala\")\n)\n\nlazy val coreJS = core.js\n  .disablePlugins(DoctestPlugin)\n  .enablePlugins(ScalaJSBundlerPlugin)\n  .settings(\n    scalaJSLinkerConfig ~= (_.withModuleKind(ModuleKind.CommonJSModule))\n  )\n\nlazy val coreNative = core.native\n  .disablePlugins(DoctestPlugin)\n  .settings(commonNativeSettings)\n\nlazy val docs = project\n  .in(file(\"docs\"))\n  .enablePlugins(MdocPlugin)\n  .dependsOn(coreJVM)\n  .settings(\n    mdocIn := baseDirectory.value / \"src\",\n    mdocOut := baseDirectory.value / \"../docs\",\n    githubWorkflowArtifactUpload := false,\n    libraryDependencies += \"org.typelevel\" %%% \"cats-effect\" % \"3.7.0\"\n  )\n\nlazy val commonSettings = Seq(\n  Compile / unmanagedResources ++= {\n    val base = baseDirectory.value / \"..\"\n    (base / \"NOTICE\") +: (base / \"LICENSE\") +: (base / \"CONTRIBUTING\") +: ((base / \"licenses\") * \"LICENSE_*\").get\n  }\n)\n\nlazy val commonNativeSettings = Seq(\n  tlVersionIntroduced := List(\"2.12\", \"2.13\", \"3\").map(_ -> \"3.8.0\").toMap\n)\n"
  },
  {
    "path": "docs/guide.md",
    "content": "ip4s: IP Addresses for Scala & Scala.js\n=======================================\n\nThis is the guide for IP Addresses for Scala & Scala.js. This library provides the package `com.comcast.ip4s`, which contains all types. It is a small package and it is often useful to import all of its contents via `import com.comcast.ip4s._` -- doing so will enable some syntax.\n\n# IP Addresses\n\nThe `IpAddress` type represents either an IPv4 address or an IPv6 address. The primary mechanism to construct an `IpAddress` is `IpAddress.fromString`, which converts a string to an `Option[IpAddress]`. You can also construct an `IpAddress` from a byte array of either 4 bytes or 16 bytes.\n\n```scala\nimport com.comcast.ip4s.IpAddress\n\nval home = IpAddress.fromString(\"127.0.0.1\")\n// home: Option[IpAddress] = Some(127.0.0.1)\nval home6 = IpAddress.fromString(\"::1\")\n// home6: Option[IpAddress] = Some(::1)\n```\n\nThe `toString` method on `IpAddress` renders the IP in dotted-decimal notation if it is a V4 address and condensed string notation if it is a V6 address. The `toBytes` method converts the IP into a 4 or 16 element byte array. There are a few more methods on `IpAddress` that we'll look at later but not many more -- the API is small.\n\nSometimes it is useful to explicitly require an IPv4 or IPv6 address -- for example, when modelling the configuration of a device that requires an IPv6 address. This can be accomplished by using the `Ipv4Address` or `Ipv6Address` types, both of which are subtypes of `IpAddress`. We can construct these types directly via methods on their companions:\n\n```scala\nimport com.comcast.ip4s.{Ipv4Address, Ipv6Address}\n\nval explicitV4Home = Ipv4Address.fromString(\"127.0.0.1\")\n// explicitV4Home: Option[Ipv4Address] = Some(127.0.0.1)\nval explicitV6Home = Ipv6Address.fromString(\"::1\")\n// explicitV6Home: Option[Ipv6Address] = Some(::1)\n```\n\nBecause `Ipv4Address` and `Ipv6Address` are subtypes of `IpAddress`, we can pattern match on an `IpAddress` or use the `fold` method:\n\n```scala\nimport com.comcast.ip4s.{Ipv4Address, Ipv6Address}\n\nval homeIsV4 = home.get match {\n  case _: Ipv4Address => true\n  case _: Ipv6Address => false\n}\n// homeIsV4: Boolean = true\n\nval home6IsV4 = home6.get.fold(_ => true, _ => false)\n// home6IsV4: Boolean = false\n```\n\n## IP Literals\n\nIn the previous examples, all of the IP addresses were wrapped in an `Option`. When a string is statically (i.e. at compile time) known to be a valid IP address, we can avoid the `Option` entirely by using IP address string interpolators.\n\n```scala\nimport com.comcast.ip4s._\n\nval home = ip\"127.0.0.1\"\n// home: IpAddress = 127.0.0.1\nval home4 = ipv4\"127.0.0.1\"\n// home4: Ipv4Address = 127.0.0.1\nval home6 = ipv6\"::1\"\n// home6: Ipv6Address = ::1\n```\n\nThe `ip` interpolator returns an `IpAddress`, the `ipv4` interpolator returns an `Ipv4Address`, and the `ipv6` interpolator returns an `Ipv6Address`. If the string is not a valid IP of the requested type, the expression will fail to compile.\n\n## IPv6 String Formats\n\nIPv6 addresses have a number of special string formats. The default format (what's returned by `toString`) adheres to [RFC5952](https://tools.ietf.org/html/rfc5952) -- e.g., maximal use of `::` to condense string length. If instead you want a string that does not use `::` and expresses each hextet as 4 characters, call `.toUncondensedString`. Note that the `toString` method never outputs a mixed string consisting of both V6 hextets and a dotted decimal V4 address. For example, the address consisting of 12 0 bytes followed by 127, 0, 0, 1 is rendered as `::7f00:1` instead of `::127.0.0.1`. `Ipv6Address.fromString` and `IpAddress.fromString` can parse all of these formats.\n\n```scala\nimport com.comcast.ip4s._\n\nval home = ipv6\"::7f00:1\"\n// home: Ipv6Address = ::7f00:1\nval homeLong = home.toUncondensedString\n// homeLong: String = 0000:0000:0000:0000:0000:0000:7f00:0001\nval homeMixed = home.toMixedString\n// homeMixed: String = ::127.0.0.1\n\nval parsedHomeLong = Ipv6Address.fromString(homeLong)\n// parsedHomeLong: Option[Ipv6Address] = Some(::7f00:1)\nval parsedHomeMixed = Ipv6Address.fromString(homeMixed)\n// parsedHomeMixed: Option[Ipv6Address] = Some(::7f00:1)\n```\n\n## Ordering\n\nIP addresses have a defined ordering, making them sortable. Note when comparing an IPv4 address to an IPv6 address, the V4 address is converted to a V6 address by left padding with 0 bytes (aka, a \"compatible\" V4-in-V6 address).\n\n```scala\nval ips = List(ipv4\"10.1.1.1\", ipv4\"10.1.2.0\", ipv4\"10.1.0.0\", ipv6\"::1\", ipv6\"ff3b::\")\n// ips: List[IpAddress] = List(10.1.1.1, 10.1.2.0, 10.1.0.0, ::1, ff3b::)\nval sorted = ips.sorted\n// sorted: List[IpAddress] = List(::1, 10.1.0.0, 10.1.1.1, 10.1.2.0, ff3b::)\n```\n\n## JVM Integration\n\nWhen compiling for the JVM, the various IP address classes have a `toInetAddress` method which returns a `java.net.InetAddress`, allowing easy integration with libraries that use `InetAddress`.\n\n```scala\nval homeIA = ip\"127.0.0.1\".toInetAddress\n// homeIA: InetAddress = /127.0.0.1\nval home4IA = ipv4\"127.0.0.1\".toInetAddress\n// home4IA: Inet4Address = /127.0.0.1\nval home6IA = ipv6\"::1\".toInetAddress\n// home6IA: InetAddress = /0:0:0:0:0:0:0:1\n```\n\n# Multicast\n\nBoth IPv4 and IPv6 have reserved address ranges for multicast and smaller reserved ranges for source specific multicast. The address types in this library are aware of these ranges:\n\n```scala\nval ips = List(ip\"127.0.0.1\", ip\"224.10.10.10\", ip\"232.11.11.11\", ip\"::1\", ip\"ff00::10\", ip\"ff3b::11\")\n// ips: List[IpAddress] = List(127.0.0.1, 224.10.10.10, 232.11.11.11, ::1, ff00::10, ff3b::11)\nval multicastIps = ips.filter(_.isMulticast)\n// multicastIps: List[IpAddress] = List(224.10.10.10, 232.11.11.11, ff00::10, ff3b::11)\nval ssmIps = ips.filter(_.isSourceSpecificMulticast)\n// ssmIps: List[IpAddress] = List(232.11.11.11, ff3b::11)\n```\n\n## Multicast Witnesses\n\nOften, especially when modelling configuration of systems, you need a type that indicates an address is a valid multicast or source specific multicast address. That's provided by the `Multicast` and `SourceSpecificMulticast` types. These roughly look like:\n\n```scala\nsealed trait Multicast[A <: IpAddress] { def address: A }\nsealed trait SourceSpecificMulticast[A <: IpAddress] extends Multicast[A]\n```\n\nThese wrappers serve as type level witnesses that the wrapped address is a valid multicast / source specific multicast address. These types are parameterized by the type of IP address -- either `IpAddress`, `Ipv4Address`, or `Ipv6Address`, to allow for accurate domain modeling at the type level. For example, if an application only supported IPv6 source specific multicast, we can use `SourceSpecificMulticast[Ipv6Address]`.\n\nTo construct instances of `Multicast[A]` and `SourceSpecificMulticast[A]`, we can use the `asMulticast` and `asSourceSpecificMulticast` methods on `IpAddress`:\n\n```scala\nval multicastIps = ips.flatMap(_.asMulticast)\n// multicastIps: List[Multicast[IpAddress]] = List(224.10.10.10, 232.11.11.11, ff00::10, ff3b::11)\nval ssmIps = ips.flatMap(_.asSourceSpecificMulticast)\n// ssmIps: List[Strict[IpAddress]] = List(232.11.11.11, ff3b::11)\n```\n\nIt's common for source specific multicast to be used with group addresses outside the designated source specific multicast address range. To support such cases, use `asSourceSpecificMulticastLenient`:\n\n```scala\nval lenient = ips.flatMap(_.asSourceSpecificMulticastLenient)\n// lenient: List[SourceSpecificMulticast[IpAddress]] = List(224.10.10.10, 232.11.11.11, ff00::10, ff3b::11)\n```\n\nAdditionally, the `SourceSpecificMulticast.Strict[A]` type provides the guarantee that the wrapped address is in the RFC defined source specific range.\n\n## Multicast Literals\n\nThere are string interpolators for constructing multicast and source specific multicast address from literal strings, similar to the `ip`, `ipv4`, and `ipv6` interpolators. The multicast interpolators are:\n\n|Interpolator|Description|Result Type|Example|\n|------------|-----------|-----------|-------|\n|`mip`|Multicast IP|`Multicast[IpAddress]`|`mip\"224.10.10.10\"`|\n|`mipv4`|V4 Multicast IP|`Multicast[Ipv4Address]`|`mipv4\"224.10.10.10\"`|\n|`mipv6`|V6 Multicast IP|`Multicast[Ipv6Address]`|`mipv6\"ff3b::10\"`|\n|`ssmip`|Source Specific Multicast IP|`SourceSpecificMulticast[IpAddress]`|`ssmip\"224.10.10.10\"`|\n|`ssmipv4`|V4 Source Specific Multicast IP|`SourceSpecificMulticast[Ipv4Address]`|`ssmipv4\"224.10.10.10\"`|\n|`ssmipv6`|V6 Source Specific Multicast IP|`SourceSpecificMulticast[Ipv6Address]`|`ssmipv6\"ff3b::10\"`|\n\n## Multicast Joins\n\nThe `MulticastJoin` type models a request to join a multicast group. There are two types of joins -- any source joins and source specific joins. An any source join is specified by supplying a multicast IP address whereas a source specific join is specified by supplying a source IP address and a source specific multicast address. In both cases, the multicast IP address is referred to as the group.\n\nThis is modeled roughly as:\n\n```scala\nsealed trait MulticastJoin[A <: IpAddress]\ncase class AnySourceMulticastJoin[A <: IpAddress](group: Multicast[A]) extends MulticastJoin[A]\ncase class SourceSpecificMulticastJoin[A <: IpAddress](source: A, group: SourceSpecificMulticast[A]) extends MulticastJoin[A]\n```\n\n`MulticastJoin` and its subtypes are parameterized by the address type in order to optionally constrain the join to V4 or V6 addresses. The `AnySourceMulticastJoin` and `SourceSpecificMulticastJoin` types are exposed, instead of being kept as an implementation detail (or data constructor), for a similar reason -- to allow modelling where a type or function wants a very specific type like `SourceSpecificMulticastJoin[Ipv6Address]` while supporting other scnenarios that want something much more general like a `MulticastJoin[IpAddress]`.\n\nTo construct a `MulticastJoin`, we can use the `asm` and `ssm` methods in the `MulticastJoin` companion.\n\n```scala\nval j1 = MulticastJoin.ssm(ipv4\"10.11.12.13\", ssmipv4\"232.1.2.3\")\n// j1: MulticastJoin[Ipv4Address] = 10.11.12.13@232.1.2.3\nval j2 = MulticastJoin.ssm(ipv4\"10.11.12.13\", ipv4\"232.1.2.3\".asSourceSpecificMulticast.get)\n// j2: MulticastJoin[Ipv4Address] = 10.11.12.13@232.1.2.3\nval j3 = MulticastJoin.asm(mipv6\"ff3b::10\")\n// j3: MulticastJoin[Ipv6Address] = ff3b::10\n```\n\n# CIDR\n\nCIDR (classless inter-domain routing) addresses are a compact representation of an IP address and a routing prefix. They are expressed as an IP followed by `/prefixLength` where `prefixLength` represents the number of bits of the start of the address that define the routing prefix. For example, the CIDR `10.123.45.67/8` represents the IP `10.123.45.67` and the routing prefix of `255.0.0.0`.\n\nThe `Cidr` type represents CIDR addresses. It's parameterized by the type of IP address you are working with -- either `IpAddress`, `Ipv4Address`, or `Ipv6Address`.\n\n```scala\nval x = Cidr(ip\"10.123.45.67\", 8)\n// x: Cidr[IpAddress] = 10.123.45.67/8\nval y = Cidr(ipv4\"10.123.45.67\", 8)\n// y: Cidr[Ipv4Address] = 10.123.45.67/8\nval z = Cidr(ipv6\"ff3b::10\", 16)\n// z: Cidr[Ipv6Address] = ff3b::10/16\n```\n\nA shortand for constructing a `Cidr` is available as a method on `IpAddress`:\n\n```scala\nval x = ip\"10.123.45.67\" / 8\n// x: Cidr[IpAddress] = 10.123.45.67/8\nval y = ipv4\"10.123.45.67\" / 8\n// y: Cidr[Ipv4Address] = 10.123.45.67/8\nval z = ipv6\"ff3b::10\" / 16\n// z: Cidr[Ipv6Address] = ff3b::10/16\n```\n\nThe `Cidr` object provides mechanisms for parsing CIDR strings as well:\n\n```scala\nval parsedX = Cidr.fromString(\"10.123.45.67/8\")\n// parsedX: Option[Cidr[IpAddress]] = Some(10.123.45.67/8)\nval parsedY = Cidr.fromString4(\"10.123.45.67/8\")\n// parsedY: Option[Cidr[Ipv4Address]] = Some(10.123.45.67/8)\nval parsedZ = Cidr.fromString6(\"ff3b::10/16\")\n// parsedZ: Option[Cidr[Ipv6Address]] = Some(ff3b::10/16)\n```\n\nGiven a `Cidr[A]`, we can ask various things about the routing prefix:\n\n```scala\nval prefixX = x.prefix\n// prefixX: IpAddress = 10.0.0.0\nval prefixZ = z.prefix\n// prefixZ: Ipv6Address = ff3b::\n\nval maskX = x.mask\n// maskX: IpAddress = 255.0.0.0\nval maskZ = z.mask\n// maskZ: Ipv6Address = ffff::\n\nval doesXContainHome = x.contains(home)\n// doesXContainHome: Boolean = false\nval doesXContainSuccessor = x.contains(x.address.next)\n// doesXContainSuccessor: Boolean = true\nval lastAddressInX = x.last\n// lastAddressInX: IpAddress = 10.255.255.255\n```\n\n# Socket Addresses\n\nA socket address is an IP address and a TCP/UDP port number. This is roughly modeled as:\n\n```scala\ncase class SocketAddress[+A <: IpAddress](ip: A, port: Port)\n```\n\nLike we saw with `CIDR` and `MulticastJoin`, `SocketAddress` is polymorphic in address type, allowing expression of constraints like a socket address with an IPv6 IP. `SocketAddress` can be converted to and from a string representation, where V6 addresses are surrounded by square brackets.\n\n```scala\nval s = SocketAddress(ipv4\"127.0.0.1\", port\"5555\")\n// s: SocketAddress[Ipv4Address] = 127.0.0.1:5555\nval s1 = SocketAddress.fromString(s.toString)\n// s1: Option[SocketAddress[Host]] = Some(127.0.0.1:5555)\nval t = SocketAddress(ipv6\"::1\", port\"5555\")\n// t: SocketAddress[Ipv6Address] = [::1]:5555\nval t1 = SocketAddress.fromString6(t.toString)\n// t1: Option[SocketAddress[Ipv6Address]] = Some([::1]:5555)\n```\n\nOn the JVM, a `SocketAddress` can be converted to a `java.net.InetSocketAddress` via the `toInetSocketAddress` method.\n\n```scala\nval u = t.toInetSocketAddress\n// u: InetSocketAddress = /[0:0:0:0:0:0:0:1]:5555\n```\n\n## Multicast Socket Addresses\n\nSimilarly, a multicast socket address is a multicast join and a UDP port number. It is defined polymorphically in both the IP address type and the join type (general join, any source join, or source specific join). For example, compare the types of `s` and `t`:\n\n```scala\nimport com.comcast.ip4s._\n\nval s = MulticastSocketAddress(SourceSpecificMulticastJoin(ipv4\"10.10.10.10\", ssmipv4\"232.10.11.12\"), port\"5555\")\n// s: MulticastSocketAddress[SourceSpecificMulticastJoin, Ipv4Address] = 10.10.10.10@232.10.11.12:5555\nval t = MulticastSocketAddress(MulticastJoin.ssm(ip\"10.10.10.10\", ssmip\"232.10.11.12\"), port\"5555\")\n// t: MulticastSocketAddress[MulticastJoin, IpAddress] = 10.10.10.10@232.10.11.12:5555\n```\n\n# Hostnames\n\nThe `Hostname` type models an RFC1123 compliant hostname -- limited to 253 total characters, labels separated by periods, and each label consisting of ASCII letters and digits and dashes, not beginning or ending in a dash, and not exceeding 63 characters.\n\n```scala\nval home = Hostname.fromString(\"localhost\")\n// home: Option[Hostname] = Some(localhost)\nval ls = home.map(_.labels)\n// ls: Option[List[Label]] = Some(List(localhost))\nval comcast = host\"comcast.com\"\n// comcast: Hostname = comcast.com\nval cs = comcast.labels\n// cs: List[Label] = List(comcast, com)\n```\n\n## Hostname Resolution\n\nOn the JVM, hostnames can be resolved to IP addresses via `resolve` and `resolveAll`:\n\n```scala\nimport com.comcast.ip4s._\nimport cats.effect.IO, cats.effect.unsafe.implicits.global\n\nval home = host\"localhost\"\n// home: Hostname = localhost\nval homeIp = home.resolve[IO]\n// homeIp: IO[IpAddress] = IO(...)\nhomeIp.unsafeRunSync()\n// res4: IpAddress = 127.0.0.1\n\nval homeIps = home.resolveAll[IO]\n// homeIps: IO[List[IpAddress]] = IO(...)\nhomeIps.unsafeRunSync()\n// res5: List[IpAddress] = List(127.0.0.1, ::1)\n```\n\n# Internationalized Domain Names\n\nRFC1123 hostnames are limited to ASCII characters. The `IDN` type provides a way to represent Unicode hostnames.\n\n```scala\nval unicodeComcast = idn\"comcast。com\"\n// unicodeComcast: IDN = comcast。com\nunicodeComcast.hostname\n// res6: Hostname = comcast.com\n\nval emojiRegistrar = idn\"i❤.ws\"\n// emojiRegistrar: IDN = i❤.ws\nemojiRegistrar.hostname\n// res7: Hostname = xn--i-7iq.ws\n```\n\n# Hosts\n\nThe `Host` type is a common supertype of `IpAddress`, `Hostname`, and `IDN`.\n\n```scala\nval hosts = List(ip\"127.0.0.255\", home, emojiRegistrar)\n// hosts: List[Host] = List(127.0.0.255, localhost, i❤.ws)\n\nimport cats.syntax.all._\nval hostIps = hosts.traverse(_.resolve[IO]).unsafeRunSync()\n// hostIps: List[IpAddress] = List(127.0.0.255, 127.0.0.1, 132.148.137.119)\n```\n"
  },
  {
    "path": "docs/src/guide.md",
    "content": "ip4s: IP Addresses for Scala & Scala.js\n=======================================\n\nThis is the guide for IP Addresses for Scala & Scala.js. This library provides the package `com.comcast.ip4s`, which contains all types. It is a small package and it is often useful to import all of its contents via `import com.comcast.ip4s._` -- doing so will enable some syntax.\n\n# IP Addresses\n\nThe `IpAddress` type represents either an IPv4 address or an IPv6 address. The primary mechanism to construct an `IpAddress` is `IpAddress.fromString`, which converts a string to an `Option[IpAddress]`. You can also construct an `IpAddress` from a byte array of either 4 bytes or 16 bytes.\n\n```scala mdoc:to-string\nimport com.comcast.ip4s.IpAddress\n\nval home = IpAddress.fromString(\"127.0.0.1\")\nval home6 = IpAddress.fromString(\"::1\")\n```\n\nThe `toString` method on `IpAddress` renders the IP in dotted-decimal notation if it is a V4 address and condensed string notation if it is a V6 address. The `toBytes` method converts the IP into a 4 or 16 element byte array. There are a few more methods on `IpAddress` that we'll look at later but not many more -- the API is small.\n\nSometimes it is useful to explicitly require an IPv4 or IPv6 address -- for example, when modelling the configuration of a device that requires an IPv6 address. This can be accomplished by using the `Ipv4Address` or `Ipv6Address` types, both of which are subtypes of `IpAddress`. We can construct these types directly via methods on their companions:\n\n```scala mdoc:to-string\nimport com.comcast.ip4s.{Ipv4Address, Ipv6Address}\n\nval explicitV4Home = Ipv4Address.fromString(\"127.0.0.1\")\nval explicitV6Home = Ipv6Address.fromString(\"::1\")\n```\n\nBecause `Ipv4Address` and `Ipv6Address` are subtypes of `IpAddress`, we can pattern match on an `IpAddress` or use the `fold` method:\n\n```scala mdoc:to-string\nimport com.comcast.ip4s.{Ipv4Address, Ipv6Address}\n\nval homeIsV4 = home.get match {\n  case _: Ipv4Address => true\n  case _: Ipv6Address => false\n}\n\nval home6IsV4 = home6.get.fold(_ => true, _ => false)\n```\n\n## IP Literals\n\nIn the previous examples, all of the IP addresses were wrapped in an `Option`. When a string is statically (i.e. at compile time) known to be a valid IP address, we can avoid the `Option` entirely by using IP address string interpolators.\n\n```scala mdoc:reset:to-string\nimport com.comcast.ip4s._\n\nval home = ip\"127.0.0.1\"\nval home4 = ipv4\"127.0.0.1\"\nval home6 = ipv6\"::1\"\n```\n\nThe `ip` interpolator returns an `IpAddress`, the `ipv4` interpolator returns an `Ipv4Address`, and the `ipv6` interpolator returns an `Ipv6Address`. If the string is not a valid IP of the requested type, the expression will fail to compile.\n\n## IPv6 String Formats\n\nIPv6 addresses have a number of special string formats. The default format (what's returned by `toString`) adheres to [RFC5952](https://tools.ietf.org/html/rfc5952) -- e.g., maximal use of `::` to condense string length. If instead you want a string that does not use `::` and expresses each hextet as 4 characters, call `.toUncondensedString`. Note that the `toString` method never outputs a mixed string consisting of both V6 hextets and a dotted decimal V4 address. For example, the address consisting of 12 0 bytes followed by 127, 0, 0, 1 is rendered as `::7f00:1` instead of `::127.0.0.1`. `Ipv6Address.fromString` and `IpAddress.fromString` can parse all of these formats.\n\n```scala mdoc:reset:to-string\nimport com.comcast.ip4s._\n\nval home = ipv6\"::7f00:1\"\nval homeLong = home.toUncondensedString\nval homeMixed = home.toMixedString\n\nval parsedHomeLong = Ipv6Address.fromString(homeLong)\nval parsedHomeMixed = Ipv6Address.fromString(homeMixed)\n```\n\n## Ordering\n\nIP addresses have a defined ordering, making them sortable. Note when comparing an IPv4 address to an IPv6 address, the V4 address is converted to a V6 address by left padding with 0 bytes (aka, a \"compatible\" V4-in-V6 address).\n\n```scala mdoc:nest:to-string\nval ips = List(ipv4\"10.1.1.1\", ipv4\"10.1.2.0\", ipv4\"10.1.0.0\", ipv6\"::1\", ipv6\"ff3b::\")\nval sorted = ips.sorted\n```\n\n## JVM Integration\n\nWhen compiling for the JVM, the various IP address classes have a `toInetAddress` method which returns a `java.net.InetAddress`, allowing easy integration with libraries that use `InetAddress`.\n\n```scala mdoc:to-string\nval homeIA = ip\"127.0.0.1\".toInetAddress\nval home4IA = ipv4\"127.0.0.1\".toInetAddress\nval home6IA = ipv6\"::1\".toInetAddress\n```\n\n# Multicast\n\nBoth IPv4 and IPv6 have reserved address ranges for multicast and smaller reserved ranges for source specific multicast. The address types in this library are aware of these ranges:\n\n```scala mdoc:nest:to-string\nval ips = List(ip\"127.0.0.1\", ip\"224.10.10.10\", ip\"232.11.11.11\", ip\"::1\", ip\"ff00::10\", ip\"ff3b::11\")\nval multicastIps = ips.filter(_.isMulticast)\nval ssmIps = ips.filter(_.isSourceSpecificMulticast)\n```\n\n## Multicast Witnesses\n\nOften, especially when modelling configuration of systems, you need a type that indicates an address is a valid multicast or source specific multicast address. That's provided by the `Multicast` and `SourceSpecificMulticast` types. These roughly look like:\n\n```scala mdoc:to-string\nsealed trait Multicast[A <: IpAddress] { def address: A }\nsealed trait SourceSpecificMulticast[A <: IpAddress] extends Multicast[A]\n```\n\nThese wrappers serve as type level witnesses that the wrapped address is a valid multicast / source specific multicast address. These types are parameterized by the type of IP address -- either `IpAddress`, `Ipv4Address`, or `Ipv6Address`, to allow for accurate domain modeling at the type level. For example, if an application only supported IPv6 source specific multicast, we can use `SourceSpecificMulticast[Ipv6Address]`.\n\nTo construct instances of `Multicast[A]` and `SourceSpecificMulticast[A]`, we can use the `asMulticast` and `asSourceSpecificMulticast` methods on `IpAddress`:\n\n```scala mdoc:nest:to-string\nval multicastIps = ips.flatMap(_.asMulticast)\nval ssmIps = ips.flatMap(_.asSourceSpecificMulticast)\n```\n\nIt's common for source specific multicast to be used with group addresses outside the designated source specific multicast address range. To support such cases, use `asSourceSpecificMulticastLenient`:\n\n```scala mdoc:nest:to-string\nval lenient = ips.flatMap(_.asSourceSpecificMulticastLenient)\n```\n\nAdditionally, the `SourceSpecificMulticast.Strict[A]` type provides the guarantee that the wrapped address is in the RFC defined source specific range.\n\n## Multicast Literals\n\nThere are string interpolators for constructing multicast and source specific multicast address from literal strings, similar to the `ip`, `ipv4`, and `ipv6` interpolators. The multicast interpolators are:\n\n|Interpolator|Description|Result Type|Example|\n|------------|-----------|-----------|-------|\n|`mip`|Multicast IP|`Multicast[IpAddress]`|`mip\"224.10.10.10\"`|\n|`mipv4`|V4 Multicast IP|`Multicast[Ipv4Address]`|`mipv4\"224.10.10.10\"`|\n|`mipv6`|V6 Multicast IP|`Multicast[Ipv6Address]`|`mipv6\"ff3b::10\"`|\n|`ssmip`|Source Specific Multicast IP|`SourceSpecificMulticast[IpAddress]`|`ssmip\"224.10.10.10\"`|\n|`ssmipv4`|V4 Source Specific Multicast IP|`SourceSpecificMulticast[Ipv4Address]`|`ssmipv4\"224.10.10.10\"`|\n|`ssmipv6`|V6 Source Specific Multicast IP|`SourceSpecificMulticast[Ipv6Address]`|`ssmipv6\"ff3b::10\"`|\n\n## Multicast Joins\n\nThe `MulticastJoin` type models a request to join a multicast group. There are two types of joins -- any source joins and source specific joins. An any source join is specified by supplying a multicast IP address whereas a source specific join is specified by supplying a source IP address and a source specific multicast address. In both cases, the multicast IP address is referred to as the group.\n\nThis is modeled roughly as:\n\n```scala mdoc:to-string\nsealed trait MulticastJoin[A <: IpAddress]\ncase class AnySourceMulticastJoin[A <: IpAddress](group: Multicast[A]) extends MulticastJoin[A]\ncase class SourceSpecificMulticastJoin[A <: IpAddress](source: A, group: SourceSpecificMulticast[A]) extends MulticastJoin[A]\n```\n\n`MulticastJoin` and its subtypes are parameterized by the address type in order to optionally constrain the join to V4 or V6 addresses. The `AnySourceMulticastJoin` and `SourceSpecificMulticastJoin` types are exposed, instead of being kept as an implementation detail (or data constructor), for a similar reason -- to allow modelling where a type or function wants a very specific type like `SourceSpecificMulticastJoin[Ipv6Address]` while supporting other scnenarios that want something much more general like a `MulticastJoin[IpAddress]`.\n\nTo construct a `MulticastJoin`, we can use the `asm` and `ssm` methods in the `MulticastJoin` companion.\n\n```scala mdoc:to-string\nval j1 = MulticastJoin.ssm(ipv4\"10.11.12.13\", ssmipv4\"232.1.2.3\")\nval j2 = MulticastJoin.ssm(ipv4\"10.11.12.13\", ipv4\"232.1.2.3\".asSourceSpecificMulticast.get)\nval j3 = MulticastJoin.asm(mipv6\"ff3b::10\")\n```\n\n# CIDR\n\nCIDR (classless inter-domain routing) addresses are a compact representation of an IP address and a routing prefix. They are expressed as an IP followed by `/prefixLength` where `prefixLength` represents the number of bits of the start of the address that define the routing prefix. For example, the CIDR `10.123.45.67/8` represents the IP `10.123.45.67` and the routing prefix of `255.0.0.0`.\n\nThe `Cidr` type represents CIDR addresses. It's parameterized by the type of IP address you are working with -- either `IpAddress`, `Ipv4Address`, or `Ipv6Address`.\n\n```scala mdoc:nest:to-string\nval x = Cidr(ip\"10.123.45.67\", 8)\nval y = Cidr(ipv4\"10.123.45.67\", 8)\nval z = Cidr(ipv6\"ff3b::10\", 16)\n```\n\nA shortand for constructing a `Cidr` is available as a method on `IpAddress`:\n\n```scala mdoc:nest:to-string\nval x = ip\"10.123.45.67\" / 8\nval y = ipv4\"10.123.45.67\" / 8\nval z = ipv6\"ff3b::10\" / 16\n```\n\nThe `Cidr` object provides mechanisms for parsing CIDR strings as well:\n\n```scala mdoc:nest:to-string\nval parsedX = Cidr.fromString(\"10.123.45.67/8\")\nval parsedY = Cidr.fromString4(\"10.123.45.67/8\")\nval parsedZ = Cidr.fromString6(\"ff3b::10/16\")\n```\n\nGiven a `Cidr[A]`, we can ask various things about the routing prefix:\n\n```scala mdoc:nest:to-string\nval prefixX = x.prefix\nval prefixZ = z.prefix\n\nval maskX = x.mask\nval maskZ = z.mask\n\nval doesXContainHome = x.contains(home)\nval doesXContainSuccessor = x.contains(x.address.next)\nval lastAddressInX = x.last\n```\n\n# Socket Addresses\n\nA socket address is an IP address and a TCP/UDP port number. This is roughly modeled as:\n\n```scala\ncase class SocketAddress[+A <: IpAddress](ip: A, port: Port)\n```\n\nLike we saw with `CIDR` and `MulticastJoin`, `SocketAddress` is polymorphic in address type, allowing expression of constraints like a socket address with an IPv6 IP. `SocketAddress` can be converted to and from a string representation, where V6 addresses are surrounded by square brackets.\n\n```scala mdoc:nest:to-string\nval s = SocketAddress(ipv4\"127.0.0.1\", port\"5555\")\nval s1 = SocketAddress.fromString(s.toString)\nval t = SocketAddress(ipv6\"::1\", port\"5555\")\nval t1 = SocketAddress.fromString6(t.toString)\n```\n\nOn the JVM, a `SocketAddress` can be converted to a `java.net.InetSocketAddress` via the `toInetSocketAddress` method.\n\n```scala mdoc:nest:to-string\nval u = t.toInetSocketAddress\n```\n\n## Multicast Socket Addresses\n\nSimilarly, a multicast socket address is a multicast join and a UDP port number. It is defined polymorphically in both the IP address type and the join type (general join, any source join, or source specific join). For example, compare the types of `s` and `t`:\n\n```scala mdoc:reset:to-string\nimport com.comcast.ip4s._\n\nval s = MulticastSocketAddress(SourceSpecificMulticastJoin(ipv4\"10.10.10.10\", ssmipv4\"232.10.11.12\"), port\"5555\")\nval t = MulticastSocketAddress(MulticastJoin.ssm(ip\"10.10.10.10\", ssmip\"232.10.11.12\"), port\"5555\")\n```\n\n# Hostnames\n\nThe `Hostname` type models an RFC1123 compliant hostname -- limited to 253 total characters, labels separated by periods, and each label consisting of ASCII letters and digits and dashes, not beginning or ending in a dash, and not exceeding 63 characters.\n\n```scala mdoc:nest:to-string\nval home = Hostname.fromString(\"localhost\")\nval ls = home.map(_.labels)\nval comcast = host\"comcast.com\"\nval cs = comcast.labels\n```\n\n## Hostname Resolution\n\nOn the JVM, hostnames can be resolved to IP addresses via `resolve` and `resolveAll`:\n\n```scala mdoc:reset:to-string\nimport com.comcast.ip4s._\nimport cats.effect.IO, cats.effect.unsafe.implicits.global\n\nval home = host\"localhost\"\nval homeIp = home.resolve[IO]\nhomeIp.unsafeRunSync()\n\nval homeIps = home.resolveAll[IO]\nhomeIps.unsafeRunSync()\n```\n\n# Internationalized Domain Names\n\nRFC1123 hostnames are limited to ASCII characters. The `IDN` type provides a way to represent Unicode hostnames.\n\n```scala mdoc:to-string\nval unicodeComcast = idn\"comcast。com\"\nunicodeComcast.hostname\n\nval emojiRegistrar = idn\"i❤.ws\"\nemojiRegistrar.hostname\n```\n\n# Hosts\n\nThe `Host` type is a common supertype of `IpAddress`, `Hostname`, and `IDN`.\n\n```scala mdoc:to-string\nval hosts = List(ip\"127.0.0.255\", home, emojiRegistrar)\n\nimport cats.syntax.all._\nval hostIps = hosts.traverse(_.resolve[IO]).unsafeRunSync()\n```\n"
  },
  {
    "path": "js/src/main/scala/com/comcast/ip4s/DnsPlatform.scala",
    "content": "/*\n * Copyright 2018 Comcast Cable Communications Management, LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.comcast.ip4s\n\nimport cats.effect.kernel.Async\nimport cats.syntax.all._\n\nimport scala.scalajs.js\nimport scala.scalajs.js._\nimport scala.scalajs.js.annotation.JSImport\n\nimport org.typelevel.scalaccompat.annotation._\n\nprivate[ip4s] trait DnsCompanionPlatform {\n  def forAsync[F[_]](implicit F: Async[F]): Dns[F] = new UnsealedDns[F] {\n    def resolve(hostname: Hostname): F[IpAddress] =\n      F.fromPromise(F.delay(dnsPromises.lookup(hostname.toString, LookupOptions(all = false))))\n        .flatMap { address =>\n          IpAddress\n            .fromString(address.asInstanceOf[LookupResult].address)\n            .liftTo[F](new RuntimeException(\"Node.js returned invalid IP address\"))\n        }\n        .adaptError {\n          case ex @ js.JavaScriptException(error: js.Error) if error.message.contains(\"ENOTFOUND\") =>\n            new JavaScriptUnknownHostException(s\"$hostname: Name or service not known\", ex)\n        }\n\n    def resolveOption(hostname: Hostname): F[Option[IpAddress]] =\n      resolve(hostname).map(_.some).recover { case _: UnknownHostException => None }\n\n    def resolveAll(hostname: Hostname): F[List[IpAddress]] =\n      F.fromPromise(F.delay(dnsPromises.lookup(hostname.toString, LookupOptions(all = true))))\n        .flatMap { addresses =>\n          addresses\n            .asInstanceOf[js.Array[LookupResult]]\n            .toList\n            .traverse { address =>\n              IpAddress\n                .fromString(address.address)\n                .liftTo[F](new RuntimeException(\"Node.js returned invalid IP address\"))\n            }\n        }\n        .recover {\n          case js.JavaScriptException(error: js.Error) if error.message.contains(\"ENOTFOUND\") =>\n            Nil\n        }\n\n    def reverse(address: IpAddress): F[Hostname] =\n      reverseAllOrError(address).flatMap(_.headOption.liftTo(new UnknownHostException(address.toString)))\n\n    def reverseOption(address: IpAddress): F[Option[Hostname]] = reverseAll(address).map(_.headOption)\n\n    def reverseAll(address: IpAddress): F[List[Hostname]] =\n      reverseAllOrError(address).recover { case _: UnknownHostException => Nil }\n\n    private def reverseAllOrError(address: IpAddress): F[List[Hostname]] =\n      F.fromPromise(F.delay(dnsPromises.reverse(address.toString)))\n        .flatMap { hostnames =>\n          hostnames.toList.traverse { hostname =>\n            Hostname\n              .fromString(hostname)\n              .liftTo[F](new RuntimeException(\"Node.js returned invalid hostname\"))\n          }\n        }\n        .adaptError {\n          case ex @ js.JavaScriptException(error: js.Error) if error.message.contains(\"ENOTFOUND\") =>\n            new JavaScriptUnknownHostException(address.toString, ex)\n        }\n\n    def loopback: F[IpAddress] = resolve(Hostname.fromString(\"localhost\").get)\n  }\n}\n\n@js.native\n@JSImport(\"dns\", \"promises\")\n@nowarn212(\"cat=unused\")\nprivate[ip4s] object dnsPromises extends js.Any {\n\n  def lookup(hostname: String, options: LookupOptions): js.Promise[LookupResult | js.Array[LookupResult]] = js.native\n\n  def reverse(ip: String): js.Promise[js.Array[String]] = js.native\n}\n\nprivate[ip4s] sealed trait LookupOptions extends js.Object\nobject LookupOptions {\n  def apply(all: Boolean): LookupOptions = js.Dynamic.literal(all = all).asInstanceOf[LookupOptions]\n}\n\n@js.native\nprivate[ip4s] sealed trait LookupResult extends js.Object {\n  def address: String = js.native\n  def family: Int = js.native\n}\n"
  },
  {
    "path": "js/src/main/scala/com/comcast/ip4s/IpAddressPlatform.scala",
    "content": "/*\n * Copyright 2018 Comcast Cable Communications Management, LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.comcast.ip4s\n\nprivate[ip4s] trait IpAddressPlatform\nprivate[ip4s] trait IpAddressCompanionPlatform\n\nprivate[ip4s] trait Ipv4AddressPlatform extends IpAddressPlatform\nprivate[ip4s] trait Ipv4AddressCompanionPlatform\n\nprivate[ip4s] trait Ipv6AddressPlatform extends IpAddressPlatform\nprivate[ip4s] trait Ipv6AddressCompanionPlatform\n"
  },
  {
    "path": "js/src/main/scala/com/comcast/ip4s/NetworkInterfacePlatform.scala",
    "content": "/*\n * Copyright 2018 Comcast Cable Communications Management, LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.comcast.ip4s\n\nprivate[ip4s] trait NetworkInterfaceCompanionPlatform\n"
  },
  {
    "path": "js/src/main/scala/com/comcast/ip4s/NetworkInterfacesPlatform.scala",
    "content": "/*\n * Copyright 2018 Comcast Cable Communications Management, LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.comcast.ip4s\n\nimport cats.effect.Sync\n\nimport scala.scalajs.js\nimport scala.scalajs.js.annotation.JSImport\n\nprivate[ip4s] trait NetworkInterfacesCompanionPlatform {\n\n  def forSync[F[_]](implicit F: Sync[F]): NetworkInterfaces[F] =\n    new NetworkInterfaces.SyncNetworkInterfaces[F] {\n\n      def getAll: F[Map[String, NetworkInterface]] =\n        F.blocking {\n          val dict = osFacade.networkInterfaces()\n          var result = collection.immutable.ListMap.empty[String, NetworkInterface]\n          dict.keys.foreach(k => result = result + (k -> fromNetworkInterfaceInfo(k, dict(k))))\n          result\n        }\n\n      private def fromNetworkInterfaceInfo(\n          name: String,\n          nia: js.Array[osFacade.NetworkInterfaceInfo]\n      ): NetworkInterface =\n        NetworkInterface(\n          name,\n          name,\n          MacAddress.fromString(nia.head.mac).filterNot(_.toLong == 0),\n          nia.view.map { ni =>\n            val cidr = Cidr.fromString(ni.cidr).get\n            cidr.address match {\n              case v6: Ipv6Address =>\n                if (ni.scopeid == 0) cidr\n                else Cidr(v6.withScopeId(ni.scopeid.toString), cidr.prefixBits)\n              case _ => cidr\n            }\n          }.toList,\n          nia.head.internal,\n          true\n        )\n    }\n}\n\nprivate[ip4s] object osFacade {\n\n  @js.native\n  @JSImport(\"os\", \"networkInterfaces\")\n  def networkInterfaces(): js.Dictionary[js.Array[NetworkInterfaceInfo]] = js.native\n\n  @js.native\n  trait NetworkInterfaceInfo extends js.Object {\n    def mac: String = js.native\n    def cidr: String = js.native\n    def scopeid: Int = js.native\n    def internal: Boolean = js.native\n  }\n\n}\n"
  },
  {
    "path": "js/src/main/scala/com/comcast/ip4s/SocketAddressPlatform.scala",
    "content": "/*\n * Copyright 2018 Comcast Cable Communications Management, LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.comcast.ip4s\n\nprivate[ip4s] trait SocketAddressPlatform[+A <: Host]\nprivate[ip4s] trait SocketAddressCompanionPlatform\n"
  },
  {
    "path": "js/src/main/scala/com/comcast/ip4s/UnknownHostException.scala",
    "content": "/*\n * Copyright 2018 Comcast Cable Communications Management, LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.comcast.ip4s\n\nimport java.io.IOException\nimport scala.scalajs.js\nimport scala.util.control.NoStackTrace\n\nclass UnknownHostException(message: String = null, cause: Throwable = null) extends IOException(message, cause)\n\nprivate[ip4s] class JavaScriptUnknownHostException(message: String, cause: js.JavaScriptException)\n    extends UnknownHostException(message, cause)\n    with NoStackTrace\n"
  },
  {
    "path": "js/src/main/scala/com/comcast/ip4s/ip4splatform.scala",
    "content": "/*\n * Copyright 2018 Comcast Cable Communications Management, LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.comcast.ip4s\n\nprivate[comcast] trait ip4splatform\n"
  },
  {
    "path": "jvm-native/src/main/scala/com/comcast/ip4s/DnsPlatform.scala",
    "content": "/*\n * Copyright 2018 Comcast Cable Communications Management, LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.comcast.ip4s\n\nimport cats.effect.kernel.Async\nimport cats.effect.kernel.Sync\nimport cats.syntax.all._\n\nimport java.net.InetAddress\n\nprivate[ip4s] trait DnsCompanionPlatform {\n  def forAsync[F[_]: Async]: Dns[F] = forSync // alias for cross-compiling w/ JS\n\n  def forSync[F[_]](implicit F: Sync[F]): Dns[F] = new UnsealedDns[F] {\n    def resolve(hostname: Hostname): F[IpAddress] =\n      F.blocking {\n        val addr = InetAddress.getByName(hostname.toString)\n        IpAddress.fromBytes(addr.getAddress).get\n      }\n\n    def resolveOption(hostname: Hostname): F[Option[IpAddress]] =\n      resolve(hostname).map(_.some).recover { case _: UnknownHostException => None }\n\n    def resolveAll(hostname: Hostname): F[List[IpAddress]] =\n      F.blocking {\n        try {\n          val addrs = InetAddress.getAllByName(hostname.toString)\n          addrs.toList.flatMap(addr => IpAddress.fromBytes(addr.getAddress))\n        } catch {\n          case _: UnknownHostException => Nil\n        }\n      }\n\n    def reverse(address: IpAddress): F[Hostname] = {\n      val inetAddress = address.toInetAddress\n      F.blocking {\n        inetAddress.getCanonicalHostName\n      } flatMap { hn =>\n        (if (hn == inetAddress.getHostAddress)\n           None // getCanonicalHostName returns the IP address as a string on failure\n         else\n           Hostname.fromString(hn))\n          .liftTo[F](new UnknownHostException(address.toString))\n      }\n    }\n\n    def reverseOption(address: IpAddress): F[Option[Hostname]] =\n      reverse(address).map(_.some).recover { case _: UnknownHostException => None }\n\n    def reverseAll(address: IpAddress): F[List[Hostname]] =\n      reverseOption(address).map(_.toList)\n\n    def loopback: F[IpAddress] =\n      F.blocking(IpAddress.fromInetAddress(InetAddress.getByName(null)))\n  }\n}\n"
  },
  {
    "path": "jvm-native/src/main/scala/com/comcast/ip4s/IpAddressPlatform.scala",
    "content": "/*\n * Copyright 2018 Comcast Cable Communications Management, LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.comcast.ip4s\n\nimport java.net.{InetAddress, Inet4Address, Inet6Address}\n\nprivate[ip4s] trait IpAddressPlatform {\n\n  /** Converts this address to a `java.net.InetAddress`. Note this method only exists on the JVM. */\n  def toInetAddress: InetAddress\n}\n\nprivate[ip4s] trait IpAddressCompanionPlatform {\n\n  /** Converts the supplied `InetAddress` to an `IpAddress`. */\n  def fromInetAddress(address: InetAddress): IpAddress =\n    address match {\n      case v4: Inet4Address => Ipv4Address.fromInet4Address(v4)\n      case v6: Inet6Address => Ipv6Address.fromInet6Address(v6)\n      case _                => throw new UnsupportedOperationException(s\"unsupported address type: $address\")\n    }\n}\n\nprivate[ip4s] trait Ipv4AddressPlatform extends IpAddressPlatform {\n  protected val bytes: Array[Byte]\n\n  override def toInetAddress: Inet4Address =\n    InetAddress.getByAddress(bytes).asInstanceOf[Inet4Address]\n}\n\nprivate[ip4s] trait Ipv4AddressCompanionPlatform {\n\n  /** Converts the supplied `Inet4Address` to an `Ipv4Address`. */\n  def fromInet4Address(address: Inet4Address): Ipv4Address =\n    Ipv4Address.fromBytes(address.getAddress).get\n}\n\nprivate[ip4s] trait Ipv6AddressPlatform extends IpAddressPlatform {\n  protected val bytes: Array[Byte]\n\n  override def toInetAddress: InetAddress =\n    InetAddress.getByAddress(bytes)\n}\n\nprivate[ip4s] trait Ipv6AddressCompanionPlatform {\n\n  /** Converts the supplied `Inet6Address` to an `Ipv6Address`. */\n  def fromInet6Address(address: Inet6Address): Ipv6Address = {\n    val scopeId = address.getScopeId\n    val unscoped = Ipv6Address.fromBytes(address.getAddress).get\n    if (scopeId == 0) unscoped\n    else unscoped.withScopeId(scopeId.toString)\n  }\n}\n"
  },
  {
    "path": "jvm-native/src/main/scala/com/comcast/ip4s/NetworkInterfacePlatform.scala",
    "content": "/*\n * Copyright 2018 Comcast Cable Communications Management, LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.comcast.ip4s\n\nimport java.net.{InterfaceAddress as JInterfaceAddress, NetworkInterface as JNetworkInterface}\n\nimport Ip4sCollectionCompat.*\n\nprivate[ip4s] trait NetworkInterfaceCompanionPlatform {\n\n  def fromJava(jni: JNetworkInterface): NetworkInterface =\n    NetworkInterface(\n      jni.getName,\n      jni.getDisplayName,\n      Option(jni.getHardwareAddress).flatMap(MacAddress.fromBytes),\n      jni.getInterfaceAddresses.asScala.toList.map(fromJavaInterfaceAddress),\n      jni.isLoopback,\n      jni.isUp\n    )\n\n  private def fromJavaInterfaceAddress(ifa: JInterfaceAddress): Cidr[IpAddress] =\n    Cidr(IpAddress.fromInetAddress(ifa.getAddress), ifa.getNetworkPrefixLength.toInt)\n}\n"
  },
  {
    "path": "jvm-native/src/main/scala/com/comcast/ip4s/NetworkInterfacesPlatform.scala",
    "content": "/*\n * Copyright 2018 Comcast Cable Communications Management, LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.comcast.ip4s\n\nimport cats.effect.Sync\nimport java.net.NetworkInterface as JNetworkInterface\n\nimport Ip4sCollectionCompat.*\n\nprivate[ip4s] trait NetworkInterfacesCompanionPlatform {\n\n  def forSync[F[_]](implicit F: Sync[F]): NetworkInterfaces[F] =\n    new NetworkInterfaces.SyncNetworkInterfaces[F] {\n      def getAll: F[Map[String, NetworkInterface]] =\n        F.blocking {\n          collection.immutable.ListMap.empty[String, NetworkInterface] ++\n            JNetworkInterface.getNetworkInterfaces.asScala.toList.map(jni =>\n              jni.getName -> NetworkInterface.fromJava(jni)\n            )\n        }\n    }\n}\n"
  },
  {
    "path": "jvm-native/src/main/scala/com/comcast/ip4s/SocketAddressPlatform.scala",
    "content": "/*\n * Copyright 2018 Comcast Cable Communications Management, LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.comcast.ip4s\n\nimport cats.Applicative\n\nimport java.net.InetSocketAddress\n\nprivate[ip4s] trait SocketAddressPlatform[+A <: Host] {\n  val host: A\n  val port: Port\n\n  def toInetSocketAddress(implicit ev: A <:< IpAddress): InetSocketAddress =\n    new InetSocketAddress(host.toInetAddress, port.value)\n}\n\nprivate[ip4s] trait SocketAddressCompanionPlatform {\n  // bincompat\n  @deprecated(\"resolve is now defined on SocketAddress\", \"3.2.1\")\n  def ResolveOps(self: SocketAddress[Host]): ResolveOps = new ResolveOps(self)\n  @deprecated(\"resolve is now defined on SocketAddress\", \"3.2.1\")\n  class ResolveOps(private val self: SocketAddress[Host]) {\n\n    /** Resolves this `SocketAddress[Hostname]` to a `SocketAddress[IpAddress]`. */\n    def resolve[F[_]: Dns: Applicative]: F[SocketAddress[IpAddress]] = self.resolve\n  }\n\n  /** Converts an `InetSocketAddress` to a `SocketAddress[IpAddress]`. */\n  def fromInetSocketAddress(address: InetSocketAddress): SocketAddress[IpAddress] =\n    SocketAddress(IpAddress.fromInetAddress(address.getAddress), Port.fromInt(address.getPort).get)\n}\n"
  },
  {
    "path": "jvm-native/src/main/scala/com/comcast/ip4s/ip4splatform.scala",
    "content": "/*\n * Copyright 2018 Comcast Cable Communications Management, LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.comcast.ip4s\n\nprivate[comcast] trait ip4splatform {\n  type UnknownHostException = java.net.UnknownHostException\n}\n"
  },
  {
    "path": "project/build.properties",
    "content": "sbt.version=1.12.5\n"
  },
  {
    "path": "project/plugins.sbt",
    "content": "addSbtPlugin(\"org.typelevel\" % \"sbt-typelevel\" % \"0.8.5\")\naddSbtPlugin(\"org.scala-js\" % \"sbt-scalajs\" % \"1.20.2\")\naddSbtPlugin(\"org.scala-native\" % \"sbt-scala-native\" % \"0.5.10\")\naddSbtPlugin(\"org.portable-scala\" % \"sbt-scala-native-crossproject\" % \"1.3.2\")\naddSbtPlugin(\"ch.epfl.scala\" % \"sbt-scalajs-bundler\" % \"0.21.1\")\naddSbtPlugin(\"io.github.sbt-doctest\" % \"sbt-doctest\" % \"0.12.4\")\naddSbtPlugin(\"org.scalameta\" % \"sbt-mdoc\" % \"2.8.2\")\n\nlibraryDependencySchemes += \"com.lihaoyi\" %% \"geny\" % VersionScheme.Always\n"
  },
  {
    "path": "shared/src/main/scala/com/comcast/ip4s/Cidr.scala",
    "content": "/*\n * Copyright 2018 Comcast Cable Communications Management, LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.comcast.ip4s\n\nimport scala.util.Try\nimport scala.util.hashing.MurmurHash3\n\nimport cats.{Order, Show}\nimport com.comcast.ip4s.Cidr.Strict\n\n/** Classless Inter-Domain Routing address, which represents an IP address and its routing prefix.\n  *\n  * @param address\n  *   IP address for which this CIDR refers to\n  * @param prefixBits\n  *   number of leading 1s in the routing mask\n  */\nsealed class Cidr[+A <: IpAddress] protected (val address: A, val prefixBits: Int) extends Product with Serializable {\n  def copy[AA >: A <: IpAddress](address: AA = this.address, prefixBits: Int = this.prefixBits): Cidr[AA] =\n    Cidr[AA](address, prefixBits)\n\n  /** Returns a normalized cidr range, where the address is truncated to the prefix, so that the returned range\n    * is (and prints as) a spec-valid cidr range, with no bits outside the routing mask set.\n    *\n    * @return a normalized cidr range\n    *\n    * @example {{{\n    * scala> val raw = Cidr(ipv4\"10.11.12.13\", 8)\n    * raw: Cidr[Ipv4Address] = 10.11.12.13/8\n    * scala> raw.normalized\n    * res0: Cidr.Strict[Ipv4Address] = 10.0.0.0/8\n    * }}}\n    */\n  def normalized: Strict[A] = Cidr.Strict(this)\n\n  /** Returns the routing mask.\n    *\n    * @example {{{\n    * scala> Cidr(ipv4\"10.11.12.13\", 8).mask\n    * res0: Ipv4Address = 255.0.0.0\n    * scala> Cidr(ipv6\"2001:db8:abcd:12::\", 96).mask\n    * res1: Ipv6Address = ffff:ffff:ffff:ffff:ffff:ffff::\n    * }}}\n    */\n  def mask: A = address.transform(_ => Ipv4Address.mask(prefixBits), _ => Ipv6Address.mask(prefixBits))\n\n  /** Returns the routing prefix.\n    *\n    * Note: the routing prefix also serves as the first address in the range described by this CIDR.\n    *\n    * @example {{{\n    * scala> Cidr(ipv4\"10.11.12.13\", 8).prefix\n    * res0: Ipv4Address = 10.0.0.0\n    * scala> Cidr(ipv6\"2001:db8:abcd:12::\", 96).prefix\n    * res1: Ipv6Address = 2001:db8:abcd:12::\n    * scala> Cidr(ipv6\"2001:db8:abcd:12::\", 32).prefix\n    * res2: Ipv6Address = 2001:db8::\n    * }}}\n    */\n  def prefix: A =\n    address.transform(_.masked(Ipv4Address.mask(prefixBits)), _.masked(Ipv6Address.mask(prefixBits)))\n\n  /** Returns the last address in the range described by this CIDR.\n    *\n    * @example {{{\n    * scala> Cidr(ipv4\"10.11.12.13\", 8).last\n    * res0: Ipv4Address = 10.255.255.255\n    * scala> Cidr(ipv6\"2001:db8:abcd:12::\", 96).last\n    * res1: Ipv6Address = 2001:db8:abcd:12::ffff:ffff\n    * scala> Cidr(ipv6\"2001:db8:abcd:12::\", 32).last\n    * res2: Ipv6Address = 2001:db8:ffff:ffff:ffff:ffff:ffff:ffff\n    * }}}\n    */\n  def last: A =\n    address.transform(_.maskedLast(Ipv4Address.mask(prefixBits)), _.maskedLast(Ipv6Address.mask(prefixBits)))\n\n  /** Returns the number of addresses in the range described by this CIDR.\n    */\n  def totalIps: BigInt = {\n    BigInt(1) << (address.fold(_ => 32, _ => 128) - prefixBits)\n  }\n\n  /** Returns a predicate which tests if the supplied address is in the range described by this CIDR.\n    *\n    * @example {{{\n    * scala> Cidr(ipv4\"10.11.12.13\", 8).contains(ipv4\"10.100.100.100\")\n    * res0: Boolean = true\n    * scala> Cidr(ipv4\"10.11.12.13\", 8).contains(ipv4\"11.100.100.100\")\n    * res1: Boolean = false\n    * scala> val x = Cidr(ipv6\"2001:db8:abcd:12::\", 96).contains\n    * scala> x(ipv6\"2001:db8:abcd:12::5\")\n    * res2: Boolean = true\n    * scala> x(ipv6\"2001:db8::\")\n    * res3: Boolean = false\n    * }}}\n    */\n  def contains[AA >: A <: IpAddress]: AA => Boolean = {\n    val start = prefix\n    val end = last\n    a => a >= start && a <= end\n  }\n\n  /** Returns an iterator over all addresses in the range described by this CIDR.\n    * @return\n    */\n  def addresses: Iterator[IpAddress] = {\n    val start: IpAddress = prefix\n    val end: IpAddress = last\n\n    Iterator.iterate(start)(_.next).takeWhile(_ <= end)\n  }\n\n  override def toString: String = s\"$address/$prefixBits\"\n  override def hashCode: Int = MurmurHash3.productHash(this, productPrefix.hashCode)\n  override def equals(other: Any): Boolean =\n    other match {\n      case that: Cidr[?] => address == that.address && prefixBits == that.prefixBits\n      case _             => false\n    }\n  override def canEqual(other: Any): Boolean = other.isInstanceOf[Cidr[?]]\n  override def productArity: Int = 2\n  override def productElement(n: Int): Any =\n    n match {\n      case 0 => address\n      case 1 => prefixBits\n      case _ => throw new IndexOutOfBoundsException\n    }\n}\n\nobject Cidr {\n\n  /** A normalized cidr range, of which the address is identical to the prefix.\n    *\n    * This means the address will never have any bits set outside the prefix. For example, a range\n    * such as 192.168.0.1/31 is not allowed.\n    */\n  final class Strict[+A <: IpAddress] private (address: A, prefixBits: Int) extends Cidr[A](address, prefixBits) {\n    override def normalized: this.type = this\n    override def prefix = address\n  }\n\n  object Strict {\n    def apply[A <: IpAddress](cidr: Cidr[A]): Cidr.Strict[A] = cidr match {\n      case already: Strict[?] => already.asInstanceOf[Strict[A]]\n      case _                  => new Cidr.Strict(cidr.prefix, cidr.prefixBits)\n    }\n  }\n\n  /** Constructs a CIDR from the supplied IP address and prefix bit count. Note if `prefixBits` is less than 0, the\n    * built `Cidr` will have `prefixBits` set to 0. Similarly, if `prefixBits` is greater than the bit length of the\n    * address, it will be set to the bit length of the address.\n    */\n  def apply[A <: IpAddress](address: A, prefixBits: Int): Cidr[A] = {\n    val maxPrefixBits = address.fold(_ => 32, _ => 128)\n    val b = if (prefixBits < 0) 0 else if (prefixBits > maxPrefixBits) maxPrefixBits else prefixBits\n    new Cidr(address, b)\n  }\n\n  /** Constructs a CIDR from the supplied IP address and netmask. The number of leading 1 bits in the netmask are used\n    * as the prefix bits for the CIDR.\n    */\n  def fromIpAndMask[A <: IpAddress](address: A, mask: A): Cidr[A] =\n    address / mask.prefixBits\n\n  /** Constructs a CIDR from a string of the form `ip/prefixBits`. */\n  def fromString(value: String): Option[Cidr[IpAddress]] = fromStringGeneral(value, IpAddress.fromString)\n\n  /** Constructs a CIDR from a string of the form `ipv4/prefixBits`. */\n  def fromString4(value: String): Option[Cidr[Ipv4Address]] = fromStringGeneral(value, Ipv4Address.fromString)\n\n  /** Constructs a CIDR from a string of the form `ipv6/prefixBits`. */\n  def fromString6(value: String): Option[Cidr[Ipv6Address]] = fromStringGeneral(value, Ipv6Address.fromString)\n\n  private val CidrPattern = \"\"\"([^/]+)/(\\d+)\"\"\".r\n  private def fromStringGeneral[A <: IpAddress](value: String, parseAddress: String => Option[A]): Option[Cidr[A]] =\n    value match {\n      case CidrPattern(addrStr, prefixBitsStr) =>\n        for {\n          addr <- parseAddress(addrStr)\n          maxPrefixBits = addr.fold(_ => 32, _ => 128)\n          prefixBits <- Try(prefixBitsStr.toInt).toOption if prefixBits >= 0 && prefixBits <= maxPrefixBits\n        } yield Cidr(addr, prefixBits)\n      case _ => None\n    }\n\n  implicit def order[A <: IpAddress]: Order[Cidr[A]] = Order.fromOrdering(Cidr.ordering[A])\n  implicit def ordering[A <: IpAddress]: Ordering[Cidr[A]] = Ordering.by(x => (x.address, x.prefixBits))\n  implicit def show[A <: IpAddress]: Show[Cidr[A]] = Show.fromToString[Cidr[A]]\n\n  def unapply[A <: IpAddress](c: Cidr[A]): Option[(A, Int)] = Some((c.address, c.prefixBits))\n}\n"
  },
  {
    "path": "shared/src/main/scala/com/comcast/ip4s/Dns.scala",
    "content": "/*\n * Copyright 2018 Comcast Cable Communications Management, LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.comcast.ip4s\n\nimport cats.~>\nimport cats.Functor\nimport cats.data.EitherT\nimport cats.data.Kleisli\nimport cats.effect.IO\n\n/** Capability for an effect `F[_]` which can do DNS lookups.\n  *\n  * An instance is available for any effect which has a `Sync` instance on JVM and `Async` on Node.js.\n  */\nsealed trait Dns[F[_]] { outer =>\n\n  /** Resolves the supplied hostname to an ip address using the platform DNS resolver.\n    *\n    * If the hostname cannot be resolved, the effect fails with an `UnknownHostException`.\n    */\n  def resolve(hostname: Hostname): F[IpAddress]\n\n  /** Resolves the supplied hostname to an ip address using the platform DNS resolver.\n    *\n    * If the hostname cannot be resolved, a `None` is returned.\n    */\n  def resolveOption(hostname: Hostname): F[Option[IpAddress]]\n\n  /** Resolves the supplied hostname to all ip addresses known to the platform DNS resolver.\n    *\n    * If the hostname cannot be resolved, an empty list is returned.\n    */\n  def resolveAll(hostname: Hostname): F[List[IpAddress]]\n\n  /** Reverses the supplied address to a hostname using the platform DNS resolver.\n    *\n    * If the address cannot be reversed, the effect fails with an `UnknownHostException`.\n    */\n  def reverse(address: IpAddress): F[Hostname]\n\n  /** Reverses the supplied address to a hostname using the platform DNS resolver.\n    *\n    * If the address cannot be reversed, a `None` is returned.\n    */\n  def reverseOption(address: IpAddress): F[Option[Hostname]]\n\n  /** Reverses the supplied address to all hostnames known to the platform DNS resolver.\n    *\n    * If the address cannot be reversed, an empty list is returned.\n    */\n  def reverseAll(address: IpAddress): F[List[Hostname]]\n\n  /** Gets an IP address representing the loopback interface. */\n  def loopback: F[IpAddress]\n\n  /** Translates effect type from `F` to `G` using the supplied `FunctionK` */\n  final def mapK[G[_]](fk: F ~> G): Dns[G] = new Dns[G] {\n    def resolve(hostname: Hostname) = fk(outer.resolve(hostname))\n    def resolveOption(hostname: Hostname) = fk(outer.resolveOption(hostname))\n    def resolveAll(hostname: Hostname) = fk(outer.resolveAll(hostname))\n    def reverse(address: IpAddress) = fk(outer.reverse(address))\n    def reverseOption(address: IpAddress) = fk(outer.reverseOption(address))\n    def reverseAll(address: IpAddress) = fk(outer.reverseAll(address))\n    def loopback = fk(outer.loopback)\n  }\n}\n\nprivate[ip4s] trait UnsealedDns[F[_]] extends Dns[F]\n\nobject Dns extends DnsCompanionPlatform {\n  def apply[F[_]](implicit F: Dns[F]): F.type = F\n\n  implicit def forIO: Dns[IO] = forAsync[IO]\n\n  implicit def forEitherT[F[_]: Functor, A](implicit dns: Dns[F]): Dns[EitherT[F, A, *]] =\n    dns.mapK(EitherT.liftK)\n\n  implicit def forKleisli[F[_], A](implicit dns: Dns[F]): Dns[Kleisli[F, A, *]] =\n    dns.mapK(Kleisli.liftK)\n\n}\n"
  },
  {
    "path": "shared/src/main/scala/com/comcast/ip4s/GenSocketAddress.scala",
    "content": "/*\n * Copyright 2018 Comcast Cable Communications Management, LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.comcast.ip4s\n\n/** Supertype for types that specify address information for opening a socket.\n  *\n  * There are two built-in subtypes:\n  *  - [[SocketAddress]] - which specifies address and port info for IPv4/IPv6 sockets\n  *  - [[UnixSocketAddress]] - which specifies the path to a unix domain socket\n  *\n  * This trait is left open for extension, allowing other address types to be defined.\n  * When using this trait, pattern match on the supported subtypes.\n  */\nabstract class GenSocketAddress private[ip4s] () {\n\n  /** Downcasts this address to a `SocketAddress[IpAddress]`.\n    *\n    * @throws UnsupportedOperationException if this address is not a `SocketAddress[IpAddress]`\n    */\n  def asIpUnsafe: SocketAddress[IpAddress] = this match {\n    case addr @ SocketAddress(_: IpAddress, _) => addr.asInstanceOf[SocketAddress[IpAddress]]\n    case other                                 =>\n      throw new UnsupportedOperationException(s\"asIpUnsafe only supported on SocketAddress[IpAddress]: $other\")\n  }\n}\n"
  },
  {
    "path": "shared/src/main/scala/com/comcast/ip4s/Host.scala",
    "content": "/*\n * Copyright 2018 Comcast Cable Communications Management, LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.comcast.ip4s\n\nimport cats.{Applicative, ApplicativeThrow, Order, Show}\nimport cats.syntax.all._\n\nimport org.typelevel.idna4s.core.bootstring.Bootstring\nimport org.typelevel.idna4s.core.bootstring.BootstringParams.PunycodeParams\n\nimport scala.util.hashing.MurmurHash3\n\n// annoying bincompat shim\nprivate[ip4s] trait HostPlatform\n\n/** ADT representing either an `IpAddress`, `Hostname`, or `IDN`. */\nsealed trait Host extends HostPlatform with Ordered[Host] {\n\n  def compare(that: Host): Int =\n    this match {\n      case x: Ipv4Address =>\n        that match {\n          case y: Ipv4Address => IpAddress.compareBytes(x, y)\n          case y: Ipv6Address => IpAddress.compareBytes(x.toCompatV6, y)\n          case _              => -1\n        }\n      case x: Ipv6Address =>\n        that match {\n          case y: Ipv4Address => IpAddress.compareBytes(x, y.toCompatV6)\n          case y: Ipv6Address => IpAddress.compareBytes(x, y)\n          case _              => -1\n        }\n      case x: Hostname =>\n        that match {\n          case _: Ipv4Address => 1\n          case _: Ipv6Address => 1\n          case y: Hostname    => x.toString.compare(y.toString)\n          case y: IDN         => x.toString.compare(y.hostname.toString)\n        }\n      case x: IDN =>\n        that match {\n          case _: Ipv4Address => 1\n          case _: Ipv6Address => 1\n          case y: Hostname    => x.hostname.toString.compare(y.toString)\n          case y: IDN         => x.hostname.toString.compare(y.hostname.toString)\n        }\n    }\n\n  /** Resolves this host to an ip address using the platform DNS resolver.\n    *\n    * If the host cannot be resolved, the effect fails with a `com.comcast.ip4s.UnknownHostException`.\n    */\n  def resolve[F[_]: Dns: Applicative]: F[IpAddress] =\n    this match {\n      case ip: IpAddress      => Applicative[F].pure(ip)\n      case hostname: Hostname => Dns[F].resolve(hostname)\n      case idn: IDN           => Dns[F].resolve(idn.hostname)\n    }\n\n  /** Resolves this host to an ip address using the platform DNS resolver.\n    *\n    * If the host cannot be resolved, a `None` is returned.\n    */\n  def resolveOption[F[_]: Dns: ApplicativeThrow]: F[Option[IpAddress]] =\n    resolve[F].map(Some(_): Option[IpAddress]).recover { case _: UnknownHostException => None }\n\n  /** Resolves this host to all ip addresses known to the platform DNS resolver.\n    *\n    * If the host cannot be resolved, an empty list is returned.\n    */\n  def resolveAll[F[_]: Dns: Applicative]: F[List[IpAddress]] =\n    this match {\n      case ip: IpAddress      => Applicative[F].pure(List(ip))\n      case hostname: Hostname => Dns[F].resolveAll(hostname)\n      case idn: IDN           => Dns[F].resolveAll(idn.hostname)\n    }\n}\n\nobject Host {\n  def fromString(string: String): Option[Host] =\n    IpAddress.fromString(string) orElse Hostname.fromString(string) orElse IDN.fromString(string)\n\n  implicit def show: Show[Host] = Show.fromToString[Host]\n  implicit def order: Order[Host] = Order.fromComparable[Host]\n  implicit def ordering: Ordering[Host] = _.compare(_)\n}\n\n/** RFC1123 compliant hostname.\n  *\n  * A hostname contains one or more labels, where each label consists of letters A-Z, a-z, digits 0-9, or a dash. A\n  * label may not start or end in a dash and may not exceed 63 characters in length. Labels are separated by periods and\n  * the overall hostname must not exceed 253 characters in length.\n  */\nfinal class Hostname private (val labels: List[Hostname.Label], override val toString: String) extends Host {\n\n  /** Converts this hostname to lower case. */\n  def normalized: Hostname =\n    new Hostname(labels.map(l => new Hostname.Label(l.toString.toLowerCase)), toString.toLowerCase)\n\n  override def hashCode: Int = MurmurHash3.stringHash(toString, \"Hostname\".hashCode)\n  override def equals(other: Any): Boolean =\n    other match {\n      case that: Hostname => toString == that.toString\n      case _              => false\n    }\n}\n\nobject Hostname {\n\n  /** Label component of a hostname.\n    *\n    * A label consists of letters A-Z, a-z, digits 0-9, or a dash. A label may not start or end in a dash and may not\n    * exceed 63 characters in length.\n    */\n  final class Label private[Hostname] (override val toString: String) extends Serializable with Ordered[Label] {\n    def compare(that: Label): Int = toString.compare(that.toString)\n    override def hashCode: Int = MurmurHash3.stringHash(toString, \"Label\".hashCode)\n    override def equals(other: Any): Boolean =\n      other match {\n        case that: Label => toString == that.toString\n        case _           => false\n      }\n  }\n\n  private val Pattern =\n    \"\"\"[a-zA-Z0-9](?:[a-zA-Z0-9\\-]{0,61}[a-zA-Z0-9])?(?:\\.[a-zA-Z0-9](?:[a-zA-Z0-9\\-]{0,61}[a-zA-Z0-9])?)*\"\"\".r\n\n  /** Constructs a `Hostname` from a string. */\n  def fromString(value: String): Option[Hostname] =\n    value.size match {\n      case 0            => None\n      case i if i > 253 => None\n      case _            =>\n        value match {\n          case Pattern(_*) =>\n            val labels = value\n              .split('.')\n              .iterator\n              .map(new Label(_))\n              .toList\n            if (labels.isEmpty) None else Option(new Hostname(labels, value))\n          case _ => None\n        }\n    }\n}\n\nsealed trait IpVersion\nobject IpVersion {\n  case object V4 extends IpVersion\n  case object V6 extends IpVersion\n}\n\n/** Immutable and safe representation of an IP address, either V4 or V6.\n  *\n  * ===Construction===\n  *\n  * `IpAddress` instances are constructed in a few different ways:\n  *   - via `IpAddress(\"127.0.0.1\")`, which parses a string representation of the IP and returns an `Option[IpAddress]`\n  *   - via `IpAddress.fromBytes(arr)`, which returns an IP address if the supplied array is either exactly 4 bytes or\n  *     exactly 16 bytes\n  *   - via literal syntax like `ip\"127.0.0.1\"`, which returns an `IpAddress` and fails to *compile* if the IP is\n  *     invalid.\n  *\n  * ===Type Hierarchy===\n  *\n  * There are two subtypes of `IpAddress` -- [[Ipv4Address]] and [[Ipv6Address]]. Each of these subtypes have a richer\n  * API than `IpAddress` and it is often useful to use those types directly, for example if your use case requires a V6\n  * address. It is safe to pattern match on `IpAddress` to access `Ipv4Address` or `Ipv6Address` directly, or\n  * alternatively, you can use [[fold]].\n  *\n  * ===JVM Specific API===\n  *\n  * If using `IpAddress` on the JVM, you can call `toInetAddress` to convert the address to a `java.net.InetAddress`,\n  * for use with networking libraries. This method does not exist on the Scala.js version.\n  */\nsealed abstract class IpAddress extends IpAddressPlatform with Host with Serializable {\n  protected val bytes: Array[Byte]\n\n  /** Converts this address to a network order byte array of either 4 or 16 bytes. */\n  def toBytes: Array[Byte] = bytes.clone\n\n  /** Converts this address to a value of type `A` using the supplied functions. */\n  def fold[A](v4: Ipv4Address => A, v6: Ipv6Address => A): A\n\n  /** Maps a type-preserving function across this IP address. */\n  def transform(v4: Ipv4Address => Ipv4Address, v6: Ipv6Address => Ipv6Address): this.type\n\n  /** Returns true if this address is an IPv4 address. */\n  def isV4: Boolean =\n    fold(_ => true, _ => false)\n\n  /** Returns true if this address is an IPv6 address. */\n  def isV6: Boolean =\n    fold(_ => false, _ => true)\n\n  /** Returns true if this address is either 0.0.0.0 or ::. */\n  def isWildcard: Boolean =\n    fold(_ == Ipv4Address.Wildcard, _ == Ipv6Address.Wildcard)\n\n  /** Returns true if this address is in the multicast range. */\n  def isMulticast: Boolean\n\n  /** Converts this address to a multicast address, as long as it is in the multicast address range. */\n  def asMulticast: Option[Multicast[this.type]] = Multicast.fromIpAddress(this)\n\n  /** Returns true if this address is in the source specific multicast range. */\n  def isSourceSpecificMulticast: Boolean\n\n  /** Converts this address to a source specific multicast address, as long as it is in the source specific multicast\n    * address range.\n    */\n  def asSourceSpecificMulticast: Option[SourceSpecificMulticast.Strict[this.type]] =\n    SourceSpecificMulticast.fromIpAddress(this)\n\n  /** Like `asSourceSpecificMulticast` but allows multicast addresses outside the source specific range. */\n  def asSourceSpecificMulticastLenient: Option[SourceSpecificMulticast[this.type]] =\n    SourceSpecificMulticast.fromIpAddressLenient(this)\n\n  /** Returns true if this address is a loopback address. */\n  def isLoopback: Boolean\n\n  /** Returns true if this address is a link local address. */\n  def isLinkLocal: Boolean\n\n  /** Returns true if this address is in a private range. */\n  def isPrivate: Boolean\n\n  /** Narrows this address to an Ipv4Address if that is the underlying type. */\n  def asIpv4: Option[Ipv4Address] = collapseMappedV4.fold(Some(_), _ => None)\n\n  /** Narrows this address to an Ipv6Address if that is the underlying type. */\n  def asIpv6: Option[Ipv6Address] = fold(_ => None, Some(_))\n\n  /** Returns the version of this address. */\n  def version: IpVersion = fold(_ => IpVersion.V4, _ => IpVersion.V6)\n\n  /** Returns true if this address is a V6 address containing a mapped V4 address. */\n  def isMappedV4: Boolean = fold(_ => false, Ipv6Address.MappedV4Block.contains)\n\n  /** If this address is an IPv4 mapped IPv6 address, converts to an IPv4 address, otherwise returns this. */\n  def collapseMappedV4: IpAddress =\n    fold(identity, v6 => if (v6.isMappedV4) IpAddress.fromBytes(v6.toBytes.takeRight(4)).get else v6)\n\n  /** Constructs a [[Cidr]] address from this address. */\n  def /(prefixBits: Int): Cidr[this.type] = Cidr(this, prefixBits)\n\n  /** Returns the number of leading ones in this address. When this address is a netmask, the result can be used to\n    * create a CIDR.\n    *\n    * @example {{{\n    * scala> val address = ipv4\"192.168.1.25\"\n    * scala> val netmask = ipv4\"255.255.0.0\"\n    * scala> (address / netmask.prefixBits): Cidr[Ipv4Address]\n    * res0: Cidr[Ipv4Address] = 192.168.1.25/16\n    * }}}\n    */\n  def prefixBits: Int = {\n    import java.lang.Long.numberOfLeadingZeros\n    fold(\n      m => numberOfLeadingZeros(~(m.toLong | 0xffffffff00000000L)) - 32,\n      m => {\n        val upper = (m.toBigInt >> 64).toLong\n        val upperNum = numberOfLeadingZeros(~upper)\n        if (upperNum == 64) upperNum + numberOfLeadingZeros(~(m.toBigInt.toLong)) else upperNum\n      }\n    )\n  }\n\n  /** Gets the IP address after this address, with overflow from the maximum value to the minimum value. */\n  def next: IpAddress\n\n  /** Gets the IP address before this address, with underflow from minimum value to the maximum value. */\n  def previous: IpAddress\n\n  /** Returns the default/common representation of this address. */\n  protected[this] def toDefaultString: String\n\n  /** Converts this address to a string form that is compatible for use in a URI per RFC3986 (namely, IPv6 addresses are\n    * rendered in condensed form and surrounded by brackets).\n    */\n  def toUriString: String\n\n  /** Returns the default/common representation of this address. For IPv4\n    * addresses, returns the dotted decimal representation of the address. For\n    * IPv6 addresses, returns the condensed string representation of the array\n    * per RFC5952.\n    *\n    * This method returns the same result as [[`toUriString`]] except that IPv6\n    * addresses are not surrounded by brackets.\n    */\n  final override def toString: String = toDefaultString\n\n  override def equals(other: Any): Boolean =\n    other match {\n      case that: IpAddress => java.util.Arrays.equals(bytes, that.bytes)\n      case _               => false\n    }\n\n  override def hashCode: Int = java.util.Arrays.hashCode(bytes)\n}\n\nobject IpAddress extends IpAddressCompanionPlatform {\n\n  /** Parses an IP address from a string, either in dotted decimal notation or in RFC4291 notation. */\n  def fromString(value: String): Option[IpAddress] =\n    Ipv4Address.fromString(value) orElse Ipv6Address.fromString(value)\n\n  /** Constructs an IP address from either a 4-element byte array or a 16-element byte array. Any other size array\n    * results in a `None`.\n    */\n  def fromBytes(bytes: Array[Byte]): Option[IpAddress] =\n    Ipv4Address.fromBytes(bytes) orElse Ipv6Address.fromBytes(bytes)\n\n  /** Gets an IP address repesenting the loopback interface. */\n  def loopback[F[_]](implicit F: Dns[F]): F[IpAddress] =\n    F.loopback\n\n  private[ip4s] def compareBytes(x: IpAddress, y: IpAddress): Int = {\n    var i, result = 0\n    val xb = x.bytes\n    val yb = y.bytes\n    val sz = xb.length\n    while (i < sz && result == 0) {\n      result = Integer.compare(xb(i) & 0xff, yb(i) & 0xff)\n      i += 1\n    }\n    result\n  }\n\n  implicit def order[A <: IpAddress]: Order[A] = Order.fromOrdering(IpAddress.ordering[A])\n  implicit def ordering[A <: IpAddress]: Ordering[A] = _.compare(_)\n}\n\n/** Representation of an IPv4 address that works on both the JVM and Scala.js. */\nfinal class Ipv4Address private (protected val bytes: Array[Byte]) extends IpAddress with Ipv4AddressPlatform {\n  override def fold[A](v4: Ipv4Address => A, v6: Ipv6Address => A): A = v4(this)\n\n  override def transform(v4: Ipv4Address => Ipv4Address, v6: Ipv6Address => Ipv6Address): this.type =\n    v4(this).asInstanceOf[this.type]\n\n  /** Returns the dotted decimal representation of this address. */\n  override protected[this] def toDefaultString: String =\n    s\"${bytes(0) & 0xff}.${bytes(1) & 0xff}.${bytes(2) & 0xff}.${bytes(3) & 0xff}\"\n\n  override def toUriString: String = toDefaultString\n\n  /** Gets the IPv4 address after this address, with overflow from `255.255.255.255` to `0.0.0.0`. */\n  override def next: Ipv4Address = Ipv4Address.fromLong(toLong + 1)\n\n  /** Gets the IPv4 address before this address, with underflow from `0.0.0.0` to `255.255.255.255`. */\n  override def previous: Ipv4Address = Ipv4Address.fromLong(toLong - 1)\n\n  /** Converts this address to a 32-bit unsigned integer. */\n  def toLong: Long = {\n    val bs = bytes\n    var result = 0L\n    for (i <- 0 until bs.size) {\n      result = (result << 8) | (0x0ff & bs(i))\n    }\n    result\n  }\n\n  override def isMulticast: Boolean =\n    this >= Ipv4Address.MulticastRangeStart && this <= Ipv4Address.MulticastRangeEnd\n\n  override def isSourceSpecificMulticast: Boolean =\n    this >= Ipv4Address.SourceSpecificMulticastRangeStart && this <= Ipv4Address.SourceSpecificMulticastRangeEnd\n\n  override def isLoopback: Boolean =\n    Ipv4Address.Classes.Loopback.contains(this)\n\n  override def isLinkLocal: Boolean =\n    Ipv4Address.Classes.LinkLocal.contains(this)\n\n  override def isPrivate: Boolean =\n    Ipv4Address.Classes.Private.A.contains(this) ||\n      Ipv4Address.Classes.Private.B.contains(this) ||\n      Ipv4Address.Classes.Private.C.contains(this)\n\n  /** Converts this V4 address to a compat V6 address, where the first 12 bytes are all zero and the last 4 bytes\n    * contain the bytes of this V4 address.\n    */\n  def toCompatV6: Ipv6Address = {\n    val compat = new Array[Byte](16)\n    compat(12) = bytes(0)\n    compat(13) = bytes(1)\n    compat(14) = bytes(2)\n    compat(15) = bytes(3)\n    Ipv6Address.fromBytes(compat).get\n  }\n\n  /** Converts this V4 address to a mapped V6 address, where the first 10 bytes are all zero, the next two bytes are\n    * `ff`, and the last 4 bytes contain the bytes of this V4 address.\n    */\n  def toMappedV6: Ipv6Address = {\n    val mapped = new Array[Byte](16)\n    mapped(10) = 255.toByte\n    mapped(11) = 255.toByte\n    mapped(12) = bytes(0)\n    mapped(13) = bytes(1)\n    mapped(14) = bytes(2)\n    mapped(15) = bytes(3)\n    Ipv6Address.fromBytes(mapped).get\n  }\n\n  /** Applies the supplied mask to this address.\n    *\n    * @example {{{\n    * scala> ipv4\"192.168.29.1\".masked(ipv4\"255.255.0.0\")\n    * res0: Ipv4Address = 192.168.0.0\n    * }}}\n    */\n  def masked(mask: Ipv4Address): Ipv4Address =\n    Ipv4Address.fromLong(toLong & mask.toLong)\n\n  /** Computes the last address in the network identified by applying the supplied mask to this address.\n    *\n    * @example {{{\n    * scala> ipv4\"192.168.29.1\".maskedLast(ipv4\"255.255.0.0\")\n    * res0: Ipv4Address = 192.168.255.255\n    * }}}\n    */\n  def maskedLast(mask: Ipv4Address): Ipv4Address =\n    Ipv4Address.fromLong(toLong & mask.toLong | ~mask.toLong)\n}\n\nobject Ipv4Address extends Ipv4AddressCompanionPlatform {\n\n  /** Wildcard IPv4 address - 0.0.0.0 */\n  val Wildcard: Ipv4Address = fromBytes(0, 0, 0, 0)\n\n  /** First IP address in the IPv4 multicast range. */\n  val MulticastRangeStart: Ipv4Address = fromBytes(224, 0, 0, 0)\n\n  /** Last IP address in the IPv4 multicast range. */\n  val MulticastRangeEnd: Ipv4Address = fromBytes(239, 255, 255, 255)\n\n  /** First IP address in the IPv4 source specific multicast range. */\n  val SourceSpecificMulticastRangeStart: Ipv4Address = fromBytes(232, 0, 0, 0)\n\n  /** Last IP address in the IPv4 source specific multicast range. */\n  val SourceSpecificMulticastRangeEnd: Ipv4Address =\n    fromBytes(232, 255, 255, 255)\n\n  /** IPv4 address classes represented as CIDRs. */\n  object Classes {\n\n    /** Class A: 0.0.0.0 - 127.255.255.255 */\n    val A: Cidr[Ipv4Address] = Cidr(fromBytes(0, 0, 0, 0), 1)\n\n    /** Class B: 128.0.0.0 - 191.255.255.255 */\n    val B: Cidr[Ipv4Address] = Cidr(fromBytes(128, 0, 0, 0), 2)\n\n    /** Class C: 192.0.0.0 - 223.255.255.255 */\n    val C: Cidr[Ipv4Address] = Cidr(fromBytes(192, 0, 0, 0), 3)\n\n    /** Class D: 224.0.0.0 - 239.255.255.255 */\n    val D: Cidr[Ipv4Address] = Cidr(fromBytes(224, 0, 0, 0), 4)\n\n    /** Class E: 240.0.0.0 - 255.255.255.255 */\n    val E: Cidr[Ipv4Address] = Cidr(fromBytes(240, 0, 0, 0), 5)\n\n    /** Private address ranges. */\n    object Private {\n\n      /** Class A: 10.0.0.0 - 10.255.255.255 */\n      val A: Cidr[Ipv4Address] = Cidr(fromBytes(10, 0, 0, 0), 8)\n\n      /** Class B: 172.16.0.0 - 172.31.255.255 */\n      val B: Cidr[Ipv4Address] = Cidr(fromBytes(172, 16, 0, 0), 12)\n\n      /** Class A: 192.168.0.0 - 192.168.255.255 */\n      val C: Cidr[Ipv4Address] = Cidr(fromBytes(192, 168, 0, 0), 16)\n    }\n\n    /** Loopback: 127.0.0.0 - 127.255.255.255. */\n    val Loopback: Cidr[Ipv4Address] = Cidr(fromBytes(127, 0, 0, 0), 8)\n\n    /** Link local: 169.254.0.0 - 169.254.255.255. */\n    val LinkLocal: Cidr[Ipv4Address] = Cidr(fromBytes(169, 254, 0, 0), 16)\n  }\n\n  /** Parses an IPv4 address from a dotted-decimal string, returning `None` if the string is not a valid IPv4 address.\n    */\n  def fromString(value: String): Option[Ipv4Address] = {\n    val trimmed = value.trim\n    val fields = trimmed.split('.')\n    if (fields.length == 4) {\n      val bytes = new Array[Byte](4)\n      var idx = 0\n      var result: Option[Ipv4Address] = null\n      while (idx < bytes.length && (result eq null)) {\n        try {\n          val value = fields(idx).toInt\n          if (value >= 0 && value < 256) bytes(idx) = value.toByte\n          else result = None\n          idx += 1\n        } catch {\n          case _: NumberFormatException =>\n            result = None\n        }\n      }\n      if (result eq null) Some(unsafeFromBytes(bytes)) else result\n    } else None\n  }\n\n  /** Constructs an IPv4 address from a 4-element byte array. Returns `Some` when array is exactly 4-bytes and `None`\n    * otherwise.\n    */\n  def fromBytes(bytes: Array[Byte]): Option[Ipv4Address] =\n    if (bytes.size == 4) Some(unsafeFromBytes(bytes.clone))\n    else None\n\n  private def unsafeFromBytes(bytes: Array[Byte]): Ipv4Address =\n    new Ipv4Address(bytes)\n\n  /** Constructs an address from the specified 4 bytes.\n    *\n    * Each byte is represented as an `Int` to avoid having to manually call `.toByte` on each value -- the `toByte` call\n    * is done inside this function.\n    */\n  def fromBytes(a: Int, b: Int, c: Int, d: Int): Ipv4Address = {\n    val bytes = new Array[Byte](4)\n    bytes(0) = a.toByte\n    bytes(1) = b.toByte\n    bytes(2) = c.toByte\n    bytes(3) = d.toByte\n    unsafeFromBytes(bytes)\n  }\n\n  /** Constructs an IPv4 address from a `Long`, using the lower 32-bits. */\n  def fromLong(value: Long): Ipv4Address = {\n    val bytes = new Array[Byte](4)\n    var rem = value\n    for (i <- 3 to 0 by -1) {\n      bytes(i) = (rem & 0x0ff).toByte\n      rem = rem >> 8\n    }\n    unsafeFromBytes(bytes)\n  }\n\n  /** Computes a mask by setting the first / left-most `n` bits high.\n    *\n    * @example {{{\n    * scala> Ipv4Address.mask(16)\n    * res0: Ipv4Address = 255.255.0.0\n    * }}}\n    */\n  def mask(bits: Int): Ipv4Address = {\n    val b = if (bits < 0) 0 else if (bits > 32) 32 else bits\n    Ipv4Address.fromLong(if (b == 32) -1L else ~(-1 >>> b).toLong)\n  }\n}\n\n/** Representation of an IPv6 address that works on both the JVM and Scala.js. */\nfinal class Ipv6Address private (protected val bytes: Array[Byte], val scopeId: Option[String])\n    extends IpAddress\n    with Ipv6AddressPlatform {\n  override def fold[A](v4: Ipv4Address => A, v6: Ipv6Address => A): A = v6(this)\n  override def transform(v4: Ipv4Address => Ipv4Address, v6: Ipv6Address => Ipv6Address): this.type =\n    v6(this).asInstanceOf[this.type]\n\n  /** Returns the condensed string representation of the array per RFC5952. */\n  override protected[this] def toDefaultString: String = {\n    val fields: Array[Int] = new Array[Int](8)\n    var condensing = false\n    var condensedStart, maxCondensedStart = -1\n    var condensedLength, maxCondensedLength = 0\n    var idx = 0\n    while (idx < 8) {\n      val j = 2 * idx\n      val field = ((0x0ff & bytes(j)) << 8) | (0x0ff & bytes(j + 1))\n      fields(idx) = field\n      if (field == 0) {\n        if (!condensing) {\n          condensing = true\n          condensedStart = idx\n          condensedLength = 0\n        }\n        condensedLength += 1\n      } else {\n        condensing = false\n      }\n      if (condensedLength > maxCondensedLength) {\n        maxCondensedLength = condensedLength\n        maxCondensedStart = condensedStart\n      }\n      idx += 1\n    }\n    if (maxCondensedLength == 1) maxCondensedStart = -1\n    val str = new StringBuilder\n    idx = 0\n    while (idx < 8) {\n      if (idx == maxCondensedStart) {\n        str.append(\"::\")\n        idx += maxCondensedLength\n      } else {\n        val hextet = Integer.toString(fields(idx), 16)\n        str.append(hextet)\n        idx += 1\n        if (idx < 8 && idx != maxCondensedStart) str.append(\":\")\n      }\n    }\n    scopeId.foreach(sid => str.append(\"%\").append(sid))\n    str.toString\n  }\n\n  /** Returns an uncondensed string representation of the array. */\n  def toUncondensedString: String = {\n    val str = new StringBuilder\n    var idx = 0\n    val bytes = toBytes\n    while (idx < 16) {\n      val field = ((bytes(idx) & 0xff) << 8) | (bytes(idx + 1) & 0xff)\n      val hextet = f\"$field%04x\"\n      str.append(hextet)\n      idx += 2\n      if (idx < 15) str.append(\":\")\n    }\n    scopeId.foreach(sid => str.append(\"%\").append(sid))\n    str.toString\n  }\n\n  /** Converts this address to a string of form `x:x:x:x:x:x:a.b.c.d` where each `x` represents 16-bits and `a.b.c.d` is\n    * IPv4 dotted decimal notation. Consecutive 0 `x` fields are condensed with `::`.\n    *\n    * For example, the IPv4 address `127.0.0.1` can be converted to a compatible IPv6 address via\n    * [[Ipv4Address#toCompatV6]], which is represented as the string `::7f00:1` and the mixed string `::127.0.0.1`.\n    *\n    * Similarly, `127.0.0.1` can be converted to a mapped V6 address via [[Ipv4Address#toMappedV6]], resulting in\n    * `::ffff:7f00:1` and the mixed string `::ffff:127.0.0.1`.\n    *\n    * This format is described in RFC4291 section 2.2.3.\n    *\n    * @example {{{\n    * scala> ipv6\"::7f00:1\".toMixedString\n    * res0: String = ::127.0.0.1\n    * scala> ipv6\"ff3b:1234::ffab:7f00:1\".toMixedString\n    * res0: String = ff3b:1234::ffab:127.0.0.1\n    * }}}\n    */\n  def toMixedString: String = {\n    val bytes = toBytes\n    val v4 = Ipv4Address.fromBytes(bytes.slice(12, 16)).get\n    bytes(12) = 0\n    bytes(13) = 1\n    bytes(14) = 0\n    bytes(15) = 1\n    val s = Ipv6Address.unsafeFromBytes(bytes).toString\n    val prefix = s.slice(0, s.size - 3)\n    prefix + v4.toString\n  }\n\n  override def toUriString: String = s\"[$toDefaultString]\"\n\n  /** Gets the IPv6 address after this address, with overflow from `ffff:ffff:....:ffff` to `::`. */\n  override def next: Ipv6Address = Ipv6Address.fromBigInt(toBigInt + 1)\n\n  /** Gets the IPv6 address before this address, with underflow from `::` to `ffff:ffff:....:ffff`. */\n  override def previous: Ipv6Address = Ipv6Address.fromBigInt(toBigInt - 1)\n\n  /** Converts this address to a 128-bit unsigned integer. */\n  def toBigInt: BigInt = {\n    val bs = bytes\n    var result = BigInt(0)\n    for (i <- 0 until bs.size) {\n      result = (result << 8) | (0x0ff & bs(i))\n    }\n    result\n  }\n\n  override def isMulticast: Boolean =\n    this >= Ipv6Address.MulticastRangeStart && this <= Ipv6Address.MulticastRangeEnd\n\n  override def isSourceSpecificMulticast: Boolean =\n    this >= Ipv6Address.SourceSpecificMulticastRangeStart && this <= Ipv6Address.SourceSpecificMulticastRangeEnd\n\n  override def isLoopback: Boolean =\n    this == Ipv6Address.Loopback || (isMappedV4 && collapseMappedV4.isLoopback)\n\n  override def isLinkLocal: Boolean =\n    Ipv6Address.LinkLocalBlock.contains(this) || (isMappedV4 && collapseMappedV4.isLinkLocal)\n\n  override def isPrivate: Boolean =\n    Ipv6Address.UniqueLocalBlock.contains(this) || (isMappedV4 && collapseMappedV4.isPrivate)\n\n  /** Applies the supplied mask to this address.\n    *\n    * @example {{{\n    * scala> ipv6\"ff3b::1\".masked(ipv6\"fff0::\")\n    * res0: Ipv6Address = ff30::\n    * }}}\n    */\n  def masked(mask: Ipv6Address): Ipv6Address =\n    Ipv6Address.fromBigInt(toBigInt & mask.toBigInt)\n\n  /** Computes the last address in the network identified by applying the supplied mask to this address.\n    *\n    * @example {{{\n    * scala> ipv6\"ff3b::1\".maskedLast(ipv6\"fff0::\")\n    * res0: Ipv6Address = ff3f:ffff:ffff:ffff:ffff:ffff:ffff:ffff\n    * }}}\n    */\n  def maskedLast(mask: Ipv6Address): Ipv6Address =\n    Ipv6Address.fromBigInt(toBigInt & mask.toBigInt | ~mask.toBigInt)\n\n  /** Returns a new address with the supplied scope id. */\n  def withScopeId(scopeId: String): Ipv6Address =\n    new Ipv6Address(bytes, Some(scopeId))\n\n  /** Returns a new address with the scope id, if defined, removed. */\n  def withoutScopeId: Ipv6Address =\n    new Ipv6Address(bytes, None)\n\n  override def equals(other: Any): Boolean =\n    other match {\n      case that: Ipv6Address => java.util.Arrays.equals(bytes, that.bytes) && scopeId == that.scopeId\n      case _                 => false\n    }\n\n  override def hashCode: Int = {\n    val hash = java.util.Arrays.hashCode(bytes)\n    scopeId match {\n      case None    => hash\n      case Some(s) =>\n        val h = MurmurHash3.mix(hash, s.##)\n        MurmurHash3.finalizeHash(h, 2)\n    }\n  }\n}\n\nobject Ipv6Address extends Ipv6AddressCompanionPlatform {\n\n  /** Wildcard IPv6 address - 0.0.0.0 */\n  val Wildcard: Ipv6Address =\n    fromBytes(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)\n\n  /** First IP address in the IPv6 multicast range. */\n  val MulticastRangeStart: Ipv6Address =\n    fromBytes(255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)\n\n  /** Last IP address in the IPv6 multicast range. */\n  val MulticastRangeEnd: Ipv6Address =\n    fromBytes(255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255)\n\n  /** First IP address in the IPv6 source specific multicast range. */\n  val SourceSpecificMulticastRangeStart: Ipv6Address =\n    fromBytes(255, 48, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)\n\n  /** Last IP address in the IPv6 source specific multicast range. */\n  val SourceSpecificMulticastRangeEnd: Ipv6Address =\n    fromBytes(255, 63, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255)\n\n  /** CIDR which defines the mapped IPv4 address block (https://datatracker.ietf.org/doc/html/rfc4291#section-2.5.5.2).\n    */\n  val MappedV4Block: Cidr[Ipv6Address] =\n    Cidr(Ipv6Address.fromBytes(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 0, 0, 0, 0), 96)\n\n  /** Alias for ::1. */\n  val Loopback: Ipv6Address = Ipv6Address.fromBytes(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1)\n\n  /** CIDR which defines the unique local address block. */\n  val UniqueLocalBlock: Cidr[Ipv6Address] =\n    Cidr(Ipv6Address.fromBytes(0xfc, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0), 7)\n\n  /** CIDR which defines the linked scope unicast address block. */\n  val LinkLocalBlock: Cidr[Ipv6Address] =\n    Cidr(Ipv6Address.fromBytes(0xfe, 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0), 10)\n\n  /** Parses an IPv6 address from a string in RFC4291 notation, returning `None` if the string is not a valid IPv6\n    * address.\n    */\n  def fromString(value: String): Option[Ipv6Address] = {\n    val scopeSeparator = value.indexOf('%')\n    if (scopeSeparator < 0) {\n      fromNonMixedString(value) orElse fromMixedString(value)\n    } else {\n      val addrPart = value.substring(0, scopeSeparator)\n      val scopePart = value.substring(scopeSeparator + 1)\n      val addr = fromNonMixedString(addrPart) orElse fromMixedString(addrPart)\n      addr.map(_.withScopeId(scopePart))\n    }\n  }\n\n  private def fromNonMixedString(value: String): Option[Ipv6Address] = {\n    var prefix: List[Int] = Nil\n    var beforeCondenser = true\n    var suffix: List[Int] = Nil\n    val trimmed = value.trim()\n    var result: Option[Ipv6Address] = null\n    var idx = 0\n    var fieldStart = 0\n    while (idx < trimmed.length && (result eq null)) {\n      val c = trimmed(idx)\n\n      if ((c == ':' && idx > 0) || idx == trimmed.length - 1) {\n        val field = trimmed.substring(fieldStart, if (c == ':') idx else idx + 1)\n        if (field.size > 4) {\n          result = None\n        } else {\n          try {\n            val fieldValue = Integer.parseInt(field, 16)\n            if (beforeCondenser) prefix = fieldValue :: prefix\n            else suffix = fieldValue :: suffix\n          } catch {\n            case _: NumberFormatException =>\n              result = None\n          }\n        }\n      }\n\n      if (c == ':') {\n        if (idx == trimmed.length - 1) {\n          // String terminated in a single colon\n          result = None\n        } else if (trimmed(idx + 1) == ':') {\n          beforeCondenser = false\n          idx += 1\n        }\n        fieldStart = idx + 1\n      }\n\n      idx += 1\n    }\n\n    // If after traversing all fields, there was no condenser, then fail unless there are exactly 8 fields\n    if (beforeCondenser && (prefix.size + suffix.size) != 8) result = None\n\n    if (result ne null) {\n      result\n    } else if (prefix.isEmpty && suffix.isEmpty && (trimmed.isEmpty || trimmed != \"::\")) {\n      None\n    } else {\n      val bytes = new Array[Byte](16)\n      idx = 0\n      val prefixSize = prefix.size\n      val suffixSize = suffix.size\n      val numCondensedZeroes = 8 - (prefixSize + suffixSize)\n\n      if (numCondensedZeroes < 0) {\n        None\n      } else {\n        var prefixIdx = prefixSize - 1\n        while (prefixIdx >= 0) {\n          val value = prefix(prefixIdx)\n          bytes(idx) = (value >> 8).toByte\n          bytes(idx + 1) = value.toByte\n          prefixIdx -= 1\n          idx += 2\n        }\n\n        idx += numCondensedZeroes * 2\n\n        var suffixIdx = suffixSize - 1\n        while (suffixIdx >= 0) {\n          val value = suffix(suffixIdx)\n          bytes(idx) = (value >> 8).toByte\n          bytes(idx + 1) = value.toByte\n          suffixIdx -= 1\n          idx += 2\n        }\n\n        Some(unsafeFromBytes(bytes))\n      }\n    }\n  }\n\n  private val MixedStringFormat = \"\"\"([:a-fA-F0-9]+:)(\\d+\\.\\d+\\.\\d+\\.\\d+)\"\"\".r\n  private def fromMixedString(value: String): Option[Ipv6Address] =\n    value match {\n      case MixedStringFormat(prefix, v4Str) =>\n        for {\n          pfx <- fromNonMixedString(prefix + \"0:0\")\n          v4 <- Ipv4Address.fromString(v4Str)\n        } yield {\n          val bytes = pfx.toBytes\n          val v4bytes = v4.toBytes\n          bytes(12) = v4bytes(0)\n          bytes(13) = v4bytes(1)\n          bytes(14) = v4bytes(2)\n          bytes(15) = v4bytes(3)\n          unsafeFromBytes(bytes)\n        }\n      case _ => None\n    }\n\n  /** Constructs an IPv6 address from a 16-element byte array. Returns `Some` when array is exactly 16-bytes and `None`\n    * otherwise.\n    */\n  def fromBytes(bytes: Array[Byte]): Option[Ipv6Address] =\n    if (bytes.size == 16) Some(unsafeFromBytes(bytes.clone))\n    else None\n\n  private def unsafeFromBytes(bytes: Array[Byte]): Ipv6Address =\n    new Ipv6Address(bytes, None)\n\n  /** Constructs an address from the specified 16 bytes.\n    *\n    * Each byte is represented as an `Int` to avoid having to manually call `.toByte` on each value -- the `toByte` call\n    * is done inside this function.\n    */\n  def fromBytes(\n      b0: Int,\n      b1: Int,\n      b2: Int,\n      b3: Int,\n      b4: Int,\n      b5: Int,\n      b6: Int,\n      b7: Int,\n      b8: Int,\n      b9: Int,\n      b10: Int,\n      b11: Int,\n      b12: Int,\n      b13: Int,\n      b14: Int,\n      b15: Int\n  ): Ipv6Address = {\n    val bytes = new Array[Byte](16)\n    bytes(0) = b0.toByte\n    bytes(1) = b1.toByte\n    bytes(2) = b2.toByte\n    bytes(3) = b3.toByte\n    bytes(4) = b4.toByte\n    bytes(5) = b5.toByte\n    bytes(6) = b6.toByte\n    bytes(7) = b7.toByte\n    bytes(8) = b8.toByte\n    bytes(9) = b9.toByte\n    bytes(10) = b10.toByte\n    bytes(11) = b11.toByte\n    bytes(12) = b12.toByte\n    bytes(13) = b13.toByte\n    bytes(14) = b14.toByte\n    bytes(15) = b15.toByte\n    unsafeFromBytes(bytes)\n  }\n\n  /** Constructs an IPv6 address from a `BigInt`, using the lower 128-bits. */\n  def fromBigInt(value: BigInt): Ipv6Address = {\n    val bytes = new Array[Byte](16)\n    var rem = value\n    for (i <- 15 to 0 by -1) {\n      bytes(i) = (rem & 0x0ff).toByte\n      rem = rem >> 8\n    }\n    unsafeFromBytes(bytes)\n  }\n\n  /** Computes a mask by setting the first / left-most `n` bits high.\n    *\n    * @example {{{\n    * scala> Ipv6Address.mask(32)\n    * res0: Ipv6Address = ffff:ffff::\n    * }}}\n    */\n  def mask(bits: Int): Ipv6Address = {\n    val b = if (bits < 0) 0 else if (bits > 128) 128 else bits\n    Ipv6Address\n      .fromBigInt {\n        if (b == 128) (BigInt(-1L) << 64) | BigInt(-1L)\n        else if (b < 64) BigInt(~(-1L >>> b)) << 64\n        else (BigInt(-1L) << 64) | BigInt(~(-1L >>> (b - 64)))\n      }\n  }\n}\n\n/** Internationalized domain name, as specified by RFC3490 and RFC5891.\n  *\n  * This type models internationalized hostnames. An IDN provides unicode labels, a unicode string form, and an ASCII\n  * hostname form.\n  *\n  * A well formed IDN consists of one or more labels separated by dots. Each label may contain unicode characters as\n  * long as the converted ASCII form meets the requirements of RFC1123 (e.g., 63 or fewer characters and no leading or\n  * trailing dash). A dot is represented as an ASCII period or one of the unicode dots: full stop, ideographic full\n  * stop, fullwidth full stop, halfwidth ideographic full stop.\n  *\n  * The `toString` method returns the IDN in the form in which it was constructed. Sometimes it is useful to normalize\n  * the IDN -- converting all dots to an ASCII period and converting all labels to lowercase.\n  *\n  * Note: equality and comparison of IDNs is case-sensitive. Consider comparing normalized toString values for a more\n  * lenient notion of equality.\n  */\nfinal class IDN private (val labels: List[IDN.Label], val hostname: Hostname, override val toString: String)\n    extends Host {\n\n  /** Converts this IDN to lower case and replaces dots with ASCII periods. */\n  def normalized: IDN = {\n    val newLabels = labels.map(l => new IDN.Label(l.toString.toLowerCase))\n    new IDN(newLabels, hostname.normalized, newLabels.toList.mkString(\".\"))\n  }\n\n  override def hashCode: Int = MurmurHash3.stringHash(toString, \"IDN\".hashCode)\n  override def equals(other: Any): Boolean =\n    other match {\n      case that: IDN => toString == that.toString\n      case _         => false\n    }\n}\n\nobject IDN {\n\n  /** Label component of an IDN. */\n  final class Label private[IDN] (override val toString: String) extends Serializable with Ordered[Label] {\n    def compare(that: Label): Int = toString.compare(that.toString)\n    override def hashCode: Int = MurmurHash3.stringHash(toString, \"Label\".hashCode)\n    override def equals(other: Any): Boolean =\n      other match {\n        case that: Label => toString == that.toString\n        case _           => false\n      }\n  }\n\n  private val DotPattern = \"[\\\\.\\u002e\\u3002\\uff0e\\uff61]\"\n  private val DotPatternR = DotPattern.r.pattern\n\n  /** Constructs a `IDN` from a string. */\n  def fromString(value: String): Option[IDN] =\n    value.size match {\n      case 0 => None\n      case _ =>\n        val labels = value\n          .split(DotPattern)\n          .iterator\n          .map(new Label(_))\n          .toList\n        Option(labels).filterNot(_.isEmpty).flatMap { ls =>\n          val hostname = toAscii(value).flatMap(Hostname.fromString)\n          hostname.map(h => new IDN(ls, h, value))\n        }\n    }\n\n  /** Converts the supplied (ASCII) hostname in to an IDN. */\n  def fromHostname(hostname: Hostname): IDN = {\n    val labels =\n      hostname.labels.map(l => new Label(toUnicode(l.toString)))\n    new IDN(labels, hostname, labels.toList.mkString(\".\"))\n  }\n\n  private[ip4s] def toAscii(value: String): Option[String] =\n    DotPatternR\n      .split(value, -1)\n      .toList\n      .traverse { label =>\n        if (label.forall(_ < 128)) Some(label)\n        else Bootstring.encodeRaw(PunycodeParams)(label).toOption.map(\"xn--\" + _)\n      }\n      .map(_.mkString(\".\"))\n\n  private[ip4s] def toUnicode(value: String): String =\n    DotPatternR\n      .split(value, -1)\n      .toList\n      .traverse { label =>\n        if (label.startsWith(\"xn--\")) Bootstring.decodeRaw(PunycodeParams)(label.substring(4, label.length))\n        else Right(label)\n      }\n      .map(_.mkString(\".\"))\n      .fold(e => throw new IllegalArgumentException(e), identity[String](_))\n\n  implicit val show: Show[IDN] = Show.fromToString[IDN]\n}\n"
  },
  {
    "path": "shared/src/main/scala/com/comcast/ip4s/MacAddress.scala",
    "content": "/*\n * Copyright 2018 Comcast Cable Communications Management, LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.comcast.ip4s\n\nimport scala.util.control.NonFatal\nimport cats.{Order, Show}\n\n/** 6-byte MAC address. */\nfinal class MacAddress private (private val bytes: Array[Byte]) extends Ordered[MacAddress] with Serializable {\n  def toBytes: Array[Byte] = bytes.clone\n\n  def toLong: Long = {\n    val bs = bytes\n    var result = 0L\n    for (i <- 0 until bs.size) {\n      result = (result << 8) | (0x0ff & bs(i))\n    }\n    result\n  }\n\n  override def compare(that: MacAddress): Int = {\n    var i, result = 0\n    val tb = that.bytes\n    val sz = bytes.length\n    while (i < sz && result == 0) {\n      result = Integer.compare(bytes(i) & 0xff, tb(i) & 0xff)\n      i += 1\n    }\n    result\n  }\n\n  override def equals(other: Any): Boolean = other match {\n    case that: MacAddress => java.util.Arrays.equals(bytes, that.bytes)\n    case _                => false\n  }\n\n  override def hashCode: Int = java.util.Arrays.hashCode(bytes)\n\n  override def toString: String =\n    bytes.map(b => f\"${0xff & b}%02x\").mkString(\"\", \":\", \"\")\n}\n\nobject MacAddress {\n\n  /** Constructs a `MacAddress` from a 6-element byte array. Returns `Some` when array is exactly 6-bytes and `None`\n    * otherwise.\n    */\n  def fromBytes(bytes: Array[Byte]): Option[MacAddress] = {\n    if (bytes.length == 6) Some(new MacAddress(bytes))\n    else None\n  }\n\n  /** Constructs a `MacAddress` from the specified 6 bytes.\n    *\n    * Each byte is represented as an `Int` to avoid having to manually call `.toByte` on each value -- the `toByte` call\n    * is done inside this function.\n    */\n  def fromBytes(\n      b0: Int,\n      b1: Int,\n      b2: Int,\n      b3: Int,\n      b4: Int,\n      b5: Int\n  ): MacAddress = {\n    val bytes = new Array[Byte](6)\n    bytes(0) = b0.toByte\n    bytes(1) = b1.toByte\n    bytes(2) = b2.toByte\n    bytes(3) = b3.toByte\n    bytes(4) = b4.toByte\n    bytes(5) = b5.toByte\n    new MacAddress(bytes)\n  }\n\n  /** Constructs a `MacAddress` from a `Long`, using the lower 48-bits. */\n  def fromLong(value: Long): MacAddress = {\n    val bytes = new Array[Byte](6)\n    var rem = value\n    for (i <- 5 to 0 by -1) {\n      bytes(i) = (rem & 0x0ff).toByte\n      rem = rem >> 8\n    }\n    new MacAddress(bytes)\n  }\n\n  /** Parses a `MacAddress` from a string, returning `None` if the string is not a valid mac. */\n  def fromString(value: String): Option[MacAddress] = {\n    val trimmed = value.trim\n    val fields = trimmed.split(':')\n    if (fields.length == 6) {\n      val result = new Array[Byte](6)\n      var i = 0\n      while (i < result.length) {\n        val field = fields(i)\n        if (field.size == 2) {\n          try {\n            result(i) = (0xff & Integer.parseInt(field, 16)).toByte\n            i += 1\n          } catch {\n            case NonFatal(_) => return None\n          }\n        } else return None\n      }\n      Some(new MacAddress(result))\n    } else None\n  }\n\n  implicit val order: Order[MacAddress] = Order.fromComparable[MacAddress]\n  implicit val show: Show[MacAddress] = Show.fromToString[MacAddress]\n}\n"
  },
  {
    "path": "shared/src/main/scala/com/comcast/ip4s/Multicast.scala",
    "content": "/*\n * Copyright 2018 Comcast Cable Communications Management, LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.comcast.ip4s\n\nimport cats.{Order, Show}\n\n/** Witness that the wrapped address of type `A` is a multicast address.\n  *\n  * An instance of `Multicast` is typically created by either calling `Multicast.apply` or by using the `asMulticast`\n  * method on `IpAddress`.\n  */\nsealed trait Multicast[+A <: IpAddress] extends Product with Serializable {\n  def address: A\n}\n\nobject Multicast {\n  private case class DefaultMulticast[+A <: IpAddress](address: A) extends Multicast[A] {\n    override def toString: String = address.toString\n    override def equals(that: Any): Boolean = that match {\n      case m: Multicast[?] => address == m.address\n      case _               => false\n    }\n    override def hashCode: Int = address.hashCode\n  }\n\n  /** Constructs a multicast IP address. Returns `None` is the supplied address is not in the valid multicast range. */\n  def fromIpAddress[A <: IpAddress](address: A): Option[Multicast[A]] =\n    if (address.isSourceSpecificMulticast) Some(SourceSpecificMulticast.unsafeCreate(address))\n    else if (address.isMulticast) Some(DefaultMulticast(address))\n    else None\n\n  implicit def order[J[x <: IpAddress] <: Multicast[x], A <: IpAddress]: Order[J[A]] =\n    Order.fromOrdering(Multicast.ordering[J, A])\n  implicit def ordering[J[x <: IpAddress] <: Multicast[x], A <: IpAddress]: Ordering[J[A]] = Ordering.by(_.address)\n  implicit def show[J[x <: IpAddress] <: Multicast[x], A <: IpAddress]: Show[J[A]] =\n    Show.fromToString[J[A]]\n}\n\n/** Witnesses that the wrapped address of type `A` is a source specific multicast address.\n  *\n  * An instance of `SourceSpecificMulticast` is typically created by either calling `Multicast.apply` or by using\n  * `asSourceSpecificMulticast` and `asSourceSpecificMulticastLenient` methods on `IpAddress`.\n  */\nsealed trait SourceSpecificMulticast[+A <: IpAddress] extends Multicast[A] {\n\n  /** Ensures the referenced address is in the RFC defined source specific address range. */\n  def strict: Option[SourceSpecificMulticast.Strict[A]] =\n    if (address.isSourceSpecificMulticast) Some(SourceSpecificMulticast.unsafeCreateStrict(address)) else None\n\n  override def toString: String = address.toString\n}\n\nobject SourceSpecificMulticast {\n\n  /** Indicates the address is within the RFC defined source specific multicast range. */\n  trait Strict[+A <: IpAddress] extends SourceSpecificMulticast[A]\n\n  private case class DefaultSourceSpecificMulticast[+A <: IpAddress](address: A) extends SourceSpecificMulticast[A] {\n    override def toString: String = address.toString\n    override def equals(that: Any): Boolean = that match {\n      case m: Multicast[?] => address == m.address\n      case _               => false\n    }\n    override def hashCode: Int = address.hashCode\n  }\n\n  /** Constructs a source specific multicast IP address. Returns `None` if the supplied address is not in the valid\n    * source specific multicast range.\n    */\n  def fromIpAddress[A <: IpAddress](address: A): Option[SourceSpecificMulticast.Strict[A]] =\n    if (address.isSourceSpecificMulticast) Some(unsafeCreateStrict(address)) else None\n\n  /** Constructs a source specific multicast IP address. Unlike `fromIpAddress`, multicast addresses outside the RFC\n    * defined source specific range are allowed.\n    */\n  def fromIpAddressLenient[A <: IpAddress](address: A): Option[SourceSpecificMulticast[A]] =\n    if (address.isMulticast) Some(unsafeCreate(address)) else None\n\n  private[ip4s] def unsafeCreate[A <: IpAddress](address: A): SourceSpecificMulticast[A] =\n    DefaultSourceSpecificMulticast(address)\n\n  private[ip4s] def unsafeCreateStrict[A <: IpAddress](address: A): SourceSpecificMulticast.Strict[A] =\n    new DefaultSourceSpecificMulticast(address) with Strict[A]\n\n  implicit def ordering[A <: IpAddress]: Ordering[SourceSpecificMulticast[A]] =\n    Multicast.ordering[SourceSpecificMulticast, A]\n}\n"
  },
  {
    "path": "shared/src/main/scala/com/comcast/ip4s/MulticastJoin.scala",
    "content": "/*\n * Copyright 2018 Comcast Cable Communications Management, LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.comcast.ip4s\n\nimport cats.{Order, Show}\n\n/** Represents a join of a multicast group.\n  *\n  * This is represented as an ADT consisting of two constructors, [[AnySourceMulticastJoin]] and\n  * [[SourceSpecificMulticastJoin]]. These constructors are provided as top level types to allow domain modeling where a\n  * specific join type is required. The address type is parameterized for a similar reason -- to allow domain modeling\n  * where a specific address type is required.\n  */\nsealed abstract class MulticastJoin[+A <: IpAddress] extends Product with Serializable {\n\n  /** Converts this join to a value of type `A` using the supplied functions. */\n  def fold[B](asm: AnySourceMulticastJoin[A] => B, ssm: SourceSpecificMulticastJoin[A] => B): B =\n    this match {\n      case a: AnySourceMulticastJoin[A]      => asm(a)\n      case a: SourceSpecificMulticastJoin[A] => ssm(a)\n    }\n\n  /** Narrows to an `AnySourceMulticastJoin`. */\n  def asAsm: Option[AnySourceMulticastJoin[A]] = fold(Some(_), _ => None)\n\n  /** Narrows to a `SourceSpecificMulticastJoin`. */\n  def asSsm: Option[SourceSpecificMulticastJoin[A]] = fold(_ => None, Some(_))\n\n  /** Returns the source address and group address. If this join is an any-source join, `None` is returned for the\n    * source. Otherwise, this join is a source specific join and `Some(src)` is returned for the source.\n    */\n  def sourceAndGroup: (Option[A], Multicast[A]) =\n    fold(asm => (None, asm.group), ssm => (Some(ssm.source), ssm.group))\n\n  override def toString: String =\n    fold(asm => asm.group.toString, ssm => s\"${ssm.source}@${ssm.group}\")\n}\n\nobject MulticastJoin {\n\n  /** Constructs an `AnySourceMulticastJoin[A]`. */\n  def asm[A <: IpAddress](group: Multicast[A]): MulticastJoin[A] =\n    AnySourceMulticastJoin(group)\n\n  /** Constructs a `SourceSpecificMulticastJoin[A]`. */\n  def ssm[A <: IpAddress](source: A, group: SourceSpecificMulticast[A]): MulticastJoin[A] =\n    SourceSpecificMulticastJoin(source, group)\n\n  def fromString(value: String): Option[MulticastJoin[IpAddress]] =\n    fromStringGeneric(value, IpAddress.fromString)\n\n  def fromString4(value: String): Option[MulticastJoin[Ipv4Address]] =\n    fromStringGeneric(value, Ipv4Address.fromString)\n\n  def fromString6(value: String): Option[MulticastJoin[Ipv6Address]] =\n    fromStringGeneric(value, Ipv6Address.fromString)\n\n  private val Pattern = \"\"\"(?:([^@]+)@)?(.+)\"\"\".r\n  private[ip4s] def fromStringGeneric[A <: IpAddress](\n      value: String,\n      parse: String => Option[A]\n  ): Option[MulticastJoin[A]] =\n    value match {\n      case Pattern(sourceStr, groupStr) =>\n        Option(sourceStr) match {\n          case Some(sourceStr) =>\n            for {\n              source <- parse(sourceStr)\n              group <- parse(groupStr).flatMap(_.asSourceSpecificMulticastLenient)\n            } yield ssm(source, group)\n          case None =>\n            for {\n              group <- parse(groupStr).flatMap(_.asSourceSpecificMulticastLenient)\n            } yield asm(group)\n        }\n      case _ => None\n    }\n\n  implicit def order[J[x <: IpAddress] <: MulticastJoin[x], A <: IpAddress]: Order[J[A]] =\n    Order.fromOrdering(MulticastJoin.ordering[J, A])\n  implicit def ordering[J[x <: IpAddress] <: MulticastJoin[x], A <: IpAddress]: Ordering[J[A]] =\n    Ordering.by(_.sourceAndGroup)\n  implicit def show[J[x <: IpAddress] <: MulticastJoin[x], A <: IpAddress]: Show[J[A]] =\n    Show.fromToString[J[A]]\n}\n\n/** Multicast join to a group without a source filter. */\nfinal case class AnySourceMulticastJoin[+A <: IpAddress](group: Multicast[A]) extends MulticastJoin[A]\n\nobject AnySourceMulticastJoin {\n  def fromString(value: String): Option[AnySourceMulticastJoin[IpAddress]] =\n    MulticastJoin.fromStringGeneric(value, IpAddress.fromString).flatMap(_.asAsm)\n\n  def fromString4(value: String): Option[AnySourceMulticastJoin[Ipv4Address]] =\n    MulticastJoin.fromStringGeneric(value, Ipv4Address.fromString).flatMap(_.asAsm)\n\n  def fromString6(value: String): Option[AnySourceMulticastJoin[Ipv6Address]] =\n    MulticastJoin.fromStringGeneric(value, Ipv6Address.fromString).flatMap(_.asAsm)\n}\n\n/** Multicast join to a group from the specified source. */\nfinal case class SourceSpecificMulticastJoin[+A <: IpAddress](source: A, group: SourceSpecificMulticast[A])\n    extends MulticastJoin[A]\n\nobject SourceSpecificMulticastJoin {\n  def fromString(value: String): Option[SourceSpecificMulticastJoin[IpAddress]] =\n    MulticastJoin.fromStringGeneric(value, IpAddress.fromString).flatMap(_.asSsm)\n\n  def fromString4(value: String): Option[SourceSpecificMulticastJoin[Ipv4Address]] =\n    MulticastJoin.fromStringGeneric(value, Ipv4Address.fromString).flatMap(_.asSsm)\n\n  def fromString6(value: String): Option[SourceSpecificMulticastJoin[Ipv6Address]] =\n    MulticastJoin.fromStringGeneric(value, Ipv6Address.fromString).flatMap(_.asSsm)\n}\n"
  },
  {
    "path": "shared/src/main/scala/com/comcast/ip4s/MulticastSocketAddress.scala",
    "content": "/*\n * Copyright 2018 Comcast Cable Communications Management, LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.comcast.ip4s\n\nimport cats.{Order, Show}\n\n/** A multicast join of the specified type and a port number. Used to describe UDP join of a multicast group.\n  */\nfinal case class MulticastSocketAddress[J[+x <: IpAddress] <: MulticastJoin[x], +A <: IpAddress](\n    join: J[A],\n    port: Port\n) {\n\n  /** Narrows join to an `AnySourceMulticastJoin`. */\n  def asAsm: Option[MulticastSocketAddress[AnySourceMulticastJoin, A]] =\n    join.asAsm.map(j => MulticastSocketAddress(j, port))\n\n  /** Narrows join to a `SourceSpecificMulticastJoin`. */\n  def asSsm: Option[MulticastSocketAddress[SourceSpecificMulticastJoin, A]] =\n    join.asSsm.map(j => MulticastSocketAddress(j, port))\n\n  override def toString: String = {\n    val (source, group) = join.sourceAndGroup\n    group.address.fold(\n      _ => s\"$join:$port\",\n      _ =>\n        source match {\n          case None      => s\"[${group.address}]:$port\"\n          case Some(src) => s\"[$src]@[${group.address}]:$port\"\n        }\n    )\n  }\n}\n\nobject MulticastSocketAddress {\n  def fromString(value: String): Option[MulticastSocketAddress[MulticastJoin, IpAddress]] =\n    fromString4(value) orElse fromString6(value)\n\n  private val V4Pattern = \"\"\"(?:([^@]+)@)?([^:]+):(\\d+)\"\"\".r\n  def fromString4(value: String): Option[MulticastSocketAddress[MulticastJoin, Ipv4Address]] =\n    fromStringGeneric(value, V4Pattern, Ipv4Address.fromString)\n\n  private val V6Pattern = \"\"\"(?:\\[([^\\]]+)\\]@)?\\[([^\\]]+)\\]:(\\d+)\"\"\".r\n  def fromString6(value: String): Option[MulticastSocketAddress[MulticastJoin, Ipv6Address]] =\n    fromStringGeneric(value, V6Pattern, Ipv6Address.fromString)\n\n  def anySourceFromString(value: String): Option[MulticastSocketAddress[AnySourceMulticastJoin, IpAddress]] =\n    anySourceFromString4(value) orElse anySourceFromString6(value)\n  def anySourceFromString6(value: String): Option[MulticastSocketAddress[AnySourceMulticastJoin, Ipv6Address]] =\n    fromString6(value).flatMap(_.asAsm)\n  def anySourceFromString4(value: String): Option[MulticastSocketAddress[AnySourceMulticastJoin, Ipv4Address]] =\n    fromString4(value).flatMap(_.asAsm)\n\n  def sourceSpecificFromString(value: String): Option[MulticastSocketAddress[SourceSpecificMulticastJoin, IpAddress]] =\n    sourceSpecificFromString4(value) orElse sourceSpecificFromString6(value)\n  def sourceSpecificFromString6(\n      value: String\n  ): Option[MulticastSocketAddress[SourceSpecificMulticastJoin, Ipv6Address]] =\n    fromString6(value).flatMap(_.asSsm)\n  def sourceSpecificFromString4(\n      value: String\n  ): Option[MulticastSocketAddress[SourceSpecificMulticastJoin, Ipv4Address]] =\n    fromString4(value).flatMap(_.asSsm)\n\n  private def fromStringGeneric[A <: IpAddress](\n      value: String,\n      pattern: util.matching.Regex,\n      parse: String => Option[A]\n  ): Option[MulticastSocketAddress[MulticastJoin, A]] = {\n    val Pattern = pattern\n    value match {\n      case Pattern(sourceStr, groupStr, portStr) =>\n        Option(sourceStr) match {\n          case Some(sourceStr) =>\n            for {\n              source <- parse(sourceStr)\n              group <- parse(groupStr).flatMap(_.asSourceSpecificMulticastLenient)\n              port <- Port.fromString(portStr)\n            } yield MulticastSocketAddress(MulticastJoin.ssm(source, group), port)\n          case None =>\n            for {\n              group <- parse(groupStr).flatMap(_.asMulticast)\n              port <- Port.fromString(portStr)\n            } yield MulticastSocketAddress(MulticastJoin.asm(group), port)\n        }\n      case _ => None\n    }\n  }\n\n  implicit def order[J[+x <: IpAddress] <: MulticastJoin[x], A <: IpAddress]: Order[MulticastSocketAddress[J, A]] =\n    Order.fromOrdering(MulticastSocketAddress.ordering[J, A])\n  implicit def ordering[J[+x <: IpAddress] <: MulticastJoin[x], A <: IpAddress]\n      : Ordering[MulticastSocketAddress[J, A]] = Ordering.by(x => (x.join, x.port))\n  implicit def show[J[+x <: IpAddress] <: MulticastJoin[x], A <: IpAddress]: Show[MulticastSocketAddress[J, A]] =\n    Show.fromToString[MulticastSocketAddress[J, A]]\n}\n"
  },
  {
    "path": "shared/src/main/scala/com/comcast/ip4s/NetworkInterface.scala",
    "content": "/*\n * Copyright 2018 Comcast Cable Communications Management, LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.comcast.ip4s\n\n/** Description of a network interface.\n  *\n  * Note this is an immutable description, representing a snapshot of the state of the network interface at the time the operating system was queried.\n  *\n  * To get an instance of `NetworkInterface`, use `NetworkInterfaces[F].getAll`.\n  */\nsealed trait NetworkInterface {\n\n  /** Unique name for the interface. */\n  def name: String\n\n  /** Descriptive name of the interface, suitable for use on user interfaces. */\n  def displayName: String\n\n  /** MAC address of the interface, if available. */\n  def macAddress: Option[MacAddress]\n\n  /** IP addresses associated with the interface. */\n  def addresses: List[Cidr[IpAddress]]\n\n  /** True if the interface is a loopback interface. */\n  def isLoopback: Boolean\n\n  /** True if the interface is up/active. */\n  def isUp: Boolean\n}\n\nobject NetworkInterface extends NetworkInterfaceCompanionPlatform {\n\n  def apply(\n      name: String,\n      displayName: String,\n      macAddress: Option[MacAddress],\n      addresses: List[Cidr[IpAddress]],\n      isLoopback: Boolean,\n      isUp: Boolean\n  ): NetworkInterface =\n    DefaultNetworkInterface(name, displayName, macAddress, addresses, isLoopback, isUp)\n\n  private case class DefaultNetworkInterface(\n      name: String,\n      displayName: String,\n      macAddress: Option[MacAddress],\n      addresses: List[Cidr[IpAddress]],\n      isLoopback: Boolean,\n      isUp: Boolean\n  ) extends NetworkInterface {\n    override def toString: String =\n      s\"NetworkInterface($name, $displayName, $macAddress, $addresses, $isLoopback, $isUp)\"\n  }\n}\n"
  },
  {
    "path": "shared/src/main/scala/com/comcast/ip4s/NetworkInterfaces.scala",
    "content": "/*\n * Copyright 2018 Comcast Cable Communications Management, LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.comcast.ip4s\n\nimport cats.effect.{IO, Sync}\nimport cats.syntax.all.*\n\n/** Capability for an effect `F[_]` which can query for local network interfaces.\n  *\n  * An instance is available for any effect which has a `Sync` instance.\n  */\nsealed trait NetworkInterfaces[F[_]] {\n\n  /** Gets a map of network interfaces by name. */\n  def getAll: F[Map[String, NetworkInterface]]\n\n  /** Gets the network interface with the specified name. */\n  def getByName(name: String): F[Option[NetworkInterface]]\n\n  /** Gets the network interface with the specified address. */\n  def getByAddress(address: IpAddress): F[Option[NetworkInterface]]\n\n  /** Gets the network interfaces with the specified MAC address. */\n  def getByMacAddress(address: MacAddress): F[List[NetworkInterface]]\n}\n\nobject NetworkInterfaces extends NetworkInterfacesCompanionPlatform {\n  private[ip4s] abstract class SyncNetworkInterfaces[F[_]: Sync] extends NetworkInterfaces[F] {\n    def getByName(name: String): F[Option[NetworkInterface]] =\n      getAll.map(_.get(name))\n\n    def getByAddress(address: IpAddress): F[Option[NetworkInterface]] =\n      getAll.map { all =>\n        all.values.collectFirst { case iface if iface.addresses.exists(_.address == address) => iface }\n      }\n\n    def getByMacAddress(address: MacAddress): F[List[NetworkInterface]] =\n      getAll.map { all =>\n        all.values.collect { case iface if iface.macAddress == Some(address) => iface }.toList\n      }\n  }\n\n  def apply[F[_]](implicit F: NetworkInterfaces[F]): F.type = F\n\n  implicit def forIO: NetworkInterfaces[IO] = forSync[IO]\n}\n"
  },
  {
    "path": "shared/src/main/scala/com/comcast/ip4s/Port.scala",
    "content": "/*\n * Copyright 2018 Comcast Cable Communications Management, LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.comcast.ip4s\n\nimport scala.util.Try\nimport scala.util.hashing.MurmurHash3\n\nimport cats.{Order, Show}\n\n/** TCP or UDP port number. */\nfinal class Port private (val value: Int) extends Product with Serializable with Ordered[Port] {\n  def copy(value: Int): Option[Port] = Port.fromInt(value)\n  def compare(that: Port): Int = value.compare(that.value)\n  override def toString: String = value.toString\n  override def hashCode: Int = MurmurHash3.productHash(this, productPrefix.hashCode)\n  override def equals(other: Any): Boolean =\n    other match {\n      case that: Port => value == that.value\n      case _          => false\n    }\n  override def canEqual(other: Any): Boolean = other.isInstanceOf[Port]\n  override def productArity: Int = 1\n  override def productElement(n: Int): Any =\n    if (n == 0) value else throw new IndexOutOfBoundsException\n}\n\nobject Port {\n  final val MinValue = 0\n  final val MaxValue = 65535\n\n  final val Wildcard: Port = new Port(0)\n\n  def fromInt(value: Int): Option[Port] =\n    if (value >= MinValue && value <= MaxValue) Some(new Port(value)) else None\n\n  def fromString(value: String): Option[Port] =\n    Try(value.toInt).toOption.flatMap(fromInt)\n\n  def unapply(p: Port): Option[Int] = Some(p.value)\n\n  implicit val order: Order[Port] = Order.fromComparable[Port]\n  implicit val show: Show[Port] = Show.fromToString[Port]\n}\n"
  },
  {
    "path": "shared/src/main/scala/com/comcast/ip4s/SocketAddress.scala",
    "content": "/*\n * Copyright 2018 Comcast Cable Communications Management, LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.comcast.ip4s\n\nimport cats.{Applicative, Order, Show}\nimport cats.syntax.all._\n\n/** An IP address of the specified type and a port number. Used to describe the source or destination of a socket.\n  */\nfinal case class SocketAddress[+A <: Host](host: A, port: Port) extends GenSocketAddress with SocketAddressPlatform[A] {\n  override def toString: String =\n    host match {\n      case _: Ipv6Address => s\"[$host]:$port\"\n      case _              => s\"$host:$port\"\n    }\n\n  /** Resolves this `SocketAddress[Hostname]` to a `SocketAddress[IpAddress]`. */\n  final def resolve[F[_]: Dns: Applicative]: F[SocketAddress[IpAddress]] =\n    host.resolve.map(SocketAddress(_, port))\n}\n\nobject SocketAddress extends SocketAddressCompanionPlatform {\n\n  /** Alias for 0.0.0.0:0. */\n  val Wildcard: SocketAddress[Host] = SocketAddress(Ipv4Address.Wildcard, Port.Wildcard)\n\n  /** Alias for 0.0.0.0:port. */\n  def port(port: Port): SocketAddress[Host] = SocketAddress(Ipv4Address.Wildcard, port)\n\n  def fromString(value: String): Option[SocketAddress[Host]] =\n    fromStringIp(value) orElse fromStringHostname(value) orElse fromStringIDN(value)\n\n  def fromStringIp(value: String): Option[SocketAddress[IpAddress]] =\n    fromString4(value) orElse fromString6(value)\n\n  private val UnescapedPattern = \"\"\"([^:]+):(\\d+)\"\"\".r\n\n  def fromString4(value: String): Option[SocketAddress[Ipv4Address]] =\n    value match {\n      case UnescapedPattern(ip, port) =>\n        for {\n          addr <- Ipv4Address.fromString(ip)\n          prt <- Port.fromString(port)\n        } yield SocketAddress(addr, prt)\n      case _ => None\n    }\n\n  private val V6Pattern = \"\"\"\\[(.+)\\]:(\\d+)\"\"\".r\n  def fromString6(value: String): Option[SocketAddress[Ipv6Address]] =\n    value match {\n      case V6Pattern(ip, port) =>\n        for {\n          addr <- Ipv6Address.fromString(ip)\n          prt <- Port.fromString(port)\n        } yield SocketAddress(addr, prt)\n      case _ => None\n    }\n\n  def fromStringHostname(value: String): Option[SocketAddress[Hostname]] =\n    value match {\n      case UnescapedPattern(s, port) =>\n        for {\n          hostname <- Hostname.fromString(s)\n          prt <- Port.fromString(port)\n        } yield SocketAddress(hostname, prt)\n      case _ => None\n    }\n\n  def fromStringIDN(value: String): Option[SocketAddress[IDN]] =\n    value match {\n      case UnescapedPattern(s, port) =>\n        for {\n          idn <- IDN.fromString(s)\n          prt <- Port.fromString(port)\n        } yield SocketAddress(idn, prt)\n      case _ => None\n    }\n\n  implicit def order[A <: Host]: Order[SocketAddress[A]] = Order.fromOrdering(SocketAddress.ordering[A])\n  implicit def ordering[A <: Host]: Ordering[SocketAddress[A]] = Ordering.by(x => (x.host: Host, x.port))\n  implicit def show[A <: Host]: Show[SocketAddress[A]] = Show.fromToString[SocketAddress[A]]\n}\n"
  },
  {
    "path": "shared/src/main/scala/com/comcast/ip4s/UnixSocketAddress.scala",
    "content": "/*\n * Copyright 2018 Comcast Cable Communications Management, LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.comcast.ip4s\n\nimport cats.{Order, Show}\n\nfinal case class UnixSocketAddress(path: String) extends GenSocketAddress with Ordered[UnixSocketAddress] {\n  override def toString: String = path\n  override def compare(that: UnixSocketAddress): Int = path.compare(that.path)\n}\n\nobject UnixSocketAddress {\n  implicit val order: Order[UnixSocketAddress] = Order.fromComparable[UnixSocketAddress]\n  implicit val show: Show[UnixSocketAddress] = Show.fromToString[UnixSocketAddress]\n}\n"
  },
  {
    "path": "shared/src/main/scala-2/Literals.scala",
    "content": "/*\n * Copyright 2018 Comcast Cable Communications Management, LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.comcast.ip4s\n\nimport org.typelevel.literally.Literally\n\n/** Macros that support literal string interpolators. */\nobject Literals {\n\n  object ip extends Literally[IpAddress] {\n    def validate(c: Context)(s: String): Either[String, c.Expr[IpAddress]] = {\n      import c.universe._\n      IpAddress.fromString(s) match {\n        case Some(_) => Right(c.Expr(q\"_root_.com.comcast.ip4s.IpAddress.fromString($s).get\"))\n        case None    => Left(\"invalid IP address\")\n      }\n    }\n    def make(c: Context)(args: c.Expr[Any]*): c.Expr[IpAddress] = apply(c)(args*)\n  }\n\n  object ipv4 extends Literally[Ipv4Address] {\n    def validate(c: Context)(s: String): Either[String, c.Expr[Ipv4Address]] = {\n      import c.universe._\n      Ipv4Address.fromString(s) match {\n        case Some(_) => Right(c.Expr(q\"_root_.com.comcast.ip4s.Ipv4Address.fromString($s).get\"))\n        case None    => Left(\"invalid IPv4 address\")\n      }\n    }\n    def make(c: Context)(args: c.Expr[Any]*): c.Expr[Ipv4Address] = apply(c)(args*)\n  }\n\n  object ipv6 extends Literally[Ipv6Address] {\n    def validate(c: Context)(s: String): Either[String, c.Expr[Ipv6Address]] = {\n      import c.universe._\n      Ipv6Address.fromString(s) match {\n        case Some(_) => Right(c.Expr(q\"_root_.com.comcast.ip4s.Ipv6Address.fromString($s).get\"))\n        case None    => Left(\"invalid IPv6 address\")\n      }\n    }\n    def make(c: Context)(args: c.Expr[Any]*): c.Expr[Ipv6Address] = apply(c)(args*)\n  }\n\n  object mip extends Literally[Multicast[IpAddress]] {\n    def validate(c: Context)(s: String): Either[String, c.Expr[Multicast[IpAddress]]] = {\n      import c.universe._\n      IpAddress.fromString(s).flatMap(_.asMulticast) match {\n        case Some(_) => Right(c.Expr(q\"_root_.com.comcast.ip4s.IpAddress.fromString($s).get.asMulticast.get\"))\n        case None    => Left(\"invalid IP multicast address\")\n      }\n    }\n    def make(c: Context)(args: c.Expr[Any]*): c.Expr[Multicast[IpAddress]] = apply(c)(args*)\n  }\n\n  object mipv4 extends Literally[Multicast[Ipv4Address]] {\n    def validate(c: Context)(s: String): Either[String, c.Expr[Multicast[Ipv4Address]]] = {\n      import c.universe._\n      Ipv4Address.fromString(s).flatMap(_.asMulticast) match {\n        case Some(_) => Right(c.Expr(q\"_root_.com.comcast.ip4s.Ipv4Address.fromString($s).get.asMulticast.get\"))\n        case None    => Left(\"invalid IPv4 multicast address\")\n      }\n    }\n    def make(c: Context)(args: c.Expr[Any]*): c.Expr[Multicast[Ipv4Address]] = apply(c)(args*)\n  }\n\n  object mipv6 extends Literally[Multicast[Ipv6Address]] {\n    def validate(c: Context)(s: String): Either[String, c.Expr[Multicast[Ipv6Address]]] = {\n      import c.universe._\n      Ipv6Address.fromString(s).flatMap(_.asMulticast) match {\n        case Some(_) => Right(c.Expr(q\"_root_.com.comcast.ip4s.Ipv6Address.fromString($s).get.asMulticast.get\"))\n        case None    => Left(\"invalid IPv6 multicast address\")\n      }\n    }\n    def make(c: Context)(args: c.Expr[Any]*): c.Expr[Multicast[Ipv6Address]] = apply(c)(args*)\n  }\n\n  object ssmip extends Literally[SourceSpecificMulticast.Strict[IpAddress]] {\n    def validate(c: Context)(s: String): Either[String, c.Expr[SourceSpecificMulticast.Strict[IpAddress]]] = {\n      import c.universe._\n      IpAddress.fromString(s).flatMap(_.asSourceSpecificMulticast) match {\n        case Some(_) =>\n          Right(c.Expr(q\"_root_.com.comcast.ip4s.IpAddress.fromString($s).get.asSourceSpecificMulticast.get\"))\n        case None => Left(\"invalid source specific IP multicast address\")\n      }\n    }\n    def make(c: Context)(args: c.Expr[Any]*): c.Expr[SourceSpecificMulticast.Strict[IpAddress]] = apply(c)(args*)\n  }\n\n  object ssmipv4 extends Literally[SourceSpecificMulticast.Strict[Ipv4Address]] {\n    def validate(c: Context)(s: String): Either[String, c.Expr[SourceSpecificMulticast.Strict[Ipv4Address]]] = {\n      import c.universe._\n      Ipv4Address.fromString(s).flatMap(_.asSourceSpecificMulticast) match {\n        case Some(_) =>\n          Right(c.Expr(q\"_root_.com.comcast.ip4s.Ipv4Address.fromString($s).get.asSourceSpecificMulticast.get\"))\n        case None => Left(\"invalid source specific IPv4 multicast address\")\n      }\n    }\n    def make(c: Context)(args: c.Expr[Any]*): c.Expr[SourceSpecificMulticast.Strict[Ipv4Address]] = apply(c)(args*)\n  }\n\n  object ssmipv6 extends Literally[SourceSpecificMulticast.Strict[Ipv6Address]] {\n    def validate(c: Context)(s: String): Either[String, c.Expr[SourceSpecificMulticast.Strict[Ipv6Address]]] = {\n      import c.universe._\n      Ipv6Address.fromString(s).flatMap(_.asSourceSpecificMulticast) match {\n        case Some(_) =>\n          Right(c.Expr(q\"_root_.com.comcast.ip4s.Ipv6Address.fromString($s).get.asSourceSpecificMulticast.get\"))\n        case None => Left(\"invalid source specific IPv6 multicast address\")\n      }\n    }\n    def make(c: Context)(args: c.Expr[Any]*): c.Expr[SourceSpecificMulticast.Strict[Ipv6Address]] = apply(c)(args*)\n  }\n\n  object port extends Literally[Port] {\n    def validate(c: Context)(s: String): Either[String, c.Expr[Port]] = {\n      import c.universe._\n      scala.util.Try(s.toInt).toOption.flatMap(Port.fromInt) match {\n        case Some(_) => Right(c.Expr(q\"_root_.com.comcast.ip4s.Port.fromInt($s.toInt).get\"))\n        case None    => Left(\"invalid port\")\n      }\n    }\n    def make(c: Context)(args: c.Expr[Any]*): c.Expr[Port] = apply(c)(args*)\n  }\n\n  object hostname extends Literally[Hostname] {\n    def validate(c: Context)(s: String): Either[String, c.Expr[Hostname]] = {\n      import c.universe._\n      Hostname.fromString(s) match {\n        case Some(_) => Right(c.Expr(q\"_root_.com.comcast.ip4s.Hostname.fromString($s).get\"))\n        case None    => Left(\"invalid hostname\")\n      }\n    }\n    def make(c: Context)(args: c.Expr[Any]*): c.Expr[Hostname] = apply(c)(args*)\n  }\n\n  object idn extends Literally[IDN] {\n    def validate(c: Context)(s: String): Either[String, c.Expr[IDN]] = {\n      import c.universe._\n      IDN.fromString(s) match {\n        case Some(_) => Right(c.Expr(q\"_root_.com.comcast.ip4s.IDN.fromString($s).get\"))\n        case None    => Left(\"invalid IDN\")\n      }\n    }\n    def make(c: Context)(args: c.Expr[Any]*): c.Expr[IDN] = apply(c)(args*)\n  }\n}\n"
  },
  {
    "path": "shared/src/main/scala-2/package.scala",
    "content": "/*\n * Copyright 2018 Comcast Cable Communications Management, LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.comcast\n\npackage object ip4s extends ip4splatform {\n  final implicit class IpLiteralSyntax(val sc: StringContext) extends AnyVal {\n    def ip(args: Any*): IpAddress = macro Literals.ip.make\n    def ipv4(args: Any*): Ipv4Address =\n      macro Literals.ipv4.make\n    def ipv6(args: Any*): Ipv6Address =\n      macro Literals.ipv6.make\n\n    def mip(args: Any*): Multicast[IpAddress] =\n      macro Literals.mip.make\n    def mipv4(args: Any*): Multicast[Ipv4Address] =\n      macro Literals.mipv4.make\n    def mipv6(args: Any*): Multicast[Ipv6Address] =\n      macro Literals.mipv6.make\n\n    def ssmip(args: Any*): SourceSpecificMulticast.Strict[IpAddress] =\n      macro Literals.ssmip.make\n    def ssmipv4(args: Any*): SourceSpecificMulticast.Strict[Ipv4Address] =\n      macro Literals.ssmipv4.make\n    def ssmipv6(args: Any*): SourceSpecificMulticast.Strict[Ipv6Address] =\n      macro Literals.ssmipv6.make\n\n    def port(args: Any*): Port =\n      macro Literals.port.make\n    def host(args: Any*): Hostname =\n      macro Literals.hostname.make\n    def idn(args: Any*): IDN =\n      macro Literals.idn.make\n  }\n}\n"
  },
  {
    "path": "shared/src/main/scala-2.12/com/comcast/ip4s/CollectionCompat.scala",
    "content": "/*\n * Copyright 2018 Comcast Cable Communications Management, LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.comcast.ip4s\n\nimport scala.collection.JavaConverters._\n\nprivate[ip4s] object Ip4sCollectionCompat {\n  implicit class JIterableOps[A](private val self: java.lang.Iterable[A]) extends AnyVal {\n    def asScala: Iterable[A] = iterableAsScalaIterable(self)\n  }\n  implicit class ListOps[A](private val self: List[A]) extends AnyVal {\n    def asJava: java.util.List[A] = seqAsJavaList(self)\n  }\n  implicit class EnumerationOps[A](private val self: java.util.Enumeration[A]) extends AnyVal {\n    def asScala: Iterator[A] = enumerationAsScalaIterator(self)\n  }\n}\n"
  },
  {
    "path": "shared/src/main/scala-2.13/com/comcast/ip4s/CollectionCompat.scala",
    "content": "/*\n * Copyright 2018 Comcast Cable Communications Management, LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.comcast.ip4s\n\nimport scala.jdk.CollectionConverters._\n\nprivate[ip4s] object Ip4sCollectionCompat {\n  implicit class JIterableOps[A](private val self: java.lang.Iterable[A]) extends AnyVal {\n    def asScala: Iterable[A] = IterableHasAsScala(self).asScala\n  }\n  implicit class ListOps[A](private val self: List[A]) extends AnyVal {\n    def asJava: java.util.List[A] = SeqHasAsJava(self).asJava\n  }\n  implicit class EnumerationOps[A](private val self: java.util.Enumeration[A]) extends AnyVal {\n    def asScala: Iterator[A] = EnumerationHasAsScala(self).asScala\n  }\n}\n"
  },
  {
    "path": "shared/src/main/scala-3/Literals.scala",
    "content": "/*\n * Copyright 2018 Comcast Cable Communications Management, LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.comcast.ip4s\n\nimport org.typelevel.literally.Literally\n\nextension (inline ctx: StringContext)\n  inline def ip(inline args: Any*): IpAddress =\n    ${ Literals.ip('ctx, 'args) }\n\n  inline def ipv4(inline args: Any*): Ipv4Address =\n    ${ Literals.ipv4('ctx, 'args) }\n\n  inline def ipv6(inline args: Any*): Ipv6Address =\n    ${ Literals.ipv6('ctx, 'args) }\n\n  inline def mip(inline args: Any*): Multicast[IpAddress] =\n    ${ Literals.mip('ctx, 'args) }\n\n  inline def mipv4(inline args: Any*): Multicast[Ipv4Address] =\n    ${ Literals.mipv4('ctx, 'args) }\n\n  inline def mipv6(inline args: Any*): Multicast[Ipv6Address] =\n    ${ Literals.mipv6('ctx, 'args) }\n\n  inline def ssmip(inline args: Any*): SourceSpecificMulticast.Strict[IpAddress] =\n    ${ Literals.ssmip('ctx, 'args) }\n\n  inline def ssmipv4(inline args: Any*): SourceSpecificMulticast.Strict[Ipv4Address] =\n    ${ Literals.ssmipv4('ctx, 'args) }\n\n  inline def ssmipv6(inline args: Any*): SourceSpecificMulticast.Strict[Ipv6Address] =\n    ${ Literals.ssmipv6('ctx, 'args) }\n\n  inline def port(inline args: Any*): Port =\n    ${ Literals.port('ctx, 'args) }\n\n  inline def host(inline args: Any*): Hostname =\n    ${ Literals.host('ctx, 'args) }\n\n  inline def idn(inline args: Any*): IDN =\n    ${ Literals.idn('ctx, 'args) }\n\nobject Literals:\n\n  object ip extends Literally[IpAddress]:\n    def validate(s: String)(using Quotes) =\n      IpAddress.fromString(s) match\n        case Some(_) => Right('{ _root_.com.comcast.ip4s.IpAddress.fromString(${ Expr(s) }).get })\n        case None    => Left(\"Invalid IP address\")\n\n  object ipv4 extends Literally[Ipv4Address]:\n    def validate(s: String)(using Quotes) =\n      Ipv4Address.fromString(s) match\n        case Some(_) => Right('{ _root_.com.comcast.ip4s.Ipv4Address.fromString(${ Expr(s) }).get })\n        case None    => Left(\"Invalid IPv4 address\")\n\n  object ipv6 extends Literally[Ipv6Address]:\n    def validate(s: String)(using Quotes) =\n      Ipv6Address.fromString(s) match\n        case Some(_) => Right('{ _root_.com.comcast.ip4s.Ipv6Address.fromString(${ Expr(s) }).get })\n        case None    => Left(\"Invalid IPv6 address\")\n\n  object mip extends Literally[Multicast[IpAddress]]:\n    def validate(s: String)(using Quotes) =\n      IpAddress.fromString(s).flatMap(_.asMulticast) match\n        case Some(_) => Right('{ _root_.com.comcast.ip4s.IpAddress.fromString(${ Expr(s) }).get.asMulticast.get })\n        case None    => Left(\"Invalid IP multicast address\")\n\n  object mipv4 extends Literally[Multicast[Ipv4Address]]:\n    def validate(s: String)(using Quotes) =\n      Ipv4Address.fromString(s).flatMap(_.asMulticast) match\n        case Some(_) => Right('{ _root_.com.comcast.ip4s.Ipv4Address.fromString(${ Expr(s) }).get.asMulticast.get })\n        case None    => Left(\"Invalid IPv4 multicast address\")\n\n  object mipv6 extends Literally[Multicast[Ipv6Address]]:\n    def validate(s: String)(using Quotes) =\n      Ipv6Address.fromString(s).flatMap(_.asMulticast) match\n        case Some(_) => Right('{ _root_.com.comcast.ip4s.Ipv6Address.fromString(${ Expr(s) }).get.asMulticast.get })\n        case None    => Left(\"Invalid IPv6 multicast address\")\n\n  object ssmip extends Literally[SourceSpecificMulticast.Strict[IpAddress]]:\n    def validate(s: String)(using Quotes) =\n      IpAddress.fromString(s).flatMap(_.asSourceSpecificMulticast) match\n        case Some(_) =>\n          Right('{\n            _root_.com.comcast.ip4s.IpAddress.fromString(${ Expr(s) }).get.asSourceSpecificMulticast.get\n          })\n        case None => Left(\"Invalid source specific IP multicast address\")\n\n  object ssmipv4 extends Literally[SourceSpecificMulticast.Strict[Ipv4Address]]:\n    def validate(s: String)(using Quotes) =\n      Ipv4Address.fromString(s).flatMap(_.asSourceSpecificMulticast) match\n        case Some(_) =>\n          Right('{\n            _root_.com.comcast.ip4s.Ipv4Address.fromString(${ Expr(s) }).get.asSourceSpecificMulticast.get\n          })\n        case None => Left(\"Invalid source specific IPv4 multicast address\")\n\n  object ssmipv6 extends Literally[SourceSpecificMulticast.Strict[Ipv6Address]]:\n    def validate(s: String)(using Quotes) =\n      Ipv6Address.fromString(s).flatMap(_.asSourceSpecificMulticast) match\n        case Some(_) =>\n          Right('{\n            _root_.com.comcast.ip4s.Ipv6Address.fromString(${ Expr(s) }).get.asSourceSpecificMulticast.get\n          })\n        case None => Left(\"Invalid source specific IPv6 multicast address\")\n\n  object port extends Literally[Port]:\n    def validate(s: String)(using Quotes) =\n      s.toIntOption.flatMap(Port.fromInt) match\n        case Some(_) => Right('{ _root_.com.comcast.ip4s.Port.fromInt(${ Expr(s.toInt) }).get })\n        case None    => Left(\"Invalid port\")\n\n  object host extends Literally[Hostname]:\n    def validate(s: String)(using Quotes) =\n      Hostname.fromString(s) match\n        case Some(_) => Right('{ _root_.com.comcast.ip4s.Hostname.fromString(${ Expr(s) }).get })\n        case None    => Left(\"Invalid hostname\")\n\n  object idn extends Literally[IDN]:\n    def validate(s: String)(using Quotes) =\n      IDN.fromString(s) match\n        case Some(_) => Right('{ _root_.com.comcast.ip4s.IDN.fromString(${ Expr(s) }).get })\n        case None    => Left(\"Invalid IDN\")\n"
  },
  {
    "path": "shared/src/main/scala-3/com/comcast/ip4s/CollectionCompat.scala",
    "content": "/*\n * Copyright 2018 Comcast Cable Communications Management, LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.comcast.ip4s\n\nimport scala.jdk.CollectionConverters.*\n\nprivate[ip4s] object Ip4sCollectionCompat {\n  implicit class JIterableOps[A](private val self: java.lang.Iterable[A]) extends AnyVal {\n    def asScala: Iterable[A] = IterableHasAsScala(self).asScala\n  }\n  implicit class ListOps[A](private val self: List[A]) extends AnyVal {\n    def asJava: java.util.List[A] = SeqHasAsJava(self).asJava\n  }\n  implicit class EnumerationOps[A](private val self: java.util.Enumeration[A]) extends AnyVal {\n    def asScala: Iterator[A] = EnumerationHasAsScala(self).asScala\n  }\n}\n"
  },
  {
    "path": "shared/src/main/scala-3/package.scala",
    "content": "/*\n * Copyright 2018 Comcast Cable Communications Management, LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.comcast\n\npackage object ip4s extends ip4splatform\n"
  },
  {
    "path": "test-kit/js/src/test/scala/com/comcast/ip4s/TestPlatform.scala",
    "content": "/*\n * Copyright 2018 Comcast Cable Communications Management, LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.comcast.ip4s\n\nobject TestPlatform {\n  def isJVM = false\n  def isJS = true\n  def isNative = false\n}\n"
  },
  {
    "path": "test-kit/jvm/src/test/scala/com/comcast/ip4s/Ipv6AddressJvmTest.scala",
    "content": "/*\n * Copyright 2018 Comcast Cable Communications Management, LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.comcast.ip4s\n\nimport com.google.common.net.InetAddresses\nimport java.net.{InetAddress, Inet6Address}\nimport org.scalacheck.{Arbitrary, Gen, Test}\n\nimport org.scalacheck.Prop.forAll\nimport Arbitraries._\n\nclass Ipv6AddressJvmTest extends BaseTestSuite {\n  override protected def scalaCheckTestParameters: Test.Parameters =\n    super.scalaCheckTestParameters.withMinSuccessfulTests(10000)\n\n  test(\"to string - roundtrips through strings\") {\n    forAll(Gen.listOfN(16, Arbitrary.arbitrary[Byte])) { bytesList =>\n      if (bytesList.size == 16) {\n        val bytes = bytesList.toArray\n        val str =\n          InetAddresses.toAddrString(InetAddress.getByAddress(bytes))\n        assertEquals(Ipv6Address.fromString(str).map(_.toString), Some(str))\n      }\n    }\n  }\n\n  test(\"to string - follows RFC5952\") {\n    forAll(Gen.listOfN(16, Arbitrary.arbitrary[Byte])) { bytesList =>\n      if (bytesList.size == 16) {\n        val bytes = bytesList.toArray\n        val expected =\n          InetAddresses.toAddrString(InetAddress.getByAddress(bytes))\n        assertEquals(Ipv6Address.fromBytes(bytes).map(_.toString), Some(expected))\n      }\n    }\n  }\n\n  test(\"support conversion to Inet6Address\") {\n    forAll { (ip: Ipv6Address) => assert(ip.toInetAddress.isInstanceOf[Inet6Address]) }\n  }\n\n  test(\"toInetAddress with IPv4 mapped addresses\") {\n    assertEquals(ip\"::ffff:f:f\".toInetAddress, InetAddress.getByName(\"::ffff:f:f\"))\n  }\n}\n"
  },
  {
    "path": "test-kit/jvm/src/test/scala/com/comcast/ip4s/TestPlatform.scala",
    "content": "/*\n * Copyright 2018 Comcast Cable Communications Management, LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.comcast.ip4s\n\nobject TestPlatform {\n  def isJVM = true\n  def isJS = false\n  def isNative = false\n}\n"
  },
  {
    "path": "test-kit/native/src/test/scala/com/comcast/ip4s/TestPlatform.scala",
    "content": "/*\n * Copyright 2018 Comcast Cable Communications Management, LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.comcast.ip4s\n\nobject TestPlatform {\n  def isJVM = false\n  def isJS = false\n  def isNative = true\n}\n"
  },
  {
    "path": "test-kit/shared/src/main/scala/com/comcast/ip4s/Arbitraries.scala",
    "content": "/*\n * Copyright 2018 Comcast Cable Communications Management, LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.comcast.ip4s\n\nimport org.scalacheck.{Arbitrary, Gen}\n\nobject Arbitraries {\n  val ipv4Generator: Gen[Ipv4Address] = for {\n    bytes <- Gen.listOfN(4, Arbitrary.arbitrary[Byte])\n  } yield Ipv4Address.fromBytes(bytes.toArray).get\n\n  implicit val ipv4Arbitrary: Arbitrary[Ipv4Address] = Arbitrary(ipv4Generator)\n\n  val ipv6Generator: Gen[Ipv6Address] = for {\n    bytes <- Gen.listOfN(16, Arbitrary.arbitrary[Byte])\n  } yield Ipv6Address.fromBytes(bytes.toArray).get\n\n  implicit val ipv6Arbitrary: Arbitrary[Ipv6Address] = Arbitrary(ipv6Generator)\n\n  val ipGenerator: Gen[IpAddress] = Gen.oneOf(ipv4Generator, ipv6Generator)\n\n  implicit val ipArbitrary: Arbitrary[IpAddress] = Arbitrary(ipGenerator)\n\n  def cidrGenerator[A <: IpAddress](genIp: Gen[A]): Gen[Cidr[A]] =\n    for {\n      ip <- genIp\n      bitLength = ip.fold(_ => 32, _ => 128)\n      prefix <- Gen.chooseNum(0, bitLength)\n    } yield ip / prefix\n\n  implicit def cidrArbitrary[A <: IpAddress](implicit arbIp: Arbitrary[A]): Arbitrary[Cidr[A]] =\n    Arbitrary(cidrGenerator(arbIp.arbitrary))\n\n  implicit def cidrStrictArbitrary[A <: IpAddress](implicit arbIp: Arbitrary[A]): Arbitrary[Cidr.Strict[A]] = Arbitrary(\n    cidrGenerator(arbIp.arbitrary).map(_.normalized)\n  )\n\n  val portGenerator: Gen[Port] = Gen.chooseNum(0, 65535).map(Port.fromInt(_).get)\n\n  implicit val portArbitrary: Arbitrary[Port] = Arbitrary(portGenerator)\n\n  def socketAddressGenerator[A <: IpAddress](genIp: Gen[A], genPort: Gen[Port]): Gen[SocketAddress[A]] =\n    for {\n      ip <- genIp\n      port <- genPort\n    } yield SocketAddress(ip, port)\n\n  implicit def socketAddressArbitrary[A <: IpAddress](implicit\n      arbIp: Arbitrary[A],\n      arbPort: Arbitrary[Port]\n  ): Arbitrary[SocketAddress[A]] =\n    Arbitrary(socketAddressGenerator(arbIp.arbitrary, arbPort.arbitrary))\n\n  val multicastGenerator4: Gen[Multicast[Ipv4Address]] = for {\n    ip <- ipv4Generator\n  } yield Ipv4Address.fromLong(ip.toLong & ~(15 << 28) | (14 << 28)).asMulticast.get\n\n  implicit val multicastArbitrary4: Arbitrary[Multicast[Ipv4Address]] = Arbitrary(multicastGenerator4)\n\n  val multicastGenerator6: Gen[Multicast[Ipv6Address]] = for {\n    ip <- ipv6Generator\n  } yield Ipv6Address.fromBigInt(ip.toBigInt & ~(BigInt(255) << 120) | (BigInt(255) << 120)).asMulticast.get\n\n  implicit val multicastArbitrary6: Arbitrary[Multicast[Ipv6Address]] = Arbitrary(multicastGenerator6)\n\n  val multicastGenerator: Gen[Multicast[IpAddress]] = Gen.oneOf(multicastGenerator4, multicastGenerator6)\n\n  implicit val multicastArbitrary: Arbitrary[Multicast[IpAddress]] = Arbitrary(multicastGenerator)\n\n  def multicastJoinGenerator[A <: IpAddress](genSource: Gen[A], genGroup: Gen[Multicast[A]]): Gen[MulticastJoin[A]] =\n    genGroup.flatMap { group =>\n      group.address.asSourceSpecificMulticast match {\n        case Some(grp) =>\n          genSource.filter(_.getClass == grp.address.getClass).flatMap(src => MulticastJoin.ssm(src, grp))\n        case None => MulticastJoin.asm(group)\n      }\n    }\n\n  implicit def multicastJoinArbitrary[A <: IpAddress](implicit\n      arbSource: Arbitrary[A],\n      arbGroup: Arbitrary[Multicast[A]]\n  ): Arbitrary[MulticastJoin[A]] =\n    Arbitrary(multicastJoinGenerator(arbSource.arbitrary, arbGroup.arbitrary))\n\n  def multicastSocketAddressGenerator[A <: IpAddress](\n      genJoin: Gen[MulticastJoin[A]],\n      genPort: Gen[Port]\n  ): Gen[MulticastSocketAddress[MulticastJoin, A]] =\n    for {\n      join <- genJoin\n      port <- genPort\n    } yield MulticastSocketAddress(join, port)\n\n  implicit def multicastSocketAddressArbitrary[A <: IpAddress](implicit\n      arbJoin: Arbitrary[MulticastJoin[A]],\n      arbPort: Arbitrary[Port]\n  ): Arbitrary[MulticastSocketAddress[MulticastJoin, A]] =\n    Arbitrary(multicastSocketAddressGenerator(arbJoin.arbitrary, arbPort.arbitrary))\n\n  val hostnameGenerator: Gen[Hostname] = {\n    val genLabel: Gen[String] = for {\n      first <- Gen.alphaNumChar\n      middleLen <- Gen.chooseNum(0, 61)\n      middle <- Gen.listOfN(middleLen, Gen.oneOf(Gen.alphaNumChar, Gen.const('-'))).map(_.mkString)\n      last <- if (middleLen > 0) Gen.alphaNumChar.map(Some(_)) else Gen.option(Gen.alphaNumChar)\n    } yield first.toString + middle + last.fold(\"\")(_.toString)\n    for {\n      numLabels <- Gen.chooseNum(1, 5)\n      labels <- Gen.listOfN(numLabels, genLabel)\n      if labels.foldLeft(0)(_ + _.size) < (253 - (numLabels - 1))\n    } yield Hostname.fromString(labels.mkString(\".\")).get\n  }\n\n  implicit val hostnameArbitrary: Arbitrary[Hostname] = Arbitrary(hostnameGenerator)\n\n  lazy val idnGenerator: Gen[IDN] = {\n    val genChar: Gen[Char] = Gen.oneOf(Gen.alphaNumChar, Gen.const('δ'), Gen.const('π'), Gen.const('θ'))\n    val genLabel: Gen[String] = for {\n      first <- genChar\n      middleLen <- Gen.chooseNum(0, 61)\n      middle <- Gen.listOfN(middleLen, Gen.oneOf(genChar, Gen.const('-'))).map(_.mkString)\n      last <- if (middleLen > 0) genChar.map(Some(_)) else Gen.option(genChar)\n      str = first.toString + middle + last.fold(\"\")(_.toString)\n      if IDN.toAscii(str).size < 64\n    } yield str\n    for {\n      numLabels <- Gen.chooseNum(1, 5)\n      labels <- Gen.listOfN(numLabels, genLabel)\n      dot <- Gen.oneOf('.', '\\u002e', '\\u3002', '\\uff0e', '\\uff61')\n      idn = IDN.fromString(labels.mkString(dot.toString)) if idn.isDefined\n    } yield idn.get\n  }\n\n  implicit def idnArbitrary: Arbitrary[IDN] = Arbitrary(idnGenerator)\n\n  val macAddressGenerator: Gen[MacAddress] = for {\n    bytes <- Gen.listOfN(6, Arbitrary.arbitrary[Byte])\n  } yield MacAddress.fromBytes(bytes.toArray).get\n\n  implicit val macAddressArbitrary: Arbitrary[MacAddress] = Arbitrary(macAddressGenerator)\n}\n"
  },
  {
    "path": "test-kit/shared/src/test/scala/com/comcast/ip4s/BaseTestSuite.scala",
    "content": "/*\n * Copyright 2018 Comcast Cable Communications Management, LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.comcast.ip4s\n\nimport munit.ScalaCheckSuite\nimport org.scalacheck.Test\n\nabstract class BaseTestSuite extends ScalaCheckSuite {\n  override protected def scalaCheckTestParameters: Test.Parameters =\n    super.scalaCheckTestParameters.withMinSuccessfulTests(5000)\n\n  protected def group(name: String)(thunk: => Unit): Unit = {\n    val countBefore = munitTestsBuffer.size\n    val _ = thunk\n    val countAfter = munitTestsBuffer.size\n    val countRegistered = countAfter - countBefore\n    val registered = munitTestsBuffer.toList.drop(countBefore)\n    (0 until countRegistered).foreach(_ => munitTestsBuffer.remove(countBefore))\n    registered.foreach(t => munitTestsBuffer += t.withName(s\"$name - ${t.name}\"))\n  }\n}\n"
  },
  {
    "path": "test-kit/shared/src/test/scala/com/comcast/ip4s/CidrStrictTest.scala",
    "content": "/*\n * Copyright 2018 Comcast Cable Communications Management, LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.comcast.ip4s\n\nimport org.scalacheck.Prop.forAll\nimport Arbitraries._\n\nclass CidrStrictTest extends BaseTestSuite {\n  property(\"prefix and address are identical\") {\n    forAll { (cidr: Cidr.Strict[IpAddress]) => assertEquals(cidr.address, cidr.prefix) }\n  }\n}\n"
  },
  {
    "path": "test-kit/shared/src/test/scala/com/comcast/ip4s/CidrTest.scala",
    "content": "/*\n * Copyright 2018 Comcast Cable Communications Management, LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.comcast.ip4s\n\nimport org.scalacheck.Prop.forAll\nimport Arbitraries._\n\nclass CidrTest extends BaseTestSuite {\n  property(\"roundtrip through string\") {\n    forAll { (cidr: Cidr[IpAddress]) => assertEquals(Cidr.fromString(cidr.toString), Some(cidr)) }\n  }\n\n  property(\"fromIpAndMask\") {\n    forAll { (ip: IpAddress, prefixBits0: Int) =>\n      val max = ip.fold(_ => 32, _ => 128)\n      val prefixBits = ((prefixBits0 % max).abs + 1)\n      val maskInt = BigInt(-1) << (max - prefixBits)\n      val mask = ip.fold(_ => Ipv4Address.fromLong(maskInt.toLong & 0xffffffff), _ => Ipv6Address.fromBigInt(maskInt))\n      assertEquals(Cidr.fromIpAndMask(ip, mask), Cidr(ip, prefixBits))\n    }\n  }\n\n  property(\"parsing from string: only masks with a valid length return a CIDR\") {\n    forAll { (ip: IpAddress, prefixBits: Int) =>\n      val cidr = Cidr.fromString(s\"$ip/$prefixBits\")\n      val max = ip.fold(_ => 32, _ => 128)\n      assertEquals(cidr.isDefined, (prefixBits <= max && prefixBits >= 0))\n    }\n  }\n\n  test(\"addresses should return iterator\") {\n    val cidr = Cidr(ipv4\"10.0.0.0\", 8)\n    val addresses = cidr.addresses\n\n    assertEquals(addresses.size, 16777216)\n  }\n}\n"
  },
  {
    "path": "test-kit/shared/src/test/scala/com/comcast/ip4s/DnsTest.scala",
    "content": "/*\n * Copyright 2018 Comcast Cable Communications Management, LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.comcast.ip4s\n\nimport cats.effect.IO\nimport cats.syntax.all._\nimport munit.CatsEffectSuite\n\nclass DnsTest extends CatsEffectSuite {\n\n  test(\"resolve/reverseAll round-trip\") {\n    for {\n      hostname <- Hostname.fromString(\"comcast.com\").liftTo[IO](new NoSuchElementException)\n      address <- Dns[IO].resolve(hostname)\n      hostnames <- Dns[IO].reverseAll(address)\n      _ <- IO(assert(hostnames.nonEmpty))\n      reversedAddress <- hostnames.traverse(Dns[IO].resolve)\n    } yield assert(reversedAddress.forall(_ == address))\n  }\n\n  test(\"resolveAll/reverse round-trip\") {\n    for {\n      hostname <- Hostname.fromString(\"comcast.com\").liftTo[IO](new NoSuchElementException)\n      addresses <- Dns[IO].resolveAll(hostname)\n      _ <- IO(assert(addresses.nonEmpty))\n      hostnames <- addresses.traverse(Dns[IO].reverse)\n      reversedAddresses <- hostnames.traverse(Dns[IO].resolve)\n    } yield assertEquals(Set(addresses), Set(reversedAddresses))\n  }\n\n  test(\"loopback\") {\n    val loopbacks = Set(ip\"127.0.0.1\", ip\"::1\")\n    Dns[IO].loopback.flatMap { loopback =>\n      IO(assert(loopbacks.contains(clue(loopback))))\n    }\n  }\n\n  test(\"resolve unknown host\") {\n    (Dns[IO].resolve(host\"not.example.com\") >>\n      IO.raiseError(new AssertionError(\"Did not raise `UnknownHostException`\"))).recover {\n      case ex: UnknownHostException =>\n        val msg = ex.getMessage\n        val expected = List(\n          \"not.example.com: Name or service not known\",\n          \"not.example.com: nodename nor servname provided, or not known\",\n          \"not.example.com: No address associated with hostname\"\n        )\n        assert(expected.contains(msg), msg)\n    }\n  }\n\n  test(\"reverse unknown ip\") {\n    Dns[IO].reverse(ip\"240.0.0.0\").interceptMessage[UnknownHostException](\"240.0.0.0\")\n  }\n\n}\n"
  },
  {
    "path": "test-kit/shared/src/test/scala/com/comcast/ip4s/Examples.scala",
    "content": "/*\n * Copyright 2018 Comcast Cable Communications Management, LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.comcast.ip4s\n\nobject Examples {\n  val localhost4: Ipv4Address = ipv4\"127.0.0.1\"\n  val localhost6: Ipv6Address = ipv6\"::1\"\n\n  val ssmv4: SourceSpecificMulticastJoin[Ipv4Address] =\n    SourceSpecificMulticastJoin(localhost4, ssmipv4\"232.10.10.10\")\n  val ssmv4WithPort: MulticastSocketAddress[SourceSpecificMulticastJoin, Ipv4Address] =\n    MulticastSocketAddress(ssmv4, port\"5555\")\n\n  val asmv4: AnySourceMulticastJoin[Ipv4Address] =\n    AnySourceMulticastJoin(ssmipv4\"232.10.10.10\")\n  val asmv4WithPort: MulticastSocketAddress[AnySourceMulticastJoin, Ipv4Address] =\n    MulticastSocketAddress(asmv4, port\"5555\")\n}\n"
  },
  {
    "path": "test-kit/shared/src/test/scala/com/comcast/ip4s/HostnameTest.scala",
    "content": "/*\n * Copyright 2018 Comcast Cable Communications Management, LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.comcast.ip4s\n\nimport org.scalacheck.Prop.forAll\nimport Arbitraries._\n\nclass HostnameTest extends BaseTestSuite {\n  test(\"roundtrip through string\") {\n    forAll { (h: Hostname) => assertEquals(Hostname.fromString(h.toString), Some(h)) }\n  }\n\n  test(\"allow access to labels\") {\n    forAll { (h: Hostname) => assertEquals(Hostname.fromString(h.labels.toList.mkString(\".\")), Some(h)) }\n  }\n\n  test(\"require overall length be less than 254 chars\") {\n    forAll { (h: Hostname) =>\n      val hstr = h.toString\n      val h2 = hstr + \".\" + hstr\n      val expected = if (h2.length > 253) None else Some(Hostname.fromString(h2).get)\n      assertEquals(Hostname.fromString(h2), expected)\n    }\n  }\n\n  test(\"require labels be less than 64 chars\") {\n    forAll { (h: Hostname) =>\n      val hstr = h.toString\n      val suffix = new String(Array.fill(63)(hstr.last))\n      val tooLong = hstr + suffix\n      assertEquals(Hostname.fromString(tooLong), None)\n    }\n  }\n\n  test(\"disallow labels that end in a dash\") {\n    forAll { (h: Hostname) =>\n      val hstr = h.toString\n      val disallowed = hstr + \"-\"\n      assertEquals(Hostname.fromString(disallowed), None)\n    }\n  }\n\n  test(\"disallow labels that start with a dash\") {\n    forAll { (h: Hostname) =>\n      val hstr = h.toString\n      val disallowed = \"-\" + hstr\n      assertEquals(Hostname.fromString(disallowed), None)\n    }\n  }\n}\n"
  },
  {
    "path": "test-kit/shared/src/test/scala/com/comcast/ip4s/IDNTest.scala",
    "content": "/*\n * Copyright 2018 Comcast Cable Communications Management, LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.comcast.ip4s\n\nimport org.scalacheck.Prop.forAll\nimport Arbitraries._\n\nclass IDNTest extends BaseTestSuite {\n\n  test(\"support any hostname\") {\n    forAll { (h: Hostname) =>\n      val representable = h.labels.forall(l => !l.toString.toLowerCase.startsWith(\"xn--\"))\n      if (representable) {\n        val i = IDN.fromHostname(h)\n        assertEquals(i.hostname, h)\n        assertEquals(IDN.fromString(h.toString), Some(i))\n      }\n    }\n  }\n\n  test(\"roundtrip through string\") {\n    forAll { (i: IDN) => assertEquals(IDN.fromString(i.toString), Some(i)) }\n  }\n\n  test(\"allow access to labels\") {\n    forAll { (i: IDN) => assertEquals(IDN.fromString(i.labels.toList.mkString(\".\")).map(_.labels), Some(i.labels)) }\n  }\n\n  test(\"require overall ascii length be less than 254 chars\") {\n    forAll { (i: IDN) =>\n      val istr = i.toString\n      val i2 = istr + \".\" + istr\n      val expected = if (i.hostname.toString.length > (253 / 2)) None else Some(IDN.fromString(i2).get)\n      assertEquals(IDN.fromString(i2), expected)\n    }\n  }\n\n  test(\"require labels be less than 64 ascii chars\") {\n    forAll { (i: IDN) =>\n      val str = i.toString\n      val suffix = new String(Array.fill(63)(str.last))\n      val tooLong = str + suffix\n      assertEquals(IDN.fromString(tooLong), None)\n    }\n  }\n\n  test(\"disallow labels that end in a dash\") {\n    forAll { (i: IDN) =>\n      val str = i.toString\n      // Note: simply appending a dash to final label doesn't guarantee the ASCII encoded label ends with a dash\n      val disallowed = str + \".a-\"\n      assertEquals(IDN.fromString(disallowed), None)\n    }\n  }\n\n  test(\"disallow labels that start with a dash\") {\n    forAll { (i: IDN) =>\n      val str = i.toString\n      val disallowed = \"-a.\" + str\n      assertEquals(IDN.fromString(disallowed), None)\n    }\n  }\n\n  test(\"support normalization\") {\n    forAll { (i: IDN) =>\n      assertEquals(i.normalized, IDN.fromString(i.labels.map(_.toString.toLowerCase).toList.mkString(\".\")).get)\n    }\n  }\n}\n"
  },
  {
    "path": "test-kit/shared/src/test/scala/com/comcast/ip4s/IdnaSuite.scala",
    "content": "/*\n * Copyright 2018 Comcast Cable Communications Management, LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n// https://github.com/libuv/libuv/blob/97dcdb1926f6aca43171e1614338bcef067abd59/test/test-idna.c\n\n/* Copyright The libuv project and contributors. All rights reserved.\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\npackage com.comcast.ip4s\n\nclass IdnaSuite extends BaseTestSuite {\n\n  case class TestCase(decoded: String, encoded: Option[String])\n  object TestCase {\n    def apply(decoded: String, encoded: String): TestCase =\n      apply(decoded, Some(encoded))\n  }\n\n  val testData = List(\n    // No conversion\n    TestCase(\"\", \"\"),\n    TestCase(\".\", \".\"),\n    TestCase(\".com\", \".com\"),\n    TestCase(\"example\", \"example\"),\n    TestCase(\"example-\", \"example-\"),\n    TestCase(\"straße.de\", \"xn--strae-oqa.de\"),\n    // Test cases adapted from punycode.js. Most are from RFC 3492.\n    TestCase(\"foo.bar\", \"foo.bar\"),\n    TestCase(\"mañana.com\", \"xn--maana-pta.com\"),\n    TestCase(\"example.com.\", \"example.com.\"),\n    TestCase(\"bücher.com\", \"xn--bcher-kva.com\"),\n    TestCase(\"café.com\", \"xn--caf-dma.com\"),\n    TestCase(\"café.café.com\", \"xn--caf-dma.xn--caf-dma.com\"),\n    TestCase(\"☃-⌘.com\", \"xn----dqo34k.com\"),\n    TestCase(\"퐀☃-⌘.com\", \"xn----dqo34kn65z.com\"),\n    TestCase(\"💩.la\", \"xn--ls8h.la\"),\n    TestCase(\"mañana.com\", \"xn--maana-pta.com\"),\n    TestCase(\"mañana。com\", \"xn--maana-pta.com\"),\n    TestCase(\"mañana．com\", \"xn--maana-pta.com\"),\n    TestCase(\"mañana｡com\", \"xn--maana-pta.com\"),\n    TestCase(\"ü\", \"xn--tda\"),\n    TestCase(\".ü\", \".xn--tda\"),\n    TestCase(\"ü.ü\", \"xn--tda.xn--tda\"),\n    TestCase(\"ü.ü.\", \"xn--tda.xn--tda.\"),\n    TestCase(\"üëäö♥\", \"xn--4can8av2009b\"),\n    TestCase(\n      \"Willst du die Blüthe des frühen, die Früchte des späteren Jahres\",\n      \"xn--Willst du die Blthe des frhen, die Frchte des spteren Jahres-x9e96lkal\"\n    ),\n    TestCase(\"ليهمابتكلموشعربي؟\", \"xn--egbpdaj6bu4bxfgehfvwxn\"),\n    TestCase(\"他们为什么不说中文\", \"xn--ihqwcrb4cv8a8dqg056pqjye\"),\n    TestCase(\"他們爲什麽不說中文\", \"xn--ihqwctvzc91f659drss3x8bo0yb\"),\n    TestCase(\"Pročprostěnemluvíčesky\", \"xn--Proprostnemluvesky-uyb24dma41a\"),\n    TestCase(\"למההםפשוטלאמדבריםעברית\", \"xn--4dbcagdahymbxekheh6e0a7fei0b\"),\n    TestCase(\"यहलोगहिन्दीक्योंनहींबोलसकतेहैं\", \"xn--i1baa7eci9glrd9b2ae1bj0hfcgg6iyaf8o0a1dig0cd\"),\n    TestCase(\"なぜみんな日本語を話してくれないのか\", \"xn--n8jok5ay5dzabd5bym9f0cm5685rrjetr6pdxa\"),\n    TestCase(\"세계의모든사람들이한국어를이해한다면얼마나좋을까\", \"xn--989aomsvi5e83db1d2a355cv1e0vak1dwrv93d5xbh15a0dt30a5jpsd879ccm6fea98c\"),\n    TestCase(\"почемужеонинеговорятпорусски\", \"xn--b1abfaaepdrnnbgefbadotcwatmq2g4l\"),\n    TestCase(\"PorquénopuedensimplementehablarenEspañol\", \"xn--PorqunopuedensimplementehablarenEspaol-fmd56a\"),\n    TestCase(\"TạisaohọkhôngthểchỉnóitiếngViệt\", \"xn--TisaohkhngthchnitingVit-kjcr8268qyxafd2f1b9g\"),\n    TestCase(\"3年B組金八先生\", \"xn--3B-ww4c5e180e575a65lsy2b\"),\n    TestCase(\"安室奈美恵-with-SUPER-MONKEYS\", \"xn---with-SUPER-MONKEYS-pc58ag80a8qai00g7n9n\"),\n    TestCase(\"Hello-Another-Way-それぞれの場所\", \"xn--Hello-Another-Way--fc4qua05auwb3674vfr0b\"),\n    TestCase(\"ひとつ屋根の下2\", \"xn--2-u9tlzr9756bt3uc0v\"),\n    TestCase(\"MajiでKoiする5秒前\", \"xn--MajiKoi5-783gue6qz075azm5e\"),\n    TestCase(\"パフィーdeルンバ\", \"xn--de-jg4avhby1noc0d\"),\n    TestCase(\"そのスピードで\", \"xn--d9juau41awczczp\"),\n    TestCase(\"-> $1.00 <-\", \"-> $1.00 <-\"),\n    // Test cases from https://unicode.org/reports/tr46/\n    TestCase(\"faß.de\", \"xn--fa-hia.de\"),\n    TestCase(\"βόλος.com\", \"xn--nxasmm1c.com\"),\n    TestCase(\"ශ්‍රී.com\", \"xn--10cl1a0b660p.com\"),\n    TestCase(\"نامه‌ای.com\", \"xn--mgba3gch31f060k.com\")\n  )\n\n  group(\"toAscii\") {\n    testData.foreach { case TestCase(decoded, encoded) =>\n      test(decoded) {\n        assertEquals(IDN.toAscii(decoded), encoded)\n      }\n    }\n  }\n\n  group(\"toUnicode\") {\n    testData.foreach {\n      case TestCase(decoded, Some(encoded)) =>\n        test(encoded) {\n          assertEquals(IDN.toUnicode(encoded), decoded.replaceAll(\"[\\u002e\\u3002\\uff0e\\uff61]\", \".\"))\n        }\n      case _ => ()\n    }\n  }\n\n}\n"
  },
  {
    "path": "test-kit/shared/src/test/scala/com/comcast/ip4s/Ipv4AddressTest.scala",
    "content": "/*\n * Copyright 2018 Comcast Cable Communications Management, LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.comcast.ip4s\n\nimport org.scalacheck.{Arbitrary, Gen}\nimport org.scalacheck.Prop.forAll\n\nimport Arbitraries._\n\nclass Ipv4AddressTest extends BaseTestSuite {\n  test(\"parsing from string - does not parse the empty string\") {\n    assertEquals(Ipv4Address.fromString(\"\"), None)\n  }\n\n  test(\"parsing from string - does not parse white space string\") {\n    assertEquals(Ipv4Address.fromString(\" \"), None)\n  }\n\n  test(\"roundtrip through string\") {\n    forAll(Gen.listOfN(4, Arbitrary.arbitrary[Byte])) { bytesList =>\n      if (bytesList.size == 4) {\n        val bytes = bytesList.toArray\n        val addr = Ipv4Address.fromBytes(bytes).get\n        assertEquals(Ipv4Address.fromString(addr.toString), Some(addr))\n      }\n    }\n  }\n\n  test(\"roundtrip through long\") {\n    forAll(Gen.listOfN(4, Arbitrary.arbitrary[Byte])) { bytesList =>\n      if (bytesList.size == 4) {\n        val bytes = bytesList.toArray\n        val addr = Ipv4Address.fromBytes(bytes).get\n        assertEquals(Ipv4Address.fromLong(addr.toLong), addr)\n      }\n    }\n  }\n\n  test(\"support ordering\") {\n    forAll { (left: Ipv4Address, right: Ipv4Address) =>\n      val longCompare = left.toLong.compare(right.toLong)\n      val result = Ordering[Ipv4Address].compare(left, right)\n      assertEquals(result, longCompare)\n    }\n  }\n\n  test(\"support computing next IP\") {\n    assertEquals(Ipv4Address.fromString(\"255.255.255.255\").map(_.next), Ipv4Address.fromString(\"0.0.0.0\"))\n    forAll { (ip: Ipv4Address) => assertEquals(ip.next, Ipv4Address.fromLong(ip.toLong + 1)) }\n  }\n\n  test(\"support computing previous IP\") {\n    assertEquals(Ipv4Address.fromString(\"0.0.0.0\").map(_.previous), Ipv4Address.fromString(\"255.255.255.255\"))\n    forAll { (ip: Ipv4Address) => assertEquals(ip.previous, Ipv4Address.fromLong(ip.toLong - 1)) }\n  }\n\n  test(\"isPrivate\") {\n    assert(!ipv4\"10.0.0.0\".previous.isPrivate)\n    assert(ipv4\"10.0.0.0\".isPrivate)\n    assert(ipv4\"10.255.255.255\".isPrivate)\n    assert(!ipv4\"10.255.255.255\".next.isPrivate)\n\n    assert(!ipv4\"172.16.0.0\".previous.isPrivate)\n    assert(ipv4\"172.16.0.0\".isPrivate)\n    assert(ipv4\"172.31.255.255\".isPrivate)\n    assert(!ipv4\"172.31.255.255\".next.isPrivate)\n\n    assert(!ipv4\"192.168.0.0\".previous.isPrivate)\n    assert(ipv4\"192.168.0.0\".isPrivate)\n    assert(ipv4\"192.168.255.255\".isPrivate)\n    assert(!ipv4\"192.168.255.255\".next.isPrivate)\n  }\n\n  test(\"isLoopback\") {\n    assert(ipv4\"127.0.0.1\".isLoopback)\n    assert(ipv4\"127.255.255.255\".isLoopback)\n    assert(!ipv4\"128.0.0.0\".isLoopback)\n  }\n\n  test(\"isLinkLocal\") {\n    assert(!ipv4\"127.0.0.1\".isLinkLocal)\n    assert(ipv4\"169.254.0.0\".isLinkLocal)\n    assert(ipv4\"169.254.255.255\".isLinkLocal)\n    assert(!ipv4\"169.254.255.255\".next.isLinkLocal)\n  }\n}\n"
  },
  {
    "path": "test-kit/shared/src/test/scala/com/comcast/ip4s/Ipv6AddressTest.scala",
    "content": "/*\n * Copyright 2018 Comcast Cable Communications Management, LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.comcast.ip4s\n\nimport org.scalacheck.{Arbitrary, Gen}\n\nimport org.scalacheck.Prop.forAll\nimport Arbitraries._\n\nclass Ipv6AddressTest extends BaseTestSuite {\n  test(\"parsing from string - does not parse the empty string\") {\n    assertEquals(Ipv6Address.fromString(\"\"), None)\n  }\n\n  test(\"parsing from string - does not parse white space string\") {\n    assertEquals(Ipv6Address.fromString(\" \"), None)\n  }\n\n  test(\"parsing from string - does not parse a single :\") {\n    assertEquals(Ipv6Address.fromString(\":\"), None)\n    assertEquals(Ipv6Address.fromString(\" : \"), None)\n  }\n\n  test(\"parsing from string - does not parse invalid number of sections in ipv6\") {\n    assertEquals(Ipv6Address.fromString(\"::1:1:1:1:1:1:2:1:3\"), None)\n    assertEquals(Ipv6Address.fromString(\"1:1:1:1:1:1:2:1:3\"), None)\n    // https://github.com/Comcast/ip4s/issues/662\n    assertEquals(Ipv6Address.fromString(\"18ec:cb0e:5795:a7df:6c7d:902c:9bd4\"), None)\n    assertEquals(Ipv6Address.fromString(\"18ec:cb0e\"), None)\n    assertEquals(Ipv6Address.fromString(\"18ec:\"), None)\n  }\n\n  test(\"parsing from string - does parse ::\") {\n    assertEquals(Ipv6Address.fromString(\"::\").isDefined, true)\n    assertEquals(Ipv6Address.fromString(\" :: \").isDefined, true)\n  }\n\n  test(\"parsing from string - supports mixed strings\") {\n    forAll { (v4: Ipv4Address) =>\n      assertEquals(Ipv6Address.fromString(\"::\" + v4), Some(v4.toCompatV6))\n      assertEquals(Ipv6Address.fromString(\"::ffff:\" + v4), Some(v4.toMappedV6))\n    }\n  }\n\n  test(\"parsing from string - does not misinterpret hosts\") {\n    assertEquals(Ipv6Address.fromString(\"db\"), None)\n  }\n\n  test(\"support converting to uncondensed string form\") {\n    forAll(Gen.listOfN(16, Arbitrary.arbitrary[Byte])) { bytesList =>\n      if (bytesList.size == 16) {\n        val bytes = bytesList.toArray\n        val addr = Ipv6Address.fromBytes(bytes).get\n        assert(addr.toUncondensedString.size == 4 * 8 + 7)\n      }\n    }\n  }\n\n  test(\"roundtrip through uncondensed strings\") {\n    forAll(Gen.listOfN(16, Arbitrary.arbitrary[Byte])) { bytesList =>\n      if (bytesList.size == 16) {\n        val bytes = bytesList.toArray\n        val addr = Ipv6Address.fromBytes(bytes).get\n        assertEquals(Ipv6Address.fromString(addr.toUncondensedString), Some(addr))\n      }\n    }\n  }\n\n  test(\"support converting to mixed string form\") {\n    forAll { (v4: Ipv4Address) =>\n      assertEquals(v4.toCompatV6.toMixedString, \"::\" + v4)\n      assertEquals(v4.toMappedV6.toMixedString, \"::ffff:\" + v4)\n    }\n  }\n\n  test(\"roundtrip through mixed strings\") {\n    forAll(Gen.listOfN(16, Arbitrary.arbitrary[Byte])) { bytesList =>\n      if (bytesList.size == 16) {\n        val bytes = bytesList.toArray\n        val addr = Ipv6Address.fromBytes(bytes).get\n        assertEquals(Ipv6Address.fromString(addr.toMixedString), Some(addr))\n      }\n    }\n  }\n\n  test(\"roundtrip through BigInt\") {\n    forAll(Gen.listOfN(16, Arbitrary.arbitrary[Byte])) { bytesList =>\n      if (bytesList.size == 16) {\n        val bytes = bytesList.toArray\n        val addr = Ipv6Address.fromBytes(bytes).get\n        assertEquals(Ipv6Address.fromBigInt(addr.toBigInt), addr)\n      }\n    }\n  }\n\n  test(\"support ordering\") {\n    forAll { (left: Ipv6Address, right: Ipv6Address) =>\n      val bigIntCompare = left.toBigInt.compare(right.toBigInt)\n      val result = Ordering[Ipv6Address].compare(left, right)\n      assertEquals(result, bigIntCompare)\n    }\n  }\n\n  test(\"support computing next IP\") {\n    assertEquals(\n      Ipv6Address.fromString(\"ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff\").map(_.next),\n      Ipv6Address.fromString(\"::\")\n    )\n    forAll { (ip: Ipv6Address) => assertEquals(ip.next, Ipv6Address.fromBigInt(ip.toBigInt + 1)) }\n  }\n\n  test(\"support computing previous IP\") {\n    assertEquals(\n      Ipv6Address.fromString(\"::\").map(_.previous),\n      Ipv6Address.fromString(\"ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff\")\n    )\n    forAll { (ip: Ipv6Address) => assertEquals(ip.previous, Ipv6Address.fromBigInt(ip.toBigInt - 1)) }\n  }\n\n  test(\"converting V4 mapped address\") {\n    val addr = ip\"::ffff:f:f\"\n    assertEquals[Any, Any](addr.getClass, classOf[Ipv6Address])\n    assertEquals(addr.version, IpVersion.V6)\n    assertEquals(addr.toString, \"::ffff:f:f\")\n    assertEquals[Any, Any](addr.collapseMappedV4.getClass, classOf[Ipv4Address])\n    assertEquals[Any, Any](addr.asIpv6, Some(addr))\n    assertEquals[Any, Any](addr.asIpv4, Some(ip\"0.15.0.15\"))\n  }\n\n  test(\"isPrivate\") {\n    assert(!ipv6\"fc00::\".previous.isPrivate)\n    assert(ipv6\"fc00::\".isPrivate)\n    assert(ipv6\"fe00::\".previous.isPrivate)\n    assert(!ipv6\"fe00::\".isPrivate)\n    // mapped v4\n    assert(ipv6\"::ffff:10.1.1.1\".isPrivate)\n  }\n\n  test(\"isLoopback\") {\n    assert(ipv6\"::1\".isLoopback)\n    assert(ipv6\"::ffff:127.0.0.1\".isLoopback)\n  }\n\n  test(\"isLinkLocal\") {\n    assert(ipv6\"fe80::1\".isLinkLocal)\n    assert(ipv6\"::ffff:169.254.0.0\".isLinkLocal)\n  }\n\n  test(\"scope ids\") {\n    assertEquals(ipv6\"fe80::1\".scopeId, None)\n    assert(ipv6\"fe80::1%en0\".isLinkLocal)\n    assertEquals(ipv6\"fe80::1%en0\".scopeId, Some(\"en0\"))\n    assert(ipv6\"fe80::1\" != ipv6\"fe80::1%en0\")\n    assert(ipv6\"fe80::1\".## != ipv6\"fe80::1%en0\".##)\n  }\n}\n"
  },
  {
    "path": "test-kit/shared/src/test/scala/com/comcast/ip4s/MacAddressTest.scala",
    "content": "/*\n * Copyright 2018 Comcast Cable Communications Management, LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.comcast.ip4s\n\nimport org.scalacheck.{Arbitrary, Gen, Prop}\nimport Prop.forAll\nimport Arbitraries._\n\nclass MacAddressTest extends BaseTestSuite {\n  test(\"roundtrip through string\") {\n    forAll(Gen.listOfN(6, Arbitrary.arbitrary[Byte])) { bytesList =>\n      if (bytesList.size == 6) {\n        val bytes = bytesList.toArray\n        val addr = MacAddress.fromBytes(bytes).get\n        assertEquals(MacAddress.fromString(addr.toString), Some(addr))\n      }\n    }\n  }\n\n  test(\"support ordering\") {\n    forAll { (left: MacAddress, right: MacAddress) =>\n      val longCompare = left.toLong.compare(right.toLong)\n      val result = Ordering[MacAddress].compare(left, right)\n      assertEquals(result, longCompare)\n    }\n  }\n}\n"
  },
  {
    "path": "test-kit/shared/src/test/scala/com/comcast/ip4s/MulticastSocketAddressTest.scala",
    "content": "/*\n * Copyright 2018 Comcast Cable Communications Management, LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.comcast.ip4s\n\nimport org.scalacheck.Prop.forAll\nimport Arbitraries._\n\nclass MulticastSocketAddressTest extends BaseTestSuite {\n  test(\"roundtrip through string\") {\n    forAll { (msa: MulticastSocketAddress[MulticastJoin, IpAddress]) =>\n      assertEquals(MulticastSocketAddress.fromString(msa.toString), Some(msa))\n    }\n  }\n}\n"
  },
  {
    "path": "test-kit/shared/src/test/scala/com/comcast/ip4s/MulticastTest.scala",
    "content": "/*\n * Copyright 2018 Comcast Cable Communications Management, LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.comcast.ip4s\n\nimport org.scalacheck.Prop.forAll\nimport Arbitraries._\n\nclass MulticastTest extends BaseTestSuite {\n  test(\"support equality\") {\n    forAll { (mip: Multicast[IpAddress]) =>\n      assertEquals(mip.address.asMulticast, Some(mip))\n      mip.address.asSourceSpecificMulticastLenient.foreach(x => assertEquals(mip, x))\n      mip.address.asSourceSpecificMulticastLenient.foreach(x => assert(x == mip))\n    }\n  }\n\n  test(\"support SSM outside source specific range\") {\n    assertEquals(ip\"239.10.10.10\".asSourceSpecificMulticast, None)\n    assertEquals(\n      ip\"239.10.10.10\".asSourceSpecificMulticastLenient,\n      Some(SourceSpecificMulticast.unsafeCreate(ip\"239.10.10.10\"))\n    )\n  }\n}\n"
  },
  {
    "path": "test-kit/shared/src/test/scala/com/comcast/ip4s/NetworkInterfacesTest.scala",
    "content": "/*\n * Copyright 2018 Comcast Cable Communications Management, LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.comcast.ip4s\n\nimport cats.effect.IO\nimport cats.syntax.all.*\nimport munit.CatsEffectSuite\n\nclass NetworkInterfacesTest extends CatsEffectSuite {\n\n  test(\"getAll\") {\n    NetworkInterfaces[IO].getAll.map { nis =>\n      assert(nis.nonEmpty)\n    }\n  }\n\n  test(\"getByName\") {\n    NetworkInterfaces[IO].getAll.flatMap { nis =>\n      nis.values.toList.traverse { ni =>\n        NetworkInterfaces[IO].getByName(ni.name).map { nni =>\n          assertEquals(nni, Some(ni))\n        }\n      }\n    }\n  }\n\n  test(\"getByAddress\") {\n    NetworkInterfaces[IO].getAll.flatMap { nis =>\n      nis.values.toList.traverse { ni =>\n        ni.addresses.traverse { cidr =>\n          NetworkInterfaces[IO].getByAddress(cidr.address).map { nni =>\n            assertEquals(nni, Some(ni))\n          }\n        }\n      }\n    }\n  }\n\n  test(\"getByMacAddress\") {\n    NetworkInterfaces[IO].getAll.flatMap { nis =>\n      nis.values.toList.traverse { ni =>\n        ni.macAddress.traverse { mac =>\n          NetworkInterfaces[IO].getByMacAddress(mac).map { nni =>\n            assert(nni.contains(ni))\n          }\n        }\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "test-kit/shared/src/test/scala/com/comcast/ip4s/PortTest.scala",
    "content": "/*\n * Copyright 2018 Comcast Cable Communications Management, LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.comcast.ip4s\n\nimport org.scalacheck.Prop.forAll\nimport Arbitraries._\n\nclass PortTest extends BaseTestSuite {\n  test(\"roundtrip through string\") {\n    forAll { (p: Port) => assertEquals(Port.fromString(p.toString), Some(p)) }\n  }\n}\n"
  },
  {
    "path": "test-kit/shared/src/test/scala/com/comcast/ip4s/SocketAddressTest.scala",
    "content": "/*\n * Copyright 2018 Comcast Cable Communications Management, LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.comcast.ip4s\n\nimport org.scalacheck.Prop.forAll\nimport Arbitraries._\n\nclass SocketAddressTest extends BaseTestSuite {\n  test(\"roundtrip through string\") {\n    forAll { (sa: SocketAddress[IpAddress]) => assertEquals(SocketAddress.fromString(sa.toString), Some(sa)) }\n  }\n}\n"
  }
]