Full Code of Comcast/ip4s for AI

main 64cc655f5d75 cached
70 files
224.8 KB
63.9k tokens
1 requests
Download .txt
Showing preview only (248K chars total). Download the full file or copy to clipboard to get everything.
Repository: Comcast/ip4s
Branch: main
Commit: 64cc655f5d75
Files: 70
Total size: 224.8 KB

Directory structure:
gitextract_slxgux55/

├── .git-blame-ignore-revs
├── .github/
│   └── workflows/
│       ├── ci.yml
│       └── clean.yml
├── .gitignore
├── .sbtopts
├── .scalafmt.conf
├── CODE_OF_CONDUCT.md
├── CONTRIBUTING
├── LICENSE
├── NOTICE
├── README.md
├── build.sbt
├── docs/
│   ├── guide.md
│   └── src/
│       └── guide.md
├── js/
│   └── src/
│       └── main/
│           └── scala/
│               └── com/
│                   └── comcast/
│                       └── ip4s/
│                           ├── DnsPlatform.scala
│                           ├── IpAddressPlatform.scala
│                           ├── NetworkInterfacePlatform.scala
│                           ├── NetworkInterfacesPlatform.scala
│                           ├── SocketAddressPlatform.scala
│                           ├── UnknownHostException.scala
│                           └── ip4splatform.scala
├── jvm-native/
│   └── src/
│       └── main/
│           └── scala/
│               └── com/
│                   └── comcast/
│                       └── ip4s/
│                           ├── DnsPlatform.scala
│                           ├── IpAddressPlatform.scala
│                           ├── NetworkInterfacePlatform.scala
│                           ├── NetworkInterfacesPlatform.scala
│                           ├── SocketAddressPlatform.scala
│                           └── ip4splatform.scala
├── project/
│   ├── build.properties
│   └── plugins.sbt
├── shared/
│   └── src/
│       └── main/
│           ├── scala/
│           │   └── com/
│           │       └── comcast/
│           │           └── ip4s/
│           │               ├── Cidr.scala
│           │               ├── Dns.scala
│           │               ├── GenSocketAddress.scala
│           │               ├── Host.scala
│           │               ├── MacAddress.scala
│           │               ├── Multicast.scala
│           │               ├── MulticastJoin.scala
│           │               ├── MulticastSocketAddress.scala
│           │               ├── NetworkInterface.scala
│           │               ├── NetworkInterfaces.scala
│           │               ├── Port.scala
│           │               ├── SocketAddress.scala
│           │               └── UnixSocketAddress.scala
│           ├── scala-2/
│           │   ├── Literals.scala
│           │   └── package.scala
│           ├── scala-2.12/
│           │   └── com/
│           │       └── comcast/
│           │           └── ip4s/
│           │               └── CollectionCompat.scala
│           ├── scala-2.13/
│           │   └── com/
│           │       └── comcast/
│           │           └── ip4s/
│           │               └── CollectionCompat.scala
│           └── scala-3/
│               ├── Literals.scala
│               ├── com/
│               │   └── comcast/
│               │       └── ip4s/
│               │           └── CollectionCompat.scala
│               └── package.scala
└── test-kit/
    ├── js/
    │   └── src/
    │       └── test/
    │           └── scala/
    │               └── com/
    │                   └── comcast/
    │                       └── ip4s/
    │                           └── TestPlatform.scala
    ├── jvm/
    │   └── src/
    │       └── test/
    │           └── scala/
    │               └── com/
    │                   └── comcast/
    │                       └── ip4s/
    │                           ├── Ipv6AddressJvmTest.scala
    │                           └── TestPlatform.scala
    ├── native/
    │   └── src/
    │       └── test/
    │           └── scala/
    │               └── com/
    │                   └── comcast/
    │                       └── ip4s/
    │                           └── TestPlatform.scala
    └── shared/
        └── src/
            ├── main/
            │   └── scala/
            │       └── com/
            │           └── comcast/
            │               └── ip4s/
            │                   └── Arbitraries.scala
            └── test/
                └── scala/
                    └── com/
                        └── comcast/
                            └── ip4s/
                                ├── BaseTestSuite.scala
                                ├── CidrStrictTest.scala
                                ├── CidrTest.scala
                                ├── DnsTest.scala
                                ├── Examples.scala
                                ├── HostnameTest.scala
                                ├── IDNTest.scala
                                ├── IdnaSuite.scala
                                ├── Ipv4AddressTest.scala
                                ├── Ipv6AddressTest.scala
                                ├── MacAddressTest.scala
                                ├── MulticastSocketAddressTest.scala
                                ├── MulticastTest.scala
                                ├── NetworkInterfacesTest.scala
                                ├── PortTest.scala
                                └── SocketAddressTest.scala

================================================
FILE CONTENTS
================================================

================================================
FILE: .git-blame-ignore-revs
================================================
# Scala Steward: Reformat with scalafmt 3.9.7
5ccb66cf1c2c22e7c4aeb4c73fec92b874e6d296


================================================
FILE: .github/workflows/ci.yml
================================================
# This file was automatically generated by sbt-github-actions using the
# githubWorkflowGenerate task. You should add and commit this file to
# your git repository. It goes without saying that you shouldn't edit
# this file by hand! Instead, if you wish to make changes, you should
# change your sbt build configuration to revise the workflow description
# to meet your needs, then regenerate this file.

name: Continuous Integration

on:
  pull_request:
    branches: ['**', '!update/**', '!pr/**']
  push:
    branches: ['**', '!update/**', '!pr/**']
    tags: [v*]

env:
  GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}


concurrency:
  group: ${{ github.workflow }} @ ${{ github.ref }}
  cancel-in-progress: true

jobs:
  build:
    name: Test
    strategy:
      matrix:
        os: [ubuntu-22.04]
        scala: [2.12, 2.13, 3]
        java: [temurin@8]
        project: [rootJS, rootJVM, rootNative]
    runs-on: ${{ matrix.os }}
    timeout-minutes: 60
    steps:
      - name: Checkout current branch (full)
        uses: actions/checkout@v6
        with:
          fetch-depth: 0

      - name: Setup sbt
        uses: sbt/setup-sbt@v1

      - name: Setup Java (temurin@8)
        id: setup-java-temurin-8
        if: matrix.java == 'temurin@8'
        uses: actions/setup-java@v5
        with:
          distribution: temurin
          java-version: 8
          cache: sbt

      - name: sbt update
        if: matrix.java == 'temurin@8' && steps.setup-java-temurin-8.outputs.cache-hit == 'false'
        run: sbt +update

      - name: Check that workflows are up to date
        run: sbt githubWorkflowCheck

      - name: Check headers and formatting
        if: matrix.java == 'temurin@8' && matrix.os == 'ubuntu-22.04'
        run: sbt 'project ${{ matrix.project }}' '++ ${{ matrix.scala }}' headerCheckAll scalafmtCheckAll 'project /' scalafmtSbtCheck

      - name: scalaJSLink
        if: matrix.project == 'rootJS'
        run: sbt 'project ${{ matrix.project }}' '++ ${{ matrix.scala }}' Test/scalaJSLinkerResult

      - name: nativeLink
        if: matrix.project == 'rootNative'
        run: sbt 'project ${{ matrix.project }}' '++ ${{ matrix.scala }}' Test/nativeLink

      - name: Test
        run: sbt 'project ${{ matrix.project }}' '++ ${{ matrix.scala }}' test

      - name: Check binary compatibility
        if: matrix.java == 'temurin@8' && matrix.os == 'ubuntu-22.04'
        run: sbt 'project ${{ matrix.project }}' '++ ${{ matrix.scala }}' mimaReportBinaryIssues

      - name: Generate API documentation
        if: matrix.java == 'temurin@8' && matrix.os == 'ubuntu-22.04'
        run: sbt 'project ${{ matrix.project }}' '++ ${{ matrix.scala }}' doc

      - name: Make target directories
        if: github.event_name != 'pull_request' && (startsWith(github.ref, 'refs/tags/v') || github.ref == 'refs/heads/main')
        run: mkdir -p native/target test-kit/js/target js/target test-kit/native/target test-kit/jvm/target jvm/target project/target

      - name: Compress target directories
        if: github.event_name != 'pull_request' && (startsWith(github.ref, 'refs/tags/v') || github.ref == 'refs/heads/main')
        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

      - name: Upload target directories
        if: github.event_name != 'pull_request' && (startsWith(github.ref, 'refs/tags/v') || github.ref == 'refs/heads/main')
        uses: actions/upload-artifact@v5
        with:
          name: target-${{ matrix.os }}-${{ matrix.java }}-${{ matrix.scala }}-${{ matrix.project }}
          path: targets.tar

  publish:
    name: Publish Artifacts
    needs: [build]
    if: github.event_name != 'pull_request' && (startsWith(github.ref, 'refs/tags/v') || github.ref == 'refs/heads/main')
    strategy:
      matrix:
        os: [ubuntu-22.04]
        java: [temurin@8]
    runs-on: ${{ matrix.os }}
    steps:
      - name: Checkout current branch (full)
        uses: actions/checkout@v6
        with:
          fetch-depth: 0

      - name: Setup sbt
        uses: sbt/setup-sbt@v1

      - name: Setup Java (temurin@8)
        id: setup-java-temurin-8
        if: matrix.java == 'temurin@8'
        uses: actions/setup-java@v5
        with:
          distribution: temurin
          java-version: 8
          cache: sbt

      - name: sbt update
        if: matrix.java == 'temurin@8' && steps.setup-java-temurin-8.outputs.cache-hit == 'false'
        run: sbt +update

      - name: Download target directories (2.12, rootJS)
        uses: actions/download-artifact@v6
        with:
          name: target-${{ matrix.os }}-${{ matrix.java }}-2.12-rootJS

      - name: Inflate target directories (2.12, rootJS)
        run: |
          tar xf targets.tar
          rm targets.tar

      - name: Download target directories (2.12, rootJVM)
        uses: actions/download-artifact@v6
        with:
          name: target-${{ matrix.os }}-${{ matrix.java }}-2.12-rootJVM

      - name: Inflate target directories (2.12, rootJVM)
        run: |
          tar xf targets.tar
          rm targets.tar

      - name: Download target directories (2.12, rootNative)
        uses: actions/download-artifact@v6
        with:
          name: target-${{ matrix.os }}-${{ matrix.java }}-2.12-rootNative

      - name: Inflate target directories (2.12, rootNative)
        run: |
          tar xf targets.tar
          rm targets.tar

      - name: Download target directories (2.13, rootJS)
        uses: actions/download-artifact@v6
        with:
          name: target-${{ matrix.os }}-${{ matrix.java }}-2.13-rootJS

      - name: Inflate target directories (2.13, rootJS)
        run: |
          tar xf targets.tar
          rm targets.tar

      - name: Download target directories (2.13, rootJVM)
        uses: actions/download-artifact@v6
        with:
          name: target-${{ matrix.os }}-${{ matrix.java }}-2.13-rootJVM

      - name: Inflate target directories (2.13, rootJVM)
        run: |
          tar xf targets.tar
          rm targets.tar

      - name: Download target directories (2.13, rootNative)
        uses: actions/download-artifact@v6
        with:
          name: target-${{ matrix.os }}-${{ matrix.java }}-2.13-rootNative

      - name: Inflate target directories (2.13, rootNative)
        run: |
          tar xf targets.tar
          rm targets.tar

      - name: Download target directories (3, rootJS)
        uses: actions/download-artifact@v6
        with:
          name: target-${{ matrix.os }}-${{ matrix.java }}-3-rootJS

      - name: Inflate target directories (3, rootJS)
        run: |
          tar xf targets.tar
          rm targets.tar

      - name: Download target directories (3, rootJVM)
        uses: actions/download-artifact@v6
        with:
          name: target-${{ matrix.os }}-${{ matrix.java }}-3-rootJVM

      - name: Inflate target directories (3, rootJVM)
        run: |
          tar xf targets.tar
          rm targets.tar

      - name: Download target directories (3, rootNative)
        uses: actions/download-artifact@v6
        with:
          name: target-${{ matrix.os }}-${{ matrix.java }}-3-rootNative

      - name: Inflate target directories (3, rootNative)
        run: |
          tar xf targets.tar
          rm targets.tar

      - name: Import signing key
        if: env.PGP_SECRET != '' && env.PGP_PASSPHRASE == ''
        env:
          PGP_SECRET: ${{ secrets.PGP_SECRET }}
          PGP_PASSPHRASE: ${{ secrets.PGP_PASSPHRASE }}
        run: echo $PGP_SECRET | base64 -d -i - | gpg --import

      - name: Import signing key and strip passphrase
        if: env.PGP_SECRET != '' && env.PGP_PASSPHRASE != ''
        env:
          PGP_SECRET: ${{ secrets.PGP_SECRET }}
          PGP_PASSPHRASE: ${{ secrets.PGP_PASSPHRASE }}
        run: |
          echo "$PGP_SECRET" | base64 -d -i - > /tmp/signing-key.gpg
          echo "$PGP_PASSPHRASE" | gpg --pinentry-mode loopback --passphrase-fd 0 --import /tmp/signing-key.gpg
          (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)

      - name: Publish
        env:
          SONATYPE_USERNAME: ${{ secrets.SONATYPE_USERNAME }}
          SONATYPE_PASSWORD: ${{ secrets.SONATYPE_PASSWORD }}
          SONATYPE_CREDENTIAL_HOST: ${{ secrets.SONATYPE_CREDENTIAL_HOST }}
        run: sbt tlCiRelease

  dependency-submission:
    name: Submit Dependencies
    if: github.event.repository.fork == false && github.event_name != 'pull_request'
    strategy:
      matrix:
        os: [ubuntu-22.04]
        java: [temurin@8]
    runs-on: ${{ matrix.os }}
    steps:
      - name: Checkout current branch (full)
        uses: actions/checkout@v6
        with:
          fetch-depth: 0

      - name: Setup sbt
        uses: sbt/setup-sbt@v1

      - name: Setup Java (temurin@8)
        id: setup-java-temurin-8
        if: matrix.java == 'temurin@8'
        uses: actions/setup-java@v5
        with:
          distribution: temurin
          java-version: 8
          cache: sbt

      - name: sbt update
        if: matrix.java == 'temurin@8' && steps.setup-java-temurin-8.outputs.cache-hit == 'false'
        run: sbt +update

      - name: Submit Dependencies
        uses: scalacenter/sbt-dependency-submission@v2
        with:
          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
          configs-ignore: test scala-tool scala-doc-tool test-internal


================================================
FILE: .github/workflows/clean.yml
================================================
# This file was automatically generated by sbt-github-actions using the
# githubWorkflowGenerate task. You should add and commit this file to
# your git repository. It goes without saying that you shouldn't edit
# this file by hand! Instead, if you wish to make changes, you should
# change your sbt build configuration to revise the workflow description
# to meet your needs, then regenerate this file.

name: Clean

on: push

jobs:
  delete-artifacts:
    name: Delete Artifacts
    runs-on: ubuntu-latest
    env:
      GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
    steps:
      - name: Delete artifacts
        run: |
          # Customize those three lines with your repository and credentials:
          REPO=${GITHUB_API_URL}/repos/${{ github.repository }}

          # A shortcut to call GitHub API.
          ghapi() { curl --silent --location --user _:$GITHUB_TOKEN "$@"; }

          # A temporary file which receives HTTP response headers.
          TMPFILE=/tmp/tmp.$$

          # An associative array, key: artifact name, value: number of artifacts of that name.
          declare -A ARTCOUNT

          # Process all artifacts on this repository, loop on returned "pages".
          URL=$REPO/actions/artifacts
          while [[ -n "$URL" ]]; do

            # Get current page, get response headers in a temporary file.
            JSON=$(ghapi --dump-header $TMPFILE "$URL")

            # Get URL of next page. Will be empty if we are at the last page.
            URL=$(grep '^Link:' "$TMPFILE" | tr ',' '\n' | grep 'rel="next"' | head -1 | sed -e 's/.*<//' -e 's/>.*//')
            rm -f $TMPFILE

            # Number of artifacts on this page:
            COUNT=$(( $(jq <<<$JSON -r '.artifacts | length') ))

            # Loop on all artifacts on this page.
            for ((i=0; $i < $COUNT; i++)); do

              # Get name of artifact and count instances of this name.
              name=$(jq <<<$JSON -r ".artifacts[$i].name?")
              ARTCOUNT[$name]=$(( $(( ${ARTCOUNT[$name]} )) + 1))

              id=$(jq <<<$JSON -r ".artifacts[$i].id?")
              size=$(( $(jq <<<$JSON -r ".artifacts[$i].size_in_bytes?") ))
              printf "Deleting '%s' #%d, %'d bytes\n" $name ${ARTCOUNT[$name]} $size
              ghapi -X DELETE $REPO/actions/artifacts/$id
            done
          done


================================================
FILE: .gitignore
================================================
target
.classpath
.project
.settings/
.worksheet/
.cache
.tags
.idea
.idea_modules/
.DS_Store
*.swp
.metaserver
.bloop
.metals
.vscode
project/metals.sbt
project/project
.bsp



================================================
FILE: .sbtopts
================================================
-J-Xms2g
-J-Xmx4g
-J-XX:MaxMetaspaceSize=512m


================================================
FILE: .scalafmt.conf
================================================
version = "3.10.7"

maxColumn: 120

runner.dialect = scala213source3

project.layout = StandardConvention

rewrite.scala3.convertToNewSyntax = true
rewrite.scala3.newSyntax.control = false
runner.dialectOverride.allowUnderscoreAsTypePlaceholder = false
runner.dialectOverride.allowSignificantIndentation = false
runner.dialectOverride.allowStarWildcardImport = false

docstrings.wrap = "no"


================================================
FILE: CODE_OF_CONDUCT.md
================================================
# Code of Conduct

We 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.

Everyone 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.

## Moderation

Any questions, concerns, or moderation requests please contact a member of the project.

- [Michael Pilquist](mailto:mpilquist@gmail.com)

[Scala Code of Conduct]: https://www.scala-lang.org/conduct/


================================================
FILE: CONTRIBUTING
================================================
If you would like to contribute code to this project you can do so through
GitHub by forking the repository and sending a pull request.

