Commit b07e3455 authored by Yedidya Feldblum's avatar Yedidya Feldblum Committed by Facebook GitHub Bot

MacAddress updaters and factories

Summary: [Folly] `MacAddress` updater and factory suite for parsing string and binary representations and throwing or returning unexpected on parse failures.

Differential Revision: D21273907

fbshipit-source-id: 664cd1980cb12f3352eecf0e25671dca04c4a8a0
parent 35581869
......@@ -72,7 +72,28 @@ string MacAddress::toString() const {
return result;
}
void MacAddress::parse(StringPiece str) {
Expected<Unit, MacAddressFormatError> MacAddress::trySetFromString(
StringPiece value) {
return setFromString(value, [](auto _, auto) { return makeUnexpected(_); });
}
void MacAddress::setFromString(StringPiece value) {
setFromString(value, [](auto, auto _) { return _(), unit; });
}
Expected<Unit, MacAddressFormatError> MacAddress::trySetFromBinary(
ByteRange value) {
return setFromBinary(value, [](auto _, auto) { return makeUnexpected(_); });
}
void MacAddress::setFromBinary(ByteRange value) {
setFromBinary(value, [](auto, auto _) { return _(), unit; });
}
template <typename OnError>
Expected<Unit, MacAddressFormatError> MacAddress::setFromString(
StringPiece str,
OnError err) {
// Helper function to convert a single hex char into an integer
auto isSeparatorChar = [](char c) { return c == ':' || c == '-'; };
......@@ -80,24 +101,30 @@ void MacAddress::parse(StringPiece str) {
auto p = str.begin();
for (unsigned int byteIndex = 0; byteIndex < SIZE; ++byteIndex) {
if (p == str.end()) {
throw invalid_argument(
sformat("invalid MAC address '{}': not enough digits", str));
return err(MacAddressFormatError::Invalid, [&] {
throw invalid_argument(
sformat("invalid MAC address '{}': not enough digits", str));
});
}
// Skip over ':' or '-' separators between bytes
if (byteIndex != 0 && isSeparatorChar(*p)) {
++p;
if (p == str.end()) {
throw invalid_argument(
sformat("invalid MAC address '{}': not enough digits", str));
return err(MacAddressFormatError::Invalid, [&] {
throw invalid_argument(
sformat("invalid MAC address '{}': not enough digits", str));
});
}
}
// Parse the upper nibble
uint8_t upper = detail::hexTable[static_cast<uint8_t>(*p)];
if (upper & 0x10) {
throw invalid_argument(
sformat("invalid MAC address '{}': contains non-hex digit", str));
return err(MacAddressFormatError::Invalid, [&] {
throw invalid_argument(
sformat("invalid MAC address '{}': contains non-hex digit", str));
});
}
++p;
......@@ -115,8 +142,10 @@ void MacAddress::parse(StringPiece str) {
lower = upper;
upper = 0;
} else {
throw invalid_argument(
sformat("invalid MAC address '{}': contains non-hex digit", str));
return err(MacAddressFormatError::Invalid, [&] {
throw invalid_argument(sformat(
"invalid MAC address '{}': contains non-hex digit", str));
});
}
}
++p;
......@@ -128,21 +157,29 @@ void MacAddress::parse(StringPiece str) {
if (p != str.end()) {
// String is too long to be a MAC address
throw invalid_argument(
sformat("invalid MAC address '{}': found trailing characters", str));
return err(MacAddressFormatError::Invalid, [&] {
throw invalid_argument(
sformat("invalid MAC address '{}': found trailing characters", str));
});
}
// Only update now that we have successfully parsed the entire
// string. This way we remain unchanged on error.
setFromBinary(ByteRange(parsed, SIZE));
return setFromBinary(ByteRange(parsed, SIZE), err);
}
void MacAddress::setFromBinary(ByteRange value) {
template <typename OnError>
Expected<Unit, MacAddressFormatError> MacAddress::setFromBinary(
ByteRange value,
OnError err) {
if (value.size() != SIZE) {
throw invalid_argument(
sformat("MAC address must be 6 bytes long, got ", value.size()));
return err(MacAddressFormatError::Invalid, [&] {
throw invalid_argument(
sformat("MAC address must be 6 bytes long, got ", value.size()));
});
}
memcpy(bytes_ + 2, value.begin(), SIZE);
return unit;
}
std::ostream& operator<<(std::ostream& os, MacAddress address) {
......
......@@ -19,13 +19,19 @@
#include <iosfwd>
#include <folly/Conv.h>
#include <folly/Expected.h>
#include <folly/Range.h>
#include <folly/Unit.h>
#include <folly/lang/Bits.h>
namespace folly {
class IPAddressV6;
enum class MacAddressFormatError {
Invalid,
};
/*
* MacAddress represents an IEEE 802 MAC address.
*/
......@@ -50,9 +56,33 @@ class MacAddress {
*/
explicit MacAddress(StringPiece str);
static Expected<MacAddress, MacAddressFormatError> tryFromString(
StringPiece value) {
MacAddress ret;
auto ok = ret.trySetFromString(value);
if (!ok) {
return makeUnexpected(ok.error());
}
return ret;
}
static MacAddress fromString(StringPiece value) {
MacAddress ret;
ret.setFromString(value);
return ret;
}
/*
* Construct a MAC address from its 6-byte binary value
*/
static Expected<MacAddress, MacAddressFormatError> tryFromBinary(
ByteRange value) {
MacAddress ret;
auto ok = ret.trySetFromBinary(value);
if (!ok) {
return makeUnexpected(ok.error());
}
return ret;
}
static MacAddress fromBinary(ByteRange value) {
MacAddress ret;
ret.setFromBinary(value);
......@@ -135,11 +165,16 @@ class MacAddress {
/*
* Update the current MacAddress object from a human-readable string.
*/
void parse(StringPiece str);
Expected<Unit, MacAddressFormatError> trySetFromString(StringPiece value);
void setFromString(StringPiece value);
void parse(StringPiece str) {
setFromString(str);
}
/*
* Update the current MacAddress object from a 6-byte binary representation.
*/
Expected<Unit, MacAddressFormatError> trySetFromBinary(ByteRange value);
void setFromBinary(ByteRange value);
bool isBroadcast() const {
......@@ -204,6 +239,16 @@ class MacAddress {
bytes_[1] = 0;
}
template <typename OnError>
Expected<Unit, MacAddressFormatError> setFromString(
StringPiece value,
OnError err);
template <typename OnError>
Expected<Unit, MacAddressFormatError> setFromBinary(
ByteRange value,
OnError err);
/* We store the 6 bytes starting at bytes_[2] (most significant)
through bytes_[7] (least).
bytes_[0] and bytes_[1] are always equal to 0 to simplify comparisons.
......
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