68 Take RFactor of insertions/deletions on text field

A delta on a xvector field is represented by a set of ordered insertion intervals I and ordered deletion intervals D.

I,D are expressed in the same post insertion q-coordinates.

B represents a contiguous buffer of the elements to be inserted but not deleted by the delta. Each element takes up 'elementSize' bytes.

Precondition : B.size() == (I\D).Extent() * elementSize.

v is a given causally valid vector time. This function replaces I by RFactor(I,v). It also partitions B into Binside and Boutside according to whether an element in B (i.e. an element to be inserted but not deleted by the delta) was inserted by an operation identified by (s,t) inside X(v) or not.

I ← Rfactor(I,v)

Binside, Boutside ← B partitioned according to whether the insertion is inside X(v) or outside X(v).

void TakeRFactor(
    const VectorTime& v,
    VectorInsertions& I,
    const VectorDeletions& D,
    ssize_t elementSize,
    const xvector<octet_t>& B,
    xvector<octet_t>& Binside,
    xvector<octet_t>& Boutside)
{
    Binside.clear();
    Boutside.clear();

    // For processing the buffer B associated with insertions for I\D
    const octet_t* p = B.data();

    // Allow for measurement of extents of ([0,q) intersect D) for monotone increasing
    // values of q.
    MeasureExtentsOnVectorDeletions measureDExtents(MakeRangeFromBiDirectionalIterators(D));

    // Iterate through the intervals of I. For each interval we determine whether it exists
    // in X(v). This allows us to delete intervals inside X(v) as we go, in order to convert
    // I into an RFactor.
    VectorInsertions::Node* i = I.m_c.m_first;
    while(i)
    {
        /*
        Note that D may split interval i up into any number of parts

                 q1                                  q2
                 [------------------------------------)         i (an interval of I)
                     [--)     [---)           [------------)    D
                 [---)  [-----)   [-----------)                 i\D


        For each i = [q1,q2) in I we can use measureDExtents to measure

            e1 = D.extent(q1)
            e2 = D.extent(q2)

        Then e2-e1 represents the extent of (i intersect D).

        That means the extent of i\D is

            (q2-q1) - (e2-e1)

        Let

            n = ((q2-q1) - (e2-e1)) * elementSize

        n represents the number of bytes in B associated with the insertion i.
        */

        // Note that e1 must be calculated before e2, in order to follow the rule that
        // measureDExtents.Extent(q) is called with monotone increasing values of q.
        const ssize_t e1 = measureDExtents.Extent(i->m_q);
        const ssize_t e2 = measureDExtents.Extent(i->m_q + i->m_n);
        const ssize_t n = (i->m_n - (e2-e1)) * elementSize;    // total extent of (i\D)

        VectorInsertions::Node* next = i->m_next;
        if (v.ExtentContains(i->m_opid))
        {
            // i inside X(v)
            Binside.append(p, p+n);

            // i is not part of the RFactor so erase i from I
            I.m_c.Erase(i);
        }
        else
        {
            // i outside X(V)
            Boutside.append(p, p+n);
        }
        p += n;
        i = next;
    }
    cxAssert(p == B.data_end());
}

Concept: FilteredListUpdater

A FilteredListUpdater is a concept with drop and keep methods which is used to define which elements in a linear list to drop and keep.

concept FilteredListUpdater
{
    void drop(ssize_t n);   // drop the next n elements of the linear list
    void keep(ssize_t n);   // keep the next n elements of the linear list
};

A realisation of this concept on a reflected xvector is as follows:

class FilteredListUpdaterOnReflectedVector
{
public:
    FilteredListUpdaterOnReflectedVector(ReflectedVector& rv) :
        rv(rv)
    {
        keepPos = dropPos = rv.vf->data();
    }

    void drop(ssize_t n)
    {
        if (!isPOD)
        {
            DestructReflectedArrayVariable(rbcElement, n, dropPos);
        }
        dropPos += n * elementSize;
    }

    void keep(ssize_t n)
    {
        ssize_t numBytes = n * elementSize;
        std::memmove(keepPos, dropPos, numBytes);
        keepPos += numBytes;
        dropPos += numBytes;
    }

private:
    ReflectedVector& rv;
    octet_t* keepPos;
    octet_t* dropPos;
};

New idea

We want the function TakeRFactor to be moved from cxOperation to cxOT.

A non-leaky abstraction of a vector is needed, which doesn't have to deal with the vector element size.

Splitting B into Binside, Boutside is not required. Instead we just want to transform B to Boutside and delete the elements that we were moving into Binside.

Consider that we assume the vector B is represented by a variable of type VectorFieldUpdater. The RFactor of B is defined by the calls to keep/drop on its elements.

// Calculates the RFactor of the composite operation (I,D,B) with respect to vector time v
// and replaces I and B with their RFactor versions.
// For an optimisation D is not updated because it happen not to be required.
template<typename VectorInsertions, typename VectorDeletions, typename FilteredListUpdater, typename VectorTime>
void TakeRFactor(VectorInsertions& I, const VectorDeletions& D, FilteredListUpdater& B, const VectorTime& v)
{
    // Allow for measurement of extents of ([0,q) intersect D) for monotone increasing
    // values of q.
    MeasureExtentsOnVectorDeletions measureDExtents(MakeRangeFromBiDirectionalIterators(D));

    // Iterate through the intervals of I. For each interval we determine whether it exists
    // in X(v). This allows us to delete intervals inside X(v) as we go, in order to convert
    // I into an RFactor.
    for (auto i = I.m_c.m_first ; i ; i = i->m_next)
    {
        /*
        Note that D may split interval i up into any number of parts

                 q1                                  q2
                 [------------------------------------)         i (an interval of I)
                     [--)     [---)           [------------)    D
                 [---)  [-----)   [-----------)                 i\D


        For each i = [q1,q2) in I we can use measureDExtents to measure

            e1 = D.extent(q1)
            e2 = D.extent(q2)

        Then e2-e1 represents the extent of (i intersect D).

        That means the extent of i\D is

            n = (q2-q1) - (e2-e1)

        n represents the number of elements in B associated with the insertion interval i.
        */

        // Note that e1 must be calculated before e2, in order to follow the rule that
        // measureDExtents.Extent(q) is called with monotone increasing values of q.
        auto e1 = measureDExtents.Extent(i->m_q);
        auto e2 = measureDExtents.Extent(i->m_q + i->m_n);
        auto n = (i->m_n - (e2-e1));    // total extent of (i\D)

        if (v.ExtentContains(i->m_opid))
        {
            // i inside X(v)
            B.drop(n);
            I.m_c.Erase(i);     // i is not part of the RFactor
        }
        else
        {
            // i outside X(V)
            B.keep(n);
        }
    }
}

VectorRFactor.h in cxOT

Source: Ceda/cxOT/VectorRFactor.h