1 /** 2 * Copyright (c) 2021-2022 Huawei Device Co., Ltd. 3 * Licensed under the Apache License, Version 2.0 (the "License"); 4 * you may not use this file except in compliance with the License. 5 * You may obtain a copy of the License at 6 * 7 * http://www.apache.org/licenses/LICENSE-2.0 8 * 9 * Unless required by applicable law or agreed to in writing, software 10 * distributed under the License is distributed on an "AS IS" BASIS, 11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 * See the License for the specific language governing permissions and 13 * limitations under the License. 14 */ 15 16 #ifndef _PANDA_VERIFIER_EQ_CLASSES_HPP 17 #define _PANDA_VERIFIER_EQ_CLASSES_HPP 18 19 #include "lazy.h" 20 #include "abstract_index.h" 21 22 #include "runtime/include/mem/panda_containers.h" 23 24 #include <limits> 25 26 namespace panda::verifier { 27 /* 28 todo: 29 optimization: tree-organization 30 each classentry is either: fixed index, or pointer to other classindex. 31 each classentry has refcounter 32 when object class is determined, after finding actual class, object class set 33 to this classindex and refcounts of classentry of prev class and new one recalculated. 34 when refcount == 0 class is disposed 35 36 introduce flatten operation 37 */ 38 39 template <typename T> 40 class EqClass; 41 42 template <> 43 class EqClass<size_t> { 44 public: 45 using Idx = AbstractIndex<size_t, EqClass>; 46 47 class ClassIndex : public Idx { 48 public: 49 ClassIndex &operator=(size_t val) 50 { 51 Idx::operator=(val); 52 return *this; 53 } 54 }; 55 56 class ObjIndex : public Idx { 57 public: 58 ObjIndex &operator=(size_t val) 59 { 60 Idx::operator=(val); 61 return *this; 62 } 63 }; 64 65 struct ClassEntry { 66 size_t Size = 0; 67 ObjIndex Head; 68 ObjIndex Tail; 69 }; 70 71 struct ObjectEntry { 72 ClassIndex Class; 73 ObjIndex Next; 74 ObjIndex Prev; 75 }; 76 ClsEntry(ClassIndex cls)77 ClassEntry &ClsEntry(ClassIndex cls) 78 { 79 return EqClasses_[cls]; 80 } 81 ClsEntry(ClassIndex cls)82 const ClassEntry &ClsEntry(ClassIndex cls) const 83 { 84 return EqClasses_[cls]; 85 } 86 NewClassIndex()87 ClassIndex NewClassIndex() 88 { 89 ClassIndex cls; 90 if (!FreeClassIndices_.empty()) { 91 cls = FreeClassIndices_.back(); 92 FreeClassIndices_.pop_back(); 93 } else { 94 cls = EqClasses_.size(); 95 EqClasses_.push_back({}); 96 } 97 return cls; 98 } 99 DisposeClassIndex(ClassIndex idx)100 void DisposeClassIndex(ClassIndex idx) 101 { 102 auto &entry = ClsEntry(idx); 103 entry.Head.Invalidate(); 104 entry.Tail.Invalidate(); 105 entry.Size = 0; 106 FreeClassIndices_.push_back(idx); 107 } 108 ObjEntry(ObjIndex idx)109 ObjectEntry &ObjEntry(ObjIndex idx) 110 { 111 return Objects_[idx]; 112 } 113 ObjEntry(ObjIndex idx)114 const ObjectEntry &ObjEntry(ObjIndex idx) const 115 { 116 return Objects_[idx]; 117 } 118 ObjClass(ObjIndex idx)119 ClassIndex ObjClass(ObjIndex idx) const 120 { 121 return Objects_[idx].Class; 122 } 123 JoinClasses(ClassIndex lhs_class,ClassIndex rhs_class)124 ClassIndex JoinClasses(ClassIndex lhs_class, ClassIndex rhs_class) 125 { 126 if (lhs_class == rhs_class) { 127 return lhs_class; 128 } 129 auto [lhs, rhs] = 130 (lhs_class < rhs_class ? std::tuple {lhs_class, rhs_class} : std::tuple {rhs_class, lhs_class}); 131 auto &lhs_cls_entry = ClsEntry(lhs); 132 auto &rhs_cls_entry = ClsEntry(rhs); 133 auto &lhs_tail_obj_entry = ObjEntry(lhs_cls_entry.Tail); 134 auto &rhs_head_obj_entry = ObjEntry(rhs_cls_entry.Head); 135 lhs_tail_obj_entry.Next = rhs_cls_entry.Head; 136 rhs_head_obj_entry.Prev = lhs_cls_entry.Tail; 137 lhs_cls_entry.Tail = rhs_cls_entry.Tail; 138 lhs_cls_entry.Size += rhs_cls_entry.Size; 139 auto obj = lhs_tail_obj_entry.Next; 140 while (obj.IsValid()) { 141 auto &obj_entry = ObjEntry(obj); 142 obj_entry.Class = lhs; 143 obj = obj_entry.Next; 144 } 145 DisposeClassIndex(rhs); 146 return lhs; 147 } 148 NewObjIndex()149 ObjIndex NewObjIndex() 150 { 151 ObjIndex obj; 152 if (!FreeObjIndices_.empty()) { 153 obj = FreeObjIndices_.back(); 154 FreeObjIndices_.pop_back(); 155 } else { 156 obj = Objects_.size(); 157 Objects_.push_back({}); 158 } 159 ClassIndex cls = NewClassIndex(); 160 auto &cls_entry = ClsEntry(cls); 161 cls_entry.Head = cls_entry.Tail = obj; 162 ++cls_entry.Size; 163 ObjEntry(obj).Class = cls; 164 return obj; 165 } 166 DisposeObjIndex(ObjIndex obj)167 void DisposeObjIndex(ObjIndex obj) 168 { 169 if (!obj.IsValid()) { 170 return; 171 } 172 auto cls = ObjClass(obj); 173 auto &cls_entry = ClsEntry(cls); 174 auto &obj_entry = ObjEntry(obj); 175 auto &prev = obj_entry.Prev; 176 auto &next = obj_entry.Next; 177 if (prev.IsValid()) { 178 ObjEntry(prev).Next = next; 179 } 180 if (next.IsValid()) { 181 ObjEntry(next).Prev = prev; 182 } 183 if (cls_entry.Head == obj) { 184 cls_entry.Head = next; 185 } 186 if (cls_entry.Tail == obj) { 187 cls_entry.Tail = prev; 188 } 189 --cls_entry.Size; 190 if (cls_entry.Size == 0) { 191 DisposeClassIndex(cls); 192 } 193 obj_entry.Next.Invalidate(); 194 obj_entry.Prev.Invalidate(); 195 obj_entry.Class.Invalidate(); 196 FreeObjIndices_.push_back(obj); 197 } 198 199 template <typename F> EquateLazy(F fetcher)200 void EquateLazy(F fetcher) 201 { 202 if (auto obj = fetcher()) { 203 ClassIndex cls = ObjClass(*obj); 204 ForEach(fetcher, [this, &cls](ObjIndex object) { cls = JoinClasses(cls, ObjClass(object)); }); 205 } 206 } 207 208 template <typename It> Equate(It begin,It end)209 void Equate(It begin, It end) 210 { 211 if (begin == end) { 212 return; 213 } 214 It it = begin; 215 ClassIndex cls = ObjClass(*it++); 216 for (; it != end; ++it) { 217 cls = JoinClasses(cls, ObjClass(*it)); 218 } 219 } 220 Equate(std::initializer_list<ObjIndex> objects)221 void Equate(std::initializer_list<ObjIndex> objects) 222 { 223 Equate(objects.begin(), objects.end()); 224 } 225 AllEqualToLazy(ObjIndex idx)226 auto AllEqualToLazy(ObjIndex idx) 227 { 228 ClassIndex cls = ObjClass(idx); 229 ObjIndex obj = ClsEntry(cls).Head; 230 return [this, obj]() mutable -> std::optional<ObjIndex> { 231 auto o = obj; 232 if (obj.IsValid()) { 233 obj = ObjEntry(obj).Next; 234 return o; 235 } 236 return {}; 237 }; 238 } 239 240 template <typename F> IsAllEqualLazy(F fetcher)241 bool IsAllEqualLazy(F fetcher) 242 { 243 if (auto obj = fetcher()) { 244 ClassIndex cls = ObjClass(*obj); 245 return FoldLeft(fetcher, true, 246 [this, cls](bool result, ObjIndex object) { return result && cls == ObjClass(object); }); 247 } 248 return true; 249 } 250 251 template <typename It> IsAllEqual(It begin,It end)252 bool IsAllEqual(It begin, It end) 253 { 254 if (begin == end) { 255 return true; 256 } 257 It it = begin; 258 ClassIndex cls = ObjClass(*it++); 259 for (; it != end; ++it) { 260 if (cls != ObjClass(*it)) { 261 return false; 262 } 263 } 264 return true; 265 } 266 IsAllEqual(std::initializer_list<ObjIndex> objects)267 bool IsAllEqual(std::initializer_list<ObjIndex> objects) 268 { 269 return IsAllEqual(objects.begin(), objects.end()); 270 } 271 ClassSizeOf(ObjIndex obj)272 size_t ClassSizeOf(ObjIndex obj) const 273 { 274 return ClsEntry(ObjClass(obj)).Size; 275 } 276 AmountOfUsedObjIndices()277 size_t AmountOfUsedObjIndices() const 278 { 279 return Objects_.size() - FreeObjIndices_.size(); 280 } 281 AmountOfUsedClassIndices()282 size_t AmountOfUsedClassIndices() const 283 { 284 return EqClasses_.size() - FreeClassIndices_.size(); 285 } 286 shrink_to_fit()287 void shrink_to_fit() 288 { 289 // todo: 290 // 1. get tail indices from EqClasses_ & Object_ 291 // 2. remove them from FreeClassIndices_ & FreeObjIndices_ 292 // 3. optimize all vectors in capacity 293 } 294 295 private: 296 PandaVector<ClassIndex> FreeClassIndices_; 297 PandaVector<ObjIndex> FreeObjIndices_; 298 PandaVector<ClassEntry> EqClasses_; 299 PandaVector<ObjectEntry> Objects_; 300 }; 301 } // namespace panda::verifier 302 303 namespace std { 304 template <> 305 struct hash<panda::verifier::EqClass<size_t>::ObjIndex> { 306 size_t operator()(const panda::verifier::EqClass<size_t>::ObjIndex &idx) const 307 { 308 return panda::verifier::StdHash( 309 static_cast<panda::verifier::AbstractIndex<size_t, panda::verifier::EqClass<size_t>>>(idx)); 310 } 311 }; 312 } // namespace std 313 314 namespace panda::verifier { 315 template <typename Obj> 316 class EqClass : private EqClass<size_t> { 317 public: 318 using Base = EqClass<size_t>; 319 320 std::optional<ObjIndex> GetIndex(const Obj &obj) const 321 { 322 auto it = ObjToIndex_.find(obj); 323 if (it != ObjToIndex_.end()) { 324 return it->second; 325 } 326 return {}; 327 } 328 329 ObjIndex GetIndexOrCreate(const Obj &obj) 330 { 331 if (auto i = GetIndex(obj)) { 332 return *i; 333 } 334 auto idx = Base::NewObjIndex(); 335 ObjToIndex_[obj] = idx; 336 return idx; 337 } 338 339 void DisposeObject(const Obj &obj) 340 { 341 ObjIndex idx = GetIndexOrCreate(obj); 342 ObjToIndex_.erase(obj); 343 IndexToObj_.erase(idx); 344 Base::DisposeObjIndex(idx); 345 } 346 347 template <typename F> 348 void EquateLazy(F fetcher) 349 { 350 Base::EquateLazy(Transform(fetcher, [this](const Obj &obj) { return GetIndexOrCreate(obj); })); 351 } 352 353 template <typename It> 354 void Equate(It begin, It end) 355 { 356 Base::EquateLazy([this, &begin, end]() -> std::optional<ObjIndex> { 357 if (begin != end) { 358 return GetIndexOrCreate(*begin++); 359 } 360 return {}; 361 }); 362 } 363 364 void Equate(std::initializer_list<Obj> objects) 365 { 366 Equate(objects.begin(), objects.end()); 367 } 368 369 auto AllEqualToLazy(const Obj &obj) 370 { 371 return Transform(Base::AllEqualToLazy(GetIndexOrCreate(obj)), 372 [this](ObjIndex idx) { return IndexToObj_[idx]; }); 373 } 374 375 template <typename F> 376 bool IsAllEqualLazy(F fetcher) 377 { 378 return Base::IsAllEqualLazy(Transform(fetcher, [this](const Obj &obj) { return GetIndexOrCreate(obj); })); 379 } 380 381 template <typename It> 382 bool IsAllEqual(It begin, It end) 383 { 384 return Base::IsAllEqualLazy([this, &begin, end]() -> std::optional<ObjIndex> { 385 if (begin != end) { 386 return GetIndexOrCreate(*begin++); 387 } 388 return {}; 389 }); 390 } 391 392 bool IsAllEqual(std::initializer_list<Obj> objects) 393 { 394 return IsAllEqual(objects.begin(), objects.end()); 395 } 396 397 size_t ClassSizeOf(const Obj &obj) const 398 { 399 if (auto idx = GetIndex(obj)) { 400 return Base::ClassSizeOf(*idx); 401 } 402 return 0; 403 } 404 405 private: 406 PandaUnorderedMap<Obj, ObjIndex> ObjToIndex_; 407 PandaUnorderedMap<ObjIndex, Obj> IndexToObj_; 408 }; 409 } // namespace panda::verifier 410 411 #endif // !_PANDA_VERIFIER_EQ_CLASSES_HPP 412