diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index ed440a0c5cc..77dc75fd567 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -20,7 +20,8 @@ /TRIAGE.md @chipkent @rcaudy /licenses @chipkent @rcaudy /docker @devinrsmith @jcferretti @rcaudy -/engine/function/ @chipkent @kosak @rcaudy -/py @chipkent @jmao-denver @rcaudy -/R @chipkent @alexpeters1208 @rcaudy -*.proto @devinrsmith @nbauernfeind @niloc132 @rcaudy +/engine/function/ @chipkent @kosak @rcaudy @cpwright +/py @chipkent @jmao-denver @rcaudy @cpwright +/R @chipkent @rcaudy @cpwright +*.proto @devinrsmith @nbauernfeind @niloc132 @rcaudy @cpwright +*.gwt.xml @niloc132 @rcaudy @nbauernfeind @cpwright diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 5ace4600a1f..425314d8db0 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -2,5 +2,29 @@ version: 2 updates: - package-ecosystem: "github-actions" directory: "/" + open-pull-requests-limit: 99 schedule: interval: "weekly" + commit-message: + prefix: "chore(github-actions)" + labels: + - "NoDocumentationNeeded" + - "NoReleaseNotesNeeded" + - "version-bump" + assignees: + - "devinrsmith" + - "stanbrub" + - package-ecosystem: "gradle" + directory: "/" + open-pull-requests-limit: 99 + schedule: + interval: "weekly" + commit-message: + prefix: "chore(gradle)" + labels: + - "NoDocumentationNeeded" + - "NoReleaseNotesNeeded" + - "version-bump" + assignees: + - "devinrsmith" + - "rcaudy" diff --git a/.github/scripts/gradle-properties.sh b/.github/scripts/gradle-properties.sh index 88f00dce804..5a468670ef0 100755 --- a/.github/scripts/gradle-properties.sh +++ b/.github/scripts/gradle-properties.sh @@ -39,6 +39,10 @@ JAVA_INSTALL_PATHS="${JAVA_INSTALL_PATHS}${JAVA_HOME_20_X64:+$JAVA_HOME_20_X64,} JAVA_INSTALL_PATHS="${JAVA_INSTALL_PATHS}${JAVA_HOME_21_X64:+$JAVA_HOME_21_X64,}" JAVA_INSTALL_PATHS="${JAVA_INSTALL_PATHS}${JAVA_HOME_22_X64:+$JAVA_HOME_22_X64,}" JAVA_INSTALL_PATHS="${JAVA_INSTALL_PATHS}${JAVA_HOME_23_X64:+$JAVA_HOME_23_X64,}" +JAVA_INSTALL_PATHS="${JAVA_INSTALL_PATHS}${JAVA_HOME_24_X64:+$JAVA_HOME_24_X64,}" +JAVA_INSTALL_PATHS="${JAVA_INSTALL_PATHS}${JAVA_HOME_25_X64:+$JAVA_HOME_25_X64,}" +JAVA_INSTALL_PATHS="${JAVA_INSTALL_PATHS}${JAVA_HOME_26_X64:+$JAVA_HOME_26_X64,}" +JAVA_INSTALL_PATHS="${JAVA_INSTALL_PATHS}${JAVA_HOME_27_X64:+$JAVA_HOME_27_X64,}" # Our CI JDKs should be pre-provisioned and invoked correctly, # we shouldn't rely on gradle for any of this logic. diff --git a/.github/workflows/build-ci.yml b/.github/workflows/build-ci.yml index 3c250fcfc54..a8eecea3f1f 100644 --- a/.github/workflows/build-ci.yml +++ b/.github/workflows/build-ci.yml @@ -29,6 +29,9 @@ jobs: distribution: 'temurin' java-version: '17' + - name: Setup Gradle + uses: gradle/actions/setup-gradle@v4 + - name: Set JAVA_HOME run: echo "JAVA_HOME=${{ steps.setup-java-11.outputs.path }}" >> $GITHUB_ENV @@ -88,11 +91,7 @@ jobs: password: ${{ secrets.GITHUB_TOKEN }} - name: Create Dockerfile and context - uses: burrunan/gradle-cache-action@v1 - with: - job-id: build-server - arguments: --scan outputVersion docker-server-slim:prepareDocker docker-server:prepareDockerAll - gradle-version: wrapper + run: ./gradlew --scan outputVersion docker-server-slim:prepareDocker docker-server:prepareDockerAll - name: Get Deephaven Version id: deephaven_version @@ -103,7 +102,7 @@ jobs: # https://github.com/docker/build-push-action/blob/master/docs/advanced/cache.md#github-cache # https://github.com/docker/buildx/pull/535 - name: Docker build server - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: build-args: | BASE=deephaven/server-base:local-build @@ -116,7 +115,7 @@ jobs: # Note: server-slim does not need BASE/SERVER build-args like the other server images - name: Docker build server slim - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: build-args: | DEEPHAVEN_VERSION=${{ steps.deephaven_version.outputs.deephaven_version }} @@ -158,6 +157,9 @@ jobs: distribution: 'temurin' java-version: '11' + - name: Setup Gradle + uses: gradle/actions/setup-gradle@v4 + - name: Set JAVA_HOME run: echo "JAVA_HOME=${{ steps.setup-java-11.outputs.path }}" >> $GITHUB_ENV @@ -182,11 +184,7 @@ jobs: password: ${{ secrets.GITHUB_TOKEN }} - name: Create Dockerfile and context - uses: burrunan/gradle-cache-action@v1 - with: - job-id: build-web - arguments: --scan outputVersion docker-web-plugin-packager:prepareDocker - gradle-version: wrapper + run: ./gradlew --scan outputVersion docker-web-plugin-packager:prepareDocker - name: Get Deephaven Version id: deephaven_version @@ -194,7 +192,7 @@ jobs: echo "deephaven_version=$(cat build/version)" >> $GITHUB_OUTPUT - name: Docker build - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: build-args: | DEEPHAVEN_VERSION=${{ steps.deephaven_version.outputs.deephaven_version }} diff --git a/.github/workflows/check-ci.yml b/.github/workflows/check-ci.yml index 56485ce65bb..37b1592322a 100644 --- a/.github/workflows/check-ci.yml +++ b/.github/workflows/check-ci.yml @@ -33,6 +33,9 @@ jobs: distribution: 'temurin' java-version: '17' + - name: Setup Gradle + uses: gradle/actions/setup-gradle@v4 + - name: Set JAVA_HOME run: echo "JAVA_HOME=${{ steps.setup-java-11.outputs.path }}" >> $GITHUB_ENV @@ -42,11 +45,7 @@ jobs: cat gradle.properties - name: Check - uses: burrunan/gradle-cache-action@v1 - with: - job-id: checks - arguments: --scan --continue check - gradle-version: wrapper + run: ./gradlew --scan --continue check - name: Upload Test Results uses: actions/upload-artifact@v4 diff --git a/.github/workflows/cla.yml b/.github/workflows/cla.yml index d337a5782cf..1cf714dca75 100644 --- a/.github/workflows/cla.yml +++ b/.github/workflows/cla.yml @@ -21,7 +21,7 @@ jobs: steps: - name: "CLA Assistant" if: (github.event.comment.body == 'recheck' || github.event.comment.body == 'I have read the CLA Document and I hereby sign the CLA') || github.event_name == 'pull_request_target' - uses: cla-assistant/github-action@v2.4.0 + uses: cla-assistant/github-action@v2.6.1 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} PERSONAL_ACCESS_TOKEN : ${{ secrets.CLA_PERSONAL_ACCESS_TOKEN }} diff --git a/.github/workflows/conventional-pr-check.yml b/.github/workflows/conventional-pr-check.yml new file mode 100644 index 00000000000..8eb4be6bda7 --- /dev/null +++ b/.github/workflows/conventional-pr-check.yml @@ -0,0 +1,40 @@ +name: 'Conventional PR' + +on: + pull_request_target: + types: + - opened + - edited + - synchronize + +jobs: + pr-check: + runs-on: ubuntu-22.04 + steps: + - uses: amannn/action-semantic-pull-request@v5 + id: lint_pr_title + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + - uses: marocchino/sticky-pull-request-comment@v2 + # When the previous steps fails, the workflow would stop. By adding this + # condition you can continue the execution with the populated error message. + if: always() && (steps.lint_pr_title.outputs.error_message != null) + with: + header: pr-title-lint-error + message: | + Pull Request titles must follow the [Conventional Commits specification](https://www.conventionalcommits.org/en/v1.0.0/). + Breaking changes that are expected to impact users must include "BREAKING CHANGE: " at the end of the PR description. + + Details: + + ``` + ${{ steps.lint_pr_title.outputs.error_message }} + ``` + + # Delete a previous comment when the issue has been resolved + - if: ${{ steps.lint_pr_title.outputs.error_message == null }} + uses: marocchino/sticky-pull-request-comment@v2 + with: + header: pr-title-lint-error + delete: true diff --git a/.github/workflows/create-docs-issues.yml b/.github/workflows/create-docs-issues.yml index 77f52569deb..6e6fc938ea5 100644 --- a/.github/workflows/create-docs-issues.yml +++ b/.github/workflows/create-docs-issues.yml @@ -26,7 +26,7 @@ jobs: await script({github, context}); - name: Slack Failure Message - uses: slackapi/slack-github-action@v1.26.0 + uses: slackapi/slack-github-action@v1.27.0 id: slack-failure-message if: failure() && github.ref == 'refs/heads/main' && github.repository_owner == 'deephaven' env: diff --git a/.github/workflows/dependency-submission.yml b/.github/workflows/dependency-submission.yml index 634846a22af..9d217aa7743 100644 --- a/.github/workflows/dependency-submission.yml +++ b/.github/workflows/dependency-submission.yml @@ -30,7 +30,7 @@ jobs: cat gradle.properties - name: Generate and submit dependency graph - uses: gradle/actions/dependency-submission@v3 + uses: gradle/actions/dependency-submission@v4 env: # Dependencies derived from :server-jetty-app runtimeClasspath get a "runtime" scope and everything else gets # a "development" scope. Ideally, gradle would be able to pass along the finer dependency details (for diff --git a/.github/workflows/docs-ci.yml b/.github/workflows/docs-ci.yml index cafe7d6c53e..34e75095638 100644 --- a/.github/workflows/docs-ci.yml +++ b/.github/workflows/docs-ci.yml @@ -16,7 +16,7 @@ jobs: cd tmp-deephaven-core-v2/${{ github.ref_name }}/ mkdir -p javadoc pydoc client-api cd client-api - mkdir -p javascript python cpp-examples cpp r + mkdir -p javascript python cpp-examples cpp r protobuf - name: Deploy Directories if: ${{ github.event_name == 'push' }} @@ -31,7 +31,7 @@ jobs: remote_key: ${{ secrets.DEEPHAVEN_CORE_SSH_KEY }} symlink: if: ${{ github.event_name == 'push' && startsWith(github.ref, 'refs/heads/release/v') }} - needs: [javadoc, typedoc, pydoc, cppdoc, rdoc] + needs: [javadoc, typedoc, pydoc, cppdoc, rdoc, protodoc] runs-on: ubuntu-22.04 steps: - name: Setup Git @@ -84,6 +84,9 @@ jobs: distribution: 'temurin' java-version: '17' + - name: Setup Gradle + uses: gradle/actions/setup-gradle@v4 + - name: Set JAVA_HOME run: echo "JAVA_HOME=${{ steps.setup-java-11.outputs.path }}" >> $GITHUB_ENV @@ -93,12 +96,8 @@ jobs: cat gradle.properties - name: All Javadoc - uses: burrunan/gradle-cache-action@v1 - with: - job-id: allJavadoc - arguments: --scan outputVersion combined-javadoc:allJavadoc - gradle-version: wrapper - + run: ./gradlew --scan outputVersion combined-javadoc:allJavadoc + - name: Get Deephaven Version id: dhc-version run: echo "version=$(cat build/version)" >> $GITHUB_OUTPUT @@ -147,6 +146,9 @@ jobs: distribution: 'temurin' java-version: '17' + - name: Setup Gradle + uses: gradle/actions/setup-gradle@v4 + - name: Set JAVA_HOME run: echo "JAVA_HOME=${{ steps.setup-java-11.outputs.path }}" >> $GITHUB_ENV @@ -156,12 +158,8 @@ jobs: cat gradle.properties - name: Run typedoc on JS API - uses: burrunan/gradle-cache-action@v1 - with: - job-id: typedoc - arguments: --scan outputVersion :web-client-api:types:typedoc - gradle-version: wrapper - + run: ./gradlew --scan outputVersion :web-client-api:types:typedoc + - name: Get Deephaven Version id: dhc-version run: echo "version=$(cat build/version)" >> $GITHUB_OUTPUT @@ -210,6 +208,9 @@ jobs: distribution: 'temurin' java-version: '17' + - name: Setup Gradle + uses: gradle/actions/setup-gradle@v4 + - name: Set JAVA_HOME run: echo "JAVA_HOME=${{ steps.setup-java-11.outputs.path }}" >> $GITHUB_ENV @@ -219,12 +220,8 @@ jobs: cat gradle.properties - name: Generate Python Docs - uses: burrunan/gradle-cache-action@v1 - with: - job-id: pythonDocs - arguments: --scan outputVersion sphinx:pythonDocs sphinx:pydeephavenDocs - gradle-version: wrapper - + run: ./gradlew --scan outputVersion sphinx:pythonDocs sphinx:pydeephavenDocs + - name: Get Deephaven Version id: dhc-version run: echo "version=$(cat build/version)" >> $GITHUB_OUTPUT @@ -293,6 +290,9 @@ jobs: distribution: 'temurin' java-version: '11' + - name: Setup Gradle + uses: gradle/actions/setup-gradle@v4 + - name: Set JAVA_HOME run: echo "JAVA_HOME=${{ steps.setup-java-11.outputs.path }}" >> $GITHUB_ENV @@ -302,12 +302,8 @@ jobs: cat gradle.properties - name: Generate C++ Docs - uses: burrunan/gradle-cache-action@v1 - with: - job-id: cppDocs - arguments: --scan outputVersion sphinx:cppClientDocs sphinx:cppExamplesDocs - gradle-version: wrapper - + run: ./gradlew --scan outputVersion sphinx:cppClientDocs sphinx:cppExamplesDocs + - name: Get Deephaven Version id: dhc-version run: echo "version=$(cat build/version)" >> $GITHUB_OUTPUT @@ -368,6 +364,9 @@ jobs: distribution: 'temurin' java-version: '11' + - name: Setup Gradle + uses: gradle/actions/setup-gradle@v4 + - name: Set JAVA_HOME run: echo "JAVA_HOME=${{ steps.setup-java-11.outputs.path }}" >> $GITHUB_ENV @@ -377,12 +376,8 @@ jobs: cat gradle.properties - name: Generate R Docs - uses: burrunan/gradle-cache-action@v1 - with: - job-id: rDocs - arguments: --scan outputVersion R:rClientSite - gradle-version: wrapper - + run: ./gradlew --scan outputVersion R:rClientSite + - name: Get Deephaven Version id: dhc-version run: echo "version=$(cat build/version)" >> $GITHUB_OUTPUT @@ -409,3 +404,57 @@ jobs: - name: Upload JVM Error Logs uses: actions/upload-artifact@v4 if: failure() + + protodoc: + needs: [makedirs] + runs-on: ubuntu-22.04 + concurrency: + group: protodoc-${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: ${{ github.ref != 'refs/heads/main' }} + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup JDK 11 + id: setup-java-11 + uses: actions/setup-java@v4 + with: + distribution: 'temurin' + java-version: '11' + + - name: Setup Gradle + uses: gradle/actions/setup-gradle@v4 + + - name: Set JAVA_HOME + run: echo "JAVA_HOME=${{ steps.setup-java-11.outputs.path }}" >> $GITHUB_ENV + + - name: Setup gradle properties + run: | + .github/scripts/gradle-properties.sh >> gradle.properties + cat gradle.properties + + - name: Generate Protobuf Docs + run: ./gradlew --scan outputVersion :proto:proto-backplane-grpc:generateProtobuf + + - name: Get Deephaven Version + id: dhc-version + run: echo "version=$(cat build/version)" >> $GITHUB_OUTPUT + + - name: Upload Protobuf Docs + if: ${{ github.event_name == 'push' }} + uses: actions/upload-artifact@v4 + with: + name: protobuf-docs-${{ steps.dhc-version.outputs.version }} + path: 'proto/proto-backplane-grpc/build/generated/source/proto/main/proto-doc/single-html/index.html' + + - name: Deploy Protobuf Docs + if: ${{ github.event_name == 'push' }} + uses: burnett01/rsync-deployments@5.2 + with: + switches: -rlptDvz --delete + path: proto/proto-backplane-grpc/build/generated/source/proto/main/proto-doc/single-html/index.html + remote_path: deephaven-core-v2/${{ github.ref_name }}/client-api/protobuf/ + remote_host: ${{ secrets.DOCS_HOST }} + remote_port: ${{ secrets.DOCS_PORT }} + remote_user: ${{ secrets.DOCS_USER }} + remote_key: ${{ secrets.DEEPHAVEN_CORE_SSH_KEY }} \ No newline at end of file diff --git a/.github/workflows/links.yml b/.github/workflows/links.yml new file mode 100644 index 00000000000..3757953eb31 --- /dev/null +++ b/.github/workflows/links.yml @@ -0,0 +1,28 @@ +name: Links + +on: + repository_dispatch: + workflow_dispatch: + schedule: + - cron: "00 18 * * *" + +jobs: + linkChecker: + runs-on: ubuntu-latest + permissions: + issues: write + if: ${{ github.repository_owner == 'deephaven' }} + steps: + - uses: actions/checkout@v4 + + - name: Link Checker + id: lychee + uses: lycheeverse/lychee-action@v2 + + - name: Create Issue From File + if: env.lychee_exit_code != 0 + uses: peter-evans/create-issue-from-file@v5 + with: + title: Link Checker Report + content-filepath: ./lychee/out.md + labels: report, automated issue diff --git a/.github/workflows/nightly-check-ci.yml b/.github/workflows/nightly-check-ci.yml index c306f5bf42e..e433c63df30 100644 --- a/.github/workflows/nightly-check-ci.yml +++ b/.github/workflows/nightly-check-ci.yml @@ -7,7 +7,7 @@ on: # 2AM EST == 6AM UTC - cron: '0 6 * * *' push: - branches: [ 'nightly/**', 'release/v*' ] + branches: [ 'nightly/**', 'release/v*', 'dependabot/**' ] jobs: nightly: @@ -15,7 +15,7 @@ jobs: fail-fast: false matrix: gradle-task: ['check', 'testSerial', 'testParallel', 'testOutOfBand'] - test-jvm-version: ['11', '17', '21', '22'] + test-jvm-version: ['11', '17', '21', '23'] if: ${{ github.repository_owner == 'deephaven' || github.event_name != 'schedule' }} runs-on: ubuntu-22.04 concurrency: @@ -48,12 +48,15 @@ jobs: distribution: 'temurin' java-version: '21' - - name: Setup JDK 22 - id: setup-java-22 + - name: Setup JDK 23 + id: setup-java-23 uses: actions/setup-java@v4 with: distribution: 'temurin' - java-version: '22' + java-version: '23' + + - name: Setup Gradle + uses: gradle/actions/setup-gradle@v4 - name: Set JAVA_HOME run: echo "JAVA_HOME=${{ steps.setup-java-11.outputs.path }}" >> $GITHUB_ENV @@ -64,11 +67,7 @@ jobs: cat gradle.properties - name: Run gradle ${{ matrix.gradle-task }} on java ${{ matrix.test-jvm-version }} - uses: burrunan/gradle-cache-action@v1 - with: - job-id: gradle-run - arguments: --scan --continue --rerun-tasks ${{ matrix.gradle-task }} -PtestRuntimeVersion=${{ matrix.test-jvm-version }} - gradle-version: wrapper + run: ./gradlew --scan --continue --rerun-tasks ${{ matrix.gradle-task }} -PtestRuntimeVersion=${{ matrix.test-jvm-version }} - name: Upload Test Results uses: actions/upload-artifact@v4 @@ -91,7 +90,7 @@ jobs: - name: Publish Test Results uses: scacap/action-surefire-report@v1 - if: ${{ github.repository_owner == 'deephaven' }} + if: ${{ github.repository_owner == 'deephaven' && github.ref == 'refs/heads/main' }} env: NODE_OPTIONS: '--max_old_space_size=4096' with: @@ -101,9 +100,9 @@ jobs: report_paths: '**/build/test-results/*/TEST-*.xml' - name: Slack Nightly Failure - uses: slackapi/slack-github-action@v1.26.0 + uses: slackapi/slack-github-action@v1.27.0 id: slack-nightly-failure - if: ${{ failure() && github.repository_owner == 'deephaven' }} + if: ${{ failure() && github.repository_owner == 'deephaven' && github.ref == 'refs/heads/main' }} with: payload: | { diff --git a/.github/workflows/nightly-image-check.yml b/.github/workflows/nightly-image-check.yml index 310dacb144d..e2f75b3a15f 100644 --- a/.github/workflows/nightly-image-check.yml +++ b/.github/workflows/nightly-image-check.yml @@ -20,6 +20,9 @@ jobs: distribution: 'temurin' java-version: '11' + - name: Setup Gradle + uses: gradle/actions/setup-gradle@v4 + - name: Set JAVA_HOME run: echo "JAVA_HOME=${{ steps.setup-java-11.outputs.path }}" >> $GITHUB_ENV @@ -29,14 +32,10 @@ jobs: cat gradle.properties - name: Run gradle - uses: burrunan/gradle-cache-action@v1 - with: - job-id: image-compare - arguments: --continue pullImage compareImage - gradle-version: wrapper + run: ./gradlew --continue pullImage compareImage - name: Notify Slack - uses: slackapi/slack-github-action@v1.26.0 + uses: slackapi/slack-github-action@v1.27.0 id: notify-slack if: ${{ failure() }} env: diff --git a/.github/workflows/nightly-publish-ci.yml b/.github/workflows/nightly-publish-ci.yml new file mode 100644 index 00000000000..927c0b2009a --- /dev/null +++ b/.github/workflows/nightly-publish-ci.yml @@ -0,0 +1,67 @@ +name: Nightly Publish CI + +on: + schedule: + # 1AM EST == 5AM UTC + - cron: '0 5 * * *' + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: false + +jobs: + nightly-publish: + runs-on: ubuntu-24.04 + if: ${{ github.repository_owner == 'deephaven' }} + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup JDK 11 + id: setup-java-11 + uses: actions/setup-java@v4 + with: + distribution: 'temurin' + java-version: '11' + + - name: Setup JDK 17 + id: setup-java-17 + uses: actions/setup-java@v4 + with: + distribution: 'temurin' + java-version: '17' + + - name: Setup Gradle + uses: gradle/actions/setup-gradle@v4 + + - name: Set JAVA_HOME + run: echo "JAVA_HOME=${{ steps.setup-java-11.outputs.path }}" >> $GITHUB_ENV + + - name: Setup gradle properties + run: | + .github/scripts/gradle-properties.sh >> gradle.properties + cat gradle.properties + + - name: Publish to OSSRH snapshots repository + # We are not worried here about using --no-parallel (as we are with release publishing), since publishing + # snapshots does not even create staging repositories. + run: ./gradlew publish + env: + ORG_GRADLE_PROJECT_ossrhUsername: ${{ secrets.SONATYPE_USERNAME }} + ORG_GRADLE_PROJECT_ossrhPassword: ${{ secrets.SONATYPE_PASSWORD }} + ORG_GRADLE_PROJECT_signingKey: ${{ secrets.CI_AT_DEEPHAVEN_KEY }} + ORG_GRADLE_PROJECT_signingPassword: ${{ secrets.CI_AT_DEEPHAVEN_PASSWORD }} + ORG_GRADLE_PROJECT_signingRequired: true + + - name: Slack Nightly Failure + uses: slackapi/slack-github-action@v1.27.0 + id: slack-nightly-failure + if: failure() + with: + payload: | + { + "slack_message": "Nightly publish failure @ ${{ github.head_ref }} ${{ github.sha }}" + } + env: + SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_NIGHTLY_FAILURE }} diff --git a/.github/workflows/publish-ci.yml b/.github/workflows/publish-ci.yml index 53dc8335ad0..65c6ef8b60d 100644 --- a/.github/workflows/publish-ci.yml +++ b/.github/workflows/publish-ci.yml @@ -33,6 +33,9 @@ jobs: distribution: 'temurin' java-version: '17' + - name: Setup Gradle + uses: gradle/actions/setup-gradle@v4 + - name: Set JAVA_HOME run: echo "JAVA_HOME=${{ steps.setup-java-11.outputs.path }}" >> $GITHUB_ENV @@ -51,20 +54,12 @@ jobs: - name: Build all artifacts, publish to Maven Local if: ${{ !startsWith(github.ref, 'refs/heads/release/v') }} - uses: burrunan/gradle-cache-action@v1 - with: - job-id: publish-local - arguments: server-netty-app:build server-jetty-app:build py-server:build py-embedded-server:build py-client:build py-client-ticking:build web-client-api:types:build publishToMavenLocal - gradle-version: wrapper + run: ./gradlew server-netty-app:build server-jetty-app:build py-server:build py-embedded-server:build py-client:build py-client-ticking:build web-client-api:types:build publishToMavenLocal - name: Build all artifacts, publish to Sonatype for staging to Maven Central if: ${{ startsWith(github.ref, 'refs/heads/release/v') }} - uses: burrunan/gradle-cache-action@v1 - with: - job-id: publish - # We need to be explicit here about no parallelism to ensure we don't create disjointed staging repositories. - arguments: --no-parallel server-netty-app:build server-jetty-app:build py-server:build py-embedded-server:build py-client:build py-client-ticking:build web-client-api:types:build publish - gradle-version: wrapper + # We need to be explicit here about no parallelism to ensure we don't create disjointed staging repositories. + run: ./gradlew --no-parallel server-netty-app:build server-jetty-app:build py-server:build py-embedded-server:build py-client:build py-client-ticking:build web-client-api:types:build publish env: ORG_GRADLE_PROJECT_ossrhUsername: ${{ secrets.SONATYPE_USERNAME }} ORG_GRADLE_PROJECT_ossrhPassword: ${{ secrets.SONATYPE_PASSWORD }} diff --git a/.github/workflows/quick-ci.yml b/.github/workflows/quick-ci.yml index 56c51ede52f..8416bf485c5 100644 --- a/.github/workflows/quick-ci.yml +++ b/.github/workflows/quick-ci.yml @@ -27,6 +27,9 @@ jobs: distribution: 'temurin' java-version: '11' + - name: Setup Gradle + uses: gradle/actions/setup-gradle@v4 + - name: Set JAVA_HOME run: echo "JAVA_HOME=${{ steps.setup-java-11.outputs.path }}" >> $GITHUB_ENV @@ -36,12 +39,8 @@ jobs: cat gradle.properties - name: Quick Task - uses: burrunan/gradle-cache-action@v1 - with: - job-id: quick-task - # Even though quick includes spotlessCheck, we want to make sure it runs first and fails ASAP for quick feedback - arguments: --scan spotlessCheck quick - gradle-version: wrapper + # Even though quick includes spotlessCheck, we want to make sure it runs first and fails ASAP for quick feedback + run: ./gradlew --scan spotlessCheck quick - name: Upload JVM Error Logs uses: actions/upload-artifact@v4 @@ -64,4 +63,4 @@ jobs: run: pip install vermin==1.6.0 - name: Verify minimum version support - run: vermin -t=3.8 --no-tips --eval-annotations --violations py/server/deephaven py/client py/client-ticking py/embedded-server + run: vermin -t=3.8 --no-tips --eval-annotations --violations --feature fstring-self-doc --feature union-types py/server/deephaven py/client py/client-ticking py/embedded-server diff --git a/.github/workflows/tag-base-images.yml b/.github/workflows/tag-base-images.yml index 3ce799ed989..910391c0c00 100644 --- a/.github/workflows/tag-base-images.yml +++ b/.github/workflows/tag-base-images.yml @@ -29,9 +29,12 @@ jobs: distribution: 'temurin' java-version: '17' + - name: Setup Gradle + uses: gradle/actions/setup-gradle@v4 + - name: Setup Crane if: ${{ startsWith(github.ref, 'refs/heads/release/v') }} - uses: imjasonh/setup-crane@v0.3 + uses: imjasonh/setup-crane@v0.4 - name: Set JAVA_HOME run: echo "JAVA_HOME=${{ steps.setup-java-11.outputs.path }}" >> $GITHUB_ENV @@ -50,11 +53,7 @@ jobs: password: ${{ secrets.GITHUB_TOKEN }} - name: Create Crane scripts - uses: burrunan/gradle-cache-action@v1 - with: - job-id: crane-scripts - arguments: createCraneTagScript - gradle-version: wrapper + run: ./gradlew createCraneTagScript - name: Tag upstream images if: ${{ startsWith(github.ref, 'refs/heads/release/v') }} diff --git a/.github/workflows/update-web.yml b/.github/workflows/update-web.yml index be6a7357ab3..a467a16d65e 100644 --- a/.github/workflows/update-web.yml +++ b/.github/workflows/update-web.yml @@ -33,7 +33,7 @@ jobs: sed -i "s/^ARG CHART_VERSION=.*/ARG CHART_VERSION=$CHART_VERSION/" ./web/client-ui/Dockerfile sed -i "s/^ARG WIDGET_VERSION=.*/ARG WIDGET_VERSION=$WIDGET_VERSION/" ./web/client-ui/Dockerfile - name: Create Pull Request - uses: peter-evans/create-pull-request@v6 + uses: peter-evans/create-pull-request@v7 env: WEB_VERSION: ${{steps.web_versions.outputs.WEB_VERSION}} with: diff --git a/.lycheeignore b/.lycheeignore new file mode 100644 index 00000000000..af1ffb5766d --- /dev/null +++ b/.lycheeignore @@ -0,0 +1,7 @@ +http://localhost: +https://localhost: +https://docs.deephaven.io/ +https://10.0.1.50:8123/iris/connection.json +https://deephaven.io/core/release/vX.Y.Z/javadoc +https://github.com/deephaven/deephaven.io +https://developer.mozilla.org/en-US/docs/Web/API/Transferable diff --git a/Base/build.gradle b/Base/build.gradle index 872cf943208..dfca5c784f1 100644 --- a/Base/build.gradle +++ b/Base/build.gradle @@ -7,14 +7,15 @@ dependencies { api project(':clock') - implementation depTrove3 - compileOnlyApi depAnnotations - implementation depCommonsLang3 + implementation libs.trove + compileOnlyApi libs.jetbrains.annotations + implementation libs.commons.lang3 - api 'io.deephaven:hash:0.1.0' + api libs.deephaven.hash - Classpaths.inheritJUnitClassic(project, 'testImplementation') - Classpaths.inheritJMock(project, 'testImplementation') + testImplementation libs.junit4 + testImplementation libs.jmock.junit4 + testImplementation libs.jmock.imposters testImplementation project(":base-test-utils") } diff --git a/Base/gradle.properties b/Base/gradle.properties index c186bbfdde1..9e8588f2620 100644 --- a/Base/gradle.properties +++ b/Base/gradle.properties @@ -1 +1,2 @@ io.deephaven.project.ProjectType=JAVA_PUBLIC +languageLevel=8 diff --git a/Base/src/main/java/io/deephaven/base/FileUtils.java b/Base/src/main/java/io/deephaven/base/FileUtils.java index 7650308821e..820e733c0a9 100644 --- a/Base/src/main/java/io/deephaven/base/FileUtils.java +++ b/Base/src/main/java/io/deephaven/base/FileUtils.java @@ -282,7 +282,7 @@ public static URI convertToURI(final String source, final boolean isDirectory) { return convertToURI(new File(uri), isDirectory); } String path = uri.getPath(); - final boolean endsWithSlash = path.charAt(path.length() - 1) == URI_SEPARATOR_CHAR; + final boolean endsWithSlash = !path.isEmpty() && path.charAt(path.length() - 1) == URI_SEPARATOR_CHAR; if (!isDirectory && endsWithSlash) { throw new IllegalArgumentException("Non-directory URI should not end with a slash: " + uri); } @@ -324,6 +324,11 @@ public static URI convertToURI(final File file, final boolean isDirectory) { if (isDirectory && absPath.charAt(absPath.length() - 1) != URI_SEPARATOR_CHAR) { absPath = absPath + URI_SEPARATOR_CHAR; } + if (absPath.charAt(0) != URI_SEPARATOR_CHAR) { + absPath = URI_SEPARATOR_CHAR + absPath; + // ^This is especially useful for Windows where the absolute path does not start with a slash. + // For example, for absolute path "C:\path\to\file", the URI would be "file:/C:/path/to/file". + } try { return new URI(FILE_URI_SCHEME, null, absPath, null); } catch (final URISyntaxException e) { diff --git a/Base/src/main/java/io/deephaven/base/MathUtil.java b/Base/src/main/java/io/deephaven/base/MathUtil.java index 555409a709b..f9755cb14f0 100644 --- a/Base/src/main/java/io/deephaven/base/MathUtil.java +++ b/Base/src/main/java/io/deephaven/base/MathUtil.java @@ -129,6 +129,20 @@ public static int roundUpPowerOf2(int x) { return Math.max(Integer.highestOneBit(x - 1) << 1, 1); } + /** + * Rounds up to the next power of 2 for {@code x}; if {@code x} is already a power of 2, {@code x} will be returned. + * Values outside the range {@code 1 <= x <= Long.MAX_VALUE} will return {@code 1}. + * + *

