Problem: you want to encode arbitrary binary data as
a hashtag on Mastodon.
A solution: transform to a hex string and shift the
0..f up to a..p so all the bytes are lowercase Latin characters.
First, load up {tidyverse}:
library(tidyverse)
I want to transform a string like “Hello” to a hex string:
chars2hexstr <- function(t) {
t |>
charToRaw() |>
as.character() |>
paste(collapse = "")
}
Testing:
chars2hexstr("Hello")
[1] "48656c6c6f"
Consulting an ASCII
table, looks good. “H” = 0x48, “e” = 0x65, “l” = 0x6c, and “o” =
0x6f.
Let’s undo it:
hexstr2chars <- function(hexstr) {
substring(hexstr,
seq(1, nchar(hexstr) - 1, 2),
seq(2, nchar(hexstr), 2)) |>
strtoi(base = 16) |>
as.raw() |>
rawToChar()
}
And a test:
hexstr2chars("48656c6c6f")
[1] "Hello"
Next, I want to shift 0..f up to a..p. I’m sure there’s an easier
way. Here’s how I’m doing it. First, setup a mapping:
charmap <- data.frame(
hex = c(0:9, letters[1:6]),
let = letters[1:16])
charmap
These two functions translate back and forth between 0..f (hex) and
a..p (I’m going to call it a lethex), using a strsplit
and
join
:
hex2lethex <- function(hstr) {
data.frame(hex = (hstr |> strsplit(""))[[1]]) |>
left_join(charmap, by = "hex") |>
pull(let) |>
paste(collapse = "")
}
lethex2hex <- function(lethex) {
data.frame(let = (lethex |> strsplit(""))[[1]]) |>
left_join(charmap, by = "let") |>
pull(hex) |>
paste(collapse = "")
}
Try it with “Hello”. Here’s the hex string:
hello_hex <- chars2hexstr("Hello")
hello_hex
[1] "48656c6c6f"
Now as a lethex:
hello_lethex <- hello_hex |> hex2lethex()
hello_lethex
[1] "eigfgmgmgp"
And back again:
hello_lethex |> lethex2hex()
[1] "48656c6c6f"
Finally, a couple of wrappers, which take a string to a lethex string
and back again:
str2hashtag <- function(s) {
s |> chars2hexstr() |> hex2lethex()
}
hashtag2str <- function(h) {
h |> lethex2hex() |> hexstr2chars()
}
str2hashtag("Hello")
[1] "eigfgmgmgp"
hashtag2str("eigfgmgmgp")
[1] "Hello"
Finally:
hashtag2str("gihehehahddkcpcphhhhhhcohjgphfhehfgcgfcogdgpgncphhgbhegdgidphgdngefbhhdehhdjfhghfigdfb")
LS0tDQp0aXRsZTogIkVuY29kZSBieXRlcyBhcyBoYXNodGFncyINCmF1dGhvcjogIkBhbmRpQHRlY2gubGdidCINCm91dHB1dDogDQogIGh0bWxfbm90ZWJvb2s6IA0KICAgIGNvZGVfZm9sZGluZzogbm9uZQ0KLS0tDQoNCioqUHJvYmxlbToqKiB5b3Ugd2FudCB0byBlbmNvZGUgYXJiaXRyYXJ5IGJpbmFyeSBkYXRhIGFzIGEgaGFzaHRhZyBvbiBNYXN0b2Rvbi4NCg0KKipBIHNvbHV0aW9uOioqIHRyYW5zZm9ybSB0byBhIGhleCBzdHJpbmcgYW5kIHNoaWZ0IHRoZSAwLi5mIHVwIHRvIGEuLnAgc28gYWxsIHRoZSBieXRlcyBhcmUgbG93ZXJjYXNlIExhdGluIGNoYXJhY3RlcnMuDQoNCg0KRmlyc3QsIGxvYWQgdXAge3RpZHl2ZXJzZX06DQoNCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQpsaWJyYXJ5KHRpZHl2ZXJzZSkNCmBgYA0KDQoNCkkgd2FudCB0byB0cmFuc2Zvcm0gYSBzdHJpbmcgbGlrZSAiSGVsbG8iIHRvIGEgaGV4IHN0cmluZzoNCg0KYGBge3J9DQpjaGFyczJoZXhzdHIgPC0gZnVuY3Rpb24odCkgew0KICB0IHw+DQogICAgY2hhclRvUmF3KCkgfD4NCiAgICBhcy5jaGFyYWN0ZXIoKSB8Pg0KICAgIHBhc3RlKGNvbGxhcHNlID0gIiIpDQp9DQpgYGANCg0KVGVzdGluZzoNCg0KYGBge3J9DQpjaGFyczJoZXhzdHIoIkhlbGxvIikNCmBgYA0KDQpDb25zdWx0aW5nIGFuIFtBU0NJSSB0YWJsZV0oaHR0cHM6Ly9lbi53aWtpcGVkaWEub3JnL3dpa2kvQVNDSUkjUHJpbnRhYmxlX2NoYXJhY3RlcnMpLCBsb29rcyBnb29kLiAiSCIgPSAweDQ4LCAiZSIgPSAweDY1LCAibCIgPSAweDZjLCBhbmQgIm8iID0gMHg2Zi4NCg0KDQpMZXQncyB1bmRvIGl0Og0KDQpgYGB7cn0NCmhleHN0cjJjaGFycyA8LSBmdW5jdGlvbihoZXhzdHIpIHsNCiAgc3Vic3RyaW5nKGhleHN0ciwNCiAgICAgICAgICAgIHNlcSgxLCBuY2hhcihoZXhzdHIpIC0gMSwgMiksDQogICAgICAgICAgICBzZXEoMiwgbmNoYXIoaGV4c3RyKSwgMikpIHw+DQogIHN0cnRvaShiYXNlID0gMTYpIHw+DQogIGFzLnJhdygpIHw+DQogIHJhd1RvQ2hhcigpDQp9DQpgYGANCg0KDQpBbmQgYSB0ZXN0Og0KDQpgYGB7cn0NCmhleHN0cjJjaGFycygiNDg2NTZjNmM2ZiIpDQpgYGANCg0KTmV4dCwgSSB3YW50IHRvIHNoaWZ0IDAuLmYgdXAgdG8gYS4ucC4gSSdtIHN1cmUgdGhlcmUncyBhbiBlYXNpZXIgd2F5LiBIZXJlJ3MgaG93IEknbSBkb2luZyBpdC4gRmlyc3QsIHNldHVwIGEgbWFwcGluZzoNCg0KYGBge3J9DQpjaGFybWFwIDwtIGRhdGEuZnJhbWUoDQogIGhleCA9IGMoMDo5LCBsZXR0ZXJzWzE6Nl0pLA0KICBsZXQgPSBsZXR0ZXJzWzE6MTZdKQ0KY2hhcm1hcA0KYGBgDQoNClRoZXNlIHR3byBmdW5jdGlvbnMgdHJhbnNsYXRlIGJhY2sgYW5kIGZvcnRoIGJldHdlZW4gMC4uZiAoaGV4KSBhbmQgYS4ucCAoSSdtIGdvaW5nIHRvIGNhbGwgaXQgYSBsZXRoZXgpLCB1c2luZyBhIGBzdHJzcGxpdGAgYW5kIGBqb2luYDoNCg0KYGBge3J9DQpoZXgybGV0aGV4IDwtIGZ1bmN0aW9uKGhzdHIpIHsNCiAgZGF0YS5mcmFtZShoZXggPSAoaHN0ciB8PiBzdHJzcGxpdCgiIikpW1sxXV0pIHw+DQogICAgbGVmdF9qb2luKGNoYXJtYXAsIGJ5ID0gImhleCIpIHw+DQogICAgcHVsbChsZXQpIHw+DQogICAgcGFzdGUoY29sbGFwc2UgPSAiIikNCn0NCg0KbGV0aGV4MmhleCA8LSBmdW5jdGlvbihsZXRoZXgpIHsNCiAgZGF0YS5mcmFtZShsZXQgPSAobGV0aGV4IHw+IHN0cnNwbGl0KCIiKSlbWzFdXSkgfD4NCiAgICBsZWZ0X2pvaW4oY2hhcm1hcCwgYnkgPSAibGV0IikgfD4NCiAgICBwdWxsKGhleCkgfD4NCiAgICBwYXN0ZShjb2xsYXBzZSA9ICIiKQ0KfQ0KYGBgDQoNClRyeSBpdCB3aXRoICJIZWxsbyIuIEhlcmUncyB0aGUgaGV4IHN0cmluZzoNCg0KYGBge3J9DQpoZWxsb19oZXggPC0gY2hhcnMyaGV4c3RyKCJIZWxsbyIpDQpoZWxsb19oZXgNCmBgYA0KTm93IGFzIGEgbGV0aGV4Og0KDQpgYGB7cn0NCmhlbGxvX2xldGhleCA8LSBoZWxsb19oZXggfD4gaGV4MmxldGhleCgpDQpoZWxsb19sZXRoZXgNCmBgYA0KDQpBbmQgYmFjayBhZ2FpbjoNCg0KYGBge3J9DQpoZWxsb19sZXRoZXggfD4gbGV0aGV4MmhleCgpDQpgYGANCg0KRmluYWxseSwgYSBjb3VwbGUgb2Ygd3JhcHBlcnMsIHdoaWNoIHRha2UgYSBzdHJpbmcgdG8gYSBsZXRoZXggc3RyaW5nIGFuZCBiYWNrIGFnYWluOg0KDQpgYGB7cn0NCnN0cjJoYXNodGFnIDwtIGZ1bmN0aW9uKHMpIHsNCiAgcyB8PiBjaGFyczJoZXhzdHIoKSB8PiBoZXgybGV0aGV4KCkNCn0NCg0KaGFzaHRhZzJzdHIgPC0gZnVuY3Rpb24oaCkgew0KICBoIHw+IGxldGhleDJoZXgoKSB8PiBoZXhzdHIyY2hhcnMoKQ0KfQ0KYGBgDQoNCg0KYGBge3J9DQpzdHIyaGFzaHRhZygiSGVsbG8iKQ0KYGBgDQoNCmBgYHtyfQ0KaGFzaHRhZzJzdHIoImVpZ2ZnbWdtZ3AiKQ0KYGBgDQoNCkZpbmFsbHk6DQoNCmBgYHtyIGV2YWw9RkFMU0V9DQpoYXNodGFnMnN0cigiZ2loZWhlaGFoZGRrY3BjcGhoaGhoaGNvaGpncGhmaGVoZmdjZ2Zjb2dkZ3BnbmNwaGhnYmhlZ2RnaWRwaGdkbmdlZmJoaGRlaGhkamZoZ2hmaWdkZmIiKQ0KYGBgDQo=