X-Git-Url: http://plrg.eecs.uci.edu/git/?p=folly.git;a=blobdiff_plain;f=folly%2FVarint.h;h=c28d7f724fa2ddace0307b5b71d3dd8accbd1a9c;hp=3349cb153c44c84652baad9e96149b970c9c9412;hb=90ce64f987677e11200729afa6bb9c4b64caf01e;hpb=0558d398803721bc1ab0a6418abc32230cbcdb03 diff --git a/folly/Varint.h b/folly/Varint.h index 3349cb15..c28d7f72 100644 --- a/folly/Varint.h +++ b/folly/Varint.h @@ -1,5 +1,5 @@ /* - * Copyright 2013 Facebook, Inc. + * Copyright 2017 Facebook, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,10 +14,14 @@ * limitations under the License. */ -#ifndef FOLLY_VARINT_H_ -#define FOLLY_VARINT_H_ +#pragma once -#include "folly/Range.h" +#include + +#include +#include +#include +#include namespace folly { @@ -54,8 +58,23 @@ 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. */ -uint64_t decodeVarint(ByteRange& data); +template +Expected tryDecodeVarint(Range& data); /** * ZigZag encoding that maps signed integers with a small absolute value @@ -69,7 +88,8 @@ uint64_t decodeVarint(ByteRange& data); inline uint64_t encodeZigZag(int64_t val) { // Bit-twiddling magic stolen from the Google protocol buffer document; // val >> 63 is an arithmetic shift because val is signed - return static_cast((val << 1) ^ (val >> 63)); + auto uval = static_cast(val); + return static_cast((uval << 1) ^ (val >> 63)); } inline int64_t decodeZigZag(uint64_t val) { @@ -84,30 +104,89 @@ inline size_t encodeVarint(uint64_t val, uint8_t* buf) { *p++ = 0x80 | (val & 0x7f); val >>= 7; } - *p++ = val; - return p - buf; + *p++ = uint8_t(val); + 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; } -inline uint64_t decodeVarint(ByteRange& data) { +template +inline Expected tryDecodeVarint(Range& data) { + static_assert( + std::is_same::type, char>::value || + std::is_same::type, unsigned char>::value, + "Only character ranges are supported"); + const int8_t* begin = reinterpret_cast(data.begin()); const int8_t* end = reinterpret_cast(data.end()); const int8_t* p = begin; uint64_t val = 0; - if (LIKELY(end - begin >= kMaxVarintLength64)) { // fast path + // end is always greater than or equal to begin, so this subtraction is safe + 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; @@ -115,15 +194,14 @@ inline uint64_t decodeVarint(ByteRange& data) { val |= static_cast(*p++ & 0x7f) << shift; shift += 7; } - if (p == end) throw std::invalid_argument("Invalid varint value"); + if (p == end) { + return makeUnexpected(DecodeVarintError::TooFewBytes); + } val |= static_cast(*p++) << shift; } - data.advance(p - begin); + data.uncheckedAdvance(p - begin); return val; } -} // namespaces - -#endif /* FOLLY_VARINT_H_ */ - +} // namespace folly