Skip to content
Closed
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
Jump to file
Failed to load files.
Loading
Diff view
Diff view
55 changes: 55 additions & 0 deletions src/fountain_decoder.c
Original file line number Diff line number Diff line change
Expand Up @@ -1373,6 +1373,61 @@ fountain_decoder_estimated_percent_complete(fountain_decoder_t *decoder) {
return progress > 0.99 ? 0.99 : progress;
}

// Weighted-mixed-frames completion estimate. Ports SeedSigner's
// weight_mixed_frames=True method (helpers/ur2/fountain_decoder.py): count the
// fully decoded fragments, plus partial credit for fragments that are still
// only present inside mixed (XOR'd) frames. Each mixed frame contributes
// 1/(frames mixed) to every index it covers; each index's total contribution
// is capped at 0.75 so a not-yet-decoded fragment never counts as much as a
// decoded one (and so the reported percentage cannot decrease mid-decode).
// Backward-compatible addition — the reference estimate above is unchanged.
double fountain_decoder_estimated_percent_complete_weighted(
fountain_decoder_t *decoder) {
if (!decoder)
return 0.0;
if (fountain_decoder_is_complete(decoder))
return 1.0;
if (!decoder->expected_part_indexes ||
decoder->expected_part_indexes->count == 0)
return 0.0;

size_t parts = decoder->expected_part_indexes->count;

// Per-index partial scores from the mixed parts. Fragment indexes are in
// [0, seq_len) == [0, parts), so a scratch array keyed by index mirrors the
// Python dict `mixed_index_scoring`. Fall back to the reference estimate if
// the scratch allocation fails.
double *scoring = calloc(parts, sizeof(double));
if (!scoring)
return fountain_decoder_estimated_percent_complete(decoder);

mixed_parts_hash_t *hash = decoder->mixed_parts_hash;
if (hash) {
for (size_t b = 0; b < hash->capacity; b++) {
for (hash_entry_t *entry = hash->buckets[b]; entry; entry = entry->next) {
size_t cnt = entry->key.count;
if (cnt == 0)
continue;
double score = 1.0 / (double)cnt;
for (size_t k = 0; k < cnt; k++) {
size_t index = entry->key.indexes[k];
if (index < parts)
scoring[index] += score;
}
}
}
}

double mixed_score = 0.0;
for (size_t i = 0; i < parts; i++) {
mixed_score += scoring[i] < 0.75 ? scoring[i] : 0.75;
}
free(scoring);

double num_complete = (double)decoder->received_part_indexes.count;
return (num_complete + mixed_score) / (double)parts;
}

uint8_t *fountain_decoder_result_message(fountain_decoder_t *decoder) {
if (!decoder || !decoder->result)
return NULL;
Expand Down
9 changes: 9 additions & 0 deletions src/fountain_decoder.h
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,15 @@ size_t fountain_decoder_processed_parts_count(fountain_decoder_t *decoder);
*/
double fountain_decoder_estimated_percent_complete(fountain_decoder_t *decoder);

/**
* Get estimated completion percentage using the weighted-mixed-frames method
* (partial credit for fragments still only present inside mixed/XOR'd frames).
* @param decoder Pointer to fountain decoder
* @return Completion percentage (0.0 to 1.0)
*/
double fountain_decoder_estimated_percent_complete_weighted(
fountain_decoder_t *decoder);

/**
* Get result message
* @param decoder Pointer to fountain decoder
Expand Down
7 changes: 7 additions & 0 deletions src/ur_decoder.c
Original file line number Diff line number Diff line change
Expand Up @@ -458,6 +458,13 @@ double ur_decoder_estimated_percent_complete(ur_decoder_t *decoder) {
return fountain_decoder_estimated_percent_complete(decoder->fountain_decoder);
}

double ur_decoder_estimated_percent_complete_weighted(ur_decoder_t *decoder) {
if (!decoder || !decoder->fountain_decoder)
return 0.0;
return fountain_decoder_estimated_percent_complete_weighted(
decoder->fountain_decoder);
}

ur_decoder_error_t ur_decoder_get_last_error(ur_decoder_t *decoder) {
return decoder ? decoder->last_error : UR_DECODER_ERROR_NULL_POINTER;
}
Expand Down
7 changes: 7 additions & 0 deletions src/ur_decoder.h
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,13 @@ size_t ur_decoder_processed_parts_count(ur_decoder_t *decoder);
*/
double ur_decoder_estimated_percent_complete(ur_decoder_t *decoder);

/**
* Get estimated completion percentage using the weighted-mixed-frames method.
* @param decoder Pointer to URDecoder instance
* @return Completion percentage (0.0 to 1.0)
*/
double ur_decoder_estimated_percent_complete_weighted(ur_decoder_t *decoder);

/**
* Get last error
* @param decoder Pointer to URDecoder instance
Expand Down
28 changes: 22 additions & 6 deletions uUR.c
Original file line number Diff line number Diff line change
Expand Up @@ -295,18 +295,34 @@ static mp_obj_t ur_decoder_processed_parts_count_py(mp_obj_t self_in) {
static MP_DEFINE_CONST_FUN_OBJ_1(ur_decoder_processed_parts_count_obj,
ur_decoder_processed_parts_count_py);

// estimated_percent_complete method
static mp_obj_t ur_decoder_estimated_percent_complete_py(mp_obj_t self_in) {
mp_obj_ur_decoder_t *self = MP_OBJ_TO_PTR(self_in);
// estimated_percent_complete(weight_mixed_frames=False) method.
// weight_mixed_frames is an opt-in flag: the default (False) returns the
// original reference estimate byte-for-byte, so existing callers are
// unaffected; True selects the weighted-mixed-frames method, which gives
// partial credit for fragments still only present inside mixed/XOR'd frames.
static mp_obj_t ur_decoder_estimated_percent_complete_py(size_t n_args,
const mp_obj_t *pos_args,
mp_map_t *kw_args) {
static const mp_arg_t allowed_args[] = {
{MP_QSTR_weight_mixed_frames, MP_ARG_BOOL, {.u_bool = false}},
};
mp_arg_val_t parsed[MP_ARRAY_SIZE(allowed_args)];
mp_arg_parse_all(n_args - 1, pos_args + 1, kw_args,
MP_ARRAY_SIZE(allowed_args), allowed_args, parsed);

mp_obj_ur_decoder_t *self = MP_OBJ_TO_PTR(pos_args[0]);
if (!self->decoder) {
return mp_obj_new_float(0.0);
}

return mp_obj_new_float(ur_decoder_estimated_percent_complete(self->decoder));
double pct =
parsed[0].u_bool
? ur_decoder_estimated_percent_complete_weighted(self->decoder)
: ur_decoder_estimated_percent_complete(self->decoder);
return mp_obj_new_float(pct);
}
static MP_DEFINE_CONST_FUN_OBJ_1(ur_decoder_estimated_percent_complete_obj,
ur_decoder_estimated_percent_complete_py);
static MP_DEFINE_CONST_FUN_OBJ_KW(ur_decoder_estimated_percent_complete_obj, 1,
ur_decoder_estimated_percent_complete_py);

// URDecoder locals dict
static const mp_rom_map_elem_t ur_decoder_locals_dict_table[] = {
Expand Down
Loading