Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
version: 2
updates:
- package-ecosystem: "gomod"
directory: "/"
schedule:
interval: "weekly"

- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "weekly"
48 changes: 48 additions & 0 deletions .github/workflows/codeql.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
name: "CodeQL"

on:
push:
branches: [ "main" ]
pull_request:

jobs:
analyze:
name: Analyze (${{ matrix.language }})
runs-on: ${{ (matrix.language == 'swift' && 'macos-latest') || 'ubuntu-latest' }}
permissions:
security-events: write
packages: read
actions: read
contents: read

strategy:
fail-fast: false
matrix:
language: [ 'go' ]

steps:
- name: Checkout repository
uses: actions/checkout@v6

- name: Set up Go
uses: actions/setup-go@v6
with:
cache: false
go-version-file: go.mod
if: ${{ matrix.language == 'go' }}

- name: Initialize CodeQL
uses: github/codeql-action/init@v4
with:
languages: ${{ matrix.language }}
timeout-minutes: 10

- name: Autobuild (${{ matrix.language }})
uses: github/codeql-action/autobuild@v4
timeout-minutes: 10

- name: Perform CodeQL Analysis (${{ matrix.language }})
uses: github/codeql-action/analyze@v4
with:
category: "/language:${{matrix.language}}"
timeout-minutes: 10
13 changes: 13 additions & 0 deletions .github/workflows/tests-main.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
name: Tests - Main Push

on:
push:
branches: [ main ]

permissions:
contents: read

jobs:
call-reusable:
uses: ./.github/workflows/tests-workflow.yml

12 changes: 12 additions & 0 deletions .github/workflows/tests-pr.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
name: Tests - Pull Request

on:
pull_request:

permissions:
contents: read

jobs:
call-reusable:
uses: ./.github/workflows/tests-workflow.yml

58 changes: 58 additions & 0 deletions .github/workflows/tests-workflow.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
name: Tests

on:
workflow_call:

permissions:
contents: read

jobs:
lint-and-tidy:
name: Verify Linting
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6

- name: Set up Go
uses: actions/setup-go@v6
with:
go-version: "1.25"

- name: Set up golangci-lint
run: |
curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/v2.7.1/install.sh | sh -s -- -b $(go env GOPATH)/bin v2.7.1

- name: Run lint check
run: make lint

- name: Run tidy check
run: |
go mod tidy
# Fail if go.mod or go.sum changed
git diff --exit-code go.mod go.sum

test:
name: Verify Unit Tests
runs-on: ubuntu-latest
strategy:
matrix:
go:
- '1.25'
- '1.24'
- '1.23'
- '1.22'
- '1.21'
- '1.20'
- '1.19'
- '1.18'
steps:
- uses: actions/checkout@v6

- name: Set up Go ${{ matrix.go }}
uses: actions/setup-go@v6
with:
go-version: ${{ matrix.go }}

- name: Run unit tests
run: make test

50 changes: 50 additions & 0 deletions .golangci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
version: 2

linters:
enable:
- asasalint
- asciicheck
- bidichk
- containedctx
- contextcheck
- decorder
- durationcheck
- errcheck
- errorlint
- exptostd
- fatcontext
- forbidigo
- gocheckcompilerdirectives
- gochecksumtype
- goconst
- godoclint
- gosmopolitan
- grouper
- iface
- importas
- mirror
- misspell
- musttag
- nilerr
- nilnil
- perfsprint
- prealloc
- reassign
- sloglint
- testifylint
- thelper
- unconvert
- wastedassign
- whitespace
settings:
goconst:
min-len: 4
min-occurrences: 4

formatters:
enable:
- gofmt
- goimports

run:
timeout: 600s
25 changes: 25 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
export GO111MODULE=on

.PHONY: default test test-cover test-fuzz bench lint


test:
go test -race -cover ./...

test-cover:
go test -race -coverprofile=test.out ./... && go tool cover --html=test.out

