-
-
Notifications
You must be signed in to change notification settings - Fork 24
Expand file tree
/
Copy pathbytewords.cpp
More file actions
174 lines (151 loc) · 5.78 KB
/
bytewords.cpp
File metadata and controls
174 lines (151 loc) · 5.78 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
//
// bytewords.cpp
//
// Copyright © 2020 by Blockchain Commons, LLC
// Licensed under the "BSD-2-Clause Plus Patent License"
//
#include "bytewords.hpp"
#include "utils.hpp"
#include <stdexcept>
#include <algorithm>
#include <iterator>
namespace ur {
using namespace std;
static const char* bytewords = "ableacidalsoapexaquaarchatomauntawayaxisbackbaldbarnbeltbetabiasbluebodybragbrewbulbbuzzcalmcashcatschefcityclawcodecolacookcostcruxcurlcuspcyandarkdatadaysdelidicedietdoordowndrawdropdrumdulldutyeacheasyechoedgeepicevenexamexiteyesfactfairfernfigsfilmfishfizzflapflewfluxfoxyfreefrogfuelfundgalagamegeargemsgiftgirlglowgoodgraygrimgurugushgyrohalfhanghardhawkheathelphighhillholyhopehornhutsicedideaidleinchinkyintoirisironitemjadejazzjoinjoltjowljudojugsjumpjunkjurykeepkenokeptkeyskickkilnkingkitekiwiknoblamblavalazyleaflegsliarlimplionlistlogoloudloveluaulucklungmainmanymathmazememomenumeowmildmintmissmonknailnavyneednewsnextnoonnotenumbobeyoboeomitonyxopenovalowlspaidpartpeckplaypluspoempoolposepuffpumapurrquadquizraceramprealredorichroadrockroofrubyruinrunsrustsafesagascarsetssilkskewslotsoapsolosongstubsurfswantacotasktaxitenttiedtimetinytoiltombtoystriptunatwinuglyundouniturgeuservastveryvetovialvibeviewvisavoidvowswallwandwarmwaspwavewaxywebswhatwhenwhizwolfworkyankyawnyellyogayurtzapszerozestzinczonezoom";
uint8_t decode_word(const string& word, size_t word_len) {
if(word.length() != word_len) {
return 0; //throw runtime_error("Invalid Bytewords.");
}
static int16_t* array = NULL;
const size_t dim = 26;
// Since the first and last letters of each Byteword are unique,
// we can use them as indexes into a two-dimensional lookup table.
// This table is generated lazily.
if(array == NULL) {
const size_t array_len = dim * dim;
array = (int16_t*)malloc(array_len * sizeof(int16_t));
for(size_t i = 0; i < array_len; i++) {
array[i] = -1;
}
for(size_t i = 0; i < 256; i++) {
const char* byteword = bytewords + i * 4;
size_t x = byteword[0] - 'a';
size_t y = byteword[3] - 'a';
size_t offset = y * dim + x;
array[offset] = i;
}
}
// If the coordinates generated by the first and last letters are out of bounds,
// or the lookup table contains -1 at the coordinates, then the word is not valid.
int x = tolower(word[0]) - 'a';
int y = tolower(word[word_len == 4 ? 3 : 1]) - 'a';
if(!(0 <= x && x < dim && 0 <= y && y < dim)) {
return 0; //throw runtime_error("Invalid Bytewords.");
}
size_t offset = y * dim + x;
int16_t value = array[offset];
if(value == -1) {
return 0; //throw runtime_error("Invalid Bytewords.");
}
// If we're decoding a full four-letter word, verify that the two middle letters are correct.
if(word_len == 4) {
const char* byteword = bytewords + value * 4;
int c1 = tolower(word[1]);
int c2 = tolower(word[2]);
if(c1 != byteword[1] || c2 != byteword[2]) {
return 0; //throw runtime_error("Invalid Bytewords.");
}
}
// Successful decode.
return value;
}
static const string get_word(uint8_t index) {
auto p = &bytewords[index * 4];
return string(p, p + 4);
}
static const string get_minimal_word(uint8_t index) {
string word;
word.reserve(2);
auto p = &bytewords[index * 4];
word.push_back(*p);
word.push_back(*(p + 3));
return word;
}
static const string encode(const ByteVector& buf, const string& separator) {
auto len = buf.size();
StringVector words;
words.reserve(len);
for(int i = 0; i < len; i++) {
auto byte = buf[i];
words.push_back(get_word(byte));
}
return join(words, separator);
}
static const ByteVector add_crc(const ByteVector& buf) {
auto crc_buf = crc32_bytes(buf);
auto result = buf;
append(result, crc_buf);
return result;
}
static const string encode_with_separator(const ByteVector& buf, const string& separator) {
auto crc_buf = add_crc(buf);
return encode(crc_buf, separator);
}
static const string encode_minimal(const ByteVector& buf) {
string result;
auto crc_buf = add_crc(buf);
auto len = crc_buf.size();
for(int i = 0; i < len; i++) {
auto byte = crc_buf[i];
result.append(get_minimal_word(byte));
}
return result;
}
static const ByteVector _decode(const string& s, char separator, size_t word_len) {
StringVector words;
if(word_len == 4) {
words = split(s, separator);
} else {
words = partition(s, 2);
}
ByteVector buf;
transform(words.begin(), words.end(), back_inserter(buf), [&](auto word) { return decode_word(word, word_len); });
if(buf.size() < 5) {
return ByteVector(); //throw runtime_error("Invalid Bytewords.");
}
auto p = split(buf, buf.size() - 4);
auto body = p.first;
auto body_checksum = p.second;
auto checksum = crc32_bytes(body);
if(checksum != body_checksum) {
return ByteVector(); //throw runtime_error("Invalid Bytewords.");
}
return body;
}
string Bytewords::encode(style style, const ByteVector& bytes) {
switch(style) {
case standard:
return encode_with_separator(bytes, " ");
case uri:
return encode_with_separator(bytes, "-");
case minimal:
return encode_minimal(bytes);
default:
;//assert(false);
}
return string();
}
ByteVector Bytewords::decode(style style, const string& string) {
switch(style) {
case standard:
return _decode(string, ' ', 4);
case uri:
return _decode(string, '-', 4);
case minimal:
return _decode(string, 0, 2);
default:
assert(false);
}
return ByteVector();
}
}