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
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
2 changes: 0 additions & 2 deletions .gitattributes

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,27 +1,38 @@
name: Release package
name: _release-package (reusable)

# Manually-triggered release build. Produces a Release-mode NuGet package
# (.nupkg + .snupkg), tags master with the release version, and publishes a
# GitHub Release with the package attached as a downloadable asset.
# Shared release logic used by the per-package release workflows
# (release-base65536.yml / release-base2048.yml). Not directly triggerable - it only
# has a workflow_call trigger.
#
# This does NOT push to NuGet.org (or any other package registry) - that
# still requires your own API key and is a separate, deliberate step you run
# afterwards against the package attached to the release.
# Produces a Release-mode NuGet package (.nupkg + .snupkg) for the given project, tags
# master with a package-scoped release version, and publishes a GitHub Release with the
# package attached as a downloadable asset.
#
# This does NOT push to NuGet.org (or any other package registry) - that still requires
# your own API key and is a separate, deliberate step you run afterwards against the
# package attached to the release.

on:
workflow_dispatch:
workflow_call:
inputs:
project-path:
description: 'Path to the .csproj to release'
required: true
type: string
tag-prefix:
description: 'Prefix for the git tag / release title, e.g. "base65536"'
required: true
type: string
version:
description: 'Release version override (e.g. 1.2.0). Leave empty to use the version in the csproj.'
description: 'Version override (e.g. 1.2.0). Leave empty to use the version in the csproj.'
required: false
type: string

permissions:
contents: write

jobs:
package:
runs-on: ubuntu-latest
permissions:
contents: write

steps:
- name: Require master
Expand All @@ -44,11 +55,11 @@ jobs:
id: version
shell: bash
run: |
if [ -n "${{ github.event.inputs.version }}" ]; then
VERSION="${{ github.event.inputs.version }}"
if [ -n "${{ inputs.version }}" ]; then
VERSION="${{ inputs.version }}"
echo "flag=-p:Version=$VERSION" >> "$GITHUB_OUTPUT"
else
VERSION=$(grep -oPm1 '(?<=<Version>)[^<]+' CyberDot.Encoding.Base65536/CyberDot.Encoding.Base65536.csproj)
VERSION=$(grep -oPm1 '(?<=<Version>)[^<]+' "${{ inputs.project-path }}")
echo "flag=" >> "$GITHUB_OUTPUT"
fi

Expand All @@ -58,7 +69,7 @@ jobs:
fi

echo "version=$VERSION" >> "$GITHUB_OUTPUT"
echo "tag=v$VERSION" >> "$GITHUB_OUTPUT"
echo "tag=${{ inputs.tag-prefix }}-v$VERSION" >> "$GITHUB_OUTPUT"

- name: Ensure tag does not already exist
run: |
Expand All @@ -68,17 +79,17 @@ jobs:
fi

- name: Restore
run: dotnet restore Base65536.sln
run: dotnet restore CyberDot.Encoding.sln

- name: Build (Release)
run: dotnet build Base65536.sln --configuration Release --no-restore ${{ steps.version.outputs.flag }}
run: dotnet build CyberDot.Encoding.sln --configuration Release --no-restore ${{ steps.version.outputs.flag }}

- name: Test (Release)
run: dotnet test Base65536.sln --configuration Release --no-build --verbosity normal
run: dotnet test CyberDot.Encoding.sln --configuration Release --no-build --verbosity normal

