Consistent use of sformat in address-related files
[folly.git] / folly / MacAddress.cpp
1 /*
2  * Copyright 2017 Facebook, Inc.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *   http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 #include <folly/MacAddress.h>
18
19 #include <ostream>
20
21 #include <folly/Exception.h>
22 #include <folly/Format.h>
23 #include <folly/IPAddressV6.h>
24 #include <folly/String.h>
25
26 using std::invalid_argument;
27 using std::string;
28
29 namespace folly {
30
31 const MacAddress MacAddress::BROADCAST{Endian::big(uint64_t(0xffffffffffffU))};
32 const MacAddress MacAddress::ZERO;
33
34 MacAddress::MacAddress(StringPiece str) {
35   memset(&bytes_, 0, 8);
36   parse(str);
37 }
38
39 MacAddress MacAddress::createMulticast(IPAddressV6 v6addr) {
40   // This method should only be used for multicast addresses.
41   DCHECK(v6addr.isMulticast());
42
43   uint8_t bytes[SIZE];
44   bytes[0] = 0x33;
45   bytes[1] = 0x33;
46   memcpy(bytes + 2, v6addr.bytes() + 12, 4);
47   return fromBinary(ByteRange(bytes, SIZE));
48 }
49
50 string MacAddress::toString() const {
51   static const char hexValues[] = "0123456789abcdef";
52   string result;
53   result.resize(17);
54   result[0] = hexValues[getByte(0) >> 4];
55   result[1] = hexValues[getByte(0) & 0xf];
56   result[2] = ':';
57   result[3] = hexValues[getByte(1) >> 4];
58   result[4] = hexValues[getByte(1) & 0xf];
59   result[5] = ':';
60   result[6] = hexValues[getByte(2) >> 4];
61   result[7] = hexValues[getByte(2) & 0xf];
62   result[8] = ':';
63   result[9] = hexValues[getByte(3) >> 4];
64   result[10] = hexValues[getByte(3) & 0xf];
65   result[11] = ':';
66   result[12] = hexValues[getByte(4) >> 4];
67   result[13] = hexValues[getByte(4) & 0xf];
68   result[14] = ':';
69   result[15] = hexValues[getByte(5) >> 4];
70   result[16] = hexValues[getByte(5) & 0xf];
71   return result;
72 }
73
74 void MacAddress::parse(StringPiece str) {
75   // Helper function to convert a single hex char into an integer
76   auto isSeparatorChar = [](char c) {
77     return c == ':' || c == '-';
78   };
79
80   uint8_t parsed[SIZE];
81   auto p = str.begin();
82   for (unsigned int byteIndex = 0; byteIndex < SIZE; ++byteIndex) {
83     if (p == str.end()) {
84       throw invalid_argument(
85           sformat("invalid MAC address '{}': not enough digits", str));
86     }
87
88     // Skip over ':' or '-' separators between bytes
89     if (byteIndex != 0 && isSeparatorChar(*p)) {
90       ++p;
91       if (p == str.end()) {
92         throw invalid_argument(
93             sformat("invalid MAC address '{}': not enough digits", str));
94       }
95     }
96
97     // Parse the upper nibble
98     uint8_t upper = detail::hexTable[static_cast<uint8_t>(*p)];
99     if (upper & 0x10) {
100       throw invalid_argument(
101           sformat("invalid MAC address '{}': contains non-hex digit", str));
102     }
103     ++p;
104
105     // Parse the lower nibble
106     uint8_t lower;
107     if (p == str.end()) {
108       lower = upper;
109       upper = 0;
110     } else {
111       lower = detail::hexTable[static_cast<uint8_t>(*p)];
112       if (lower & 0x10) {
113         // Also accept ':', '-', or '\0', to handle the case where one
114         // of the bytes was represented by just a single digit.
115         if (isSeparatorChar(*p)) {
116           lower = upper;
117           upper = 0;
118         } else {
119           throw invalid_argument(
120               sformat("invalid MAC address '{}': contains non-hex digit", str));
121         }
122       }
123       ++p;
124     }
125
126     // Update parsed with the newly parsed byte
127     parsed[byteIndex] = (upper << 4) | lower;
128   }
129
130   if (p != str.end()) {
131     // String is too long to be a MAC address
132     throw invalid_argument(
133         sformat("invalid MAC address '{}': found trailing characters", str));
134   }
135
136   // Only update now that we have successfully parsed the entire
137   // string.  This way we remain unchanged on error.
138   setFromBinary(ByteRange(parsed, SIZE));
139 }
140
141 void MacAddress::setFromBinary(ByteRange value) {
142   if (value.size() != SIZE) {
143     throw invalid_argument(
144         sformat("MAC address must be 6 bytes long, got ", value.size()));
145   }
146   memcpy(bytes_ + 2, value.begin(), SIZE);
147 }
148
149 std::ostream& operator<<(std::ostream& os, MacAddress address) {
150   os << address.toString();
151   return os;
152 }
153
154 } // namespace folly