Before Comcast merges your code into the project you must sign the [Comcast
Contributor License Agreement (CLA)](https://gist.github.com/ComcastOSS/a7b8933dd8e368535378cda25c92d19a).

If you haven't previously signed a Comcast CLA, you'll automatically be asked
to when you open a pull request. Alternatively, we can send you a PDF that
you can sign and scan back to us. Please create a new GitHub issue to request
a PDF version of the CLA.



================================================
FILE: LICENSE
================================================
                                 Apache License
                           Version 2.0, January 2004
                        http://www.apache.org/licenses/

   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION

   1. Definitions.

      "License" shall mean the terms and conditions for use, reproduction,
      and distribution as defined by Sections 1 through 9 of this document.

      "Licensor" shall mean the copyright owner or entity authorized by
      the copyright owner that is granting the License.

      "Legal Entity" shall mean the union of the acting entity and all
      other entities that control, are controlled by, or are under common
      control with that entity. For the purposes of this definition,
      "control" means (i) the power, direct or indirect, to cause the
      direction or management of such entity, whether by contract or
      otherwise, or (ii) ownership of fifty percent (50%) or more of the
      outstanding shares, or (iii) beneficial ownership of such entity.

      "You" (or "Your") shall mean an individual or Legal Entity
      exercising permissions granted by this License.

      "Source" form shall mean the preferred form for making modifications,
      including but not limited to software source code, documentation
      source, and configuration files.

      "Object" form shall mean any form resulting from mechanical
      transformation or translation of a Source form, including but
      not limited to compiled object code, generated documentation,
      and conversions to other media types.

      "Work" shall mean the work of authorship, whether in Source or
      Object form, made available under the License, as indicated by a
      copyright notice that is included in or attached to the work
      (an example is provided in the Appendix below).

      "Derivative Works" shall mean any work, whether in Source or Object
      form, that is based on (or derived from) the Work and for which the
      editorial revisions, annotations, elaborations, or other modifications
      represent, as a whole, an original work of authorship. For the purposes
      of this License, Derivative Works shall not include works that remain
      separable from, or merely link (or bind by name) to the interfaces of,
      the Work and Derivative Works thereof.

      "Contribution" shall mean any work of authorship, including
      the original version of the Work and any modifications or additions
      to that Work or Derivative Works thereof, that is intentionally
      submitted to Licensor for inclusion in the Work by the copyright owner
      or by an individual or Legal Entity authorized to submit on behalf of
      the copyright owner. For the purposes of this definition, "submitted"
      means any form of electronic, verbal, or written communication sent
      to the Licensor or its representatives, including but not limited to
      communication on electronic mailing lists, source code control systems,
      and issue tracking systems that are managed by, or on behalf of, the
      Licensor for the purpose of discussing and improving the Work, but
      excluding communication that is conspicuously marked or otherwise
      designated in writing by the copyright owner as "Not a Contribution."

      "Contributor" shall mean Licensor and any individual or Legal Entity
      on behalf of whom a Contribution has been received by Licensor and
      subsequently incorporated within the Work.

   2. Grant of Copyright License. Subject to the terms and conditions of
      this License, each Contributor hereby grants to You a perpetual,
      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
      copyright license to reproduce, prepare Derivative Works of,
      publicly display, publicly perform, sublicense, and distribute the
      Work and such Derivative Works in Source or Object form.

   3. Grant of Patent License. Subject to the terms and conditions of
      this License, each Contributor hereby grants to You a perpetual,
      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
      (except as stated in this section) patent license to make, have made,
      use, offer to sell, sell, import, and otherwise transfer the Work,
      where such license applies only to those patent claims licensable
      by such Contributor that are necessarily infringed by their
      Contribution(s) alone or by combination of their Contribution(s)
      with the Work to which such Contribution(s) was submitted. If You
      institute patent litigation against any entity (including a
      cross-claim or counterclaim in a lawsuit) alleging that the Work
      or a Contribution incorporated within the Work constitutes direct
      or contributory patent infringement, then any patent licenses
      granted to You under this License for that Work shall terminate
      as of the date such litigation is filed.

   4. Redistribution. You may reproduce and distribute copies of the
      Work or Derivative Works thereof in any medium, with or without
      modifications, and in Source or Object form, provided that You
      meet the following conditions:

      (a) You must give any other recipients of the Work or
          Derivative Works a copy of this License; and

      (b) You must cause any modified files to carry prominent notices
          stating that You changed the files; and

      (c) You must retain, in the Source form of any Derivative Works
          that You distribute, all copyright, patent, trademark, and
          attribution notices from the Source form of the Work,
          excluding those notices that do not pertain to any part of
          the Derivative Works; and

      (d) If the Work includes a "NOTICE" text file as part of its
          distribution, then any Derivative Works that You distribute must
          include a readable copy of the attribution notices contained
          within such NOTICE file, excluding those notices that do not
          pertain to any part of the Derivative Works, in at least one
          of the following places: within a NOTICE text file distributed
          as part of the Derivative Works; within the Source form or
          documentation, if provided along with the Derivative Works; or,
          within a display generated by the Derivative Works, if and
          wherever such third-party notices normally appear. The contents
          of the NOTICE file are for informational purposes only and
          do not modify the License. You may add Your own attribution
          notices within Derivative Works that You distribute, alongside
          or as an addendum to the NOTICE text from the Work, provided
          that such additional attribution notices cannot be construed
          as modifying the License.

      You may add Your own copyright statement to Your modifications and
      may provide additional or different license terms and conditions
      for use, reproduction, or distribution of Your modifications, or
      for any such Derivative Works as a whole, provided Your use,
      reproduction, and distribution of the Work otherwise complies with
      the conditions stated in this License.

   5. Submission of Contributions. Unless You explicitly state otherwise,
      any Contribution intentionally submitted for inclusion in the Work
      by You to the Licensor shall be under the terms and conditions of
      this License, without any additional terms or conditions.
      Notwithstanding the above, nothing herein shall supersede or modify
      the terms of any separate license agreement you may have executed
      with Licensor regarding such Contributions.

   6. Trademarks. This License does not grant permission to use the trade
      names, trademarks, service marks, or product names of the Licensor,
      except as required for reasonable and customary use in describing the
      origin of the Work and reproducing the content of the NOTICE file.

   7. Disclaimer of Warranty. Unless required by applicable law or
      agreed to in writing, Licensor provides the Work (and each
      Contributor provides its Contributions) on an "AS IS" BASIS,
      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
      implied, including, without limitation, any warranties or conditions
      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
      PARTICULAR PURPOSE. You are solely responsible for determining the
      appropriateness of using or redistributing the Work and assume any
      risks associated with Your exercise of permissions under this License.

   8. Limitation of Liability. In no event and under no legal theory,
      whether in tort (including negligence), contract, or otherwise,
      unless required by applicable law (such as deliberate and grossly
      negligent acts) or agreed to in writing, shall any Contributor be
      liable to You for damages, including any direct, indirect, special,
      incidental, or consequential damages of any character arising as a
      result of this License or out of the use or inability to use the
      Work (including but not limited to damages for loss of goodwill,
      work stoppage, computer failure or malfunction, or any and all
      other commercial damages or losses), even if such Contributor
      has been advised of the possibility of such damages.

   9. Accepting Warranty or Additional Liability. While redistributing
      the Work or Derivative Works thereof, You may choose to offer,
      and charge a fee for, acceptance of support, warranty, indemnity,
      or other liability obligations and/or rights consistent with this
      License. However, in accepting such obligations, You may act only
      on Your own behalf and on Your sole responsibility, not on behalf
      of any other Contributor, and only if You agree to indemnify,
      defend, and hold each Contributor harmless for any liability
      incurred by, or claims asserted against, such Contributor by reason
      of your accepting any such warranty or additional liability.

   END OF TERMS AND CONDITIONS

   APPENDIX: How to apply the Apache License to your work.

      To apply the Apache License to your work, attach the following
      boilerplate notice, with the fields enclosed by brackets "[]"
      replaced with your own identifying information. (Don't include
      the brackets!)  The text should be enclosed in the appropriate
      comment syntax for the file format. We also recommend that a
      file or class name and description of purpose be included on the
      same "printed page" as the copyright notice for easier
      identification within third-party archives.

   Copyright [yyyy] [name of copyright owner]

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.


================================================
FILE: NOTICE
================================================
IP Addresses for Scala and Scala.js
Copyright 2018 Comcast Cable Communications Management, LLC
Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
This product includes software developed at Comcast (http://www.comcast.com/).

This 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.

```
Copyright Mathias Bynens <https://mathiasbynens.be/>

Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:

The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
```

This product includes sources derived from [libuv](https://github.com/libuv/libuv/), which is available under the following license.

```
Copyright (c) 2015-present libuv project contributors.

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to
deal in the Software without restriction, including without limitation the
rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
sell copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
IN THE SOFTWARE.
```


================================================
FILE: README.md
================================================
[![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)
[![javadoc](https://javadoc.io/badge2/com.comcast/ip4s-core_3/javadoc.svg)](https://javadoc.io/doc/com.comcast/ip4s-core_3)

ip4s: IP Addresses for Scala, Scala.js & Scala Native
=====================================================

This project defines immutable, safe data structures for describing IP addresses, multicast joins, socket addresses and similar IP & network related data types.

There are two defining characteristics of this project that make it different from other similar projects:
- all data types are immutable and every function/method is referentially transparent (e.g., no accidental DNS lookups by calling `InetAddress.getByName(...)`)
- published for Scala, Scala.js and Scala Native

See the [guide](docs/guide.md) and [ScalaDoc](https://javadoc.io/doc/com.comcast/ip4s-core_3) for more details.

## Getting Binaries

This library is published on Maven Central under group id `com.comcast` and artifact id `ip4s-core_${scalaBinaryVersion}`. Add the following to your SBT build:

```scala
libraryDependencies += "com.comcast" %% "ip4s-core" % "version"
```

## Interop

As 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).

## Copyright and License

This project is made available under the [Apache License, Version 2.0](LICENSE). Copyright information can be found in [NOTICE](NOTICE).

## Code of Conduct

See the [Code of Conduct](CODE_OF_CONDUCT.md).



================================================
FILE: build.sbt
================================================
import com.typesafe.tools.mima.core._

ThisBuild / tlBaseVersion := "3.7"

ThisBuild / organization := "com.comcast"
ThisBuild / organizationName := "Comcast Cable Communications Management, LLC"

ThisBuild / startYear := Some(2018)

ThisBuild / developers ++= List(
  tlGitHubDev("mpilquist", "Michael Pilquist"),
  tlGitHubDev("matthughes", "Matt Hughes"),
  tlGitHubDev("nequissimus", "Tim Steinbach")
)

ThisBuild / githubWorkflowJavaVersions := Seq(JavaSpec.temurin("8"))

ThisBuild / crossScalaVersions := List("2.12.21", "2.13.18", "3.3.7")

ThisBuild / tlVersionIntroduced := Map("3" -> "3.0.3")

ThisBuild / doctestTestFramework := DoctestTestFramework.ScalaCheck

ThisBuild / initialCommands := "import com.comcast.ip4s._"

ThisBuild / mimaBinaryIssueFilters ++= Seq(
  ProblemFilters.exclude[ReversedMissingMethodProblem]("com.comcast.ip4s.IpAddress.toDefaultString"), // #553
  ProblemFilters.exclude[DirectMissingMethodProblem]("com.comcast.ip4s.Ipv6Address.toInetAddress"),
  ProblemFilters.exclude[ReversedMissingMethodProblem]("com.comcast.ip4s.Dns.*"), // sealed trait
  // Scala 3 (erroneously?) considered Multicast/SourceSpecificMulticast as sum types
  ProblemFilters.exclude[DirectMissingMethodProblem]("com.comcast.ip4s.Multicast.ordinal"),
  ProblemFilters.exclude[MissingTypesProblem]("com.comcast.ip4s.Multicast$"),
  ProblemFilters.exclude[DirectMissingMethodProblem]("com.comcast.ip4s.SourceSpecificMulticast.ordinal"),
  ProblemFilters.exclude[MissingTypesProblem]("com.comcast.ip4s.SourceSpecificMulticast$"),
  ProblemFilters.exclude[ReversedMissingMethodProblem]("com.comcast.ip4s.IpAddress.isPrivate"), // #562
  ProblemFilters.exclude[ReversedMissingMethodProblem]("com.comcast.ip4s.IpAddress.isLoopback"),
  ProblemFilters.exclude[ReversedMissingMethodProblem]("com.comcast.ip4s.IpAddress.isLinkLocal"),
  // Removed JS-specifc Punycode bindings
  ProblemFilters.exclude[MissingClassProblem]("com.comcast.ip4s.Punycode"),
  ProblemFilters.exclude[MissingClassProblem]("com.comcast.ip4s.Punycode$"),
  ProblemFilters.exclude[MissingTypesProblem]("com.comcast.ip4s.IDN$"),
  ProblemFilters.exclude[MissingClassProblem]("com.comcast.ip4s.IDNCompanionPlatform")
)

lazy val root = tlCrossRootProject.aggregate(core, testKit)

lazy val testKit = crossProject(JVMPlatform, JSPlatform, NativePlatform)
  .in(file("./test-kit"))
  .settings(commonSettings)
  .settings(
    name := "ip4s-test-kit"
  )
  .settings(mimaPreviousArtifacts := Set.empty)
  .settings(
    libraryDependencies ++= Seq(
      "org.scalacheck" %%% "scalacheck" % "1.19.0",
      "org.scalameta" %%% "munit-scalacheck" % "1.2.0" % Test,
      "org.typelevel" %%% "munit-cats-effect" % "2.2.0" % Test
    )
  )
  .jvmSettings(
    libraryDependencies += "com.google.guava" % "guava" % "33.5.0-jre" % "test"
  )
  .dependsOn(core % "compile->compile")

lazy val testKitJVM = testKit.jvm
lazy val testKitJS = testKit.js
  .disablePlugins(DoctestPlugin)
  .enablePlugins(ScalaJSBundlerPlugin)
lazy val testKitNative = testKit.js
  .disablePlugins(DoctestPlugin)
  .settings(commonNativeSettings)

lazy val core = crossProject(JVMPlatform, JSPlatform, NativePlatform)
  .in(file("."))
  .settings(commonSettings)
  .settings(
    name := "ip4s-core",
    libraryDependencies ++= {
      if (tlIsScala3.value) Nil
      else List("org.scala-lang" % "scala-reflect" % scalaVersion.value % "provided")
    },
    scalacOptions := scalacOptions.value.filterNot(_ == "-source:3.0-migration"),
    Compile / doc / scalacOptions ++= (if (scalaVersion.value.startsWith("2.")) Seq("-nowarn")
                                       else Nil)
  )
  .settings(
    libraryDependencies ++= Seq(
      "org.typelevel" %%% "literally" % "1.2.0",
      "org.typelevel" %%% "cats-core" % "2.13.0",
      "org.typelevel" %%% "cats-effect" % "3.7.0",
      "org.typelevel" %%% "idna4s-core" % "0.1.0",
      "org.scalacheck" %%% "scalacheck" % "1.19.0" % Test
    )
  )

lazy val coreJVM = core.jvm.settings(
  doctestIgnoreRegex := Some(".*Literals.scala")
)

lazy val coreJS = core.js
  .disablePlugins(DoctestPlugin)
  .enablePlugins(ScalaJSBundlerPlugin)
  .settings(
    scalaJSLinkerConfig ~= (_.withModuleKind(ModuleKind.CommonJSModule))
  )

lazy val coreNative = core.native
  .disablePlugins(DoctestPlugin)
  .settings(commonNativeSettings)

lazy val docs = project
  .in(file("docs"))
  .enablePlugins(MdocPlugin)
  .dependsOn(coreJVM)
  .settings(
    mdocIn := baseDirectory.value / "src",
    mdocOut := baseDirectory.value / "../docs",
    githubWorkflowArtifactUpload := false,
    libraryDependencies += "org.typelevel" %%% "cats-effect" % "3.7.0"
  )

lazy val commonSettings = Seq(
  Compile / unmanagedResources ++= {
    val base = baseDirectory.value / ".."
    (base / "NOTICE") +: (base / "LICENSE") +: (base / "CONTRIBUTING") +: ((base / "licenses") * "LICENSE_*").get
  }
)

lazy val commonNativeSettings = Seq(
  tlVersionIntroduced := List("2.12", "2.13", "3").map(_ -> "3.8.0").toMap
)


================================================
FILE: docs/guide.md
================================================
ip4s: IP Addresses for Scala & Scala.js
=======================================

This 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.

# IP Addresses

The `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.

```scala
import com.comcast.ip4s.IpAddress

val home = IpAddress.fromString("127.0.0.1")
// home: Option[IpAddress] = Some(127.0.0.1)
val home6 = IpAddress.fromString("::1")
// home6: Option[IpAddress] = Some(::1)
```

The `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.

Sometimes 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:

```scala
import com.comcast.ip4s.{Ipv4Address, Ipv6Address}

val explicitV4Home = Ipv4Address.fromString("127.0.0.1")
// explicitV4Home: Option[Ipv4Address] = Some(127.0.0.1)
val explicitV6Home = Ipv6Address.fromString("::1")
// explicitV6Home: Option[Ipv6Address] = Some(::1)
```

Because `Ipv4Address` and `Ipv6Address` are subtypes of `IpAddress`, we can pattern match on an `IpAddress` or use the `fold` method:

```scala
import com.comcast.ip4s.{Ipv4Address, Ipv6Address}

val homeIsV4 = home.get match {
  case _: Ipv4Address => true
  case _: Ipv6Address => false
}
// homeIsV4: Boolean = true

val home6IsV4 = home6.get.fold(_ => true, _ => false)
// home6IsV4: Boolean = false
```

## IP Literals

In 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.

```scala
import com.comcast.ip4s._

val home = ip"127.0.0.1"
// home: IpAddress = 127.0.0.1
val home4 = ipv4"127.0.0.1"
// home4: Ipv4Address = 127.0.0.1
val home6 = ipv6"::1"
// home6: Ipv6Address = ::1
```

The `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.

## IPv6 String Formats

IPv6 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.

```scala
import com.comcast.ip4s._

val home = ipv6"::7f00:1"
// home: Ipv6Address = ::7f00:1
val homeLong = home.toUncondensedString
// homeLong: String = 0000:0000:0000:0000:0000:0000:7f00:0001
val homeMixed = home.toMixedString
// homeMixed: String = ::127.0.0.1

val parsedHomeLong = Ipv6Address.fromString(homeLong)
// parsedHomeLong: Option[Ipv6Address] = Some(::7f00:1)
val parsedHomeMixed = Ipv6Address.fromString(homeMixed)
// parsedHomeMixed: Option[Ipv6Address] = Some(::7f00:1)
```

## Ordering

IP 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).

```scala
val ips = List(ipv4"10.1.1.1", ipv4"10.1.2.0", ipv4"10.1.0.0", ipv6"::1", ipv6"ff3b::")
// ips: List[IpAddress] = List(10.1.1.1, 10.1.2.0, 10.1.0.0, ::1, ff3b::)
val sorted = ips.sorted
// sorted: List[IpAddress] = List(::1, 10.1.0.0, 10.1.1.1, 10.1.2.0, ff3b::)
```

## JVM Integration

When 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`.

```scala
val homeIA = ip"127.0.0.1".toInetAddress
// homeIA: InetAddress = /127.0.0.1
val home4IA = ipv4"127.0.0.1".toInetAddress
// home4IA: Inet4Address = /127.0.0.1
val home6IA = ipv6"::1".toInetAddress
// home6IA: InetAddress = /0:0:0:0:0:0:0:1
```

# Multicast

Both 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:

```scala
val 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")
// ips: List[IpAddress] = List(127.0.0.1, 224.10.10.10, 232.11.11.11, ::1, ff00::10, ff3b::11)
val multicastIps = ips.filter(_.isMulticast)
// multicastIps: List[IpAddress] = List(224.10.10.10, 232.11.11.11, ff00::10, ff3b::11)
val ssmIps = ips.filter(_.isSourceSpecificMulticast)
// ssmIps: List[IpAddress] = List(232.11.11.11, ff3b::11)
```

## Multicast Witnesses

Often, 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:

```scala
sealed trait Multicast[A <: IpAddress] { def address: A }
sealed trait SourceSpecificMulticast[A <: IpAddress] extends Multicast[A]
```

These 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]`.

To construct instances of `Multicast[A]` and `SourceSpecificMulticast[A]`, we can use the `asMulticast` and `asSourceSpecificMulticast` methods on `IpAddress`:

```scala
val multicastIps = ips.flatMap(_.asMulticast)
// multicastIps: List[Multicast[IpAddress]] = List(224.10.10.10, 232.11.11.11, ff00::10, ff3b::11)
val ssmIps = ips.flatMap(_.asSourceSpecificMulticast)
// ssmIps: List[Strict[IpAddress]] = List(232.11.11.11, ff3b::11)
```

It'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`:

```scala
val lenient = ips.flatMap(_.asSourceSpecificMulticastLenient)
// lenient: List[SourceSpecificMulticast[IpAddress]] = List(224.10.10.10, 232.11.11.11, ff00::10, ff3b::11)
```

Additionally, the `SourceSpecificMulticast.Strict[A]` type provides the guarantee that the wrapped address is in the RFC defined source specific range.

## Multicast Literals

There 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:

|Interpolator|Description|Result Type|Example|
|------------|-----------|-----------|-------|
|`mip`|Multicast IP|`Multicast[IpAddress]`|`mip"224.10.10.10"`|
|`mipv4`|V4 Multicast IP|`Multicast[Ipv4Address]`|`mipv4"224.10.10.10"`|
|`mipv6`|V6 Multicast IP|`Multicast[Ipv6Address]`|`mipv6"ff3b::10"`|
|`ssmip`|Source Specific Multicast IP|`SourceSpecificMulticast[IpAddress]`|`ssmip"224.10.10.10"`|
|`ssmipv4`|V4 Source Specific Multicast IP|`SourceSpecificMulticast[Ipv4Address]`|`ssmipv4"224.10.10.10"`|
|`ssmipv6`|V6 Source Specific Multicast IP|`SourceSpecificMulticast[Ipv6Address]`|`ssmipv6"ff3b::10"`|

## Multicast Joins

The `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.

This is modeled roughly as:

```scala
sealed trait MulticastJoin[A <: IpAddress]
case class AnySourceMulticastJoin[A <: IpAddress](group: Multicast[A]) extends MulticastJoin[A]
case class SourceSpecificMulticastJoin[A <: IpAddress](source: A, group: SourceSpecificMulticast[A]) extends MulticastJoin[A]
```

`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]`.

To construct a `MulticastJoin`, we can use the `asm` and `ssm` methods in the `MulticastJoin` companion.

```scala
val j1 = MulticastJoin.ssm(ipv4"10.11.12.13", ssmipv4"232.1.2.3")
// j1: MulticastJoin[Ipv4Address] = 10.11.12.13@232.1.2.3
val j2 = MulticastJoin.ssm(ipv4"10.11.12.13", ipv4"232.1.2.3".asSourceSpecificMulticast.get)
// j2: MulticastJoin[Ipv4Address] = 10.11.12.13@232.1.2.3
val j3 = MulticastJoin.asm(mipv6"ff3b::10")
// j3: MulticastJoin[Ipv6Address] = ff3b::10
```

# CIDR

CIDR (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`.

The `Cidr` type represents CIDR addresses. It's parameterized by the type of IP address you are working with -- either `IpAddress`, `Ipv4Address`, or `Ipv6Address`.

```scala
val x = Cidr(ip"10.123.45.67", 8)
// x: Cidr[IpAddress] = 10.123.45.67/8
val y = Cidr(ipv4"10.123.45.67", 8)
// y: Cidr[Ipv4Address] = 10.123.45.67/8
val z = Cidr(ipv6"ff3b::10", 16)
// z: Cidr[Ipv6Address] = ff3b::10/16
```

A shortand for constructing a `Cidr` is available as a method on `IpAddress`:

```scala
val x = ip"10.123.45.67" / 8
// x: Cidr[IpAddress] = 10.123.45.67/8
val y = ipv4"10.123.45.67" / 8
// y: Cidr[Ipv4Address] = 10.123.45.67/8
val z = ipv6"ff3b::10" / 16
// z: Cidr[Ipv6Address] = ff3b::10/16
```

The `Cidr` object provides mechanisms for parsing CIDR strings as well:

```scala
val parsedX = Cidr.fromString("10.123.45.67/8")
// parsedX: Option[Cidr[IpAddress]] = Some(10.123.45.67/8)
val parsedY = Cidr.fromString4("10.123.45.67/8")
// parsedY: Option[Cidr[Ipv4Address]] = Some(10.123.45.67/8)
val parsedZ = Cidr.fromString6("ff3b::10/16")
// parsedZ: Option[Cidr[Ipv6Address]] = Some(ff3b::10/16)
```

Given a `Cidr[A]`, we can ask various things about the routing prefix:

```scala
val prefixX = x.prefix
// prefixX: IpAddress = 10.0.0.0
val prefixZ = z.prefix
// prefixZ: Ipv6Address = ff3b::

val maskX = x.mask
// maskX: IpAddress = 255.0.0.0
val maskZ = z.mask
// maskZ: Ipv6Address = ffff::

val doesXContainHome = x.contains(home)
// doesXContainHome: Boolean = false
val doesXContainSuccessor = x.contains(x.address.next)
// doesXContainSuccessor: Boolean = true
val lastAddressInX = x.last
// lastAddressInX: IpAddress = 10.255.255.255
```

# Socket Addresses

A socket address is an IP address and a TCP/UDP port number. This is roughly modeled as:

```scala
case class SocketAddress[+A <: IpAddress](ip: A, port: Port)
```

Like 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.

```scala
val s = SocketAddress(ipv4"127.0.0.1", port"5555")
// s: SocketAddress[Ipv4Address] = 127.0.0.1:5555
val s1 = SocketAddress.fromString(s.toString)
// s1: Option[SocketAddress[Host]] = Some(127.0.0.1:5555)
val t = SocketAddress(ipv6"::1", port"5555")
// t: SocketAddress[Ipv6Address] = [::1]:5555
val t1 = SocketAddress.fromString6(t.toString)
// t1: Option[SocketAddress[Ipv6Address]] = Some([::1]:5555)
```

On the JVM, a `SocketAddress` can be converted to a `java.net.InetSocketAddress` via the `toInetSocketAddress` method.

```scala
val u = t.toInetSocketAddress
// u: InetSocketAddress = /[0:0:0:0:0:0:0:1]:5555
```

## Multicast Socket Addresses

Similarly, 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`:

```scala
import com.comcast.ip4s._

val s = MulticastSocketAddress(SourceSpecificMulticastJoin(ipv4"10.10.10.10", ssmipv4"232.10.11.12"), port"5555")
// s: MulticastSocketAddress[SourceSpecificMulticastJoin, Ipv4Address] = 10.10.10.10@232.10.11.12:5555
val t = MulticastSocketAddress(MulticastJoin.ssm(ip"10.10.10.10", ssmip"232.10.11.12"), port"5555")
// t: MulticastSocketAddress[MulticastJoin, IpAddress] = 10.10.10.10@232.10.11.12:5555
```

# Hostnames

The `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.

```scala
val home = Hostname.fromString("localhost")
// home: Option[Hostname] = Some(localhost)
val ls = home.map(_.labels)
// ls: Option[List[Label]] = Some(List(localhost))
val comcast = host"comcast.com"
// comcast: Hostname = comcast.com
val cs = comcast.labels
// cs: List[Label] = List(comcast, com)
```

## Hostname Resolution

On the JVM, hostnames can be resolved to IP addresses via `resolve` and `resolveAll`:

```scala
import com.comcast.ip4s._
import cats.effect.IO, cats.effect.unsafe.implicits.global

val home = host"localhost"
// home: Hostname = localhost
val homeIp = home.resolve[IO]
// homeIp: IO[IpAddress] = IO(...)
homeIp.unsafeRunSync()
// res4: IpAddress = 127.0.0.1

val homeIps = home.resolveAll[IO]
// homeIps: IO[List[IpAddress]] = IO(...)
homeIps.unsafeRunSync()
// res5: List[IpAddress] = List(127.0.0.1, ::1)
```

# Internationalized Domain Names

RFC1123 hostnames are limited to ASCII characters. The `IDN` type provides a way to represent Unicode hostnames.

```scala
val unicodeComcast = idn"comcast。com"
// unicodeComcast: IDN = comcast。com
unicodeComcast.hostname
// res6: Hostname = comcast.com

val emojiRegistrar = idn"i❤.ws"
// emojiRegistrar: IDN = i❤.ws
emojiRegistrar.hostname
// res7: Hostname = xn--i-7iq.ws
```

# Hosts

The `Host` type is a common supertype of `IpAddress`, `Hostname`, and `IDN`.

```scala
val hosts = List(ip"127.0.0.255", home, emojiRegistrar)
// hosts: List[Host] = List(127.0.0.255, localhost, i❤.ws)

import cats.syntax.all._
val hostIps = hosts.traverse(_.resolve[IO]).unsafeRunSync()
// hostIps: List[IpAddress] = List(127.0.0.255, 127.0.0.1, 132.148.137.119)
```


================================================
FILE: docs/src/guide.md
================================================
ip4s: IP Addresses for Scala & Scala.js
=======================================

This 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.

# IP Addresses

The `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.

```scala mdoc:to-string
import com.comcast.ip4s.IpAddress

val home = IpAddress.fromString("127.0.0.1")
val home6 = IpAddress.fromString("::1")
```

The `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.

Sometimes 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:

```scala mdoc:to-string
import com.comcast.ip4s.{Ipv4Address, Ipv6Address}

val explicitV4Home = Ipv4Address.fromString("127.0.0.1")
val explicitV6Home = Ipv6Address.fromString("::1")
```

Because `Ipv4Address` and `Ipv6Address` are subtypes of `IpAddress`, we can pattern match on an `IpAddress` or use the `fold` method:

```scala mdoc:to-string
import com.comcast.ip4s.{Ipv4Address, Ipv6Address}

val homeIsV4 = home.get match {
  case _: Ipv4Address => true
  case _: Ipv6Address => false
}

val home6IsV4 = home6.get.fold(_ => true, _ => false)
```

## IP Literals

In 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.

```scala mdoc:reset:to-string
import com.comcast.ip4s._

val home = ip"127.0.0.1"
val home4 = ipv4"127.0.0.1"
val home6 = ipv6"::1"
```

The `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.

## IPv6 String Formats

IPv6 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.

```scala mdoc:reset:to-string
import com.comcast.ip4s._

val home = ipv6"::7f00:1"
val homeLong = home.toUncondensedString
val homeMixed = home.toMixedString

val parsedHomeLong = Ipv6Address.fromString(homeLong)
val parsedHomeMixed = Ipv6Address.fromString(homeMixed)
```

## Ordering

IP 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).

```scala mdoc:nest:to-string
val ips = List(ipv4"10.1.1.1", ipv4"10.1.2.0", ipv4"10.1.0.0", ipv6"::1", ipv6"ff3b::")
val sorted = ips.sorted
```

## JVM Integration

When 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`.

```scala mdoc:to-string
val homeIA = ip"127.0.0.1".toInetAddress
val home4IA = ipv4"127.0.0.1".toInetAddress
val home6IA = ipv6"::1".toInetAddress
```

# Multicast

Both 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:

```scala mdoc:nest:to-string
val 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")
val multicastIps = ips.filter(_.isMulticast)
val ssmIps = ips.filter(_.isSourceSpecificMulticast)
```

## Multicast Witnesses

Often, 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:

```scala mdoc:to-string
sealed trait Multicast[A <: IpAddress] { def address: A }
sealed trait SourceSpecificMulticast[A <: IpAddress] extends Multicast[A]
```

These 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]`.

To construct instances of `Multicast[A]` and `SourceSpecificMulticast[A]`, we can use the `asMulticast` and `asSourceSpecificMulticast` methods on `IpAddress`:

```scala mdoc:nest:to-string
val multicastIps = ips.flatMap(_.asMulticast)
val ssmIps = ips.flatMap(_.asSourceSpecificMulticast)
```

It'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`:

```scala mdoc:nest:to-string
val lenient = ips.flatMap(_.asSourceSpecificMulticastLenient)
```

Additionally, the `SourceSpecificMulticast.Strict[A]` type provides the guarantee that the wrapped address is in the RFC defined source specific range.

## Multicast Literals

There 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:

|Interpolator|Description|Result Type|Example|
|------------|-----------|-----------|-------|
|`mip`|Multicast IP|`Multicast[IpAddress]`|`mip"224.10.10.10"`|
|`mipv4`|V4 Multicast IP|`Multicast[Ipv4Address]`|`mipv4"224.10.10.10"`|
|`mipv6`|V6 Multicast IP|`Multicast[Ipv6Address]`|`mipv6"ff3b::10"`|
|`ssmip`|Source Specific Multicast IP|`SourceSpecificMulticast[IpAddress]`|`ssmip"224.10.10.10"`|
|`ssmipv4`|V4 Source Specific Multicast IP|`SourceSpecificMulticast[Ipv4Address]`|`ssmipv4"224.10.10.10"`|
|`ssmipv6`|V6 Source Specific Multicast IP|`SourceSpecificMulticast[Ipv6Address]`|`ssmipv6"ff3b::10"`|

## Multicast Joins

The `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.

This is modeled roughly as:

```scala mdoc:to-string
sealed trait MulticastJoin[A <: IpAddress]
case class AnySourceMulticastJoin[A <: IpAddress](group: Multicast[A]) extends MulticastJoin[A]
case class SourceSpecificMulticastJoin[A <: IpAddress](source: A, group: SourceSpecificMulticast[A]) extends MulticastJoin[A]
```

`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]`.

To construct a `MulticastJoin`, we can use the `asm` and `ssm` methods in the `MulticastJoin` companion.

```scala mdoc:to-string
val j1 = MulticastJoin.ssm(ipv4"10.11.12.13", ssmipv4"232.1.2.3")
val j2 = MulticastJoin.ssm(ipv4"10.11.12.13", ipv4"232.1.2.3".asSourceSpecificMulticast.get)
val j3 = MulticastJoin.asm(mipv6"ff3b::10")
```

# CIDR

CIDR (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`.

The `Cidr` type represents CIDR addresses. It's parameterized by the type of IP address you are working with -- either `IpAddress`, `Ipv4Address`, or `Ipv6Address`.

```scala mdoc:nest:to-string
val x = Cidr(ip"10.123.45.67", 8)
val y = Cidr(ipv4"10.123.45.67", 8)
val z = Cidr(ipv6"ff3b::10", 16)
```

A shortand for constructing a `Cidr` is available as a method on `IpAddress`:

```scala mdoc:nest:to-string
val x = ip"10.123.45.67" / 8
val y = ipv4"10.123.45.67" / 8
val z = ipv6"ff3b::10" / 16
```

The `Cidr` object provides mechanisms for parsing CIDR strings as well:

```scala mdoc:nest:to-string
val parsedX = Cidr.fromString("10.123.45.67/8")
val parsedY = Cidr.fromString4("10.123.45.67/8")
val parsedZ = Cidr.fromString6("ff3b::10/16")
```

Given a `Cidr[A]`, we can ask various things about the routing prefix:

```scala mdoc:nest:to-string
val prefixX = x.prefix
val prefixZ = z.prefix

val maskX = x.mask
val maskZ = z.mask

val doesXContainHome = x.contains(home)
val doesXContainSuccessor = x.contains(x.address.next)
val lastAddressInX = x.last
```

# Socket Addresses

A socket address is an IP address and a TCP/UDP port number. This is roughly modeled as:

```scala
case class SocketAddress[+A <: IpAddress](ip: A, port: Port)
```

Like 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.

```scala mdoc:nest:to-string
val s = SocketAddress(ipv4"127.0.0.1", port"5555")
val s1 = SocketAddress.fromString(s.toString)
val t = SocketAddress(ipv6"::1", port"5555")
val t1 = SocketAddress.fromString6(t.toString)
```

On the JVM, a `SocketAddress` can be converted to a `java.net.InetSocketAddress` via the `toInetSocketAddress` method.

```scala mdoc:nest:to-string
val u = t.toInetSocketAddress
```

## Multicast Socket Addresses

Similarly, 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`:

```scala mdoc:reset:to-string
import com.comcast.ip4s._

val s = MulticastSocketAddress(SourceSpecificMulticastJoin(ipv4"10.10.10.10", ssmipv4"232.10.11.12"), port"5555")
val t = MulticastSocketAddress(MulticastJoin.ssm(ip"10.10.10.10", ssmip"232.10.11.12"), port"5555")
```

# Hostnames

The `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.

```scala mdoc:nest:to-string
val home = Hostname.fromString("localhost")
val ls = home.map(_.labels)
val comcast = host"comcast.com"
val cs = comcast.labels
```

## Hostname Resolution

On the JVM, hostnames can be resolved to IP addresses via `resolve` and `resolveAll`:

```scala mdoc:reset:to-string
import com.comcast.ip4s._
import cats.effect.IO, cats.effect.unsafe.implicits.global

val home = host"localhost"
val homeIp = home.resolve[IO]
homeIp.unsafeRunSync()

val homeIps = home.resolveAll[IO]
homeIps.unsafeRunSync()
```

# Internationalized Domain Names

RFC1123 hostnames are limited to ASCII characters. The `IDN` type provides a way to represent Unicode hostnames.

```scala mdoc:to-string
val unicodeComcast = idn"comcast。com"
unicodeComcast.hostname

val emojiRegistrar = idn"i❤.ws"
emojiRegistrar.hostname
```

# Hosts

The `Host` type is a common supertype of `IpAddress`, `Hostname`, and `IDN`.

```scala mdoc:to-string
val hosts = List(ip"127.0.0.255", home, emojiRegistrar)

import cats.syntax.all._
val hostIps = hosts.traverse(_.resolve[IO]).unsafeRunSync()
```


================================================
FILE: js/src/main/scala/com/comcast/ip4s/DnsPlatform.scala
================================================
/*
 * Copyright 2018 Comcast Cable Communications Management, LLC
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.comcast.ip4s

import cats.effect.kernel.Async
import cats.syntax.all._

import scala.scalajs.js
import scala.scalajs.js._
import scala.scalajs.js.annotation.JSImport

import org.typelevel.scalaccompat.annotation._

private[ip4s] trait DnsCompanionPlatform {
  def forAsync[F[_]](implicit F: Async[F]): Dns[F] = new UnsealedDns[F] {
    def resolve(hostname: Hostname): F[IpAddress] =
      F.fromPromise(F.delay(dnsPromises.lookup(hostname.toString, LookupOptions(all = false))))
        .flatMap { address =>
          IpAddress
            .fromString(address.asInstanceOf[LookupResult].address)
            .liftTo[F](new RuntimeException("Node.js returned invalid IP address"))
        }
        .adaptError {
          case ex @ js.JavaScriptException(error: js.Error) if error.message.contains("ENOTFOUND") =>
            new JavaScriptUnknownHostException(s"$hostname: Name or service not known", ex)
        }

    def resolveOption(hostname: Hostname): F[Option[IpAddress]] =
      resolve(hostname).map(_.some).recover { case _: UnknownHostException => None }

    def resolveAll(hostname: Hostname): F[List[IpAddress]] =
      F.fromPromise(F.delay(dnsPromises.lookup(hostname.toString, LookupOptions(all = true))))
        .flatMap { addresses =>
          addresses
            .asInstanceOf[js.Array[LookupResult]]
            .toList
            .traverse { address =>
              IpAddress
                .fromString(address.address)
                .liftTo[F](new RuntimeException("Node.js returned invalid IP address"))
            }
        }
        .recover {
          case js.JavaScriptException(error: js.Error) if error.message.contains("ENOTFOUND") =>
            Nil
        }

    def reverse(address: IpAddress): F[Hostname] =
      reverseAllOrError(address).flatMap(_.headOption.liftTo(new UnknownHostException(address.toString)))

    def reverseOption(address: IpAddress): F[Option[Hostname]] = reverseAll(address).map(_.headOption)

    def reverseAll(address: IpAddress): F[List[Hostname]] =
      reverseAllOrError(address).recover { case _: UnknownHostException => Nil }

    private def reverseAllOrError(address: IpAddress): F[List[Hostname]] =
      F.fromPromise(F.delay(dnsPromises.reverse(address.toString)))
        .flatMap { hostnames =>
          hostnames.toList.traverse { hostname =>
            Hostname
              .fromString(hostname)
              .liftTo[F](new RuntimeException("Node.js returned invalid hostname"))
          }
        }
        .adaptError {
          case ex @ js.JavaScriptException(error: js.Error) if error.message.contains("ENOTFOUND") =>
            new JavaScriptUnknownHostException(address.toString, ex)
        }

    def loopback: F[IpAddress] = resolve(Hostname.fromString("localhost").get)
  }
}

@js.native
@JSImport("dns", "promises")
@nowarn212("cat=unused")
private[ip4s] object dnsPromises extends js.Any {

  def lookup(hostname: String, options: LookupOptions): js.Promise[LookupResult | js.Array[LookupResult]] = js.native

  def reverse(ip: String): js.Promise[js.Array[String]] = js.native
}

private[ip4s] sealed trait LookupOptions extends js.Object
object LookupOptions {
  def apply(all: Boolean): LookupOptions = js.Dynamic.literal(all = all).asInstanceOf[LookupOptions]
}

@js.native
private[ip4s] sealed trait LookupResult extends js.Object {
  def address: String = js.native
  def family: Int = js.native
}


================================================
FILE: js/src/main/scala/com/comcast/ip4s/IpAddressPlatform.scala
================================================
/*
 * Copyright 2018 Comcast Cable Communications Management, LLC
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.comcast.ip4s

private[ip4s] trait IpAddressPlatform
private[ip4s] trait IpAddressCompanionPlatform

private[ip4s] trait Ipv4AddressPlatform extends IpAddressPlatform
private[ip4s] trait Ipv4AddressCompanionPlatform

private[ip4s] trait Ipv6AddressPlatform extends IpAddressPlatform
private[ip4s] trait Ipv6AddressCompanionPlatform


================================================
FILE: js/src/main/scala/com/comcast/ip4s/NetworkInterfacePlatform.scala
================================================
/*
 * Copyright 2018 Comcast Cable Communications Management, LLC
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.comcast.ip4s

private[ip4s] trait NetworkInterfaceCompanionPlatform


================================================
FILE: js/src/main/scala/com/comcast/ip4s/NetworkInterfacesPlatform.scala
================================================
/*
 * Copyright 2018 Comcast Cable Communications Management, LLC
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.comcast.ip4s

import cats.effect.Sync

import scala.scalajs.js
import scala.scalajs.js.annotation.JSImport

private[ip4s] trait NetworkInterfacesCompanionPlatform {

  def forSync[F[_]](implicit F: Sync[F]): NetworkInterfaces[F] =
    new NetworkInterfaces.SyncNetworkInterfaces[F] {

      def getAll: F[Map[String, NetworkInterface]] =
        F.blocking {
          val dict = osFacade.networkInterfaces()
          var result = collection.immutable.ListMap.empty[String, NetworkInterface]
          dict.keys.foreach(k => result = result + (k -> fromNetworkInterfaceInfo(k, dict(k))))
          result
        }

      private def fromNetworkInterfaceInfo(
          name: String,
          nia: js.Array[osFacade.NetworkInterfaceInfo]
      ): NetworkInterface =
        NetworkInterface(
          name,
          name,
          MacAddress.fromString(nia.head.mac).filterNot(_.toLong == 0),
          nia.view.map { ni =>
            val cidr = Cidr.fromString(ni.cidr).get
            cidr.address match {
              case v6: Ipv6Address =>
                if (ni.scopeid == 0) cidr
                else Cidr(v6.withScopeId(ni.scopeid.toString), cidr.prefixBits)
              case _ => cidr
            }
          }.toList,
          nia.head.internal,
          true
        )
    }
}

private[ip4s] object osFacade {

  @js.native
  @JSImport("os", "networkInterfaces")
  def networkInterfaces(): js.Dictionary[js.Array[NetworkInterfaceInfo]] = js.native

  @js.native
  trait NetworkInterfaceInfo extends js.Object {
    def mac: String = js.native
    def cidr: String = js.native
    def scopeid: Int = js.native
    def internal: Boolean = js.native
  }

}


================================================
FILE: js/src/main/scala/com/comcast/ip4s/SocketAddressPlatform.scala
================================================
/*
 * Copyright 2018 Comcast Cable Communications Management, LLC
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.comcast.ip4s

private[ip4s] trait SocketAddressPlatform[+A <: Host]
private[ip4s] trait SocketAddressCompanionPlatform


================================================
FILE: js/src/main/scala/com/comcast/ip4s/UnknownHostException.scala
================================================
/*
 * Copyright 2018 Comcast Cable Communications Management, LLC
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.comcast.ip4s

import java.io.IOException
import scala.scalajs.js
import scala.util.control.NoStackTrace

class UnknownHostException(message: String = null, cause: Throwable = null) extends IOException(message, cause)

private[ip4s] class JavaScriptUnknownHostException(message: String, cause: js.JavaScriptException)
    extends UnknownHostException(message, cause)
    with NoStackTrace


================================================
FILE: js/src/main/scala/com/comcast/ip4s/ip4splatform.scala
================================================
/*
 * Copyright 2018 Comcast Cable Communications Management, LLC
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.comcast.ip4s

private[comcast] trait ip4splatform


================================================
FILE: jvm-native/src/main/scala/com/comcast/ip4s/DnsPlatform.scala
================================================
/*
 * Copyright 2018 Comcast Cable Communications Management, LLC
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.comcast.ip4s

import cats.effect.kernel.Async
import cats.effect.kernel.Sync
import cats.syntax.all._

import java.net.InetAddress

private[ip4s] trait DnsCompanionPlatform {
  def forAsync[F[_]: Async]: Dns[F] = forSync // alias for cross-compiling w/ JS

  def forSync[F[_]](implicit F: Sync[F]): Dns[F] = new UnsealedDns[F] {
    def resolve(hostname: Hostname): F[IpAddress] =
      F.blocking {
        val addr = InetAddress.getByName(hostname.toString)
        IpAddress.fromBytes(addr.getAddress).get
      }

    def resolveOption(hostname: Hostname): F[Option[IpAddress]] =
      resolve(hostname).map(_.some).recover { case _: UnknownHostException => None }

    def resolveAll(hostname: Hostname): F[List[IpAddress]] =
      F.blocking {
        try {
          val addrs = InetAddress.getAllByName(hostname.toString)
          addrs.toList.flatMap(addr => IpAddress.fromBytes(addr.getAddress))
        } catch {
          case _: UnknownHostException => Nil
        }
      }

    def reverse(address: IpAddress): F[Hostname] = {
      val inetAddress = address.toInetAddress
      F.blocking {
        inetAddress.getCanonicalHostName
      } flatMap { hn =>
        (if (hn == inetAddress.getHostAddress)
           None // getCanonicalHostName returns the IP address as a string on failure
         else
           Hostname.fromString(hn))
          .liftTo[F](new UnknownHostException(address.toString))
      }
    }

    def reverseOption(address: IpAddress): F[Option[Hostname]] =
      reverse(address).map(_.some).recover { case _: UnknownHostException => None }

    def reverseAll(address: IpAddress): F[List[Hostname]] =
      reverseOption(address).map(_.toList)

    def loopback: F[IpAddress] =
      F.blocking(IpAddress.fromInetAddress(InetAddress.getByName(null)))
  }
}


================================================
FILE: jvm-native/src/main/scala/com/comcast/ip4s/IpAddressPlatform.scala
================================================
/*
 * Copyright 2018 Comcast Cable Communications Management, LLC
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.comcast.ip4s

import java.net.{InetAddress, Inet4Address, Inet6Address}

private[ip4s] trait IpAddressPlatform {

  /** Converts this address to a `java.net.InetAddress`. Note this method only exists on the JVM. */
  def toInetAddress: InetAddress
}

private[ip4s] trait IpAddressCompanionPlatform {

  /** Converts the supplied `InetAddress` to an `IpAddress`. */
  def fromInetAddress(address: InetAddress): IpAddress =
    address match {
      case v4: Inet4Address => Ipv4Address.fromInet4Address(v4)
      case v6: Inet6Address => Ipv6Address.fromInet6Address(v6)
      case _                => throw new UnsupportedOperationException(s"unsupported address type: $address")
    }
}

private[ip4s] trait Ipv4AddressPlatform extends IpAddressPlatform {
  protected val bytes: Array[Byte]

  override def toInetAddress: Inet4Address =
    InetAddress.getByAddress(bytes).asInstanceOf[Inet4Address]
}

private[ip4s] trait Ipv4AddressCompanionPlatform {

  /** Converts the supplied `Inet4Address` to an `Ipv4Address`. */
  def fromInet4Address(address: Inet4Address): Ipv4Address =
    Ipv4Address.fromBytes(address.getAddress).get
}

private[ip4s] trait Ipv6AddressPlatform extends IpAddressPlatform {
  protected val bytes: Array[Byte]

  override def toInetAddress: InetAddress =
    InetAddress.getByAddress(bytes)
}

private[ip4s] trait Ipv6AddressCompanionPlatform {

  /** Converts the supplied `Inet6Address` to an `Ipv6Address`. */
  def fromInet6Address(address: Inet6Address): Ipv6Address = {
    val scopeId = address.getScopeId
    val unscoped = Ipv6Address.fromBytes(address.getAddress).get
    if (scopeId == 0) unscoped
    else unscoped.withScopeId(scopeId.toString)
  }
}


================================================
FILE: jvm-native/src/main/scala/com/comcast/ip4s/NetworkInterfacePlatform.scala
================================================
/*
 * Copyright 2018 Comcast Cable Communications Management, LLC
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.comcast.ip4s

import java.net.{InterfaceAddress as JInterfaceAddress, NetworkInterface as JNetworkInterface}

import Ip4sCollectionCompat.*

private[ip4s] trait NetworkInterfaceCompanionPlatform {

  def fromJava(jni: JNetworkInterface): NetworkInterface =
    NetworkInterface(
      jni.getName,
      jni.getDisplayName,
      Option(jni.getHardwareAddress).flatMap(MacAddress.fromBytes),
      jni.getInterfaceAddresses.asScala.toList.map(fromJavaInterfaceAddress),
      jni.isLoopback,
      jni.isUp
    )

  private def fromJavaInterfaceAddress(ifa: JInterfaceAddress): Cidr[IpAddress] =
    Cidr(IpAddress.fromInetAddress(ifa.getAddress), ifa.getNetworkPrefixLength.toInt)
}


================================================
FILE: jvm-native/src/main/scala/com/comcast/ip4s/NetworkInterfacesPlatform.scala
================================================
/*
 * Copyright 2018 Comcast Cable Communications Management, LLC
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.comcast.ip4s

import cats.effect.Sync
import java.net.NetworkInterface as JNetworkInterface

import Ip4sCollectionCompat.*

private[ip4s] trait NetworkInterfacesCompanionPlatform {

  def forSync[F[_]](implicit F: Sync[F]): NetworkInterfaces[F] =
    new NetworkInterfaces.SyncNetworkInterfaces[F] {
      def getAll: F[Map[String, NetworkInterface]] =
        F.blocking {
          collection.immutable.ListMap.empty[String, NetworkInterface] ++
            JNetworkInterface.getNetworkInterfaces.asScala.toList.map(jni =>
              jni.getName -> NetworkInterface.fromJava(jni)
            )
        }
    }
}


================================================
FILE: jvm-native/src/main/scala/com/comcast/ip4s/SocketAddressPlatform.scala
================================================
/*
 * Copyright 2018 Comcast Cable Communications Management, LLC
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.comcast.ip4s

import cats.Applicative

import java.net.InetSocketAddress

private[ip4s] trait SocketAddressPlatform[+A <: Host] {
  val host: A
  val port: Port

  def toInetSocketAddress(implicit ev: A <:< IpAddress): InetSocketAddress =
    new InetSocketAddress(host.toInetAddress, port.value)
}

private[ip4s] trait SocketAddressCompanionPlatform {
  // bincompat
  @deprecated("resolve is now defined on SocketAddress", "3.2.1")
  def ResolveOps(self: SocketAddress[Host]): ResolveOps = new ResolveOps(self)
  @deprecated("resolve is now defined on SocketAddress", "3.2.1")
  class ResolveOps(private val self: SocketAddress[Host]) {

    /** Resolves this `SocketAddress[Hostname]` to a `SocketAddress[IpAddress]`. */
    def resolve[F[_]: Dns: Applicative]: F[SocketAddress[IpAddress]] = self.resolve
  }

  /** Converts an `InetSocketAddress` to a `SocketAddress[IpAddress]`. */
  def fromInetSocketAddress(address: InetSocketAddress): SocketAddress[IpAddress] =
    SocketAddress(IpAddress.fromInetAddress(address.getAddress), Port.fromInt(address.getPort).get)
}


