static InfAdoptedSplitOperation*
inf_text_default_delete_operation_transform_split(
  InfTextDeleteOperation* operation,
  guint split_pos,
  guint split_len)
{
  InfTextDefaultDeleteOperationPrivate* priv;
  InfTextChunk* first_chunk;
  InfTextChunk* second_chunk;
  GObject* first;
  GObject* second;
  InfAdoptedSplitOperation* result;

  priv = INF_TEXT_DEFAULT_DELETE_OPERATION_PRIVATE(operation);

  first_chunk = inf_text_chunk_substring(priv->chunk, 0, split_pos);
  second_chunk = inf_text_chunk_substring(
    priv->chunk,
    split_pos,
    inf_text_chunk_get_length(priv->chunk) - split_pos
  );

  first = g_object_new(
    INF_TEXT_TYPE_DEFAULT_DELETE_OPERATION,
    "position", priv->position,
    "chunk", first_chunk,
    NULL
  );

  second = g_object_new(
    INF_TEXT_TYPE_DEFAULT_DELETE_OPERATION,
    "position", priv->position + split_pos + split_len,
    "chunk", second_chunk,
    NULL
  );

  inf_text_chunk_free(first_chunk);
  inf_text_chunk_free(second_chunk);

  result = inf_adopted_split_operation_new(
    INF_ADOPTED_OPERATION(first),
    INF_ADOPTED_OPERATION(second)
  );

  g_object_unref(first);
  g_object_unref(second);
  return result;
}
static InfAdoptedOperation*
inf_text_remote_delete_operation_copy(InfAdoptedOperation* operation)
{
  InfTextRemoteDeleteOperationPrivate* priv;
  GObject* result;
  InfTextRemoteDeleteOperationPrivate* result_priv;

  priv = INF_TEXT_REMOTE_DELETE_OPERATION_PRIVATE(operation);

  result = g_object_new(
    INF_TEXT_TYPE_REMOTE_DELETE_OPERATION,
    "position", priv->position,
    "length", priv->length,
    NULL
  );

  result_priv = INF_TEXT_REMOTE_DELETE_OPERATION_PRIVATE(result);

  result_priv->recon = inf_text_remote_delete_operation_recon_copy(
    priv->recon
  );

  result_priv->recon_offset = priv->recon_offset;
  return INF_ADOPTED_OPERATION(result);
}
static void
inf_adopted_session_noop_timeout_func(gpointer user_data)
{
  InfAdoptedSession* session;
  InfAdoptedSessionPrivate* priv;
  InfAdoptedOperation* op;
  InfAdoptedRequest* request;

  session = INF_ADOPTED_SESSION(user_data);
  priv = INF_ADOPTED_SESSION_PRIVATE(session);
  priv->noop_timeout = NULL;
  g_assert(priv->next_noop_user != NULL);

  op = INF_ADOPTED_OPERATION(inf_adopted_no_operation_new());
  request = inf_adopted_algorithm_generate_request_noexec(
    priv->algorithm,
    priv->next_noop_user->user,
    op
  );
  g_object_unref(op);

  /* This resets noop_time for this user, determines the next user for
   * which to generate a noop request and schedules the new timeout. */
  inf_adopted_session_broadcast_request(session, request);
  g_object_unref(request);
}
示例#4
0
static void
inf_adopted_request_set_property(GObject* object,
                                 guint prop_id,
                                 const GValue* value,
                                 GParamSpec* pspec)
{
    InfAdoptedRequest* request;
    InfAdoptedRequestPrivate* priv;

    request = INF_ADOPTED_REQUEST(object);
    priv = INF_ADOPTED_REQUEST_PRIVATE(request);

    switch(prop_id)
    {
    case PROP_TYPE:
        priv->type = g_value_get_enum(value);
        break;
    case PROP_VECTOR:
        g_assert(priv->vector == NULL); /* construct only */
        priv->vector = g_value_dup_boxed(value);
        break;
    case PROP_USER_ID:
        g_assert(priv->user_id == 0); /* construct only */
        g_assert(g_value_get_uint(value) != 0); /* 0 is invalid ID */
        priv->user_id = g_value_get_uint(value);
        break;
    case PROP_OPERATION:
        g_assert(priv->operation == NULL); /* construct only */
        priv->operation = INF_ADOPTED_OPERATION(g_value_dup_object(value));
        break;
    default:
        G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
        break;
    }
}
static InfAdoptedOperation*
inf_adopted_no_operation_transform(InfAdoptedOperation* operation,
                                   InfAdoptedOperation* against,
                                   InfAdoptedOperation* operation_lcs,
                                   InfAdoptedOperation* against_lcs,
                                   gint concurrency_id)
{
  return INF_ADOPTED_OPERATION(inf_adopted_no_operation_new());
}
static InfAdoptedOperation*
inf_text_default_insert_operation_revert(InfAdoptedOperation* operation)
{
  InfTextDefaultInsertOperationPrivate* priv;
  priv = INF_TEXT_DEFAULT_INSERT_OPERATION_PRIVATE(operation);

  return INF_ADOPTED_OPERATION(
    inf_text_default_delete_operation_new(priv->position, priv->chunk)
  );
}
/**
 * inf_text_delete_operation_transform_insert:
 * @operation: A #InfTextDeleteOperation.
 * @against: A #InfTextInsertOperation.
 *
 * Returns a new operation that includes the effect of @against into
 * @operation.
 *
 * Return Value: A new #InfAdoptedOperation.
 **/
