diff --git a/upb/mini_table/message.c b/upb/mini_table/message.c index 5984a6c45de0..2ef0acedb822 100644 --- a/upb/mini_table/message.c +++ b/upb/mini_table/message.c @@ -27,21 +27,29 @@ const upb_MiniTableField* upb_MiniTable_FindFieldByNumber( } // Slow case: binary search - int lo = m->UPB_PRIVATE(dense_below); - int hi = m->UPB_PRIVATE(field_count) - 1; + uint32_t lo = m->UPB_PRIVATE(dense_below); + uint32_t hi = m->UPB_PRIVATE(field_count) - 1; + const upb_MiniTableField* base = m->UPB_PRIVATE(fields); while (lo <= hi) { - int mid = (lo + hi) / 2; - uint32_t num = m->UPB_PRIVATE(fields)[mid].UPB_PRIVATE(number); - if (num < number) { - lo = mid + 1; - continue; + uint32_t mid = (hi + lo) / 2; + uint32_t num = base[mid].UPB_ONLYBITS(number); + // These comparison operations allow, on ARM machines, to fuse all these + // branches into one comparison followed by two CSELs to set the lo/hi + // values, followed by a BNE to continue or terminate the loop. Since binary + // search branches are generally unpredictable (50/50 in each direction), + // this is a good deal. + uint32_t hi_mid = mid - 1; + uint32_t lo_mid = mid + 1; + if (num == number) { + return &base[mid]; } - if (num > number) { - hi = mid - 1; - continue; + if (num < number) { + lo = lo_mid; + } else { + hi = hi_mid; } - return &m->UPB_PRIVATE(fields)[mid]; } + return NULL; }