LOCRDT editeng,sw: yrs weak link peer cursors for editengine
Replace unreliable integer cursors with WeakRef cursors which maintain their position automatically. This requires yrs v0.23.1 with the yweak_read() function. Unfortunately this crashes currently, so hide it behind #if YRS_WEAK. https://github.com/y-crdt/y-crdt/issues/536 Change-Id: I74ec1eaa25c7cb554efd38ee632df041ac92d5c2 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/183311 Tested-by: Jenkins Reviewed-by: Michael Stahl <michael.stahl@allotropia.de>
This commit is contained in:
@@ -51,6 +51,8 @@ class ImpEditEngine;
|
||||
class IYrsTransactionSupplier;
|
||||
typedef struct TransactionInner YTransaction;
|
||||
typedef struct YTextEvent YTextEvent;
|
||||
typedef struct Branch Branch;
|
||||
typedef struct YOutput YOutput;
|
||||
#endif
|
||||
|
||||
|
||||
@@ -134,6 +136,7 @@ public:
|
||||
void YrsWriteEEState();
|
||||
void YrsReadEEState(YTransaction *, ImpEditEngine & rIEE);
|
||||
void YrsApplyEEDelta(YTransaction *, YTextEvent const* pEvent, ImpEditEngine & rIEE);
|
||||
bool YrsWriteEECursor(YTransaction *, Branch const& rArray, YOutput const* pCurrent, EditSelection const& rSelection);
|
||||
::std::optional<EditSelection> YrsReadEECursor(::std::pair<int64_t, int64_t> point,
|
||||
::std::optional<::std::pair<int64_t, int64_t>> oMark);
|
||||
void YrsSetStyle(sal_Int32 nPara, ::std::u16string_view rStyle);
|
||||
|
||||
@@ -2712,12 +2712,98 @@ void EditDoc::YrsReadEEState(YTransaction *const pTxn, ImpEditEngine & rIEE)
|
||||
rIEE.RemoveParagraph(nodes); // remove pre-existing one from InitDoc()
|
||||
}
|
||||
|
||||
bool EditDoc::YrsWriteEECursor(YTransaction *const pTxn, Branch const& rArray,
|
||||
YOutput const*const pCurrent, EditSelection const& rSelection)
|
||||
{
|
||||
EditSelection sel{rSelection};
|
||||
sel.Adjust(*this); // unfortunately min/max naming may be misleading...
|
||||
YrsWrite const yw{GetYrsWrite(m_pYrsSupplier, m_CommentId)};
|
||||
uint32_t start{0};
|
||||
for (auto paras{GetPos(sel.Min().GetNode())}; paras != 0; --paras)
|
||||
{
|
||||
start += GetObject(paras-1)->Len() + 1;
|
||||
}
|
||||
uint32_t end{start};
|
||||
start += sel.Min().GetIndex();
|
||||
if (sel.HasRange())
|
||||
{
|
||||
auto const nStartNode{GetPos(sel.Min().GetNode())};
|
||||
for (auto paras{GetPos(sel.Max().GetNode()) - nStartNode};
|
||||
paras != 0; --paras)
|
||||
{
|
||||
end += GetObject(nStartNode + paras-1)->Len() + 1;
|
||||
}
|
||||
end += sel.Max().GetIndex();
|
||||
}
|
||||
else
|
||||
{
|
||||
end = start;
|
||||
}
|
||||
if (pCurrent != nullptr && pCurrent->tag == Y_ARRAY
|
||||
&& yarray_len(pCurrent->value.y_type) == 2
|
||||
// could do without the comment id? no, empty-text leaves pBranch null!
|
||||
&& yarray_get(pCurrent->value.y_type, pTxn, 0)->tag == Y_JSON_STR
|
||||
&& strcmp(yarray_get(pCurrent->value.y_type, pTxn, 0)->value.str, m_CommentId.getStr()) == 0)
|
||||
{
|
||||
::std::unique_ptr<YOutput, YOutputDeleter> const pWeak{yarray_get(pCurrent->value.y_type, pTxn, 1)};
|
||||
yvalidate(pWeak->tag == Y_WEAK_LINK);
|
||||
Branch * pBranch{nullptr};
|
||||
uint32_t oldStart{SAL_MAX_UINT32};
|
||||
uint32_t oldEnd{SAL_MAX_UINT32};
|
||||
yweak_read(pWeak->value.y_type, pTxn, &pBranch, &oldStart, &oldEnd);
|
||||
assert(oldStart != SAL_MAX_UINT32 && oldEnd != SAL_MAX_UINT32);
|
||||
if (oldStart == start && oldEnd == end)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
Weak const*const pWeak{ytext_quote(yw.pText, pTxn, start, end, Y_FALSE, Y_TRUE)};
|
||||
::std::vector<YInput> positions;
|
||||
positions.push_back(yinput_string(m_CommentId.getStr()));
|
||||
positions.push_back(yinput_weak(pWeak));
|
||||
YInput const input{yinput_yarray(positions.data(), 2)};
|
||||
yarray_remove_range(&rArray, pTxn, 1, 1);
|
||||
yarray_insert_range(&rArray, pTxn, 1, &input, 1);
|
||||
// yweak_destroy(pWeak); // NO! UAF
|
||||
return true;
|
||||
}
|
||||
|
||||
::std::optional<EditSelection> EditDoc::YrsReadEECursor(
|
||||
::std::pair<int64_t, int64_t> const i_point,
|
||||
::std::optional<::std::pair<int64_t, int64_t>> const i_oMark)
|
||||
{
|
||||
// TODO should not use ints here?
|
||||
::std::optional<EditPaM> oMark;
|
||||
#if defined(YRS_WEAK)
|
||||
auto start{i_point.first}; // returned from yweak_read(), need to convert
|
||||
ContentNode * pNode{nullptr};
|
||||
for (decltype(Count()) i = 0; i < Count(); ++i)
|
||||
{
|
||||
pNode = GetObject(i);
|
||||
if (start <= o3tl::make_unsigned(pNode->Len()))
|
||||
{
|
||||
break;
|
||||
}
|
||||
start -= pNode->Len() + 1;
|
||||
}
|
||||
yvalidate(start <= o3tl::make_unsigned(pNode->Len()));
|
||||
EditPaM point{pNode, static_cast<sal_Int32>(start)};
|
||||
if (i_oMark)
|
||||
{
|
||||
auto end{i_oMark->first};
|
||||
for (decltype(Count()) i = 0; i < Count(); ++i)
|
||||
{
|
||||
pNode = GetObject(i);
|
||||
if (end <= o3tl::make_unsigned(pNode->Len()))
|
||||
{
|
||||
break;
|
||||
}
|
||||
end -= pNode->Len() + 1;
|
||||
}
|
||||
yvalidate(end <= o3tl::make_unsigned(pNode->Len()));
|
||||
oMark.emplace(pNode, static_cast<sal_Int32>(end));
|
||||
}
|
||||
#else
|
||||
if (i_oMark)
|
||||
{
|
||||
// yvalidate(i_oMark->first < Count());
|
||||
@@ -2750,6 +2836,7 @@ void EditDoc::YrsReadEEState(YTransaction *const pTxn, ImpEditEngine & rIEE)
|
||||
return {};
|
||||
}
|
||||
EditPaM point{&rNode, static_cast<sal_Int32>(i_point.second)};
|
||||
#endif
|
||||
|
||||
return EditSelection{point, oMark ? *oMark : point};
|
||||
}
|
||||
|
||||
@@ -372,6 +372,11 @@ void EditView::YrsGetSelectionRectangles(
|
||||
}
|
||||
}
|
||||
|
||||
bool EditView::YrsWriteEECursor(YTransaction *const pTxn, Branch const& rArray, YOutput const*const pCurrent)
|
||||
{
|
||||
return getEditEngine().GetEditDoc().YrsWriteEECursor(pTxn, rArray, pCurrent, getImpl().GetEditSelection());
|
||||
}
|
||||
|
||||
void EditView::YrsApplyEECursor(OString const& rPeerId, OUString const& rAuthor,
|
||||
::std::pair<int64_t, int64_t> const point,
|
||||
::std::optional<::std::pair<int64_t, int64_t>> const oMark)
|
||||
|
||||
@@ -42,6 +42,7 @@ class IYrsTransactionSupplier;
|
||||
typedef struct TransactionInner YTransaction;
|
||||
typedef struct YTextEvent YTextEvent;
|
||||
typedef struct Branch Branch;
|
||||
typedef struct YOutput YOutput;
|
||||
#endif
|
||||
|
||||
class EditTextObject;
|
||||
@@ -417,6 +418,7 @@ public:
|
||||
void YrsReadEEState(YTransaction *);
|
||||
void YrsApplyEEDelta(YTransaction *, YTextEvent const* pEvent);
|
||||
OString GetYrsCommentId() const;
|
||||
bool YrsWriteEECursor(YTransaction *, Branch const& rArray, YOutput const* pCurrent);
|
||||
void YrsApplyEECursor(OString const& rPeerId, OUString const& rAuthor,
|
||||
::std::pair<int64_t, int64_t> point,
|
||||
::std::optional<::std::pair<int64_t, int64_t>> oMark);
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#define YRS_WEAK
|
||||
extern "C" {
|
||||
#if defined(__clang__)
|
||||
#pragma clang diagnostic push
|
||||
|
||||
@@ -740,7 +740,12 @@ void YrsReadCursor(ObserveCursorState & rState, OString const& rPeerId,
|
||||
{
|
||||
Branch const*const pArray{rCursor.value.y_type};
|
||||
auto const len{yarray_len(pArray)};
|
||||
#if defined(YRS_WEAK)
|
||||
if (len == 2
|
||||
&& yarray_get(pArray, rState.pTxn, 0)->tag == Y_JSON_STR)
|
||||
#else
|
||||
if (len == 3 || len == 5)
|
||||
#endif
|
||||
{
|
||||
::std::unique_ptr<YOutput, YOutputDeleter> const pComment{yarray_get(pArray, rState.pTxn, 0)};
|
||||
yvalidate(pComment->tag == Y_JSON_STR && pComment->len < SAL_MAX_INT32);
|
||||
@@ -748,6 +753,20 @@ void YrsReadCursor(ObserveCursorState & rState, OString const& rPeerId,
|
||||
YrsInvalidateEECursors(rState, rPeerId, &commentId);
|
||||
YrsInvalidateSwCursors(rState, rPeerId, rAuthor, false);
|
||||
::std::optional<::std::pair<int64_t, int64_t>> oMark;
|
||||
#if defined(YRS_WEAK)
|
||||
::std::unique_ptr<YOutput, YOutputDeleter> const pWeak{yarray_get(pArray, rState.pTxn, 1)};
|
||||
yvalidate(pWeak->tag == Y_WEAK_LINK);
|
||||
Branch * pBranch{nullptr};
|
||||
uint32_t start{SAL_MAX_UINT32};
|
||||
uint32_t end{SAL_MAX_UINT32};
|
||||
yweak_read(pWeak->value.y_type, rState.pTxn, &pBranch, &start, &end);
|
||||
yvalidate(start < SAL_MAX_INT32 && end < SAL_MAX_INT32);
|
||||
if (start != end)
|
||||
{
|
||||
oMark.emplace(end, -1);
|
||||
}
|
||||
::std::pair<int64_t, int64_t> const pos{start, -1};
|
||||
#else
|
||||
if (len == 5)
|
||||
{
|
||||
::std::unique_ptr<YOutput, YOutputDeleter> const pNode{
|
||||
@@ -763,6 +782,7 @@ void YrsReadCursor(ObserveCursorState & rState, OString const& rPeerId,
|
||||
::std::unique_ptr<YOutput, YOutputDeleter> const pContent{yarray_get(pArray, rState.pTxn, 2)};
|
||||
yvalidate(pContent->tag == Y_JSON_INT);
|
||||
::std::pair<int64_t, int64_t> const pos{pNode->value.integer, pContent->value.integer};
|
||||
#endif
|
||||
rState.CursorUpdates.emplace_back(rPeerId, ::std::optional<OString>{commentId}, ::std::optional<OUString>{rAuthor}, pos, oMark);
|
||||
}
|
||||
else if (len == 2 || len == 4)
|
||||
@@ -901,6 +921,15 @@ extern "C" void observe_cursors(void *const pState, uint32_t count, YEvent const
|
||||
YrsReadCursor(rState, peerId, pChange[2].values[0], author, false);
|
||||
break;
|
||||
}
|
||||
#if defined(YRS_WEAK)
|
||||
case Y_WEAK_LINK:
|
||||
{
|
||||
// not sure what this is, but yffi doesn't have any API
|
||||
// for it, let's hope we can just ignore it
|
||||
// YWeakLinkEvent const*const pEvent{&events[i].content.weak};
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
default:
|
||||
assert(false);
|
||||
}
|
||||
@@ -1263,6 +1292,12 @@ void DocumentStateManager::YrsNotifyCursorUpdate()
|
||||
pShell->GetView().GetPostItMgr()->GetActiveSidebarWin()})
|
||||
{
|
||||
// TODO StickyIndex cannot be inserted into YDoc ?
|
||||
#if defined(YRS_WEAK)
|
||||
if (pWin->GetOutlinerView()->GetEditView().YrsWriteEECursor(pTxn, *pEntry->value.y_type, pCurrent.get()))
|
||||
{
|
||||
YrsCommitModified();
|
||||
}
|
||||
#else
|
||||
ESelection const sel{pWin->GetOutlinerView()->GetSelection()};
|
||||
::std::vector<YInput> positions;
|
||||
// the ID of the comment
|
||||
@@ -1301,6 +1336,7 @@ void DocumentStateManager::YrsNotifyCursorUpdate()
|
||||
YrsCommitModified();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
else if (!pShell->IsStdMode()
|
||||
|| pShell->GetSelectedObjCount() != 0
|
||||
@@ -1342,6 +1378,9 @@ void DocumentStateManager::YrsNotifyCursorUpdate()
|
||||
{
|
||||
if (pCurrent == nullptr || pCurrent->tag != Y_ARRAY
|
||||
|| yarray_len(pCurrent->value.y_type) != 2
|
||||
#if defined(YRS_WEAK)
|
||||
|| yarray_get(pCurrent->value.y_type, pTxn, 0)->tag != Y_JSON_INT
|
||||
#endif
|
||||
|| yarray_get(pCurrent->value.y_type, pTxn, 0)->value.integer != positions[0].value.integer
|
||||
|| yarray_get(pCurrent->value.y_type, pTxn, 1)->value.integer != positions[1].value.integer)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user