================================================
FILE: jvm-native/src/main/scala/com/comcast/ip4s/ip4splatform.scala
================================================
/*
 * Copyright 2018 Comcast Cable Communications Management, LLC
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.comcast.ip4s

private[comcast] trait ip4splatform {
  type UnknownHostException = java.net.UnknownHostException
}


================================================
FILE: project/build.properties
================================================
sbt.version=1.12.5


================================================
FILE: project/plugins.sbt
================================================
addSbtPlugin("org.typelevel" % "sbt-typelevel" % "0.8.5")
addSbtPlugin("org.scala-js" % "sbt-scalajs" % "1.20.2")
addSbtPlugin("org.scala-native" % "sbt-scala-native" % "0.5.10")
addSbtPlugin("org.portable-scala" % "sbt-scala-native-crossproject" % "1.3.2")
addSbtPlugin("ch.epfl.scala" % "sbt-scalajs-bundler" % "0.21.1")
addSbtPlugin("io.github.sbt-doctest" % "sbt-doctest" % "0.12.4")
addSbtPlugin("org.scalameta" % "sbt-mdoc" % "2.8.2")

libraryDependencySchemes += "com.lihaoyi" %% "geny" % VersionScheme.Always


================================================
FILE: shared/src/main/scala/com/comcast/ip4s/Cidr.scala
================================================
/*
 * Copyright 2018 Comcast Cable Communications Management, LLC
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.comcast.ip4s

import scala.util.Try
import scala.util.hashing.MurmurHash3

import cats.{Order, Show}
import com.comcast.ip4s.Cidr.Strict

/** Classless Inter-Domain Routing address, which represents an IP address and its routing prefix.
  *
  * @param address
  *   IP address for which this CIDR refers to
  * @param prefixBits
  *   number of leading 1s in the routing mask
  */
sealed class Cidr[+A <: IpAddress] protected (val address: A, val prefixBits: Int) extends Product with Serializable {
  def copy[AA >: A <: IpAddress](address: AA = this.address, prefixBits: Int = this.prefixBits): Cidr[AA] =
    Cidr[AA](address, prefixBits)

  /** Returns a normalized cidr range, where the address is truncated to the prefix, so that the returned range
    * is (and prints as) a spec-valid cidr range, with no bits outside the routing mask set.
    *
    * @return a normalized cidr range
    *
    * @example {{{
    * scala> val raw = Cidr(ipv4"10.11.12.13", 8)
    * raw: Cidr[Ipv4Address] = 10.11.12.13/8
    * scala> raw.normalized
    * res0: Cidr.Strict[Ipv4Address] = 10.0.0.0/8
    * }}}
    */
  def normalized: Strict[A] = Cidr.Strict(this)

  /** Returns the routing mask.
    *
    * @example {{{
    * scala> Cidr(ipv4"10.11.12.13", 8).mask
    * res0: Ipv4Address = 255.0.0.0
    * scala> Cidr(ipv6"2001:db8:abcd:12::", 96).mask
    * res1: Ipv6Address = ffff:ffff:ffff:ffff:ffff:ffff::
    * }}}
    */
  def mask: A = address.transform(_ => Ipv4Address.mask(prefixBits), _ => Ipv6Address.mask(prefixBits))

