#include "TypeSequenceManager.hpp" #include "SequenceData.hpp" #include "moab/Error.hpp" #include #include namespace moab { TypeSequenceManager::~TypeSequenceManager() { // We assume that for there to be multiple sequences referencing // the same SequenceData, there must be some portion of the // SequenceData that is unused. Otherwise the sequences should // have been merged. Given that assumption, it is the case that // either a) a SequenceData is in availableList or b) the // SequenceData is referenced by exactly one sequence. // Delete every entity sequence for (iterator i = begin(); i != end(); ++i) { EntitySequence* seq = *i; // Check for case b) above if (seq->using_entire_data()) { // Delete sequence before data, because sequence // has a pointer to data and may try to dereference // that pointer during its destruction. SequenceData* data = seq->data(); delete seq; delete data; } else { delete seq; } } sequenceSet.clear(); // Case a) above for (data_iterator i = availableList.begin(); i != availableList.end(); ++i) delete *i; availableList.clear(); } ErrorCode TypeSequenceManager::merge_internal(iterator i, iterator j) { EntitySequence* dead = *j; sequenceSet.erase(j); ErrorCode rval = (*i)->merge(*dead); if (MB_SUCCESS != rval) { sequenceSet.insert(dead); return rval; } if (lastReferenced == dead) lastReferenced = *i; delete dead; // If merging results in no unused portions of the SequenceData, // remove it from the available list. if ((*i)->using_entire_data()) availableList.erase((*i)->data()); return MB_SUCCESS; } ErrorCode TypeSequenceManager::check_merge_next(iterator i) { iterator j = i; ++j; if (j == end() || (*j)->data() != (*i)->data() || (*j)->start_handle() > (*i)->end_handle() + 1) return MB_SUCCESS; assert((*i)->end_handle() + 1 == (*j)->start_handle()); return merge_internal(i, j); } ErrorCode TypeSequenceManager::check_merge_prev(iterator i) { if (i == begin()) return MB_SUCCESS; iterator j = i; --j; if ((*j)->data() != (*i)->data() || (*j)->end_handle() + 1 < (*i)->start_handle()) return MB_SUCCESS; assert((*j)->end_handle() + 1 == (*i)->start_handle()); return merge_internal(i, j); } ErrorCode TypeSequenceManager::insert_sequence(EntitySequence* seq_ptr) { if (!seq_ptr->data()) return MB_FAILURE; if (seq_ptr->data()->start_handle() > seq_ptr->start_handle() || seq_ptr->data()->end_handle() < seq_ptr->end_handle() || seq_ptr->end_handle() < seq_ptr->start_handle()) return MB_FAILURE; iterator i = lower_bound(seq_ptr->start_handle()); if (i != end()) { if ((*i)->start_handle() <= seq_ptr->end_handle()) return MB_ALREADY_ALLOCATED; if (seq_ptr->data() != (*i)->data() && (*i)->data()->start_handle() <= seq_ptr->data()->end_handle()) return MB_ALREADY_ALLOCATED; } if (i != begin()) { iterator j = i; --j; if (seq_ptr->data() != (*j)->data() && (*j)->data()->end_handle() >= seq_ptr->data()->start_handle()) return MB_ALREADY_ALLOCATED; } i = sequenceSet.insert(i, seq_ptr); // Merge with previous sequence ? if (seq_ptr->start_handle() > seq_ptr->data()->start_handle() && i != begin()) { if (MB_SUCCESS != check_merge_prev(i)) { sequenceSet.erase(i); return MB_FAILURE; } } // Merge with next sequence ? if ((*i)->end_handle() < (*i)->data()->end_handle()) { if (MB_SUCCESS != check_merge_next(i)) { sequenceSet.erase(i); return MB_FAILURE; } } // We merged adjacent sequences sharing a SequenceData, so // we can safely assume that unless this EntitySequence is // using the entire SequenceData, there are unused portions. if (!seq_ptr->using_entire_data()) availableList.insert(seq_ptr->data()); // lastReferenced is only allowed to be NULL if there are // no sequences (avoids unnecessary if's in fast path). if (!lastReferenced) lastReferenced = seq_ptr; // Each SequenceData has a pointer to the first EntitySequence // referencing it. Update that pointer if the new sequence is // the first one. if ((*i)->start_handle() == (*i)->data()->start_handle() || lower_bound((*i)->data()->start_handle()) == i) (*i)->data()->seqManData.firstSequence = i; assert(check_valid_data(seq_ptr)); return MB_SUCCESS; } ErrorCode TypeSequenceManager::replace_subsequence(EntitySequence* seq_ptr, const int* tag_sizes, int num_tag_sizes) { // Find the sequence of interest iterator i = lower_bound(seq_ptr->start_handle()); if (i == end() || (*i)->data() == seq_ptr->data()) return MB_FAILURE; // New sequence must be a subset of an existing one if (seq_ptr->start_handle() < (*i)->start_handle() || seq_ptr->end_handle() > (*i)->end_handle()) return MB_FAILURE; // New sequence's data must be new also, and cannot intersect // any existing sequence (just require that the data range // matches the sequence range for now) if (!seq_ptr->using_entire_data()) return MB_FAILURE; // Copy tag data (move ownership of var-len data) SequenceData* const dead_data = (*i)->data(); dead_data->move_tag_data(seq_ptr->data(), tag_sizes, num_tag_sizes); // Split sequences sharing old data into two groups: // p->i : first sequence to i // i->n : i to one past last sequence iterator p, n = i; p = (*i)->data()->seqManData.firstSequence; for (++n; n != end() && (*n)->data() == (*i)->data(); ++n); // First subdivide EntitySequence as necessary // Move i to be the first sequence past the insertion point // such that the new order will be: // [p,i-1] seq_ptr [i,n] // where p == i if no previous sequence // Four possible cases: // 0. All entities in sequence are in new sequence // 1. Old entities in sequence before and after new sequence, // requiring sequence to be split. // 2. Old entities after new sequence // 3. Old entities before new sequence const bool some_before = ((*i)->start_handle() < seq_ptr->start_handle()); const bool some_after = ((*i)-> end_handle() > seq_ptr-> end_handle()); // Case 0 if (!(some_before || some_after)) { // Remove dead sequence from internal lists EntitySequence* seq = *i; iterator dead = i; ++i; if (p == dead) p = i; sequenceSet.erase(dead); // Delete old sequence delete seq; // Make sure lastReferenced isn't stale if (lastReferenced == seq) lastReferenced = seq_ptr; } // Case 1 else if (some_before && some_after) { i = split_sequence(i, seq_ptr->start_handle()); (*i)->pop_front(seq_ptr->size()); } // Case 2 else if (some_after) { (*i)->pop_front(seq_ptr->size()); } // Case 3 else { // some_before (*i)->pop_back(seq_ptr->size()); ++i; } // Now subdivide the underlying sequence data as necessary availableList.erase(dead_data); if (p != i) { iterator last = i; --last; SequenceData* new_data = (*p)->create_data_subset((*p)->start_handle(), (*last)->end_handle()); new_data->seqManData.firstSequence = p; for (; p != i; ++p) (*p)->data(new_data); // Copy tag data (move ownership of var-len data) dead_data->move_tag_data(new_data, tag_sizes, num_tag_sizes); if (!(*new_data->seqManData.firstSequence)->using_entire_data()) availableList.insert(new_data); } if (i != n) { iterator last = n; --last; SequenceData* new_data = (*i)->create_data_subset((*i)->start_handle(), (*last)->end_handle()); new_data->seqManData.firstSequence = i; for (; i != n; ++i) (*i)->data(new_data); // Copy tag data (move ownership of var-len data) dead_data->move_tag_data(new_data, tag_sizes, num_tag_sizes); if (!(*new_data->seqManData.firstSequence)->using_entire_data()) availableList.insert(new_data); } delete dead_data; // Put new sequence in lists return insert_sequence(seq_ptr); } TypeSequenceManager::iterator TypeSequenceManager::erase(iterator i) { EntitySequence* seq = *i; SequenceData* data = seq->data(); iterator j; // Check if we need to delete the referenced SequenceData also bool delete_data; if (seq->using_entire_data()) // Only sequence delete_data = true; else if (data->seqManData.firstSequence != i) { // Earlier sequence? delete_data = false; availableList.insert(data); } else { // Later sequence ? j = i; ++j; delete_data = (j == end() || (*j)->data() != data); if (delete_data) availableList.erase(data); else { availableList.insert(data); data->seqManData.firstSequence = j; } } // Remove sequence, updating i to be next sequence j = i++; sequenceSet.erase(j); // Make sure lastReferenced isn't stale. It can only be NULL if // no sequences. if (lastReferenced == seq) lastReferenced = sequenceSet.empty() ? 0 : *sequenceSet.begin(); // Always delete sequence before the SequenceData it references. assert(0 == find(seq->start_handle())); delete seq; if (delete_data) delete data; else { assert(check_valid_data(*data->seqManData.firstSequence)); assert(lastReferenced != seq); } return i; } ErrorCode TypeSequenceManager::remove_sequence(const EntitySequence* seq_ptr, bool& unreferenced_data) { // Remove sequence from set iterator i = lower_bound(seq_ptr->start_handle()); if (i == end() || *i != seq_ptr) return MB_ENTITY_NOT_FOUND; sequenceSet.erase(i); // Check if this is the only sequence referencing its data if (seq_ptr->using_entire_data()) unreferenced_data = true; else { i = lower_bound(seq_ptr->data()->start_handle()); unreferenced_data = i == end() || (*i)->data() != seq_ptr->data(); if (unreferenced_data) availableList.erase(seq_ptr->data()); else seq_ptr->data()->seqManData.firstSequence = i; // Might be 'i' already } if (lastReferenced == seq_ptr) lastReferenced = sequenceSet.empty() ? 0 : *sequenceSet.begin(); return MB_SUCCESS; } TypeSequenceManager::iterator TypeSequenceManager::find_free_handle(EntityHandle min_start_handle, EntityHandle max_end_handle, bool& append_out, int values_per_ent) { for (data_iterator i = availableList.begin(); i != availableList.end(); ++i) { if ((*(*i)->seqManData.firstSequence)->values_per_entity() != values_per_ent) continue; if ((*i)->start_handle() > max_end_handle || (*i)->end_handle() < min_start_handle) continue; for (iterator j = (*i)->seqManData.firstSequence; j != end() && (*j)->start_handle() <= (max_end_handle + 1) && (*j)->data() == *i; ++j) { if ((*j)->end_handle() + 1 < min_start_handle) continue; if ((*j)->start_handle() > (*i)->start_handle() && (*j)->start_handle() > min_start_handle) { append_out = false; return j; } if ((*j)->end_handle() < (*i)->end_handle() && (*j)->end_handle() < max_end_handle) { append_out = true; return j; } } } return end(); } bool TypeSequenceManager::is_free_sequence(EntityHandle start, EntityID num_entities, SequenceData*& data_out, int values_per_ent) { data_out = 0; if (empty()) return true; const_iterator i = lower_bound(start); if (i == end()) { --i; // Safe because already tested empty() // If we don't overlap the last data object... if ((*i)->data()->end_handle() < start) return true; data_out = (*i)->data(); if ((*i)->values_per_entity() != values_per_ent) return false; // If we overlap a data object, we must be entirely inside of it return start + num_entities - 1 <= (*i)->data()->end_handle(); } #ifndef NDEBUG if (i != begin()) { const_iterator j = i; --j; assert((*j)->end_handle() < start); } #endif // Check if we fit in the block of free handles if (start + num_entities > (*i)->start_handle()) // start + num + 1 >= i->start return false; // Check if we overlap the data for the next sequence if (start + num_entities > (*i)->data()->start_handle()) { data_out = (*i)->data(); if ((*i)->values_per_entity() != values_per_ent) return false; // If overlap, must be entirely contained return start >= data_out->start_handle() && start + num_entities - 1 <= data_out->end_handle(); } // Check if we overlap the data for the previous sequence if (i != begin()) { --i; if ((*i)->data()->end_handle() >= start) { data_out = (*i)->data(); if ((*i)->values_per_entity() != values_per_ent) return false; return start + num_entities - 1 <= (*i)->data()->end_handle(); } } // Unused handle block that overlaps no SequenceData return true; } EntityHandle TypeSequenceManager::find_free_block(EntityID num_entities, EntityHandle min_start_handle, EntityHandle max_end_handle) { const_iterator i = lower_bound(min_start_handle); if (i == end()) return min_start_handle; if ((*i)->start_handle() < min_start_handle + num_entities) return min_start_handle; EntityHandle prev_end = (*i)->end_handle(); ++i; for (; i != end(); prev_end = (*i)->end_handle(), ++i) { EntityID len = (*i)->start_handle() - prev_end - 1; if (len >= num_entities) break; } if (prev_end + num_entities > max_end_handle) return 0; else return prev_end + 1; } struct range_data { EntityID num_entities; EntityHandle min_start_handle, max_end_handle; EntityHandle first, last; }; static bool check_range(const range_data& d, bool prefer_end, EntityHandle& result) { EntityHandle first = std::max(d.min_start_handle, d.first); EntityHandle last = std::min(d.max_end_handle, d.last); if (last < first + d.num_entities - 1) { result = 0; return false; } result = prefer_end ? last + 1 - d.num_entities : first; return true; } EntityHandle TypeSequenceManager::find_free_sequence(EntityID num_entities, EntityHandle min_start_handle, EntityHandle max_end_handle, SequenceData*& data_out, EntityID &data_size, int num_verts) { if (max_end_handle < min_start_handle + num_entities - 1) return 0; EntityHandle result; iterator p, i = lower_bound(min_start_handle); range_data d = {num_entities, min_start_handle, max_end_handle, 0, 0}; if (i == end()) { data_out = 0; return min_start_handle; } else if (i == begin()) { if ((*i)->values_per_entity() == num_verts) { d.first = (*i)->data()->start_handle(); d.last = (*i)->start_handle() - 1; if (check_range(d, true, result)) { data_out = (*i)->data(); return result; } } d.first = min_start_handle; d.last = (*i)->data()->start_handle() - 1; if (check_range(d, true, result)) { data_out = 0; // This will back up against the end of the seq data, so // size the data that way data_size = num_entities; return result; } p = i++; } else { p = i; --p; } for (; i != end() && (*i)->start_handle() < max_end_handle; p = i++) { if ((*p)->data() == (*i)->data()) { if ((*p)->values_per_entity() == num_verts) { d.first = (*p)->end_handle() + 1; d.last = (*i)->start_handle() - 1; if (check_range(d, false, result)) { data_out = (*p)->data(); return result; } } } else { if ((*p)->values_per_entity() == num_verts) { d.first = (*p)->end_handle() + 1; d.last = (*p)->data()->end_handle(); if (check_range(d, false, result)) { data_out = (*p)->data(); return result; } } if ((*i)->values_per_entity() == num_verts) { d.first = (*i)->data()->start_handle(); d.last = (*i)->start_handle() - 1; if (check_range(d, true, result)) { data_out = (*i)->data(); return result; } } d.first = (*p)->data()->end_handle() + 1; d.last = (*i)->data()->start_handle() - 1; if (check_range(d, false, result)) { data_out = 0; data_size = d.last - d.first + 1; return result; } } } if ((*p)->values_per_entity() == num_verts) { d.first = (*p)->end_handle() + 1; d.last = (*p)->data()->end_handle(); if (check_range(d, false, result)) { data_out = (*p)->data(); return result; } } d.first = (*p)->data()->end_handle() + 1; d.last = max_end_handle; if (check_range(d, false, result)) { data_out = 0; return result; } data_out = 0; return 0; } EntityHandle TypeSequenceManager::last_free_handle(EntityHandle after_this) const { int junk; const_iterator it = lower_bound(after_this); if (it == end()) return CREATE_HANDLE(TYPE_FROM_HANDLE(after_this), MB_END_ID, junk); else if ((*it)->start_handle() > after_this) { // Need to check against the sequence data first EntityHandle rhandle = (*it)->data()->start_handle(); return rhandle - 1; } else return 0; } ErrorCode TypeSequenceManager::check_valid_handles(Error* /* error_handler */, EntityHandle first, EntityHandle last) const { const_iterator i = lower_bound(first); if (i == end() || (*i)->start_handle() > first) { #if 0 // MB_ENTITY_NOT_FOUND could be a non-error condition, do not call // MB_SET_ERR on it fprintf( stderr, "[Warning]: Invalid entity handle: 0x%lx\n", (unsigned long)first ); #endif return MB_ENTITY_NOT_FOUND; } while ((*i)->end_handle() < last) { EntityHandle prev_end = (*i)->end_handle(); ++i; if (i == end() || prev_end + 1 != (*i)->start_handle()) return MB_ENTITY_NOT_FOUND; } return MB_SUCCESS; } ErrorCode TypeSequenceManager::erase(Error* /* error_handler */, EntityHandle h) { EntitySequence* seq = find(h); if (!seq) { #if 0 // MB_ENTITY_NOT_FOUND could be a non-error condition, do not call // MB_SET_ERR on it fprintf( stderr, "[Warning]: Invalid entity handle: 0x%lx\n", (unsigned long)h ); #endif return MB_ENTITY_NOT_FOUND; } if (seq->start_handle() == h) { if (seq->end_handle() != h) { if (seq->using_entire_data()) availableList.insert(seq->data()); seq->pop_front(1); return MB_SUCCESS; } SequenceData* data = seq->data(); bool delete_data; ErrorCode rval = remove_sequence(seq, delete_data); if (MB_SUCCESS != rval) return rval; delete seq; if (delete_data) delete data; } else if (seq->end_handle() == h) { if (seq->using_entire_data()) availableList.insert(seq->data()); seq->pop_back(1); } else { iterator i = lower_bound(h); if ((*i)->using_entire_data()) availableList.insert((*i)->data()); i = split_sequence(i, h); seq = *i; assert(seq->start_handle() == h); seq->pop_front(1); } return MB_SUCCESS; } ErrorCode TypeSequenceManager::erase(Error* /* error */, EntityHandle first, EntityHandle last) { // First check that all entities in range are valid ErrorCode rval = check_valid_handles(NULL, first, last); if (MB_SUCCESS != rval) return rval; // Now remove entities // Get first sequence intersecting range iterator i = lower_bound(first); if (i == end()) // Shouldn't be possible given check_valid_handles call above. return MB_ENTITY_NOT_FOUND; // If range is entirely in interior of sequence, need to split sequence. if ((*i)->start_handle() < first && (*i)->end_handle() > last) { if ((*i)->using_entire_data()) availableList.insert((*i)->data()); i = split_sequence(i, first); (*i)->pop_front(last - first + 1); assert(check_valid_data(*i)); return MB_SUCCESS; } // If range doesn't entirely contain first sequence, remove some // handles from the end of the sequence and advance to the next // sequence. if ((*i)->start_handle() < first) { if ((*i)->using_entire_data()) availableList.insert((*i)->data()); (*i)->pop_back((*i)->end_handle() - first + 1); ++i; } // Destroy all sequences contained entirely within the range while (i != end() && (*i)->end_handle() <= last) i = erase(i); // If necessary, remove entities from the beginning of the // last sequence. if (i != end() && (*i)->start_handle() <= last) { if ((*i)->using_entire_data()) availableList.insert((*i)->data()); (*i)->pop_front(last - (*i)->start_handle() + 1); assert(check_valid_data(*i)); } return MB_SUCCESS; } TypeSequenceManager::iterator TypeSequenceManager::split_sequence(iterator i, EntityHandle h) { EntitySequence* seq = (*i)->split(h); if (!seq) return end(); i = sequenceSet.insert(i, seq); assert(check_valid_data(*i)); return i; } ErrorCode TypeSequenceManager::is_free_handle(EntityHandle handle, iterator& seq_iter_out, SequenceData*& data_ptr_out, EntityHandle& block_start, EntityHandle& block_end, int values_per_ent) { int junk; block_start = CREATE_HANDLE(TYPE_FROM_HANDLE(handle), MB_START_ID, junk); block_end = CREATE_HANDLE(TYPE_FROM_HANDLE(handle), MB_END_ID, junk); iterator i = lower_bound(handle); if (i != end()) { block_end = (*i)->start_handle() - 1; // If sequence contains handle, then already allocated if ((*i)->start_handle() <= handle) return MB_ALREADY_ALLOCATED; // Handle is not within an existing sequence, but is // within an existing SequenceData... if ((*i)->data()->start_handle() <= handle) { // If values_per_entity don't match, can't put new entity // in existing SequenceData if ((*i)->values_per_entity() != values_per_ent) return MB_ALREADY_ALLOCATED; data_ptr_out = (*i)->data(); if (block_end == handle) { // Prepend to existing sequence seq_iter_out = i; block_start = handle; } else { // Add new sequence to existing SequenceData seq_iter_out = end(); if (i == begin() || (*--i)->data() != data_ptr_out) block_start = data_ptr_out->start_handle(); else block_start = (*i)->end_handle() + 1; } return MB_SUCCESS; } } if (i != begin()) { --i; block_start = (*i)->end_handle() + 1; // Handle is within previous sequence data... if ((*i)->data()->end_handle() >= handle) { // If values_per_entity don't match, can't put new entity // in existing SequenceData if ((*i)->values_per_entity() != values_per_ent) return MB_ALREADY_ALLOCATED; data_ptr_out = (*i)->data(); if (block_start == handle) { // Append to existing sequence seq_iter_out = i; block_end = handle; } else { // Add new sequence to existing SequenceData seq_iter_out = end(); if (++i == end() || (*i)->data() != data_ptr_out) block_end = data_ptr_out->end_handle(); else block_end = (*i)->start_handle() - 1; } return MB_SUCCESS; } } seq_iter_out = end(); data_ptr_out = 0; return MB_SUCCESS; } ErrorCode TypeSequenceManager::notify_appended(iterator seq) { ErrorCode rval = check_merge_next(seq); if ((*seq)->using_entire_data()) availableList.erase((*seq)->data()); return rval; } ErrorCode TypeSequenceManager::notify_prepended(iterator seq) { ErrorCode rval = check_merge_prev(seq); if ((*seq)->using_entire_data()) availableList.erase((*seq)->data()); return rval; } void TypeSequenceManager::get_memory_use(unsigned long long& entity_storage, unsigned long long& total_storage) const { entity_storage = total_storage = 0; if (empty()) return; EntityType mytype = TYPE_FROM_HANDLE(lastReferenced->start_handle()); int junk; get_memory_use(CREATE_HANDLE(mytype, MB_START_ID, junk), CREATE_HANDLE(mytype, MB_END_ID, junk), entity_storage, total_storage); } void TypeSequenceManager::append_memory_use(EntityHandle first, EntityHandle last, const SequenceData* data, unsigned long long& entity_storage, unsigned long long& total_storage) const { const unsigned long allocated_count = data->size(); unsigned long bytes_per_ent, seq_size; const_iterator i = data->seqManData.firstSequence; (*i)->get_const_memory_use(bytes_per_ent, seq_size); unsigned long other_ent_mem = 0; unsigned long occupied_count = 0, entity_count = 0, sequence_count = 0; for (; i != end() && (*i)->data() == data; ++i) { occupied_count += (*i)->size(); ++sequence_count; EntityHandle start = std::max(first, (*i)->start_handle()); EntityHandle stop = std::min(last, (*i)->end_handle()); if (stop < start) continue; entity_count += stop - start + 1; other_ent_mem += (*i)->get_per_entity_memory_use(start, stop); } unsigned long sum = sequence_count * seq_size + allocated_count * bytes_per_ent; // Watch for overflow assert(entity_count > 0 && occupied_count > 0 && allocated_count > 0); if (std::numeric_limits::max() / entity_count <= sum) { total_storage += sum * (entity_count / occupied_count) + other_ent_mem; entity_storage += sum * (entity_count / allocated_count) + other_ent_mem; } else { total_storage += sum * entity_count / occupied_count + other_ent_mem; entity_storage += sum * entity_count / allocated_count + other_ent_mem; } } void TypeSequenceManager::get_memory_use(EntityHandle first, EntityHandle last, unsigned long long& entity_storage, unsigned long long& total_storage) const { entity_storage = total_storage = 0; while (first <= last) { const_iterator i = lower_bound(first); if (i == end()) return; SequenceData* data = (*i)->data(); if (first < data->end_handle()) { append_memory_use(first, last, data, entity_storage, total_storage); } first = data->end_handle() + 1; } } EntityID TypeSequenceManager::get_occupied_size(const SequenceData* data) const { EntityID result = 0; for (const_iterator i = data->seqManData.firstSequence; i != end() && (*i)->data() == data; ++i) result += (*i)->size(); return result; } #ifndef NDEBUG bool TypeSequenceManager::check_valid_data(const EntitySequence* seq) const { // Caller passed a sequence that should be contained, so cannot be empty if (empty()) return false; // Make sure lastReferenced points to something if (!lastReferenced) return false; const_iterator seqi = sequenceSet.lower_bound(lastReferenced); if (seqi == sequenceSet.end() || *seqi != lastReferenced) return false; // Make sure passed sequence is in list const EntitySequence* seq2 = find(seq->start_handle()); if (seq2 != seq) return false; // Check all sequences referencing the same SequenceData const SequenceData* data = seq->data(); const_iterator i = lower_bound(data->start_handle()); if (i != data->seqManData.firstSequence) return false; if (i != begin()) { const_iterator j = i; --j; if ((*j)->end_handle() >= data->start_handle()) return false; if ((*j)->data()->end_handle() >= data->start_handle()) return false; } for (;;) { seq2 = *i; ++i; if (i == end()) return true; if ((*i)->data() != data) break; if (seq2->end_handle() >= (*i)->start_handle()) return false; } if ((*i)->start_handle() <= data->end_handle()) return false; if ((*i)->data()->start_handle() <= data->end_handle()) return false; return true; } #endif } // namespace moab