test-fuzz:
go test -fuzz='^FuzzEncode$$' -fuzztime=2m ./base85/...
go test -fuzz='^FuzzDecode$$' -fuzztime=2m ./base85/...
go test -fuzz='^FuzzEncodeWithPadding$$' -fuzztime=2m ./base85/...
go test -fuzz='^FuzzDecodeWithPadding$$' -fuzztime=2m ./base85/...
go test -fuzz='^FuzzStreamRoundTrip$$' -fuzztime=2m ./base85/...
go test -fuzz='^FuzzStreamRoundTripWithPadding$$' -fuzztime=2m ./base85/...

bench:
go test --benchmem -benchtime=10s -bench='Benchmark.*' -run='^$$' ./...

lint:
golangci-lint run --timeout=600s && go vet ./...

53 changes: 52 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,53 @@
# encoding
Encoding utilities for data processing and storage

Lightweight encoding packages for niche use cases not covered by Go's standard library. These packages are useful when you need compact encodings with specific character set requirements.

The API closely mirrors Go's `encoding/base64` and related packages, making it easy to transition to standard implementations if they become available. Packages will be deprecated and removed if equivalent functionality is added to the standard library.

## Installation

```bash
go get github.com/go-analyze/encoding@latest
```

## base85

The standard library's `encoding/ascii85` package only supports the Adobe/btoa variant with a fixed alphabet. This package provides base85 encoding with support for custom alphabets via `NewEncoding()`, following the same pattern as `encoding/base64`.

### RFC1924

RFC1924 defines a base85 encoding designed for compact representation of IPv6 addresses. It uses an 85-character alphabet consisting of `0-9`, `A-Z`, `a-z`, and 23 punctuation symbols, deliberately excluding characters that could cause parsing issues in various contexts (quotes, comma, period, slash, colon, brackets, and backslash).

```go
package main

import (
"fmt"

"github.com/go-analyze/encoding/base85"
)

func main() {
data := []byte("Hello, World!")

// Encode
encoded := base85.RFC1924.EncodeToString(data)
fmt.Println(encoded) // NM&qnZ!92JZ*pv8Ap

// Decode
decoded, err := base85.RFC1924.DecodeString(encoded)
if err != nil {
panic(err)
}
fmt.Println(string(decoded)) // Hello, World!
}
```

### Custom Alphabets

Create encodings with custom 85-character alphabets:

```go
enc := base85.NewEncoding("0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz!#$%&()*+-;<=>?@^_`{|}~")
encoded := enc.EncodeToString(data)
```
49 changes: 49 additions & 0 deletions base85/bench_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package base85

import (
"encoding/ascii85"
"testing"
)

var benchData = []byte("The quick brown fox jumps over the lazy dog. 0123456789!@#$%^&*()")

func BenchmarkEncodeBase85(b *testing.B) {
dst := make([]byte, RFC1924.EncodedLen(len(benchData)))
b.ResetTimer()

for i := 0; i < b.N; i++ {
RFC1924.Encode(dst, benchData)
}
}

func BenchmarkDecodeBase85(b *testing.B) {
encoded := RFC1924.EncodeToString(benchData)
src := []byte(encoded)
dst := make([]byte, RFC1924.DecodedLen(len(src)))
b.ResetTimer()

for i := 0; i < b.N; i++ {
_, _ = RFC1924.Decode(dst, src)
}
}

func BenchmarkEncodeAscii85(b *testing.B) {
dst := make([]byte, ascii85.MaxEncodedLen(len(benchData)))
b.ResetTimer()

for i := 0; i < b.N; i++ {
ascii85.Encode(dst, benchData)
}
}

func BenchmarkDecodeAscii85(b *testing.B) {
src := make([]byte, ascii85.MaxEncodedLen(len(benchData)))
n := ascii85.Encode(src, benchData)
src = src[:n]
dst := make([]byte, len(benchData))
b.ResetTimer()

for i := 0; i < b.N; i++ {
_, _, _ = ascii85.Decode(dst, src, true)
}
}
Loading