--- /dev/null
+/*
+ * Copyright 2014 Facebook, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "MacAddress.h"
+
+#include "folly/Exception.h"
+#include "folly/IPAddressV6.h"
+
+using std::invalid_argument;
+using std::string;
+
+namespace folly {
+
+const MacAddress MacAddress::BROADCAST{Endian::big(0xffffffffffffU)};
+const MacAddress MacAddress::ZERO;
+
+MacAddress::MacAddress(StringPiece str) {
+ memset(&bytes_, 0, 8);
+ parse(str);
+}
+
+MacAddress MacAddress::createMulticast(IPAddressV6 v6addr) {
+ // This method should only be used for multicast addresses.
+ DCHECK(v6addr.isMulticast());
+
+ uint8_t bytes[SIZE];
+ bytes[0] = 0x33;
+ bytes[1] = 0x33;
+ memcpy(bytes + 2, v6addr.bytes() + 12, 4);
+ return fromBinary(ByteRange(bytes, SIZE));
+}
+
+string MacAddress::toString() const {
+ static const char hexValues[] = "0123456789abcdef";
+ string result;
+ result.resize(17);
+ result[0] = hexValues[getByte(0) >> 4];
+ result[1] = hexValues[getByte(0) & 0xf];
+ result[2] = ':';
+ result[3] = hexValues[getByte(1) >> 4];
+ result[4] = hexValues[getByte(1) & 0xf];
+ result[5] = ':';
+ result[6] = hexValues[getByte(2) >> 4];
+ result[7] = hexValues[getByte(2) & 0xf];
+ result[8] = ':';
+ result[9] = hexValues[getByte(3) >> 4];
+ result[10] = hexValues[getByte(3) & 0xf];
+ result[11] = ':';
+ result[12] = hexValues[getByte(4) >> 4];
+ result[13] = hexValues[getByte(4) & 0xf];
+ result[14] = ':';
+ result[15] = hexValues[getByte(5) >> 4];
+ result[16] = hexValues[getByte(5) & 0xf];
+ return result;
+}
+
+void MacAddress::parse(StringPiece str) {
+ // Helper function to convert a single hex char into an integer
+ auto unhex = [](char c) -> int {
+ return c >= '0' && c <= '9' ? c - '0' :
+ c >= 'A' && c <= 'F' ? c - 'A' + 10 :
+ c >= 'a' && c <= 'f' ? c - 'a' + 10 :
+ -1;
+ };
+ auto isSeparatorChar = [](char c) {
+ return c == ':' || c == '-';
+ };
+
+ uint8_t parsed[SIZE];
+ auto p = str.begin();
+ for (unsigned int byteIndex = 0; byteIndex < SIZE; ++byteIndex) {
+ if (p == str.end()) {
+ throw invalid_argument(to<string>("invalid MAC address \"", str,
+ "\": not enough digits"));
+ }
+
+ // Skip over ':' or '-' separators between bytes
+ if (byteIndex != 0 && isSeparatorChar(*p)) {
+ ++p;
+ if (p == str.end()) {
+ throw invalid_argument(to<string>("invalid MAC address \"", str,
+ "\": not enough digits"));
+ }
+ }
+
+ // Parse the upper nibble
+ int upper = unhex(*p);
+ if (upper < 0) {
+ throw invalid_argument(to<string>("invalid MAC address \"", str,
+ "\": contains non-hex digit"));
+ }
+ ++p;
+
+ // Parse the lower nibble
+ int lower;
+ if (p == str.end()) {
+ lower = upper;
+ upper = 0;
+ } else {
+ lower = unhex(*p);
+ if (lower < 0) {
+ // Also accept ':', '-', or '\0', to handle the case where one
+ // of the bytes was represented by just a single digit.
+ if (isSeparatorChar(*p)) {
+ lower = upper;
+ upper = 0;
+ } else {
+ throw invalid_argument(to<string>("invalid MAC address \"", str,
+ "\": contains non-hex digit"));
+ }
+ }
+ ++p;
+ }
+
+ // Update parsed with the newly parsed byte
+ parsed[byteIndex] = ((upper << 4) | lower);
+ }
+
+ if (p != str.end()) {
+ // String is too long to be a MAC address
+ throw invalid_argument(to<string>("invalid MAC address \"", str,
+ "\": found trailing characters"));
+ }
+
+ // Only update now that we have successfully parsed the entire
+ // string. This way we remain unchanged on error.
+ setFromBinary(ByteRange(parsed, SIZE));
+}
+
+void MacAddress::setFromBinary(ByteRange value) {
+ if (value.size() != SIZE) {
+ throw invalid_argument(to<string>("MAC address must be 6 bytes "
+ "long, got ", value.size()));
+ }
+ memcpy(bytes_ + 2, value.begin(), SIZE);
+}
+
+std::ostream& operator<<(std::ostream& os, MacAddress address) {
+ os << address.toString();
+ return os;
+}
+
+} // folly