- name: Pack (Release)
run: >
dotnet pack CyberDot.Encoding.Base65536/CyberDot.Encoding.Base65536.csproj
dotnet pack "${{ inputs.project-path }}"
--configuration Release
--no-build
--output "${{ github.workspace }}/artifacts"
Expand All @@ -87,7 +98,7 @@ jobs:
- name: Upload package artifact
uses: actions/upload-artifact@v4
with:
name: nuget-package
name: nuget-package-${{ inputs.tag-prefix }}
path: |
artifacts/*.nupkg
artifacts/*.snupkg
Expand Down
20 changes: 11 additions & 9 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,19 +28,21 @@ jobs:
dotnet-version: '8.0.x'

- name: Restore
run: dotnet restore Base65536.sln
run: dotnet restore CyberDot.Encoding.sln

- name: Build
run: dotnet build Base65536.sln --configuration Debug --no-restore
run: dotnet build CyberDot.Encoding.sln --configuration Debug --no-restore

- name: Test
run: dotnet test Base65536.sln --configuration Debug --no-build --verbosity normal
run: dotnet test CyberDot.Encoding.sln --configuration Debug --no-build --verbosity normal

- name: Verify NuGet package generation
# Packed to the runner's temp directory and never uploaded, so this step
# only proves packaging succeeds - it leaves nothing behind.
run: >
dotnet pack CyberDot.Encoding.Base65536/CyberDot.Encoding.Base65536.csproj
--configuration Debug
--no-build
--output "${{ runner.temp }}/nupkg-verify"
# only proves packaging succeeds - it leaves nothing behind. Covers every
# packable project in the solution, not just one.
run: |
for project in \
src/CyberDot.Encoding.Base65536/CyberDot.Encoding.Base65536.csproj \
src/CyberDot.Encoding.Base2048/CyberDot.Encoding.Base2048.csproj; do
dotnet pack "$project" --configuration Debug --no-build --output "${{ runner.temp }}/nupkg-verify"
done
22 changes: 22 additions & 0 deletions .github/workflows/release-base2048.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
name: Release CyberDot.Encoding.Base2048

# Manually-triggered release for the Base2048 package. See _release-package.yml for
# what this actually does.

on:
workflow_dispatch:
inputs:
version:
description: 'Release version override (e.g. 1.2.0). Leave empty to use the version in the csproj.'
required: false
type: string

jobs:
release:
permissions:
contents: write
uses: ./.github/workflows/_release-package.yml
with:
project-path: src/CyberDot.Encoding.Base2048/CyberDot.Encoding.Base2048.csproj
tag-prefix: base2048
version: ${{ inputs.version }}
22 changes: 22 additions & 0 deletions .github/workflows/release-base65536.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
name: Release CyberDot.Encoding.Base65536

# Manually-triggered release for the Base65536 package. See _release-package.yml for
# what this actually does.

on:
workflow_dispatch:
inputs:
version:
description: 'Release version override (e.g. 1.2.0). Leave empty to use the version in the csproj.'
required: false
type: string

jobs:
release:
permissions:
contents: write
uses: ./.github/workflows/_release-package.yml
with:
project-path: src/CyberDot.Encoding.Base65536/CyberDot.Encoding.Base65536.csproj
tag-prefix: base65536
version: ${{ inputs.version }}
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
bin
obj
lib
.vs
.vs
.claude
.git
28 changes: 0 additions & 28 deletions Base65536.sln

This file was deleted.

43 changes: 43 additions & 0 deletions CyberDot.Encoding.sln
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 18
VisualStudioVersion = 18.9.12105.275 stable
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CyberDot.Encoding.Base65536", "src\CyberDot.Encoding.Base65536\CyberDot.Encoding.Base65536.csproj", "{C021E149-EB05-4EF0-9A4C-78A0808AC413}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CyberDot.Encoding.Base65536.Tests", "src\CyberDot.Encoding.Base65536.Tests\CyberDot.Encoding.Base65536.Tests.csproj", "{F4232660-0EA0-432E-8713-F4612E88AA80}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CyberDot.Encoding.Base2048", "src\CyberDot.Encoding.Base2048\CyberDot.Encoding.Base2048.csproj", "{C7494689-9E71-4F15-883E-027E244E3E6D}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CyberDot.Encoding.Base2048.Tests", "src\CyberDot.Encoding.Base2048.Tests\CyberDot.Encoding.Base2048.Tests.csproj", "{287EE26D-CAE7-4AA3-93C5-4A6B2C8A9D05}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{C021E149-EB05-4EF0-9A4C-78A0808AC413}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{C021E149-EB05-4EF0-9A4C-78A0808AC413}.Debug|Any CPU.Build.0 = Debug|Any CPU
{C021E149-EB05-4EF0-9A4C-78A0808AC413}.Release|Any CPU.ActiveCfg = Release|Any CPU
{C021E149-EB05-4EF0-9A4C-78A0808AC413}.Release|Any CPU.Build.0 = Release|Any CPU
{F4232660-0EA0-432E-8713-F4612E88AA80}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{F4232660-0EA0-432E-8713-F4612E88AA80}.Debug|Any CPU.Build.0 = Debug|Any CPU
{F4232660-0EA0-432E-8713-F4612E88AA80}.Release|Any CPU.ActiveCfg = Release|Any CPU
{F4232660-0EA0-432E-8713-F4612E88AA80}.Release|Any CPU.Build.0 = Release|Any CPU
{C7494689-9E71-4F15-883E-027E244E3E6D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{C7494689-9E71-4F15-883E-027E244E3E6D}.Debug|Any CPU.Build.0 = Debug|Any CPU
{C7494689-9E71-4F15-883E-027E244E3E6D}.Release|Any CPU.ActiveCfg = Release|Any CPU
{C7494689-9E71-4F15-883E-027E244E3E6D}.Release|Any CPU.Build.0 = Release|Any CPU
{287EE26D-CAE7-4AA3-93C5-4A6B2C8A9D05}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{287EE26D-CAE7-4AA3-93C5-4A6B2C8A9D05}.Debug|Any CPU.Build.0 = Debug|Any CPU
{287EE26D-CAE7-4AA3-93C5-4A6B2C8A9D05}.Release|Any CPU.ActiveCfg = Release|Any CPU
{287EE26D-CAE7-4AA3-93C5-4A6B2C8A9D05}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {9D040A49-47AE-4503-B1C2-7D3E82AF66AB}
EndGlobalSection
EndGlobal
60 changes: 34 additions & 26 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,16 @@
# base65536
# CyberDot.Encoding

An implementation of [base65536][1] encoding in C#.
Binary-to-text encoding implementations in C#.

Base65536 only uses "safe" Unicode code points (no unassigned code points, no
whitespace, no control characters), which means the encoded text is equally valid as UTF-8, UTF-16 or UTF-32.
| Package | NuGet | Description |
| --- | --- | --- |
| [CyberDot.Encoding.Base65536](src/CyberDot.Encoding.Base65536) | `CyberDot.Encoding.Base65536` | [Base65536][base65536-js] - packs 2 bytes per character using "safe" Unicode code points, so the encoded text is equally valid as UTF-8, UTF-16 or UTF-32. |
| [CyberDot.Encoding.Base2048](src/CyberDot.Encoding.Base2048) | `CyberDot.Encoding.Base2048` | [Base2048][base2048-js] - packs 11 bits per character, optimised for services with per-character length limits (e.g. Twitter). |

Both packages expose the same shape of API: a static `Encode`/`Decode` pair for one-shot
use, plus `ICryptoTransform` implementations (`To*Transform`/`From*Transform`) that plug
into `CryptoStream` for streaming encode/decode without buffering the whole payload in
memory.

## Usage

Expand All @@ -12,38 +19,39 @@ using CyberDot.Encoding.Base65536;

var bytes = System.Text.Encoding.UTF8.GetBytes("hello world");
var encoded = Base65536.Encode(bytes); // Output: 驨ꍬ啯𒁷ꍲᕤ
var decoded = Base65536.Decode(encoded); // Output: hello world
```

```csharp
using CyberDot.Encoding.Base2048;

var decoded = Base65536.Decode(encoded); // Output: hello world
var bytes = System.Text.Encoding.UTF8.GetBytes("hello world");
var encoded = Base2048.Encode(bytes); // Output: ڵϠɲණæஊಢࢷ
var decoded = Base2048.Decode(encoded); // Output: hello world
```

### Streaming
See each package's own README for streaming usage and further details:
- [src/CyberDot.Encoding.Base65536/README.md](src/CyberDot.Encoding.Base65536/README.md)
- [src/CyberDot.Encoding.Base2048/README.md](src/CyberDot.Encoding.Base2048/README.md)

`ToBase65536Transform`/`FromBase65536Transform` implement
`ICryptoTransform`, so they plug into `CryptoStream` for streaming encode/decode without
buffering the whole payload in memory. They default to UTF-8, or accept any of the three UTF encodings
via an `Encoding` parameter.
## Solution layout

```csharp
using System.Security.Cryptography;

using var output = new MemoryStream();
using (var cryptoStream = new CryptoStream(output, new ToBase65536Transform(), CryptoStreamMode.Write))
{
sourceStream.CopyTo(cryptoStream);
}

using var decoded = new MemoryStream();
using (var cryptoStream = new CryptoStream(encodedStream, new FromBase65536Transform(), CryptoStreamMode.Read))
{
cryptoStream.CopyTo(decoded);
}
```
CyberDot.Encoding.sln
src/
CyberDot.Encoding.Base65536/ library
CyberDot.Encoding.Base65536.Tests/ tests
CyberDot.Encoding.Base2048/ library
CyberDot.Encoding.Base2048.Tests/ tests
```

## Credits
Javascript original implementation: [base65536](https://github.com/ferno/base65536).
- Base65536 JavaScript original: [qntm/base65536][base65536-js]
- Base2048 JavaScript original: [qntm/base2048][base2048-js]

## License

The MIT License (MIT)

[1]: https://github.com/qntm/base65536
[base65536-js]: https://github.com/qntm/base65536
[base2048-js]: https://github.com/qntm/base2048
Loading
Loading