pax_global_header00006660000000000000000000000064143343356260014523gustar00rootroot0000000000000052 comment=da52c98bb97cfaa321be04030bc97dcee7de63ff go-base36-0.2.0/000077500000000000000000000000001433433562600132105ustar00rootroot00000000000000go-base36-0.2.0/.github/000077500000000000000000000000001433433562600145505ustar00rootroot00000000000000go-base36-0.2.0/.github/workflows/000077500000000000000000000000001433433562600166055ustar00rootroot00000000000000go-base36-0.2.0/.github/workflows/automerge.yml000066400000000000000000000003761433433562600213260ustar00rootroot00000000000000# File managed by web3-bot. DO NOT EDIT. # See https://github.com/protocol/.github/ for details. name: Automerge on: [ pull_request ] jobs: automerge: uses: protocol/.github/.github/workflows/automerge.yml@master with: job: 'automerge' go-base36-0.2.0/.github/workflows/go-check.yml000066400000000000000000000052251433433562600210140ustar00rootroot00000000000000# File managed by web3-bot. DO NOT EDIT. # See https://github.com/protocol/.github/ for details. on: [push, pull_request] name: Go Checks jobs: unit: runs-on: ubuntu-latest name: All env: RUNGOGENERATE: false steps: - uses: actions/checkout@v3 with: submodules: recursive - uses: actions/setup-go@v3 with: go-version: "1.19.x" - name: Run repo-specific setup uses: ./.github/actions/go-check-setup if: hashFiles('./.github/actions/go-check-setup') != '' - name: Read config if: hashFiles('./.github/workflows/go-check-config.json') != '' run: | if jq -re .gogenerate ./.github/workflows/go-check-config.json; then echo "RUNGOGENERATE=true" >> $GITHUB_ENV fi - name: Install staticcheck run: go install honnef.co/go/tools/cmd/staticcheck@376210a89477dedbe6fdc4484b233998650d7b3c # 2022.1.3 (v0.3.3) - name: Check that go.mod is tidy uses: protocol/multiple-go-modules@v1.2 with: run: | go mod tidy if [[ -n $(git ls-files --other --exclude-standard --directory -- go.sum) ]]; then echo "go.sum was added by go mod tidy" exit 1 fi git diff --exit-code -- go.sum go.mod - name: gofmt if: ${{ success() || failure() }} # run this step even if the previous one failed run: | out=$(gofmt -s -l .) if [[ -n "$out" ]]; then echo $out | awk '{print "::error file=" $0 ",line=0,col=0::File is not gofmt-ed."}' exit 1 fi - name: go vet if: ${{ success() || failure() }} # run this step even if the previous one failed uses: protocol/multiple-go-modules@v1.2 with: run: go vet ./... - name: staticcheck if: ${{ success() || failure() }} # run this step even if the previous one failed uses: protocol/multiple-go-modules@v1.2 with: run: | set -o pipefail staticcheck ./... | sed -e 's@\(.*\)\.go@./\1.go@g' - name: go generate uses: protocol/multiple-go-modules@v1.2 if: (success() || failure()) && env.RUNGOGENERATE == 'true' with: run: | git clean -fd # make sure there aren't untracked files / directories go generate ./... # check if go generate modified or added any files if ! $(git add . && git diff-index HEAD --exit-code --quiet); then echo "go generated caused changes to the repository:" git status --short exit 1 fi go-base36-0.2.0/.github/workflows/go-test.yml000066400000000000000000000051161433433562600207150ustar00rootroot00000000000000# File managed by web3-bot. DO NOT EDIT. # See https://github.com/protocol/.github/ for details. on: [push, pull_request] name: Go Test jobs: unit: strategy: fail-fast: false matrix: os: [ "ubuntu", "windows", "macos" ] go: [ "1.18.x", "1.19.x" ] env: COVERAGES: "" runs-on: ${{ format('{0}-latest', matrix.os) }} name: ${{ matrix.os }} (go ${{ matrix.go }}) steps: - uses: actions/checkout@v3 with: submodules: recursive - uses: actions/setup-go@v3 with: go-version: ${{ matrix.go }} - name: Go information run: | go version go env - name: Use msys2 on windows if: ${{ matrix.os == 'windows' }} shell: bash # The executable for msys2 is also called bash.cmd # https://github.com/actions/virtual-environments/blob/main/images/win/Windows2019-Readme.md#shells # If we prepend its location to the PATH # subsequent 'shell: bash' steps will use msys2 instead of gitbash run: echo "C:/msys64/usr/bin" >> $GITHUB_PATH - name: Run repo-specific setup uses: ./.github/actions/go-test-setup if: hashFiles('./.github/actions/go-test-setup') != '' - name: Run tests uses: protocol/multiple-go-modules@v1.2 with: # Use -coverpkg=./..., so that we include cross-package coverage. # If package ./A imports ./B, and ./A's tests also cover ./B, # this means ./B's coverage will be significantly higher than 0%. run: go test -v -shuffle=on -coverprofile=module-coverage.txt -coverpkg=./... ./... - name: Run tests (32 bit) if: ${{ matrix.os != 'macos' }} # can't run 32 bit tests on OSX. uses: protocol/multiple-go-modules@v1.2 env: GOARCH: 386 with: run: | export "PATH=${{ env.PATH_386 }}:$PATH" go test -v -shuffle=on ./... - name: Run tests with race detector if: ${{ matrix.os == 'ubuntu' }} # speed things up. Windows and OSX VMs are slow uses: protocol/multiple-go-modules@v1.2 with: run: go test -v -race ./... - name: Collect coverage files shell: bash run: echo "COVERAGES=$(find . -type f -name 'module-coverage.txt' | tr -s '\n' ',' | sed 's/,$//')" >> $GITHUB_ENV - name: Upload coverage to Codecov uses: codecov/codecov-action@81cd2dc8148241f03f5839d295e000b8f761e378 # v3.1.0 with: files: '${{ env.COVERAGES }}' env_vars: OS=${{ matrix.os }}, GO=${{ matrix.go }} go-base36-0.2.0/.github/workflows/release-check.yml000066400000000000000000000004101433433562600220160ustar00rootroot00000000000000# File managed by web3-bot. DO NOT EDIT. # See https://github.com/protocol/.github/ for details. name: Release Checker on: pull_request: paths: [ 'version.json' ] jobs: release-check: uses: protocol/.github/.github/workflows/release-check.yml@master go-base36-0.2.0/.github/workflows/releaser.yml000066400000000000000000000003571433433562600211370ustar00rootroot00000000000000# File managed by web3-bot. DO NOT EDIT. # See https://github.com/protocol/.github/ for details. name: Releaser on: push: paths: [ 'version.json' ] jobs: releaser: uses: protocol/.github/.github/workflows/releaser.yml@master go-base36-0.2.0/.github/workflows/tagpush.yml000066400000000000000000000003551433433562600210060ustar00rootroot00000000000000# File managed by web3-bot. DO NOT EDIT. # See https://github.com/protocol/.github/ for details. name: Tag Push Checker on: push: tags: - v* jobs: releaser: uses: protocol/.github/.github/workflows/tagpush.yml@master go-base36-0.2.0/LICENSE.md000066400000000000000000000022351433433562600146160ustar00rootroot00000000000000The software contents of this repository are Copyright (c) Protocol Labs, Licensed under the `Permissive License Stack`, meaning either of: - Apache-2.0 Software License: https://www.apache.org/licenses/LICENSE-2.0 ([...4tr2kfsq](https://gateway.ipfs.io/ipfs/bafkreiankqxazcae4onkp436wag2lj3ccso4nawxqkkfckd6cg4tr2kfsq)) - MIT Software License: https://opensource.org/licenses/MIT ([...vljevcba](https://gateway.ipfs.io/ipfs/bafkreiepofszg4gfe2gzuhojmksgemsub2h4uy2gewdnr35kswvljevcba)) You may not use the contents of this repository except in compliance with one of the listed Licenses. For an extended clarification of the intent behind the choice of Licensing please refer to https://protocol.ai/blog/announcing-the-permissive-license-stack/ Unless required by applicable law or agreed to in writing, software distributed under the terms listed in this notice is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See each License for the specific language governing permissions and limitations under that License. `SPDX-License-Identifier: Apache-2.0 OR MIT` go-base36-0.2.0/README.md000066400000000000000000000010561433433562600144710ustar00rootroot00000000000000multiformats/go-base36 ======================= > Simple base36 codec This is an optimized codec for []byte <=> base36 string conversion ## Documentation https://pkg.go.dev/github.com/multiformats/go-base36 ## Lead Maintainer [Steven Allen](https://github.com/stebalien) ## Contributing Contributions are welcome! This repository is related to the IPFS project and therefore governed by our [contributing guidelines](https://github.com/ipfs/community/blob/master/CONTRIBUTING.md). ## License [SPDX-License-Identifier: Apache-2.0 OR MIT](LICENSE.md) go-base36-0.2.0/base36.go000066400000000000000000000066351433433562600146340ustar00rootroot00000000000000/* Package base36 provides a reasonably fast implementation of a binary base36 codec. */ package base36 // Simplified code based on https://godoc.org/github.com/mr-tron/base58 // which in turn is based on https://github.com/trezor/trezor-crypto/commit/89a7d7797b806fac import ( "fmt" ) const UcAlphabet = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ" const LcAlphabet = "0123456789abcdefghijklmnopqrstuvwxyz" const maxDigitOrdinal = 'z' const maxDigitValueB36 = 35 var revAlphabet [maxDigitOrdinal + 1]byte func init() { for i := range revAlphabet { revAlphabet[i] = maxDigitValueB36 + 1 } for i, c := range UcAlphabet { revAlphabet[byte(c)] = byte(i) if c > '9' { revAlphabet[byte(c)+32] = byte(i) } } } // EncodeToStringUc encodes the given byte-buffer as base36 using [0-9A-Z] as // the digit-alphabet func EncodeToStringUc(b []byte) string { return encode(b, UcAlphabet) } // EncodeToStringLc encodes the given byte-buffer as base36 using [0-9a-z] as // the digit-alphabet func EncodeToStringLc(b []byte) string { return encode(b, LcAlphabet) } func encode(inBuf []byte, al string) string { bufsz := len(inBuf) zcnt := 0 for zcnt < bufsz && inBuf[zcnt] == 0 { zcnt++ } // It is crucial to make this as short as possible, especially for // the usual case of CIDs. bufsz = zcnt + // This is an integer simplification of // ceil(log(256)/log(36)) (bufsz-zcnt)*277/179 + 1 // Note: pools *DO NOT* help, the overhead of zeroing // kills any performance gain to be had out := make([]byte, bufsz) var idx, stopIdx int var carry uint32 stopIdx = bufsz - 1 for _, b := range inBuf[zcnt:] { idx = bufsz - 1 for carry = uint32(b); idx > stopIdx || carry != 0; idx-- { carry += uint32((out[idx])) * 256 out[idx] = byte(carry % 36) carry /= 36 } stopIdx = idx } // Determine the additional "zero-gap" in the buffer (aside from zcnt) for stopIdx = zcnt; stopIdx < bufsz && out[stopIdx] == 0; stopIdx++ { } // Now encode the values with actual alphabet in-place vBuf := out[stopIdx-zcnt:] bufsz = len(vBuf) for idx = 0; idx < bufsz; idx++ { out[idx] = al[vBuf[idx]] } return string(out[:bufsz]) } // DecodeString takes a base36 encoded string and returns a slice of the decoded // bytes. func DecodeString(s string) ([]byte, error) { if len(s) == 0 { return nil, fmt.Errorf("can not decode zero-length string") } zcnt := 0 for zcnt < len(s) && s[zcnt] == '0' { zcnt++ } // the 32bit algo stretches the result up to 2 times binu := make([]byte, 2*(((len(s))*179/277)+1)) // no more than 84 bytes when len(s) <= 64 outi := make([]uint32, (len(s)+3)/4) // no more than 16 bytes when len(s) <= 64 for _, r := range s { if r > maxDigitOrdinal || revAlphabet[r] > maxDigitValueB36 { return nil, fmt.Errorf("invalid base36 character (%q)", r) } c := uint64(revAlphabet[r]) for j := len(outi) - 1; j >= 0; j-- { t := uint64(outi[j])*36 + c c = (t >> 32) outi[j] = uint32(t & 0xFFFFFFFF) } } mask := (uint(len(s)%4) * 8) if mask == 0 { mask = 32 } mask -= 8 outidx := 0 for j := 0; j < len(outi); j++ { for mask < 32 { // loop relies on uint overflow binu[outidx] = byte(outi[j] >> mask) mask -= 8 outidx++ } mask = 24 } // find the most significant byte post-decode, if any for msb := zcnt; msb < outidx; msb++ { if binu[msb] > 0 { return binu[msb-zcnt : outidx : outidx], nil } } // it's all zeroes return binu[:outidx:outidx], nil } go-base36-0.2.0/base36_test.go000066400000000000000000000052441433433562600156660ustar00rootroot00000000000000package base36 import ( "bytes" "crypto/rand" "fmt" "testing" ) var testEncoders = []func([]byte) string{ EncodeToStringLc, EncodeToStringUc, } func testBasicTrip(t *testing.T, src, enc, result string) { if enc != result { t.Fatalf("Encoding failed: expected '%s', got '%s'", result, enc) } trip, err := DecodeString(enc) if err != nil { t.Fatalf("Unexpected error when decoding '%s': %s", enc, err) } if !bytes.Equal(trip, []byte(src)) { t.Fatalf("Encode-Decode roundtrip failed: expected '%s', got '%s'", src, trip) } } func TestBasicTripLc(t *testing.T) { testBasicTrip(t, "Decentralize everything!!!", EncodeToStringLc([]byte("Decentralize everything!!!")), "m552ng4dabi4neu1oo8l4i5mndwmpc3mkukwtxy9", ) } func TestBasicTripUc(t *testing.T) { testBasicTrip(t, "Decentralize everything!!!", EncodeToStringUc([]byte("Decentralize everything!!!")), "M552NG4DABI4NEU1OO8L4I5MNDWMPC3MKUKWTXY9", ) } func TestPermute(t *testing.T) { buf := make([]byte, 137+16) // sufficiently large prime number of bytes + another 16 to test leading 0s rand.Read(buf[16:]) for _, encoder := range testEncoders { // test roundtrip from the full zero-prefixed buffer down to a single byte for i := 0; i < len(buf); i++ { // use a copy to verify we are not overwriting the supplied buffer newBuf := make([]byte, len(buf)-i) copy(newBuf, buf[i:]) enc := encoder(newBuf) out, err := DecodeString(enc) if err != nil { t.Fatal(err) } if !bytes.Equal(newBuf, buf[i:]) { t.Fatal("the provided buffer was modified", buf[i:], out) } if !bytes.Equal(buf[i:], out) { t.Fatal("input wasn't the same as output", buf[i:], out) } } } } var benchmarkBuf [36]byte // typical CID size var benchmarkDecodeTgt string func init() { rand.Read(benchmarkBuf[:]) benchmarkDecodeTgt = testEncoders[0](benchmarkBuf[:]) } func BenchmarkRoundTrip(b *testing.B) { b.ResetTimer() for i, encoder := range testEncoders { b.Run(fmt.Sprintf("encoder #%d", i+1), func(b *testing.B) { for i := 0; i < b.N; i++ { enc := encoder(benchmarkBuf[:]) out, err := DecodeString(enc) if err != nil { b.Fatal(err) } if !bytes.Equal(benchmarkBuf[:], out) { b.Fatal("input wasnt the same as output", benchmarkBuf, out) } } }) } } func BenchmarkEncode(b *testing.B) { b.ResetTimer() for i, encoder := range testEncoders { b.Run(fmt.Sprintf("encoder #%d", i+1), func(b *testing.B) { for i := 0; i < b.N; i++ { encoder(benchmarkBuf[:]) } }) } } func BenchmarkDecode(b *testing.B) { b.ResetTimer() b.Run("Decoding", func(b *testing.B) { for i := 0; i < b.N; i++ { DecodeString(benchmarkDecodeTgt) } }) } go-base36-0.2.0/go.mod000066400000000000000000000000621433433562600143140ustar00rootroot00000000000000module github.com/multiformats/go-base36 go 1.18 go-base36-0.2.0/go.sum000066400000000000000000000000001433433562600143310ustar00rootroot00000000000000go-base36-0.2.0/version.json000066400000000000000000000000321433433562600155630ustar00rootroot00000000000000{ "version": "v0.2.0" }