Commit ba9034b9 authored by Jon Maltiel Swenson's avatar Jon Maltiel Swenson Committed by Facebook Github Bot

Add non-throwing alternative to decodeVarint

Summary:
In mcrouter code, we would like a non-throwing alternative to `decodeVarint()`. There
are real, expected scenarios where only part of a serialized varint fits into a packet,
in which case mcrouter fails to decode the varint and throws.  In these scenarios,
throwing too many exceptions can lead to lock contention and degrade performance.

Reviewed By: yfeldblum

Differential Revision: D5153823

fbshipit-source-id: 138273af832903f0b04bee0bcacddd66b4274129
parent da541af3
...@@ -17,7 +17,10 @@ ...@@ -17,7 +17,10 @@
#pragma once #pragma once
#include <type_traits> #include <type_traits>
#include <folly/Conv.h> #include <folly/Conv.h>
#include <folly/Expected.h>
#include <folly/Likely.h>
#include <folly/Range.h> #include <folly/Range.h>
namespace folly { namespace folly {
...@@ -55,10 +58,24 @@ size_t encodeVarint(uint64_t val, uint8_t* buf); ...@@ -55,10 +58,24 @@ size_t encodeVarint(uint64_t val, uint8_t* buf);
/** /**
* Decode a value from a given buffer, advances data past the returned value. * Decode a value from a given buffer, advances data past the returned value.
* Throws on error.
*/ */
template <class T> template <class T>
uint64_t decodeVarint(Range<T*>& data); uint64_t decodeVarint(Range<T*>& data);
enum class DecodeVarintError {
TooManyBytes = 0,
TooFewBytes = 1,
};
/**
* A variant of decodeVarint() that does not throw on error. Useful in contexts
* where only part of a serialized varint may be attempted to be decoded, e.g.,
* when a serialized varint arrives on the boundary of a network packet.
*/
template <class T>
Expected<uint64_t, DecodeVarintError> tryDecodeVarint(Range<T*>& data);
/** /**
* ZigZag encoding that maps signed integers with a small absolute value * ZigZag encoding that maps signed integers with a small absolute value
* to unsigned integers with a small (positive) values. Without this, * to unsigned integers with a small (positive) values. Without this,
...@@ -93,6 +110,18 @@ inline size_t encodeVarint(uint64_t val, uint8_t* buf) { ...@@ -93,6 +110,18 @@ inline size_t encodeVarint(uint64_t val, uint8_t* buf) {
template <class T> template <class T>
inline uint64_t decodeVarint(Range<T*>& data) { inline uint64_t decodeVarint(Range<T*>& data) {
auto expected = tryDecodeVarint(data);
if (!expected) {
throw std::invalid_argument(
expected.error() == DecodeVarintError::TooManyBytes
? "Invalid varint value: too many bytes."
: "Invalid varint value: too few bytes.");
}
return *expected;
}
template <class T>
inline Expected<uint64_t, DecodeVarintError> tryDecodeVarint(Range<T*>& data) {
static_assert( static_assert(
std::is_same<typename std::remove_cv<T>::type, char>::value || std::is_same<typename std::remove_cv<T>::type, char>::value ||
std::is_same<typename std::remove_cv<T>::type, unsigned char>::value, std::is_same<typename std::remove_cv<T>::type, unsigned char>::value,
...@@ -117,7 +146,7 @@ inline uint64_t decodeVarint(Range<T*>& data) { ...@@ -117,7 +146,7 @@ inline uint64_t decodeVarint(Range<T*>& data) {
b = *p++; val |= (b & 0x7f) << 49; if (b >= 0) break; b = *p++; val |= (b & 0x7f) << 49; if (b >= 0) break;
b = *p++; val |= (b & 0x7f) << 56; if (b >= 0) break; b = *p++; val |= (b & 0x7f) << 56; if (b >= 0) break;
b = *p++; val |= (b & 0x7f) << 63; if (b >= 0) break; b = *p++; val |= (b & 0x7f) << 63; if (b >= 0) break;
throw std::invalid_argument("Invalid varint value. Too big."); return makeUnexpected(DecodeVarintError::TooManyBytes);
} while (false); } while (false);
} else { } else {
int shift = 0; int shift = 0;
...@@ -126,9 +155,7 @@ inline uint64_t decodeVarint(Range<T*>& data) { ...@@ -126,9 +155,7 @@ inline uint64_t decodeVarint(Range<T*>& data) {
shift += 7; shift += 7;
} }
if (p == end) { if (p == end) {
throw std::invalid_argument("Invalid varint value. Too small: " + return makeUnexpected(DecodeVarintError::TooFewBytes);
folly::to<std::string>(end - begin) +
" bytes");
} }
val |= static_cast<uint64_t>(*p++) << shift; val |= static_cast<uint64_t>(*p++) << shift;
} }
...@@ -137,4 +164,4 @@ inline uint64_t decodeVarint(Range<T*>& data) { ...@@ -137,4 +164,4 @@ inline uint64_t decodeVarint(Range<T*>& data) {
return val; return val;
} }
} // namespaces } // folly
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment