diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index efb55cf..6747a94 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -6,12 +6,6 @@ on: pull_request: branches: [ main ] -env: - # renovate: datasource=go depName=mvdan.cc/gofumpt - GOFUMPT_VERSION: v0.3.1 - # renovate: datasource=go depName=github.com/golangci/golangci-lint - GOLANGCI_LINT_VERSION: v1.53.3 - jobs: skip-check: name: Skip check @@ -24,7 +18,7 @@ jobs: contents: read steps: - id: skip-check - uses: fkirc/skip-duplicate-actions@9d116fa7e55f295019cfab7e3ab72b478bcf7fdd # tag=v4.0.0 + uses: fkirc/skip-duplicate-actions@v5 with: do_not_skip: '["schedule", "workflow_dispatch"]' paths: |- @@ -44,10 +38,10 @@ jobs: if: ${{ needs.skip-check.outputs.should_skip != 'true' }} steps: - name: Check out the code - uses: actions/checkout@2541b1294d2704b0964813337f33b291d3f8596b # tag=v3.0.2 + uses: actions/checkout@v4 - name: Set up Go - uses: actions/setup-go@84cbf8094393cdc5fe1fe1671ff2647332956b1a # tag=v3.2.1 + uses: actions/setup-go@v5 with: go-version-file: 'go.mod' check-latest: true @@ -69,6 +63,4 @@ jobs: run: make vet - name: Lint - uses: golangci/golangci-lint-action@0ad9a0988b3973e851ab0a07adf248ec2e100376 # tag=v3.3.1 - with: - version: ${{ env.GOLANGCI_LINT_VERSION }} + uses: golangci/golangci-lint-action@v6 diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index b9c213c..ff18116 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -39,17 +39,17 @@ jobs: steps: - name: Check out the code - uses: actions/checkout@2541b1294d2704b0964813337f33b291d3f8596b # tag=v3.0.2 + uses: actions/checkout@v4 - name: Set up Go - uses: actions/setup-go@84cbf8094393cdc5fe1fe1671ff2647332956b1a # tag=v3.2.1 + uses: actions/setup-go@v5 with: go-version-file: 'go.mod' cache: true # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL - uses: github/codeql-action/init@0c670bbf0414f39666df6ce8e718ec5662c21e03 # tag=v2.1.17 + uses: github/codeql-action/init@v3 with: languages: ${{ matrix.language }} # If you wish to specify custom queries, you can do so here or in a config file. @@ -68,4 +68,4 @@ jobs: - run: make build - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@0c670bbf0414f39666df6ce8e718ec5662c21e03 # tag=v2.1.17 + uses: github/codeql-action/analyze@v3 diff --git a/.github/workflows/container.yml b/.github/workflows/container.yml deleted file mode 100644 index 45b64d3..0000000 --- a/.github/workflows/container.yml +++ /dev/null @@ -1,135 +0,0 @@ -name: Container - -on: - push: - branches: [ main ] - pull_request: - branches: [ main ] - -env: - # renovate: datasource=go depName=github.com/goreleaser/goreleaser - GORELEASER_VERSION: v1.10.2 - -jobs: - skip-check: - name: Skip check - continue-on-error: true - runs-on: ubuntu-latest - outputs: - should_skip: ${{ steps.skip-check.outputs.should_skip }} - permissions: - actions: write - contents: read - steps: - - id: skip-check - uses: fkirc/skip-duplicate-actions@9d116fa7e55f295019cfab7e3ab72b478bcf7fdd # tag=v4.0.0 - with: - do_not_skip: '["schedule", "workflow_dispatch"]' - paths: |- - [ - "**.go", - ".dockerignore", - ".github/workflows/container.yml", - "Dockerfile*", - "Makefile", - "go.mod", - "go.sum" - ] - skip_after_successful_duplicate: false - - - build-binaries: - name: Build binaries using goreleaser - needs: skip-check - if: ${{ needs.skip-check.outputs.should_skip != 'true' }} - runs-on: ubuntu-latest - container: - image: docker.io/goreleaser/goreleaser-cross:v1.18.3 - options: --privileged - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - GORELEASER_CURRENT_TAG: "${{ env.goreleaser_current_tag }}" - steps: - - name: Check out the code - uses: actions/checkout@2541b1294d2704b0964813337f33b291d3f8596b # tag=v3.0.2 - - - name: Set up Go - uses: actions/setup-go@84cbf8094393cdc5fe1fe1671ff2647332956b1a # tag=v3.2.1 - with: - go-version-file: 'go.mod' - cache: true - - - name: Run Goreleaser - run: goreleaser release --rm-dist --skip-validate --skip-publish --snapshot --debug - - - name: Archive generated artifacts - uses: actions/upload-artifact@3cea5372237819ed00197afe530f5a7ea3e805c8 # tag=v3.1.0 - with: - name: parca-debuginfo-dist-container - if-no-files-found: error - path: | - goreleaser/dist - !goreleaser/dist/*.txt - - build-and-push-container: - name: Container build and push (when merged) - needs: build-binaries - runs-on: ubuntu-latest - container: - # https://github.com/containers/podman/tree/main/contrib/podmanimage - # Specifying SHA repeatedly fails: - # @sha256:421ac576cebff98e90c531e7b9ce4482370ecc7cee59abc2341714031bfb5f43 - image: quay.io/containers/podman:v4.1.1 - options: >- - --device /dev/fuse:rw - --privileged - --security-opt label=disable - --security-opt seccomp=unconfined - permissions: - id-token: write - packages: write - contents: read - steps: - - name: Install dependencies - run: dnf install --assumeyes --repo fedora git make jq - - - name: Check out code into the Go module directory - uses: actions/checkout@2541b1294d2704b0964813337f33b291d3f8596b # tag=v3.0.2 - - - name: Set up Go - uses: actions/setup-go@84cbf8094393cdc5fe1fe1671ff2647332956b1a # tag=v3.2.1 - with: - go-version-file: 'go.mod' - check-latest: true - - - uses: actions/download-artifact@v3 - with: - name: parca-debuginfo-dist-container - path: goreleaser/dist - - - name: Build container - run: make container - - - name: Check images are created - run: podman images | grep 'ghcr.io/parca-dev/parca-debuginfo' - - - name: Install cosign - if: ${{ github.event_name != 'pull_request' }} - uses: sigstore/cosign-installer@09a077b27eb1310dcfb21981bee195b30ce09de0 # tag=v2.5.0 - - - name: Login to registry - if: ${{ github.event_name != 'pull_request' }} - run: | - echo "${{ secrets.GITHUB_TOKEN }}" | podman login -u parca-dev --password-stdin ghcr.io - - - name: Install crane - if: ${{ github.event_name != 'pull_request' }} - uses: imjasonh/setup-crane@e82f1b9a8007d399333baba4d75915558e9fb6a4 # tag=v0.2 - - - name: Push and sign container - if: ${{ github.event_name != 'pull_request' }} - env: - COSIGN_EXPERIMENTAL: true - run: | - make push-container - make sign-container diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index a9eeb20..c035cc6 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -19,7 +19,7 @@ jobs: contents: read steps: - id: skip-check - uses: fkirc/skip-duplicate-actions@9d116fa7e55f295019cfab7e3ab72b478bcf7fdd # tag=v4.0.0 + uses: fkirc/skip-duplicate-actions@v5 with: do_not_skip: '["schedule", "workflow_dispatch"]' paths: |- @@ -38,10 +38,10 @@ jobs: runs-on: ubuntu-latest steps: - name: Check out the code - uses: actions/checkout@2541b1294d2704b0964813337f33b291d3f8596b # tag=v3.0.2 + uses: actions/checkout@v4 - name: Set up Go - uses: actions/setup-go@84cbf8094393cdc5fe1fe1671ff2647332956b1a # tag=v3.2.1 + uses: actions/setup-go@v5 with: go-version-file: 'go.mod' cache: true diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index c10c82e..a0b77d5 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -8,10 +8,6 @@ on: permissions: contents: write -env: - # renovate: datasource=go depName=github.com/goreleaser/goreleaser - GORELEASER_VERSION: v1.18.2 - jobs: binaries: name: Goreleaser release @@ -19,103 +15,38 @@ jobs: if: startsWith(github.ref, 'refs/tags/') steps: - name: Checkout - uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3 + uses: actions/checkout@v4 with: fetch-depth: 0 - name: Set up Go - uses: actions/setup-go@fac708d6674e30b6ba41289acaab6d4b75aa0753 # v4.0.1 + uses: actions/setup-go@v5 with: go-version-file: 'go.mod' - name: Run GoReleaser - uses: goreleaser/goreleaser-action@336e29918d653399e599bfca99fadc1d7ffbc9f7 # v4.3.0 + uses: goreleaser/goreleaser-action@v6 with: distribution: goreleaser - version: ${{ env.GORELEASER_VERSION }} - args: release --clean --timeout=60m + args: release --clean env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - name: Archive generated artifacts - uses: actions/upload-artifact@3cea5372237819ed00197afe530f5a7ea3e805c8 # tag=v3.1.0 + uses: actions/upload-artifact@v4 with: name: parca-debuginfo-dist-release if-no-files-found: error path: | - goreleaser/dist - !goreleaser/dist/*.txt + dist/ docs: name: Publish Docs runs-on: ubuntu-latest needs: binaries steps: - - uses: actions/checkout@93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8 # tag=v3.1.0 + - uses: actions/checkout@v4 - name: Publish Vercel run: | curl -X POST "https://api.vercel.com/v1/integrations/deploy/${{ secrets.VERCEL_WEBHOOK }}" - - container: - name: Build and release container images - runs-on: ubuntu-latest - needs: binaries - container: - # https://github.com/containers/podman/tree/main/contrib/podmanimage - # Specifying SHA repeatedly fails: - # @sha256:421ac576cebff98e90c531e7b9ce4482370ecc7cee59abc2341714031bfb5f43 - image: quay.io/containers/podman:v4.1.1 - options: >- - --device /dev/fuse:rw - --privileged - --security-opt label=disable - --security-opt seccomp=unconfined - permissions: - id-token: write - packages: write - contents: read - steps: - - name: Install dependencies - run: dnf install --assumeyes --repo fedora git make jq - - - name: Check out code into the Go module directory - uses: actions/checkout@2541b1294d2704b0964813337f33b291d3f8596b # tag=v3.0.2 - - - name: Get branch name - shell: bash - run: echo "GITHUB_BRANCH_NAME=$(echo ${GITHUB_REF#refs/heads/} | tr / -)" >> $GITHUB_ENV - - - uses: actions/download-artifact@v3 - with: - name: parca-debuginfo-dist-release - path: goreleaser/dist - - - name: Build container - run: make container - - - name: Check images are created - run: podman images | grep 'ghcr.io/parca-dev/parca-debuginfo' - - - name: Login to registry - if: ${{ github.event_name != 'pull_request' }} - run: | - echo "${{ secrets.GITHUB_TOKEN }}" | podman login -u parca-dev --password-stdin ghcr.io - - - name: Install cosign - uses: sigstore/cosign-installer@09a077b27eb1310dcfb21981bee195b30ce09de0 # tag=v2.5.0 - - - name: Install crane - if: ${{ github.event_name != 'pull_request' }} - uses: imjasonh/setup-crane@e82f1b9a8007d399333baba4d75915558e9fb6a4 # tag=v0.2 - - - name: Push container - if: ${{ github.event_name != 'pull_request' }} - run: | - make push-container - - - name: Sign container - env: - COSIGN_EXPERIMENTAL: true - run: | - make sign-container diff --git a/.golangci.yml b/.golangci.yml index 9d35950..1d582a4 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -13,28 +13,21 @@ linters: - style - unused disable: - - exhaustivestruct - exhaustruct - funlen - gci - gochecknoglobals - godox - - goerr113 - - golint - - gomnd - gomoddirectives - - interfacer - ireturn - lll - - maligned - nlreturn - paralleltest - - scopelint - testpackage - varnamelen - wrapcheck - wsl - - nosnakecase + - err113 issues: exclude-rules: @@ -56,7 +49,7 @@ linters-settings: - pkg: github.com/pkg/errors desc: Use fmt.Errorf instead errcheck: - exclude: ./.errcheck_excludes.txt + exclude-functions: ./.errcheck_excludes.txt goimports: local-prefixes: github.com/parca-dev/parca-debuginfo gofumpt: diff --git a/.goreleaser.yml b/.goreleaser.yml index 11c9e1f..eb609b4 100644 --- a/.goreleaser.yml +++ b/.goreleaser.yml @@ -1,11 +1,4 @@ -# NOTICE: This file is written with the assumption that it will be used in parca-dev/cross-builder. -# - There are paths in this file that are specific to parca-dev/cross-builder and Github Actions. -# - Unfortunately, Goreleaser does not support templating environment variables per build config. -project_name: parca-debuginfo -dist: ./goreleaser/dist -before: - hooks: - - go mod tidy +version: 2 builds: - main: ./cmd/parca-debuginfo/ id: "parca-debuginfo" @@ -28,25 +21,29 @@ builds: # {{.CommitDate}} is the date of the commit to make builds reproducible. - -X main.version={{.Version}} -X main.commit={{.FullCommit}} -X main.date={{.CommitDate}} -X main.goArch={{.Runtime.Goarch}} archives: - - replacements: - linux: Linux - darwin: Darwin - amd64: x86_64 + - name_template: >- + {{ .ProjectName }}_ + {{- trimprefix .Version "v" }}_ + {{- title .Os }}_ + {{- if eq .Arch "amd64_v1" }}x86_64 + {{- else if eq .Arch "arm64_v8.0" }}aarch64 + {{- else }}{{ .Arch }}{{ end }} + format: binary format_overrides: - goos: windows format: zip checksum: name_template: 'checksums.txt' snapshot: - name_template: "{{ incpatch .Tag }}-next" + version_template: "{{ incpatch .Tag }}-next" release: + github: + owner: parca-dev + name: parca-debuginfo prerelease: auto - # Defaults to empty. + draft: false + name_template: '{{ .Tag }}' footer: | - ## Docker images - - `docker pull ghcr.io/parca-dev/parca-debuginfo:{{ .Tag }}` - ## Thanks! Join our [Discord server](https://discord.com/invite/ZgUpYgpzXy); diff --git a/cmd/parca-debuginfo/main.go b/cmd/parca-debuginfo/main.go index cc474ec..3198b58 100644 --- a/cmd/parca-debuginfo/main.go +++ b/cmd/parca-debuginfo/main.go @@ -70,8 +70,7 @@ type flags struct { Extract struct { OutputDir string `kong:"help='Output directory path to use for extracted debug information files.',default='out'"` - Paths []string `kong:"required,arg,name='path',help='Paths to extract debug information.',type:'path'"` - CompressDWARFSections bool `kong:"default=false,help:'Compress debuginfo files DWARF sections before uploading.'"` + Paths []string `kong:"required,arg,name='path',help='Paths to extract debug information.',type:'path'"` } `cmd:"" help:"Extract debug information."` Buildid struct { @@ -93,19 +92,7 @@ func main() { } } -type uploadInfo struct { - buildID string - path string - reader io.ReadSeeker - size int64 -} - func run(kongCtx *kong.Context, flags flags) error { - opts := []elfwriter.Option{} - if flags.Extract.CompressDWARFSections { - opts = append(opts, elfwriter.WithCompressDWARFSections()) - } - var g grun.Group ctx, cancel := context.WithCancel(context.Background()) switch kongCtx.Command() { @@ -197,13 +184,13 @@ func run(kongCtx *kong.Context, flags flags) error { if err != nil { return fmt.Errorf("check if upload should be initiated for %q with Build ID %q: %w", flags.Upload.Path, buildID, err) } - if !shouldInitiate.ShouldInitiateUpload { - fmt.Fprintf(os.Stdout, "Skipping upload of %q with Build ID %q as the store instructed not to: %s\n", flags.Upload.Path, buildID, shouldInitiate.Reason) + if !shouldInitiate.GetShouldInitiateUpload() { + fmt.Fprintf(os.Stdout, "Skipping upload of %q with Build ID %q as the store instructed not to: %s\n", flags.Upload.Path, buildID, shouldInitiate.GetReason()) return nil } if flags.Upload.NoInitiate { - fmt.Fprintf(os.Stdout, "Not initiating upload of %q with Build ID %q as requested, but would have requested that next, because: %s\n", flags.Upload.Path, buildID, shouldInitiate.Reason) + fmt.Fprintf(os.Stdout, "Not initiating upload of %q with Build ID %q as requested, but would have requested that next, because: %s\n", flags.Upload.Path, buildID, shouldInitiate.GetReason()) return nil } @@ -228,24 +215,24 @@ func run(kongCtx *kong.Context, flags flags) error { } if flags.LogLevel == LogLevelDebug { - fmt.Fprintf(os.Stdout, "Upload instructions\nBuildID: %s\nUploadID: %s\nUploadStrategy: %s\nSignedURL: %s\nType: %s\n", initiationResp.UploadInstructions.BuildId, initiationResp.UploadInstructions.UploadId, initiationResp.UploadInstructions.UploadStrategy.String(), initiationResp.UploadInstructions.SignedUrl, initiationResp.UploadInstructions.Type) + fmt.Fprintf(os.Stdout, "Upload instructions\nBuildID: %s\nUploadID: %s\nUploadStrategy: %s\nSignedURL: %s\nType: %s\n", initiationResp.GetUploadInstructions().GetBuildId(), initiationResp.GetUploadInstructions().GetUploadId(), initiationResp.GetUploadInstructions().GetUploadStrategy().String(), initiationResp.GetUploadInstructions().GetSignedUrl(), initiationResp.GetUploadInstructions().GetType()) } - switch initiationResp.UploadInstructions.UploadStrategy { + switch initiationResp.GetUploadInstructions().GetUploadStrategy() { case debuginfopb.UploadInstructions_UPLOAD_STRATEGY_GRPC: if flags.LogLevel == LogLevelDebug { fmt.Fprintf(os.Stdout, "Performing a gRPC upload for %q with Build ID %q.", flags.Upload.Path, buildID) } - _, err = grpcUploadClient.Upload(ctx, initiationResp.UploadInstructions, reader) + _, err = grpcUploadClient.Upload(ctx, initiationResp.GetUploadInstructions(), reader) case debuginfopb.UploadInstructions_UPLOAD_STRATEGY_SIGNED_URL: if flags.LogLevel == LogLevelDebug { fmt.Fprintf(os.Stdout, "Performing a signed URL upload for %q with Build ID %q.", flags.Upload.Path, buildID) } - err = uploadViaSignedURL(ctx, initiationResp.UploadInstructions.SignedUrl, reader) + err = uploadViaSignedURL(ctx, initiationResp.GetUploadInstructions().GetSignedUrl(), reader) case debuginfopb.UploadInstructions_UPLOAD_STRATEGY_UNSPECIFIED: err = errors.New("no upload strategy specified") default: - err = fmt.Errorf("unknown upload strategy: %v", initiationResp.UploadInstructions.UploadStrategy) + err = fmt.Errorf("unknown upload strategy: %v", initiationResp.GetUploadInstructions().GetUploadStrategy()) } if err != nil { return fmt.Errorf("upload %q with Build ID %q: %w", flags.Upload.Path, buildID, err) @@ -253,7 +240,7 @@ func run(kongCtx *kong.Context, flags flags) error { _, err = debuginfoClient.MarkUploadFinished(ctx, &debuginfopb.MarkUploadFinishedRequest{ BuildId: buildID, - UploadId: initiationResp.UploadInstructions.UploadId, + UploadId: initiationResp.GetUploadInstructions().GetUploadId(), Type: debuginfoTypeStringToPb(flags.Upload.Type), }) if err != nil { @@ -270,7 +257,7 @@ func run(kongCtx *kong.Context, flags flags) error { if err := os.RemoveAll(flags.Extract.OutputDir); err != nil { return fmt.Errorf("failed to clean output dir, %s: %w", flags.Extract.OutputDir, err) } - if err := os.MkdirAll(flags.Extract.OutputDir, 0o755); err != nil { + if err := os.MkdirAll(flags.Extract.OutputDir, 0o755); err != nil { //nolint:mnd return fmt.Errorf("failed to create output dir, %s: %w", flags.Extract.OutputDir, err) } for _, path := range flags.Extract.Paths { @@ -475,7 +462,7 @@ func grpcConn(reg prometheus.Registerer, flags flags) (*grpc.ClientConn, error) })) } - return grpc.Dial(flags.Upload.StoreAddress, opts...) + return grpc.NewClient(flags.Upload.StoreAddress, opts...) } type perRequestBearerToken struct { @@ -547,7 +534,7 @@ func getSectionData(elfFile *elf.File, sectionName string) ([]byte, error) { } data, err := section.Data() if err != nil { - return nil, fmt.Errorf("failed to read data from section %s: %v", sectionName, err) + return nil, fmt.Errorf("failed to read data from section %s: %w", sectionName, err) } return data, nil } @@ -555,9 +542,9 @@ func getSectionData(elfFile *elf.File, sectionName string) ([]byte, error) { // getBuildIDFromNotes returns the build ID from an ELF notes section data. func getBuildIDFromNotes(notes []byte) (string, error) { // 0x3 is the "Build ID" type. Not sure where this is standardized. - buildID, found, err := getNoteHexString(notes, "GNU", 0x3) + buildID, found, err := getNoteHexString(notes, "GNU", 0x3) //nolint:mnd if err != nil { - return "", fmt.Errorf("could not determine BuildID: %v", err) + return "", fmt.Errorf("could not determine BuildID: %w", err) } if !found { return "", ErrNoBuildID @@ -567,8 +554,7 @@ func getBuildIDFromNotes(notes []byte) (string, error) { // getNoteHexString returns the hex string contents of an ELF note from a note section, as described // in the ELF standard in Figure 2-3. -func getNoteHexString(sectionBytes []byte, name string, noteType uint32) ( - noteHexString string, found bool, err error) { +func getNoteHexString(sectionBytes []byte, name string, noteType uint32) (string, bool, error) { // The data stored inside ELF notes is made of one or multiple structs, containing the // following fields: // - namesz // 32-bit, size of "name" @@ -579,27 +565,29 @@ func getNoteHexString(sectionBytes []byte, name string, noteType uint32) ( // Because of this structure, the information of the build id starts at the 17th byte. // Null terminated string - nameBytes := append([]byte(name), 0x0) - noteTypeBytes := make([]byte, 4) + nameBytes := append([]byte(name), 0x0) //nolint:mnd + noteTypeBytes := make([]byte, 4) //nolint:mnd binary.LittleEndian.PutUint32(noteTypeBytes, noteType) - noteHeader := append(noteTypeBytes, nameBytes...) //nolint:gocritic + noteHeader := append(noteTypeBytes, nameBytes...) //nolint:gocritic,makezero // Try to find the note in the section idx := bytes.Index(sectionBytes, noteHeader) if idx == -1 { return "", false, nil } - if idx < 4 { // there needs to be room for descsz + // there needs to be room for descsz + if idx < 4 { //nolint:mnd return "", false, errors.New("could not read note data size") } idxDataStart := idx + len(noteHeader) - idxDataStart += (4 - (idxDataStart & 3)) & 3 // data is 32bit-aligned, round up + // data is 32bit-aligned, round up + idxDataStart += (4 - (idxDataStart & 3)) & 3 //nolint:mnd // read descsz and compute the last index of the note data dataSize := binary.LittleEndian.Uint32(sectionBytes[idx-4 : idx]) - idxDataEnd := uint64(idxDataStart) + uint64(dataSize) + idxDataEnd := uint64(idxDataStart) + uint64(dataSize) //nolint:gosec // Check sanity (64 is totally arbitrary, as we only use it for Linux ID and Build ID) if idxDataEnd > uint64(len(sectionBytes)) || dataSize > 64 {