  /** Returns the routing prefix.
    *
    * Note: the routing prefix also serves as the first address in the range described by this CIDR.
    *
    * @example {{{
    * scala> Cidr(ipv4"10.11.12.13", 8).prefix
    * res0: Ipv4Address = 10.0.0.0
    * scala> Cidr(ipv6"2001:db8:abcd:12::", 96).prefix
    * res1: Ipv6Address = 2001:db8:abcd:12::
    * scala> Cidr(ipv6"2001:db8:abcd:12::", 32).prefix
    * res2: Ipv6Address = 2001:db8::
    * }}}
    */
  def prefix: A =
    address.transform(_.masked(Ipv4Address.mask(prefixBits)), _.masked(Ipv6Address.mask(prefixBits)))

  /** Returns the last address in the range described by this CIDR.
    *
    * @example {{{
    * scala> Cidr(ipv4"10.11.12.13", 8).last
    * res0: Ipv4Address = 10.255.255.255
    * scala> Cidr(ipv6"2001:db8:abcd:12::", 96).last
    * res1: Ipv6Address = 2001:db8:abcd:12::ffff:ffff
    * scala> Cidr(ipv6"2001:db8:abcd:12::", 32).last
    * res2: Ipv6Address = 2001:db8:ffff:ffff:ffff:ffff:ffff:ffff
    * }}}
    */
  def last: A =
    address.transform(_.maskedLast(Ipv4Address.mask(prefixBits)), _.maskedLast(Ipv6Address.mask(prefixBits)))

  /** Returns the number of addresses in the range described by this CIDR.
    */
  def totalIps: BigInt = {
    BigInt(1) << (address.fold(_ => 32, _ => 128) - prefixBits)
  }

  /** Returns a predicate which tests if the supplied address is in the range described by this CIDR.
    *
    * @example {{{
    * scala> Cidr(ipv4"10.11.12.13", 8).contains(ipv4"10.100.100.100")
    * res0: Boolean = true
    * scala> Cidr(ipv4"10.11.12.13", 8).contains(ipv4"11.100.100.100")
    * res1: Boolean = false
    * scala> val x = Cidr(ipv6"2001:db8:abcd:12::", 96).contains
    * scala> x(ipv6"2001:db8:abcd:12::5")
    * res2: Boolean = true
    * scala> x(ipv6"2001:db8::")
    * res3: Boolean = false
    * }}}
    */
  def contains[AA >: A <: IpAddress]: AA => Boolean = {
    val start = prefix
    val end = last
    a => a >= start && a <= end
  }

  /** Returns an iterator over all addresses in the range described by this CIDR.
    * @return
    */
  def addresses: Iterator[IpAddress] = {
    val start: IpAddress = prefix
    val end: IpAddress = last

    Iterator.iterate(start)(_.next).takeWhile(_ <= end)
  }

  override def toString: String = s"$address/$prefixBits"
  override def hashCode: Int = MurmurHash3.productHash(this, productPrefix.hashCode)
  override def equals(other: Any): Boolean =
    other match {
      case that: Cidr[?] => address == that.address && prefixBits == that.prefixBits
      case _             => false
    }
  override def canEqual(other: Any): Boolean = other.isInstanceOf[Cidr[?]]
  override def productArity: Int = 2
  override def productElement(n: Int): Any =
    n match {
      case 0 => address
      case 1 => prefixBits
      case _ => throw new IndexOutOfBoundsException
    }
}

object Cidr {

  /** A normalized cidr range, of which the address is identical to the prefix.
    *
    * This means the address will never have any bits set outside the prefix. For example, a range
    * such as 192.168.0.1/31 is not allowed.
    */
  final class Strict[+A <: IpAddress] private (address: A, prefixBits: Int) extends Cidr[A](address, prefixBits) {
    override def normalized: this.type = this
    override def prefix = address
  }

  object Strict {
    def apply[A <: IpAddress](cidr: Cidr[A]): Cidr.Strict[A] = cidr match {
      case already: Strict[?] => already.asInstanceOf[Strict[A]]
      case _                  => new Cidr.Strict(cidr.prefix, cidr.prefixBits)
    }
  }

  /** Constructs a CIDR from the supplied IP address and prefix bit count. Note if `prefixBits` is less than 0, the
    * built `Cidr` will have `prefixBits` set to 0. Similarly, if `prefixBits` is greater than the bit length of the
    * address, it will be set to the bit length of the address.
    */
  def apply[A <: IpAddress](address: A, prefixBits: Int): Cidr[A] = {
    val maxPrefixBits = address.fold(_ => 32, _ => 128)
    val b = if (prefixBits < 0) 0 else if (prefixBits > maxPrefixBits) maxPrefixBits else prefixBits
    new Cidr(address, b)
  }

  /** Constructs a CIDR from the supplied IP address and netmask. The number of leading 1 bits in the netmask are used
    * as the prefix bits for the CIDR.
    */
  def fromIpAndMask[A <: IpAddress](address: A, mask: A): Cidr[A] =
    address / mask.prefixBits

  /** Constructs a CIDR from a string of the form `ip/prefixBits`. */
  def fromString(value: String): Option[Cidr[IpAddress]] = fromStringGeneral(value, IpAddress.fromString)

  /** Constructs a CIDR from a string of the form `ipv4/prefixBits`. */
  def fromString4(value: String): Option[Cidr[Ipv4Address]] = fromStringGeneral(value, Ipv4Address.fromString)

  /** Constructs a CIDR from a string of the form `ipv6/prefixBits`. */
  def fromString6(value: String): Option[Cidr[Ipv6Address]] = fromStringGeneral(value, Ipv6Address.fromString)

  private val CidrPattern = """([^/]+)/(\d+)""".r
  private def fromStringGeneral[A <: IpAddress](value: String, parseAddress: String => Option[A]): Option[Cidr[A]] =
    value match {
      case CidrPattern(addrStr, prefixBitsStr) =>
        for {
          addr <- parseAddress(addrStr)
          maxPrefixBits = addr.fold(_ => 32, _ => 128)
          prefixBits <- Try(prefixBitsStr.toInt).toOption if prefixBits >= 0 && prefixBits <= maxPrefixBits
        } yield Cidr(addr, prefixBits)
      case _ => None
    }

  implicit def order[A <: IpAddress]: Order[Cidr[A]] = Order.fromOrdering(Cidr.ordering[A])
  implicit def ordering[A <: IpAddress]: Ordering[Cidr[A]] = Ordering.by(x => (x.address, x.prefixBits))
  implicit def show[A <: IpAddress]: Show[Cidr[A]] = Show.fromToString[Cidr[A]]

  def unapply[A <: IpAddress](c: Cidr[A]): Option[(A, Int)] = Some((c.address, c.prefixBits))
}


================================================
FILE: shared/src/main/scala/com/comcast/ip4s/Dns.scala
================================================
/*
 * Copyright 2018 Comcast Cable Communications Management, LLC
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.comcast.ip4s

import cats.~>
import cats.Functor
import cats.data.EitherT
import cats.data.Kleisli
import cats.effect.IO

/** Capability for an effect `F[_]` which can do DNS lookups.
  *
  * An instance is available for any effect which has a `Sync` instance on JVM and `Async` on Node.js.
  */
sealed trait Dns[F[_]] { outer =>

  /** Resolves the supplied hostname to an ip address using the platform DNS resolver.
    *
    * If the hostname cannot be resolved, the effect fails with an `UnknownHostException`.
    */
  def resolve(hostname: Hostname): F[IpAddress]

  /** Resolves the supplied hostname to an ip address using the platform DNS resolver.
    *
    * If the hostname cannot be resolved, a `None` is returned.
    */
  def resolveOption(hostname: Hostname): F[Option[IpAddress]]

  /** Resolves the supplied hostname to all ip addresses known to the platform DNS resolver.
    *
    * If the hostname cannot be resolved, an empty list is returned.
    */
  def resolveAll(hostname: Hostname): F[List[IpAddress]]

  /** Reverses the supplied address to a hostname using the platform DNS resolver.
    *
    * If the address cannot be reversed, the effect fails with an `UnknownHostException`.
    */
  def reverse(address: IpAddress): F[Hostname]

  /** Reverses the supplied address to a hostname using the platform DNS resolver.
    *
    * If the address cannot be reversed, a `None` is returned.
    */
  def reverseOption(address: IpAddress): F[Option[Hostname]]

  /** Reverses the supplied address to all hostnames known to the platform DNS resolver.
    *
    * If the address cannot be reversed, an empty list is returned.
    */
  def reverseAll(address: IpAddress): F[List[Hostname]]

  /** Gets an IP address representing the loopback interface. */
  def loopback: F[IpAddress]

  /** Translates effect type from `F` to `G` using the supplied `FunctionK` */
  final def mapK[G[_]](fk: F ~> G): Dns[G] = new Dns[G] {
    def resolve(hostname: Hostname) = fk(outer.resolve(hostname))
    def resolveOption(hostname: Hostname) = fk(outer.resolveOption(hostname))
    def resolveAll(hostname: Hostname) = fk(outer.resolveAll(hostname))
    def reverse(address: IpAddress) = fk(outer.reverse(address))
    def reverseOption(address: IpAddress) = fk(outer.reverseOption(address))
    def reverseAll(address: IpAddress) = fk(outer.reverseAll(address))
    def loopback = fk(outer.loopback)
  }
}

private[ip4s] trait UnsealedDns[F[_]] extends Dns[F]

object Dns extends DnsCompanionPlatform {
  def apply[F[_]](implicit F: Dns[F]): F.type = F

  implicit def forIO: Dns[IO] = forAsync[IO]

  implicit def forEitherT[F[_]: Functor, A](implicit dns: Dns[F]): Dns[EitherT[F, A, *]] =
    dns.mapK(EitherT.liftK)

  implicit def forKleisli[F[_], A](implicit dns: Dns[F]): Dns[Kleisli[F, A, *]] =
    dns.mapK(Kleisli.liftK)

}


================================================
FILE: shared/src/main/scala/com/comcast/ip4s/GenSocketAddress.scala
================================================
/*
 * Copyright 2018 Comcast Cable Communications Management, LLC
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.comcast.ip4s

/** Supertype for types that specify address information for opening a socket.
  *
  * There are two built-in subtypes:
  *  - [[SocketAddress]] - which specifies address and port info for IPv4/IPv6 sockets
  *  - [[UnixSocketAddress]] - which specifies the path to a unix domain socket
  *
  * This trait is left open for extension, allowing other address types to be defined.
  * When using this trait, pattern match on the supported subtypes.
  */
abstract class GenSocketAddress private[ip4s] () {

  /** Downcasts this address to a `SocketAddress[IpAddress]`.
    *
    * @throws UnsupportedOperationException if this address is not a `SocketAddress[IpAddress]`
    */
  def asIpUnsafe: SocketAddress[IpAddress] = this match {
    case addr @ SocketAddress(_: IpAddress, _) => addr.asInstanceOf[SocketAddress[IpAddress]]
    case other                                 =>
      throw new UnsupportedOperationException(s"asIpUnsafe only supported on SocketAddress[IpAddress]: $other")
  }
}


================================================
FILE: shared/src/main/scala/com/comcast/ip4s/Host.scala
================================================
/*
 * Copyright 2018 Comcast Cable Communications Management, LLC
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.comcast.ip4s

import cats.{Applicative, ApplicativeThrow, Order, Show}
import cats.syntax.all._

import org.typelevel.idna4s.core.bootstring.Bootstring
import org.typelevel.idna4s.core.bootstring.BootstringParams.PunycodeParams

import scala.util.hashing.MurmurHash3

// annoying bincompat shim
private[ip4s] trait HostPlatform

/** ADT representing either an `IpAddress`, `Hostname`, or `IDN`. */
sealed trait Host extends HostPlatform with Ordered[Host] {

  def compare(that: Host): Int =
    this match {
      case x: Ipv4Address =>
        that match {
          case y: Ipv4Address => IpAddress.compareBytes(x, y)
          case y: Ipv6Address => IpAddress.compareBytes(x.toCompatV6, y)
          case _              => -1
        }
      case x: Ipv6Address =>
        that match {
          case y: Ipv4Address => IpAddress.compareBytes(x, y.toCompatV6)
          case y: Ipv6Address => IpAddress.compareBytes(x, y)
          case _              => -1
        }
      case x: Hostname =>
        that match {
          case _: Ipv4Address => 1
          case _: Ipv6Address => 1
          case y: Hostname    => x.toString.compare(y.toString)
          case y: IDN         => x.toString.compare(y.hostname.toString)
        }
      case x: IDN =>
        that match {
          case _: Ipv4Address => 1
          case _: Ipv6Address => 1
          case y: Hostname    => x.hostname.toString.compare(y.toString)
          case y: IDN         => x.hostname.toString.compare(y.hostname.toString)
        }
    }

  /** Resolves this host to an ip address using the platform DNS resolver.
    *
    * If the host cannot be resolved, the effect fails with a `com.comcast.ip4s.UnknownHostException`.
    */
  def resolve[F[_]: Dns: Applicative]: F[IpAddress] =
    this match {
      case ip: IpAddress      => Applicative[F].pure(ip)
      case hostname: Hostname => Dns[F].resolve(hostname)
      case idn: IDN           => Dns[F].resolve(idn.hostname)
    }

  /** Resolves this host to an ip address using the platform DNS resolver.
    *
    * If the host cannot be resolved, a `None` is returned.
    */
  def resolveOption[F[_]: Dns: ApplicativeThrow]: F[Option[IpAddress]] =
    resolve[F].map(Some(_): Option[IpAddress]).recover { case _: UnknownHostException => None }

  /** Resolves this host to all ip addresses known to the platform DNS resolver.
    *
    * If the host cannot be resolved, an empty list is returned.
    */
  def resolveAll[F[_]: Dns: Applicative]: F[List[IpAddress]] =
    this match {
      case ip: IpAddress      => Applicative[F].pure(List(ip))
      case hostname: Hostname => Dns[F].resolveAll(hostname)
      case idn: IDN           => Dns[F].resolveAll(idn.hostname)
    }
}

object Host {
  def fromString(string: String): Option[Host] =
    IpAddress.fromString(string) orElse Hostname.fromString(string) orElse IDN.fromString(string)

  implicit def show: Show[Host] = Show.fromToString[Host]
  implicit def order: Order[Host] = Order.fromComparable[Host]
  implicit def ordering: Ordering[Host] = _.compare(_)
}

/** RFC1123 compliant hostname.
  *
  * A hostname contains one or more labels, where each 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 exceed 63 characters in length. Labels are separated by periods and
  * the overall hostname must not exceed 253 characters in length.
  */
final class Hostname private (val labels: List[Hostname.Label], override val toString: String) extends Host {

  /** Converts this hostname to lower case. */
  def normalized: Hostname =
    new Hostname(labels.map(l => new Hostname.Label(l.toString.toLowerCase)), toString.toLowerCase)

  override def hashCode: Int = MurmurHash3.stringHash(toString, "Hostname".hashCode)
  override def equals(other: Any): Boolean =
    other match {
      case that: Hostname => toString == that.toString
      case _              => false
    }
}

object Hostname {

  /** Label component of a hostname.
    *
    * 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
    * exceed 63 characters in length.
    */
  final class Label private[Hostname] (override val toString: String) extends Serializable with Ordered[Label] {
    def compare(that: Label): Int = toString.compare(that.toString)
    override def hashCode: Int = MurmurHash3.stringHash(toString, "Label".hashCode)
    override def equals(other: Any): Boolean =
      other match {
        case that: Label => toString == that.toString
        case _           => false
      }
  }