InfAdoptedOperation*
inf_text_delete_operation_transform_insert(InfTextDeleteOperation* operation,
                                           InfTextInsertOperation* against)
{
  InfTextDeleteOperationIface* iface;
  guint own_pos;
  guint own_len;
  guint other_pos;
  guint other_len;

  g_return_val_if_fail(INF_TEXT_IS_DELETE_OPERATION(operation), NULL);
  g_return_val_if_fail(INF_TEXT_IS_INSERT_OPERATION(against), NULL);

  iface = INF_TEXT_DELETE_OPERATION_GET_IFACE(operation);
  g_return_val_if_fail(iface->transform_position != NULL, NULL);
  g_return_val_if_fail(iface->transform_split != NULL, NULL);

  own_pos = inf_text_delete_operation_get_position(operation);
  own_len = inf_text_delete_operation_get_length(operation);
  other_pos = inf_text_insert_operation_get_position(against);
  other_len = inf_text_insert_operation_get_length(against);

  if(other_pos >= own_pos + own_len)
  {
    return inf_adopted_operation_copy(INF_ADOPTED_OPERATION(operation));
  }
  else if(other_pos <= own_pos)
  {
    return INF_ADOPTED_OPERATION(
      iface->transform_position(operation, own_pos + other_len)
    );
  }
  else
  {
    return INF_ADOPTED_OPERATION(
      iface->transform_split(operation, other_pos - own_pos, other_len)
    );
  }
}
static InfAdoptedOperation*
inf_text_default_delete_operation_copy(InfAdoptedOperation* operation)
{
  InfTextDefaultDeleteOperationPrivate* priv;
  priv = INF_TEXT_DEFAULT_DELETE_OPERATION_PRIVATE(operation);

  return INF_ADOPTED_OPERATION(
    g_object_new(
      INF_TEXT_TYPE_DEFAULT_DELETE_OPERATION,
      "position", priv->position,
      "chunk", priv->chunk,
      NULL
    )
  );
}
static InfAdoptedSplitOperation*
inf_text_remote_delete_operation_transform_split(
  InfTextDeleteOperation* operation,
  guint split_pos,
  guint split_len)
{
  /* Need to split the delete operation and the recon list */
  InfTextRemoteDeleteOperationPrivate* priv;
  InfAdoptedSplitOperation* result;
  GObject* first_operation;
  GObject* second_operation;
  InfTextRemoteDeleteOperationPrivate* result_priv;
  InfTextRemoteDeleteOperationRecon* recon;
  InfTextRemoteDeleteOperationRecon* new_recon;
  GSList* first_recon;
  GSList* second_recon;
  guint recon_cur_len;
  GSList* item;

  priv = INF_TEXT_REMOTE_DELETE_OPERATION_PRIVATE(operation);

  first_recon = NULL;
  second_recon = NULL;
  recon_cur_len = 0;

  for(item = priv->recon; item != NULL; item = g_slist_next(item))
  {
    recon = (InfTextRemoteDeleteOperationRecon*)item->data;

    g_assert(recon->position >= recon_cur_len);
    if(recon->position - recon_cur_len <= split_pos)
    {
      new_recon = g_slice_new(InfTextRemoteDeleteOperationRecon);
      new_recon->position = recon->position;
      new_recon->chunk = inf_text_chunk_copy(recon->chunk);
      first_recon = g_slist_prepend(first_recon, new_recon);

      recon_cur_len += inf_text_chunk_get_length(recon->chunk);
    }
    else
    {
      new_recon = g_slice_new(InfTextRemoteDeleteOperationRecon);
      new_recon->position = recon->position - (split_pos + recon_cur_len);
      new_recon->chunk = inf_text_chunk_copy(recon->chunk);
      second_recon = g_slist_prepend(second_recon, new_recon);
    }
  }

  first_operation = g_object_new(
    INF_TEXT_TYPE_REMOTE_DELETE_OPERATION,
    "position", priv->position,
    "length", split_pos,
    NULL
  );
  
  second_operation = g_object_new(
    INF_TEXT_TYPE_REMOTE_DELETE_OPERATION,
    "position", priv->position + split_len,
    "length", priv->length - split_pos,
    NULL
  );

  result_priv = INF_TEXT_REMOTE_DELETE_OPERATION_PRIVATE(first_operation);
  result_priv->recon = g_slist_reverse(first_recon);
  result_priv->recon_offset = priv->recon_offset;

  result_priv = INF_TEXT_REMOTE_DELETE_OPERATION_PRIVATE(second_operation);
  result_priv->recon = g_slist_reverse(second_recon);
  result_priv->recon_offset = priv->recon_offset + split_pos + recon_cur_len;

  result = inf_adopted_split_operation_new(
    INF_ADOPTED_OPERATION(first_operation),
    INF_ADOPTED_OPERATION(second_operation)
  );

  g_object_unref(G_OBJECT(second_operation));
  g_object_unref(G_OBJECT(first_operation));
  return result;
}
static InfAdoptedOperation*
inf_text_remote_delete_operation_apply_transformed(InfAdoptedOperation* op,
                                                   InfAdoptedOperation* trans,
                                                   InfAdoptedUser* by,
                                                   InfBuffer* buffer,
                                                   GError** error)
{
  InfTextRemoteDeleteOperationPrivate* priv;
  InfTextChunk* chunk;
  InfTextChunk* temp_slice;
  GSList* list;
  GSList* item;
  InfAdoptedOperation* operation;
  GSList* recon_list;
  GSList* recon_item;
  InfTextRemoteDeleteOperationRecon* recon;
  InfTextDefaultDeleteOperation* result;

  g_assert(INF_TEXT_IS_REMOTE_DELETE_OPERATION(op));
  g_assert(INF_TEXT_IS_BUFFER(buffer));

  /* TODO: We can probably optimize this function, but then we should
   * a) profile it and b) in many cases input parameters to this function
   * are trivial anyway. */
  if(INF_ADOPTED_IS_SPLIT_OPERATION(trans))
  {
    list = inf_adopted_split_operation_unsplit(
      INF_ADOPTED_SPLIT_OPERATION(trans)
    );
  }
  else
  {
    list = g_slist_prepend(NULL, trans);
  }

  chunk = inf_text_chunk_new(
    inf_text_buffer_get_encoding(INF_TEXT_BUFFER(buffer))
  );

  /* We assume the list of remote delete operations to be in order */
  for(item = list; item != NULL; item = g_slist_next(item))
  {
    g_assert(INF_TEXT_IS_REMOTE_DELETE_OPERATION(item->data));
    priv = INF_TEXT_REMOTE_DELETE_OPERATION_PRIVATE(item->data);

    operation = INF_ADOPTED_OPERATION(item->data);

    if(priv->length > 0)
    {
      temp_slice = inf_text_buffer_get_slice(
        INF_TEXT_BUFFER(buffer),
        priv->position,
        priv->length
      );

      recon_list = inf_text_remote_delete_operation_recon_feed(
        priv->recon,
        0,
        temp_slice
      );

      inf_text_chunk_free(temp_slice);
    }
    else
    {
      recon_list = priv->recon;
    }

    for(recon_item = recon_list;
        recon_item != NULL;
        recon_item = g_slist_next(recon_item))
    {
      recon = (InfTextRemoteDeleteOperationRecon*)recon_item->data;
      g_assert(priv->recon_offset + recon->position ==
               inf_text_chunk_get_length(chunk));

      inf_text_chunk_insert_chunk(
        chunk,
        inf_text_chunk_get_length(chunk),
        recon->chunk
      );
    }

    /* Free recon list if newly allocated */
    if(priv->length > 0)
      inf_text_remote_delete_operation_recon_free(recon_list);

    if(!inf_adopted_operation_apply(operation, by, buffer, error))
    {
      g_slist_free(list);
      inf_text_chunk_free(chunk);
      return NULL;
    }
  }

  g_slist_free(list);

  priv = INF_TEXT_REMOTE_DELETE_OPERATION_PRIVATE(op);
  result = inf_text_default_delete_operation_new(priv->position, chunk);
  inf_text_chunk_free(chunk);

  return INF_ADOPTED_OPERATION(result);
}
示例#11
0
static InfAdoptedOperation*
inf_adopted_no_operation_revert(InfAdoptedOperation* operation)
{
  return INF_ADOPTED_OPERATION(inf_adopted_no_operation_new());
}
/**
 * inf_text_delete_operation_transform_delete:
 * @operation: A #InfTextDeleteOperation.
 * @against: Another #InfTextDeleteOperation.
 *
 * Returns a new operation that includes the effect of @against into
 * @operation.
 *
 * Return Value: A new #InfAdoptedOperation.
 **/
