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