Skip to content

Commit

Permalink
factor out some types and constants
Browse files Browse the repository at this point in the history
* BECH32_CHECKSUM_SIZE = 6
* bech32_checksum_t = uint_fast32_t
* bech32_constant_t = uint_least32_t
  • Loading branch information
whitslack committed Mar 18, 2024
1 parent c305b31 commit 9db7c45
Show file tree
Hide file tree
Showing 4 changed files with 36 additions and 32 deletions.
4 changes: 2 additions & 2 deletions bech32.c
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@ int main(int argc, char *argv[]) {

unsigned char in[BECH32_MAX_SIZE];
size_t n_in = 0, nmax_in = decode ? BECH32_MAX_SIZE :
(BECH32_MAX_SIZE - n_hrp - 1/*separator*/ - (version >= 0) - 6/*checksum*/) * 5 / CHAR_BIT;
(BECH32_MAX_SIZE - n_hrp - 1/*separator*/ - (version >= 0) - BECH32_CHECKSUM_SIZE) * 5 / CHAR_BIT;
if (decode || hex) {
for (int c;;) {
if (decode ? (c = getchar()) < 0 || c == '\n' : (c = gethex()) < 0) {
Expand Down Expand Up @@ -190,7 +190,7 @@ int main(int argc, char *argv[]) {
errx(EX_DATAERR, errmsg((enum bech32_error) ret));
}
else {
n_out = n_hrp + 1/*separator*/ + (version >= 0) + (n_in * CHAR_BIT + 4) / 5 + 6/*checksum*/;
n_out = n_hrp + 1/*separator*/ + (version >= 0) + (n_in * CHAR_BIT + 4) / 5 + BECH32_CHECKSUM_SIZE;
assert(n_out <= sizeof out);
ssize_t ret;
struct bech32_encoder_state state;
Expand Down
28 changes: 16 additions & 12 deletions bech32.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,19 +12,23 @@
extern "C" {
#endif

static const uint_least32_t BECH32M_CONST = UINT32_C(0x2bc830a3);
typedef uint_fast32_t bech32_checksum_t;
typedef uint_least32_t bech32_constant_t;

static const bech32_constant_t BECH32M_CONST = UINT32_C(0x2bc830a3);

static const size_t
BECH32_CHECKSUM_SIZE = 6,
BECH32_HRP_MIN_SIZE = 1,
BECH32_HRP_MAX_SIZE = 83,
BECH32_MIN_SIZE = BECH32_HRP_MIN_SIZE + 1/*separator*/ + 6/*checksum*/,
BECH32_MAX_SIZE = 90,
BECH32_HRP_MAX_SIZE = BECH32_MAX_SIZE - 1/*separator*/ - BECH32_CHECKSUM_SIZE,
BECH32_MIN_SIZE = BECH32_HRP_MIN_SIZE + 1/*separator*/ + BECH32_CHECKSUM_SIZE,
WITNESS_PROGRAM_MIN_SIZE = 2,
WITNESS_PROGRAM_MAX_SIZE = 40,
WITNESS_PROGRAM_PKH_SIZE = 20,
WITNESS_PROGRAM_SH_SIZE = 32,
SEGWIT_ADDRESS_MIN_SIZE = BECH32_HRP_MIN_SIZE + 1/*separator*/ + 1/*version*/ +
((WITNESS_PROGRAM_MIN_SIZE * CHAR_BIT + 4) / 5) + 6/*checksum*/;
((WITNESS_PROGRAM_MIN_SIZE * CHAR_BIT + 4) / 5) + BECH32_CHECKSUM_SIZE;

static const unsigned WITNESS_MAX_VERSION = 16;

Expand Down Expand Up @@ -76,12 +80,12 @@ struct bech32_encoder_state {
*
* Only the #nbits least significant bits of this field are valid.
*/
uint_fast32_t bits;
bech32_checksum_t bits;

/**
* @brief The intermediate checksum state.
*/
uint_fast32_t chk;
bech32_checksum_t chk;

};

Expand Down Expand Up @@ -150,7 +154,7 @@ enum bech32_error bech32_encode_data(
*/
enum bech32_error bech32_encode_finish(
struct bech32_encoder_state *restrict state,
uint_least32_t constant)
bech32_constant_t constant)
__attribute__ ((__access__ (read_write, 1), __nonnull__, __nothrow__, __warn_unused_result__));


Expand Down Expand Up @@ -180,12 +184,12 @@ struct bech32_decoder_state {
*
* Only the #nbits least significant bits of this field are valid.
*/
uint_fast32_t bits;
bech32_checksum_t bits;

/**
* @brief The intermediate checksum state.
*/
uint_fast32_t chk;
bech32_checksum_t chk;

};

Expand Down Expand Up @@ -253,7 +257,7 @@ enum bech32_error bech32_decode_data(
*/
ssize_t bech32_decode_finish(
struct bech32_decoder_state *restrict state,
uint_least32_t constant)
bech32_constant_t constant)
__attribute__ ((__access__ (read_write, 1), __nonnull__, __nothrow__, __warn_unused_result__));


Expand Down Expand Up @@ -378,7 +382,7 @@ class Encoder {

void write(const void *in, size_t nbits_in);

std::string finish(uint_least32_t constant = BECH32M_CONST);
std::string finish(bech32_constant_t constant = BECH32M_CONST);

};

Expand Down Expand Up @@ -415,7 +419,7 @@ class Decoder {
return this->read(this->bits_remaining() & ~static_cast<size_t>(CHAR_BIT - 1));
}

size_t finish(uint_least32_t constant = BECH32M_CONST);
size_t finish(bech32_constant_t constant = BECH32M_CONST);

};

Expand Down
30 changes: 15 additions & 15 deletions libbech32.c
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
#define _pure __attribute__ ((__pure__))


static inline uint_fast32_t _const polymod(uint_fast32_t chk) {
static inline bech32_checksum_t _const polymod(bech32_checksum_t chk) {
static const uint_least32_t LUT[32] = {
#define _(i) ( \
(((i) & 1 << 0) ? UINT32_C(0x3b6a57b2) : 0) ^ \
Expand All @@ -28,7 +28,7 @@ static inline uint_fast32_t _const polymod(uint_fast32_t chk) {
return (chk & UINT32_C(0x1FFFFFF)) << 5 ^ LUT[chk >> 25];
}

static inline uint_fast32_t _pure polymod_hrp(uint_fast32_t chk, const char *hrp, size_t n_hrp) {
static inline bech32_checksum_t _pure polymod_hrp(bech32_checksum_t chk, const char *hrp, size_t n_hrp) {
for (size_t i = 0; i < n_hrp; ++i)
chk = polymod(chk) ^ (hrp[i] >> 5 | (hrp[i] >= 'A' && hrp[i] <= 'Z'));
chk = polymod(chk);
Expand Down Expand Up @@ -56,7 +56,7 @@ static inline bool _pure is_mixed_case(const char *in, size_t n_in) {
size_t bech32_encoded_size(size_t n_hrp, size_t nbits_in, size_t n_pad) {
size_t n_out;
if (_unlikely(__builtin_uaddl_overflow(nbits_in, 4, &nbits_in) ||
__builtin_uaddl_overflow(n_hrp, 1/*separator*/ + nbits_in / 5 + 6/*checksum*/, &n_out) ||
__builtin_uaddl_overflow(n_hrp, 1/*separator*/ + nbits_in / 5 + BECH32_CHECKSUM_SIZE, &n_out) ||
__builtin_uaddl_overflow(n_out, n_pad, &n_out)))
return SIZE_MAX;
return n_out;
Expand All @@ -68,7 +68,7 @@ static void encode(struct bech32_encoder_state *restrict state) {
's', '3', 'j', 'n', '5', '4', 'k', 'h', 'c', 'e', '6', 'm', 'u', 'a', '7', 'l'
};
while (state->nbits >= 5) {
uint_fast32_t v = state->bits >> (state->nbits -= 5) & 0x1F;
bech32_checksum_t v = state->bits >> (state->nbits -= 5) & 0x1F;
state->chk = polymod(state->chk) ^ v;
*state->out++ = ENCODE[v], --state->n_out;
}
Expand All @@ -82,7 +82,7 @@ enum bech32_error bech32_encode_begin(struct bech32_encoder_state *restrict stat
for (size_t i = 0; i < n_hrp; ++i)
if (_unlikely(hrp[i] < 0x21 || hrp[i] >= 0x7F))
return BECH32_HRP_ILLEGAL_CHAR;
if (_unlikely(__builtin_usubl_overflow(n_out, n_hrp, &n_out) || n_out < 1/*separator*/ + 6/*checksum*/))
if (_unlikely(__builtin_usubl_overflow(n_out, n_hrp, &n_out) || n_out < 1/*separator*/ + BECH32_CHECKSUM_SIZE))
return BECH32_BUFFER_INADEQUATE;
for (size_t i = 0; i < n_hrp; ++i)
out[i] = hrp[i] | (hrp[i] >= 'A' && hrp[i] <= 'Z' ? 0x20 : 0);
Expand All @@ -109,17 +109,17 @@ enum bech32_error bech32_encode_data(struct bech32_encoder_state *restrict state
}
}

enum bech32_error bech32_encode_finish(struct bech32_encoder_state *restrict state, uint_least32_t constant) {
if (_unlikely(state->n_out < !!state->nbits + 6/*checksum*/))
enum bech32_error bech32_encode_finish(struct bech32_encoder_state *restrict state, bech32_constant_t constant) {
if (_unlikely(state->n_out < !!state->nbits + BECH32_CHECKSUM_SIZE))
return BECH32_BUFFER_INADEQUATE;
if (state->nbits) {
state->bits <<= 5 - state->nbits, state->nbits = 5;
encode(state);
}
state->bits = state->chk;
for (int i = 0; i < 6; ++i)
for (size_t i = 0; i < BECH32_CHECKSUM_SIZE; ++i)
state->bits = polymod(state->bits);
state->bits ^= constant, state->nbits = 30;
state->bits ^= constant, state->nbits = BECH32_CHECKSUM_SIZE * 5;
encode(state);
if (_unlikely(state->chk != constant))
return BECH32_CHECKSUM_FAILURE;
Expand Down Expand Up @@ -169,7 +169,7 @@ ssize_t bech32_decode_begin(struct bech32_decoder_state *restrict state, const c
}
if (_unlikely(is_mixed_case(in, n_in)))
return BECH32_MIXED_CASE;
if (_unlikely(__builtin_usubl_overflow(n_in, n_hrp + 1/*separator*/ + 6/*checksum*/, &n_in)))
if (_unlikely(__builtin_usubl_overflow(n_in, n_hrp + 1/*separator*/ + BECH32_CHECKSUM_SIZE, &n_in)))
return BECH32_TOO_SHORT;
state->in = in + n_hrp + 1/*separator*/, state->n_in = n_in;
state->nbits = 0;
Expand All @@ -193,12 +193,12 @@ enum bech32_error bech32_decode_data(struct bech32_decoder_state *restrict state
return 0;
}

ssize_t bech32_decode_finish(struct bech32_decoder_state *restrict state, uint_least32_t constant) {
ssize_t bech32_decode_finish(struct bech32_decoder_state *restrict state, bech32_constant_t constant) {
ssize_t nbits_pad = state->nbits;
if (_unlikely(state->n_in || nbits_pad && (state->bits & (1 << nbits_pad) - 1)))
return BECH32_PADDING_ERROR;
state->n_in = 6, state->nbits = 0;
if (_unlikely(!decode(state, 30)))
state->n_in = BECH32_CHECKSUM_SIZE, state->nbits = 0;
if (_unlikely(!decode(state, BECH32_CHECKSUM_SIZE * 5)))
return BECH32_ILLEGAL_CHAR;
state->nbits = 0;
if (_unlikely(state->chk != constant || state->n_in))
Expand All @@ -216,7 +216,7 @@ ssize_t segwit_address_encode(char *restrict address, size_t n_address, const un
return SEGWIT_VERSION_ILLEGAL;
if (version == 0 && _unlikely(!(n_program == WITNESS_PROGRAM_PKH_SIZE || n_program == WITNESS_PROGRAM_SH_SIZE)))
return SEGWIT_PROGRAM_ILLEGAL_SIZE;
size_t n_actual = n_hrp + 1/*separator*/ + 1/*version*/ + (n_program * CHAR_BIT + 4) / 5 + 6/*checksum*/;
size_t n_actual = n_hrp + 1/*separator*/ + 1/*version*/ + (n_program * CHAR_BIT + 4) / 5 + BECH32_CHECKSUM_SIZE;
if (_unlikely(n_address < n_actual + 1/*null terminator*/))
return BECH32_BUFFER_INADEQUATE;
enum bech32_error error;
Expand All @@ -238,7 +238,7 @@ ssize_t segwit_address_decode(unsigned char *restrict program, size_t n_program,
struct bech32_decoder_state state;
if (_unlikely((ret = bech32_decode_begin(&state, address, n_address)) < 0))
return ret;
size_t n_actual = (n_address - ret/*hrp*/ - 1/*separator*/ - 1/*version*/ - 6/*checksum*/) * 5 / CHAR_BIT;
size_t n_actual = (n_address - ret/*hrp*/ - 1/*separator*/ - 1/*version*/ - BECH32_CHECKSUM_SIZE) * 5 / CHAR_BIT;
if (_unlikely(n_actual < WITNESS_PROGRAM_MIN_SIZE))
return SEGWIT_PROGRAM_TOO_SHORT;
if (_unlikely(n_actual > WITNESS_PROGRAM_MAX_SIZE))
Expand Down
6 changes: 3 additions & 3 deletions libbech32_c++.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ void Encoder::write(const void *in, size_t nbits_in) {
out.resize(state.out - out.data());
}

std::string Encoder::finish(uint_least32_t constant) {
std::string Encoder::finish(bech32_constant_t constant) {
size_t written = out.size();
out.resize(written + (state.n_out = ::bech32_encoded_size(0, state.nbits, 0) - 1));
state.out = out.data() + written;
Expand Down Expand Up @@ -94,7 +94,7 @@ std::vector<std::byte> Decoder::read(size_t nbits) {
return out;
}

size_t Decoder::finish(uint_least32_t constant) {
size_t Decoder::finish(bech32_constant_t constant) {
if (auto ret = ::bech32_decode_finish(&state, constant); ret < 0)
throw Error(static_cast<enum ::bech32_error>(ret));
else
Expand All @@ -115,7 +115,7 @@ std::string encode_segwit_address(const void *program, size_t n_program, std::st
std::tuple<std::vector<std::byte>, std::string_view, unsigned> decode_segwit_address(std::string_view address) {
std::tuple<std::vector<std::byte>, std::string_view, unsigned> ret;
auto &[program, hrp, version] = ret;
program.resize((address.size() - 1/*hrp*/ - 1/*separator*/ - 6/*checksum*/) * 5 / CHAR_BIT);
program.resize((address.size() - 1/*hrp*/ - 1/*separator*/ - BECH32_CHECKSUM_SIZE) * 5 / CHAR_BIT);
size_t n_hrp;
if (auto ret = ::segwit_address_decode(reinterpret_cast<unsigned char *>(program.data()), program.size(), address.data(), address.size(), &n_hrp, &version); ret < 0)
throw Error(static_cast<enum ::bech32_error>(ret));
Expand Down

0 comments on commit 9db7c45

Please sign in to comment.