span.h 6.1 KB
Newer Older
1256748979@qq.com committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200
/* Copyright (c) 2017, Google Inc.
 *
 * Permission to use, copy, modify, and/or distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
 * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
 * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */

#ifndef OPENSSL_HEADER_SSL_SPAN_H
#define OPENSSL_HEADER_SSL_SPAN_H

#include <openssl/base.h>

#if !defined(BORINGSSL_NO_CXX)

extern "C++" {

#include <stdlib.h>

#include <algorithm>
#include <type_traits>

BSSL_NAMESPACE_BEGIN

template <typename T>
class Span;

namespace internal {
template <typename T>
class SpanBase {
  // Put comparison operator implementations into a base class with const T, so
  // they can be used with any type that implicitly converts into a Span.
  static_assert(std::is_const<T>::value,
                "Span<T> must be derived from SpanBase<const T>");

  friend bool operator==(Span<T> lhs, Span<T> rhs) {
    // MSVC issues warning C4996 because std::equal is unsafe. The pragma to
    // suppress the warning mysteriously has no effect, hence this
    // implementation. See
    // https://msdn.microsoft.com/en-us/library/aa985974.aspx.
    if (lhs.size() != rhs.size()) {
      return false;
    }
    for (T *l = lhs.begin(), *r = rhs.begin(); l != lhs.end() && r != rhs.end();
         ++l, ++r) {
      if (*l != *r) {
        return false;
      }
    }
    return true;
  }

  friend bool operator!=(Span<T> lhs, Span<T> rhs) { return !(lhs == rhs); }
};
}  // namespace internal

// A Span<T> is a non-owning reference to a contiguous array of objects of type
// |T|. Conceptually, a Span is a simple a pointer to |T| and a count of
// elements accessible via that pointer. The elements referenced by the Span can
// be mutated if |T| is mutable.
//
// A Span can be constructed from container types implementing |data()| and
// |size()| methods. If |T| is constant, construction from a container type is
// implicit. This allows writing methods that accept data from some unspecified
// container type:
//
// // Foo views data referenced by v.
// void Foo(bssl::Span<const uint8_t> v) { ... }
//
// std::vector<uint8_t> vec;
// Foo(vec);
//
// For mutable Spans, conversion is explicit:
//
// // FooMutate mutates data referenced by v.
// void FooMutate(bssl::Span<uint8_t> v) { ... }
//
// FooMutate(bssl::Span<uint8_t>(vec));
//
// You can also use the |MakeSpan| and |MakeConstSpan| factory methods to
// construct Spans in order to deduce the type of the Span automatically.
//
// FooMutate(bssl::MakeSpan(vec));
//
// Note that Spans have value type sematics. They are cheap to construct and
// copy, and should be passed by value whenever a method would otherwise accept
// a reference or pointer to a container or array.
template <typename T>
class Span : private internal::SpanBase<const T> {
 private:
  // Heuristically test whether C is a container type that can be converted into
  // a Span by checking for data() and size() member functions.
  //
  // TODO(davidben): Switch everything to std::enable_if_t when we remove
  // support for MSVC 2015. Although we could write our own enable_if_t and MSVC
  // 2015 has std::enable_if_t anyway, MSVC 2015's SFINAE implementation is
  // problematic and does not work below unless we write the ::type at use.
  template <typename C>
  using EnableIfContainer = std::enable_if<
      std::is_convertible<decltype(std::declval<C>().data()), T *>::value &&
      std::is_integral<decltype(std::declval<C>().size())>::value>;

  static const size_t npos = static_cast<size_t>(-1);

 public:
  constexpr Span() : Span(nullptr, 0) {}
  constexpr Span(T *ptr, size_t len) : data_(ptr), size_(len) {}

  template <size_t N>
  constexpr Span(T (&array)[N]) : Span(array, N) {}

  template <
      typename C, typename = typename EnableIfContainer<C>::type,
      typename = typename std::enable_if<std::is_const<T>::value, C>::type>
  Span(const C &container) : data_(container.data()), size_(container.size()) {}

  template <
      typename C, typename = typename EnableIfContainer<C>::type,
      typename = typename std::enable_if<!std::is_const<T>::value, C>::type>
  explicit Span(C &container)
      : data_(container.data()), size_(container.size()) {}

  T *data() const { return data_; }
  size_t size() const { return size_; }
  bool empty() const { return size_ == 0; }

  T *begin() const { return data_; }
  const T *cbegin() const { return data_; }
  T *end() const { return data_ + size_; }
  const T *cend() const { return end(); }

  T &front() const {
    if (size_ == 0) {
      abort();
    }
    return data_[0];
  }
  T &back() const {
    if (size_ == 0) {
      abort();
    }
    return data_[size_ - 1];
  }

  T &operator[](size_t i) const {
    if (i >= size_) {
      abort();
    }
    return data_[i];
  }
  T &at(size_t i) const { return (*this)[i]; }

  Span subspan(size_t pos = 0, size_t len = npos) const {
    if (pos > size_) {
      abort();  // absl::Span throws an exception here.
    }
    return Span(data_ + pos, std::min(size_ - pos, len));
  }

 private:
  T *data_;
  size_t size_;
};

template <typename T>
const size_t Span<T>::npos;

template <typename T>
Span<T> MakeSpan(T *ptr, size_t size) {
  return Span<T>(ptr, size);
}

template <typename C>
auto MakeSpan(C &c) -> decltype(MakeSpan(c.data(), c.size())) {
  return MakeSpan(c.data(), c.size());
}

template <typename T>
Span<const T> MakeConstSpan(T *ptr, size_t size) {
  return Span<const T>(ptr, size);
}

template <typename C>
auto MakeConstSpan(const C &c) -> decltype(MakeConstSpan(c.data(), c.size())) {
  return MakeConstSpan(c.data(), c.size());
}

BSSL_NAMESPACE_END

}  // extern C++

#endif  // !defined(BORINGSSL_NO_CXX)

#endif  // OPENSSL_HEADER_SSL_SPAN_H