From b5770a0f25ef797842fb3f49419b1deef19b299c Mon Sep 17 00:00:00 2001 From: Fatih Cetinkaya <965295+devfacet@users.noreply.github.com> Date: Thu, 14 Apr 2022 19:40:56 -0400 Subject: [PATCH] Initial commit --- .github/workflows/release.yaml | 66 +++++ .github/workflows/test.yaml | 46 ++++ .gitignore | 17 ++ CONTRIBUTING.md | 9 + LICENSE.txt | 21 ++ Makefile | 96 ++++++++ README.md | 51 ++++ byteman.go | 32 +++ byteman_test.go | 69 ++++++ byteorder.go | 37 +++ go.mod | 3 + numbers.go | 181 ++++++++++++++ numbers_test.go | 431 +++++++++++++++++++++++++++++++++ strings.go | 44 ++++ strings_test.go | 79 ++++++ 15 files changed, 1182 insertions(+) create mode 100644 .github/workflows/release.yaml create mode 100644 .github/workflows/test.yaml create mode 100644 .gitignore create mode 100644 CONTRIBUTING.md create mode 100644 LICENSE.txt create mode 100644 Makefile create mode 100644 README.md create mode 100644 byteman.go create mode 100644 byteman_test.go create mode 100644 byteorder.go create mode 100644 go.mod create mode 100644 numbers.go create mode 100644 numbers_test.go create mode 100644 strings.go create mode 100644 strings_test.go diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml new file mode 100644 index 0000000..236e732 --- /dev/null +++ b/.github/workflows/release.yaml @@ -0,0 +1,66 @@ +name: Release + +permissions: + contents: write + +on: + push: + tags: [ 'v*.*.*' ] + + workflow_dispatch: + inputs: + releaseTag: + description: 'Existing git tag to be released (i.e. v1.0.0)' + required: true + +jobs: + + test: + runs-on: ubuntu-latest + steps: + - name: Setup Go environment + uses: actions/setup-go@v2 + with: + go-version: '1.17' + + - name: Checkout code + uses: actions/checkout@v2 + with: + # https://github.com/actions/checkout/issues/100 + fetch-depth: 0 + + - name: Install test tools + run: | + make test-tools + + - name: Run Tests + run: | + make test + + release: + runs-on: ubuntu-latest + needs: test + outputs: + RELEASE_TAG: ${{ steps.env.outputs.RELEASE_TAG }} + steps: + - name: Setup ENV + id: env + run: | + if [[ "${GITHUB_EVENT_NAME}" == "workflow_dispatch" ]]; then + RELEASE_TAG=${{ github.event.inputs.releaseTag }} + elif [[ "${GITHUB_REF}" == refs/tags/v*.*.* ]]; then + RELEASE_TAG=${GITHUB_REF/refs\/tags\//} + fi + if [[ "${RELEASE_TAG}" != v*.*.* ]]; then + echo "invalid release tag (${RELEASE_TAG} - ${GITHUB_REF}), only semver is allowed (i.e v*.*.*)" + exit 1 + fi + echo "::set-output name=RELEASE_TAG::${RELEASE_TAG}" + + - name: Checkout code + uses: actions/checkout@v2 + + - name: Release + uses: softprops/action-gh-release@v0.1.14 + with: + tag_name: ${{ steps.env.outputs.RELEASE_TAG }} diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml new file mode 100644 index 0000000..a93a20d --- /dev/null +++ b/.github/workflows/test.yaml @@ -0,0 +1,46 @@ +name: Test + +on: + push: + branches: [ '*' ] + tags-ignore: [ '*' ] + pull_request: + branches: [ '*' ] + + workflow_dispatch: + inputs: + logLevel: + description: 'Log level' + default: 'info' + type: choice + options: + - debug + - error + - fatal + - info + - panic + - warning + +jobs: + + test: + runs-on: ubuntu-latest + steps: + - name: Setup Go environment + uses: actions/setup-go@v2 + with: + go-version: '1.17' + + - name: Checkout code + uses: actions/checkout@v2 + with: + # https://github.com/actions/checkout/issues/100 + fetch-depth: 0 + + - name: Install test tools + run: | + make test-tools + + - name: Run Tests + run: | + make test diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..b95c191 --- /dev/null +++ b/.gitignore @@ -0,0 +1,17 @@ +.DS_Store +.vscode +.vs +Thumbs.db + +*.exe +*.log +*.out +*.prof +*.so + +logs/ +node_modules/ +reports/ +releases/ +tmp/ +vendor/ diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..3bf8f3f --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,9 @@ +# Contributing + +- Code contributions must be through pull requests. +- Run tests, linting and formatting before make a pull request. +- Pull requests can not be merged without being reviewed. +- Use "Issues" for bug reports, feature requests, discussions and typos. +- Do not refactor existing code without a discussion. +- Do not add a new third party dependency without a discussion. +- Use semantic versioning and git tags for versioning. diff --git a/LICENSE.txt b/LICENSE.txt new file mode 100644 index 0000000..c9cba3a --- /dev/null +++ b/LICENSE.txt @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2022 Fatih Cetinkaya (https://github.com/devfacet/byteman) + +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. diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..98007ac --- /dev/null +++ b/Makefile @@ -0,0 +1,96 @@ +# Init vars. +MAKEFILE := $(lastword $(MAKEFILE_LIST)) +BASENAME := $(shell basename "$(PWD)") + +.PHONY: help +all: help +help: Makefile + @echo + @echo " Commands:" + @echo + @sed -n 's/^##//p' $< | sed -e 's/^/ /' | sort + @echo + +## test Run gofmt, golint, staticcheck, go vet and go test. +test: + $(eval FMT=$(shell find . -type f -name '*.go' | grep -v -E '^./vendor|^./third_party' | xargs -L1 dirname | uniq | xargs gofmt -l | wc -l | tr -d ' ')) + @if [ "$(FMT)" != "0" ]; then \ + echo "some files are not formatted, run 'make fmt'"; \ + exit 1; \ + fi + + $(eval LINT=$(shell find . -type f -name '*.go' | grep -v -E '^./vendor|^./third_party' | xargs -L1 dirname | uniq | xargs golint | wc -l | tr -d ' ')) + @if [ "$(LINT)" != "0" ]; then \ + echo "some files have linting errors, run 'make lint'"; \ + exit 1; \ + fi + + $(eval STATICCHECK=$(shell find . -type f -name '*.go' | grep -v -E '^./vendor|^./third_party' | xargs -L1 dirname | uniq | xargs staticcheck | wc -l | tr -d ' ')) + @if [ "$(STATICCHECK)" != "0" ]; then \ + echo "some files have staticcheck errors, run 'make staticcheck'"; \ + exit 1; \ + fi + + $(eval GOVET=$(shell find . -type f -name '*.go' | grep -v -E '^./vendor' | xargs -L1 dirname | uniq | xargs go vet 2>&1 | wc -l | tr -d ' ')) + @if [ "$(GOVET)" != "0" ]; then \ + echo "some files have vetting errors, run 'make vet'"; \ + exit 1; \ + fi + + @$(MAKE) -f $(MAKEFILE) test-go + +## test-go Run go test +test-go: + @find . -type f -name '*.go' | grep -v -E '^./vendor|^./third_party' | xargs -L1 dirname | uniq | xargs go test -v -race + +## test-benchmarks Run go benchmarks +test-benchmarks: + @find . -type f -name '*.go' | grep -v -E '^./vendor|^./third_party' | xargs -L1 dirname | uniq | xargs go test -benchmem -bench + +## test-ui Launch test UI +test-ui: + $(eval GOCONVEY_PATH=$(shell which goconvey)) + @if [ -z "$(GOCONVEY_PATH)" ]; then \ + GO111MODULE=off go get github.com/smartystreets/goconvey; \ + fi + goconvey -port 8088 -excludedDirs vendor,node_modules,assets + +## test-clean Clean test cache +test-clean: + @go clean -testcache + +## test-tools Install test tools +test-tools: + $(eval GOLINT_PATH=$(shell which golint)) + @if [ -z "$(GOLINT_PATH)" ]; then \ + GO111MODULE=off go get golang.org/x/lint/golint; \ + fi + $(eval STATICCHECK_PATH=$(shell which staticcheck)) + @if [ -z "$(STATICCHECK_PATH)" ]; then \ + GO111MODULE=off go get honnef.co/go/tools/cmd/staticcheck; \ + fi + +## fmt Run formating +fmt: + @find . -type f -name '*.go' | grep -v -E '^./vendor|^./third_party' | xargs -L1 dirname | uniq | xargs gofmt -l + +## lint Run linting +lint: + @find . -type f -name '*.go' | grep -v -E '^./vendor|^./third_party' | xargs -L1 dirname | uniq | xargs golint + +## staticcheck Run staticcheck +staticcheck: + @find . -type f -name '*.go' | grep -v -E '^./vendor|^./third_party' | xargs -L1 dirname | uniq | xargs staticcheck + +## vet Run vetting +vet: + @find . -type f -name '*.go' | grep -v -E '^./vendor' | xargs -L1 dirname | uniq | xargs go vet 2>&1 + +## release Release a version +release: + @if [ "$(shell echo \$${GIT_TAG:0:1})" != "v" ]; then \ + echo "invalid GIT_TAG (${GIT_TAG}). Try something like 'make release GIT_TAG=v1.0.0'"; \ + exit 1; \ + fi + git tag -a $(GIT_TAG) -m "$(GIT_TAG)" + git push --follow-tags diff --git a/README.md b/README.md new file mode 100644 index 0000000..0422cc0 --- /dev/null +++ b/README.md @@ -0,0 +1,51 @@ +# Byteman + +[![Godoc][doc-image]][doc-url] [![Release][release-image]][release-url] [![Build][build-image]][build-url] + +A Golang library that provides functions for bytes and bits. + +## Usage + +See [byteman_test.go](byteman_test.go), [numbers_test.go](numbers_test.go) and [strings_test.go](strings_test.go). + +## Test + +```shell +# Test everything: +make test + +# For BDD development: +# It will open a new browser window. Make sure: +# 1. There is no errors on the terminal window. +# 2. There is no other open GoConvey page. +make test-ui + +# Benchmarks +make test-benchmarks +``` + +## Release + +```shell +# Update and commit CHANGELOG.md first (i.e. git add CHANGELOG.md && git commit -m "v1.0.0"). +# Set GIT_TAG using semver (i.e. GIT_TAG=v1.0.0) +make release GIT_TAG= +``` + +## Contributing + +See [CONTRIBUTING.md](CONTRIBUTING.md) + +## License + +Licensed under The MIT License (MIT) +For the full copyright and license information, please view the LICENSE.txt file. + +[doc-url]: https://pkg.go.dev/github.com/devfacet/byteman +[doc-image]: https://pkg.go.dev/badge/github.com/devfacet/byteman + +[release-url]: https://github.com/devfacet/byteman/releases/latest +[release-image]: https://img.shields.io/github/release/devfacet/byteman.svg?style=flat-square + +[build-url]: https://github.com/devfacet/byteman/actions/workflows/test.yaml +[build-image]: https://github.com/devfacet/byteman/actions/workflows/test.yaml/badge.svg diff --git a/byteman.go b/byteman.go new file mode 100644 index 0000000..b09ecdb --- /dev/null +++ b/byteman.go @@ -0,0 +1,32 @@ +// Byteman +// For the full copyright and license information, please view the LICENSE.txt file. + +// Package byteman provides functions for bytes and bits. +package byteman + +// Combine combines the given byte slices. +func Combine(bs ...[]byte) []byte { + b := []byte{} + for _, v := range bs { + b = append(b, v...) + } + return b +} + +// Resize resizes the given byte slice. +func Resize(b []byte, size int) []byte { + if size == 0 { + size = len(b) + } else if size < 0 { + if ns := len(b) + size; ns > 0 { + size = ns + } else { + size = 0 + } + } + nb := make([]byte, size) + if b != nil { + copy(nb, b) + } + return nb +} diff --git a/byteman_test.go b/byteman_test.go new file mode 100644 index 0000000..d63ddee --- /dev/null +++ b/byteman_test.go @@ -0,0 +1,69 @@ +// Byteman +// For the full copyright and license information, please view the LICENSE.txt file. + +package byteman_test + +import ( + "bytes" + "testing" + + "github.com/devfacet/byteman" +) + +func TestCombine(t *testing.T) { + table := []struct { + arg0 []byte + arg1 []byte + out []byte + }{ + {[]byte("foo"), []byte("bar"), []byte("foobar")}, + {[]byte("foo"), []byte(""), []byte("foo")}, + {[]byte(""), []byte("bar"), []byte("bar")}, + {[]byte(""), []byte(""), []byte("")}, + {[]byte(""), []byte(""), []byte{}}, + {[]byte{}, []byte{}, []byte{}}, + {[]byte{}, []byte{}, []byte("")}, + } + for _, v := range table { + b := byteman.Combine(v.arg0, v.arg1) + if !bytes.Equal(b, v.out) { + t.Errorf("got %v, want %v", b, v.out) + } + } +} + +func BenchmarkCombine(b *testing.B) { + for i := 0; i < b.N; i++ { + byteman.Combine([]byte("a"), []byte("b")) + } +} + +func TestResize(t *testing.T) { + table := []struct { + arg0 []byte + arg1 int + out []byte + }{ + {[]byte("foo"), 0, []byte{0x66, 0x6f, 0x6f}}, + {[]byte("foo"), 1, []byte{0x66}}, + {[]byte("foo"), 2, []byte{0x66, 0x6f}}, + {[]byte("foo"), 3, []byte{0x66, 0x6f, 0x6f}}, + {[]byte("foo"), -1, []byte{0x66, 0x6f}}, + {[]byte("foo"), -2, []byte{0x66}}, + {[]byte("foo"), -3, []byte{}}, + {[]byte("foo"), -4, []byte{}}, + {[]byte("foo"), 4, []byte{0x66, 0x6f, 0x6f, 0x00}}, + } + for _, v := range table { + b := byteman.Resize(v.arg0, v.arg1) + if !bytes.Equal(b, v.out) { + t.Errorf("got %v, want %v", b, v.out) + } + } +} + +func BenchmarkResize(b *testing.B) { + for i := 0; i < b.N; i++ { + byteman.Resize([]byte("a"), 1) + } +} diff --git a/byteorder.go b/byteorder.go new file mode 100644 index 0000000..e4e6075 --- /dev/null +++ b/byteorder.go @@ -0,0 +1,37 @@ +// Byteman +// For the full copyright and license information, please view the LICENSE.txt file. + +package byteman + +// ByteOrderType represents the byte order type. +type ByteOrderType uint8 + +const ( + // ByteOrderTypeLittleEndian represents the little-endian byte order type. + ByteOrderTypeLittleEndian ByteOrderType = 0 + // ByteOrderTypeBigEndian represents the big-endian byte order type. + ByteOrderTypeBigEndian ByteOrderType = 1 +) + +// ByteOrder provides interface for endianness. +type ByteOrder interface { + Type() ByteOrderType +} + +// LittleEndian represents the little-endian byte order. +type LittleEndian struct { +} + +// Type returns the byte order type. +func (bo *LittleEndian) Type() ByteOrderType { + return ByteOrderTypeLittleEndian +} + +// BigEndian represents the big-endian byte order. +type BigEndian struct { +} + +// Type returns the byte order type. +func (bo *BigEndian) Type() ByteOrderType { + return ByteOrderTypeBigEndian +} diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..0f1b12d --- /dev/null +++ b/go.mod @@ -0,0 +1,3 @@ +module github.com/devfacet/byteman + +go 1.17 diff --git a/numbers.go b/numbers.go new file mode 100644 index 0000000..5855730 --- /dev/null +++ b/numbers.go @@ -0,0 +1,181 @@ +// Byteman +// For the full copyright and license information, please view the LICENSE.txt file. + +package byteman + +import ( + "encoding/binary" +) + +const ( + // IntSize represents the supported integer value size. + IntSize = 32 << (^uint(0) >> 63) // 32 or 64 +) + +// FromUint returns a byte slice by the given Uint and byte order (endianness). +func FromUint(number interface{}, bo ByteOrder) []byte { + switch v := number.(type) { + case uint: + b := make([]byte, IntSize/8) + if bo.Type() == ByteOrderTypeBigEndian { + if IntSize == 64 { + binary.BigEndian.PutUint64(b, uint64(v)) + } else { + binary.BigEndian.PutUint32(b, uint32(v)) + } + } else { + if IntSize == 64 { + binary.LittleEndian.PutUint64(b, uint64(v)) + } else { + binary.LittleEndian.PutUint32(b, uint32(v)) + } + } + return b + case uint8: + b := make([]byte, 1) + b[0] = v + return b + case uint16: + b := make([]byte, 2) + if bo.Type() == ByteOrderTypeBigEndian { + binary.BigEndian.PutUint16(b, v) + } else { + binary.LittleEndian.PutUint16(b, v) + } + return b + case uint32: + b := make([]byte, 4) + if bo.Type() == ByteOrderTypeBigEndian { + binary.BigEndian.PutUint32(b, v) + } else { + binary.LittleEndian.PutUint32(b, v) + } + return b + case uint64: + b := make([]byte, 8) + if bo.Type() == ByteOrderTypeBigEndian { + binary.BigEndian.PutUint64(b, v) + } else { + binary.LittleEndian.PutUint64(b, v) + } + return b + default: + return nil + } +} + +// FromInt returns a byte slice by the given int and byte order (endianness). +func FromInt(number interface{}, bo ByteOrder) []byte { + switch v := number.(type) { + case int: + return FromUint(uint(v), bo) + case int8: + return FromUint(uint8(v), bo) + case int16: + return FromUint(uint16(v), bo) + case int32: + return FromUint(uint32(v), bo) + case int64: + return FromUint(uint64(v), bo) + default: + return nil + } +} + +// Uint returns an uint value by the given byte slice and byte order (endianness). +func Uint(b []byte, bo ByteOrder) uint { + if len(b) == 8 && IntSize == 64 { + if bo.Type() == ByteOrderTypeBigEndian { + return uint(binary.BigEndian.Uint64(b)) + } + return uint(binary.LittleEndian.Uint64(b)) + } else if len(b) >= 4 { + if bo.Type() == ByteOrderTypeBigEndian { + return uint(binary.BigEndian.Uint32(b)) + } + return uint(binary.LittleEndian.Uint32(b)) + } + return 0 +} + +// Uint16 returns an uint16 value by the given byte slice and byte order (endianness). +func Uint16(b []byte, bo ByteOrder) uint16 { + if len(b) == 2 { + if bo.Type() == ByteOrderTypeBigEndian { + return binary.BigEndian.Uint16(b) + } + return binary.LittleEndian.Uint16(b) + } + return 0 +} + +// Uint32 returns an uint32 value by the given byte slice and byte order (endianness). +func Uint32(b []byte, bo ByteOrder) uint32 { + if len(b) == 4 { + if bo.Type() == ByteOrderTypeBigEndian { + return binary.BigEndian.Uint32(b) + } + return binary.LittleEndian.Uint32(b) + } + return 0 +} + +// Uint64 returns an uint64 value by the given byte slice and byte order (endianness). +func Uint64(b []byte, bo ByteOrder) uint64 { + if len(b) == 8 { + if bo.Type() == ByteOrderTypeBigEndian { + return binary.BigEndian.Uint64(b) + } + return binary.LittleEndian.Uint64(b) + } + return 0 +} + +// Int returns an int value by the given byte slice and byte order (endianness). +func Int(b []byte, bo ByteOrder) int { + if len(b) == 8 && IntSize == 64 { + if bo.Type() == ByteOrderTypeBigEndian { + return int(binary.BigEndian.Uint64(b)) + } + return int(binary.LittleEndian.Uint64(b)) + } else if len(b) >= 4 { + if bo.Type() == ByteOrderTypeBigEndian { + return int(binary.BigEndian.Uint32(b)) + } + return int(binary.LittleEndian.Uint32(b)) + } + return 0 +} + +// Int16 returns an int16 value by the given byte slice and byte order (endianness). +func Int16(b []byte, bo ByteOrder) int16 { + if len(b) == 2 { + if bo.Type() == ByteOrderTypeBigEndian { + return int16(binary.BigEndian.Uint16(b)) + } + return int16(binary.LittleEndian.Uint16(b)) + } + return 0 +} + +// Int32 returns an int32 value by the given byte slice and byte order (endianness). +func Int32(b []byte, bo ByteOrder) int32 { + if len(b) == 4 { + if bo.Type() == ByteOrderTypeBigEndian { + return int32(binary.BigEndian.Uint32(b)) + } + return int32(binary.LittleEndian.Uint32(b)) + } + return 0 +} + +// Int64 returns an int64 value by the given byte slice and byte order (endianness). +func Int64(b []byte, bo ByteOrder) int64 { + if len(b) == 8 { + if bo.Type() == ByteOrderTypeBigEndian { + return int64(binary.BigEndian.Uint64(b)) + } + return int64(binary.LittleEndian.Uint64(b)) + } + return 0 +} diff --git a/numbers_test.go b/numbers_test.go new file mode 100644 index 0000000..ef1e5a1 --- /dev/null +++ b/numbers_test.go @@ -0,0 +1,431 @@ +// Byteman +// For the full copyright and license information, please view the LICENSE.txt file. + +package byteman_test + +import ( + "bytes" + "math" + "testing" + + "github.com/devfacet/byteman" +) + +func TestFromUint(t *testing.T) { + table := []struct { + arg0 interface{} + arg1 byteman.ByteOrder + out []byte + }{ + {uint(math.MaxInt), &byteman.BigEndian{}, []byte{}}, + {uint(math.MaxInt), &byteman.LittleEndian{}, []byte{}}, + {uint8(0), &byteman.BigEndian{}, []byte{0x0}}, + {uint16(12345), &byteman.BigEndian{}, []byte{0x30, 0x39}}, + {uint16(54321), &byteman.BigEndian{}, []byte{0xD4, 0x31}}, + {uint8(math.MaxUint8), &byteman.BigEndian{}, []byte{0xff}}, + {uint16(math.MaxUint16), &byteman.BigEndian{}, []byte{0xff, 0xff}}, + {uint32(math.MaxUint32), &byteman.BigEndian{}, []byte{0xff, 0xff, 0xff, 0xff}}, + {uint64(math.MaxUint64), &byteman.BigEndian{}, []byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}}, + {uint8(0), &byteman.LittleEndian{}, []byte{0x0}}, + {uint16(12345), &byteman.LittleEndian{}, []byte{0x39, 0x30}}, + {uint16(54321), &byteman.LittleEndian{}, []byte{0x31, 0xD4}}, + {uint8(math.MaxUint8), &byteman.LittleEndian{}, []byte{0xff}}, + {uint16(math.MaxUint16), &byteman.LittleEndian{}, []byte{0xff, 0xff}}, + {uint32(math.MaxUint32), &byteman.LittleEndian{}, []byte{0xff, 0xff, 0xff, 0xff}}, + {uint64(math.MaxUint64), &byteman.LittleEndian{}, []byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}}, + } + if byteman.IntSize == 64 { + table[0].out = []byte{0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff} + table[1].out = []byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f} + } else { + table[0].out = []byte{0xff, 0xff, 0xff, 0xff} + table[1].out = []byte{0xff, 0xff, 0xff, 0xff} + } + for _, v := range table { + b := byteman.FromUint(v.arg0, v.arg1) + if !bytes.Equal(b, v.out) { + t.Errorf("got %v, want %v", b, v.out) + } + } + + if b := byteman.FromUint(float64(0), &byteman.BigEndian{}); b != nil { + t.Errorf("got %v, want nil", b) + } else if b := byteman.FromUint(float64(0), &byteman.LittleEndian{}); b != nil { + t.Errorf("got %v, want nil", b) + } +} + +func BenchmarkFromUintBigEndian(b *testing.B) { + for i := 0; i < b.N; i++ { + byteman.FromUint(uint64(math.MaxUint64), &byteman.BigEndian{}) + } +} + +func BenchmarkFromUintLittleEndian(b *testing.B) { + for i := 0; i < b.N; i++ { + byteman.FromUint(uint64(math.MaxUint64), &byteman.LittleEndian{}) + } +} + +func TestFromInt(t *testing.T) { + table := []struct { + arg0 interface{} + arg1 byteman.ByteOrder + out []byte + }{ + {int(math.MaxInt), &byteman.BigEndian{}, []byte{}}, + {int(math.MaxInt), &byteman.LittleEndian{}, []byte{}}, + {int8(0), &byteman.BigEndian{}, []byte{0x0}}, + {int16(12345), &byteman.BigEndian{}, []byte{0x30, 0x39}}, + {int16(12321), &byteman.BigEndian{}, []byte{0x30, 0x21}}, + {int8(math.MaxInt8), &byteman.BigEndian{}, []byte{0x7f}}, + {int16(math.MaxInt16), &byteman.BigEndian{}, []byte{0x7f, 0xff}}, + {int32(math.MaxInt32), &byteman.BigEndian{}, []byte{0x7f, 0xff, 0xff, 0xff}}, + {int64(math.MaxInt64), &byteman.BigEndian{}, []byte{0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}}, + {int8(0), &byteman.LittleEndian{}, []byte{0x0}}, + {int16(12345), &byteman.LittleEndian{}, []byte{0x39, 0x30}}, + {int16(12321), &byteman.LittleEndian{}, []byte{0x21, 0x30}}, + {int8(math.MaxInt8), &byteman.LittleEndian{}, []byte{0x7f}}, + {int16(math.MaxInt16), &byteman.LittleEndian{}, []byte{0xff, 0x7f}}, + {int32(math.MaxInt32), &byteman.LittleEndian{}, []byte{0xff, 0xff, 0xff, 0x7f}}, + {int64(math.MaxInt64), &byteman.LittleEndian{}, []byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f}}, + } + if byteman.IntSize == 64 { + table[0].out = []byte{0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff} + table[1].out = []byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f} + } else { + table[0].out = []byte{0x7f, 0xff, 0xff, 0xff} + table[1].out = []byte{0xff, 0xff, 0xff, 0x7f} + } + for _, v := range table { + b := byteman.FromInt(v.arg0, v.arg1) + if !bytes.Equal(b, v.out) { + t.Errorf("got %v, want %v", b, v.out) + } + } + + if b := byteman.FromInt(float64(0), &byteman.BigEndian{}); b != nil { + t.Errorf("got %v, want nil", b) + } else if b := byteman.FromInt(float64(0), &byteman.LittleEndian{}); b != nil { + t.Errorf("got %v, want nil", b) + } +} + +func BenchmarkFromIntBigEndian(b *testing.B) { + for i := 0; i < b.N; i++ { + byteman.FromInt(uint64(math.MaxUint64), &byteman.BigEndian{}) + } +} + +func BenchmarkFromIntLittleEndian(b *testing.B) { + for i := 0; i < b.N; i++ { + byteman.FromInt(uint64(math.MaxUint64), &byteman.LittleEndian{}) + } +} + +func TestUint(t *testing.T) { + table := []struct { + arg0 []byte + arg1 byteman.ByteOrder + out uint + }{ + {[]byte{0xff, 0xff, 0xff, 0xff}, &byteman.BigEndian{}, math.MaxUint32}, + {[]byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, &byteman.BigEndian{}, math.MaxUint}, + {[]byte{0x49, 0x96, 0x02, 0xd2}, &byteman.BigEndian{}, 1234567890}, + {[]byte{0xbf, 0x63, 0xc8, 0x86}, &byteman.BigEndian{}, 3210987654}, + {[]byte{0xff, 0xff, 0xff, 0xff}, &byteman.LittleEndian{}, math.MaxUint32}, + {[]byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, &byteman.LittleEndian{}, math.MaxUint}, + {[]byte{0xd2, 0x02, 0x96, 0x49}, &byteman.LittleEndian{}, 1234567890}, + {[]byte{0x86, 0xc8, 0x63, 0xbf}, &byteman.LittleEndian{}, 3210987654}, + } + for _, v := range table { + if i := byteman.Uint(v.arg0, v.arg1); i != v.out { + t.Errorf("got %v, want %v", i, v.out) + } + } + + if i := byteman.Uint([]byte{}, &byteman.BigEndian{}); i != 0 { + t.Errorf("got %v, want 0", i) + } else if i := byteman.Uint([]byte{}, &byteman.LittleEndian{}); i != 0 { + t.Errorf("got %v, want 0", i) + } +} + +func BenchmarkUintBigEndian(b *testing.B) { + for i := 0; i < b.N; i++ { + byteman.Uint([]byte{0xff, 0xff, 0xff, 0xff}, &byteman.BigEndian{}) + } +} + +func BenchmarkUintLittleEndian(b *testing.B) { + for i := 0; i < b.N; i++ { + byteman.Uint([]byte{0xff, 0xff, 0xff, 0xff}, &byteman.LittleEndian{}) + } +} + +func TestUint16(t *testing.T) { + table := []struct { + arg0 []byte + arg1 byteman.ByteOrder + out uint16 + }{ + {[]byte{0xff, 0xff}, &byteman.BigEndian{}, math.MaxUint16}, + {[]byte{0x30, 0x39}, &byteman.BigEndian{}, 12345}, + {[]byte{0xD4, 0x31}, &byteman.BigEndian{}, 54321}, + {[]byte{0xff, 0xff}, &byteman.LittleEndian{}, math.MaxUint16}, + {[]byte{0x39, 0x30}, &byteman.LittleEndian{}, 12345}, + {[]byte{0x31, 0xD4}, &byteman.LittleEndian{}, 54321}, + } + for _, v := range table { + if i := byteman.Uint16(v.arg0, v.arg1); i != v.out { + t.Errorf("got %v, want %v", i, v.out) + } + } + + if i := byteman.Uint16([]byte{}, &byteman.BigEndian{}); i != 0 { + t.Errorf("got %v, want 0", i) + } else if i := byteman.Uint16([]byte{}, &byteman.LittleEndian{}); i != 0 { + t.Errorf("got %v, want 0", i) + } +} + +func BenchmarkUint16BigEndian(b *testing.B) { + for i := 0; i < b.N; i++ { + byteman.Uint16([]byte{0xff, 0xff}, &byteman.BigEndian{}) + } +} + +func BenchmarkUint16LittleEndian(b *testing.B) { + for i := 0; i < b.N; i++ { + byteman.Uint16([]byte{0xff, 0xff}, &byteman.LittleEndian{}) + } +} + +func TestUint32(t *testing.T) { + table := []struct { + arg0 []byte + arg1 byteman.ByteOrder + out uint32 + }{ + {[]byte{0xff, 0xff, 0xff, 0xff}, &byteman.BigEndian{}, math.MaxUint32}, + {[]byte{0x49, 0x96, 0x02, 0xd2}, &byteman.BigEndian{}, 1234567890}, + {[]byte{0xbf, 0x63, 0xc8, 0x86}, &byteman.BigEndian{}, 3210987654}, + {[]byte{0xff, 0xff, 0xff, 0xff}, &byteman.LittleEndian{}, math.MaxUint32}, + {[]byte{0xd2, 0x02, 0x96, 0x49}, &byteman.LittleEndian{}, 1234567890}, + {[]byte{0x86, 0xc8, 0x63, 0xbf}, &byteman.LittleEndian{}, 3210987654}, + } + for _, v := range table { + if i := byteman.Uint32(v.arg0, v.arg1); i != v.out { + t.Errorf("got %v, want %v", i, v.out) + } + } + + if i := byteman.Uint32([]byte{}, &byteman.BigEndian{}); i != 0 { + t.Errorf("got %v, want 0", i) + } else if i := byteman.Uint32([]byte{}, &byteman.LittleEndian{}); i != 0 { + t.Errorf("got %v, want 0", i) + } +} + +func BenchmarkUint32BigEndian(b *testing.B) { + for i := 0; i < b.N; i++ { + byteman.Uint32([]byte{0xff, 0xff, 0xff, 0xff}, &byteman.BigEndian{}) + } +} + +func BenchmarkUint32LittleEndian(b *testing.B) { + for i := 0; i < b.N; i++ { + byteman.Uint32([]byte{0xff, 0xff, 0xff, 0xff}, &byteman.LittleEndian{}) + } +} + +func TestUint64(t *testing.T) { + table := []struct { + arg0 []byte + arg1 byteman.ByteOrder + out uint64 + }{ + {[]byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, &byteman.BigEndian{}, math.MaxUint64}, + {[]byte{0xab, 0x54, 0xa9, 0x8c, 0xeb, 0x1f, 0x0a, 0xd2}, &byteman.BigEndian{}, 12345678901234567890}, + {[]byte{0xf5, 0x00, 0xbf, 0x80, 0xb2, 0x98, 0xf5, 0x2d}, &byteman.BigEndian{}, 17654321098765432109}, + {[]byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, &byteman.LittleEndian{}, math.MaxUint64}, + {[]byte{0xd2, 0x0a, 0x1f, 0xeb, 0x8c, 0xa9, 0x54, 0xab}, &byteman.LittleEndian{}, 12345678901234567890}, + {[]byte{0x2d, 0xf5, 0x98, 0xb2, 0x80, 0xbf, 0x00, 0xf5}, &byteman.LittleEndian{}, 17654321098765432109}, + } + for _, v := range table { + if i := byteman.Uint64(v.arg0, v.arg1); i != v.out { + t.Errorf("got %v, want %v", i, v.out) + } + } + if i := byteman.Uint64([]byte{}, &byteman.BigEndian{}); i != 0 { + t.Errorf("got %v, want 0", i) + } else if i := byteman.Uint64([]byte{}, &byteman.LittleEndian{}); i != 0 { + t.Errorf("got %v, want 0", i) + } +} + +func BenchmarkUint64BigEndian(b *testing.B) { + for i := 0; i < b.N; i++ { + byteman.Uint64([]byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, &byteman.BigEndian{}) + } +} + +func BenchmarkUint64LittleEndian(b *testing.B) { + for i := 0; i < b.N; i++ { + byteman.Uint64([]byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, &byteman.LittleEndian{}) + } +} + +func TestInt(t *testing.T) { + table := []struct { + arg0 []byte + arg1 byteman.ByteOrder + out int + }{ + {[]byte{0x7f, 0xff, 0xff, 0xff}, &byteman.BigEndian{}, math.MaxInt32}, + {[]byte{0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, &byteman.BigEndian{}, math.MaxInt}, + {[]byte{0x49, 0x96, 0x02, 0xd2}, &byteman.BigEndian{}, 1234567890}, + {[]byte{0x7d, 0xc2, 0x29, 0x3f}, &byteman.BigEndian{}, 2109876543}, + {[]byte{0xff, 0xff, 0xff, 0x7f}, &byteman.LittleEndian{}, math.MaxInt32}, + {[]byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f}, &byteman.LittleEndian{}, math.MaxInt}, + {[]byte{0xd2, 0x02, 0x96, 0x49}, &byteman.LittleEndian{}, 1234567890}, + {[]byte{0x3f, 0x29, 0xc2, 0x7d}, &byteman.LittleEndian{}, 2109876543}, + } + for _, v := range table { + if i := byteman.Int(v.arg0, v.arg1); i != v.out { + t.Errorf("got %v, want %v", i, v.out) + } + } + + if i := byteman.Int([]byte{}, &byteman.BigEndian{}); i != 0 { + t.Errorf("got %v, want 0", i) + } else if i := byteman.Int([]byte{}, &byteman.LittleEndian{}); i != 0 { + t.Errorf("got %v, want 0", i) + } +} + +func BenchmarkIntBigEndian(b *testing.B) { + for i := 0; i < b.N; i++ { + byteman.Int([]byte{0x7f, 0xff, 0xff, 0xff}, &byteman.BigEndian{}) + } +} + +func BenchmarkIntLittleEndian(b *testing.B) { + for i := 0; i < b.N; i++ { + byteman.Int([]byte{0x7f, 0xff, 0xff, 0xff}, &byteman.LittleEndian{}) + } +} + +func TestInt16(t *testing.T) { + table := []struct { + arg0 []byte + arg1 byteman.ByteOrder + out int16 + }{ + {[]byte{0x7f, 0xff}, &byteman.BigEndian{}, math.MaxInt16}, + {[]byte{0x30, 0x39}, &byteman.BigEndian{}, 12345}, + {[]byte{0x30, 0x21}, &byteman.BigEndian{}, 12321}, + {[]byte{0xff, 0x7f}, &byteman.LittleEndian{}, math.MaxInt16}, + {[]byte{0x39, 0x30}, &byteman.LittleEndian{}, 12345}, + {[]byte{0x21, 0x30}, &byteman.LittleEndian{}, 12321}, + } + for _, v := range table { + if i := byteman.Int16(v.arg0, v.arg1); i != v.out { + t.Errorf("got %v, want %v", i, v.out) + } + } + + if i := byteman.Int16([]byte{}, &byteman.BigEndian{}); i != 0 { + t.Errorf("got %v, want 0", i) + } else if i := byteman.Int16([]byte{}, &byteman.LittleEndian{}); i != 0 { + t.Errorf("got %v, want 0", i) + } +} + +func BenchmarkInt16BigEndian(b *testing.B) { + for i := 0; i < b.N; i++ { + byteman.Int16([]byte{0xff, 0xff}, &byteman.BigEndian{}) + } +} + +func BenchmarkInt16LittleEndian(b *testing.B) { + for i := 0; i < b.N; i++ { + byteman.Int16([]byte{0xff, 0xff}, &byteman.LittleEndian{}) + } +} + +func TestInt32(t *testing.T) { + table := []struct { + arg0 []byte + arg1 byteman.ByteOrder + out int32 + }{ + {[]byte{0x7f, 0xff, 0xff, 0xff}, &byteman.BigEndian{}, math.MaxInt32}, + {[]byte{0x49, 0x96, 0x02, 0xd2}, &byteman.BigEndian{}, 1234567890}, + {[]byte{0x7d, 0xc2, 0x29, 0x3f}, &byteman.BigEndian{}, 2109876543}, + {[]byte{0xff, 0xff, 0xff, 0x7f}, &byteman.LittleEndian{}, math.MaxInt32}, + {[]byte{0xd2, 0x02, 0x96, 0x49}, &byteman.LittleEndian{}, 1234567890}, + {[]byte{0x3f, 0x29, 0xc2, 0x7d}, &byteman.LittleEndian{}, 2109876543}, + } + for _, v := range table { + if i := byteman.Int32(v.arg0, v.arg1); i != v.out { + t.Errorf("got %v, want %v", i, v.out) + } + } + + if i := byteman.Int32([]byte{}, &byteman.BigEndian{}); i != 0 { + t.Errorf("got %v, want 0", i) + } else if i := byteman.Int32([]byte{}, &byteman.LittleEndian{}); i != 0 { + t.Errorf("got %v, want 0", i) + } +} + +func BenchmarkInt32BigEndian(b *testing.B) { + for i := 0; i < b.N; i++ { + byteman.Int32([]byte{0xff, 0xff}, &byteman.BigEndian{}) + } +} + +func BenchmarkInt32LittleEndian(b *testing.B) { + for i := 0; i < b.N; i++ { + byteman.Int32([]byte{0xff, 0xff}, &byteman.LittleEndian{}) + } +} + +func TestInt64(t *testing.T) { + table := []struct { + arg0 []byte + arg1 byteman.ByteOrder + out int64 + }{ + {[]byte{0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, &byteman.BigEndian{}, math.MaxInt64}, + {[]byte{0x11, 0x22, 0x10, 0xf4, 0x7d, 0xe9, 0x81, 0x15}, &byteman.BigEndian{}, 1234567890123456789}, + {[]byte{0x4b, 0x62, 0xbb, 0x26, 0xf4, 0x3f, 0xc5, 0xeb}, &byteman.BigEndian{}, 5432109876543210987}, + {[]byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f}, &byteman.LittleEndian{}, math.MaxInt64}, + {[]byte{0x15, 0x81, 0xe9, 0x7d, 0xf4, 0x10, 0x22, 0x11}, &byteman.LittleEndian{}, 1234567890123456789}, + {[]byte{0xeb, 0xc5, 0x3f, 0xf4, 0x26, 0xbb, 0x62, 0x4b}, &byteman.LittleEndian{}, 5432109876543210987}, + } + for _, v := range table { + if i := byteman.Int64(v.arg0, v.arg1); i != v.out { + t.Errorf("got %v, want %v", i, v.out) + } + } + + if i := byteman.Int64([]byte{}, &byteman.BigEndian{}); i != 0 { + t.Errorf("got %v, want 0", i) + } else if i := byteman.Int64([]byte{}, &byteman.LittleEndian{}); i != 0 { + t.Errorf("got %v, want 0", i) + } +} + +func BenchmarkInt64BigEndian(b *testing.B) { + for i := 0; i < b.N; i++ { + byteman.Int64([]byte{0xff, 0xff}, &byteman.BigEndian{}) + } +} + +func BenchmarkInt64LittleEndian(b *testing.B) { + for i := 0; i < b.N; i++ { + byteman.Int64([]byte{0xff, 0xff}, &byteman.LittleEndian{}) + } +} diff --git a/strings.go b/strings.go new file mode 100644 index 0000000..a0e35f9 --- /dev/null +++ b/strings.go @@ -0,0 +1,44 @@ +// Byteman +// For the full copyright and license information, please view the LICENSE.txt file. + +package byteman + +import ( + "encoding/hex" +) + +// FromString returns a byte slice by the given string and desired byte size. +func FromString(str string, size int) []byte { + if size == 0 { + size = len(str) + } else if size < 0 { + if ns := len(str) + size; ns > 0 { + size = ns + } else { + size = 0 + } + } + b := make([]byte, size) + copy(b, []byte(str)) + return b +} + +// FromHex returns a byte slice by the given ASCII Hex code and desired byte size. +func FromHex(hexcode string, size int) []byte { + decoded, err := hex.DecodeString(hexcode) + if err != nil { + return nil + } + if size == 0 { + size = len(decoded) + } else if size < 0 { + if ns := len(decoded) + size; ns > 0 { + size = ns + } else { + size = 0 + } + } + b := make([]byte, size) + copy(b, []byte(decoded)) + return b +} diff --git a/strings_test.go b/strings_test.go new file mode 100644 index 0000000..8d7937f --- /dev/null +++ b/strings_test.go @@ -0,0 +1,79 @@ +// Byteman +// For the full copyright and license information, please view the LICENSE.txt file. + +package byteman_test + +import ( + "bytes" + "testing" + + "github.com/devfacet/byteman" +) + +func TestFromString(t *testing.T) { + table := []struct { + arg0 string + arg1 int + out []byte + }{ + {"foo", 0, []byte("foo")}, + {"bar", 1, []byte("b")}, + {"baz", 2, []byte("ba")}, + {"qux", 3, []byte("qux")}, + {"foo", 4, []byte{0x66, 0x6f, 0x6f, 0x00}}, + {"foo", -1, []byte("fo")}, + {"bar", -2, []byte("b")}, + {"baz", -3, []byte{}}, + {"qux", -4, []byte{}}, + {"", 0, []byte("")}, + {"", 0, []byte{}}, + } + for _, v := range table { + b := byteman.FromString(v.arg0, v.arg1) + if !bytes.Equal(b, v.out) { + t.Errorf("got %v, want %v", b, v.out) + } + } +} + +func BenchmarkFromString(b *testing.B) { + for i := 0; i < b.N; i++ { + byteman.FromString("foo", 3) + } +} + +func TestFromHex(t *testing.T) { + table := []struct { + arg0 string + arg1 int + out []byte + }{ + {"414243", 0, []byte("ABC")}, + {"414243", 1, []byte("A")}, + {"414243", 2, []byte("AB")}, + {"414243", 3, []byte("ABC")}, + {"414243", 4, []byte{0x41, 0x42, 0x43, 0x00}}, + {"414243", -1, []byte("AB")}, + {"414243", -2, []byte("A")}, + {"414243", -3, []byte{}}, + {"414243", -4, []byte{}}, + {"", 0, []byte("")}, + {"", 0, []byte{}}, + } + for _, v := range table { + b := byteman.FromHex(v.arg0, v.arg1) + if !bytes.Equal(b, v.out) { + t.Errorf("got %v, want %v", b, v.out) + } + } + + if b := byteman.FromHex("9", 1); b != nil { + t.Errorf("got %v, want nil", b) + } +} + +func BenchmarkFromHex(b *testing.B) { + for i := 0; i < b.N; i++ { + byteman.FromHex("A", 1) + } +}