aboutsummaryrefslogtreecommitdiffstats
path: root/library/cpp/containers/comptrie/writeable_node.cpp
blob: d0e986f99a0d2ab8a12fbcf2263fdc2d6ea94071 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
#include "writeable_node.h"
#include "node.h"
#include "comptrie_impl.h"

namespace NCompactTrie {
    TWriteableNode::TWriteableNode() 
        : LeafPos(nullptr) 
        , LeafLength(0) 
        , ForwardOffset(NPOS) 
        , LeftOffset(NPOS) 
        , RightOffset(NPOS) 
        , Label(0) 
    { 
    } 

    static size_t GetOffsetFromEnd(const TNode& node, size_t absOffset) { 
        return absOffset ? absOffset - node.GetOffset() - node.GetCoreLength() : NPOS; 
    } 
 
    TWriteableNode::TWriteableNode(const TNode& node, const char* data) 
        : LeafPos(node.IsFinal() ? data + node.GetLeafOffset() : nullptr) 
        , LeafLength(node.GetLeafLength()) 
        , ForwardOffset(GetOffsetFromEnd(node, node.GetForwardOffset())) 
        , LeftOffset(GetOffsetFromEnd(node, node.GetLeftOffset())) 
        , RightOffset(GetOffsetFromEnd(node, node.GetRightOffset())) 
        , Label(node.GetLabel()) 
    { 
    } 

    size_t TWriteableNode::Measure() const { 
        size_t len = 2 + LeafLength; 
        size_t fwdLen = 0; 
        size_t lastLen = 0; 
        size_t lastFwdLen = 0; 
        // Now, increase all the offsets by the length and recalculate everything, until it converges 
        do { 
            lastLen = len; 
            lastFwdLen = fwdLen; 

            len = 2 + LeafLength; 
            len += MeasureOffset(LeftOffset != NPOS ? LeftOffset + lastLen : 0); 
            len += MeasureOffset(RightOffset != NPOS ? RightOffset + lastLen : 0); 
 
            // Relative forward offset of 0 means we don't need extra length for an epsilon link. 
            // But an epsilon link means we need an extra 1 for the flags and the forward offset is measured 
            // from the start of the epsilon link, not from the start of our node. 
            if (ForwardOffset != NPOS && ForwardOffset != 0) { 
                fwdLen = MeasureOffset(ForwardOffset + lastFwdLen) + 1; 
                len += fwdLen; 
            } 

        } while (lastLen != len || lastFwdLen != fwdLen); 

        return len; 
    } 

    size_t TWriteableNode::Pack(char* buffer) const { 
        const size_t length = Measure(); 

        char flags = 0; 
        if (LeafPos) { 
            flags |= MT_FINAL; 
        } 
        if (ForwardOffset != NPOS) { 
            flags |= MT_NEXT; 
        } 

        const size_t leftOffset = LeftOffset != NPOS ? LeftOffset + length : 0; 
        const size_t rightOffset = RightOffset != NPOS ? RightOffset + length : 0; 
        const size_t leftOffsetSize = MeasureOffset(leftOffset); 
        const size_t rightOffsetSize = MeasureOffset(rightOffset); 
        flags |= (leftOffsetSize << MT_LEFTSHIFT); 
        flags |= (rightOffsetSize << MT_RIGHTSHIFT); 

        buffer[0] = flags; 
        buffer[1] = Label; 
        size_t usedLen = 2; 
        usedLen += PackOffset(buffer + usedLen, leftOffset); 
        usedLen += PackOffset(buffer + usedLen, rightOffset); 

        if (LeafPos && LeafLength) { 
            memcpy(buffer + usedLen, LeafPos, LeafLength); 
            usedLen += LeafLength; 
        } 
 
        if (ForwardOffset != NPOS && ForwardOffset != 0) { 
            const size_t fwdOffset = ForwardOffset + length - usedLen; 
            size_t fwdOffsetSize = MeasureOffset(fwdOffset); 
            buffer[usedLen++] = (char)(fwdOffsetSize & MT_SIZEMASK); 
            usedLen += PackOffset(buffer + usedLen, fwdOffset); 
        } 
        Y_ASSERT(usedLen == length); 
        return usedLen; 
    }

}