pax_global_header00006660000000000000000000000064144042463360014520gustar00rootroot0000000000000052 comment=58a41f7df1d82d16599e259325b3337d69f70e3b go-multibase-0.2.0/000077500000000000000000000000001440424633600141075ustar00rootroot00000000000000go-multibase-0.2.0/.codecov.yml000066400000000000000000000000151440424633600163260ustar00rootroot00000000000000comment: off go-multibase-0.2.0/.github/000077500000000000000000000000001440424633600154475ustar00rootroot00000000000000go-multibase-0.2.0/.github/workflows/000077500000000000000000000000001440424633600175045ustar00rootroot00000000000000go-multibase-0.2.0/.github/workflows/automerge.yml000066400000000000000000000003761440424633600222250ustar00rootroot00000000000000# 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-multibase-0.2.0/.github/workflows/go-check.yml000066400000000000000000000047111440424633600217120ustar00rootroot00000000000000# 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 steps: - uses: actions/checkout@v3 with: submodules: recursive - id: config uses: protocol/.github/.github/actions/read-config@master - uses: actions/setup-go@v3 with: go-version: 1.20.x - name: Run repo-specific setup uses: ./.github/actions/go-check-setup if: hashFiles('./.github/actions/go-check-setup') != '' - name: Install staticcheck run: go install honnef.co/go/tools/cmd/staticcheck@4970552d932f48b71485287748246cf3237cebdf # 2023.1 (v0.4.0) - 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()) && fromJSON(steps.config.outputs.json).gogenerate == true with: run: | git clean -fd # make sure there aren't untracked files / directories go generate -x ./... # 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-multibase-0.2.0/.github/workflows/go-test.yml000066400000000000000000000060341440424633600216140ustar00rootroot00000000000000# 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.19.x","1.20.x"] env: COVERAGES: "" runs-on: ${{ fromJSON(vars[format('UCI_GO_TEST_RUNNER_{0}', matrix.os)] || format('"{0}-latest"', matrix.os)) }} name: ${{ matrix.os }} (go ${{ matrix.go }}) steps: - uses: actions/checkout@v3 with: submodules: recursive - id: config uses: protocol/.github/.github/actions/read-config@master - 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 if: contains(fromJSON(steps.config.outputs.json).skipOSes, matrix.os) == false 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) # can't run 32 bit tests on OSX. if: matrix.os != 'macos' && fromJSON(steps.config.outputs.json).skip32bit != true && contains(fromJSON(steps.config.outputs.json).skipOSes, matrix.os) == false uses: protocol/multiple-go-modules@v1.2 env: GOARCH: 386 with: run: | export "PATH=$PATH_386:$PATH" go test -v -shuffle=on ./... - name: Run tests with race detector # speed things up. Windows and OSX VMs are slow if: matrix.os == 'ubuntu' && contains(fromJSON(steps.config.outputs.json).skipOSes, matrix.os) == false 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@d9f34f8cd5cb3b3eb79b3e4b5dae3a16df499a70 # v3.1.1 with: files: '${{ env.COVERAGES }}' env_vars: OS=${{ matrix.os }}, GO=${{ matrix.go }} go-multibase-0.2.0/.github/workflows/release-check.yml000066400000000000000000000004621440424633600227240ustar00rootroot00000000000000# File managed by web3-bot. DO NOT EDIT. # See https://github.com/protocol/.github/ for details. name: Release Checker on: pull_request_target: paths: [ 'version.json' ] jobs: release-check: uses: protocol/.github/.github/workflows/release-check.yml@master with: go-version: 1.20.x go-multibase-0.2.0/.github/workflows/releaser.yml000066400000000000000000000003571440424633600220360ustar00rootroot00000000000000# 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-multibase-0.2.0/.github/workflows/tagpush.yml000066400000000000000000000003551440424633600217050ustar00rootroot00000000000000# 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-multibase-0.2.0/.gitignore000066400000000000000000000000451440424633600160760ustar00rootroot00000000000000*.swp multibase-conv/multibase-conv go-multibase-0.2.0/.gitmodules000066400000000000000000000001251440424633600162620ustar00rootroot00000000000000[submodule "spec"] path = spec url = https://github.com/multiformats/multibase.git go-multibase-0.2.0/LICENSE000066400000000000000000000020751440424633600151200ustar00rootroot00000000000000The MIT License (MIT) Copyright (c) 2016 Protocol Labs Inc. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. go-multibase-0.2.0/Makefile000066400000000000000000000001451440424633600155470ustar00rootroot00000000000000test: deps go test -count=1 -race -v ./... export IPFS_API ?= v04x.ipfs.io deps: go get -t ./... go-multibase-0.2.0/README.md000066400000000000000000000033761440424633600153770ustar00rootroot00000000000000# go-multibase [![](https://img.shields.io/badge/made%20by-Protocol%20Labs-blue.svg?style=flat-square)](http://ipn.io) [![](https://img.shields.io/badge/project-multiformats-blue.svg?style=flat-square)](https://github.com/multiformats/multiformats) [![](https://img.shields.io/badge/freenode-%23ipfs-blue.svg?style=flat-square)](https://webchat.freenode.net/?channels=%23ipfs) [![](https://img.shields.io/badge/readme%20style-standard-brightgreen.svg?style=flat-square)](https://github.com/RichardLitt/standard-readme) [![Travis CI](https://img.shields.io/travis/multiformats/go-multibase.svg?style=flat-square&branch=master)](https://travis-ci.org/multiformats/go-multibase) [![codecov.io](https://img.shields.io/codecov/c/github/multiformats/go-multibase.svg?style=flat-square&branch=master)](https://codecov.io/github/multiformats/go-multibase?branch=master) > Implementation of [multibase](https://github.com/multiformats/multibase) -self identifying base encodings- in Go. ## Install `go-multibase` is a standard Go module which can be installed with: ```sh go get github.com/multiformats/go-multibase ``` ## Contribute Contributions welcome. Please check out [the issues](https://github.com/multiformats/go-multibase/issues). Check out our [contributing document](https://github.com/multiformats/multiformats/blob/master/contributing.md) for more information on how we work, and about contributing in general. Please be aware that all interactions related to multiformats are subject to the IPFS [Code of Conduct](https://github.com/ipfs/community/blob/master/code-of-conduct.md). Small note: If editing the README, please conform to the [standard-readme](https://github.com/RichardLitt/standard-readme) specification. ## License [MIT](LICENSE) ยฉ 2016 Protocol Labs Inc. go-multibase-0.2.0/base16.go000066400000000000000000000006611440424633600155220ustar00rootroot00000000000000package multibase func hexEncodeToStringUpper(src []byte) string { dst := make([]byte, len(src)*2) hexEncodeUpper(dst, src) return string(dst) } var hexTableUppers = [16]byte{ '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F', } func hexEncodeUpper(dst, src []byte) int { for i, v := range src { dst[i*2] = hexTableUppers[v>>4] dst[i*2+1] = hexTableUppers[v&0x0f] } return len(src) * 2 } go-multibase-0.2.0/base2.go000066400000000000000000000021301440424633600154260ustar00rootroot00000000000000package multibase import ( "fmt" "strconv" "strings" ) // binaryEncodeToString takes an array of bytes and returns // multibase binary representation func binaryEncodeToString(src []byte) string { dst := make([]byte, len(src)*8) encodeBinary(dst, src) return string(dst) } // encodeBinary takes the src and dst bytes and converts each // byte to their binary rep using power reduction method func encodeBinary(dst []byte, src []byte) { for i, b := range src { for j := 0; j < 8; j++ { if b&(1<>3) for i, dstIndex := 0, 0; i < len(s); i = i + 8 { value, err := strconv.ParseInt(s[i:i+8], 2, 0) if err != nil { return nil, fmt.Errorf("error while conversion: %s", err) } data[dstIndex] = byte(value) dstIndex++ } return data, nil } go-multibase-0.2.0/base256emoji.go000066400000000000000000000074541440424633600166430ustar00rootroot00000000000000package multibase import ( "strconv" "strings" "unicode/utf8" ) var base256emojiTable = [256]rune{ // Curated list, this is just a list of things that *somwhat* are related to our comunity '๐Ÿš€', '๐Ÿช', 'โ˜„', '๐Ÿ›ฐ', '๐ŸŒŒ', // Space '๐ŸŒ‘', '๐ŸŒ’', '๐ŸŒ“', '๐ŸŒ”', '๐ŸŒ•', '๐ŸŒ–', '๐ŸŒ—', '๐ŸŒ˜', // Moon '๐ŸŒ', '๐ŸŒ', '๐ŸŒŽ', // Our Home, for now (earth) '๐Ÿ‰', // Dragon!!! 'โ˜€', // Our Garden, for now (sol) '๐Ÿ’ป', '๐Ÿ–ฅ', '๐Ÿ’พ', '๐Ÿ’ฟ', // Computer // The rest is completed from https://home.unicode.org/emoji/emoji-frequency/ at the time of creation (december 2021) (the data is from 2019), most used first until we reach 256. // We exclude modifier based emojies (such as flags) as they are bigger than one single codepoint. // Some other emojies were removed adhoc for various reasons. '๐Ÿ˜‚', 'โค', '๐Ÿ˜', '๐Ÿคฃ', '๐Ÿ˜Š', '๐Ÿ™', '๐Ÿ’•', '๐Ÿ˜ญ', '๐Ÿ˜˜', '๐Ÿ‘', '๐Ÿ˜…', '๐Ÿ‘', '๐Ÿ˜', '๐Ÿ”ฅ', '๐Ÿฅฐ', '๐Ÿ’”', '๐Ÿ’–', '๐Ÿ’™', '๐Ÿ˜ข', '๐Ÿค”', '๐Ÿ˜†', '๐Ÿ™„', '๐Ÿ’ช', '๐Ÿ˜‰', 'โ˜บ', '๐Ÿ‘Œ', '๐Ÿค—', '๐Ÿ’œ', '๐Ÿ˜”', '๐Ÿ˜Ž', '๐Ÿ˜‡', '๐ŸŒน', '๐Ÿคฆ', '๐ŸŽ‰', '๐Ÿ’ž', 'โœŒ', 'โœจ', '๐Ÿคท', '๐Ÿ˜ฑ', '๐Ÿ˜Œ', '๐ŸŒธ', '๐Ÿ™Œ', '๐Ÿ˜‹', '๐Ÿ’—', '๐Ÿ’š', '๐Ÿ˜', '๐Ÿ’›', '๐Ÿ™‚', '๐Ÿ’“', '๐Ÿคฉ', '๐Ÿ˜„', '๐Ÿ˜€', '๐Ÿ–ค', '๐Ÿ˜ƒ', '๐Ÿ’ฏ', '๐Ÿ™ˆ', '๐Ÿ‘‡', '๐ŸŽถ', '๐Ÿ˜’', '๐Ÿคญ', 'โฃ', '๐Ÿ˜œ', '๐Ÿ’‹', '๐Ÿ‘€', '๐Ÿ˜ช', '๐Ÿ˜‘', '๐Ÿ’ฅ', '๐Ÿ™‹', '๐Ÿ˜ž', '๐Ÿ˜ฉ', '๐Ÿ˜ก', '๐Ÿคช', '๐Ÿ‘Š', '๐Ÿฅณ', '๐Ÿ˜ฅ', '๐Ÿคค', '๐Ÿ‘‰', '๐Ÿ’ƒ', '๐Ÿ˜ณ', 'โœ‹', '๐Ÿ˜š', '๐Ÿ˜', '๐Ÿ˜ด', '๐ŸŒŸ', '๐Ÿ˜ฌ', '๐Ÿ™ƒ', '๐Ÿ€', '๐ŸŒท', '๐Ÿ˜ป', '๐Ÿ˜“', 'โญ', 'โœ…', '๐Ÿฅบ', '๐ŸŒˆ', '๐Ÿ˜ˆ', '๐Ÿค˜', '๐Ÿ’ฆ', 'โœ”', '๐Ÿ˜ฃ', '๐Ÿƒ', '๐Ÿ’', 'โ˜น', '๐ŸŽŠ', '๐Ÿ’˜', '๐Ÿ˜ ', 'โ˜', '๐Ÿ˜•', '๐ŸŒบ', '๐ŸŽ‚', '๐ŸŒป', '๐Ÿ˜', '๐Ÿ–•', '๐Ÿ’', '๐Ÿ™Š', '๐Ÿ˜น', '๐Ÿ—ฃ', '๐Ÿ’ซ', '๐Ÿ’€', '๐Ÿ‘‘', '๐ŸŽต', '๐Ÿคž', '๐Ÿ˜›', '๐Ÿ”ด', '๐Ÿ˜ค', '๐ŸŒผ', '๐Ÿ˜ซ', 'โšฝ', '๐Ÿค™', 'โ˜•', '๐Ÿ†', '๐Ÿคซ', '๐Ÿ‘ˆ', '๐Ÿ˜ฎ', '๐Ÿ™†', '๐Ÿป', '๐Ÿƒ', '๐Ÿถ', '๐Ÿ’', '๐Ÿ˜ฒ', '๐ŸŒฟ', '๐Ÿงก', '๐ŸŽ', 'โšก', '๐ŸŒž', '๐ŸŽˆ', 'โŒ', 'โœŠ', '๐Ÿ‘‹', '๐Ÿ˜ฐ', '๐Ÿคจ', '๐Ÿ˜ถ', '๐Ÿค', '๐Ÿšถ', '๐Ÿ’ฐ', '๐Ÿ“', '๐Ÿ’ข', '๐ŸคŸ', '๐Ÿ™', '๐Ÿšจ', '๐Ÿ’จ', '๐Ÿคฌ', 'โœˆ', '๐ŸŽ€', '๐Ÿบ', '๐Ÿค“', '๐Ÿ˜™', '๐Ÿ’Ÿ', '๐ŸŒฑ', '๐Ÿ˜–', '๐Ÿ‘ถ', '๐Ÿฅด', 'โ–ถ', 'โžก', 'โ“', '๐Ÿ’Ž', '๐Ÿ’ธ', 'โฌ‡', '๐Ÿ˜จ', '๐ŸŒš', '๐Ÿฆ‹', '๐Ÿ˜ท', '๐Ÿ•บ', 'โš ', '๐Ÿ™…', '๐Ÿ˜Ÿ', '๐Ÿ˜ต', '๐Ÿ‘Ž', '๐Ÿคฒ', '๐Ÿค ', '๐Ÿคง', '๐Ÿ“Œ', '๐Ÿ”ต', '๐Ÿ’…', '๐Ÿง', '๐Ÿพ', '๐Ÿ’', '๐Ÿ˜—', '๐Ÿค‘', '๐ŸŒŠ', '๐Ÿคฏ', '๐Ÿท', 'โ˜Ž', '๐Ÿ’ง', '๐Ÿ˜ฏ', '๐Ÿ’†', '๐Ÿ‘†', '๐ŸŽค', '๐Ÿ™‡', '๐Ÿ‘', 'โ„', '๐ŸŒด', '๐Ÿ’ฃ', '๐Ÿธ', '๐Ÿ’Œ', '๐Ÿ“', '๐Ÿฅ€', '๐Ÿคข', '๐Ÿ‘…', '๐Ÿ’ก', '๐Ÿ’ฉ', '๐Ÿ‘', '๐Ÿ“ธ', '๐Ÿ‘ป', '๐Ÿค', '๐Ÿคฎ', '๐ŸŽผ', '๐Ÿฅต', '๐Ÿšฉ', '๐ŸŽ', '๐ŸŠ', '๐Ÿ‘ผ', '๐Ÿ’', '๐Ÿ“ฃ', '๐Ÿฅ‚', } var base256emojiReverseTable map[rune]byte func init() { base256emojiReverseTable = make(map[rune]byte, len(base256emojiTable)) for i, v := range base256emojiTable { base256emojiReverseTable[v] = byte(i) } } func base256emojiEncode(in []byte) string { var l int for _, v := range in { l += utf8.RuneLen(base256emojiTable[v]) } var out strings.Builder out.Grow(l) for _, v := range in { out.WriteRune(base256emojiTable[v]) } return out.String() } type base256emojiCorruptInputError struct { index int char rune } func (e base256emojiCorruptInputError) Error() string { return "illegal base256emoji data at input byte " + strconv.FormatInt(int64(e.index), 10) + ", char: '" + string(e.char) + "'" } func (e base256emojiCorruptInputError) String() string { return e.Error() } func base256emojiDecode(in string) ([]byte, error) { out := make([]byte, utf8.RuneCountInString(in)) var stri int for i := 0; len(in) > 0; i++ { r, n := utf8.DecodeRuneInString(in) in = in[n:] var ok bool out[i], ok = base256emojiReverseTable[r] if !ok { return nil, base256emojiCorruptInputError{stri, r} } stri += n } return out, nil } go-multibase-0.2.0/base256emoji_test.go000066400000000000000000000010031440424633600176620ustar00rootroot00000000000000package multibase import "testing" func TestBase256EmojiAlphabet(t *testing.T) { var c uint for _, v := range base256emojiTable { if v != rune(0) { c++ } } if c != 256 { t.Errorf("Base256Emoji count is wrong, expected 256, got %d.", c) } } func TestBase256EmojiUniq(t *testing.T) { m := make(map[rune]struct{}, len(base256emojiTable)) for i, v := range base256emojiTable { _, ok := m[v] if ok { t.Errorf("Base256Emoji duplicate %s at index %d.", string(v), i) } m[v] = struct{}{} } } go-multibase-0.2.0/base32.go000066400000000000000000000012371440424633600155200ustar00rootroot00000000000000package multibase import ( b32 "github.com/multiformats/go-base32" ) var base32StdLowerPad = b32.NewEncodingCI("abcdefghijklmnopqrstuvwxyz234567") var base32StdLowerNoPad = base32StdLowerPad.WithPadding(b32.NoPadding) var base32StdUpperPad = b32.NewEncodingCI("ABCDEFGHIJKLMNOPQRSTUVWXYZ234567") var base32StdUpperNoPad = base32StdUpperPad.WithPadding(b32.NoPadding) var base32HexLowerPad = b32.NewEncodingCI("0123456789abcdefghijklmnopqrstuv") var base32HexLowerNoPad = base32HexLowerPad.WithPadding(b32.NoPadding) var base32HexUpperPad = b32.NewEncodingCI("0123456789ABCDEFGHIJKLMNOPQRSTUV") var base32HexUpperNoPad = base32HexUpperPad.WithPadding(b32.NoPadding) go-multibase-0.2.0/encoder.go000066400000000000000000000030421440424633600160540ustar00rootroot00000000000000package multibase import ( "fmt" "unicode/utf8" ) // Encoder is a multibase encoding that is verified to be supported and // supports an Encode method that does not return an error type Encoder struct { enc Encoding } // NewEncoder create a new Encoder from an Encoding func NewEncoder(base Encoding) (Encoder, error) { _, ok := EncodingToStr[base] if !ok { return Encoder{-1}, fmt.Errorf("unsupported multibase encoding: %d", base) } return Encoder{base}, nil } // MustNewEncoder is like NewEncoder but will panic if the encoding is // invalid. func MustNewEncoder(base Encoding) Encoder { _, ok := EncodingToStr[base] if !ok { panic("Unsupported multibase encoding") } return Encoder{base} } // EncoderByName creates an encoder from a string, the string can // either be the multibase name or single character multibase prefix func EncoderByName(str string) (Encoder, error) { var base Encoding var ok bool if len(str) == 0 { return Encoder{-1}, fmt.Errorf("empty multibase encoding") } else if utf8.RuneCountInString(str) == 1 { r, _ := utf8.DecodeRuneInString(str) base = Encoding(r) _, ok = EncodingToStr[base] } else { base, ok = Encodings[str] } if !ok { return Encoder{-1}, fmt.Errorf("unsupported multibase encoding: %s", str) } return Encoder{base}, nil } func (p Encoder) Encoding() Encoding { return p.enc } // Encode encodes the multibase using the given Encoder. func (p Encoder) Encode(data []byte) string { str, err := Encode(p.enc, data) if err != nil { // should not happen panic(err) } return str } go-multibase-0.2.0/encoder_test.go000066400000000000000000000022031440424633600171110ustar00rootroot00000000000000package multibase import ( "testing" "unicode/utf8" ) func TestInvalidCode(t *testing.T) { _, err := NewEncoder('q') if err == nil { t.Error("expected failure") } } func TestInvalidName(t *testing.T) { values := []string{"invalid", "", "q"} for _, val := range values { _, err := EncoderByName(val) if err == nil { t.Errorf("EncoderByName(%v) expected failure", val) } } } func TestEncoder(t *testing.T) { for name, code := range Encodings { encoder, err := NewEncoder(code) if err != nil { t.Fatal(err) } // Make sure the MustNewEncoder doesn't panic MustNewEncoder(code) str, err := Encode(code, sampleBytes) if err != nil { t.Fatal(err) } str2 := encoder.Encode(sampleBytes) if str != str2 { t.Errorf("encoded string mismatch: %s != %s", str, str2) } _, err = EncoderByName(name) if err != nil { t.Fatalf("EncoderByName(%s) failed: %v", name, err) } // Test that an encoder can be created from the single letter // prefix r, _ := utf8.DecodeRuneInString(str) _, err = EncoderByName(string(r)) if err != nil { t.Fatalf("EncoderByName(%s) failed: %v", string(r), err) } } } go-multibase-0.2.0/go.mod000066400000000000000000000002701440424633600152140ustar00rootroot00000000000000module github.com/multiformats/go-multibase go 1.19 require ( github.com/mr-tron/base58 v1.1.0 github.com/multiformats/go-base32 v0.0.3 github.com/multiformats/go-base36 v0.1.0 ) go-multibase-0.2.0/go.sum000066400000000000000000000010331440424633600152370ustar00rootroot00000000000000github.com/mr-tron/base58 v1.1.0 h1:Y51FGVJ91WBqCEabAi5OPUz38eAx8DakuAm5svLcsfQ= github.com/mr-tron/base58 v1.1.0/go.mod h1:xcD2VGqlgYjBdcBLw+TuYLr8afG+Hj8g2eTVqeSzSU8= github.com/multiformats/go-base32 v0.0.3 h1:tw5+NhuwaOjJCC5Pp82QuXbrmLzWg7uxlMFp8Nq/kkI= github.com/multiformats/go-base32 v0.0.3/go.mod h1:pLiuGC8y0QR3Ue4Zug5UzK9LjgbkL8NSQj0zQ5Nz/AA= github.com/multiformats/go-base36 v0.1.0 h1:JR6TyF7JjGd3m6FbLU2cOxhC0Li8z8dLNGQ89tUg4F4= github.com/multiformats/go-base36 v0.1.0/go.mod h1:kFGE83c6s80PklsHO9sRn2NCoffoRdUUOENyW/Vv6sM= go-multibase-0.2.0/multibase-conv/000077500000000000000000000000001440424633600170375ustar00rootroot00000000000000go-multibase-0.2.0/multibase-conv/main.go000066400000000000000000000014301440424633600203100ustar00rootroot00000000000000package main import ( "fmt" "os" multibase "github.com/multiformats/go-multibase" ) func main() { if len(os.Args) < 3 { fmt.Printf("usage: %s ...\n", os.Args[0]) os.Exit(1) } var newBase multibase.Encoding if baseParam := os.Args[1]; len(baseParam) != 0 { newBase = multibase.Encoding(baseParam[0]) } else { fmt.Fprintln(os.Stderr, " is empty") os.Exit(1) } input := os.Args[2:] for _, strmbase := range input { _, data, err := multibase.Decode(strmbase) if err != nil { fmt.Fprintf(os.Stderr, "error while decoding: %s\n", err) os.Exit(1) } newCid, err := multibase.Encode(newBase, data) if err != nil { fmt.Fprintf(os.Stderr, "error while encoding: %s\n", err) os.Exit(1) } fmt.Println(newCid) } } go-multibase-0.2.0/multibase.go000066400000000000000000000141751440424633600164330ustar00rootroot00000000000000package multibase import ( "encoding/base64" "encoding/hex" "fmt" "unicode/utf8" b58 "github.com/mr-tron/base58/base58" b32 "github.com/multiformats/go-base32" b36 "github.com/multiformats/go-base36" ) // Encoding identifies the type of base-encoding that a multibase is carrying. type Encoding int // These are the encodings specified in the standard, not are all // supported yet const ( Identity = 0x00 Base2 = '0' Base8 = '7' Base10 = '9' Base16 = 'f' Base16Upper = 'F' Base32 = 'b' Base32Upper = 'B' Base32pad = 'c' Base32padUpper = 'C' Base32hex = 'v' Base32hexUpper = 'V' Base32hexPad = 't' Base32hexPadUpper = 'T' Base36 = 'k' Base36Upper = 'K' Base58BTC = 'z' Base58Flickr = 'Z' Base64 = 'm' Base64url = 'u' Base64pad = 'M' Base64urlPad = 'U' Base256Emoji = '๐Ÿš€' ) // EncodingToStr is a map of the supported encoding, unsupported encoding // specified in standard are left out var EncodingToStr = map[Encoding]string{ 0x00: "identity", '0': "base2", 'f': "base16", 'F': "base16upper", 'b': "base32", 'B': "base32upper", 'c': "base32pad", 'C': "base32padupper", 'v': "base32hex", 'V': "base32hexupper", 't': "base32hexpad", 'T': "base32hexpadupper", 'k': "base36", 'K': "base36upper", 'z': "base58btc", 'Z': "base58flickr", 'm': "base64", 'u': "base64url", 'M': "base64pad", 'U': "base64urlpad", Base256Emoji: "base256emoji", } var Encodings = map[string]Encoding{} func init() { for e, n := range EncodingToStr { Encodings[n] = e } } // ErrUnsupportedEncoding is returned when the selected encoding is not known or // implemented. var ErrUnsupportedEncoding = fmt.Errorf("selected encoding not supported") // Encode encodes a given byte slice with the selected encoding and returns a // multibase string (). It will return // an error if the selected base is not known. func Encode(base Encoding, data []byte) (string, error) { switch base { case Identity: // 0x00 inside a string is OK in golang and causes no problems with the length calculation. return string(rune(Identity)) + string(data), nil case Base2: return string(Base2) + binaryEncodeToString(data), nil case Base16: return string(Base16) + hex.EncodeToString(data), nil case Base16Upper: return string(Base16Upper) + hexEncodeToStringUpper(data), nil case Base32: return string(Base32) + base32StdLowerNoPad.EncodeToString(data), nil case Base32Upper: return string(Base32Upper) + base32StdUpperNoPad.EncodeToString(data), nil case Base32hex: return string(Base32hex) + base32HexLowerNoPad.EncodeToString(data), nil case Base32hexUpper: return string(Base32hexUpper) + base32HexUpperNoPad.EncodeToString(data), nil case Base32pad: return string(Base32pad) + base32StdLowerPad.EncodeToString(data), nil case Base32padUpper: return string(Base32padUpper) + base32StdUpperPad.EncodeToString(data), nil case Base32hexPad: return string(Base32hexPad) + base32HexLowerPad.EncodeToString(data), nil case Base32hexPadUpper: return string(Base32hexPadUpper) + base32HexUpperPad.EncodeToString(data), nil case Base36: return string(Base36) + b36.EncodeToStringLc(data), nil case Base36Upper: return string(Base36Upper) + b36.EncodeToStringUc(data), nil case Base58BTC: return string(Base58BTC) + b58.EncodeAlphabet(data, b58.BTCAlphabet), nil case Base58Flickr: return string(Base58Flickr) + b58.EncodeAlphabet(data, b58.FlickrAlphabet), nil case Base64pad: return string(Base64pad) + base64.StdEncoding.EncodeToString(data), nil case Base64urlPad: return string(Base64urlPad) + base64.URLEncoding.EncodeToString(data), nil case Base64url: return string(Base64url) + base64.RawURLEncoding.EncodeToString(data), nil case Base64: return string(Base64) + base64.RawStdEncoding.EncodeToString(data), nil case Base256Emoji: return string(Base256Emoji) + base256emojiEncode(data), nil default: return "", ErrUnsupportedEncoding } } // Decode takes a multibase string and decodes into a bytes buffer. // It will return an error if the selected base is not known. func Decode(data string) (Encoding, []byte, error) { if len(data) == 0 { return 0, nil, fmt.Errorf("cannot decode multibase for zero length string") } r, _ := utf8.DecodeRuneInString(data) enc := Encoding(r) switch enc { case Identity: return Identity, []byte(data[1:]), nil case Base2: bytes, err := decodeBinaryString(data[1:]) return enc, bytes, err case Base16, Base16Upper: bytes, err := hex.DecodeString(data[1:]) return enc, bytes, err case Base32, Base32Upper: bytes, err := b32.RawStdEncoding.DecodeString(data[1:]) return enc, bytes, err case Base32hex, Base32hexUpper: bytes, err := b32.RawHexEncoding.DecodeString(data[1:]) return enc, bytes, err case Base32pad, Base32padUpper: bytes, err := b32.StdEncoding.DecodeString(data[1:]) return enc, bytes, err case Base32hexPad, Base32hexPadUpper: bytes, err := b32.HexEncoding.DecodeString(data[1:]) return enc, bytes, err case Base36, Base36Upper: bytes, err := b36.DecodeString(data[1:]) return enc, bytes, err case Base58BTC: bytes, err := b58.DecodeAlphabet(data[1:], b58.BTCAlphabet) return Base58BTC, bytes, err case Base58Flickr: bytes, err := b58.DecodeAlphabet(data[1:], b58.FlickrAlphabet) return Base58Flickr, bytes, err case Base64pad: bytes, err := base64.StdEncoding.DecodeString(data[1:]) return Base64pad, bytes, err case Base64urlPad: bytes, err := base64.URLEncoding.DecodeString(data[1:]) return Base64urlPad, bytes, err case Base64: bytes, err := base64.RawStdEncoding.DecodeString(data[1:]) return Base64, bytes, err case Base64url: bytes, err := base64.RawURLEncoding.DecodeString(data[1:]) return Base64url, bytes, err case Base256Emoji: bytes, err := base256emojiDecode(data[4:]) return Base256Emoji, bytes, err default: return -1, nil, ErrUnsupportedEncoding } } go-multibase-0.2.0/multibase_test.go000066400000000000000000000176251440424633600174750ustar00rootroot00000000000000package multibase import ( "bytes" "crypto/rand" "sort" "testing" ) func TestMap(t *testing.T) { for s, e := range Encodings { s2 := EncodingToStr[e] if s != s2 { t.Errorf("round trip failed on encoding map: %s != %s", s, s2) } } for e, s := range EncodingToStr { e2 := Encodings[s] if e != e2 { t.Errorf("round trip failed on encoding map: '%c' != '%c'", e, e2) } } } var sampleBytes = []byte("Decentralize everything!!!") var encodedSamples = map[Encoding]string{ Identity: string(rune(0x00)) + "Decentralize everything!!!", Base2: "00100010001100101011000110110010101101110011101000111001001100001011011000110100101111010011001010010000001100101011101100110010101110010011110010111010001101000011010010110111001100111001000010010000100100001", Base16: "f446563656e7472616c697a652065766572797468696e67212121", Base16Upper: "F446563656E7472616C697A652065766572797468696E67212121", Base32: "birswgzloorzgc3djpjssazlwmvzhs5dinfxgoijbee", Base32Upper: "BIRSWGZLOORZGC3DJPJSSAZLWMVZHS5DINFXGOIJBEE", Base32pad: "cirswgzloorzgc3djpjssazlwmvzhs5dinfxgoijbee======", Base32padUpper: "CIRSWGZLOORZGC3DJPJSSAZLWMVZHS5DINFXGOIJBEE======", Base32hex: "v8him6pbeehp62r39f9ii0pbmclp7it38d5n6e89144", Base32hexUpper: "V8HIM6PBEEHP62R39F9II0PBMCLP7IT38D5N6E89144", Base32hexPad: "t8him6pbeehp62r39f9ii0pbmclp7it38d5n6e89144======", Base32hexPadUpper: "T8HIM6PBEEHP62R39F9II0PBMCLP7IT38D5N6E89144======", Base36: "km552ng4dabi4neu1oo8l4i5mndwmpc3mkukwtxy9", Base36Upper: "KM552NG4DABI4NEU1OO8L4I5MNDWMPC3MKUKWTXY9", Base58BTC: "z36UQrhJq9fNDS7DiAHM9YXqDHMPfr4EMArvt", Base58Flickr: "Z36tpRGiQ9Endr7dHahm9xwQdhmoER4emaRVT", Base64: "mRGVjZW50cmFsaXplIGV2ZXJ5dGhpbmchISE", Base64url: "uRGVjZW50cmFsaXplIGV2ZXJ5dGhpbmchISE", Base64pad: "MRGVjZW50cmFsaXplIGV2ZXJ5dGhpbmchISE=", Base64urlPad: "URGVjZW50cmFsaXplIGV2ZXJ5dGhpbmchISE=", Base256Emoji: "๐Ÿš€๐Ÿ’›โœ‹๐Ÿ’ƒโœ‹๐Ÿ˜ป๐Ÿ˜ˆ๐Ÿฅบ๐Ÿคค๐Ÿ€๐ŸŒŸ๐Ÿ’โœ‹๐Ÿ˜…โœ‹๐Ÿ’ฆโœ‹๐Ÿฅบ๐Ÿƒ๐Ÿ˜ˆ๐Ÿ˜ด๐ŸŒŸ๐Ÿ˜ป๐Ÿ˜๐Ÿ‘๐Ÿ‘๐Ÿ‘", } func testEncode(t *testing.T, encoding Encoding, bytes []byte, expected string) { actual, err := Encode(encoding, bytes) if err != nil { t.Error(err) return } if actual != expected { t.Errorf("encoding failed for %c (%d / %s), expected: %s, got: %s", encoding, encoding, EncodingToStr[encoding], expected, actual) } } func testDecode(t *testing.T, expectedEncoding Encoding, expectedBytes []byte, data string) { actualEncoding, actualBytes, err := Decode(data) if err != nil { t.Error(err) return } if actualEncoding != expectedEncoding { t.Errorf("wrong encoding code, expected: %c (%d), got %c (%d)", expectedEncoding, expectedEncoding, actualEncoding, actualEncoding) } if !bytes.Equal(actualBytes, expectedBytes) { t.Errorf("decoding failed for %c (%d), expected: %v, got %v", actualEncoding, actualEncoding, expectedBytes, actualBytes) } } func TestEncode(t *testing.T) { for encoding := range EncodingToStr { testEncode(t, encoding, sampleBytes, encodedSamples[encoding]) } } func TestDecode(t *testing.T) { for encoding := range EncodingToStr { testDecode(t, encoding, sampleBytes, encodedSamples[encoding]) } } func TestRoundTrip(t *testing.T) { for base := range EncodingToStr { if int(base) == 0 { // skip identity: any byte goes there continue } _, _, err := Decode(string(rune(base)) + "\u00A0") if err == nil { t.Fatal(EncodingToStr[base] + " decode should fail on low-unicode") } _, _, err = Decode(string(rune(base)) + "\u1F4A8") if err == nil { t.Fatal(EncodingToStr[base] + " decode should fail on emoji") } _, _, err = Decode(string(rune(base)) + "!") if err == nil { t.Fatal(EncodingToStr[base] + " decode should fail on punctuation") } _, _, err = Decode(string(rune(base)) + "\xA0") if err == nil { t.Fatal(EncodingToStr[base] + " decode should fail on high-latin1") } } buf := make([]byte, 137+16) // sufficiently large prime number of bytes + another 16 to test leading 0s rand.Read(buf[16:]) for base := range EncodingToStr { // 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, err := Encode(base, newBuf) if err != nil { t.Fatal(err) } e, out, err := Decode(enc) if err != nil { t.Fatal(err) } if e != base { t.Fatal("got wrong encoding out") } 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 wasnt the same as output", buf[i:], out) } // When we have 3 leading zeroes, do a few extra tests // ( choice of leading zeroes is arbitrary - just cutting down on test permutations ) if i == 13 { // if this is a case-insensitive codec semi-randomly swap case in enc and try again name := EncodingToStr[base] if name[len(name)-5:] == "upper" || Encodings[name+"upper"] > 0 { caseTamperedEnc := []byte(enc) for _, j := range []int{3, 5, 8, 13, 21, 23, 29, 47, 52} { if caseTamperedEnc[j] >= 65 && caseTamperedEnc[j] <= 90 { caseTamperedEnc[j] += 32 } else if caseTamperedEnc[j] >= 97 && caseTamperedEnc[j] <= 122 { caseTamperedEnc[j] -= 32 } } e, out, err := Decode(string(caseTamperedEnc)) if err != nil { t.Fatal(err) } if e != base { t.Fatal("got wrong encoding out") } if !bytes.Equal(buf[i:], out) { t.Fatal("input wasn't the same as output", buf[i:], out) } } } } } // Test that nothing overflows maxValueBuf := make([]byte, 131) for i := 0; i < len(maxValueBuf); i++ { maxValueBuf[i] = 0xFF } for base := range EncodingToStr { // test roundtrip from the complete buffer down to a single byte for i := 0; i < len(maxValueBuf); i++ { enc, err := Encode(base, maxValueBuf[i:]) if err != nil { t.Fatal(err) } e, out, err := Decode(enc) if err != nil { t.Fatal(err) } if e != base { t.Fatal("got wrong encoding out") } if !bytes.Equal(maxValueBuf[i:], out) { t.Fatal("input wasn't the same as output", maxValueBuf[i:], out) } } } _, _, err := Decode("") if err == nil { t.Fatal("shouldn't be able to decode empty string") } } var benchmarkBuf [36]byte // typical CID size var benchmarkCodecs []string func init() { rand.Read(benchmarkBuf[:]) benchmarkCodecs = make([]string, 0, len(Encodings)) for n := range Encodings { // // Only bench b36 and b58 // if len(n) < 6 || (n[4:6] != "36" && n[4:6] != "58") { // continue // } benchmarkCodecs = append(benchmarkCodecs, n) } sort.Strings(benchmarkCodecs) } func BenchmarkRoundTrip(b *testing.B) { b.ResetTimer() for _, name := range benchmarkCodecs { b.Run(name, func(b *testing.B) { base := Encodings[name] for i := 0; i < b.N; i++ { enc, err := Encode(base, benchmarkBuf[:]) if err != nil { b.Fatal(err) } e, out, err := Decode(enc) if err != nil { b.Fatal(err) } if e != base { b.Fatal("got wrong encoding out") } if !bytes.Equal(benchmarkBuf[:], out) { b.Fatal("input wasnt the same as output", benchmarkBuf, out) } } }) } } func BenchmarkEncode(b *testing.B) { b.ResetTimer() for _, name := range benchmarkCodecs { b.Run(name, func(b *testing.B) { base := Encodings[name] for i := 0; i < b.N; i++ { _, err := Encode(base, benchmarkBuf[:]) if err != nil { b.Fatal(err) } } }) } } func BenchmarkDecode(b *testing.B) { b.ResetTimer() for _, name := range benchmarkCodecs { b.Run(name, func(b *testing.B) { enc, _ := Encode(Encodings[name], benchmarkBuf[:]) for i := 0; i < b.N; i++ { _, _, err := Decode(enc) if err != nil { b.Fatal(err) } } }) } } go-multibase-0.2.0/package.json000066400000000000000000000003001440424633600163660ustar00rootroot00000000000000{ "author": "whyrusleeping", "bugs": { "url": "https://github.com/multiformats/go-multibase" }, "language": "go", "license": "", "name": "go-multibase", "version": "0.3.0" } go-multibase-0.2.0/spec/000077500000000000000000000000001440424633600150415ustar00rootroot00000000000000go-multibase-0.2.0/spec_test.go000066400000000000000000000076271440424633600164430ustar00rootroot00000000000000package multibase import ( "encoding/csv" "os" "path/filepath" "strconv" "strings" "testing" "unicode/utf8" ) func TestSpec(t *testing.T) { file, err := os.Open("spec/multibase.csv") if err != nil { t.Fatal(err) } defer file.Close() reader := csv.NewReader(file) reader.LazyQuotes = false reader.FieldsPerRecord = 4 reader.TrimLeadingSpace = true values, err := reader.ReadAll() if err != nil { t.Error(err) } expectedEncodings := make(map[Encoding]string, len(values)-1) for _, v := range values[1:] { encoding := v[0] codeStr := v[1] var code Encoding if strings.HasPrefix(codeStr, "0x") { i, err := strconv.ParseUint(codeStr[2:], 16, 64) if err != nil { t.Errorf("invalid multibase byte %q", codeStr) continue } code = Encoding(i) } else { codeRune, length := utf8.DecodeRuneInString(codeStr) if code == utf8.RuneError { t.Errorf("multibase %q wasn't valid utf8", codeStr) continue } if length != len(codeStr) { t.Errorf("multibase %q wasn't a single character", codeStr) continue } code = Encoding(codeRune) } expectedEncodings[code] = encoding } for name, enc := range Encodings { expectedName, ok := expectedEncodings[enc] if !ok { t.Errorf("encoding %q (%c) not defined in the spec", name, enc) continue } if expectedName != name { t.Errorf("encoding %q (%c) has unexpected name %q", expectedName, enc, name) } } } func TestSpecVectors(t *testing.T) { files, err := filepath.Glob("spec/tests/*.csv") if err != nil { t.Fatal(err) } for _, fname := range files { t.Run(fname, func(t *testing.T) { file, err := os.Open(fname) if err != nil { t.Error(err) return } defer file.Close() reader := csv.NewReader(file) reader.LazyQuotes = false reader.FieldsPerRecord = 2 reader.TrimLeadingSpace = true values, err := reader.ReadAll() if err != nil { t.Error(err) } if len(values) == 0 { t.Error("no test values") return } header := values[0] var decodeOnly bool switch header[0] { case "encoding": case "non-canonical encoding": decodeOnly = true default: t.Errorf("invalid test spec %q", fname) return } testValue, err := strconv.Unquote("\"" + header[1] + "\"") if err != nil { t.Error("failed to unquote testcase:", err) return } for _, testCase := range values[1:] { encodingName := testCase[0] expected := testCase[1] t.Run(encodingName, func(t *testing.T) { encoder, err := EncoderByName(encodingName) if err != nil { t.Skipf("skipping %s: not supported", encodingName) return } if !decodeOnly { t.Logf("encoding %q with %s", testValue, encodingName) actual := encoder.Encode([]byte(testValue)) if expected != actual { t.Errorf("expected %q, got %q", expected, actual) } } t.Logf("decoding %q", expected) encoding, decoded, err := Decode(expected) if err != nil { t.Error("failed to decode:", err) return } expectedEncoding := Encodings[encodingName] if encoding != expectedEncoding { t.Errorf("expected encoding to be %c, got %c", expectedEncoding, encoding) } if string(decoded) != testValue { t.Errorf("failed to decode %q to %q, got %q", expected, testValue, string(decoded)) } }) } }) } } func FuzzDecode(f *testing.F) { files, err := filepath.Glob("spec/tests/*.csv") if err != nil { f.Fatal(err) } for _, fname := range files { func() { file, err := os.Open(fname) if err != nil { f.Fatal(err) } defer file.Close() reader := csv.NewReader(file) reader.LazyQuotes = false reader.FieldsPerRecord = 2 reader.TrimLeadingSpace = true values, err := reader.ReadAll() if err != nil { f.Fatal(err) } for _, tc := range values[1:] { f.Add(tc[1]) } }() } f.Fuzz(func(_ *testing.T, data string) { Decode(data) }) } go-multibase-0.2.0/version.json000066400000000000000000000000321440424633600164620ustar00rootroot00000000000000{ "version": "v0.2.0" }