#include "opaque_trie_iterator.h" #include "comptrie_impl.h" #include "node.h" namespace NCompactTrie { TOpaqueTrieIterator::TOpaqueTrieIterator(const TOpaqueTrie& trie, const char* emptyValue, bool atend, size_t maxKeyLength) : Trie(trie) , EmptyValue(emptyValue) , AtEmptyValue(emptyValue && !atend) , MaxKeyLength(maxKeyLength) { if (!AtEmptyValue && !atend) Forward(); } bool TOpaqueTrieIterator::operator==(const TOpaqueTrieIterator& rhs) const { return (Trie == rhs.Trie && Forks == rhs.Forks && EmptyValue == rhs.EmptyValue && AtEmptyValue == rhs.AtEmptyValue && MaxKeyLength == rhs.MaxKeyLength); } bool TOpaqueTrieIterator::HasMaxKeyLength() const { return MaxKeyLength != size_t(-1) && MeasureNarrowKey() == MaxKeyLength; } bool TOpaqueTrieIterator::Forward() { if (AtEmptyValue) { AtEmptyValue = false; bool res = Forward(); // TODO delete this after format change if (res && MeasureNarrowKey() != 0) { return res; // there was not "\0" key } // otherwise we are skipping "\0" key } if (!Trie.Length) return false; if (Forks.Empty()) { TFork fork(Trie.Data, 0, Trie.Length, Trie.SkipFunction); Forks.Push(fork); } else { TFork* topFork = &Forks.Top(); while (!topFork->NextDirection()) { if (topFork->Node.GetOffset() >= Trie.Length) return false; Forks.Pop(); if (Forks.Empty()) return false; topFork = &Forks.Top(); } } Y_ASSERT(!Forks.Empty()); while (Forks.Top().CurrentDirection != D_FINAL && !HasMaxKeyLength()) { TFork nextFork = Forks.Top().NextFork(Trie.SkipFunction); Forks.Push(nextFork); } TFork& top = Forks.Top(); static_assert(D_FINAL < D_NEXT, "relative order of NEXT and FINAL directions has changed"); if (HasMaxKeyLength() && top.CurrentDirection == D_FINAL && top.HasDirection(D_NEXT)) { top.NextDirection(); } return true; } bool TOpaqueTrieIterator::Backward() { if (AtEmptyValue) return false; if (!Trie.Length) { if (EmptyValue) { // A trie that has only the empty value; // we are not at the empty value, so move to it. AtEmptyValue = true; return true; } else { // Empty trie. return false; } } if (Forks.Empty()) { TFork fork(Trie.Data, 0, Trie.Length, Trie.SkipFunction); fork.LastDirection(); Forks.Push(fork); } else { TFork* topFork = &Forks.Top(); while (!topFork->PrevDirection()) { if (topFork->Node.GetOffset() >= Trie.Length) return false; Forks.Pop(); if (!Forks.Empty()) { topFork = &Forks.Top(); } else { // When there are no more forks, // we have to iterate over the empty value. if (!EmptyValue) return false; AtEmptyValue = true; return true; } } } Y_ASSERT(!Forks.Empty()); while (Forks.Top().CurrentDirection != D_FINAL && !HasMaxKeyLength()) { TFork nextFork = Forks.Top().NextFork(Trie.SkipFunction); nextFork.LastDirection(); Forks.Push(nextFork); } TFork& top = Forks.Top(); static_assert(D_FINAL < D_NEXT, "relative order of NEXT and FINAL directions has changed"); if (HasMaxKeyLength() && top.CurrentDirection == D_NEXT && top.HasDirection(D_FINAL)) { top.PrevDirection(); } if (MeasureNarrowKey() == 0) { // This is the '\0' key, skip it and get to the EmptyValue. AtEmptyValue = true; Forks.Clear(); } return true; } const char* TOpaqueTrieIterator::GetValuePtr() const { if (!Forks.Empty()) { const TFork& lastFork = Forks.Top(); Y_ASSERT(lastFork.Node.IsFinal() && lastFork.CurrentDirection == D_FINAL); return Trie.Data + lastFork.GetValueOffset(); } Y_ASSERT(AtEmptyValue); return EmptyValue; } //------------------------------------------------------------------------- TString TForkStack::GetKey() const { if (HasEmptyKey()) { return TString(); } TString result(Key); if (TopHasLabelInKey()) { result.append(Top().GetLabel()); } return result; } bool TForkStack::HasEmptyKey() const { // Special case: if we get a single zero label, treat it as an empty key // TODO delete this after format change if (TopHasLabelInKey()) { return Key.size() == 0 && Top().GetLabel() == '\0'; } else { return Key.size() == 1 && Key[0] == '\0'; } } size_t TForkStack::MeasureKey() const { size_t result = Key.size() + (TopHasLabelInKey() ? 1 : 0); if (result == 1 && HasEmptyKey()) { return 0; } return result; } //------------------------------------------------------------------------- TFork::TFork(const char* data, size_t offset, size_t limit, const ILeafSkipper& skipper) : Node(data, offset, skipper) , Data(data) , Limit(limit) , CurrentDirection(TDirection(0)) { #if COMPTRIE_DATA_CHECK if (Node.GetOffset() >= Limit - 1) ythrow yexception() << "gone beyond the limit, data is corrupted"; #endif while (CurrentDirection < D_MAX && !HasDirection(CurrentDirection)) { ++CurrentDirection; } } bool TFork::operator==(const TFork& rhs) const { return (Data == rhs.Data && Node.GetOffset() == rhs.Node.GetOffset() && CurrentDirection == rhs.CurrentDirection); } inline bool TFork::NextDirection() { do { ++CurrentDirection; } while (CurrentDirection < D_MAX && !HasDirection(CurrentDirection)); return CurrentDirection < D_MAX; } inline bool TFork::PrevDirection() { if (CurrentDirection == TDirection(0)) { return false; } do { --CurrentDirection; } while (CurrentDirection > 0 && !HasDirection(CurrentDirection)); return HasDirection(CurrentDirection); } void TFork::LastDirection() { CurrentDirection = D_MAX; PrevDirection(); } bool TFork::SetDirection(TDirection direction) { if (!HasDirection(direction)) { return false; } CurrentDirection = direction; return true; } char TFork::GetLabel() const { return Node.GetLabel(); } size_t TFork::GetValueOffset() const { return Node.GetLeafOffset(); } }