  private val Pattern =
    """[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

  /** Constructs a `Hostname` from a string. */
  def fromString(value: String): Option[Hostname] =
    value.size match {
      case 0            => None
      case i if i > 253 => None
      case _            =>
        value match {
          case Pattern(_*) =>
            val labels = value
              .split('.')
              .iterator
              .map(new Label(_))
              .toList
            if (labels.isEmpty) None else Option(new Hostname(labels, value))
          case _ => None
        }
    }
}

sealed trait IpVersion
object IpVersion {
  case object V4 extends IpVersion
  case object V6 extends IpVersion
}

/** Immutable and safe representation of an IP address, either V4 or V6.
  *
  * ===Construction===
  *
  * `IpAddress` instances are constructed in a few different ways:
  *   - via `IpAddress("127.0.0.1")`, which parses a string representation of the IP and returns an `Option[IpAddress]`
  *   - via `IpAddress.fromBytes(arr)`, which returns an IP address if the supplied array is either exactly 4 bytes or
  *     exactly 16 bytes
  *   - via literal syntax like `ip"127.0.0.1"`, which returns an `IpAddress` and fails to *compile* if the IP is
  *     invalid.
  *
  * ===Type Hierarchy===
  *
  * There are two subtypes of `IpAddress` -- [[Ipv4Address]] and [[Ipv6Address]]. Each of these subtypes have a richer
  * API than `IpAddress` and it is often useful to use those types directly, for example if your use case requires a V6
  * address. It is safe to pattern match on `IpAddress` to access `Ipv4Address` or `Ipv6Address` directly, or
  * alternatively, you can use [[fold]].
  *
  * ===JVM Specific API===
  *
  * If using `IpAddress` on the JVM, you can call `toInetAddress` to convert the address to a `java.net.InetAddress`,
  * for use with networking libraries. This method does not exist on the Scala.js version.
  */
sealed abstract class IpAddress extends IpAddressPlatform with Host with Serializable {
  protected val bytes: Array[Byte]

  /** Converts this address to a network order byte array of either 4 or 16 bytes. */
  def toBytes: Array[Byte] = bytes.clone

  /** Converts this address to a value of type `A` using the supplied functions. */
  def fold[A](v4: Ipv4Address => A, v6: Ipv6Address => A): A

  /** Maps a type-preserving function across this IP address. */
  def transform(v4: Ipv4Address => Ipv4Address, v6: Ipv6Address => Ipv6Address): this.type

  /** Returns true if this address is an IPv4 address. */
  def isV4: Boolean =
    fold(_ => true, _ => false)

  /** Returns true if this address is an IPv6 address. */
  def isV6: Boolean =
    fold(_ => false, _ => true)

  /** Returns true if this address is either 0.0.0.0 or ::. */
  def isWildcard: Boolean =
    fold(_ == Ipv4Address.Wildcard, _ == Ipv6Address.Wildcard)

  /** Returns true if this address is in the multicast range. */
  def isMulticast: Boolean

  /** Converts this address to a multicast address, as long as it is in the multicast address range. */
  def asMulticast: Option[Multicast[this.type]] = Multicast.fromIpAddress(this)

  /** Returns true if this address is in the source specific multicast range. */
  def isSourceSpecificMulticast: Boolean

  /** Converts this address to a source specific multicast address, as long as it is in the source specific multicast
    * address range.
    */
  def asSourceSpecificMulticast: Option[SourceSpecificMulticast.Strict[this.type]] =
    SourceSpecificMulticast.fromIpAddress(this)

  /** Like `asSourceSpecificMulticast` but allows multicast addresses outside the source specific range. */
  def asSourceSpecificMulticastLenient: Option[SourceSpecificMulticast[this.type]] =
    SourceSpecificMulticast.fromIpAddressLenient(this)

  /** Returns true if this address is a loopback address. */
  def isLoopback: Boolean

  /** Returns true if this address is a link local address. */
  def isLinkLocal: Boolean

  /** Returns true if this address is in a private range. */
  def isPrivate: Boolean

  /** Narrows this address to an Ipv4Address if that is the underlying type. */
  def asIpv4: Option[Ipv4Address] = collapseMappedV4.fold(Some(_), _ => None)

  /** Narrows this address to an Ipv6Address if that is the underlying type. */
  def asIpv6: Option[Ipv6Address] = fold(_ => None, Some(_))

  /** Returns the version of this address. */
  def version: IpVersion = fold(_ => IpVersion.V4, _ => IpVersion.V6)

  /** Returns true if this address is a V6 address containing a mapped V4 address. */
  def isMappedV4: Boolean = fold(_ => false, Ipv6Address.MappedV4Block.contains)

  /** If this address is an IPv4 mapped IPv6 address, converts to an IPv4 address, otherwise returns this. */
  def collapseMappedV4: IpAddress =
    fold(identity, v6 => if (v6.isMappedV4) IpAddress.fromBytes(v6.toBytes.takeRight(4)).get else v6)

  /** Constructs a [[Cidr]] address from this address. */
  def /(prefixBits: Int): Cidr[this.type] = Cidr(this, prefixBits)

  /** Returns the number of leading ones in this address. When this address is a netmask, the result can be used to
    * create a CIDR.
    *
    * @example {{{
    * scala> val address = ipv4"192.168.1.25"
    * scala> val netmask = ipv4"255.255.0.0"
    * scala> (address / netmask.prefixBits): Cidr[Ipv4Address]
    * res0: Cidr[Ipv4Address] = 192.168.1.25/16
    * }}}
    */
  def prefixBits: Int = {
    import java.lang.Long.numberOfLeadingZeros
    fold(
      m => numberOfLeadingZeros(~(m.toLong | 0xffffffff00000000L)) - 32,
      m => {
        val upper = (m.toBigInt >> 64).toLong
        val upperNum = numberOfLeadingZeros(~upper)
        if (upperNum == 64) upperNum + numberOfLeadingZeros(~(m.toBigInt.toLong)) else upperNum
      }
    )
  }

  /** Gets the IP address after this address, with overflow from the maximum value to the minimum value. */
  def next: IpAddress

  /** Gets the IP address before this address, with underflow from minimum value to the maximum value. */
  def previous: IpAddress

  /** Returns the default/common representation of this address. */
  protected[this] def toDefaultString: String

  /** Converts this address to a string form that is compatible for use in a URI per RFC3986 (namely, IPv6 addresses are
    * rendered in condensed form and surrounded by brackets).
    */
  def toUriString: String

  /** Returns the default/common representation of this address. For IPv4
    * addresses, returns the dotted decimal representation of the address. For
    * IPv6 addresses, returns the condensed string representation of the array
    * per RFC5952.
    *
    * This method returns the same result as [[`toUriString`]] except that IPv6
    * addresses are not surrounded by brackets.
    */
  final override def toString: String = toDefaultString

  override def equals(other: Any): Boolean =
    other match {
      case that: IpAddress => java.util.Arrays.equals(bytes, that.bytes)
      case _               => false
    }

  override def hashCode: Int = java.util.Arrays.hashCode(bytes)
}

object IpAddress extends IpAddressCompanionPlatform {

  /** Parses an IP address from a string, either in dotted decimal notation or in RFC4291 notation. */
  def fromString(value: String): Option[IpAddress] =
    Ipv4Address.fromString(value) orElse Ipv6Address.fromString(value)

  /** Constructs an IP address from either a 4-element byte array or a 16-element byte array. Any other size array
    * results in a `None`.
    */
  def fromBytes(bytes: Array[Byte]): Option[IpAddress] =
    Ipv4Address.fromBytes(bytes) orElse Ipv6Address.fromBytes(bytes)

  /** Gets an IP address repesenting the loopback interface. */
  def loopback[F[_]](implicit F: Dns[F]): F[IpAddress] =
    F.loopback

  private[ip4s] def compareBytes(x: IpAddress, y: IpAddress): Int = {
    var i, result = 0
    val xb = x.bytes
    val yb = y.bytes
    val sz = xb.length
    while (i < sz && result == 0) {
      result = Integer.compare(xb(i) & 0xff, yb(i) & 0xff)
      i += 1
    }
    result
  }

  implicit def order[A <: IpAddress]: Order[A] = Order.fromOrdering(IpAddress.ordering[A])
  implicit def ordering[A <: IpAddress]: Ordering[A] = _.compare(_)
}

/** Representation of an IPv4 address that works on both the JVM and Scala.js. */
final class Ipv4Address private (protected val bytes: Array[Byte]) extends IpAddress with Ipv4AddressPlatform {
  override def fold[A](v4: Ipv4Address => A, v6: Ipv6Address => A): A = v4(this)

  override def transform(v4: Ipv4Address => Ipv4Address, v6: Ipv6Address => Ipv6Address): this.type =
    v4(this).asInstanceOf[this.type]

  /** Returns the dotted decimal representation of this address. */
  override protected[this] def toDefaultString: String =
    s"${bytes(0) & 0xff}.${bytes(1) & 0xff}.${bytes(2) & 0xff}.${bytes(3) & 0xff}"

  override def toUriString: String = toDefaultString

  /** Gets the IPv4 address after this address, with overflow from `255.255.255.255` to `0.0.0.0`. */
  override def next: Ipv4Address = Ipv4Address.fromLong(toLong + 1)

  /** Gets the IPv4 address before this address, with underflow from `0.0.0.0` to `255.255.255.255`. */
  override def previous: Ipv4Address = Ipv4Address.fromLong(toLong - 1)

  /** Converts this address to a 32-bit unsigned integer. */
  def toLong: Long = {
    val bs = bytes
    var result = 0L
    for (i <- 0 until bs.size) {
      result = (result << 8) | (0x0ff & bs(i))
    }
    result
  }

  override def isMulticast: Boolean =
    this >= Ipv4Address.MulticastRangeStart && this <= Ipv4Address.MulticastRangeEnd

  override def isSourceSpecificMulticast: Boolean =
    this >= Ipv4Address.SourceSpecificMulticastRangeStart && this <= Ipv4Address.SourceSpecificMulticastRangeEnd

  override def isLoopback: Boolean =
    Ipv4Address.Classes.Loopback.contains(this)

  override def isLinkLocal: Boolean =
    Ipv4Address.Classes.LinkLocal.contains(this)

  override def isPrivate: Boolean =
    Ipv4Address.Classes.Private.A.contains(this) ||
      Ipv4Address.Classes.Private.B.contains(this) ||
      Ipv4Address.Classes.Private.C.contains(this)

  /** Converts this V4 address to a compat V6 address, where the first 12 bytes are all zero and the last 4 bytes
    * contain the bytes of this V4 address.
    */
  def toCompatV6: Ipv6Address = {
    val compat = new Array[Byte](16)
    compat(12) = bytes(0)
    compat(13) = bytes(1)
    compat(14) = bytes(2)
    compat(15) = bytes(3)
    Ipv6Address.fromBytes(compat).get
  }

  /** Converts this V4 address to a mapped V6 address, where the first 10 bytes are all zero, the next two bytes are
    * `ff`, and the last 4 bytes contain the bytes of this V4 address.
    */
  def toMappedV6: Ipv6Address = {
    val mapped = new Array[Byte](16)
    mapped(10) = 255.toByte
    mapped(11) = 255.toByte
    mapped(12) = bytes(0)
    mapped(13) = bytes(1)
    mapped(14) = bytes(2)
    mapped(15) = bytes(3)
    Ipv6Address.fromBytes(mapped).get
  }

  /** Applies the supplied mask to this address.
    *
    * @example {{{
    * scala> ipv4"192.168.29.1".masked(ipv4"255.255.0.0")
    * res0: Ipv4Address = 192.168.0.0
    * }}}
    */
  def masked(mask: Ipv4Address): Ipv4Address =
    Ipv4Address.fromLong(toLong & mask.toLong)

  /** Computes the last address in the network identified by applying the supplied mask to this address.
    *
    * @example {{{
    * scala> ipv4"192.168.29.1".maskedLast(ipv4"255.255.0.0")
    * res0: Ipv4Address = 192.168.255.255
    * }}}
    */
  def maskedLast(mask: Ipv4Address): Ipv4Address =
    Ipv4Address.fromLong(toLong & mask.toLong | ~mask.toLong)
}

object Ipv4Address extends Ipv4AddressCompanionPlatform {

  /** Wildcard IPv4 address - 0.0.0.0 */
  val Wildcard: Ipv4Address = fromBytes(0, 0, 0, 0)

  /** First IP address in the IPv4 multicast range. */
  val MulticastRangeStart: Ipv4Address = fromBytes(224, 0, 0, 0)

  /** Last IP address in the IPv4 multicast range. */
  val MulticastRangeEnd: Ipv4Address = fromBytes(239, 255, 255, 255)

  /** First IP address in the IPv4 source specific multicast range. */
  val SourceSpecificMulticastRangeStart: Ipv4Address = fromBytes(232, 0, 0, 0)

  /** Last IP address in the IPv4 source specific multicast range. */
  val SourceSpecificMulticastRangeEnd: Ipv4Address =
    fromBytes(232, 255, 255, 255)

  /** IPv4 address classes represented as CIDRs. */
  object Classes {

    /** Class A: 0.0.0.0 - 127.255.255.255 */
    val A: Cidr[Ipv4Address] = Cidr(fromBytes(0, 0, 0, 0), 1)

    /** Class B: 128.0.0.0 - 191.255.255.255 */
    val B: Cidr[Ipv4Address] = Cidr(fromBytes(128, 0, 0, 0), 2)

    /** Class C: 192.0.0.0 - 223.255.255.255 */
    val C: Cidr[Ipv4Address] = Cidr(fromBytes(192, 0, 0, 0), 3)

    /** Class D: 224.0.0.0 - 239.255.255.255 */
    val D: Cidr[Ipv4Address] = Cidr(fromBytes(224, 0, 0, 0), 4)

    /** Class E: 240.0.0.0 - 255.255.255.255 */
    val E: Cidr[Ipv4Address] = Cidr(fromBytes(240, 0, 0, 0), 5)

    /** Private address ranges. */
    object Private {

      /** Class A: 10.0.0.0 - 10.255.255.255 */
      val A: Cidr[Ipv4Address] = Cidr(fromBytes(10, 0, 0, 0), 8)

      /** Class B: 172.16.0.0 - 172.31.255.255 */
      val B: Cidr[Ipv4Address] = Cidr(fromBytes(172, 16, 0, 0), 12)

      /** Class A: 192.168.0.0 - 192.168.255.255 */
      val C: Cidr[Ipv4Address] = Cidr(fromBytes(192, 168, 0, 0), 16)
    }

    /** Loopback: 127.0.0.0 - 127.255.255.255. */
    val Loopback: Cidr[Ipv4Address] = Cidr(fromBytes(127, 0, 0, 0), 8)

    /** Link local: 169.254.0.0 - 169.254.255.255. */
    val LinkLocal: Cidr[Ipv4Address] = Cidr(fromBytes(169, 254, 0, 0), 16)
  }

  /** Parses an IPv4 address from a dotted-decimal string, returning `None` if the string is not a valid IPv4 address.
    */
  def fromString(value: String): Option[Ipv4Address] = {
    val trimmed = value.trim
    val fields = trimmed.split('.')
    if (fields.length == 4) {
      val bytes = new Array[Byte](4)
      var idx = 0
      var result: Option[Ipv4Address] = null
      while (idx < bytes.length && (result eq null)) {
        try {
          val value = fields(idx).toInt
          if (value >= 0 && value < 256) bytes(idx) = value.toByte
          else result = None
          idx += 1
        } catch {
          case _: NumberFormatException =>
            result = None
        }
      }
      if (result eq null) Some(unsafeFromBytes(bytes)) else result
    } else None
  }

  /** Constructs an IPv4 address from a 4-element byte array. Returns `Some` when array is exactly 4-bytes and `None`
    * otherwise.
    */
  def fromBytes(bytes: Array[Byte]): Option[Ipv4Address] =
    if (bytes.size == 4) Some(unsafeFromBytes(bytes.clone))
    else None

  private def unsafeFromBytes(bytes: Array[Byte]): Ipv4Address =
    new Ipv4Address(bytes)

  /** Constructs an address from the specified 4 bytes.
    *
    * Each byte is represented as an `Int` to avoid having to manually call `.toByte` on each value -- the `toByte` call
    * is done inside this function.
    */
  def fromBytes(a: Int, b: Int, c: Int, d: Int): Ipv4Address = {
    val bytes = new Array[Byte](4)
    bytes(0) = a.toByte
    bytes(1) = b.toByte
    bytes(2) = c.toByte
    bytes(3) = d.toByte
    unsafeFromBytes(bytes)
  }

  /** Constructs an IPv4 address from a `Long`, using the lower 32-bits. */
  def fromLong(value: Long): Ipv4Address = {
    val bytes = new Array[Byte](4)
    var rem = value
    for (i <- 3 to 0 by -1) {
      bytes(i) = (rem & 0x0ff).toByte
      rem = rem >> 8
    }
    unsafeFromBytes(bytes)
  }

  /** Computes a mask by setting the first / left-most `n` bits high.
    *
    * @example {{{
    * scala> Ipv4Address.mask(16)
    * res0: Ipv4Address = 255.255.0.0
    * }}}
    */
  def mask(bits: Int): Ipv4Address = {
    val b = if (bits < 0) 0 else if (bits > 32) 32 else bits
    Ipv4Address.fromLong(if (b == 32) -1L else ~(-1 >>> b).toLong)
  }
}

/** Representation of an IPv6 address that works on both the JVM and Scala.js. */
final class Ipv6Address private (protected val bytes: Array[Byte], val scopeId: Option[String])
    extends IpAddress
    with Ipv6AddressPlatform {
  override def fold[A](v4: Ipv4Address => A, v6: Ipv6Address => A): A = v6(this)
  override def transform(v4: Ipv4Address => Ipv4Address, v6: Ipv6Address => Ipv6Address): this.type =
    v6(this).asInstanceOf[this.type]

  /** Returns the condensed string representation of the array per RFC5952. */
  override protected[this] def toDefaultString: String = {
    val fields: Array[Int] = new Array[Int](8)
    var condensing = false
    var condensedStart, maxCondensedStart = -1
    var condensedLength, maxCondensedLength = 0
    var idx = 0
    while (idx < 8) {
      val j = 2 * idx
      val field = ((0x0ff & bytes(j)) << 8) | (0x0ff & bytes(j + 1))
      fields(idx) = field
      if (field == 0) {
        if (!condensing) {
          condensing = true
          condensedStart = idx
          condensedLength = 0
        }
        condensedLength += 1
      } else {
        condensing = false
      }
      if (condensedLength > maxCondensedLength) {
        maxCondensedLength = condensedLength
        maxCondensedStart = condensedStart
      }
      idx += 1
    }
    if (maxCondensedLength == 1) maxCondensedStart = -1
    val str = new StringBuilder
    idx = 0
    while (idx < 8) {
      if (idx == maxCondensedStart) {
        str.append("::")
        idx += maxCondensedLength
      } else {
        val hextet = Integer.toString(fields(idx), 16)
        str.append(hextet)
        idx += 1
        if (idx < 8 && idx != maxCondensedStart) str.append(":")
      }
    }
    scopeId.foreach(sid => str.append("%").append(sid))
    str.toString
  }

  /** Returns an uncondensed string representation of the array. */
  def toUncondensedString: String = {
    val str = new StringBuilder
    var idx = 0
    val bytes = toBytes
    while (idx < 16) {
      val field = ((bytes(idx) & 0xff) << 8) | (bytes(idx + 1) & 0xff)
      val hextet = f"$field%04x"
      str.append(hextet)
      idx += 2
      if (idx < 15) str.append(":")
    }
    scopeId.foreach(sid => str.append("%").append(sid))
    str.toString
  }

  /** 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
    * IPv4 dotted decimal notation. Consecutive 0 `x` fields are condensed with `::`.
    *
    * For example, the IPv4 address `127.0.0.1` can be converted to a compatible IPv6 address via
    * [[Ipv4Address#toCompatV6]], which is represented as the string `::7f00:1` and the mixed string `::127.0.0.1`.
    *
    * Similarly, `127.0.0.1` can be converted to a mapped V6 address via [[Ipv4Address#toMappedV6]], resulting in
    * `::ffff:7f00:1` and the mixed string `::ffff:127.0.0.1`.
    *
    * This format is described in RFC4291 section 2.2.3.
    *
    * @example {{{
    * scala> ipv6"::7f00:1".toMixedString
    * res0: String = ::127.0.0.1
    * scala> ipv6"ff3b:1234::ffab:7f00:1".toMixedString
    * res0: String = ff3b:1234::ffab:127.0.0.1
    * }}}
    */
  def toMixedString: String = {
    val bytes = toBytes
    val v4 = Ipv4Address.fromBytes(bytes.slice(12, 16)).get
    bytes(12) = 0
    bytes(13) = 1
    bytes(14) = 0
    bytes(15) = 1
    val s = Ipv6Address.unsafeFromBytes(bytes).toString
    val prefix = s.slice(0, s.size - 3)
    prefix + v4.toString
  }

  override def toUriString: String = s"[$toDefaultString]"

  /** Gets the IPv6 address after this address, with overflow from `ffff:ffff:....:ffff` to `::`. */
  override def next: Ipv6Address = Ipv6Address.fromBigInt(toBigInt + 1)

  /** Gets the IPv6 address before this address, with underflow from `::` to `ffff:ffff:....:ffff`. */
  override def previous: Ipv6Address = Ipv6Address.fromBigInt(toBigInt - 1)

  /** Converts this address to a 128-bit unsigned integer. */
  def toBigInt: BigInt = {
    val bs = bytes
    var result = BigInt(0)
    for (i <- 0 until bs.size) {
      result = (result << 8) | (0x0ff & bs(i))
    }
    result
  }

  override def isMulticast: Boolean =
    this >= Ipv6Address.MulticastRangeStart && this <= Ipv6Address.MulticastRangeEnd

  override def isSourceSpecificMulticast: Boolean =
    this >= Ipv6Address.SourceSpecificMulticastRangeStart && this <= Ipv6Address.SourceSpecificMulticastRangeEnd

  override def isLoopback: Boolean =
    this == Ipv6Address.Loopback || (isMappedV4 && collapseMappedV4.isLoopback)

  override def isLinkLocal: Boolean =
    Ipv6Address.LinkLocalBlock.contains(this) || (isMappedV4 && collapseMappedV4.isLinkLocal)

  override def isPrivate: Boolean =
    Ipv6Address.UniqueLocalBlock.contains(this) || (isMappedV4 && collapseMappedV4.isPrivate)

  /** Applies the supplied mask to this address.
    *
    * @example {{{
    * scala> ipv6"ff3b::1".masked(ipv6"fff0::")
    * res0: Ipv6Address = ff30::
    * }}}
    */
  def masked(mask: Ipv6Address): Ipv6Address =
    Ipv6Address.fromBigInt(toBigInt & mask.toBigInt)

  /** Computes the last address in the network identified by applying the supplied mask to this address.
    *
    * @example {{{
    * scala> ipv6"ff3b::1".maskedLast(ipv6"fff0::")
    * res0: Ipv6Address = ff3f:ffff:ffff:ffff:ffff:ffff:ffff:ffff
    * }}}
    */
  def maskedLast(mask: Ipv6Address): Ipv6Address =
    Ipv6Address.fromBigInt(toBigInt & mask.toBigInt | ~mask.toBigInt)

  /** Returns a new address with the supplied scope id. */
  def withScopeId(scopeId: String): Ipv6Address =
    new Ipv6Address(bytes, Some(scopeId))

  /** Returns a new address with the scope id, if defined, removed. */
  def withoutScopeId: Ipv6Address =
    new Ipv6Address(bytes, None)

  override def equals(other: Any): Boolean =
    other match {
      case that: Ipv6Address => java.util.Arrays.equals(bytes, that.bytes) && scopeId == that.scopeId
      case _                 => false
    }

  override def hashCode: Int = {
    val hash = java.util.Arrays.hashCode(bytes)
    scopeId match {
      case None    => hash
      case Some(s) =>
        val h = MurmurHash3.mix(hash, s.##)
        MurmurHash3.finalizeHash(h, 2)
    }
  }
}

object Ipv6Address extends Ipv6AddressCompanionPlatform {

  /** Wildcard IPv6 address - 0.0.0.0 */
  val Wildcard: Ipv6Address =
    fromBytes(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)

  /** First IP address in the IPv6 multicast range. */
  val MulticastRangeStart: Ipv6Address =
    fromBytes(255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)

  /** Last IP address in the IPv6 multicast range. */
  val MulticastRangeEnd: Ipv6Address =
    fromBytes(255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255)

  /** First IP address in the IPv6 source specific multicast range. */
  val SourceSpecificMulticastRangeStart: Ipv6Address =
    fromBytes(255, 48, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)

  /** Last IP address in the IPv6 source specific multicast range. */
  val SourceSpecificMulticastRangeEnd: Ipv6Address =
    fromBytes(255, 63, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255)

  /** CIDR which defines the mapped IPv4 address block (https://datatracker.ietf.org/doc/html/rfc4291#section-2.5.5.2).
    */
  val MappedV4Block: Cidr[Ipv6Address] =
    Cidr(Ipv6Address.fromBytes(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 0, 0, 0, 0), 96)

  /** Alias for ::1. */
  val Loopback: Ipv6Address = Ipv6Address.fromBytes(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1)

  /** CIDR which defines the unique local address block. */
  val UniqueLocalBlock: Cidr[Ipv6Address] =
    Cidr(Ipv6Address.fromBytes(0xfc, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0), 7)

  /** CIDR which defines the linked scope unicast address block. */
  val LinkLocalBlock: Cidr[Ipv6Address] =
    Cidr(Ipv6Address.fromBytes(0xfe, 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0), 10)

  /** Parses an IPv6 address from a string in RFC4291 notation, returning `None` if the string is not a valid IPv6
    * address.
    */
  def fromString(value: String): Option[Ipv6Address] = {
    val scopeSeparator = value.indexOf('%')
    if (scopeSeparator < 0) {
      fromNonMixedString(value) orElse fromMixedString(value)
    } else {
      val addrPart = value.substring(0, scopeSeparator)
      val scopePart = value.substring(scopeSeparator + 1)
      val addr = fromNonMixedString(addrPart) orElse fromMixedString(addrPart)
      addr.map(_.withScopeId(scopePart))
    }
  }

  private def fromNonMixedString(value: String): Option[Ipv6Address] = {
    var prefix: List[Int] = Nil
    var beforeCondenser = true
    var suffix: List[Int] = Nil
    val trimmed = value.trim()
    var result: Option[Ipv6Address] = null
    var idx = 0
    var fieldStart = 0
    while (idx < trimmed.length && (result eq null)) {
      val c = trimmed(idx)

      if ((c == ':' && idx > 0) || idx == trimmed.length - 1) {
        val field = trimmed.substring(fieldStart, if (c == ':') idx else idx + 1)
        if (field.size > 4) {
          result = None
        } else {
          try {
            val fieldValue = Integer.parseInt(field, 16)
            if (beforeCondenser) prefix = fieldValue :: prefix
            else suffix = fieldValue :: suffix
          } catch {
            case _: NumberFormatException =>
              result = None
          }
        }
      }

      if (c == ':') {
        if (idx == trimmed.length - 1) {
          // String terminated in a single colon
          result = None
        } else if (trimmed(idx + 1) == ':') {
          beforeCondenser = false
          idx += 1
        }
        fieldStart = idx + 1
      }

      idx += 1
    }

    // If after traversing all fields, there was no condenser, then fail unless there are exactly 8 fields
    if (beforeCondenser && (prefix.size + suffix.size) != 8) result = None

    if (result ne null) {
      result
    } else if (prefix.isEmpty && suffix.isEmpty && (trimmed.isEmpty || trimmed != "::")) {
      None
    } else {
      val bytes = new Array[Byte](16)
      idx = 0
      val prefixSize = prefix.size
      val suffixSize = suffix.size
      val numCondensedZeroes = 8 - (prefixSize + suffixSize)

      if (numCondensedZeroes < 0) {
        None
      } else {
        var prefixIdx = prefixSize - 1
        while (prefixIdx >= 0) {
          val value = prefix(prefixIdx)
          bytes(idx) = (value >> 8).toByte
          bytes(idx + 1) = value.toByte
          prefixIdx -= 1
          idx += 2
        }

        idx += numCondensedZeroes * 2

        var suffixIdx = suffixSize - 1
        while (suffixIdx >= 0) {
          val value = suffix(suffixIdx)
          bytes(idx) = (value >> 8).toByte
          bytes(idx + 1) = value.toByte
          suffixIdx -= 1
          idx += 2
        }

        Some(unsafeFromBytes(bytes))
      }
    }
  }

  private val MixedStringFormat = """([:a-fA-F0-9]+:)(\d+\.\d+\.\d+\.\d+)""".r
  private def fromMixedString(value: String): Option[Ipv6Address] =
    value match {
      case MixedStringFormat(prefix, v4Str) =>
        for {
          pfx <- fromNonMixedString(prefix + "0:0")
          v4 <- Ipv4Address.fromString(v4Str)
        } yield {
          val bytes = pfx.toBytes
          val v4bytes = v4.toBytes
          bytes(12) = v4bytes(0)
          bytes(13) = v4bytes(1)
          bytes(14) = v4bytes(2)
          bytes(15) = v4bytes(3)
          unsafeFromBytes(bytes)
        }
      case _ => None
    }

  /** Constructs an IPv6 address from a 16-element byte array. Returns `Some` when array is exactly 16-bytes and `None`
    * otherwise.
    */
  def fromBytes(bytes: Array[Byte]): Option[Ipv6Address] =
    if (bytes.size == 16) Some(unsafeFromBytes(bytes.clone))
    else None

  private def unsafeFromBytes(bytes: Array[Byte]): Ipv6Address =
    new Ipv6Address(bytes, None)

  /** Constructs an address from the specified 16 bytes.
    *
    * Each byte is represented as an `Int` to avoid having to manually call `.toByte` on each value -- the `toByte` call
    * is done inside this function.
    */
  def fromBytes(
      b0: Int,
      b1: Int,
      b2: Int,
      b3: Int,
      b4: Int,
      b5: Int,
      b6: Int,
      b7: Int,
      b8: Int,
      b9: Int,
      b10: Int,
      b11: Int,
      b12: Int,
      b13: Int,
      b14: Int,
      b15: Int
  ): Ipv6Address = {
    val bytes = new Array[Byte](16)
    bytes(0) = b0.toByte
    bytes(1) = b1.toByte
    bytes(2) = b2.toByte
    bytes(3) = b3.toByte
    bytes(4) = b4.toByte
    bytes(5) = b5.toByte
    bytes(6) = b6.toByte
    bytes(7) = b7.toByte
    bytes(8) = b8.toByte
    bytes(9) = b9.toByte
    bytes(10) = b10.toByte
    bytes(11) = b11.toByte
    bytes(12) = b12.toByte
    bytes(13) = b13.toByte
    bytes(14) = b14.toByte
    bytes(15) = b15.toByte
    unsafeFromBytes(bytes)
  }

  /** Constructs an IPv6 address from a `BigInt`, using the lower 128-bits. */
  def fromBigInt(value: BigInt): Ipv6Address = {
    val bytes = new Array[Byte](16)
    var rem = value
    for (i <- 15 to 0 by -1) {
      bytes(i) = (rem & 0x0ff).toByte
      rem = rem >> 8
    }
    unsafeFromBytes(bytes)
  }

  /** Computes a mask by setting the first / left-most `n` bits high.
    *
    * @example {{{
    * scala> Ipv6Address.mask(32)
    * res0: Ipv6Address = ffff:ffff::
    * }}}
    */
  def mask(bits: Int): Ipv6Address = {
    val b = if (bits < 0) 0 else if (bits > 128) 128 else bits
    Ipv6Address
      .fromBigInt {
        if (b == 128) (BigInt(-1L) << 64) | BigInt(-1L)
        else if (b < 64) BigInt(~(-1L >>> b)) << 64
        else (BigInt(-1L) << 64) | BigInt(~(-1L >>> (b - 64)))
      }
  }
}

/** Internationalized domain name, as specified by RFC3490 and RFC5891.
  *
  * This type models internationalized hostnames. An IDN provides unicode labels, a unicode string form, and an ASCII
  * hostname form.
  *
  * A well formed IDN consists of one or more labels separated by dots. Each label may contain unicode characters as
  * long as the converted ASCII form meets the requirements of RFC1123 (e.g., 63 or fewer characters and no leading or
  * trailing dash). A dot is represented as an ASCII period or one of the unicode dots: full stop, ideographic full
  * stop, fullwidth full stop, halfwidth ideographic full stop.
  *
  * The `toString` method returns the IDN in the form in which it was constructed. Sometimes it is useful to normalize
  * the IDN -- converting all dots to an ASCII period and converting all labels to lowercase.
  *
  * Note: equality and comparison of IDNs is case-sensitive. Consider comparing normalized toString values for a more
  * lenient notion of equality.
  */
final class IDN private (val labels: List[IDN.Label], val hostname: Hostname, override val toString: String)
    extends Host {

  /** Converts this IDN to lower case and replaces dots with ASCII periods. */
  def normalized: IDN = {
    val newLabels = labels.map(l => new IDN.Label(l.toString.toLowerCase))
    new IDN(newLabels, hostname.normalized, newLabels.toList.mkString("."))
  }

  override def hashCode: Int = MurmurHash3.stringHash(toString, "IDN".hashCode)
  override def equals(other: Any): Boolean =
    other match {
      case that: IDN => toString == that.toString
      case _         => false
    }
}

object IDN {

  /** Label component of an IDN. */
  final class Label private[IDN] (override val toString: String) extends Serializable with Ordered[Label] {
    def compare(that: Label): Int = toString.compare(that.toString)
    override def hashCode: Int = MurmurHash3.stringHash(toString, "Label".hashCode)
    override def equals(other: Any): Boolean =
      other match {
        case that: Label => toString == that.toString
        case _           => false
      }
  }

  private val DotPattern = "[\\.\u002e\u3002\uff0e\uff61]"
  private val DotPatternR = DotPattern.r.pattern

  /** Constructs a `IDN` from a string. */
  def fromString(value: String): Option[IDN] =
    value.size match {
      case 0 => None
      case _ =>
        val labels = value
          .split(DotPattern)
          .iterator
          .map(new Label(_))
          .toList
        Option(labels).filterNot(_.isEmpty).flatMap { ls =>
          val hostname = toAscii(value).flatMap(Hostname.fromString)
          hostname.map(h => new IDN(ls, h, value))
        }
    }

  /** Converts the supplied (ASCII) hostname in to an IDN. */
  def fromHostname(hostname: Hostname): IDN = {
    val labels =
      hostname.labels.map(l => new Label(toUnicode(l.toString)))
    new IDN(labels, hostname, labels.toList.mkString("."))
  }

  private[ip4s] def toAscii(value: String): Option[String] =
    DotPatternR
      .split(value, -1)
      .toList
      .traverse { label =>
        if (label.forall(_ < 128)) Some(label)
        else Bootstring.encodeRaw(PunycodeParams)(label).toOption.map("xn--" + _)
      }
      .map(_.mkString("."))

  private[ip4s] def toUnicode(value: String): String =
    DotPatternR
      .split(value, -1)
      .toList
      .traverse { label =>
        if (label.startsWith("xn--")) Bootstring.decodeRaw(PunycodeParams)(label.substring(4, label.length))
        else Right(label)
      }
      .map(_.mkString("."))
      .fold(e => throw new IllegalArgumentException(e), identity[String](_))

  implicit val show: Show[IDN] = Show.fromToString[IDN]
}


================================================
FILE: shared/src/main/scala/com/comcast/ip4s/MacAddress.scala
================================================
/*
 * Copyright 2018 Comcast Cable Communications Management, LLC
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.comcast.ip4s

import scala.util.control.NonFatal
import cats.{Order, Show}

/** 6-byte MAC address. */
final class MacAddress private (private val bytes: Array[Byte]) extends Ordered[MacAddress] with Serializable {
  def toBytes: Array[Byte] = bytes.clone

  def toLong: Long = {
    val bs = bytes
    var result = 0L
    for (i <- 0 until bs.size) {
      result = (result << 8) | (0x0ff & bs(i))
    }
    result
  }

  override def compare(that: MacAddress): Int = {
    var i, result = 0
    val tb = that.bytes
    val sz = bytes.length
    while (i < sz && result == 0) {
      result = Integer.compare(bytes(i) & 0xff, tb(i) & 0xff)
      i += 1
    }
    result
  }

  override def equals(other: Any): Boolean = other match {
    case that: MacAddress => java.util.Arrays.equals(bytes, that.bytes)
    case _                => false
  }

  override def hashCode: Int = java.util.Arrays.hashCode(bytes)

  override def toString: String =
    bytes.map(b => f"${0xff & b}%02x").mkString("", ":", "")
}

object MacAddress {

  /** Constructs a `MacAddress` from a 6-element byte array. Returns `Some` when array is exactly 6-bytes and `None`
    * otherwise.
    */
  def fromBytes(bytes: Array[Byte]): Option[MacAddress] = {
    if (bytes.length == 6) Some(new MacAddress(bytes))
    else None
  }

  /** Constructs a `MacAddress` from the specified 6 bytes.
    *
    * Each byte is represented as an `Int` to avoid having to manually call `.toByte` on each value -- the `toByte` call
    * is done inside this function.
    */
  def fromBytes(
      b0: Int,
      b1: Int,
      b2: Int,
      b3: Int,
      b4: Int,
      b5: Int
  ): MacAddress = {
    val bytes = new Array[Byte](6)
    bytes(0) = b0.toByte
    bytes(1) = b1.toByte
    bytes(2) = b2.toByte
    bytes(3) = b3.toByte
    bytes(4) = b4.toByte
    bytes(5) = b5.toByte
    new MacAddress(bytes)
  }

  /** Constructs a `MacAddress` from a `Long`, using the lower 48-bits. */
  def fromLong(value: Long): MacAddress = {
    val bytes = new Array[Byte](6)
    var rem = value
    for (i <- 5 to 0 by -1) {
      bytes(i) = (rem & 0x0ff).toByte
      rem = rem >> 8
    }
    new MacAddress(bytes)
  }

  /** Parses a `MacAddress` from a string, returning `None` if the string is not a valid mac. */
  def fromString(value: String): Option[MacAddress] = {
    val trimmed = value.trim
    val fields = trimmed.split(':')
    if (fields.length == 6) {
      val result = new Array[Byte](6)
      var i = 0
      while (i < result.length) {
        val field = fields(i)
        if (field.size == 2) {
          try {
            result(i) = (0xff & Integer.parseInt(field, 16)).toByte
            i += 1
          } catch {
            case NonFatal(_) => return None
          }
        } else return None
      }
      Some(new MacAddress(result))
    } else None
  }

  implicit val order: Order[MacAddress] = Order.fromComparable[MacAddress]
  implicit val show: Show[MacAddress] = Show.fromToString[MacAddress]
}


================================================
FILE: shared/src/main/scala/com/comcast/ip4s/Multicast.scala
================================================
/*
 * Copyright 2018 Comcast Cable Communications Management, LLC
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.comcast.ip4s

import cats.{Order, Show}

/** Witness that the wrapped address of type `A` is a multicast address.
  *
  * An instance of `Multicast` is typically created by either calling `Multicast.apply` or by using the `asMulticast`
  * method on `IpAddress`.
  */
sealed trait Multicast[+A <: IpAddress] extends Product with Serializable {
  def address: A
}

object Multicast {
  private case class DefaultMulticast[+A <: IpAddress](address: A) extends Multicast[A] {
    override def toString: String = address.toString
    override def equals(that: Any): Boolean = that match {
      case m: Multicast[?] => address == m.address
      case _               => false
    }
    override def hashCode: Int = address.hashCode
  }

  /** Constructs a multicast IP address. Returns `None` is the supplied address is not in the valid multicast range. */
  def fromIpAddress[A <: IpAddress](address: A): Option[Multicast[A]] =
    if (address.isSourceSpecificMulticast) Some(SourceSpecificMulticast.unsafeCreate(address))
    else if (address.isMulticast) Some(DefaultMulticast(address))
    else None

  implicit def order[J[x <: IpAddress] <: Multicast[x], A <: IpAddress]: Order[J[A]] =
    Order.fromOrdering(Multicast.ordering[J, A])
  implicit def ordering[J[x <: IpAddress] <: Multicast[x], A <: IpAddress]: Ordering[J[A]] = Ordering.by(_.address)
  implicit def show[J[x <: IpAddress] <: Multicast[x], A <: IpAddress]: Show[J[A]] =
    Show.fromToString[J[A]]
}

/** Witnesses that the wrapped address of type `A` is a source specific multicast address.
  *
  * An instance of `SourceSpecificMulticast` is typically created by either calling `Multicast.apply` or by using
  * `asSourceSpecificMulticast` and `asSourceSpecificMulticastLenient` methods on `IpAddress`.
  */
sealed trait SourceSpecificMulticast[+A <: IpAddress] extends Multicast[A] {

  /** Ensures the referenced address is in the RFC defined source specific address range. */
  def strict: Option[SourceSpecificMulticast.Strict[A]] =
    if (address.isSourceSpecificMulticast) Some(SourceSpecificMulticast.unsafeCreateStrict(address)) else None

  override def toString: String = address.toString
}

object SourceSpecificMulticast {

  /** Indicates the address is within the RFC defined source specific multicast range. */
  trait Strict[+A <: IpAddress] extends SourceSpecificMulticast[A]

  private case class DefaultSourceSpecificMulticast[+A <: IpAddress](address: A) extends SourceSpecificMulticast[A] {
    override def toString: String = address.toString
    override def equals(that: Any): Boolean = that match {
      case m: Multicast[?] => address == m.address
      case _               => false
    }
    override def hashCode: Int = address.hashCode
  }

  /** Constructs a source specific multicast IP address. Returns `None` if the supplied address is not in the valid
    * source specific multicast range.
    */
  def fromIpAddress[A <: IpAddress](address: A): Option[SourceSpecificMulticast.Strict[A]] =
    if (address.isSourceSpecificMulticast) Some(unsafeCreateStrict(address)) else None

  /** Constructs a source specific multicast IP address. Unlike `fromIpAddress`, multicast addresses outside the RFC
    * defined source specific range are allowed.
    */
  def fromIpAddressLenient[A <: IpAddress](address: A): Option[SourceSpecificMulticast[A]] =
    if (address.isMulticast) Some(unsafeCreate(address)) else None

  private[ip4s] def unsafeCreate[A <: IpAddress](address: A): SourceSpecificMulticast[A] =
    DefaultSourceSpecificMulticast(address)

  private[ip4s] def unsafeCreateStrict[A <: IpAddress](address: A): SourceSpecificMulticast.Strict[A] =
    new DefaultSourceSpecificMulticast(address) with Strict[A]

  implicit def ordering[A <: IpAddress]: Ordering[SourceSpecificMulticast[A]] =
    Multicast.ordering[SourceSpecificMulticast, A]
}


================================================
FILE: shared/src/main/scala/com/comcast/ip4s/MulticastJoin.scala
================================================
/*
 * Copyright 2018 Comcast Cable Communications Management, LLC
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.comcast.ip4s

import cats.{Order, Show}

/** Represents a join of a multicast group.
  *
  * This is represented as an ADT consisting of two constructors, [[AnySourceMulticastJoin]] and
  * [[SourceSpecificMulticastJoin]]. These constructors are provided as top level types to allow domain modeling where a
  * specific join type is required. The address type is parameterized for a similar reason -- to allow domain modeling
  * where a specific address type is required.
  */
sealed abstract class MulticastJoin[+A <: IpAddress] extends Product with Serializable {

  /** Converts this join to a value of type `A` using the supplied functions. */
  def fold[B](asm: AnySourceMulticastJoin[A] => B, ssm: SourceSpecificMulticastJoin[A] => B): B =
    this match {
      case a: AnySourceMulticastJoin[A]      => asm(a)
      case a: SourceSpecificMulticastJoin[A] => ssm(a)
    }

  /** Narrows to an `AnySourceMulticastJoin`. */
  def asAsm: Option[AnySourceMulticastJoin[A]] = fold(Some(_), _ => None)

  /** Narrows to a `SourceSpecificMulticastJoin`. */
  def asSsm: Option[SourceSpecificMulticastJoin[A]] = fold(_ => None, Some(_))

  /** Returns the source address and group address. If this join is an any-source join, `None` is returned for the
    * source. Otherwise, this join is a source specific join and `Some(src)` is returned for the source.
    */
  def sourceAndGroup: (Option[A], Multicast[A]) =
    fold(asm => (None, asm.group), ssm => (Some(ssm.source), ssm.group))

  override def toString: String =
    fold(asm => asm.group.toString, ssm => s"${ssm.source}@${ssm.group}")
}

object MulticastJoin {

  /** Constructs an `AnySourceMulticastJoin[A]`. */
  def asm[A <: IpAddress](group: Multicast[A]): MulticastJoin[A] =
    AnySourceMulticastJoin(group)

  /** Constructs a `SourceSpecificMulticastJoin[A]`. */
  def ssm[A <: IpAddress](source: A, group: SourceSpecificMulticast[A]): MulticastJoin[A] =
    SourceSpecificMulticastJoin(source, group)

  def fromString(value: String): Option[MulticastJoin[IpAddress]] =
    fromStringGeneric(value, IpAddress.fromString)

  def fromString4(value: String): Option[MulticastJoin[Ipv4Address]] =
    fromStringGeneric(value, Ipv4Address.fromString)

  def fromString6(value: String): Option[MulticastJoin[Ipv6Address]] =
    fromStringGeneric(value, Ipv6Address.fromString)

  private val Pattern = """(?:([^@]+)@)?(.+)""".r
  private[ip4s] def fromStringGeneric[A <: IpAddress](
      value: String,
      parse: String => Option[A]
  ): Option[MulticastJoin[A]] =
    value match {
      case Pattern(sourceStr, groupStr) =>
        Option(sourceStr) match {
          case Some(sourceStr) =>
            for {
              source <- parse(sourceStr)
              group <- parse(groupStr).flatMap(_.asSourceSpecificMulticastLenient)
            } yield ssm(source, group)
          case None =>
            for {
              group <- parse(groupStr).flatMap(_.asSourceSpecificMulticastLenient)
            } yield asm(group)
        }
      case _ => None
    }

  implicit def order[J[x <: IpAddress] <: MulticastJoin[x], A <: IpAddress]: Order[J[A]] =
    Order.fromOrdering(MulticastJoin.ordering[J, A])
  implicit def ordering[J[x <: IpAddress] <: MulticastJoin[x], A <: IpAddress]: Ordering[J[A]] =
    Ordering.by(_.sourceAndGroup)
  implicit def show[J[x <: IpAddress] <: MulticastJoin[x], A <: IpAddress]: Show[J[A]] =
    Show.fromToString[J[A]]
}

/** Multicast join to a group without a source filter. */
final case class AnySourceMulticastJoin[+A <: IpAddress](group: Multicast[A]) extends MulticastJoin[A]

object AnySourceMulticastJoin {
  def fromString(value: String): Option[AnySourceMulticastJoin[IpAddress]] =
    MulticastJoin.fromStringGeneric(value, IpAddress.fromString).flatMap(_.asAsm)

  def fromString4(value: String): Option[AnySourceMulticastJoin[Ipv4Address]] =
    MulticastJoin.fromStringGeneric(value, Ipv4Address.fromString).flatMap(_.asAsm)

  def fromString6(value: String): Option[AnySourceMulticastJoin[Ipv6Address]] =
    MulticastJoin.fromStringGeneric(value, Ipv6Address.fromString).flatMap(_.asAsm)
}

/** Multicast join to a group from the specified source. */
final case class SourceSpecificMulticastJoin[+A <: IpAddress](source: A, group: SourceSpecificMulticast[A])
    extends MulticastJoin[A]

object SourceSpecificMulticastJoin {
  def fromString(value: String): Option[SourceSpecificMulticastJoin[IpAddress]] =
    MulticastJoin.fromStringGeneric(value, IpAddress.fromString).flatMap(_.asSsm)

  def fromString4(value: String): Option[SourceSpecificMulticastJoin[Ipv4Address]] =
    MulticastJoin.fromStringGeneric(value, Ipv4Address.fromString).flatMap(_.asSsm)

  def fromString6(value: String): Option[SourceSpecificMulticastJoin[Ipv6Address]] =
    MulticastJoin.fromStringGeneric(value, Ipv6Address.fromString).flatMap(_.asSsm)
}


================================================
FILE: shared/src/main/scala/com/comcast/ip4s/MulticastSocketAddress.scala
================================================
/*
 * Copyright 2018 Comcast Cable Communications Management, LLC
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.comcast.ip4s

import cats.{Order, Show}

/** A multicast join of the specified type and a port number. Used to describe UDP join of a multicast group.
  */
final case class MulticastSocketAddress[J[+x <: IpAddress] <: MulticastJoin[x], +A <: IpAddress](
    join: J[A],
    port: Port
) {

  /** Narrows join to an `AnySourceMulticastJoin`. */
  def asAsm: Option[MulticastSocketAddress[AnySourceMulticastJoin, A]] =
    join.asAsm.map(j => MulticastSocketAddress(j, port))

  /** Narrows join to a `SourceSpecificMulticastJoin`. */
  def asSsm: Option[MulticastSocketAddress[SourceSpecificMulticastJoin, A]] =
    join.asSsm.map(j => MulticastSocketAddress(j, port))

  override def toString: String = {
    val (source, group) = join.sourceAndGroup
    group.address.fold(
      _ => s"$join:$port",
      _ =>
        source match {
          case None      => s"[${group.address}]:$port"
          case Some(src) => s"[$src]@[${group.address}]:$port"
        }
    )
  }
}

object MulticastSocketAddress {
  def fromString(value: String): Option[MulticastSocketAddress[MulticastJoin, IpAddress]] =
    fromString4(value) orElse fromString6(value)

  private val V4Pattern = """(?:([^@]+)@)?([^:]+):(\d+)""".r
  def fromString4(value: String): Option[MulticastSocketAddress[MulticastJoin, Ipv4Address]] =
    fromStringGeneric(value, V4Pattern, Ipv4Address.fromString)

  private val V6Pattern = """(?:\[([^\]]+)\]@)?\[([^\]]+)\]:(\d+)""".r
  def fromString6(value: String): Option[MulticastSocketAddress[MulticastJoin, Ipv6Address]] =
    fromStringGeneric(value, V6Pattern, Ipv6Address.fromString)

  def anySourceFromString(value: String): Option[MulticastSocketAddress[AnySourceMulticastJoin, IpAddress]] =
    anySourceFromString4(value) orElse anySourceFromString6(value)
  def anySourceFromString6(value: String): Option[MulticastSocketAddress[AnySourceMulticastJoin, Ipv6Address]] =
    fromString6(value).flatMap(_.asAsm)
  def anySourceFromString4(value: String): Option[MulticastSocketAddress[AnySourceMulticastJoin, Ipv4Address]] =
    fromString4(value).flatMap(_.asAsm)

  def sourceSpecificFromString(value: String): Option[MulticastSocketAddress[SourceSpecificMulticastJoin, IpAddress]] =
    sourceSpecificFromString4(value) orElse sourceSpecificFromString6(value)
  def sourceSpecificFromString6(
      value: String
  ): Option[MulticastSocketAddress[SourceSpecificMulticastJoin, Ipv6Address]] =
    fromString6(value).flatMap(_.asSsm)
  def sourceSpecificFromString4(
      value: String
  ): Option[MulticastSocketAddress[SourceSpecificMulticastJoin, Ipv4Address]] =
    fromString4(value).flatMap(_.asSsm)

  private def fromStringGeneric[A <: IpAddress](
      value: String,
      pattern: util.matching.Regex,
      parse: String => Option[A]
  ): Option[MulticastSocketAddress[MulticastJoin, A]] = {
    val Pattern = pattern
    value match {
      case Pattern(sourceStr, groupStr, portStr) =>
        Option(sourceStr) match {
          case Some(sourceStr) =>
            for {
              source <- parse(sourceStr)
              group <- parse(groupStr).flatMap(_.asSourceSpecificMulticastLenient)
              port <- Port.fromString(portStr)
            } yield MulticastSocketAddress(MulticastJoin.ssm(source, group), port)
          case None =>
            for {
              group <- parse(groupStr).flatMap(_.asMulticast)
              port <- Port.fromString(portStr)
            } yield MulticastSocketAddress(MulticastJoin.asm(group), port)
        }
      case _ => None
    }
  }

  implicit def order[J[+x <: IpAddress] <: MulticastJoin[x], A <: IpAddress]: Order[MulticastSocketAddress[J, A]] =
    Order.fromOrdering(MulticastSocketAddress.ordering[J, A])
  implicit def ordering[J[+x <: IpAddress] <: MulticastJoin[x], A <: IpAddress]
      : Ordering[MulticastSocketAddress[J, A]] = Ordering.by(x => (x.join, x.port))
  implicit def show[J[+x <: IpAddress] <: MulticastJoin[x], A <: IpAddress]: Show[MulticastSocketAddress[J, A]] =
    Show.fromToString[MulticastSocketAddress[J, A]]
}


================================================
FILE: shared/src/main/scala/com/comcast/ip4s/NetworkInterface.scala
================================================
/*
 * Copyright 2018 Comcast Cable Communications Management, LLC
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.comcast.ip4s

/** Description of a network interface.
  *
  * Note this is an immutable description, representing a snapshot of the state of the network interface at the time the operating system was queried.
  *
  * To get an instance of `NetworkInterface`, use `NetworkInterfaces[F].getAll`.
  */
sealed trait NetworkInterface {

  /** Unique name for the interface. */
  def name: String

  /** Descriptive name of the interface, suitable for use on user interfaces. */
  def displayName: String

  /** MAC address of the interface, if available. */
  def macAddress: Option[MacAddress]

  /** IP addresses associated with the interface. */
  def addresses: List[Cidr[IpAddress]]

  /** True if the interface is a loopback interface. */
  def isLoopback: Boolean

  /** True if the interface is up/active. */
  def isUp: Boolean
}

object NetworkInterface extends NetworkInterfaceCompanionPlatform {

  def apply(
      name: String,
      displayName: String,
      macAddress: Option[MacAddress],
      addresses: List[Cidr[IpAddress]],
      isLoopback: Boolean,
      isUp: Boolean
  ): NetworkInterface =
    DefaultNetworkInterface(name, displayName, macAddress, addresses, isLoopback, isUp)

  private case class DefaultNetworkInterface(
      name: String,
      displayName: String,
      macAddress: Option[MacAddress],
      addresses: List[Cidr[IpAddress]],
      isLoopback: Boolean,
      isUp: Boolean
  ) extends NetworkInterface {
    override def toString: String =
      s"NetworkInterface($name, $displayName, $macAddress, $addresses, $isLoopback, $isUp)"
  }
}


================================================
FILE: shared/src/main/scala/com/comcast/ip4s/NetworkInterfaces.scala
================================================
/*
 * Copyright 2018 Comcast Cable Communications Management, LLC
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.comcast.ip4s

import cats.effect.{IO, Sync}
import cats.syntax.all.*

/** Capability for an effect `F[_]` which can query for local network interfaces.
  *
  * An instance is available for any effect which has a `Sync` instance.
  */
sealed trait NetworkInterfaces[F[_]] {

  /** Gets a map of network interfaces by name. */
  def getAll: F[Map[String, NetworkInterface]]

  /** Gets the network interface with the specified name. */
  def getByName(name: String): F[Option[NetworkInterface]]

  /** Gets the network interface with the specified address. */
  def getByAddress(address: IpAddress): F[Option[NetworkInterface]]

  /** Gets the network interfaces with the specified MAC address. */
  def getByMacAddress(address: MacAddress): F[List[NetworkInterface]]
}

object NetworkInterfaces extends NetworkInterfacesCompanionPlatform {
  private[ip4s] abstract class SyncNetworkInterfaces[F[_]: Sync] extends NetworkInterfaces[F] {
    def getByName(name: String): F[Option[NetworkInterface]] =
      getAll.map(_.get(name))

    def getByAddress(address: IpAddress): F[Option[NetworkInterface]] =
      getAll.map { all =>
        all.values.collectFirst { case iface if iface.addresses.exists(_.address == address) => iface }
      }

    def getByMacAddress(address: MacAddress): F[List[NetworkInterface]] =
      getAll.map { all =>
        all.values.collect { case iface if iface.macAddress == Some(address) => iface }.toList
      }
  }

  def apply[F[_]](implicit F: NetworkInterfaces[F]): F.type = F

  implicit def forIO: NetworkInterfaces[IO] = forSync[IO]
}


================================================
FILE: shared/src/main/scala/com/comcast/ip4s/Port.scala
================================================
/*
 * Copyright 2018 Comcast Cable Communications Management, LLC
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.comcast.ip4s

import scala.util.Try
import scala.util.hashing.MurmurHash3

import cats.{Order, Show}

/** TCP or UDP port number. */
final class Port private (val value: Int) extends Product with Serializable with Ordered[Port] {
  def copy(value: Int): Option[Port] = Port.fromInt(value)
  def compare(that: Port): Int = value.compare(that.value)
  override def toString: String = value.toString
  override def hashCode: Int = MurmurHash3.productHash(this, productPrefix.hashCode)
  override def equals(other: Any): Boolean =
    other match {
      case that: Port => value == that.value
      case _          => false
    }
  override def canEqual(other: Any): Boolean = other.isInstanceOf[Port]
  override def productArity: Int = 1
  override def productElement(n: Int): Any =
    if (n == 0) value else throw new IndexOutOfBoundsException
}

object Port {
  final val MinValue = 0
  final val MaxValue = 65535

  final val Wildcard: Port = new Port(0)

  def fromInt(value: Int): Option[Port] =
    if (value >= MinValue && value <= MaxValue) Some(new Port(value)) else None

  def fromString(value: String): Option[Port] =
    Try(value.toInt).toOption.flatMap(fromInt)

  def unapply(p: Port): Option[Int] = Some(p.value)

  implicit val order: Order[Port] = Order.fromComparable[Port]
  implicit val show: Show[Port] = Show.fromToString[Port]
}


================================================
FILE: shared/src/main/scala/com/comcast/ip4s/SocketAddress.scala
================================================
/*
 * Copyright 2018 Comcast Cable Communications Management, LLC
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.comcast.ip4s

import cats.{Applicative, Order, Show}
import cats.syntax.all._

/** An IP address of the specified type and a port number. Used to describe the source or destination of a socket.
  */
final case class SocketAddress[+A <: Host](host: A, port: Port) extends GenSocketAddress with SocketAddressPlatform[A] {
  override def toString: String =
    host match {
      case _: Ipv6Address => s"[$host]:$port"
      case _              => s"$host:$port"
    }

  /** Resolves this `SocketAddress[Hostname]` to a `SocketAddress[IpAddress]`. */
  final def resolve[F[_]: Dns: Applicative]: F[SocketAddress[IpAddress]] =
    host.resolve.map(SocketAddress(_, port))
}

object SocketAddress extends SocketAddressCompanionPlatform {

  /** Alias for 0.0.0.0:0. */
  val Wildcard: SocketAddress[Host] = SocketAddress(Ipv4Address.Wildcard, Port.Wildcard)

  /** Alias for 0.0.0.0:port. */
  def port(port: Port): SocketAddress[Host] = SocketAddress(Ipv4Address.Wildcard, port)

  def fromString(value: String): Option[SocketAddress[Host]] =
    fromStringIp(value) orElse fromStringHostname(value) orElse fromStringIDN(value)

  def fromStringIp(value: String): Option[SocketAddress[IpAddress]] =
    fromString4(value) orElse fromString6(value)

  private val UnescapedPattern = """([^:]+):(\d+)""".r

  def fromString4(value: String): Option[SocketAddress[Ipv4Address]] =
    value match {
      case UnescapedPattern(ip, port) =>
        for {
          addr <- Ipv4Address.fromString(ip)
          prt <- Port.fromString(port)
        } yield SocketAddress(addr, prt)
      case _ => None
    }

  private val V6Pattern = """\[(.+)\]:(\d+)""".r
  def fromString6(value: String): Option[SocketAddress[Ipv6Address]] =
    value match {
      case V6Pattern(ip, port) =>
        for {
          addr <- Ipv6Address.fromString(ip)
          prt <- Port.fromString(port)
        } yield SocketAddress(addr, prt)
      case _ => None
    }

  def fromStringHostname(value: String): Option[SocketAddress[Hostname]] =
    value match {
      case UnescapedPattern(s, port) =>
        for {
          hostname <- Hostname.fromString(s)
          prt <- Port.fromString(port)
        } yield SocketAddress(hostname, prt)
      case _ => None
    }

  def fromStringIDN(value: String): Option[SocketAddress[IDN]] =
    value match {
      case UnescapedPattern(s, port) =>
        for {
          idn <- IDN.fromString(s)
          prt <- Port.fromString(port)
        } yield SocketAddress(idn, prt)
      case _ => None
    }

  implicit def order[A <: Host]: Order[SocketAddress[A]] = Order.fromOrdering(SocketAddress.ordering[A])
  implicit def ordering[A <: Host]: Ordering[SocketAddress[A]] = Ordering.by(x => (x.host: Host, x.port))
  implicit def show[A <: Host]: Show[SocketAddress[A]] = Show.fromToString[SocketAddress[A]]
}


================================================
FILE: shared/src/main/scala/com/comcast/ip4s/UnixSocketAddress.scala
================================================
/*
 * Copyright 2018 Comcast Cable Communications Management, LLC
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.comcast.ip4s

import cats.{Order, Show}

final case class UnixSocketAddress(path: String) extends GenSocketAddress with Ordered[UnixSocketAddress] {
  override def toString: String = path
  override def compare(that: UnixSocketAddress): Int = path.compare(that.path)
}

object UnixSocketAddress {
  implicit val order: Order[UnixSocketAddress] = Order.fromComparable[UnixSocketAddress]
  implicit val show: Show[UnixSocketAddress] = Show.fromToString[UnixSocketAddress]
}


================================================
FILE: shared/src/main/scala-2/Literals.scala
================================================
/*
 * Copyright 2018 Comcast Cable Communications Management, LLC
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.comcast.ip4s

import org.typelevel.literally.Literally

/** Macros that support literal string interpolators. */
object Literals {

  object ip extends Literally[IpAddress] {
    def validate(c: Context)(s: String): Either[String, c.Expr[IpAddress]] = {
      import c.universe._
      IpAddress.fromString(s) match {
        case Some(_) => Right(c.Expr(q"_root_.com.comcast.ip4s.IpAddress.fromString($s).get"))
        case None    => Left("invalid IP address")
      }
    }
    def make(c: Context)(args: c.Expr[Any]*): c.Expr[IpAddress] = apply(c)(args*)
  }

  object ipv4 extends Literally[Ipv4Address] {
    def validate(c: Context)(s: String): Either[String, c.Expr[Ipv4Address]] = {
      import c.universe._
      Ipv4Address.fromString(s) match {
        case Some(_) => Right(c.Expr(q"_root_.com.comcast.ip4s.Ipv4Address.fromString($s).get"))
        case None    => Left("invalid IPv4 address")
      }
    }
    def make(c: Context)(args: c.Expr[Any]*): c.Expr[Ipv4Address] = apply(c)(args*)
  }

  object ipv6 extends Literally[Ipv6Address] {
    def validate(c: Context)(s: String): Either[String, c.Expr[Ipv6Address]] = {
      import c.universe._
      Ipv6Address.fromString(s) match {
        case Some(_) => Right(c.Expr(q"_root_.com.comcast.ip4s.Ipv6Address.fromString($s).get"))
        case None    => Left("invalid IPv6 address")
      }
    }
    def make(c: Context)(args: c.Expr[Any]*): c.Expr[Ipv6Address] = apply(c)(args*)
  }

  object mip extends Literally[Multicast[IpAddress]] {
    def validate(c: Context)(s: String): Either[String, c.Expr[Multicast[IpAddress]]] = {
      import c.universe._
      IpAddress.fromString(s).flatMap(_.asMulticast) match {
        case Some(_) => Right(c.Expr(q"_root_.com.comcast.ip4s.IpAddress.fromString($s).get.asMulticast.get"))
        case None    => Left("invalid IP multicast address")
      }
    }
    def make(c: Context)(args: c.Expr[Any]*): c.Expr[Multicast[IpAddress]] = apply(c)(args*)
  }

  object mipv4 extends Literally[Multicast[Ipv4Address]] {
    def validate(c: Context)(s: String): Either[String, c.Expr[Multicast[Ipv4Address]]] = {
      import c.universe._
      Ipv4Address.fromString(s).flatMap(_.asMulticast) match {
        case Some(_) => Right(c.Expr(q"_root_.com.comcast.ip4s.Ipv4Address.fromString($s).get.asMulticast.get"))
        case None    => Left("invalid IPv4 multicast address")
      }
    }
    def make(c: Context)(args: c.Expr[Any]*): c.Expr[Multicast[Ipv4Address]] = apply(c)(args*)
  }

  object mipv6 extends Literally[Multicast[Ipv6Address]] {
    def validate(c: Context)(s: String): Either[String, c.Expr[Multicast[Ipv6Address]]] = {
      import c.universe._
      Ipv6Address.fromString(s).flatMap(_.asMulticast) match {
        case Some(_) => Right(c.Expr(q"_root_.com.comcast.ip4s.Ipv6Address.fromString($s).get.asMulticast.get"))
        case None    => Left("invalid IPv6 multicast address")
      }
    }
    def make(c: Context)(args: c.Expr[Any]*): c.Expr[Multicast[Ipv6Address]] = apply(c)(args*)
  }

  object ssmip extends Literally[SourceSpecificMulticast.Strict[IpAddress]] {
    def validate(c: Context)(s: String): Either[String, c.Expr[SourceSpecificMulticast.Strict[IpAddress]]] = {
      import c.universe._
      IpAddress.fromString(s).flatMap(_.asSourceSpecificMulticast) match {
        case Some(_) =>
          Right(c.Expr(q"_root_.com.comcast.ip4s.IpAddress.fromString($s).get.asSourceSpecificMulticast.get"))
        case None => Left("invalid source specific IP multicast address")
      }
    }
    def make(c: Context)(args: c.Expr[Any]*): c.Expr[SourceSpecificMulticast.Strict[IpAddress]] = apply(c)(args*)
  }

  object ssmipv4 extends Literally[SourceSpecificMulticast.Strict[Ipv4Address]] {
    def validate(c: Context)(s: String): Either[String, c.Expr[SourceSpecificMulticast.Strict[Ipv4Address]]] = {
      import c.universe._
      Ipv4Address.fromString(s).flatMap(_.asSourceSpecificMulticast) match {
        case Some(_) =>
          Right(c.Expr(q"_root_.com.comcast.ip4s.Ipv4Address.fromString($s).get.asSourceSpecificMulticast.get"))
        case None => Left("invalid source specific IPv4 multicast address")
      }
    }
    def make(c: Context)(args: c.Expr[Any]*): c.Expr[SourceSpecificMulticast.Strict[Ipv4Address]] = apply(c)(args*)
  }

  object ssmipv6 extends Literally[SourceSpecificMulticast.Strict[Ipv6Address]] {
    def validate(c: Context)(s: String): Either[String, c.Expr[SourceSpecificMulticast.Strict[Ipv6Address]]] = {
      import c.universe._
      Ipv6Address.fromString(s).flatMap(_.asSourceSpecificMulticast) match {
        case Some(_) =>
          Right(c.Expr(q"_root_.com.comcast.ip4s.Ipv6Address.fromString($s).get.asSourceSpecificMulticast.get"))
        case None => Left("invalid source specific IPv6 multicast address")
      }
    }
    def make(c: Context)(args: c.Expr[Any]*): c.Expr[SourceSpecificMulticast.Strict[Ipv6Address]] = apply(c)(args*)
  }

  object port extends Literally[Port] {
    def validate(c: Context)(s: String): Either[String, c.Expr[Port]] = {
      import c.universe._
      scala.util.Try(s.toInt).toOption.flatMap(Port.fromInt) match {
        case Some(_) => Right(c.Expr(q"_root_.com.comcast.ip4s.Port.fromInt($s.toInt).get"))
        case None    => Left("invalid port")
      }
    }
    def make(c: Context)(args: c.Expr[Any]*): c.Expr[Port] = apply(c)(args*)
  }

  object hostname extends Literally[Hostname] {
    def validate(c: Context)(s: String): Either[String, c.Expr[Hostname]] = {
      import c.universe._
      Hostname.fromString(s) match {
        case Some(_) => Right(c.Expr(q"_root_.com.comcast.ip4s.Hostname.fromString($s).get"))
        case None    => Left("invalid hostname")
      }
    }
    def make(c: Context)(args: c.Expr[Any]*): c.Expr[Hostname] = apply(c)(args*)
  }

  object idn extends Literally[IDN] {
    def validate(c: Context)(s: String): Either[String, c.Expr[IDN]] = {
      import c.universe._
      IDN.fromString(s) match {
        case Some(_) => Right(c.Expr(q"_root_.com.comcast.ip4s.IDN.fromString($s).get"))
        case None    => Left("invalid IDN")
      }
    }
    def make(c: Context)(args: c.Expr[Any]*): c.Expr[IDN] = apply(c)(args*)
  }
}


================================================
FILE: shared/src/main/scala-2/package.scala
================================================
/*
 * Copyright 2018 Comcast Cable Communications Management, LLC
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.comcast

package object ip4s extends ip4splatform {
  final implicit class IpLiteralSyntax(val sc: StringContext) extends AnyVal {
    def ip(args: Any*): IpAddress = macro Literals.ip.make
    def ipv4(args: Any*): Ipv4Address =
      macro Literals.ipv4.make
    def ipv6(args: Any*): Ipv6Address =
      macro Literals.ipv6.make

    def mip(args: Any*): Multicast[IpAddress] =
      macro Literals.mip.make
    def mipv4(args: Any*): Multicast[Ipv4Address] =
      macro Literals.mipv4.make
    def mipv6(args: Any*): Multicast[Ipv6Address] =
      macro Literals.mipv6.make

    def ssmip(args: Any*): SourceSpecificMulticast.Strict[IpAddress] =
      macro Literals.ssmip.make
    def ssmipv4(args: Any*): SourceSpecificMulticast.Strict[Ipv4Address] =
      macro Literals.ssmipv4.make
    def ssmipv6(args: Any*): SourceSpecificMulticast.Strict[Ipv6Address] =
      macro Literals.ssmipv6.make

    def port(args: Any*): Port =
      macro Literals.port.make
    def host(args: Any*): Hostname =
      macro Literals.hostname.make
    def idn(args: Any*): IDN =
      macro Literals.idn.make
  }
}


================================================
FILE: shared/src/main/scala-2.12/com/comcast/ip4s/CollectionCompat.scala
================================================
/*
 * Copyright 2018 Comcast Cable Communications Management, LLC
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.comcast.ip4s

import scala.collection.JavaConverters._

private[ip4s] object Ip4sCollectionCompat {
  implicit class JIterableOps[A](private val self: java.lang.Iterable[A]) extends AnyVal {
    def asScala: Iterable[A] = iterableAsScalaIterable(self)
  }
  implicit class ListOps[A](private val self: List[A]) extends AnyVal {
    def asJava: java.util.List[A] = seqAsJavaList(self)
  }
  implicit class EnumerationOps[A](private val self: java.util.Enumeration[A]) extends AnyVal {
    def asScala: Iterator[A] = enumerationAsScalaIterator(self)
  }
}


================================================
FILE: shared/src/main/scala-2.13/com/comcast/ip4s/CollectionCompat.scala
================================================
/*
 * Copyright 2018 Comcast Cable Communications Management, LLC
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.comcast.ip4s

import scala.jdk.CollectionConverters._

private[ip4s] object Ip4sCollectionCompat {
  implicit class JIterableOps[A](private val self: java.lang.Iterable[A]) extends AnyVal {
    def asScala: Iterable[A] = IterableHasAsScala(self).asScala
  }
  implicit class ListOps[A](private val self: List[A]) extends AnyVal {
    def asJava: java.util.List[A] = SeqHasAsJava(self).asJava
  }
  implicit class EnumerationOps[A](private val self: java.util.Enumeration[A]) extends AnyVal {
    def asScala: Iterator[A] = EnumerationHasAsScala(self).asScala
  }
}


================================================
FILE: shared/src/main/scala-3/Literals.scala
================================================
/*
 * Copyright 2018 Comcast Cable Communications Management, LLC
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.comcast.ip4s

import org.typelevel.literally.Literally

extension (inline ctx: StringContext)
  inline def ip(inline args: Any*): IpAddress =
    ${ Literals.ip('ctx, 'args) }

  inline def ipv4(inline args: Any*): Ipv4Address =
    ${ Literals.ipv4('ctx, 'args) }

  inline def ipv6(inline args: Any*): Ipv6Address =
    ${ Literals.ipv6('ctx, 'args) }

  inline def mip(inline args: Any*): Multicast[IpAddress] =
    ${ Literals.mip('ctx, 'args) }

  inline def mipv4(inline args: Any*): Multicast[Ipv4Address] =
    ${ Literals.mipv4('ctx, 'args) }

  inline def mipv6(inline args: Any*): Multicast[Ipv6Address] =
    ${ Literals.mipv6('ctx, 'args) }

  inline def ssmip(inline args: Any*): SourceSpecificMulticast.Strict[IpAddress] =
    ${ Literals.ssmip('ctx, 'args) }

  inline def ssmipv4(inline args: Any*): SourceSpecificMulticast.Strict[Ipv4Address] =
    ${ Literals.ssmipv4('ctx, 'args) }

  inline def ssmipv6(inline args: Any*): SourceSpecificMulticast.Strict[Ipv6Address] =
    ${ Literals.ssmipv6('ctx, 'args) }

  inline def port(inline args: Any*): Port =
    ${ Literals.port('ctx, 'args) }

  inline def host(inline args: Any*): Hostname =
    ${ Literals.host('ctx, 'args) }

  inline def idn(inline args: Any*): IDN =
    ${ Literals.idn('ctx, 'args) }

object Literals:

  object ip extends Literally[IpAddress]:
    def validate(s: String)(using Quotes) =
      IpAddress.fromString(s) match
        case Some(_) => Right('{ _root_.com.comcast.ip4s.IpAddress.fromString(${ Expr(s) }).get })
        case None    => Left("Invalid IP address")

  object ipv4 extends Literally[Ipv4Address]:
    def validate(s: String)(using Quotes) =
      Ipv4Address.fromString(s) match
        case Some(_) => Right('{ _root_.com.comcast.ip4s.Ipv4Address.fromString(${ Expr(s) }).get })
        case None    => Left("Invalid IPv4 address")

  object ipv6 extends Literally[Ipv6Address]:
    def validate(s: String)(using Quotes) =
      Ipv6Address.fromString(s) match
        case Some(_) => Right('{ _root_.com.comcast.ip4s.Ipv6Address.fromString(${ Expr(s) }).get })
        case None    => Left("Invalid IPv6 address")

  object mip extends Literally[Multicast[IpAddress]]:
    def validate(s: String)(using Quotes) =
      IpAddress.fromString(s).flatMap(_.asMulticast) match
        case Some(_) => Right('{ _root_.com.comcast.ip4s.IpAddress.fromString(${ Expr(s) }).get.asMulticast.get })
        case None    => Left("Invalid IP multicast address")

  object mipv4 extends Literally[Multicast[Ipv4Address]]:
    def validate(s: String)(using Quotes) =
      Ipv4Address.fromString(s).flatMap(_.asMulticast) match
        case Some(_) => Right('{ _root_.com.comcast.ip4s.Ipv4Address.fromString(${ Expr(s) }).get.asMulticast.get })
        case None    => Left("Invalid IPv4 multicast address")

  object mipv6 extends Literally[Multicast[Ipv6Address]]:
    def validate(s: String)(using Quotes) =
      Ipv6Address.fromString(s).flatMap(_.asMulticast) match
        case Some(_) => Right('{ _root_.com.comcast.ip4s.Ipv6Address.fromString(${ Expr(s) }).get.asMulticast.get })
        case None    => Left("Invalid IPv6 multicast address")

  object ssmip extends Literally[SourceSpecificMulticast.Strict[IpAddress]]:
    def validate(s: String)(using Quotes) =
      IpAddress.fromString(s).flatMap(_.asSourceSpecificMulticast) match
        case Some(_) =>
          Right('{
            _root_.com.comcast.ip4s.IpAddress.fromString(${ Expr(s) }).get.asSourceSpecificMulticast.get
          })
        case None => Left("Invalid source specific IP multicast address")

  object ssmipv4 extends Literally[SourceSpecificMulticast.Strict[Ipv4Address]]:
    def validate(s: String)(using Quotes) =
      Ipv4Address.fromString(s).flatMap(_.asSourceSpecificMulticast) match
        case Some(_) =>
          Right('{
            _root_.com.comcast.ip4s.Ipv4Address.fromString(${ Expr(s) }).get.asSourceSpecificMulticast.get
          })
        case None => Left("Invalid source specific IPv4 multicast address")

  object ssmipv6 extends Literally[SourceSpecificMulticast.Strict[Ipv6Address]]:
    def validate(s: String)(using Quotes) =
      Ipv6Address.fromString(s).flatMap(_.asSourceSpecificMulticast) match
        case Some(_) =>
          Right('{
            _root_.com.comcast.ip4s.Ipv6Address.fromString(${ Expr(s) }).get.asSourceSpecificMulticast.get
          })
        case None => Left("Invalid source specific IPv6 multicast address")

  object port extends Literally[Port]:
    def validate(s: String)(using Quotes) =
      s.toIntOption.flatMap(Port.fromInt) match
        case Some(_) => Right('{ _root_.com.comcast.ip4s.Port.fromInt(${ Expr(s.toInt) }).get })
        case None    => Left("Invalid port")

  object host extends Literally[Hostname]:
    def validate(s: String)(using Quotes) =
      Hostname.fromString(s) match
        case Some(_) => Right('{ _root_.com.comcast.ip4s.Hostname.fromString(${ Expr(s) }).get })
        case None    => Left("Invalid hostname")

  object idn extends Literally[IDN]:
    def validate(s: String)(using Quotes) =
      IDN.fromString(s) match
        case Some(_) => Right('{ _root_.com.comcast.ip4s.IDN.fromString(${ Expr(s) }).get })
        case None    => Left("Invalid IDN")


================================================
FILE: shared/src/main/scala-3/com/comcast/ip4s/CollectionCompat.scala
================================================
/*
 * Copyright 2018 Comcast Cable Communications Management, LLC
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.comcast.ip4s

import scala.jdk.CollectionConverters.*

private[ip4s] object Ip4sCollectionCompat {
  implicit class JIterableOps[A](private val self: java.lang.Iterable[A]) extends AnyVal {
    def asScala: Iterable[A] = IterableHasAsScala(self).asScala
  }
  implicit class ListOps[A](private val self: List[A]) extends AnyVal {
    def asJava: java.util.List[A] = SeqHasAsJava(self).asJava
  }
  implicit class EnumerationOps[A](private val self: java.util.Enumeration[A]) extends AnyVal {
    def asScala: Iterator[A] = EnumerationHasAsScala(self).asScala
  }
}


================================================
FILE: shared/src/main/scala-3/package.scala
================================================
/*
 * Copyright 2018 Comcast Cable Communications Management, LLC
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.comcast

package object ip4s extends ip4splatform


================================================
FILE: test-kit/js/src/test/scala/com/comcast/ip4s/TestPlatform.scala
================================================
/*
 * Copyright 2018 Comcast Cable Communications Management, LLC
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.comcast.ip4s

object TestPlatform {
  def isJVM = false
  def isJS = true
  def isNative = false
}


================================================
FILE: test-kit/jvm/src/test/scala/com/comcast/ip4s/Ipv6AddressJvmTest.scala
================================================
/*
 * Copyright 2018 Comcast Cable Communications Management, LLC
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.comcast.ip4s

import com.google.common.net.InetAddresses
import java.net.{InetAddress, Inet6Address}
import org.scalacheck.{Arbitrary, Gen, Test}

import org.scalacheck.Prop.forAll
import Arbitraries._

class Ipv6AddressJvmTest extends BaseTestSuite {
  override protected def scalaCheckTestParameters: Test.Parameters =
    super.scalaCheckTestParameters.withMinSuccessfulTests(10000)

  test("to string - roundtrips through strings") {
    forAll(Gen.listOfN(16, Arbitrary.arbitrary[Byte])) { bytesList =>
      if (bytesList.size == 16) {
        val bytes = bytesList.toArray
        val str =
          InetAddresses.toAddrString(InetAddress.getByAddress(bytes))
        assertEquals(Ipv6Address.fromString(str).map(_.toString), Some(str))
      }
    }
  }

  test("to string - follows RFC5952") {
    forAll(Gen.listOfN(16, Arbitrary.arbitrary[Byte])) { bytesList =>
      if (bytesList.size == 16) {
        val bytes = bytesList.toArray
        val expected =
          InetAddresses.toAddrString(InetAddress.getByAddress(bytes))
        assertEquals(Ipv6Address.fromBytes(bytes).map(_.toString), Some(expected))
      }
    }
  }

  test("support conversion to Inet6Address") {
    forAll { (ip: Ipv6Address) => assert(ip.toInetAddress.isInstanceOf[Inet6Address]) }
  }

  test("toInetAddress with IPv4 mapped addresses") {
    assertEquals(ip"::ffff:f:f".toInetAddress, InetAddress.getByName("::ffff:f:f"))
  }
}


================================================
FILE: test-kit/jvm/src/test/scala/com/comcast/ip4s/TestPlatform.scala
================================================
/*
 * Copyright 2018 Comcast Cable Communications Management, LLC
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.comcast.ip4s

object TestPlatform {
  def isJVM = true
  def isJS = false
  def isNative = false
}


================================================
FILE: test-kit/native/src/test/scala/com/comcast/ip4s/TestPlatform.scala
================================================
/*
 * Copyright 2018 Comcast Cable Communications Management, LLC
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.comcast.ip4s

object TestPlatform {
  def isJVM = false
  def isJS = false
  def isNative = true
}


================================================
FILE: test-kit/shared/src/main/scala/com/comcast/ip4s/Arbitraries.scala
================================================
/*
 * Copyright 2018 Comcast Cable Communications Management, LLC
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.comcast.ip4s

import org.scalacheck.{Arbitrary, Gen}

object Arbitraries {
  val ipv4Generator: Gen[Ipv4Address] = for {
    bytes <- Gen.listOfN(4, Arbitrary.arbitrary[Byte])
  } yield Ipv4Address.fromBytes(bytes.toArray).get

  implicit val ipv4Arbitrary: Arbitrary[Ipv4Address] = Arbitrary(ipv4Generator)

  val ipv6Generator: Gen[Ipv6Address] = for {
    bytes <- Gen.listOfN(16, Arbitrary.arbitrary[Byte])
  } yield Ipv6Address.fromBytes(bytes.toArray).get

  implicit val ipv6Arbitrary: Arbitrary[Ipv6Address] = Arbitrary(ipv6Generator)

  val ipGenerator: Gen[IpAddress] = Gen.oneOf(ipv4Generator, ipv6Generator)

  implicit val ipArbitrary: Arbitrary[IpAddress] = Arbitrary(ipGenerator)

  def cidrGenerator[A <: IpAddress](genIp: Gen[A]): Gen[Cidr[A]] =
    for {
      ip <- genIp
      bitLength = ip.fold(_ => 32, _ => 128)
      prefix <- Gen.chooseNum(0, bitLength)
    } yield ip / prefix

  implicit def cidrArbitrary[A <: IpAddress](implicit arbIp: Arbitrary[A]): Arbitrary[Cidr[A]] =
    Arbitrary(cidrGenerator(arbIp.arbitrary))

  implicit def cidrStrictArbitrary[A <: IpAddress](implicit arbIp: Arbitrary[A]): Arbitrary[Cidr.Strict[A]] = Arbitrary(
    cidrGenerator(arbIp.arbitrary).map(_.normalized)
  )

  val portGenerator: Gen[Port] = Gen.chooseNum(0, 65535).map(Port.fromInt(_).get)

  implicit val portArbitrary: Arbitrary[Port] = Arbitrary(portGenerator)

  def socketAddressGenerator[A <: IpAddress](genIp: Gen[A], genPort: Gen[Port]): Gen[SocketAddress[A]] =
    for {
      ip 
Download .txt
gitextract_slxgux55/

├── .git-blame-ignore-revs
├── .github/
│   └── workflows/
│       ├── ci.yml
│       └── clean.yml
├── .gitignore
├── .sbtopts
├── .scalafmt.conf
├── CODE_OF_CONDUCT.md
├── CONTRIBUTING
├── LICENSE
├── NOTICE
├── README.md
├── build.sbt
├── docs/
│   ├── guide.md
│   └── src/
│       └── guide.md
├── js/
│   └── src/
│       └── main/
│           └── scala/
│               └── com/
│                   └── comcast/
│                       └── ip4s/
│                           ├── DnsPlatform.scala
│                           ├── IpAddressPlatform.scala
│                           ├── NetworkInterfacePlatform.scala
│                           ├── NetworkInterfacesPlatform.scala
│                           ├── SocketAddressPlatform.scala
│                           ├── UnknownHostException.scala
│                           └── ip4splatform.scala
├── jvm-native/
│   └── src/
│       └── main/
│           └── scala/
│               └── com/
│                   └── comcast/
│                       └── ip4s/
│                           ├── DnsPlatform.scala
│                           ├── IpAddressPlatform.scala
│                           ├── NetworkInterfacePlatform.scala
│                           ├── NetworkInterfacesPlatform.scala
│                           ├── SocketAddressPlatform.scala
│                           └── ip4splatform.scala
├── project/
│   ├── build.properties
│   └── plugins.sbt
├── shared/
│   └── src/
│       └── main/
│           ├── scala/
│           │   └── com/
│           │       └── comcast/
│           │           └── ip4s/
│           │               ├── Cidr.scala
│           │               ├── Dns.scala
│           │               ├── GenSocketAddress.scala
│           │               ├── Host.scala
│           │               ├── MacAddress.scala
│           │               ├── Multicast.scala
│           │               ├── MulticastJoin.scala
│           │               ├── MulticastSocketAddress.scala
│           │               ├── NetworkInterface.scala
│           │               ├── NetworkInterfaces.scala
│           │               ├── Port.scala
│           │               ├── SocketAddress.scala
│           │               └── UnixSocketAddress.scala
│           ├── scala-2/
│           │   ├── Literals.scala
│           │   └── package.scala
│           ├── scala-2.12/
│           │   └── com/
│           │       └── comcast/
│           │           └── ip4s/
│           │               └── CollectionCompat.scala
│           ├── scala-2.13/
│           │   └── com/
│           │       └── comcast/
│           │           └── ip4s/
│           │               └── CollectionCompat.scala
│           └── scala-3/
│               ├── Literals.scala
│               ├── com/
│               │   └── comcast/
│               │       └── ip4s/
│               │           └── CollectionCompat.scala
│               └── package.scala
└── test-kit/
    ├── js/
    │   └── src/
    │       └── test/
    │           └── scala/
    │               └── com/
    │                   └── comcast/
    │                       └── ip4s/
    │                           └── TestPlatform.scala
    ├── jvm/
    │   └── src/
    │       └── test/
    │           └── scala/
    │               └── com/
    │                   └── comcast/
    │                       └── ip4s/
    │                           ├── Ipv6AddressJvmTest.scala
    │                           └── TestPlatform.scala
    ├── native/
    │   └── src/
    │       └── test/
    │           └── scala/
    │               └── com/
    │                   └── comcast/
    │                       └── ip4s/
    │                           └── TestPlatform.scala
    └── shared/
        └── src/
            ├── main/
            │   └── scala/
            │       └── com/
            │           └── comcast/
            │               └── ip4s/
            │                   └── Arbitraries.scala
            └── test/
                └── scala/
                    └── com/
                        └── comcast/
                            └── ip4s/
                                ├── BaseTestSuite.scala
                                ├── CidrStrictTest.scala
                                ├── CidrTest.scala
                                ├── DnsTest.scala
                                ├── Examples.scala
                                ├── HostnameTest.scala
                                ├── IDNTest.scala
                                ├── IdnaSuite.scala
                                ├── Ipv4AddressTest.scala
                                ├── Ipv6AddressTest.scala
                                ├── MacAddressTest.scala
                                ├── MulticastSocketAddressTest.scala
                                ├── MulticastTest.scala
                                ├── NetworkInterfacesTest.scala
                                ├── PortTest.scala
                                └── SocketAddressTest.scala
Condensed preview — 70 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (244K chars).
[
  {
    "path": ".git-blame-ignore-revs",
    "chars": 87,
    "preview": "# Scala Steward: Reformat with scalafmt 3.9.7\n5ccb66cf1c2c22e7c4aeb4c73fec92b874e6d296\n"
  },
  {
    "path": ".github/workflows/ci.yml",
    "chars": 9676,
    "preview": "# This file was automatically generated by sbt-github-actions using the\n# githubWorkflowGenerate task. You should add an"
  },
  {
    "path": ".github/workflows/clean.yml",
    "chars": 2337,
    "preview": "# This file was automatically generated by sbt-github-actions using the\n# githubWorkflowGenerate task. You should add an"
  },
  {
    "path": ".gitignore",
    "chars": 176,
    "preview": "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."
  },
  {
    "path": ".sbtopts",
    "chars": 46,
    "preview": "-J-Xms2g\n-J-Xmx4g\n-J-XX:MaxMetaspaceSize=512m\n"
  },
  {
    "path": ".scalafmt.conf",
    "chars": 391,
    "preview": "version = \"3.10.7\"\n\nmaxColumn: 120\n\nrunner.dialect = scala213source3\n\nproject.layout = StandardConvention\n\nrewrite.scala"
  },
  {
    "path": "CODE_OF_CONDUCT.md",
    "chars": 746,
    "preview": "# Code of Conduct\n\nWe are committed to providing a friendly, safe and welcoming environment for all, regardless of level"
  },
  {
    "path": "CONTRIBUTING",
    "chars": 579,
    "preview": "If you would like to contribute code to this project you can do so through\nGitHub by forking the repository and sending "
  },
  {
    "path": "LICENSE",
    "chars": 11357,
    "preview": "                                 Apache License\n                           Version 2.0, January 2004\n                   "
  },
  {
    "path": "NOTICE",
    "chars": 3178,
    "preview": "IP Addresses for Scala and Scala.js\nCopyright 2018 Comcast Cable Communications Management, LLC\nLicensed under the Apach"
  },
  {
    "path": "README.md",
    "chars": 1664,
    "preview": "[![Published Artifact](https://img.shields.io/maven-central/v/com.comcast/ip4s-core_3.svg)](http://search.maven.org/#sea"
  },
  {
    "path": "build.sbt",
    "chars": 4992,
    "preview": "import com.typesafe.tools.mima.core._\n\nThisBuild / tlBaseVersion := \"3.7\"\n\nThisBuild / organization := \"com.comcast\"\nThi"
  },
  {
    "path": "docs/guide.md",
    "chars": 16060,
    "preview": "ip4s: IP Addresses for Scala & Scala.js\n=======================================\n\nThis is the guide for IP Addresses for "
  },
  {
    "path": "docs/src/guide.md",
    "chars": 13224,
    "preview": "ip4s: IP Addresses for Scala & Scala.js\n=======================================\n\nThis is the guide for IP Addresses for "
  },
  {
    "path": "js/src/main/scala/com/comcast/ip4s/DnsPlatform.scala",
    "chars": 4060,
    "preview": "/*\n * Copyright 2018 Comcast Cable Communications Management, LLC\n *\n * Licensed under the Apache License, Version 2.0 ("
  },
  {
    "path": "js/src/main/scala/com/comcast/ip4s/IpAddressPlatform.scala",
    "chars": 972,
    "preview": "/*\n * Copyright 2018 Comcast Cable Communications Management, LLC\n *\n * Licensed under the Apache License, Version 2.0 ("
  },
  {
    "path": "js/src/main/scala/com/comcast/ip4s/NetworkInterfacePlatform.scala",
    "chars": 709,
    "preview": "/*\n * Copyright 2018 Comcast Cable Communications Management, LLC\n *\n * Licensed under the Apache License, Version 2.0 ("
  },
  {
    "path": "js/src/main/scala/com/comcast/ip4s/NetworkInterfacesPlatform.scala",
    "chars": 2317,
    "preview": "/*\n * Copyright 2018 Comcast Cable Communications Management, LLC\n *\n * Licensed under the Apache License, Version 2.0 ("
  },
  {
    "path": "js/src/main/scala/com/comcast/ip4s/SocketAddressPlatform.scala",
    "chars": 760,
    "preview": "/*\n * Copyright 2018 Comcast Cable Communications Management, LLC\n *\n * Licensed under the Apache License, Version 2.0 ("
  },
  {
    "path": "js/src/main/scala/com/comcast/ip4s/UnknownHostException.scala",
    "chars": 1029,
    "preview": "/*\n * Copyright 2018 Comcast Cable Communications Management, LLC\n *\n * Licensed under the Apache License, Version 2.0 ("
  },
  {
    "path": "js/src/main/scala/com/comcast/ip4s/ip4splatform.scala",
    "chars": 691,
    "preview": "/*\n * Copyright 2018 Comcast Cable Communications Management, LLC\n *\n * Licensed under the Apache License, Version 2.0 ("
  },
  {
    "path": "jvm-native/src/main/scala/com/comcast/ip4s/DnsPlatform.scala",
    "chars": 2443,
    "preview": "/*\n * Copyright 2018 Comcast Cable Communications Management, LLC\n *\n * Licensed under the Apache License, Version 2.0 ("
  },
  {
    "path": "jvm-native/src/main/scala/com/comcast/ip4s/IpAddressPlatform.scala",
    "chars": 2334,
    "preview": "/*\n * Copyright 2018 Comcast Cable Communications Management, LLC\n *\n * Licensed under the Apache License, Version 2.0 ("
  },
  {
    "path": "jvm-native/src/main/scala/com/comcast/ip4s/NetworkInterfacePlatform.scala",
    "chars": 1325,
    "preview": "/*\n * Copyright 2018 Comcast Cable Communications Management, LLC\n *\n * Licensed under the Apache License, Version 2.0 ("
  },
  {
    "path": "jvm-native/src/main/scala/com/comcast/ip4s/NetworkInterfacesPlatform.scala",
    "chars": 1258,
    "preview": "/*\n * Copyright 2018 Comcast Cable Communications Management, LLC\n *\n * Licensed under the Apache License, Version 2.0 ("
  },
  {
    "path": "jvm-native/src/main/scala/com/comcast/ip4s/SocketAddressPlatform.scala",
    "chars": 1714,
    "preview": "/*\n * Copyright 2018 Comcast Cable Communications Management, LLC\n *\n * Licensed under the Apache License, Version 2.0 ("
  },
  {
    "path": "jvm-native/src/main/scala/com/comcast/ip4s/ip4splatform.scala",
    "chars": 755,
    "preview": "/*\n * Copyright 2018 Comcast Cable Communications Management, LLC\n *\n * Licensed under the Apache License, Version 2.0 ("
  },
  {
    "path": "project/build.properties",
    "chars": 19,
    "preview": "sbt.version=1.12.5\n"
  },
  {
    "path": "project/plugins.sbt",
    "chars": 517,
    "preview": "addSbtPlugin(\"org.typelevel\" % \"sbt-typelevel\" % \"0.8.5\")\naddSbtPlugin(\"org.scala-js\" % \"sbt-scalajs\" % \"1.20.2\")\naddSbt"
  },
  {
    "path": "shared/src/main/scala/com/comcast/ip4s/Cidr.scala",
    "chars": 7991,
    "preview": "/*\n * Copyright 2018 Comcast Cable Communications Management, LLC\n *\n * Licensed under the Apache License, Version 2.0 ("
  },
  {
    "path": "shared/src/main/scala/com/comcast/ip4s/Dns.scala",
    "chars": 3486,
    "preview": "/*\n * Copyright 2018 Comcast Cable Communications Management, LLC\n *\n * Licensed under the Apache License, Version 2.0 ("
  },
  {
    "path": "shared/src/main/scala/com/comcast/ip4s/GenSocketAddress.scala",
    "chars": 1649,
    "preview": "/*\n * Copyright 2018 Comcast Cable Communications Management, LLC\n *\n * Licensed under the Apache License, Version 2.0 ("
  },
  {
    "path": "shared/src/main/scala/com/comcast/ip4s/Host.scala",
    "chars": 38637,
    "preview": "/*\n * Copyright 2018 Comcast Cable Communications Management, LLC\n *\n * Licensed under the Apache License, Version 2.0 ("
  },
  {
    "path": "shared/src/main/scala/com/comcast/ip4s/MacAddress.scala",
    "chars": 3633,
    "preview": "/*\n * Copyright 2018 Comcast Cable Communications Management, LLC\n *\n * Licensed under the Apache License, Version 2.0 ("
  },
  {
    "path": "shared/src/main/scala/com/comcast/ip4s/Multicast.scala",
    "chars": 4498,
    "preview": "/*\n * Copyright 2018 Comcast Cable Communications Management, LLC\n *\n * Licensed under the Apache License, Version 2.0 ("
  },
  {
    "path": "shared/src/main/scala/com/comcast/ip4s/MulticastJoin.scala",
    "chars": 5501,
    "preview": "/*\n * Copyright 2018 Comcast Cable Communications Management, LLC\n *\n * Licensed under the Apache License, Version 2.0 ("
  },
  {
    "path": "shared/src/main/scala/com/comcast/ip4s/MulticastSocketAddress.scala",
    "chars": 4683,
    "preview": "/*\n * Copyright 2018 Comcast Cable Communications Management, LLC\n *\n * Licensed under the Apache License, Version 2.0 ("
  },
  {
    "path": "shared/src/main/scala/com/comcast/ip4s/NetworkInterface.scala",
    "chars": 2226,
    "preview": "/*\n * Copyright 2018 Comcast Cable Communications Management, LLC\n *\n * Licensed under the Apache License, Version 2.0 ("
  },
  {
    "path": "shared/src/main/scala/com/comcast/ip4s/NetworkInterfaces.scala",
    "chars": 2214,
    "preview": "/*\n * Copyright 2018 Comcast Cable Communications Management, LLC\n *\n * Licensed under the Apache License, Version 2.0 ("
  },
  {
    "path": "shared/src/main/scala/com/comcast/ip4s/Port.scala",
    "chars": 1994,
    "preview": "/*\n * Copyright 2018 Comcast Cable Communications Management, LLC\n *\n * Licensed under the Apache License, Version 2.0 ("
  },
  {
    "path": "shared/src/main/scala/com/comcast/ip4s/SocketAddress.scala",
    "chars": 3469,
    "preview": "/*\n * Copyright 2018 Comcast Cable Communications Management, LLC\n *\n * Licensed under the Apache License, Version 2.0 ("
  },
  {
    "path": "shared/src/main/scala/com/comcast/ip4s/UnixSocketAddress.scala",
    "chars": 1113,
    "preview": "/*\n * Copyright 2018 Comcast Cable Communications Management, LLC\n *\n * Licensed under the Apache License, Version 2.0 ("
  },
  {
    "path": "shared/src/main/scala-2/Literals.scala",
    "chars": 6873,
    "preview": "/*\n * Copyright 2018 Comcast Cable Communications Management, LLC\n *\n * Licensed under the Apache License, Version 2.0 ("
  },
  {
    "path": "shared/src/main/scala-2/package.scala",
    "chars": 1746,
    "preview": "/*\n * Copyright 2018 Comcast Cable Communications Management, LLC\n *\n * Licensed under the Apache License, Version 2.0 ("
  },
  {
    "path": "shared/src/main/scala-2.12/com/comcast/ip4s/CollectionCompat.scala",
    "chars": 1195,
    "preview": "/*\n * Copyright 2018 Comcast Cable Communications Management, LLC\n *\n * Licensed under the Apache License, Version 2.0 ("
  },
  {
    "path": "shared/src/main/scala-2.13/com/comcast/ip4s/CollectionCompat.scala",
    "chars": 1206,
    "preview": "/*\n * Copyright 2018 Comcast Cable Communications Management, LLC\n *\n * Licensed under the Apache License, Version 2.0 ("
  },
  {
    "path": "shared/src/main/scala-3/Literals.scala",
    "chars": 5899,
    "preview": "/*\n * Copyright 2018 Comcast Cable Communications Management, LLC\n *\n * Licensed under the Apache License, Version 2.0 ("
  },
  {
    "path": "shared/src/main/scala-3/com/comcast/ip4s/CollectionCompat.scala",
    "chars": 1206,
    "preview": "/*\n * Copyright 2018 Comcast Cable Communications Management, LLC\n *\n * Licensed under the Apache License, Version 2.0 ("
  },
  {
    "path": "shared/src/main/scala-3/package.scala",
    "chars": 691,
    "preview": "/*\n * Copyright 2018 Comcast Cable Communications Management, LLC\n *\n * Licensed under the Apache License, Version 2.0 ("
  },
  {
    "path": "test-kit/js/src/test/scala/com/comcast/ip4s/TestPlatform.scala",
    "chars": 740,
    "preview": "/*\n * Copyright 2018 Comcast Cable Communications Management, LLC\n *\n * Licensed under the Apache License, Version 2.0 ("
  },
  {
    "path": "test-kit/jvm/src/test/scala/com/comcast/ip4s/Ipv6AddressJvmTest.scala",
    "chars": 2065,
    "preview": "/*\n * Copyright 2018 Comcast Cable Communications Management, LLC\n *\n * Licensed under the Apache License, Version 2.0 ("
  },
  {
    "path": "test-kit/jvm/src/test/scala/com/comcast/ip4s/TestPlatform.scala",
    "chars": 740,
    "preview": "/*\n * Copyright 2018 Comcast Cable Communications Management, LLC\n *\n * Licensed under the Apache License, Version 2.0 ("
  },
  {
    "path": "test-kit/native/src/test/scala/com/comcast/ip4s/TestPlatform.scala",
    "chars": 740,
    "preview": "/*\n * Copyright 2018 Comcast Cable Communications Management, LLC\n *\n * Licensed under the Apache License, Version 2.0 ("
  },
  {
    "path": "test-kit/shared/src/main/scala/com/comcast/ip4s/Arbitraries.scala",
    "chars": 6290,
    "preview": "/*\n * Copyright 2018 Comcast Cable Communications Management, LLC\n *\n * Licensed under the Apache License, Version 2.0 ("
  },
  {
    "path": "test-kit/shared/src/test/scala/com/comcast/ip4s/BaseTestSuite.scala",
    "chars": 1351,
    "preview": "/*\n * Copyright 2018 Comcast Cable Communications Management, LLC\n *\n * Licensed under the Apache License, Version 2.0 ("
  },
  {
    "path": "test-kit/shared/src/test/scala/com/comcast/ip4s/CidrStrictTest.scala",
    "chars": 900,
    "preview": "/*\n * Copyright 2018 Comcast Cable Communications Management, LLC\n *\n * Licensed under the Apache License, Version 2.0 ("
  },
  {
    "path": "test-kit/shared/src/test/scala/com/comcast/ip4s/CidrTest.scala",
    "chars": 1807,
    "preview": "/*\n * Copyright 2018 Comcast Cable Communications Management, LLC\n *\n * Licensed under the Apache License, Version 2.0 ("
  },
  {
    "path": "test-kit/shared/src/test/scala/com/comcast/ip4s/DnsTest.scala",
    "chars": 2427,
    "preview": "/*\n * Copyright 2018 Comcast Cable Communications Management, LLC\n *\n * Licensed under the Apache License, Version 2.0 ("
  },
  {
    "path": "test-kit/shared/src/test/scala/com/comcast/ip4s/Examples.scala",
    "chars": 1254,
    "preview": "/*\n * Copyright 2018 Comcast Cable Communications Management, LLC\n *\n * Licensed under the Apache License, Version 2.0 ("
  },
  {
    "path": "test-kit/shared/src/test/scala/com/comcast/ip4s/HostnameTest.scala",
    "chars": 2014,
    "preview": "/*\n * Copyright 2018 Comcast Cable Communications Management, LLC\n *\n * Licensed under the Apache License, Version 2.0 ("
  },
  {
    "path": "test-kit/shared/src/test/scala/com/comcast/ip4s/IDNTest.scala",
    "chars": 2623,
    "preview": "/*\n * Copyright 2018 Comcast Cable Communications Management, LLC\n *\n * Licensed under the Apache License, Version 2.0 ("
  },
  {
    "path": "test-kit/shared/src/test/scala/com/comcast/ip4s/IdnaSuite.scala",
    "chars": 5547,
    "preview": "/*\n * Copyright 2018 Comcast Cable Communications Management, LLC\n *\n * Licensed under the Apache License, Version 2.0 ("
  },
  {
    "path": "test-kit/shared/src/test/scala/com/comcast/ip4s/Ipv4AddressTest.scala",
    "chars": 3362,
    "preview": "/*\n * Copyright 2018 Comcast Cable Communications Management, LLC\n *\n * Licensed under the Apache License, Version 2.0 ("
  },
  {
    "path": "test-kit/shared/src/test/scala/com/comcast/ip4s/Ipv6AddressTest.scala",
    "chars": 5814,
    "preview": "/*\n * Copyright 2018 Comcast Cable Communications Management, LLC\n *\n * Licensed under the Apache License, Version 2.0 ("
  },
  {
    "path": "test-kit/shared/src/test/scala/com/comcast/ip4s/MacAddressTest.scala",
    "chars": 1358,
    "preview": "/*\n * Copyright 2018 Comcast Cable Communications Management, LLC\n *\n * Licensed under the Apache License, Version 2.0 ("
  },
  {
    "path": "test-kit/shared/src/test/scala/com/comcast/ip4s/MulticastSocketAddressTest.scala",
    "chars": 968,
    "preview": "/*\n * Copyright 2018 Comcast Cable Communications Management, LLC\n *\n * Licensed under the Apache License, Version 2.0 ("
  },
  {
    "path": "test-kit/shared/src/test/scala/com/comcast/ip4s/MulticastTest.scala",
    "chars": 1337,
    "preview": "/*\n * Copyright 2018 Comcast Cable Communications Management, LLC\n *\n * Licensed under the Apache License, Version 2.0 ("
  },
  {
    "path": "test-kit/shared/src/test/scala/com/comcast/ip4s/NetworkInterfacesTest.scala",
    "chars": 1745,
    "preview": "/*\n * Copyright 2018 Comcast Cable Communications Management, LLC\n *\n * Licensed under the Apache License, Version 2.0 ("
  },
  {
    "path": "test-kit/shared/src/test/scala/com/comcast/ip4s/PortTest.scala",
    "chars": 872,
    "preview": "/*\n * Copyright 2018 Comcast Cable Communications Management, LLC\n *\n * Licensed under the Apache License, Version 2.0 ("
  },
  {
    "path": "test-kit/shared/src/test/scala/com/comcast/ip4s/SocketAddressTest.scala",
    "chars": 913,
    "preview": "/*\n * Copyright 2018 Comcast Cable Communications Management, LLC\n *\n * Licensed under the Apache License, Version 2.0 ("
  }
]

About this extraction

This page contains the full source code of the Comcast/ip4s GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 70 files (224.8 KB), approximately 63.9k tokens. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!