InfAdoptedOperation*
inf_text_delete_operation_transform_delete(InfTextDeleteOperation* operation,
                                           InfTextDeleteOperation* against)
{
  InfTextDeleteOperationIface* iface;
  guint own_pos;
  guint own_len;
  guint other_pos;
  guint other_len;

  g_return_val_if_fail(INF_TEXT_IS_DELETE_OPERATION(operation), NULL);
  g_return_val_if_fail(INF_TEXT_IS_DELETE_OPERATION(against), NULL);

  iface = INF_TEXT_DELETE_OPERATION_GET_IFACE(operation);
  g_return_val_if_fail(iface->transform_position != NULL, NULL);
  g_return_val_if_fail(iface->transform_overlap != NULL, NULL);

  own_pos = inf_text_delete_operation_get_position(operation);
  own_len = inf_text_delete_operation_get_length(operation);
  other_pos = inf_text_delete_operation_get_position(against);
  other_len = inf_text_delete_operation_get_length(against);

  if(own_pos + own_len <= other_pos)
  {
    return inf_adopted_operation_copy(INF_ADOPTED_OPERATION(operation));
  }
  else if(own_pos >= other_pos + other_len)
  {
    return INF_ADOPTED_OPERATION(
      iface->transform_position(operation, own_pos - other_len)
    );
  }
  /* Somehow overlapping now */
  else if(other_pos <= own_pos && other_pos + other_len >= own_pos + own_len)
  {
    return INF_ADOPTED_OPERATION(
      iface->transform_overlap(
        operation,
        against,
        other_pos,
        0,
        own_pos - other_pos,
        own_len
      )
    );
  }
  else if(other_pos <= own_pos && other_pos + other_len < own_pos + own_len)
  {
    return INF_ADOPTED_OPERATION(
      iface->transform_overlap(
        operation,
        against,
        other_pos,
        0,
        own_pos - other_pos,
        other_pos + other_len - own_pos
      )
    );
  }
  else if(other_pos > own_pos && other_pos + other_len >= own_pos + own_len)
  {
    return INF_ADOPTED_OPERATION(
      iface->transform_overlap(
        operation,
        against,
        own_pos,
        other_pos - own_pos,
        0,
        own_pos + own_len - other_pos
      )
    );
  }
  else
  {
    return INF_ADOPTED_OPERATION(
      iface->transform_overlap(
        operation,
        against,
        own_pos,
        other_pos - own_pos,
        0,
        other_len
      )
    );
  }
}
/**
 * inf_text_insert_operation_transform_insert:
 * @operation: A #InfTextInsertOperation.
 * @against: Another #InfTextInsertOperation.
 * @op_lcs: The given operation in a previous state, or %NULL.
 * @ag_lcs: The @against operation in a previous state, or %NULL.
 * @cid: The concurrency ID for the transformation.
 *
 * Returns a new operation that includes the effect of @against into
 * @operation.
 *
 * Return Value: A new #InfAdoptedOperation.
 **/
