Google OR-Tools v9.15
a fast and portable software suite for combinatorial optimization
Loading...
Searching...
No Matches
container.h
Go to the documentation of this file.
1// Copyright 2010-2025 Google LLC
2// Licensed under the Apache License, Version 2.0 (the "License");
3// you may not use this file except in compliance with the License.
4// You may obtain a copy of the License at
5//
6// http://www.apache.org/licenses/LICENSE-2.0
7//
8// Unless required by applicable law or agreed to in writing, software
9// distributed under the License is distributed on an "AS IS" BASIS,
10// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11// See the License for the specific language governing permissions and
12// limitations under the License.
13
14#ifndef ORTOOLS_SAT_CONTAINER_H_
15#define ORTOOLS_SAT_CONTAINER_H_
16
17#include <algorithm>
18#include <cstddef>
19#include <cstdint>
20#include <cstdlib>
21#include <cstring>
22#include <limits>
23#include <type_traits>
24#include <utility>
25
26#include "absl/container/inlined_vector.h"
27#include "absl/log/check.h"
28#include "absl/types/span.h"
31
32namespace operations_research {
33namespace sat {
34
35// This is a very specific container that is optimized for the specific usage
36// patterns of BinaryImplicationGraph.
37// It stores an ordered set of literals and an unordered set of offsets.
38// internally, both arrays are stored contiguously, literals first, then
39// offsets. There might be a hole between the two arrays. In comments, we denote
40// literals as `L` and offsets as `O`, and holes as `.`. For example, `LLL..OO`
41// has 3 literals and 2 offsets, with a hole of size 2 in between.
43 public:
44 LiteralsOrOffsets() = default;
45
47 if (capacity_ > kInlineElements) free(data_.ptr);
48 }
49
52
54 num_literals_ = o.num_literals_;
55 num_offsets_ = o.num_offsets_;
56 capacity_ = o.capacity_;
57 data_ = o.data_;
58 o.capacity_ = kInlineElements; // So we don't double-free.
59 }
60
62 num_literals_ = o.num_literals_;
63 num_offsets_ = o.num_offsets_;
64 capacity_ = o.capacity_;
65 data_ = o.data_;
66 o.capacity_ = kInlineElements; // So we don't double-free.
67 return *this;
68 }
69
70 using Offset = int;
71
72 // Adds a literal to the end of the list of literals.
73 void PushBackLiteral(Literal literal) {
74 if (num_literals_ + num_offsets_ >= capacity_) {
75 GrowCapacity();
76 }
77 DCHECK_LT(num_literals_ + num_offsets_, capacity_);
78 InternalAddress()[num_literals_++].literal = literal;
79 }
80
81 // Adds an offset to the set of offsets.
82 void InsertOffset(Offset offset) {
83 if (num_literals_ + num_offsets_ >= capacity_) {
84 GrowCapacity();
85 }
86 DCHECK_LT(num_literals_ + num_offsets_, capacity_);
87 InternalAddress()[capacity_ - (++num_offsets_)].offset = offset;
88 }
89
90 int num_literals() const { return num_literals_; }
91 int num_offsets() const { return num_offsets_; }
92
93 absl::Span<const Literal> literals() const;
94 absl::Span<Literal> literals();
95
96 // Note that order is arbitrary.
97 absl::Span<const Offset> offsets() const;
98 absl::Span<Offset> offsets();
99
100 // Clearing functions.
101 // Call ShrinkToFit() if you want to save memory.
102 void ClearLiterals() { num_literals_ = 0; }
103 void ClearOffsets() { num_offsets_ = 0; }
104 void Clear() {
105 num_literals_ = 0;
106 num_offsets_ = 0;
107 }
108
109 // A bit faster than Clear() + ShrinkToFit().
111 num_literals_ = 0;
112 num_offsets_ = 0;
113 if (capacity_ > kInlineElements) free(data_.ptr);
114 capacity_ = kInlineElements;
115 }
116
117 // Use as little memory as possible.
118 void ShrinkToFit() {
119 if (capacity_ == kInlineElements) return; // Smallest possible size.
120
121 const uint32_t needed = num_literals_ + num_offsets_;
122 if (needed == capacity_) return;
123
124 DCHECK_GT(capacity_, kInlineElements);
125 LiteralOrOffset* old_ptr = data_.ptr; // This is valid memory.
126 if (needed > kInlineElements) {
127 LiteralOrOffset* new_ptr = static_cast<LiteralOrOffset*>(
128 malloc(static_cast<ptrdiff_t>(needed) * sizeof(LiteralOrOffset)));
129 CopyFromOldToNew(old_ptr, capacity_, new_ptr, needed);
130 capacity_ = needed;
131 data_.ptr = new_ptr;
132 } else {
133 CopyFromOldToNew(old_ptr, capacity_, data_.inlined, kInlineElements);
134 capacity_ = kInlineElements;
135 }
136
137 free(old_ptr);
138 }
139
140 // Resizes the list of literals to or shorter length.
141 void TruncateLiterals(int new_size) {
142 CHECK_GE(new_size, 0);
143 CHECK_LE(new_size, num_literals_);
144 num_literals_ = new_size;
145 }
146
147 // Removes all literals for which `predicate` returns true, and truncates the
148 // list of literals to the number of remaining literals (memory is not
149 // released).
150 template <typename Predicate>
151 void RemoveLiteralsIf(Predicate predicate);
152
153 // Returns the backing capacity for literals and offsets.
154 uint32_t capacity() const { return capacity_; }
155
156 static constexpr uint32_t kInlineElements = 4;
157
158 private:
159 // Elements are either literals or offsets.
160 union LiteralOrOffset {
161 Literal literal;
162 Offset offset;
163 };
164 static_assert(std::is_trivially_destructible_v<LiteralOrOffset>);
165 static_assert(std::is_trivially_copyable_v<LiteralOrOffset>);
166
167 LiteralOrOffset* InternalAddress() {
168 return capacity_ == kInlineElements ? data_.inlined : data_.ptr;
169 }
170 const LiteralOrOffset* InternalAddress() const {
171 return capacity_ == kInlineElements ? data_.inlined : data_.ptr;
172 }
173
174 void CopyFromOldToNew(LiteralOrOffset* old_ptr, uint32_t old_capacity,
175 LiteralOrOffset* new_ptr, uint32_t new_capacity) {
176 memcpy(new_ptr, old_ptr, num_literals_ * sizeof(LiteralOrOffset));
177 memcpy(new_ptr + (new_capacity - num_offsets_),
178 old_ptr + (old_capacity - num_offsets_),
179 num_offsets_ * sizeof(LiteralOrOffset));
180 }
181
182 void GrowCapacity() {
183 // TODO(user): crash later.
184 // For now, we do if we use more than 2 GB per LiteralOrOffsets.
185 CHECK_LE(capacity_, std::numeric_limits<uint32_t>::max() / 2);
186 const uint32_t new_capacity =
187 static_cast<uint32_t>(1.3 * static_cast<double>(capacity_));
188 CHECK_GT(new_capacity, kInlineElements);
189
190 LiteralOrOffset* new_ptr = static_cast<LiteralOrOffset*>(
191 malloc(static_cast<ptrdiff_t>(new_capacity) * sizeof(LiteralOrOffset)));
192 CopyFromOldToNew(InternalAddress(), capacity_, new_ptr, new_capacity);
193
194 if (capacity_ > kInlineElements) {
195 free(data_.ptr);
196 }
197 capacity_ = new_capacity;
198 data_.ptr = new_ptr;
199 }
200
201 // Invariants:
202 // num_literals_ + num_offsets_ <= capacity_
203 // capacity_ >= kInlineElements.
204 uint32_t num_literals_ = 0;
205 uint32_t num_offsets_ = 0;
206 uint32_t capacity_ = kInlineElements;
207
208 // Inlined memory or a pointer to the content.
209 //
210 // TODO(user): If we want to be more compact, we can set kInlineElements == 2.
211 // We can probably fit a third one, if we don't care about the aligment since
212 // we only use 3 * int32_t above. Experiments.
213 union {
214 LiteralOrOffset inlined[kInlineElements];
215 LiteralOrOffset* ptr;
216 } data_;
217};
218
219inline absl::Span<const Literal> LiteralsOrOffsets::literals() const {
220 DCHECK_LE(num_literals_, capacity_);
221 // Casting is OK because "pointer to LiteralOrOffset" is
222 // pointer-interconvertible with "pointer to Literal": `LiteralOrOffset` is
223 // an union object and the other is a non-static data member of that object.
224 return absl::MakeConstSpan(
225 reinterpret_cast<const Literal*>(InternalAddress()), num_literals_);
226}
227
228inline absl::Span<Literal> LiteralsOrOffsets::literals() {
229 DCHECK_LE(num_literals_, capacity_);
230 return absl::MakeSpan(reinterpret_cast<Literal*>(InternalAddress()),
231 num_literals_);
232}
233
234inline absl::Span<const LiteralsOrOffsets::Offset> LiteralsOrOffsets::offsets()
235 const {
236 DCHECK_LE(num_offsets_, capacity_);
237 return absl::MakeConstSpan(
238 reinterpret_cast<const Offset*>(InternalAddress() +
239 (capacity_ - num_offsets_)),
240 num_offsets_);
241}
242
243inline absl::Span<LiteralsOrOffsets::Offset> LiteralsOrOffsets::offsets() {
244 DCHECK_LE(num_offsets_, capacity_);
245 return absl::MakeSpan(
246 reinterpret_cast<Offset*>(InternalAddress() + (capacity_ - num_offsets_)),
247 num_offsets_);
248}
249
250template <typename Predicate>
251void LiteralsOrOffsets::RemoveLiteralsIf(Predicate predicate) {
252 const auto new_end = std::remove_if(literals().begin(), literals().end(),
253 std::move(predicate));
254 num_literals_ = new_end - literals().begin();
255}
256
257} // namespace sat
258} // namespace operations_research
259
260#endif // ORTOOLS_SAT_CONTAINER_H_
LiteralOrOffset inlined[kInlineElements]
Definition container.h:214
absl::Span< const Literal > literals() const
Definition container.h:219
LiteralsOrOffsets(const LiteralsOrOffsets &)=delete
LiteralsOrOffsets & operator=(const LiteralsOrOffsets &)=delete
static constexpr uint32_t kInlineElements
Definition container.h:156
void RemoveLiteralsIf(Predicate predicate)
Definition container.h:251
LiteralsOrOffsets(LiteralsOrOffsets &&o)
Definition container.h:53
LiteralsOrOffsets & operator=(LiteralsOrOffsets &&o)
Definition container.h:61
absl::Span< const Offset > offsets() const
Definition container.h:234
OR-Tools root namespace.
ClosedInterval::Iterator end(ClosedInterval interval)
ClosedInterval::Iterator begin(ClosedInterval interval)
void * malloc(YYSIZE_T)
void free(void *)