+ * Equivalent to {@code Math.max(Long.highestOneBit(x - 1) << 1, 1)}. + * + * @param x the value + * @return the next power of 2 for {@code x} + */ + public static long roundUpPowerOf2(long x) { + return Math.max(Long.highestOneBit(x - 1) << 1, 1); + } + /** * Rounds up to the next power of 2 for {@code size <= MAX_POWER_OF_2}, otherwise returns * {@link ArrayUtil#MAX_ARRAY_SIZE}. diff --git a/Stats/src/main/java/io/deephaven/stats/util/OSUtil.java b/Base/src/main/java/io/deephaven/base/OSUtil.java similarity index 98% rename from Stats/src/main/java/io/deephaven/stats/util/OSUtil.java rename to Base/src/main/java/io/deephaven/base/OSUtil.java index 4eceea9a42b..91167f03dfc 100644 --- a/Stats/src/main/java/io/deephaven/stats/util/OSUtil.java +++ b/Base/src/main/java/io/deephaven/base/OSUtil.java @@ -1,7 +1,7 @@ // // Copyright (c) 2016-2024 Deephaven Data Labs and Patent Pending // -package io.deephaven.stats.util; +package io.deephaven.base; import org.jetbrains.annotations.NotNull; diff --git a/Base/src/main/java/io/deephaven/base/ringbuffer/AggregatingByteRingBuffer.java b/Base/src/main/java/io/deephaven/base/ringbuffer/AggregatingByteRingBuffer.java index f4f2ce461a8..5f42dfcd519 100644 --- a/Base/src/main/java/io/deephaven/base/ringbuffer/AggregatingByteRingBuffer.java +++ b/Base/src/main/java/io/deephaven/base/ringbuffer/AggregatingByteRingBuffer.java @@ -434,11 +434,16 @@ public void clear() { final long prevHead = internalBuffer.head; final int prevSize = size(); - internalBuffer.clear(); + // Reset the pointers in the ring buffer without clearing the storage array. This leaves existing `identityVal` + // entries in place for the next `evaluate()` call. + internalBuffer.head = internalBuffer.tail = 0; calcHead = calcTail = 0; - // Reset the cleared storage entries to the identity value + + // Reset the previously populated storage entries to the identity value. After this call, all entries in the + // storage buffer are `identityVal` fillWithIdentityVal(prevHead, prevSize); + // Reset the tree buffer with the identity value Arrays.fill(treeStorage, identityVal); } diff --git a/Base/src/main/java/io/deephaven/base/ringbuffer/AggregatingCharRingBuffer.java b/Base/src/main/java/io/deephaven/base/ringbuffer/AggregatingCharRingBuffer.java index e931ba3f1c6..f6148c481ed 100644 --- a/Base/src/main/java/io/deephaven/base/ringbuffer/AggregatingCharRingBuffer.java +++ b/Base/src/main/java/io/deephaven/base/ringbuffer/AggregatingCharRingBuffer.java @@ -430,11 +430,16 @@ public void clear() { final long prevHead = internalBuffer.head; final int prevSize = size(); - internalBuffer.clear(); + // Reset the pointers in the ring buffer without clearing the storage array. This leaves existing `identityVal` + // entries in place for the next `evaluate()` call. + internalBuffer.head = internalBuffer.tail = 0; calcHead = calcTail = 0; - // Reset the cleared storage entries to the identity value + + // Reset the previously populated storage entries to the identity value. After this call, all entries in the + // storage buffer are `identityVal` fillWithIdentityVal(prevHead, prevSize); + // Reset the tree buffer with the identity value Arrays.fill(treeStorage, identityVal); } diff --git a/Base/src/main/java/io/deephaven/base/ringbuffer/AggregatingDoubleRingBuffer.java b/Base/src/main/java/io/deephaven/base/ringbuffer/AggregatingDoubleRingBuffer.java index 516474e31ac..cd5963e54ec 100644 --- a/Base/src/main/java/io/deephaven/base/ringbuffer/AggregatingDoubleRingBuffer.java +++ b/Base/src/main/java/io/deephaven/base/ringbuffer/AggregatingDoubleRingBuffer.java @@ -434,11 +434,16 @@ public void clear() { final long prevHead = internalBuffer.head; final int prevSize = size(); - internalBuffer.clear(); + // Reset the pointers in the ring buffer without clearing the storage array. This leaves existing `identityVal` + // entries in place for the next `evaluate()` call. + internalBuffer.head = internalBuffer.tail = 0; calcHead = calcTail = 0; - // Reset the cleared storage entries to the identity value + + // Reset the previously populated storage entries to the identity value. After this call, all entries in the + // storage buffer are `identityVal` fillWithIdentityVal(prevHead, prevSize); + // Reset the tree buffer with the identity value Arrays.fill(treeStorage, identityVal); } diff --git a/Base/src/main/java/io/deephaven/base/ringbuffer/AggregatingFloatRingBuffer.java b/Base/src/main/java/io/deephaven/base/ringbuffer/AggregatingFloatRingBuffer.java index fedb32a7ab5..f6d6b43a619 100644 --- a/Base/src/main/java/io/deephaven/base/ringbuffer/AggregatingFloatRingBuffer.java +++ b/Base/src/main/java/io/deephaven/base/ringbuffer/AggregatingFloatRingBuffer.java @@ -434,11 +434,16 @@ public void clear() { final long prevHead = internalBuffer.head; final int prevSize = size(); - internalBuffer.clear(); + // Reset the pointers in the ring buffer without clearing the storage array. This leaves existing `identityVal` + // entries in place for the next `evaluate()` call. + internalBuffer.head = internalBuffer.tail = 0; calcHead = calcTail = 0; - // Reset the cleared storage entries to the identity value + + // Reset the previously populated storage entries to the identity value. After this call, all entries in the + // storage buffer are `identityVal` fillWithIdentityVal(prevHead, prevSize); + // Reset the tree buffer with the identity value Arrays.fill(treeStorage, identityVal); } diff --git a/Base/src/main/java/io/deephaven/base/ringbuffer/AggregatingIntRingBuffer.java b/Base/src/main/java/io/deephaven/base/ringbuffer/AggregatingIntRingBuffer.java index 9794c76ec0d..b542a07d8d5 100644 --- a/Base/src/main/java/io/deephaven/base/ringbuffer/AggregatingIntRingBuffer.java +++ b/Base/src/main/java/io/deephaven/base/ringbuffer/AggregatingIntRingBuffer.java @@ -434,11 +434,16 @@ public void clear() { final long prevHead = internalBuffer.head; final int prevSize = size(); - internalBuffer.clear(); + // Reset the pointers in the ring buffer without clearing the storage array. This leaves existing `identityVal` + // entries in place for the next `evaluate()` call. + internalBuffer.head = internalBuffer.tail = 0; calcHead = calcTail = 0; - // Reset the cleared storage entries to the identity value + + // Reset the previously populated storage entries to the identity value. After this call, all entries in the + // storage buffer are `identityVal` fillWithIdentityVal(prevHead, prevSize); + // Reset the tree buffer with the identity value Arrays.fill(treeStorage, identityVal); } diff --git a/Base/src/main/java/io/deephaven/base/ringbuffer/AggregatingLongRingBuffer.java b/Base/src/main/java/io/deephaven/base/ringbuffer/AggregatingLongRingBuffer.java index c402fe5db2f..da3b07750f5 100644 --- a/Base/src/main/java/io/deephaven/base/ringbuffer/AggregatingLongRingBuffer.java +++ b/Base/src/main/java/io/deephaven/base/ringbuffer/AggregatingLongRingBuffer.java @@ -434,11 +434,16 @@ public void clear() { final long prevHead = internalBuffer.head; final int prevSize = size(); - internalBuffer.clear(); + // Reset the pointers in the ring buffer without clearing the storage array. This leaves existing `identityVal` + // entries in place for the next `evaluate()` call. + internalBuffer.head = internalBuffer.tail = 0; calcHead = calcTail = 0; - // Reset the cleared storage entries to the identity value + + // Reset the previously populated storage entries to the identity value. After this call, all entries in the + // storage buffer are `identityVal` fillWithIdentityVal(prevHead, prevSize); + // Reset the tree buffer with the identity value Arrays.fill(treeStorage, identityVal); } diff --git a/Base/src/main/java/io/deephaven/base/ringbuffer/AggregatingObjectRingBuffer.java b/Base/src/main/java/io/deephaven/base/ringbuffer/AggregatingObjectRingBuffer.java index b064b8a4383..1c6eb1d8c46 100644 --- a/Base/src/main/java/io/deephaven/base/ringbuffer/AggregatingObjectRingBuffer.java +++ b/Base/src/main/java/io/deephaven/base/ringbuffer/AggregatingObjectRingBuffer.java @@ -434,11 +434,16 @@ public void clear() { final long prevHead = internalBuffer.head; final int prevSize = size(); - internalBuffer.clear(); + // Reset the pointers in the ring buffer without clearing the storage array. This leaves existing `identityVal` + // entries in place for the next `evaluate()` call. + internalBuffer.head = internalBuffer.tail = 0; calcHead = calcTail = 0; - // Reset the cleared storage entries to the identity value + + // Reset the previously populated storage entries to the identity value. After this call, all entries in the + // storage buffer are `identityVal` fillWithIdentityVal(prevHead, prevSize); + // Reset the tree buffer with the identity value Arrays.fill(treeStorage, identityVal); } diff --git a/Base/src/main/java/io/deephaven/base/ringbuffer/AggregatingShortRingBuffer.java b/Base/src/main/java/io/deephaven/base/ringbuffer/AggregatingShortRingBuffer.java index 1e6707cb473..9e4a1c27aca 100644 --- a/Base/src/main/java/io/deephaven/base/ringbuffer/AggregatingShortRingBuffer.java +++ b/Base/src/main/java/io/deephaven/base/ringbuffer/AggregatingShortRingBuffer.java @@ -434,11 +434,16 @@ public void clear() { final long prevHead = internalBuffer.head; final int prevSize = size(); - internalBuffer.clear(); + // Reset the pointers in the ring buffer without clearing the storage array. This leaves existing `identityVal` + // entries in place for the next `evaluate()` call. + internalBuffer.head = internalBuffer.tail = 0; calcHead = calcTail = 0; - // Reset the cleared storage entries to the identity value + + // Reset the previously populated storage entries to the identity value. After this call, all entries in the + // storage buffer are `identityVal` fillWithIdentityVal(prevHead, prevSize); + // Reset the tree buffer with the identity value Arrays.fill(treeStorage, identityVal); } diff --git a/Base/src/main/java/io/deephaven/base/ringbuffer/ByteRingBuffer.java b/Base/src/main/java/io/deephaven/base/ringbuffer/ByteRingBuffer.java index aede7521309..fa8b5a891d1 100644 --- a/Base/src/main/java/io/deephaven/base/ringbuffer/ByteRingBuffer.java +++ b/Base/src/main/java/io/deephaven/base/ringbuffer/ByteRingBuffer.java @@ -9,6 +9,7 @@ import io.deephaven.base.MathUtil; import io.deephaven.base.verify.Assert; +import org.jetbrains.annotations.TestOnly; import java.io.Serializable; import java.util.NoSuchElementException; @@ -19,7 +20,7 @@ * {@code long} values. Head and tail will not wrap around; instead we use storage arrays sized to 2^N to allow fast * determination of storage indices through a mask operation. */ -public class ByteRingBuffer implements Serializable { +public class ByteRingBuffer implements RingBuffer, Serializable { static final long FIXUP_THRESHOLD = 1L << 62; final boolean growable; byte[] storage; @@ -32,7 +33,7 @@ public class ByteRingBuffer implements Serializable { * * @param capacity minimum capacity of the ring buffer */ - public ByteRingBuffer(int capacity) { + public ByteRingBuffer(final int capacity) { this(capacity, true); } @@ -42,7 +43,7 @@ public ByteRingBuffer(int capacity) { * @param capacity minimum capacity of ring buffer * @param growable whether to allow growth when the buffer is full. */ - public ByteRingBuffer(int capacity, boolean growable) { + public ByteRingBuffer(final int capacity, final boolean growable) { Assert.leq(capacity, "ByteRingBuffer capacity", MathUtil.MAX_POWER_OF_2); this.growable = growable; @@ -59,7 +60,7 @@ public ByteRingBuffer(int capacity, boolean growable) { * * @param increase Increase amount. The ring buffer's capacity will be increased by at least this amount. */ - protected void grow(int increase) { + protected void grow(final int increase) { final int size = size(); final long newCapacity = (long) storage.length + increase; // assert that we are not asking for the impossible @@ -83,7 +84,7 @@ protected void grow(int increase) { * * @param dest The destination buffer. */ - protected void copyRingBufferToArray(byte[] dest) { + protected void copyRingBufferToArray(final byte[] dest) { final int size = size(); final int storageHead = (int) (head & mask); @@ -99,27 +100,35 @@ protected void copyRingBufferToArray(byte[] dest) { System.arraycopy(storage, 0, dest, firstCopyLen, secondCopyLen); } + @Override public boolean isFull() { return size() == storage.length; } + @Override public boolean isEmpty() { return tail == head; } + @Override public int size() { return Math.toIntExact(tail - head); } + @Override public int capacity() { return storage.length; } + @Override public int remaining() { return storage.length - size(); } + @Override public void clear() { + // region object-bulk-clear + // endregion object-bulk-clear tail = head = 0; } @@ -131,7 +140,7 @@ public void clear() { * @throws UnsupportedOperationException when {@code growable} is {@code false} and buffer is full * @return {@code true} if the byte was added successfully */ - public boolean add(byte e) { + public boolean add(final byte e) { if (isFull()) { if (!growable) { throw new UnsupportedOperationException("Ring buffer is full and growth is disabled"); @@ -151,7 +160,8 @@ public boolean add(byte e) { * @param count the minimum number of empty entries in the buffer after this call * @throws UnsupportedOperationException when {@code growable} is {@code false} and buffer is full */ - public void ensureRemaining(int count) { + @Override + public void ensureRemaining(final int count) { if (remaining() < count) { if (!growable) { throw new UnsupportedOperationException("Ring buffer is full and growth is disabled"); @@ -168,7 +178,7 @@ public void ensureRemaining(int count) { * * @param e the value to add to the buffer */ - public void addUnsafe(byte e) { + public void addUnsafe(final byte e) { // This is an extremely paranoid wrap check that in all likelihood will never run. With FIXUP_THRESHOLD at // 1 << 62, and the user pushing 2^32 values per second(!), it will take 68 years to wrap this counter . if (tail >= FIXUP_THRESHOLD) { @@ -188,7 +198,7 @@ public void addUnsafe(byte e) { * @param notFullResult value to return is the buffer is not full * @return the overwritten entry if the buffer is full, the provided value otherwise */ - public byte addOverwrite(byte e, byte notFullResult) { + public byte addOverwrite(final byte e, final byte notFullResult) { byte val = notFullResult; if (isFull()) { val = remove(); @@ -204,7 +214,7 @@ public byte addOverwrite(byte e, byte notFullResult) { * @param e the byte to be added to the buffer * @return true if the value was added successfully, false otherwise */ - public boolean offer(byte e) { + public boolean offer(final byte e) { if (isFull()) { return false; } @@ -218,7 +228,7 @@ public boolean offer(byte e) { * @param count The number of elements to remove. * @throws NoSuchElementException if the buffer is empty */ - public byte[] remove(int count) { + public byte[] remove(final int count) { final int size = size(); if (size < count) { throw new NoSuchElementException(); @@ -264,7 +274,7 @@ public byte removeUnsafe() { * @param onEmpty the value to return if the ring buffer is empty * @return The removed element if the ring buffer was non-empty, otherwise the value of 'onEmpty' */ - public byte poll(byte onEmpty) { + public byte poll(final byte onEmpty) { if (isEmpty()) { return onEmpty; } @@ -291,7 +301,7 @@ public byte element() { * @param onEmpty the value to return if the ring buffer is empty * @return The head element if the ring buffer is non-empty, otherwise the value of 'onEmpty' */ - public byte peek(byte onEmpty) { + public byte peek(final byte onEmpty) { if (isEmpty()) { return onEmpty; } @@ -314,7 +324,7 @@ public byte front() { * @throws NoSuchElementException if the buffer is empty * @return The element at the specified offset */ - public byte front(int offset) { + public byte front(final int offset) { if (offset < 0 || offset >= size()) { throw new NoSuchElementException(); } @@ -341,7 +351,7 @@ public byte back() { * @param onEmpty the value to return if the ring buffer is empty * @return The tail element if the ring buffer is non-empty, otherwise the value of 'onEmpty' */ - public byte peekBack(byte onEmpty) { + public byte peekBack(final byte onEmpty) { if (isEmpty()) { return onEmpty; } @@ -385,4 +395,14 @@ public void remove() { throw new UnsupportedOperationException(); } } + + /** + * Get the storage array for this ring buffer. This is intended for testing and debugging purposes only. + * + * @return The storage array for this ring buffer. + */ + @TestOnly + public byte[] getStorage() { + return storage; + } } diff --git a/Base/src/main/java/io/deephaven/base/ringbuffer/CharRingBuffer.java b/Base/src/main/java/io/deephaven/base/ringbuffer/CharRingBuffer.java index 68c58a866e7..84bff19d07e 100644 --- a/Base/src/main/java/io/deephaven/base/ringbuffer/CharRingBuffer.java +++ b/Base/src/main/java/io/deephaven/base/ringbuffer/CharRingBuffer.java @@ -5,6 +5,7 @@ import io.deephaven.base.MathUtil; import io.deephaven.base.verify.Assert; +import org.jetbrains.annotations.TestOnly; import java.io.Serializable; import java.util.NoSuchElementException; @@ -15,7 +16,7 @@ * {@code long} values. Head and tail will not wrap around; instead we use storage arrays sized to 2^N to allow fast * determination of storage indices through a mask operation. */ -public class CharRingBuffer implements Serializable { +public class CharRingBuffer implements RingBuffer, Serializable { static final long FIXUP_THRESHOLD = 1L << 62; final boolean growable; char[] storage; @@ -28,7 +29,7 @@ public class CharRingBuffer implements Serializable { * * @param capacity minimum capacity of the ring buffer */ - public CharRingBuffer(int capacity) { + public CharRingBuffer(final int capacity) { this(capacity, true); } @@ -38,7 +39,7 @@ public CharRingBuffer(int capacity) { * @param capacity minimum capacity of ring buffer * @param growable whether to allow growth when the buffer is full. */ - public CharRingBuffer(int capacity, boolean growable) { + public CharRingBuffer(final int capacity, final boolean growable) { Assert.leq(capacity, "CharRingBuffer capacity", MathUtil.MAX_POWER_OF_2); this.growable = growable; @@ -55,7 +56,7 @@ public CharRingBuffer(int capacity, boolean growable) { * * @param increase Increase amount. The ring buffer's capacity will be increased by at least this amount. */ - protected void grow(int increase) { + protected void grow(final int increase) { final int size = size(); final long newCapacity = (long) storage.length + increase; // assert that we are not asking for the impossible @@ -79,7 +80,7 @@ protected void grow(int increase) { * * @param dest The destination buffer. */ - protected void copyRingBufferToArray(char[] dest) { + protected void copyRingBufferToArray(final char[] dest) { final int size = size(); final int storageHead = (int) (head & mask); @@ -95,27 +96,35 @@ protected void copyRingBufferToArray(char[] dest) { System.arraycopy(storage, 0, dest, firstCopyLen, secondCopyLen); } + @Override public boolean isFull() { return size() == storage.length; } + @Override public boolean isEmpty() { return tail == head; } + @Override public int size() { return Math.toIntExact(tail - head); } + @Override public int capacity() { return storage.length; } + @Override public int remaining() { return storage.length - size(); } + @Override public void clear() { + // region object-bulk-clear + // endregion object-bulk-clear tail = head = 0; } @@ -127,7 +136,7 @@ public void clear() { * @throws UnsupportedOperationException when {@code growable} is {@code false} and buffer is full * @return {@code true} if the char was added successfully */ - public boolean add(char e) { + public boolean add(final char e) { if (isFull()) { if (!growable) { throw new UnsupportedOperationException("Ring buffer is full and growth is disabled"); @@ -147,7 +156,8 @@ public boolean add(char e) { * @param count the minimum number of empty entries in the buffer after this call * @throws UnsupportedOperationException when {@code growable} is {@code false} and buffer is full */ - public void ensureRemaining(int count) { + @Override + public void ensureRemaining(final int count) { if (remaining() < count) { if (!growable) { throw new UnsupportedOperationException("Ring buffer is full and growth is disabled"); @@ -164,7 +174,7 @@ public void ensureRemaining(int count) { * * @param e the value to add to the buffer */ - public void addUnsafe(char e) { + public void addUnsafe(final char e) { // This is an extremely paranoid wrap check that in all likelihood will never run. With FIXUP_THRESHOLD at // 1 << 62, and the user pushing 2^32 values per second(!), it will take 68 years to wrap this counter . if (tail >= FIXUP_THRESHOLD) { @@ -184,7 +194,7 @@ public void addUnsafe(char e) { * @param notFullResult value to return is the buffer is not full * @return the overwritten entry if the buffer is full, the provided value otherwise */ - public char addOverwrite(char e, char notFullResult) { + public char addOverwrite(final char e, final char notFullResult) { char val = notFullResult; if (isFull()) { val = remove(); @@ -200,7 +210,7 @@ public char addOverwrite(char e, char notFullResult) { * @param e the char to be added to the buffer * @return true if the value was added successfully, false otherwise */ - public boolean offer(char e) { + public boolean offer(final char e) { if (isFull()) { return false; } @@ -214,7 +224,7 @@ public boolean offer(char e) { * @param count The number of elements to remove. * @throws NoSuchElementException if the buffer is empty */ - public char[] remove(int count) { + public char[] remove(final int count) { final int size = size(); if (size < count) { throw new NoSuchElementException(); @@ -260,7 +270,7 @@ public char removeUnsafe() { * @param onEmpty the value to return if the ring buffer is empty * @return The removed element if the ring buffer was non-empty, otherwise the value of 'onEmpty' */ - public char poll(char onEmpty) { + public char poll(final char onEmpty) { if (isEmpty()) { return onEmpty; } @@ -287,7 +297,7 @@ public char element() { * @param onEmpty the value to return if the ring buffer is empty * @return The head element if the ring buffer is non-empty, otherwise the value of 'onEmpty' */ - public char peek(char onEmpty) { + public char peek(final char onEmpty) { if (isEmpty()) { return onEmpty; } @@ -310,7 +320,7 @@ public char front() { * @throws NoSuchElementException if the buffer is empty * @return The element at the specified offset */ - public char front(int offset) { + public char front(final int offset) { if (offset < 0 || offset >= size()) { throw new NoSuchElementException(); } @@ -337,7 +347,7 @@ public char back() { * @param onEmpty the value to return if the ring buffer is empty * @return The tail element if the ring buffer is non-empty, otherwise the value of 'onEmpty' */ - public char peekBack(char onEmpty) { + public char peekBack(final char onEmpty) { if (isEmpty()) { return onEmpty; } @@ -381,4 +391,14 @@ public void remove() { throw new UnsupportedOperationException(); } } + + /** + * Get the storage array for this ring buffer. This is intended for testing and debugging purposes only. + * + * @return The storage array for this ring buffer. + */ + @TestOnly + public char[] getStorage() { + return storage; + } } diff --git a/Base/src/main/java/io/deephaven/base/ringbuffer/DoubleRingBuffer.java b/Base/src/main/java/io/deephaven/base/ringbuffer/DoubleRingBuffer.java index 8e942f8937a..2c28fbdd469 100644 --- a/Base/src/main/java/io/deephaven/base/ringbuffer/DoubleRingBuffer.java +++ b/Base/src/main/java/io/deephaven/base/ringbuffer/DoubleRingBuffer.java @@ -9,6 +9,7 @@ import io.deephaven.base.MathUtil; import io.deephaven.base.verify.Assert; +import org.jetbrains.annotations.TestOnly; import java.io.Serializable; import java.util.NoSuchElementException; @@ -19,7 +20,7 @@ * {@code long} values. Head and tail will not wrap around; instead we use storage arrays sized to 2^N to allow fast * determination of storage indices through a mask operation. */ -public class DoubleRingBuffer implements Serializable { +public class DoubleRingBuffer implements RingBuffer, Serializable { static final long FIXUP_THRESHOLD = 1L << 62; final boolean growable; double[] storage; @@ -32,7 +33,7 @@ public class DoubleRingBuffer implements Serializable { * * @param capacity minimum capacity of the ring buffer */ - public DoubleRingBuffer(int capacity) { + public DoubleRingBuffer(final int capacity) { this(capacity, true); } @@ -42,7 +43,7 @@ public DoubleRingBuffer(int capacity) { * @param capacity minimum capacity of ring buffer * @param growable whether to allow growth when the buffer is full. */ - public DoubleRingBuffer(int capacity, boolean growable) { + public DoubleRingBuffer(final int capacity, final boolean growable) { Assert.leq(capacity, "DoubleRingBuffer capacity", MathUtil.MAX_POWER_OF_2); this.growable = growable; @@ -59,7 +60,7 @@ public DoubleRingBuffer(int capacity, boolean growable) { * * @param increase Increase amount. The ring buffer's capacity will be increased by at least this amount. */ - protected void grow(int increase) { + protected void grow(final int increase) { final int size = size(); final long newCapacity = (long) storage.length + increase; // assert that we are not asking for the impossible @@ -83,7 +84,7 @@ protected void grow(int increase) { * * @param dest The destination buffer. */ - protected void copyRingBufferToArray(double[] dest) { + protected void copyRingBufferToArray(final double[] dest) { final int size = size(); final int storageHead = (int) (head & mask); @@ -99,27 +100,35 @@ protected void copyRingBufferToArray(double[] dest) { System.arraycopy(storage, 0, dest, firstCopyLen, secondCopyLen); } + @Override public boolean isFull() { return size() == storage.length; } + @Override public boolean isEmpty() { return tail == head; } + @Override public int size() { return Math.toIntExact(tail - head); } + @Override public int capacity() { return storage.length; } + @Override public int remaining() { return storage.length - size(); } + @Override public void clear() { + // region object-bulk-clear + // endregion object-bulk-clear tail = head = 0; } @@ -131,7 +140,7 @@ public void clear() { * @throws UnsupportedOperationException when {@code growable} is {@code false} and buffer is full * @return {@code true} if the double was added successfully */ - public boolean add(double e) { + public boolean add(final double e) { if (isFull()) { if (!growable) { throw new UnsupportedOperationException("Ring buffer is full and growth is disabled"); @@ -151,7 +160,8 @@ public boolean add(double e) { * @param count the minimum number of empty entries in the buffer after this call * @throws UnsupportedOperationException when {@code growable} is {@code false} and buffer is full */ - public void ensureRemaining(int count) { + @Override + public void ensureRemaining(final int count) { if (remaining() < count) { if (!growable) { throw new UnsupportedOperationException("Ring buffer is full and growth is disabled"); @@ -168,7 +178,7 @@ public void ensureRemaining(int count) { * * @param e the value to add to the buffer */ - public void addUnsafe(double e) { + public void addUnsafe(final double e) { // This is an extremely paranoid wrap check that in all likelihood will never run. With FIXUP_THRESHOLD at // 1 << 62, and the user pushing 2^32 values per second(!), it will take 68 years to wrap this counter . if (tail >= FIXUP_THRESHOLD) { @@ -188,7 +198,7 @@ public void addUnsafe(double e) { * @param notFullResult value to return is the buffer is not full * @return the overwritten entry if the buffer is full, the provided value otherwise */ - public double addOverwrite(double e, double notFullResult) { + public double addOverwrite(final double e, final double notFullResult) { double val = notFullResult; if (isFull()) { val = remove(); @@ -204,7 +214,7 @@ public double addOverwrite(double e, double notFullResult) { * @param e the double to be added to the buffer * @return true if the value was added successfully, false otherwise */ - public boolean offer(double e) { + public boolean offer(final double e) { if (isFull()) { return false; } @@ -218,7 +228,7 @@ public boolean offer(double e) { * @param count The number of elements to remove. * @throws NoSuchElementException if the buffer is empty */ - public double[] remove(int count) { + public double[] remove(final int count) { final int size = size(); if (size < count) { throw new NoSuchElementException(); @@ -264,7 +274,7 @@ public double removeUnsafe() { * @param onEmpty the value to return if the ring buffer is empty * @return The removed element if the ring buffer was non-empty, otherwise the value of 'onEmpty' */ - public double poll(double onEmpty) { + public double poll(final double onEmpty) { if (isEmpty()) { return onEmpty; } @@ -291,7 +301,7 @@ public double element() { * @param onEmpty the value to return if the ring buffer is empty * @return The head element if the ring buffer is non-empty, otherwise the value of 'onEmpty' */ - public double peek(double onEmpty) { + public double peek(final double onEmpty) { if (isEmpty()) { return onEmpty; } @@ -314,7 +324,7 @@ public double front() { * @throws NoSuchElementException if the buffer is empty * @return The element at the specified offset */ - public double front(int offset) { + public double front(final int offset) { if (offset < 0 || offset >= size()) { throw new NoSuchElementException(); } @@ -341,7 +351,7 @@ public double back() { * @param onEmpty the value to return if the ring buffer is empty * @return The tail element if the ring buffer is non-empty, otherwise the value of 'onEmpty' */ - public double peekBack(double onEmpty) { + public double peekBack(final double onEmpty) { if (isEmpty()) { return onEmpty; } @@ -385,4 +395,14 @@ public void remove() { throw new UnsupportedOperationException(); } } + + /** + * Get the storage array for this ring buffer. This is intended for testing and debugging purposes only. + * + * @return The storage array for this ring buffer. + */ + @TestOnly + public double[] getStorage() { + return storage; + } } diff --git a/Base/src/main/java/io/deephaven/base/ringbuffer/FloatRingBuffer.java b/Base/src/main/java/io/deephaven/base/ringbuffer/FloatRingBuffer.java index 16c8751c447..d432710383e 100644 --- a/Base/src/main/java/io/deephaven/base/ringbuffer/FloatRingBuffer.java +++ b/Base/src/main/java/io/deephaven/base/ringbuffer/FloatRingBuffer.java @@ -9,6 +9,7 @@ import io.deephaven.base.MathUtil; import io.deephaven.base.verify.Assert; +import org.jetbrains.annotations.TestOnly; import java.io.Serializable; import java.util.NoSuchElementException; @@ -19,7 +20,7 @@ * {@code long} values. Head and tail will not wrap around; instead we use storage arrays sized to 2^N to allow fast * determination of storage indices through a mask operation. */ -public class FloatRingBuffer implements Serializable { +public class FloatRingBuffer implements RingBuffer, Serializable { static final long FIXUP_THRESHOLD = 1L << 62; final boolean growable; float[] storage; @@ -32,7 +33,7 @@ public class FloatRingBuffer implements Serializable { * * @param capacity minimum capacity of the ring buffer */ - public FloatRingBuffer(int capacity) { + public FloatRingBuffer(final int capacity) { this(capacity, true); } @@ -42,7 +43,7 @@ public FloatRingBuffer(int capacity) { * @param capacity minimum capacity of ring buffer * @param growable whether to allow growth when the buffer is full. */ - public FloatRingBuffer(int capacity, boolean growable) { + public FloatRingBuffer(final int capacity, final boolean growable) { Assert.leq(capacity, "FloatRingBuffer capacity", MathUtil.MAX_POWER_OF_2); this.growable = growable; @@ -59,7 +60,7 @@ public FloatRingBuffer(int capacity, boolean growable) { * * @param increase Increase amount. The ring buffer's capacity will be increased by at least this amount. */ - protected void grow(int increase) { + protected void grow(final int increase) { final int size = size(); final long newCapacity = (long) storage.length + increase; // assert that we are not asking for the impossible @@ -83,7 +84,7 @@ protected void grow(int increase) { * * @param dest The destination buffer. */ - protected void copyRingBufferToArray(float[] dest) { + protected void copyRingBufferToArray(final float[] dest) { final int size = size(); final int storageHead = (int) (head & mask); @@ -99,27 +100,35 @@ protected void copyRingBufferToArray(float[] dest) { System.arraycopy(storage, 0, dest, firstCopyLen, secondCopyLen); } + @Override public boolean isFull() { return size() == storage.length; } + @Override public boolean isEmpty() { return tail == head; } + @Override public int size() { return Math.toIntExact(tail - head); } + @Override public int capacity() { return storage.length; } + @Override public int remaining() { return storage.length - size(); } + @Override public void clear() { + // region object-bulk-clear + // endregion object-bulk-clear tail = head = 0; } @@ -131,7 +140,7 @@ public void clear() { * @throws UnsupportedOperationException when {@code growable} is {@code false} and buffer is full * @return {@code true} if the float was added successfully */ - public boolean add(float e) { + public boolean add(final float e) { if (isFull()) { if (!growable) { throw new UnsupportedOperationException("Ring buffer is full and growth is disabled"); @@ -151,7 +160,8 @@ public boolean add(float e) { * @param count the minimum number of empty entries in the buffer after this call * @throws UnsupportedOperationException when {@code growable} is {@code false} and buffer is full */ - public void ensureRemaining(int count) { + @Override + public void ensureRemaining(final int count) { if (remaining() < count) { if (!growable) { throw new UnsupportedOperationException("Ring buffer is full and growth is disabled"); @@ -168,7 +178,7 @@ public void ensureRemaining(int count) { * * @param e the value to add to the buffer */ - public void addUnsafe(float e) { + public void addUnsafe(final float e) { // This is an extremely paranoid wrap check that in all likelihood will never run. With FIXUP_THRESHOLD at // 1 << 62, and the user pushing 2^32 values per second(!), it will take 68 years to wrap this counter . if (tail >= FIXUP_THRESHOLD) { @@ -188,7 +198,7 @@ public void addUnsafe(float e) { * @param notFullResult value to return is the buffer is not full * @return the overwritten entry if the buffer is full, the provided value otherwise */ - public float addOverwrite(float e, float notFullResult) { + public float addOverwrite(final float e, final float notFullResult) { float val = notFullResult; if (isFull()) { val = remove(); @@ -204,7 +214,7 @@ public float addOverwrite(float e, float notFullResult) { * @param e the float to be added to the buffer * @return true if the value was added successfully, false otherwise */ - public boolean offer(float e) { + public boolean offer(final float e) { if (isFull()) { return false; } @@ -218,7 +228,7 @@ public boolean offer(float e) { * @param count The number of elements to remove. * @throws NoSuchElementException if the buffer is empty */ - public float[] remove(int count) { + public float[] remove(final int count) { final int size = size(); if (size < count) { throw new NoSuchElementException(); @@ -264,7 +274,7 @@ public float removeUnsafe() { * @param onEmpty the value to return if the ring buffer is empty * @return The removed element if the ring buffer was non-empty, otherwise the value of 'onEmpty' */ - public float poll(float onEmpty) { + public float poll(final float onEmpty) { if (isEmpty()) { return onEmpty; } @@ -291,7 +301,7 @@ public float element() { * @param onEmpty the value to return if the ring buffer is empty * @return The head element if the ring buffer is non-empty, otherwise the value of 'onEmpty' */ - public float peek(float onEmpty) { + public float peek(final float onEmpty) { if (isEmpty()) { return onEmpty; } @@ -314,7 +324,7 @@ public float front() { * @throws NoSuchElementException if the buffer is empty * @return The element at the specified offset */ - public float front(int offset) { + public float front(final int offset) { if (offset < 0 || offset >= size()) { throw new NoSuchElementException(); } @@ -341,7 +351,7 @@ public float back() { * @param onEmpty the value to return if the ring buffer is empty * @return The tail element if the ring buffer is non-empty, otherwise the value of 'onEmpty' */ - public float peekBack(float onEmpty) { + public float peekBack(final float onEmpty) { if (isEmpty()) { return onEmpty; } @@ -385,4 +395,14 @@ public void remove() { throw new UnsupportedOperationException(); } } + + /** + * Get the storage array for this ring buffer. This is intended for testing and debugging purposes only. + * + * @return The storage array for this ring buffer. + */ + @TestOnly + public float[] getStorage() { + return storage; + } } diff --git a/Base/src/main/java/io/deephaven/base/ringbuffer/IntRingBuffer.java b/Base/src/main/java/io/deephaven/base/ringbuffer/IntRingBuffer.java index 80a47f0a389..ca83c1715d8 100644 --- a/Base/src/main/java/io/deephaven/base/ringbuffer/IntRingBuffer.java +++ b/Base/src/main/java/io/deephaven/base/ringbuffer/IntRingBuffer.java @@ -9,6 +9,7 @@ import io.deephaven.base.MathUtil; import io.deephaven.base.verify.Assert; +import org.jetbrains.annotations.TestOnly; import java.io.Serializable; import java.util.NoSuchElementException; @@ -19,7 +20,7 @@ * {@code long} values. Head and tail will not wrap around; instead we use storage arrays sized to 2^N to allow fast * determination of storage indices through a mask operation. */ -public class IntRingBuffer implements Serializable { +public class IntRingBuffer implements RingBuffer, Serializable { static final long FIXUP_THRESHOLD = 1L << 62; final boolean growable; int[] storage; @@ -32,7 +33,7 @@ public class IntRingBuffer implements Serializable { * * @param capacity minimum capacity of the ring buffer */ - public IntRingBuffer(int capacity) { + public IntRingBuffer(final int capacity) { this(capacity, true); } @@ -42,7 +43,7 @@ public IntRingBuffer(int capacity) { * @param capacity minimum capacity of ring buffer * @param growable whether to allow growth when the buffer is full. */ - public IntRingBuffer(int capacity, boolean growable) { + public IntRingBuffer(final int capacity, final boolean growable) { Assert.leq(capacity, "IntRingBuffer capacity", MathUtil.MAX_POWER_OF_2); this.growable = growable; @@ -59,7 +60,7 @@ public IntRingBuffer(int capacity, boolean growable) { * * @param increase Increase amount. The ring buffer's capacity will be increased by at least this amount. */ - protected void grow(int increase) { + protected void grow(final int increase) { final int size = size(); final long newCapacity = (long) storage.length + increase; // assert that we are not asking for the impossible @@ -83,7 +84,7 @@ protected void grow(int increase) { * * @param dest The destination buffer. */ - protected void copyRingBufferToArray(int[] dest) { + protected void copyRingBufferToArray(final int[] dest) { final int size = size(); final int storageHead = (int) (head & mask); @@ -99,27 +100,35 @@ protected void copyRingBufferToArray(int[] dest) { System.arraycopy(storage, 0, dest, firstCopyLen, secondCopyLen); } + @Override public boolean isFull() { return size() == storage.length; } + @Override public boolean isEmpty() { return tail == head; } + @Override public int size() { return Math.toIntExact(tail - head); } + @Override public int capacity() { return storage.length; } + @Override public int remaining() { return storage.length - size(); } + @Override public void clear() { + // region object-bulk-clear + // endregion object-bulk-clear tail = head = 0; } @@ -131,7 +140,7 @@ public void clear() { * @throws UnsupportedOperationException when {@code growable} is {@code false} and buffer is full * @return {@code true} if the int was added successfully */ - public boolean add(int e) { + public boolean add(final int e) { if (isFull()) { if (!growable) { throw new UnsupportedOperationException("Ring buffer is full and growth is disabled"); @@ -151,7 +160,8 @@ public boolean add(int e) { * @param count the minimum number of empty entries in the buffer after this call * @throws UnsupportedOperationException when {@code growable} is {@code false} and buffer is full */ - public void ensureRemaining(int count) { + @Override + public void ensureRemaining(final int count) { if (remaining() < count) { if (!growable) { throw new UnsupportedOperationException("Ring buffer is full and growth is disabled"); @@ -168,7 +178,7 @@ public void ensureRemaining(int count) { * * @param e the value to add to the buffer */ - public void addUnsafe(int e) { + public void addUnsafe(final int e) { // This is an extremely paranoid wrap check that in all likelihood will never run. With FIXUP_THRESHOLD at // 1 << 62, and the user pushing 2^32 values per second(!), it will take 68 years to wrap this counter . if (tail >= FIXUP_THRESHOLD) { @@ -188,7 +198,7 @@ public void addUnsafe(int e) { * @param notFullResult value to return is the buffer is not full * @return the overwritten entry if the buffer is full, the provided value otherwise */ - public int addOverwrite(int e, int notFullResult) { + public int addOverwrite(final int e, final int notFullResult) { int val = notFullResult; if (isFull()) { val = remove(); @@ -204,7 +214,7 @@ public int addOverwrite(int e, int notFullResult) { * @param e the int to be added to the buffer * @return true if the value was added successfully, false otherwise */ - public boolean offer(int e) { + public boolean offer(final int e) { if (isFull()) { return false; } @@ -218,7 +228,7 @@ public boolean offer(int e) { * @param count The number of elements to remove. * @throws NoSuchElementException if the buffer is empty */ - public int[] remove(int count) { + public int[] remove(final int count) { final int size = size(); if (size < count) { throw new NoSuchElementException(); @@ -264,7 +274,7 @@ public int removeUnsafe() { * @param onEmpty the value to return if the ring buffer is empty * @return The removed element if the ring buffer was non-empty, otherwise the value of 'onEmpty' */ - public int poll(int onEmpty) { + public int poll(final int onEmpty) { if (isEmpty()) { return onEmpty; } @@ -291,7 +301,7 @@ public int element() { * @param onEmpty the value to return if the ring buffer is empty * @return The head element if the ring buffer is non-empty, otherwise the value of 'onEmpty' */ - public int peek(int onEmpty) { + public int peek(final int onEmpty) { if (isEmpty()) { return onEmpty; } @@ -314,7 +324,7 @@ public int front() { * @throws NoSuchElementException if the buffer is empty * @return The element at the specified offset */ - public int front(int offset) { + public int front(final int offset) { if (offset < 0 || offset >= size()) { throw new NoSuchElementException(); } @@ -341,7 +351,7 @@ public int back() { * @param onEmpty the value to return if the ring buffer is empty * @return The tail element if the ring buffer is non-empty, otherwise the value of 'onEmpty' */ - public int peekBack(int onEmpty) { + public int peekBack(final int onEmpty) { if (isEmpty()) { return onEmpty; } @@ -385,4 +395,14 @@ public void remove() { throw new UnsupportedOperationException(); } } + + /** + * Get the storage array for this ring buffer. This is intended for testing and debugging purposes only. + * + * @return The storage array for this ring buffer. + */ + @TestOnly + public int[] getStorage() { + return storage; + } } diff --git a/Base/src/main/java/io/deephaven/base/ringbuffer/LongRingBuffer.java b/Base/src/main/java/io/deephaven/base/ringbuffer/LongRingBuffer.java index de5f8df9c90..43d9312711f 100644 --- a/Base/src/main/java/io/deephaven/base/ringbuffer/LongRingBuffer.java +++ b/Base/src/main/java/io/deephaven/base/ringbuffer/LongRingBuffer.java @@ -9,6 +9,7 @@ import io.deephaven.base.MathUtil; import io.deephaven.base.verify.Assert; +import org.jetbrains.annotations.TestOnly; import java.io.Serializable; import java.util.NoSuchElementException; @@ -19,7 +20,7 @@ * {@code long} values. Head and tail will not wrap around; instead we use storage arrays sized to 2^N to allow fast * determination of storage indices through a mask operation. */ -public class LongRingBuffer implements Serializable { +public class LongRingBuffer implements RingBuffer, Serializable { static final long FIXUP_THRESHOLD = 1L << 62; final boolean growable; long[] storage; @@ -32,7 +33,7 @@ public class LongRingBuffer implements Serializable { * * @param capacity minimum capacity of the ring buffer */ - public LongRingBuffer(int capacity) { + public LongRingBuffer(final int capacity) { this(capacity, true); } @@ -42,7 +43,7 @@ public LongRingBuffer(int capacity) { * @param capacity minimum capacity of ring buffer * @param growable whether to allow growth when the buffer is full. */ - public LongRingBuffer(int capacity, boolean growable) { + public LongRingBuffer(final int capacity, final boolean growable) { Assert.leq(capacity, "LongRingBuffer capacity", MathUtil.MAX_POWER_OF_2); this.growable = growable; @@ -59,7 +60,7 @@ public LongRingBuffer(int capacity, boolean growable) { * * @param increase Increase amount. The ring buffer's capacity will be increased by at least this amount. */ - protected void grow(int increase) { + protected void grow(final int increase) { final int size = size(); final long newCapacity = (long) storage.length + increase; // assert that we are not asking for the impossible @@ -83,7 +84,7 @@ protected void grow(int increase) { * * @param dest The destination buffer. */ - protected void copyRingBufferToArray(long[] dest) { + protected void copyRingBufferToArray(final long[] dest) { final int size = size(); final int storageHead = (int) (head & mask); @@ -99,27 +100,35 @@ protected void copyRingBufferToArray(long[] dest) { System.arraycopy(storage, 0, dest, firstCopyLen, secondCopyLen); } + @Override public boolean isFull() { return size() == storage.length; } + @Override public boolean isEmpty() { return tail == head; } + @Override public int size() { return Math.toIntExact(tail - head); } + @Override public int capacity() { return storage.length; } + @Override public int remaining() { return storage.length - size(); } + @Override public void clear() { + // region object-bulk-clear + // endregion object-bulk-clear tail = head = 0; } @@ -131,7 +140,7 @@ public void clear() { * @throws UnsupportedOperationException when {@code growable} is {@code false} and buffer is full * @return {@code true} if the long was added successfully */ - public boolean add(long e) { + public boolean add(final long e) { if (isFull()) { if (!growable) { throw new UnsupportedOperationException("Ring buffer is full and growth is disabled"); @@ -151,7 +160,8 @@ public boolean add(long e) { * @param count the minimum number of empty entries in the buffer after this call * @throws UnsupportedOperationException when {@code growable} is {@code false} and buffer is full */ - public void ensureRemaining(int count) { + @Override + public void ensureRemaining(final int count) { if (remaining() < count) { if (!growable) { throw new UnsupportedOperationException("Ring buffer is full and growth is disabled"); @@ -168,7 +178,7 @@ public void ensureRemaining(int count) { * * @param e the value to add to the buffer */ - public void addUnsafe(long e) { + public void addUnsafe(final long e) { // This is an extremely paranoid wrap check that in all likelihood will never run. With FIXUP_THRESHOLD at // 1 << 62, and the user pushing 2^32 values per second(!), it will take 68 years to wrap this counter . if (tail >= FIXUP_THRESHOLD) { @@ -188,7 +198,7 @@ public void addUnsafe(long e) { * @param notFullResult value to return is the buffer is not full * @return the overwritten entry if the buffer is full, the provided value otherwise */ - public long addOverwrite(long e, long notFullResult) { + public long addOverwrite(final long e, final long notFullResult) { long val = notFullResult; if (isFull()) { val = remove(); @@ -204,7 +214,7 @@ public long addOverwrite(long e, long notFullResult) { * @param e the long to be added to the buffer * @return true if the value was added successfully, false otherwise */ - public boolean offer(long e) { + public boolean offer(final long e) { if (isFull()) { return false; } @@ -218,7 +228,7 @@ public boolean offer(long e) { * @param count The number of elements to remove. * @throws NoSuchElementException if the buffer is empty */ - public long[] remove(int count) { + public long[] remove(final int count) { final int size = size(); if (size < count) { throw new NoSuchElementException(); @@ -264,7 +274,7 @@ public long removeUnsafe() { * @param onEmpty the value to return if the ring buffer is empty * @return The removed element if the ring buffer was non-empty, otherwise the value of 'onEmpty' */ - public long poll(long onEmpty) { + public long poll(final long onEmpty) { if (isEmpty()) { return onEmpty; } @@ -291,7 +301,7 @@ public long element() { * @param onEmpty the value to return if the ring buffer is empty * @return The head element if the ring buffer is non-empty, otherwise the value of 'onEmpty' */ - public long peek(long onEmpty) { + public long peek(final long onEmpty) { if (isEmpty()) { return onEmpty; } @@ -314,7 +324,7 @@ public long front() { * @throws NoSuchElementException if the buffer is empty * @return The element at the specified offset */ - public long front(int offset) { + public long front(final int offset) { if (offset < 0 || offset >= size()) { throw new NoSuchElementException(); } @@ -341,7 +351,7 @@ public long back() { * @param onEmpty the value to return if the ring buffer is empty * @return The tail element if the ring buffer is non-empty, otherwise the value of 'onEmpty' */ - public long peekBack(long onEmpty) { + public long peekBack(final long onEmpty) { if (isEmpty()) { return onEmpty; } @@ -385,4 +395,14 @@ public void remove() { throw new UnsupportedOperationException(); } } + + /** + * Get the storage array for this ring buffer. This is intended for testing and debugging purposes only. + * + * @return The storage array for this ring buffer. + */ + @TestOnly + public long[] getStorage() { + return storage; + } } diff --git a/Base/src/main/java/io/deephaven/base/ringbuffer/ObjectRingBuffer.java b/Base/src/main/java/io/deephaven/base/ringbuffer/ObjectRingBuffer.java index ad93ddd1f43..4c7dc788602 100644 --- a/Base/src/main/java/io/deephaven/base/ringbuffer/ObjectRingBuffer.java +++ b/Base/src/main/java/io/deephaven/base/ringbuffer/ObjectRingBuffer.java @@ -11,6 +11,7 @@ import io.deephaven.base.MathUtil; import io.deephaven.base.verify.Assert; +import org.jetbrains.annotations.TestOnly; import java.io.Serializable; import java.util.NoSuchElementException; @@ -21,7 +22,7 @@ * {@code long} values. Head and tail will not wrap around; instead we use storage arrays sized to 2^N to allow fast * determination of storage indices through a mask operation. */ -public class ObjectRingBuffer implements Serializable { +public class ObjectRingBuffer implements RingBuffer, Serializable { static final long FIXUP_THRESHOLD = 1L << 62; final boolean growable; T[] storage; @@ -34,7 +35,7 @@ public class ObjectRingBuffer implements Serializable { * * @param capacity minimum capacity of the ring buffer */ - public ObjectRingBuffer(int capacity) { + public ObjectRingBuffer(final int capacity) { this(capacity, true); } @@ -44,7 +45,7 @@ public ObjectRingBuffer(int capacity) { * @param capacity minimum capacity of ring buffer * @param growable whether to allow growth when the buffer is full. */ - public ObjectRingBuffer(int capacity, boolean growable) { + public ObjectRingBuffer(final int capacity, final boolean growable) { Assert.leq(capacity, "ObjectRingBuffer capacity", MathUtil.MAX_POWER_OF_2); this.growable = growable; @@ -61,7 +62,7 @@ public ObjectRingBuffer(int capacity, boolean growable) { * * @param increase Increase amount. The ring buffer's capacity will be increased by at least this amount. */ - protected void grow(int increase) { + protected void grow(final int increase) { final int size = size(); final long newCapacity = (long) storage.length + increase; // assert that we are not asking for the impossible @@ -85,7 +86,7 @@ protected void grow(int increase) { * * @param dest The destination buffer. */ - protected void copyRingBufferToArray(T[] dest) { + protected void copyRingBufferToArray(final T[] dest) { final int size = size(); final int storageHead = (int) (head & mask); @@ -101,27 +102,43 @@ protected void copyRingBufferToArray(T[] dest) { System.arraycopy(storage, 0, dest, firstCopyLen, secondCopyLen); } + @Override public boolean isFull() { return size() == storage.length; } + @Override public boolean isEmpty() { return tail == head; } + @Override public int size() { return Math.toIntExact(tail - head); } + @Override public int capacity() { return storage.length; } + @Override public int remaining() { return storage.length - size(); } + @Override public void clear() { + // region object-bulk-clear + final int storageHead = (int) (head & mask); + final int size = size(); + // firstLen is either the size of the ring buffer or the distance from head to the end of the storage array. + final int firstLen = Math.min(storage.length - storageHead, size); + // secondLen is the number of elements remaining from the first clear. + final int secondLen = size - firstLen; + Arrays.fill(storage, storageHead, storageHead + firstLen, null); + Arrays.fill(storage, 0, secondLen, null); + // endregion object-bulk-clear tail = head = 0; } @@ -133,7 +150,7 @@ public void clear() { * @throws UnsupportedOperationException when {@code growable} is {@code false} and buffer is full * @return {@code true} if the Object was added successfully */ - public boolean add(T e) { + public boolean add(final T e) { if (isFull()) { if (!growable) { throw new UnsupportedOperationException("Ring buffer is full and growth is disabled"); @@ -153,7 +170,8 @@ public boolean add(T e) { * @param count the minimum number of empty entries in the buffer after this call * @throws UnsupportedOperationException when {@code growable} is {@code false} and buffer is full */ - public void ensureRemaining(int count) { + @Override + public void ensureRemaining(final int count) { if (remaining() < count) { if (!growable) { throw new UnsupportedOperationException("Ring buffer is full and growth is disabled"); @@ -170,7 +188,7 @@ public void ensureRemaining(int count) { * * @param e the value to add to the buffer */ - public void addUnsafe(T e) { + public void addUnsafe(final T e) { // This is an extremely paranoid wrap check that in all likelihood will never run. With FIXUP_THRESHOLD at // 1 << 62, and the user pushing 2^32 values per second(!), it will take 68 years to wrap this counter . if (tail >= FIXUP_THRESHOLD) { @@ -190,7 +208,7 @@ public void addUnsafe(T e) { * @param notFullResult value to return is the buffer is not full * @return the overwritten entry if the buffer is full, the provided value otherwise */ - public T addOverwrite(T e, T notFullResult) { + public T addOverwrite(final T e, final T notFullResult) { T val = notFullResult; if (isFull()) { val = remove(); @@ -206,7 +224,7 @@ public T addOverwrite(T e, T notFullResult) { * @param e the Object to be added to the buffer * @return true if the value was added successfully, false otherwise */ - public boolean offer(T e) { + public boolean offer(final T e) { if (isFull()) { return false; } @@ -220,7 +238,7 @@ public boolean offer(T e) { * @param count The number of elements to remove. * @throws NoSuchElementException if the buffer is empty */ - public T[] remove(int count) { + public T[] remove(final int count) { final int size = size(); if (size < count) { throw new NoSuchElementException(); @@ -281,7 +299,7 @@ public T removeUnsafe() { * @param onEmpty the value to return if the ring buffer is empty * @return The removed element if the ring buffer was non-empty, otherwise the value of 'onEmpty' */ - public T poll(T onEmpty) { + public T poll(final T onEmpty) { if (isEmpty()) { return onEmpty; } @@ -308,7 +326,7 @@ public T element() { * @param onEmpty the value to return if the ring buffer is empty * @return The head element if the ring buffer is non-empty, otherwise the value of 'onEmpty' */ - public T peek(T onEmpty) { + public T peek(final T onEmpty) { if (isEmpty()) { return onEmpty; } @@ -331,7 +349,7 @@ public T front() { * @throws NoSuchElementException if the buffer is empty * @return The element at the specified offset */ - public T front(int offset) { + public T front(final int offset) { if (offset < 0 || offset >= size()) { throw new NoSuchElementException(); } @@ -358,7 +376,7 @@ public T back() { * @param onEmpty the value to return if the ring buffer is empty * @return The tail element if the ring buffer is non-empty, otherwise the value of 'onEmpty' */ - public T peekBack(T onEmpty) { + public T peekBack(final T onEmpty) { if (isEmpty()) { return onEmpty; } @@ -402,4 +420,14 @@ public void remove() { throw new UnsupportedOperationException(); } } + + /** + * Get the storage array for this ring buffer. This is intended for testing and debugging purposes only. + * + * @return The storage array for this ring buffer. + */ + @TestOnly + public T[] getStorage() { + return storage; + } } diff --git a/Base/src/main/java/io/deephaven/base/ringbuffer/RingBuffer.java b/Base/src/main/java/io/deephaven/base/ringbuffer/RingBuffer.java new file mode 100644 index 00000000000..4c9f0b55a3e --- /dev/null +++ b/Base/src/main/java/io/deephaven/base/ringbuffer/RingBuffer.java @@ -0,0 +1,79 @@ +// +// Copyright (c) 2016-2024 Deephaven Data Labs and Patent Pending +// +package io.deephaven.base.ringbuffer; + +import org.jetbrains.annotations.NotNull; + +/** + * Generic interface for a ring buffer and factory methods for buffer creation. + */ +public interface RingBuffer { + @NotNull + static RingBuffer makeRingBuffer( + @NotNull final Class dataType, + final int capacity, + final boolean growable) { + final RingBuffer result; + if (dataType == char.class || dataType == Character.class) { + result = new CharRingBuffer(capacity, growable); + } else if (dataType == byte.class || dataType == Byte.class) { + result = new ByteRingBuffer(capacity, growable); + } else if (dataType == double.class || dataType == Double.class) { + result = new DoubleRingBuffer(capacity, growable); + } else if (dataType == float.class || dataType == Float.class) { + result = new FloatRingBuffer(capacity, growable); + } else if (dataType == int.class || dataType == Integer.class) { + result = new IntRingBuffer(capacity, growable); + } else if (dataType == long.class || dataType == Long.class) { + result = new LongRingBuffer(capacity, growable); + } else if (dataType == short.class || dataType == Short.class) { + result = new ShortRingBuffer(capacity, growable); + } else { + result = new ObjectRingBuffer(capacity, growable); + } + return result; + } + + /** + * Whether the buffer is completely full. + */ + boolean isFull(); + + /** + * Whether the buffer is entirely empty. + */ + boolean isEmpty(); + + /** + * Return how many items are currently in the buffer. + */ + int size(); + + /** + * Return how many items can fit in the buffer at its current capacity. If the buffer can grow, this number can + * change. + */ + int capacity(); + + /** + * Return how many free slots exist in this buffer at its current capacity. + */ + int remaining(); + + /** + * Clear the buffer of all values. If this is an object ring buffer, this will additionally set all values to + * {@code null}. + */ + void clear(); + + /** + * Ensure that at least {@code count} free slots are available in the buffer. If the buffer is growable, the + * capacity may be increased to accommodate the new slots. If the buffer is not growable and there are insufficient + * free slots, an {@link UnsupportedOperationException} will be thrown. + * + * @param count the number of free slots to ensure are available + * @throws UnsupportedOperationException if the buffer is not growable and there are insufficient free slots + */ + void ensureRemaining(int count); +} diff --git a/Base/src/main/java/io/deephaven/base/ringbuffer/ShortRingBuffer.java b/Base/src/main/java/io/deephaven/base/ringbuffer/ShortRingBuffer.java index 89619fb839c..94076d3a40f 100644 --- a/Base/src/main/java/io/deephaven/base/ringbuffer/ShortRingBuffer.java +++ b/Base/src/main/java/io/deephaven/base/ringbuffer/ShortRingBuffer.java @@ -9,6 +9,7 @@ import io.deephaven.base.MathUtil; import io.deephaven.base.verify.Assert; +import org.jetbrains.annotations.TestOnly; import java.io.Serializable; import java.util.NoSuchElementException; @@ -19,7 +20,7 @@ * {@code long} values. Head and tail will not wrap around; instead we use storage arrays sized to 2^N to allow fast * determination of storage indices through a mask operation. */ -public class ShortRingBuffer implements Serializable { +public class ShortRingBuffer implements RingBuffer, Serializable { static final long FIXUP_THRESHOLD = 1L << 62; final boolean growable; short[] storage; @@ -32,7 +33,7 @@ public class ShortRingBuffer implements Serializable { * * @param capacity minimum capacity of the ring buffer */ - public ShortRingBuffer(int capacity) { + public ShortRingBuffer(final int capacity) { this(capacity, true); } @@ -42,7 +43,7 @@ public ShortRingBuffer(int capacity) { * @param capacity minimum capacity of ring buffer * @param growable whether to allow growth when the buffer is full. */ - public ShortRingBuffer(int capacity, boolean growable) { + public ShortRingBuffer(final int capacity, final boolean growable) { Assert.leq(capacity, "ShortRingBuffer capacity", MathUtil.MAX_POWER_OF_2); this.growable = growable; @@ -59,7 +60,7 @@ public ShortRingBuffer(int capacity, boolean growable) { * * @param increase Increase amount. The ring buffer's capacity will be increased by at least this amount. */ - protected void grow(int increase) { + protected void grow(final int increase) { final int size = size(); final long newCapacity = (long) storage.length + increase; // assert that we are not asking for the impossible @@ -83,7 +84,7 @@ protected void grow(int increase) { * * @param dest The destination buffer. */ - protected void copyRingBufferToArray(short[] dest) { + protected void copyRingBufferToArray(final short[] dest) { final int size = size(); final int storageHead = (int) (head & mask); @@ -99,27 +100,35 @@ protected void copyRingBufferToArray(short[] dest) { System.arraycopy(storage, 0, dest, firstCopyLen, secondCopyLen); } + @Override public boolean isFull() { return size() == storage.length; } + @Override public boolean isEmpty() { return tail == head; } + @Override public int size() { return Math.toIntExact(tail - head); } + @Override public int capacity() { return storage.length; } + @Override public int remaining() { return storage.length - size(); } + @Override public void clear() { + // region object-bulk-clear + // endregion object-bulk-clear tail = head = 0; } @@ -131,7 +140,7 @@ public void clear() { * @throws UnsupportedOperationException when {@code growable} is {@code false} and buffer is full * @return {@code true} if the short was added successfully */ - public boolean add(short e) { + public boolean add(final short e) { if (isFull()) { if (!growable) { throw new UnsupportedOperationException("Ring buffer is full and growth is disabled"); @@ -151,7 +160,8 @@ public boolean add(short e) { * @param count the minimum number of empty entries in the buffer after this call * @throws UnsupportedOperationException when {@code growable} is {@code false} and buffer is full */ - public void ensureRemaining(int count) { + @Override + public void ensureRemaining(final int count) { if (remaining() < count) { if (!growable) { throw new UnsupportedOperationException("Ring buffer is full and growth is disabled"); @@ -168,7 +178,7 @@ public void ensureRemaining(int count) { * * @param e the value to add to the buffer */ - public void addUnsafe(short e) { + public void addUnsafe(final short e) { // This is an extremely paranoid wrap check that in all likelihood will never run. With FIXUP_THRESHOLD at // 1 << 62, and the user pushing 2^32 values per second(!), it will take 68 years to wrap this counter . if (tail >= FIXUP_THRESHOLD) { @@ -188,7 +198,7 @@ public void addUnsafe(short e) { * @param notFullResult value to return is the buffer is not full * @return the overwritten entry if the buffer is full, the provided value otherwise */ - public short addOverwrite(short e, short notFullResult) { + public short addOverwrite(final short e, final short notFullResult) { short val = notFullResult; if (isFull()) { val = remove(); @@ -204,7 +214,7 @@ public short addOverwrite(short e, short notFullResult) { * @param e the short to be added to the buffer * @return true if the value was added successfully, false otherwise */ - public boolean offer(short e) { + public boolean offer(final short e) { if (isFull()) { return false; } @@ -218,7 +228,7 @@ public boolean offer(short e) { * @param count The number of elements to remove. * @throws NoSuchElementException if the buffer is empty */ - public short[] remove(int count) { + public short[] remove(final int count) { final int size = size(); if (size < count) { throw new NoSuchElementException(); @@ -264,7 +274,7 @@ public short removeUnsafe() { * @param onEmpty the value to return if the ring buffer is empty * @return The removed element if the ring buffer was non-empty, otherwise the value of 'onEmpty' */ - public short poll(short onEmpty) { + public short poll(final short onEmpty) { if (isEmpty()) { return onEmpty; } @@ -291,7 +301,7 @@ public short element() { * @param onEmpty the value to return if the ring buffer is empty * @return The head element if the ring buffer is non-empty, otherwise the value of 'onEmpty' */ - public short peek(short onEmpty) { + public short peek(final short onEmpty) { if (isEmpty()) { return onEmpty; } @@ -314,7 +324,7 @@ public short front() { * @throws NoSuchElementException if the buffer is empty * @return The element at the specified offset */ - public short front(int offset) { + public short front(final int offset) { if (offset < 0 || offset >= size()) { throw new NoSuchElementException(); } @@ -341,7 +351,7 @@ public short back() { * @param onEmpty the value to return if the ring buffer is empty * @return The tail element if the ring buffer is non-empty, otherwise the value of 'onEmpty' */ - public short peekBack(short onEmpty) { + public short peekBack(final short onEmpty) { if (isEmpty()) { return onEmpty; } @@ -385,4 +395,14 @@ public void remove() { throw new UnsupportedOperationException(); } } + + /** + * Get the storage array for this ring buffer. This is intended for testing and debugging purposes only. + * + * @return The storage array for this ring buffer. + */ + @TestOnly + public short[] getStorage() { + return storage; + } } diff --git a/Base/src/main/java/io/deephaven/base/stats/Composite.java b/Base/src/main/java/io/deephaven/base/stats/Composite.java index 81c66d4f55f..676bf593bf3 100644 --- a/Base/src/main/java/io/deephaven/base/stats/Composite.java +++ b/Base/src/main/java/io/deephaven/base/stats/Composite.java @@ -25,13 +25,13 @@ public Composite(long now, Value... values) { // ---------------------------------------------------------------- private static Value[] checkValues(Value[] values) { - Require.neqNull(values, "values", 1); - Require.gtZero(values.length, "values.length", 1); - Require.neqNull(values[0], "values[0]", 1); + Require.neqNull(values, "values"); + Require.gtZero(values.length, "values.length"); + Require.neqNull(values[0], "values[0]"); char typeTag = values[0].getTypeTag(); for (int nIndex = 1; nIndex < values.length; nIndex++) { - Require.neqNull(values[nIndex], "values[nIndex]", 1); - Require.eq(values[nIndex].getTypeTag(), "values[nIndex].getTypeTag()", typeTag, "typeTag", 1); + Require.neqNull(values[nIndex], "values[nIndex]"); + Require.eq(values[nIndex].getTypeTag(), "values[nIndex].getTypeTag()", typeTag, "typeTag"); } return values; } diff --git a/Base/src/main/java/io/deephaven/base/verify/Assert.java b/Base/src/main/java/io/deephaven/base/verify/Assert.java index 4e01086ae14..ee33fe7af0a 100644 --- a/Base/src/main/java/io/deephaven/base/verify/Assert.java +++ b/Base/src/main/java/io/deephaven/base/verify/Assert.java @@ -3,7 +3,6 @@ // package io.deephaven.base.verify; -import java.awt.EventQueue; import java.util.function.Consumer; // -------------------------------------------------------------------- @@ -24,12 +23,6 @@ *

  • void valuesNeverOccur(value0, name0, value1, name1, ... ) * * - * - *