InfAdoptedOperation*
inf_text_insert_operation_transform_insert(InfTextInsertOperation* operation,
                                           InfTextInsertOperation* against,
                                           InfTextInsertOperation* op_lcs,
                                           InfTextInsertOperation* ag_lcs,
                                           InfAdoptedConcurrencyId cid)
{
  InfTextInsertOperationIface* iface;
  guint op_pos;
  guint against_pos;
  guint op_lcs_pos;
  guint against_lcs_pos;
  guint against_length;

  g_return_val_if_fail(INF_TEXT_IS_INSERT_OPERATION(operation), NULL);
  g_return_val_if_fail(INF_TEXT_IS_INSERT_OPERATION(against), NULL);

  iface = INF_TEXT_INSERT_OPERATION_GET_IFACE(operation);
  g_return_val_if_fail(iface->transform_position != NULL, NULL);

  op_pos = inf_text_insert_operation_get_position(operation);
  against_pos = inf_text_insert_operation_get_position(against);

  if(op_pos < against_pos)
  {
    return inf_adopted_operation_copy(INF_ADOPTED_OPERATION(operation));
  }
  else if(op_pos > against_pos)
  {
    against_length = inf_text_insert_operation_get_length(against);

    return INF_ADOPTED_OPERATION(
      iface->transform_position(operation, op_pos + against_length)
    );
  }
  else
  {
    g_assert(op_lcs != NULL);
    g_assert(ag_lcs != NULL);

    op_lcs_pos = inf_text_insert_operation_get_position(op_lcs);
    against_lcs_pos = inf_text_insert_operation_get_position(ag_lcs);

    if(op_lcs_pos < against_lcs_pos ||
       (op_lcs_pos == against_lcs_pos &&
        cid == INF_ADOPTED_CONCURRENCY_OTHER))
    {
      return inf_adopted_operation_copy(INF_ADOPTED_OPERATION(operation));
    }
    else if(op_lcs_pos > against_lcs_pos ||
            (op_lcs_pos == against_lcs_pos &&
             cid == INF_ADOPTED_CONCURRENCY_SELF))
    {
      against_length = inf_text_insert_operation_get_length(against);

      return INF_ADOPTED_OPERATION(
        iface->transform_position(operation, op_pos + against_length)
      );
    }
    else
    {
      g_assert_not_reached();
      return NULL;
    }
  }
}