// Decode unsigned integer values in any base between 10 and 36. int decode(uint64_t value, char* const rc, int radix) { int rev = DECODE_BUF_LEN; if (radix < MIN_RADIX || radix > MAX_RADIX) radix = MIN_RADIX; if (radix == 10) // go faster for this option { while (true) { rc[rev--] = static_cast<unsigned char>(value % 10) + '0'; value /= 10; if (!value) break; } } else { while (true) { int temp = static_cast<int>(value % radix); rc[rev--] = static_cast<unsigned char>(temp < 10 ? temp + '0' : temp - 10 + 'A'); value /= radix; if (!value) break; } } return adjust_prefix(radix, rev, false, rc); }
// Decode signed integer values in any base between 10 and 36. int decode(int64_t value, char* const rc, int radix) { if (value >= 0) return decode(static_cast<uint64_t>(value), rc, radix); int rev = DECODE_BUF_LEN; if (radix < MIN_RADIX || radix > MAX_RADIX) radix = MIN_RADIX; // The remainder with negative values is not consistent across compilers. if (radix == 10) // go faster for this option { while (true) { int64_t temp = (value / 10) * 10 - value; rc[rev--] = static_cast<unsigned char>(temp) + '0'; value /= 10; if (!value) break; } } else { while (true) { int64_t temp = (value / radix) * radix - value; rc[rev--] = static_cast<unsigned char>(temp < 10 ? temp + '0' : temp - 10 + 'A'); value /= radix; if (!value) break; } } return adjust_prefix(radix, rev, true, rc); }
/*! * Perform a recursive length prefix update on all messages containing the * given part and possibly the part as such up to its origin. * * This is where all the magic happens. This function takes a binary stream and * reads data from it, until it reaches a length-prefixed field. Messages are * always length-prefixed, so binary parts that are not can be safely skipped. * If the binary stream's current offset matches the tag offset of the part * exactly, and the part is empty, we ran into an initialization update, so we * can directly abort here. * * Otherwise, if the binary part contains the part, and thus does not match * exactly, we must have found a submessage and need to recurse. The length * prefixes must be updated from within, since they are encoded as * variable-sized integers and may also affect the size of parent messages. * * After recursing, it is mandatory to check if the part needs to be * realigned, since there may have been updates on the length prefixes of * submessages that affect the offsets of the part, and perform such a * realignment if necessary. * * Finally, we write the new length prefix and adjust the delta value if the * new length prefix exceeds the length of the current one. That easy. * * \warning The lines excluded from code coverage are impossible to occur, * but they are necessary to ensure application-wide proper error handling. * * \param[in,out] part Part * \param[in,out] stream Binary stream * \param[in,out] delta Delta * \return Error code */ static pb_error_t adjust_recursive( pb_part_t *part, pb_binary_stream_t *stream, ptrdiff_t *delta) { assert(part && stream && delta && *delta); assert(pb_part_aligned(part)); pb_error_t error = PB_ERROR_NONE; /* Read from the binary stream until the end of the part */ while (pb_binary_stream_offset(stream) < part->offset.end) { if (pb_binary_stream_offset(stream) == part->offset.start - *delta && pb_part_empty(part)) break; /* Skip non-length-prefixed fields */ pb_tag_t tag = 0; uint32_t bytes; size_t offset = pb_binary_stream_offset(stream); if ((error = pb_binary_stream_read_varint32(stream, &tag))) break; /* LCOV_EXCL_LINE */ if ((tag & 7) != PB_WIRETYPE_LENGTH) { if (!skip_jump[tag & 7] || (error = skip_jump[tag & 7](stream))) break; /* LCOV_EXCL_LINE */ continue; } /* Create a temporary part, so we can use adjust_prefix() */ pb_part_t temp = { .binary = pb_part_binary(part), .version = pb_part_version(part), .offset = { .start = 0, .end = 0, .diff = { .origin = offset, .tag = offset, .length = pb_binary_stream_offset(stream) } } }; /* Read length from length-prefixed field */ if ((error = pb_binary_stream_read_varint32(stream, &bytes))) break; /* LCOV_EXCL_LINE */ /* Update offsets */ temp.offset.start = pb_binary_stream_offset(stream); temp.offset.end = temp.offset.start + bytes; temp.offset.diff.origin -= temp.offset.start; temp.offset.diff.tag -= temp.offset.start; temp.offset.diff.length -= temp.offset.start; /* Abort, if we're past the origin */ if (temp.offset.start > part->offset.start + part->offset.diff.origin) break; /* The temporary part lies within the part */ if (temp.offset.start <= part->offset.start && temp.offset.end >= part->offset.end - *delta) { temp.offset.end += *delta; /* The parts don't match, so recurse */ if (temp.offset.start != part->offset.start || temp.offset.end != part->offset.end) { pb_binary_stream_t substream = pb_binary_stream_create_at( pb_binary_stream_binary(stream), temp.offset.start); error = adjust_recursive(part, &substream, delta); pb_binary_stream_destroy(&substream); if (unlikely_(error)) break; /* LCOV_EXCL_LINE */ /* Parts may be unaligned due to length prefix update */ if ((!pb_part_aligned(part) && (error = pb_part_align(part))) || (!pb_part_aligned(&temp) && (error = pb_part_align(&temp)))) break; /* LCOV_EXCL_LINE */ } /* Adjust length prefix */ error = adjust_prefix(&temp, delta); break; /* Otherwise just skip binary part */ } else if ((error = pb_binary_stream_skip(stream, bytes))) { break; /* LCOV_EXCL_LINE */ } } /* Invalidate part on error */ if (unlikely_(error)) pb_part_invalidate(part); /* LCOV_EXCL_LINE */ return error; }