X-Git-Url: http://plrg.eecs.uci.edu/git/?p=folly.git;a=blobdiff_plain;f=folly%2FVarint.h;h=12e2b909ef9353716db39e2bafca30f43e082c91;hp=6cb42d1fa9d2f84308a92547fc483a255f676ca0;hb=6746259314362d89f3d1d1dbfdf00c9fe18de202;hpb=ed8c80a0e0988e4ce687f51ca832a00e4a6b7930 diff --git a/folly/Varint.h b/folly/Varint.h index 6cb42d1f..12e2b909 100644 --- a/folly/Varint.h +++ b/folly/Varint.h @@ -1,5 +1,5 @@ /* - * Copyright 2017 Facebook, Inc. + * Copyright 2013-present Facebook, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,7 +17,10 @@ #pragma once #include + #include +#include +#include #include namespace folly { @@ -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. + * Throws on error. */ template uint64_t decodeVarint(Range& 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 +Expected tryDecodeVarint(Range& data); + /** * ZigZag encoding that maps signed integers with a small absolute value * to unsigned integers with a small (positive) values. Without this, @@ -88,11 +105,23 @@ inline size_t encodeVarint(uint64_t val, uint8_t* buf) { val >>= 7; } *p++ = uint8_t(val); - return p - buf; + return size_t(p - buf); } template inline uint64_t decodeVarint(Range& 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 +inline Expected tryDecodeVarint(Range& data) { static_assert( std::is_same::type, char>::value || std::is_same::type, unsigned char>::value, @@ -107,17 +136,57 @@ inline uint64_t decodeVarint(Range& data) { if (LIKELY(size_t(end - begin) >= kMaxVarintLength64)) { // fast path int64_t b; do { - b = *p++; val = (b & 0x7f) ; if (b >= 0) break; - b = *p++; val |= (b & 0x7f) << 7; if (b >= 0) break; - b = *p++; val |= (b & 0x7f) << 14; if (b >= 0) break; - b = *p++; val |= (b & 0x7f) << 21; if (b >= 0) break; - b = *p++; val |= (b & 0x7f) << 28; if (b >= 0) break; - b = *p++; val |= (b & 0x7f) << 35; if (b >= 0) break; - b = *p++; val |= (b & 0x7f) << 42; 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) << 63; if (b >= 0) break; - throw std::invalid_argument("Invalid varint value. Too big."); + b = *p++; + val = (b & 0x7f); + if (b >= 0) { + break; + } + b = *p++; + val |= (b & 0x7f) << 7; + if (b >= 0) { + break; + } + b = *p++; + val |= (b & 0x7f) << 14; + if (b >= 0) { + break; + } + b = *p++; + val |= (b & 0x7f) << 21; + if (b >= 0) { + break; + } + b = *p++; + val |= (b & 0x7f) << 28; + if (b >= 0) { + break; + } + b = *p++; + val |= (b & 0x7f) << 35; + if (b >= 0) { + break; + } + b = *p++; + val |= (b & 0x7f) << 42; + 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 & 0x01) << 63; + if (b >= 0) { + break; + } + return makeUnexpected(DecodeVarintError::TooManyBytes); } while (false); } else { int shift = 0; @@ -126,9 +195,7 @@ inline uint64_t decodeVarint(Range& data) { shift += 7; } if (p == end) { - throw std::invalid_argument("Invalid varint value. Too small: " + - folly::to(end - begin) + - " bytes"); + return makeUnexpected(DecodeVarintError::TooFewBytes); } val |= static_cast(*p++) << shift; } @@ -137,4 +204,4 @@ inline uint64_t decodeVarint(Range& data) { return val; } -} // namespaces +} // namespace folly