1 2 /** 3 * Copyright (C) 2018-present MongoDB, Inc. 4 * 5 * This program is free software: you can redistribute it and/or modify 6 * it under the terms of the Server Side Public License, version 1, 7 * as published by MongoDB, Inc. 8 * 9 * This program is distributed in the hope that it will be useful, 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 * Server Side Public License for more details. 13 * 14 * You should have received a copy of the Server Side Public License 15 * along with this program. If not, see 16 * <http://www.mongodb.com/licensing/server-side-public-license>. 17 * 18 * As a special exception, the copyright holders give permission to link the 19 * code of portions of this program with the OpenSSL library under certain 20 * conditions as described in each individual source file and distribute 21 * linked combinations including the program with the OpenSSL library. You 22 * must comply with the Server Side Public License in all respects for 23 * all of the code used other than as permitted herein. If you modify file(s) 24 * with this exception, you may extend this exception to your version of the 25 * file(s), but you are not obligated to do so. If you do not wish to do so, 26 * delete this exception statement from your version. If you delete this 27 * exception statement from all source files in the program, then also delete 28 * it in the license file. 29 */ 30 31 #pragma once 32 33 #include <cstring> 34 #include <tuple> 35 #include <type_traits> 36 37 #include "mongo/base/data_type.h" 38 #include "mongo/base/error_codes.h" 39 #include "mongo/base/status_with.h" 40 #include "mongo/platform/endian.h" 41 #include "mongo/util/mongoutils/str.h" 42 43 namespace mongo { 44 45 class ConstDataRange { 46 public: 47 // begin and end should point to the first and one past last bytes in 48 // the range you wish to view. 49 // 50 // debug_offset provides a way to indicate that the ConstDataRange is 51 // located at an offset into some larger logical buffer. By setting it 52 // to a non-zero value, you'll change the Status messages that are 53 // returned on failure to be offset by the amount passed to this 54 // constructor. 55 ConstDataRange(const char* begin, const char* end, std::ptrdiff_t debug_offset = 0) _begin(begin)56 : _begin(begin), _end(end), _debug_offset(debug_offset) { 57 invariant(end >= begin); 58 } 59 60 ConstDataRange(const char* begin, std::size_t length, std::ptrdiff_t debug_offset = 0) 61 : ConstDataRange(begin, begin + length, debug_offset) {} 62 data()63 const char* data() const { 64 return _begin; 65 } 66 length()67 size_t length() const { 68 return _end - _begin; 69 } 70 empty()71 bool empty() const { 72 return length() == 0; 73 } 74 75 template <typename T> 76 Status read(T* t, size_t offset = 0) const { 77 if (offset > length()) { 78 return makeOffsetStatus(offset); 79 } 80 81 return DataType::load( 82 t, _begin + offset, length() - offset, nullptr, offset + _debug_offset); 83 } 84 85 template <typename T> 86 StatusWith<T> read(std::size_t offset = 0) const { 87 T t(DataType::defaultConstruct<T>()); 88 Status s = read(&t, offset); 89 90 if (s.isOK()) { 91 return StatusWith<T>(std::move(t)); 92 } else { 93 return StatusWith<T>(std::move(s)); 94 } 95 } 96 97 friend bool operator==(const ConstDataRange& lhs, const ConstDataRange& rhs) { 98 return std::tie(lhs._begin, lhs._end) == std::tie(rhs._begin, rhs._end); 99 } 100 101 friend bool operator!=(const ConstDataRange& lhs, const ConstDataRange& rhs) { 102 return !(lhs == rhs); 103 } 104 105 106 protected: 107 const char* _begin; 108 const char* _end; 109 std::ptrdiff_t _debug_offset; 110 111 Status makeOffsetStatus(size_t offset) const; 112 }; 113 114 class DataRange : public ConstDataRange { 115 public: 116 typedef char* bytes_type; 117 118 DataRange(bytes_type begin, bytes_type end, std::ptrdiff_t debug_offset = 0) ConstDataRange(begin,end,debug_offset)119 : ConstDataRange(begin, end, debug_offset) {} 120 121 DataRange(bytes_type begin, std::size_t length, std::ptrdiff_t debug_offset = 0) ConstDataRange(begin,length,debug_offset)122 : ConstDataRange(begin, length, debug_offset) {} 123 124 template <typename T> 125 Status write(const T& value, std::size_t offset = 0) { 126 if (offset > length()) { 127 return makeOffsetStatus(offset); 128 } 129 130 return DataType::store(value, 131 const_cast<char*>(_begin + offset), 132 length() - offset, 133 nullptr, 134 offset + _debug_offset); 135 } 136 }; 137 138 struct DataRangeTypeHelper { 139 static Status makeStoreStatus(size_t t_length, size_t length, std::ptrdiff_t debug_offset); 140 }; 141 142 // Enable for classes derived from ConstDataRange 143 template <typename T> 144 struct DataType::Handler<T, 145 typename std::enable_if<std::is_base_of<ConstDataRange, T>::value>::type> { 146 static Status load( 147 T* t, const char* ptr, size_t length, size_t* advanced, std::ptrdiff_t debug_offset) { 148 if (t) { 149 // Assuming you know what you're doing at the read above this 150 // is fine. Either you're reading into a readable buffer, so 151 // ptr started off non-const, or the const_cast will feed back 152 // to const char* taking Const variants. So it'll get tossed 153 // out again. 154 *t = T(const_cast<char*>(ptr), const_cast<char*>(ptr) + length); 155 } 156 157 if (advanced) { 158 *advanced = length; 159 } 160 161 return Status::OK(); 162 } 163 164 static Status store( 165 const T& t, char* ptr, size_t length, size_t* advanced, std::ptrdiff_t debug_offset) { 166 if (t.length() > length) { 167 return DataRangeTypeHelper::makeStoreStatus(t.length(), length, debug_offset); 168 } 169 170 if (ptr) { 171 std::memcpy(ptr, t.data(), t.length()); 172 } 173 174 if (advanced) { 175 *advanced = t.length(); 176 } 177 178 return Status::OK(); 179 } 180 181 static T defaultConstruct() { 182 return T(nullptr, nullptr); 183 } 184 }; 185 186 } // namespace mongo 187