Lua exception handling test
[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/IPAddressV6.h>
23
24 using std::invalid_argument;
25 using std::string;
26
27 namespace folly {
28
29 const MacAddress MacAddress::BROADCAST{Endian::big(uint64_t(0xffffffffffffU))};
30 const MacAddress MacAddress::ZERO;
31
32 MacAddress::MacAddress(StringPiece str) {
33   memset(&bytes_, 0, 8);
34   parse(str);
35 }
36
37 MacAddress MacAddress::createMulticast(IPAddressV6 v6addr) {
38   // This method should only be used for multicast addresses.
39   DCHECK(v6addr.isMulticast());
40
41   uint8_t bytes[SIZE];
42   bytes[0] = 0x33;
43   bytes[1] = 0x33;
44   memcpy(bytes + 2, v6addr.bytes() + 12, 4);
45   return fromBinary(ByteRange(bytes, SIZE));
46 }
47
48 string MacAddress::toString() const {
49   static const char hexValues[] = "0123456789abcdef";
50   string result;
51   result.resize(17);
52   result[0] = hexValues[getByte(0) >> 4];
53   result[1] = hexValues[getByte(0) & 0xf];
54   result[2] = ':';
55   result[3] = hexValues[getByte(1) >> 4];
56   result[4] = hexValues[getByte(1) & 0xf];
57   result[5] = ':';
58   result[6] = hexValues[getByte(2) >> 4];
59   result[7] = hexValues[getByte(2) & 0xf];
60   result[8] = ':';
61   result[9] = hexValues[getByte(3) >> 4];
62   result[10] = hexValues[getByte(3) & 0xf];
63   result[11] = ':';
64   result[12] = hexValues[getByte(4) >> 4];
65   result[13] = hexValues[getByte(4) & 0xf];
66   result[14] = ':';
67   result[15] = hexValues[getByte(5) >> 4];
68   result[16] = hexValues[getByte(5) & 0xf];
69   return result;
70 }
71
72 void MacAddress::parse(StringPiece str) {
73   // Helper function to convert a single hex char into an integer
74   auto unhex = [](char c) -> int {
75     return c >= '0' && c <= '9' ? c - '0' :
76            c >= 'A' && c <= 'F' ? c - 'A' + 10 :
77            c >= 'a' && c <= 'f' ? c - 'a' + 10 :
78            -1;
79   };
80   auto isSeparatorChar = [](char c) {
81     return c == ':' || c == '-';
82   };
83
84   uint8_t parsed[SIZE];
85   auto p = str.begin();
86   for (unsigned int byteIndex = 0; byteIndex < SIZE; ++byteIndex) {
87     if (p == str.end()) {
88       throw invalid_argument(to<string>("invalid MAC address \"", str,
89                                         "\": not enough digits"));
90     }
91
92     // Skip over ':' or '-' separators between bytes
93     if (byteIndex != 0 && isSeparatorChar(*p)) {
94       ++p;
95       if (p == str.end()) {
96         throw invalid_argument(to<string>("invalid MAC address \"", str,
97                                           "\": not enough digits"));
98       }
99     }
100
101     // Parse the upper nibble
102     int upper = unhex(*p);
103     if (upper < 0) {
104       throw invalid_argument(to<string>("invalid MAC address \"", str,
105                                         "\": contains non-hex digit"));
106     }
107     ++p;
108
109     // Parse the lower nibble
110     int lower;
111     if (p == str.end()) {
112       lower = upper;
113       upper = 0;
114     } else {
115       lower = unhex(*p);
116       if (lower < 0) {
117         // Also accept ':', '-', or '\0', to handle the case where one
118         // of the bytes was represented by just a single digit.
119         if (isSeparatorChar(*p)) {
120           lower = upper;
121           upper = 0;
122         } else {
123           throw invalid_argument(to<string>("invalid MAC address \"", str,
124                                             "\": contains non-hex digit"));
125         }
126       }
127       ++p;
128     }
129
130     // Update parsed with the newly parsed byte
131     parsed[byteIndex] = uint8_t((upper << 4) | lower);
132   }
133
134   if (p != str.end()) {
135     // String is too long to be a MAC address
136     throw invalid_argument(to<string>("invalid MAC address \"", str,
137                                       "\": found trailing characters"));
138   }
139
140   // Only update now that we have successfully parsed the entire
141   // string.  This way we remain unchanged on error.
142   setFromBinary(ByteRange(parsed, SIZE));
143 }
144
145 void MacAddress::setFromBinary(ByteRange value) {
146   if (value.size() != SIZE) {
147     throw invalid_argument(to<string>("MAC address must be 6 bytes "
148                                       "long, got ", value.size()));
149   }
150   memcpy(bytes_ + 2, value.begin(), SIZE);
151 }
152
153 std::ostream& operator<<(std::ostream& os, MacAddress address) {
154   os << address.toString();
155   return os;
156 }
157
158 } // folly