spent far too much time getting compile time code to work.

This commit is contained in:
Cheng 2022-06-12 17:38:26 +10:00
parent aab2dcbd6a
commit f6db345939
No known key found for this signature in database
GPG Key ID: D51301E176B31828
4 changed files with 129 additions and 40 deletions

View File

@ -25,11 +25,11 @@ namespace ro {
// copy constructor
sql(const sql& a) = delete;
// move constructor
sql(sql&& p) :std::unique_ptr<Icompiled_sql>(p.release()) { }
sql(sql&& p) noexcept :std::unique_ptr<Icompiled_sql>(p.release()) { }
// copy assignment
sql& operator=(const sql) = delete;
// Move assignment
sql& operator=(sql&& p) {
sql& operator=(sql&& p) noexcept {
std::unique_ptr<Icompiled_sql>::reset(p.release());
}
sql(Icompiled_sql* p) :std::unique_ptr<Icompiled_sql>(p) {}

View File

@ -14,18 +14,54 @@ namespace ro {
return id ^ (id >> 32);
}
class charmap :public std::array<char, 0x100> {
class charmap {
public:
std::array< char, 0x100> index{ 0, };
charmap() = delete;
constexpr charmap(const char* p, const char* q) {
constexpr charmap(const char * p, const char * q) {
while (unsigned int pu{ static_cast<unsigned char>(*p++) }) {
assert((*this)[pu] == 0);
(*this)[pu] = *q++;
assert(index[pu] == 0);
index[pu] = *q++;
}
assert(*(p - 1) == '\0' && *q == '\0');
assert(*(p - 1) == '\0' && *q == '\0');
/* when an instance of this class is declared constexpr,
an assert does not trigger a run time error,
because expression evaluated at compile time.
Instead the compiler reports that the expression
did not evaluate to a constant,
The error is confusing, because the error points to
the declaration where the initialization was invoked,
instead of pointing to the assert.
*/
}
};
class charmapWithFixup {
public:
std::array< char, 0x100> index{ 0, };
charmapWithFixup() = delete;
constexpr charmapWithFixup(const char* p, const char* q) {
while (unsigned int pu{ static_cast<unsigned char>(*p++) }) {
assert(index[pu] == 0);
index[pu] = *q++;
}
index['I'] = index['l']='0';
assert(*(p - 1) == '\0' && *q == '\0');
/* when an instance of this class is declared constexpr,
an assert does not trigger a run time error,
because expression evaluated at compile time.
Instead the compiler reports that the expression
did not evaluate to a constant,
The error is confusing, because the error points to
the declaration where the initialization was invoked,
instead of pointing to the assert.
*/
}
};
//template <> class base58<scalar> : public CompileSizedString<44> {};
static_assert(sizeof(base58<point>) == 46, "base58 point strings unexpected size");
@ -33,17 +69,18 @@ namespace ro {
static constexpr char index2MpirBase58[] { "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuv" };
void map_base_from_mpir58(char* p) {
static const charmap map(index2MpirBase58, index2cryptoBase58);
while (unsigned int pu{ static_cast<unsigned char>(*p) }) {
*p++ = map[pu];
static constexpr charmap map(index2MpirBase58, index2cryptoBase58);
while (uint8_t pu{ static_cast<uint8_t>(*p) }) {
*p++ = map.index[pu];
}
}
void map_base_to_mpir58(const char* p, char* q, size_t count) {
static const charmap map(index2cryptoBase58, index2MpirBase58);
static constexpr charmap map(index2cryptoBase58, index2MpirBase58);
static_assert(map.index[0xF0] == 0);
while (count--) {
unsigned int pu{ static_cast<unsigned char>(*p++) };
char c{ map[pu] };
char c{ map.index[pu] };
if (c == '\0') throw NotBase58Exception();
*q++ = c;
}

View File

@ -15,11 +15,30 @@
static constexpr uint8_t index2base64[]{
"0123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnpqrstuvwxyz!$*+-_"
};
/*
control characters are stops
! $ * + - permitted The characters "#%&'(),. are stops
0-9 permitted The characters :;<=>? are stops
A-Z and _ permitted, @ and [\]^ are stops
a-z permitted. {|} ~` are stops, as is the mysterious control character 0x7F (del)
*/
// Unlike regular baseINV, and incompatible with it. Intended to encode stuff small enough
// that it might be human typed and human transmitted, therefore maps 'o' and 'O' to '0', 'I' and
// 'l' to '1'
// uses six url safe additional characters !$*+-_ to bring it up to six bits
//
// But on reflection, useless, since human typed stuff like this should use Bitcoin's base 58 encoding
// So going to switch to regular base64, despite the unreasonably immense amount of work I put into it.
// Unfortunately, Wireguard, with which I am going to need to interoperate, uses RFC4648, whose
// algorithm is fundamentally different - no special treatment for I, O, o, and l, and uses =
// to handle the case where you have boundary problems between eight and six bit groups.
// They force everything to four character groups, using an = sign to indicate that the
// bytes being represented stop before a multiple of three. https://www.base64encode.org
static_assert(index2base64[63] == '_', "surprise numeral at 63");
// Being intended for small bits of data, assumes no whitespace within an entity
@ -35,41 +54,71 @@ static_assert(index2base64[63] == '_', "surprise numeral at 63");
// will return a number of bits less than the size of the input bit buffer, less than the bitcount
// it was given.
// Table to convert ascii to six bit.
static constexpr std::array<constexpr uint8_t, 0x100> ascii2six_ar{
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, //control characters
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, //control characters
0xff, 0x3a, 0xff, 0xff, 0x3b, 0xff, 0xff, 0xff, 0xff, 0xff, 0x3c, 0x3d, 0xff, 0x3e, 0xff, 0xff, // ! $ * + - permitted The characters "#%&'(),. are stops
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, // 0-9 permitted The characters :;<=>? are stops
0xff, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x01, 0x12, 0x13, 0x14, 0x15, 0x16, 0x00,
0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0xff, 0xff, 0xff, 0xff, 0x3f, // A-Z and _ permitted, @ and [\]^ are stops
0xff, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x01, 0x2d, 0x2e, 0x00,
0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0xff, 0xff, 0xff, 0xff, 0xff, //a-z the characters {|} ~` are stops, as is the mysterious control character 0x7F (del)
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff // utf8 multibyte characters, all stops.
};
// Table to convert ascii to six bit as a good old fashioned non owning naked pointer to const,
// whose storage is owned by a const static which exists until the program terminates.
static const uint8_t *ascii2six{ &ascii2six_ar[0] };
// Compile time execution is C++ is a pain, because expressions are apt to unpredictably lose
// their constexpr character for reasons that are far from clear.
//
// You can declare anything reasonable to be constexpr, and the compiler will not issue an
// error message until the code that attempts to use what you have declared constexpr is
// invoked from somewhere else "expression does not evaluate to constant"
//
// an assert in an expression evaluated at compile time does not trigger a run time error,
// Instead the compiler reports that the expression did not evaluate to a constant,
//
// The error is confusing, because the error points to the declaration where the initialization
// was invoked,instead of pointing to the assert.
// To debug code intended to be run at compile time, exercise it at run time with
// auto ptr(std::make_unique<Class>());
// at run time;
class charindex {
public:
std::array< uint8_t, 0x100> index{ 0, };
// this non const array will become constexpr and be constructed at compile time
// when an instance of this class is created in a constexpr expression.
charindex() = delete;
constexpr charindex(const uint8_t* p) {
uint8_t pu{ 0 };
do { index[pu++] = 0xFF; } while (pu);
uint8_t i{ 0 };
while (pu = static_cast<uint8_t>(p[i])) {
index[pu] = i;
i++;
assert(i != 0); //prevents unending execution,
// inside a constexp, generates an "expression does not evaluate to constant"
// error at compile time, rather than breaking at run time.
}
index['o'] = index['O'] = 0;
index['l'] = index['I'] = 1;
}
};
static constexpr charindex ascii2six_ar(index2base64);
//
//
// You really have to write compile time code in templates as a language, which is the totally
// obscure and hard to use language apt to generate remarkably voluminous error messages
// will little obvious connection to the actual problem, and surprising result that are ver
// difficult to predict in advance or understand at all.
// In general, the better solution is to have a routine that is called once and only once at the
// beginning of the program, which initializes a bunch of static const values, if that solution is
// adequate, or to have a preproces routine written in python which generates the required C
// files and header files.
// After this experiment in compile time code, I swear off it.
// Table to convert ascii to six bit as a good old fashioned non owning naked pointer to const,
// whose storage is owned by a const static which exists until the program terminates.
const uint8_t* const ascii2six{ &ascii2six_ar.index[0] };
void ascii2test() {
for (unsigned int i{ 0 }; i < 0x100;i++) {
char v = i;
unsigned int j = ascii2six[i];
char w = index2base64[j];
if (j < 64) {
assert(v == w || v == 'I' || v == 'l' || v == 'o' || v == 'O');
}
}
}
// Decode does not have an input span of encoded characters, but a char *, because it assumes
// the string is always terminated by an invalid character, such as the trailing null at the end of

View File

@ -21,6 +21,8 @@ Namespace testbed is only defined in this cpp file, hence nothing within
If it needs to interact with the outside world, should post a message
analogously to queue_error_message, which then calls back to a
routine in this file.*/
void ascii2test();
extern const uint8_t* const ascii2six;
namespace testbed {
using ristretto255::hash, ristretto255::hsh, ristretto255::scalar,
@ -49,5 +51,6 @@ If using a dialog, exceptions within the dialog will result in an error message
void testbed() {
// queue_error_message("hello world");
ascii2test();
}
}