Add unit tests for opening non-ELF files.
[folly.git] / folly / String-inl.h
index 28aead22360baa8e9b64ccf5218960f1b0f17f8c..f6b5ba993dffbba4d2cc318db4c269f314a0833d 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright 2014 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.
  * limitations under the License.
  */
 
-#ifndef FOLLY_STRING_INL_H_
-#define FOLLY_STRING_INL_H_
+#pragma once
 
-#include <stdexcept>
 #include <iterator>
+#include <stdexcept>
 
-#ifndef FOLLY_BASE_STRING_H_
+#include <folly/CppAttributes.h>
+
+#ifndef FOLLY_STRING_H_
 #error This file may only be included from String.h
 #endif
 
@@ -32,7 +33,7 @@ namespace detail {
 // an octal escape sequence, or 'P' if the character is printable and
 // should be printed as is.
 extern const char cEscapeTable[];
-}  // namespace detail
+} // namespace detail
 
 template <class String>
 void cEscape(StringPiece str, String& out) {
@@ -51,7 +52,7 @@ void cEscape(StringPiece str, String& out) {
     if (e == 'P') {  // printable
       ++p;
     } else if (e == 'O') {  // octal
-      out.append(&*last, p - last);
+      out.append(&*last, size_t(p - last));
       esc[1] = '0' + ((v >> 6) & 7);
       esc[2] = '0' + ((v >> 3) & 7);
       esc[3] = '0' + (v & 7);
@@ -59,14 +60,14 @@ void cEscape(StringPiece str, String& out) {
       ++p;
       last = p;
     } else {  // special 1-character escape
-      out.append(&*last, p - last);
+      out.append(&*last, size_t(p - last));
       esc[1] = e;
       out.append(esc, 2);
       ++p;
       last = p;
     }
   }
-  out.append(&*last, p - last);
+  out.append(&*last, size_t(p - last));
 }
 
 namespace detail {
@@ -79,7 +80,7 @@ extern const char cUnescapeTable[];
 
 // Map from the character code to the hex value, or 16 if invalid hex char.
 extern const unsigned char hexTable[];
-}  // namespace detail
+} // namespace detail
 
 template <class String>
 void cUnescape(StringPiece str, String& out, bool strict) {
@@ -157,7 +158,7 @@ namespace detail {
 // 3 = space, replace with '+' in QUERY mode
 // 4 = percent-encode
 extern const unsigned char uriEscapeTable[];
-}  // namespace detail
+} // namespace detail
 
 template <class String>
 void uriEscape(StringPiece str, String& out, UriEscapeMode mode) {
@@ -178,12 +179,12 @@ void uriEscape(StringPiece str, String& out, UriEscapeMode mode) {
     if (LIKELY(discriminator <= minEncode)) {
       ++p;
     } else if (mode == UriEscapeMode::QUERY && discriminator == 3) {
-      out.append(&*last, p - last);
+      out.append(&*last, size_t(p - last));
       out.push_back('+');
       ++p;
       last = p;
     } else {
-      out.append(&*last, p - last);
+      out.append(&*last, size_t(p - last));
       esc[1] = hexValues[v >> 4];
       esc[2] = hexValues[v & 0x0f];
       out.append(esc, 3);
@@ -191,7 +192,7 @@ void uriEscape(StringPiece str, String& out, UriEscapeMode mode) {
       last = p;
     }
   }
-  out.append(&*last, p - last);
+  out.append(&*last, size_t(p - last));
 }
 
 template <class String>
@@ -203,7 +204,6 @@ void uriUnescape(StringPiece str, String& out, UriEscapeMode mode) {
   // this is faster than calling push_back repeatedly.
   while (p != str.end()) {
     char c = *p;
-    unsigned char v = static_cast<unsigned char>(v);
     switch (c) {
     case '%':
       {
@@ -215,7 +215,7 @@ void uriUnescape(StringPiece str, String& out, UriEscapeMode mode) {
         if (UNLIKELY(h1 == 16 || h2 == 16)) {
           throw std::invalid_argument("invalid percent encode sequence");
         }
-        out.append(&*last, p - last);
+        out.append(&*last, size_t(p - last));
         out.push_back((h1 << 4) | h2);
         p += 3;
         last = p;
@@ -223,19 +223,20 @@ void uriUnescape(StringPiece str, String& out, UriEscapeMode mode) {
       }
     case '+':
       if (mode == UriEscapeMode::QUERY) {
-        out.append(&*last, p - last);
+        out.append(&*last, size_t(p - last));
         out.push_back(' ');
         ++p;
         last = p;
         break;
       }
       // else fallthrough
+      FOLLY_FALLTHROUGH;
     default:
       ++p;
       break;
     }
   }
-  out.append(&*last, p - last);
+  out.append(&*last, size_t(p - last));
 }
 
 namespace detail {
@@ -265,29 +266,6 @@ inline char delimFront(StringPiece s) {
   return *s.start();
 }
 
-/*
- * These output conversion templates allow us to support multiple
- * output string types, even when we are using an arbitrary
- * OutputIterator.
- */
-template<class OutStringT> struct OutputConverter {};
-
-template<> struct OutputConverter<std::string> {
-  std::string operator()(StringPiece sp) const {
-    return sp.toString();
-  }
-};
-
-template<> struct OutputConverter<fbstring> {
-  fbstring operator()(StringPiece sp) const {
-    return sp.toFbstring();
-  }
-};
-
-template<> struct OutputConverter<StringPiece> {
-  StringPiece operator()(StringPiece sp) const { return sp; }
-};
-
 /*
  * Shared implementation for all the split() overloads.
  *
@@ -297,7 +275,7 @@ template<> struct OutputConverter<StringPiece> {
  *
  * @param ignoreEmpty iff true, don't copy empty segments to output
  */
-template<class OutStringT, class DelimT, class OutputIterator>
+template <class OutStringT, class DelimT, class OutputIterator>
 void internalSplit(DelimT delim, StringPiece sp, OutputIterator out,
     bool ignoreEmpty) {
   assert(sp.empty() || sp.start() != nullptr);
@@ -306,11 +284,9 @@ void internalSplit(DelimT delim, StringPiece sp, OutputIterator out,
   const size_t strSize = sp.size();
   const size_t dSize = delimSize(delim);
 
-  OutputConverter<OutStringT> conv;
-
   if (dSize > strSize || dSize == 0) {
     if (!ignoreEmpty || strSize > 0) {
-      *out++ = conv(sp);
+      *out++ = to<OutStringT>(sp);
     }
     return;
   }
@@ -320,12 +296,12 @@ void internalSplit(DelimT delim, StringPiece sp, OutputIterator out,
       ignoreEmpty);
   }
 
-  int tokenStartPos = 0;
-  int tokenSize = 0;
+  size_t tokenStartPos = 0;
+  size_t tokenSize = 0;
   for (size_t i = 0; i <= strSize - dSize; ++i) {
     if (atDelim(&s[i], delim)) {
       if (!ignoreEmpty || tokenSize > 0) {
-        *out++ = conv(StringPiece(&s[tokenStartPos], tokenSize));
+        *out++ = to<OutStringT>(sp.subpiece(tokenStartPos, tokenSize));
       }
 
       tokenStartPos = i + dSize;
@@ -337,45 +313,34 @@ void internalSplit(DelimT delim, StringPiece sp, OutputIterator out,
   }
   tokenSize = strSize - tokenStartPos;
   if (!ignoreEmpty || tokenSize > 0) {
-    *out++ = conv(StringPiece(&s[tokenStartPos], tokenSize));
+    *out++ = to<OutStringT>(sp.subpiece(tokenStartPos, tokenSize));
   }
 }
 
-template<class String> StringPiece prepareDelim(const String& s) {
+template <class String> StringPiece prepareDelim(const String& s) {
   return StringPiece(s);
 }
 inline char prepareDelim(char c) { return c; }
 
-template <class Dst>
-struct convertTo {
-  template <class Src>
-  static Dst from(const Src& src) { return folly::to<Dst>(src); }
-  static Dst from(const Dst& src) { return src; }
-};
-
-template<bool exact,
-         class Delim,
-         class OutputType>
-typename std::enable_if<IsSplitTargetType<OutputType>::value, bool>::type
-splitFixed(const Delim& delimiter,
-           StringPiece input,
-           OutputType& out) {
+template <bool exact, class Delim, class OutputType>
+bool splitFixed(const Delim& delimiter, StringPiece input, OutputType& output) {
+  static_assert(
+      exact || std::is_same<OutputType, StringPiece>::value ||
+          IsSomeString<OutputType>::value,
+      "split<false>() requires that the last argument be a string type");
   if (exact && UNLIKELY(std::string::npos != input.find(delimiter))) {
     return false;
   }
-  out = convertTo<OutputType>::from(input);
+  output = folly::to<OutputType>(input);
   return true;
 }
 
-template<bool exact,
-         class Delim,
-         class OutputType,
-         class... OutputTypes>
-typename std::enable_if<IsSplitTargetType<OutputType>::value, bool>::type
-splitFixed(const Delim& delimiter,
-           StringPiece input,
-           OutputType& outHead,
-           OutputTypes&... outTail) {
+template <bool exact, class Delim, class OutputType, class... OutputTypes>
+bool splitFixed(
+    const Delim& delimiter,
+    StringPiece input,
+    OutputType& outHead,
+    OutputTypes&... outTail) {
   size_t cut = input.find(delimiter);
   if (UNLIKELY(cut == std::string::npos)) {
     return false;
@@ -384,17 +349,17 @@ splitFixed(const Delim& delimiter,
   StringPiece tail(input.begin() + cut + detail::delimSize(delimiter),
                    input.end());
   if (LIKELY(splitFixed<exact>(delimiter, tail, outTail...))) {
-    outHead = convertTo<OutputType>::from(head);
+    outHead = folly::to<OutputType>(head);
     return true;
   }
   return false;
 }
 
-}
+} // namespace detail
 
 //////////////////////////////////////////////////////////////////////
 
-template<class Delim, class String, class OutputType>
+template <class Delim, class String, class OutputType>
 void split(const Delim& delimiter,
            const String& input,
            std::vector<OutputType>& out,
@@ -406,7 +371,7 @@ void split(const Delim& delimiter,
     ignoreEmpty);
 }
 
-template<class Delim, class String, class OutputType>
+template <class Delim, class String, class OutputType>
 void split(const Delim& delimiter,
            const String& input,
            fbvector<OutputType>& out,
@@ -418,8 +383,11 @@ void split(const Delim& delimiter,
     ignoreEmpty);
 }
 
-template<class OutputValueType, class Delim, class String,
-         class OutputIterator>
+template <
+    class OutputValueType,
+    class Delim,
+    class String,
+    class OutputIterator>
 void splitTo(const Delim& delimiter,
              const String& input,
              OutputIterator out,
@@ -431,20 +399,13 @@ void splitTo(const Delim& delimiter,
     ignoreEmpty);
 }
 
-template<bool exact,
-         class Delim,
-         class OutputType,
-         class... OutputTypes>
-typename std::enable_if<IsSplitTargetType<OutputType>::value, bool>::type
-split(const Delim& delimiter,
-      StringPiece input,
-      OutputType& outHead,
-      OutputTypes&... outTail) {
+template <bool exact, class Delim, class... OutputTypes>
+typename std::enable_if<
+    AllConvertible<OutputTypes...>::value && sizeof...(OutputTypes) >= 1,
+    bool>::type
+split(const Delim& delimiter, StringPiece input, OutputTypes&... outputs) {
   return detail::splitFixed<exact>(
-    detail::prepareDelim(delimiter),
-    input,
-    outHead,
-    outTail...);
+      detail::prepareDelim(delimiter), input, outputs...);
 }
 
 namespace detail {
@@ -516,7 +477,7 @@ internalJoin(Delim delimiter,
   internalJoinAppend(delimiter, begin, end, output);
 }
 
-}  // namespace detail
+} // namespace detail
 
 template <class Delim, class Iterator, class String>
 void join(const Delim& delimiter,
@@ -530,8 +491,11 @@ void join(const Delim& delimiter,
     output);
 }
 
-template <class String1, class String2>
-void backslashify(const String1& input, String2& output, bool hex_style) {
+template <class OutputString>
+void backslashify(
+    folly::StringPiece input,
+    OutputString& output,
+    bool hex_style) {
   static const char hexValues[] = "0123456789abcdef";
   output.clear();
   output.reserve(3 * input.size());
@@ -543,14 +507,21 @@ void backslashify(const String1& input, String2& output, bool hex_style) {
       if (hex_style) {
         hex_append = true;
       } else {
-        if (c == '\r') output += 'r';
-        else if (c == '\n') output += 'n';
-        else if (c == '\t') output += 't';
-        else if (c == '\a') output += 'a';
-        else if (c == '\b') output += 'b';
-        else if (c == '\0') output += '0';
-        else if (c == '\\') output += '\\';
-        else {
+        if (c == '\r') {
+          output += 'r';
+        } else if (c == '\n') {
+          output += 'n';
+        } else if (c == '\t') {
+          output += 't';
+        } else if (c == '\a') {
+          output += 'a';
+        } else if (c == '\b') {
+          output += 'b';
+        } else if (c == '\0') {
+          output += '0';
+        } else if (c == '\\') {
+          output += '\\';
+        } else {
           hex_append = true;
         }
       }
@@ -567,8 +538,8 @@ void backslashify(const String1& input, String2& output, bool hex_style) {
 
 template <class String1, class String2>
 void humanify(const String1& input, String2& output) {
-  int numUnprintable = 0;
-  int numPrintablePrefix = 0;
+  size_t numUnprintable = 0;
+  size_t numPrintablePrefix = 0;
   for (unsigned char c : input) {
     if (c < 0x20 || c > 0x7e || c == '\\') {
       ++numUnprintable;
@@ -609,13 +580,15 @@ void humanify(const String1& input, String2& output) {
   }
 }
 
-template<class InputString, class OutputString>
+template <class InputString, class OutputString>
 bool hexlify(const InputString& input, OutputString& output,
              bool append_output) {
-  if (!append_output) output.clear();
+  if (!append_output) {
+    output.clear();
+  }
 
   static char hexValues[] = "0123456789abcdef";
-  int j = output.size();
+  auto j = output.size();
   output.resize(2 * input.size() + output.size());
   for (size_t i = 0; i < input.size(); ++i) {
     int ch = input[i];
@@ -625,24 +598,19 @@ bool hexlify(const InputString& input, OutputString& output,
   return true;
 }
 
-template<class InputString, class OutputString>
+template <class InputString, class OutputString>
 bool unhexlify(const InputString& input, OutputString& output) {
   if (input.size() % 2 != 0) {
     return false;
   }
   output.resize(input.size() / 2);
   int j = 0;
-  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;
-  };
 
   for (size_t i = 0; i < input.size(); i += 2) {
-    int highBits = unhex(input[i]);
-    int lowBits = unhex(input[i + 1]);
-    if (highBits < 0 || lowBits < 0) {
+    int highBits = detail::hexTable[static_cast<uint8_t>(input[i])];
+    int lowBits = detail::hexTable[static_cast<uint8_t>(input[i + 1])];
+    if ((highBits | lowBits) & 0x10) {
+      // One of the characters wasn't a hex digit
       return false;
     }
     output[j++] = (highBits << 4) + lowBits;
@@ -657,7 +625,7 @@ namespace detail {
  */
 size_t hexDumpLine(const void* ptr, size_t offset, size_t size,
                    std::string& line);
-}  // namespace detail
+} // namespace detail
 
 template <class OutIt>
 void hexDump(const void* ptr, size_t size, OutIt out) {
@@ -669,6 +637,4 @@ void hexDump(const void* ptr, size_t size, OutIt out) {
   }
 }
 
-}  // namespace folly
-
-#endif /* FOLLY_STRING_INL_H_ */
+} // namespace folly